PCI: Powersaving: Poweroff the switch
Signed-off-by: wanghaoxiang <wanghaoxiang@huawei.com>
This commit is contained in:
Executable → Regular
+23
-1
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user