#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <machine/iomod.h>
#include <machine/autoconf.h>
#include <dev/pci/pcivar.h>
#include <dev/wscons/wsdisplayvar.h>
#include <hppa/hppa/machdep.h>
#include <dev/ic/stireg.h>
#include <dev/ic/stivar.h>
#include <hppa/dev/sti_pci_var.h>
#define PCI_ROM_SIZE(mr) \
(PCI_MAPREG_ROM_ADDR(mr) & -PCI_MAPREG_ROM_ADDR(mr))
#include "opt_sti_pci.h"
#ifdef STI_PCI_DEBUG
#define DPRINTF printf
#else
#define DPRINTF while (0) printf
#endif
int
sti_pci_is_console(struct pci_attach_args *paa, bus_addr_t *bases)
{
hppa_hpa_t consaddr;
uint32_t cf;
int pagezero_cookie;
int bar;
int rc;
KASSERT(paa != NULL);
pagezero_cookie = hppa_pagezero_map();
consaddr = (hppa_hpa_t)PAGE0->mem_cons.pz_hpa;
hppa_pagezero_unmap(pagezero_cookie);
for (bar = PCI_MAPREG_START; bar <= PCI_MAPREG_PPB_END; ) {
bus_addr_t addr;
bus_size_t size;
cf = pci_conf_read(paa->pa_pc, paa->pa_tag, bar);
rc = pci_mapreg_info(paa->pa_pc, paa->pa_tag, bar,
PCI_MAPREG_TYPE(cf), &addr, &size, NULL);
if (PCI_MAPREG_TYPE(cf) == PCI_MAPREG_TYPE_IO) {
bar += 4;
} else {
if (PCI_MAPREG_MEM_TYPE(cf) ==
PCI_MAPREG_MEM_TYPE_64BIT)
bar += 8;
else
bar += 4;
}
if (rc == 0 && (hppa_hpa_t)addr == consaddr)
return 1;
}
return 0;
}
static int
sti_pci_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) {
sc->sc_disable_rom(sc);
printf("%s: unexpected bar %02x for region %d\n",
device_xname(sc->sc_dev), bar, region);
sc->sc_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) {
sc->sc_disable_rom(sc);
aprint_error_dev(sc->sc_dev, "invalid bar %02x for region %d\n",
bar, region);
sc->sc_enable_rom(sc);
return (rc);
}
sc->bases[region] = addr;
return (0);
}
int
sti_pci_check_rom(struct sti_softc *sc, struct pci_attach_args *pa,
bus_space_handle_t *rom_handle, bus_size_t *rom_size)
{
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;
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;
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;
}
sc->sc_disable_rom(sc);
selected = (bus_addr_t)-1;
for (offs = 0; offs < romsize; offs += subsize) {
sc->sc_enable_rom(sc);
tmp = bus_space_read_4(pa->pa_memt, romh, offs + 0);
tmp = le32toh(tmp);
if (tmp != 0x55aa0000) {
sc->sc_disable_rom(sc);
if (offs == 0) {
aprint_error_dev(sc->sc_dev,
"invalid PCI ROM header signature (%08x)\n",
tmp);
rc = EINVAL;
}
break;
}
tmp = bus_space_read_4(pa->pa_memt, romh, offs + 4);
tmp = le32toh(tmp);
if (tmp != 0x00000001) {
sc->sc_disable_rom(sc);
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 STI_PCI_DEBUG
sc->sc_disable_rom(sc);
DPRINTF("ROM offset %08x size %08x type %08x",
(u_int)offs, (u_int)subsize, tmp);
sc->sc_enable_rom(sc);
#endif
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) {
sc->sc_disable_rom(sc);
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);
sc->sc_disable_rom(sc);
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:
DPRINTF(" (wrong architecture)");
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;
}
sc->sc_enable_rom(sc);
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_pci_readbar(sc, pa, i,
bus_space_read_1(pa->pa_memt, romh, offs + i));
if (rc != 0)
goto fail;
}
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);
sc->sc_disable_rom(sc);
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, rom_handle);
if (rc != 0) {
aprint_error_dev(sc->sc_dev, "can't map STI ROM (%d)\n",
rc);
goto fail2;
}
*rom_size = stiromsize;
sc->sc_disable_rom(sc);
sc->sc_flags &= ~STI_ROM_ENABLED;
return 0;
fail:
bus_space_unmap(pa->pa_memt, romh, romsize);
fail2:
sc->sc_disable_rom(sc);
return rc;
}