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>"); 135