Message ID | 1d82eff3670f60df24228e5c83cf663c6dd61eaf.1733726572.git.unicorn_wang@outlook.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | Add PCIe support to Sophgo SG2042 SoC | expand |
On Mon, Dec 09, 2024 at 03:19:57PM +0800, Chen Wang wrote: > From: Chen Wang <unicorn_wang@outlook.com> > > Add support for PCIe controller in SG2042 SoC. The controller > uses the Cadence PCIe core programmed by pcie-cadence*.c. The > PCIe controller will work in host mode only. > +++ b/drivers/pci/controller/cadence/Makefile > @@ -4,3 +4,4 @@ obj-$(CONFIG_PCIE_CADENCE_HOST) += pcie-cadence-host.o > obj-$(CONFIG_PCIE_CADENCE_EP) += pcie-cadence-ep.o > obj-$(CONFIG_PCIE_CADENCE_PLAT) += pcie-cadence-plat.o > obj-$(CONFIG_PCI_J721E) += pci-j721e.o > +obj-$(CONFIG_PCIE_SG2042) += pcie-sg2042.o > \ No newline at end of file Add the newline. > +++ b/drivers/pci/controller/cadence/pcie-sg2042.c > +#include "../../../irqchip/irq-msi-lib.h" This is the only file outside drivers/irqchip/ that includes this. What's special about this driver? Maybe this is a hint that something here belongs in drivers/irqchip/? > +#ifdef CONFIG_SMP No other drivers test CONFIG_SMP, why should this be different? > +static int sg2042_pcie_msi_irq_set_affinity(struct irq_data *d, > + const struct cpumask *mask, > + bool force) > +{ > + if (d->parent_data) > + return irq_chip_set_affinity_parent(d, mask, force); > + > + return -EINVAL; > +} > +#endif /* CONFIG_SMP */ > +static int sg2042_pcie_init_msi_data(struct sg2042_pcie *pcie) > +{ > + struct device *dev = pcie->cdns_pcie->dev; > + u32 value; > + int ret; > + > + raw_spin_lock_init(&pcie->msi_lock); > + > + /* > + * Though the PCIe controller can address >32-bit address space, to > + * facilitate endpoints that support only 32-bit MSI target address, > + * the mask is set to 32-bit to make sure that MSI target address is > + * always a 32-bit address > + */ > + ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); Not sure this is needed. Does DT dma-ranges not cover this? > +static int sg2042_pcie_setup_msi(struct sg2042_pcie *pcie, struct device_node *msi_node) Wrap to fit in 80 columns like the rest. > +/* > + * SG2042 only support 4-byte aligned access, so for the rootbus (i.e. to read > + * the PCIe controller itself, read32 is required. For non-rootbus (i.e. to read s/PCIe controller/Root Port/ > + * the PCIe peripheral registers, supports 1/2/4 byte aligned access, so > + * directly use read should be fine. s/use read/using read/ > +static int sg2042_pcie_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct device_node *np = dev->of_node; > + struct pci_host_bridge *bridge; > + struct device_node *np_syscon; > + struct device_node *msi_node; > + struct cdns_pcie *cdns_pcie; > + struct sg2042_pcie *pcie; > + struct cdns_pcie_rc *rc; > + struct regmap *syscon; > + int ret; > + > + if (!IS_ENABLED(CONFIG_PCIE_CADENCE_HOST)) > + return -ENODEV; I don't think this is needed since CONFIG_PCIE_SG2042 selects PCIE_CADENCE_HOST. Bjorn
Hi Chen, kernel test robot noticed the following build warnings: [auto build test WARNING on fac04efc5c793dccbd07e2d59af9f90b7fc0dca4] url: https://github.com/intel-lab-lkp/linux/commits/Chen-Wang/dt-bindings-pci-Add-Sophgo-SG2042-PCIe-host/20241209-152613 base: fac04efc5c793dccbd07e2d59af9f90b7fc0dca4 patch link: https://lore.kernel.org/r/1d82eff3670f60df24228e5c83cf663c6dd61eaf.1733726572.git.unicorn_wang%40outlook.com patch subject: [PATCH v2 2/5] PCI: sg2042: Add Sophgo SG2042 PCIe driver config: sh-randconfig-r071-20241215 (https://download.01.org/0day-ci/archive/20241215/202412151758.8ckkzHnf-lkp@intel.com/config) compiler: sh4-linux-gcc (GCC) 14.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241215/202412151758.8ckkzHnf-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202412151758.8ckkzHnf-lkp@intel.com/ All warnings (new ones prefixed by >>): In file included from drivers/pci/controller/cadence/pcie-sg2042.c:23: >> drivers/pci/controller/cadence/../../../irqchip/irq-msi-lib.h:25:39: warning: 'struct msi_domain_info' declared inside parameter list will not be visible outside of this definition or declaration 25 | struct msi_domain_info *info); | ^~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:306:15: error: variable 'sg2042_pcie_msi_parent_ops' has initializer but incomplete type 306 | static struct msi_parent_ops sg2042_pcie_msi_parent_ops = { | ^~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:307:10: error: 'struct msi_parent_ops' has no member named 'required_flags' 307 | .required_flags = SG2042_PCIE_MSI_FLAGS_REQUIRED, | ^~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:301:41: error: 'MSI_FLAG_USE_DEF_DOM_OPS' undeclared here (not in a function) 301 | #define SG2042_PCIE_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ | ^~~~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:307:35: note: in expansion of macro 'SG2042_PCIE_MSI_FLAGS_REQUIRED' 307 | .required_flags = SG2042_PCIE_MSI_FLAGS_REQUIRED, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:302:41: error: 'MSI_FLAG_USE_DEF_CHIP_OPS' undeclared here (not in a function) 302 | MSI_FLAG_USE_DEF_CHIP_OPS) | ^~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:307:35: note: in expansion of macro 'SG2042_PCIE_MSI_FLAGS_REQUIRED' 307 | .required_flags = SG2042_PCIE_MSI_FLAGS_REQUIRED, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ >> drivers/pci/controller/cadence/pcie-sg2042.c:301:40: warning: excess elements in struct initializer 301 | #define SG2042_PCIE_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ | ^ drivers/pci/controller/cadence/pcie-sg2042.c:307:35: note: in expansion of macro 'SG2042_PCIE_MSI_FLAGS_REQUIRED' 307 | .required_flags = SG2042_PCIE_MSI_FLAGS_REQUIRED, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:301:40: note: (near initialization for 'sg2042_pcie_msi_parent_ops') 301 | #define SG2042_PCIE_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ | ^ drivers/pci/controller/cadence/pcie-sg2042.c:307:35: note: in expansion of macro 'SG2042_PCIE_MSI_FLAGS_REQUIRED' 307 | .required_flags = SG2042_PCIE_MSI_FLAGS_REQUIRED, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:308:10: error: 'struct msi_parent_ops' has no member named 'supported_flags' 308 | .supported_flags = SG2042_PCIE_MSI_FLAGS_SUPPORTED, | ^~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:304:41: error: 'MSI_GENERIC_FLAGS_MASK' undeclared here (not in a function) 304 | #define SG2042_PCIE_MSI_FLAGS_SUPPORTED MSI_GENERIC_FLAGS_MASK | ^~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:308:35: note: in expansion of macro 'SG2042_PCIE_MSI_FLAGS_SUPPORTED' 308 | .supported_flags = SG2042_PCIE_MSI_FLAGS_SUPPORTED, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:304:41: warning: excess elements in struct initializer 304 | #define SG2042_PCIE_MSI_FLAGS_SUPPORTED MSI_GENERIC_FLAGS_MASK | ^~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:308:35: note: in expansion of macro 'SG2042_PCIE_MSI_FLAGS_SUPPORTED' 308 | .supported_flags = SG2042_PCIE_MSI_FLAGS_SUPPORTED, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:304:41: note: (near initialization for 'sg2042_pcie_msi_parent_ops') 304 | #define SG2042_PCIE_MSI_FLAGS_SUPPORTED MSI_GENERIC_FLAGS_MASK | ^~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:308:35: note: in expansion of macro 'SG2042_PCIE_MSI_FLAGS_SUPPORTED' 308 | .supported_flags = SG2042_PCIE_MSI_FLAGS_SUPPORTED, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:309:10: error: 'struct msi_parent_ops' has no member named 'bus_select_mask' 309 | .bus_select_mask = MATCH_PCI_MSI, | ^~~~~~~~~~~~~~~ >> drivers/pci/controller/cadence/../../../irqchip/irq-msi-lib.h:15:33: warning: excess elements in struct initializer 15 | #define MATCH_PCI_MSI (0) | ^ drivers/pci/controller/cadence/pcie-sg2042.c:309:35: note: in expansion of macro 'MATCH_PCI_MSI' 309 | .bus_select_mask = MATCH_PCI_MSI, | ^~~~~~~~~~~~~ drivers/pci/controller/cadence/../../../irqchip/irq-msi-lib.h:15:33: note: (near initialization for 'sg2042_pcie_msi_parent_ops') 15 | #define MATCH_PCI_MSI (0) | ^ drivers/pci/controller/cadence/pcie-sg2042.c:309:35: note: in expansion of macro 'MATCH_PCI_MSI' 309 | .bus_select_mask = MATCH_PCI_MSI, | ^~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:310:10: error: 'struct msi_parent_ops' has no member named 'bus_select_token' 310 | .bus_select_token = DOMAIN_BUS_NEXUS, | ^~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:310:35: warning: excess elements in struct initializer 310 | .bus_select_token = DOMAIN_BUS_NEXUS, | ^~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:310:35: note: (near initialization for 'sg2042_pcie_msi_parent_ops') drivers/pci/controller/cadence/pcie-sg2042.c:311:10: error: 'struct msi_parent_ops' has no member named 'prefix' 311 | .prefix = "SG2042-", | ^~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:311:35: warning: excess elements in struct initializer 311 | .prefix = "SG2042-", | ^~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:311:35: note: (near initialization for 'sg2042_pcie_msi_parent_ops') drivers/pci/controller/cadence/pcie-sg2042.c:312:10: error: 'struct msi_parent_ops' has no member named 'init_dev_msi_info' 312 | .init_dev_msi_info = msi_lib_init_dev_msi_info, | ^~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:312:35: warning: excess elements in struct initializer 312 | .init_dev_msi_info = msi_lib_init_dev_msi_info, | ^~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:312:35: note: (near initialization for 'sg2042_pcie_msi_parent_ops') drivers/pci/controller/cadence/pcie-sg2042.c: In function 'sg2042_pcie_setup_msi': drivers/pci/controller/cadence/pcie-sg2042.c:344:22: error: 'struct irq_domain' has no member named 'msi_parent_ops' 344 | parent_domain->msi_parent_ops = &sg2042_pcie_msi_parent_ops; | ^~ drivers/pci/controller/cadence/pcie-sg2042.c: At top level: drivers/pci/controller/cadence/pcie-sg2042.c:306:30: error: storage size of 'sg2042_pcie_msi_parent_ops' isn't known 306 | static struct msi_parent_ops sg2042_pcie_msi_parent_ops = { | ^~~~~~~~~~~~~~~~~~~~~~~~~~ vim +25 drivers/pci/controller/cadence/../../../irqchip/irq-msi-lib.h 72e257c6f058032 Thomas Gleixner 2024-06-23 11 8c41ccec839c622 Thomas Gleixner 2024-06-23 12 #ifdef CONFIG_PCI_MSI 8c41ccec839c622 Thomas Gleixner 2024-06-23 13 #define MATCH_PCI_MSI BIT(DOMAIN_BUS_PCI_MSI) 8c41ccec839c622 Thomas Gleixner 2024-06-23 14 #else 8c41ccec839c622 Thomas Gleixner 2024-06-23 @15 #define MATCH_PCI_MSI (0) 8c41ccec839c622 Thomas Gleixner 2024-06-23 16 #endif 8c41ccec839c622 Thomas Gleixner 2024-06-23 17 496436f4a514a3f Thomas Gleixner 2024-06-23 18 #define MATCH_PLATFORM_MSI BIT(DOMAIN_BUS_PLATFORM_MSI) 496436f4a514a3f Thomas Gleixner 2024-06-23 19 72e257c6f058032 Thomas Gleixner 2024-06-23 20 int msi_lib_irq_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec, 72e257c6f058032 Thomas Gleixner 2024-06-23 21 enum irq_domain_bus_token bus_token); 72e257c6f058032 Thomas Gleixner 2024-06-23 22 72e257c6f058032 Thomas Gleixner 2024-06-23 23 bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain, 72e257c6f058032 Thomas Gleixner 2024-06-23 24 struct irq_domain *real_parent, 72e257c6f058032 Thomas Gleixner 2024-06-23 @25 struct msi_domain_info *info); 72e257c6f058032 Thomas Gleixner 2024-06-23 26
Hi Chen, kernel test robot noticed the following build errors: [auto build test ERROR on fac04efc5c793dccbd07e2d59af9f90b7fc0dca4] url: https://github.com/intel-lab-lkp/linux/commits/Chen-Wang/dt-bindings-pci-Add-Sophgo-SG2042-PCIe-host/20241209-152613 base: fac04efc5c793dccbd07e2d59af9f90b7fc0dca4 patch link: https://lore.kernel.org/r/1d82eff3670f60df24228e5c83cf663c6dd61eaf.1733726572.git.unicorn_wang%40outlook.com patch subject: [PATCH v2 2/5] PCI: sg2042: Add Sophgo SG2042 PCIe driver config: sh-randconfig-r071-20241215 (https://download.01.org/0day-ci/archive/20241215/202412151947.yDHMy3jh-lkp@intel.com/config) compiler: sh4-linux-gcc (GCC) 14.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241215/202412151947.yDHMy3jh-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202412151947.yDHMy3jh-lkp@intel.com/ All errors (new ones prefixed by >>): In file included from drivers/pci/controller/cadence/pcie-sg2042.c:23: drivers/pci/controller/cadence/../../../irqchip/irq-msi-lib.h:25:39: warning: 'struct msi_domain_info' declared inside parameter list will not be visible outside of this definition or declaration 25 | struct msi_domain_info *info); | ^~~~~~~~~~~~~~~ >> drivers/pci/controller/cadence/pcie-sg2042.c:306:15: error: variable 'sg2042_pcie_msi_parent_ops' has initializer but incomplete type 306 | static struct msi_parent_ops sg2042_pcie_msi_parent_ops = { | ^~~~~~~~~~~~~~ >> drivers/pci/controller/cadence/pcie-sg2042.c:307:10: error: 'struct msi_parent_ops' has no member named 'required_flags' 307 | .required_flags = SG2042_PCIE_MSI_FLAGS_REQUIRED, | ^~~~~~~~~~~~~~ >> drivers/pci/controller/cadence/pcie-sg2042.c:301:41: error: 'MSI_FLAG_USE_DEF_DOM_OPS' undeclared here (not in a function) 301 | #define SG2042_PCIE_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ | ^~~~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:307:35: note: in expansion of macro 'SG2042_PCIE_MSI_FLAGS_REQUIRED' 307 | .required_flags = SG2042_PCIE_MSI_FLAGS_REQUIRED, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ >> drivers/pci/controller/cadence/pcie-sg2042.c:302:41: error: 'MSI_FLAG_USE_DEF_CHIP_OPS' undeclared here (not in a function) 302 | MSI_FLAG_USE_DEF_CHIP_OPS) | ^~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:307:35: note: in expansion of macro 'SG2042_PCIE_MSI_FLAGS_REQUIRED' 307 | .required_flags = SG2042_PCIE_MSI_FLAGS_REQUIRED, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:301:40: warning: excess elements in struct initializer 301 | #define SG2042_PCIE_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ | ^ drivers/pci/controller/cadence/pcie-sg2042.c:307:35: note: in expansion of macro 'SG2042_PCIE_MSI_FLAGS_REQUIRED' 307 | .required_flags = SG2042_PCIE_MSI_FLAGS_REQUIRED, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:301:40: note: (near initialization for 'sg2042_pcie_msi_parent_ops') 301 | #define SG2042_PCIE_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ | ^ drivers/pci/controller/cadence/pcie-sg2042.c:307:35: note: in expansion of macro 'SG2042_PCIE_MSI_FLAGS_REQUIRED' 307 | .required_flags = SG2042_PCIE_MSI_FLAGS_REQUIRED, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ >> drivers/pci/controller/cadence/pcie-sg2042.c:308:10: error: 'struct msi_parent_ops' has no member named 'supported_flags' 308 | .supported_flags = SG2042_PCIE_MSI_FLAGS_SUPPORTED, | ^~~~~~~~~~~~~~~ >> drivers/pci/controller/cadence/pcie-sg2042.c:304:41: error: 'MSI_GENERIC_FLAGS_MASK' undeclared here (not in a function) 304 | #define SG2042_PCIE_MSI_FLAGS_SUPPORTED MSI_GENERIC_FLAGS_MASK | ^~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:308:35: note: in expansion of macro 'SG2042_PCIE_MSI_FLAGS_SUPPORTED' 308 | .supported_flags = SG2042_PCIE_MSI_FLAGS_SUPPORTED, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:304:41: warning: excess elements in struct initializer 304 | #define SG2042_PCIE_MSI_FLAGS_SUPPORTED MSI_GENERIC_FLAGS_MASK | ^~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:308:35: note: in expansion of macro 'SG2042_PCIE_MSI_FLAGS_SUPPORTED' 308 | .supported_flags = SG2042_PCIE_MSI_FLAGS_SUPPORTED, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:304:41: note: (near initialization for 'sg2042_pcie_msi_parent_ops') 304 | #define SG2042_PCIE_MSI_FLAGS_SUPPORTED MSI_GENERIC_FLAGS_MASK | ^~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:308:35: note: in expansion of macro 'SG2042_PCIE_MSI_FLAGS_SUPPORTED' 308 | .supported_flags = SG2042_PCIE_MSI_FLAGS_SUPPORTED, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ >> drivers/pci/controller/cadence/pcie-sg2042.c:309:10: error: 'struct msi_parent_ops' has no member named 'bus_select_mask' 309 | .bus_select_mask = MATCH_PCI_MSI, | ^~~~~~~~~~~~~~~ drivers/pci/controller/cadence/../../../irqchip/irq-msi-lib.h:15:33: warning: excess elements in struct initializer 15 | #define MATCH_PCI_MSI (0) | ^ drivers/pci/controller/cadence/pcie-sg2042.c:309:35: note: in expansion of macro 'MATCH_PCI_MSI' 309 | .bus_select_mask = MATCH_PCI_MSI, | ^~~~~~~~~~~~~ drivers/pci/controller/cadence/../../../irqchip/irq-msi-lib.h:15:33: note: (near initialization for 'sg2042_pcie_msi_parent_ops') 15 | #define MATCH_PCI_MSI (0) | ^ drivers/pci/controller/cadence/pcie-sg2042.c:309:35: note: in expansion of macro 'MATCH_PCI_MSI' 309 | .bus_select_mask = MATCH_PCI_MSI, | ^~~~~~~~~~~~~ >> drivers/pci/controller/cadence/pcie-sg2042.c:310:10: error: 'struct msi_parent_ops' has no member named 'bus_select_token' 310 | .bus_select_token = DOMAIN_BUS_NEXUS, | ^~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:310:35: warning: excess elements in struct initializer 310 | .bus_select_token = DOMAIN_BUS_NEXUS, | ^~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:310:35: note: (near initialization for 'sg2042_pcie_msi_parent_ops') >> drivers/pci/controller/cadence/pcie-sg2042.c:311:10: error: 'struct msi_parent_ops' has no member named 'prefix' 311 | .prefix = "SG2042-", | ^~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:311:35: warning: excess elements in struct initializer 311 | .prefix = "SG2042-", | ^~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:311:35: note: (near initialization for 'sg2042_pcie_msi_parent_ops') >> drivers/pci/controller/cadence/pcie-sg2042.c:312:10: error: 'struct msi_parent_ops' has no member named 'init_dev_msi_info' 312 | .init_dev_msi_info = msi_lib_init_dev_msi_info, | ^~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:312:35: warning: excess elements in struct initializer 312 | .init_dev_msi_info = msi_lib_init_dev_msi_info, | ^~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pci/controller/cadence/pcie-sg2042.c:312:35: note: (near initialization for 'sg2042_pcie_msi_parent_ops') drivers/pci/controller/cadence/pcie-sg2042.c: In function 'sg2042_pcie_setup_msi': >> drivers/pci/controller/cadence/pcie-sg2042.c:344:22: error: 'struct irq_domain' has no member named 'msi_parent_ops' 344 | parent_domain->msi_parent_ops = &sg2042_pcie_msi_parent_ops; | ^~ drivers/pci/controller/cadence/pcie-sg2042.c: At top level: >> drivers/pci/controller/cadence/pcie-sg2042.c:306:30: error: storage size of 'sg2042_pcie_msi_parent_ops' isn't known 306 | static struct msi_parent_ops sg2042_pcie_msi_parent_ops = { | ^~~~~~~~~~~~~~~~~~~~~~~~~~ vim +/sg2042_pcie_msi_parent_ops +306 drivers/pci/controller/cadence/pcie-sg2042.c 300 > 301 #define SG2042_PCIE_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ > 302 MSI_FLAG_USE_DEF_CHIP_OPS) 303 > 304 #define SG2042_PCIE_MSI_FLAGS_SUPPORTED MSI_GENERIC_FLAGS_MASK 305 > 306 static struct msi_parent_ops sg2042_pcie_msi_parent_ops = { > 307 .required_flags = SG2042_PCIE_MSI_FLAGS_REQUIRED, > 308 .supported_flags = SG2042_PCIE_MSI_FLAGS_SUPPORTED, > 309 .bus_select_mask = MATCH_PCI_MSI, > 310 .bus_select_token = DOMAIN_BUS_NEXUS, > 311 .prefix = "SG2042-", > 312 .init_dev_msi_info = msi_lib_init_dev_msi_info, 313 }; 314 315 static int sg2042_pcie_setup_msi(struct sg2042_pcie *pcie, struct device_node *msi_node) 316 { 317 struct device *dev = pcie->cdns_pcie->dev; 318 struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); 319 struct irq_domain *parent_domain; 320 int ret = 0; 321 322 if (!of_property_read_bool(msi_node, "msi-controller")) 323 return -ENODEV; 324 325 ret = of_irq_get_byname(msi_node, "msi"); 326 if (ret <= 0) { 327 dev_err(dev, "%pOF: failed to get MSI irq\n", msi_node); 328 return ret; 329 } 330 pcie->msi_irq = ret; 331 332 irq_set_chained_handler_and_data(pcie->msi_irq, 333 sg2042_pcie_msi_chained_isr, pcie); 334 335 parent_domain = irq_domain_create_linear(fwnode, MSI_DEF_NUM_VECTORS, 336 &sg2042_pcie_msi_domain_ops, pcie); 337 if (!parent_domain) { 338 dev_err(dev, "%pfw: Failed to create IRQ domain\n", fwnode); 339 return -ENOMEM; 340 } 341 irq_domain_update_bus_token(parent_domain, DOMAIN_BUS_NEXUS); 342 343 parent_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT; > 344 parent_domain->msi_parent_ops = &sg2042_pcie_msi_parent_ops; 345 346 pcie->msi_domain = parent_domain; 347 348 ret = sg2042_pcie_init_msi_data(pcie); 349 if (ret) { 350 dev_err(dev, "Failed to initialize MSI data!\n"); 351 return ret; 352 } 353 354 return 0; 355 } 356
On 2024/12/11 1:31, Bjorn Helgaas wrote: > On Mon, Dec 09, 2024 at 03:19:57PM +0800, Chen Wang wrote: >> From: Chen Wang <unicorn_wang@outlook.com> >> >> Add support for PCIe controller in SG2042 SoC. The controller >> uses the Cadence PCIe core programmed by pcie-cadence*.c. The >> PCIe controller will work in host mode only. >> +++ b/drivers/pci/controller/cadence/Makefile >> @@ -4,3 +4,4 @@ obj-$(CONFIG_PCIE_CADENCE_HOST) += pcie-cadence-host.o >> obj-$(CONFIG_PCIE_CADENCE_EP) += pcie-cadence-ep.o >> obj-$(CONFIG_PCIE_CADENCE_PLAT) += pcie-cadence-plat.o >> obj-$(CONFIG_PCI_J721E) += pci-j721e.o >> +obj-$(CONFIG_PCIE_SG2042) += pcie-sg2042.o >> \ No newline at end of file > Add the newline. ok > >> +++ b/drivers/pci/controller/cadence/pcie-sg2042.c >> +#include "../../../irqchip/irq-msi-lib.h" > This is the only file outside drivers/irqchip/ that includes this. > What's special about this driver? Maybe this is a hint that something > here belongs in drivers/irqchip/? This file is included due to MSI parent model is used in this driver and I used helper functions such as msi_lib_init_dev_msi_info. Actually I see Marc is working on MSI cleanup and will make irq-msi-lib.h globally available, see https://lore.kernel.org/linux-riscv/20241204124549.607054-2-maz@kernel.org/ But before his PR is merged, we may have to include the file like this now. > >> +#ifdef CONFIG_SMP > No other drivers test CONFIG_SMP, why should this be different? ok, seems it'ok to remove this test. > >> +static int sg2042_pcie_msi_irq_set_affinity(struct irq_data *d, >> + const struct cpumask *mask, >> + bool force) >> +{ >> + if (d->parent_data) >> + return irq_chip_set_affinity_parent(d, mask, force); >> + >> + return -EINVAL; >> +} >> +#endif /* CONFIG_SMP */ >> +static int sg2042_pcie_init_msi_data(struct sg2042_pcie *pcie) >> +{ >> + struct device *dev = pcie->cdns_pcie->dev; >> + u32 value; >> + int ret; >> + >> + raw_spin_lock_init(&pcie->msi_lock); >> + >> + /* >> + * Though the PCIe controller can address >32-bit address space, to >> + * facilitate endpoints that support only 32-bit MSI target address, >> + * the mask is set to 32-bit to make sure that MSI target address is >> + * always a 32-bit address >> + */ >> + ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); > Not sure this is needed. Does DT dma-ranges not cover this? I prefer not introduce dma-ranges, we just need to allocate a small continuous space here, and using dma_set_coherent_mask is also one of the commonly used methods. > >> +static int sg2042_pcie_setup_msi(struct sg2042_pcie *pcie, struct device_node *msi_node) > Wrap to fit in 80 columns like the rest. ok > >> +/* >> + * SG2042 only support 4-byte aligned access, so for the rootbus (i.e. to read >> + * the PCIe controller itself, read32 is required. For non-rootbus (i.e. to read > s/PCIe controller/Root Port/ ok > >> + * the PCIe peripheral registers, supports 1/2/4 byte aligned access, so >> + * directly use read should be fine. > s/use read/using read/ ok, thanks > >> +static int sg2042_pcie_probe(struct platform_device *pdev) >> +{ >> + struct device *dev = &pdev->dev; >> + struct device_node *np = dev->of_node; >> + struct pci_host_bridge *bridge; >> + struct device_node *np_syscon; >> + struct device_node *msi_node; >> + struct cdns_pcie *cdns_pcie; >> + struct sg2042_pcie *pcie; >> + struct cdns_pcie_rc *rc; >> + struct regmap *syscon; >> + int ret; >> + >> + if (!IS_ENABLED(CONFIG_PCIE_CADENCE_HOST)) >> + return -ENODEV; > I don't think this is needed since CONFIG_PCIE_SG2042 selects > PCIE_CADENCE_HOST. Yes, you are right. Thanks, Chen > > Bjorn
diff --git a/drivers/pci/controller/cadence/Kconfig b/drivers/pci/controller/cadence/Kconfig index 8a0044bb3989..ddf86bbe687d 100644 --- a/drivers/pci/controller/cadence/Kconfig +++ b/drivers/pci/controller/cadence/Kconfig @@ -42,6 +42,16 @@ config PCIE_CADENCE_PLAT_EP endpoint mode. This PCIe controller may be embedded into many different vendors SoCs. +config PCIE_SG2042 + bool "Sophgo SG2042 PCIe controller (host mode)" + depends on ARCH_SOPHGO || COMPILE_TEST + depends on OF + select PCIE_CADENCE_HOST + help + Say Y here if you want to support the Sophgo SG2042 PCIe platform + controller in host mode. Sophgo SG2042 PCIe controller uses Cadence + PCIe core. + config PCI_J721E bool @@ -67,4 +77,5 @@ config PCI_J721E_EP Say Y here if you want to support the TI J721E PCIe platform controller in endpoint mode. TI J721E PCIe controller uses Cadence PCIe core. + endmenu diff --git a/drivers/pci/controller/cadence/Makefile b/drivers/pci/controller/cadence/Makefile index 9bac5fb2f13d..89aa316f54ac 100644 --- a/drivers/pci/controller/cadence/Makefile +++ b/drivers/pci/controller/cadence/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_PCIE_CADENCE_HOST) += pcie-cadence-host.o obj-$(CONFIG_PCIE_CADENCE_EP) += pcie-cadence-ep.o obj-$(CONFIG_PCIE_CADENCE_PLAT) += pcie-cadence-plat.o obj-$(CONFIG_PCI_J721E) += pci-j721e.o +obj-$(CONFIG_PCIE_SG2042) += pcie-sg2042.o \ No newline at end of file diff --git a/drivers/pci/controller/cadence/pcie-sg2042.c b/drivers/pci/controller/cadence/pcie-sg2042.c new file mode 100644 index 000000000000..78893d3b5c47 --- /dev/null +++ b/drivers/pci/controller/cadence/pcie-sg2042.c @@ -0,0 +1,534 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * pcie-sg2042 - PCIe controller driver for Sophgo SG2042 SoC + * + * Copyright (C) 2024 Sophgo Technology Inc. + * Copyright (C) 2024 Chen Wang <unicorn_wang@outlook.com> + */ + +#include <linux/bits.h> +#include <linux/irq.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqdomain.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/msi.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_pci.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> + +#include "../../../irqchip/irq-msi-lib.h" + +#include "pcie-cadence.h" + +/* + * SG2042 PCIe controller supports two ways to report MSI: + * + * - Method A, the PCIe controller implements an MSI interrupt controller + * inside, and connect to PLIC upward through one interrupt line. + * Provides memory-mapped MSI address, and by programming the upper 32 + * bits of the address to zero, it can be compatible with old PCIe devices + * that only support 32-bit MSI address. + * + * - Method B, the PCIe controller connects to PLIC upward through an + * independent MSI controller "sophgo,sg2042-msi" on the SOC. The MSI + * controller provides multiple(up to 32) interrupt sources to PLIC. + * Compared with the first method, the advantage is that the interrupt + * source is expanded, but because for SG2042, the MSI address provided by + * the MSI controller is fixed and only supports 64-bit address(> 2^32), + * it is not compatible with old PCIe devices that only support 32-bit MSI + * address. + * + * Method A & B can be configured in DTS, default is Method B. + */ + +#define MAX_MSI_IRQS 8 +#define MAX_MSI_IRQS_PER_CTRL 1 +#define MAX_MSI_CTRLS (MAX_MSI_IRQS / MAX_MSI_IRQS_PER_CTRL) +#define MSI_DEF_NUM_VECTORS MAX_MSI_IRQS +#define BYTE_NUM_PER_MSI_VEC 4 + +#define REG_CLEAR 0x0804 +#define REG_STATUS 0x0810 +#define REG_LINK0_MSI_ADDR_SIZE 0x085C +#define REG_LINK1_MSI_ADDR_SIZE 0x080C +#define REG_LINK0_MSI_ADDR_LOW 0x0860 +#define REG_LINK0_MSI_ADDR_HIGH 0x0864 +#define REG_LINK1_MSI_ADDR_LOW 0x0868 +#define REG_LINK1_MSI_ADDR_HIGH 0x086C + +#define REG_CLEAR_LINK0_BIT 2 +#define REG_CLEAR_LINK1_BIT 3 +#define REG_STATUS_LINK0_BIT 2 +#define REG_STATUS_LINK1_BIT 3 + +#define REG_LINK0_MSI_ADDR_SIZE_MASK GENMASK(15, 0) +#define REG_LINK1_MSI_ADDR_SIZE_MASK GENMASK(31, 16) + +struct sg2042_pcie { + struct cdns_pcie *cdns_pcie; + + struct regmap *syscon; + + u32 port; + + struct irq_domain *msi_domain; + + int msi_irq; + + dma_addr_t msi_phys; + void *msi_virt; + + u32 num_applied_vecs; /* used to speed up ISR */ + + raw_spinlock_t msi_lock; + DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS); +}; + +static void sg2042_pcie_msi_clear_status(struct sg2042_pcie *pcie) +{ + u32 status, clr_msi_in_bit; + + if (pcie->port == 1) + clr_msi_in_bit = BIT(REG_CLEAR_LINK1_BIT); + else + clr_msi_in_bit = BIT(REG_CLEAR_LINK0_BIT); + + regmap_read(pcie->syscon, REG_CLEAR, &status); + status |= clr_msi_in_bit; + regmap_write(pcie->syscon, REG_CLEAR, status); + + /* need write 0 to reset, hardware can not reset automatically */ + status &= ~clr_msi_in_bit; + regmap_write(pcie->syscon, REG_CLEAR, status); +} + +#ifdef CONFIG_SMP +static int sg2042_pcie_msi_irq_set_affinity(struct irq_data *d, + const struct cpumask *mask, + bool force) +{ + if (d->parent_data) + return irq_chip_set_affinity_parent(d, mask, force); + + return -EINVAL; +} +#endif /* CONFIG_SMP */ + +static void sg2042_pcie_msi_irq_compose_msi_msg(struct irq_data *d, + struct msi_msg *msg) +{ + struct sg2042_pcie *pcie = irq_data_get_irq_chip_data(d); + struct device *dev = pcie->cdns_pcie->dev; + + msg->address_lo = lower_32_bits(pcie->msi_phys) + BYTE_NUM_PER_MSI_VEC * d->hwirq; + msg->address_hi = upper_32_bits(pcie->msi_phys); + msg->data = 1; + + if (d->hwirq > pcie->num_applied_vecs) + pcie->num_applied_vecs = d->hwirq; + + dev_dbg(dev, "compose MSI msg hwirq[%ld] address_hi[%#x] address_lo[%#x]\n", + d->hwirq, msg->address_hi, msg->address_lo); +} + +static void sg2042_pcie_msi_irq_ack(struct irq_data *d) +{ + struct sg2042_pcie *pcie = irq_data_get_irq_chip_data(d); + + sg2042_pcie_msi_clear_status(pcie); +} + +static struct irq_chip sg2042_pcie_msi_bottom_chip = { + .name = "SG2042 PCIe PLIC-MSI translator", + .irq_ack = sg2042_pcie_msi_irq_ack, + .irq_compose_msi_msg = sg2042_pcie_msi_irq_compose_msi_msg, +#ifdef CONFIG_SMP + .irq_set_affinity = sg2042_pcie_msi_irq_set_affinity, +#endif +}; + +static int sg2042_pcie_irq_domain_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *args) +{ + struct sg2042_pcie *pcie = domain->host_data; + unsigned long flags; + u32 i; + int bit; + + raw_spin_lock_irqsave(&pcie->msi_lock, flags); + + bit = bitmap_find_free_region(pcie->msi_irq_in_use, MSI_DEF_NUM_VECTORS, + order_base_2(nr_irqs)); + + raw_spin_unlock_irqrestore(&pcie->msi_lock, flags); + + if (bit < 0) + return -ENOSPC; + + for (i = 0; i < nr_irqs; i++) + irq_domain_set_info(domain, virq + i, bit + i, + &sg2042_pcie_msi_bottom_chip, + pcie, handle_edge_irq, + NULL, NULL); + + return 0; +} + +static void sg2042_pcie_irq_domain_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *d = irq_domain_get_irq_data(domain, virq); + struct sg2042_pcie *pcie = irq_data_get_irq_chip_data(d); + unsigned long flags; + + raw_spin_lock_irqsave(&pcie->msi_lock, flags); + + bitmap_release_region(pcie->msi_irq_in_use, d->hwirq, + order_base_2(nr_irqs)); + + raw_spin_unlock_irqrestore(&pcie->msi_lock, flags); +} + +static const struct irq_domain_ops sg2042_pcie_msi_domain_ops = { + .alloc = sg2042_pcie_irq_domain_alloc, + .free = sg2042_pcie_irq_domain_free, +}; + +static int sg2042_pcie_init_msi_data(struct sg2042_pcie *pcie) +{ + struct device *dev = pcie->cdns_pcie->dev; + u32 value; + int ret; + + raw_spin_lock_init(&pcie->msi_lock); + + /* + * Though the PCIe controller can address >32-bit address space, to + * facilitate endpoints that support only 32-bit MSI target address, + * the mask is set to 32-bit to make sure that MSI target address is + * always a 32-bit address + */ + ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); + if (ret < 0) + return ret; + + pcie->msi_virt = dma_alloc_coherent(dev, BYTE_NUM_PER_MSI_VEC * MAX_MSI_IRQS, + &pcie->msi_phys, GFP_KERNEL); + if (!pcie->msi_virt) + return -ENOMEM; + + /* Program the MSI address and size */ + if (pcie->port == 1) { + regmap_write(pcie->syscon, REG_LINK1_MSI_ADDR_LOW, + lower_32_bits(pcie->msi_phys)); + regmap_write(pcie->syscon, REG_LINK1_MSI_ADDR_HIGH, + upper_32_bits(pcie->msi_phys)); + + regmap_read(pcie->syscon, REG_LINK1_MSI_ADDR_SIZE, &value); + value = (value & REG_LINK1_MSI_ADDR_SIZE_MASK) | MAX_MSI_IRQS; + regmap_write(pcie->syscon, REG_LINK1_MSI_ADDR_SIZE, value); + } else { + regmap_write(pcie->syscon, REG_LINK0_MSI_ADDR_LOW, + lower_32_bits(pcie->msi_phys)); + regmap_write(pcie->syscon, REG_LINK0_MSI_ADDR_HIGH, + upper_32_bits(pcie->msi_phys)); + + regmap_read(pcie->syscon, REG_LINK0_MSI_ADDR_SIZE, &value); + value = (value & REG_LINK0_MSI_ADDR_SIZE_MASK) | (MAX_MSI_IRQS << 16); + regmap_write(pcie->syscon, REG_LINK0_MSI_ADDR_SIZE, value); + } + + return 0; +} + +static irqreturn_t sg2042_pcie_msi_handle_irq(struct sg2042_pcie *pcie) +{ + u32 i, pos; + unsigned long val; + u32 status, num_vectors; + irqreturn_t ret = IRQ_NONE; + + num_vectors = pcie->num_applied_vecs; + for (i = 0; i <= num_vectors; i++) { + status = readl((void *)(pcie->msi_virt + i * BYTE_NUM_PER_MSI_VEC)); + if (!status) + continue; + + ret = IRQ_HANDLED; + val = status; + pos = 0; + while ((pos = find_next_bit(&val, MAX_MSI_IRQS_PER_CTRL, + pos)) != MAX_MSI_IRQS_PER_CTRL) { + generic_handle_domain_irq(pcie->msi_domain, + (i * MAX_MSI_IRQS_PER_CTRL) + + pos); + pos++; + } + writel(0, ((void *)(pcie->msi_virt) + i * BYTE_NUM_PER_MSI_VEC)); + } + return ret; +} + +static void sg2042_pcie_msi_chained_isr(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + u32 status, st_msi_in_bit; + struct sg2042_pcie *pcie; + + chained_irq_enter(chip, desc); + + pcie = irq_desc_get_handler_data(desc); + if (pcie->port == 1) + st_msi_in_bit = REG_STATUS_LINK1_BIT; + else + st_msi_in_bit = REG_STATUS_LINK0_BIT; + + regmap_read(pcie->syscon, REG_STATUS, &status); + if ((status >> st_msi_in_bit) & 0x1) { + sg2042_pcie_msi_clear_status(pcie); + + sg2042_pcie_msi_handle_irq(pcie); + } + + chained_irq_exit(chip, desc); +} + +#define SG2042_PCIE_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS) + +#define SG2042_PCIE_MSI_FLAGS_SUPPORTED MSI_GENERIC_FLAGS_MASK + +static struct msi_parent_ops sg2042_pcie_msi_parent_ops = { + .required_flags = SG2042_PCIE_MSI_FLAGS_REQUIRED, + .supported_flags = SG2042_PCIE_MSI_FLAGS_SUPPORTED, + .bus_select_mask = MATCH_PCI_MSI, + .bus_select_token = DOMAIN_BUS_NEXUS, + .prefix = "SG2042-", + .init_dev_msi_info = msi_lib_init_dev_msi_info, +}; + +static int sg2042_pcie_setup_msi(struct sg2042_pcie *pcie, struct device_node *msi_node) +{ + struct device *dev = pcie->cdns_pcie->dev; + struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); + struct irq_domain *parent_domain; + int ret = 0; + + if (!of_property_read_bool(msi_node, "msi-controller")) + return -ENODEV; + + ret = of_irq_get_byname(msi_node, "msi"); + if (ret <= 0) { + dev_err(dev, "%pOF: failed to get MSI irq\n", msi_node); + return ret; + } + pcie->msi_irq = ret; + + irq_set_chained_handler_and_data(pcie->msi_irq, + sg2042_pcie_msi_chained_isr, pcie); + + parent_domain = irq_domain_create_linear(fwnode, MSI_DEF_NUM_VECTORS, + &sg2042_pcie_msi_domain_ops, pcie); + if (!parent_domain) { + dev_err(dev, "%pfw: Failed to create IRQ domain\n", fwnode); + return -ENOMEM; + } + irq_domain_update_bus_token(parent_domain, DOMAIN_BUS_NEXUS); + + parent_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT; + parent_domain->msi_parent_ops = &sg2042_pcie_msi_parent_ops; + + pcie->msi_domain = parent_domain; + + ret = sg2042_pcie_init_msi_data(pcie); + if (ret) { + dev_err(dev, "Failed to initialize MSI data!\n"); + return ret; + } + + return 0; +} + +static void sg2042_pcie_free_msi(struct sg2042_pcie *pcie) +{ + struct device *dev = pcie->cdns_pcie->dev; + + if (pcie->msi_irq) + irq_set_chained_handler_and_data(pcie->msi_irq, NULL, NULL); + + if (pcie->msi_virt) + dma_free_coherent(dev, BYTE_NUM_PER_MSI_VEC * MAX_MSI_IRQS, + pcie->msi_virt, pcie->msi_phys); +} + +/* + * SG2042 only support 4-byte aligned access, so for the rootbus (i.e. to read + * the PCIe controller itself, read32 is required. For non-rootbus (i.e. to read + * the PCIe peripheral registers, supports 1/2/4 byte aligned access, so + * directly use read should be fine. + * + * The same is true for write. + */ +static int sg2042_pcie_config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *value) +{ + if (pci_is_root_bus(bus)) + return pci_generic_config_read32(bus, devfn, where, size, + value); + + return pci_generic_config_read(bus, devfn, where, size, value); +} + +static int sg2042_pcie_config_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 value) +{ + if (pci_is_root_bus(bus)) + return pci_generic_config_write32(bus, devfn, where, size, + value); + + return pci_generic_config_write(bus, devfn, where, size, value); +} + +static struct pci_ops sg2042_pcie_host_ops = { + .map_bus = cdns_pci_map_bus, + .read = sg2042_pcie_config_read, + .write = sg2042_pcie_config_write, +}; + +/* Dummy ops which will be assigned to cdns_pcie.ops, which must be !NULL. */ +static const struct cdns_pcie_ops sg2042_cdns_pcie_ops = {}; + +static int sg2042_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct pci_host_bridge *bridge; + struct device_node *np_syscon; + struct device_node *msi_node; + struct cdns_pcie *cdns_pcie; + struct sg2042_pcie *pcie; + struct cdns_pcie_rc *rc; + struct regmap *syscon; + int ret; + + if (!IS_ENABLED(CONFIG_PCIE_CADENCE_HOST)) + return -ENODEV; + + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc)); + if (!bridge) { + dev_err(dev, "Failed to alloc host bridge!\n"); + return -ENOMEM; + } + + bridge->ops = &sg2042_pcie_host_ops; + + rc = pci_host_bridge_priv(bridge); + cdns_pcie = &rc->pcie; + cdns_pcie->dev = dev; + cdns_pcie->ops = &sg2042_cdns_pcie_ops; + pcie->cdns_pcie = cdns_pcie; + + np_syscon = of_parse_phandle(np, "sophgo,syscon-pcie-ctrl", 0); + if (!np_syscon) { + dev_err(dev, "Failed to get syscon node\n"); + return -ENOMEM; + } + syscon = syscon_node_to_regmap(np_syscon); + if (IS_ERR(syscon)) { + dev_err(dev, "Failed to get regmap for syscon\n"); + return -ENOMEM; + } + pcie->syscon = syscon; + + if (of_property_read_u32(np, "sophgo,pcie-port", &pcie->port)) { + dev_err(dev, "Unable to parse sophgo,pcie-port\n"); + return -EINVAL; + } + + platform_set_drvdata(pdev, pcie); + + pm_runtime_enable(dev); + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "pm_runtime_get_sync failed\n"); + goto err_get_sync; + } + + msi_node = of_parse_phandle(dev->of_node, "msi-parent", 0); + if (!msi_node) { + dev_err(dev, "Failed to get msi-parent!\n"); + return -1; + } + + if (of_device_is_compatible(msi_node, "sophgo,sg2042-pcie-msi")) { + ret = sg2042_pcie_setup_msi(pcie, msi_node); + if (ret < 0) + goto err_setup_msi; + } + + ret = cdns_pcie_init_phy(dev, cdns_pcie); + if (ret) { + dev_err(dev, "Failed to init phy!\n"); + goto err_setup_msi; + } + + ret = cdns_pcie_host_setup(rc); + if (ret < 0) { + dev_err(dev, "Failed to setup host!\n"); + goto err_host_setup; + } + + return 0; + +err_host_setup: + cdns_pcie_disable_phy(cdns_pcie); + +err_setup_msi: + sg2042_pcie_free_msi(pcie); + +err_get_sync: + pm_runtime_put(dev); + pm_runtime_disable(dev); + + return ret; +} + +static void sg2042_pcie_shutdown(struct platform_device *pdev) +{ + struct sg2042_pcie *pcie = platform_get_drvdata(pdev); + struct cdns_pcie *cdns_pcie = pcie->cdns_pcie; + struct device *dev = &pdev->dev; + + sg2042_pcie_free_msi(pcie); + + cdns_pcie_disable_phy(cdns_pcie); + + pm_runtime_put(dev); + pm_runtime_disable(dev); +} + +static const struct of_device_id sg2042_pcie_of_match[] = { + { .compatible = "sophgo,sg2042-pcie-host" }, + {}, +}; + +static struct platform_driver sg2042_pcie_driver = { + .driver = { + .name = "sg2042-pcie", + .of_match_table = sg2042_pcie_of_match, + .pm = &cdns_pcie_pm_ops, + }, + .probe = sg2042_pcie_probe, + .shutdown = sg2042_pcie_shutdown, +}; +builtin_platform_driver(sg2042_pcie_driver);