/*
* Copyright (c) 2007 Mark Kettenis
*
* 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 and this permission notice 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.
*/
/*
* Driver for the National Semiconductor PC87560 Legacy I/O chip.
*/
/*
* The firmware doesn't always switch the IDE function into native
* mode. So we do that ourselves since it makes life much simpler.
* Note that we have to do this in the match function since the
* Legacy I/O function attaches after the IDE function.
*/
if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_NS &&
PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_NS_PC87415) {
bhlc = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG);
if (!PCI_HDRTYPE_MULTIFN(bhlc))
return (0);
tag = pci_make_tag(pa->pa_pc, pa->pa_bus, pa->pa_device, 1);
id = pci_conf_read(pa->pa_pc, tag, PCI_ID_REG);
if (PCI_VENDOR(id) != PCI_VENDOR_NS ||
PCI_PRODUCT(id) != PCI_PRODUCT_NS_PC87560)
return (0);
sc->sc_iot = pa->pa_iot;
if (bus_space_map(sc->sc_iot, SSIO_PIC1, 2, 0, &sc->sc_ic1h)) {
aprint_error_dev(self, "unable to map PIC1 registers\n");
return;
}
if (bus_space_map(sc->sc_iot, SSIO_PIC2, 2, 0, &sc->sc_ic2h)) {
aprint_error_dev(self, "unable to map PIC2 registers\n");
goto unmap_ic1;
}
if (pci_intr_map(pa, &ih)) {
aprint_error_dev(self, "unable to map interrupt\n");
goto unmap_ic2;
}
intrstr = pci_intr_string(pa->pa_pc, ih, buf, sizeof(buf));
sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_TTY, ssio_intr,
sc);
if (sc->sc_ih == NULL) {
aprint_error_dev(self, "could not establish interrupt");
if (intrstr != NULL)
aprint_error(" at %s", intrstr);
aprint_error("\n");
goto unmap_ic2;
}
aprint_normal_dev(self, "interrupting at %s\n", intrstr);
/*
* We use the following interrupt mapping:
*
* USB (INTD#) IRQ 1
* IDE Channel 1 IRQ 5
* Serial Port 1 IRQ 4
* Serial Port 2 IRQ 3
* Parallel Port IRQ 7
*
* USB and IDE are set to level triggered, all others to edge
* triggered.
*
* We disable all other interrupts since we don't need them.
*/
reg = pci_conf_read(pa->pa_pc, pa->pa_tag, SSIO_PCI_DMA_RC2);
reg &= ~(SSIO_PCI_INT_TC1_MASK << SSIO_PCI_INT_TC1_SHIFT);
reg |= 0x22 << SSIO_PCI_INT_TC1_SHIFT;
pci_conf_write(pa->pa_pc, pa->pa_tag, SSIO_PCI_DMA_RC2, reg);
#if NUKBD > 0
/*
* If a USB keyboard is used for console input, the firmware passes
* the mmio address of the USB controller the keyboard is attached
* to. Since we know the USB controller is function 2 on the same
* device and comes right after us (we're function 1 remember),
* this is a convenient spot to mark the USB keyboard as console
* if the address matches.
*/
tag = pci_make_tag(pa->pa_pc, pa->pa_bus, pa->pa_device, 2);
reg = pci_conf_read(pa->pa_pc, tag, PCI_CBMEM);