429 lines
10 KiB
C
429 lines
10 KiB
C
/* $NetBSD: sti_pci.c,v 1.2 2017/10/05 06:37:45 msaitoh Exp $ */
|
|
|
|
/* $OpenBSD: sti_pci.c,v 1.7 2009/02/06 22:51:04 miod Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2006, 2007 Miodrag Vallat.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice, this permission notice, and the disclaimer below
|
|
* appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/device.h>
|
|
|
|
#include <dev/pci/pcireg.h>
|
|
#include <dev/pci/pcivar.h>
|
|
#include <dev/pci/pcidevs.h>
|
|
|
|
#include <dev/wscons/wsdisplayvar.h>
|
|
|
|
#include <dev/ic/stireg.h>
|
|
#include <dev/ic/stivar.h>
|
|
|
|
#ifdef STIDEBUG
|
|
#define DPRINTF(s) do { \
|
|
if (stidebug) \
|
|
printf s; \
|
|
} while(0)
|
|
|
|
extern int stidebug;
|
|
#else
|
|
#define DPRINTF(s) /* */
|
|
#endif
|
|
|
|
int sti_pci_match(device_t, cfdata_t, void *);
|
|
void sti_pci_attach(device_t, device_t, void *);
|
|
|
|
void sti_pci_end_attach(device_t dev);
|
|
|
|
struct sti_pci_softc {
|
|
device_t sc_dev;
|
|
|
|
struct sti_softc sc_base;
|
|
|
|
pci_chipset_tag_t sc_pc;
|
|
pcitag_t sc_tag;
|
|
|
|
bus_space_handle_t sc_romh;
|
|
};
|
|
|
|
CFATTACH_DECL_NEW(sti_pci, sizeof(struct sti_pci_softc),
|
|
sti_pci_match, sti_pci_attach, NULL, NULL);
|
|
|
|
int sti_readbar(struct sti_softc *, struct pci_attach_args *, u_int, int);
|
|
int sti_check_rom(struct sti_pci_softc *, struct pci_attach_args *);
|
|
void sti_pci_enable_rom(struct sti_softc *);
|
|
void sti_pci_disable_rom(struct sti_softc *);
|
|
void sti_pci_enable_rom_internal(struct sti_pci_softc *);
|
|
void sti_pci_disable_rom_internal(struct sti_pci_softc *);
|
|
|
|
int sti_pci_is_console(struct pci_attach_args *, bus_addr_t *);
|
|
|
|
#define PCI_ROM_SIZE(mr) \
|
|
(PCI_MAPREG_ROM_ADDR(mr) & -PCI_MAPREG_ROM_ADDR(mr))
|
|
|
|
int
|
|
sti_pci_match(device_t parent, cfdata_t cf, void *aux)
|
|
{
|
|
struct pci_attach_args *paa = aux;
|
|
|
|
if (PCI_VENDOR(paa->pa_id) != PCI_VENDOR_HP)
|
|
return 0;
|
|
|
|
if (PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_EG ||
|
|
PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_FX2 ||
|
|
PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_FX4 ||
|
|
PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_FX6 ||
|
|
PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_FXE)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
sti_pci_attach(device_t parent, device_t self, void *aux)
|
|
{
|
|
struct sti_pci_softc *spc = device_private(self);
|
|
struct pci_attach_args *paa = aux;
|
|
int ret;
|
|
|
|
spc->sc_dev = self;
|
|
|
|
spc->sc_pc = paa->pa_pc;
|
|
spc->sc_tag = paa->pa_tag;
|
|
spc->sc_base.sc_dev = self;
|
|
spc->sc_base.sc_enable_rom = sti_pci_enable_rom;
|
|
spc->sc_base.sc_disable_rom = sti_pci_disable_rom;
|
|
|
|
aprint_normal("\n");
|
|
|
|
if (sti_check_rom(spc, paa) != 0)
|
|
return;
|
|
|
|
aprint_normal("%s", device_xname(self));
|
|
ret = sti_pci_is_console(paa, spc->sc_base. bases);
|
|
if (ret != 0)
|
|
spc->sc_base.sc_flags |= STI_CONSOLE;
|
|
|
|
ret = sti_attach_common(&spc->sc_base, paa->pa_iot, paa->pa_memt,
|
|
spc->sc_romh, STI_CODEBASE_MAIN);
|
|
if (ret == 0)
|
|
config_interrupts(self, sti_pci_end_attach);
|
|
|
|
}
|
|
|
|
void sti_pci_end_attach(device_t dev)
|
|
{
|
|
struct sti_pci_softc *spc = device_private(dev);
|
|
struct sti_softc *sc = &spc->sc_base;
|
|
|
|
sti_end_attach(sc);
|
|
}
|
|
|
|
|
|
/*
|
|
* Grovel the STI ROM image.
|
|
*/
|
|
int
|
|
sti_check_rom(struct sti_pci_softc *spc, struct pci_attach_args *pa)
|
|
{
|
|
struct sti_softc *sc = &spc->sc_base;
|
|
pcireg_t address, mask;
|
|
bus_space_handle_t romh;
|
|
bus_size_t romsize, subsize, stiromsize;
|
|
bus_addr_t selected, offs, suboffs;
|
|
uint32_t tmp;
|
|
int i;
|
|
int rc;
|
|
|
|
/* sort of inline sti_pci_enable_rom(sc) */
|
|
address = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM);
|
|
pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM,
|
|
~PCI_MAPREG_ROM_ENABLE);
|
|
mask = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM);
|
|
address |= PCI_MAPREG_ROM_ENABLE;
|
|
pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM, address);
|
|
sc->sc_flags |= STI_ROM_ENABLED;
|
|
/*
|
|
* Map the complete ROM for now.
|
|
*/
|
|
|
|
romsize = PCI_ROM_SIZE(mask);
|
|
DPRINTF(("%s: mapping rom @ %lx for %lx\n", __func__,
|
|
(long)PCI_MAPREG_ROM_ADDR(address), (long)romsize));
|
|
|
|
rc = bus_space_map(pa->pa_memt, PCI_MAPREG_ROM_ADDR(address), romsize,
|
|
0, &romh);
|
|
if (rc != 0) {
|
|
aprint_error_dev(sc->sc_dev, "can't map PCI ROM (%d)\n", rc);
|
|
goto fail2;
|
|
}
|
|
|
|
sti_pci_disable_rom_internal(spc);
|
|
/*
|
|
* Iterate over the ROM images, pick the best candidate.
|
|
*/
|
|
|
|
selected = (bus_addr_t)-1;
|
|
for (offs = 0; offs < romsize; offs += subsize) {
|
|
sti_pci_enable_rom_internal(spc);
|
|
/*
|
|
* Check for a valid ROM header.
|
|
*/
|
|
tmp = bus_space_read_4(pa->pa_memt, romh, offs + 0);
|
|
tmp = le32toh(tmp);
|
|
if (tmp != 0x55aa0000) {
|
|
sti_pci_disable_rom_internal(spc);
|
|
if (offs == 0) {
|
|
aprint_error_dev(sc->sc_dev,
|
|
"invalid PCI ROM header signature (%08x)\n",
|
|
tmp);
|
|
rc = EINVAL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Check ROM type.
|
|
*/
|
|
tmp = bus_space_read_4(pa->pa_memt, romh, offs + 4);
|
|
tmp = le32toh(tmp);
|
|
if (tmp != 0x00000001) { /* 1 == STI ROM */
|
|
sti_pci_disable_rom_internal(spc);
|
|
if (offs == 0) {
|
|
aprint_error_dev(sc->sc_dev,
|
|
"invalid PCI ROM type (%08x)\n", tmp);
|
|
rc = EINVAL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
subsize = (bus_addr_t)bus_space_read_2(pa->pa_memt, romh,
|
|
offs + 0x0c);
|
|
subsize <<= 9;
|
|
|
|
#ifdef STIDEBUG
|
|
sti_pci_disable_rom_internal(spc);
|
|
DPRINTF(("ROM offset %08x size %08x type %08x",
|
|
(u_int)offs, (u_int)subsize, tmp));
|
|
sti_pci_enable_rom_internal(spc);
|
|
#endif
|
|
|
|
/*
|
|
* Check for a valid ROM data structure.
|
|
* We do not need it except to know what architecture the ROM
|
|
* code is for.
|
|
*/
|
|
|
|
suboffs = offs +(bus_addr_t)bus_space_read_2(pa->pa_memt, romh,
|
|
offs + 0x18);
|
|
tmp = bus_space_read_4(pa->pa_memt, romh, suboffs + 0);
|
|
tmp = le32toh(tmp);
|
|
if (tmp != 0x50434952) { /* PCIR */
|
|
sti_pci_disable_rom_internal(spc);
|
|
if (offs == 0) {
|
|
aprint_error_dev(sc->sc_dev, "invalid PCI data"
|
|
" signature (%08x)\n", tmp);
|
|
rc = EINVAL;
|
|
} else {
|
|
DPRINTF((" invalid PCI data signature %08x\n",
|
|
tmp));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
tmp = bus_space_read_1(pa->pa_memt, romh, suboffs + 0x14);
|
|
sti_pci_disable_rom_internal(spc);
|
|
DPRINTF((" code %02x", tmp));
|
|
|
|
switch (tmp) {
|
|
#ifdef __hppa__
|
|
case 0x10:
|
|
if (selected == (bus_addr_t)-1)
|
|
selected = offs;
|
|
break;
|
|
#endif
|
|
#ifdef __i386__
|
|
case 0x00:
|
|
if (selected == (bus_addr_t)-1)
|
|
selected = offs;
|
|
break;
|
|
#endif
|
|
default:
|
|
#ifdef STIDEBUG
|
|
DPRINTF((" (wrong architecture)"));
|
|
#endif
|
|
break;
|
|
}
|
|
DPRINTF(("%s\n", selected == offs ? " -> SELECTED" : ""));
|
|
}
|
|
|
|
if (selected == (bus_addr_t)-1) {
|
|
if (rc == 0) {
|
|
aprint_error_dev(sc->sc_dev, "found no ROM with "
|
|
"correct microcode architecture\n");
|
|
rc = ENOEXEC;
|
|
}
|
|
goto fail;
|
|
}
|
|
|
|
/*
|
|
* Read the STI region BAR assignments.
|
|
*/
|
|
|
|
sti_pci_enable_rom_internal(spc);
|
|
offs = selected +
|
|
(bus_addr_t)bus_space_read_2(pa->pa_memt, romh, selected + 0x0e);
|
|
for (i = 0; i < STI_REGION_MAX; i++) {
|
|
rc = sti_readbar(sc, pa, i,
|
|
bus_space_read_1(pa->pa_memt, romh, offs + i));
|
|
if (rc != 0)
|
|
goto fail;
|
|
}
|
|
|
|
/*
|
|
* Find out where the STI ROM itself lies, and its size.
|
|
*/
|
|
|
|
offs = selected +
|
|
(bus_addr_t)bus_space_read_4(pa->pa_memt, romh, selected + 0x08);
|
|
stiromsize = (bus_addr_t)bus_space_read_4(pa->pa_memt, romh,
|
|
offs + 0x18);
|
|
stiromsize = le32toh(stiromsize);
|
|
sti_pci_disable_rom_internal(spc);
|
|
|
|
/*
|
|
* Replace our mapping with a smaller mapping of only the area
|
|
* we are interested in.
|
|
*/
|
|
|
|
DPRINTF(("remapping rom @ %lx for %lx\n",
|
|
(long)(PCI_MAPREG_ROM_ADDR(address) + offs), (long)stiromsize));
|
|
bus_space_unmap(pa->pa_memt, romh, romsize);
|
|
rc = bus_space_map(pa->pa_memt, PCI_MAPREG_ROM_ADDR(address) + offs,
|
|
stiromsize, 0, &spc->sc_romh);
|
|
if (rc != 0) {
|
|
aprint_error_dev(sc->sc_dev, "can't map STI ROM (%d)\n",
|
|
rc);
|
|
goto fail2;
|
|
}
|
|
sti_pci_disable_rom_internal(spc);
|
|
sc->sc_flags &= ~STI_ROM_ENABLED;
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
bus_space_unmap(pa->pa_memt, romh, romsize);
|
|
fail2:
|
|
sti_pci_disable_rom_internal(spc);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Decode a BAR register.
|
|
*/
|
|
int
|
|
sti_readbar(struct sti_softc *sc, struct pci_attach_args *pa, u_int region,
|
|
int bar)
|
|
{
|
|
bus_addr_t addr;
|
|
bus_size_t size;
|
|
uint32_t cf;
|
|
int rc;
|
|
|
|
if (bar == 0) {
|
|
sc->bases[region] = 0;
|
|
return (0);
|
|
}
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if (bar < PCI_MAPREG_START || bar > PCI_MAPREG_PPB_END) {
|
|
sti_pci_disable_rom(sc);
|
|
printf("%s: unexpected bar %02x for region %d\n",
|
|
device_xname(sc->sc_dev), bar, region);
|
|
sti_pci_enable_rom(sc);
|
|
}
|
|
#endif
|
|
|
|
cf = pci_conf_read(pa->pa_pc, pa->pa_tag, bar);
|
|
|
|
rc = pci_mapreg_info(pa->pa_pc, pa->pa_tag, bar, PCI_MAPREG_TYPE(cf),
|
|
&addr, &size, NULL);
|
|
|
|
if (rc != 0) {
|
|
sti_pci_disable_rom(sc);
|
|
aprint_error_dev(sc->sc_dev, "invalid bar %02x for region %d\n",
|
|
bar, region);
|
|
sti_pci_enable_rom(sc);
|
|
return (rc);
|
|
}
|
|
|
|
sc->bases[region] = addr;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Enable PCI ROM.
|
|
*/
|
|
void
|
|
sti_pci_enable_rom_internal(struct sti_pci_softc *spc)
|
|
{
|
|
pcireg_t address;
|
|
|
|
KASSERT(spc != NULL);
|
|
|
|
address = pci_conf_read(spc->sc_pc, spc->sc_tag, PCI_MAPREG_ROM);
|
|
address |= PCI_MAPREG_ROM_ENABLE;
|
|
pci_conf_write(spc->sc_pc, spc->sc_tag, PCI_MAPREG_ROM, address);
|
|
}
|
|
|
|
void
|
|
sti_pci_enable_rom(struct sti_softc *sc)
|
|
{
|
|
struct sti_pci_softc *spc = device_private(sc->sc_dev);
|
|
|
|
if (!ISSET(sc->sc_flags, STI_ROM_ENABLED)) {
|
|
sti_pci_enable_rom_internal(spc);
|
|
}
|
|
SET(sc->sc_flags, STI_ROM_ENABLED);
|
|
}
|
|
|
|
/*
|
|
* Disable PCI ROM.
|
|
*/
|
|
void
|
|
sti_pci_disable_rom_internal(struct sti_pci_softc *spc)
|
|
{
|
|
pcireg_t address;
|
|
|
|
KASSERT(spc != NULL);
|
|
|
|
address = pci_conf_read(spc->sc_pc, spc->sc_tag, PCI_MAPREG_ROM);
|
|
address &= ~PCI_MAPREG_ROM_ENABLE;
|
|
pci_conf_write(spc->sc_pc, spc->sc_tag, PCI_MAPREG_ROM, address);
|
|
}
|
|
|
|
void
|
|
sti_pci_disable_rom(struct sti_softc *sc)
|
|
{
|
|
struct sti_pci_softc *spc = device_private(sc->sc_dev);
|
|
|
|
if (ISSET(sc->sc_flags, STI_ROM_ENABLED)) {
|
|
sti_pci_disable_rom_internal(spc);
|
|
}
|
|
CLR(sc->sc_flags, STI_ROM_ENABLED);
|
|
}
|