Greg Kroah-Hartman | b244131 | 2017-11-01 15:07:57 +0100 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
David S. Miller | 5fce09c | 2008-12-05 00:43:03 -0800 | [diff] [blame] | 2 | #include <linux/kernel.h> |
| 3 | #include <linux/string.h> |
| 4 | #include <linux/init.h> |
| 5 | #include <linux/of.h> |
| 6 | #include <linux/of_platform.h> |
| 7 | |
| 8 | #include <asm/oplib.h> |
| 9 | #include <asm/prom.h> |
| 10 | #include <asm/irq.h> |
| 11 | #include <asm/upa.h> |
| 12 | |
| 13 | #include "prom.h" |
| 14 | |
| 15 | #ifdef CONFIG_PCI |
| 16 | /* PSYCHO interrupt mapping support. */ |
| 17 | #define PSYCHO_IMAP_A_SLOT0 0x0c00UL |
| 18 | #define PSYCHO_IMAP_B_SLOT0 0x0c20UL |
| 19 | static unsigned long psycho_pcislot_imap_offset(unsigned long ino) |
| 20 | { |
| 21 | unsigned int bus = (ino & 0x10) >> 4; |
| 22 | unsigned int slot = (ino & 0x0c) >> 2; |
| 23 | |
| 24 | if (bus == 0) |
| 25 | return PSYCHO_IMAP_A_SLOT0 + (slot * 8); |
| 26 | else |
| 27 | return PSYCHO_IMAP_B_SLOT0 + (slot * 8); |
| 28 | } |
| 29 | |
| 30 | #define PSYCHO_OBIO_IMAP_BASE 0x1000UL |
| 31 | |
| 32 | #define PSYCHO_ONBOARD_IRQ_BASE 0x20 |
| 33 | #define psycho_onboard_imap_offset(__ino) \ |
| 34 | (PSYCHO_OBIO_IMAP_BASE + (((__ino) & 0x1f) << 3)) |
| 35 | |
| 36 | #define PSYCHO_ICLR_A_SLOT0 0x1400UL |
| 37 | #define PSYCHO_ICLR_SCSI 0x1800UL |
| 38 | |
| 39 | #define psycho_iclr_offset(ino) \ |
| 40 | ((ino & 0x20) ? (PSYCHO_ICLR_SCSI + (((ino) & 0x1f) << 3)) : \ |
| 41 | (PSYCHO_ICLR_A_SLOT0 + (((ino) & 0x1f)<<3))) |
| 42 | |
| 43 | static unsigned int psycho_irq_build(struct device_node *dp, |
| 44 | unsigned int ino, |
| 45 | void *_data) |
| 46 | { |
| 47 | unsigned long controller_regs = (unsigned long) _data; |
| 48 | unsigned long imap, iclr; |
| 49 | unsigned long imap_off, iclr_off; |
| 50 | int inofixup = 0; |
| 51 | |
| 52 | ino &= 0x3f; |
| 53 | if (ino < PSYCHO_ONBOARD_IRQ_BASE) { |
| 54 | /* PCI slot */ |
| 55 | imap_off = psycho_pcislot_imap_offset(ino); |
| 56 | } else { |
| 57 | /* Onboard device */ |
| 58 | imap_off = psycho_onboard_imap_offset(ino); |
| 59 | } |
| 60 | |
| 61 | /* Now build the IRQ bucket. */ |
| 62 | imap = controller_regs + imap_off; |
| 63 | |
| 64 | iclr_off = psycho_iclr_offset(ino); |
| 65 | iclr = controller_regs + iclr_off; |
| 66 | |
| 67 | if ((ino & 0x20) == 0) |
| 68 | inofixup = ino & 0x03; |
| 69 | |
| 70 | return build_irq(inofixup, iclr, imap); |
| 71 | } |
| 72 | |
| 73 | static void __init psycho_irq_trans_init(struct device_node *dp) |
| 74 | { |
| 75 | const struct linux_prom64_registers *regs; |
| 76 | |
| 77 | dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); |
| 78 | dp->irq_trans->irq_build = psycho_irq_build; |
| 79 | |
| 80 | regs = of_get_property(dp, "reg", NULL); |
| 81 | dp->irq_trans->data = (void *) regs[2].phys_addr; |
| 82 | } |
| 83 | |
| 84 | #define sabre_read(__reg) \ |
| 85 | ({ u64 __ret; \ |
| 86 | __asm__ __volatile__("ldxa [%1] %2, %0" \ |
| 87 | : "=r" (__ret) \ |
| 88 | : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \ |
| 89 | : "memory"); \ |
| 90 | __ret; \ |
| 91 | }) |
| 92 | |
| 93 | struct sabre_irq_data { |
| 94 | unsigned long controller_regs; |
| 95 | unsigned int pci_first_busno; |
| 96 | }; |
| 97 | #define SABRE_CONFIGSPACE 0x001000000UL |
| 98 | #define SABRE_WRSYNC 0x1c20UL |
| 99 | |
| 100 | #define SABRE_CONFIG_BASE(CONFIG_SPACE) \ |
| 101 | (CONFIG_SPACE | (1UL << 24)) |
| 102 | #define SABRE_CONFIG_ENCODE(BUS, DEVFN, REG) \ |
| 103 | (((unsigned long)(BUS) << 16) | \ |
| 104 | ((unsigned long)(DEVFN) << 8) | \ |
| 105 | ((unsigned long)(REG))) |
| 106 | |
| 107 | /* When a device lives behind a bridge deeper in the PCI bus topology |
| 108 | * than APB, a special sequence must run to make sure all pending DMA |
| 109 | * transfers at the time of IRQ delivery are visible in the coherency |
| 110 | * domain by the cpu. This sequence is to perform a read on the far |
| 111 | * side of the non-APB bridge, then perform a read of Sabre's DMA |
| 112 | * write-sync register. |
| 113 | */ |
| 114 | static void sabre_wsync_handler(unsigned int ino, void *_arg1, void *_arg2) |
| 115 | { |
| 116 | unsigned int phys_hi = (unsigned int) (unsigned long) _arg1; |
| 117 | struct sabre_irq_data *irq_data = _arg2; |
| 118 | unsigned long controller_regs = irq_data->controller_regs; |
| 119 | unsigned long sync_reg = controller_regs + SABRE_WRSYNC; |
| 120 | unsigned long config_space = controller_regs + SABRE_CONFIGSPACE; |
| 121 | unsigned int bus, devfn; |
| 122 | u16 _unused; |
| 123 | |
| 124 | config_space = SABRE_CONFIG_BASE(config_space); |
| 125 | |
| 126 | bus = (phys_hi >> 16) & 0xff; |
| 127 | devfn = (phys_hi >> 8) & 0xff; |
| 128 | |
| 129 | config_space |= SABRE_CONFIG_ENCODE(bus, devfn, 0x00); |
| 130 | |
| 131 | __asm__ __volatile__("membar #Sync\n\t" |
| 132 | "lduha [%1] %2, %0\n\t" |
| 133 | "membar #Sync" |
| 134 | : "=r" (_unused) |
| 135 | : "r" ((u16 *) config_space), |
| 136 | "i" (ASI_PHYS_BYPASS_EC_E_L) |
| 137 | : "memory"); |
| 138 | |
| 139 | sabre_read(sync_reg); |
| 140 | } |
| 141 | |
| 142 | #define SABRE_IMAP_A_SLOT0 0x0c00UL |
| 143 | #define SABRE_IMAP_B_SLOT0 0x0c20UL |
| 144 | #define SABRE_ICLR_A_SLOT0 0x1400UL |
| 145 | #define SABRE_ICLR_B_SLOT0 0x1480UL |
| 146 | #define SABRE_ICLR_SCSI 0x1800UL |
| 147 | #define SABRE_ICLR_ETH 0x1808UL |
| 148 | #define SABRE_ICLR_BPP 0x1810UL |
| 149 | #define SABRE_ICLR_AU_REC 0x1818UL |
| 150 | #define SABRE_ICLR_AU_PLAY 0x1820UL |
| 151 | #define SABRE_ICLR_PFAIL 0x1828UL |
| 152 | #define SABRE_ICLR_KMS 0x1830UL |
| 153 | #define SABRE_ICLR_FLPY 0x1838UL |
| 154 | #define SABRE_ICLR_SHW 0x1840UL |
| 155 | #define SABRE_ICLR_KBD 0x1848UL |
| 156 | #define SABRE_ICLR_MS 0x1850UL |
| 157 | #define SABRE_ICLR_SER 0x1858UL |
| 158 | #define SABRE_ICLR_UE 0x1870UL |
| 159 | #define SABRE_ICLR_CE 0x1878UL |
| 160 | #define SABRE_ICLR_PCIERR 0x1880UL |
| 161 | |
| 162 | static unsigned long sabre_pcislot_imap_offset(unsigned long ino) |
| 163 | { |
| 164 | unsigned int bus = (ino & 0x10) >> 4; |
| 165 | unsigned int slot = (ino & 0x0c) >> 2; |
| 166 | |
| 167 | if (bus == 0) |
| 168 | return SABRE_IMAP_A_SLOT0 + (slot * 8); |
| 169 | else |
| 170 | return SABRE_IMAP_B_SLOT0 + (slot * 8); |
| 171 | } |
| 172 | |
| 173 | #define SABRE_OBIO_IMAP_BASE 0x1000UL |
| 174 | #define SABRE_ONBOARD_IRQ_BASE 0x20 |
| 175 | #define sabre_onboard_imap_offset(__ino) \ |
| 176 | (SABRE_OBIO_IMAP_BASE + (((__ino) & 0x1f) << 3)) |
| 177 | |
| 178 | #define sabre_iclr_offset(ino) \ |
| 179 | ((ino & 0x20) ? (SABRE_ICLR_SCSI + (((ino) & 0x1f) << 3)) : \ |
| 180 | (SABRE_ICLR_A_SLOT0 + (((ino) & 0x1f)<<3))) |
| 181 | |
| 182 | static int sabre_device_needs_wsync(struct device_node *dp) |
| 183 | { |
| 184 | struct device_node *parent = dp->parent; |
| 185 | const char *parent_model, *parent_compat; |
| 186 | |
| 187 | /* This traversal up towards the root is meant to |
| 188 | * handle two cases: |
| 189 | * |
| 190 | * 1) non-PCI bus sitting under PCI, such as 'ebus' |
| 191 | * 2) the PCI controller interrupts themselves, which |
| 192 | * will use the sabre_irq_build but do not need |
| 193 | * the DMA synchronization handling |
| 194 | */ |
| 195 | while (parent) { |
Rob Herring | 88ca055 | 2018-11-16 15:06:59 -0600 | [diff] [blame] | 196 | if (of_node_is_type(parent, "pci")) |
David S. Miller | 5fce09c | 2008-12-05 00:43:03 -0800 | [diff] [blame] | 197 | break; |
| 198 | parent = parent->parent; |
| 199 | } |
| 200 | |
| 201 | if (!parent) |
| 202 | return 0; |
| 203 | |
| 204 | parent_model = of_get_property(parent, |
| 205 | "model", NULL); |
| 206 | if (parent_model && |
| 207 | (!strcmp(parent_model, "SUNW,sabre") || |
| 208 | !strcmp(parent_model, "SUNW,simba"))) |
| 209 | return 0; |
| 210 | |
| 211 | parent_compat = of_get_property(parent, |
| 212 | "compatible", NULL); |
| 213 | if (parent_compat && |
| 214 | (!strcmp(parent_compat, "pci108e,a000") || |
| 215 | !strcmp(parent_compat, "pci108e,a001"))) |
| 216 | return 0; |
| 217 | |
| 218 | return 1; |
| 219 | } |
| 220 | |
| 221 | static unsigned int sabre_irq_build(struct device_node *dp, |
| 222 | unsigned int ino, |
| 223 | void *_data) |
| 224 | { |
| 225 | struct sabre_irq_data *irq_data = _data; |
| 226 | unsigned long controller_regs = irq_data->controller_regs; |
| 227 | const struct linux_prom_pci_registers *regs; |
| 228 | unsigned long imap, iclr; |
| 229 | unsigned long imap_off, iclr_off; |
| 230 | int inofixup = 0; |
Sam Ravnborg | 44ed3c0 | 2011-01-22 11:32:20 +0000 | [diff] [blame] | 231 | int irq; |
David S. Miller | 5fce09c | 2008-12-05 00:43:03 -0800 | [diff] [blame] | 232 | |
| 233 | ino &= 0x3f; |
| 234 | if (ino < SABRE_ONBOARD_IRQ_BASE) { |
| 235 | /* PCI slot */ |
| 236 | imap_off = sabre_pcislot_imap_offset(ino); |
| 237 | } else { |
| 238 | /* onboard device */ |
| 239 | imap_off = sabre_onboard_imap_offset(ino); |
| 240 | } |
| 241 | |
| 242 | /* Now build the IRQ bucket. */ |
| 243 | imap = controller_regs + imap_off; |
| 244 | |
| 245 | iclr_off = sabre_iclr_offset(ino); |
| 246 | iclr = controller_regs + iclr_off; |
| 247 | |
| 248 | if ((ino & 0x20) == 0) |
| 249 | inofixup = ino & 0x03; |
| 250 | |
Sam Ravnborg | 44ed3c0 | 2011-01-22 11:32:20 +0000 | [diff] [blame] | 251 | irq = build_irq(inofixup, iclr, imap); |
David S. Miller | 5fce09c | 2008-12-05 00:43:03 -0800 | [diff] [blame] | 252 | |
| 253 | /* If the parent device is a PCI<->PCI bridge other than |
| 254 | * APB, we have to install a pre-handler to ensure that |
| 255 | * all pending DMA is drained before the interrupt handler |
| 256 | * is run. |
| 257 | */ |
| 258 | regs = of_get_property(dp, "reg", NULL); |
| 259 | if (regs && sabre_device_needs_wsync(dp)) { |
Sam Ravnborg | 44ed3c0 | 2011-01-22 11:32:20 +0000 | [diff] [blame] | 260 | irq_install_pre_handler(irq, |
David S. Miller | 5fce09c | 2008-12-05 00:43:03 -0800 | [diff] [blame] | 261 | sabre_wsync_handler, |
| 262 | (void *) (long) regs->phys_hi, |
| 263 | (void *) irq_data); |
| 264 | } |
| 265 | |
Sam Ravnborg | 44ed3c0 | 2011-01-22 11:32:20 +0000 | [diff] [blame] | 266 | return irq; |
David S. Miller | 5fce09c | 2008-12-05 00:43:03 -0800 | [diff] [blame] | 267 | } |
| 268 | |
| 269 | static void __init sabre_irq_trans_init(struct device_node *dp) |
| 270 | { |
| 271 | const struct linux_prom64_registers *regs; |
| 272 | struct sabre_irq_data *irq_data; |
| 273 | const u32 *busrange; |
| 274 | |
| 275 | dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); |
| 276 | dp->irq_trans->irq_build = sabre_irq_build; |
| 277 | |
| 278 | irq_data = prom_early_alloc(sizeof(struct sabre_irq_data)); |
| 279 | |
| 280 | regs = of_get_property(dp, "reg", NULL); |
| 281 | irq_data->controller_regs = regs[0].phys_addr; |
| 282 | |
| 283 | busrange = of_get_property(dp, "bus-range", NULL); |
| 284 | irq_data->pci_first_busno = busrange[0]; |
| 285 | |
| 286 | dp->irq_trans->data = irq_data; |
| 287 | } |
| 288 | |
| 289 | /* SCHIZO interrupt mapping support. Unlike Psycho, for this controller the |
| 290 | * imap/iclr registers are per-PBM. |
| 291 | */ |
| 292 | #define SCHIZO_IMAP_BASE 0x1000UL |
| 293 | #define SCHIZO_ICLR_BASE 0x1400UL |
| 294 | |
| 295 | static unsigned long schizo_imap_offset(unsigned long ino) |
| 296 | { |
| 297 | return SCHIZO_IMAP_BASE + (ino * 8UL); |
| 298 | } |
| 299 | |
| 300 | static unsigned long schizo_iclr_offset(unsigned long ino) |
| 301 | { |
| 302 | return SCHIZO_ICLR_BASE + (ino * 8UL); |
| 303 | } |
| 304 | |
| 305 | static unsigned long schizo_ino_to_iclr(unsigned long pbm_regs, |
| 306 | unsigned int ino) |
| 307 | { |
| 308 | |
| 309 | return pbm_regs + schizo_iclr_offset(ino); |
| 310 | } |
| 311 | |
| 312 | static unsigned long schizo_ino_to_imap(unsigned long pbm_regs, |
| 313 | unsigned int ino) |
| 314 | { |
| 315 | return pbm_regs + schizo_imap_offset(ino); |
| 316 | } |
| 317 | |
| 318 | #define schizo_read(__reg) \ |
| 319 | ({ u64 __ret; \ |
| 320 | __asm__ __volatile__("ldxa [%1] %2, %0" \ |
| 321 | : "=r" (__ret) \ |
| 322 | : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \ |
| 323 | : "memory"); \ |
| 324 | __ret; \ |
| 325 | }) |
| 326 | #define schizo_write(__reg, __val) \ |
| 327 | __asm__ __volatile__("stxa %0, [%1] %2" \ |
| 328 | : /* no outputs */ \ |
| 329 | : "r" (__val), "r" (__reg), \ |
| 330 | "i" (ASI_PHYS_BYPASS_EC_E) \ |
| 331 | : "memory") |
| 332 | |
| 333 | static void tomatillo_wsync_handler(unsigned int ino, void *_arg1, void *_arg2) |
| 334 | { |
| 335 | unsigned long sync_reg = (unsigned long) _arg2; |
| 336 | u64 mask = 1UL << (ino & IMAP_INO); |
| 337 | u64 val; |
| 338 | int limit; |
| 339 | |
| 340 | schizo_write(sync_reg, mask); |
| 341 | |
| 342 | limit = 100000; |
| 343 | val = 0; |
| 344 | while (--limit) { |
| 345 | val = schizo_read(sync_reg); |
| 346 | if (!(val & mask)) |
| 347 | break; |
| 348 | } |
| 349 | if (limit <= 0) { |
Sam Ravnborg | 9018113 | 2009-01-06 13:19:28 -0800 | [diff] [blame] | 350 | printk("tomatillo_wsync_handler: DMA won't sync [%llx:%llx]\n", |
David S. Miller | 5fce09c | 2008-12-05 00:43:03 -0800 | [diff] [blame] | 351 | val, mask); |
| 352 | } |
| 353 | |
| 354 | if (_arg1) { |
| 355 | static unsigned char cacheline[64] |
| 356 | __attribute__ ((aligned (64))); |
| 357 | |
| 358 | __asm__ __volatile__("rd %%fprs, %0\n\t" |
| 359 | "or %0, %4, %1\n\t" |
| 360 | "wr %1, 0x0, %%fprs\n\t" |
| 361 | "stda %%f0, [%5] %6\n\t" |
| 362 | "wr %0, 0x0, %%fprs\n\t" |
| 363 | "membar #Sync" |
| 364 | : "=&r" (mask), "=&r" (val) |
| 365 | : "0" (mask), "1" (val), |
| 366 | "i" (FPRS_FEF), "r" (&cacheline[0]), |
| 367 | "i" (ASI_BLK_COMMIT_P)); |
| 368 | } |
| 369 | } |
| 370 | |
| 371 | struct schizo_irq_data { |
| 372 | unsigned long pbm_regs; |
| 373 | unsigned long sync_reg; |
| 374 | u32 portid; |
| 375 | int chip_version; |
| 376 | }; |
| 377 | |
| 378 | static unsigned int schizo_irq_build(struct device_node *dp, |
| 379 | unsigned int ino, |
| 380 | void *_data) |
| 381 | { |
| 382 | struct schizo_irq_data *irq_data = _data; |
| 383 | unsigned long pbm_regs = irq_data->pbm_regs; |
| 384 | unsigned long imap, iclr; |
| 385 | int ign_fixup; |
Sam Ravnborg | 44ed3c0 | 2011-01-22 11:32:20 +0000 | [diff] [blame] | 386 | int irq; |
David S. Miller | 5fce09c | 2008-12-05 00:43:03 -0800 | [diff] [blame] | 387 | int is_tomatillo; |
| 388 | |
| 389 | ino &= 0x3f; |
| 390 | |
| 391 | /* Now build the IRQ bucket. */ |
| 392 | imap = schizo_ino_to_imap(pbm_regs, ino); |
| 393 | iclr = schizo_ino_to_iclr(pbm_regs, ino); |
| 394 | |
| 395 | /* On Schizo, no inofixup occurs. This is because each |
| 396 | * INO has it's own IMAP register. On Psycho and Sabre |
| 397 | * there is only one IMAP register for each PCI slot even |
| 398 | * though four different INOs can be generated by each |
| 399 | * PCI slot. |
| 400 | * |
| 401 | * But, for JBUS variants (essentially, Tomatillo), we have |
| 402 | * to fixup the lowest bit of the interrupt group number. |
| 403 | */ |
| 404 | ign_fixup = 0; |
| 405 | |
| 406 | is_tomatillo = (irq_data->sync_reg != 0UL); |
| 407 | |
| 408 | if (is_tomatillo) { |
| 409 | if (irq_data->portid & 1) |
| 410 | ign_fixup = (1 << 6); |
| 411 | } |
| 412 | |
Sam Ravnborg | 44ed3c0 | 2011-01-22 11:32:20 +0000 | [diff] [blame] | 413 | irq = build_irq(ign_fixup, iclr, imap); |
David S. Miller | 5fce09c | 2008-12-05 00:43:03 -0800 | [diff] [blame] | 414 | |
| 415 | if (is_tomatillo) { |
Sam Ravnborg | 44ed3c0 | 2011-01-22 11:32:20 +0000 | [diff] [blame] | 416 | irq_install_pre_handler(irq, |
David S. Miller | 5fce09c | 2008-12-05 00:43:03 -0800 | [diff] [blame] | 417 | tomatillo_wsync_handler, |
| 418 | ((irq_data->chip_version <= 4) ? |
| 419 | (void *) 1 : (void *) 0), |
| 420 | (void *) irq_data->sync_reg); |
| 421 | } |
| 422 | |
Sam Ravnborg | 44ed3c0 | 2011-01-22 11:32:20 +0000 | [diff] [blame] | 423 | return irq; |
David S. Miller | 5fce09c | 2008-12-05 00:43:03 -0800 | [diff] [blame] | 424 | } |
| 425 | |
| 426 | static void __init __schizo_irq_trans_init(struct device_node *dp, |
| 427 | int is_tomatillo) |
| 428 | { |
| 429 | const struct linux_prom64_registers *regs; |
| 430 | struct schizo_irq_data *irq_data; |
| 431 | |
| 432 | dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); |
| 433 | dp->irq_trans->irq_build = schizo_irq_build; |
| 434 | |
| 435 | irq_data = prom_early_alloc(sizeof(struct schizo_irq_data)); |
| 436 | |
| 437 | regs = of_get_property(dp, "reg", NULL); |
| 438 | dp->irq_trans->data = irq_data; |
| 439 | |
| 440 | irq_data->pbm_regs = regs[0].phys_addr; |
| 441 | if (is_tomatillo) |
| 442 | irq_data->sync_reg = regs[3].phys_addr + 0x1a18UL; |
| 443 | else |
| 444 | irq_data->sync_reg = 0UL; |
| 445 | irq_data->portid = of_getintprop_default(dp, "portid", 0); |
| 446 | irq_data->chip_version = of_getintprop_default(dp, "version#", 0); |
| 447 | } |
| 448 | |
| 449 | static void __init schizo_irq_trans_init(struct device_node *dp) |
| 450 | { |
| 451 | __schizo_irq_trans_init(dp, 0); |
| 452 | } |
| 453 | |
| 454 | static void __init tomatillo_irq_trans_init(struct device_node *dp) |
| 455 | { |
| 456 | __schizo_irq_trans_init(dp, 1); |
| 457 | } |
| 458 | |
| 459 | static unsigned int pci_sun4v_irq_build(struct device_node *dp, |
| 460 | unsigned int devino, |
| 461 | void *_data) |
| 462 | { |
| 463 | u32 devhandle = (u32) (unsigned long) _data; |
| 464 | |
| 465 | return sun4v_build_irq(devhandle, devino); |
| 466 | } |
| 467 | |
| 468 | static void __init pci_sun4v_irq_trans_init(struct device_node *dp) |
| 469 | { |
| 470 | const struct linux_prom64_registers *regs; |
| 471 | |
| 472 | dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); |
| 473 | dp->irq_trans->irq_build = pci_sun4v_irq_build; |
| 474 | |
| 475 | regs = of_get_property(dp, "reg", NULL); |
| 476 | dp->irq_trans->data = (void *) (unsigned long) |
| 477 | ((regs->phys_addr >> 32UL) & 0x0fffffff); |
| 478 | } |
| 479 | |
| 480 | struct fire_irq_data { |
| 481 | unsigned long pbm_regs; |
| 482 | u32 portid; |
| 483 | }; |
| 484 | |
| 485 | #define FIRE_IMAP_BASE 0x001000 |
| 486 | #define FIRE_ICLR_BASE 0x001400 |
| 487 | |
| 488 | static unsigned long fire_imap_offset(unsigned long ino) |
| 489 | { |
| 490 | return FIRE_IMAP_BASE + (ino * 8UL); |
| 491 | } |
| 492 | |
| 493 | static unsigned long fire_iclr_offset(unsigned long ino) |
| 494 | { |
| 495 | return FIRE_ICLR_BASE + (ino * 8UL); |
| 496 | } |
| 497 | |
| 498 | static unsigned long fire_ino_to_iclr(unsigned long pbm_regs, |
| 499 | unsigned int ino) |
| 500 | { |
| 501 | return pbm_regs + fire_iclr_offset(ino); |
| 502 | } |
| 503 | |
| 504 | static unsigned long fire_ino_to_imap(unsigned long pbm_regs, |
| 505 | unsigned int ino) |
| 506 | { |
| 507 | return pbm_regs + fire_imap_offset(ino); |
| 508 | } |
| 509 | |
| 510 | static unsigned int fire_irq_build(struct device_node *dp, |
| 511 | unsigned int ino, |
| 512 | void *_data) |
| 513 | { |
| 514 | struct fire_irq_data *irq_data = _data; |
| 515 | unsigned long pbm_regs = irq_data->pbm_regs; |
| 516 | unsigned long imap, iclr; |
| 517 | unsigned long int_ctrlr; |
| 518 | |
| 519 | ino &= 0x3f; |
| 520 | |
| 521 | /* Now build the IRQ bucket. */ |
| 522 | imap = fire_ino_to_imap(pbm_regs, ino); |
| 523 | iclr = fire_ino_to_iclr(pbm_regs, ino); |
| 524 | |
| 525 | /* Set the interrupt controller number. */ |
| 526 | int_ctrlr = 1 << 6; |
| 527 | upa_writeq(int_ctrlr, imap); |
| 528 | |
| 529 | /* The interrupt map registers do not have an INO field |
| 530 | * like other chips do. They return zero in the INO |
| 531 | * field, and the interrupt controller number is controlled |
| 532 | * in bits 6 to 9. So in order for build_irq() to get |
| 533 | * the INO right we pass it in as part of the fixup |
| 534 | * which will get added to the map register zero value |
| 535 | * read by build_irq(). |
| 536 | */ |
| 537 | ino |= (irq_data->portid << 6); |
| 538 | ino -= int_ctrlr; |
| 539 | return build_irq(ino, iclr, imap); |
| 540 | } |
| 541 | |
| 542 | static void __init fire_irq_trans_init(struct device_node *dp) |
| 543 | { |
| 544 | const struct linux_prom64_registers *regs; |
| 545 | struct fire_irq_data *irq_data; |
| 546 | |
| 547 | dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); |
| 548 | dp->irq_trans->irq_build = fire_irq_build; |
| 549 | |
| 550 | irq_data = prom_early_alloc(sizeof(struct fire_irq_data)); |
| 551 | |
| 552 | regs = of_get_property(dp, "reg", NULL); |
| 553 | dp->irq_trans->data = irq_data; |
| 554 | |
| 555 | irq_data->pbm_regs = regs[0].phys_addr; |
| 556 | irq_data->portid = of_getintprop_default(dp, "portid", 0); |
| 557 | } |
| 558 | #endif /* CONFIG_PCI */ |
| 559 | |
| 560 | #ifdef CONFIG_SBUS |
| 561 | /* INO number to IMAP register offset for SYSIO external IRQ's. |
| 562 | * This should conform to both Sunfire/Wildfire server and Fusion |
| 563 | * desktop designs. |
| 564 | */ |
| 565 | #define SYSIO_IMAP_SLOT0 0x2c00UL |
| 566 | #define SYSIO_IMAP_SLOT1 0x2c08UL |
| 567 | #define SYSIO_IMAP_SLOT2 0x2c10UL |
| 568 | #define SYSIO_IMAP_SLOT3 0x2c18UL |
| 569 | #define SYSIO_IMAP_SCSI 0x3000UL |
| 570 | #define SYSIO_IMAP_ETH 0x3008UL |
| 571 | #define SYSIO_IMAP_BPP 0x3010UL |
| 572 | #define SYSIO_IMAP_AUDIO 0x3018UL |
| 573 | #define SYSIO_IMAP_PFAIL 0x3020UL |
| 574 | #define SYSIO_IMAP_KMS 0x3028UL |
| 575 | #define SYSIO_IMAP_FLPY 0x3030UL |
| 576 | #define SYSIO_IMAP_SHW 0x3038UL |
| 577 | #define SYSIO_IMAP_KBD 0x3040UL |
| 578 | #define SYSIO_IMAP_MS 0x3048UL |
| 579 | #define SYSIO_IMAP_SER 0x3050UL |
| 580 | #define SYSIO_IMAP_TIM0 0x3060UL |
| 581 | #define SYSIO_IMAP_TIM1 0x3068UL |
| 582 | #define SYSIO_IMAP_UE 0x3070UL |
| 583 | #define SYSIO_IMAP_CE 0x3078UL |
| 584 | #define SYSIO_IMAP_SBERR 0x3080UL |
| 585 | #define SYSIO_IMAP_PMGMT 0x3088UL |
| 586 | #define SYSIO_IMAP_GFX 0x3090UL |
| 587 | #define SYSIO_IMAP_EUPA 0x3098UL |
| 588 | |
| 589 | #define bogon ((unsigned long) -1) |
| 590 | static unsigned long sysio_irq_offsets[] = { |
| 591 | /* SBUS Slot 0 --> 3, level 1 --> 7 */ |
| 592 | SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, |
| 593 | SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, |
| 594 | SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, |
| 595 | SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, |
| 596 | SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, |
| 597 | SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, |
| 598 | SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, |
| 599 | SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, |
| 600 | |
| 601 | /* Onboard devices (not relevant/used on SunFire). */ |
| 602 | SYSIO_IMAP_SCSI, |
| 603 | SYSIO_IMAP_ETH, |
| 604 | SYSIO_IMAP_BPP, |
| 605 | bogon, |
| 606 | SYSIO_IMAP_AUDIO, |
| 607 | SYSIO_IMAP_PFAIL, |
| 608 | bogon, |
| 609 | bogon, |
| 610 | SYSIO_IMAP_KMS, |
| 611 | SYSIO_IMAP_FLPY, |
| 612 | SYSIO_IMAP_SHW, |
| 613 | SYSIO_IMAP_KBD, |
| 614 | SYSIO_IMAP_MS, |
| 615 | SYSIO_IMAP_SER, |
| 616 | bogon, |
| 617 | bogon, |
| 618 | SYSIO_IMAP_TIM0, |
| 619 | SYSIO_IMAP_TIM1, |
| 620 | bogon, |
| 621 | bogon, |
| 622 | SYSIO_IMAP_UE, |
| 623 | SYSIO_IMAP_CE, |
| 624 | SYSIO_IMAP_SBERR, |
| 625 | SYSIO_IMAP_PMGMT, |
| 626 | SYSIO_IMAP_GFX, |
| 627 | SYSIO_IMAP_EUPA, |
| 628 | }; |
| 629 | |
| 630 | #undef bogon |
| 631 | |
| 632 | #define NUM_SYSIO_OFFSETS ARRAY_SIZE(sysio_irq_offsets) |
| 633 | |
| 634 | /* Convert Interrupt Mapping register pointer to associated |
| 635 | * Interrupt Clear register pointer, SYSIO specific version. |
| 636 | */ |
| 637 | #define SYSIO_ICLR_UNUSED0 0x3400UL |
| 638 | #define SYSIO_ICLR_SLOT0 0x3408UL |
| 639 | #define SYSIO_ICLR_SLOT1 0x3448UL |
| 640 | #define SYSIO_ICLR_SLOT2 0x3488UL |
| 641 | #define SYSIO_ICLR_SLOT3 0x34c8UL |
| 642 | static unsigned long sysio_imap_to_iclr(unsigned long imap) |
| 643 | { |
| 644 | unsigned long diff = SYSIO_ICLR_UNUSED0 - SYSIO_IMAP_SLOT0; |
| 645 | return imap + diff; |
| 646 | } |
| 647 | |
| 648 | static unsigned int sbus_of_build_irq(struct device_node *dp, |
| 649 | unsigned int ino, |
| 650 | void *_data) |
| 651 | { |
| 652 | unsigned long reg_base = (unsigned long) _data; |
| 653 | const struct linux_prom_registers *regs; |
| 654 | unsigned long imap, iclr; |
| 655 | int sbus_slot = 0; |
| 656 | int sbus_level = 0; |
| 657 | |
| 658 | ino &= 0x3f; |
| 659 | |
| 660 | regs = of_get_property(dp, "reg", NULL); |
| 661 | if (regs) |
| 662 | sbus_slot = regs->which_io; |
| 663 | |
| 664 | if (ino < 0x20) |
| 665 | ino += (sbus_slot * 8); |
| 666 | |
| 667 | imap = sysio_irq_offsets[ino]; |
| 668 | if (imap == ((unsigned long)-1)) { |
| 669 | prom_printf("get_irq_translations: Bad SYSIO INO[%x]\n", |
| 670 | ino); |
| 671 | prom_halt(); |
| 672 | } |
| 673 | imap += reg_base; |
| 674 | |
| 675 | /* SYSIO inconsistency. For external SLOTS, we have to select |
| 676 | * the right ICLR register based upon the lower SBUS irq level |
| 677 | * bits. |
| 678 | */ |
| 679 | if (ino >= 0x20) { |
| 680 | iclr = sysio_imap_to_iclr(imap); |
| 681 | } else { |
| 682 | sbus_level = ino & 0x7; |
| 683 | |
| 684 | switch(sbus_slot) { |
| 685 | case 0: |
| 686 | iclr = reg_base + SYSIO_ICLR_SLOT0; |
| 687 | break; |
| 688 | case 1: |
| 689 | iclr = reg_base + SYSIO_ICLR_SLOT1; |
| 690 | break; |
| 691 | case 2: |
| 692 | iclr = reg_base + SYSIO_ICLR_SLOT2; |
| 693 | break; |
| 694 | default: |
| 695 | case 3: |
| 696 | iclr = reg_base + SYSIO_ICLR_SLOT3; |
| 697 | break; |
Joe Perches | 6cb79b3 | 2011-06-03 14:45:23 +0000 | [diff] [blame] | 698 | } |
David S. Miller | 5fce09c | 2008-12-05 00:43:03 -0800 | [diff] [blame] | 699 | |
| 700 | iclr += ((unsigned long)sbus_level - 1UL) * 8UL; |
| 701 | } |
| 702 | return build_irq(sbus_level, iclr, imap); |
| 703 | } |
| 704 | |
| 705 | static void __init sbus_irq_trans_init(struct device_node *dp) |
| 706 | { |
| 707 | const struct linux_prom64_registers *regs; |
| 708 | |
| 709 | dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); |
| 710 | dp->irq_trans->irq_build = sbus_of_build_irq; |
| 711 | |
| 712 | regs = of_get_property(dp, "reg", NULL); |
| 713 | dp->irq_trans->data = (void *) (unsigned long) regs->phys_addr; |
| 714 | } |
| 715 | #endif /* CONFIG_SBUS */ |
| 716 | |
| 717 | |
| 718 | static unsigned int central_build_irq(struct device_node *dp, |
| 719 | unsigned int ino, |
| 720 | void *_data) |
| 721 | { |
| 722 | struct device_node *central_dp = _data; |
Grant Likely | cd4cd73 | 2010-07-22 16:04:30 -0600 | [diff] [blame] | 723 | struct platform_device *central_op = of_find_device_by_node(central_dp); |
David S. Miller | 5fce09c | 2008-12-05 00:43:03 -0800 | [diff] [blame] | 724 | struct resource *res; |
| 725 | unsigned long imap, iclr; |
| 726 | u32 tmp; |
| 727 | |
Rob Herring | 29c990d | 2018-11-16 15:06:58 -0600 | [diff] [blame] | 728 | if (of_node_name_eq(dp, "eeprom")) { |
David S. Miller | 5fce09c | 2008-12-05 00:43:03 -0800 | [diff] [blame] | 729 | res = ¢ral_op->resource[5]; |
Rob Herring | 29c990d | 2018-11-16 15:06:58 -0600 | [diff] [blame] | 730 | } else if (of_node_name_eq(dp, "zs")) { |
David S. Miller | 5fce09c | 2008-12-05 00:43:03 -0800 | [diff] [blame] | 731 | res = ¢ral_op->resource[4]; |
Rob Herring | 29c990d | 2018-11-16 15:06:58 -0600 | [diff] [blame] | 732 | } else if (of_node_name_eq(dp, "clock-board")) { |
David S. Miller | 5fce09c | 2008-12-05 00:43:03 -0800 | [diff] [blame] | 733 | res = ¢ral_op->resource[3]; |
| 734 | } else { |
| 735 | return ino; |
| 736 | } |
| 737 | |
| 738 | imap = res->start + 0x00UL; |
| 739 | iclr = res->start + 0x10UL; |
| 740 | |
| 741 | /* Set the INO state to idle, and disable. */ |
| 742 | upa_writel(0, iclr); |
| 743 | upa_readl(iclr); |
| 744 | |
| 745 | tmp = upa_readl(imap); |
| 746 | tmp &= ~0x80000000; |
| 747 | upa_writel(tmp, imap); |
| 748 | |
| 749 | return build_irq(0, iclr, imap); |
| 750 | } |
| 751 | |
| 752 | static void __init central_irq_trans_init(struct device_node *dp) |
| 753 | { |
| 754 | dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); |
| 755 | dp->irq_trans->irq_build = central_build_irq; |
| 756 | |
| 757 | dp->irq_trans->data = dp; |
| 758 | } |
| 759 | |
| 760 | struct irq_trans { |
| 761 | const char *name; |
| 762 | void (*init)(struct device_node *); |
| 763 | }; |
| 764 | |
| 765 | #ifdef CONFIG_PCI |
| 766 | static struct irq_trans __initdata pci_irq_trans_table[] = { |
| 767 | { "SUNW,sabre", sabre_irq_trans_init }, |
| 768 | { "pci108e,a000", sabre_irq_trans_init }, |
| 769 | { "pci108e,a001", sabre_irq_trans_init }, |
| 770 | { "SUNW,psycho", psycho_irq_trans_init }, |
| 771 | { "pci108e,8000", psycho_irq_trans_init }, |
| 772 | { "SUNW,schizo", schizo_irq_trans_init }, |
| 773 | { "pci108e,8001", schizo_irq_trans_init }, |
| 774 | { "SUNW,schizo+", schizo_irq_trans_init }, |
| 775 | { "pci108e,8002", schizo_irq_trans_init }, |
| 776 | { "SUNW,tomatillo", tomatillo_irq_trans_init }, |
| 777 | { "pci108e,a801", tomatillo_irq_trans_init }, |
| 778 | { "SUNW,sun4v-pci", pci_sun4v_irq_trans_init }, |
| 779 | { "pciex108e,80f0", fire_irq_trans_init }, |
| 780 | }; |
| 781 | #endif |
| 782 | |
| 783 | static unsigned int sun4v_vdev_irq_build(struct device_node *dp, |
| 784 | unsigned int devino, |
| 785 | void *_data) |
| 786 | { |
| 787 | u32 devhandle = (u32) (unsigned long) _data; |
| 788 | |
| 789 | return sun4v_build_irq(devhandle, devino); |
| 790 | } |
| 791 | |
| 792 | static void __init sun4v_vdev_irq_trans_init(struct device_node *dp) |
| 793 | { |
| 794 | const struct linux_prom64_registers *regs; |
| 795 | |
| 796 | dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); |
| 797 | dp->irq_trans->irq_build = sun4v_vdev_irq_build; |
| 798 | |
| 799 | regs = of_get_property(dp, "reg", NULL); |
| 800 | dp->irq_trans->data = (void *) (unsigned long) |
| 801 | ((regs->phys_addr >> 32UL) & 0x0fffffff); |
| 802 | } |
| 803 | |
| 804 | void __init irq_trans_init(struct device_node *dp) |
| 805 | { |
| 806 | #ifdef CONFIG_PCI |
| 807 | const char *model; |
| 808 | int i; |
| 809 | #endif |
| 810 | |
| 811 | #ifdef CONFIG_PCI |
| 812 | model = of_get_property(dp, "model", NULL); |
| 813 | if (!model) |
| 814 | model = of_get_property(dp, "compatible", NULL); |
| 815 | if (model) { |
| 816 | for (i = 0; i < ARRAY_SIZE(pci_irq_trans_table); i++) { |
| 817 | struct irq_trans *t = &pci_irq_trans_table[i]; |
| 818 | |
| 819 | if (!strcmp(model, t->name)) { |
| 820 | t->init(dp); |
| 821 | return; |
| 822 | } |
| 823 | } |
| 824 | } |
| 825 | #endif |
| 826 | #ifdef CONFIG_SBUS |
Rob Herring | 29c990d | 2018-11-16 15:06:58 -0600 | [diff] [blame] | 827 | if (of_node_name_eq(dp, "sbus") || |
| 828 | of_node_name_eq(dp, "sbi")) { |
David S. Miller | 5fce09c | 2008-12-05 00:43:03 -0800 | [diff] [blame] | 829 | sbus_irq_trans_init(dp); |
| 830 | return; |
| 831 | } |
| 832 | #endif |
Rob Herring | 29c990d | 2018-11-16 15:06:58 -0600 | [diff] [blame] | 833 | if (of_node_name_eq(dp, "fhc") && |
| 834 | of_node_name_eq(dp->parent, "central")) { |
David S. Miller | 5fce09c | 2008-12-05 00:43:03 -0800 | [diff] [blame] | 835 | central_irq_trans_init(dp); |
| 836 | return; |
| 837 | } |
Rob Herring | 29c990d | 2018-11-16 15:06:58 -0600 | [diff] [blame] | 838 | if (of_node_name_eq(dp, "virtual-devices") || |
| 839 | of_node_name_eq(dp, "niu")) { |
David S. Miller | 5fce09c | 2008-12-05 00:43:03 -0800 | [diff] [blame] | 840 | sun4v_vdev_irq_trans_init(dp); |
| 841 | return; |
| 842 | } |
| 843 | } |