obio.c revision 1.67
1218885Sdim/* $NetBSD: obio.c,v 1.67 2005/06/30 17:03:54 drochner Exp $ */ 2218885Sdim 3218885Sdim/*- 4218885Sdim * Copyright (c) 1997,1998 The NetBSD Foundation, Inc. 5218885Sdim * All rights reserved. 6218885Sdim * 7218885Sdim * This code is derived from software contributed to The NetBSD Foundation 8218885Sdim * by Paul Kranenburg. 9218885Sdim * 10218885Sdim * Redistribution and use in source and binary forms, with or without 11218885Sdim * modification, are permitted provided that the following conditions 12218885Sdim * are met: 13218885Sdim * 1. Redistributions of source code must retain the above copyright 14218885Sdim * notice, this list of conditions and the following disclaimer. 15218885Sdim * 2. Redistributions in binary form must reproduce the above copyright 16218885Sdim * notice, this list of conditions and the following disclaimer in the 17223017Sdim * documentation and/or other materials provided with the distribution. 18223017Sdim * 3. All advertising materials mentioning features or use of this software 19223017Sdim * must display the following acknowledgement: 20218885Sdim * This product includes software developed by the NetBSD 21218885Sdim * Foundation, Inc. and its contributors. 22218885Sdim * 4. Neither the name of The NetBSD Foundation nor the names of its 23218885Sdim * contributors may be used to endorse or promote products derived 24218885Sdim * from this software without specific prior written permission. 25218885Sdim * 26218885Sdim * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27218885Sdim * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28218885Sdim * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29218885Sdim * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30218885Sdim * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31218885Sdim * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32218885Sdim * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33218885Sdim * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34218885Sdim * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35218885Sdim * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36218885Sdim * POSSIBILITY OF SUCH DAMAGE. 37218885Sdim */ 38218885Sdim 39218885Sdim#include <sys/cdefs.h> 40218885Sdim__KERNEL_RCSID(0, "$NetBSD: obio.c,v 1.67 2005/06/30 17:03:54 drochner Exp $"); 41218885Sdim 42218885Sdim#include "locators.h" 43218885Sdim 44218885Sdim#include <sys/param.h> 45218885Sdim#include <sys/systm.h> 46218885Sdim#include <sys/device.h> 47223017Sdim#include <sys/malloc.h> 48223017Sdim 49223017Sdim#ifdef DEBUG 50223017Sdim#include <sys/proc.h> 51223017Sdim#include <sys/syslog.h> 52223017Sdim#endif 53223017Sdim 54223017Sdim#include <uvm/uvm_extern.h> 55223017Sdim 56223017Sdim#include <machine/bus.h> 57223017Sdim#include <sparc/dev/sbusvar.h> 58223017Sdim#include <machine/autoconf.h> 59223017Sdim#include <machine/oldmon.h> 60223017Sdim#include <machine/cpu.h> 61223017Sdim#include <machine/ctlreg.h> 62223017Sdim#include <sparc/sparc/asm.h> 63223017Sdim#include <sparc/sparc/vaddrs.h> 64223017Sdim#include <sparc/sparc/cpuvar.h> 65223017Sdim 66223017Sdimstruct obio4_softc { 67223017Sdim struct device sc_dev; /* base device */ 68223017Sdim bus_space_tag_t sc_bustag; /* parent bus tag */ 69223017Sdim bus_dma_tag_t sc_dmatag; /* parent bus DMA tag */ 70223017Sdim}; 71223017Sdim 72223017Sdimunion obio_softc { 73223017Sdim struct device sc_dev; /* base device */ 74223017Sdim struct obio4_softc sc_obio; /* sun4 obio */ 75218885Sdim struct sbus_softc sc_sbus; /* sun4m obio is another sbus slot */ 76218885Sdim}; 77218885Sdim 78218885Sdim 79218885Sdim/* autoconfiguration driver */ 80218885Sdimstatic int obiomatch __P((struct device *, struct cfdata *, void *)); 81218885Sdimstatic void obioattach __P((struct device *, struct device *, void *)); 82218885Sdim 83218885SdimCFATTACH_DECL(obio, sizeof(union obio_softc), 84218885Sdim obiomatch, obioattach, NULL, NULL); 85218885Sdim 86218885Sdimstatic int obio_attached; 87218885Sdim 88218885Sdim/* 89218885Sdim * This `obio4_busattachargs' data structure only exists to pass down 90218885Sdim * to obiosearch() the name of a device that must be configured early. 91218885Sdim */ 92218885Sdimstruct obio4_busattachargs { 93218885Sdim struct mainbus_attach_args *ma; 94218885Sdim const char *name; 95218885Sdim}; 96218885Sdim 97218885Sdim#if defined(SUN4) 98218885Sdimstatic int obioprint __P((void *, const char *)); 99218885Sdimstatic int obiosearch __P((struct device *, struct cfdata *, 100218885Sdim const locdesc_t *, void *)); 101218885Sdimstatic paddr_t obio_bus_mmap __P((bus_space_tag_t, bus_addr_t, off_t, 102218885Sdim int, int)); 103218885Sdimstatic int _obio_bus_map __P((bus_space_tag_t, bus_addr_t, 104223017Sdim bus_size_t, int, 105223017Sdim vaddr_t, bus_space_handle_t *)); 106223017Sdim 107223017Sdim/* There's at most one obio bus, so we can allocate the bus tag statically */ 108223017Sdimstatic struct sparc_bus_space_tag obio_space_tag; 109223017Sdim#endif 110223017Sdim 111223017Sdim/* 112223017Sdim * Translate obio `interrupts' property value to processor IPL (see sbus.c) 113223017Sdim * Apparently, the `interrupts' property on obio devices is just 114223017Sdim * the processor IPL. 115223017Sdim */ 116223017Sdimstatic int intr_obio2ipl[] = { 117223017Sdim 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 118223017Sdim}; 119223017Sdim 120223017Sdimint 121218885Sdimobiomatch(parent, cf, aux) 122218885Sdim struct device *parent; 123218885Sdim struct cfdata *cf; 124218885Sdim void *aux; 125218885Sdim{ 126218885Sdim struct mainbus_attach_args *ma = aux; 127218885Sdim 128218885Sdim if (obio_attached) 129218885Sdim return 0; 130218885Sdim 131218885Sdim return (strcmp(cf->cf_name, ma->ma_name) == 0); 132218885Sdim} 133218885Sdim 134218885Sdimvoid 135218885Sdimobioattach(parent, self, aux) 136218885Sdim struct device *parent, *self; 137218885Sdim void *aux; 138218885Sdim{ 139218885Sdim struct mainbus_attach_args *ma = aux; 140218885Sdim 141218885Sdim obio_attached = 1; 142218885Sdim 143218885Sdim printf("\n"); 144218885Sdim 145218885Sdim if (CPU_ISSUN4) { 146218885Sdim#if defined(SUN4) 147218885Sdim struct obio4_softc *sc = &((union obio_softc *)self)->sc_obio; 148218885Sdim struct obio4_busattachargs oa; 149218885Sdim const char *const *cpp; 150218885Sdim static const char *const special4[] = { 151218885Sdim /* find these first */ 152218885Sdim "timer", 153218885Sdim "dma", /* need this before `esp', if any */ 154218885Sdim NULL 155218885Sdim }; 156218885Sdim 157218885Sdim sc->sc_bustag = ma->ma_bustag; 158218885Sdim sc->sc_dmatag = ma->ma_dmatag; 159218885Sdim 160218885Sdim memcpy(&obio_space_tag, sc->sc_bustag, sizeof(obio_space_tag)); 161218885Sdim obio_space_tag.cookie = sc; 162218885Sdim obio_space_tag.parent = sc->sc_bustag; 163218885Sdim obio_space_tag.sparc_bus_map = _obio_bus_map; 164218885Sdim obio_space_tag.sparc_bus_mmap = obio_bus_mmap; 165218885Sdim 166218885Sdim oa.ma = ma; 167218885Sdim 168218885Sdim /* Find all `early' obio devices */ 169218885Sdim for (cpp = special4; *cpp != NULL; cpp++) { 170218885Sdim oa.name = *cpp; 171218885Sdim config_search_ia(obiosearch, self, "obio", &oa); 172218885Sdim } 173218885Sdim 174218885Sdim /* Find all other obio devices */ 175218885Sdim oa.name = NULL; 176218885Sdim config_search_ia(obiosearch, self, "obio", &oa); 177218885Sdim#endif 178218885Sdim return; 179218885Sdim } else if (CPU_ISSUN4M) { 180218885Sdim /* 181218885Sdim * Attach the on-board I/O bus at on a sun4m. 182218885Sdim * In this case we treat the obio bus as another sbus slot. 183218885Sdim */ 184218885Sdim struct sbus_softc *sc = &((union obio_softc *)self)->sc_sbus; 185218885Sdim 186223017Sdim static const char *const special4m[] = { 187223017Sdim /* find these first */ 188223017Sdim "eeprom", 189223017Sdim "counter", 190223017Sdim#if 0 /* Not all sun4m's have an `auxio' */ 191223017Sdim "auxio", 192223017Sdim#endif 193223017Sdim "", 194223017Sdim /* place device to ignore here */ 195223017Sdim "interrupt", 196223017Sdim NULL 197223017Sdim }; 198223017Sdim 199223017Sdim sc->sc_bustag = ma->ma_bustag; 200223017Sdim sc->sc_dmatag = ma->ma_dmatag; 201223017Sdim sc->sc_intr2ipl = intr_obio2ipl; 202223017Sdim 203223017Sdim sbus_attach_common(sc, "obio", ma->ma_node, special4m); 204223017Sdim } else { 205223017Sdim printf("obio on this machine?\n"); 206223017Sdim } 207223017Sdim} 208223017Sdim 209223017Sdim#if defined(SUN4) 210223017Sdimint 211223017Sdimobioprint(args, busname) 212223017Sdim void *args; 213223017Sdim const char *busname; 214223017Sdim{ 215223017Sdim union obio_attach_args *uoba = args; 216223017Sdim struct obio4_attach_args *oba = &uoba->uoba_oba4; 217223017Sdim 218223017Sdim aprint_normal(" addr 0x%lx", (u_long)BUS_ADDR_PADDR(oba->oba_paddr)); 219223017Sdim if (oba->oba_pri != -1) 220223017Sdim aprint_normal(" level %d", oba->oba_pri); 221223017Sdim 222223017Sdim return (UNCONF); 223223017Sdim} 224223017Sdim 225223017Sdimint 226223017Sdim_obio_bus_map(t, ba, size, flags, va, hp) 227223017Sdim bus_space_tag_t t; 228223017Sdim bus_addr_t ba; 229223017Sdim bus_size_t size; 230223017Sdim int flags; 231223017Sdim vaddr_t va; 232223017Sdim bus_space_handle_t *hp; 233223017Sdim{ 234223017Sdim 235223017Sdim if ((flags & OBIO_BUS_MAP_USE_ROM) != 0 && 236223017Sdim obio_find_rom_map(ba, size, hp) == 0) 237223017Sdim return (0); 238223017Sdim 239223017Sdim return (bus_space_map2(t->parent, ba, size, flags, va, hp)); 240223017Sdim} 241223017Sdim 242223017Sdimpaddr_t 243223017Sdimobio_bus_mmap(t, ba, off, prot, flags) 244223017Sdim bus_space_tag_t t; 245223017Sdim bus_addr_t ba; 246223017Sdim off_t off; 247223017Sdim int prot; 248223017Sdim int flags; 249223017Sdim{ 250223017Sdim 251223017Sdim return (bus_space_mmap(t->parent, ba, off, prot, flags)); 252223017Sdim} 253223017Sdim 254223017Sdimint 255223017Sdimobiosearch(parent, cf, ldesc, aux) 256223017Sdim struct device *parent; 257223017Sdim struct cfdata *cf; 258223017Sdim const locdesc_t *ldesc; 259223017Sdim void *aux; 260223017Sdim{ 261223017Sdim struct obio4_busattachargs *oap = aux; 262223017Sdim union obio_attach_args uoba; 263223017Sdim struct obio4_attach_args *oba = &uoba.uoba_oba4; 264223017Sdim int addr; 265223017Sdim 266223017Sdim /* Check whether we're looking for a specifically named device */ 267223017Sdim if (oap->name != NULL && strcmp(oap->name, cf->cf_name) != 0) 268223017Sdim return (0); 269223017Sdim 270223017Sdim /* 271223017Sdim * Avoid sun4m entries which don't have valid PAs. 272223017Sdim * no point in even probing them. 273223017Sdim */ 274223017Sdim addr = cf->cf_loc[OBIOCF_ADDR]; 275223017Sdim if (addr == -1) 276223017Sdim return (0); 277223017Sdim 278223017Sdim /* 279223017Sdim * On the 4/100 obio addresses must be mapped at 280223017Sdim * 0x0YYYYYYY, but alias higher up (we avoid the 281223017Sdim * alias condition because it causes pmap difficulties) 282223017Sdim * XXX: We also assume that 4/[23]00 obio addresses 283223017Sdim * must be 0xZYYYYYYY, where (Z != 0) 284223017Sdim */ 285223017Sdim if (cpuinfo.cpu_type == CPUTYP_4_100 && (addr & 0xf0000000)) 286223017Sdim return (0); 287223017Sdim if (cpuinfo.cpu_type != CPUTYP_4_100 && !(addr & 0xf0000000)) 288223017Sdim return (0); 289223017Sdim 290223017Sdim uoba.uoba_isobio4 = 1; 291223017Sdim oba->oba_bustag = &obio_space_tag; 292223017Sdim oba->oba_dmatag = oap->ma->ma_dmatag; 293223017Sdim oba->oba_paddr = BUS_ADDR(PMAP_OBIO, addr); 294223017Sdim oba->oba_pri = cf->cf_loc[OBIOCF_LEVEL]; 295223017Sdim 296223017Sdim if (config_match(parent, cf, &uoba) == 0) 297223017Sdim return (0); 298223017Sdim 299223017Sdim config_attach(parent, cf, &uoba, obioprint); 300223017Sdim return (1); 301223017Sdim} 302223017Sdim 303223017Sdim 304223017Sdim/* 305223017Sdim * If we can find a mapping that was established by the rom, use it. 306223017Sdim * Else, create a new mapping. 307223017Sdim */ 308223017Sdimint 309223017Sdimobio_find_rom_map(ba, len, hp) 310223017Sdim bus_addr_t ba; 311223017Sdim int len; 312223017Sdim bus_space_handle_t *hp; 313223017Sdim{ 314223017Sdim#define getpte(va) lda(va, ASI_PTE) 315223017Sdim 316223017Sdim u_long pa, pf; 317223017Sdim int pgtype; 318223017Sdim u_long va, pte; 319223017Sdim 320223017Sdim if (len > PAGE_SIZE) 321223017Sdim return (EINVAL); 322223017Sdim 323223017Sdim pa = BUS_ADDR_PADDR(ba); 324223017Sdim pf = pa >> PGSHIFT; 325223017Sdim pgtype = PMAP_T2PTE_4(PMAP_OBIO); 326223017Sdim 327223017Sdim for (va = OLDMON_STARTVADDR; va < OLDMON_ENDVADDR; va += PAGE_SIZE) { 328223017Sdim pte = getpte(va); 329223017Sdim if ((pte & PG_V) == 0 || (pte & PG_TYPE) != pgtype || 330223017Sdim (pte & PG_PFNUM) != pf) 331223017Sdim continue; 332223017Sdim 333223017Sdim /* 334223017Sdim * Found entry in PROM's pagetable 335223017Sdim * note: preserve page offset 336223017Sdim */ 337223017Sdim *hp = (bus_space_handle_t)(va | (pa & PGOFSET)); 338223017Sdim return (0); 339223017Sdim } 340223017Sdim 341223017Sdim return (ENOENT); 342223017Sdim} 343223017Sdim#endif /* SUN4 */ 344223017Sdim