PCI: mvebu: subsystem ids, AER and INTx
Subsystems (4)
Pali Rohár
maintainerPATCH 1/11 PCI: pci-bridge-emul: Re-arrange register tests
In reply to Pali RohárFrom: Russell King <[email protected]>
Re-arrange the tests for which sets of registers are being accessed so that it is easier to add further regions later. No functional change.
--- drivers/pci/pci-bridge-emul.c | 61 ++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 30 deletions(-)
drivers/pci/pci-bridge-emul.c +31 -30
@@ -418,25 +418,25 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,__le32 *cfgspace;const struct pci_bridge_reg_behavior *behavior; -if (bridge->has_pcie && reg >=PCI_CAP_PCIE_END ) { - *value =0 ; -return PCIBIOS_SUCCESSFUL ; - } - -if (!bridge->has_pcie && reg >=PCI_BRIDGE_CONF_END ) { +if (reg <PCI_BRIDGE_CONF_END ) { +/* Emulated PCI space */ + read_op = bridge->ops->read_base; + cfgspace = (__le32 *) &bridge->conf; + behavior = bridge->pci_regs_behavior; + }else if (!bridge->has_pcie) { +/* PCIe space is not implemented, and no PCI capabilities */ *value =0 ;return PCIBIOS_SUCCESSFUL ; - } - -if (bridge->has_pcie && reg >=PCI_CAP_PCIE_START ) { + }else if (reg <PCI_CAP_PCIE_END ) { +/* Our emulated PCIe capability */ reg -=PCI_CAP_PCIE_START ; read_op = bridge->ops->read_pcie; cfgspace = (__le32 *) &bridge->pcie_conf; behavior = bridge->pcie_cap_regs_behavior; }else { - read_op = bridge->ops->read_base; - cfgspace = (__le32 *) &bridge->conf; - behavior = bridge->pci_regs_behavior; +/* Beyond our PCIe space */ + *value =0 ; +return PCIBIOS_SUCCESSFUL ; }if (read_op)
@@ -480,11 +480,27 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,__le32 *cfgspace;const struct pci_bridge_reg_behavior *behavior; -if (bridge->has_pcie && reg >=PCI_CAP_PCIE_END ) -return PCIBIOS_SUCCESSFUL ; + ret =pci_bridge_emul_conf_read (bridge, reg,4 , &old); +if (ret !=PCIBIOS_SUCCESSFUL ) +return ret; -if (!bridge->has_pcie && reg >=PCI_BRIDGE_CONF_END ) +if (reg <PCI_BRIDGE_CONF_END ) { +/* Emulated PCI space */ + write_op = bridge->ops->write_base; + cfgspace = (__le32 *) &bridge->conf; + behavior = bridge->pci_regs_behavior; + }else if (!bridge->has_pcie) { +/* PCIe space is not implemented, and no PCI capabilities */ return PCIBIOS_SUCCESSFUL ; + }else if (reg <PCI_CAP_PCIE_END ) { +/* Our emulated PCIe capability */ + reg -=PCI_CAP_PCIE_START ; + write_op = bridge->ops->write_pcie; + cfgspace = (__le32 *) &bridge->pcie_conf; + behavior = bridge->pcie_cap_regs_behavior; + }else { +return PCIBIOS_SUCCESSFUL ; + } shift = (where &0x3 ) *8 ;
@@ -497,21 +513,6 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,else return PCIBIOS_BAD_REGISTER_NUMBER ; - ret =pci_bridge_emul_conf_read (bridge, reg,4 , &old); -if (ret !=PCIBIOS_SUCCESSFUL ) -return ret; - -if (bridge->has_pcie && reg >=PCI_CAP_PCIE_START ) { - reg -=PCI_CAP_PCIE_START ; - write_op = bridge->ops->write_pcie; - cfgspace = (__le32 *) &bridge->pcie_conf; - behavior = bridge->pcie_cap_regs_behavior; - }else { - write_op = bridge->ops->write_base; - cfgspace = (__le32 *) &bridge->conf; - behavior = bridge->pci_regs_behavior; - } -/* Keep all bits, except the RW bits */ new = old & (~mask | ~behavior[reg /4 ].rw);
Signature · 1 line
2.20.1
Pali Rohár
maintainerPATCH 4/11 dt-bindings: PCI: mvebu: Add num-lanes property
In reply to Pali RohárController driver needs to correctly configure PCIe link if it contains 1 or 4 SerDes PCIe lanes. Therefore add a new 'num-lanes' DT property for mvebu PCIe controller. Property 'num-lanes' seems to be de-facto standard way how number of lanes is specified in other PCIe controllers.
--- Documentation/devicetree/bindings/pci/mvebu-pci.txt | 11 +++++++++++ 1 file changed, 11 insertions(+)
Documentation/devicetree/bindings/pci/mvebu-pci.txt +11 -0
@@ -77,6 +77,7 @@ and the following optional properties: - marvell,pcie-lane: the physical PCIe lane number, for ports having multiple lanes. If this property is not found, we assume that the value is 0. +- num-lanes: number of SerDes PCIe lanes for this link (1 or 4) - reset-gpios: optional GPIO to PERST# - reset-delay-us: delay in us to wait after reset de-assertion, if not specified will default to 100ms, as required by the PCIe specification.
@@ -141,6 +142,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 58>; marvell,pcie-port = <0>; marvell,pcie-lane = <0>; + num-lanes = <1>; /* low-active PERST# reset on GPIO 25 */ reset-gpios = <&gpio0 25 1>; /* wait 20ms for device settle after reset deassertion */
@@ -161,6 +163,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 59>; marvell,pcie-port = <0>; marvell,pcie-lane = <1>; + num-lanes = <1>; clocks = <&gateclk 6>; };
@@ -177,6 +180,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 60>; marvell,pcie-port = <0>; marvell,pcie-lane = <2>; + num-lanes = <1>; clocks = <&gateclk 7>; };
@@ -193,6 +197,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 61>; marvell,pcie-port = <0>; marvell,pcie-lane = <3>; + num-lanes = <1>; clocks = <&gateclk 8>; };
@@ -209,6 +214,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 62>; marvell,pcie-port = <1>; marvell,pcie-lane = <0>; + num-lanes = <1>; clocks = <&gateclk 9>; };
@@ -225,6 +231,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 63>; marvell,pcie-port = <1>; marvell,pcie-lane = <1>; + num-lanes = <1>; clocks = <&gateclk 10>; };
@@ -241,6 +248,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 64>; marvell,pcie-port = <1>; marvell,pcie-lane = <2>; + num-lanes = <1>; clocks = <&gateclk 11>; };
@@ -257,6 +265,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 65>; marvell,pcie-port = <1>; marvell,pcie-lane = <3>; + num-lanes = <1>; clocks = <&gateclk 12>; };
@@ -273,6 +282,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 99>; marvell,pcie-port = <2>; marvell,pcie-lane = <0>; + num-lanes = <1>; clocks = <&gateclk 26>; };
@@ -289,6 +299,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 103>; marvell,pcie-port = <3>; marvell,pcie-lane = <0>; + num-lanes = <1>; clocks = <&gateclk 27>; }; };
Signature · 1 line
2.20.1
Pali Rohár
maintainercover PCI: mvebu: subsystem ids, AER and INTx
This patch series extends pci-bridge-emul.c driver to emulate PCI Subsystem Vendor ID capability and PCIe extended capabilities. And then implement in pci-mvebu.c driver support for PCI Subsystem Vendor IDs, PCIe AER registers, support for legacy INTx interrupts, configuration for X1/X4 mode and usage of new PCI child_ops API.
This patch series depends on other pci-mvebu and pci-bridge-emul patches from:
https://lore.kernel.org/linux-pci/[email protected]/
Pali Rohár (9):
PCI: pci-bridge-emul: Add support for PCI Bridge Subsystem Vendor ID
capability
dt-bindings: PCI: mvebu: Add num-lanes property
PCI: mvebu: Correctly configure x1/x4 mode
PCI: mvebu: Add support for PCI Bridge Subsystem Vendor ID on emulated
bridge
PCI: mvebu: Add support for Advanced Error Reporting registers on
emulated bridge
PCI: mvebu: Use child_ops API
dt-bindings: PCI: mvebu: Update information about intx interrupts
PCI: mvebu: Implement support for legacy INTx interrupts
ARM: dts: armada-385.dtsi: Add definitions for PCIe legacy INTx
interrupts
Russell King (2):
PCI: pci-bridge-emul: Re-arrange register tests
PCI: pci-bridge-emul: Add support for PCIe extended capabilities
.../devicetree/bindings/pci/mvebu-pci.txt | 16 + arch/arm/boot/dts/armada-385.dtsi | 52 ++- drivers/pci/controller/pci-mvebu.c | 352 +++++++++++++++--- drivers/pci/pci-bridge-emul.c | 167 ++++++--- drivers/pci/pci-bridge-emul.h | 17 + 5 files changed, 494 insertions(+), 110 deletions(-)
Signature · 1 line
2.20.1
Pali Rohár
maintainerPATCH 2/11 PCI: pci-bridge-emul: Add support for PCIe extended capabilities
From: Russell King <[email protected]>
Add support for PCIe extended capabilities, which we just redirect to the emulating driver.
--- drivers/pci/pci-bridge-emul.c | 77 +++++++++++++++++++++++------------ drivers/pci/pci-bridge-emul.h | 15 +++++++ 2 files changed, 67 insertions(+), 25 deletions(-)
drivers/pci/pci-bridge-emul.c +52 -25
@@ -433,10 +433,16 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, read_op = bridge->ops->read_pcie; cfgspace = (__le32 *) &bridge->pcie_conf; behavior = bridge->pcie_cap_regs_behavior; - }else { -/* Beyond our PCIe space */ + }else if (reg <PCI_CFG_SPACE_SIZE ) { +/* Rest of PCI space not implemented */ *value =0 ;return PCIBIOS_SUCCESSFUL ; + }else { +/* PCIe extended capability space */ + reg -=PCI_CFG_SPACE_SIZE ; + read_op = bridge->ops->read_ext; + cfgspace =NULL ; + behavior =NULL ; }if (read_op)
@@ -444,15 +450,20 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,else ret =PCI_BRIDGE_EMUL_NOT_HANDLED ; -if (ret ==PCI_BRIDGE_EMUL_NOT_HANDLED ) - *value =le32_to_cpu (cfgspace[reg /4 ]); +if (ret ==PCI_BRIDGE_EMUL_NOT_HANDLED ) { +if (cfgspace) + *value =le32_to_cpu (cfgspace[reg /4 ]); +else + *value =0 ; + }/* * Make sure we never return any reserved bit with a value * different from 0. */ - *value &= behavior[reg /4 ].ro | behavior[reg /4 ].rw | - behavior[reg /4 ].w1c; +if (behavior) + *value &= behavior[reg /4 ].ro | behavior[reg /4 ].rw | + behavior[reg /4 ].w1c;if (size ==1 ) *value = (*value >> (8 * (where &3 ))) &0xff ;
@@ -498,8 +509,15 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, write_op = bridge->ops->write_pcie; cfgspace = (__le32 *) &bridge->pcie_conf; behavior = bridge->pcie_cap_regs_behavior; - }else { + }else if (reg <PCI_CFG_SPACE_SIZE ) { +/* Rest of PCI space not implemented */ return PCIBIOS_SUCCESSFUL ; + }else { +/* PCIe extended capability space */ + reg -=PCI_CFG_SPACE_SIZE ; + write_op = bridge->ops->write_ext; + cfgspace =NULL ; + behavior =NULL ; } shift = (where &0x3 ) *8 ;
@@ -513,29 +531,38 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,else return PCIBIOS_BAD_REGISTER_NUMBER ; -/* Keep all bits, except the RW bits */ - new = old & (~mask | ~behavior[reg /4 ].rw); +if (behavior) { +/* Keep all bits, except the RW bits */ + new = old & (~mask | ~behavior[reg /4 ].rw); -/* Update the value of the RW bits */ - new |= (value << shift) & (behavior[reg /4 ].rw & mask); +/* Update the value of the RW bits */ + new |= (value << shift) & (behavior[reg /4 ].rw & mask); -/* Clear the W1C bits */ - new &= ~((value << shift) & (behavior[reg /4 ].w1c & mask)); +/* Clear the W1C bits */ + new &= ~((value << shift) & (behavior[reg /4 ].w1c & mask)); + }else { + new = old & ~mask; + new |= (value << shift) & mask; + } -/* Save the new value with the cleared W1C bits into the cfgspace */ - cfgspace[reg /4 ] =cpu_to_le32 (new); +if (cfgspace) { +/* Save the new value with the cleared W1C bits into the cfgspace */ + cfgspace[reg /4 ] =cpu_to_le32 (new); + } -/* -* Clear the W1C bits not specified by the write mask, so that the -* write_op() does not clear them. -*/ - new &= ~(behavior[reg /4 ].w1c & ~mask); +if (behavior) { +/* +* Clear the W1C bits not specified by the write mask, so that the +* write_op() does not clear them. +*/ + new &= ~(behavior[reg /4 ].w1c & ~mask); -/* -* Set the W1C bits specified by the write mask, so that write_op() -* knows about that they are to be cleared. -*/ - new |= (value << shift) & (behavior[reg /4 ].w1c & mask); +/* +* Set the W1C bits specified by the write mask, so that write_op() +* knows about that they are to be cleared. +*/ + new |= (value << shift) & (behavior[reg /4 ].w1c & mask); + }if (write_op)write_op (bridge, reg, old, new, mask);
drivers/pci/pci-bridge-emul.h +15 -0
@@ -90,6 +90,14 @@ struct pci_bridge_emul_ops {*/ pci_bridge_emul_read_status_t (*read_pcie)(struct pci_bridge_emul *bridge,int reg,u32 *value); + +/* +* Same as ->read_base(), except it is for reading from the +* PCIe extended capability configuration space. +*/ +pci_bridge_emul_read_status_t (*read_ext)(struct pci_bridge_emul *bridge, +int reg,u32 *value); +/* * Called when writing to the regular PCI bridge configuration * space. old is the current value, new is the new value being
@@ -105,6 +113,13 @@ struct pci_bridge_emul_ops {*/ void (*write_pcie)(struct pci_bridge_emul *bridge,int reg,u32 old,u32 new,u32 mask); + +/* +* Same as ->write_base(), except it is for writing from the +* PCIe extended capability configuration space. +*/ +void (*write_ext)(struct pci_bridge_emul *bridge,int reg, +u32 old,u32 new,u32 mask); };struct pci_bridge_reg_behavior ;
Signature · 1 line
2.20.1
Pali Rohár
maintainerPATCH 3/11 PCI: pci-bridge-emul: Add support for PCI Bridge Subsystem Vendor ID capability
In reply to Pali RohárThis is read-only capability in PCI config space. Put it between base PCI capability and base PCI Express capability.
Driver just have to specify subsystem_vendor_id and subsystem_id fields in emulated bridge structure and pci-bridge-emul takes care of correctly compose PCI Bridge Subsystem Vendor ID capability.
--- drivers/pci/pci-bridge-emul.c | 69 +++++++++++++++++++++++++---------- drivers/pci/pci-bridge-emul.h | 2 + 2 files changed, 51 insertions(+), 20 deletions(-)
drivers/pci/pci-bridge-emul.c +49 -20
@@ -21,8 +21,11 @@#include "pci-bridge-emul.h" #define PCI_BRIDGE_CONF_END PCI_STD_HEADER_SIZEOF +#define PCI_CAP_SSID_SIZEOF (PCI_SSVID_DEVICE_ID + 2) +#define PCI_CAP_SSID_START PCI_BRIDGE_CONF_END +#define PCI_CAP_SSID_END (PCI_CAP_SSID_START + PCI_CAP_SSID_SIZEOF)#define PCI_CAP_PCIE_SIZEOF (PCI_EXP_SLTSTA2 + 2) -#define PCI_CAP_PCIE_START PCI_BRIDGE_CONF_END +#define PCI_CAP_PCIE_START PCI_CAP_SSID_END#define PCI_CAP_PCIE_END (PCI_CAP_PCIE_START + PCI_CAP_PCIE_SIZEOF)/**
@@ -315,6 +318,25 @@ struct pci_bridge_reg_behavior pcie_cap_regs_behavior[PCI_CAP_PCIE_SIZEOF / 4] = }, }; +static pci_bridge_emul_read_status_t +pci_bridge_emul_read_ssid (struct pci_bridge_emul *bridge,int reg,u32 *value) +{ +switch (reg) { +case PCI_CAP_LIST_ID : + *value =PCI_CAP_ID_SSVID | + (bridge->has_pcie ? (PCI_CAP_PCIE_START <<8 ) :0 ); +return PCI_BRIDGE_EMUL_HANDLED ; + +case PCI_SSVID_VENDOR_ID : + *value = bridge->subsystem_vendor_id | + (bridge->subsystem_id <<16 ); +return PCI_BRIDGE_EMUL_HANDLED ; + +default : +return PCI_BRIDGE_EMUL_NOT_HANDLED ; + } +} +/* * Initialize a pci_bridge_emul structure to represent a fake PCI * bridge configuration space. The caller needs to have initialized
@@ -337,9 +359,17 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge,if (!bridge->pci_regs_behavior)return -ENOMEM ; -if (bridge->has_pcie) { +if (bridge->subsystem_vendor_id) + bridge->conf.capabilities_pointer =PCI_CAP_SSID_START ; +else if (bridge->has_pcie) bridge->conf.capabilities_pointer =PCI_CAP_PCIE_START ; +else + bridge->conf.capabilities_pointer =0 ; + +if (bridge->conf.capabilities_pointer) bridge->conf.status |=cpu_to_le16 (PCI_STATUS_CAP_LIST ); + +if (bridge->has_pcie) { bridge->pcie_conf.cap_id =PCI_CAP_ID_EXP ; bridge->pcie_conf.cap |=cpu_to_le16 (PCI_EXP_TYPE_ROOT_PORT <<4 ); bridge->pcie_cap_regs_behavior =
@@ -423,26 +453,28 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, read_op = bridge->ops->read_base; cfgspace = (__le32 *) &bridge->conf; behavior = bridge->pci_regs_behavior; - }else if (!bridge->has_pcie) { -/* PCIe space is not implemented, and no PCI capabilities */ - *value =0 ; -return PCIBIOS_SUCCESSFUL ; - }else if (reg <PCI_CAP_PCIE_END ) { + }else if (reg >=PCI_CAP_SSID_START && reg <PCI_CAP_SSID_END && bridge->subsystem_vendor_id) { +/* Emulated PCI Bridge Subsystem Vendor ID capability */ + reg -=PCI_CAP_SSID_START ; + read_op = pci_bridge_emul_read_ssid; + cfgspace =NULL ; + behavior =NULL ; + }else if (reg >=PCI_CAP_PCIE_START && reg <PCI_CAP_PCIE_END && bridge->has_pcie) {/* Our emulated PCIe capability */ reg -=PCI_CAP_PCIE_START ; read_op = bridge->ops->read_pcie; cfgspace = (__le32 *) &bridge->pcie_conf; behavior = bridge->pcie_cap_regs_behavior; - }else if (reg <PCI_CFG_SPACE_SIZE ) { -/* Rest of PCI space not implemented */ - *value =0 ; -return PCIBIOS_SUCCESSFUL ; - }else { + }else if (reg >=PCI_CFG_SPACE_SIZE && bridge->has_pcie) {/* PCIe extended capability space */ reg -=PCI_CFG_SPACE_SIZE ; read_op = bridge->ops->read_ext; cfgspace =NULL ; behavior =NULL ; + }else { +/* Not implemented */ + *value =0 ; +return PCIBIOS_SUCCESSFUL ; }if (read_op)
@@ -500,24 +532,21 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, write_op = bridge->ops->write_base; cfgspace = (__le32 *) &bridge->conf; behavior = bridge->pci_regs_behavior; - }else if (!bridge->has_pcie) { -/* PCIe space is not implemented, and no PCI capabilities */ -return PCIBIOS_SUCCESSFUL ; - }else if (reg <PCI_CAP_PCIE_END ) { + }else if (reg >=PCI_CAP_PCIE_START && reg <PCI_CAP_PCIE_END && bridge->has_pcie) {/* Our emulated PCIe capability */ reg -=PCI_CAP_PCIE_START ; write_op = bridge->ops->write_pcie; cfgspace = (__le32 *) &bridge->pcie_conf; behavior = bridge->pcie_cap_regs_behavior; - }else if (reg <PCI_CFG_SPACE_SIZE ) { -/* Rest of PCI space not implemented */ -return PCIBIOS_SUCCESSFUL ; - }else { + }else if (reg >=PCI_CFG_SPACE_SIZE && bridge->has_pcie) {/* PCIe extended capability space */ reg -=PCI_CFG_SPACE_SIZE ; write_op = bridge->ops->write_ext; cfgspace =NULL ; behavior =NULL ; + }else { +/* Not implemented */ +return PCIBIOS_SUCCESSFUL ; } shift = (where &0x3 ) *8 ;
drivers/pci/pci-bridge-emul.h +2 -0
@@ -132,6 +132,8 @@ struct pci_bridge_emul {struct pci_bridge_reg_behavior *pcie_cap_regs_behavior;void *data;bool has_pcie; +u16 subsystem_vendor_id; +u16 subsystem_id; };enum {
Signature · 1 line
2.20.1
Pali Rohár
maintainerPATCH 7/11 PCI: mvebu: Add support for Advanced Error Reporting registers on emulated bridge
In reply to Pali RohárAER registers start at mvebu offset 0x0100. Registers PCI_ERR_ROOT_COMMAND, PCI_ERR_ROOT_STATUS and PCI_ERR_ROOT_ERR_SRC are not supported on pre-XP hardware and returns zeros.
Note that AER interrupt is not supported yet as mvebu emulated bridge does not implement interrupts support at all yet.
Also remove custom macro PCIE_HEADER_LOG_4_OFF as it is unused and correctly this register should be referenced via standard macros with offset, e.g. as: PCIE_CAP_PCIERR_OFF + PCI_ERR_HEADER_LOG + 4.
--- drivers/pci/controller/pci-mvebu.c | 67 +++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-)
drivers/pci/controller/pci-mvebu.c +66 -1
@@ -34,7 +34,7 @@#define PCIE_BAR_HI_OFF (n) (0x0014 + ((n) << 3))#define PCIE_SSDEV_ID_OFF 0x002c#define PCIE_CAP_PCIEXP 0x0060 -#define PCIE_HEADER_LOG_4_OFF 0x0128 +#define PCIE_CAP_PCIERR_OFF 0x0100#define PCIE_BAR_CTRL_OFF (n) (0x1804 + (((n) - 1) * 4))#define PCIE_WIN04_CTRL_OFF (n) (0x1820 + ((n) << 4))#define PCIE_WIN04_BASE_OFF (n) (0x1824 + ((n) << 4))
@@ -603,6 +603,37 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,return PCI_BRIDGE_EMUL_HANDLED ; } +static pci_bridge_emul_read_status_t +mvebu_pci_bridge_emul_ext_conf_read (struct pci_bridge_emul *bridge, +int reg,u32 *value) +{ +struct mvebu_pcie_port *port = bridge->data; + +switch (reg) { +case 0 : +case PCI_ERR_UNCOR_STATUS : +case PCI_ERR_UNCOR_MASK : +case PCI_ERR_UNCOR_SEVER : +case PCI_ERR_COR_STATUS : +case PCI_ERR_COR_MASK : +case PCI_ERR_CAP : +case PCI_ERR_HEADER_LOG +0 : +case PCI_ERR_HEADER_LOG +4 : +case PCI_ERR_HEADER_LOG +8 : +case PCI_ERR_HEADER_LOG +12 : +case PCI_ERR_ROOT_COMMAND : +case PCI_ERR_ROOT_STATUS : +case PCI_ERR_ROOT_ERR_SRC : + *value =mvebu_readl (port,PCIE_CAP_PCIERR_OFF + reg); +break ; + +default : +return PCI_BRIDGE_EMUL_NOT_HANDLED ; + } + +return PCI_BRIDGE_EMUL_HANDLED ; +} +static void mvebu_pci_bridge_emul_base_conf_write (struct pci_bridge_emul *bridge,int reg,u32 old,u32 new,u32 mask)
@@ -715,11 +746,45 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, } } +static void +mvebu_pci_bridge_emul_ext_conf_write (struct pci_bridge_emul *bridge, +int reg,u32 old,u32 new,u32 mask) +{ +struct mvebu_pcie_port *port = bridge->data; + +switch (reg) { +/* These are W1C registers, so clear other bits */ +case PCI_ERR_UNCOR_STATUS : +case PCI_ERR_COR_STATUS : +case PCI_ERR_ROOT_STATUS : + new &= mask; + fallthrough; + +case PCI_ERR_UNCOR_MASK : +case PCI_ERR_UNCOR_SEVER : +case PCI_ERR_COR_MASK : +case PCI_ERR_CAP : +case PCI_ERR_HEADER_LOG +0 : +case PCI_ERR_HEADER_LOG +4 : +case PCI_ERR_HEADER_LOG +8 : +case PCI_ERR_HEADER_LOG +12 : +case PCI_ERR_ROOT_COMMAND : +case PCI_ERR_ROOT_ERR_SRC : +mvebu_writel (port, new,PCIE_CAP_PCIERR_OFF + reg); +break ; + +default : +break ; + } +} +static const struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = { .read_base = mvebu_pci_bridge_emul_base_conf_read, .write_base = mvebu_pci_bridge_emul_base_conf_write, .read_pcie = mvebu_pci_bridge_emul_pcie_conf_read, .write_pcie = mvebu_pci_bridge_emul_pcie_conf_write, + .read_ext = mvebu_pci_bridge_emul_ext_conf_read, + .write_ext = mvebu_pci_bridge_emul_ext_conf_write, };/*
Signature · 1 line
2.20.1
Pali Rohár
maintainerPATCH 6/11 PCI: mvebu: Add support for PCI Bridge Subsystem Vendor ID on emulated bridge
In reply to Pali RohárRegister with Subsystem Device/Vendor ID is at offset 0x2c. Export is via emulated bridge.
After this change Subsystem ID is visible in lspci output at line:
Capabilities: [40] Subsystem
--- drivers/pci/controller/pci-mvebu.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/pci/controller/pci-mvebu.c +4 -0
@@ -32,6 +32,7 @@#define PCIE_DEV_REV_OFF 0x0008#define PCIE_BAR_LO_OFF (n) (0x0010 + ((n) << 3))#define PCIE_BAR_HI_OFF (n) (0x0014 + ((n) << 3)) +#define PCIE_SSDEV_ID_OFF 0x002c#define PCIE_CAP_PCIEXP 0x0060#define PCIE_HEADER_LOG_4_OFF 0x0128#define PCIE_BAR_CTRL_OFF (n) (0x1804 + (((n) - 1) * 4))
@@ -731,6 +732,7 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)struct pci_bridge_emul *bridge = &port->bridge;u32 dev_id =mvebu_readl (port,PCIE_DEV_ID_OFF );u32 dev_rev =mvebu_readl (port,PCIE_DEV_REV_OFF ); +u32 ssdev_id =mvebu_readl (port,PCIE_SSDEV_ID_OFF );u32 pcie_cap =mvebu_readl (port,PCIE_CAP_PCIEXP );u8 pcie_cap_ver = ((pcie_cap >>16 ) &PCI_EXP_FLAGS_VERS );
@@ -752,6 +754,8 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)*/ bridge->pcie_conf.cap =cpu_to_le16 (pcie_cap_ver); + bridge->subsystem_vendor_id = ssdev_id &0xffff ; + bridge->subsystem_id = ssdev_id >>16 ; bridge->has_pcie = true; bridge->data = port; bridge->ops = &mvebu_pci_bridge_emul_ops;
Signature · 1 line
2.20.1
Pali Rohár
maintainerPATCH 9/11 dt-bindings: PCI: mvebu: Update information about intx interrupts
In reply to Pali Rohár--- Documentation/devicetree/bindings/pci/mvebu-pci.txt | 5 +++++ 1 file changed, 5 insertions(+)
Documentation/devicetree/bindings/pci/mvebu-pci.txt +5 -0
@@ -81,6 +81,11 @@ and the following optional properties: - reset-gpios: optional GPIO to PERST# - reset-delay-us: delay in us to wait after reset de-assertion, if not specified will default to 100ms, as required by the PCIe specification. +- interrupt-names: list of interrupt names, supported are: + - "intx" - interrupt line triggered by one of the legacy interrupt +- interrupts or interrupts-extended: List of the interrupt sources which + corresponding to the "interrupt-names". If non-empty then also additional + 'interrupt-controller' subnode must be defined. Example:
Signature · 1 line
2.20.1
Pali Rohár
maintainerPATCH 5/11 PCI: mvebu: Correctly configure x1/x4 mode
In reply to Pali RohárIf x1/x4 mode is not set correctly then link with endpoint card is not established.
Use DTS property 'num-lanes' to deteriminate x1/x4 mode.
--- drivers/pci/controller/pci-mvebu.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
drivers/pci/controller/pci-mvebu.c +18 -1
@@ -93,6 +93,7 @@ struct mvebu_pcie_port {void __iomem *base;u32 port;u32 lane; +bool is_x4;int devfn;unsigned int mem_target;unsigned int mem_attr;
@@ -233,13 +234,25 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, mask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF ); ctrl |=PCIE_CTRL_RC_MODE ;mvebu_writel (port, ctrl,PCIE_CTRL_OFF ); +/* +* Set Maximum Link Width to X1 or X4 in Root Port's PCIe Link +* Capability register. This register is defined by PCIe specification +* as read-only but this mvebu controller has it as read-write and must +* be set to number of SerDes PCIe lanes (1 or 4). If this register is +* not set correctly then link with endpoint card is not established. +*/ + lnkcap =mvebu_readl (port,PCIE_CAP_PCIEXP +PCI_EXP_LNKCAP ); + lnkcap &= ~PCI_EXP_LNKCAP_MLW ; + lnkcap |= (port->is_x4 ?4 :1 ) <<4 ; +mvebu_writel (port, lnkcap,PCIE_CAP_PCIEXP +PCI_EXP_LNKCAP ); +/* Disable Root Bridge I/O space, memory space and bus mastering. */ cmd =mvebu_readl (port,PCIE_CMD_OFF ); cmd &= ~(PCI_COMMAND_IO |PCI_COMMAND_MEMORY |PCI_COMMAND_MASTER );
@@ -986,6 +999,7 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,struct device *dev = &pcie->pdev->dev;enum of_gpio_flags flags;int reset_gpio, ret; +u32 num_lanes; port->pcie = pcie;
@@ -998,6 +1012,9 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,if (of_property_read_u32 (child,"marvell,pcie-lane" , &port->lane)) port->lane =0 ; +if (!of_property_read_u32 (child,"num-lanes" , &num_lanes) && num_lanes ==4 ) + port->is_x4 = true; + port->name =devm_kasprintf (dev,GFP_KERNEL ,"pcie%d.%d" , port->port, port->lane);if (!port->name) {
Signature · 1 line
2.20.1
Pali Rohár
maintainerPATCH 8/11 PCI: mvebu: Use child_ops API
In reply to Pali RohárSplit struct pci_ops between ops and child_ops. Member ops is used for accessing PCIe Root Ports via pci-bridge-emul.c driver and child_ops for accessing real PCIe cards.
There is no need to mix these two struct pci_ops into one as PCI core code already provides separate callbacks via bridge->ops and bridge->child_ops.
--- drivers/pci/controller/pci-mvebu.c | 82 ++++++++++++++++-------------- 1 file changed, 44 insertions(+), 38 deletions(-)
drivers/pci/controller/pci-mvebu.c +44 -38
@@ -294,11 +294,29 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)mvebu_writel (port, mask,PCIE_MASK_OFF ); } -static int mvebu_pcie_hw_rd_conf (struct mvebu_pcie_port *port, -struct pci_bus *bus, -u32 devfn,int where,int size,u32 *val) +static struct mvebu_pcie_port *mvebu_pcie_find_port (struct mvebu_pcie *pcie, +struct pci_bus *bus, +int devfn); + +static int mvebu_pcie_child_rd_conf (struct pci_bus *bus,u32 devfn,int where, +int size,u32 *val) { -void __iomem *conf_data = port->base +PCIE_CONF_DATA_OFF ; +struct mvebu_pcie *pcie = bus->sysdata; +struct mvebu_pcie_port *port; +void __iomem *conf_data; + + port =mvebu_pcie_find_port (pcie, bus, devfn); +if (!port) { + *val =0xffffffff ; +return PCIBIOS_DEVICE_NOT_FOUND ; + } + +if (!mvebu_pcie_link_up (port)) { + *val =0xffffffff ; +return PCIBIOS_DEVICE_NOT_FOUND ; + } + + conf_data = port->base +PCIE_CONF_DATA_OFF ;mvebu_writel (port,PCIE_CONF_ADDR (bus->number, devfn, where),PCIE_CONF_ADDR_OFF );
@@ -321,11 +339,21 @@ static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port,return PCIBIOS_SUCCESSFUL ; } -static int mvebu_pcie_hw_wr_conf (struct mvebu_pcie_port *port, -struct pci_bus *bus, -u32 devfn,int where,int size,u32 val) +static int mvebu_pcie_child_wr_conf (struct pci_bus *bus,u32 devfn, +int where,int size,u32 val) { -void __iomem *conf_data = port->base +PCIE_CONF_DATA_OFF ; +struct mvebu_pcie *pcie = bus->sysdata; +struct mvebu_pcie_port *port; +void __iomem *conf_data; + + port =mvebu_pcie_find_port (pcie, bus, devfn); +if (!port) +return PCIBIOS_DEVICE_NOT_FOUND ; + +if (!mvebu_pcie_link_up (port)) +return PCIBIOS_DEVICE_NOT_FOUND ; + + conf_data = port->base +PCIE_CONF_DATA_OFF ;mvebu_writel (port,PCIE_CONF_ADDR (bus->number, devfn, where),PCIE_CONF_ADDR_OFF );
@@ -347,6 +375,11 @@ static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port,return PCIBIOS_SUCCESSFUL ; } +static struct pci_ops mvebu_pcie_child_ops = { + .read = mvebu_pcie_child_rd_conf, + .write = mvebu_pcie_child_wr_conf, +}; +/* * Remove windows, starting from the largest ones to the smallest * ones.
@@ -862,25 +895,12 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn, {struct mvebu_pcie *pcie = bus->sysdata;struct mvebu_pcie_port *port; -int ret; port =mvebu_pcie_find_port (pcie, bus, devfn);if (!port)return PCIBIOS_DEVICE_NOT_FOUND ; -/* Access the emulated PCI-to-PCI bridge */ -if (bus->number ==0 ) -return pci_bridge_emul_conf_write (&port->bridge, where, - size, val); - -if (!mvebu_pcie_link_up (port)) -return PCIBIOS_DEVICE_NOT_FOUND ; - -/* Access the real PCIe interface */ - ret =mvebu_pcie_hw_wr_conf (port, bus, devfn, - where, size, val); - -return ret; +return pci_bridge_emul_conf_write (&port->bridge, where, size, val); }/* PCI configuration space read function */
@@ -889,7 +909,6 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, {struct mvebu_pcie *pcie = bus->sysdata;struct mvebu_pcie_port *port; -int ret; port =mvebu_pcie_find_port (pcie, bus, devfn);if (!port) {
@@ -897,21 +916,7 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,return PCIBIOS_DEVICE_NOT_FOUND ; } -/* Access the emulated PCI-to-PCI bridge */ -if (bus->number ==0 ) -return pci_bridge_emul_conf_read (&port->bridge, where, - size, val); - -if (!mvebu_pcie_link_up (port)) { - *val =0xffffffff ; -return PCIBIOS_DEVICE_NOT_FOUND ; - } - -/* Access the real PCIe interface */ - ret =mvebu_pcie_hw_rd_conf (port, bus, devfn, - where, size, val); - -return ret; +return pci_bridge_emul_conf_read (&port->bridge, where, size, val); }static struct pci_ops mvebu_pcie_ops = {
@@ -1421,6 +1426,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev) bridge->sysdata = pcie; bridge->ops = &mvebu_pcie_ops; + bridge->child_ops = &mvebu_pcie_child_ops; bridge->align_resource = mvebu_pcie_align_resource; bridge->map_irq = mvebu_pcie_map_irq;
Signature · 1 line
2.20.1
Pali Rohár
maintainerPATCH 10/11 PCI: mvebu: Implement support for legacy INTx interrupts
In reply to Pali RohárThis adds support for legacy INTx interrupts received from other PCIe devices and which are reported by a new INTx irq chip.
With this change, kernel can distinguish between INTA, INTB, INTC and INTD interrupts.
Note that for this support, device tree files has to be properly adjusted to provide "interrupts" or "interrupts-extended" property with intx interrupt source, "interrupt-names" property with "intx" string and also 'interrupt-controller' subnode must be defined.
If device tree files do not provide these nodes then driver would work as before.
--- drivers/pci/controller/pci-mvebu.c | 182 +++++++++++++++++++++++++++-- 1 file changed, 174 insertions(+), 8 deletions(-)
drivers/pci/controller/pci-mvebu.c +174 -8
@@ -54,9 +54,10 @@ PCIE_CONF_ADDR_EN)#define PCIE_CONF_DATA_OFF 0x18fc#define PCIE_INT_CAUSE_OFF 0x1900 +#define PCIE_INT_UNMASK_OFF 0x1910 +#define PCIE_INT_INTX (i) BIT(24+i)#define PCIE_INT_PM_PME BIT(28) -#define PCIE_MASK_OFF 0x1910 -#define PCIE_MASK_ENABLE_INTS 0x0f000000 +#define PCIE_INT_ALL_MASK GENMASK(31, 0)#define PCIE_CTRL_OFF 0x1a00#define PCIE_CTRL_X1_MODE 0x0001#define PCIE_CTRL_RC_MODE BIT(1)
@@ -110,6 +111,10 @@ struct mvebu_pcie_port {struct mvebu_pcie_window iowin;u32 saved_pcie_stat;struct resource regs; +struct irq_domain *intx_irq_domain; +struct irq_chip intx_irq_chip; +raw_spinlock_t irq_lock; +int intx_irq; };static inline void mvebu_writel (struct mvebu_pcie_port *port,u32 val,u32 reg)
@@ -235,7 +240,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, lnkcap, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, unmask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF );
@@ -288,10 +293,30 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)/* Point PCIe unit MBUS decode windows to DRAM space. */ mvebu_pcie_setup_wins (port); -/* Enable interrupt lines A-D. */ - mask =mvebu_readl (port,PCIE_MASK_OFF ); - mask |=PCIE_MASK_ENABLE_INTS ; -mvebu_writel (port, mask,PCIE_MASK_OFF ); +/* Mask all interrupt sources. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (port->intx_irq <=0 ) { +/* +* When neither "summary" interrupt, nor "intx" interrupt was +* specified in DT then unmask all legacy INTx interrupts as in +* this case driver does not provide a way for masking and +* unmasking of individual legacy INTx interrupts. In this case +* all interrupts, including legacy INTx are reported via one +* shared GIC source and therefore kernel cannot distinguish +* which individual legacy INTx was triggered. These interrupts +* are shared, so it should not cause any issue. Just +* performance penalty as every PCIe interrupt handler needs to +* be called when some interrupt is triggered. +*/ + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (0 ) |PCIE_INT_INTX (1 ) | +PCIE_INT_INTX (2 ) |PCIE_INT_INTX (3 ); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); + } } static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,
@@ -924,6 +949,109 @@ static struct pci_ops mvebu_pcie_ops = { .write = mvebu_pcie_wr_conf, }; +static void mvebu_pcie_intx_irq_mask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask &= ~PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static void mvebu_pcie_intx_irq_unmask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static int mvebu_pcie_intx_irq_map (struct irq_domain *h, +unsigned int virq,irq_hw_number_t hwirq) +{ +struct mvebu_pcie_port *port = h->host_data; + +irq_set_status_flags (virq,IRQ_LEVEL ); +irq_set_chip_and_handler (virq, &port->intx_irq_chip, handle_level_irq); +irq_set_chip_data (virq, port); + +return 0 ; +} + +static const struct irq_domain_ops mvebu_pcie_intx_irq_domain_ops = { + .map = mvebu_pcie_intx_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int mvebu_pcie_init_irq_domain (struct mvebu_pcie_port *port) +{ +struct device *dev = &port->pcie->pdev->dev; +struct device_node *pcie_intc_node; + +raw_spin_lock_init (&port->irq_lock); + + port->intx_irq_chip.name =devm_kasprintf (dev,GFP_KERNEL , +"mvebu-%s-INTx" , + port->name); + port->intx_irq_chip.irq_mask = mvebu_pcie_intx_irq_mask; + port->intx_irq_chip.irq_unmask = mvebu_pcie_intx_irq_unmask; + + pcie_intc_node =of_get_next_child (port->dn,NULL ); +if (!pcie_intc_node) { +dev_err (dev,"No PCIe Intc node found for %s\n" , port->name); +return -ENODEV ; + } + + port->intx_irq_domain =irq_domain_add_linear (pcie_intc_node,PCI_NUM_INTX , + &mvebu_pcie_intx_irq_domain_ops, + port); +of_node_put (pcie_intc_node); +if (!port->intx_irq_domain) { +devm_kfree (dev, port->intx_irq_chip.name); +dev_err (dev,"Failed to get INTx IRQ domain for %s\n" , port->name); +return -ENOMEM ; + } + +return 0 ; +} + +static void mvebu_pcie_irq_handler (struct irq_desc *desc) +{ +struct mvebu_pcie_port *port =irq_desc_get_handler_data (desc); +struct irq_chip *chip =irq_desc_get_chip (desc); +struct device *dev = &port->pcie->pdev->dev; +u32 cause, unmask, status; +int i; + +chained_irq_enter (chip, desc); + + cause =mvebu_readl (port,PCIE_INT_CAUSE_OFF ); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + status = cause & unmask; + +/* Process legacy INTx interrupts */ +for (i =0 ; i <PCI_NUM_INTX ; i++) { +if (!(status &PCIE_INT_INTX (i))) +continue ; + +if (generic_handle_domain_irq (port->intx_irq_domain, i) == -EINVAL ) +dev_err_ratelimited (dev,"unexpected INT%c IRQ\n" , (char )i+'A' ); + } + +chained_irq_exit (chip, desc); +} +static int mvebu_pcie_map_irq (const struct pci_dev *dev,u8 slot,u8 pin) {/* Interrupt support on mvebu emulated bridges is not implemented yet */
@@ -1121,6 +1249,16 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, port->io_attr =-1 ; } +/* +* Old DT bindings do not contain "intx" interrupt +* so do not fail probing driver when interrupt does not exist. +*/ + port->intx_irq =of_irq_get_byname (child,"intx" ); +if (port->intx_irq == -EPROBE_DEFER ) { + ret = port->intx_irq; + goto err; + } + reset_gpio =of_get_named_gpio_flags (child,"reset-gpios" ,0 , &flags);if (reset_gpio == -EPROBE_DEFER ) { ret = reset_gpio;
@@ -1317,6 +1455,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)for (i =0 ; i < pcie->nports; i++) {struct mvebu_pcie_port *port = &pcie->ports[i]; +int irq = port->intx_irq; child = port->dn;if (!child)
@@ -1344,6 +1483,22 @@ static int mvebu_pcie_probe(struct platform_device *pdev)continue ; } +if (irq >0 ) { + ret =mvebu_pcie_init_irq_domain (port); +if (ret) { +dev_err (dev,"%s: cannot init irq domain\n" , + port->name); +pci_bridge_emul_cleanup (&port->bridge); +devm_iounmap (dev, port->base); + port->base =NULL ; +mvebu_pcie_powerdown (port); +continue ; + } +irq_set_chained_handler_and_data (irq, + mvebu_pcie_irq_handler, + port); + } +/* * PCIe topology exported by mvebu hw is quite complicated. In * reality has something like N fully independent host bridges
@@ -1448,6 +1603,7 @@ static int mvebu_pcie_remove(struct platform_device *pdev)for (i =0 ; i < pcie->nports; i++) {struct mvebu_pcie_port *port = &pcie->ports[i]; +int irq = port->intx_irq;if (!port->base)continue ;
@@ -1458,7 +1614,17 @@ static int mvebu_pcie_remove(struct platform_device *pdev)mvebu_writel (port, cmd,PCIE_CMD_OFF );/* Mask all interrupt sources. */ -mvebu_writel (port,0 ,PCIE_MASK_OFF ); +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +/* Remove IRQ domains. */ +if (port->intx_irq_domain) +irq_domain_remove (port->intx_irq_domain); + +if (irq >0 ) +irq_set_chained_handler_and_data (irq,NULL ,NULL );/* Free config space for emulated root bridge. */ pci_bridge_emul_cleanup (&port->bridge);
Signature · 1 line
2.20.1
Pali Rohár
maintainerPATCH 11/11 ARM: dts: armada-385.dtsi: Add definitions for PCIe legacy INTx interrupts
In reply to Pali RohárWith this change legacy INTA, INTB, INTC and INTD interrupts are reported separately and not mixed into one Linux virq source anymore.
--- arch/arm/boot/dts/armada-385.dtsi | 52 ++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 8 deletions(-)
arch/arm/boot/dts/armada-385.dtsi +44 -8
@@ -69,16 +69,25 @@ reg = <0x0800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 0x81000000 0 0 0x81000000 0x1 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie1_intc 0>, + <0 0 0 2 &pcie1_intc 1>, + <0 0 0 3 &pcie1_intc 2>, + <0 0 0 4 &pcie1_intc 3>; marvell,pcie-port = <0>; marvell,pcie-lane = <0>; clocks = <&gateclk 8>; status = "disabled"; + pcie1_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /* x1 port */
@@ -88,16 +97,25 @@ reg = <0x1000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 0x81000000 0 0 0x81000000 0x2 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie2_intc 0>, + <0 0 0 2 &pcie2_intc 1>, + <0 0 0 3 &pcie2_intc 2>, + <0 0 0 4 &pcie2_intc 3>; marvell,pcie-port = <1>; marvell,pcie-lane = <0>; clocks = <&gateclk 5>; status = "disabled"; + pcie2_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /* x1 port */
@@ -107,16 +125,25 @@ reg = <0x1800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 0x81000000 0 0 0x81000000 0x3 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie3_intc 0>, + <0 0 0 2 &pcie3_intc 1>, + <0 0 0 3 &pcie3_intc 2>, + <0 0 0 4 &pcie3_intc 3>; marvell,pcie-port = <2>; marvell,pcie-lane = <0>; clocks = <&gateclk 6>; status = "disabled"; + pcie3_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /*
@@ -129,16 +156,25 @@ reg = <0x2000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0 0x81000000 0 0 0x81000000 0x4 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie4_intc 0>, + <0 0 0 2 &pcie4_intc 1>, + <0 0 0 3 &pcie4_intc 2>, + <0 0 0 4 &pcie4_intc 3>; marvell,pcie-port = <3>; marvell,pcie-lane = <0>; clocks = <&gateclk 7>; status = "disabled"; + pcie4_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; }; };
Signature · 1 line
2.20.1
Re: [PATCH 08/11] PCI: mvebu: Use child_ops API
In reply to Pali RohárQuoted 7 lines · Pali Rohár
Split struct
pci_opsbetween ops andchild_ops. Member ops is used for accessing PCIe Root Ports viapci-bridge-emul.cdriver andchild_opsfor accessing real PCIe cards.There is no need to mix these two struct
pci_opsinto one as PCI core code already provides separate callbacks viabridge->opsandbridge->child_ops.
I had similar patches including mvebu that I never got around to sending out. I pushed the branch out now at least[1].
Quoted 56 lines
Signed-off-by Pali Rohár <[email protected]>--- drivers/pci/controller/pci-mvebu.c | 82 ++++++++++++++++-------------- 1 file changed, 44 insertions(+), 38 deletions(-)drivers/pci/controller/pci-mvebu.c +26
-8@@ -294,11 +294,29 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)mvebu_writel (port, mask,PCIE_MASK_OFF ); } -static int mvebu_pcie_hw_rd_conf (struct mvebu_pcie_port *port, -struct pci_bus *bus, -u32 devfn,int where,int size,u32 *val) +static struct mvebu_pcie_port *mvebu_pcie_find_port (struct mvebu_pcie *pcie, +struct pci_bus *bus, +int devfn); + +static int mvebu_pcie_child_rd_conf (struct pci_bus *bus,u32 devfn,int where, +int size,u32 *val) { -void __iomem *conf_data = port->base +PCIE_CONF_DATA_OFF ; +struct mvebu_pcie *pcie = bus->sysdata; +struct mvebu_pcie_port *port; +void __iomem *conf_data; + + port =mvebu_pcie_find_port (pcie, bus, devfn); +if (!port) { + *val =0xffffffff ; +return PCIBIOS_DEVICE_NOT_FOUND ; + } + +if (!mvebu_pcie_link_up (port)) { + *val =0xffffffff ; +return PCIBIOS_DEVICE_NOT_FOUND ; + } + + conf_data = port->base +PCIE_CONF_DATA_OFF ;mvebu_writel (port,PCIE_CONF_ADDR (bus->number, devfn, where),PCIE_CONF_ADDR_OFF );@@ -321,11 +339,21 @@ static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port,return PCIBIOS_SUCCESSFUL ; } -static int mvebu_pcie_hw_wr_conf (struct mvebu_pcie_port *port, -struct pci_bus *bus, -u32 devfn,int where,int size,u32 val) +static int mvebu_pcie_child_wr_conf (struct pci_bus *bus,u32 devfn, +int where,int size,u32 val) { -void __iomem *conf_data = port->base +PCIE_CONF_DATA_OFF ; +struct mvebu_pcie *pcie = bus->sysdata; +struct mvebu_pcie_port *port;
drivers/pci/controller/pci-mvebu.c static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port, · line 356
Quoted 10 lines
drivers/pci/controller/pci-mvebu.c +10
-0+void __iomem *conf_data; + + port =mvebu_pcie_find_port (pcie, bus, devfn); +if (!port) +return PCIBIOS_DEVICE_NOT_FOUND ; + +if (!mvebu_pcie_link_up (port)) +return PCIBIOS_DEVICE_NOT_FOUND ; + + conf_data = port->base +PCIE_CONF_DATA_OFF ;
The same code in read and write is a hint to use .map_bus().
Quoted 16 lines
drivers/pci/controller/pci-mvebu.c +5
-0mvebu_writel (port,PCIE_CONF_ADDR (bus->number, devfn, where),PCIE_CONF_ADDR_OFF );@@ -347,6 +375,11 @@ static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port,return PCIBIOS_SUCCESSFUL ; } +static struct pci_ops mvebu_pcie_child_ops = { + .read = mvebu_pcie_child_rd_conf, + .write = mvebu_pcie_child_wr_conf, +}; +/* * Remove windows, starting from the largest ones to the smallest * ones.
drivers/pci/controller/pci-mvebu.c static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn, · line 901
Quoted 8 lines
drivers/pci/controller/pci-mvebu.c +0
-1@@ -862,25 +895,12 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn, {struct mvebu_pcie *pcie = bus->sysdata;struct mvebu_pcie_port *port; -int ret; port =mvebu_pcie_find_port (pcie, bus, devfn);if (!port)return PCIBIOS_DEVICE_NOT_FOUND ;
It would be nice to go from 'bus' to 'bridge' ptr directly, but I still had this in my version. I guess a standard RP struct as part of decoupling host bridges from RPs would solve this issue.
Rob
[1] https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git/log/?h=pci-child-bus-ops
Pali Rohár
maintainerRe: [PATCH 08/11] PCI: mvebu: Use child_ops API
Quoted 18 lines · On Wednesday 05 January 2022 09:41:51 Rob Herring wrote:
Quoted 18 lines
drivers/pci/controller/pci-mvebu.c +5
-1@@ -347,6 +375,11 @@ static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port,return PCIBIOS_SUCCESSFUL ; } +static struct pci_ops mvebu_pcie_child_ops = { + .read = mvebu_pcie_child_rd_conf, + .write = mvebu_pcie_child_wr_conf, +}; +/* * Remove windows, starting from the largest ones to the smallest * ones. @@ -862,25 +895,12 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn, {struct mvebu_pcie *pcie = bus->sysdata;struct mvebu_pcie_port *port; -int ret;
Quoted 7 lines
drivers/pci/controller/pci-mvebu.c static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn, · line 901
Quoted 3 lines
drivers/pci/controller/pci-mvebu.c +0
-0port =mvebu_pcie_find_port (pcie, bus, devfn);if (!port)return PCIBIOS_DEVICE_NOT_FOUND ;It would be nice to go from 'bus' to 'bridge' ptr directly, but I still had this in my version. I guess a standard RP struct as part of decoupling host bridges from RPs would solve this issue.
Hello!
The problem is somewhere else. This driver is misusing bus 0 for doing non-transparent bus-bridging between multiple PCI domains by registering roots ports across all domains into domain 0, bus 0. All details are in this my patch which documents this strange driver behavior:
https://lore.kernel.org/linux-pci/[email protected]/
So the correct solution is is split these multidomain mixing and then every domain would have exactly one root port (as it is designed in HW).
Re: [PATCH 10/11] PCI: mvebu: Implement support for legacy INTx interrupts
In reply to Pali RohárQuoted 38 lines · Pali Rohár
This adds support for legacy INTx interrupts received from other PCIe devices and which are reported by a new INTx irq chip.
With this change, kernel can distinguish between INTA, INTB, INTC and INTD interrupts.
Note that for this support, device tree files has to be properly adjusted to provide "interrupts" or "interrupts-extended" property with intx interrupt source, "interrupt-names" property with "intx" string and also 'interrupt-controller' subnode must be defined.
If device tree files do not provide these nodes then driver would work as before.
Signed-off-by Pali Rohár <[email protected]>--- drivers/pci/controller/pci-mvebu.c | 182 +++++++++++++++++++++++++++-- 1 file changed, 174 insertions(+), 8 deletions(-)drivers/pci/controller/pci-mvebu.c +3
-2@@ -54,9 +54,10 @@ PCIE_CONF_ADDR_EN)#define PCIE_CONF_DATA_OFF 0x18fc#define PCIE_INT_CAUSE_OFF 0x1900 +#define PCIE_INT_UNMASK_OFF 0x1910 +#define PCIE_INT_INTX (i) BIT(24+i)#define PCIE_INT_PM_PME BIT(28) -#define PCIE_MASK_OFF 0x1910 -#define PCIE_MASK_ENABLE_INTS 0x0f000000 +#define PCIE_INT_ALL_MASK GENMASK(31, 0)#define PCIE_CTRL_OFF 0x1a00#define PCIE_CTRL_X1_MODE 0x0001#define PCIE_CTRL_RC_MODE BIT(1)
drivers/pci/controller/pci-mvebu.c struct mvebu_pcie_port { · line 115
Quoted 5 lines
drivers/pci/controller/pci-mvebu.c +2
-0@@ -110,6 +111,10 @@ struct mvebu_pcie_port {struct mvebu_pcie_window iowin;u32 saved_pcie_stat;struct resource regs; +struct irq_domain *intx_irq_domain; +struct irq_chip intx_irq_chip;
Why is this structure per port? It really should be global. Printing the port number in the name isn't enough of a reason.
Quoted 35 lines
drivers/pci/controller/pci-mvebu.c +16
-5+raw_spinlock_t irq_lock; +int intx_irq; };static inline void mvebu_writel (struct mvebu_pcie_port *port,u32 val,u32 reg)@@ -235,7 +240,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, lnkcap, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, unmask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF );@@ -288,10 +293,30 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)/* Point PCIe unit MBUS decode windows to DRAM space. */ mvebu_pcie_setup_wins (port); -/* Enable interrupt lines A-D. */ - mask =mvebu_readl (port,PCIE_MASK_OFF ); - mask |=PCIE_MASK_ENABLE_INTS ; -mvebu_writel (port, mask,PCIE_MASK_OFF ); +/* Mask all interrupt sources. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (port->intx_irq <=0 ) { +/* +* When neither "summary" interrupt, nor "intx" interrupt was +* specified in DT then unmask all legacy INTx interrupts as in +* this case driver does not provide a way for masking and +* unmasking of individual legacy INTx interrupts. In this case +* all interrupts, including legacy INTx are reported via one
drivers/pci/controller/pci-mvebu.c static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) · line 318
Quoted 10 lines
drivers/pci/controller/pci-mvebu.c +10
-0+* shared GIC source and therefore kernel cannot distinguish +* which individual legacy INTx was triggered. These interrupts +* are shared, so it should not cause any issue. Just +* performance penalty as every PCIe interrupt handler needs to +* be called when some interrupt is triggered. +*/ + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (0 ) |PCIE_INT_INTX (1 ) | +PCIE_INT_INTX (2 ) |PCIE_INT_INTX (3 ); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF );
Maybe worth printing a warning here, so that the user knows they are on thin ice.
Quoted 53 lines
drivers/pci/controller/pci-mvebu.c +46
-0+ } } static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,@@ -924,6 +949,109 @@ static struct pci_ops mvebu_pcie_ops = { .write = mvebu_pcie_wr_conf, }; +static void mvebu_pcie_intx_irq_mask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask &= ~PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static void mvebu_pcie_intx_irq_unmask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static int mvebu_pcie_intx_irq_map (struct irq_domain *h, +unsigned int virq,irq_hw_number_t hwirq) +{ +struct mvebu_pcie_port *port = h->host_data; + +irq_set_status_flags (virq,IRQ_LEVEL ); +irq_set_chip_and_handler (virq, &port->intx_irq_chip, handle_level_irq); +irq_set_chip_data (virq, port); + +return 0 ; +} + +static const struct irq_domain_ops mvebu_pcie_intx_irq_domain_ops = { + .map = mvebu_pcie_intx_irq_map, + .xlate = irq_domain_xlate_onecell, +}; +
drivers/pci/controller/pci-mvebu.c static struct pci_ops mvebu_pcie_ops = { · line 1006
Quoted 10 lines
drivers/pci/controller/pci-mvebu.c +10
-0+static int mvebu_pcie_init_irq_domain (struct mvebu_pcie_port *port) +{ +struct device *dev = &port->pcie->pdev->dev; +struct device_node *pcie_intc_node; + +raw_spin_lock_init (&port->irq_lock); + + port->intx_irq_chip.name =devm_kasprintf (dev,GFP_KERNEL , +"mvebu-%s-INTx" , + port->name);
That's exactly what I really don't want to see. It prevents sharing of the irq_chip structure, and gets in the way of making it const in the future. Yes, I know that some drivers do that. I can't fix those, because /proc/interrupts is ABI. But I really don't want to see more of these.
/sys/kernel/debug/irqs already has all the information you need, as it will happily give you the domain name and the interrupt topology.
Quoted 24 lines
drivers/pci/controller/pci-mvebu.c +24
-0+ port->intx_irq_chip.irq_mask = mvebu_pcie_intx_irq_mask; + port->intx_irq_chip.irq_unmask = mvebu_pcie_intx_irq_unmask; + + pcie_intc_node =of_get_next_child (port->dn,NULL ); +if (!pcie_intc_node) { +dev_err (dev,"No PCIe Intc node found for %s\n" , port->name); +return -ENODEV ; + } + + port->intx_irq_domain =irq_domain_add_linear (pcie_intc_node,PCI_NUM_INTX , + &mvebu_pcie_intx_irq_domain_ops, + port); +of_node_put (pcie_intc_node); +if (!port->intx_irq_domain) { +devm_kfree (dev, port->intx_irq_chip.name); +dev_err (dev,"Failed to get INTx IRQ domain for %s\n" , port->name); +return -ENOMEM ; + } + +return 0 ; +} + +static void mvebu_pcie_irq_handler (struct irq_desc *desc) +{
drivers/pci/controller/pci-mvebu.c static struct pci_ops mvebu_pcie_ops = { · line 1040
Quoted 10 lines
drivers/pci/controller/pci-mvebu.c +10
-0+struct mvebu_pcie_port *port =irq_desc_get_handler_data (desc); +struct irq_chip *chip =irq_desc_get_chip (desc); +struct device *dev = &port->pcie->pdev->dev; +u32 cause, unmask, status; +int i; + +chained_irq_enter (chip, desc); + + cause =mvebu_readl (port,PCIE_INT_CAUSE_OFF ); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF );
Why do you need to read this? If the CAUSE register also returns the masked interrupts that are pending, it may be worth keeping a shadow copy of the this register, as you end-up having an extra MMIO read on each and every interrupt, which can't be great for performance.
Quoted 82 lines
drivers/pci/controller/pci-mvebu.c +46
-1+ status = cause & unmask; + +/* Process legacy INTx interrupts */ +for (i =0 ; i <PCI_NUM_INTX ; i++) { +if (!(status &PCIE_INT_INTX (i))) +continue ; + +if (generic_handle_domain_irq (port->intx_irq_domain, i) == -EINVAL ) +dev_err_ratelimited (dev,"unexpected INT%c IRQ\n" , (char )i+'A' ); + } + +chained_irq_exit (chip, desc); +} +static int mvebu_pcie_map_irq (const struct pci_dev *dev,u8 slot,u8 pin) {/* Interrupt support on mvebu emulated bridges is not implemented yet */ @@ -1121,6 +1249,16 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, port->io_attr =-1 ; } +/* +* Old DT bindings do not contain "intx" interrupt +* so do not fail probing driver when interrupt does not exist. +*/ + port->intx_irq =of_irq_get_byname (child,"intx" ); +if (port->intx_irq == -EPROBE_DEFER ) { + ret = port->intx_irq; + goto err; + } + reset_gpio =of_get_named_gpio_flags (child,"reset-gpios" ,0 , &flags);if (reset_gpio == -EPROBE_DEFER ) { ret = reset_gpio;@@ -1317,6 +1455,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)for (i =0 ; i < pcie->nports; i++) {struct mvebu_pcie_port *port = &pcie->ports[i]; +int irq = port->intx_irq; child = port->dn;if (!child)@@ -1344,6 +1483,22 @@ static int mvebu_pcie_probe(struct platform_device *pdev)continue ; } +if (irq >0 ) { + ret =mvebu_pcie_init_irq_domain (port); +if (ret) { +dev_err (dev,"%s: cannot init irq domain\n" , + port->name); +pci_bridge_emul_cleanup (&port->bridge); +devm_iounmap (dev, port->base); + port->base =NULL ; +mvebu_pcie_powerdown (port); +continue ; + } +irq_set_chained_handler_and_data (irq, + mvebu_pcie_irq_handler, + port); + } +/* * PCIe topology exported by mvebu hw is quite complicated. In * reality has something like N fully independent host bridges @@ -1448,6 +1603,7 @@ static int mvebu_pcie_remove(struct platform_device *pdev)for (i =0 ; i < pcie->nports; i++) {struct mvebu_pcie_port *port = &pcie->ports[i]; +int irq = port->intx_irq;if (!port->base)continue ;@@ -1458,7 +1614,17 @@ static int mvebu_pcie_remove(struct platform_device *pdev)mvebu_writel (port, cmd,PCIE_CMD_OFF );/* Mask all interrupt sources. */ -mvebu_writel (port,0 ,PCIE_MASK_OFF ); +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF );
drivers/pci/controller/pci-mvebu.c static int mvebu_pcie_remove(struct platform_device *pdev) · line 1630
Quoted 10 lines
drivers/pci/controller/pci-mvebu.c +7
-0+ +/* Remove IRQ domains. */ +if (port->intx_irq_domain) +irq_domain_remove (port->intx_irq_domain); + +if (irq >0 ) +irq_set_chained_handler_and_data (irq,NULL ,NULL );/* Free config space for emulated root bridge. */ pci_bridge_emul_cleanup (&port->bridge);
Thanks,
M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
Pali Rohár
maintainerRe: [PATCH 10/11] PCI: mvebu: Implement support for legacy INTx interrupts
Quoted 40 lines · On Thursday 06 January 2022 15:28:20 Marc Zyngier wrote:
Quoted 38 lines · Pali Rohár
This adds support for legacy INTx interrupts received from other PCIe devices and which are reported by a new INTx irq chip.
With this change, kernel can distinguish between INTA, INTB, INTC and INTD interrupts.
Note that for this support, device tree files has to be properly adjusted to provide "interrupts" or "interrupts-extended" property with intx interrupt source, "interrupt-names" property with "intx" string and also 'interrupt-controller' subnode must be defined.
If device tree files do not provide these nodes then driver would work as before.
Signed-off-by Pali Rohár <[email protected]>--- drivers/pci/controller/pci-mvebu.c | 182 +++++++++++++++++++++++++++-- 1 file changed, 174 insertions(+), 8 deletions(-)drivers/pci/controller/pci-mvebu.c +3
-2@@ -54,9 +54,10 @@ PCIE_CONF_ADDR_EN)#define PCIE_CONF_DATA_OFF 0x18fc#define PCIE_INT_CAUSE_OFF 0x1900 +#define PCIE_INT_UNMASK_OFF 0x1910 +#define PCIE_INT_INTX (i) BIT(24+i)#define PCIE_INT_PM_PME BIT(28) -#define PCIE_MASK_OFF 0x1910 -#define PCIE_MASK_ENABLE_INTS 0x0f000000 +#define PCIE_INT_ALL_MASK GENMASK(31, 0)#define PCIE_CTRL_OFF 0x1a00#define PCIE_CTRL_X1_MODE 0x0001#define PCIE_CTRL_RC_MODE BIT(1)@@ -110,6 +111,10 @@ struct mvebu_pcie_port {
Quoted 8 lines
drivers/pci/controller/pci-mvebu.c struct mvebu_pcie_port { · line 115
Quoted 5 lines
drivers/pci/controller/pci-mvebu.c +2
-0struct mvebu_pcie_window iowin;u32 saved_pcie_stat;struct resource regs; +struct irq_domain *intx_irq_domain; +struct irq_chip intx_irq_chip;Why is this structure per port? It really should be global. Printing the port number in the name isn't enough of a reason.
Because each port has its own independent set of INTA-INTD interrupts.
Quoted 40 lines
Quoted 40 lines
drivers/pci/controller/pci-mvebu.c +21
-5+raw_spinlock_t irq_lock; +int intx_irq; };static inline void mvebu_writel (struct mvebu_pcie_port *port,u32 val,u32 reg)@@ -235,7 +240,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, lnkcap, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, unmask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF );@@ -288,10 +293,30 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)/* Point PCIe unit MBUS decode windows to DRAM space. */ mvebu_pcie_setup_wins (port); -/* Enable interrupt lines A-D. */ - mask =mvebu_readl (port,PCIE_MASK_OFF ); - mask |=PCIE_MASK_ENABLE_INTS ; -mvebu_writel (port, mask,PCIE_MASK_OFF ); +/* Mask all interrupt sources. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (port->intx_irq <=0 ) { +/* +* When neither "summary" interrupt, nor "intx" interrupt was +* specified in DT then unmask all legacy INTx interrupts as in +* this case driver does not provide a way for masking and +* unmasking of individual legacy INTx interrupts. In this case +* all interrupts, including legacy INTx are reported via one +* shared GIC source and therefore kernel cannot distinguish +* which individual legacy INTx was triggered. These interrupts +* are shared, so it should not cause any issue. Just +* performance penalty as every PCIe interrupt handler needs to +* be called when some interrupt is triggered.
Quoted 8 lines
drivers/pci/controller/pci-mvebu.c static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) · line 318
Quoted 5 lines
drivers/pci/controller/pci-mvebu.c +5
-0+*/ + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (0 ) |PCIE_INT_INTX (1 ) | +PCIE_INT_INTX (2 ) |PCIE_INT_INTX (3 ); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF );Maybe worth printing a warning here, so that the user knows they are on thin ice.
Ok. I can add it here. Anyway, this is default current state without this patch.
Quoted 61 lines
Quoted 61 lines
drivers/pci/controller/pci-mvebu.c +54
-0+ } } static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,@@ -924,6 +949,109 @@ static struct pci_ops mvebu_pcie_ops = { .write = mvebu_pcie_wr_conf, }; +static void mvebu_pcie_intx_irq_mask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask &= ~PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static void mvebu_pcie_intx_irq_unmask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static int mvebu_pcie_intx_irq_map (struct irq_domain *h, +unsigned int virq,irq_hw_number_t hwirq) +{ +struct mvebu_pcie_port *port = h->host_data; + +irq_set_status_flags (virq,IRQ_LEVEL ); +irq_set_chip_and_handler (virq, &port->intx_irq_chip, handle_level_irq); +irq_set_chip_data (virq, port); + +return 0 ; +} + +static const struct irq_domain_ops mvebu_pcie_intx_irq_domain_ops = { + .map = mvebu_pcie_intx_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int mvebu_pcie_init_irq_domain (struct mvebu_pcie_port *port) +{ +struct device *dev = &port->pcie->pdev->dev; +struct device_node *pcie_intc_node; + +raw_spin_lock_init (&port->irq_lock); + + port->intx_irq_chip.name = devm_kasprintf(dev, GFP_KERNEL,
Quoted 8 lines
drivers/pci/controller/pci-mvebu.c static struct pci_ops mvebu_pcie_ops = { · line 1006
Quoted 2 lines
drivers/pci/controller/pci-mvebu.c +2
-0+"mvebu-%s-INTx" , + port->name);That's exactly what I really don't want to see. It prevents sharing of the
irq_chipstructure, and gets in the way of making it const in the future. Yes, I know that some drivers do that. I can't fix those, because/proc/interruptsis ABI. But I really don't want to see more of these.
Well, I do not understand why it should be shared and with who. HW has N independent IRQ chips for legacy interrupts. And each one will be specified in DT per HW layout / design.
Quoted 32 lines
/sys/kernel/debug/irqsalready has all the information you need, as it will happily give you the domain name and the interrupt topology.Quoted 29 lines
drivers/pci/controller/pci-mvebu.c +29
-0+ port->intx_irq_chip.irq_mask = mvebu_pcie_intx_irq_mask; + port->intx_irq_chip.irq_unmask = mvebu_pcie_intx_irq_unmask; + + pcie_intc_node =of_get_next_child (port->dn,NULL ); +if (!pcie_intc_node) { +dev_err (dev,"No PCIe Intc node found for %s\n" , port->name); +return -ENODEV ; + } + + port->intx_irq_domain =irq_domain_add_linear (pcie_intc_node,PCI_NUM_INTX , + &mvebu_pcie_intx_irq_domain_ops, + port); +of_node_put (pcie_intc_node); +if (!port->intx_irq_domain) { +devm_kfree (dev, port->intx_irq_chip.name); +dev_err (dev,"Failed to get INTx IRQ domain for %s\n" , port->name); +return -ENOMEM ; + } + +return 0 ; +} + +static void mvebu_pcie_irq_handler (struct irq_desc *desc) +{ +struct mvebu_pcie_port *port =irq_desc_get_handler_data (desc); +struct irq_chip *chip =irq_desc_get_chip (desc); +struct device *dev = &port->pcie->pdev->dev; +u32 cause, unmask, status; +int i;
Quoted 8 lines
drivers/pci/controller/pci-mvebu.c static struct pci_ops mvebu_pcie_ops = { · line 1040
Quoted 5 lines
drivers/pci/controller/pci-mvebu.c +5
-0+ +chained_irq_enter (chip, desc); + + cause =mvebu_readl (port,PCIE_INT_CAUSE_OFF ); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF );Why do you need to read this? If the CAUSE register also returns the masked interrupts that are pending,
Yes, it returns all interrupts, even those which are masked for which is not system interested.
Quoted 3 lines
it may be worth keeping a shadow
copy of the this register, as you end-up having an extra MMIO read on each and every interrupt, which can't be great for performance.
Ok. I can set shadow copy of unmask register into port structure. But it has really performance impact when reading directly unmask register from hw and when reading it from port shadow copy structure?
Quoted 99 lines
Quoted 82 lines
drivers/pci/controller/pci-mvebu.c +46
-1+ status = cause & unmask; + +/* Process legacy INTx interrupts */ +for (i =0 ; i <PCI_NUM_INTX ; i++) { +if (!(status &PCIE_INT_INTX (i))) +continue ; + +if (generic_handle_domain_irq (port->intx_irq_domain, i) == -EINVAL ) +dev_err_ratelimited (dev,"unexpected INT%c IRQ\n" , (char )i+'A' ); + } + +chained_irq_exit (chip, desc); +} +static int mvebu_pcie_map_irq (const struct pci_dev *dev,u8 slot,u8 pin) {/* Interrupt support on mvebu emulated bridges is not implemented yet */ @@ -1121,6 +1249,16 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, port->io_attr =-1 ; } +/* +* Old DT bindings do not contain "intx" interrupt +* so do not fail probing driver when interrupt does not exist. +*/ + port->intx_irq =of_irq_get_byname (child,"intx" ); +if (port->intx_irq == -EPROBE_DEFER ) { + ret = port->intx_irq; + goto err; + } + reset_gpio =of_get_named_gpio_flags (child,"reset-gpios" ,0 , &flags);if (reset_gpio == -EPROBE_DEFER ) { ret = reset_gpio;@@ -1317,6 +1455,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)for (i =0 ; i < pcie->nports; i++) {struct mvebu_pcie_port *port = &pcie->ports[i]; +int irq = port->intx_irq; child = port->dn;if (!child)@@ -1344,6 +1483,22 @@ static int mvebu_pcie_probe(struct platform_device *pdev)continue ; } +if (irq >0 ) { + ret =mvebu_pcie_init_irq_domain (port); +if (ret) { +dev_err (dev,"%s: cannot init irq domain\n" , + port->name); +pci_bridge_emul_cleanup (&port->bridge); +devm_iounmap (dev, port->base); + port->base =NULL ; +mvebu_pcie_powerdown (port); +continue ; + } +irq_set_chained_handler_and_data (irq, + mvebu_pcie_irq_handler, + port); + } +/* * PCIe topology exported by mvebu hw is quite complicated. In * reality has something like N fully independent host bridges @@ -1448,6 +1603,7 @@ static int mvebu_pcie_remove(struct platform_device *pdev)for (i =0 ; i < pcie->nports; i++) {struct mvebu_pcie_port *port = &pcie->ports[i]; +int irq = port->intx_irq;if (!port->base)continue ;@@ -1458,7 +1614,17 @@ static int mvebu_pcie_remove(struct platform_device *pdev)mvebu_writel (port, cmd,PCIE_CMD_OFF );/* Mask all interrupt sources. */ -mvebu_writel (port,0 ,PCIE_MASK_OFF ); +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF );drivers/pci/controller/pci-mvebu.c static int mvebu_pcie_remove(struct platform_device *pdev) · line 1630
Quoted 10 lines
drivers/pci/controller/pci-mvebu.c +7
-0+ +/* Remove IRQ domains. */ +if (port->intx_irq_domain) +irq_domain_remove (port->intx_irq_domain); + +if (irq >0 ) +irq_set_chained_handler_and_data (irq,NULL ,NULL );/* Free config space for emulated root bridge. */ pci_bridge_emul_cleanup (&port->bridge);Thanks,
M.Signature · 1 line
Without deviation from the norm, progress is not possible.
Re: [PATCH 10/11] PCI: mvebu: Implement support for legacy INTx interrupts
Quoted 51 lines · Pali Rohár
Quoted 40 lines · On Thursday 06 January 2022 15:28:20 Marc Zyngier wrote:
Quoted 38 lines · Pali Rohár
This adds support for legacy INTx interrupts received from other PCIe devices and which are reported by a new INTx irq chip.
With this change, kernel can distinguish between INTA, INTB, INTC and INTD interrupts.
Note that for this support, device tree files has to be properly adjusted to provide "interrupts" or "interrupts-extended" property with intx interrupt source, "interrupt-names" property with "intx" string and also 'interrupt-controller' subnode must be defined.
If device tree files do not provide these nodes then driver would work as before.
Signed-off-by Pali Rohár <[email protected]>--- drivers/pci/controller/pci-mvebu.c | 182 +++++++++++++++++++++++++++-- 1 file changed, 174 insertions(+), 8 deletions(-)drivers/pci/controller/pci-mvebu.c +3
-2@@ -54,9 +54,10 @@ PCIE_CONF_ADDR_EN)#define PCIE_CONF_DATA_OFF 0x18fc#define PCIE_INT_CAUSE_OFF 0x1900 +#define PCIE_INT_UNMASK_OFF 0x1910 +#define PCIE_INT_INTX (i) BIT(24+i)#define PCIE_INT_PM_PME BIT(28) -#define PCIE_MASK_OFF 0x1910 -#define PCIE_MASK_ENABLE_INTS 0x0f000000 +#define PCIE_INT_ALL_MASK GENMASK(31, 0)#define PCIE_CTRL_OFF 0x1a00#define PCIE_CTRL_X1_MODE 0x0001#define PCIE_CTRL_RC_MODE BIT(1)@@ -110,6 +111,10 @@ struct mvebu_pcie_port {Quoted 8 lines
drivers/pci/controller/pci-mvebu.c struct mvebu_pcie_port { · line 115
Quoted 5 lines
drivers/pci/controller/pci-mvebu.c +2
-0struct mvebu_pcie_window iowin;u32 saved_pcie_stat;struct resource regs; +struct irq_domain *intx_irq_domain; +struct irq_chip intx_irq_chip;Why is this structure per port? It really should be global. Printing the port number in the name isn't enough of a reason.
Quoted 2 lines
Because each port has its own independent set of INTA-INTD interrupts.
That doesn't warrant a copy of an irq_chip structure that contains the exact same callbacks, and only differs by *a string*. And the use of this string is only to end-up in /proc/interrupts, which is totally pointless.
Quoted 123 lines
Quoted 41 lines
Quoted 41 lines
drivers/pci/controller/pci-mvebu.c +21
-5+raw_spinlock_t irq_lock; +int intx_irq; };static inline void mvebu_writel (struct mvebu_pcie_port *port,u32 val,u32 reg)@@ -235,7 +240,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, lnkcap, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, unmask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF );@@ -288,10 +293,30 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)/* Point PCIe unit MBUS decode windows to DRAM space. */ mvebu_pcie_setup_wins (port); -/* Enable interrupt lines A-D. */ - mask =mvebu_readl (port,PCIE_MASK_OFF ); - mask |=PCIE_MASK_ENABLE_INTS ; -mvebu_writel (port, mask,PCIE_MASK_OFF ); +/* Mask all interrupt sources. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (port->intx_irq <=0 ) { +/* +* When neither "summary" interrupt, nor "intx" interrupt was +* specified in DT then unmask all legacy INTx interrupts as in +* this case driver does not provide a way for masking and +* unmasking of individual legacy INTx interrupts. In this case +* all interrupts, including legacy INTx are reported via one +* shared GIC source and therefore kernel cannot distinguish +* which individual legacy INTx was triggered. These interrupts +* are shared, so it should not cause any issue. Just +* performance penalty as every PCIe interrupt handler needs to +* be called when some interrupt is triggered. Quoted 8 lines
drivers/pci/controller/pci-mvebu.c static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) · line 318
Quoted 5 lines
drivers/pci/controller/pci-mvebu.c +5
-0+*/ + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (0 ) |PCIE_INT_INTX (1 ) | +PCIE_INT_INTX (2 ) |PCIE_INT_INTX (3 ); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF );Maybe worth printing a warning here, so that the user knows they are on thin ice.
Ok. I can add it here. Anyway, this is default current state without this patch.
Quoted 61 lines
Quoted 61 lines
drivers/pci/controller/pci-mvebu.c +54
-0+ } } static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,@@ -924,6 +949,109 @@ static struct pci_ops mvebu_pcie_ops = { .write = mvebu_pcie_wr_conf, }; +static void mvebu_pcie_intx_irq_mask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask &= ~PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static void mvebu_pcie_intx_irq_unmask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static int mvebu_pcie_intx_irq_map (struct irq_domain *h, +unsigned int virq,irq_hw_number_t hwirq) +{ +struct mvebu_pcie_port *port = h->host_data; + +irq_set_status_flags (virq,IRQ_LEVEL ); +irq_set_chip_and_handler (virq, &port->intx_irq_chip, handle_level_irq); +irq_set_chip_data (virq, port); + +return 0 ; +} + +static const struct irq_domain_ops mvebu_pcie_intx_irq_domain_ops = { + .map = mvebu_pcie_intx_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int mvebu_pcie_init_irq_domain (struct mvebu_pcie_port *port) +{ +struct device *dev = &port->pcie->pdev->dev; +struct device_node *pcie_intc_node; + +raw_spin_lock_init (&port->irq_lock); + + port->intx_irq_chip.name = devm_kasprintf(dev, GFP_KERNEL,Quoted 8 lines
drivers/pci/controller/pci-mvebu.c static struct pci_ops mvebu_pcie_ops = { · line 1006
Quoted 2 lines
drivers/pci/controller/pci-mvebu.c +2
-0+"mvebu-%s-INTx" , + port->name);That's exactly what I really don't want to see. It prevents sharing of the
irq_chipstructure, and gets in the way of making it const in the future. Yes, I know that some drivers do that. I can't fix those, because/proc/interruptsis ABI. But I really don't want to see more of these.
Quoted 3 lines
Well, I do not understand why it should be shared and with who. HW has N independent IRQ chips for legacy interrupts. And each one will be specified in DT per HW layout / design.
If you have multiple ports, all the ports can share the irq_chip structure. Actually scratch that. They *MUST* share the structure. The only reason you're not sharing it is to be able to print this useless string in /proc/interrupts.
M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
Pali Rohár
maintainerRe: [PATCH 10/11] PCI: mvebu: Implement support for legacy INTx interrupts
Quoted 184 lines · On Thursday 06 January 2022 15:55:11 Marc Zyngier wrote:
Quoted 51 lines · Pali Rohár
Quoted 40 lines · On Thursday 06 January 2022 15:28:20 Marc Zyngier wrote:
Quoted 38 lines · Pali Rohár
This adds support for legacy INTx interrupts received from other PCIe devices and which are reported by a new INTx irq chip.
With this change, kernel can distinguish between INTA, INTB, INTC and INTD interrupts.
Note that for this support, device tree files has to be properly adjusted to provide "interrupts" or "interrupts-extended" property with intx interrupt source, "interrupt-names" property with "intx" string and also 'interrupt-controller' subnode must be defined.
If device tree files do not provide these nodes then driver would work as before.
Signed-off-by Pali Rohár <[email protected]>--- drivers/pci/controller/pci-mvebu.c | 182 +++++++++++++++++++++++++++-- 1 file changed, 174 insertions(+), 8 deletions(-)drivers/pci/controller/pci-mvebu.c +3
-2@@ -54,9 +54,10 @@ PCIE_CONF_ADDR_EN)#define PCIE_CONF_DATA_OFF 0x18fc#define PCIE_INT_CAUSE_OFF 0x1900 +#define PCIE_INT_UNMASK_OFF 0x1910 +#define PCIE_INT_INTX (i) BIT(24+i)#define PCIE_INT_PM_PME BIT(28) -#define PCIE_MASK_OFF 0x1910 -#define PCIE_MASK_ENABLE_INTS 0x0f000000 +#define PCIE_INT_ALL_MASK GENMASK(31, 0)#define PCIE_CTRL_OFF 0x1a00#define PCIE_CTRL_X1_MODE 0x0001#define PCIE_CTRL_RC_MODE BIT(1)@@ -110,6 +111,10 @@ struct mvebu_pcie_port {Quoted 8 lines
drivers/pci/controller/pci-mvebu.c struct mvebu_pcie_port { · line 115
Quoted 5 lines
drivers/pci/controller/pci-mvebu.c +2
-0struct mvebu_pcie_window iowin;u32 saved_pcie_stat;struct resource regs; +struct irq_domain *intx_irq_domain; +struct irq_chip intx_irq_chip;Why is this structure per port? It really should be global. Printing the port number in the name isn't enough of a reason.
Quoted 2 lines
Because each port has its own independent set of INTA-INTD interrupts.
That doesn't warrant a copy of an
irq_chipstructure that contains the exact same callbacks, and only differs by *a string*. And the use of this string is only to end-up in/proc/interrupts, which is totally pointless.Quoted 123 lines
Quoted 41 lines
Quoted 41 lines
drivers/pci/controller/pci-mvebu.c +21
-5+raw_spinlock_t irq_lock; +int intx_irq; };static inline void mvebu_writel (struct mvebu_pcie_port *port,u32 val,u32 reg)@@ -235,7 +240,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, lnkcap, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, unmask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF );@@ -288,10 +293,30 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)/* Point PCIe unit MBUS decode windows to DRAM space. */ mvebu_pcie_setup_wins (port); -/* Enable interrupt lines A-D. */ - mask =mvebu_readl (port,PCIE_MASK_OFF ); - mask |=PCIE_MASK_ENABLE_INTS ; -mvebu_writel (port, mask,PCIE_MASK_OFF ); +/* Mask all interrupt sources. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (port->intx_irq <=0 ) { +/* +* When neither "summary" interrupt, nor "intx" interrupt was +* specified in DT then unmask all legacy INTx interrupts as in +* this case driver does not provide a way for masking and +* unmasking of individual legacy INTx interrupts. In this case +* all interrupts, including legacy INTx are reported via one +* shared GIC source and therefore kernel cannot distinguish +* which individual legacy INTx was triggered. These interrupts +* are shared, so it should not cause any issue. Just +* performance penalty as every PCIe interrupt handler needs to +* be called when some interrupt is triggered. Quoted 8 lines
drivers/pci/controller/pci-mvebu.c static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) · line 318
Quoted 5 lines
drivers/pci/controller/pci-mvebu.c +5
-0+*/ + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (0 ) |PCIE_INT_INTX (1 ) | +PCIE_INT_INTX (2 ) |PCIE_INT_INTX (3 ); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF );Maybe worth printing a warning here, so that the user knows they are on thin ice.
Ok. I can add it here. Anyway, this is default current state without this patch.
Quoted 69 lines
Quoted 53 lines
drivers/pci/controller/pci-mvebu.c +46
-0+ } } static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,@@ -924,6 +949,109 @@ static struct pci_ops mvebu_pcie_ops = { .write = mvebu_pcie_wr_conf, }; +static void mvebu_pcie_intx_irq_mask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask &= ~PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static void mvebu_pcie_intx_irq_unmask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static int mvebu_pcie_intx_irq_map (struct irq_domain *h, +unsigned int virq,irq_hw_number_t hwirq) +{ +struct mvebu_pcie_port *port = h->host_data; + +irq_set_status_flags (virq,IRQ_LEVEL ); +irq_set_chip_and_handler (virq, &port->intx_irq_chip, handle_level_irq); +irq_set_chip_data (virq, port); + +return 0 ; +} + +static const struct irq_domain_ops mvebu_pcie_intx_irq_domain_ops = { + .map = mvebu_pcie_intx_irq_map, + .xlate = irq_domain_xlate_onecell, +}; +drivers/pci/controller/pci-mvebu.c static struct pci_ops mvebu_pcie_ops = { · line 1006
Quoted 10 lines
drivers/pci/controller/pci-mvebu.c +10
-0+static int mvebu_pcie_init_irq_domain (struct mvebu_pcie_port *port) +{ +struct device *dev = &port->pcie->pdev->dev; +struct device_node *pcie_intc_node; + +raw_spin_lock_init (&port->irq_lock); + + port->intx_irq_chip.name =devm_kasprintf (dev,GFP_KERNEL , +"mvebu-%s-INTx" , + port->name);That's exactly what I really don't want to see. It prevents sharing of the
irq_chipstructure, and gets in the way of making it const in the future. Yes, I know that some drivers do that. I can't fix those, because/proc/interruptsis ABI. But I really don't want to see more of these.
Quoted 8 lines
Quoted 3 lines
Well, I do not understand why it should be shared and with who. HW has N independent IRQ chips for legacy interrupts. And each one will be specified in DT per HW layout / design.
If you have multiple ports, all the ports can share the
irq_chipstructure. Actually scratch that. They *MUST* share the structure. The only reason you're not sharing it is to be able to print this useless string in/proc/interrupts.
What is the point of sharing one irq chip if HW has N independent irq chips (for legacy interrupts)? I do not catch it yet. And I do not care here for /proc/interrupts, so also I have not caught what do you mean be last sentence with "the only reason".
And I still do not see how it could even work to have just one irq chip and one irq domain as each irq domain needs to know to which port it belongs, so it can mask/unmask interrupts from correct port. Also initialization of domain is taking DT node and for each port it is different.
So I'm somehow confused here...
The improvement in this patch is to be able to mask INTA interrupts on port 1 and let INTA interrupts unmasked on port 2 if there drivers are interested only for interrupts from device connected to port 2.
And if all interrupts are going to be shared (again) then it does not solve any problem.
Re: [PATCH 10/11] PCI: mvebu: Implement support for legacy INTx interrupts
Quoted 206 lines · Pali Rohár
Quoted 184 lines · On Thursday 06 January 2022 15:55:11 Marc Zyngier wrote:
Quoted 51 lines · Pali Rohár
Quoted 40 lines · On Thursday 06 January 2022 15:28:20 Marc Zyngier wrote:
Quoted 38 lines · Pali Rohár
This adds support for legacy INTx interrupts received from other PCIe devices and which are reported by a new INTx irq chip.
With this change, kernel can distinguish between INTA, INTB, INTC and INTD interrupts.
Note that for this support, device tree files has to be properly adjusted to provide "interrupts" or "interrupts-extended" property with intx interrupt source, "interrupt-names" property with "intx" string and also 'interrupt-controller' subnode must be defined.
If device tree files do not provide these nodes then driver would work as before.
Signed-off-by Pali Rohár <[email protected]>--- drivers/pci/controller/pci-mvebu.c | 182 +++++++++++++++++++++++++++-- 1 file changed, 174 insertions(+), 8 deletions(-)drivers/pci/controller/pci-mvebu.c +3
-2@@ -54,9 +54,10 @@ PCIE_CONF_ADDR_EN)#define PCIE_CONF_DATA_OFF 0x18fc#define PCIE_INT_CAUSE_OFF 0x1900 +#define PCIE_INT_UNMASK_OFF 0x1910 +#define PCIE_INT_INTX (i) BIT(24+i)#define PCIE_INT_PM_PME BIT(28) -#define PCIE_MASK_OFF 0x1910 -#define PCIE_MASK_ENABLE_INTS 0x0f000000 +#define PCIE_INT_ALL_MASK GENMASK(31, 0)#define PCIE_CTRL_OFF 0x1a00#define PCIE_CTRL_X1_MODE 0x0001#define PCIE_CTRL_RC_MODE BIT(1)@@ -110,6 +111,10 @@ struct mvebu_pcie_port {Quoted 8 lines
drivers/pci/controller/pci-mvebu.c struct mvebu_pcie_port { · line 115
Quoted 5 lines
drivers/pci/controller/pci-mvebu.c +2
-0struct mvebu_pcie_window iowin;u32 saved_pcie_stat;struct resource regs; +struct irq_domain *intx_irq_domain; +struct irq_chip intx_irq_chip;Why is this structure per port? It really should be global. Printing the port number in the name isn't enough of a reason.
Quoted 2 lines
Because each port has its own independent set of INTA-INTD interrupts.
That doesn't warrant a copy of an
irq_chipstructure that contains the exact same callbacks, and only differs by *a string*. And the use of this string is only to end-up in/proc/interrupts, which is totally pointless.Quoted 123 lines
Quoted 41 lines
Quoted 41 lines
drivers/pci/controller/pci-mvebu.c +21
-5+raw_spinlock_t irq_lock; +int intx_irq; };static inline void mvebu_writel (struct mvebu_pcie_port *port,u32 val,u32 reg)@@ -235,7 +240,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, lnkcap, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, unmask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF );@@ -288,10 +293,30 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)/* Point PCIe unit MBUS decode windows to DRAM space. */ mvebu_pcie_setup_wins (port); -/* Enable interrupt lines A-D. */ - mask =mvebu_readl (port,PCIE_MASK_OFF ); - mask |=PCIE_MASK_ENABLE_INTS ; -mvebu_writel (port, mask,PCIE_MASK_OFF ); +/* Mask all interrupt sources. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (port->intx_irq <=0 ) { +/* +* When neither "summary" interrupt, nor "intx" interrupt was +* specified in DT then unmask all legacy INTx interrupts as in +* this case driver does not provide a way for masking and +* unmasking of individual legacy INTx interrupts. In this case +* all interrupts, including legacy INTx are reported via one +* shared GIC source and therefore kernel cannot distinguish +* which individual legacy INTx was triggered. These interrupts +* are shared, so it should not cause any issue. Just +* performance penalty as every PCIe interrupt handler needs to +* be called when some interrupt is triggered. Quoted 8 lines
drivers/pci/controller/pci-mvebu.c static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) · line 318
Quoted 5 lines
drivers/pci/controller/pci-mvebu.c +5
-0+*/ + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (0 ) |PCIE_INT_INTX (1 ) | +PCIE_INT_INTX (2 ) |PCIE_INT_INTX (3 ); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF );Maybe worth printing a warning here, so that the user knows they are on thin ice.
Ok. I can add it here. Anyway, this is default current state without this patch.
Quoted 69 lines
Quoted 53 lines
drivers/pci/controller/pci-mvebu.c +46
-0+ } } static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,@@ -924,6 +949,109 @@ static struct pci_ops mvebu_pcie_ops = { .write = mvebu_pcie_wr_conf, }; +static void mvebu_pcie_intx_irq_mask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask &= ~PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static void mvebu_pcie_intx_irq_unmask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static int mvebu_pcie_intx_irq_map (struct irq_domain *h, +unsigned int virq,irq_hw_number_t hwirq) +{ +struct mvebu_pcie_port *port = h->host_data; + +irq_set_status_flags (virq,IRQ_LEVEL ); +irq_set_chip_and_handler (virq, &port->intx_irq_chip, handle_level_irq); +irq_set_chip_data (virq, port); + +return 0 ; +} + +static const struct irq_domain_ops mvebu_pcie_intx_irq_domain_ops = { + .map = mvebu_pcie_intx_irq_map, + .xlate = irq_domain_xlate_onecell, +}; +drivers/pci/controller/pci-mvebu.c static struct pci_ops mvebu_pcie_ops = { · line 1006
Quoted 10 lines
drivers/pci/controller/pci-mvebu.c +10
-0+static int mvebu_pcie_init_irq_domain (struct mvebu_pcie_port *port) +{ +struct device *dev = &port->pcie->pdev->dev; +struct device_node *pcie_intc_node; + +raw_spin_lock_init (&port->irq_lock); + + port->intx_irq_chip.name =devm_kasprintf (dev,GFP_KERNEL , +"mvebu-%s-INTx" , + port->name);That's exactly what I really don't want to see. It prevents sharing of the
irq_chipstructure, and gets in the way of making it const in the future. Yes, I know that some drivers do that. I can't fix those, because/proc/interruptsis ABI. But I really don't want to see more of these.Quoted 8 lines
Quoted 3 lines
Well, I do not understand why it should be shared and with who. HW has N independent IRQ chips for legacy interrupts. And each one will be specified in DT per HW layout / design.
If you have multiple ports, all the ports can share the
irq_chipstructure. Actually scratch that. They *MUST* share the structure. The only reason you're not sharing it is to be able to print this useless string in/proc/interrupts.What is the point of sharing one irq chip if HW has N independent irq chips (for legacy interrupts)? I do not catch it yet. And I do not care here for
/proc/interrupts, so also I have not caught what do you mean be last sentence with "the only reason".And I still do not see how it could even work to have just one irq chip and one irq domain as each irq domain needs to know to which port it belongs, so it can mask/unmask interrupts from correct port. Also initialization of domain is taking DT node and for each port it is different.
Quoted 8 lines
So I'm somehow confused here...
The improvement in this patch is to be able to mask INTA interrupts on port 1 and let INTA interrupts unmasked on port 2 if there drivers are interested only for interrupts from device connected to port 2.
And if all interrupts are going to be shared (again) then it does not solve any problem.
You are completely missing my point. I'm talking about data structures, you're talking about interrupts. You have this:
struct mvebu_pcie_port {
// Tons of stuff
struct irq_chip intx_chip;
};What I want you to do is:
struct mvebu_pcie_port {
// Tons of stuff
};static struct irq_chip intx_chip = {
.name = "INTx",
.irq_mask = mvebu_pcie_intx_irq_mask,
.irq_unmask = mvebu_pcie_intx_irq_unmask;
};That's it. No more, no less.
M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
Re: [PATCH 10/11] PCI: mvebu: Implement support for legacy INTx interrupts
Quoted 237 lines · On Thu, 06 Jan 2022 16:27:44 +0000 Marc Zyngier <[email protected]> wrote:
Quoted 206 lines · Pali Rohár
Quoted 184 lines · On Thursday 06 January 2022 15:55:11 Marc Zyngier wrote:
Quoted 51 lines · Pali Rohár
Quoted 40 lines · On Thursday 06 January 2022 15:28:20 Marc Zyngier wrote:
Quoted 38 lines · Pali Rohár
This adds support for legacy INTx interrupts received from other PCIe devices and which are reported by a new INTx irq chip.
With this change, kernel can distinguish between INTA, INTB, INTC and INTD interrupts.
Note that for this support, device tree files has to be properly adjusted to provide "interrupts" or "interrupts-extended" property with intx interrupt source, "interrupt-names" property with "intx" string and also 'interrupt-controller' subnode must be defined.
If device tree files do not provide these nodes then driver would work as before.
Signed-off-by Pali Rohár <[email protected]>--- drivers/pci/controller/pci-mvebu.c | 182 +++++++++++++++++++++++++++-- 1 file changed, 174 insertions(+), 8 deletions(-)drivers/pci/controller/pci-mvebu.c +3
-2@@ -54,9 +54,10 @@ PCIE_CONF_ADDR_EN)#define PCIE_CONF_DATA_OFF 0x18fc#define PCIE_INT_CAUSE_OFF 0x1900 +#define PCIE_INT_UNMASK_OFF 0x1910 +#define PCIE_INT_INTX (i) BIT(24+i)#define PCIE_INT_PM_PME BIT(28) -#define PCIE_MASK_OFF 0x1910 -#define PCIE_MASK_ENABLE_INTS 0x0f000000 +#define PCIE_INT_ALL_MASK GENMASK(31, 0)#define PCIE_CTRL_OFF 0x1a00#define PCIE_CTRL_X1_MODE 0x0001#define PCIE_CTRL_RC_MODE BIT(1)@@ -110,6 +111,10 @@ struct mvebu_pcie_port {Quoted 8 lines
drivers/pci/controller/pci-mvebu.c struct mvebu_pcie_port { · line 115
Quoted 5 lines
drivers/pci/controller/pci-mvebu.c +2
-0struct mvebu_pcie_window iowin;u32 saved_pcie_stat;struct resource regs; +struct irq_domain *intx_irq_domain; +struct irq_chip intx_irq_chip;Why is this structure per port? It really should be global. Printing the port number in the name isn't enough of a reason.
Quoted 2 lines
Because each port has its own independent set of INTA-INTD interrupts.
That doesn't warrant a copy of an
irq_chipstructure that contains the exact same callbacks, and only differs by *a string*. And the use of this string is only to end-up in/proc/interrupts, which is totally pointless.Quoted 123 lines
Quoted 41 lines
Quoted 41 lines
drivers/pci/controller/pci-mvebu.c +21
-5+raw_spinlock_t irq_lock; +int intx_irq; };static inline void mvebu_writel (struct mvebu_pcie_port *port,u32 val,u32 reg)@@ -235,7 +240,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, lnkcap, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, unmask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF );@@ -288,10 +293,30 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)/* Point PCIe unit MBUS decode windows to DRAM space. */ mvebu_pcie_setup_wins (port); -/* Enable interrupt lines A-D. */ - mask =mvebu_readl (port,PCIE_MASK_OFF ); - mask |=PCIE_MASK_ENABLE_INTS ; -mvebu_writel (port, mask,PCIE_MASK_OFF ); +/* Mask all interrupt sources. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (port->intx_irq <=0 ) { +/* +* When neither "summary" interrupt, nor "intx" interrupt was +* specified in DT then unmask all legacy INTx interrupts as in +* this case driver does not provide a way for masking and +* unmasking of individual legacy INTx interrupts. In this case +* all interrupts, including legacy INTx are reported via one +* shared GIC source and therefore kernel cannot distinguish +* which individual legacy INTx was triggered. These interrupts +* are shared, so it should not cause any issue. Just +* performance penalty as every PCIe interrupt handler needs to +* be called when some interrupt is triggered. Quoted 8 lines
drivers/pci/controller/pci-mvebu.c static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) · line 318
Quoted 5 lines
drivers/pci/controller/pci-mvebu.c +5
-0+*/ + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (0 ) |PCIE_INT_INTX (1 ) | +PCIE_INT_INTX (2 ) |PCIE_INT_INTX (3 ); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF );Maybe worth printing a warning here, so that the user knows they are on thin ice.
Ok. I can add it here. Anyway, this is default current state without this patch.
Quoted 69 lines
Quoted 53 lines
drivers/pci/controller/pci-mvebu.c +46
-0+ } } static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,@@ -924,6 +949,109 @@ static struct pci_ops mvebu_pcie_ops = { .write = mvebu_pcie_wr_conf, }; +static void mvebu_pcie_intx_irq_mask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask &= ~PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static void mvebu_pcie_intx_irq_unmask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static int mvebu_pcie_intx_irq_map (struct irq_domain *h, +unsigned int virq,irq_hw_number_t hwirq) +{ +struct mvebu_pcie_port *port = h->host_data; + +irq_set_status_flags (virq,IRQ_LEVEL ); +irq_set_chip_and_handler (virq, &port->intx_irq_chip, handle_level_irq); +irq_set_chip_data (virq, port); + +return 0 ; +} + +static const struct irq_domain_ops mvebu_pcie_intx_irq_domain_ops = { + .map = mvebu_pcie_intx_irq_map, + .xlate = irq_domain_xlate_onecell, +}; +drivers/pci/controller/pci-mvebu.c static struct pci_ops mvebu_pcie_ops = { · line 1006
Quoted 10 lines
drivers/pci/controller/pci-mvebu.c +10
-0+static int mvebu_pcie_init_irq_domain (struct mvebu_pcie_port *port) +{ +struct device *dev = &port->pcie->pdev->dev; +struct device_node *pcie_intc_node; + +raw_spin_lock_init (&port->irq_lock); + + port->intx_irq_chip.name =devm_kasprintf (dev,GFP_KERNEL , +"mvebu-%s-INTx" , + port->name);That's exactly what I really don't want to see. It prevents sharing of the
irq_chipstructure, and gets in the way of making it const in the future. Yes, I know that some drivers do that. I can't fix those, because/proc/interruptsis ABI. But I really don't want to see more of these.Quoted 8 lines
Quoted 3 lines
Well, I do not understand why it should be shared and with who. HW has N independent IRQ chips for legacy interrupts. And each one will be specified in DT per HW layout / design.
If you have multiple ports, all the ports can share the
irq_chipstructure. Actually scratch that. They *MUST* share the structure. The only reason you're not sharing it is to be able to print this useless string in/proc/interrupts.What is the point of sharing one irq chip if HW has N independent irq chips (for legacy interrupts)? I do not catch it yet. And I do not care here for
/proc/interrupts, so also I have not caught what do you mean be last sentence with "the only reason".And I still do not see how it could even work to have just one irq chip and one irq domain as each irq domain needs to know to which port it belongs, so it can mask/unmask interrupts from correct port. Also initialization of domain is taking DT node and for each port it is different.
Quoted 8 lines
So I'm somehow confused here...
The improvement in this patch is to be able to mask INTA interrupts on port 1 and let INTA interrupts unmasked on port 2 if there drivers are interested only for interrupts from device connected to port 2.
And if all interrupts are going to be shared (again) then it does not solve any problem.
You are completely missing my point. I'm talking about data structures, you're talking about interrupts. You have this:
struct mvebu_pcie_port { // Tons of stuff struct irq_chip intx_chip; };What I want you to do is:
struct mvebu_pcie_port { // Tons of stuff };static struct irq_chip intx_chip = { .name = "INTx", .irq_mask = mvebu_pcie_intx_irq_mask, .irq_unmask = mvebu_pcie_intx_irq_unmask; };
Quoted 4 lines
That's it. No more, no less.
M.
Hmm, but struct irq_chip contains a dynamic member,
struct device *parent_device;
Isn't that used? Or are you planning to kill it?
Marek
Re: [PATCH 10/11] PCI: mvebu: Implement support for legacy INTx interrupts
Quoted 23 lines · Marek Behún
Quoted 20 lines · On Thu, 06 Jan 2022 16:27:44 +0000 Marc Zyngier <[email protected]> wrote:
You are completely missing my point. I'm talking about data structures, you're talking about interrupts. You have this:
struct mvebu_pcie_port { // Tons of stuff struct irq_chip intx_chip; };What I want you to do is:
struct mvebu_pcie_port { // Tons of stuff };static struct irq_chip intx_chip = { .name = "INTx", .irq_mask = mvebu_pcie_intx_irq_mask, .irq_unmask = mvebu_pcie_intx_irq_unmask; };
Quoted 8 lines
Quoted 4 lines
That's it. No more, no less.
M.Hmm, but struct
irq_chipcontains a dynamic member,
struct device*parent_device;
Isn't that used? Or are you planning to kill it?
Indeed, and I am definitely planning to kill it. This is the wrong place for this stuff, and I want it gone. There are thankfully very few users of this misfeature.
M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
Pali Rohár
maintainerRe: [PATCH 10/11] PCI: mvebu: Implement support for legacy INTx interrupts
Quoted 30 lines · On Thursday 06 January 2022 17:31:36 Marc Zyngier wrote:
Quoted 28 lines · Marek Behún
Quoted 24 lines · On Thu, 06 Jan 2022 16:27:44 +0000 Marc Zyngier <[email protected]> wrote:
You are completely missing my point. I'm talking about data structures, you're talking about interrupts. You have this:
struct mvebu_pcie_port { // Tons of stuff struct irq_chip intx_chip; };What I want you to do is:
struct mvebu_pcie_port { // Tons of stuff };static struct irq_chip intx_chip = { .name = "INTx", .irq_mask = mvebu_pcie_intx_irq_mask, .irq_unmask = mvebu_pcie_intx_irq_unmask; };That's it. No more, no less.
M.
Quoted 7 lines
Quoted 3 lines
Hmm, but struct
irq_chipcontains a dynamic member,
struct device*parent_device;
Isn't that used? Or are you planning to kill it?Indeed, and I am definitely planning to kill it. This is the wrong place for this stuff, and I want it gone. There are thankfully very few users of this misfeature.
Ok, so what about this change?
drivers/pci/controller/pci-mvebu.c +177 -8
@@ -54,9 +54,10 @@ PCIE_CONF_ADDR_EN)#define PCIE_CONF_DATA_OFF 0x18fc#define PCIE_INT_CAUSE_OFF 0x1900 +#define PCIE_INT_UNMASK_OFF 0x1910 +#define PCIE_INT_INTX (i) BIT(24+i)#define PCIE_INT_PM_PME BIT(28) -#define PCIE_MASK_OFF 0x1910 -#define PCIE_MASK_ENABLE_INTS 0x0f000000 +#define PCIE_INT_ALL_MASK GENMASK(31, 0)#define PCIE_CTRL_OFF 0x1a00#define PCIE_CTRL_X1_MODE 0x0001#define PCIE_CTRL_RC_MODE BIT(1)
@@ -110,6 +111,9 @@ struct mvebu_pcie_port {struct mvebu_pcie_window iowin;u32 saved_pcie_stat;struct resource regs; +struct irq_domain *intx_irq_domain; +raw_spinlock_t irq_lock; +int intx_irq; };static inline void mvebu_writel (struct mvebu_pcie_port *port,u32 val,u32 reg)
@@ -235,7 +239,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, lnkcap, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, unmask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF );
@@ -288,10 +292,30 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)/* Point PCIe unit MBUS decode windows to DRAM space. */ mvebu_pcie_setup_wins (port); -/* Enable interrupt lines A-D. */ - mask =mvebu_readl (port,PCIE_MASK_OFF ); - mask |=PCIE_MASK_ENABLE_INTS ; -mvebu_writel (port, mask,PCIE_MASK_OFF ); +/* Mask all interrupt sources. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (port->intx_irq <=0 ) { +/* +* When neither "summary" interrupt, nor "intx" interrupt was +* specified in DT then unmask all legacy INTx interrupts as in +* this case driver does not provide a way for masking and +* unmasking of individual legacy INTx interrupts. In this case +* all interrupts, including legacy INTx are reported via one +* shared GIC source and therefore kernel cannot distinguish +* which individual legacy INTx was triggered. These interrupts +* are shared, so it should not cause any issue. Just +* performance penalty as every PCIe interrupt handler needs to +* be called when some interrupt is triggered. +*/ + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (0 ) |PCIE_INT_INTX (1 ) | +PCIE_INT_INTX (2 ) |PCIE_INT_INTX (3 ); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); + } } static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,
@@ -924,6 +948,108 @@ static struct pci_ops mvebu_pcie_ops = { .write = mvebu_pcie_wr_conf, }; +static void mvebu_pcie_intx_irq_mask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask &= ~PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static void mvebu_pcie_intx_irq_unmask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static struct irq_chip intx_irq_chip = { + .name ="mvebu-INTx" , + .irq_mask = mvebu_pcie_intx_irq_mask, + .irq_unmask = mvebu_pcie_intx_irq_unmask, +}; + +static int mvebu_pcie_intx_irq_map (struct irq_domain *h, +unsigned int virq,irq_hw_number_t hwirq) +{ +struct mvebu_pcie_port *port = h->host_data; + +irq_set_status_flags (virq,IRQ_LEVEL ); +irq_set_chip_and_handler (virq, &intx_irq_chip, handle_level_irq); +irq_set_chip_data (virq, port); + +return 0 ; +} + +static const struct irq_domain_ops mvebu_pcie_intx_irq_domain_ops = { + .map = mvebu_pcie_intx_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int mvebu_pcie_init_irq_domain (struct mvebu_pcie_port *port) +{ +struct device *dev = &port->pcie->pdev->dev; +struct device_node *pcie_intc_node; + +raw_spin_lock_init (&port->irq_lock); + + pcie_intc_node =of_get_next_child (port->dn,NULL ); +if (!pcie_intc_node) { +dev_err (dev,"No PCIe Intc node found for %s\n" , port->name); +return -ENODEV ; + } + + port->intx_irq_domain =irq_domain_add_linear (pcie_intc_node,PCI_NUM_INTX , + &mvebu_pcie_intx_irq_domain_ops, + port); +of_node_put (pcie_intc_node); +if (!port->intx_irq_domain) { +dev_err (dev,"Failed to get INTx IRQ domain for %s\n" , port->name); +return -ENOMEM ; + } + +return 0 ; +} + +static void mvebu_pcie_irq_handler (struct irq_desc *desc) +{ +struct mvebu_pcie_port *port =irq_desc_get_handler_data (desc); +struct irq_chip *chip =irq_desc_get_chip (desc); +struct device *dev = &port->pcie->pdev->dev; +u32 cause, unmask, status; +int i; + +chained_irq_enter (chip, desc); + + cause =mvebu_readl (port,PCIE_INT_CAUSE_OFF ); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + status = cause & unmask; + +/* Process legacy INTx interrupts */ +for (i =0 ; i <PCI_NUM_INTX ; i++) { +if (!(status &PCIE_INT_INTX (i))) +continue ; + +if (generic_handle_domain_irq (port->intx_irq_domain, i) == -EINVAL ) +dev_err_ratelimited (dev,"unexpected INT%c IRQ\n" , (char )i+'A' ); + } + +chained_irq_exit (chip, desc); +} +static int mvebu_pcie_map_irq (const struct pci_dev *dev,u8 slot,u8 pin) {/* Interrupt support on mvebu emulated bridges is not implemented yet */
@@ -1121,6 +1247,21 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, port->io_attr =-1 ; } +/* +* Old DT bindings do not contain "intx" interrupt +* so do not fail probing driver when interrupt does not exist. +*/ + port->intx_irq =of_irq_get_byname (child,"intx" ); +if (port->intx_irq == -EPROBE_DEFER ) { + ret = port->intx_irq; + goto err; + } +if (port->intx_irq <=0 ) { +dev_warn (dev,"%s: legacy INTx interrupts cannot be masked individually, " +"%pOF does not contain intx interrupt\n" , + port->name, child); + } + reset_gpio =of_get_named_gpio_flags (child,"reset-gpios" ,0 , &flags);if (reset_gpio == -EPROBE_DEFER ) { ret = reset_gpio;
@@ -1317,6 +1458,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)for (i =0 ; i < pcie->nports; i++) {struct mvebu_pcie_port *port = &pcie->ports[i]; +int irq = port->intx_irq; child = port->dn;if (!child)
@@ -1344,6 +1486,22 @@ static int mvebu_pcie_probe(struct platform_device *pdev)continue ; } +if (irq >0 ) { + ret =mvebu_pcie_init_irq_domain (port); +if (ret) { +dev_err (dev,"%s: cannot init irq domain\n" , + port->name); +pci_bridge_emul_cleanup (&port->bridge); +devm_iounmap (dev, port->base); + port->base =NULL ; +mvebu_pcie_powerdown (port); +continue ; + } +irq_set_chained_handler_and_data (irq, + mvebu_pcie_irq_handler, + port); + } +/* * PCIe topology exported by mvebu hw is quite complicated. In * reality has something like N fully independent host bridges
@@ -1448,6 +1606,7 @@ static int mvebu_pcie_remove(struct platform_device *pdev)for (i =0 ; i < pcie->nports; i++) {struct mvebu_pcie_port *port = &pcie->ports[i]; +int irq = port->intx_irq;if (!port->base)continue ;
@@ -1458,7 +1617,17 @@ static int mvebu_pcie_remove(struct platform_device *pdev)mvebu_writel (port, cmd,PCIE_CMD_OFF );/* Mask all interrupt sources. */ -mvebu_writel (port,0 ,PCIE_MASK_OFF ); +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +/* Remove IRQ domains. */ +if (port->intx_irq_domain) +irq_domain_remove (port->intx_irq_domain); + +if (irq >0 ) +irq_set_chained_handler_and_data (irq,NULL ,NULL );/* Free config space for emulated root bridge. */ pci_bridge_emul_cleanup (&port->bridge);
Signature · 1 line
2.20.1
Re: [PATCH 10/11] PCI: mvebu: Implement support for legacy INTx interrupts
Quoted 48 lines · Pali Rohár
Quoted 30 lines · On Thursday 06 January 2022 17:31:36 Marc Zyngier wrote:
Quoted 28 lines · Marek Behún
Quoted 24 lines · On Thu, 06 Jan 2022 16:27:44 +0000 Marc Zyngier <[email protected]> wrote:
You are completely missing my point. I'm talking about data structures, you're talking about interrupts. You have this:
struct mvebu_pcie_port { // Tons of stuff struct irq_chip intx_chip; };What I want you to do is:
struct mvebu_pcie_port { // Tons of stuff };static struct irq_chip intx_chip = { .name = "INTx", .irq_mask = mvebu_pcie_intx_irq_mask, .irq_unmask = mvebu_pcie_intx_irq_unmask; };That's it. No more, no less.
M.Quoted 7 lines
Quoted 3 lines
Hmm, but struct
irq_chipcontains a dynamic member,
struct device*parent_device;
Isn't that used? Or are you planning to kill it?Indeed, and I am definitely planning to kill it. This is the wrong place for this stuff, and I want it gone. There are thankfully very few users of this misfeature.
Ok, so what about this change?
drivers/pci/controller/pci-mvebu.c +1
-1@@ -1458,7 +1617,17 @@ static int mvebu_pcie_remove(struct platform_device *pdev)mvebu_writel (port, cmd,PCIE_CMD_OFF );/* Mask all interrupt sources. */ -mvebu_writel (port,0 ,PCIE_MASK_OFF ); +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF );
drivers/pci/controller/pci-mvebu.c static int mvebu_pcie_remove(struct platform_device *pdev) · line 1630
Quoted 10 lines
drivers/pci/controller/pci-mvebu.c +10
-0+ +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +/* Remove IRQ domains. */ +if (port->intx_irq_domain) +irq_domain_remove (port->intx_irq_domain); + +if (irq >0 ) +irq_set_chained_handler_and_data (irq,NULL ,NULL );
You really want this to be done *before* you remove the domain, as there still could be interrupts in flight at this point.
M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
Re: [PATCH 04/11] dt-bindings: PCI: mvebu: Add num-lanes property
In reply to Pali RohárQuoted 10 lines · Pali Rohár
Controller driver needs to correctly configure PCIe link if it contains 1 or 4 SerDes PCIe lanes. Therefore add a new 'num-lanes' DT property for mvebu PCIe controller. Property 'num-lanes' seems to be de-facto standard way how number of lanes is specified in other PCIe controllers.
Signed-off-by Pali Rohár <[email protected]>--- Documentation/devicetree/bindings/pci/mvebu-pci.txt | 11 +++++++++++ 1 file changed, 11 insertions(+)
Re: [PATCH 09/11] dt-bindings: PCI: mvebu: Update information about intx interrupts
In reply to Pali RohárQuoted 5 lines · Pali Rohár
Signed-off-by Pali Rohár <[email protected]>--- Documentation/devicetree/bindings/pci/mvebu-pci.txt | 5 +++++ 1 file changed, 5 insertions(+)
Pali Rohár
maintainerRe: [PATCH 08/11] PCI: mvebu: Use child_ops API
In reply to Rob HerringQuoted 11 lines · On Wednesday 05 January 2022 09:41:51 Rob Herring wrote:
Quoted 7 lines · Pali Rohár
Split struct
pci_opsbetween ops andchild_ops. Member ops is used for accessing PCIe Root Ports viapci-bridge-emul.cdriver andchild_opsfor accessing real PCIe cards.There is no need to mix these two struct
pci_opsinto one as PCI core code already provides separate callbacks viabridge->opsandbridge->child_ops.I had similar patches including mvebu that I never got around to sending out. I pushed the branch out now at least[1].
Are you going to finish your patch series and send it? Because if yes, I can drop this my patch in v3 and let all ->child_ops conversion for you.
Re: [PATCH 08/11] PCI: mvebu: Use child_ops API
Quoted 11 lines · Pali Rohár
Quoted 9 lines · On Wednesday 05 January 2022 09:41:51 Rob Herring wrote:
Quoted 7 lines · Pali Rohár
Split struct
pci_opsbetween ops andchild_ops. Member ops is used for accessing PCIe Root Ports viapci-bridge-emul.cdriver andchild_opsfor accessing real PCIe cards.There is no need to mix these two struct
pci_opsinto one as PCI core code already provides separate callbacks viabridge->opsandbridge->child_ops.
Quoted 6 lines
Quoted 2 lines
I had similar patches including mvebu that I never got around to sending out. I pushed the branch out now at least[1].
Are you going to finish your patch series and send it? Because if yes, I can drop this my patch in v3 and let all ->child_ops conversion for you.
Not any time soon.
Rob
Pali Rohár
maintainerv2 cover PCI: mvebu: subsystem ids, AER and INTx
In reply to Pali RohárThis patch series extends pci-bridge-emul.c driver to emulate PCI Subsystem Vendor ID capability and PCIe extended capabilities. And then implement in pci-mvebu.c driver support for PCI Subsystem Vendor IDs, PCIe AER registers, support for legacy INTx interrupts, configuration for X1/X4 mode and usage of new PCI child_ops API.
This patch series depends on other pci-mvebu and pci-bridge-emul patches from:
https://lore.kernel.org/linux-pci/[email protected]/
Changes in v2:
* use static structures for INTx interrupts
* remove INTx domain after unregistering INTx handler
Pali Rohár (9):
PCI: pci-bridge-emul: Add support for PCI Bridge Subsystem Vendor ID
capability
dt-bindings: PCI: mvebu: Add num-lanes property
PCI: mvebu: Correctly configure x1/x4 mode
PCI: mvebu: Add support for PCI Bridge Subsystem Vendor ID on emulated
bridge
PCI: mvebu: Add support for Advanced Error Reporting registers on
emulated bridge
PCI: mvebu: Use child_ops API
dt-bindings: PCI: mvebu: Update information about intx interrupts
PCI: mvebu: Implement support for legacy INTx interrupts
ARM: dts: armada-385.dtsi: Add definitions for PCIe legacy INTx
interrupts
Russell King (2):
PCI: pci-bridge-emul: Re-arrange register tests
PCI: pci-bridge-emul: Add support for PCIe extended capabilities
.../devicetree/bindings/pci/mvebu-pci.txt | 16 + arch/arm/boot/dts/armada-385.dtsi | 52 ++- drivers/pci/controller/pci-mvebu.c | 355 +++++++++++++++--- drivers/pci/pci-bridge-emul.c | 167 +++++--- drivers/pci/pci-bridge-emul.h | 17 + 5 files changed, 497 insertions(+), 110 deletions(-)
Signature · 1 line
2.20.1
Pali Rohár
maintainerPATCH v2 4/11 dt-bindings: PCI: mvebu: Add num-lanes property
Controller driver needs to correctly configure PCIe link if it contains 1 or 4 SerDes PCIe lanes. Therefore add a new 'num-lanes' DT property for mvebu PCIe controller. Property 'num-lanes' seems to be de-facto standard way how number of lanes is specified in other PCIe controllers.
--- Documentation/devicetree/bindings/pci/mvebu-pci.txt | 11 +++++++++++ 1 file changed, 11 insertions(+)
Documentation/devicetree/bindings/pci/mvebu-pci.txt +11 -0
@@ -77,6 +77,7 @@ and the following optional properties: - marvell,pcie-lane: the physical PCIe lane number, for ports having multiple lanes. If this property is not found, we assume that the value is 0. +- num-lanes: number of SerDes PCIe lanes for this link (1 or 4) - reset-gpios: optional GPIO to PERST# - reset-delay-us: delay in us to wait after reset de-assertion, if not specified will default to 100ms, as required by the PCIe specification.
@@ -141,6 +142,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 58>; marvell,pcie-port = <0>; marvell,pcie-lane = <0>; + num-lanes = <1>; /* low-active PERST# reset on GPIO 25 */ reset-gpios = <&gpio0 25 1>; /* wait 20ms for device settle after reset deassertion */
@@ -161,6 +163,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 59>; marvell,pcie-port = <0>; marvell,pcie-lane = <1>; + num-lanes = <1>; clocks = <&gateclk 6>; };
@@ -177,6 +180,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 60>; marvell,pcie-port = <0>; marvell,pcie-lane = <2>; + num-lanes = <1>; clocks = <&gateclk 7>; };
@@ -193,6 +197,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 61>; marvell,pcie-port = <0>; marvell,pcie-lane = <3>; + num-lanes = <1>; clocks = <&gateclk 8>; };
@@ -209,6 +214,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 62>; marvell,pcie-port = <1>; marvell,pcie-lane = <0>; + num-lanes = <1>; clocks = <&gateclk 9>; };
@@ -225,6 +231,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 63>; marvell,pcie-port = <1>; marvell,pcie-lane = <1>; + num-lanes = <1>; clocks = <&gateclk 10>; };
@@ -241,6 +248,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 64>; marvell,pcie-port = <1>; marvell,pcie-lane = <2>; + num-lanes = <1>; clocks = <&gateclk 11>; };
@@ -257,6 +265,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 65>; marvell,pcie-port = <1>; marvell,pcie-lane = <3>; + num-lanes = <1>; clocks = <&gateclk 12>; };
@@ -273,6 +282,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 99>; marvell,pcie-port = <2>; marvell,pcie-lane = <0>; + num-lanes = <1>; clocks = <&gateclk 26>; };
@@ -289,6 +299,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 103>; marvell,pcie-port = <3>; marvell,pcie-lane = <0>; + num-lanes = <1>; clocks = <&gateclk 27>; }; };
Signature · 1 line
2.20.1
Pali Rohár
maintainerPATCH v2 1/11 PCI: pci-bridge-emul: Re-arrange register tests
In reply to Pali RohárFrom: Russell King <[email protected]>
Re-arrange the tests for which sets of registers are being accessed so that it is easier to add further regions later. No functional change.
--- drivers/pci/pci-bridge-emul.c | 61 ++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 30 deletions(-)
drivers/pci/pci-bridge-emul.c +31 -30
@@ -418,25 +418,25 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,__le32 *cfgspace;const struct pci_bridge_reg_behavior *behavior; -if (bridge->has_pcie && reg >=PCI_CAP_PCIE_END ) { - *value =0 ; -return PCIBIOS_SUCCESSFUL ; - } - -if (!bridge->has_pcie && reg >=PCI_BRIDGE_CONF_END ) { +if (reg <PCI_BRIDGE_CONF_END ) { +/* Emulated PCI space */ + read_op = bridge->ops->read_base; + cfgspace = (__le32 *) &bridge->conf; + behavior = bridge->pci_regs_behavior; + }else if (!bridge->has_pcie) { +/* PCIe space is not implemented, and no PCI capabilities */ *value =0 ;return PCIBIOS_SUCCESSFUL ; - } - -if (bridge->has_pcie && reg >=PCI_CAP_PCIE_START ) { + }else if (reg <PCI_CAP_PCIE_END ) { +/* Our emulated PCIe capability */ reg -=PCI_CAP_PCIE_START ; read_op = bridge->ops->read_pcie; cfgspace = (__le32 *) &bridge->pcie_conf; behavior = bridge->pcie_cap_regs_behavior; }else { - read_op = bridge->ops->read_base; - cfgspace = (__le32 *) &bridge->conf; - behavior = bridge->pci_regs_behavior; +/* Beyond our PCIe space */ + *value =0 ; +return PCIBIOS_SUCCESSFUL ; }if (read_op)
@@ -480,11 +480,27 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,__le32 *cfgspace;const struct pci_bridge_reg_behavior *behavior; -if (bridge->has_pcie && reg >=PCI_CAP_PCIE_END ) -return PCIBIOS_SUCCESSFUL ; + ret =pci_bridge_emul_conf_read (bridge, reg,4 , &old); +if (ret !=PCIBIOS_SUCCESSFUL ) +return ret; -if (!bridge->has_pcie && reg >=PCI_BRIDGE_CONF_END ) +if (reg <PCI_BRIDGE_CONF_END ) { +/* Emulated PCI space */ + write_op = bridge->ops->write_base; + cfgspace = (__le32 *) &bridge->conf; + behavior = bridge->pci_regs_behavior; + }else if (!bridge->has_pcie) { +/* PCIe space is not implemented, and no PCI capabilities */ return PCIBIOS_SUCCESSFUL ; + }else if (reg <PCI_CAP_PCIE_END ) { +/* Our emulated PCIe capability */ + reg -=PCI_CAP_PCIE_START ; + write_op = bridge->ops->write_pcie; + cfgspace = (__le32 *) &bridge->pcie_conf; + behavior = bridge->pcie_cap_regs_behavior; + }else { +return PCIBIOS_SUCCESSFUL ; + } shift = (where &0x3 ) *8 ;
@@ -497,21 +513,6 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,else return PCIBIOS_BAD_REGISTER_NUMBER ; - ret =pci_bridge_emul_conf_read (bridge, reg,4 , &old); -if (ret !=PCIBIOS_SUCCESSFUL ) -return ret; - -if (bridge->has_pcie && reg >=PCI_CAP_PCIE_START ) { - reg -=PCI_CAP_PCIE_START ; - write_op = bridge->ops->write_pcie; - cfgspace = (__le32 *) &bridge->pcie_conf; - behavior = bridge->pcie_cap_regs_behavior; - }else { - write_op = bridge->ops->write_base; - cfgspace = (__le32 *) &bridge->conf; - behavior = bridge->pci_regs_behavior; - } -/* Keep all bits, except the RW bits */ new = old & (~mask | ~behavior[reg /4 ].rw);
Signature · 1 line
2.20.1
Pali Rohár
maintainerPATCH v2 3/11 PCI: pci-bridge-emul: Add support for PCI Bridge Subsystem Vendor ID capability
In reply to Pali RohárThis is read-only capability in PCI config space. Put it between base PCI capability and base PCI Express capability.
Driver just have to specify subsystem_vendor_id and subsystem_id fields in emulated bridge structure and pci-bridge-emul takes care of correctly compose PCI Bridge Subsystem Vendor ID capability.
--- drivers/pci/pci-bridge-emul.c | 69 +++++++++++++++++++++++++---------- drivers/pci/pci-bridge-emul.h | 2 + 2 files changed, 51 insertions(+), 20 deletions(-)
drivers/pci/pci-bridge-emul.c +49 -20
@@ -21,8 +21,11 @@#include "pci-bridge-emul.h" #define PCI_BRIDGE_CONF_END PCI_STD_HEADER_SIZEOF +#define PCI_CAP_SSID_SIZEOF (PCI_SSVID_DEVICE_ID + 2) +#define PCI_CAP_SSID_START PCI_BRIDGE_CONF_END +#define PCI_CAP_SSID_END (PCI_CAP_SSID_START + PCI_CAP_SSID_SIZEOF)#define PCI_CAP_PCIE_SIZEOF (PCI_EXP_SLTSTA2 + 2) -#define PCI_CAP_PCIE_START PCI_BRIDGE_CONF_END +#define PCI_CAP_PCIE_START PCI_CAP_SSID_END#define PCI_CAP_PCIE_END (PCI_CAP_PCIE_START + PCI_CAP_PCIE_SIZEOF)/**
@@ -315,6 +318,25 @@ struct pci_bridge_reg_behavior pcie_cap_regs_behavior[PCI_CAP_PCIE_SIZEOF / 4] = }, }; +static pci_bridge_emul_read_status_t +pci_bridge_emul_read_ssid (struct pci_bridge_emul *bridge,int reg,u32 *value) +{ +switch (reg) { +case PCI_CAP_LIST_ID : + *value =PCI_CAP_ID_SSVID | + (bridge->has_pcie ? (PCI_CAP_PCIE_START <<8 ) :0 ); +return PCI_BRIDGE_EMUL_HANDLED ; + +case PCI_SSVID_VENDOR_ID : + *value = bridge->subsystem_vendor_id | + (bridge->subsystem_id <<16 ); +return PCI_BRIDGE_EMUL_HANDLED ; + +default : +return PCI_BRIDGE_EMUL_NOT_HANDLED ; + } +} +/* * Initialize a pci_bridge_emul structure to represent a fake PCI * bridge configuration space. The caller needs to have initialized
@@ -337,9 +359,17 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge,if (!bridge->pci_regs_behavior)return -ENOMEM ; -if (bridge->has_pcie) { +if (bridge->subsystem_vendor_id) + bridge->conf.capabilities_pointer =PCI_CAP_SSID_START ; +else if (bridge->has_pcie) bridge->conf.capabilities_pointer =PCI_CAP_PCIE_START ; +else + bridge->conf.capabilities_pointer =0 ; + +if (bridge->conf.capabilities_pointer) bridge->conf.status |=cpu_to_le16 (PCI_STATUS_CAP_LIST ); + +if (bridge->has_pcie) { bridge->pcie_conf.cap_id =PCI_CAP_ID_EXP ; bridge->pcie_conf.cap |=cpu_to_le16 (PCI_EXP_TYPE_ROOT_PORT <<4 ); bridge->pcie_cap_regs_behavior =
@@ -423,26 +453,28 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, read_op = bridge->ops->read_base; cfgspace = (__le32 *) &bridge->conf; behavior = bridge->pci_regs_behavior; - }else if (!bridge->has_pcie) { -/* PCIe space is not implemented, and no PCI capabilities */ - *value =0 ; -return PCIBIOS_SUCCESSFUL ; - }else if (reg <PCI_CAP_PCIE_END ) { + }else if (reg >=PCI_CAP_SSID_START && reg <PCI_CAP_SSID_END && bridge->subsystem_vendor_id) { +/* Emulated PCI Bridge Subsystem Vendor ID capability */ + reg -=PCI_CAP_SSID_START ; + read_op = pci_bridge_emul_read_ssid; + cfgspace =NULL ; + behavior =NULL ; + }else if (reg >=PCI_CAP_PCIE_START && reg <PCI_CAP_PCIE_END && bridge->has_pcie) {/* Our emulated PCIe capability */ reg -=PCI_CAP_PCIE_START ; read_op = bridge->ops->read_pcie; cfgspace = (__le32 *) &bridge->pcie_conf; behavior = bridge->pcie_cap_regs_behavior; - }else if (reg <PCI_CFG_SPACE_SIZE ) { -/* Rest of PCI space not implemented */ - *value =0 ; -return PCIBIOS_SUCCESSFUL ; - }else { + }else if (reg >=PCI_CFG_SPACE_SIZE && bridge->has_pcie) {/* PCIe extended capability space */ reg -=PCI_CFG_SPACE_SIZE ; read_op = bridge->ops->read_ext; cfgspace =NULL ; behavior =NULL ; + }else { +/* Not implemented */ + *value =0 ; +return PCIBIOS_SUCCESSFUL ; }if (read_op)
@@ -500,24 +532,21 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, write_op = bridge->ops->write_base; cfgspace = (__le32 *) &bridge->conf; behavior = bridge->pci_regs_behavior; - }else if (!bridge->has_pcie) { -/* PCIe space is not implemented, and no PCI capabilities */ -return PCIBIOS_SUCCESSFUL ; - }else if (reg <PCI_CAP_PCIE_END ) { + }else if (reg >=PCI_CAP_PCIE_START && reg <PCI_CAP_PCIE_END && bridge->has_pcie) {/* Our emulated PCIe capability */ reg -=PCI_CAP_PCIE_START ; write_op = bridge->ops->write_pcie; cfgspace = (__le32 *) &bridge->pcie_conf; behavior = bridge->pcie_cap_regs_behavior; - }else if (reg <PCI_CFG_SPACE_SIZE ) { -/* Rest of PCI space not implemented */ -return PCIBIOS_SUCCESSFUL ; - }else { + }else if (reg >=PCI_CFG_SPACE_SIZE && bridge->has_pcie) {/* PCIe extended capability space */ reg -=PCI_CFG_SPACE_SIZE ; write_op = bridge->ops->write_ext; cfgspace =NULL ; behavior =NULL ; + }else { +/* Not implemented */ +return PCIBIOS_SUCCESSFUL ; } shift = (where &0x3 ) *8 ;
drivers/pci/pci-bridge-emul.h +2 -0
@@ -132,6 +132,8 @@ struct pci_bridge_emul {struct pci_bridge_reg_behavior *pcie_cap_regs_behavior;void *data;bool has_pcie; +u16 subsystem_vendor_id; +u16 subsystem_id; };enum {
Signature · 1 line
2.20.1
Pali Rohár
maintainerPATCH v2 2/11 PCI: pci-bridge-emul: Add support for PCIe extended capabilities
In reply to Pali RohárFrom: Russell King <[email protected]>
Add support for PCIe extended capabilities, which we just redirect to the emulating driver.
--- drivers/pci/pci-bridge-emul.c | 77 +++++++++++++++++++++++------------ drivers/pci/pci-bridge-emul.h | 15 +++++++ 2 files changed, 67 insertions(+), 25 deletions(-)
drivers/pci/pci-bridge-emul.c +52 -25
@@ -433,10 +433,16 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, read_op = bridge->ops->read_pcie; cfgspace = (__le32 *) &bridge->pcie_conf; behavior = bridge->pcie_cap_regs_behavior; - }else { -/* Beyond our PCIe space */ + }else if (reg <PCI_CFG_SPACE_SIZE ) { +/* Rest of PCI space not implemented */ *value =0 ;return PCIBIOS_SUCCESSFUL ; + }else { +/* PCIe extended capability space */ + reg -=PCI_CFG_SPACE_SIZE ; + read_op = bridge->ops->read_ext; + cfgspace =NULL ; + behavior =NULL ; }if (read_op)
@@ -444,15 +450,20 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,else ret =PCI_BRIDGE_EMUL_NOT_HANDLED ; -if (ret ==PCI_BRIDGE_EMUL_NOT_HANDLED ) - *value =le32_to_cpu (cfgspace[reg /4 ]); +if (ret ==PCI_BRIDGE_EMUL_NOT_HANDLED ) { +if (cfgspace) + *value =le32_to_cpu (cfgspace[reg /4 ]); +else + *value =0 ; + }/* * Make sure we never return any reserved bit with a value * different from 0. */ - *value &= behavior[reg /4 ].ro | behavior[reg /4 ].rw | - behavior[reg /4 ].w1c; +if (behavior) + *value &= behavior[reg /4 ].ro | behavior[reg /4 ].rw | + behavior[reg /4 ].w1c;if (size ==1 ) *value = (*value >> (8 * (where &3 ))) &0xff ;
@@ -498,8 +509,15 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, write_op = bridge->ops->write_pcie; cfgspace = (__le32 *) &bridge->pcie_conf; behavior = bridge->pcie_cap_regs_behavior; - }else { + }else if (reg <PCI_CFG_SPACE_SIZE ) { +/* Rest of PCI space not implemented */ return PCIBIOS_SUCCESSFUL ; + }else { +/* PCIe extended capability space */ + reg -=PCI_CFG_SPACE_SIZE ; + write_op = bridge->ops->write_ext; + cfgspace =NULL ; + behavior =NULL ; } shift = (where &0x3 ) *8 ;
@@ -513,29 +531,38 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,else return PCIBIOS_BAD_REGISTER_NUMBER ; -/* Keep all bits, except the RW bits */ - new = old & (~mask | ~behavior[reg /4 ].rw); +if (behavior) { +/* Keep all bits, except the RW bits */ + new = old & (~mask | ~behavior[reg /4 ].rw); -/* Update the value of the RW bits */ - new |= (value << shift) & (behavior[reg /4 ].rw & mask); +/* Update the value of the RW bits */ + new |= (value << shift) & (behavior[reg /4 ].rw & mask); -/* Clear the W1C bits */ - new &= ~((value << shift) & (behavior[reg /4 ].w1c & mask)); +/* Clear the W1C bits */ + new &= ~((value << shift) & (behavior[reg /4 ].w1c & mask)); + }else { + new = old & ~mask; + new |= (value << shift) & mask; + } -/* Save the new value with the cleared W1C bits into the cfgspace */ - cfgspace[reg /4 ] =cpu_to_le32 (new); +if (cfgspace) { +/* Save the new value with the cleared W1C bits into the cfgspace */ + cfgspace[reg /4 ] =cpu_to_le32 (new); + } -/* -* Clear the W1C bits not specified by the write mask, so that the -* write_op() does not clear them. -*/ - new &= ~(behavior[reg /4 ].w1c & ~mask); +if (behavior) { +/* +* Clear the W1C bits not specified by the write mask, so that the +* write_op() does not clear them. +*/ + new &= ~(behavior[reg /4 ].w1c & ~mask); -/* -* Set the W1C bits specified by the write mask, so that write_op() -* knows about that they are to be cleared. -*/ - new |= (value << shift) & (behavior[reg /4 ].w1c & mask); +/* +* Set the W1C bits specified by the write mask, so that write_op() +* knows about that they are to be cleared. +*/ + new |= (value << shift) & (behavior[reg /4 ].w1c & mask); + }if (write_op)write_op (bridge, reg, old, new, mask);
drivers/pci/pci-bridge-emul.h +15 -0
@@ -90,6 +90,14 @@ struct pci_bridge_emul_ops {*/ pci_bridge_emul_read_status_t (*read_pcie)(struct pci_bridge_emul *bridge,int reg,u32 *value); + +/* +* Same as ->read_base(), except it is for reading from the +* PCIe extended capability configuration space. +*/ +pci_bridge_emul_read_status_t (*read_ext)(struct pci_bridge_emul *bridge, +int reg,u32 *value); +/* * Called when writing to the regular PCI bridge configuration * space. old is the current value, new is the new value being
@@ -105,6 +113,13 @@ struct pci_bridge_emul_ops {*/ void (*write_pcie)(struct pci_bridge_emul *bridge,int reg,u32 old,u32 new,u32 mask); + +/* +* Same as ->write_base(), except it is for writing from the +* PCIe extended capability configuration space. +*/ +void (*write_ext)(struct pci_bridge_emul *bridge,int reg, +u32 old,u32 new,u32 mask); };struct pci_bridge_reg_behavior ;
Signature · 1 line
2.20.1
Pali Rohár
maintainerPATCH v2 5/11 PCI: mvebu: Correctly configure x1/x4 mode
In reply to Pali RohárIf x1/x4 mode is not set correctly then link with endpoint card is not established.
Use DTS property 'num-lanes' to deteriminate x1/x4 mode.
--- drivers/pci/controller/pci-mvebu.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
drivers/pci/controller/pci-mvebu.c +18 -1
@@ -93,6 +93,7 @@ struct mvebu_pcie_port {void __iomem *base;u32 port;u32 lane; +bool is_x4;int devfn;unsigned int mem_target;unsigned int mem_attr;
@@ -233,13 +234,25 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, mask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF ); ctrl |=PCIE_CTRL_RC_MODE ;mvebu_writel (port, ctrl,PCIE_CTRL_OFF ); +/* +* Set Maximum Link Width to X1 or X4 in Root Port's PCIe Link +* Capability register. This register is defined by PCIe specification +* as read-only but this mvebu controller has it as read-write and must +* be set to number of SerDes PCIe lanes (1 or 4). If this register is +* not set correctly then link with endpoint card is not established. +*/ + lnkcap =mvebu_readl (port,PCIE_CAP_PCIEXP +PCI_EXP_LNKCAP ); + lnkcap &= ~PCI_EXP_LNKCAP_MLW ; + lnkcap |= (port->is_x4 ?4 :1 ) <<4 ; +mvebu_writel (port, lnkcap,PCIE_CAP_PCIEXP +PCI_EXP_LNKCAP ); +/* Disable Root Bridge I/O space, memory space and bus mastering. */ cmd =mvebu_readl (port,PCIE_CMD_OFF ); cmd &= ~(PCI_COMMAND_IO |PCI_COMMAND_MEMORY |PCI_COMMAND_MASTER );
@@ -986,6 +999,7 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,struct device *dev = &pcie->pdev->dev;enum of_gpio_flags flags;int reset_gpio, ret; +u32 num_lanes; port->pcie = pcie;
@@ -998,6 +1012,9 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,if (of_property_read_u32 (child,"marvell,pcie-lane" , &port->lane)) port->lane =0 ; +if (!of_property_read_u32 (child,"num-lanes" , &num_lanes) && num_lanes ==4 ) + port->is_x4 = true; + port->name =devm_kasprintf (dev,GFP_KERNEL ,"pcie%d.%d" , port->port, port->lane);if (!port->name) {
Signature · 1 line
2.20.1
Pali Rohár
maintainerPATCH v2 11/11 ARM: dts: armada-385.dtsi: Add definitions for PCIe legacy INTx interrupts
In reply to Pali RohárWith this change legacy INTA, INTB, INTC and INTD interrupts are reported separately and not mixed into one Linux virq source anymore.
--- arch/arm/boot/dts/armada-385.dtsi | 52 ++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 8 deletions(-)
arch/arm/boot/dts/armada-385.dtsi +44 -8
@@ -69,16 +69,25 @@ reg = <0x0800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 0x81000000 0 0 0x81000000 0x1 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie1_intc 0>, + <0 0 0 2 &pcie1_intc 1>, + <0 0 0 3 &pcie1_intc 2>, + <0 0 0 4 &pcie1_intc 3>; marvell,pcie-port = <0>; marvell,pcie-lane = <0>; clocks = <&gateclk 8>; status = "disabled"; + pcie1_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /* x1 port */
@@ -88,16 +97,25 @@ reg = <0x1000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 0x81000000 0 0 0x81000000 0x2 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie2_intc 0>, + <0 0 0 2 &pcie2_intc 1>, + <0 0 0 3 &pcie2_intc 2>, + <0 0 0 4 &pcie2_intc 3>; marvell,pcie-port = <1>; marvell,pcie-lane = <0>; clocks = <&gateclk 5>; status = "disabled"; + pcie2_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /* x1 port */
@@ -107,16 +125,25 @@ reg = <0x1800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 0x81000000 0 0 0x81000000 0x3 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie3_intc 0>, + <0 0 0 2 &pcie3_intc 1>, + <0 0 0 3 &pcie3_intc 2>, + <0 0 0 4 &pcie3_intc 3>; marvell,pcie-port = <2>; marvell,pcie-lane = <0>; clocks = <&gateclk 6>; status = "disabled"; + pcie3_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /*
@@ -129,16 +156,25 @@ reg = <0x2000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0 0x81000000 0 0 0x81000000 0x4 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie4_intc 0>, + <0 0 0 2 &pcie4_intc 1>, + <0 0 0 3 &pcie4_intc 2>, + <0 0 0 4 &pcie4_intc 3>; marvell,pcie-port = <3>; marvell,pcie-lane = <0>; clocks = <&gateclk 7>; status = "disabled"; + pcie4_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; }; };
Signature · 1 line
2.20.1
Pali Rohár
maintainerPATCH v2 7/11 PCI: mvebu: Add support for Advanced Error Reporting registers on emulated bridge
In reply to Pali RohárAER registers start at mvebu offset 0x0100. Registers PCI_ERR_ROOT_COMMAND, PCI_ERR_ROOT_STATUS and PCI_ERR_ROOT_ERR_SRC are not supported on pre-XP hardware and returns zeros.
Note that AER interrupt is not supported yet as mvebu emulated bridge does not implement interrupts support at all yet.
Also remove custom macro PCIE_HEADER_LOG_4_OFF as it is unused and correctly this register should be referenced via standard macros with offset, e.g. as: PCIE_CAP_PCIERR_OFF + PCI_ERR_HEADER_LOG + 4.
--- drivers/pci/controller/pci-mvebu.c | 67 +++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-)
drivers/pci/controller/pci-mvebu.c +66 -1
@@ -34,7 +34,7 @@#define PCIE_BAR_HI_OFF (n) (0x0014 + ((n) << 3))#define PCIE_SSDEV_ID_OFF 0x002c#define PCIE_CAP_PCIEXP 0x0060 -#define PCIE_HEADER_LOG_4_OFF 0x0128 +#define PCIE_CAP_PCIERR_OFF 0x0100#define PCIE_BAR_CTRL_OFF (n) (0x1804 + (((n) - 1) * 4))#define PCIE_WIN04_CTRL_OFF (n) (0x1820 + ((n) << 4))#define PCIE_WIN04_BASE_OFF (n) (0x1824 + ((n) << 4))
@@ -603,6 +603,37 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,return PCI_BRIDGE_EMUL_HANDLED ; } +static pci_bridge_emul_read_status_t +mvebu_pci_bridge_emul_ext_conf_read (struct pci_bridge_emul *bridge, +int reg,u32 *value) +{ +struct mvebu_pcie_port *port = bridge->data; + +switch (reg) { +case 0 : +case PCI_ERR_UNCOR_STATUS : +case PCI_ERR_UNCOR_MASK : +case PCI_ERR_UNCOR_SEVER : +case PCI_ERR_COR_STATUS : +case PCI_ERR_COR_MASK : +case PCI_ERR_CAP : +case PCI_ERR_HEADER_LOG +0 : +case PCI_ERR_HEADER_LOG +4 : +case PCI_ERR_HEADER_LOG +8 : +case PCI_ERR_HEADER_LOG +12 : +case PCI_ERR_ROOT_COMMAND : +case PCI_ERR_ROOT_STATUS : +case PCI_ERR_ROOT_ERR_SRC : + *value =mvebu_readl (port,PCIE_CAP_PCIERR_OFF + reg); +break ; + +default : +return PCI_BRIDGE_EMUL_NOT_HANDLED ; + } + +return PCI_BRIDGE_EMUL_HANDLED ; +} +static void mvebu_pci_bridge_emul_base_conf_write (struct pci_bridge_emul *bridge,int reg,u32 old,u32 new,u32 mask)
@@ -715,11 +746,45 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, } } +static void +mvebu_pci_bridge_emul_ext_conf_write (struct pci_bridge_emul *bridge, +int reg,u32 old,u32 new,u32 mask) +{ +struct mvebu_pcie_port *port = bridge->data; + +switch (reg) { +/* These are W1C registers, so clear other bits */ +case PCI_ERR_UNCOR_STATUS : +case PCI_ERR_COR_STATUS : +case PCI_ERR_ROOT_STATUS : + new &= mask; + fallthrough; + +case PCI_ERR_UNCOR_MASK : +case PCI_ERR_UNCOR_SEVER : +case PCI_ERR_COR_MASK : +case PCI_ERR_CAP : +case PCI_ERR_HEADER_LOG +0 : +case PCI_ERR_HEADER_LOG +4 : +case PCI_ERR_HEADER_LOG +8 : +case PCI_ERR_HEADER_LOG +12 : +case PCI_ERR_ROOT_COMMAND : +case PCI_ERR_ROOT_ERR_SRC : +mvebu_writel (port, new,PCIE_CAP_PCIERR_OFF + reg); +break ; + +default : +break ; + } +} +static const struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = { .read_base = mvebu_pci_bridge_emul_base_conf_read, .write_base = mvebu_pci_bridge_emul_base_conf_write, .read_pcie = mvebu_pci_bridge_emul_pcie_conf_read, .write_pcie = mvebu_pci_bridge_emul_pcie_conf_write, + .read_ext = mvebu_pci_bridge_emul_ext_conf_read, + .write_ext = mvebu_pci_bridge_emul_ext_conf_write, };/*
Signature · 1 line
2.20.1
Pali Rohár
maintainerPATCH v2 9/11 dt-bindings: PCI: mvebu: Update information about intx interrupts
In reply to Pali Rohár--- Documentation/devicetree/bindings/pci/mvebu-pci.txt | 5 +++++ 1 file changed, 5 insertions(+)
Documentation/devicetree/bindings/pci/mvebu-pci.txt +5 -0
@@ -81,6 +81,11 @@ and the following optional properties: - reset-gpios: optional GPIO to PERST# - reset-delay-us: delay in us to wait after reset de-assertion, if not specified will default to 100ms, as required by the PCIe specification. +- interrupt-names: list of interrupt names, supported are: + - "intx" - interrupt line triggered by one of the legacy interrupt +- interrupts or interrupts-extended: List of the interrupt sources which + corresponding to the "interrupt-names". If non-empty then also additional + 'interrupt-controller' subnode must be defined. Example:
Signature · 1 line
2.20.1
Pali Rohár
maintainerPATCH v2 10/11 PCI: mvebu: Implement support for legacy INTx interrupts
In reply to Pali RohárThis adds support for legacy INTx interrupts received from other PCIe devices and which are reported by a new INTx irq chip.
With this change, kernel can distinguish between INTA, INTB, INTC and INTD interrupts.
Note that for this support, device tree files has to be properly adjusted to provide "interrupts" or "interrupts-extended" property with intx interrupt source, "interrupt-names" property with "intx" string and also 'interrupt-controller' subnode must be defined.
If device tree files do not provide these nodes then driver would work as before.
--- drivers/pci/controller/pci-mvebu.c | 185 +++++++++++++++++++++++++++-- 1 file changed, 177 insertions(+), 8 deletions(-)
drivers/pci/controller/pci-mvebu.c +177 -8
@@ -54,9 +54,10 @@ PCIE_CONF_ADDR_EN)#define PCIE_CONF_DATA_OFF 0x18fc#define PCIE_INT_CAUSE_OFF 0x1900 +#define PCIE_INT_UNMASK_OFF 0x1910 +#define PCIE_INT_INTX (i) BIT(24+i)#define PCIE_INT_PM_PME BIT(28) -#define PCIE_MASK_OFF 0x1910 -#define PCIE_MASK_ENABLE_INTS 0x0f000000 +#define PCIE_INT_ALL_MASK GENMASK(31, 0)#define PCIE_CTRL_OFF 0x1a00#define PCIE_CTRL_X1_MODE 0x0001#define PCIE_CTRL_RC_MODE BIT(1)
@@ -110,6 +111,9 @@ struct mvebu_pcie_port {struct mvebu_pcie_window iowin;u32 saved_pcie_stat;struct resource regs; +struct irq_domain *intx_irq_domain; +raw_spinlock_t irq_lock; +int intx_irq; };static inline void mvebu_writel (struct mvebu_pcie_port *port,u32 val,u32 reg)
@@ -235,7 +239,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, lnkcap, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, unmask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF );
@@ -288,10 +292,30 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)/* Point PCIe unit MBUS decode windows to DRAM space. */ mvebu_pcie_setup_wins (port); -/* Enable interrupt lines A-D. */ - mask =mvebu_readl (port,PCIE_MASK_OFF ); - mask |=PCIE_MASK_ENABLE_INTS ; -mvebu_writel (port, mask,PCIE_MASK_OFF ); +/* Mask all interrupt sources. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (port->intx_irq <=0 ) { +/* +* When neither "summary" interrupt, nor "intx" interrupt was +* specified in DT then unmask all legacy INTx interrupts as in +* this case driver does not provide a way for masking and +* unmasking of individual legacy INTx interrupts. In this case +* all interrupts, including legacy INTx are reported via one +* shared GIC source and therefore kernel cannot distinguish +* which individual legacy INTx was triggered. These interrupts +* are shared, so it should not cause any issue. Just +* performance penalty as every PCIe interrupt handler needs to +* be called when some interrupt is triggered. +*/ + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (0 ) |PCIE_INT_INTX (1 ) | +PCIE_INT_INTX (2 ) |PCIE_INT_INTX (3 ); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); + } } static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,
@@ -924,6 +948,108 @@ static struct pci_ops mvebu_pcie_ops = { .write = mvebu_pcie_wr_conf, }; +static void mvebu_pcie_intx_irq_mask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask &= ~PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static void mvebu_pcie_intx_irq_unmask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static struct irq_chip intx_irq_chip = { + .name ="mvebu-INTx" , + .irq_mask = mvebu_pcie_intx_irq_mask, + .irq_unmask = mvebu_pcie_intx_irq_unmask, +}; + +static int mvebu_pcie_intx_irq_map (struct irq_domain *h, +unsigned int virq,irq_hw_number_t hwirq) +{ +struct mvebu_pcie_port *port = h->host_data; + +irq_set_status_flags (virq,IRQ_LEVEL ); +irq_set_chip_and_handler (virq, &intx_irq_chip, handle_level_irq); +irq_set_chip_data (virq, port); + +return 0 ; +} + +static const struct irq_domain_ops mvebu_pcie_intx_irq_domain_ops = { + .map = mvebu_pcie_intx_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int mvebu_pcie_init_irq_domain (struct mvebu_pcie_port *port) +{ +struct device *dev = &port->pcie->pdev->dev; +struct device_node *pcie_intc_node; + +raw_spin_lock_init (&port->irq_lock); + + pcie_intc_node =of_get_next_child (port->dn,NULL ); +if (!pcie_intc_node) { +dev_err (dev,"No PCIe Intc node found for %s\n" , port->name); +return -ENODEV ; + } + + port->intx_irq_domain =irq_domain_add_linear (pcie_intc_node,PCI_NUM_INTX , + &mvebu_pcie_intx_irq_domain_ops, + port); +of_node_put (pcie_intc_node); +if (!port->intx_irq_domain) { +dev_err (dev,"Failed to get INTx IRQ domain for %s\n" , port->name); +return -ENOMEM ; + } + +return 0 ; +} + +static void mvebu_pcie_irq_handler (struct irq_desc *desc) +{ +struct mvebu_pcie_port *port =irq_desc_get_handler_data (desc); +struct irq_chip *chip =irq_desc_get_chip (desc); +struct device *dev = &port->pcie->pdev->dev; +u32 cause, unmask, status; +int i; + +chained_irq_enter (chip, desc); + + cause =mvebu_readl (port,PCIE_INT_CAUSE_OFF ); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + status = cause & unmask; + +/* Process legacy INTx interrupts */ +for (i =0 ; i <PCI_NUM_INTX ; i++) { +if (!(status &PCIE_INT_INTX (i))) +continue ; + +if (generic_handle_domain_irq (port->intx_irq_domain, i) == -EINVAL ) +dev_err_ratelimited (dev,"unexpected INT%c IRQ\n" , (char )i+'A' ); + } + +chained_irq_exit (chip, desc); +} +static int mvebu_pcie_map_irq (const struct pci_dev *dev,u8 slot,u8 pin) {/* Interrupt support on mvebu emulated bridges is not implemented yet */
@@ -1121,6 +1247,21 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, port->io_attr =-1 ; } +/* +* Old DT bindings do not contain "intx" interrupt +* so do not fail probing driver when interrupt does not exist. +*/ + port->intx_irq =of_irq_get_byname (child,"intx" ); +if (port->intx_irq == -EPROBE_DEFER ) { + ret = port->intx_irq; + goto err; + } +if (port->intx_irq <=0 ) { +dev_warn (dev,"%s: legacy INTx interrupts cannot be masked individually, " +"%pOF does not contain intx interrupt\n" , + port->name, child); + } + reset_gpio =of_get_named_gpio_flags (child,"reset-gpios" ,0 , &flags);if (reset_gpio == -EPROBE_DEFER ) { ret = reset_gpio;
@@ -1317,6 +1458,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)for (i =0 ; i < pcie->nports; i++) {struct mvebu_pcie_port *port = &pcie->ports[i]; +int irq = port->intx_irq; child = port->dn;if (!child)
@@ -1344,6 +1486,22 @@ static int mvebu_pcie_probe(struct platform_device *pdev)continue ; } +if (irq >0 ) { + ret =mvebu_pcie_init_irq_domain (port); +if (ret) { +dev_err (dev,"%s: cannot init irq domain\n" , + port->name); +pci_bridge_emul_cleanup (&port->bridge); +devm_iounmap (dev, port->base); + port->base =NULL ; +mvebu_pcie_powerdown (port); +continue ; + } +irq_set_chained_handler_and_data (irq, + mvebu_pcie_irq_handler, + port); + } +/* * PCIe topology exported by mvebu hw is quite complicated. In * reality has something like N fully independent host bridges
@@ -1448,6 +1606,7 @@ static int mvebu_pcie_remove(struct platform_device *pdev)for (i =0 ; i < pcie->nports; i++) {struct mvebu_pcie_port *port = &pcie->ports[i]; +int irq = port->intx_irq;if (!port->base)continue ;
@@ -1458,7 +1617,17 @@ static int mvebu_pcie_remove(struct platform_device *pdev)mvebu_writel (port, cmd,PCIE_CMD_OFF );/* Mask all interrupt sources. */ -mvebu_writel (port,0 ,PCIE_MASK_OFF ); +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (irq >0 ) +irq_set_chained_handler_and_data (irq,NULL ,NULL ); + +/* Remove IRQ domains. */ +if (port->intx_irq_domain) +irq_domain_remove (port->intx_irq_domain);/* Free config space for emulated root bridge. */ pci_bridge_emul_cleanup (&port->bridge);
Signature · 1 line
2.20.1
Pali Rohár
maintainerPATCH v2 6/11 PCI: mvebu: Add support for PCI Bridge Subsystem Vendor ID on emulated bridge
In reply to Pali RohárRegister with Subsystem Device/Vendor ID is at offset 0x2c. Export is via emulated bridge.
After this change Subsystem ID is visible in lspci output at line:
Capabilities: [40] Subsystem
--- drivers/pci/controller/pci-mvebu.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/pci/controller/pci-mvebu.c +4 -0
@@ -32,6 +32,7 @@#define PCIE_DEV_REV_OFF 0x0008#define PCIE_BAR_LO_OFF (n) (0x0010 + ((n) << 3))#define PCIE_BAR_HI_OFF (n) (0x0014 + ((n) << 3)) +#define PCIE_SSDEV_ID_OFF 0x002c#define PCIE_CAP_PCIEXP 0x0060#define PCIE_HEADER_LOG_4_OFF 0x0128#define PCIE_BAR_CTRL_OFF (n) (0x1804 + (((n) - 1) * 4))
@@ -731,6 +732,7 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)struct pci_bridge_emul *bridge = &port->bridge;u32 dev_id =mvebu_readl (port,PCIE_DEV_ID_OFF );u32 dev_rev =mvebu_readl (port,PCIE_DEV_REV_OFF ); +u32 ssdev_id =mvebu_readl (port,PCIE_SSDEV_ID_OFF );u32 pcie_cap =mvebu_readl (port,PCIE_CAP_PCIEXP );u8 pcie_cap_ver = ((pcie_cap >>16 ) &PCI_EXP_FLAGS_VERS );
@@ -752,6 +754,8 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)*/ bridge->pcie_conf.cap =cpu_to_le16 (pcie_cap_ver); + bridge->subsystem_vendor_id = ssdev_id &0xffff ; + bridge->subsystem_id = ssdev_id >>16 ; bridge->has_pcie = true; bridge->data = port; bridge->ops = &mvebu_pci_bridge_emul_ops;
Signature · 1 line
2.20.1
Pali Rohár
maintainerPATCH v2 8/11 PCI: mvebu: Use child_ops API
In reply to Pali RohárSplit struct pci_ops between ops and child_ops. Member ops is used for accessing PCIe Root Ports via pci-bridge-emul.c driver and child_ops for accessing real PCIe cards.
There is no need to mix these two struct pci_ops into one as PCI core code already provides separate callbacks via bridge->ops and bridge->child_ops.
--- drivers/pci/controller/pci-mvebu.c | 82 ++++++++++++++++-------------- 1 file changed, 44 insertions(+), 38 deletions(-)
drivers/pci/controller/pci-mvebu.c +44 -38
@@ -294,11 +294,29 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)mvebu_writel (port, mask,PCIE_MASK_OFF ); } -static int mvebu_pcie_hw_rd_conf (struct mvebu_pcie_port *port, -struct pci_bus *bus, -u32 devfn,int where,int size,u32 *val) +static struct mvebu_pcie_port *mvebu_pcie_find_port (struct mvebu_pcie *pcie, +struct pci_bus *bus, +int devfn); + +static int mvebu_pcie_child_rd_conf (struct pci_bus *bus,u32 devfn,int where, +int size,u32 *val) { -void __iomem *conf_data = port->base +PCIE_CONF_DATA_OFF ; +struct mvebu_pcie *pcie = bus->sysdata; +struct mvebu_pcie_port *port; +void __iomem *conf_data; + + port =mvebu_pcie_find_port (pcie, bus, devfn); +if (!port) { + *val =0xffffffff ; +return PCIBIOS_DEVICE_NOT_FOUND ; + } + +if (!mvebu_pcie_link_up (port)) { + *val =0xffffffff ; +return PCIBIOS_DEVICE_NOT_FOUND ; + } + + conf_data = port->base +PCIE_CONF_DATA_OFF ;mvebu_writel (port,PCIE_CONF_ADDR (bus->number, devfn, where),PCIE_CONF_ADDR_OFF );
@@ -321,11 +339,21 @@ static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port,return PCIBIOS_SUCCESSFUL ; } -static int mvebu_pcie_hw_wr_conf (struct mvebu_pcie_port *port, -struct pci_bus *bus, -u32 devfn,int where,int size,u32 val) +static int mvebu_pcie_child_wr_conf (struct pci_bus *bus,u32 devfn, +int where,int size,u32 val) { -void __iomem *conf_data = port->base +PCIE_CONF_DATA_OFF ; +struct mvebu_pcie *pcie = bus->sysdata; +struct mvebu_pcie_port *port; +void __iomem *conf_data; + + port =mvebu_pcie_find_port (pcie, bus, devfn); +if (!port) +return PCIBIOS_DEVICE_NOT_FOUND ; + +if (!mvebu_pcie_link_up (port)) +return PCIBIOS_DEVICE_NOT_FOUND ; + + conf_data = port->base +PCIE_CONF_DATA_OFF ;mvebu_writel (port,PCIE_CONF_ADDR (bus->number, devfn, where),PCIE_CONF_ADDR_OFF );
@@ -347,6 +375,11 @@ static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port,return PCIBIOS_SUCCESSFUL ; } +static struct pci_ops mvebu_pcie_child_ops = { + .read = mvebu_pcie_child_rd_conf, + .write = mvebu_pcie_child_wr_conf, +}; +/* * Remove windows, starting from the largest ones to the smallest * ones.
@@ -862,25 +895,12 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn, {struct mvebu_pcie *pcie = bus->sysdata;struct mvebu_pcie_port *port; -int ret; port =mvebu_pcie_find_port (pcie, bus, devfn);if (!port)return PCIBIOS_DEVICE_NOT_FOUND ; -/* Access the emulated PCI-to-PCI bridge */ -if (bus->number ==0 ) -return pci_bridge_emul_conf_write (&port->bridge, where, - size, val); - -if (!mvebu_pcie_link_up (port)) -return PCIBIOS_DEVICE_NOT_FOUND ; - -/* Access the real PCIe interface */ - ret =mvebu_pcie_hw_wr_conf (port, bus, devfn, - where, size, val); - -return ret; +return pci_bridge_emul_conf_write (&port->bridge, where, size, val); }/* PCI configuration space read function */
@@ -889,7 +909,6 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, {struct mvebu_pcie *pcie = bus->sysdata;struct mvebu_pcie_port *port; -int ret; port =mvebu_pcie_find_port (pcie, bus, devfn);if (!port) {
@@ -897,21 +916,7 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,return PCIBIOS_DEVICE_NOT_FOUND ; } -/* Access the emulated PCI-to-PCI bridge */ -if (bus->number ==0 ) -return pci_bridge_emul_conf_read (&port->bridge, where, - size, val); - -if (!mvebu_pcie_link_up (port)) { - *val =0xffffffff ; -return PCIBIOS_DEVICE_NOT_FOUND ; - } - -/* Access the real PCIe interface */ - ret =mvebu_pcie_hw_rd_conf (port, bus, devfn, - where, size, val); - -return ret; +return pci_bridge_emul_conf_read (&port->bridge, where, size, val); }static struct pci_ops mvebu_pcie_ops = {
@@ -1421,6 +1426,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev) bridge->sysdata = pcie; bridge->ops = &mvebu_pcie_ops; + bridge->child_ops = &mvebu_pcie_child_ops; bridge->align_resource = mvebu_pcie_align_resource; bridge->map_irq = mvebu_pcie_map_irq;
Signature · 1 line
2.20.1
Re: [PATCH v2 09/11] dt-bindings: PCI: mvebu: Update information about intx interrupts
In reply to Pali RohárQuoted 12 lines · On Wed, 12 Jan 2022 16:18:12 +0100 Pali Rohár <[email protected]> wrote:
Signed-off-by Pali Rohár <[email protected]>Acked-by Rob Herring <[email protected]>--- Documentation/devicetree/bindings/pci/mvebu-pci.txt | 5 +++++ 1 file changed, 5 insertions(+)Documentation/devicetree/bindings/pci/mvebu-pci.txt +0
-0@@ -81,6 +81,11 @@ and the following optional properties: - reset-gpios: optional GPIO to PERST#
Documentation/devicetree/bindings/pci/mvebu-pci.txt and the following optional properties: · line 91
Quoted 10 lines
Documentation/devicetree/bindings/pci/mvebu-pci.txt +5
-0- reset-delay-us: delay in us to wait after reset de-assertion, if not specified will default to 100ms, as required by the PCIe specification. +- interrupt-names: list of interrupt names, supported are: + - "intx" - interrupt line triggered by one of the legacy interrupt +- interrupts or interrupts-extended: List of the interrupt sources which + corresponding to the "interrupt-names". If non-empty then also additional + 'interrupt-controller' subnode must be defined. Example:
Empty commit message. At least add something like this:
Document the following additional properties in the mvebu-pci DT binding:
- interrupt-names
- interrupts / interrupts-extended
Marek
Rob Herring
maintainerRe: [PATCH v2 08/11] PCI: mvebu: Use child_ops API
In reply to Pali RohárQuoted 64 lines · Pali Rohár
Split struct
pci_opsbetween ops andchild_ops. Member ops is used for accessing PCIe Root Ports viapci-bridge-emul.cdriver andchild_opsfor accessing real PCIe cards.There is no need to mix these two struct
pci_opsinto one as PCI core code already provides separate callbacks viabridge->opsandbridge->child_ops.Signed-off-by Pali Rohár <[email protected]>--- drivers/pci/controller/pci-mvebu.c | 82 ++++++++++++++++-------------- 1 file changed, 44 insertions(+), 38 deletions(-)drivers/pci/controller/pci-mvebu.c +27
-8@@ -294,11 +294,29 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)mvebu_writel (port, mask,PCIE_MASK_OFF ); } -static int mvebu_pcie_hw_rd_conf (struct mvebu_pcie_port *port, -struct pci_bus *bus, -u32 devfn,int where,int size,u32 *val) +static struct mvebu_pcie_port *mvebu_pcie_find_port (struct mvebu_pcie *pcie, +struct pci_bus *bus, +int devfn); + +static int mvebu_pcie_child_rd_conf (struct pci_bus *bus,u32 devfn,int where, +int size,u32 *val) { -void __iomem *conf_data = port->base +PCIE_CONF_DATA_OFF ; +struct mvebu_pcie *pcie = bus->sysdata; +struct mvebu_pcie_port *port; +void __iomem *conf_data; + + port =mvebu_pcie_find_port (pcie, bus, devfn); +if (!port) { + *val =0xffffffff ; +return PCIBIOS_DEVICE_NOT_FOUND ; + } + +if (!mvebu_pcie_link_up (port)) { + *val =0xffffffff ; +return PCIBIOS_DEVICE_NOT_FOUND ; + } + + conf_data = port->base +PCIE_CONF_DATA_OFF ;mvebu_writel (port,PCIE_CONF_ADDR (bus->number, devfn, where),PCIE_CONF_ADDR_OFF );@@ -321,11 +339,21 @@ static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port,return PCIBIOS_SUCCESSFUL ; } -static int mvebu_pcie_hw_wr_conf (struct mvebu_pcie_port *port, -struct pci_bus *bus, -u32 devfn,int where,int size,u32 val) +static int mvebu_pcie_child_wr_conf (struct pci_bus *bus,u32 devfn, +int where,int size,u32 val) { -void __iomem *conf_data = port->base +PCIE_CONF_DATA_OFF ; +struct mvebu_pcie *pcie = bus->sysdata; +struct mvebu_pcie_port *port; +void __iomem *conf_data;
drivers/pci/controller/pci-mvebu.c static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port, · line 357
Quoted 10 lines
drivers/pci/controller/pci-mvebu.c +9
-0+ + port =mvebu_pcie_find_port (pcie, bus, devfn); +if (!port) +return PCIBIOS_DEVICE_NOT_FOUND ; + +if (!mvebu_pcie_link_up (port)) +return PCIBIOS_DEVICE_NOT_FOUND ; + + conf_data = port->base +PCIE_CONF_DATA_OFF ;
Again, the same setup code in read and write is a sign to use .map_bus(). You can copy it from my version I pointed you to.
Rob
Pali Rohár
maintainerRe: [PATCH v2 08/11] PCI: mvebu: Use child_ops API
Quoted 79 lines · On Thursday 20 January 2022 10:49:05 Rob Herring wrote:
Quoted 64 lines · Pali Rohár
Split struct
pci_opsbetween ops andchild_ops. Member ops is used for accessing PCIe Root Ports viapci-bridge-emul.cdriver andchild_opsfor accessing real PCIe cards.There is no need to mix these two struct
pci_opsinto one as PCI core code already provides separate callbacks viabridge->opsandbridge->child_ops.Signed-off-by Pali Rohár <[email protected]>--- drivers/pci/controller/pci-mvebu.c | 82 ++++++++++++++++-------------- 1 file changed, 44 insertions(+), 38 deletions(-)drivers/pci/controller/pci-mvebu.c +27
-8@@ -294,11 +294,29 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)mvebu_writel (port, mask,PCIE_MASK_OFF ); } -static int mvebu_pcie_hw_rd_conf (struct mvebu_pcie_port *port, -struct pci_bus *bus, -u32 devfn,int where,int size,u32 *val) +static struct mvebu_pcie_port *mvebu_pcie_find_port (struct mvebu_pcie *pcie, +struct pci_bus *bus, +int devfn); + +static int mvebu_pcie_child_rd_conf (struct pci_bus *bus,u32 devfn,int where, +int size,u32 *val) { -void __iomem *conf_data = port->base +PCIE_CONF_DATA_OFF ; +struct mvebu_pcie *pcie = bus->sysdata; +struct mvebu_pcie_port *port; +void __iomem *conf_data; + + port =mvebu_pcie_find_port (pcie, bus, devfn); +if (!port) { + *val =0xffffffff ; +return PCIBIOS_DEVICE_NOT_FOUND ; + } + +if (!mvebu_pcie_link_up (port)) { + *val =0xffffffff ; +return PCIBIOS_DEVICE_NOT_FOUND ; + } + + conf_data = port->base +PCIE_CONF_DATA_OFF ;mvebu_writel (port,PCIE_CONF_ADDR (bus->number, devfn, where),PCIE_CONF_ADDR_OFF );@@ -321,11 +339,21 @@ static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port,return PCIBIOS_SUCCESSFUL ; } -static int mvebu_pcie_hw_wr_conf (struct mvebu_pcie_port *port, -struct pci_bus *bus, -u32 devfn,int where,int size,u32 val) +static int mvebu_pcie_child_wr_conf (struct pci_bus *bus,u32 devfn, +int where,int size,u32 val) { -void __iomem *conf_data = port->base +PCIE_CONF_DATA_OFF ; +struct mvebu_pcie *pcie = bus->sysdata; +struct mvebu_pcie_port *port; +void __iomem *conf_data;drivers/pci/controller/pci-mvebu.c static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port, · line 357
Quoted 10 lines
drivers/pci/controller/pci-mvebu.c +9
-0+ + port =mvebu_pcie_find_port (pcie, bus, devfn); +if (!port) +return PCIBIOS_DEVICE_NOT_FOUND ; + +if (!mvebu_pcie_link_up (port)) +return PCIBIOS_DEVICE_NOT_FOUND ; + + conf_data = port->base +PCIE_CONF_DATA_OFF ;Again, the same setup code in read and write is a sign to use .map_bus(). You can copy it from my version I pointed you to.
Quoted 1 line
Rob
I'm planning to do other cleanup in followup patches. But there are too many mvebu and aardvark patches on the list waiting, and I do not want to send another batch.
Rob Herring
maintainerRe: [PATCH v2 05/11] PCI: mvebu: Correctly configure x1/x4 mode
In reply to Pali RohárQuoted 15 lines · Pali Rohár
If x1/x4 mode is not set correctly then link with endpoint card is not established.
Use DTS property 'num-lanes' to deteriminate x1/x4 mode.
Signed-off-by Pali Rohár <[email protected]>--- drivers/pci/controller/pci-mvebu.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)drivers/pci/controller/pci-mvebu.c +0
-0
drivers/pci/controller/pci-mvebu.c struct mvebu_pcie_port { · line 96
Quoted 4 lines
drivers/pci/controller/pci-mvebu.c +1
-0@@ -93,6 +93,7 @@ struct mvebu_pcie_port {void __iomem *base;u32 port;u32 lane; +bool is_x4;
I would just store the number of lanes.
Quoted 15 lines
drivers/pci/controller/pci-mvebu.c +1
-1int devfn;unsigned int mem_target;unsigned int mem_attr;@@ -233,13 +234,25 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, mask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF ); ctrl |=PCIE_CTRL_RC_MODE ;mvebu_writel (port, ctrl,PCIE_CTRL_OFF );
drivers/pci/controller/pci-mvebu.c static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) · line 253
Quoted 10 lines
drivers/pci/controller/pci-mvebu.c +10
-0+/* +* Set Maximum Link Width to X1 or X4 in Root Port's PCIe Link +* Capability register. This register is defined by PCIe specification +* as read-only but this mvebu controller has it as read-write and must +* be set to number of SerDes PCIe lanes (1 or 4). If this register is +* not set correctly then link with endpoint card is not established. +*/ + lnkcap =mvebu_readl (port,PCIE_CAP_PCIEXP +PCI_EXP_LNKCAP ); + lnkcap &= ~PCI_EXP_LNKCAP_MLW ; + lnkcap |= (port->is_x4 ?4 :1 ) <<4 ;
then this is just: lanes << 4
Quoted 14 lines
drivers/pci/controller/pci-mvebu.c +3
-0+mvebu_writel (port, lnkcap,PCIE_CAP_PCIEXP +PCI_EXP_LNKCAP ); +/* Disable Root Bridge I/O space, memory space and bus mastering. */ cmd =mvebu_readl (port,PCIE_CMD_OFF ); cmd &= ~(PCI_COMMAND_IO |PCI_COMMAND_MEMORY |PCI_COMMAND_MASTER );@@ -986,6 +999,7 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,struct device *dev = &pcie->pdev->dev;enum of_gpio_flags flags;int reset_gpio, ret; +u32 num_lanes; port->pcie = pcie;
drivers/pci/controller/pci-mvebu.c static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, · line 1016
Quoted 5 lines
drivers/pci/controller/pci-mvebu.c +2
-0@@ -998,6 +1012,9 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,if (of_property_read_u32 (child,"marvell,pcie-lane" , &port->lane)) port->lane =0 ; +if (!of_property_read_u32 (child,"num-lanes" , &num_lanes) && num_lanes ==4 ) + port->is_x4 = true;
And this can be:
num_lanes = 1;of_property_read_u32(child, "num-lanes", &num_lanes);
If you want to validate the DT is only 1 or 4, make the DT schema do that.
Quoted 8 lines
drivers/pci/controller/pci-mvebu.c +1
-0+ port->name =devm_kasprintf (dev,GFP_KERNEL ,"pcie%d.%d" , port->port, port->lane);if (!port->name) {Signature · 1 line
2.20.1
Pali Rohár
maintainerRe: [PATCH v2 05/11] PCI: mvebu: Correctly configure x1/x4 mode
Quoted 38 lines · On Thursday 20 January 2022 11:09:17 Rob Herring wrote:
Quoted 15 lines · Pali Rohár
If x1/x4 mode is not set correctly then link with endpoint card is not established.
Use DTS property 'num-lanes' to deteriminate x1/x4 mode.
Signed-off-by Pali Rohár <[email protected]>--- drivers/pci/controller/pci-mvebu.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)drivers/pci/controller/pci-mvebu.c +0
-0drivers/pci/controller/pci-mvebu.c struct mvebu_pcie_port { · line 96
Quoted 4 lines
drivers/pci/controller/pci-mvebu.c +1
-0@@ -93,6 +93,7 @@ struct mvebu_pcie_port {void __iomem *base;u32 port;u32 lane; +bool is_x4;I would just store the number of lanes.
Quoted 15 lines
drivers/pci/controller/pci-mvebu.c +1
-1int devfn;unsigned int mem_target;unsigned int mem_attr;@@ -233,13 +234,25 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, mask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF ); ctrl |=PCIE_CTRL_RC_MODE ;mvebu_writel (port, ctrl,PCIE_CTRL_OFF );
Quoted 12 lines
drivers/pci/controller/pci-mvebu.c static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) · line 253
Quoted 10 lines
drivers/pci/controller/pci-mvebu.c +10
-0+/* +* Set Maximum Link Width to X1 or X4 in Root Port's PCIe Link +* Capability register. This register is defined by PCIe specification +* as read-only but this mvebu controller has it as read-write and must +* be set to number of SerDes PCIe lanes (1 or 4). If this register is +* not set correctly then link with endpoint card is not established. +*/ + lnkcap =mvebu_readl (port,PCIE_CAP_PCIEXP +PCI_EXP_LNKCAP ); + lnkcap &= ~PCI_EXP_LNKCAP_MLW ; + lnkcap |= (port->is_x4 ?4 :1 ) <<4 ;then this is just: lanes << 4
As only 1 and 4 are valid valid values, I chose this style (is_x4) to ensure that no other (invalid) value is written into mvebu register. Setting width to smaller number (if incorrect value is provided) still allows controller to work and link should be negotiated. So setting 1 is a sane default when controller does not have configured 4 SerDes lines to PCIe.
Quoted 22 lines
Quoted 14 lines
drivers/pci/controller/pci-mvebu.c +3
-0+mvebu_writel (port, lnkcap,PCIE_CAP_PCIEXP +PCI_EXP_LNKCAP ); +/* Disable Root Bridge I/O space, memory space and bus mastering. */ cmd =mvebu_readl (port,PCIE_CMD_OFF ); cmd &= ~(PCI_COMMAND_IO |PCI_COMMAND_MEMORY |PCI_COMMAND_MASTER );@@ -986,6 +999,7 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,struct device *dev = &pcie->pdev->dev;enum of_gpio_flags flags;int reset_gpio, ret; +u32 num_lanes; port->pcie = pcie;drivers/pci/controller/pci-mvebu.c static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, · line 1016
Quoted 5 lines
drivers/pci/controller/pci-mvebu.c +2
-0@@ -998,6 +1012,9 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,if (of_property_read_u32 (child,"marvell,pcie-lane" , &port->lane)) port->lane =0 ; +if (!of_property_read_u32 (child,"num-lanes" , &num_lanes) && num_lanes ==4 ) + port->is_x4 = true;And this can be:
Quoted 5 lines
num_lanes= 1;of_property_read_u32(child, "num-lanes", &num_lanes);If you want to validate the DT is only 1 or 4, make the DT schema do that.
The problem is that there is no schema for this platform and PCIe yet. So adding it would mean to first convert everything to schema and then this constrain can be expressed in schema.
I'm planning to look at possibility to write schema for this platform but I do not want to do it before open issues with representation of other pcie properties in dts schema are resolved.
Quoted 9 lines
Quoted 9 lines
drivers/pci/controller/pci-mvebu.c +1
-0+ port->name =devm_kasprintf (dev,GFP_KERNEL ,"pcie%d.%d" , port->port, port->lane);if (!port->name) {Signature · 1 line
2.20.1
Rob Herring
maintainerRe: [PATCH v2 08/11] PCI: mvebu: Use child_ops API
In reply to Pali RohárQuoted 81 lines · Pali Rohár
Quoted 79 lines · On Thursday 20 January 2022 10:49:05 Rob Herring wrote:
Quoted 64 lines · Pali Rohár
Split struct
pci_opsbetween ops andchild_ops. Member ops is used for accessing PCIe Root Ports viapci-bridge-emul.cdriver andchild_opsfor accessing real PCIe cards.There is no need to mix these two struct
pci_opsinto one as PCI core code already provides separate callbacks viabridge->opsandbridge->child_ops.Signed-off-by Pali Rohár <[email protected]>--- drivers/pci/controller/pci-mvebu.c | 82 ++++++++++++++++-------------- 1 file changed, 44 insertions(+), 38 deletions(-)drivers/pci/controller/pci-mvebu.c +27
-8@@ -294,11 +294,29 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)mvebu_writel (port, mask,PCIE_MASK_OFF ); } -static int mvebu_pcie_hw_rd_conf (struct mvebu_pcie_port *port, -struct pci_bus *bus, -u32 devfn,int where,int size,u32 *val) +static struct mvebu_pcie_port *mvebu_pcie_find_port (struct mvebu_pcie *pcie, +struct pci_bus *bus, +int devfn); + +static int mvebu_pcie_child_rd_conf (struct pci_bus *bus,u32 devfn,int where, +int size,u32 *val) { -void __iomem *conf_data = port->base +PCIE_CONF_DATA_OFF ; +struct mvebu_pcie *pcie = bus->sysdata; +struct mvebu_pcie_port *port; +void __iomem *conf_data; + + port =mvebu_pcie_find_port (pcie, bus, devfn); +if (!port) { + *val =0xffffffff ; +return PCIBIOS_DEVICE_NOT_FOUND ; + } + +if (!mvebu_pcie_link_up (port)) { + *val =0xffffffff ; +return PCIBIOS_DEVICE_NOT_FOUND ; + } + + conf_data = port->base +PCIE_CONF_DATA_OFF ;mvebu_writel (port,PCIE_CONF_ADDR (bus->number, devfn, where),PCIE_CONF_ADDR_OFF );@@ -321,11 +339,21 @@ static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port,return PCIBIOS_SUCCESSFUL ; } -static int mvebu_pcie_hw_wr_conf (struct mvebu_pcie_port *port, -struct pci_bus *bus, -u32 devfn,int where,int size,u32 val) +static int mvebu_pcie_child_wr_conf (struct pci_bus *bus,u32 devfn, +int where,int size,u32 val) { -void __iomem *conf_data = port->base +PCIE_CONF_DATA_OFF ; +struct mvebu_pcie *pcie = bus->sysdata; +struct mvebu_pcie_port *port; +void __iomem *conf_data;drivers/pci/controller/pci-mvebu.c static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port, · line 357
Quoted 10 lines
drivers/pci/controller/pci-mvebu.c +9
-0+ + port =mvebu_pcie_find_port (pcie, bus, devfn); +if (!port) +return PCIBIOS_DEVICE_NOT_FOUND ; + +if (!mvebu_pcie_link_up (port)) +return PCIBIOS_DEVICE_NOT_FOUND ; + + conf_data = port->base +PCIE_CONF_DATA_OFF ;Again, the same setup code in read and write is a sign to use .map_bus(). You can copy it from my version I pointed you to.
Quoted 5 lines
Quoted 1 line
Rob
I'm planning to do other cleanup in followup patches. But there are too many mvebu and aardvark patches on the list waiting, and I do not want to send another batch.
It can all be part of this patch.
Rob
Re: [PATCH v2 10/11] PCI: mvebu: Implement support for legacy INTx interrupts
In reply to Pali RohárQuoted 6 lines · Pali Rohár
This adds support for legacy INTx interrupts received from other PCIe devices and which are reported by a new INTx irq chip.
With this change, kernel can distinguish between INTA, INTB, INTC and INTD interrupts.
Quoted 7 lines
Note that for this support, device tree files has to be properly adjusted to provide "interrupts" or "interrupts-extended" property with intx interrupt source, "interrupt-names" property with "intx" string and also 'interrupt-controller' subnode must be defined.
If device tree files do not provide these nodes then driver would work as before.
Nit: this information is not useful. DT rules are written in DT bindings, not in kernel commit logs. All I am saying is that firmware developers should not have to read this log to write firmware.
Quoted 10 lines
Signed-off-by Pali Rohár <[email protected]>--- drivers/pci/controller/pci-mvebu.c | 185 +++++++++++++++++++++++++++-- 1 file changed, 177 insertions(+), 8 deletions(-)drivers/pci/controller/pci-mvebu.c +0
-0
drivers/pci/controller/pci-mvebu.c line 57
Quoted 4 lines
drivers/pci/controller/pci-mvebu.c +1
-0@@ -54,9 +54,10 @@ PCIE_CONF_ADDR_EN)#define PCIE_CONF_DATA_OFF 0x18fc#define PCIE_INT_CAUSE_OFF 0x1900 +#define PCIE_INT_UNMASK_OFF 0x1910
Nit: I understand it is tempting but here you are redefining or better giving a proper label to a register. Separate patch please.
drivers/pci/controller/pci-mvebu.c line 58
Quoted 3 lines
drivers/pci/controller/pci-mvebu.c +1
-1+#define PCIE_INT_INTX (i) BIT(24+i)#define PCIE_INT_PM_PME BIT(28) -#define PCIE_MASK_OFF 0x1910
See above.
Quoted 41 lines
drivers/pci/controller/pci-mvebu.c +14
-6-#define PCIE_MASK_ENABLE_INTS 0x0f000000 +#define PCIE_INT_ALL_MASK GENMASK(31, 0)#define PCIE_CTRL_OFF 0x1a00#define PCIE_CTRL_X1_MODE 0x0001#define PCIE_CTRL_RC_MODE BIT(1)@@ -110,6 +111,9 @@ struct mvebu_pcie_port {struct mvebu_pcie_window iowin;u32 saved_pcie_stat;struct resource regs; +struct irq_domain *intx_irq_domain; +raw_spinlock_t irq_lock; +int intx_irq; };static inline void mvebu_writel (struct mvebu_pcie_port *port,u32 val,u32 reg)@@ -235,7 +239,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, lnkcap, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, unmask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF );@@ -288,10 +292,30 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)/* Point PCIe unit MBUS decode windows to DRAM space. */ mvebu_pcie_setup_wins (port); -/* Enable interrupt lines A-D. */ - mask =mvebu_readl (port,PCIE_MASK_OFF ); - mask |=PCIE_MASK_ENABLE_INTS ; -mvebu_writel (port, mask,PCIE_MASK_OFF ); +/* Mask all interrupt sources. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (port->intx_irq <=0 ) { +/* +* When neither "summary" interrupt, nor "intx" interrupt was
drivers/pci/controller/pci-mvebu.c static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) · line 313
Quoted 10 lines
drivers/pci/controller/pci-mvebu.c +10
-0+* specified in DT then unmask all legacy INTx interrupts as in +* this case driver does not provide a way for masking and +* unmasking of individual legacy INTx interrupts. In this case +* all interrupts, including legacy INTx are reported via one +* shared GIC source and therefore kernel cannot distinguish +* which individual legacy INTx was triggered. These interrupts +* are shared, so it should not cause any issue. Just +* performance penalty as every PCIe interrupt handler needs to +* be called when some interrupt is triggered. +*/
This comment applies to current mainline right (ie it describes how current mainline handles INTx) ? IMO you should split it out in a separate patch.
I understand it is hard but a patch is a logical _change_, this comment is a change per se, it is a clarification on current behaviour.
Quoted 124 lines
drivers/pci/controller/pci-mvebu.c +110
-0+ unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (0 ) |PCIE_INT_INTX (1 ) | +PCIE_INT_INTX (2 ) |PCIE_INT_INTX (3 ); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); + } } static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,@@ -924,6 +948,108 @@ static struct pci_ops mvebu_pcie_ops = { .write = mvebu_pcie_wr_conf, }; +static void mvebu_pcie_intx_irq_mask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask &= ~PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static void mvebu_pcie_intx_irq_unmask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static struct irq_chip intx_irq_chip = { + .name ="mvebu-INTx" , + .irq_mask = mvebu_pcie_intx_irq_mask, + .irq_unmask = mvebu_pcie_intx_irq_unmask, +}; + +static int mvebu_pcie_intx_irq_map (struct irq_domain *h, +unsigned int virq,irq_hw_number_t hwirq) +{ +struct mvebu_pcie_port *port = h->host_data; + +irq_set_status_flags (virq,IRQ_LEVEL ); +irq_set_chip_and_handler (virq, &intx_irq_chip, handle_level_irq); +irq_set_chip_data (virq, port); + +return 0 ; +} + +static const struct irq_domain_ops mvebu_pcie_intx_irq_domain_ops = { + .map = mvebu_pcie_intx_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int mvebu_pcie_init_irq_domain (struct mvebu_pcie_port *port) +{ +struct device *dev = &port->pcie->pdev->dev; +struct device_node *pcie_intc_node; + +raw_spin_lock_init (&port->irq_lock); + + pcie_intc_node =of_get_next_child (port->dn,NULL ); +if (!pcie_intc_node) { +dev_err (dev,"No PCIe Intc node found for %s\n" , port->name); +return -ENODEV ; + } + + port->intx_irq_domain =irq_domain_add_linear (pcie_intc_node,PCI_NUM_INTX , + &mvebu_pcie_intx_irq_domain_ops, + port); +of_node_put (pcie_intc_node); +if (!port->intx_irq_domain) { +dev_err (dev,"Failed to get INTx IRQ domain for %s\n" , port->name); +return -ENOMEM ; + } + +return 0 ; +} + +static void mvebu_pcie_irq_handler (struct irq_desc *desc) +{ +struct mvebu_pcie_port *port =irq_desc_get_handler_data (desc); +struct irq_chip *chip =irq_desc_get_chip (desc); +struct device *dev = &port->pcie->pdev->dev; +u32 cause, unmask, status; +int i; + +chained_irq_enter (chip, desc); + + cause =mvebu_readl (port,PCIE_INT_CAUSE_OFF ); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + status = cause & unmask; + +/* Process legacy INTx interrupts */ +for (i =0 ; i <PCI_NUM_INTX ; i++) { +if (!(status &PCIE_INT_INTX (i))) +continue ; + +if (generic_handle_domain_irq (port->intx_irq_domain, i) == -EINVAL ) +dev_err_ratelimited (dev,"unexpected INT%c IRQ\n" , (char )i+'A' ); + } + +chained_irq_exit (chip, desc); +} +static int mvebu_pcie_map_irq (const struct pci_dev *dev,u8 slot,u8 pin) {/* Interrupt support on mvebu emulated bridges is not implemented yet */ @@ -1121,6 +1247,21 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, port->io_attr =-1 ; } +/* +* Old DT bindings do not contain "intx" interrupt +* so do not fail probing driver when interrupt does not exist.
drivers/pci/controller/pci-mvebu.c static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, · line 1262
Quoted 10 lines
drivers/pci/controller/pci-mvebu.c +10
-0+*/ + port->intx_irq =of_irq_get_byname (child,"intx" ); +if (port->intx_irq == -EPROBE_DEFER ) { + ret = port->intx_irq; + goto err; + } +if (port->intx_irq <=0 ) { +dev_warn (dev,"%s: legacy INTx interrupts cannot be masked individually, " +"%pOF does not contain intx interrupt\n" , + port->name, child);
Here you end up with a new warning on existing firmware. Is it legitimate ? I would remove the dev_warn().
Rob certainly has more insightful advice on this.
Thanks,
Lorenzo
Quoted 66 lines
drivers/pci/controller/pci-mvebu.c +31
-1+ } + reset_gpio =of_get_named_gpio_flags (child,"reset-gpios" ,0 , &flags);if (reset_gpio == -EPROBE_DEFER ) { ret = reset_gpio;@@ -1317,6 +1458,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)for (i =0 ; i < pcie->nports; i++) {struct mvebu_pcie_port *port = &pcie->ports[i]; +int irq = port->intx_irq; child = port->dn;if (!child)@@ -1344,6 +1486,22 @@ static int mvebu_pcie_probe(struct platform_device *pdev)continue ; } +if (irq >0 ) { + ret =mvebu_pcie_init_irq_domain (port); +if (ret) { +dev_err (dev,"%s: cannot init irq domain\n" , + port->name); +pci_bridge_emul_cleanup (&port->bridge); +devm_iounmap (dev, port->base); + port->base =NULL ; +mvebu_pcie_powerdown (port); +continue ; + } +irq_set_chained_handler_and_data (irq, + mvebu_pcie_irq_handler, + port); + } +/* * PCIe topology exported by mvebu hw is quite complicated. In * reality has something like N fully independent host bridges @@ -1448,6 +1606,7 @@ static int mvebu_pcie_remove(struct platform_device *pdev)for (i =0 ; i < pcie->nports; i++) {struct mvebu_pcie_port *port = &pcie->ports[i]; +int irq = port->intx_irq;if (!port->base)continue ;@@ -1458,7 +1617,17 @@ static int mvebu_pcie_remove(struct platform_device *pdev)mvebu_writel (port, cmd,PCIE_CMD_OFF );/* Mask all interrupt sources. */ -mvebu_writel (port,0 ,PCIE_MASK_OFF ); +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (irq >0 ) +irq_set_chained_handler_and_data (irq,NULL ,NULL ); + +/* Remove IRQ domains. */ +if (port->intx_irq_domain) +irq_domain_remove (port->intx_irq_domain);/* Free config space for emulated root bridge. */ pci_bridge_emul_cleanup (&port->bridge);Signature · 1 line
2.20.1
Quoted 8 lines · Pali Rohár
This patch series extends
pci-bridge-emul.cdriver to emulate PCI Subsystem Vendor ID capability and PCIe extended capabilities. And then implement inpci-mvebu.cdriver support for PCI Subsystem Vendor IDs, PCIe AER registers, support for legacy INTx interrupts, configuration for X1/X4 mode and usage of new PCIchild_opsAPI.This patch series depends on other pci-mvebu and pci-bridge-emul patches from:
https://lore.kernel.org/linux-pci/[email protected]/
Hi Pali,
I went through the series quickly and I don't think there is anything controversial, posted some minor comments. Do not repost yet, I will be back at v5.17-rc5 and work towards merging it for v5.18.
Lorenzo
Quoted 30 lines
Pali Rohár (9):
PCI: pci-bridge-emul: Add support for PCI Bridge Subsystem Vendor ID
capability
dt-bindings: PCI: mvebu: Add num-lanes property
PCI: mvebu: Correctly configure x1/x4 mode
PCI: mvebu: Add support for PCI Bridge Subsystem Vendor ID on emulated
bridge
PCI: mvebu: Add support for Advanced Error Reporting registers on
emulated bridge
PCI: mvebu: Usechild_opsAPI
dt-bindings: PCI: mvebu: Update information about intx interrupts
PCI: mvebu: Implement support for legacy INTx interrupts
ARM: dts:armada-385.dtsi: Add definitions for PCIe legacy INTx
interruptsRussell King (2):
PCI: pci-bridge-emul: Re-arrange register tests
PCI: pci-bridge-emul: Add support for PCIe extended capabilities.../devicetree/bindings/pci/mvebu-pci.txt | 16 + arch/arm/boot/dts/armada-385.dtsi | 52 ++- drivers/pci/controller/pci-mvebu.c | 352 +++++++++++++++--- drivers/pci/pci-bridge-emul.c | 167 ++++++--- drivers/pci/pci-bridge-emul.h | 17 + 5 files changed, 494 insertions(+), 110 deletions(-)Signature · 1 line
2.20.1
Pali Rohár
maintainerRe: [PATCH v2 10/11] PCI: mvebu: Implement support for legacy INTx interrupts
In reply to Lorenzo PieralisiQuoted 12 lines · On Friday 11 February 2022 17:19:17 Lorenzo Pieralisi wrote:
Quoted 11 lines · Pali Rohár
This adds support for legacy INTx interrupts received from other PCIe devices and which are reported by a new INTx irq chip.
With this change, kernel can distinguish between INTA, INTB, INTC and INTD interrupts.
Note that for this support, device tree files has to be properly adjusted to provide "interrupts" or "interrupts-extended" property with intx interrupt source, "interrupt-names" property with "intx" string and also 'interrupt-controller' subnode must be defined.
Quoted 6 lines
Quoted 2 lines
If device tree files do not provide these nodes then driver would work as before.
Nit: this information is not useful. DT rules are written in DT bindings, not in kernel commit logs. All I am saying is that firmware developers should not have to read this log to write firmware.
It was not intended for firmware developers, but for reviewers of this patch to understand, what is happening in code and that with old DT files this patch does not change driver behavior (= work as before).
Quoted 5 lines
Quoted 5 lines
Signed-off-by Pali Rohár <[email protected]>--- drivers/pci/controller/pci-mvebu.c | 185 +++++++++++++++++++++++++++-- 1 file changed, 177 insertions(+), 8 deletions(-)
Quoted 12 lines
drivers/pci/controller/pci-mvebu.c line 57
Quoted 9 lines
drivers/pci/controller/pci-mvebu.c +1
-0@@ -54,9 +54,10 @@ PCIE_CONF_ADDR_EN)#define PCIE_CONF_DATA_OFF 0x18fc#define PCIE_INT_CAUSE_OFF 0x1900 +#define PCIE_INT_UNMASK_OFF 0x1910Nit: I understand it is tempting but here you are redefining or better giving a proper label to a register. Separate patch please.
Ok!
Quoted 53 lines
drivers/pci/controller/pci-mvebu.c line 58
Quoted 3 lines
drivers/pci/controller/pci-mvebu.c +1
-1+#define PCIE_INT_INTX (i) BIT(24+i)#define PCIE_INT_PM_PME BIT(28) -#define PCIE_MASK_OFF 0x1910See above.
Quoted 47 lines
drivers/pci/controller/pci-mvebu.c +20
-6-#define PCIE_MASK_ENABLE_INTS 0x0f000000 +#define PCIE_INT_ALL_MASK GENMASK(31, 0)#define PCIE_CTRL_OFF 0x1a00#define PCIE_CTRL_X1_MODE 0x0001#define PCIE_CTRL_RC_MODE BIT(1)@@ -110,6 +111,9 @@ struct mvebu_pcie_port {struct mvebu_pcie_window iowin;u32 saved_pcie_stat;struct resource regs; +struct irq_domain *intx_irq_domain; +raw_spinlock_t irq_lock; +int intx_irq; };static inline void mvebu_writel (struct mvebu_pcie_port *port,u32 val,u32 reg)@@ -235,7 +239,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, lnkcap, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, unmask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF );@@ -288,10 +292,30 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)/* Point PCIe unit MBUS decode windows to DRAM space. */ mvebu_pcie_setup_wins (port); -/* Enable interrupt lines A-D. */ - mask =mvebu_readl (port,PCIE_MASK_OFF ); - mask |=PCIE_MASK_ENABLE_INTS ; -mvebu_writel (port, mask,PCIE_MASK_OFF ); +/* Mask all interrupt sources. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (port->intx_irq <=0 ) { +/* +* When neither "summary" interrupt, nor "intx" interrupt was +* specified in DT then unmask all legacy INTx interrupts as in +* this case driver does not provide a way for masking and +* unmasking of individual legacy INTx interrupts. In this case +* all interrupts, including legacy INTx are reported via one +* shared GIC source and therefore kernel cannot distinguish +* which individual legacy INTx was triggered. These interrupts
Quoted 8 lines
drivers/pci/controller/pci-mvebu.c static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) · line 313
Quoted 4 lines
drivers/pci/controller/pci-mvebu.c +4
-0+* are shared, so it should not cause any issue. Just +* performance penalty as every PCIe interrupt handler needs to +* be called when some interrupt is triggered. +*/ This comment applies to current mainline right (ie it describes how current mainline handles INTx) ? IMO you should split it out in a separate patch.
This above comment describe what happens in if-branch when intx_irq is not set (as written in comment "when intx interrupt was not specified in DT"). You are right that this is also the behavior in the current mainline.
I'm not sure if this comment can be split out as support for "intx" interrupt is in this patch.
Quoted 3 lines
I understand it is hard but a patch is a logical _change_, this comment is a change per se, it is a clarification on current behaviour.
Ok, I could try to split this comment into two patches, but part about if-branch comment needs to stay in "this" patch.
Quoted 121 lines
Quoted 121 lines
drivers/pci/controller/pci-mvebu.c +107
-0+ unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (0 ) |PCIE_INT_INTX (1 ) | +PCIE_INT_INTX (2 ) |PCIE_INT_INTX (3 ); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); + } } static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,@@ -924,6 +948,108 @@ static struct pci_ops mvebu_pcie_ops = { .write = mvebu_pcie_wr_conf, }; +static void mvebu_pcie_intx_irq_mask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask &= ~PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static void mvebu_pcie_intx_irq_unmask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static struct irq_chip intx_irq_chip = { + .name ="mvebu-INTx" , + .irq_mask = mvebu_pcie_intx_irq_mask, + .irq_unmask = mvebu_pcie_intx_irq_unmask, +}; + +static int mvebu_pcie_intx_irq_map (struct irq_domain *h, +unsigned int virq,irq_hw_number_t hwirq) +{ +struct mvebu_pcie_port *port = h->host_data; + +irq_set_status_flags (virq,IRQ_LEVEL ); +irq_set_chip_and_handler (virq, &intx_irq_chip, handle_level_irq); +irq_set_chip_data (virq, port); + +return 0 ; +} + +static const struct irq_domain_ops mvebu_pcie_intx_irq_domain_ops = { + .map = mvebu_pcie_intx_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int mvebu_pcie_init_irq_domain (struct mvebu_pcie_port *port) +{ +struct device *dev = &port->pcie->pdev->dev; +struct device_node *pcie_intc_node; + +raw_spin_lock_init (&port->irq_lock); + + pcie_intc_node =of_get_next_child (port->dn,NULL ); +if (!pcie_intc_node) { +dev_err (dev,"No PCIe Intc node found for %s\n" , port->name); +return -ENODEV ; + } + + port->intx_irq_domain =irq_domain_add_linear (pcie_intc_node,PCI_NUM_INTX , + &mvebu_pcie_intx_irq_domain_ops, + port); +of_node_put (pcie_intc_node); +if (!port->intx_irq_domain) { +dev_err (dev,"Failed to get INTx IRQ domain for %s\n" , port->name); +return -ENOMEM ; + } + +return 0 ; +} + +static void mvebu_pcie_irq_handler (struct irq_desc *desc) +{ +struct mvebu_pcie_port *port =irq_desc_get_handler_data (desc); +struct irq_chip *chip =irq_desc_get_chip (desc); +struct device *dev = &port->pcie->pdev->dev; +u32 cause, unmask, status; +int i; + +chained_irq_enter (chip, desc); + + cause =mvebu_readl (port,PCIE_INT_CAUSE_OFF ); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + status = cause & unmask; + +/* Process legacy INTx interrupts */ +for (i =0 ; i <PCI_NUM_INTX ; i++) { +if (!(status &PCIE_INT_INTX (i))) +continue ; + +if (generic_handle_domain_irq (port->intx_irq_domain, i) == -EINVAL ) +dev_err_ratelimited (dev,"unexpected INT%c IRQ\n" , (char )i+'A' ); + } + +chained_irq_exit (chip, desc); +} +static int mvebu_pcie_map_irq (const struct pci_dev *dev,u8 slot,u8 pin) {/* Interrupt support on mvebu emulated bridges is not implemented yet */ @@ -1121,6 +1247,21 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, port->io_attr =-1 ; }
Quoted 16 lines
drivers/pci/controller/pci-mvebu.c static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, · line 1262
Quoted 13 lines
drivers/pci/controller/pci-mvebu.c +13
-0+/* +* Old DT bindings do not contain "intx" interrupt +* so do not fail probing driver when interrupt does not exist. +*/ + port->intx_irq =of_irq_get_byname (child,"intx" ); +if (port->intx_irq == -EPROBE_DEFER ) { + ret = port->intx_irq; + goto err; + } +if (port->intx_irq <=0 ) { +dev_warn (dev,"%s: legacy INTx interrupts cannot be masked individually, " +"%pOF does not contain intx interrupt\n" , + port->name, child);Here you end up with a new warning on existing firmware. Is it legitimate ? I would remove the
dev_warn().
I added this warning in v2 because Marc wanted it.
Should I (again) remove it in v3?
Quoted 71 lines
Rob certainly has more insightful advice on this.
Thanks,
LorenzoQuoted 66 lines
drivers/pci/controller/pci-mvebu.c +31
-1+ } + reset_gpio =of_get_named_gpio_flags (child,"reset-gpios" ,0 , &flags);if (reset_gpio == -EPROBE_DEFER ) { ret = reset_gpio;@@ -1317,6 +1458,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)for (i =0 ; i < pcie->nports; i++) {struct mvebu_pcie_port *port = &pcie->ports[i]; +int irq = port->intx_irq; child = port->dn;if (!child)@@ -1344,6 +1486,22 @@ static int mvebu_pcie_probe(struct platform_device *pdev)continue ; } +if (irq >0 ) { + ret =mvebu_pcie_init_irq_domain (port); +if (ret) { +dev_err (dev,"%s: cannot init irq domain\n" , + port->name); +pci_bridge_emul_cleanup (&port->bridge); +devm_iounmap (dev, port->base); + port->base =NULL ; +mvebu_pcie_powerdown (port); +continue ; + } +irq_set_chained_handler_and_data (irq, + mvebu_pcie_irq_handler, + port); + } +/* * PCIe topology exported by mvebu hw is quite complicated. In * reality has something like N fully independent host bridges @@ -1448,6 +1606,7 @@ static int mvebu_pcie_remove(struct platform_device *pdev)for (i =0 ; i < pcie->nports; i++) {struct mvebu_pcie_port *port = &pcie->ports[i]; +int irq = port->intx_irq;if (!port->base)continue ;@@ -1458,7 +1617,17 @@ static int mvebu_pcie_remove(struct platform_device *pdev)mvebu_writel (port, cmd,PCIE_CMD_OFF );/* Mask all interrupt sources. */ -mvebu_writel (port,0 ,PCIE_MASK_OFF ); +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (irq >0 ) +irq_set_chained_handler_and_data (irq,NULL ,NULL ); + +/* Remove IRQ domains. */ +if (port->intx_irq_domain) +irq_domain_remove (port->intx_irq_domain);/* Free config space for emulated root bridge. */ pci_bridge_emul_cleanup (&port->bridge);Signature · 1 line
2.20.1
Pali Rohár
maintainerQuoted 1 line · On Friday 11 February 2022 17:50:45 Lorenzo Pieralisi wrote:
Do not repost yet, I will be back at v5.17-rc5 and work towards merging it for v5.18.
Ok!
Re: [PATCH v2 10/11] PCI: mvebu: Implement support for legacy INTx interrupts
In reply to Pali RohárOn Fri, Feb 11, 2022 at 06:52:02PM +0100, Pali Rohár wrote:
[...]
Quoted 21 lines
Quoted 4 lines
Quoted 4 lines
drivers/pci/controller/pci-mvebu.c +0
-0@@ -1121,6 +1247,21 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, port->io_attr =-1 ; }Quoted 16 lines
drivers/pci/controller/pci-mvebu.c static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, · line 1262
Quoted 13 lines
drivers/pci/controller/pci-mvebu.c +13
-0+/* +* Old DT bindings do not contain "intx" interrupt +* so do not fail probing driver when interrupt does not exist. +*/ + port->intx_irq =of_irq_get_byname (child,"intx" ); +if (port->intx_irq == -EPROBE_DEFER ) { + ret = port->intx_irq; + goto err; + } +if (port->intx_irq <=0 ) { +dev_warn (dev,"%s: legacy INTx interrupts cannot be masked individually, " +"%pOF does not contain intx interrupt\n" , + port->name, child);Here you end up with a new warning on existing firmware. Is it legitimate ? I would remove the
dev_warn().
Quoted 3 lines
I added this warning in v2 because Marc wanted it.
Should I (again) remove it in v3?
No, I asked a question and gave an opinion, I appreciate Marc's concern so leave it (ie not everyone running a new kernel with new warnings on existing firmware would be happy - maybe it is a good way of forcing a firmware upgrade, you will tell me).
Lorenzo
Quoted 71 lines
Quoted 71 lines
Rob certainly has more insightful advice on this.
Thanks,
LorenzoQuoted 66 lines
drivers/pci/controller/pci-mvebu.c +31
-1+ } + reset_gpio =of_get_named_gpio_flags (child,"reset-gpios" ,0 , &flags);if (reset_gpio == -EPROBE_DEFER ) { ret = reset_gpio;@@ -1317,6 +1458,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)for (i =0 ; i < pcie->nports; i++) {struct mvebu_pcie_port *port = &pcie->ports[i]; +int irq = port->intx_irq; child = port->dn;if (!child)@@ -1344,6 +1486,22 @@ static int mvebu_pcie_probe(struct platform_device *pdev)continue ; } +if (irq >0 ) { + ret =mvebu_pcie_init_irq_domain (port); +if (ret) { +dev_err (dev,"%s: cannot init irq domain\n" , + port->name); +pci_bridge_emul_cleanup (&port->bridge); +devm_iounmap (dev, port->base); + port->base =NULL ; +mvebu_pcie_powerdown (port); +continue ; + } +irq_set_chained_handler_and_data (irq, + mvebu_pcie_irq_handler, + port); + } +/* * PCIe topology exported by mvebu hw is quite complicated. In * reality has something like N fully independent host bridges @@ -1448,6 +1606,7 @@ static int mvebu_pcie_remove(struct platform_device *pdev)for (i =0 ; i < pcie->nports; i++) {struct mvebu_pcie_port *port = &pcie->ports[i]; +int irq = port->intx_irq;if (!port->base)continue ;@@ -1458,7 +1617,17 @@ static int mvebu_pcie_remove(struct platform_device *pdev)mvebu_writel (port, cmd,PCIE_CMD_OFF );/* Mask all interrupt sources. */ -mvebu_writel (port,0 ,PCIE_MASK_OFF ); +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (irq >0 ) +irq_set_chained_handler_and_data (irq,NULL ,NULL ); + +/* Remove IRQ domains. */ +if (port->intx_irq_domain) +irq_domain_remove (port->intx_irq_domain);/* Free config space for emulated root bridge. */ pci_bridge_emul_cleanup (&port->bridge);Signature · 1 line
2.20.1
Re: [PATCH v2 10/11] PCI: mvebu: Implement support for legacy INTx interrupts
Quoted 26 lines · Lorenzo Pieralisi
On Fri, Feb 11, 2022 at 06:52:02PM +0100, Pali Rohár wrote:
[...]
Quoted 21 lines
Quoted 20 lines
Quoted 7 lines
drivers/pci/controller/pci-mvebu.c +3
-0@@ -1121,6 +1247,21 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, port->io_attr =-1 ; } +/* +* Old DT bindings do not contain "intx" interrupt +* so do not fail probing driver when interrupt does not exist. drivers/pci/controller/pci-mvebu.c static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, · line 1262
Quoted 10 lines
drivers/pci/controller/pci-mvebu.c +10
-0+*/ + port->intx_irq =of_irq_get_byname (child,"intx" ); +if (port->intx_irq == -EPROBE_DEFER ) { + ret = port->intx_irq; + goto err; + } +if (port->intx_irq <=0 ) { +dev_warn (dev,"%s: legacy INTx interrupts cannot be masked individually, " +"%pOF does not contain intx interrupt\n" , + port->name, child);Here you end up with a new warning on existing firmware. Is it legitimate ? I would remove the
dev_warn().
Quoted 8 lines
Quoted 3 lines
I added this warning in v2 because Marc wanted it.
Should I (again) remove it in v3?
No, I asked a question and gave an opinion, I appreciate Marc's concern so leave it (ie not everyone running a new kernel with new warnings on existing firmware would be happy - maybe it is a good way of forcing a firmware upgrade, you will tell me).
My concern is that short of being able to mask these interrupts, it is possible for a device to assert an interrupt that no driver handles, and the kernel spurious interrupt detector won't be able to shut it up. At this stage, the machine is totally dead (screaming *level* interrupt).
The dev_warn() could toned down to a dev_warn_once() though.
M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
Re: [PATCH v2 11/11] ARM: dts: armada-385.dtsi: Add definitions for PCIe legacy INTx interrupts
In reply to Pali RohárHello Pali,
Quoted 6 lines
With this change legacy INTA, INTB, INTC and INTD interrupts are reported separately and not mixed into one Linux virq source anymore.
Signed-off-by Pali Rohár <[email protected]>--- arch/arm/boot/dts/armada-385.dtsi | 52 ++++++++++++++++++++++++++-----
Is there any reason for not doing the same change in armada-380.dtsi ?
Grégory
Quoted 121 lines
1 file changed, 44 insertions(+), 8 deletions(-)arch/arm/boot/dts/armada-385.dtsi +44
-8@@ -69,16 +69,25 @@ reg = <0x0800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 0x81000000 0 0 0x81000000 0x1 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie1_intc 0>, + <0 0 0 2 &pcie1_intc 1>, + <0 0 0 3 &pcie1_intc 2>, + <0 0 0 4 &pcie1_intc 3>; marvell,pcie-port = <0>; marvell,pcie-lane = <0>; clocks = <&gateclk 8>; status = "disabled"; + pcie1_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /* x1 port */@@ -88,16 +97,25 @@ reg = <0x1000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 0x81000000 0 0 0x81000000 0x2 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie2_intc 0>, + <0 0 0 2 &pcie2_intc 1>, + <0 0 0 3 &pcie2_intc 2>, + <0 0 0 4 &pcie2_intc 3>; marvell,pcie-port = <1>; marvell,pcie-lane = <0>; clocks = <&gateclk 5>; status = "disabled"; + pcie2_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /* x1 port */@@ -107,16 +125,25 @@ reg = <0x1800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 0x81000000 0 0 0x81000000 0x3 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie3_intc 0>, + <0 0 0 2 &pcie3_intc 1>, + <0 0 0 3 &pcie3_intc 2>, + <0 0 0 4 &pcie3_intc 3>; marvell,pcie-port = <2>; marvell,pcie-lane = <0>; clocks = <&gateclk 6>; status = "disabled"; + pcie3_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /*@@ -129,16 +156,25 @@ reg = <0x2000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0 0x81000000 0 0 0x81000000 0x4 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie4_intc 0>, + <0 0 0 2 &pcie4_intc 1>, + <0 0 0 3 &pcie4_intc 2>, + <0 0 0 4 &pcie4_intc 3>; marvell,pcie-port = <3>; marvell,pcie-lane = <0>; clocks = <&gateclk 7>; status = "disabled"; + pcie4_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; }; };Signature · 1 line
2.20.1
Signature · 3 lines
Gregory Clement, Bootlin Embedded Linux and Kernel engineering http://bootlin.com
Pali Rohár
maintainerRe: [PATCH v2 11/11] ARM: dts: armada-385.dtsi: Add definitions for PCIe legacy INTx interrupts
Quoted 10 lines · On Monday 14 February 2022 16:07:13 Gregory CLEMENT wrote:
Hello Pali,
Quoted 6 lines
With this change legacy INTA, INTB, INTC and INTD interrupts are reported separately and not mixed into one Linux virq source anymore.
Signed-off-by Pali Rohár <[email protected]>--- arch/arm/boot/dts/armada-385.dtsi | 52 ++++++++++++++++++++++++++-----Is there any reason for not doing the same change in
armada-380.dtsi?
I do not have A380 HW, so I did this change only for A385 which I have tested.
Quoted 128 lines
Grégory
Quoted 121 lines
1 file changed, 44 insertions(+), 8 deletions(-)arch/arm/boot/dts/armada-385.dtsi +44
-8@@ -69,16 +69,25 @@ reg = <0x0800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 0x81000000 0 0 0x81000000 0x1 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie1_intc 0>, + <0 0 0 2 &pcie1_intc 1>, + <0 0 0 3 &pcie1_intc 2>, + <0 0 0 4 &pcie1_intc 3>; marvell,pcie-port = <0>; marvell,pcie-lane = <0>; clocks = <&gateclk 8>; status = "disabled"; + pcie1_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /* x1 port */@@ -88,16 +97,25 @@ reg = <0x1000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 0x81000000 0 0 0x81000000 0x2 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie2_intc 0>, + <0 0 0 2 &pcie2_intc 1>, + <0 0 0 3 &pcie2_intc 2>, + <0 0 0 4 &pcie2_intc 3>; marvell,pcie-port = <1>; marvell,pcie-lane = <0>; clocks = <&gateclk 5>; status = "disabled"; + pcie2_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /* x1 port */@@ -107,16 +125,25 @@ reg = <0x1800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 0x81000000 0 0 0x81000000 0x3 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie3_intc 0>, + <0 0 0 2 &pcie3_intc 1>, + <0 0 0 3 &pcie3_intc 2>, + <0 0 0 4 &pcie3_intc 3>; marvell,pcie-port = <2>; marvell,pcie-lane = <0>; clocks = <&gateclk 6>; status = "disabled"; + pcie3_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /*@@ -129,16 +156,25 @@ reg = <0x2000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0 0x81000000 0 0 0x81000000 0x4 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie4_intc 0>, + <0 0 0 2 &pcie4_intc 1>, + <0 0 0 3 &pcie4_intc 2>, + <0 0 0 4 &pcie4_intc 3>; marvell,pcie-port = <3>; marvell,pcie-lane = <0>; clocks = <&gateclk 7>; status = "disabled"; + pcie4_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; }; };Signature · 1 line
2.20.1Signature · 3 lines
Gregory Clement, Bootlin Embedded Linux and Kernel engineering http://bootlin.com
Gregory CLEMENT
Acked-byRe: [PATCH v2 11/11] ARM: dts: armada-385.dtsi: Add definitions for PCIe legacy INTx interrupts
Hello,
Quoted 6 lines · On Monday 14 February 2022 16:07:13 Gregory CLEMENT wrote:
Quoted 5 lines
Hello Pali,
Quoted 3 lines
With this change legacy INTA, INTB, INTC and INTD interrupts are reported separately and not mixed into one Linux virq source anymore.
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
Signed-off-by Pali Rohár <[email protected]>--- arch/arm/boot/dts/armada-385.dtsi | 52 ++++++++++++++++++++++++++-----Is there any reason for not doing the same change in
armada-380.dtsi?I do not have A380 HW, so I did this change only for A385 which I have tested.
OK fair enough.
So you can add my
Moreover to keep biscetability this patch should be merged after the support in the driver. So the easier is to let merge it through the PCI subsystem with the other patches from this series. I do not think there will be any other changes in this file so there won't be any merge conflicts.
Thanks,
Grégory
Quoted 129 lines
Quoted 129 lines
Grégory
Quoted 121 lines
1 file changed, 44 insertions(+), 8 deletions(-)arch/arm/boot/dts/armada-385.dtsi +44
-8@@ -69,16 +69,25 @@ reg = <0x0800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 0x81000000 0 0 0x81000000 0x1 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie1_intc 0>, + <0 0 0 2 &pcie1_intc 1>, + <0 0 0 3 &pcie1_intc 2>, + <0 0 0 4 &pcie1_intc 3>; marvell,pcie-port = <0>; marvell,pcie-lane = <0>; clocks = <&gateclk 8>; status = "disabled"; + pcie1_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /* x1 port */@@ -88,16 +97,25 @@ reg = <0x1000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 0x81000000 0 0 0x81000000 0x2 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie2_intc 0>, + <0 0 0 2 &pcie2_intc 1>, + <0 0 0 3 &pcie2_intc 2>, + <0 0 0 4 &pcie2_intc 3>; marvell,pcie-port = <1>; marvell,pcie-lane = <0>; clocks = <&gateclk 5>; status = "disabled"; + pcie2_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /* x1 port */@@ -107,16 +125,25 @@ reg = <0x1800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 0x81000000 0 0 0x81000000 0x3 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie3_intc 0>, + <0 0 0 2 &pcie3_intc 1>, + <0 0 0 3 &pcie3_intc 2>, + <0 0 0 4 &pcie3_intc 3>; marvell,pcie-port = <2>; marvell,pcie-lane = <0>; clocks = <&gateclk 6>; status = "disabled"; + pcie3_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /*@@ -129,16 +156,25 @@ reg = <0x2000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0 0x81000000 0 0 0x81000000 0x4 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie4_intc 0>, + <0 0 0 2 &pcie4_intc 1>, + <0 0 0 3 &pcie4_intc 2>, + <0 0 0 4 &pcie4_intc 3>; marvell,pcie-port = <3>; marvell,pcie-lane = <0>; clocks = <&gateclk 7>; status = "disabled"; + pcie4_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; }; };Signature · 1 line
2.20.1Signature · 3 lines
Gregory Clement, Bootlin Embedded Linux and Kernel engineering http://bootlin.com
Signature · 3 lines
Gregory Clement, Bootlin Embedded Linux and Kernel engineering http://bootlin.com
Re: [PATCH v2 11/11] ARM: dts: armada-385.dtsi: Add definitions for PCIe legacy INTx interrupts
In reply to a message not in the archiveHello,
Sorry for jumping in the conversation, but I read this thread and I have an Armada A388 HW so I can test it, if desired.
Luís
Quoted 176 lines · Luís Mendes
Hello,
Sorry for jumping in the conversation, but I read this thread and I have an Armada A388 HW so I can test it, if desired.
Luís
Quoted 168 lines · Gregory CLEMENT
Hello,
Quoted 6 lines · On Monday 14 February 2022 16:07:13 Gregory CLEMENT wrote:
Quoted 5 lines
Hello Pali,
Quoted 3 lines
With this change legacy INTA, INTB, INTC and INTD interrupts are reported separately and not mixed into one Linux virq source anymore.
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
Signed-off-by Pali Rohár <[email protected]>--- arch/arm/boot/dts/armada-385.dtsi | 52 ++++++++++++++++++++++++++-----Is there any reason for not doing the same change in
armada-380.dtsi?I do not have A380 HW, so I did this change only for A385 which I have tested.
OK fair enough.
So you can add my
Acked-by Gregory CLEMENT <[email protected]>Moreover to keep biscetability this patch should be merged after the support in the driver. So the easier is to let merge it through the PCI subsystem with the other patches from this series. I do not think there will be any other changes in this file so there won't be any merge conflicts.
Thanks,
Grégory
Quoted 121 lines
Quoted 121 lines
Grégory
Quoted 118 lines
1 file changed, 44 insertions(+), 8 deletions(-)arch/arm/boot/dts/armada-385.dtsi +44
-8@@ -69,16 +69,25 @@ reg = <0x0800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 0x81000000 0 0 0x81000000 0x1 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie1_intc 0>, + <0 0 0 2 &pcie1_intc 1>, + <0 0 0 3 &pcie1_intc 2>, + <0 0 0 4 &pcie1_intc 3>; marvell,pcie-port = <0>; marvell,pcie-lane = <0>; clocks = <&gateclk 8>; status = "disabled"; + pcie1_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /* x1 port */@@ -88,16 +97,25 @@ reg = <0x1000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 0x81000000 0 0 0x81000000 0x2 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie2_intc 0>, + <0 0 0 2 &pcie2_intc 1>, + <0 0 0 3 &pcie2_intc 2>, + <0 0 0 4 &pcie2_intc 3>; marvell,pcie-port = <1>; marvell,pcie-lane = <0>; clocks = <&gateclk 5>; status = "disabled"; + pcie2_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /* x1 port */@@ -107,16 +125,25 @@ reg = <0x1800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 0x81000000 0 0 0x81000000 0x3 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie3_intc 0>, + <0 0 0 2 &pcie3_intc 1>, + <0 0 0 3 &pcie3_intc 2>, + <0 0 0 4 &pcie3_intc 3>; marvell,pcie-port = <2>; marvell,pcie-lane = <0>; clocks = <&gateclk 6>; status = "disabled"; + pcie3_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /*@@ -129,16 +156,25 @@ reg = <0x2000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0 0x81000000 0 0 0x81000000 0x4 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie4_intc 0>, + <0 0 0 2 &pcie4_intc 1>, + <0 0 0 3 &pcie4_intc 2>, + <0 0 0 4 &pcie4_intc 3>; marvell,pcie-port = <3>; marvell,pcie-lane = <0>; clocks = <&gateclk 7>; status = "disabled"; + pcie4_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; }; };Quoted 8 lines
Quoted 8 lines
Quoted 3 lines
--
2.20.1--
Gregory Clement, Bootlin
Embedded Linux and Kernel engineering
http://bootlin.com--
Gregory Clement, Bootlin
Embedded Linux and Kernel engineering
http://bootlin.com
Pali Rohár
maintainerRe: [PATCH v2 11/11] ARM: dts: armada-385.dtsi: Add definitions for PCIe legacy INTx interrupts
Hello! armada-388.dtsi file has #include "armada-385.dtsi" line and therefore is already covered by this my patch.
Gregory's question was about A380.
But if you want, you can test this patch series (which already covers A388) on your A388 HW. It is still better to do tests on more HW.
Quoted 185 lines · On Tuesday 15 February 2022 10:48:17 Luís Mendes wrote:
Hello,
Sorry for jumping in the conversation, but I read this thread and I have an Armada A388 HW so I can test it, if desired.
Luís
Quoted 176 lines · Luís Mendes
Hello,
Sorry for jumping in the conversation, but I read this thread and I have an Armada A388 HW so I can test it, if desired.
Luís
Quoted 168 lines · Gregory CLEMENT
Hello,
Quoted 6 lines · On Monday 14 February 2022 16:07:13 Gregory CLEMENT wrote:
Quoted 5 lines
Hello Pali,
Quoted 3 lines
With this change legacy INTA, INTB, INTC and INTD interrupts are reported separately and not mixed into one Linux virq source anymore.
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
Signed-off-by Pali Rohár <[email protected]>--- arch/arm/boot/dts/armada-385.dtsi | 52 ++++++++++++++++++++++++++-----Is there any reason for not doing the same change in
armada-380.dtsi?I do not have A380 HW, so I did this change only for A385 which I have tested.
OK fair enough.
So you can add my
Acked-by Gregory CLEMENT <[email protected]>Moreover to keep biscetability this patch should be merged after the support in the driver. So the easier is to let merge it through the PCI subsystem with the other patches from this series. I do not think there will be any other changes in this file so there won't be any merge conflicts.
Thanks,
Grégory
Quoted 121 lines
Quoted 121 lines
Grégory
Quoted 118 lines
1 file changed, 44 insertions(+), 8 deletions(-)arch/arm/boot/dts/armada-385.dtsi +44
-8@@ -69,16 +69,25 @@ reg = <0x0800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 0x81000000 0 0 0x81000000 0x1 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie1_intc 0>, + <0 0 0 2 &pcie1_intc 1>, + <0 0 0 3 &pcie1_intc 2>, + <0 0 0 4 &pcie1_intc 3>; marvell,pcie-port = <0>; marvell,pcie-lane = <0>; clocks = <&gateclk 8>; status = "disabled"; + pcie1_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /* x1 port */@@ -88,16 +97,25 @@ reg = <0x1000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 0x81000000 0 0 0x81000000 0x2 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie2_intc 0>, + <0 0 0 2 &pcie2_intc 1>, + <0 0 0 3 &pcie2_intc 2>, + <0 0 0 4 &pcie2_intc 3>; marvell,pcie-port = <1>; marvell,pcie-lane = <0>; clocks = <&gateclk 5>; status = "disabled"; + pcie2_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /* x1 port */@@ -107,16 +125,25 @@ reg = <0x1800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 0x81000000 0 0 0x81000000 0x3 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie3_intc 0>, + <0 0 0 2 &pcie3_intc 1>, + <0 0 0 3 &pcie3_intc 2>, + <0 0 0 4 &pcie3_intc 3>; marvell,pcie-port = <2>; marvell,pcie-lane = <0>; clocks = <&gateclk 6>; status = "disabled"; + pcie3_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /*@@ -129,16 +156,25 @@ reg = <0x2000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0 0x81000000 0 0 0x81000000 0x4 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie4_intc 0>, + <0 0 0 2 &pcie4_intc 1>, + <0 0 0 3 &pcie4_intc 2>, + <0 0 0 4 &pcie4_intc 3>; marvell,pcie-port = <3>; marvell,pcie-lane = <0>; clocks = <&gateclk 7>; status = "disabled"; + pcie4_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; }; };Quoted 8 lines
Quoted 8 lines
Quoted 3 lines
--
2.20.1--
Gregory Clement, Bootlin
Embedded Linux and Kernel engineering
http://bootlin.com--
Gregory Clement, Bootlin
Embedded Linux and Kernel engineering
http://bootlin.com
Pali Rohár
maintainerRe: [PATCH v2 10/11] PCI: mvebu: Implement support for legacy INTx interrupts
In reply to Pali RohárQuoted 114 lines · On Friday 11 February 2022 18:52:02 Pali Rohár wrote:
Quoted 12 lines · On Friday 11 February 2022 17:19:17 Lorenzo Pieralisi wrote:
Quoted 11 lines · Pali Rohár
This adds support for legacy INTx interrupts received from other PCIe devices and which are reported by a new INTx irq chip.
With this change, kernel can distinguish between INTA, INTB, INTC and INTD interrupts.
Note that for this support, device tree files has to be properly adjusted to provide "interrupts" or "interrupts-extended" property with intx interrupt source, "interrupt-names" property with "intx" string and also 'interrupt-controller' subnode must be defined.
Quoted 6 lines
Quoted 2 lines
If device tree files do not provide these nodes then driver would work as before.
Nit: this information is not useful. DT rules are written in DT bindings, not in kernel commit logs. All I am saying is that firmware developers should not have to read this log to write firmware.
It was not intended for firmware developers, but for reviewers of this patch to understand, what is happening in code and that with old DT files this patch does not change driver behavior (= work as before).
Quoted 5 lines
Quoted 5 lines
Signed-off-by Pali Rohár <[email protected]>--- drivers/pci/controller/pci-mvebu.c | 185 +++++++++++++++++++++++++++-- 1 file changed, 177 insertions(+), 8 deletions(-)Quoted 12 lines
drivers/pci/controller/pci-mvebu.c line 57
Quoted 9 lines
drivers/pci/controller/pci-mvebu.c +1
-0@@ -54,9 +54,10 @@ PCIE_CONF_ADDR_EN)#define PCIE_CONF_DATA_OFF 0x18fc#define PCIE_INT_CAUSE_OFF 0x1900 +#define PCIE_INT_UNMASK_OFF 0x1910Nit: I understand it is tempting but here you are redefining or better giving a proper label to a register. Separate patch please.
Ok!
Quoted 53 lines
drivers/pci/controller/pci-mvebu.c line 58
Quoted 3 lines
drivers/pci/controller/pci-mvebu.c +1
-1+#define PCIE_INT_INTX (i) BIT(24+i)#define PCIE_INT_PM_PME BIT(28) -#define PCIE_MASK_OFF 0x1910See above.
Quoted 47 lines
drivers/pci/controller/pci-mvebu.c +20
-6-#define PCIE_MASK_ENABLE_INTS 0x0f000000 +#define PCIE_INT_ALL_MASK GENMASK(31, 0)#define PCIE_CTRL_OFF 0x1a00#define PCIE_CTRL_X1_MODE 0x0001#define PCIE_CTRL_RC_MODE BIT(1)@@ -110,6 +111,9 @@ struct mvebu_pcie_port {struct mvebu_pcie_window iowin;u32 saved_pcie_stat;struct resource regs; +struct irq_domain *intx_irq_domain; +raw_spinlock_t irq_lock; +int intx_irq; };static inline void mvebu_writel (struct mvebu_pcie_port *port,u32 val,u32 reg)@@ -235,7 +239,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, lnkcap, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, unmask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF );@@ -288,10 +292,30 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)/* Point PCIe unit MBUS decode windows to DRAM space. */ mvebu_pcie_setup_wins (port); -/* Enable interrupt lines A-D. */ - mask =mvebu_readl (port,PCIE_MASK_OFF ); - mask |=PCIE_MASK_ENABLE_INTS ; -mvebu_writel (port, mask,PCIE_MASK_OFF ); +/* Mask all interrupt sources. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (port->intx_irq <=0 ) { +/* +* When neither "summary" interrupt, nor "intx" interrupt was +* specified in DT then unmask all legacy INTx interrupts as in +* this case driver does not provide a way for masking and +* unmasking of individual legacy INTx interrupts. In this case +* all interrupts, including legacy INTx are reported via one +* shared GIC source and therefore kernel cannot distinguish +* which individual legacy INTx was triggered. These interrupts Quoted 8 lines
drivers/pci/controller/pci-mvebu.c static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) · line 313
Quoted 4 lines
drivers/pci/controller/pci-mvebu.c +4
-0+* are shared, so it should not cause any issue. Just +* performance penalty as every PCIe interrupt handler needs to +* be called when some interrupt is triggered. +*/ This comment applies to current mainline right (ie it describes how current mainline handles INTx) ? IMO you should split it out in a separate patch.
This above comment describe what happens in if-branch when
intx_irqis not set (as written in comment "when intx interrupt was not specified in DT"). You are right that this is also the behavior in the current mainline.I'm not sure if this comment can be split out as support for "intx" interrupt is in this patch.
Quoted 6 lines
Quoted 3 lines
I understand it is hard but a patch is a logical _change_, this comment is a change per se, it is a clarification on current behaviour.
Ok, I could try to split this comment into two patches, but part about if-branch comment needs to stay in "this" patch.
I have done it locally.
Let me know when I should resend this patch series and I will include into it also these changes.
Quoted 213 lines
Quoted 121 lines
Quoted 121 lines
drivers/pci/controller/pci-mvebu.c +107
-0+ unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (0 ) |PCIE_INT_INTX (1 ) | +PCIE_INT_INTX (2 ) |PCIE_INT_INTX (3 ); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); + } } static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,@@ -924,6 +948,108 @@ static struct pci_ops mvebu_pcie_ops = { .write = mvebu_pcie_wr_conf, }; +static void mvebu_pcie_intx_irq_mask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask &= ~PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static void mvebu_pcie_intx_irq_unmask (struct irq_data *d) +{ +struct mvebu_pcie_port *port = d->domain->host_data; +irq_hw_number_t hwirq =irqd_to_hwirq (d); +unsigned long flags; +u32 unmask; + +raw_spin_lock_irqsave (&port->irq_lock, flags); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + unmask |=PCIE_INT_INTX (hwirq); +mvebu_writel (port, unmask,PCIE_INT_UNMASK_OFF ); +raw_spin_unlock_irqrestore (&port->irq_lock, flags); +} + +static struct irq_chip intx_irq_chip = { + .name ="mvebu-INTx" , + .irq_mask = mvebu_pcie_intx_irq_mask, + .irq_unmask = mvebu_pcie_intx_irq_unmask, +}; + +static int mvebu_pcie_intx_irq_map (struct irq_domain *h, +unsigned int virq,irq_hw_number_t hwirq) +{ +struct mvebu_pcie_port *port = h->host_data; + +irq_set_status_flags (virq,IRQ_LEVEL ); +irq_set_chip_and_handler (virq, &intx_irq_chip, handle_level_irq); +irq_set_chip_data (virq, port); + +return 0 ; +} + +static const struct irq_domain_ops mvebu_pcie_intx_irq_domain_ops = { + .map = mvebu_pcie_intx_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int mvebu_pcie_init_irq_domain (struct mvebu_pcie_port *port) +{ +struct device *dev = &port->pcie->pdev->dev; +struct device_node *pcie_intc_node; + +raw_spin_lock_init (&port->irq_lock); + + pcie_intc_node =of_get_next_child (port->dn,NULL ); +if (!pcie_intc_node) { +dev_err (dev,"No PCIe Intc node found for %s\n" , port->name); +return -ENODEV ; + } + + port->intx_irq_domain =irq_domain_add_linear (pcie_intc_node,PCI_NUM_INTX , + &mvebu_pcie_intx_irq_domain_ops, + port); +of_node_put (pcie_intc_node); +if (!port->intx_irq_domain) { +dev_err (dev,"Failed to get INTx IRQ domain for %s\n" , port->name); +return -ENOMEM ; + } + +return 0 ; +} + +static void mvebu_pcie_irq_handler (struct irq_desc *desc) +{ +struct mvebu_pcie_port *port =irq_desc_get_handler_data (desc); +struct irq_chip *chip =irq_desc_get_chip (desc); +struct device *dev = &port->pcie->pdev->dev; +u32 cause, unmask, status; +int i; + +chained_irq_enter (chip, desc); + + cause =mvebu_readl (port,PCIE_INT_CAUSE_OFF ); + unmask =mvebu_readl (port,PCIE_INT_UNMASK_OFF ); + status = cause & unmask; + +/* Process legacy INTx interrupts */ +for (i =0 ; i <PCI_NUM_INTX ; i++) { +if (!(status &PCIE_INT_INTX (i))) +continue ; + +if (generic_handle_domain_irq (port->intx_irq_domain, i) == -EINVAL ) +dev_err_ratelimited (dev,"unexpected INT%c IRQ\n" , (char )i+'A' ); + } + +chained_irq_exit (chip, desc); +} +static int mvebu_pcie_map_irq (const struct pci_dev *dev,u8 slot,u8 pin) {/* Interrupt support on mvebu emulated bridges is not implemented yet */ @@ -1121,6 +1247,21 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, port->io_attr =-1 ; }Quoted 16 lines
drivers/pci/controller/pci-mvebu.c static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, · line 1262
Quoted 13 lines
drivers/pci/controller/pci-mvebu.c +13
-0+/* +* Old DT bindings do not contain "intx" interrupt +* so do not fail probing driver when interrupt does not exist. +*/ + port->intx_irq =of_irq_get_byname (child,"intx" ); +if (port->intx_irq == -EPROBE_DEFER ) { + ret = port->intx_irq; + goto err; + } +if (port->intx_irq <=0 ) { +dev_warn (dev,"%s: legacy INTx interrupts cannot be masked individually, " +"%pOF does not contain intx interrupt\n" , + port->name, child);Here you end up with a new warning on existing firmware. Is it legitimate ? I would remove the
dev_warn().I added this warning in v2 because Marc wanted it.
Should I (again) remove it in v3?
Quoted 71 lines
Rob certainly has more insightful advice on this.
Thanks,
LorenzoQuoted 66 lines
drivers/pci/controller/pci-mvebu.c +31
-1+ } + reset_gpio =of_get_named_gpio_flags (child,"reset-gpios" ,0 , &flags);if (reset_gpio == -EPROBE_DEFER ) { ret = reset_gpio;@@ -1317,6 +1458,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)for (i =0 ; i < pcie->nports; i++) {struct mvebu_pcie_port *port = &pcie->ports[i]; +int irq = port->intx_irq; child = port->dn;if (!child)@@ -1344,6 +1486,22 @@ static int mvebu_pcie_probe(struct platform_device *pdev)continue ; } +if (irq >0 ) { + ret =mvebu_pcie_init_irq_domain (port); +if (ret) { +dev_err (dev,"%s: cannot init irq domain\n" , + port->name); +pci_bridge_emul_cleanup (&port->bridge); +devm_iounmap (dev, port->base); + port->base =NULL ; +mvebu_pcie_powerdown (port); +continue ; + } +irq_set_chained_handler_and_data (irq, + mvebu_pcie_irq_handler, + port); + } +/* * PCIe topology exported by mvebu hw is quite complicated. In * reality has something like N fully independent host bridges @@ -1448,6 +1606,7 @@ static int mvebu_pcie_remove(struct platform_device *pdev)for (i =0 ; i < pcie->nports; i++) {struct mvebu_pcie_port *port = &pcie->ports[i]; +int irq = port->intx_irq;if (!port->base)continue ;@@ -1458,7 +1617,17 @@ static int mvebu_pcie_remove(struct platform_device *pdev)mvebu_writel (port, cmd,PCIE_CMD_OFF );/* Mask all interrupt sources. */ -mvebu_writel (port,0 ,PCIE_MASK_OFF ); +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (irq >0 ) +irq_set_chained_handler_and_data (irq,NULL ,NULL ); + +/* Remove IRQ domains. */ +if (port->intx_irq_domain) +irq_domain_remove (port->intx_irq_domain);/* Free config space for emulated root bridge. */ pci_bridge_emul_cleanup (&port->bridge);Signature · 1 line
2.20.1
Luís Mendes
Tested-byRe: [PATCH v2 11/11] ARM: dts: armada-385.dtsi: Add definitions for PCIe legacy INTx interrupts
In reply to Pali RohárSuccessfully tested on my custom A388 system with two PCI express slots.
If you wish you can add a:
Quoted 195 lines · Pali Rohár
Hello!
armada-388.dtsifile has #include "armada-385.dtsi" line and therefore is already covered by this my patch.Gregory's question was about A380.
But if you want, you can test this patch series (which already covers A388) on your A388 HW. It is still better to do tests on more HW.
Quoted 185 lines · On Tuesday 15 February 2022 10:48:17 Luís Mendes wrote:
Hello,
Sorry for jumping in the conversation, but I read this thread and I have an Armada A388 HW so I can test it, if desired.
Luís
Quoted 176 lines · Luís Mendes
Hello,
Sorry for jumping in the conversation, but I read this thread and I have an Armada A388 HW so I can test it, if desired.
Luís
Quoted 168 lines · Gregory CLEMENT
Hello,
Quoted 6 lines · On Monday 14 February 2022 16:07:13 Gregory CLEMENT wrote:
Quoted 5 lines
Hello Pali,
Quoted 3 lines
With this change legacy INTA, INTB, INTC and INTD interrupts are reported separately and not mixed into one Linux virq source anymore.
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
Signed-off-by Pali Rohár <[email protected]>--- arch/arm/boot/dts/armada-385.dtsi | 52 ++++++++++++++++++++++++++-----Is there any reason for not doing the same change in
armada-380.dtsi?I do not have A380 HW, so I did this change only for A385 which I have tested.
OK fair enough.
So you can add my
Acked-by Gregory CLEMENT <[email protected]>Moreover to keep biscetability this patch should be merged after the support in the driver. So the easier is to let merge it through the PCI subsystem with the other patches from this series. I do not think there will be any other changes in this file so there won't be any merge conflicts.
Thanks,
Grégory
Quoted 121 lines
Quoted 121 lines
Grégory
Quoted 118 lines
1 file changed, 44 insertions(+), 8 deletions(-)arch/arm/boot/dts/armada-385.dtsi +44
-8@@ -69,16 +69,25 @@ reg = <0x0800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 0x81000000 0 0 0x81000000 0x1 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie1_intc 0>, + <0 0 0 2 &pcie1_intc 1>, + <0 0 0 3 &pcie1_intc 2>, + <0 0 0 4 &pcie1_intc 3>; marvell,pcie-port = <0>; marvell,pcie-lane = <0>; clocks = <&gateclk 8>; status = "disabled"; + pcie1_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /* x1 port */@@ -88,16 +97,25 @@ reg = <0x1000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 0x81000000 0 0 0x81000000 0x2 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie2_intc 0>, + <0 0 0 2 &pcie2_intc 1>, + <0 0 0 3 &pcie2_intc 2>, + <0 0 0 4 &pcie2_intc 3>; marvell,pcie-port = <1>; marvell,pcie-lane = <0>; clocks = <&gateclk 5>; status = "disabled"; + pcie2_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /* x1 port */@@ -107,16 +125,25 @@ reg = <0x1800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 0x81000000 0 0 0x81000000 0x3 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie3_intc 0>, + <0 0 0 2 &pcie3_intc 1>, + <0 0 0 3 &pcie3_intc 2>, + <0 0 0 4 &pcie3_intc 3>; marvell,pcie-port = <2>; marvell,pcie-lane = <0>; clocks = <&gateclk 6>; status = "disabled"; + pcie3_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /*@@ -129,16 +156,25 @@ reg = <0x2000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0 0x81000000 0 0 0x81000000 0x4 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie4_intc 0>, + <0 0 0 2 &pcie4_intc 1>, + <0 0 0 3 &pcie4_intc 2>, + <0 0 0 4 &pcie4_intc 3>; marvell,pcie-port = <3>; marvell,pcie-lane = <0>; clocks = <&gateclk 7>; status = "disabled"; + pcie4_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; }; };Quoted 8 lines
Quoted 8 lines
Quoted 3 lines
--
2.20.1--
Gregory Clement, Bootlin
Embedded Linux and Kernel engineering
http://bootlin.com--
Gregory Clement, Bootlin
Embedded Linux and Kernel engineering
http://bootlin.com
Pali Rohár
maintainerRe: [PATCH v2 11/11] ARM: dts: armada-385.dtsi: Add definitions for PCIe legacy INTx interrupts
Perfect, thanks!
Quoted 201 lines · On Friday 18 February 2022 21:53:43 Luís Mendes wrote:
Successfully tested on my custom A388 system with two PCI express slots.
If you wish you can add a:
Tested-by Luis Mendes <[email protected]>Quoted 195 lines · Pali Rohár
Hello!
armada-388.dtsifile has #include "armada-385.dtsi" line and therefore is already covered by this my patch.Gregory's question was about A380.
But if you want, you can test this patch series (which already covers A388) on your A388 HW. It is still better to do tests on more HW.
Quoted 185 lines · On Tuesday 15 February 2022 10:48:17 Luís Mendes wrote:
Hello,
Sorry for jumping in the conversation, but I read this thread and I have an Armada A388 HW so I can test it, if desired.
Luís
Quoted 176 lines · Luís Mendes
Hello,
Sorry for jumping in the conversation, but I read this thread and I have an Armada A388 HW so I can test it, if desired.
Luís
Quoted 168 lines · Gregory CLEMENT
Hello,
Quoted 6 lines · On Monday 14 February 2022 16:07:13 Gregory CLEMENT wrote:
Quoted 5 lines
Hello Pali,
Quoted 3 lines
With this change legacy INTA, INTB, INTC and INTD interrupts are reported separately and not mixed into one Linux virq source anymore.
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
Signed-off-by Pali Rohár <[email protected]>--- arch/arm/boot/dts/armada-385.dtsi | 52 ++++++++++++++++++++++++++-----Is there any reason for not doing the same change in
armada-380.dtsi?I do not have A380 HW, so I did this change only for A385 which I have tested.
OK fair enough.
So you can add my
Acked-by Gregory CLEMENT <[email protected]>Moreover to keep biscetability this patch should be merged after the support in the driver. So the easier is to let merge it through the PCI subsystem with the other patches from this series. I do not think there will be any other changes in this file so there won't be any merge conflicts.
Thanks,
Grégory
Quoted 121 lines
Quoted 121 lines
Grégory
Quoted 118 lines
1 file changed, 44 insertions(+), 8 deletions(-)arch/arm/boot/dts/armada-385.dtsi +44
-8@@ -69,16 +69,25 @@ reg = <0x0800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 0x81000000 0 0 0x81000000 0x1 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie1_intc 0>, + <0 0 0 2 &pcie1_intc 1>, + <0 0 0 3 &pcie1_intc 2>, + <0 0 0 4 &pcie1_intc 3>; marvell,pcie-port = <0>; marvell,pcie-lane = <0>; clocks = <&gateclk 8>; status = "disabled"; + pcie1_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /* x1 port */@@ -88,16 +97,25 @@ reg = <0x1000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 0x81000000 0 0 0x81000000 0x2 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie2_intc 0>, + <0 0 0 2 &pcie2_intc 1>, + <0 0 0 3 &pcie2_intc 2>, + <0 0 0 4 &pcie2_intc 3>; marvell,pcie-port = <1>; marvell,pcie-lane = <0>; clocks = <&gateclk 5>; status = "disabled"; + pcie2_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /* x1 port */@@ -107,16 +125,25 @@ reg = <0x1800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 0x81000000 0 0 0x81000000 0x3 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie3_intc 0>, + <0 0 0 2 &pcie3_intc 1>, + <0 0 0 3 &pcie3_intc 2>, + <0 0 0 4 &pcie3_intc 3>; marvell,pcie-port = <2>; marvell,pcie-lane = <0>; clocks = <&gateclk 6>; status = "disabled"; + pcie3_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /*@@ -129,16 +156,25 @@ reg = <0x2000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0 0x81000000 0 0 0x81000000 0x4 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie4_intc 0>, + <0 0 0 2 &pcie4_intc 1>, + <0 0 0 3 &pcie4_intc 2>, + <0 0 0 4 &pcie4_intc 3>; marvell,pcie-port = <3>; marvell,pcie-lane = <0>; clocks = <&gateclk 7>; status = "disabled"; + pcie4_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; }; };Quoted 8 lines
Quoted 8 lines
Quoted 3 lines
--
2.20.1--
Gregory Clement, Bootlin
Embedded Linux and Kernel engineering
http://bootlin.com--
Gregory Clement, Bootlin
Embedded Linux and Kernel engineering
http://bootlin.com
Re: [PATCH v2 10/11] PCI: mvebu: Implement support for legacy INTx interrupts
In reply to Pali RohárQuoted 119 lines · Pali Rohár
Quoted 118 lines · On Friday 11 February 2022 18:52:02 Pali Rohár wrote:
Quoted 12 lines · On Friday 11 February 2022 17:19:17 Lorenzo Pieralisi wrote:
Quoted 11 lines · Pali Rohár
This adds support for legacy INTx interrupts received from other PCIe devices and which are reported by a new INTx irq chip.
With this change, kernel can distinguish between INTA, INTB, INTC and INTD interrupts.
Note that for this support, device tree files has to be properly adjusted to provide "interrupts" or "interrupts-extended" property with intx interrupt source, "interrupt-names" property with "intx" string and also 'interrupt-controller' subnode must be defined.
Quoted 6 lines
Quoted 2 lines
If device tree files do not provide these nodes then driver would work as before.
Nit: this information is not useful. DT rules are written in DT bindings, not in kernel commit logs. All I am saying is that firmware developers should not have to read this log to write firmware.
It was not intended for firmware developers, but for reviewers of this patch to understand, what is happening in code and that with old DT files this patch does not change driver behavior (= work as before).
Quoted 5 lines
Quoted 5 lines
Signed-off-by Pali Rohár <[email protected]>--- drivers/pci/controller/pci-mvebu.c | 185 +++++++++++++++++++++++++++-- 1 file changed, 177 insertions(+), 8 deletions(-)Quoted 12 lines
drivers/pci/controller/pci-mvebu.c line 57
Quoted 9 lines
drivers/pci/controller/pci-mvebu.c +1
-0@@ -54,9 +54,10 @@ PCIE_CONF_ADDR_EN)#define PCIE_CONF_DATA_OFF 0x18fc#define PCIE_INT_CAUSE_OFF 0x1900 +#define PCIE_INT_UNMASK_OFF 0x1910Nit: I understand it is tempting but here you are redefining or better giving a proper label to a register. Separate patch please.
Ok!
Quoted 53 lines
drivers/pci/controller/pci-mvebu.c line 58
Quoted 3 lines
drivers/pci/controller/pci-mvebu.c +1
-1+#define PCIE_INT_INTX (i) BIT(24+i)#define PCIE_INT_PM_PME BIT(28) -#define PCIE_MASK_OFF 0x1910See above.
Quoted 47 lines
drivers/pci/controller/pci-mvebu.c +20
-6-#define PCIE_MASK_ENABLE_INTS 0x0f000000 +#define PCIE_INT_ALL_MASK GENMASK(31, 0)#define PCIE_CTRL_OFF 0x1a00#define PCIE_CTRL_X1_MODE 0x0001#define PCIE_CTRL_RC_MODE BIT(1)@@ -110,6 +111,9 @@ struct mvebu_pcie_port {struct mvebu_pcie_window iowin;u32 saved_pcie_stat;struct resource regs; +struct irq_domain *intx_irq_domain; +raw_spinlock_t irq_lock; +int intx_irq; };static inline void mvebu_writel (struct mvebu_pcie_port *port,u32 val,u32 reg)@@ -235,7 +239,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, lnkcap, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, unmask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF );@@ -288,10 +292,30 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)/* Point PCIe unit MBUS decode windows to DRAM space. */ mvebu_pcie_setup_wins (port); -/* Enable interrupt lines A-D. */ - mask =mvebu_readl (port,PCIE_MASK_OFF ); - mask |=PCIE_MASK_ENABLE_INTS ; -mvebu_writel (port, mask,PCIE_MASK_OFF ); +/* Mask all interrupt sources. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (port->intx_irq <=0 ) { +/* +* When neither "summary" interrupt, nor "intx" interrupt was +* specified in DT then unmask all legacy INTx interrupts as in +* this case driver does not provide a way for masking and +* unmasking of individual legacy INTx interrupts. In this case +* all interrupts, including legacy INTx are reported via one +* shared GIC source and therefore kernel cannot distinguish +* which individual legacy INTx was triggered. These interrupts Quoted 8 lines
drivers/pci/controller/pci-mvebu.c static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) · line 313
Quoted 4 lines
drivers/pci/controller/pci-mvebu.c +4
-0+* are shared, so it should not cause any issue. Just +* performance penalty as every PCIe interrupt handler needs to +* be called when some interrupt is triggered. +*/ This comment applies to current mainline right (ie it describes how current mainline handles INTx) ? IMO you should split it out in a separate patch.
This above comment describe what happens in if-branch when
intx_irqis not set (as written in comment "when intx interrupt was not specified in DT"). You are right that this is also the behavior in the current mainline.I'm not sure if this comment can be split out as support for "intx" interrupt is in this patch.
Quoted 3 lines
I understand it is hard but a patch is a logical _change_, this comment is a change per se, it is a clarification on current behaviour.
Quoted 7 lines
Quoted 2 lines
Ok, I could try to split this comment into two patches, but part about if-branch comment needs to stay in "this" patch.
I have done it locally.
Let me know when I should resend this patch series and I will include into it also these changes.
Hi,
yes please resend it and I will merge it.
Thanks,
Lorenzo
Pali Rohár
maintainerRe: [PATCH v2 10/11] PCI: mvebu: Implement support for legacy INTx interrupts
Quoted 123 lines · On Tuesday 22 February 2022 10:21:06 Lorenzo Pieralisi wrote:
Quoted 122 lines · Pali Rohár
Quoted 120 lines · On Friday 11 February 2022 18:52:02 Pali Rohár wrote:
Quoted 12 lines · On Friday 11 February 2022 17:19:17 Lorenzo Pieralisi wrote:
Quoted 11 lines · Pali Rohár
This adds support for legacy INTx interrupts received from other PCIe devices and which are reported by a new INTx irq chip.
With this change, kernel can distinguish between INTA, INTB, INTC and INTD interrupts.
Note that for this support, device tree files has to be properly adjusted to provide "interrupts" or "interrupts-extended" property with intx interrupt source, "interrupt-names" property with "intx" string and also 'interrupt-controller' subnode must be defined.
Quoted 6 lines
Quoted 2 lines
If device tree files do not provide these nodes then driver would work as before.
Nit: this information is not useful. DT rules are written in DT bindings, not in kernel commit logs. All I am saying is that firmware developers should not have to read this log to write firmware.
It was not intended for firmware developers, but for reviewers of this patch to understand, what is happening in code and that with old DT files this patch does not change driver behavior (= work as before).
Quoted 5 lines
Quoted 5 lines
Signed-off-by Pali Rohár <[email protected]>--- drivers/pci/controller/pci-mvebu.c | 185 +++++++++++++++++++++++++++-- 1 file changed, 177 insertions(+), 8 deletions(-)Quoted 12 lines
drivers/pci/controller/pci-mvebu.c line 57
Quoted 9 lines
drivers/pci/controller/pci-mvebu.c +1
-0@@ -54,9 +54,10 @@ PCIE_CONF_ADDR_EN)#define PCIE_CONF_DATA_OFF 0x18fc#define PCIE_INT_CAUSE_OFF 0x1900 +#define PCIE_INT_UNMASK_OFF 0x1910Nit: I understand it is tempting but here you are redefining or better giving a proper label to a register. Separate patch please.
Ok!
Quoted 53 lines
drivers/pci/controller/pci-mvebu.c line 58
Quoted 3 lines
drivers/pci/controller/pci-mvebu.c +1
-1+#define PCIE_INT_INTX (i) BIT(24+i)#define PCIE_INT_PM_PME BIT(28) -#define PCIE_MASK_OFF 0x1910See above.
Quoted 47 lines
drivers/pci/controller/pci-mvebu.c +20
-6-#define PCIE_MASK_ENABLE_INTS 0x0f000000 +#define PCIE_INT_ALL_MASK GENMASK(31, 0)#define PCIE_CTRL_OFF 0x1a00#define PCIE_CTRL_X1_MODE 0x0001#define PCIE_CTRL_RC_MODE BIT(1)@@ -110,6 +111,9 @@ struct mvebu_pcie_port {struct mvebu_pcie_window iowin;u32 saved_pcie_stat;struct resource regs; +struct irq_domain *intx_irq_domain; +raw_spinlock_t irq_lock; +int intx_irq; };static inline void mvebu_writel (struct mvebu_pcie_port *port,u32 val,u32 reg)@@ -235,7 +239,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, lnkcap, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, unmask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF );@@ -288,10 +292,30 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)/* Point PCIe unit MBUS decode windows to DRAM space. */ mvebu_pcie_setup_wins (port); -/* Enable interrupt lines A-D. */ - mask =mvebu_readl (port,PCIE_MASK_OFF ); - mask |=PCIE_MASK_ENABLE_INTS ; -mvebu_writel (port, mask,PCIE_MASK_OFF ); +/* Mask all interrupt sources. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (port->intx_irq <=0 ) { +/* +* When neither "summary" interrupt, nor "intx" interrupt was +* specified in DT then unmask all legacy INTx interrupts as in +* this case driver does not provide a way for masking and +* unmasking of individual legacy INTx interrupts. In this case +* all interrupts, including legacy INTx are reported via one +* shared GIC source and therefore kernel cannot distinguish +* which individual legacy INTx was triggered. These interrupts Quoted 8 lines
drivers/pci/controller/pci-mvebu.c static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) · line 313
Quoted 4 lines
drivers/pci/controller/pci-mvebu.c +4
-0+* are shared, so it should not cause any issue. Just +* performance penalty as every PCIe interrupt handler needs to +* be called when some interrupt is triggered. +*/ This comment applies to current mainline right (ie it describes how current mainline handles INTx) ? IMO you should split it out in a separate patch.
This above comment describe what happens in if-branch when
intx_irqis not set (as written in comment "when intx interrupt was not specified in DT"). You are right that this is also the behavior in the current mainline.I'm not sure if this comment can be split out as support for "intx" interrupt is in this patch.
Quoted 3 lines
I understand it is hard but a patch is a logical _change_, this comment is a change per se, it is a clarification on current behaviour.
Ok, I could try to split this comment into two patches, but part about if-branch comment needs to stay in "this" patch.
Quoted 8 lines
Quoted 4 lines
I have done it locally.
Let me know when I should resend this patch series and I will include into it also these changes.
Hi,
yes please resend it and I will merge it.
Done!
https://lore.kernel.org/linux-pci/[email protected]/T/#u
Quoted 3 lines
Thanks,
Lorenzo
Re: [PATCH v2 10/11] PCI: mvebu: Implement support for legacy INTx interrupts
Quoted 129 lines · Pali Rohár
Quoted 128 lines · On Tuesday 22 February 2022 10:21:06 Lorenzo Pieralisi wrote:
Quoted 126 lines · Pali Rohár
Quoted 114 lines · On Friday 11 February 2022 18:52:02 Pali Rohár wrote:
Quoted 12 lines · On Friday 11 February 2022 17:19:17 Lorenzo Pieralisi wrote:
Quoted 11 lines · Pali Rohár
This adds support for legacy INTx interrupts received from other PCIe devices and which are reported by a new INTx irq chip.
With this change, kernel can distinguish between INTA, INTB, INTC and INTD interrupts.
Note that for this support, device tree files has to be properly adjusted to provide "interrupts" or "interrupts-extended" property with intx interrupt source, "interrupt-names" property with "intx" string and also 'interrupt-controller' subnode must be defined.
Quoted 6 lines
Quoted 2 lines
If device tree files do not provide these nodes then driver would work as before.
Nit: this information is not useful. DT rules are written in DT bindings, not in kernel commit logs. All I am saying is that firmware developers should not have to read this log to write firmware.
It was not intended for firmware developers, but for reviewers of this patch to understand, what is happening in code and that with old DT files this patch does not change driver behavior (= work as before).
Quoted 5 lines
Quoted 5 lines
Signed-off-by Pali Rohár <[email protected]>--- drivers/pci/controller/pci-mvebu.c | 185 +++++++++++++++++++++++++++-- 1 file changed, 177 insertions(+), 8 deletions(-)Quoted 12 lines
drivers/pci/controller/pci-mvebu.c line 57
Quoted 9 lines
drivers/pci/controller/pci-mvebu.c +1
-0@@ -54,9 +54,10 @@ PCIE_CONF_ADDR_EN)#define PCIE_CONF_DATA_OFF 0x18fc#define PCIE_INT_CAUSE_OFF 0x1900 +#define PCIE_INT_UNMASK_OFF 0x1910Nit: I understand it is tempting but here you are redefining or better giving a proper label to a register. Separate patch please.
Ok!
Quoted 53 lines
drivers/pci/controller/pci-mvebu.c line 58
Quoted 3 lines
drivers/pci/controller/pci-mvebu.c +1
-1+#define PCIE_INT_INTX (i) BIT(24+i)#define PCIE_INT_PM_PME BIT(28) -#define PCIE_MASK_OFF 0x1910See above.
Quoted 47 lines
drivers/pci/controller/pci-mvebu.c +20
-6-#define PCIE_MASK_ENABLE_INTS 0x0f000000 +#define PCIE_INT_ALL_MASK GENMASK(31, 0)#define PCIE_CTRL_OFF 0x1a00#define PCIE_CTRL_X1_MODE 0x0001#define PCIE_CTRL_RC_MODE BIT(1)@@ -110,6 +111,9 @@ struct mvebu_pcie_port {struct mvebu_pcie_window iowin;u32 saved_pcie_stat;struct resource regs; +struct irq_domain *intx_irq_domain; +raw_spinlock_t irq_lock; +int intx_irq; };static inline void mvebu_writel (struct mvebu_pcie_port *port,u32 val,u32 reg)@@ -235,7 +239,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, lnkcap, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, unmask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF );@@ -288,10 +292,30 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)/* Point PCIe unit MBUS decode windows to DRAM space. */ mvebu_pcie_setup_wins (port); -/* Enable interrupt lines A-D. */ - mask =mvebu_readl (port,PCIE_MASK_OFF ); - mask |=PCIE_MASK_ENABLE_INTS ; -mvebu_writel (port, mask,PCIE_MASK_OFF ); +/* Mask all interrupt sources. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (port->intx_irq <=0 ) { +/* +* When neither "summary" interrupt, nor "intx" interrupt was +* specified in DT then unmask all legacy INTx interrupts as in +* this case driver does not provide a way for masking and +* unmasking of individual legacy INTx interrupts. In this case +* all interrupts, including legacy INTx are reported via one +* shared GIC source and therefore kernel cannot distinguish +* which individual legacy INTx was triggered. These interrupts Quoted 8 lines
drivers/pci/controller/pci-mvebu.c static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) · line 313
Quoted 4 lines
drivers/pci/controller/pci-mvebu.c +4
-0+* are shared, so it should not cause any issue. Just +* performance penalty as every PCIe interrupt handler needs to +* be called when some interrupt is triggered. +*/ This comment applies to current mainline right (ie it describes how current mainline handles INTx) ? IMO you should split it out in a separate patch.
This above comment describe what happens in if-branch when
intx_irqis not set (as written in comment "when intx interrupt was not specified in DT"). You are right that this is also the behavior in the current mainline.I'm not sure if this comment can be split out as support for "intx" interrupt is in this patch.
Quoted 6 lines
Quoted 3 lines
I understand it is hard but a patch is a logical _change_, this comment is a change per se, it is a clarification on current behaviour.
Ok, I could try to split this comment into two patches, but part about if-branch comment needs to stay in "this" patch.
I have done it locally.
Let me know when I should resend this patch series and I will include into it also these changes.
Quoted 6 lines
Quoted 3 lines
Hi,
yes please resend it and I will merge it.
Done!
https://lore.kernel.org/linux-pci/[email protected]/T/#u
Can you rebase it please on top of my pci/mvebu branch ?
https://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/pci.git/
Forgive me, I forgot to mention that, thanks.
Lorenzo
Pali Rohár
maintainerRe: [PATCH v2 10/11] PCI: mvebu: Implement support for legacy INTx interrupts
Quoted 137 lines · On Tuesday 22 February 2022 15:24:09 Lorenzo Pieralisi wrote:
Quoted 129 lines · Pali Rohár
Quoted 128 lines · On Tuesday 22 February 2022 10:21:06 Lorenzo Pieralisi wrote:
Quoted 126 lines · Pali Rohár
Quoted 114 lines · On Friday 11 February 2022 18:52:02 Pali Rohár wrote:
Quoted 12 lines · On Friday 11 February 2022 17:19:17 Lorenzo Pieralisi wrote:
Quoted 11 lines · Pali Rohár
This adds support for legacy INTx interrupts received from other PCIe devices and which are reported by a new INTx irq chip.
With this change, kernel can distinguish between INTA, INTB, INTC and INTD interrupts.
Note that for this support, device tree files has to be properly adjusted to provide "interrupts" or "interrupts-extended" property with intx interrupt source, "interrupt-names" property with "intx" string and also 'interrupt-controller' subnode must be defined.
Quoted 6 lines
Quoted 2 lines
If device tree files do not provide these nodes then driver would work as before.
Nit: this information is not useful. DT rules are written in DT bindings, not in kernel commit logs. All I am saying is that firmware developers should not have to read this log to write firmware.
It was not intended for firmware developers, but for reviewers of this patch to understand, what is happening in code and that with old DT files this patch does not change driver behavior (= work as before).
Quoted 5 lines
Quoted 5 lines
Signed-off-by Pali Rohár <[email protected]>--- drivers/pci/controller/pci-mvebu.c | 185 +++++++++++++++++++++++++++-- 1 file changed, 177 insertions(+), 8 deletions(-)Quoted 12 lines
drivers/pci/controller/pci-mvebu.c line 57
Quoted 9 lines
drivers/pci/controller/pci-mvebu.c +1
-0@@ -54,9 +54,10 @@ PCIE_CONF_ADDR_EN)#define PCIE_CONF_DATA_OFF 0x18fc#define PCIE_INT_CAUSE_OFF 0x1900 +#define PCIE_INT_UNMASK_OFF 0x1910Nit: I understand it is tempting but here you are redefining or better giving a proper label to a register. Separate patch please.
Ok!
Quoted 53 lines
drivers/pci/controller/pci-mvebu.c line 58
Quoted 3 lines
drivers/pci/controller/pci-mvebu.c +1
-1+#define PCIE_INT_INTX (i) BIT(24+i)#define PCIE_INT_PM_PME BIT(28) -#define PCIE_MASK_OFF 0x1910See above.
Quoted 47 lines
drivers/pci/controller/pci-mvebu.c +20
-6-#define PCIE_MASK_ENABLE_INTS 0x0f000000 +#define PCIE_INT_ALL_MASK GENMASK(31, 0)#define PCIE_CTRL_OFF 0x1a00#define PCIE_CTRL_X1_MODE 0x0001#define PCIE_CTRL_RC_MODE BIT(1)@@ -110,6 +111,9 @@ struct mvebu_pcie_port {struct mvebu_pcie_window iowin;u32 saved_pcie_stat;struct resource regs; +struct irq_domain *intx_irq_domain; +raw_spinlock_t irq_lock; +int intx_irq; };static inline void mvebu_writel (struct mvebu_pcie_port *port,u32 val,u32 reg)@@ -235,7 +239,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, lnkcap, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, unmask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF );@@ -288,10 +292,30 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)/* Point PCIe unit MBUS decode windows to DRAM space. */ mvebu_pcie_setup_wins (port); -/* Enable interrupt lines A-D. */ - mask =mvebu_readl (port,PCIE_MASK_OFF ); - mask |=PCIE_MASK_ENABLE_INTS ; -mvebu_writel (port, mask,PCIE_MASK_OFF ); +/* Mask all interrupt sources. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (port->intx_irq <=0 ) { +/* +* When neither "summary" interrupt, nor "intx" interrupt was +* specified in DT then unmask all legacy INTx interrupts as in +* this case driver does not provide a way for masking and +* unmasking of individual legacy INTx interrupts. In this case +* all interrupts, including legacy INTx are reported via one +* shared GIC source and therefore kernel cannot distinguish +* which individual legacy INTx was triggered. These interrupts Quoted 8 lines
drivers/pci/controller/pci-mvebu.c static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) · line 313
Quoted 4 lines
drivers/pci/controller/pci-mvebu.c +4
-0+* are shared, so it should not cause any issue. Just +* performance penalty as every PCIe interrupt handler needs to +* be called when some interrupt is triggered. +*/ This comment applies to current mainline right (ie it describes how current mainline handles INTx) ? IMO you should split it out in a separate patch.
This above comment describe what happens in if-branch when
intx_irqis not set (as written in comment "when intx interrupt was not specified in DT"). You are right that this is also the behavior in the current mainline.I'm not sure if this comment can be split out as support for "intx" interrupt is in this patch.
Quoted 6 lines
Quoted 3 lines
I understand it is hard but a patch is a logical _change_, this comment is a change per se, it is a clarification on current behaviour.
Ok, I could try to split this comment into two patches, but part about if-branch comment needs to stay in "this" patch.
I have done it locally.
Let me know when I should resend this patch series and I will include into it also these changes.
Quoted 6 lines
Quoted 3 lines
Hi,
yes please resend it and I will merge it.
Done!
https://lore.kernel.org/linux-pci/[email protected]/T/#u
Quoted 7 lines
Can you rebase it please on top of my pci/mvebu branch ?
https://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/pci.git/
Forgive me, I forgot to mention that, thanks.
Lorenzo
Ok! I rebased V3 on top of c3bd7dc553eea5a3595ca3aa0adee9bf83622a1f (pci/mvebu branch in your repo), fixed conflicts and pushed to my git repo https://git.kernel.org/pub/scm/linux/kernel/git/pali/linux.git/
as commit 42402f0cfc362ffb0b7e464f420d6ead342dab2b (lpieralisi-pci-mvebu branch). It is enough? Or do you want me to resend it via emails?
Re: [PATCH v2 10/11] PCI: mvebu: Implement support for legacy INTx interrupts
Quoted 144 lines · Pali Rohár
Quoted 143 lines · On Tuesday 22 February 2022 15:24:09 Lorenzo Pieralisi wrote:
Quoted 129 lines · Pali Rohár
Quoted 128 lines · On Tuesday 22 February 2022 10:21:06 Lorenzo Pieralisi wrote:
Quoted 126 lines · Pali Rohár
Quoted 114 lines · On Friday 11 February 2022 18:52:02 Pali Rohár wrote:
Quoted 12 lines · On Friday 11 February 2022 17:19:17 Lorenzo Pieralisi wrote:
Quoted 11 lines · Pali Rohár
This adds support for legacy INTx interrupts received from other PCIe devices and which are reported by a new INTx irq chip.
With this change, kernel can distinguish between INTA, INTB, INTC and INTD interrupts.
Note that for this support, device tree files has to be properly adjusted to provide "interrupts" or "interrupts-extended" property with intx interrupt source, "interrupt-names" property with "intx" string and also 'interrupt-controller' subnode must be defined.
Quoted 6 lines
Quoted 2 lines
If device tree files do not provide these nodes then driver would work as before.
Nit: this information is not useful. DT rules are written in DT bindings, not in kernel commit logs. All I am saying is that firmware developers should not have to read this log to write firmware.
It was not intended for firmware developers, but for reviewers of this patch to understand, what is happening in code and that with old DT files this patch does not change driver behavior (= work as before).
Quoted 5 lines
Quoted 5 lines
Signed-off-by Pali Rohár <[email protected]>--- drivers/pci/controller/pci-mvebu.c | 185 +++++++++++++++++++++++++++-- 1 file changed, 177 insertions(+), 8 deletions(-)Quoted 12 lines
drivers/pci/controller/pci-mvebu.c line 57
Quoted 9 lines
drivers/pci/controller/pci-mvebu.c +1
-0@@ -54,9 +54,10 @@ PCIE_CONF_ADDR_EN)#define PCIE_CONF_DATA_OFF 0x18fc#define PCIE_INT_CAUSE_OFF 0x1900 +#define PCIE_INT_UNMASK_OFF 0x1910Nit: I understand it is tempting but here you are redefining or better giving a proper label to a register. Separate patch please.
Ok!
Quoted 53 lines
drivers/pci/controller/pci-mvebu.c line 58
Quoted 3 lines
drivers/pci/controller/pci-mvebu.c +1
-1+#define PCIE_INT_INTX (i) BIT(24+i)#define PCIE_INT_PM_PME BIT(28) -#define PCIE_MASK_OFF 0x1910See above.
Quoted 47 lines
drivers/pci/controller/pci-mvebu.c +20
-6-#define PCIE_MASK_ENABLE_INTS 0x0f000000 +#define PCIE_INT_ALL_MASK GENMASK(31, 0)#define PCIE_CTRL_OFF 0x1a00#define PCIE_CTRL_X1_MODE 0x0001#define PCIE_CTRL_RC_MODE BIT(1)@@ -110,6 +111,9 @@ struct mvebu_pcie_port {struct mvebu_pcie_window iowin;u32 saved_pcie_stat;struct resource regs; +struct irq_domain *intx_irq_domain; +raw_spinlock_t irq_lock; +int intx_irq; };static inline void mvebu_writel (struct mvebu_pcie_port *port,u32 val,u32 reg)@@ -235,7 +239,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, lnkcap, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, unmask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF );@@ -288,10 +292,30 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)/* Point PCIe unit MBUS decode windows to DRAM space. */ mvebu_pcie_setup_wins (port); -/* Enable interrupt lines A-D. */ - mask =mvebu_readl (port,PCIE_MASK_OFF ); - mask |=PCIE_MASK_ENABLE_INTS ; -mvebu_writel (port, mask,PCIE_MASK_OFF ); +/* Mask all interrupt sources. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (port->intx_irq <=0 ) { +/* +* When neither "summary" interrupt, nor "intx" interrupt was +* specified in DT then unmask all legacy INTx interrupts as in +* this case driver does not provide a way for masking and +* unmasking of individual legacy INTx interrupts. In this case +* all interrupts, including legacy INTx are reported via one +* shared GIC source and therefore kernel cannot distinguish +* which individual legacy INTx was triggered. These interrupts Quoted 8 lines
drivers/pci/controller/pci-mvebu.c static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) · line 313
Quoted 4 lines
drivers/pci/controller/pci-mvebu.c +4
-0+* are shared, so it should not cause any issue. Just +* performance penalty as every PCIe interrupt handler needs to +* be called when some interrupt is triggered. +*/ This comment applies to current mainline right (ie it describes how current mainline handles INTx) ? IMO you should split it out in a separate patch.
This above comment describe what happens in if-branch when
intx_irqis not set (as written in comment "when intx interrupt was not specified in DT"). You are right that this is also the behavior in the current mainline.I'm not sure if this comment can be split out as support for "intx" interrupt is in this patch.
Quoted 6 lines
Quoted 3 lines
I understand it is hard but a patch is a logical _change_, this comment is a change per se, it is a clarification on current behaviour.
Ok, I could try to split this comment into two patches, but part about if-branch comment needs to stay in "this" patch.
I have done it locally.
Let me know when I should resend this patch series and I will include into it also these changes.
Quoted 6 lines
Quoted 3 lines
Hi,
yes please resend it and I will merge it.
Done!
https://lore.kernel.org/linux-pci/[email protected]/T/#uCan you rebase it please on top of my pci/mvebu branch ?
https://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/pci.git/
Forgive me, I forgot to mention that, thanks.
Quoted 7 lines
Quoted 1 line
Lorenzo
Ok! I rebased V3 on top of
c3bd7dc553eea5a3595ca3aa0adee9bf83622a1f(pci/mvebu branch in your repo), fixed conflicts and pushed to my git repo https://git.kernel.org/pub/scm/linux/kernel/git/pali/linux.git/
as commit42402f0cfc362ffb0b7e464f420d6ead342dab2b(lpieralisi-pci-mvebu branch). It is enough? Or do you want me to resend it via emails?
You need to re-send it to linux-pci ML, I only apply patches posted there, sorry for the churn, thanks.
Lorenzo
Pali Rohár
maintainerRe: [PATCH v2 10/11] PCI: mvebu: Implement support for legacy INTx interrupts
Quoted 153 lines · On Tuesday 22 February 2022 15:45:53 Lorenzo Pieralisi wrote:
Quoted 144 lines · Pali Rohár
Quoted 143 lines · On Tuesday 22 February 2022 15:24:09 Lorenzo Pieralisi wrote:
Quoted 129 lines · Pali Rohár
Quoted 128 lines · On Tuesday 22 February 2022 10:21:06 Lorenzo Pieralisi wrote:
Quoted 126 lines · Pali Rohár
Quoted 114 lines · On Friday 11 February 2022 18:52:02 Pali Rohár wrote:
Quoted 12 lines · On Friday 11 February 2022 17:19:17 Lorenzo Pieralisi wrote:
Quoted 11 lines · Pali Rohár
This adds support for legacy INTx interrupts received from other PCIe devices and which are reported by a new INTx irq chip.
With this change, kernel can distinguish between INTA, INTB, INTC and INTD interrupts.
Note that for this support, device tree files has to be properly adjusted to provide "interrupts" or "interrupts-extended" property with intx interrupt source, "interrupt-names" property with "intx" string and also 'interrupt-controller' subnode must be defined.
Quoted 6 lines
Quoted 2 lines
If device tree files do not provide these nodes then driver would work as before.
Nit: this information is not useful. DT rules are written in DT bindings, not in kernel commit logs. All I am saying is that firmware developers should not have to read this log to write firmware.
It was not intended for firmware developers, but for reviewers of this patch to understand, what is happening in code and that with old DT files this patch does not change driver behavior (= work as before).
Quoted 5 lines
Quoted 5 lines
Signed-off-by Pali Rohár <[email protected]>--- drivers/pci/controller/pci-mvebu.c | 185 +++++++++++++++++++++++++++-- 1 file changed, 177 insertions(+), 8 deletions(-)Quoted 12 lines
drivers/pci/controller/pci-mvebu.c line 57
Quoted 9 lines
drivers/pci/controller/pci-mvebu.c +1
-0@@ -54,9 +54,10 @@ PCIE_CONF_ADDR_EN)#define PCIE_CONF_DATA_OFF 0x18fc#define PCIE_INT_CAUSE_OFF 0x1900 +#define PCIE_INT_UNMASK_OFF 0x1910Nit: I understand it is tempting but here you are redefining or better giving a proper label to a register. Separate patch please.
Ok!
Quoted 53 lines
drivers/pci/controller/pci-mvebu.c line 58
Quoted 3 lines
drivers/pci/controller/pci-mvebu.c +1
-1+#define PCIE_INT_INTX (i) BIT(24+i)#define PCIE_INT_PM_PME BIT(28) -#define PCIE_MASK_OFF 0x1910See above.
Quoted 47 lines
drivers/pci/controller/pci-mvebu.c +20
-6-#define PCIE_MASK_ENABLE_INTS 0x0f000000 +#define PCIE_INT_ALL_MASK GENMASK(31, 0)#define PCIE_CTRL_OFF 0x1a00#define PCIE_CTRL_X1_MODE 0x0001#define PCIE_CTRL_RC_MODE BIT(1)@@ -110,6 +111,9 @@ struct mvebu_pcie_port {struct mvebu_pcie_window iowin;u32 saved_pcie_stat;struct resource regs; +struct irq_domain *intx_irq_domain; +raw_spinlock_t irq_lock; +int intx_irq; };static inline void mvebu_writel (struct mvebu_pcie_port *port,u32 val,u32 reg)@@ -235,7 +239,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)static void mvebu_pcie_setup_hw (struct mvebu_pcie_port *port) { -u32 ctrl, lnkcap, cmd, dev_rev, mask; +u32 ctrl, lnkcap, cmd, dev_rev, unmask;/* Setup PCIe controller to Root Complex mode. */ ctrl =mvebu_readl (port,PCIE_CTRL_OFF );@@ -288,10 +292,30 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)/* Point PCIe unit MBUS decode windows to DRAM space. */ mvebu_pcie_setup_wins (port); -/* Enable interrupt lines A-D. */ - mask =mvebu_readl (port,PCIE_MASK_OFF ); - mask |=PCIE_MASK_ENABLE_INTS ; -mvebu_writel (port, mask,PCIE_MASK_OFF ); +/* Mask all interrupt sources. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_UNMASK_OFF ); + +/* Clear all interrupt causes. */ +mvebu_writel (port, ~PCIE_INT_ALL_MASK ,PCIE_INT_CAUSE_OFF ); + +if (port->intx_irq <=0 ) { +/* +* When neither "summary" interrupt, nor "intx" interrupt was +* specified in DT then unmask all legacy INTx interrupts as in +* this case driver does not provide a way for masking and +* unmasking of individual legacy INTx interrupts. In this case +* all interrupts, including legacy INTx are reported via one +* shared GIC source and therefore kernel cannot distinguish +* which individual legacy INTx was triggered. These interrupts Quoted 8 lines
drivers/pci/controller/pci-mvebu.c static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) · line 313
Quoted 4 lines
drivers/pci/controller/pci-mvebu.c +4
-0+* are shared, so it should not cause any issue. Just +* performance penalty as every PCIe interrupt handler needs to +* be called when some interrupt is triggered. +*/ This comment applies to current mainline right (ie it describes how current mainline handles INTx) ? IMO you should split it out in a separate patch.
This above comment describe what happens in if-branch when
intx_irqis not set (as written in comment "when intx interrupt was not specified in DT"). You are right that this is also the behavior in the current mainline.I'm not sure if this comment can be split out as support for "intx" interrupt is in this patch.
Quoted 6 lines
Quoted 3 lines
I understand it is hard but a patch is a logical _change_, this comment is a change per se, it is a clarification on current behaviour.
Ok, I could try to split this comment into two patches, but part about if-branch comment needs to stay in "this" patch.
I have done it locally.
Let me know when I should resend this patch series and I will include into it also these changes.
Quoted 6 lines
Quoted 3 lines
Hi,
yes please resend it and I will merge it.
Done!
https://lore.kernel.org/linux-pci/[email protected]/T/#uCan you rebase it please on top of my pci/mvebu branch ?
https://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/pci.git/
Forgive me, I forgot to mention that, thanks.
Quoted 7 lines
Quoted 1 line
Lorenzo
Ok! I rebased V3 on top of
c3bd7dc553eea5a3595ca3aa0adee9bf83622a1f(pci/mvebu branch in your repo), fixed conflicts and pushed to my git repo https://git.kernel.org/pub/scm/linux/kernel/git/pali/linux.git/
as commit42402f0cfc362ffb0b7e464f420d6ead342dab2b(lpieralisi-pci-mvebu branch). It is enough? Or do you want me to resend it via emails?
Quoted 4 lines
You need to re-send it to linux-pci ML, I only apply patches posted there, sorry for the churn, thanks.
Lorenzo
Done!
https://lore.kernel.org/linux-pci/[email protected]/T/#u