1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2020 Stefan Roese <sr@denx.de>
4 */
5
6#include <dm.h>
7#include <errno.h>
8#include <fdtdec.h>
9#include <log.h>
10#include <pci.h>
11#include <linux/delay.h>
12
13#include <mach/octeon-model.h>
14#include <mach/octeon_pci.h>
15#include <mach/cvmx-regs.h>
16#include <mach/cvmx-pcie.h>
17#include <mach/cvmx-pemx-defs.h>
18
19struct octeon_pcie {
20	void *base;
21	int first_busno;
22	u32 port;
23	struct udevice *dev;
24	int pcie_port;
25};
26
27static bool octeon_bdf_invalid(pci_dev_t bdf, int first_busno)
28{
29	/*
30	 * In PCIe only a single device (0) can exist on the local bus.
31	 * Beyound the local bus, there might be a switch and everything
32	 * is possible.
33	 */
34	if ((PCI_BUS(bdf) == first_busno) && (PCI_DEV(bdf) > 0))
35		return true;
36
37	return false;
38}
39
40static int pcie_octeon_write_config(struct udevice *bus, pci_dev_t bdf,
41				    uint offset, ulong value,
42				    enum pci_size_t size)
43{
44	struct octeon_pcie *pcie = dev_get_priv(bus);
45	struct pci_controller *hose = dev_get_uclass_priv(bus);
46	int busno;
47	int port;
48
49	debug("PCIE CFG write: (b,d,f)=(%2d,%2d,%2d) ",
50	      PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf));
51	debug("(addr,size,val)=(0x%04x, %d, 0x%08lx)\n", offset, size, value);
52
53	port = pcie->pcie_port;
54	busno = PCI_BUS(bdf) - hose->first_busno + 1;
55
56	switch (size) {
57	case PCI_SIZE_8:
58		cvmx_pcie_config_write8(port, busno, PCI_DEV(bdf),
59					PCI_FUNC(bdf), offset, value);
60		break;
61	case PCI_SIZE_16:
62		cvmx_pcie_config_write16(port, busno, PCI_DEV(bdf),
63					 PCI_FUNC(bdf), offset, value);
64		break;
65	case PCI_SIZE_32:
66		cvmx_pcie_config_write32(port, busno, PCI_DEV(bdf),
67					 PCI_FUNC(bdf), offset, value);
68		break;
69	default:
70		printf("Invalid size\n");
71	};
72
73	return 0;
74}
75
76static int pcie_octeon_read_config(const struct udevice *bus, pci_dev_t bdf,
77				   uint offset, ulong *valuep,
78				   enum pci_size_t size)
79{
80	struct octeon_pcie *pcie = dev_get_priv(bus);
81	struct pci_controller *hose = dev_get_uclass_priv(bus);
82	int busno;
83	int port;
84
85	port = pcie->pcie_port;
86	busno = PCI_BUS(bdf) - hose->first_busno + 1;
87	if (octeon_bdf_invalid(bdf, pcie->first_busno)) {
88		*valuep = pci_get_ff(size);
89		return 0;
90	}
91
92	switch (size) {
93	case PCI_SIZE_8:
94		*valuep = cvmx_pcie_config_read8(port, busno, PCI_DEV(bdf),
95						 PCI_FUNC(bdf), offset);
96		break;
97	case PCI_SIZE_16:
98		*valuep = cvmx_pcie_config_read16(port, busno, PCI_DEV(bdf),
99						  PCI_FUNC(bdf), offset);
100		break;
101	case PCI_SIZE_32:
102		*valuep = cvmx_pcie_config_read32(port, busno, PCI_DEV(bdf),
103						  PCI_FUNC(bdf), offset);
104		break;
105	default:
106		printf("Invalid size\n");
107	};
108
109	debug("%02x.%02x.%02x: u%d %x -> %lx\n",
110	      PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset, *valuep);
111
112	return 0;
113}
114
115static int pcie_octeon_probe(struct udevice *dev)
116{
117	struct octeon_pcie *pcie = dev_get_priv(dev);
118	int node = cvmx_get_node_num();
119	int pcie_port;
120	int ret = 0;
121
122	/* Get port number, lane number and memory target / attr */
123	if (ofnode_read_u32(dev_ofnode(dev), "marvell,pcie-port",
124			    &pcie->port)) {
125		ret = -ENODEV;
126		goto err;
127	}
128
129	pcie->first_busno = dev_seq(dev);
130	pcie_port = ((node << 4) | pcie->port);
131	ret = cvmx_pcie_rc_initialize(pcie_port);
132	if (ret != 0)
133		return ret;
134
135	return 0;
136
137err:
138	return ret;
139}
140
141static const struct dm_pci_ops pcie_octeon_ops = {
142	.read_config = pcie_octeon_read_config,
143	.write_config = pcie_octeon_write_config,
144};
145
146static const struct udevice_id pcie_octeon_ids[] = {
147	{ .compatible = "marvell,pcie-host-octeon" },
148	{ }
149};
150
151U_BOOT_DRIVER(pcie_octeon) = {
152	.name		= "pcie_octeon",
153	.id		= UCLASS_PCI,
154	.of_match	= pcie_octeon_ids,
155	.ops		= &pcie_octeon_ops,
156	.probe		= pcie_octeon_probe,
157	.priv_auto	= sizeof(struct octeon_pcie),
158	.flags		= DM_FLAG_PRE_RELOC,
159};
160