1 /* 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 * 14 * The PCI SMCCC interface is described in DEN0115 available from: 15 * 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 32 static 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 46 static 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 */ 111 uint32_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 */ 160 uint32_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 */ 204 uint32_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 } 216