* [RFC PATCH v9 0/7] PCI: rockchip: Move PCIe WAKE# handling into pci core
@ 2017-10-27 7:17 Jeffy Chen
2017-10-27 7:17 ` [RFC PATCH v9 1/7] dt-bindings: PCI: Add definition of PCIe WAKE# irq and PCI irq Jeffy Chen
` (6 more replies)
0 siblings, 7 replies; 8+ messages in thread
From: Jeffy Chen @ 2017-10-27 7:17 UTC (permalink / raw)
To: linux-kernel, bhelgaas
Cc: linux-pm, tony, shawn.lin, briannorris, rjw, dianders, Jeffy Chen,
Xinming Hu, linux-pci, Rob Herring, Catalin Marinas, Kalle Valo,
Heiko Stuebner, linux-acpi, linux-rockchip, Nishant Sarmukadam,
Will Deacon, Matthias Kaehlcke, devicetree, Ganapathi Bhat,
Frank Rowand, Len Brown, Amitkumar Karwar, linux-arm-kernel
Currently we are handling wake irq in mrvl wifi driver. Move it into
pci core.
Tested on my chromebook bob(with cros 4.4 kernel and mrvl wifi).
Changes in v9:
Add section for PCI devices and rewrite the commit message.
Rewrite the commit message.
Fix check error in .cleanup().
Move dedicated wakeirq setup to setup() callback and use
device_set_wakeup_enable() to enable/disable.
Changes in v8:
Add optional "pci", and rewrite commit message.
Rewrite the commit message.
Add pci-of.c and use platform_pm_ops to handle the PCIe WAKE# signal.
Changes in v7:
Move PCIE_WAKE handling into pci core.
Changes in v6:
Fix device_init_wake error handling, and add some comments.
Changes in v5:
Move to pci.txt
Use "wakeup" instead of "wake"
Rebase.
Changes in v3:
Fix error handling.
Changes in v2:
Use dev_pm_set_dedicated_wake_irq.
Jeffy Chen (7):
dt-bindings: PCI: Add definition of PCIe WAKE# irq and PCI irq
of/irq: Adjust of_pci_irq parsing for multiple interrupts
mwifiex: Disable wakeup irq handling for pcie
arm64: dts: rockchip: Move PCIe WAKE# irq to pcie driver for Gru
PCI: Make pci_platform_pm_ops's callbacks optional
PCI / PM: Move acpi wakeup code to pci core
PCI / PM: Add support for the PCIe WAKE# signal for OF
Documentation/devicetree/bindings/pci/pci.txt | 8 ++
arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi | 15 +--
drivers/net/wireless/marvell/mwifiex/main.c | 4 +
drivers/of/of_pci_irq.c | 13 ++-
drivers/pci/Makefile | 2 +-
drivers/pci/pci-acpi.c | 121 ++++++++++++-------------
drivers/pci/pci-driver.c | 9 ++
drivers/pci/pci-of.c | 126 ++++++++++++++++++++++++++
drivers/pci/pci.c | 112 +++++++++++++++++++----
drivers/pci/pci.h | 31 +++++--
drivers/pci/probe.c | 12 ++-
drivers/pci/remove.c | 2 +
include/linux/pci.h | 2 +
13 files changed, 360 insertions(+), 97 deletions(-)
create mode 100644 drivers/pci/pci-of.c
--
2.11.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [RFC PATCH v9 1/7] dt-bindings: PCI: Add definition of PCIe WAKE# irq and PCI irq
2017-10-27 7:17 [RFC PATCH v9 0/7] PCI: rockchip: Move PCIe WAKE# handling into pci core Jeffy Chen
@ 2017-10-27 7:17 ` Jeffy Chen
2017-10-27 7:17 ` [RFC PATCH v9 2/7] of/irq: Adjust of_pci_irq parsing for multiple interrupts Jeffy Chen
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Jeffy Chen @ 2017-10-27 7:17 UTC (permalink / raw)
To: linux-kernel, bhelgaas
Cc: linux-pm, tony, shawn.lin, briannorris, rjw, dianders, Jeffy Chen,
devicetree, linux-pci, Rob Herring, Mark Rutland
We are going to handle PCIe WAKE# pin for PCI bus bridges and PCI
devices in the pci core, so add definitions of the optional PCIe
WAKE# pin for PCI bus bridges and PCI devices.
Also add an definition of the optional PCI interrupt pin for PCI
devices to distinguish it from the PCIe WAKE# pin.
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
---
Changes in v9:
Add section for PCI devices and rewrite the commit message.
Changes in v8:
Add optional "pci", and rewrite commit message.
Changes in v7: None
Changes in v6: None
Changes in v5:
Move to pci.txt
Changes in v3: None
Changes in v2: None
Documentation/devicetree/bindings/pci/pci.txt | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Documentation/devicetree/bindings/pci/pci.txt b/Documentation/devicetree/bindings/pci/pci.txt
index c77981c5dd18..d4406d4e15ad 100644
--- a/Documentation/devicetree/bindings/pci/pci.txt
+++ b/Documentation/devicetree/bindings/pci/pci.txt
@@ -24,3 +24,11 @@ driver implementation may support the following properties:
unsupported link speed, for instance, trying to do training for
unsupported link speed, etc. Must be '4' for gen4, '3' for gen3, '2'
for gen2, and '1' for gen1. Any other values are invalid.
+- interrupts: Interrupt specifier for each name in interrupt-names.
+- interrupt-names: May contains "wakeup" for PCIe WAKE# interrupt.
+
+PCI devices have standardized Device Tree bindings:
+
+- interrupts: Interrupt specifier for each name in interrupt-names.
+- interrupt-names: May contains "wakeup" for PCIe WAKE# interrupt and "pci" for
+ PCI interrupt.
--
2.11.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [RFC PATCH v9 2/7] of/irq: Adjust of_pci_irq parsing for multiple interrupts
2017-10-27 7:17 [RFC PATCH v9 0/7] PCI: rockchip: Move PCIe WAKE# handling into pci core Jeffy Chen
2017-10-27 7:17 ` [RFC PATCH v9 1/7] dt-bindings: PCI: Add definition of PCIe WAKE# irq and PCI irq Jeffy Chen
@ 2017-10-27 7:17 ` Jeffy Chen
2017-10-27 7:17 ` [RFC PATCH v9 3/7] mwifiex: Disable wakeup irq handling for pcie Jeffy Chen
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Jeffy Chen @ 2017-10-27 7:17 UTC (permalink / raw)
To: linux-kernel, bhelgaas
Cc: linux-pm, tony, shawn.lin, briannorris, rjw, dianders, Jeffy Chen,
Frank Rowand, devicetree, Rob Herring
Currently we are considering the first irq as the PCI interrupt pin,
but a PCI device may have multiple interrupts(e.g. PCIe WAKE# pin).
Only parse the PCI interrupt pin when the irq is unnamed or named as
"pci".
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
---
Changes in v9: None
Changes in v8: None
Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v3: None
Changes in v2: None
drivers/of/of_pci_irq.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/drivers/of/of_pci_irq.c b/drivers/of/of_pci_irq.c
index 3a05568f65df..8b69211f0b88 100644
--- a/drivers/of/of_pci_irq.c
+++ b/drivers/of/of_pci_irq.c
@@ -27,7 +27,18 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq
*/
dn = pci_device_to_OF_node(pdev);
if (dn) {
- rc = of_irq_parse_one(dn, 0, out_irq);
+ struct property *prop;
+ const char *name;
+ int index = 0;
+
+ prop = of_find_property(dn, "interrupt-names", NULL);
+ for (name = of_prop_next_string(prop, NULL); name;
+ name = of_prop_next_string(prop, name), index++) {
+ if (!strcmp(name, "pci"))
+ break;
+ }
+
+ rc = of_irq_parse_one(dn, index, out_irq);
if (!rc)
return rc;
}
--
2.11.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [RFC PATCH v9 3/7] mwifiex: Disable wakeup irq handling for pcie
2017-10-27 7:17 [RFC PATCH v9 0/7] PCI: rockchip: Move PCIe WAKE# handling into pci core Jeffy Chen
2017-10-27 7:17 ` [RFC PATCH v9 1/7] dt-bindings: PCI: Add definition of PCIe WAKE# irq and PCI irq Jeffy Chen
2017-10-27 7:17 ` [RFC PATCH v9 2/7] of/irq: Adjust of_pci_irq parsing for multiple interrupts Jeffy Chen
@ 2017-10-27 7:17 ` Jeffy Chen
2017-10-27 7:17 ` [RFC PATCH v9 4/7] arm64: dts: rockchip: Move PCIe WAKE# irq to pcie driver for Gru Jeffy Chen
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Jeffy Chen @ 2017-10-27 7:17 UTC (permalink / raw)
To: linux-kernel, bhelgaas
Cc: linux-pm, tony, shawn.lin, briannorris, rjw, dianders, Jeffy Chen,
Xinming Hu, Kalle Valo, Ganapathi Bhat, Amitkumar Karwar,
linux-wireless, Nishant Sarmukadam, netdev
We are going to handle the wakeup irq in the pci core.
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
---
Changes in v9: None
Changes in v8: None
Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v3: None
Changes in v2: None
drivers/net/wireless/marvell/mwifiex/main.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c
index ee40b739b289..ba081c16f85c 100644
--- a/drivers/net/wireless/marvell/mwifiex/main.c
+++ b/drivers/net/wireless/marvell/mwifiex/main.c
@@ -1568,6 +1568,10 @@ static void mwifiex_probe_of(struct mwifiex_adapter *adapter)
goto err_exit;
adapter->dt_node = dev->of_node;
+
+ if (adapter->iface_type != MWIFIEX_PCIE)
+ goto err_exit;
+
adapter->irq_wakeup = irq_of_parse_and_map(adapter->dt_node, 0);
if (!adapter->irq_wakeup) {
dev_dbg(dev, "fail to parse irq_wakeup from device tree\n");
--
2.11.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [RFC PATCH v9 4/7] arm64: dts: rockchip: Move PCIe WAKE# irq to pcie driver for Gru
2017-10-27 7:17 [RFC PATCH v9 0/7] PCI: rockchip: Move PCIe WAKE# handling into pci core Jeffy Chen
` (2 preceding siblings ...)
2017-10-27 7:17 ` [RFC PATCH v9 3/7] mwifiex: Disable wakeup irq handling for pcie Jeffy Chen
@ 2017-10-27 7:17 ` Jeffy Chen
2017-10-27 7:17 ` [RFC PATCH v9 5/7] PCI: Make pci_platform_pm_ops's callbacks optional Jeffy Chen
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Jeffy Chen @ 2017-10-27 7:17 UTC (permalink / raw)
To: linux-kernel, bhelgaas
Cc: linux-pm, tony, shawn.lin, briannorris, rjw, dianders, Jeffy Chen,
Matthias Kaehlcke, Heiko Stuebner, devicetree, linux-rockchip,
Rob Herring, linux-arm-kernel, Will Deacon, Mark Rutland,
Caesar Wang, Catalin Marinas
Currently we are handling PCIe WAKE# irq in mrvl wifi driver.
Move it to rockchip pcie driver since we are going to handle it in the
pci core.
Also avoid this irq been considered as the PCI interrupt pin in the
of_irq_parse_pci().
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
---
Changes in v9:
Rewrite the commit message.
Changes in v8:
Rewrite the commit message.
Changes in v7: None
Changes in v6: None
Changes in v5:
Use "wakeup" instead of "wake"
Changes in v3: None
Changes in v2: None
arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi
index 5772c52fbfd3..8e37da69f693 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi
@@ -708,7 +708,15 @@ ap_i2c_audio: &i2c8 {
ep-gpios = <&gpio2 27 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
- pinctrl-0 = <&pcie_clkreqn_cpm>, <&wifi_perst_l>;
+ pinctrl-0 = <&pcie_clkreqn_cpm>, <&wlan_host_wake_l>, <&wifi_perst_l>;
+
+ interrupts-extended = <&gic GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH 0>,
+ <&gic GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH 0>,
+ <&gic GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH 0>,
+ <&gpio0 8 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-names = "sys", "legacy", "client", "wakeup";
+ /delete-property/ interrupts;
+
vpcie3v3-supply = <&pp3300_wifi_bt>;
vpcie1v8-supply = <&wlan_pd_n>; /* HACK: see &wlan_pd_n */
vpcie0v9-supply = <&pp900_pcie>;
@@ -723,11 +731,6 @@ ap_i2c_audio: &i2c8 {
compatible = "pci1b4b,2b42";
reg = <0x83010000 0x0 0x00000000 0x0 0x00100000
0x83010000 0x0 0x00100000 0x0 0x00100000>;
- interrupt-parent = <&gpio0>;
- interrupts = <8 IRQ_TYPE_LEVEL_LOW>;
- pinctrl-names = "default";
- pinctrl-0 = <&wlan_host_wake_l>;
- wakeup-source;
};
};
};
--
2.11.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [RFC PATCH v9 5/7] PCI: Make pci_platform_pm_ops's callbacks optional
2017-10-27 7:17 [RFC PATCH v9 0/7] PCI: rockchip: Move PCIe WAKE# handling into pci core Jeffy Chen
` (3 preceding siblings ...)
2017-10-27 7:17 ` [RFC PATCH v9 4/7] arm64: dts: rockchip: Move PCIe WAKE# irq to pcie driver for Gru Jeffy Chen
@ 2017-10-27 7:17 ` Jeffy Chen
2017-10-27 7:17 ` [RFC PATCH v9 6/7] PCI / PM: Move acpi wakeup code to pci core Jeffy Chen
2017-10-27 7:17 ` [RFC PATCH v9 7/7] PCI / PM: Add support for the PCIe WAKE# signal for OF Jeffy Chen
6 siblings, 0 replies; 8+ messages in thread
From: Jeffy Chen @ 2017-10-27 7:17 UTC (permalink / raw)
To: linux-kernel, bhelgaas
Cc: linux-pm, tony, shawn.lin, briannorris, rjw, dianders, Jeffy Chen,
linux-pci
Allow platforms not to provide some of the pci_platform_pm_ops's
callbacks.
Also change the return value from -ENOSYS to -ENODEV for:
warning: drivers/pci/pci.c,594: ENOSYS means 'invalid syscall nr' and nothing else
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
---
Changes in v9: None
Changes in v8: None
Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v3: None
Changes in v2: None
drivers/pci/pci.c | 38 +++++++++++++++++++++++++-------------
drivers/pci/pci.h | 5 +----
2 files changed, 26 insertions(+), 17 deletions(-)
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index f0d68066c726..e120b00a9017 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -572,46 +572,58 @@ static void pci_restore_bars(struct pci_dev *dev)
static const struct pci_platform_pm_ops *pci_platform_pm;
-int pci_set_platform_pm(const struct pci_platform_pm_ops *ops)
+void pci_set_platform_pm(const struct pci_platform_pm_ops *ops)
{
- if (!ops->is_manageable || !ops->set_state || !ops->get_state ||
- !ops->choose_state || !ops->set_wakeup || !ops->need_resume)
- return -EINVAL;
pci_platform_pm = ops;
- return 0;
}
static inline bool platform_pci_power_manageable(struct pci_dev *dev)
{
- return pci_platform_pm ? pci_platform_pm->is_manageable(dev) : false;
+ if (pci_platform_pm && pci_platform_pm->is_manageable)
+ return pci_platform_pm->is_manageable(dev);
+
+ return false;
}
static inline int platform_pci_set_power_state(struct pci_dev *dev,
pci_power_t t)
{
- return pci_platform_pm ? pci_platform_pm->set_state(dev, t) : -ENOSYS;
+ if (pci_platform_pm && pci_platform_pm->set_state)
+ return pci_platform_pm->set_state(dev, t);
+
+ return -ENODEV;
}
static inline pci_power_t platform_pci_get_power_state(struct pci_dev *dev)
{
- return pci_platform_pm ? pci_platform_pm->get_state(dev) : PCI_UNKNOWN;
+ if (pci_platform_pm && pci_platform_pm->get_state)
+ return pci_platform_pm->get_state(dev);
+
+ return PCI_UNKNOWN;
}
static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev)
{
- return pci_platform_pm ?
- pci_platform_pm->choose_state(dev) : PCI_POWER_ERROR;
+ if (pci_platform_pm && pci_platform_pm->choose_state)
+ return pci_platform_pm->choose_state(dev);
+
+ return PCI_POWER_ERROR;
}
static inline int platform_pci_set_wakeup(struct pci_dev *dev, bool enable)
{
- return pci_platform_pm ?
- pci_platform_pm->set_wakeup(dev, enable) : -ENODEV;
+ if (pci_platform_pm && pci_platform_pm->set_wakeup)
+ return pci_platform_pm->set_wakeup(dev, enable);
+
+ return -ENODEV;
}
static inline bool platform_pci_need_resume(struct pci_dev *dev)
{
- return pci_platform_pm ? pci_platform_pm->need_resume(dev) : false;
+ if (pci_platform_pm && pci_platform_pm->need_resume)
+ return pci_platform_pm->need_resume(dev);
+
+ return false;
}
/**
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index e816a13e6259..048668271014 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -52,9 +52,6 @@ int pci_probe_reset_function(struct pci_dev *dev);
* @need_resume: returns 'true' if the given device (which is currently
* suspended) needs to be resumed to be configured for system
* wakeup.
- *
- * If given platform is generally capable of power managing PCI devices, all of
- * these callbacks are mandatory.
*/
struct pci_platform_pm_ops {
bool (*is_manageable)(struct pci_dev *dev);
@@ -65,7 +62,7 @@ struct pci_platform_pm_ops {
bool (*need_resume)(struct pci_dev *dev);
};
-int pci_set_platform_pm(const struct pci_platform_pm_ops *ops);
+void pci_set_platform_pm(const struct pci_platform_pm_ops *ops);
void pci_update_current_state(struct pci_dev *dev, pci_power_t state);
void pci_power_up(struct pci_dev *dev);
void pci_disable_enabled_device(struct pci_dev *dev);
--
2.11.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [RFC PATCH v9 6/7] PCI / PM: Move acpi wakeup code to pci core
2017-10-27 7:17 [RFC PATCH v9 0/7] PCI: rockchip: Move PCIe WAKE# handling into pci core Jeffy Chen
` (4 preceding siblings ...)
2017-10-27 7:17 ` [RFC PATCH v9 5/7] PCI: Make pci_platform_pm_ops's callbacks optional Jeffy Chen
@ 2017-10-27 7:17 ` Jeffy Chen
2017-10-27 7:17 ` [RFC PATCH v9 7/7] PCI / PM: Add support for the PCIe WAKE# signal for OF Jeffy Chen
6 siblings, 0 replies; 8+ messages in thread
From: Jeffy Chen @ 2017-10-27 7:17 UTC (permalink / raw)
To: linux-kernel, bhelgaas
Cc: linux-pm, tony, shawn.lin, briannorris, rjw, dianders, Jeffy Chen,
linux-pci, linux-acpi, Len Brown
Move acpi wakeup code to pci core as pci_set_wakeup(), so that other
platforms could reuse it.
Also add .setup_dev() / .setup_host_bridge() / .cleanup() platform pm
ops's callbacks to setup and cleanup pci devices and host bridge for
wakeup.
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
---
Changes in v9: None
Changes in v8: None
Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v3: None
Changes in v2: None
drivers/pci/pci-acpi.c | 121 +++++++++++++++++++++++------------------------
drivers/pci/pci-driver.c | 9 ++++
drivers/pci/pci.c | 84 ++++++++++++++++++++++++++++----
drivers/pci/pci.h | 28 +++++++++--
drivers/pci/probe.c | 12 ++++-
drivers/pci/remove.c | 2 +
include/linux/pci.h | 2 +
7 files changed, 180 insertions(+), 78 deletions(-)
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index 4708eb9df71b..ee96e7afe1ac 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -569,31 +569,6 @@ static pci_power_t acpi_pci_get_power_state(struct pci_dev *dev)
return state_conv[state];
}
-static int acpi_pci_propagate_wakeup(struct pci_bus *bus, bool enable)
-{
- while (bus->parent) {
- if (acpi_pm_device_can_wakeup(&bus->self->dev))
- return acpi_pm_set_bridge_wakeup(&bus->self->dev, enable);
-
- bus = bus->parent;
- }
-
- /* We have reached the root bus. */
- if (bus->bridge) {
- if (acpi_pm_device_can_wakeup(bus->bridge))
- return acpi_pm_set_bridge_wakeup(bus->bridge, enable);
- }
- return 0;
-}
-
-static int acpi_pci_wakeup(struct pci_dev *dev, bool enable)
-{
- if (acpi_pm_device_can_wakeup(&dev->dev))
- return acpi_pm_set_device_wakeup(&dev->dev, enable);
-
- return acpi_pci_propagate_wakeup(dev->bus, enable);
-}
-
static bool acpi_pci_need_resume(struct pci_dev *dev)
{
struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
@@ -610,14 +585,29 @@ static bool acpi_pci_need_resume(struct pci_dev *dev)
return !!adev->power.flags.dsw_present;
}
-static const struct pci_platform_pm_ops acpi_pci_platform_pm = {
- .is_manageable = acpi_pci_power_manageable,
- .set_state = acpi_pci_set_power_state,
- .get_state = acpi_pci_get_power_state,
- .choose_state = acpi_pci_choose_state,
- .set_wakeup = acpi_pci_wakeup,
- .need_resume = acpi_pci_need_resume,
-};
+static bool acpi_pci_can_wakeup(void *pmdata)
+{
+ struct device *dev = pmdata;
+
+ if (!dev)
+ return false;
+
+ return acpi_pm_device_can_wakeup(dev);
+}
+
+static int acpi_pci_dev_wakeup(void *pmdata, bool enable)
+{
+ struct device *dev = pmdata;
+
+ return acpi_pm_set_device_wakeup(dev, enable);
+}
+
+static int acpi_pci_bridge_wakeup(void *pmdata, bool enable)
+{
+ struct device *dev = pmdata;
+
+ return acpi_pm_set_bridge_wakeup(dev, enable);
+}
void acpi_pci_add_bus(struct pci_bus *bus)
{
@@ -658,20 +648,6 @@ void acpi_pci_remove_bus(struct pci_bus *bus)
acpi_pci_slot_remove(bus);
}
-/* ACPI bus type */
-static struct acpi_device *acpi_pci_find_companion(struct device *dev)
-{
- struct pci_dev *pci_dev = to_pci_dev(dev);
- bool check_children;
- u64 addr;
-
- check_children = pci_is_bridge(pci_dev);
- /* Please ref to ACPI spec for the syntax of _ADR */
- addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn);
- return acpi_find_child_device(ACPI_COMPANION(dev->parent), addr,
- check_children);
-}
-
/**
* pci_acpi_optimize_delay - optimize PCI D3 and D3cold delay from ACPI
* @pdev: the PCI device whose delay is to be updated
@@ -723,34 +699,55 @@ static void pci_acpi_optimize_delay(struct pci_dev *pdev,
ACPI_FREE(obj);
}
-static void pci_acpi_setup(struct device *dev)
+static void *acpi_pci_setup_dev(struct pci_dev *pci_dev)
{
- struct pci_dev *pci_dev = to_pci_dev(dev);
- struct acpi_device *adev = ACPI_COMPANION(dev);
+ struct acpi_device *adev = ACPI_COMPANION(&pci_dev->dev);
if (!adev)
- return;
+ return NULL;
pci_acpi_optimize_delay(pci_dev, adev->handle);
pci_acpi_add_pm_notifier(adev, pci_dev);
if (!adev->wakeup.flags.valid)
- return;
+ return NULL;
+
+ device_set_wakeup_capable(&pci_dev->dev, true);
+ acpi_pm_set_device_wakeup(&pci_dev->dev, false);
- device_set_wakeup_capable(dev, true);
- acpi_pci_wakeup(pci_dev, false);
+ return &pci_dev->dev;
}
-static void pci_acpi_cleanup(struct device *dev)
+static void *acpi_pci_setup_host_bridge(struct pci_host_bridge *bridge)
{
- struct acpi_device *adev = ACPI_COMPANION(dev);
+ return &bridge->dev;
+}
- if (!adev)
- return;
+static const struct pci_platform_pm_ops acpi_pci_platform_pm = {
+ .is_manageable = acpi_pci_power_manageable,
+ .set_state = acpi_pci_set_power_state,
+ .get_state = acpi_pci_get_power_state,
+ .choose_state = acpi_pci_choose_state,
+ .need_resume = acpi_pci_need_resume,
+ .setup_dev = acpi_pci_setup_dev,
+ .setup_host_bridge = acpi_pci_setup_host_bridge,
+ .can_wakeup = acpi_pci_can_wakeup,
+ .dev_wakeup = acpi_pci_dev_wakeup,
+ .bridge_wakeup = acpi_pci_bridge_wakeup,
+};
+
+/* ACPI bus type */
+static struct acpi_device *acpi_pci_find_companion(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ bool check_children;
+ u64 addr;
- pci_acpi_remove_pm_notifier(adev);
- if (adev->wakeup.flags.valid)
- device_set_wakeup_capable(dev, false);
+ check_children = pci_is_bridge(pci_dev);
+ /* Please ref to ACPI spec for the syntax of _ADR */
+ addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn);
+ return acpi_find_child_device(ACPI_COMPANION(dev->parent), addr,
+ check_children);
}
static bool pci_acpi_bus_match(struct device *dev)
@@ -762,8 +759,6 @@ static struct acpi_bus_type acpi_pci_bus = {
.name = "PCI",
.match = pci_acpi_bus_match,
.find_companion = acpi_pci_find_companion,
- .setup = pci_acpi_setup,
- .cleanup = pci_acpi_cleanup,
};
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 9be563067c0c..abb7caa8a6e5 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -421,10 +421,17 @@ static int pci_device_probe(struct device *dev)
if (error < 0)
return error;
+ pci_dev->pmdata = platform_pci_setup_dev(pci_dev);
+ if (IS_ERR(pci_dev->pmdata)) {
+ pcibios_free_irq(pci_dev);
+ return PTR_ERR(pci_dev->pmdata);
+ }
+
pci_dev_get(pci_dev);
if (pci_device_can_probe(pci_dev)) {
error = __pci_device_probe(drv, pci_dev);
if (error) {
+ platform_pci_cleanup(pci_dev->pmdata);
pcibios_free_irq(pci_dev);
pci_dev_put(pci_dev);
}
@@ -438,6 +445,8 @@ static int pci_device_remove(struct device *dev)
struct pci_dev *pci_dev = to_pci_dev(dev);
struct pci_driver *drv = pci_dev->driver;
+ platform_pci_cleanup(pci_dev->pmdata);
+
if (drv) {
if (drv->remove) {
pm_runtime_get_sync(dev);
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index e120b00a9017..6add5d3209bf 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -585,6 +585,52 @@ static inline bool platform_pci_power_manageable(struct pci_dev *dev)
return false;
}
+void *platform_pci_setup_dev(struct pci_dev *dev)
+{
+ if (pci_platform_pm && pci_platform_pm->setup_dev)
+ return pci_platform_pm->setup_dev(dev);
+
+ return NULL;
+}
+
+void *platform_pci_setup_host_bridge(struct pci_host_bridge *bridge)
+{
+ if (pci_platform_pm && pci_platform_pm->setup_host_bridge)
+ return pci_platform_pm->setup_host_bridge(bridge);
+
+ return NULL;
+}
+
+void platform_pci_cleanup(void *pmdata)
+{
+ if (pci_platform_pm && pci_platform_pm->cleanup)
+ pci_platform_pm->cleanup(pmdata);
+}
+
+static inline bool platform_pci_can_wakeup(void *pmdata)
+{
+ if (pci_platform_pm && pci_platform_pm->can_wakeup)
+ return pci_platform_pm->can_wakeup(pmdata);
+
+ return false;
+}
+
+static inline int platform_pci_dev_wakeup(void *pmdata, bool enable)
+{
+ if (pci_platform_pm && pci_platform_pm->dev_wakeup)
+ return pci_platform_pm->dev_wakeup(pmdata, enable);
+
+ return -ENODEV;
+}
+
+static inline int platform_pci_bridge_wakeup(void *pmdata, bool enable)
+{
+ if (pci_platform_pm && pci_platform_pm->bridge_wakeup)
+ return pci_platform_pm->bridge_wakeup(pmdata, enable);
+
+ return -ENODEV;
+}
+
static inline int platform_pci_set_power_state(struct pci_dev *dev,
pci_power_t t)
{
@@ -610,14 +656,6 @@ static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev)
return PCI_POWER_ERROR;
}
-static inline int platform_pci_set_wakeup(struct pci_dev *dev, bool enable)
-{
- if (pci_platform_pm && pci_platform_pm->set_wakeup)
- return pci_platform_pm->set_wakeup(dev, enable);
-
- return -ENODEV;
-}
-
static inline bool platform_pci_need_resume(struct pci_dev *dev)
{
if (pci_platform_pm && pci_platform_pm->need_resume)
@@ -1903,6 +1941,32 @@ void pci_pme_active(struct pci_dev *dev, bool enable)
}
EXPORT_SYMBOL(pci_pme_active);
+static int pci_set_wakeup(struct pci_dev *dev, bool enable)
+{
+ struct pci_bus *bus = dev->bus;
+ struct pci_host_bridge *bridge;
+
+ /* Handle per device wakeup */
+ if (platform_pci_can_wakeup(dev->pmdata))
+ return platform_pci_dev_wakeup(dev->pmdata, enable);
+
+ /* Find a parent which can handle wakeup */
+ while (bus->parent) {
+ if (platform_pci_can_wakeup(bus->self->pmdata))
+ return platform_pci_bridge_wakeup(bus->self->pmdata,
+ enable);
+
+ bus = bus->parent;
+ }
+
+ /* We have reached the root bus. */
+ bridge = to_pci_host_bridge(bus->bridge);
+ if (platform_pci_can_wakeup(bridge->pmdata))
+ return platform_pci_bridge_wakeup(bridge->pmdata, enable);
+
+ return 0;
+}
+
/**
* pci_enable_wake - enable PCI device as wakeup event source
* @dev: PCI device affected
@@ -1950,13 +2014,13 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable)
pci_pme_active(dev, true);
else
ret = 1;
- error = platform_pci_set_wakeup(dev, true);
+ error = pci_set_wakeup(dev, true);
if (ret)
ret = error;
if (!ret)
dev->wakeup_prepared = true;
} else {
- platform_pci_set_wakeup(dev, false);
+ pci_set_wakeup(dev, false);
pci_pme_active(dev, false);
dev->wakeup_prepared = false;
}
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 048668271014..dcefb9e759d7 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -47,21 +47,43 @@ int pci_probe_reset_function(struct pci_dev *dev);
* platform; to be used during system-wide transitions from a
* sleeping state to the working state and vice versa
*
- * @set_wakeup: enables/disables wakeup capability for the device
- *
* @need_resume: returns 'true' if the given device (which is currently
* suspended) needs to be resumed to be configured for system
* wakeup.
+ *
+ * @setup_dev: setup device's wakeup ability, alloc and return device's private
+ * pm data.
+ *
+ * @setup_host_bridge: setup host bridge's wakeup ability, alloc and return host
+ * bridge's private pm data.
+ *
+ * @cleanup: cleanup the private pm data and deinit wakeup ability.
+ *
+ * @can_wakeup: returns 'true' if given device is capable of waking up the
+ * system from a sleeping state.
+ *
+ * @dev_wakeup: enables/disables wakeup capability for the device.
+ *
+ * @bridge_wakeup: enables/disables wakeup capability for the bridge.
*/
struct pci_platform_pm_ops {
bool (*is_manageable)(struct pci_dev *dev);
int (*set_state)(struct pci_dev *dev, pci_power_t state);
pci_power_t (*get_state)(struct pci_dev *dev);
pci_power_t (*choose_state)(struct pci_dev *dev);
- int (*set_wakeup)(struct pci_dev *dev, bool enable);
bool (*need_resume)(struct pci_dev *dev);
+ void *(*setup_dev)(struct pci_dev *dev);
+ void *(*setup_host_bridge)(struct pci_host_bridge *bridge);
+ void (*cleanup)(void *pmdata);
+ bool (*can_wakeup)(void *pmdata);
+ int (*dev_wakeup)(void *pmdata, bool enable);
+ int (*bridge_wakeup)(void *pmdata, bool enable);
};
+void *platform_pci_setup_dev(struct pci_dev *dev);
+void *platform_pci_setup_host_bridge(struct pci_host_bridge *bridge);
+void platform_pci_cleanup(void *pmdata);
+
void pci_set_platform_pm(const struct pci_platform_pm_ops *ops);
void pci_update_current_state(struct pci_dev *dev, pci_power_t state);
void pci_power_up(struct pci_dev *dev);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index cdc2f83c11c5..b12c552a5522 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -809,7 +809,13 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
err = device_register(&bus->dev);
if (err)
- goto unregister;
+ goto unregister_bridge;
+
+ bridge->pmdata = platform_pci_setup_host_bridge(bridge);
+ if (IS_ERR(bridge->pmdata)) {
+ err = PTR_ERR(bridge->pmdata);
+ goto unregister_bus;
+ }
pcibios_add_bus(bus);
@@ -853,7 +859,9 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
return 0;
-unregister:
+unregister_bus:
+ device_unregister(&bus->dev);
+unregister_bridge:
put_device(&bridge->dev);
device_unregister(&bridge->dev);
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index 73a03d382590..d7a8cf1dc69f 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -153,6 +153,8 @@ void pci_remove_root_bus(struct pci_bus *bus)
if (!pci_is_root_bus(bus))
return;
+ platform_pci_cleanup(host_bridge->pmdata);
+
host_bridge = to_pci_host_bridge(bus->bridge);
list_for_each_entry_safe(child, tmp,
&bus->devices, bus_list)
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 80eaa2dbe3e9..628faa58c790 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -293,6 +293,7 @@ struct pci_dev {
struct pci_bus *subordinate; /* bus this device bridges to */
void *sysdata; /* hook for sys-specific extension */
+ void *pmdata; /* data for platform pm */
struct proc_dir_entry *procent; /* device entry in /proc/bus/pci */
struct pci_slot *slot; /* Physical slot this device is in */
@@ -467,6 +468,7 @@ struct pci_host_bridge {
struct pci_bus *bus; /* root bus */
struct pci_ops *ops;
void *sysdata;
+ void *pmdata; /* data for platform pm */
int busnr;
struct list_head windows; /* resource_entry */
u8 (*swizzle_irq)(struct pci_dev *, u8 *); /* platform IRQ swizzler */
--
2.11.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [RFC PATCH v9 7/7] PCI / PM: Add support for the PCIe WAKE# signal for OF
2017-10-27 7:17 [RFC PATCH v9 0/7] PCI: rockchip: Move PCIe WAKE# handling into pci core Jeffy Chen
` (5 preceding siblings ...)
2017-10-27 7:17 ` [RFC PATCH v9 6/7] PCI / PM: Move acpi wakeup code to pci core Jeffy Chen
@ 2017-10-27 7:17 ` Jeffy Chen
6 siblings, 0 replies; 8+ messages in thread
From: Jeffy Chen @ 2017-10-27 7:17 UTC (permalink / raw)
To: linux-kernel, bhelgaas
Cc: linux-pm, tony, shawn.lin, briannorris, rjw, dianders, Jeffy Chen,
linux-pci
Add pci-of.c to handle the PCIe WAKE# interrupt.
Also use the dedicated wakeirq infrastructure to simplify it.
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
---
Changes in v9:
Fix check error in .cleanup().
Move dedicated wakeirq setup to setup() callback and use
device_set_wakeup_enable() to enable/disable.
Changes in v8:
Add pci-of.c and use platform_pm_ops to handle the PCIe WAKE# signal.
Changes in v7:
Move PCIE_WAKE handling into pci core.
Changes in v6:
Fix device_init_wake error handling, and add some comments.
Changes in v5:
Rebase.
Changes in v3:
Fix error handling.
Changes in v2:
Use dev_pm_set_dedicated_wake_irq.
drivers/pci/Makefile | 2 +-
drivers/pci/pci-of.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 127 insertions(+), 1 deletion(-)
create mode 100644 drivers/pci/pci-of.c
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 66a21acad952..4f76dbdb024c 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -49,7 +49,7 @@ obj-$(CONFIG_PCI_ECAM) += ecam.o
obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
-obj-$(CONFIG_OF) += of.o
+obj-$(CONFIG_OF) += of.o pci-of.o
ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
diff --git a/drivers/pci/pci-of.c b/drivers/pci/pci-of.c
new file mode 100644
index 000000000000..cc8ab979af2c
--- /dev/null
+++ b/drivers/pci/pci-of.c
@@ -0,0 +1,126 @@
+/*
+ * OF PCI PM support
+ *
+ * Copyright (c) 2017 Rockchip, Inc.
+ *
+ * Author: Jeffy Chen <jeffy.chen@rock-chips.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/pm_wakeirq.h>
+#include "pci.h"
+
+struct of_pci_pm_data {
+ struct device *dev;
+ unsigned int wakeup_irq;
+ atomic_t wakeup_cnt;
+};
+
+static void *of_pci_setup(struct device *dev)
+{
+ struct of_pci_pm_data *data;
+ int irq, ret;
+
+ if (!dev->of_node)
+ return NULL;
+
+ data = devm_kzalloc(dev, sizeof(struct of_pci_pm_data), GFP_KERNEL);
+ if (!data)
+ return ERR_PTR(-ENOMEM);
+
+ irq = of_irq_get_byname(dev->of_node, "wakeup");
+ if (irq < 0) {
+ if (irq == -EPROBE_DEFER)
+ return ERR_PTR(irq);
+
+ return NULL;
+ }
+
+ data->wakeup_irq = irq;
+ data->dev = dev;
+
+ device_init_wakeup(dev, true);
+ ret = dev_pm_set_dedicated_wake_irq(dev, irq);
+ if (ret < 0) {
+ device_init_wakeup(dev, false);
+ return NULL;
+ }
+ device_set_wakeup_enable(dev, false);
+
+ dev_info(dev, "Wakeup IRQ %d\n", irq);
+ return data;
+}
+
+static void *of_pci_setup_dev(struct pci_dev *pci_dev)
+{
+ return of_pci_setup(&pci_dev->dev);
+}
+
+static void *of_pci_setup_host_bridge(struct pci_host_bridge *bridge)
+{
+ return of_pci_setup(bridge->dev.parent);
+}
+
+static void of_pci_cleanup(void *pmdata)
+{
+ struct of_pci_pm_data *data = pmdata;
+
+ if (!IS_ERR_OR_NULL(data)) {
+ device_init_wakeup(data->dev, false);
+ dev_pm_clear_wake_irq(data->dev);
+ }
+}
+
+static bool of_pci_can_wakeup(void *pmdata)
+{
+ struct of_pci_pm_data *data = pmdata;
+
+ if (IS_ERR_OR_NULL(data))
+ return false;
+
+ return data->wakeup_irq > 0;
+}
+
+static int of_pci_dev_wakeup(void *pmdata, bool enable)
+{
+ struct of_pci_pm_data *data = pmdata;
+ struct device *dev = data->dev;
+
+ return device_set_wakeup_enable(dev, enable);
+}
+
+static int of_pci_bridge_wakeup(void *pmdata, bool enable)
+{
+ struct of_pci_pm_data *data = pmdata;
+
+ if (enable && atomic_inc_return(&data->wakeup_cnt) != 1)
+ return 0;
+
+ if (!enable && atomic_dec_return(&data->wakeup_cnt) != 0)
+ return 0;
+
+ return of_pci_dev_wakeup(pmdata, enable);
+}
+
+static const struct pci_platform_pm_ops of_pci_platform_pm = {
+ .setup_dev = of_pci_setup_dev,
+ .setup_host_bridge = of_pci_setup_host_bridge,
+ .cleanup = of_pci_cleanup,
+ .can_wakeup = of_pci_can_wakeup,
+ .dev_wakeup = of_pci_dev_wakeup,
+ .bridge_wakeup = of_pci_bridge_wakeup,
+};
+
+static int __init of_pci_init(void)
+{
+ pci_set_platform_pm(&of_pci_platform_pm);
+ return 0;
+}
+arch_initcall(of_pci_init);
--
2.11.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
end of thread, other threads:[~2017-10-27 7:17 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-10-27 7:17 [RFC PATCH v9 0/7] PCI: rockchip: Move PCIe WAKE# handling into pci core Jeffy Chen
2017-10-27 7:17 ` [RFC PATCH v9 1/7] dt-bindings: PCI: Add definition of PCIe WAKE# irq and PCI irq Jeffy Chen
2017-10-27 7:17 ` [RFC PATCH v9 2/7] of/irq: Adjust of_pci_irq parsing for multiple interrupts Jeffy Chen
2017-10-27 7:17 ` [RFC PATCH v9 3/7] mwifiex: Disable wakeup irq handling for pcie Jeffy Chen
2017-10-27 7:17 ` [RFC PATCH v9 4/7] arm64: dts: rockchip: Move PCIe WAKE# irq to pcie driver for Gru Jeffy Chen
2017-10-27 7:17 ` [RFC PATCH v9 5/7] PCI: Make pci_platform_pm_ops's callbacks optional Jeffy Chen
2017-10-27 7:17 ` [RFC PATCH v9 6/7] PCI / PM: Move acpi wakeup code to pci core Jeffy Chen
2017-10-27 7:17 ` [RFC PATCH v9 7/7] PCI / PM: Add support for the PCIe WAKE# signal for OF Jeffy Chen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).