diff options
| author | Marius Strobl <marius@FreeBSD.org> | 2005-05-19 14:51:10 +0000 |
|---|---|---|
| committer | Marius Strobl <marius@FreeBSD.org> | 2005-05-19 14:51:10 +0000 |
| commit | 65fb49a994d52fdf59fbbc13095993cad1abe329 (patch) | |
| tree | b01c4fc232ba2ecf59b19468701e66ebeeda19ad /sys/dev/esp/esp_sbus.c | |
| parent | 410f3d914ed63cf118fd9470e726cca5b444ab4e (diff) | |
Notes
Diffstat (limited to 'sys/dev/esp/esp_sbus.c')
| -rw-r--r-- | sys/dev/esp/esp_sbus.c | 458 |
1 files changed, 364 insertions, 94 deletions
diff --git a/sys/dev/esp/esp_sbus.c b/sys/dev/esp/esp_sbus.c index 24a6a8f2c665..cc92f19fd6e5 100644 --- a/sys/dev/esp/esp_sbus.c +++ b/sys/dev/esp/esp_sbus.c @@ -109,18 +109,39 @@ struct esp_softc { struct lsi64854_softc *sc_dma; /* pointer to my DMA */ }; -static int esp_sbus_probe(device_t); +static devclass_t esp_devclass; + +static int esp_probe(device_t); +static int esp_dma_attach(device_t); +static int esp_dma_detach(device_t); static int esp_sbus_attach(device_t); static int esp_sbus_detach(device_t); -static int esp_sbus_suspend(device_t); -static int esp_sbus_resume(device_t); +static int esp_suspend(device_t); +static int esp_resume(device_t); + +static device_method_t esp_dma_methods[] = { + DEVMETHOD(device_probe, esp_probe), + DEVMETHOD(device_attach, esp_dma_attach), + DEVMETHOD(device_detach, esp_dma_detach), + DEVMETHOD(device_suspend, esp_suspend), + DEVMETHOD(device_resume, esp_resume), + {0, 0} +}; + +static driver_t esp_dma_driver = { + "esp", + esp_dma_methods, + sizeof(struct esp_softc) +}; + +DRIVER_MODULE(esp, dma, esp_dma_driver, esp_devclass, 0, 0); static device_method_t esp_sbus_methods[] = { - DEVMETHOD(device_probe, esp_sbus_probe), + DEVMETHOD(device_probe, esp_probe), DEVMETHOD(device_attach, esp_sbus_attach), DEVMETHOD(device_detach, esp_sbus_detach), - DEVMETHOD(device_suspend, esp_sbus_suspend), - DEVMETHOD(device_resume, esp_sbus_resume), + DEVMETHOD(device_suspend, esp_suspend), + DEVMETHOD(device_resume, esp_resume), {0, 0} }; @@ -130,7 +151,6 @@ static driver_t esp_sbus_driver = { sizeof(struct esp_softc) }; -static devclass_t esp_devclass; DRIVER_MODULE(esp, sbus, esp_sbus_driver, esp_devclass, 0, 0); /* @@ -146,7 +166,7 @@ static int esp_dma_setup(struct ncr53c9x_softc *, caddr_t *, size_t *, static void esp_dma_go(struct ncr53c9x_softc *); static void esp_dma_stop(struct ncr53c9x_softc *); static int esp_dma_isactive(struct ncr53c9x_softc *); -static void espattach(struct esp_softc *, struct ncr53c9x_glue *); +static int espattach(struct esp_softc *, struct ncr53c9x_glue *); static struct ncr53c9x_glue esp_sbus_glue = { esp_read_reg, @@ -162,7 +182,7 @@ static struct ncr53c9x_glue esp_sbus_glue = { }; static int -esp_sbus_probe(device_t dev) +esp_probe(device_t dev) { const char *name; @@ -170,6 +190,9 @@ esp_sbus_probe(device_t dev) if (strcmp("SUNW,fas", name) == 0) { device_set_desc(dev, "Sun FAS366 Fast-Wide SCSI"); return (BUS_PROBE_DEFAULT); + } else if (strcmp("esp", name) == 0) { + device_set_desc(dev, "Sun ESP SCSI/Sun FAS Fast-SCSI"); + return (BUS_PROBE_DEFAULT); } return (ENXIO); @@ -181,126 +204,291 @@ esp_sbus_attach(device_t dev) struct esp_softc *esc; struct ncr53c9x_softc *sc; struct lsi64854_softc *lsc; + device_t *children; + const char *name; phandle_t node; - int burst; + int burst, error, i, nchildren, slot; esc = device_get_softc(dev); bzero(esc, sizeof(struct esp_softc)); sc = &esc->sc_ncr53c9x; + lsc = NULL; esc->sc_dev = dev; + name = ofw_bus_get_name(dev); node = ofw_bus_get_node(dev); if (OF_getprop(node, "initiator-id", &sc->sc_id, sizeof(sc->sc_id)) == -1) sc->sc_id = 7; - if (OF_getprop(node, "clock-frequency", &sc->sc_freq, - sizeof(sc->sc_freq)) == -1) { - printf("failed to query OFW for clock-frequency\n"); - sc->sc_freq = sbus_get_clockfreq(dev); - } + sc->sc_freq = sbus_get_clockfreq(dev); #ifdef ESP_SBUS_DEBUG device_printf(dev, "%s: sc_id %d, freq %d\n", __func__, sc->sc_id, sc->sc_freq); #endif - /* - * allocate space for DMA, in SUNW,fas there are no separate - * DMA devices - */ - lsc = malloc(sizeof (struct lsi64854_softc), M_DEVBUF, - M_NOWAIT | M_ZERO); - if (lsc == NULL) { - device_printf(dev, "out of memory (lsi64854_softc)\n"); - return (ENOMEM); - } - esc->sc_dma = lsc; + if (strcmp(name, "SUNW,fas") == 0) { + /* + * Allocate space for DMA, in SUNW,fas there are no + * separate DMA devices. + */ + lsc = malloc(sizeof (struct lsi64854_softc), M_DEVBUF, + M_NOWAIT | M_ZERO); + if (lsc == NULL) { + device_printf(dev, "out of memory (lsi64854_softc)\n"); + return (ENOMEM); + } + esc->sc_dma = lsc; - /* - * fas has 2 register spaces: DMA (lsi64854) and SCSI core (ncr53c9x) - */ + /* + * SUNW,fas have 2 register spaces: DMA (lsi64854) and + * SCSI core (ncr53c9x). + */ - /* allocate DMA registers */ - lsc->sc_rid = 0; - if ((lsc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, - &lsc->sc_rid, RF_ACTIVE)) == NULL) { - device_printf(dev, "cannot allocate DMA registers\n"); - free(lsc, M_DEVBUF); - return (ENXIO); - } - lsc->sc_regt = rman_get_bustag(lsc->sc_res); - lsc->sc_regh = rman_get_bushandle(lsc->sc_res); + /* Allocate DMA registers. */ + lsc->sc_rid = 0; + if ((lsc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &lsc->sc_rid, RF_ACTIVE)) == NULL) { + device_printf(dev, "cannot allocate DMA registers\n"); + error = ENXIO; + goto fail_sbus_lsc; + } + lsc->sc_regt = rman_get_bustag(lsc->sc_res); + lsc->sc_regh = rman_get_bushandle(lsc->sc_res); - /* Create a parent DMA tag based on this bus */ - if (bus_dma_tag_create( - NULL, /* parent */ - PAGE_SIZE, 0, /* alignment, boundary */ - BUS_SPACE_MAXADDR, /* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ - 0, /* nsegments */ - BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ - 0, /* flags */ - NULL, NULL, /* no locking */ - &lsc->sc_parent_dmat)) { - device_printf(dev, "cannot allocate parent DMA tag\n"); - free(lsc, M_DEVBUF); - return (ENOMEM); - } - burst = sbus_get_burstsz(dev); + /* Create a parent DMA tag based on this bus. */ + error = bus_dma_tag_create( + NULL, /* parent */ + PAGE_SIZE, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ + 0, /* nsegments */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* no locking */ + &lsc->sc_parent_dmat); + if (error != 0) { + device_printf(dev, "cannot allocate parent DMA tag\n"); + goto fail_sbus_lres; + } + burst = sbus_get_burstsz(dev); #ifdef ESP_SBUS_DEBUG - printf("%s: burst 0x%x\n", __func__, burst); + printf("%s: burst 0x%x\n", __func__, burst); #endif - lsc->sc_burst = (burst & SBUS_BURST_32) ? 32 : - (burst & SBUS_BURST_16) ? 16 : 0; + lsc->sc_burst = (burst & SBUS_BURST_32) ? 32 : + (burst & SBUS_BURST_16) ? 16 : 0; + + lsc->sc_channel = L64854_CHANNEL_SCSI; + lsc->sc_client = sc; + lsc->sc_dev = dev; + + error = lsi64854_attach(lsc); + if (error != 0) { + device_printf(dev, "lsi64854_attach failed\n"); + goto fail_sbus_lpdma; + } + + /* + * Allocate SCSI core registers. + */ + esc->sc_rid = 1; + if ((esc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &esc->sc_rid, RF_ACTIVE)) == NULL) { + device_printf(dev, + "cannot allocate SCSI core registers\n"); + error = ENXIO; + goto fail_sbus_lsi; + } + esc->sc_regt = rman_get_bustag(esc->sc_res); + esc->sc_regh = rman_get_bushandle(esc->sc_res); + } else { + /* + * Search accompanying DMA engine. It should have been + * already attached otherwise there isn't much we can do. + */ + if (device_get_children(device_get_parent(dev), &children, + &nchildren) != 0) { + device_printf(dev, "cannot determine siblings\n"); + return (ENXIO); + } + slot = sbus_get_slot(dev); + for (i = 0; i < nchildren; i++) { + if (device_is_attached(children[i]) && + sbus_get_slot(children[i]) == slot && + strcmp(ofw_bus_get_name(children[i]), "dma") == 0) { + /* XXX hackery */ + esc->sc_dma = (struct lsi64854_softc *) + device_get_softc(children[i]); + break; + } + } + free(children, M_TEMP); + if (esc->sc_dma == NULL) { + device_printf(dev, "cannot find DMA engine\n"); + return (ENXIO); + } + esc->sc_dma->sc_client = sc; + + /* + * Allocate SCSI core registers. + */ + esc->sc_rid = 0; + if ((esc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &esc->sc_rid, RF_ACTIVE)) == NULL) { + device_printf(dev, + "cannot allocate SCSI core registers\n"); + return (ENXIO); + } + esc->sc_regt = rman_get_bustag(esc->sc_res); + esc->sc_regh = rman_get_bushandle(esc->sc_res); + } + + error = espattach(esc, &esp_sbus_glue); + if (error != 0) { + device_printf(dev, "espattach failed\n"); + goto fail_sbus_eres; + } + + return (0); + + fail_sbus_eres: + bus_release_resource(dev, SYS_RES_MEMORY, esc->sc_rid, esc->sc_res); + if (strcmp(name, "SUNW,fas") != 0) + return (error); + fail_sbus_lsi: + lsi64854_detach(lsc); + fail_sbus_lpdma: + bus_dma_tag_destroy(lsc->sc_parent_dmat); + fail_sbus_lres: + bus_release_resource(dev, SYS_RES_MEMORY, lsc->sc_rid, lsc->sc_res); + fail_sbus_lsc: + free(lsc, M_DEVBUF); + return (error); +} + +static int +esp_sbus_detach(device_t dev) +{ + struct esp_softc *esc; + struct ncr53c9x_softc *sc; + struct lsi64854_softc *lsc; + int error; + + esc = device_get_softc(dev); + sc = &esc->sc_ncr53c9x; + lsc = esc->sc_dma; + + bus_teardown_intr(esc->sc_dev, esc->sc_irqres, esc->sc_irq); + error = ncr53c9x_detach(sc); + if (error != 0) + return (error); + bus_release_resource(esc->sc_dev, SYS_RES_IRQ, esc->sc_irqrid, + esc->sc_irqres); + bus_release_resource(dev, SYS_RES_MEMORY, esc->sc_rid, esc->sc_res); + if (strcmp(ofw_bus_get_name(dev), "SUNW,fas") != 0) + return (0); + error = lsi64854_detach(lsc); + if (error != 0) + return (error); + bus_dma_tag_destroy(lsc->sc_parent_dmat); + bus_release_resource(dev, SYS_RES_MEMORY, lsc->sc_rid, lsc->sc_res); + free(lsc, M_DEVBUF); + + return (0); +} + +static int +esp_dma_attach(device_t dev) +{ + struct esp_softc *esc; + struct ncr53c9x_softc *sc; + phandle_t node; + int error; + + esc = device_get_softc(dev); + bzero(esc, sizeof(struct esp_softc)); + sc = &esc->sc_ncr53c9x; + + esc->sc_dev = dev; + node = ofw_bus_get_node(dev); + if (OF_getprop(node, "initiator-id", &sc->sc_id, + sizeof(sc->sc_id)) == -1) + sc->sc_id = 7; + if (OF_getprop(node, "clock-frequency", &sc->sc_freq, + sizeof(sc->sc_freq)) == -1) { + printf("failed to query OFW for clock-frequency\n"); + return (ENXIO); + } - lsc->sc_channel = L64854_CHANNEL_SCSI; - lsc->sc_client = sc; - lsc->sc_dev = dev; +#ifdef ESP_SBUS_DEBUG + device_printf(dev, "%s: sc_id %d, freq %d\n", __func__, sc->sc_id, + sc->sc_freq); +#endif - lsi64854_attach(lsc); + /* XXX hackery */ + esc->sc_dma = (struct lsi64854_softc *) + device_get_softc(device_get_parent(dev)); + esc->sc_dma->sc_client = sc; /* - * allocate SCSI core registers + * Allocate SCSI core registers. */ - esc->sc_rid = 1; + esc->sc_rid = 0; if ((esc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &esc->sc_rid, RF_ACTIVE)) == NULL) { device_printf(dev, "cannot allocate SCSI core registers\n"); - free(lsc, M_DEVBUF); return (ENXIO); } esc->sc_regt = rman_get_bustag(esc->sc_res); esc->sc_regh = rman_get_bushandle(esc->sc_res); - espattach(esc, &esp_sbus_glue); + error = espattach(esc, &esp_sbus_glue); + if (error != 0) { + device_printf(dev, "espattach failed\n"); + goto fail_dma_eres; + } return (0); + + fail_dma_eres: + bus_release_resource(dev, SYS_RES_MEMORY, esc->sc_rid, esc->sc_res); + return (error); } static int -esp_sbus_detach(device_t dev) +esp_dma_detach(device_t dev) { - struct ncr53c9x_softc *sc; struct esp_softc *esc; + struct ncr53c9x_softc *sc; + int error; esc = device_get_softc(dev); sc = &esc->sc_ncr53c9x; - return (ncr53c9x_detach(sc, 0)); + + bus_teardown_intr(esc->sc_dev, esc->sc_irqres, esc->sc_irq); + error = ncr53c9x_detach(sc); + if (error != 0) + return (error); + bus_release_resource(esc->sc_dev, SYS_RES_IRQ, esc->sc_irqrid, + esc->sc_irqres); + bus_release_resource(dev, SYS_RES_MEMORY, esc->sc_rid, esc->sc_res); + + return (0); } static int -esp_sbus_suspend(device_t dev) +esp_suspend(device_t dev) { return (ENXIO); } static int -esp_sbus_resume(device_t dev) +esp_resume(device_t dev) { return (ENXIO); @@ -309,11 +497,19 @@ esp_sbus_resume(device_t dev) /* * Attach this instance, and then all the sub-devices */ -static void +static int espattach(struct esp_softc *esc, struct ncr53c9x_glue *gluep) { struct ncr53c9x_softc *sc = &esc->sc_ncr53c9x; unsigned int uid = 0; + int error; + + /* + * The `ESC' DMA chip must be reset before we can access + * the ESP registers. + */ + if (esc->sc_dma->sc_rev == DMAREV_ESC) + DMA_RESET(esc->sc_dma); /* * Set up glue for MI code early; we use some of it here. @@ -330,13 +526,30 @@ espattach(struct esp_softc *esc, struct ncr53c9x_glue *gluep) */ /* + * Read the part-unique ID code of the SCSI chip. The contained + * value is only valid if all of the following conditions are met: + * - After power-up or chip reset. + * - Before any value is written to this register. + * - The NCRCFG2_FE bit is set. + * - A (NCRCMD_NOP | NCRCMD_DMA) command has been issued. + */ + NCRCMD(sc, NCRCMD_RSTCHIP); + NCRCMD(sc, NCRCMD_NOP); + sc->sc_cfg2 = NCRCFG2_FE; + NCR_WRITE_REG(sc, NCR_CFG2, sc->sc_cfg2); + NCRCMD(sc, NCRCMD_NOP | NCRCMD_DMA); + uid = NCR_READ_REG(sc, NCR_UID); + + /* * It is necessary to try to load the 2nd config register here, * to find out what rev the esp chip is, else the ncr53c9x_reset * will not set up the defaults correctly. */ sc->sc_cfg1 = sc->sc_id | NCRCFG1_PARENB; + NCR_WRITE_REG(sc, NCR_CFG1, sc->sc_cfg1); + sc->sc_cfg2 = 0; + NCR_WRITE_REG(sc, NCR_CFG2, sc->sc_cfg2); sc->sc_cfg2 = NCRCFG2_SCSI2 | NCRCFG2_RPE; - sc->sc_cfg3 = NCRCFG3_CDB; NCR_WRITE_REG(sc, NCR_CFG2, sc->sc_cfg2); if ((NCR_READ_REG(sc, NCR_CFG2) & ~NCRCFG2_RSVD) != @@ -357,15 +570,33 @@ espattach(struct esp_softc *esc, struct ncr53c9x_glue *gluep) sc->sc_cfg2 |= NCRCFG2_FE; sc->sc_cfg3 = 0; NCR_WRITE_REG(sc, NCR_CFG3, sc->sc_cfg3); - sc->sc_rev = NCR_VARIANT_ESP200; - - /* - * XXX spec says it's valid after power up or chip - * reset. - */ - uid = NCR_READ_REG(sc, NCR_UID); - if (((uid & 0xf8) >> 3) == 0x0a) /* XXX */ - sc->sc_rev = NCR_VARIANT_FAS366; + if (sc->sc_freq <= 25) + sc->sc_rev = NCR_VARIANT_ESP200; + else { + switch ((uid & 0xf8) >> 3) { + case 0x00: + sc->sc_rev = NCR_VARIANT_FAS100A; + break; + case 0x02: + if ((uid & 0x07) == 0x02) + sc->sc_rev = NCR_VARIANT_FAS216; + else + sc->sc_rev = NCR_VARIANT_FAS236; + break; + case 0x0a: + sc->sc_rev = NCR_VARIANT_FAS366; + break; + default: + /* + * We could just treat unknown chips + * as ESP200 but then we would most + * likely drive them out of specs. + */ + device_printf(esc->sc_dev, + "Unknown chip\n"); + return (ENXIO); + } + } } } @@ -390,10 +621,7 @@ espattach(struct esp_softc *esc, struct ncr53c9x_glue *gluep) */ sc->sc_minsync = 1000 / sc->sc_freq; - /* limit minsync due to unsolved performance issues */ - sc->sc_maxsync = sc->sc_minsync; sc->sc_maxoffset = 15; - sc->sc_extended_geom = 1; /* @@ -409,31 +637,60 @@ espattach(struct esp_softc *esc, struct ncr53c9x_glue *gluep) break; case NCR_VARIANT_ESP100A: - sc->sc_maxwidth = 1; + sc->sc_maxwidth = 0; sc->sc_maxxfer = 64 * 1024; /* Min clocks/byte is 5 */ sc->sc_minsync = ncr53c9x_cpb2stp(sc, 5); break; case NCR_VARIANT_ESP200: + sc->sc_maxwidth = 0; + sc->sc_maxxfer = 16 * 1024 * 1024; + /* Min clocks/byte is 5 */ + sc->sc_minsync = ncr53c9x_cpb2stp(sc, 5); + break; + + case NCR_VARIANT_FAS100A: + case NCR_VARIANT_FAS216: + case NCR_VARIANT_FAS236: + /* + * The onboard SCSI chips in Sun Ultra 1 are actually + * documented to be NCR53C9X which use NCRCFG3_FCLK and + * NCRCFG3_FSCSI. BSD/OS however probes these chips as + * FAS100A and uses NCRF9XCFG3_FCLK and NCRF9XCFG3_FSCSI + * instead which seems to be correct as otherwise sync + * negotiation just doesn't work. Using NCRF9XCFG3_FCLK + * and NCRF9XCFG3_FSCSI with these chips in fact also + * yields Fast-SCSI speed. + */ + sc->sc_features = NCR_F_FASTSCSI; + sc->sc_cfg3 = NCRF9XCFG3_FCLK; + sc->sc_cfg3_fscsi = NCRF9XCFG3_FSCSI; + sc->sc_maxwidth = 0; + sc->sc_maxxfer = 16 * 1024 * 1024; + break; + case NCR_VARIANT_FAS366: sc->sc_maxwidth = 1; sc->sc_maxxfer = 16 * 1024 * 1024; - /* XXX - do actually set FAST* bits */ break; } + /* Limit minsync due to unsolved performance issues. */ + sc->sc_maxsync = sc->sc_minsync; + /* Establish interrupt channel */ esc->sc_irqrid = 0; if ((esc->sc_irqres = bus_alloc_resource_any(esc->sc_dev, SYS_RES_IRQ, &esc->sc_irqrid, RF_SHAREABLE|RF_ACTIVE)) == NULL) { - device_printf(esc->sc_dev, "Cannot allocate interrupt\n"); - return; + device_printf(esc->sc_dev, "cannot allocate interrupt\n"); + return (ENXIO); } if (bus_setup_intr(esc->sc_dev, esc->sc_irqres, INTR_TYPE_BIO|INTR_MPSAFE, ncr53c9x_intr, sc, &esc->sc_irq)) { - device_printf(esc->sc_dev, "Cannot set up interrupt\n"); - return; + device_printf(esc->sc_dev, "cannot set up interrupt\n"); + error = ENXIO; + goto fail_ires; } /* Turn on target selection using the `DMA' method */ @@ -442,7 +699,20 @@ espattach(struct esp_softc *esc, struct ncr53c9x_glue *gluep) /* Do the common parts of attachment. */ sc->sc_dev = esc->sc_dev; - ncr53c9x_attach(sc); + error = ncr53c9x_attach(sc); + if (error != 0) { + device_printf(esc->sc_dev, "ncr53c9x_attach failed\n"); + goto fail_intr; + } + + return (0); + + fail_intr: + bus_teardown_intr(esc->sc_dev, esc->sc_irqres, esc->sc_irq); + fail_ires: + bus_release_resource(esc->sc_dev, SYS_RES_IRQ, esc->sc_irqrid, + esc->sc_irqres); + return (error); } /* |
