xref: /openbmc/linux/drivers/pci/controller/dwc/pci-layerscape-ep.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1a805770dSXiaowei Bao // SPDX-License-Identifier: GPL-2.0
2a805770dSXiaowei Bao /*
3a805770dSXiaowei Bao  * PCIe controller EP driver for Freescale Layerscape SoCs
4a805770dSXiaowei Bao  *
5a805770dSXiaowei Bao  * Copyright (C) 2018 NXP Semiconductor.
6a805770dSXiaowei Bao  *
7a805770dSXiaowei Bao  * Author: Xiaowei Bao <xiaowei.bao@nxp.com>
8a805770dSXiaowei Bao  */
9a805770dSXiaowei Bao 
10a805770dSXiaowei Bao #include <linux/kernel.h>
11a805770dSXiaowei Bao #include <linux/init.h>
12a805770dSXiaowei Bao #include <linux/of_pci.h>
13a805770dSXiaowei Bao #include <linux/of_platform.h>
14a805770dSXiaowei Bao #include <linux/of_address.h>
15a805770dSXiaowei Bao #include <linux/pci.h>
16a805770dSXiaowei Bao #include <linux/platform_device.h>
17a805770dSXiaowei Bao #include <linux/resource.h>
18a805770dSXiaowei Bao 
19a805770dSXiaowei Bao #include "pcie-designware.h"
20a805770dSXiaowei Bao 
21061cbfabSFrank Li #define PEX_PF0_CONFIG			0xC0014
22061cbfabSFrank Li #define PEX_PF0_CFG_READY		BIT(0)
23061cbfabSFrank Li 
24061cbfabSFrank Li /* PEX PFa PCIE PME and message interrupt registers*/
25061cbfabSFrank Li #define PEX_PF0_PME_MES_DR		0xC0020
26061cbfabSFrank Li #define PEX_PF0_PME_MES_DR_LUD		BIT(7)
27061cbfabSFrank Li #define PEX_PF0_PME_MES_DR_LDD		BIT(9)
28061cbfabSFrank Li #define PEX_PF0_PME_MES_DR_HRD		BIT(10)
29061cbfabSFrank Li 
30061cbfabSFrank Li #define PEX_PF0_PME_MES_IER		0xC0028
31061cbfabSFrank Li #define PEX_PF0_PME_MES_IER_LUDIE	BIT(7)
32061cbfabSFrank Li #define PEX_PF0_PME_MES_IER_LDDIE	BIT(9)
33061cbfabSFrank Li #define PEX_PF0_PME_MES_IER_HRDIE	BIT(10)
34061cbfabSFrank Li 
351b6a0e43SXiaowei Bao #define to_ls_pcie_ep(x)	dev_get_drvdata((x)->dev)
361b6a0e43SXiaowei Bao 
371b6a0e43SXiaowei Bao struct ls_pcie_ep_drvdata {
381b6a0e43SXiaowei Bao 	u32				func_offset;
391b6a0e43SXiaowei Bao 	const struct dw_pcie_ep_ops	*ops;
401b6a0e43SXiaowei Bao 	const struct dw_pcie_ops	*dw_pcie_ops;
411b6a0e43SXiaowei Bao };
421b6a0e43SXiaowei Bao 
43a805770dSXiaowei Bao struct ls_pcie_ep {
44a805770dSXiaowei Bao 	struct dw_pcie			*pci;
45cc255eb0SXiaowei Bao 	struct pci_epc_features		*ls_epc;
461b6a0e43SXiaowei Bao 	const struct ls_pcie_ep_drvdata *drvdata;
47061cbfabSFrank Li 	int				irq;
48*17cf8661SXiaowei Bao 	u32				lnkcap;
49061cbfabSFrank Li 	bool				big_endian;
50a805770dSXiaowei Bao };
51a805770dSXiaowei Bao 
ls_lut_readl(struct ls_pcie_ep * pcie,u32 offset)52061cbfabSFrank Li static u32 ls_lut_readl(struct ls_pcie_ep *pcie, u32 offset)
53061cbfabSFrank Li {
54061cbfabSFrank Li 	struct dw_pcie *pci = pcie->pci;
55061cbfabSFrank Li 
56061cbfabSFrank Li 	if (pcie->big_endian)
57061cbfabSFrank Li 		return ioread32be(pci->dbi_base + offset);
58061cbfabSFrank Li 	else
59061cbfabSFrank Li 		return ioread32(pci->dbi_base + offset);
60061cbfabSFrank Li }
61061cbfabSFrank Li 
ls_lut_writel(struct ls_pcie_ep * pcie,u32 offset,u32 value)62061cbfabSFrank Li static void ls_lut_writel(struct ls_pcie_ep *pcie, u32 offset, u32 value)
63061cbfabSFrank Li {
64061cbfabSFrank Li 	struct dw_pcie *pci = pcie->pci;
65061cbfabSFrank Li 
66061cbfabSFrank Li 	if (pcie->big_endian)
67061cbfabSFrank Li 		iowrite32be(value, pci->dbi_base + offset);
68061cbfabSFrank Li 	else
69061cbfabSFrank Li 		iowrite32(value, pci->dbi_base + offset);
70061cbfabSFrank Li }
71061cbfabSFrank Li 
ls_pcie_ep_event_handler(int irq,void * dev_id)72061cbfabSFrank Li static irqreturn_t ls_pcie_ep_event_handler(int irq, void *dev_id)
73061cbfabSFrank Li {
74061cbfabSFrank Li 	struct ls_pcie_ep *pcie = dev_id;
75061cbfabSFrank Li 	struct dw_pcie *pci = pcie->pci;
76061cbfabSFrank Li 	u32 val, cfg;
77*17cf8661SXiaowei Bao 	u8 offset;
78061cbfabSFrank Li 
79061cbfabSFrank Li 	val = ls_lut_readl(pcie, PEX_PF0_PME_MES_DR);
80061cbfabSFrank Li 	ls_lut_writel(pcie, PEX_PF0_PME_MES_DR, val);
81061cbfabSFrank Li 
82061cbfabSFrank Li 	if (!val)
83061cbfabSFrank Li 		return IRQ_NONE;
84061cbfabSFrank Li 
85061cbfabSFrank Li 	if (val & PEX_PF0_PME_MES_DR_LUD) {
86*17cf8661SXiaowei Bao 
87*17cf8661SXiaowei Bao 		offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
88*17cf8661SXiaowei Bao 
89*17cf8661SXiaowei Bao 		/*
90*17cf8661SXiaowei Bao 		 * The values of the Maximum Link Width and Supported Link
91*17cf8661SXiaowei Bao 		 * Speed from the Link Capabilities Register will be lost
92*17cf8661SXiaowei Bao 		 * during link down or hot reset. Restore initial value
93*17cf8661SXiaowei Bao 		 * that configured by the Reset Configuration Word (RCW).
94*17cf8661SXiaowei Bao 		 */
95*17cf8661SXiaowei Bao 		dw_pcie_dbi_ro_wr_en(pci);
96*17cf8661SXiaowei Bao 		dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, pcie->lnkcap);
97*17cf8661SXiaowei Bao 		dw_pcie_dbi_ro_wr_dis(pci);
98*17cf8661SXiaowei Bao 
99061cbfabSFrank Li 		cfg = ls_lut_readl(pcie, PEX_PF0_CONFIG);
100061cbfabSFrank Li 		cfg |= PEX_PF0_CFG_READY;
101061cbfabSFrank Li 		ls_lut_writel(pcie, PEX_PF0_CONFIG, cfg);
102061cbfabSFrank Li 		dw_pcie_ep_linkup(&pci->ep);
103061cbfabSFrank Li 
104061cbfabSFrank Li 		dev_dbg(pci->dev, "Link up\n");
105061cbfabSFrank Li 	} else if (val & PEX_PF0_PME_MES_DR_LDD) {
106061cbfabSFrank Li 		dev_dbg(pci->dev, "Link down\n");
107d28c0d84SFrank Li 		pci_epc_linkdown(pci->ep.epc);
108061cbfabSFrank Li 	} else if (val & PEX_PF0_PME_MES_DR_HRD) {
109061cbfabSFrank Li 		dev_dbg(pci->dev, "Hot reset\n");
110061cbfabSFrank Li 	}
111061cbfabSFrank Li 
112061cbfabSFrank Li 	return IRQ_HANDLED;
113061cbfabSFrank Li }
114061cbfabSFrank Li 
ls_pcie_ep_interrupt_init(struct ls_pcie_ep * pcie,struct platform_device * pdev)115061cbfabSFrank Li static int ls_pcie_ep_interrupt_init(struct ls_pcie_ep *pcie,
116061cbfabSFrank Li 				     struct platform_device *pdev)
117061cbfabSFrank Li {
118061cbfabSFrank Li 	u32 val;
119061cbfabSFrank Li 	int ret;
120061cbfabSFrank Li 
121061cbfabSFrank Li 	pcie->irq = platform_get_irq_byname(pdev, "pme");
122061cbfabSFrank Li 	if (pcie->irq < 0)
123061cbfabSFrank Li 		return pcie->irq;
124061cbfabSFrank Li 
125061cbfabSFrank Li 	ret = devm_request_irq(&pdev->dev, pcie->irq, ls_pcie_ep_event_handler,
126061cbfabSFrank Li 			       IRQF_SHARED, pdev->name, pcie);
127061cbfabSFrank Li 	if (ret) {
128061cbfabSFrank Li 		dev_err(&pdev->dev, "Can't register PCIe IRQ\n");
129061cbfabSFrank Li 		return ret;
130061cbfabSFrank Li 	}
131061cbfabSFrank Li 
132061cbfabSFrank Li 	/* Enable interrupts */
133061cbfabSFrank Li 	val = ls_lut_readl(pcie, PEX_PF0_PME_MES_IER);
134061cbfabSFrank Li 	val |=  PEX_PF0_PME_MES_IER_LDDIE | PEX_PF0_PME_MES_IER_HRDIE |
135061cbfabSFrank Li 		PEX_PF0_PME_MES_IER_LUDIE;
136061cbfabSFrank Li 	ls_lut_writel(pcie, PEX_PF0_PME_MES_IER, val);
137061cbfabSFrank Li 
138061cbfabSFrank Li 	return 0;
139061cbfabSFrank Li }
140061cbfabSFrank Li 
141a805770dSXiaowei Bao static const struct pci_epc_features*
ls_pcie_ep_get_features(struct dw_pcie_ep * ep)142a805770dSXiaowei Bao ls_pcie_ep_get_features(struct dw_pcie_ep *ep)
143a805770dSXiaowei Bao {
144cc255eb0SXiaowei Bao 	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
145cc255eb0SXiaowei Bao 	struct ls_pcie_ep *pcie = to_ls_pcie_ep(pci);
146cc255eb0SXiaowei Bao 
147cc255eb0SXiaowei Bao 	return pcie->ls_epc;
148a805770dSXiaowei Bao }
149a805770dSXiaowei Bao 
ls_pcie_ep_init(struct dw_pcie_ep * ep)150a805770dSXiaowei Bao static void ls_pcie_ep_init(struct dw_pcie_ep *ep)
151a805770dSXiaowei Bao {
152a805770dSXiaowei Bao 	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
153cc255eb0SXiaowei Bao 	struct ls_pcie_ep *pcie = to_ls_pcie_ep(pci);
154cc255eb0SXiaowei Bao 	struct dw_pcie_ep_func *ep_func;
155a805770dSXiaowei Bao 	enum pci_barno bar;
156a805770dSXiaowei Bao 
157cc255eb0SXiaowei Bao 	ep_func = dw_pcie_ep_get_func_from_ep(ep, 0);
158cc255eb0SXiaowei Bao 	if (!ep_func)
159cc255eb0SXiaowei Bao 		return;
160cc255eb0SXiaowei Bao 
161c9c13ba4SDenis Efremov 	for (bar = 0; bar < PCI_STD_NUM_BARS; bar++)
162a805770dSXiaowei Bao 		dw_pcie_ep_reset_bar(pci, bar);
163cc255eb0SXiaowei Bao 
164cc255eb0SXiaowei Bao 	pcie->ls_epc->msi_capable = ep_func->msi_cap ? true : false;
165cc255eb0SXiaowei Bao 	pcie->ls_epc->msix_capable = ep_func->msix_cap ? true : false;
166a805770dSXiaowei Bao }
167a805770dSXiaowei Bao 
ls_pcie_ep_raise_irq(struct dw_pcie_ep * ep,u8 func_no,enum pci_epc_irq_type type,u16 interrupt_num)168a805770dSXiaowei Bao static int ls_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
169a805770dSXiaowei Bao 				enum pci_epc_irq_type type, u16 interrupt_num)
170a805770dSXiaowei Bao {
171a805770dSXiaowei Bao 	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
172a805770dSXiaowei Bao 
173a805770dSXiaowei Bao 	switch (type) {
174a805770dSXiaowei Bao 	case PCI_EPC_IRQ_LEGACY:
175a805770dSXiaowei Bao 		return dw_pcie_ep_raise_legacy_irq(ep, func_no);
176a805770dSXiaowei Bao 	case PCI_EPC_IRQ_MSI:
177a805770dSXiaowei Bao 		return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
178a805770dSXiaowei Bao 	case PCI_EPC_IRQ_MSIX:
179e64844b6SXiaowei Bao 		return dw_pcie_ep_raise_msix_irq_doorbell(ep, func_no,
180e64844b6SXiaowei Bao 							  interrupt_num);
181a805770dSXiaowei Bao 	default:
182a805770dSXiaowei Bao 		dev_err(pci->dev, "UNKNOWN IRQ type\n");
183a805770dSXiaowei Bao 		return -EINVAL;
184a805770dSXiaowei Bao 	}
185a805770dSXiaowei Bao }
186a805770dSXiaowei Bao 
ls_pcie_ep_func_conf_select(struct dw_pcie_ep * ep,u8 func_no)1871b6a0e43SXiaowei Bao static unsigned int ls_pcie_ep_func_conf_select(struct dw_pcie_ep *ep,
1881b6a0e43SXiaowei Bao 						u8 func_no)
1891b6a0e43SXiaowei Bao {
1901b6a0e43SXiaowei Bao 	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
1911b6a0e43SXiaowei Bao 	struct ls_pcie_ep *pcie = to_ls_pcie_ep(pci);
1921b6a0e43SXiaowei Bao 
1931b6a0e43SXiaowei Bao 	WARN_ON(func_no && !pcie->drvdata->func_offset);
1941b6a0e43SXiaowei Bao 	return pcie->drvdata->func_offset * func_no;
1951b6a0e43SXiaowei Bao }
1961b6a0e43SXiaowei Bao 
1971b6a0e43SXiaowei Bao static const struct dw_pcie_ep_ops ls_pcie_ep_ops = {
198a805770dSXiaowei Bao 	.ep_init = ls_pcie_ep_init,
199a805770dSXiaowei Bao 	.raise_irq = ls_pcie_ep_raise_irq,
200a805770dSXiaowei Bao 	.get_features = ls_pcie_ep_get_features,
2011b6a0e43SXiaowei Bao 	.func_conf_select = ls_pcie_ep_func_conf_select,
2021b6a0e43SXiaowei Bao };
2031b6a0e43SXiaowei Bao 
2041b6a0e43SXiaowei Bao static const struct ls_pcie_ep_drvdata ls1_ep_drvdata = {
2051b6a0e43SXiaowei Bao 	.ops = &ls_pcie_ep_ops,
2061b6a0e43SXiaowei Bao };
2071b6a0e43SXiaowei Bao 
2081b6a0e43SXiaowei Bao static const struct ls_pcie_ep_drvdata ls2_ep_drvdata = {
2091b6a0e43SXiaowei Bao 	.func_offset = 0x20000,
2101b6a0e43SXiaowei Bao 	.ops = &ls_pcie_ep_ops,
2111b6a0e43SXiaowei Bao };
2121b6a0e43SXiaowei Bao 
2135bfb792fSHou Zhiqiang static const struct ls_pcie_ep_drvdata lx2_ep_drvdata = {
2145bfb792fSHou Zhiqiang 	.func_offset = 0x8000,
2155bfb792fSHou Zhiqiang 	.ops = &ls_pcie_ep_ops,
2165bfb792fSHou Zhiqiang };
2175bfb792fSHou Zhiqiang 
2181b6a0e43SXiaowei Bao static const struct of_device_id ls_pcie_ep_of_match[] = {
219be567c6cSXiaowei Bao 	{ .compatible = "fsl,ls1028a-pcie-ep", .data = &ls1_ep_drvdata },
2201b6a0e43SXiaowei Bao 	{ .compatible = "fsl,ls1046a-pcie-ep", .data = &ls1_ep_drvdata },
2211b6a0e43SXiaowei Bao 	{ .compatible = "fsl,ls1088a-pcie-ep", .data = &ls2_ep_drvdata },
2221b6a0e43SXiaowei Bao 	{ .compatible = "fsl,ls2088a-pcie-ep", .data = &ls2_ep_drvdata },
2235bfb792fSHou Zhiqiang 	{ .compatible = "fsl,lx2160ar2-pcie-ep", .data = &lx2_ep_drvdata },
2241b6a0e43SXiaowei Bao 	{ },
225a805770dSXiaowei Bao };
226a805770dSXiaowei Bao 
ls_pcie_ep_probe(struct platform_device * pdev)227a805770dSXiaowei Bao static int __init ls_pcie_ep_probe(struct platform_device *pdev)
228a805770dSXiaowei Bao {
229a805770dSXiaowei Bao 	struct device *dev = &pdev->dev;
230a805770dSXiaowei Bao 	struct dw_pcie *pci;
231a805770dSXiaowei Bao 	struct ls_pcie_ep *pcie;
232cc255eb0SXiaowei Bao 	struct pci_epc_features *ls_epc;
233a805770dSXiaowei Bao 	struct resource *dbi_base;
234*17cf8661SXiaowei Bao 	u8 offset;
235061cbfabSFrank Li 	int ret;
236a805770dSXiaowei Bao 
237a805770dSXiaowei Bao 	pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
238a805770dSXiaowei Bao 	if (!pcie)
239a805770dSXiaowei Bao 		return -ENOMEM;
240a805770dSXiaowei Bao 
241a805770dSXiaowei Bao 	pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
242a805770dSXiaowei Bao 	if (!pci)
243a805770dSXiaowei Bao 		return -ENOMEM;
244a805770dSXiaowei Bao 
245cc255eb0SXiaowei Bao 	ls_epc = devm_kzalloc(dev, sizeof(*ls_epc), GFP_KERNEL);
246cc255eb0SXiaowei Bao 	if (!ls_epc)
247cc255eb0SXiaowei Bao 		return -ENOMEM;
248cc255eb0SXiaowei Bao 
2491b6a0e43SXiaowei Bao 	pcie->drvdata = of_device_get_match_data(dev);
2501b6a0e43SXiaowei Bao 
2511b6a0e43SXiaowei Bao 	pci->dev = dev;
2521b6a0e43SXiaowei Bao 	pci->ops = pcie->drvdata->dw_pcie_ops;
2531b6a0e43SXiaowei Bao 
2541b7996a5SKrzysztof Wilczyński 	ls_epc->bar_fixed_64bit = (1 << BAR_2) | (1 << BAR_4);
255061cbfabSFrank Li 	ls_epc->linkup_notifier = true;
2561b6a0e43SXiaowei Bao 
2571b6a0e43SXiaowei Bao 	pcie->pci = pci;
2581b6a0e43SXiaowei Bao 	pcie->ls_epc = ls_epc;
2591b6a0e43SXiaowei Bao 
260a805770dSXiaowei Bao 	dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
261a805770dSXiaowei Bao 	pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base);
262a805770dSXiaowei Bao 	if (IS_ERR(pci->dbi_base))
263a805770dSXiaowei Bao 		return PTR_ERR(pci->dbi_base);
264a805770dSXiaowei Bao 
265a0fd361dSRob Herring 	pci->ep.ops = &ls_pcie_ep_ops;
266cc255eb0SXiaowei Bao 
267061cbfabSFrank Li 	pcie->big_endian = of_property_read_bool(dev->of_node, "big-endian");
268061cbfabSFrank Li 
269a805770dSXiaowei Bao 	platform_set_drvdata(pdev, pcie);
270a805770dSXiaowei Bao 
271*17cf8661SXiaowei Bao 	offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
272*17cf8661SXiaowei Bao 	pcie->lnkcap = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP);
273*17cf8661SXiaowei Bao 
274061cbfabSFrank Li 	ret = dw_pcie_ep_init(&pci->ep);
275061cbfabSFrank Li 	if (ret)
276061cbfabSFrank Li 		return ret;
277061cbfabSFrank Li 
278061cbfabSFrank Li 	return ls_pcie_ep_interrupt_init(pcie, pdev);
279a805770dSXiaowei Bao }
280a805770dSXiaowei Bao 
281a805770dSXiaowei Bao static struct platform_driver ls_pcie_ep_driver = {
282a805770dSXiaowei Bao 	.driver = {
283a805770dSXiaowei Bao 		.name = "layerscape-pcie-ep",
284a805770dSXiaowei Bao 		.of_match_table = ls_pcie_ep_of_match,
285a805770dSXiaowei Bao 		.suppress_bind_attrs = true,
286a805770dSXiaowei Bao 	},
287a805770dSXiaowei Bao };
288a805770dSXiaowei Bao builtin_platform_driver_probe(ls_pcie_ep_driver, ls_pcie_ep_probe);
289