xref: /linux/drivers/pci/controller/cadence/pcie-sg2042.c (revision d30c1683aaecb93d2ab95685dc4300a33d3cea7a)
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