Skip to content

Commit 467d9c0

Browse files
inochisabjorn-helgaas
authored andcommitted
PCI: dwc: Add Sophgo SG2044 PCIe controller driver in Root Complex mode
Add driver support for DesignWare based PCIe controller in SG2044 SoC. The driver currently supports the Root Complex mode. Signed-off-by: Inochi Amaoto <[email protected]> [mani: renamed the driver to 'pcie-sophgo.c' and Kconfig fix] Signed-off-by: Manivannan Sadhasivam <[email protected]> [bhelgaas: whitespace] Signed-off-by: Bjorn Helgaas <[email protected]> Link: https://patch.msgid.link/[email protected]
1 parent a202f09 commit 467d9c0

File tree

3 files changed

+268
-0
lines changed

3 files changed

+268
-0
lines changed

drivers/pci/controller/dwc/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,16 @@ config PCIE_UNIPHIER_EP
402402
Say Y here if you want PCIe endpoint controller support on
403403
UniPhier SoCs. This driver supports Pro5 SoC.
404404

405+
config PCIE_SOPHGO_DW
406+
bool "Sophgo DesignWare PCIe controller (host mode)"
407+
depends on ARCH_SOPHGO || COMPILE_TEST
408+
depends on PCI_MSI
409+
depends on OF
410+
select PCIE_DW_HOST
411+
help
412+
Say Y here if you want PCIe host controller support on
413+
Sophgo SoCs.
414+
405415
config PCIE_SPEAR13XX
406416
bool "STMicroelectronics SPEAr PCIe controller"
407417
depends on ARCH_SPEAR13XX || COMPILE_TEST

drivers/pci/controller/dwc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ obj-$(CONFIG_PCIE_QCOM_EP) += pcie-qcom-ep.o
2020
obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
2121
obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
2222
obj-$(CONFIG_PCIE_ROCKCHIP_DW) += pcie-dw-rockchip.o
23+
obj-$(CONFIG_PCIE_SOPHGO_DW) += pcie-sophgo.o
2324
obj-$(CONFIG_PCIE_INTEL_GW) += pcie-intel-gw.o
2425
obj-$(CONFIG_PCIE_KEEMBAY) += pcie-keembay.o
2526
obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Sophgo DesignWare based PCIe host controller driver
4+
*/
5+
6+
#include <linux/bits.h>
7+
#include <linux/clk.h>
8+
#include <linux/irqchip/chained_irq.h>
9+
#include <linux/irqdomain.h>
10+
#include <linux/module.h>
11+
#include <linux/property.h>
12+
#include <linux/platform_device.h>
13+
14+
#include "pcie-designware.h"
15+
16+
#define to_sophgo_pcie(x) dev_get_drvdata((x)->dev)
17+
18+
#define PCIE_INT_SIGNAL 0xc48
19+
#define PCIE_INT_EN 0xca0
20+
21+
#define PCIE_INT_SIGNAL_INTX GENMASK(8, 5)
22+
23+
#define PCIE_INT_EN_INTX GENMASK(4, 1)
24+
#define PCIE_INT_EN_INT_MSI BIT(5)
25+
26+
struct sophgo_pcie {
27+
struct dw_pcie pci;
28+
void __iomem *app_base;
29+
struct clk_bulk_data *clks;
30+
unsigned int clk_cnt;
31+
struct irq_domain *irq_domain;
32+
};
33+
34+
static int sophgo_pcie_readl_app(struct sophgo_pcie *sophgo, u32 reg)
35+
{
36+
return readl_relaxed(sophgo->app_base + reg);
37+
}
38+
39+
static void sophgo_pcie_writel_app(struct sophgo_pcie *sophgo, u32 val, u32 reg)
40+
{
41+
writel_relaxed(val, sophgo->app_base + reg);
42+
}
43+
44+
static void sophgo_pcie_intx_handler(struct irq_desc *desc)
45+
{
46+
struct dw_pcie_rp *pp = irq_desc_get_handler_data(desc);
47+
struct irq_chip *chip = irq_desc_get_chip(desc);
48+
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
49+
struct sophgo_pcie *sophgo = to_sophgo_pcie(pci);
50+
unsigned long hwirq, reg;
51+
52+
chained_irq_enter(chip, desc);
53+
54+
reg = sophgo_pcie_readl_app(sophgo, PCIE_INT_SIGNAL);
55+
reg = FIELD_GET(PCIE_INT_SIGNAL_INTX, reg);
56+
57+
for_each_set_bit(hwirq, &reg, PCI_NUM_INTX)
58+
generic_handle_domain_irq(sophgo->irq_domain, hwirq);
59+
60+
chained_irq_exit(chip, desc);
61+
}
62+
63+
static void sophgo_intx_irq_mask(struct irq_data *d)
64+
{
65+
struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d);
66+
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
67+
struct sophgo_pcie *sophgo = to_sophgo_pcie(pci);
68+
unsigned long flags;
69+
u32 val;
70+
71+
raw_spin_lock_irqsave(&pp->lock, flags);
72+
73+
val = sophgo_pcie_readl_app(sophgo, PCIE_INT_EN);
74+
val &= ~FIELD_PREP(PCIE_INT_EN_INTX, BIT(d->hwirq));
75+
sophgo_pcie_writel_app(sophgo, val, PCIE_INT_EN);
76+
77+
raw_spin_unlock_irqrestore(&pp->lock, flags);
78+
};
79+
80+
static void sophgo_intx_irq_unmask(struct irq_data *d)
81+
{
82+
struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d);
83+
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
84+
struct sophgo_pcie *sophgo = to_sophgo_pcie(pci);
85+
unsigned long flags;
86+
u32 val;
87+
88+
raw_spin_lock_irqsave(&pp->lock, flags);
89+
90+
val = sophgo_pcie_readl_app(sophgo, PCIE_INT_EN);
91+
val |= FIELD_PREP(PCIE_INT_EN_INTX, BIT(d->hwirq));
92+
sophgo_pcie_writel_app(sophgo, val, PCIE_INT_EN);
93+
94+
raw_spin_unlock_irqrestore(&pp->lock, flags);
95+
};
96+
97+
static struct irq_chip sophgo_intx_irq_chip = {
98+
.name = "INTx",
99+
.irq_mask = sophgo_intx_irq_mask,
100+
.irq_unmask = sophgo_intx_irq_unmask,
101+
};
102+
103+
static int sophgo_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
104+
irq_hw_number_t hwirq)
105+
{
106+
irq_set_chip_and_handler(irq, &sophgo_intx_irq_chip, handle_level_irq);
107+
irq_set_chip_data(irq, domain->host_data);
108+
109+
return 0;
110+
}
111+
112+
static const struct irq_domain_ops intx_domain_ops = {
113+
.map = sophgo_pcie_intx_map,
114+
};
115+
116+
static int sophgo_pcie_init_irq_domain(struct dw_pcie_rp *pp)
117+
{
118+
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
119+
struct sophgo_pcie *sophgo = to_sophgo_pcie(pci);
120+
struct device *dev = sophgo->pci.dev;
121+
struct fwnode_handle *intc;
122+
int irq;
123+
124+
intc = device_get_named_child_node(dev, "interrupt-controller");
125+
if (!intc) {
126+
dev_err(dev, "missing child interrupt-controller node\n");
127+
return -ENODEV;
128+
}
129+
130+
irq = fwnode_irq_get(intc, 0);
131+
if (irq < 0) {
132+
dev_err(dev, "failed to get INTx irq number\n");
133+
fwnode_handle_put(intc);
134+
return irq;
135+
}
136+
137+
sophgo->irq_domain = irq_domain_create_linear(intc, PCI_NUM_INTX,
138+
&intx_domain_ops, pp);
139+
fwnode_handle_put(intc);
140+
if (!sophgo->irq_domain) {
141+
dev_err(dev, "failed to get a INTx irq domain\n");
142+
return -EINVAL;
143+
}
144+
145+
return irq;
146+
}
147+
148+
static void sophgo_pcie_msi_enable(struct dw_pcie_rp *pp)
149+
{
150+
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
151+
struct sophgo_pcie *sophgo = to_sophgo_pcie(pci);
152+
unsigned long flags;
153+
u32 val;
154+
155+
raw_spin_lock_irqsave(&pp->lock, flags);
156+
157+
val = sophgo_pcie_readl_app(sophgo, PCIE_INT_EN);
158+
val |= PCIE_INT_EN_INT_MSI;
159+
sophgo_pcie_writel_app(sophgo, val, PCIE_INT_EN);
160+
161+
raw_spin_unlock_irqrestore(&pp->lock, flags);
162+
}
163+
164+
static int sophgo_pcie_host_init(struct dw_pcie_rp *pp)
165+
{
166+
int irq;
167+
168+
irq = sophgo_pcie_init_irq_domain(pp);
169+
if (irq < 0)
170+
return irq;
171+
172+
irq_set_chained_handler_and_data(irq, sophgo_pcie_intx_handler, pp);
173+
174+
sophgo_pcie_msi_enable(pp);
175+
176+
return 0;
177+
}
178+
179+
static const struct dw_pcie_host_ops sophgo_pcie_host_ops = {
180+
.init = sophgo_pcie_host_init,
181+
};
182+
183+
static int sophgo_pcie_clk_init(struct sophgo_pcie *sophgo)
184+
{
185+
struct device *dev = sophgo->pci.dev;
186+
int ret;
187+
188+
ret = devm_clk_bulk_get_all_enabled(dev, &sophgo->clks);
189+
if (ret < 0)
190+
return dev_err_probe(dev, ret, "failed to get clocks\n");
191+
192+
sophgo->clk_cnt = ret;
193+
194+
return 0;
195+
}
196+
197+
static int sophgo_pcie_resource_get(struct platform_device *pdev,
198+
struct sophgo_pcie *sophgo)
199+
{
200+
sophgo->app_base = devm_platform_ioremap_resource_byname(pdev, "app");
201+
if (IS_ERR(sophgo->app_base))
202+
return dev_err_probe(&pdev->dev, PTR_ERR(sophgo->app_base),
203+
"failed to map app registers\n");
204+
205+
return 0;
206+
}
207+
208+
static int sophgo_pcie_configure_rc(struct sophgo_pcie *sophgo)
209+
{
210+
struct dw_pcie_rp *pp;
211+
212+
pp = &sophgo->pci.pp;
213+
pp->ops = &sophgo_pcie_host_ops;
214+
215+
return dw_pcie_host_init(pp);
216+
}
217+
218+
static int sophgo_pcie_probe(struct platform_device *pdev)
219+
{
220+
struct device *dev = &pdev->dev;
221+
struct sophgo_pcie *sophgo;
222+
int ret;
223+
224+
sophgo = devm_kzalloc(dev, sizeof(*sophgo), GFP_KERNEL);
225+
if (!sophgo)
226+
return -ENOMEM;
227+
228+
platform_set_drvdata(pdev, sophgo);
229+
230+
sophgo->pci.dev = dev;
231+
232+
ret = sophgo_pcie_resource_get(pdev, sophgo);
233+
if (ret)
234+
return ret;
235+
236+
ret = sophgo_pcie_clk_init(sophgo);
237+
if (ret)
238+
return ret;
239+
240+
return sophgo_pcie_configure_rc(sophgo);
241+
}
242+
243+
static const struct of_device_id sophgo_pcie_of_match[] = {
244+
{ .compatible = "sophgo,sg2044-pcie" },
245+
{ }
246+
};
247+
MODULE_DEVICE_TABLE(of, sophgo_pcie_of_match);
248+
249+
static struct platform_driver sophgo_pcie_driver = {
250+
.driver = {
251+
.name = "sophgo-pcie",
252+
.of_match_table = sophgo_pcie_of_match,
253+
.suppress_bind_attrs = true,
254+
},
255+
.probe = sophgo_pcie_probe,
256+
};
257+
builtin_platform_driver(sophgo_pcie_driver);

0 commit comments

Comments
 (0)