Home | History | Annotate | Line # | Download | only in pci
sti_pci.c revision 1.1.6.2
      1 /*	$NetBSD: sti_pci.c,v 1.1.6.2 2011/03/05 20:53:58 rmind Exp $	*/
      2 
      3 /*	$OpenBSD: sti_pci.c,v 1.7 2009/02/06 22:51:04 miod Exp $	*/
      4 
      5 /*
      6  * Copyright (c) 2006, 2007 Miodrag Vallat.
      7  *
      8  * Permission to use, copy, modify, and distribute this software for any
      9  * purpose with or without fee is hereby granted, provided that the above
     10  * copyright notice, this permission notice, and the disclaimer below
     11  * appear in all copies.
     12  *
     13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     20  */
     21 
     22 #include <sys/param.h>
     23 #include <sys/systm.h>
     24 #include <sys/device.h>
     25 
     26 #include <dev/pci/pcireg.h>
     27 #include <dev/pci/pcivar.h>
     28 #include <dev/pci/pcidevs.h>
     29 
     30 #include <dev/wscons/wsdisplayvar.h>
     31 
     32 #include <dev/ic/stireg.h>
     33 #include <dev/ic/stivar.h>
     34 
     35 #ifdef STIDEBUG
     36 #define	DPRINTF(s)	do {	\
     37 	if (stidebug)		\
     38 		printf s;	\
     39 } while(0)
     40 
     41 extern int stidebug;
     42 #else
     43 #define	DPRINTF(s)	/* */
     44 #endif
     45 
     46 int	sti_pci_match(device_t, cfdata_t, void *);
     47 void	sti_pci_attach(device_t, device_t, void *);
     48 
     49 void	sti_pci_end_attach(device_t dev);
     50 
     51 struct	sti_pci_softc {
     52 	device_t		sc_dev;
     53 
     54 	struct sti_softc	sc_base;
     55 
     56 	pci_chipset_tag_t	sc_pc;
     57 	pcitag_t		sc_tag;
     58 
     59 	bus_space_handle_t	sc_romh;
     60 };
     61 
     62 CFATTACH_DECL_NEW(sti_pci, sizeof(struct sti_pci_softc),
     63     sti_pci_match, sti_pci_attach, NULL, NULL);
     64 
     65 int	sti_readbar(struct sti_softc *, struct pci_attach_args *, u_int, int);
     66 int	sti_check_rom(struct sti_pci_softc *, struct pci_attach_args *);
     67 void	sti_pci_enable_rom(struct sti_softc *);
     68 void	sti_pci_disable_rom(struct sti_softc *);
     69 void	sti_pci_enable_rom_internal(struct sti_pci_softc *);
     70 void	sti_pci_disable_rom_internal(struct sti_pci_softc *);
     71 
     72 int	sti_pci_is_console(struct pci_attach_args *, bus_addr_t *);
     73 
     74 #define PCI_ROM_ENABLE                  0x00000001
     75 #define PCI_ROM_ADDR_MASK               0xfffff800
     76 #define PCI_ROM_ADDR(mr)                                                \
     77             ((mr) & PCI_ROM_ADDR_MASK)
     78 #define PCI_ROM_SIZE(mr)                                                \
     79             (PCI_ROM_ADDR(mr) & -PCI_ROM_ADDR(mr))
     80 
     81 int
     82 sti_pci_match(device_t parent, cfdata_t cf, void *aux)
     83 {
     84 	struct pci_attach_args *paa = aux;
     85 
     86 	if (PCI_VENDOR(paa->pa_id) != PCI_VENDOR_HP)
     87 		return 0;
     88 
     89 	if (PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_EG ||
     90 	    PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_FX2 ||
     91 	    PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_FX4 ||
     92 	    PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_FX6 ||
     93 	    PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_FXE)
     94 		return 1;
     95 
     96 	return 0;
     97 }
     98 
     99 void
    100 sti_pci_attach(device_t parent, device_t self, void *aux)
    101 {
    102 	struct sti_pci_softc *spc = device_private(self);
    103 	struct pci_attach_args *paa = aux;
    104 	int ret;
    105 
    106 	spc->sc_dev = self;
    107 
    108 	spc->sc_pc = paa->pa_pc;
    109 	spc->sc_tag = paa->pa_tag;
    110 	spc->sc_base.sc_dev = self;
    111 	spc->sc_base.sc_enable_rom = sti_pci_enable_rom;
    112 	spc->sc_base.sc_disable_rom = sti_pci_disable_rom;
    113 
    114 	aprint_normal("\n");
    115 
    116 	if (sti_check_rom(spc, paa) != 0)
    117 		return;
    118 
    119 	aprint_normal("%s", device_xname(self));
    120 	ret = sti_pci_is_console(paa, spc->sc_base. bases);
    121 	if (ret != 0)
    122 		spc->sc_base.sc_flags |= STI_CONSOLE;
    123 
    124 	ret = sti_attach_common(&spc->sc_base, paa->pa_iot, paa->pa_memt,
    125 	    spc->sc_romh, STI_CODEBASE_MAIN);
    126 	if (ret == 0)
    127 		config_interrupts(self, sti_pci_end_attach);
    128 
    129 }
    130 
    131 void sti_pci_end_attach(device_t dev)
    132 {
    133 	struct sti_pci_softc *spc = device_private(dev);
    134 	struct sti_softc *sc = &spc->sc_base;
    135 
    136 	sti_end_attach(sc);
    137 }
    138 
    139 
    140 /*
    141  * Grovel the STI ROM image.
    142  */
    143 int
    144 sti_check_rom(struct sti_pci_softc *spc, struct pci_attach_args *pa)
    145 {
    146 	struct sti_softc *sc = &spc->sc_base;
    147 	pcireg_t address, mask;
    148 	bus_space_handle_t romh;
    149 	bus_size_t romsize, subsize, stiromsize;
    150 	bus_addr_t selected, offs, suboffs;
    151 	uint32_t tmp;
    152 	int i;
    153 	int rc;
    154 
    155 	/* sort of inline sti_pci_enable_rom(sc) */
    156 	address = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM);
    157 	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM, ~PCI_ROM_ENABLE);
    158 	mask = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM);
    159 	address |= PCI_ROM_ENABLE;
    160 	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM, address);
    161 	sc->sc_flags |= STI_ROM_ENABLED;
    162 	/*
    163 	 * Map the complete ROM for now.
    164 	 */
    165 
    166 	romsize = PCI_ROM_SIZE(mask);
    167 	DPRINTF(("%s: mapping rom @ %lx for %lx\n", __func__,
    168 	    (long)PCI_ROM_ADDR(address), (long)romsize));
    169 
    170 	rc = bus_space_map(pa->pa_memt, PCI_ROM_ADDR(address), romsize,
    171 	    0, &romh);
    172 	if (rc != 0) {
    173 		aprint_error_dev(sc->sc_dev, "can't map PCI ROM (%d)\n", rc);
    174 		goto fail2;
    175 	}
    176 
    177 	sti_pci_disable_rom_internal(spc);
    178 	/*
    179 	 * Iterate over the ROM images, pick the best candidate.
    180 	 */
    181 
    182 	selected = (bus_addr_t)-1;
    183 	for (offs = 0; offs < romsize; offs += subsize) {
    184 		sti_pci_enable_rom_internal(spc);
    185 		/*
    186 		 * Check for a valid ROM header.
    187 		 */
    188 		tmp = bus_space_read_4(pa->pa_memt, romh, offs + 0);
    189 		tmp = le32toh(tmp);
    190 		if (tmp != 0x55aa0000) {
    191 			sti_pci_disable_rom_internal(spc);
    192 			if (offs == 0) {
    193 				aprint_error_dev(sc->sc_dev,
    194 				    "invalid PCI ROM header signature (%08x)\n",
    195 				     tmp);
    196 				rc = EINVAL;
    197 			}
    198 			break;
    199 		}
    200 
    201 		/*
    202 		 * Check ROM type.
    203 		 */
    204 		tmp = bus_space_read_4(pa->pa_memt, romh, offs + 4);
    205 		tmp = le32toh(tmp);
    206 		if (tmp != 0x00000001) {	/* 1 == STI ROM */
    207 			sti_pci_disable_rom_internal(spc);
    208 			if (offs == 0) {
    209 				aprint_error_dev(sc->sc_dev,
    210 				    "invalid PCI ROM type (%08x)\n", tmp);
    211 				rc = EINVAL;
    212 			}
    213 			break;
    214 		}
    215 
    216 		subsize = (bus_addr_t)bus_space_read_2(pa->pa_memt, romh,
    217 		    offs + 0x0c);
    218 		subsize <<= 9;
    219 
    220 #ifdef STIDEBUG
    221 		sti_pci_disable_rom_internal(spc);
    222 		DPRINTF(("ROM offset %08x size %08x type %08x",
    223 		    (u_int)offs, (u_int)subsize, tmp));
    224 		sti_pci_enable_rom_internal(spc);
    225 #endif
    226 
    227 		/*
    228 		 * Check for a valid ROM data structure.
    229 		 * We do not need it except to know what architecture the ROM
    230 		 * code is for.
    231 		 */
    232 
    233 		suboffs = offs +(bus_addr_t)bus_space_read_2(pa->pa_memt, romh,
    234 		    offs + 0x18);
    235 		tmp = bus_space_read_4(pa->pa_memt, romh, suboffs + 0);
    236 		tmp = le32toh(tmp);
    237 		if (tmp != 0x50434952) {	/* PCIR */
    238 			sti_pci_disable_rom_internal(spc);
    239 			if (offs == 0) {
    240 				aprint_error_dev(sc->sc_dev, "invalid PCI data"
    241 				    " signature (%08x)\n", tmp);
    242 				rc = EINVAL;
    243 			} else {
    244 				DPRINTF((" invalid PCI data signature %08x\n",
    245 				    tmp));
    246 				continue;
    247 			}
    248 		}
    249 
    250 		tmp = bus_space_read_1(pa->pa_memt, romh, suboffs + 0x14);
    251 		sti_pci_disable_rom_internal(spc);
    252 		DPRINTF((" code %02x", tmp));
    253 
    254 		switch (tmp) {
    255 #ifdef __hppa__
    256 		case 0x10:
    257 			if (selected == (bus_addr_t)-1)
    258 				selected = offs;
    259 			break;
    260 #endif
    261 #ifdef __i386__
    262 		case 0x00:
    263 			if (selected == (bus_addr_t)-1)
    264 				selected = offs;
    265 			break;
    266 #endif
    267 		default:
    268 #ifdef STIDEBUG
    269 			DPRINTF((" (wrong architecture)"));
    270 #endif
    271 			break;
    272 		}
    273 		DPRINTF(("%s\n", selected == offs ? " -> SELECTED" : ""));
    274 	}
    275 
    276 	if (selected == (bus_addr_t)-1) {
    277 		if (rc == 0) {
    278 			aprint_error_dev(sc->sc_dev, "found no ROM with "
    279 			    "correct microcode architecture\n");
    280 			rc = ENOEXEC;
    281 		}
    282 		goto fail;
    283 	}
    284 
    285 	/*
    286 	 * Read the STI region BAR assignments.
    287 	 */
    288 
    289 	sti_pci_enable_rom_internal(spc);
    290 	offs = selected +
    291 	    (bus_addr_t)bus_space_read_2(pa->pa_memt, romh, selected + 0x0e);
    292 	for (i = 0; i < STI_REGION_MAX; i++) {
    293 		rc = sti_readbar(sc, pa, i,
    294 		    bus_space_read_1(pa->pa_memt, romh, offs + i));
    295 		if (rc != 0)
    296 			goto fail;
    297 	}
    298 
    299 	/*
    300 	 * Find out where the STI ROM itself lies, and its size.
    301 	 */
    302 
    303 	offs = selected +
    304 	    (bus_addr_t)bus_space_read_4(pa->pa_memt, romh, selected + 0x08);
    305 	stiromsize = (bus_addr_t)bus_space_read_4(pa->pa_memt, romh,
    306 	    offs + 0x18);
    307 	stiromsize = le32toh(stiromsize);
    308 	sti_pci_disable_rom_internal(spc);
    309 
    310 	/*
    311 	 * Replace our mapping with a smaller mapping of only the area
    312 	 * we are interested in.
    313 	 */
    314 
    315 	DPRINTF(("remapping rom @ %lx for %lx\n",
    316 	    (long)(PCI_ROM_ADDR(address) + offs), (long)stiromsize));
    317 	bus_space_unmap(pa->pa_memt, romh, romsize);
    318 	rc = bus_space_map(pa->pa_memt, PCI_ROM_ADDR(address) + offs,
    319 	    stiromsize, 0, &spc->sc_romh);
    320 	if (rc != 0) {
    321 		aprint_error_dev(sc->sc_dev, "can't map STI ROM (%d)\n",
    322 		    rc);
    323 		goto fail2;
    324 	}
    325  	sti_pci_disable_rom_internal(spc);
    326 	sc->sc_flags &= ~STI_ROM_ENABLED;
    327 
    328 	return 0;
    329 
    330 fail:
    331 	bus_space_unmap(pa->pa_memt, romh, romsize);
    332 fail2:
    333 	sti_pci_disable_rom_internal(spc);
    334 
    335 	return rc;
    336 }
    337 
    338 /*
    339  * Decode a BAR register.
    340  */
    341 int
    342 sti_readbar(struct sti_softc *sc, struct pci_attach_args *pa, u_int region,
    343     int bar)
    344 {
    345 	bus_addr_t addr;
    346 	bus_size_t size;
    347 	uint32_t cf;
    348 	int rc;
    349 
    350 	if (bar == 0) {
    351 		sc->bases[region] = 0;
    352 		return (0);
    353 	}
    354 
    355 #ifdef DIAGNOSTIC
    356 	if (bar < PCI_MAPREG_START || bar > PCI_MAPREG_PPB_END) {
    357 		sti_pci_disable_rom(sc);
    358 		printf("%s: unexpected bar %02x for region %d\n",
    359 		    device_xname(sc->sc_dev), bar, region);
    360 		sti_pci_enable_rom(sc);
    361 	}
    362 #endif
    363 
    364 	cf = pci_conf_read(pa->pa_pc, pa->pa_tag, bar);
    365 
    366 	rc = pci_mapreg_info(pa->pa_pc, pa->pa_tag, bar, PCI_MAPREG_TYPE(cf),
    367 	    &addr, &size, NULL);
    368 
    369 	if (rc != 0) {
    370 		sti_pci_disable_rom(sc);
    371 		aprint_error_dev(sc->sc_dev, "invalid bar %02x for region %d\n",
    372 		    bar, region);
    373 		sti_pci_enable_rom(sc);
    374 		return (rc);
    375 	}
    376 
    377 	sc->bases[region] = addr;
    378 	return (0);
    379 }
    380 
    381 /*
    382  * Enable PCI ROM.
    383  */
    384 void
    385 sti_pci_enable_rom_internal(struct sti_pci_softc *spc)
    386 {
    387 	pcireg_t address;
    388 
    389 	KASSERT(spc != NULL);
    390 
    391 	address = pci_conf_read(spc->sc_pc, spc->sc_tag, PCI_MAPREG_ROM);
    392 	address |= PCI_ROM_ENABLE;
    393 	pci_conf_write(spc->sc_pc, spc->sc_tag, PCI_MAPREG_ROM, address);
    394 }
    395 
    396 void
    397 sti_pci_enable_rom(struct sti_softc *sc)
    398 {
    399 	struct sti_pci_softc *spc = device_private(sc->sc_dev);
    400 
    401 	if (!ISSET(sc->sc_flags, STI_ROM_ENABLED)) {
    402 		sti_pci_enable_rom_internal(spc);
    403 	}
    404 	SET(sc->sc_flags, STI_ROM_ENABLED);
    405 }
    406 
    407 /*
    408  * Disable PCI ROM.
    409  */
    410 void
    411 sti_pci_disable_rom_internal(struct sti_pci_softc *spc)
    412 {
    413 	pcireg_t address;
    414 
    415 	KASSERT(spc != NULL);
    416 
    417 	address = pci_conf_read(spc->sc_pc, spc->sc_tag, PCI_MAPREG_ROM);
    418 	address &= ~PCI_ROM_ENABLE;
    419 	pci_conf_write(spc->sc_pc, spc->sc_tag, PCI_MAPREG_ROM, address);
    420 }
    421 
    422 void
    423 sti_pci_disable_rom(struct sti_softc *sc)
    424 {
    425 	struct sti_pci_softc *spc = device_private(sc->sc_dev);
    426 
    427 	if (ISSET(sc->sc_flags, STI_ROM_ENABLED)) {
    428 		sti_pci_disable_rom_internal(spc);
    429 	}
    430 	CLR(sc->sc_flags, STI_ROM_ENABLED);
    431 }
    432