Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PCI: sg2042: Add Sophgo SG2042 PCIe driver

Add support for PCIe controller in Sophgo SG2042 SoC. The controller uses
the Cadence PCIe core programmed by pcie-cadence* common driver. The PCIe
controller in SG2042 works in host mode only, supporting data rate up to 16
GT/s and lanes up to x16 or x8.

Signed-off-by: Chen Wang <unicorn_wang@outlook.com>
[mani: reworded description and minor code cleanups]
Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
Link: https://patch.msgid.link/01b0a57cd9dba8bed7c1f2d52997046c2c6f042b.1757643388.git.unicorn_wang@outlook.com

authored by

Chen Wang and committed by
Manivannan Sadhasivam
1c72774d 49a6c160

+145
+10
drivers/pci/controller/cadence/Kconfig
··· 42 42 endpoint mode. This PCIe controller may be embedded into many 43 43 different vendors SoCs. 44 44 45 + config PCIE_SG2042_HOST 46 + tristate "Sophgo SG2042 PCIe controller (host mode)" 47 + depends on OF && (ARCH_SOPHGO || COMPILE_TEST) 48 + select PCIE_CADENCE_HOST 49 + help 50 + Say Y here if you want to support the Sophgo SG2042 PCIe platform 51 + controller in host mode. Sophgo SG2042 PCIe controller uses Cadence 52 + PCIe core. 53 + 45 54 config PCI_J721E 46 55 tristate 47 56 select PCIE_CADENCE_HOST if PCI_J721E_HOST != n ··· 76 67 Say Y here if you want to support the TI J721E PCIe platform 77 68 controller in endpoint mode. TI J721E PCIe controller uses Cadence PCIe 78 69 core. 70 + 79 71 endmenu
+1
drivers/pci/controller/cadence/Makefile
··· 4 4 obj-$(CONFIG_PCIE_CADENCE_EP) += pcie-cadence-ep.o 5 5 obj-$(CONFIG_PCIE_CADENCE_PLAT) += pcie-cadence-plat.o 6 6 obj-$(CONFIG_PCI_J721E) += pci-j721e.o 7 + obj-$(CONFIG_PCIE_SG2042_HOST) += pcie-sg2042.o
+134
drivers/pci/controller/cadence/pcie-sg2042.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * pcie-sg2042 - PCIe controller driver for Sophgo SG2042 SoC 4 + * 5 + * Copyright (C) 2025 Sophgo Technology Inc. 6 + * Copyright (C) 2025 Chen Wang <unicorn_wang@outlook.com> 7 + */ 8 + 9 + #include <linux/mod_devicetable.h> 10 + #include <linux/pci.h> 11 + #include <linux/platform_device.h> 12 + #include <linux/pm_runtime.h> 13 + 14 + #include "pcie-cadence.h" 15 + 16 + /* 17 + * SG2042 only supports 4-byte aligned access, so for the rootbus (i.e. to 18 + * read/write the Root Port itself, read32/write32 is required. For 19 + * non-rootbus (i.e. to read/write the PCIe peripheral registers, supports 20 + * 1/2/4 byte aligned access, so directly using read/write should be fine. 21 + */ 22 + 23 + static struct pci_ops sg2042_pcie_root_ops = { 24 + .map_bus = cdns_pci_map_bus, 25 + .read = pci_generic_config_read32, 26 + .write = pci_generic_config_write32, 27 + }; 28 + 29 + static struct pci_ops sg2042_pcie_child_ops = { 30 + .map_bus = cdns_pci_map_bus, 31 + .read = pci_generic_config_read, 32 + .write = pci_generic_config_write, 33 + }; 34 + 35 + static int sg2042_pcie_probe(struct platform_device *pdev) 36 + { 37 + struct device *dev = &pdev->dev; 38 + struct pci_host_bridge *bridge; 39 + struct cdns_pcie *pcie; 40 + struct cdns_pcie_rc *rc; 41 + int ret; 42 + 43 + bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc)); 44 + if (!bridge) 45 + return dev_err_probe(dev, -ENOMEM, "Failed to alloc host bridge!\n"); 46 + 47 + bridge->ops = &sg2042_pcie_root_ops; 48 + bridge->child_ops = &sg2042_pcie_child_ops; 49 + 50 + rc = pci_host_bridge_priv(bridge); 51 + pcie = &rc->pcie; 52 + pcie->dev = dev; 53 + 54 + platform_set_drvdata(pdev, pcie); 55 + 56 + pm_runtime_set_active(dev); 57 + pm_runtime_no_callbacks(dev); 58 + devm_pm_runtime_enable(dev); 59 + 60 + ret = cdns_pcie_init_phy(dev, pcie); 61 + if (ret) 62 + return dev_err_probe(dev, ret, "Failed to init phy!\n"); 63 + 64 + ret = cdns_pcie_host_setup(rc); 65 + if (ret) { 66 + dev_err_probe(dev, ret, "Failed to setup host!\n"); 67 + cdns_pcie_disable_phy(pcie); 68 + return ret; 69 + } 70 + 71 + return 0; 72 + } 73 + 74 + static void sg2042_pcie_remove(struct platform_device *pdev) 75 + { 76 + struct cdns_pcie *pcie = platform_get_drvdata(pdev); 77 + struct device *dev = &pdev->dev; 78 + struct cdns_pcie_rc *rc; 79 + 80 + rc = container_of(pcie, struct cdns_pcie_rc, pcie); 81 + cdns_pcie_host_disable(rc); 82 + 83 + cdns_pcie_disable_phy(pcie); 84 + 85 + pm_runtime_disable(dev); 86 + } 87 + 88 + static int sg2042_pcie_suspend_noirq(struct device *dev) 89 + { 90 + struct cdns_pcie *pcie = dev_get_drvdata(dev); 91 + 92 + cdns_pcie_disable_phy(pcie); 93 + 94 + return 0; 95 + } 96 + 97 + static int sg2042_pcie_resume_noirq(struct device *dev) 98 + { 99 + struct cdns_pcie *pcie = dev_get_drvdata(dev); 100 + int ret; 101 + 102 + ret = cdns_pcie_enable_phy(pcie); 103 + if (ret) { 104 + dev_err(dev, "failed to enable PHY\n"); 105 + return ret; 106 + } 107 + 108 + return 0; 109 + } 110 + 111 + static DEFINE_NOIRQ_DEV_PM_OPS(sg2042_pcie_pm_ops, 112 + sg2042_pcie_suspend_noirq, 113 + sg2042_pcie_resume_noirq); 114 + 115 + static const struct of_device_id sg2042_pcie_of_match[] = { 116 + { .compatible = "sophgo,sg2042-pcie-host" }, 117 + {}, 118 + }; 119 + MODULE_DEVICE_TABLE(of, sg2042_pcie_of_match); 120 + 121 + static struct platform_driver sg2042_pcie_driver = { 122 + .driver = { 123 + .name = "sg2042-pcie", 124 + .of_match_table = sg2042_pcie_of_match, 125 + .pm = pm_sleep_ptr(&sg2042_pcie_pm_ops), 126 + }, 127 + .probe = sg2042_pcie_probe, 128 + .remove = sg2042_pcie_remove, 129 + }; 130 + module_platform_driver(sg2042_pcie_driver); 131 + 132 + MODULE_LICENSE("GPL"); 133 + MODULE_DESCRIPTION("PCIe controller driver for SG2042 SoCs"); 134 + MODULE_AUTHOR("Chen Wang <unicorn_wang@outlook.com>");