1 /* $NetBSD: uninorth.c,v 1.20 2018/03/22 21:30:34 macallan Exp $ */ 2 3 /*- 4 * Copyright (c) 2000 Tsubai Masanari. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: uninorth.c,v 1.20 2018/03/22 21:30:34 macallan Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/device.h> 34 #include <sys/systm.h> 35 36 #include <dev/pci/pcivar.h> 37 #include <dev/ofw/openfirm.h> 38 #include <dev/ofw/ofw_pci.h> 39 #include <powerpc/oea/cpufeat.h> 40 41 #include <machine/autoconf.h> 42 #include <machine/pio.h> 43 44 struct uninorth_softc { 45 device_t sc_dev; 46 struct genppc_pci_chipset sc_pc; 47 struct powerpc_bus_space sc_iot; 48 struct powerpc_bus_space sc_memt; 49 }; 50 51 static void uninorth_attach(device_t, device_t, void *); 52 static int uninorth_match(device_t, cfdata_t, void *); 53 54 static pcireg_t uninorth_conf_read(void *, pcitag_t, int); 55 static void uninorth_conf_write(void *, pcitag_t, int, pcireg_t); 56 static pcireg_t uninorth_conf_read_v3(void *, pcitag_t, int); 57 static void uninorth_conf_write_v3(void *, pcitag_t, int, pcireg_t); 58 59 CFATTACH_DECL_NEW(uninorth, sizeof(struct uninorth_softc), 60 uninorth_match, uninorth_attach, NULL, NULL); 61 62 static int 63 uninorth_match(device_t parent, cfdata_t cf, void *aux) 64 { 65 struct confargs *ca = aux; 66 char compat[32]; 67 68 if (strcmp(ca->ca_name, "pci") != 0) 69 return 0; 70 71 memset(compat, 0, sizeof(compat)); 72 OF_getprop(ca->ca_node, "compatible", compat, sizeof(compat)); 73 if (strcmp(compat, "uni-north") != 0 && 74 strcmp(compat, "u3-agp") != 0 && 75 strcmp(compat, "u4-pcie") != 0) 76 return 0; 77 78 return 1; 79 } 80 81 static void 82 uninorth_attach(device_t parent, device_t self, void *aux) 83 { 84 struct uninorth_softc *sc = device_private(self); 85 pci_chipset_tag_t pc = &sc->sc_pc; 86 struct confargs *ca = aux; 87 struct pcibus_attach_args pba; 88 int len, child, node = ca->ca_node; 89 uint32_t reg[2], busrange[2]; 90 char compat[32]; 91 int ver; 92 struct ranges { 93 uint32_t pci_hi, pci_mid, pci_lo; 94 uint32_t host; 95 uint32_t size_hi, size_lo; 96 } ranges[6], *rp = ranges; 97 98 printf("\n"); 99 sc->sc_dev = self; 100 101 memset(compat, 0, sizeof(compat)); 102 OF_getprop(ca->ca_node, "compatible", compat, sizeof(compat)); 103 if (strcmp(compat, "u3-agp") == 0) 104 ver = 3; 105 else if (strcmp(compat, "u4-pcie") == 0) 106 ver = 4; 107 else 108 ver = 0; 109 110 /* UniNorth address */ 111 if (OF_getprop(node, "reg", reg, sizeof(reg)) < 8) 112 return; 113 114 /* PCI bus number */ 115 if (OF_getprop(node, "bus-range", busrange, sizeof(busrange)) != 8) 116 return; 117 118 memset(&sc->sc_iot, 0, sizeof(sc->sc_iot)); 119 120 /* find i/o tag */ 121 len = OF_getprop(node, "ranges", ranges, sizeof(ranges)); 122 if (len == -1) 123 return; 124 while (len >= sizeof(ranges[0])) { 125 if ((rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) == 126 OFW_PCI_PHYS_HI_SPACE_IO) { 127 sc->sc_iot.pbs_base = rp->host; 128 sc->sc_iot.pbs_limit = rp->host + rp->size_lo; 129 break; 130 } 131 len -= sizeof(ranges[0]); 132 rp++; 133 } 134 135 /* XXX enable gmac ethernet */ 136 for (child = OF_child(node); child; child = OF_peer(child)) { 137 volatile int *gmac_gbclock_en = (void *)0xf8000020; 138 139 memset(compat, 0, sizeof(compat)); 140 OF_getprop(child, "compatible", compat, sizeof(compat)); 141 if (strcmp(compat, "gmac") == 0) 142 *gmac_gbclock_en |= 0x02; 143 } 144 145 sc->sc_iot.pbs_flags = _BUS_SPACE_LITTLE_ENDIAN|_BUS_SPACE_IO_TYPE; 146 sc->sc_iot.pbs_offset = 0; 147 if (ofwoea_map_space(RANGE_TYPE_PCI, RANGE_IO, node, &sc->sc_iot, 148 "uninorth io-space") != 0) 149 panic("Can't init uninorth io tag"); 150 151 memset(&sc->sc_memt, 0, sizeof(sc->sc_memt)); 152 sc->sc_memt.pbs_flags = _BUS_SPACE_LITTLE_ENDIAN|_BUS_SPACE_MEM_TYPE; 153 sc->sc_memt.pbs_base = 0x00000000; 154 if (ofwoea_map_space(RANGE_TYPE_PCI, RANGE_MEM, node, &sc->sc_memt, 155 "uninorth mem-space") != 0) 156 panic("Can't init uninorth mem tag"); 157 158 macppc_pci_get_chipset_tag(pc); 159 pc->pc_node = node; 160 pc->pc_bus = busrange[0]; 161 pc->pc_iot = &sc->sc_iot; 162 pc->pc_memt = &sc->sc_memt; 163 164 if (ver < 3) { 165 pc->pc_addr = oea_mapiodev(reg[0] + 0x800000, 4); 166 pc->pc_data = oea_mapiodev(reg[0] + 0xc00000, 8); 167 pc->pc_conf_read = uninorth_conf_read; 168 pc->pc_conf_write = uninorth_conf_write; 169 } else { 170 pc->pc_addr = oea_mapiodev(reg[1] + 0x800000, 4); 171 pc->pc_data = oea_mapiodev(reg[1] + 0xc00000, 8); 172 pc->pc_conf_read = uninorth_conf_read_v3; 173 pc->pc_conf_write = uninorth_conf_write_v3; 174 } 175 176 memset(&pba, 0, sizeof(pba)); 177 pba.pba_memt = pc->pc_memt; 178 pba.pba_iot = pc->pc_iot; 179 pba.pba_dmat = &pci_bus_dma_tag; 180 pba.pba_dmat64 = NULL; 181 pba.pba_bus = pc->pc_bus; 182 pba.pba_bridgetag = NULL; 183 pba.pba_pc = pc; 184 pba.pba_flags = PCI_FLAGS_IO_OKAY | PCI_FLAGS_MEM_OKAY; 185 186 config_found_ia(self, "pcibus", &pba, pcibusprint); 187 } 188 189 static pcireg_t 190 uninorth_conf_read(void *cookie, pcitag_t tag, int reg) 191 { 192 pci_chipset_tag_t pc = cookie; 193 int32_t *daddr = pc->pc_data; 194 pcireg_t data; 195 int bus, dev, func, s; 196 uint32_t x; 197 198 if ((unsigned int)reg >= PCI_CONF_SIZE) 199 return (pcireg_t) -1; 200 201 /* UniNorth seems to have a 64bit data port */ 202 if (reg & 0x04) 203 daddr++; 204 205 pci_decompose_tag(pc, tag, &bus, &dev, &func); 206 207 /* 208 * bandit's minimum device number of the first bus is 11. 209 * So we behave as if there is no device when dev < 11. 210 */ 211 if (func > 7) 212 panic("pci_conf_read: func > 7"); 213 214 if (bus == pc->pc_bus) { 215 if (dev < 11) 216 return 0xffffffff; 217 x = (1 << dev) | (func << 8) | reg; 218 } else 219 x = tag | reg | 1; 220 221 s = splhigh(); 222 223 out32rb(pc->pc_addr, x); 224 in32rb(pc->pc_addr); 225 data = 0xffffffff; 226 if (!badaddr(daddr, 4)) 227 data = in32rb(daddr); 228 out32rb(pc->pc_addr, 0); 229 in32rb(pc->pc_addr); 230 splx(s); 231 232 return data; 233 } 234 235 static void 236 uninorth_conf_write(void *cookie, pcitag_t tag, int reg, pcireg_t data) 237 { 238 pci_chipset_tag_t pc = cookie; 239 int32_t *daddr = pc->pc_data; 240 int bus, dev, func, s; 241 uint32_t x; 242 243 if ((unsigned int)reg >= PCI_CONF_SIZE) 244 return; 245 246 /* UniNorth seems to have a 64bit data port */ 247 if (reg & 0x04) 248 daddr++; 249 250 pci_decompose_tag(pc, tag, &bus, &dev, &func); 251 252 if (func > 7) 253 panic("pci_conf_write: func > 7"); 254 255 if (bus == pc->pc_bus) { 256 if (dev < 11) 257 panic("pci_conf_write: dev < 11"); 258 x = (1 << dev) | (func << 8) | reg; 259 } else 260 x = tag | reg | 1; 261 262 s = splhigh(); 263 264 out32rb(pc->pc_addr, x); 265 in32rb(pc->pc_addr); 266 out32rb(daddr, data); 267 out32rb(pc->pc_addr, 0); 268 in32rb(pc->pc_addr); 269 270 splx(s); 271 } 272 273 static pcireg_t 274 uninorth_conf_read_v3(void *cookie, pcitag_t tag, int reg) 275 { 276 pci_chipset_tag_t pc = cookie; 277 int32_t *daddr = pc->pc_data; 278 pcireg_t data; 279 int bus, dev, func, s; 280 uint32_t x; 281 282 if ((unsigned int)reg >= PCI_CONF_SIZE) 283 return (pcireg_t) -1; 284 285 /* UniNorth seems to have a 64bit data port */ 286 if (reg & 0x04) 287 daddr++; 288 289 pci_decompose_tag(pc, tag, &bus, &dev, &func); 290 291 if (bus == 0) { 292 if (dev < 11) return 0xffffffff; 293 x = (1 << dev) | (func << 8) | reg; 294 } else 295 x = (bus << 16) | (dev << 11) | (func << 8) | (reg & 0xfc) | 1; 296 /* Set extended register bits */ 297 x |= (reg >> 8) << 28; 298 299 s = splhigh(); 300 301 out32rb(pc->pc_addr, x); 302 in32rb(pc->pc_addr); 303 data = 0xffffffff; 304 if (!badaddr(daddr, 4)) { 305 data = in32rb(daddr); 306 } 307 out32rb(pc->pc_addr, 0); 308 in32rb(pc->pc_addr); 309 splx(s); 310 311 return data; 312 } 313 314 static void 315 uninorth_conf_write_v3(void *cookie, pcitag_t tag, int reg, pcireg_t data) 316 { 317 pci_chipset_tag_t pc = cookie; 318 int32_t *daddr = pc->pc_data; 319 int bus, dev, func, s; 320 uint32_t x; 321 322 if ((unsigned int)reg >= PCI_CONF_SIZE) 323 return; 324 325 /* UniNorth seems to have a 64bit data port */ 326 if (reg & 0x04) 327 daddr++; 328 329 pci_decompose_tag(pc, tag, &bus, &dev, &func); 330 331 if (bus == 0) { 332 if (dev < 11) return; 333 x = (1 << dev) | (func << 8) | reg; 334 } else 335 x = (bus << 16) | (dev << 11) | (func << 8) | (reg & 0xfc) | 1; 336 /* Set extended register bits */ 337 x |= (reg >> 8) << 28; 338 339 s = splhigh(); 340 341 out32rb(pc->pc_addr, x); 342 in32rb(pc->pc_addr); 343 out32rb(daddr, data); 344 out32rb(pc->pc_addr, 0); 345 in32rb(pc->pc_addr); 346 347 splx(s); 348 } 349