1// SPDX-License-Identifier:    GPL-2.0
2/*
3 * Copyright (C) 2018 Marvell International Ltd.
4 *
5 * https://spdx.org/licenses
6 */
7
8#include <dm.h>
9#include <errno.h>
10#include <fdtdec.h>
11#include <log.h>
12#include <malloc.h>
13#include <pci.h>
14#include <asm/global_data.h>
15
16#include <asm/io.h>
17
18#include <linux/ioport.h>
19
20DECLARE_GLOBAL_DATA_PTR;
21
22/*
23 * This driver supports multiple types of operations / host bridges / busses:
24 *
25 * OTX_ECAM: Octeon TX & TX2 ECAM (Enhanced Configuration Access Mechanism)
26 *	     Used to access the internal on-chip devices which are connected
27 *	     to internal buses
28 * OTX_PEM:  Octeon TX PEM (PCI Express MAC)
29 *	     Used to access the external (off-chip) PCI devices
30 * OTX2_PEM: Octeon TX2 PEM (PCI Express MAC)
31 *	     Used to access the external (off-chip) PCI devices
32 */
33enum {
34	OTX_ECAM,
35	OTX_PEM,
36	OTX2_PEM,
37};
38
39/**
40 * struct octeontx_pci - Driver private data
41 * @type:	Device type matched via compatible (e.g. OTX_ECAM etc)
42 * @cfg:	Config resource
43 * @bus:	Bus resource
44 */
45struct octeontx_pci {
46	unsigned int type;
47
48	struct resource cfg;
49	struct resource bus;
50};
51
52static ulong readl_size(uintptr_t addr, enum pci_size_t size)
53{
54	ulong val;
55
56	switch (size) {
57	case PCI_SIZE_8:
58		val = readb(addr);
59		break;
60	case PCI_SIZE_16:
61		val = readw(addr);
62		break;
63	case PCI_SIZE_32:
64		val = readl(addr);
65		break;
66	default:
67		printf("Invalid size\n");
68		return -EINVAL;
69	};
70
71	return val;
72}
73
74static void writel_size(uintptr_t addr, enum pci_size_t size, ulong valuep)
75{
76	switch (size) {
77	case PCI_SIZE_8:
78		writeb(valuep, addr);
79		break;
80	case PCI_SIZE_16:
81		writew(valuep, addr);
82		break;
83	case PCI_SIZE_32:
84		writel(valuep, addr);
85		break;
86	default:
87		printf("Invalid size\n");
88	};
89}
90
91static bool octeontx_bdf_invalid(pci_dev_t bdf)
92{
93	if (PCI_BUS(bdf) == 1 && PCI_DEV(bdf) > 0)
94		return true;
95
96	return false;
97}
98
99static int octeontx_ecam_read_config(const struct udevice *bus, pci_dev_t bdf,
100				     uint offset, ulong *valuep,
101				     enum pci_size_t size)
102{
103	struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
104	struct pci_controller *hose = dev_get_uclass_priv(bus);
105	uintptr_t address;
106
107	address = PCIE_ECAM_OFFSET(PCI_BUS(bdf) + pcie->bus.start - hose->first_busno,
108				   PCI_DEV(bdf), PCI_FUNC(bdf), offset);
109	*valuep = readl_size(pcie->cfg.start + address, size);
110
111	debug("%02x.%02x.%02x: u%d %x -> %lx\n",
112	      PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset, *valuep);
113
114	return 0;
115}
116
117static int octeontx_ecam_write_config(struct udevice *bus, pci_dev_t bdf,
118				      uint offset, ulong value,
119				      enum pci_size_t size)
120{
121	struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
122	struct pci_controller *hose = dev_get_uclass_priv(bus);
123	uintptr_t address;
124
125	address = PCIE_ECAM_OFFSET(PCI_BUS(bdf) + pcie->bus.start - hose->first_busno,
126				   PCI_DEV(bdf), PCI_FUNC(bdf), offset);
127	writel_size(pcie->cfg.start + address, size, value);
128
129	debug("%02x.%02x.%02x: u%d %x <- %lx\n",
130	      PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset, value);
131
132	return 0;
133}
134
135static int octeontx_pem_read_config(const struct udevice *bus, pci_dev_t bdf,
136				    uint offset, ulong *valuep,
137				    enum pci_size_t size)
138{
139	struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
140	struct pci_controller *hose = dev_get_uclass_priv(bus);
141	uintptr_t address;
142	u8 hdrtype;
143	u8 pri_bus = pcie->bus.start + 1 - hose->first_busno;
144	u32 bus_offs = (pri_bus << 16) | (pri_bus << 8) | (pri_bus << 0);
145
146	*valuep = pci_conv_32_to_size(~0UL, offset, size);
147
148	if (octeontx_bdf_invalid(bdf))
149		return -EPERM;
150
151	address = PCIE_ECAM_OFFSET(PCI_BUS(bdf) + 1 - hose->first_busno,
152				   PCI_DEV(bdf), PCI_FUNC(bdf), 0) << 4;
153	*valuep = readl_size(pcie->cfg.start + address + offset, size);
154
155	hdrtype = readb(pcie->cfg.start + address + PCI_HEADER_TYPE);
156	if (hdrtype == PCI_HEADER_TYPE_BRIDGE &&
157	    offset >= PCI_PRIMARY_BUS &&
158	    offset <= PCI_SUBORDINATE_BUS &&
159	    *valuep != pci_conv_32_to_size(~0UL, offset, size))
160		*valuep -= pci_conv_32_to_size(bus_offs, offset, size);
161
162	return 0;
163}
164
165static int octeontx_pem_write_config(struct udevice *bus, pci_dev_t bdf,
166				     uint offset, ulong value,
167				     enum pci_size_t size)
168{
169	struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
170	struct pci_controller *hose = dev_get_uclass_priv(bus);
171	uintptr_t address;
172	u8 hdrtype;
173	u8 pri_bus = pcie->bus.start + 1 - hose->first_busno;
174	u32 bus_offs = (pri_bus << 16) | (pri_bus << 8) | (pri_bus << 0);
175
176	address = PCIE_ECAM_OFFSET(PCI_BUS(bdf) + 1 - hose->first_busno,
177				   PCI_DEV(bdf), PCI_FUNC(bdf), 0) << 4;
178
179	hdrtype = readb(pcie->cfg.start + address + PCI_HEADER_TYPE);
180	if (hdrtype == PCI_HEADER_TYPE_BRIDGE &&
181	    offset >= PCI_PRIMARY_BUS &&
182	    offset <= PCI_SUBORDINATE_BUS &&
183	    value != pci_conv_32_to_size(~0UL, offset, size))
184		value +=  pci_conv_32_to_size(bus_offs, offset, size);
185
186	if (octeontx_bdf_invalid(bdf))
187		return -EPERM;
188
189	writel_size(pcie->cfg.start + address + offset, size, value);
190
191	debug("%02x.%02x.%02x: u%d %x (%lx) <- %lx\n",
192	      PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset,
193	      address, value);
194
195	return 0;
196}
197
198static int octeontx2_pem_read_config(const struct udevice *bus, pci_dev_t bdf,
199				     uint offset, ulong *valuep,
200				     enum pci_size_t size)
201{
202	struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
203	struct pci_controller *hose = dev_get_uclass_priv(bus);
204	uintptr_t address;
205
206	*valuep = pci_conv_32_to_size(~0UL, offset, size);
207
208	if (octeontx_bdf_invalid(bdf))
209		return -EPERM;
210
211	address = PCIE_ECAM_OFFSET(PCI_BUS(bdf) + 1 - hose->first_busno,
212				   PCI_DEV(bdf), PCI_FUNC(bdf), offset);
213	*valuep = readl_size(pcie->cfg.start + address, size);
214
215	debug("%02x.%02x.%02x: u%d %x (%lx) -> %lx\n",
216	      PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset,
217	      address, *valuep);
218
219	return 0;
220}
221
222static int octeontx2_pem_write_config(struct udevice *bus, pci_dev_t bdf,
223				      uint offset, ulong value,
224				      enum pci_size_t size)
225{
226	struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
227	struct pci_controller *hose = dev_get_uclass_priv(bus);
228	uintptr_t address;
229
230	if (octeontx_bdf_invalid(bdf))
231		return -EPERM;
232
233	address = PCIE_ECAM_OFFSET(PCI_BUS(bdf) + 1 - hose->first_busno,
234				   PCI_DEV(bdf), PCI_FUNC(bdf), offset);
235	writel_size(pcie->cfg.start + address, size, value);
236
237	debug("%02x.%02x.%02x: u%d %x (%lx) <- %lx\n",
238	      PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset,
239	      address, value);
240
241	return 0;
242}
243
244int pci_octeontx_read_config(const struct udevice *bus, pci_dev_t bdf,
245			     uint offset, ulong *valuep,
246			     enum pci_size_t size)
247{
248	struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
249	int ret = -EIO;
250
251	switch (pcie->type) {
252	case OTX_ECAM:
253		ret = octeontx_ecam_read_config(bus, bdf, offset, valuep,
254						size);
255		break;
256	case OTX_PEM:
257		ret = octeontx_pem_read_config(bus, bdf, offset, valuep,
258					       size);
259		break;
260	case OTX2_PEM:
261		ret = octeontx2_pem_read_config(bus, bdf, offset, valuep,
262						size);
263		break;
264	}
265
266	return ret;
267}
268
269int pci_octeontx_write_config(struct udevice *bus, pci_dev_t bdf,
270			      uint offset, ulong value,
271			      enum pci_size_t size)
272{
273	struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
274	int ret = -EIO;
275
276	switch (pcie->type) {
277	case OTX_ECAM:
278		ret = octeontx_ecam_write_config(bus, bdf, offset, value,
279						 size);
280		break;
281	case OTX_PEM:
282		ret = octeontx_pem_write_config(bus, bdf, offset, value,
283						size);
284		break;
285	case OTX2_PEM:
286		ret = octeontx2_pem_write_config(bus, bdf, offset, value,
287						 size);
288		break;
289	}
290
291	return ret;
292}
293
294static int pci_octeontx_of_to_plat(struct udevice *dev)
295{
296	return 0;
297}
298
299static int pci_octeontx_probe(struct udevice *dev)
300{
301	struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(dev);
302	int err;
303
304	pcie->type = dev_get_driver_data(dev);
305
306	err = dev_read_resource(dev, 0, &pcie->cfg);
307	if (err) {
308		debug("Error reading resource: %s\n", fdt_strerror(err));
309		return err;
310	}
311
312	err = dev_read_pci_bus_range(dev, &pcie->bus);
313	if (err) {
314		debug("Error reading resource: %s\n", fdt_strerror(err));
315		return err;
316	}
317
318	return 0;
319}
320
321static const struct dm_pci_ops pci_octeontx_ops = {
322	.read_config	= pci_octeontx_read_config,
323	.write_config	= pci_octeontx_write_config,
324};
325
326static const struct udevice_id pci_octeontx_ids[] = {
327	{ .compatible = "cavium,pci-host-thunder-ecam", .data = OTX_ECAM },
328	{ .compatible = "cavium,pci-host-octeontx-ecam", .data = OTX_ECAM },
329	{ .compatible = "pci-host-ecam-generic", .data = OTX_ECAM },
330	{ .compatible = "cavium,pci-host-thunder-pem", .data = OTX_PEM },
331	{ .compatible = "marvell,pci-host-octeontx2-pem", .data = OTX2_PEM },
332	{ }
333};
334
335U_BOOT_DRIVER(pci_octeontx) = {
336	.name	= "pci_octeontx",
337	.id	= UCLASS_PCI,
338	.of_match = pci_octeontx_ids,
339	.ops	= &pci_octeontx_ops,
340	.of_to_plat = pci_octeontx_of_to_plat,
341	.probe	= pci_octeontx_probe,
342	.priv_auto	= sizeof(struct octeontx_pci),
343	.flags = DM_FLAG_PRE_RELOC,
344};
345