From 2cee16acad1225662a1b690b2f686cfdd7b19624 Mon Sep 17 00:00:00 2001 From: w00345465 Date: Tue, 3 Apr 2018 23:49:01 +0800 Subject: [PATCH] PCI: Powersaving: Poweroff the switch Signed-off-by: wanghaoxiang --- drivers/net/ethernet/realtek/r8168_n.c | 24 ++- drivers/pci/host/pcie-kirin.c | 42 +++++- drivers/pci/host/pcie-kirin.h | 7 + drivers/pci/host/pcie-kirin970.c | 201 +++++++++++++++++++++---- 4 files changed, 238 insertions(+), 36 deletions(-) mode change 100755 => 100644 drivers/net/ethernet/realtek/r8168_n.c diff --git a/drivers/net/ethernet/realtek/r8168_n.c b/drivers/net/ethernet/realtek/r8168_n.c old mode 100755 new mode 100644 index 61ed7576a56f..2dac500bd101 --- a/drivers/net/ethernet/realtek/r8168_n.c +++ b/drivers/net/ethernet/realtek/r8168_n.c @@ -86,6 +86,7 @@ #include #include #endif +#include /* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). The RTL chips use a 64 element hash table based on the Ethernet CRC. */ @@ -443,6 +444,18 @@ static void rtl8168_phy_power_up(struct net_device *dev); static void rtl8168_phy_power_down(struct net_device *dev); static int rtl8168_set_speed(struct net_device *dev, u8 autoneg, u32 speed, u8 duplex); +#ifdef CONFIG_PCIE_KIRIN +extern int kirin_pcie_pm_control(int power_ops); +#endif + +static unsigned int remove_work_count = 1; +static struct work_struct remove_work; +static void +rtl8168_remove_work(struct work_struct *work) +{ + kirin_pcie_pm_control(0); +} + #ifdef CONFIG_R8168_NAPI static int rtl8168_poll(napi_ptr napi, napi_budget budget); #endif @@ -3543,7 +3556,16 @@ rtl8168_check_link_status(struct net_device *dev) } break; } - } +#ifdef CONFIG_PCIE_KIRIN + remove_work_count++; + if (remove_work_count > 6) { + INIT_WORK(&remove_work, rtl8168_remove_work); + schedule_work(&remove_work); + } + } else { + remove_work_count = 0; +#endif + } } static void diff --git a/drivers/pci/host/pcie-kirin.c b/drivers/pci/host/pcie-kirin.c index d6a493a1dc30..f317c563f609 100644 --- a/drivers/pci/host/pcie-kirin.c +++ b/drivers/pci/host/pcie-kirin.c @@ -105,13 +105,14 @@ static int kirin_pcie_link_up(struct pcie_port *pp) struct kirin_pcie *pcie = to_kirin_pcie(pp); u32 val = kirin_elb_readl(pcie, SOC_PCIECTRL_STATE0_ADDR); + if ((val & PCIE_LINKUP_ENABLE) == PCIE_LINKUP_ENABLE) return 1; return 0; } -static int kirin_pcie_establish_link(struct pcie_port *pp) +int kirin_pcie_establish_link(struct pcie_port *pp) { int count = 0; @@ -270,6 +271,21 @@ int kirin_pcie_restore_rc_cfg(struct kirin_pcie *pcie) return 0; } +int kirin_pcie_pm_control(int power_ops) +{ + struct kirin_pcie *pcie; + int (*pm_control)(int); + + pcie = g_kirin_pcie; + pm_control = pcie->pcie_ops->pcie_pm_control; + if (!pm_control) + return -1; + + return pm_control(power_ops); +} +EXPORT_SYMBOL_GPL(kirin_pcie_pm_control); + + static int kirin_pcie_probe(struct platform_device *pdev) { struct kirin_pcie *pcie; @@ -326,6 +342,7 @@ static int kirin_pcie_probe(struct platform_device *pdev) } ret = kirin_pcie_save_rc_cfg(pcie); + atomic_set(&(pcie->usr_suspend), 0); return 0; } @@ -353,10 +370,11 @@ static int kirin_pcie_resume_noirq(struct device *dev) if (ret) return ret; - ret = kirin_pcie_establish_link(&(pcie->pp)); - if (ret) - return ret; - + if (!atomic_read(&(pcie->usr_suspend))) { + ret = kirin_pcie_establish_link(&(pcie->pp)); + if (ret) + return ret; + } return 0; } @@ -393,6 +411,19 @@ static int kirin_pcie_suspend_noirq(struct device *dev) #endif +static void kirin_pcie_shutdown(struct platform_device *pdev) +{ + struct kirin_pcie *pcie; + + pcie = dev_get_drvdata(&(pdev->dev)); + if (pcie == NULL) { + dev_err(&pdev->dev, "Failed to get drvdata\n"); + return; + } + + pcie->pcie_ops->pcie_shutdown(pcie); +} + static const struct dev_pm_ops kirin_pcie_dev_pm_ops = { .suspend_noirq = kirin_pcie_suspend_noirq, .resume_noirq = kirin_pcie_resume_noirq, @@ -414,6 +445,7 @@ MODULE_DEVICE_TABLE(of, kirin_pcie_match); struct platform_driver kirin_pcie_driver = { .probe = kirin_pcie_probe, + .shutdown = kirin_pcie_shutdown, .driver = { .name = "Kirin-pcie", .owner = THIS_MODULE, diff --git a/drivers/pci/host/pcie-kirin.h b/drivers/pci/host/pcie-kirin.h index 8d737cfc1f2e..15e87f7cc37d 100644 --- a/drivers/pci/host/pcie-kirin.h +++ b/drivers/pci/host/pcie-kirin.h @@ -91,6 +91,9 @@ struct kirin_pcie { struct pci_saved_state *rc_saved_state; const struct kirin_pcie_ops *pcie_ops; struct regulator *ldo33; + atomic_t is_power_on; + atomic_t usr_suspend; + struct mutex power_lock; }; struct kirin_pcie_ops { @@ -99,6 +102,8 @@ struct kirin_pcie_ops { int (*pcie_resume_noirq)(struct device *dev); void (*kirin_phy_writel)(struct kirin_pcie *pcie, u32 val, u32 reg); u32 (*kirin_phy_readl)(struct kirin_pcie *pcie, u32 reg); + int (*pcie_pm_control)(int power_ops); + void (*pcie_shutdown)(struct kirin_pcie *pcie); }; static inline void kirin_elb_writel(struct kirin_pcie *pcie, u32 val, u32 reg) @@ -133,7 +138,9 @@ static inline u32 kirin_natural_phy_readl(struct kirin_pcie *pcie, u32 reg) int kirin_pcie_save_rc_cfg(struct kirin_pcie *pcie); int kirin_pcie_restore_rc_cfg(struct kirin_pcie *pcie); +int kirin_pcie_establish_link(struct pcie_port *pp); +extern struct kirin_pcie *g_kirin_pcie; extern const struct kirin_pcie_ops kirin960_pcie_ops; extern const struct kirin_pcie_ops kirin970_pcie_ops; #endif diff --git a/drivers/pci/host/pcie-kirin970.c b/drivers/pci/host/pcie-kirin970.c index 36c07407691e..bcf6ffe5a4a1 100644 --- a/drivers/pci/host/pcie-kirin970.c +++ b/drivers/pci/host/pcie-kirin970.c @@ -593,12 +593,23 @@ static int kirin970_pcie_clkreq_cfg(struct kirin_pcie *pcie, int pull_up) int kirin970_pcie_turn_on(struct kirin_pcie *pcie) { - int ret; + int ret = 0; u32 val; + mutex_lock(&pcie->power_lock); + + if (atomic_read(&(pcie->is_power_on))) + goto MUTEX_UNLOCK; + + ret = regulator_enable(pcie->ldo33); + if (ret) { + dev_err(pcie->pp.dev, "Failed to enable ldo33\n"); + goto MUTEX_UNLOCK; + } + ret = kirin970_pcie_clkreq_cfg(pcie, false); if (ret) - return ret; + goto DISABLE_LDO; /* pull downphy ISO */ regmap_write(pcie->sysctrl, 0x44, 0x20); @@ -606,7 +617,7 @@ int kirin970_pcie_turn_on(struct kirin_pcie *pcie) /* enable PCIe sys&phy pclk */ ret = kirin970_pcie_pclk_ctrl(pcie, true); if (ret) - return ret; + goto DISABLE_LDO; /* deasset PCIeCtrl&PCIePHY */ regmap_write(pcie->crgctrl, 0x88, 0x8c000000); @@ -637,23 +648,35 @@ int kirin970_pcie_turn_on(struct kirin_pcie *pcie) if (kirin970_pcie_noc_power(pcie, false)) goto ALLCLK_CLOSE; - return 0; + atomic_set(&(pcie->is_power_on), 1); + ret = 0; + goto MUTEX_UNLOCK; ALLCLK_CLOSE: kirin970_pcie_allclk_ctrl(pcie, false); PCLK_CLOSE: kirin970_pcie_pclk_ctrl(pcie, false); - return -1; +DISABLE_LDO: + regulator_disable(pcie->ldo33); + ret = -1; +MUTEX_UNLOCK: + mutex_unlock(&pcie->power_lock); + return ret; } int kirin970_pcie_turn_off(struct kirin_pcie *pcie) { u32 val; - int ret; + int ret = 0; + + mutex_lock(&pcie->power_lock); + + if (!atomic_read(&(pcie->is_power_on))) + goto MUTEX_UNLOCK; ret = kirin970_pcie_noc_power(pcie, true); if (ret) - return ret; + goto MUTEX_UNLOCK; kirin970_pcie_perst_cfg(pcie, false); @@ -671,9 +694,19 @@ int kirin970_pcie_turn_off(struct kirin_pcie *pcie) ret = kirin970_pcie_clkreq_cfg(pcie, true); if (ret) - return ret; + goto MUTEX_UNLOCK; - return 0; + atomic_set(&(pcie->is_power_on), 0); + + ret = regulator_disable(pcie->ldo33); + if (ret) { + dev_err(pcie->pp.dev, "Failed to disable ldo33\n"); + goto MUTEX_UNLOCK; + } + +MUTEX_UNLOCK: + mutex_unlock(&pcie->power_lock); + return ret; } int kirin970_pcie_power_on(struct kirin_pcie *pcie, bool on) @@ -692,17 +725,17 @@ static int kirin970_pcie_probe(struct kirin_pcie *pcie) pp = &pcie->pp; pdev = to_platform_device(pp->dev); - + ret = kirin970_pcie_get_clk(pcie, pdev); if (ret != 0) - return -ENODEV; + return -ENODEV; ret = kirin970_pcie_get_resource(pp, pdev); if (ret != 0) return -ENODEV; - + kirin_pcie_get_eyeparam(pcie, pdev); - + pcie->gpio_id_reset[0] = of_get_named_gpio(pdev->dev.of_node, "switch,reset-gpios", 0); pcie->gpio_id_reset[1] = of_get_named_gpio(pdev->dev.of_node, @@ -711,7 +744,7 @@ static int kirin970_pcie_probe(struct kirin_pcie *pcie) "m_2,reset-gpios", 0); pcie->gpio_id_reset[3] = of_get_named_gpio(pdev->dev.of_node, "mini1,reset-gpios", 0); - + if (pcie->gpio_id_reset[0] < 0) return -ENODEV; if (pcie->gpio_id_reset[1] < 0) @@ -720,7 +753,7 @@ static int kirin970_pcie_probe(struct kirin_pcie *pcie) return -ENODEV; if (pcie->gpio_id_reset[3] < 0) return -ENODEV; - + if (gpio_request((unsigned int)pcie->gpio_id_reset[0], "pcie_switch_reset")) return -EINVAL; if (gpio_request((unsigned int)pcie->gpio_id_reset[1], "pcie_eth_reset")) @@ -729,16 +762,11 @@ static int kirin970_pcie_probe(struct kirin_pcie *pcie) return -EINVAL; if (gpio_request((unsigned int)pcie->gpio_id_reset[3], "pcie_mini1_reset")) return -EINVAL; - + pcie->ldo33 = devm_regulator_get(pp->dev, "ldo33"); if(IS_ERR_OR_NULL(pcie->ldo33)) return PTR_ERR(pcie->ldo33); - ret = regulator_enable(pcie->ldo33); - if (ret) { - dev_err(pp->dev, "Failed to enable ldo33\n"); - return ret; - } ret = kirin970_pcie_pinctrl_init(pcie, pdev); if (ret != 0) @@ -762,6 +790,8 @@ static int kirin970_pcie_probe(struct kirin_pcie *pcie) if (gpio_request((unsigned int)pcie->gpio_id_clkreq[2], "pcie_mini1_clkreq")) return -EINVAL; + mutex_init(&pcie->power_lock); + ret = kirin970_pcie_power_on(pcie, true); if (ret) return ret; @@ -769,6 +799,99 @@ static int kirin970_pcie_probe(struct kirin_pcie *pcie) return 0; } +#define ETH_DEVICE 0x8168 +#define ETH_VENDOR 0x10ec + +static bool pcie_can_sleep(struct kirin_pcie *pcie) +{ + struct pci_dev *dev = NULL; + int type; + + for_each_pci_dev(dev) { + if (dev) { + type = pci_pcie_type(dev); + if ((type == PCI_EXP_TYPE_ENDPOINT) || (type == PCI_EXP_TYPE_LEG_END)) { + if ((dev->device != ETH_DEVICE) || (dev->vendor != ETH_VENDOR)) + return false; + } + } + } + + return true; +} + +static int kirin970_pcie_usr_suspend(struct kirin_pcie *pcie) +{ + int ret; + struct pcie_port *pp; + + pp = &pcie->pp; + + if (atomic_read(&(pcie->usr_suspend)) || !atomic_read(&(pcie->is_power_on))) { + dev_err(pp->dev, "Already suspend by EP\n"); + return -EINVAL; + } + + ret = kirin970_pcie_power_on(pcie, false); + if (ret) { + dev_err(pp->dev, "Failed to power off\n"); + return ret; + } + + atomic_set(&(pcie->usr_suspend), 1); + return 0; +} + +static int kirin970_pcie_usr_resume(struct kirin_pcie *pcie) +{ + int ret; + struct pcie_port *pp; + struct pci_dev *rc_dev; + + pp = &pcie->pp; + rc_dev = pcie->rc_dev; + + atomic_set(&(pcie->usr_suspend), 0); + + if (kirin970_pcie_power_on(pcie, true)) { + dev_err(pp->dev, "Failed to power on\n"); + atomic_set(&(pcie->usr_suspend), 1); + return -EINVAL; + } + + ret = kirin_pcie_establish_link(&pcie->pp); + if (ret) { + if (kirin970_pcie_power_on(pcie, false)) + dev_err(pp->dev, "Failed to power off\n"); + atomic_set(&(pcie->usr_suspend), 1); + return -EINVAL; + } + + if (rc_dev) + kirin_pcie_restore_rc_cfg(pcie); + + return 0; +} + + + +int kirin970_pcie_pm_control(int power_ops) +{ + struct kirin_pcie *pcie = g_kirin_pcie; + + if (power_ops) { + return kirin970_pcie_usr_resume(pcie); + } else { + if (!pcie_can_sleep(pcie)) + return -1; + + pci_remove_root_bus(pcie->rc_dev->bus); + + return kirin970_pcie_usr_suspend(pcie); + } +} +EXPORT_SYMBOL_GPL(kirin970_pcie_pm_control); + static int kirin970_pcie_resume_noirq(struct device *dev) { struct pci_dev *rc_dev; @@ -783,13 +906,15 @@ static int kirin970_pcie_resume_noirq(struct device *dev) pp = &pcie->pp; rc_dev = pcie->rc_dev; - if (kirin970_pcie_power_on(pcie, true)) { - dev_err(dev, "Failed to power on\n"); - return -EINVAL; - } + if (!atomic_read(&(pcie->usr_suspend))) { + if (kirin970_pcie_power_on(pcie, true)) { + dev_err(dev, "Failed to power on\n"); + return -EINVAL; + } - if (rc_dev) - kirin_pcie_restore_rc_cfg(pcie); + if (rc_dev) + kirin_pcie_restore_rc_cfg(pcie); + } return 0; } @@ -809,20 +934,36 @@ static int kirin970_pcie_suspend_noirq(struct device *dev) rc_dev = pcie->rc_dev; pp = &pcie->pp; - if (kirin970_pcie_power_on(pcie, false)) { - dev_err(dev, "Failed to power off\n"); - return -EINVAL; + if (atomic_read(&(pcie->is_power_on))) { + if (!atomic_read(&(pcie->usr_suspend))) { + if (kirin970_pcie_power_on(pcie, false)) { + dev_err(dev, "Failed to power off\n"); + return -EINVAL; + } + } } return 0; } +static void kirin970_pcie_shutdown(struct kirin_pcie *pcie) +{ + if (atomic_read(&(pcie->is_power_on))) { + if (kirin970_pcie_power_on(pcie, false)) { + dev_err(pcie->pp.dev, "Failed to power off\n"); + return; + } + } +} + const struct kirin_pcie_ops kirin970_pcie_ops = { .pcie_probe = kirin970_pcie_probe, .kirin_phy_writel = kirin970_phy_writel, .kirin_phy_readl = kirin970_phy_readl, .pcie_suspend_noirq = kirin970_pcie_suspend_noirq, .pcie_resume_noirq = kirin970_pcie_resume_noirq, + .pcie_pm_control = kirin970_pcie_pm_control, + .pcie_shutdown = kirin970_pcie_shutdown, }; EXPORT_SYMBOL_GPL(kirin970_pcie_ops);