xref: /arm-trusted-firmware/plat/rpi/rpi4/rpi4_pci_svc.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
1*91f16700Schasinglulu /*
2*91f16700Schasinglulu  * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved.
3*91f16700Schasinglulu  *
4*91f16700Schasinglulu  * SPDX-License-Identifier: BSD-3-Clause
5*91f16700Schasinglulu  *
6*91f16700Schasinglulu  * The RPi4 has a single nonstandard PCI config region. It is broken into two
7*91f16700Schasinglulu  * pieces, the root port config registers and a window to a single device's
8*91f16700Schasinglulu  * config space which can move between devices. There isn't (yet) an
9*91f16700Schasinglulu  * authoritative public document on this since the available BCM2711 reference
10*91f16700Schasinglulu  * notes that there is a PCIe root port in the memory map but doesn't describe
11*91f16700Schasinglulu  * it. Given that it's not ECAM compliant yet reasonably simple, it makes for
12*91f16700Schasinglulu  * an excellent example of the PCI SMCCC interface.
13*91f16700Schasinglulu  *
14*91f16700Schasinglulu  * The PCI SMCCC interface is described in DEN0115 available from:
15*91f16700Schasinglulu  * https://developer.arm.com/documentation/den0115/latest
16*91f16700Schasinglulu  */
17*91f16700Schasinglulu 
18*91f16700Schasinglulu #include <assert.h>
19*91f16700Schasinglulu #include <stdint.h>
20*91f16700Schasinglulu 
21*91f16700Schasinglulu #include <common/debug.h>
22*91f16700Schasinglulu #include <common/runtime_svc.h>
23*91f16700Schasinglulu #include <lib/pmf/pmf.h>
24*91f16700Schasinglulu #include <lib/runtime_instr.h>
25*91f16700Schasinglulu #include <services/pci_svc.h>
26*91f16700Schasinglulu #include <services/sdei.h>
27*91f16700Schasinglulu #include <services/std_svc.h>
28*91f16700Schasinglulu #include <smccc_helpers.h>
29*91f16700Schasinglulu 
30*91f16700Schasinglulu #include <lib/mmio.h>
31*91f16700Schasinglulu 
32*91f16700Schasinglulu static spinlock_t pci_lock;
33*91f16700Schasinglulu 
34*91f16700Schasinglulu #define PCIE_REG_BASE		U(RPI_IO_BASE + 0x01500000)
35*91f16700Schasinglulu #define PCIE_MISC_PCIE_STATUS	0x4068
36*91f16700Schasinglulu #define PCIE_EXT_CFG_INDEX	0x9000
37*91f16700Schasinglulu /* A small window pointing at the ECAM of the device selected by CFG_INDEX */
38*91f16700Schasinglulu #define PCIE_EXT_CFG_DATA	0x8000
39*91f16700Schasinglulu #define INVALID_PCI_ADDR	0xFFFFFFFF
40*91f16700Schasinglulu 
41*91f16700Schasinglulu #define	PCIE_EXT_BUS_SHIFT	20
42*91f16700Schasinglulu #define	PCIE_EXT_DEV_SHIFT	15
43*91f16700Schasinglulu #define	PCIE_EXT_FUN_SHIFT	12
44*91f16700Schasinglulu 
45*91f16700Schasinglulu 
46*91f16700Schasinglulu static uint64_t pci_segment_lib_get_base(uint32_t address, uint32_t offset)
47*91f16700Schasinglulu {
48*91f16700Schasinglulu 	uint64_t	base;
49*91f16700Schasinglulu 	uint32_t	bus, dev, fun;
50*91f16700Schasinglulu 	uint32_t	status;
51*91f16700Schasinglulu 
52*91f16700Schasinglulu 	base = PCIE_REG_BASE;
53*91f16700Schasinglulu 
54*91f16700Schasinglulu 	offset &= PCI_OFFSET_MASK;  /* Pick off the 4k register offset */
55*91f16700Schasinglulu 
56*91f16700Schasinglulu 	/* The root port is at the base of the PCIe register space */
57*91f16700Schasinglulu 	if (address != 0U) {
58*91f16700Schasinglulu 		/*
59*91f16700Schasinglulu 		 * The current device must be at CFG_DATA, a 4K window mapped,
60*91f16700Schasinglulu 		 * via CFG_INDEX, to the device we are accessing. At the same
61*91f16700Schasinglulu 		 * time we must avoid accesses to certain areas of the cfg
62*91f16700Schasinglulu 		 * space via CFG_DATA. Detect those accesses and report that
63*91f16700Schasinglulu 		 * the address is invalid.
64*91f16700Schasinglulu 		 */
65*91f16700Schasinglulu 		base += PCIE_EXT_CFG_DATA;
66*91f16700Schasinglulu 		bus = PCI_ADDR_BUS(address);
67*91f16700Schasinglulu 		dev = PCI_ADDR_DEV(address);
68*91f16700Schasinglulu 		fun = PCI_ADDR_FUN(address);
69*91f16700Schasinglulu 		address = (bus << PCIE_EXT_BUS_SHIFT) |
70*91f16700Schasinglulu 			  (dev << PCIE_EXT_DEV_SHIFT) |
71*91f16700Schasinglulu 			  (fun << PCIE_EXT_FUN_SHIFT);
72*91f16700Schasinglulu 
73*91f16700Schasinglulu 		/* Allow only dev = 0 on root port and bus 1 */
74*91f16700Schasinglulu 		if ((bus < 2U) && (dev > 0U)) {
75*91f16700Schasinglulu 			return INVALID_PCI_ADDR;
76*91f16700Schasinglulu 		}
77*91f16700Schasinglulu 
78*91f16700Schasinglulu 		/* Assure link up before reading bus 1 */
79*91f16700Schasinglulu 		status = mmio_read_32(PCIE_REG_BASE + PCIE_MISC_PCIE_STATUS);
80*91f16700Schasinglulu 		if ((status & 0x30) != 0x30) {
81*91f16700Schasinglulu 			return INVALID_PCI_ADDR;
82*91f16700Schasinglulu 		}
83*91f16700Schasinglulu 
84*91f16700Schasinglulu 		/* Adjust which device the CFG_DATA window is pointing at */
85*91f16700Schasinglulu 		mmio_write_32(PCIE_REG_BASE + PCIE_EXT_CFG_INDEX, address);
86*91f16700Schasinglulu 	}
87*91f16700Schasinglulu 	return base + offset;
88*91f16700Schasinglulu }
89*91f16700Schasinglulu 
90*91f16700Schasinglulu /**
91*91f16700Schasinglulu  * pci_read_config() - Performs a config space read at addr
92*91f16700Schasinglulu  * @addr: 32-bit, segment, BDF of requested function encoded per DEN0115
93*91f16700Schasinglulu  * @off:  register offset of function described by @addr to read
94*91f16700Schasinglulu  * @sz:	  size of read (8,16,32) bits.
95*91f16700Schasinglulu  * @val:  returned zero extended value read from config space
96*91f16700Schasinglulu  *
97*91f16700Schasinglulu  * sz bits of PCI config space is read at addr:offset, and the value
98*91f16700Schasinglulu  * is returned in val. Invalid segment/offset values return failure.
99*91f16700Schasinglulu  * Reads to valid functions that don't exist return INVALID_PCI_ADDR
100*91f16700Schasinglulu  * as is specified by PCI for requests that aren't completed by EPs.
101*91f16700Schasinglulu  * The boilerplate in pci_svc.c tends to do basic segment, off
102*91f16700Schasinglulu  * and sz validation. This routine should avoid duplicating those
103*91f16700Schasinglulu  * checks.
104*91f16700Schasinglulu  *
105*91f16700Schasinglulu  * This function maps directly to the PCI_READ function in DEN0115
106*91f16700Schasinglulu  * where detailed requirements may be found.
107*91f16700Schasinglulu  *
108*91f16700Schasinglulu  * Return: SMC_PCI_CALL_SUCCESS with val set
109*91f16700Schasinglulu  *	   SMC_PCI_CALL_INVAL_PARAM, on parameter error
110*91f16700Schasinglulu  */
111*91f16700Schasinglulu uint32_t pci_read_config(uint32_t addr, uint32_t off, uint32_t sz, uint32_t *val)
112*91f16700Schasinglulu {
113*91f16700Schasinglulu 	uint32_t ret = SMC_PCI_CALL_SUCCESS;
114*91f16700Schasinglulu 	uint64_t base;
115*91f16700Schasinglulu 
116*91f16700Schasinglulu 	spin_lock(&pci_lock);
117*91f16700Schasinglulu 	base = pci_segment_lib_get_base(addr, off);
118*91f16700Schasinglulu 
119*91f16700Schasinglulu 	if (base == INVALID_PCI_ADDR) {
120*91f16700Schasinglulu 		*val = base;
121*91f16700Schasinglulu 	} else {
122*91f16700Schasinglulu 		switch (sz) {
123*91f16700Schasinglulu 		case SMC_PCI_SZ_8BIT:
124*91f16700Schasinglulu 			*val = mmio_read_8(base);
125*91f16700Schasinglulu 			break;
126*91f16700Schasinglulu 		case SMC_PCI_SZ_16BIT:
127*91f16700Schasinglulu 			*val = mmio_read_16(base);
128*91f16700Schasinglulu 			break;
129*91f16700Schasinglulu 		case SMC_PCI_SZ_32BIT:
130*91f16700Schasinglulu 			*val = mmio_read_32(base);
131*91f16700Schasinglulu 			break;
132*91f16700Schasinglulu 		default: /* should be unreachable */
133*91f16700Schasinglulu 			*val = 0;
134*91f16700Schasinglulu 			ret = SMC_PCI_CALL_INVAL_PARAM;
135*91f16700Schasinglulu 		}
136*91f16700Schasinglulu 	}
137*91f16700Schasinglulu 	spin_unlock(&pci_lock);
138*91f16700Schasinglulu 	return ret;
139*91f16700Schasinglulu }
140*91f16700Schasinglulu 
141*91f16700Schasinglulu /**
142*91f16700Schasinglulu  * pci_write_config() - Performs a config space write at addr
143*91f16700Schasinglulu  * @addr: 32-bit, segment, BDF of requested function encoded per DEN0115
144*91f16700Schasinglulu  * @off:  register offset of function described by @addr to write
145*91f16700Schasinglulu  * @sz:	  size of write (8,16,32) bits.
146*91f16700Schasinglulu  * @val:  value to be written
147*91f16700Schasinglulu  *
148*91f16700Schasinglulu  * sz bits of PCI config space is written at addr:offset. Invalid
149*91f16700Schasinglulu  * segment/BDF values return failure. Writes to valid functions
150*91f16700Schasinglulu  * without valid EPs are ignored, as is specified by PCI.
151*91f16700Schasinglulu  * The boilerplate in pci_svc.c tends to do basic segment, off
152*91f16700Schasinglulu  * and sz validation, so it shouldn't need to be repeated here.
153*91f16700Schasinglulu  *
154*91f16700Schasinglulu  * This function maps directly to the PCI_WRITE function in DEN0115
155*91f16700Schasinglulu  * where detailed requirements may be found.
156*91f16700Schasinglulu  *
157*91f16700Schasinglulu  * Return: SMC_PCI_CALL_SUCCESS
158*91f16700Schasinglulu  *	   SMC_PCI_CALL_INVAL_PARAM, on parameter error
159*91f16700Schasinglulu  */
160*91f16700Schasinglulu uint32_t pci_write_config(uint32_t addr, uint32_t off, uint32_t sz, uint32_t val)
161*91f16700Schasinglulu {
162*91f16700Schasinglulu 	uint32_t ret = SMC_PCI_CALL_SUCCESS;
163*91f16700Schasinglulu 	uint64_t base;
164*91f16700Schasinglulu 
165*91f16700Schasinglulu 	spin_lock(&pci_lock);
166*91f16700Schasinglulu 	base = pci_segment_lib_get_base(addr, off);
167*91f16700Schasinglulu 
168*91f16700Schasinglulu 	if (base != INVALID_PCI_ADDR) {
169*91f16700Schasinglulu 		switch (sz) {
170*91f16700Schasinglulu 		case SMC_PCI_SZ_8BIT:
171*91f16700Schasinglulu 			mmio_write_8(base, val);
172*91f16700Schasinglulu 			break;
173*91f16700Schasinglulu 		case SMC_PCI_SZ_16BIT:
174*91f16700Schasinglulu 			mmio_write_16(base, val);
175*91f16700Schasinglulu 			break;
176*91f16700Schasinglulu 		case SMC_PCI_SZ_32BIT:
177*91f16700Schasinglulu 			mmio_write_32(base, val);
178*91f16700Schasinglulu 			break;
179*91f16700Schasinglulu 		default: /* should be unreachable */
180*91f16700Schasinglulu 			ret = SMC_PCI_CALL_INVAL_PARAM;
181*91f16700Schasinglulu 		}
182*91f16700Schasinglulu 	}
183*91f16700Schasinglulu 	spin_unlock(&pci_lock);
184*91f16700Schasinglulu 	return ret;
185*91f16700Schasinglulu }
186*91f16700Schasinglulu 
187*91f16700Schasinglulu /**
188*91f16700Schasinglulu  * pci_get_bus_for_seg() - returns the start->end bus range for a segment
189*91f16700Schasinglulu  * @seg:  segment being queried
190*91f16700Schasinglulu  * @bus_range:	returned bus begin + (end << 8)
191*91f16700Schasinglulu  * @nseg: returns next segment in this machine or 0 for end
192*91f16700Schasinglulu  *
193*91f16700Schasinglulu  * pci_get_bus_for_seg is called to check if a given segment is
194*91f16700Schasinglulu  * valid on this machine. If it is valid, then its bus ranges are
195*91f16700Schasinglulu  * returned along with the next valid segment on the machine. If
196*91f16700Schasinglulu  * this is the last segment, then nseg must be 0.
197*91f16700Schasinglulu  *
198*91f16700Schasinglulu  * This function maps directly to the PCI_GET_SEG_INFO function
199*91f16700Schasinglulu  * in DEN0115 where detailed requirements may be found.
200*91f16700Schasinglulu  *
201*91f16700Schasinglulu  * Return: SMC_PCI_CALL_SUCCESS, and appropriate bus_range and nseg
202*91f16700Schasinglulu  *	   SMC_PCI_CALL_NOT_IMPL, if the segment is invalid
203*91f16700Schasinglulu  */
204*91f16700Schasinglulu uint32_t pci_get_bus_for_seg(uint32_t seg, uint32_t *bus_range, uint32_t *nseg)
205*91f16700Schasinglulu {
206*91f16700Schasinglulu 	uint32_t ret = SMC_PCI_CALL_SUCCESS;
207*91f16700Schasinglulu 	*nseg = 0U; /* only a single segment */
208*91f16700Schasinglulu 	if (seg == 0U) {
209*91f16700Schasinglulu 		*bus_range = 0xFF00; /* start 0, end 255 */
210*91f16700Schasinglulu 	} else {
211*91f16700Schasinglulu 		*bus_range = 0U;
212*91f16700Schasinglulu 		ret = SMC_PCI_CALL_NOT_IMPL;
213*91f16700Schasinglulu 	}
214*91f16700Schasinglulu 	return ret;
215*91f16700Schasinglulu }
216