diff options
author | Mike Smith <msmith@FreeBSD.org> | 2000-08-30 07:52:50 +0000 |
---|---|---|
committer | Mike Smith <msmith@FreeBSD.org> | 2000-08-30 07:52:50 +0000 |
commit | 9f1776230d63537648a5174e3dac7a18c7ffdbca (patch) | |
tree | 3130d6b7c48e164dc29c8ce2a7de16a42cff3581 /sys/dev/amr/amr_pci.c | |
parent | 429a82acc661b83cceaaae92f4ba169dfe5aaa04 (diff) | |
download | src-9f1776230d63537648a5174e3dac7a18c7ffdbca.tar.gz src-9f1776230d63537648a5174e3dac7a18c7ffdbca.zip |
Notes
Diffstat (limited to 'sys/dev/amr/amr_pci.c')
-rw-r--r-- | sys/dev/amr/amr_pci.c | 429 |
1 files changed, 385 insertions, 44 deletions
diff --git a/sys/dev/amr/amr_pci.c b/sys/dev/amr/amr_pci.c index fc9f8a9ea2d4..e338e5944884 100644 --- a/sys/dev/amr/amr_pci.c +++ b/sys/dev/amr/amr_pci.c @@ -1,5 +1,6 @@ /*- - * Copyright (c) 1999 Michael Smith + * Copyright (c) 1999,2000 Michael Smith + * Copyright (c) 2000 BSDi * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,8 +31,8 @@ #include <sys/systm.h> #include <sys/kernel.h> +#include <dev/amr/amr_compat.h> #include <sys/bus.h> -#include <sys/bio.h> #include <sys/conf.h> #include <sys/devicestat.h> #include <sys/disk.h> @@ -49,23 +50,27 @@ #include <dev/amr/amrreg.h> #include <dev/amr/amrvar.h> -#if 0 -#define debug(fmt, args...) printf("%s: " fmt "\n", __FUNCTION__ , ##args) -#else -#define debug(fmt, args...) -#endif - -static int amr_pci_probe(device_t dev); -static int amr_pci_attach(device_t dev); +static int amr_pci_probe(device_t dev); +static int amr_pci_attach(device_t dev); +static int amr_pci_detach(device_t dev); +static int amr_pci_shutdown(device_t dev); +static int amr_pci_suspend(device_t dev); +static int amr_pci_resume(device_t dev); +static void amr_pci_intr(void *arg); +static void amr_pci_free(struct amr_softc *sc); +static void amr_sglist_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error); +static int amr_sglist_map(struct amr_softc *sc); +static void amr_setup_mbox_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error); +static int amr_setup_mbox(struct amr_softc *sc); static device_method_t amr_methods[] = { /* Device interface */ DEVMETHOD(device_probe, amr_pci_probe), DEVMETHOD(device_attach, amr_pci_attach), - DEVMETHOD(device_detach, amr_detach), - DEVMETHOD(device_shutdown, amr_shutdown), - DEVMETHOD(device_suspend, amr_suspend), - DEVMETHOD(device_resume, amr_resume), + DEVMETHOD(device_detach, amr_pci_detach), + DEVMETHOD(device_shutdown, amr_pci_shutdown), + DEVMETHOD(device_suspend, amr_pci_suspend), + DEVMETHOD(device_resume, amr_pci_resume), DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), @@ -78,6 +83,7 @@ static driver_t amr_pci_driver = { sizeof(struct amr_softc) }; +devclass_t amr_devclass; DRIVER_MODULE(amr, pci, amr_pci_driver, amr_devclass, 0, 0); static struct @@ -89,7 +95,8 @@ static struct } amr_device_ids[] = { {0x101e, 0x9010, 0}, {0x101e, 0x9060, 0}, - {0x8086, 0x1960, PROBE_SIGNATURE}, /* generic i960RD, check signature */ + {0x8086, 0x1960, PROBE_SIGNATURE}, /* generic i960RD, check for signature */ + {0x101e, 0x1960, 0}, {0, 0, 0} }; @@ -98,7 +105,7 @@ amr_pci_probe(device_t dev) { int i; - debug("called"); + debug_called(1); for (i = 0; amr_device_ids[i].vendor != 0; i++) { if ((pci_get_vendor(dev) == amr_device_ids[i].vendor) && @@ -122,7 +129,7 @@ amr_pci_attach(device_t dev) int rid, rtype, error; u_int32_t command; - debug("called"); + debug_called(1); /* * Initialise softc. @@ -131,30 +138,30 @@ amr_pci_attach(device_t dev) bzero(sc, sizeof(*sc)); sc->amr_dev = dev; + /* assume failure is 'not configured' */ + error = ENXIO; + /* - * Determine board type.. + * Determine board type. */ command = pci_read_config(dev, PCIR_COMMAND, 1); - if ((pci_get_vendor(dev) == 0x8086) && (pci_get_device(dev) == 0x1960)) { - sc->amr_type = AMR_TYPE_QUARTZ; - + if (pci_get_device(dev) == 0x1960) { /* * Make sure we are going to be able to talk to this board. */ if ((command & PCIM_CMD_MEMEN) == 0) { device_printf(dev, "memory window not available\n"); - return(ENXIO); + goto out; } + sc->amr_type |= AMR_TYPE_QUARTZ; } else { - sc->amr_type = AMR_TYPE_STD; - /* * Make sure we are going to be able to talk to this board. */ if ((command & PCIM_CMD_PORTEN) == 0) { device_printf(dev, "I/O window not available\n"); - return(ENXIO); + goto out; } } @@ -168,47 +175,381 @@ amr_pci_attach(device_t dev) /* * Allocate the PCI register window. */ - rid = AMR_CFG_BASE; - rtype = (sc->amr_type == AMR_TYPE_QUARTZ) ? SYS_RES_MEMORY : SYS_RES_IOPORT; + rid = PCIR_MAPS; + rtype = AMR_IS_QUARTZ(sc) ? SYS_RES_MEMORY : SYS_RES_IOPORT; sc->amr_reg = bus_alloc_resource(dev, rtype, &rid, 0, ~0, 1, RF_ACTIVE); if (sc->amr_reg == NULL) { - device_printf(sc->amr_dev, "couldn't allocate register window\n"); - amr_free(sc); - return(ENXIO); + device_printf(sc->amr_dev, "can't allocate register window\n"); + goto out; } sc->amr_btag = rman_get_bustag(sc->amr_reg); sc->amr_bhandle = rman_get_bushandle(sc->amr_reg); /* + * Allocate and connect our interrupt. + */ + rid = 0; + sc->amr_irq = bus_alloc_resource(sc->amr_dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); + if (sc->amr_irq == NULL) { + device_printf(sc->amr_dev, "can't allocate interrupt\n"); + goto out; + } + if (bus_setup_intr(sc->amr_dev, sc->amr_irq, INTR_TYPE_BIO, amr_pci_intr, sc, &sc->amr_intr)) { + device_printf(sc->amr_dev, "can't set up interrupt\n"); + goto out; + } + + debug(2, "interrupt attached"); + + /* assume failure is 'out of memory' */ + error = ENOMEM; + + /* * Allocate the parent bus DMA tag appropriate for PCI. */ - error = bus_dma_tag_create(NULL, /* parent */ + if (bus_dma_tag_create(NULL, /* parent */ + 1, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MAXBSIZE, AMR_NSEG, /* maxsize, nsegments */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + &sc->amr_parent_dmat)) { + device_printf(dev, "can't allocate parent DMA tag\n"); + goto out; + } + + /* + * Create DMA tag for mapping buffers into controller-addressable space. + */ + if (bus_dma_tag_create(sc->amr_parent_dmat, /* parent */ + 1, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MAXBSIZE, AMR_NSEG, /* maxsize, nsegments */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ + 0, /* flags */ + &sc->amr_buffer_dmat)) { + device_printf(sc->amr_dev, "can't allocate buffer DMA tag\n"); + goto out; + } + + debug(2, "dma tag done"); + + /* + * Allocate and set up mailbox in a bus-visible fashion. + */ + if ((error = amr_setup_mbox(sc)) != 0) + goto out; + + debug(2, "mailbox setup"); + + /* + * Build the scatter/gather buffers. + */ + if (amr_sglist_map(sc)) + goto out; + + debug(2, "s/g list mapped"); + + /* + * Do bus-independant initialisation, bring controller online. + */ + error = amr_attach(sc); + +out: + if (error) + amr_pci_free(sc); + return(error); +} + +/******************************************************************************** + * Disconnect from the controller completely, in preparation for unload. + */ +static int +amr_pci_detach(device_t dev) +{ + struct amr_softc *sc = device_get_softc(dev); + int error; + + debug_called(1); + + if (sc->amr_state & AMR_STATE_OPEN) + return(EBUSY); + + if ((error = amr_pci_shutdown(dev))) + return(error); + + amr_pci_free(sc); + + return(0); +} + +/******************************************************************************** + * Bring the controller down to a dormant state and detach all child devices. + * + * This function is called before detach, system shutdown, or before performing + * an operation which may add or delete system disks. (Call amr_startup to + * resume normal operation.) + * + * Note that we can assume that the bioq on the controller is empty, as we won't + * allow shutdown if any device is open. + */ +static int +amr_pci_shutdown(device_t dev) +{ + struct amr_softc *sc = device_get_softc(dev); + + debug_called(1); + + /* mark ourselves as in-shutdown */ + sc->amr_state |= AMR_STATE_SHUTDOWN; + + + /* flush controller */ + device_printf(sc->amr_dev, "flushing cache..."); + printf("%s\n", amr_flush(sc) ? "failed" : "done"); + + /* XXX disable interrupts? */ + + return(0); +} + +/******************************************************************************** + * Bring the controller to a quiescent state, ready for system suspend. + */ +static int +amr_pci_suspend(device_t dev) +{ + struct amr_softc *sc = device_get_softc(dev); + + debug_called(1); + + sc->amr_state |= AMR_STATE_SUSPEND; + + /* flush controller */ + device_printf(sc->amr_dev, "flushing cache..."); + printf("%s\n", amr_flush(sc) ? "failed" : "done"); + + /* XXX disable interrupts? */ + + return(0); +} + +/******************************************************************************** + * Bring the controller back to a state ready for operation. + */ +static int +amr_pci_resume(device_t dev) +{ + struct amr_softc *sc = device_get_softc(dev); + + debug_called(1); + + sc->amr_state &= ~AMR_STATE_SUSPEND; + + /* XXX enable interrupts? */ + + return(0); +} + +/******************************************************************************* + * Take an interrupt, or be poked by other code to look for interrupt-worthy + * status. + */ +static void +amr_pci_intr(void *arg) +{ + struct amr_softc *sc = (struct amr_softc *)arg; + + debug_called(2); + + /* collect finished commands, queue anything waiting */ + amr_done(sc); +} + +/******************************************************************************** + * Free all of the resources associated with (sc) + * + * Should not be called if the controller is active. + */ +static void +amr_pci_free(struct amr_softc *sc) +{ + u_int8_t *p; + + debug_called(1); + + amr_free(sc); + + /* destroy data-transfer DMA tag */ + if (sc->amr_buffer_dmat) + bus_dma_tag_destroy(sc->amr_buffer_dmat); + + /* free and destroy DMA memory and tag for s/g lists */ + if (sc->amr_sgtable) + bus_dmamem_free(sc->amr_sg_dmat, sc->amr_sgtable, sc->amr_sg_dmamap); + if (sc->amr_sg_dmat) + bus_dma_tag_destroy(sc->amr_sg_dmat); + + /* free and destroy DMA memory and tag for mailbox */ + if (sc->amr_mailbox) { + p = (u_int8_t *)(uintptr_t)(volatile void *)sc->amr_mailbox; + bus_dmamem_free(sc->amr_sg_dmat, p - 16, sc->amr_sg_dmamap); + } + if (sc->amr_sg_dmat) + bus_dma_tag_destroy(sc->amr_sg_dmat); + + /* disconnect the interrupt handler */ + if (sc->amr_intr) + bus_teardown_intr(sc->amr_dev, sc->amr_irq, sc->amr_intr); + if (sc->amr_irq != NULL) + bus_release_resource(sc->amr_dev, SYS_RES_IRQ, 0, sc->amr_irq); + + /* destroy the parent DMA tag */ + if (sc->amr_parent_dmat) + bus_dma_tag_destroy(sc->amr_parent_dmat); + + /* release the register window mapping */ + if (sc->amr_reg != NULL) + bus_release_resource(sc->amr_dev, + AMR_IS_QUARTZ(sc) ? SYS_RES_MEMORY : SYS_RES_IOPORT, + PCIR_MAPS, sc->amr_reg); +} + +/******************************************************************************** + * Allocate and map the scatter/gather table in bus space. + */ +static void +amr_sglist_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + struct amr_softc *sc = (struct amr_softc *)arg; + + debug_called(1); + + /* save base of s/g table's address in bus space */ + sc->amr_sgbusaddr = segs->ds_addr; +} + +static int +amr_sglist_map(struct amr_softc *sc) +{ + size_t segsize; + int error; + + debug_called(1); + + /* + * Create a single tag describing a region large enough to hold all of + * the s/g lists we will need. + * + * Note that we could probably use AMR_LIMITCMD here, but that may become tunable. + */ + segsize = sizeof(struct amr_sgentry) * AMR_NSEG * AMR_MAXCMD; + error = bus_dma_tag_create(sc->amr_parent_dmat, /* parent */ 1, 0, /* alignment, boundary */ - BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ - MAXBSIZE, AMR_NSEG, /* maxsize, nsegments */ + segsize, 1, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ - BUS_DMA_ALLOCNOW, /* flags */ - &sc->amr_parent_dmat); + 0, /* flags */ + &sc->amr_sg_dmat); if (error != 0) { - device_printf(dev, "can't allocate parent DMA tag\n"); - amr_free(sc); + device_printf(sc->amr_dev, "can't allocate scatter/gather DMA tag\n"); return(ENOMEM); } /* - * Do bus-independant initialisation. + * Allocate enough s/g maps for all commands and permanently map them into + * controller-visible space. + * + * XXX this assumes we can get enough space for all the s/g maps in one + * contiguous slab. We may need to switch to a more complex arrangement where + * we allocate in smaller chunks and keep a lookup table from slot to bus address. + * + * XXX HACK ALERT: at least some controllers don't like the s/g memory being + * allocated below 0x2000. We leak some memory if we get some + * below this mark and allocate again. We should be able to + * avoid this with the tag setup, but that does't seem to work. */ - error = amr_attach(sc); - if (error != 0) { - amr_free(sc); - return(error); +retry: + error = bus_dmamem_alloc(sc->amr_sg_dmat, (void **)&sc->amr_sgtable, BUS_DMA_NOWAIT, &sc->amr_sg_dmamap); + if (error) { + device_printf(sc->amr_dev, "can't allocate s/g table\n"); + return(ENOMEM); } + bus_dmamap_load(sc->amr_sg_dmat, sc->amr_sg_dmamap, sc->amr_sgtable, segsize, amr_sglist_map_helper, sc, 0); + if (sc->amr_sgbusaddr < 0x2000) { + debug(1, "s/g table too low (0x%x), reallocating\n", sc->amr_sgbusaddr); + goto retry; + } + return(0); +} + +/******************************************************************************** + * Allocate and set up mailbox areas for the controller (sc) + * + * The basic mailbox structure should be 16-byte aligned. This means that the + * mailbox64 structure has 4 bytes hanging off the bottom. + */ +static void +amr_setup_mbox_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + struct amr_softc *sc = (struct amr_softc *)arg; + debug_called(1); + + /* save phsyical base of the basic mailbox structure */ + sc->amr_mailboxphys = segs->ds_addr + 16; +} + +static int +amr_setup_mbox(struct amr_softc *sc) +{ + int error; + u_int8_t *p; + + debug_called(1); + /* - * Start the controller. + * Create a single tag describing a region large enough to hold the entire + * mailbox. */ - amr_startup(sc); + error = bus_dma_tag_create(sc->amr_parent_dmat, /* parent */ + 16, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + sizeof(struct amr_mailbox) + 16, 1, /* maxsize, nsegments */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ + 0, /* flags */ + &sc->amr_mailbox_dmat); + if (error != 0) { + device_printf(sc->amr_dev, "can't allocate mailbox tag\n"); + return(ENOMEM); + } + + /* + * Allocate the mailbox structure and permanently map it into + * controller-visible space. + */ + error = bus_dmamem_alloc(sc->amr_mailbox_dmat, (void **)&p, BUS_DMA_NOWAIT, + &sc->amr_mailbox_dmamap); + if (error) { + device_printf(sc->amr_dev, "can't allocate mailbox memory\n"); + return(ENOMEM); + } + bus_dmamap_load(sc->amr_mailbox_dmat, sc->amr_mailbox_dmamap, p, + sizeof(struct amr_mailbox64), amr_setup_mbox_helper, sc, 0); + /* + * Conventional mailbox is inside the mailbox64 region. + */ + bzero(p, sizeof(struct amr_mailbox64)); + sc->amr_mailbox64 = (struct amr_mailbox64 *)(p + 12); + sc->amr_mailbox = (struct amr_mailbox *)(p + 16); + return(0); } |