1 // SPDX-License-Identifier: GPL-2.0-or-later 1 2 /* 3 * Low-level PCI config space access for OLPC 4 * PCI virtualization software. 5 * 6 * Copyright © 2006 Advanced Micro Devices, 7 * 8 * The AMD Geode chipset (ie: GX2 processor, c 9 * has some I/O functions (display, southbridg 10 * that more or less behave like PCI devices, 11 * directly implement the PCI configuration sp 12 * "VSA" (Virtual System Architecture) softwar 13 * space for these devices, by trapping I/O ac 14 * (CF8/CFC) and running some code in System M 15 * On the OLPC platform, we don't want to use 16 * (a) it slows down suspend/resume, and (b) r 17 * compilers that are hard to get. So instead 18 * code simulate the PCI config registers for 19 * just simulate them the easy way, by inserti 20 * pci_write_config and pci_read_config path. 21 * are read-only anyway, so the bulk of the si 22 */ 23 24 #include <linux/pci.h> 25 #include <linux/init.h> 26 #include <asm/olpc.h> 27 #include <asm/geode.h> 28 #include <asm/pci_x86.h> 29 30 /* 31 * In the tables below, the first two line (8 32 * size masks that are used when the higher le 33 * the size of the region by writing ~0 to a b 34 * and reading back the result. 35 * 36 * The following lines are the values that are 37 * PCI config access cycles, i.e. not after ju 38 * ~0 to a base address register. 39 */ 40 41 static const uint32_t lxnb_hdr[] = { /* dev 1 42 0x0, 0x0, 0x0, 0x0, 43 0x0, 0x0, 0x0, 0x0, 44 45 0x281022, 0x2200005, 0x6000021, 0x80f8 46 0x0, 0x0, 0x0, 0x0, /* No v 47 0x0, 0x0, 0x0, 0x28100b, 48 0x0, 0x0, 0x0, 0x0, 49 0x0, 0x0, 0x0, 0x0, 50 0x0, 0x0, 0x0, 0x0, 51 0x0, 0x0, 0x0, 0x0, 52 }; 53 54 static const uint32_t gxnb_hdr[] = { /* dev 1 55 0xfffffffd, 0x0, 0x0, 0x0, 56 0x0, 0x0, 0x0, 0x0, 57 58 0x28100b, 0x2200005, 0x6000021, 0x80f8 59 0xac1d, 0x0, 0x0, 0x0, /* I/O B 60 0x0, 0x0, 0x0, 0x28100b, 61 0x0, 0x0, 0x0, 0x0, 62 0x0, 0x0, 0x0, 0x0, 63 0x0, 0x0, 0x0, 0x0, 64 0x0, 0x0, 0x0, 0x0, 65 }; 66 67 static const uint32_t lxfb_hdr[] = { /* dev 1 68 0xff000008, 0xffffc000, 0xffffc000, 0x 69 0xffffc000, 0x0, 0x0, 0x0, 70 71 0x20811022, 0x2200003, 0x3000000, 0x0, 72 0xfd000000, 0xfe000000, 0xfe004000, 0x 73 0xfe00c000, 0x0, 0x0, 0x30100b, 74 0x0, 0x0, 0x0, 0x10e, /* 75 0x0, 0x0, 0x0, 0x0, 76 0x3d0, 0x3c0, 0xa0000, 0x0, /* 77 0x0, 0x0, 0x0, 0x0, 78 }; 79 80 static const uint32_t gxfb_hdr[] = { /* dev 1 81 0xff800008, 0xffffc000, 0xffffc000, 0x 82 0x0, 0x0, 0x0, 0x0, 83 84 0x30100b, 0x2200003, 0x3000000, 0x0, 85 0xfd000000, 0xfe000000, 0xfe004000, 0x 86 0x0, 0x0, 0x0, 0x30100b, 87 0x0, 0x0, 0x0, 0x0, 88 0x0, 0x0, 0x0, 0x0, 89 0x3d0, 0x3c0, 0xa0000, 0x0, /* 90 0x0, 0x0, 0x0, 0x0, 91 }; 92 93 static const uint32_t aes_hdr[] = { /* dev 94 0xffffc000, 0x0, 0x0, 0x0, 95 0x0, 0x0, 0x0, 0x0, 96 97 0x20821022, 0x2a00006, 0x10100000, 0x8 98 0xfe010000, 0x0, 0x0, 0x0, 99 0x0, 0x0, 0x0, 0x20821022, 100 0x0, 0x0, 0x0, 0x0, 101 0x0, 0x0, 0x0, 0x0, 102 0x0, 0x0, 0x0, 0x0, 103 0x0, 0x0, 0x0, 0x0, 104 }; 105 106 107 static const uint32_t isa_hdr[] = { /* dev f 108 0xfffffff9, 0xffffff01, 0xffffffc1, 0x 109 0xffffff81, 0xffffffc1, 0x0, 0x0, 110 111 0x20901022, 0x2a00049, 0x6010003, 0x80 112 0x18b1, 0x1001, 0x1801, 0x1881, /* SMB 113 0x1401, 0x1841, 0x0, 0x20901022, 114 0x0, 0x0, 0x0, 0x0, 115 0x0, 0x0, 0x0, 0x0, 116 0x0, 0x0, 0x0, 0xaa5b, 117 0x0, 0x0, 0x0, 0x0, 118 }; 119 120 static const uint32_t ac97_hdr[] = { /* dev f 121 0xffffff81, 0x0, 0x0, 0x0, 122 0x0, 0x0, 0x0, 0x0, 123 124 0x20931022, 0x2a00041, 0x4010001, 0x0, 125 0x1481, 0x0, 0x0, 0x0, 126 0x0, 0x0, 0x0, 0x20931022, 127 0x0, 0x0, 0x0, 0x205, 128 0x0, 0x0, 0x0, 0x0, 129 0x0, 0x0, 0x0, 0x0, 130 0x0, 0x0, 0x0, 0x0, 131 }; 132 133 static const uint32_t ohci_hdr[] = { /* dev f 134 0xfffff000, 0x0, 0x0, 0x0, 135 0x0, 0x0, 0x0, 0x0, 136 137 0x20941022, 0x2300006, 0xc031002, 0x0, 138 0xfe01a000, 0x0, 0x0, 0x0, 139 0x0, 0x0, 0x0, 0x20941022, 140 0x0, 0x40, 0x0, 0x40a, 141 0xc8020001, 0x0, 0x0, 0x0, /* Cap 142 44 143 0x0, 0x0, 0x0, 0x0, 144 0x0, 0x0, 0x0, 0x0, 145 }; 146 147 static const uint32_t ehci_hdr[] = { /* dev f 148 0xfffff000, 0x0, 0x0, 0x0, 149 0x0, 0x0, 0x0, 0x0, 150 151 0x20951022, 0x2300006, 0xc032002, 0x0, 152 0xfe01b000, 0x0, 0x0, 0x0, 153 0x0, 0x0, 0x0, 0x20951022, 154 0x0, 0x40, 0x0, 0x40a, 155 0xc8020001, 0x0, 0x0, 0x0, /* Cap 156 mas 157 0x01000001, 0x0, 0x0, 0x0, /* EEC 158 0x2020, 0x0, 0x0, 0x0, /* (EH 159 61 160 }; 161 162 static uint32_t ff_loc = ~0; 163 static uint32_t zero_loc; 164 static int bar_probing; /* Set after a 165 static int is_lx; 166 167 #define NB_SLOT 0x1 /* Northbridge - GX ch 168 #define SB_SLOT 0xf /* Southbridge - CS553 169 170 static int is_simulated(unsigned int bus, unsi 171 { 172 return (!bus && ((PCI_SLOT(devfn) == N 173 (PCI_SLOT(devfn) == SB 174 } 175 176 static uint32_t *hdr_addr(const uint32_t *hdr, 177 { 178 uint32_t addr; 179 180 /* 181 * This is a little bit tricky. The h 182 * 0x20 bytes of size masks, followed 183 * In the normal case, when not probin 184 * to access the header data, so we ad 185 * thus skipping the size mask area. 186 * In the BAR probing case, we want to 187 * the BAR, so we subtract 0x10 (the c 188 * BAR0), and don't skip the size mask 189 */ 190 191 addr = (uint32_t)hdr + reg + (bar_prob 192 193 bar_probing = 0; 194 return (uint32_t *)addr; 195 } 196 197 static int pci_olpc_read(unsigned int seg, uns 198 unsigned int devfn, int reg, i 199 { 200 uint32_t *addr; 201 202 WARN_ON(seg); 203 204 /* Use the hardware mechanism for non- 205 if (!is_simulated(bus, devfn)) 206 return pci_direct_conf1.read(s 207 208 /* 209 * No device has config registers past 210 * by not storing entries for the none 211 */ 212 if (reg >= 0x70) 213 addr = &zero_loc; 214 else { 215 switch (devfn) { 216 case 0x8: 217 addr = hdr_addr(is_lx 218 break; 219 case 0x9: 220 addr = hdr_addr(is_lx 221 break; 222 case 0xa: 223 addr = is_lx ? hdr_add 224 break; 225 case 0x78: 226 addr = hdr_addr(isa_hd 227 break; 228 case 0x7b: 229 addr = hdr_addr(ac97_h 230 break; 231 case 0x7c: 232 addr = hdr_addr(ohci_h 233 break; 234 case 0x7d: 235 addr = hdr_addr(ehci_h 236 break; 237 default: 238 addr = &ff_loc; 239 break; 240 } 241 } 242 switch (len) { 243 case 1: 244 *value = *(uint8_t *)addr; 245 break; 246 case 2: 247 *value = *(uint16_t *)addr; 248 break; 249 case 4: 250 *value = *addr; 251 break; 252 default: 253 BUG(); 254 } 255 256 return 0; 257 } 258 259 static int pci_olpc_write(unsigned int seg, un 260 unsigned int devfn, int reg, i 261 { 262 WARN_ON(seg); 263 264 /* Use the hardware mechanism for non- 265 if (!is_simulated(bus, devfn)) 266 return pci_direct_conf1.write( 267 268 /* XXX we may want to extend this to s 269 270 /* 271 * Mostly we just discard writes, but 272 * (i.e. writing ~0 to a BAR), we reme 273 * the appropriate size mask on the ne 274 * to some extent, because it depends 275 * access after such a write will alwa 276 */ 277 278 if ((reg >= 0x10) && (reg < 0x2c)) { 279 /* write is to a BAR */ 280 if (value == ~0) 281 bar_probing = 1; 282 } else { 283 /* 284 * No warning on writes to ROM 285 * CACHE_LINE_SIZE, or PM regi 286 */ 287 if ((reg != PCI_ROM_ADDRESS) & 288 (reg != PCI_LA 289 (reg != PCI_CA 290 printk(KERN_WARNING "O 291 " %x reg %x va 292 } 293 294 return 0; 295 } 296 297 static const struct pci_raw_ops pci_olpc_conf 298 .read = pci_olpc_read, 299 .write = pci_olpc_write, 300 }; 301 302 int __init pci_olpc_init(void) 303 { 304 printk(KERN_INFO "PCI: Using configura 305 raw_pci_ops = &pci_olpc_conf; 306 is_lx = is_geode_lx(); 307 return 0; 308 } 309
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.