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
sg2042_pcie_probe(struct platform_device * pdev)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
sg2042_pcie_remove(struct platform_device * pdev)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
sg2042_pcie_suspend_noirq(struct device * dev)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
sg2042_pcie_resume_noirq(struct device * dev)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