xref: /linux/drivers/pci/controller/cadence/pcie-sg2042.c (revision 2f2c7254931f41b5736e3ba12aaa9ac1bbeeeb92)
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 
sg2042_pcie_probe(struct platform_device * pdev)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 
sg2042_pcie_remove(struct platform_device * pdev)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 
sg2042_pcie_suspend_noirq(struct device * dev)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 
sg2042_pcie_resume_noirq(struct device * dev)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