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 cdns_pcie_rc *rc; 78 79 rc = container_of(pcie, struct cdns_pcie_rc, pcie); 80 cdns_pcie_host_disable(rc); 81 82 cdns_pcie_disable_phy(pcie); 83 } 84 85 static int sg2042_pcie_suspend_noirq(struct device *dev) 86 { 87 struct cdns_pcie *pcie = dev_get_drvdata(dev); 88 89 cdns_pcie_disable_phy(pcie); 90 91 return 0; 92 } 93 94 static int sg2042_pcie_resume_noirq(struct device *dev) 95 { 96 struct cdns_pcie *pcie = dev_get_drvdata(dev); 97 int ret; 98 99 ret = cdns_pcie_enable_phy(pcie); 100 if (ret) { 101 dev_err(dev, "failed to enable PHY\n"); 102 return ret; 103 } 104 105 return 0; 106 } 107 108 static DEFINE_NOIRQ_DEV_PM_OPS(sg2042_pcie_pm_ops, 109 sg2042_pcie_suspend_noirq, 110 sg2042_pcie_resume_noirq); 111 112 static const struct of_device_id sg2042_pcie_of_match[] = { 113 { .compatible = "sophgo,sg2042-pcie-host" }, 114 {}, 115 }; 116 MODULE_DEVICE_TABLE(of, sg2042_pcie_of_match); 117 118 static struct platform_driver sg2042_pcie_driver = { 119 .driver = { 120 .name = "sg2042-pcie", 121 .of_match_table = sg2042_pcie_of_match, 122 .pm = pm_sleep_ptr(&sg2042_pcie_pm_ops), 123 }, 124 .probe = sg2042_pcie_probe, 125 .remove = sg2042_pcie_remove, 126 }; 127 module_platform_driver(sg2042_pcie_driver); 128 129 MODULE_LICENSE("GPL"); 130 MODULE_DESCRIPTION("PCIe controller driver for SG2042 SoCs"); 131 MODULE_AUTHOR("Chen Wang <unicorn_wang@outlook.com>"); 132