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