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