PCI: Powersaving: Poweroff the switch

Signed-off-by: wanghaoxiang <wanghaoxiang@huawei.com>
This commit is contained in:
w00345465
2018-04-03 23:49:01 +08:00
committed by Guodong Xu
parent 9c5bc08a55
commit 2cee16acad
4 changed files with 238 additions and 36 deletions
+23 -1
View File
@@ -86,6 +86,7 @@
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#endif
#include <linux/workqueue.h>
/* 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
+37 -5
View File
@@ -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,
+7
View File
@@ -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
+171 -30
View File
@@ -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);