1 /* $NetBSD: pcix.c,v 1.4 2026/09/28 20:41:01 rkujawa Exp $ */ 2 3 /* 4 * Copyright (c) 2012, 2014, 2024, 2026 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Radoslaw Kujawa. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 /* 32 * IBM PLB-PCIX host bridge, as found on the 440GP/440SPe/460EX 33 * 34 * XXX The bridge addresses are currently straight from 460EX include 35 */ 36 37 #include <sys/cdefs.h> 38 __KERNEL_RCSID(0, "$NetBSD: pcix.c,v 1.4 2026/09/28 20:41:01 rkujawa Exp $"); 39 40 #ifdef _KERNEL_OPT 41 #include "opt_pci.h" 42 #endif 43 44 #include <sys/param.h> 45 #include <sys/systm.h> 46 #include <sys/device.h> 47 #include <sys/extent.h> 48 #include <sys/bus.h> 49 50 #include <dev/pci/pcivar.h> 51 #include <dev/pci/pcireg.h> 52 #include <dev/pci/pciconf.h> 53 54 #include <powerpc/ibm4xx/amcc460ex.h> 55 #include <powerpc/ibm4xx/cpu.h> 56 #include <powerpc/ibm4xx/spr.h> 57 #include <powerpc/ibm4xx/dev/plbvar.h> 58 #include <powerpc/ibm4xx/pci_machdep.h> 59 #include <powerpc/pci_machdep.h> 60 61 /* Internal register offsets (relative to the internal register base) */ 62 #define PCIX0_POM0LAL 0x68 63 #define PCIX0_POM0LAH 0x6c 64 #define PCIX0_POM0SA 0x70 65 #define PCIX0_POM0PCIAL 0x74 66 #define PCIX0_POM0PCIAH 0x78 67 #define PCIX0_POM1LAL 0x7c 68 #define PCIX0_POM1LAH 0x80 69 #define PCIX0_POM1SA 0x84 70 #define PCIX0_POM1PCIAL 0x88 71 #define PCIX0_POM1PCIAH 0x8c 72 #define PCIX0_POM2SA 0x90 73 #define PCIX0_PIM0SAL 0x98 74 #define PCIX0_PIM0LAL 0x9c 75 #define PCIX0_PIM0LAH 0xa0 76 #define PCIX0_PIM1SA 0xa4 77 #define PCIX0_PIM1LAL 0xa8 78 #define PCIX0_PIM1LAH 0xac 79 #define PCIX0_PIM2SAL 0xb0 80 #define PCIX0_PIM0SAH 0xf8 81 82 #define PIM_SA_ENABLE 0x1 83 #define PIM_SA_PREFETCH 0x2 /* inbound read prefetch (bit 1) */ 84 85 #define PCIC_CFGADDR 0 86 #define PCIC_CFGDATA 4 87 88 static int pcix_match(device_t, cfdata_t, void *); 89 static void pcix_attach(device_t, device_t, void *); 90 static int pcix_print(void *, const char *); 91 92 CFATTACH_DECL_NEW(pcix, 0, pcix_match, pcix_attach, NULL, NULL); 93 94 static pcireg_t pcix_conf_read(void *, pcitag_t, int); 95 static void pcix_conf_write(void *, pcitag_t, int, pcireg_t); 96 static void pcix_attach_hook(device_t, device_t, 97 struct pcibus_attach_args *); 98 static int pcix_conf_hook(void *, int, int, int, pcireg_t); 99 100 /* CFGADDR/CFGDATA pair */ 101 static struct powerpc_bus_space pcix_cfg_tag = { 102 _BUS_SPACE_LITTLE_ENDIAN | _BUS_SPACE_MEM_TYPE, 103 0x00000000, /* offset */ 104 AMCC460EX_PCIX0_CFG_PLBA, /* extent base */ 105 AMCC460EX_PCIX0_CFG_PLBA + 8, /* extent limit */ 106 }; 107 108 /* Internal (window) registers */ 109 static struct powerpc_bus_space pcix_reg_tag = { 110 _BUS_SPACE_LITTLE_ENDIAN | _BUS_SPACE_MEM_TYPE, 111 0x00000000, 112 AMCC460EX_PCIX0_REGS_PLBA, 113 AMCC460EX_PCIX0_REGS_PLBA + 0x100, 114 }; 115 116 /* PCI I/O: bus 0x0000-0xffff -> PLB I/O window */ 117 static struct powerpc_bus_space pcix_io_tag = { 118 _BUS_SPACE_LITTLE_ENDIAN | _BUS_SPACE_IO_TYPE, 119 AMCC460EX_PCIX0_IO_PLBA, /* offset */ 120 0x00000000, /* extent base */ 121 0x00010000, /* extent limit */ 122 }; 123 124 /* 125 * PCI memory: bus addr = low 32 bits of PLB addr. 126 */ 127 static struct powerpc_bus_space pcix_mem_tag = { 128 _BUS_SPACE_LITTLE_ENDIAN | _BUS_SPACE_MEM_TYPE, 129 0x00000000, 130 AMCC460EX_PCIX0_MEM_BASE, 131 AMCC460EX_PCIX0_PMEM_BASE + AMCC460EX_PCIX0_PMEM_SIZE, 132 }; 133 134 static bus_space_handle_t pcix_cfg_ioh; 135 static bus_space_handle_t pcix_reg_ioh; 136 137 static struct genppc_pci_chipset pcix_chipset = { 138 .pc_conf_v = NULL, 139 .pc_attach_hook = pcix_attach_hook, 140 .pc_bus_maxdevs = ibm4xx_pci_bus_maxdevs, 141 .pc_make_tag = ibm4xx_pci_make_tag, 142 .pc_conf_read = pcix_conf_read, 143 .pc_conf_write = pcix_conf_write, 144 145 .pc_intr_v = &pcix_chipset, 146 .pc_intr_map = genppc_pci_intr_map, 147 .pc_intr_string = genppc_pci_intr_string, 148 .pc_intr_evcnt = genppc_pci_intr_evcnt, 149 .pc_intr_establish = genppc_pci_intr_establish, 150 .pc_intr_disestablish = genppc_pci_intr_disestablish, 151 .pc_intr_setattr = ibm4xx_pci_intr_setattr, 152 .pc_intr_type = genppc_pci_intr_type, 153 .pc_intr_alloc = genppc_pci_intr_alloc, 154 .pc_intr_release = genppc_pci_intr_release, 155 .pc_intx_alloc = genppc_pci_intx_alloc, 156 157 .pc_msi_v = &pcix_chipset, 158 GENPPC_PCI_MSI_INITIALIZER, 159 160 .pc_msix_v = &pcix_chipset, 161 GENPPC_PCI_MSIX_INITIALIZER, 162 163 .pc_conf_interrupt = ibm4xx_pci_conf_interrupt, 164 .pc_decompose_tag = ibm4xx_pci_decompose_tag, 165 .pc_conf_hook = pcix_conf_hook, 166 }; 167 168 static void 169 pcix_attach_hook(device_t parent, device_t self, 170 struct pcibus_attach_args *pba) 171 { 172 } 173 174 static int 175 pcix_conf_hook(void *v, int bus, int dev, int func, pcireg_t id) 176 { 177 178 return PCI_CONF_DEFAULT; 179 } 180 181 /* 182 * Translate the tag 183 */ 184 static uint32_t 185 pcix_cfg_addr(pcitag_t tag, int reg) 186 { 187 uint32_t addr = (tag & 0x00ffff00) | (reg & 0xfc); 188 189 if (tag & 0x00ff0000) /* bus number != 0 -> Type 1 */ 190 addr |= 1; 191 return addr; 192 } 193 194 static pcireg_t 195 pcix_conf_read(void *v, pcitag_t tag, int reg) 196 { 197 pcireg_t data; 198 199 if ((unsigned int)reg >= PCI_CONF_SIZE) 200 return (pcireg_t) -1; 201 202 bus_space_write_4(&pcix_cfg_tag, pcix_cfg_ioh, PCIC_CFGADDR, 203 pcix_cfg_addr(tag, reg)); 204 data = bus_space_read_4(&pcix_cfg_tag, pcix_cfg_ioh, PCIC_CFGDATA); 205 bus_space_write_4(&pcix_cfg_tag, pcix_cfg_ioh, PCIC_CFGADDR, 0); 206 return data; 207 } 208 209 static void 210 pcix_conf_write(void *v, pcitag_t tag, int reg, pcireg_t data) 211 { 212 213 if ((unsigned int)reg >= PCI_CONF_SIZE) 214 return; 215 216 bus_space_write_4(&pcix_cfg_tag, pcix_cfg_ioh, PCIC_CFGADDR, 217 pcix_cfg_addr(tag, reg)); 218 bus_space_write_4(&pcix_cfg_tag, pcix_cfg_ioh, PCIC_CFGDATA, data); 219 bus_space_write_4(&pcix_cfg_tag, pcix_cfg_ioh, PCIC_CFGADDR, 0); 220 } 221 222 #define PCIX_REG_WRITE(reg, val) \ 223 bus_space_write_4(&pcix_reg_tag, pcix_reg_ioh, (reg), (val)) 224 225 static void 226 pcix_setup_windows(void) 227 { 228 229 /* POM0: PLB -> PCI memory, 128MB */ 230 PCIX_REG_WRITE(PCIX0_POM0SA, 0); /* disable while */ 231 PCIX_REG_WRITE(PCIX0_POM0LAL, AMCC460EX_PCIX0_MEM_BASE); 232 PCIX_REG_WRITE(PCIX0_POM0LAH, AMCC460EX_PCIX0_MEM_PLBA_H); 233 PCIX_REG_WRITE(PCIX0_POM0PCIAL, AMCC460EX_PCIX0_MEM_BASE); 234 PCIX_REG_WRITE(PCIX0_POM0PCIAH, 0); 235 PCIX_REG_WRITE(PCIX0_POM0SA, ~(0x08000000 - 1) | 1); 236 237 /* POM1: PLB -> PCI prefetchable memory, identity mapped, 256MB */ 238 PCIX_REG_WRITE(PCIX0_POM1SA, 0); /* disable while */ 239 PCIX_REG_WRITE(PCIX0_POM1LAL, AMCC460EX_PCIX0_PMEM_BASE); 240 PCIX_REG_WRITE(PCIX0_POM1LAH, AMCC460EX_PCIX0_PMEM_PLBA_H); 241 PCIX_REG_WRITE(PCIX0_POM1PCIAL, AMCC460EX_PCIX0_PMEM_BASE); 242 PCIX_REG_WRITE(PCIX0_POM1PCIAH, 0); 243 PCIX_REG_WRITE(PCIX0_POM1SA, ~(AMCC460EX_PCIX0_PMEM_SIZE - 1) | 1); 244 245 /* POM2 unused */ 246 PCIX_REG_WRITE(PCIX0_POM2SA, 0); 247 248 /* PIM0: PCI 0 -> PLB 0, 2GB (covers all of RAM) */ 249 PCIX_REG_WRITE(PCIX0_PIM0SAL, 0); /* disable while */ 250 PCIX_REG_WRITE(PCIX0_PIM0LAL, 0); 251 PCIX_REG_WRITE(PCIX0_PIM0LAH, 0); 252 PCIX_REG_WRITE(PCIX0_PIM0SAH, 0xffffffff); 253 /* 254 * Enable inbound read prefetch 255 */ 256 PCIX_REG_WRITE(PCIX0_PIM0SAL, 257 ~(0x80000000U - 1) | PIM_SA_ENABLE | PIM_SA_PREFETCH); 258 259 /* PIM1/PIM2 unused */ 260 PCIX_REG_WRITE(PCIX0_PIM1SA, 0); 261 PCIX_REG_WRITE(PCIX0_PIM2SAL, 0); 262 } 263 264 static int 265 pcix_match(device_t parent, cfdata_t cf, void *aux) 266 { 267 struct plb_attach_args *paa = aux; 268 269 if (strcmp(paa->plb_name, cf->cf_name) != 0) 270 return 0; 271 272 return 1; 273 } 274 275 static void 276 pcix_attach(device_t parent, device_t self, void *aux) 277 { 278 struct plb_attach_args *paa = aux; 279 struct pcibus_attach_args pba; 280 pci_chipset_tag_t pc = &pcix_chipset; 281 282 aprint_normal(": PLB-PCIX host bridge\n"); 283 284 if (bus_space_init(&pcix_cfg_tag, "pcixcfg", NULL, 0) || 285 bus_space_map(&pcix_cfg_tag, AMCC460EX_PCIX0_CFG_PLBA, 8, 0, 286 &pcix_cfg_ioh)) 287 panic("pcix_attach: can't map config registers"); 288 if (bus_space_init(&pcix_reg_tag, "pcixreg", NULL, 0) || 289 bus_space_map(&pcix_reg_tag, AMCC460EX_PCIX0_REGS_PLBA, 0x100, 0, 290 &pcix_reg_ioh)) 291 panic("pcix_attach: can't map bridge registers"); 292 if (bus_space_init(&pcix_io_tag, "pcixio", NULL, 0)) 293 panic("pcix_attach: can't init IO tag"); 294 if (bus_space_init(&pcix_mem_tag, "pcixmem", NULL, 0)) 295 panic("pcix_attach: can't init MEM tag"); 296 297 pcix_setup_windows(); 298 299 #ifdef PCI_NETBSD_CONFIGURE 300 struct pciconf_resources *pcires = pciconf_resource_init(); 301 302 pciconf_resource_add(pcires, PCICONF_RESOURCE_IO, 303 0x1000, 0x10000 - 0x1000); 304 pciconf_resource_add(pcires, PCICONF_RESOURCE_MEM, 305 AMCC460EX_PCIX0_MEM_BASE, AMCC460EX_PCIX0_MEM_SIZE); 306 pciconf_resource_add(pcires, PCICONF_RESOURCE_PREFETCHABLE_MEM, 307 AMCC460EX_PCIX0_PMEM_BASE, AMCC460EX_PCIX0_PMEM_MAP); 308 309 aprint_verbose_dev(self, "mem 0x%08x-0x%08x, prefetchable mem " 310 "0x%08x-0x%08x\n", 311 AMCC460EX_PCIX0_MEM_BASE, 312 AMCC460EX_PCIX0_MEM_BASE + AMCC460EX_PCIX0_MEM_SIZE - 1, 313 AMCC460EX_PCIX0_PMEM_BASE, 314 AMCC460EX_PCIX0_PMEM_BASE + AMCC460EX_PCIX0_PMEM_MAP - 1); 315 316 pci_configure_bus(pc, pcires, 0, 32); 317 pciconf_resource_fini(pcires); 318 #endif 319 320 pba.pba_iot = &pcix_io_tag; 321 pba.pba_memt = &pcix_mem_tag; 322 pba.pba_dmat = paa->plb_dmat; 323 pba.pba_dmat64 = NULL; 324 pba.pba_pc = pc; 325 pba.pba_bus = 0; 326 pba.pba_bridgetag = NULL; 327 pba.pba_flags = PCI_FLAGS_MEM_OKAY | PCI_FLAGS_IO_OKAY; 328 config_found(self, &pba, pcix_print, CFARGS_NONE); 329 } 330 331 static int 332 pcix_print(void *aux, const char *p) 333 { 334 335 if (p == NULL) 336 return UNCONF; 337 return QUIET; 338 } 339