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