diff options
author | Christopher Pascoe <pascoe@cvs.openbsd.org> | 2007-03-04 14:23:09 +0000 |
---|---|---|
committer | Christopher Pascoe <pascoe@cvs.openbsd.org> | 2007-03-04 14:23:09 +0000 |
commit | a6b45738d29b3bc5aa4971ec65457e10c9e20cc5 (patch) | |
tree | 968c6b522237453efd70e5e9ca3eaede58f38c2c /sys/dev | |
parent | 54687401492eea25a87bf198fc4f1662932d7aa3 (diff) |
Now actually activate ports and try to detect devices.
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/pci/ahci.c | 104 |
1 files changed, 101 insertions, 3 deletions
diff --git a/sys/dev/pci/ahci.c b/sys/dev/pci/ahci.c index 5d852d87289..975b745611c 100644 --- a/sys/dev/pci/ahci.c +++ b/sys/dev/pci/ahci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ahci.c,v 1.60 2007/03/04 14:06:34 pascoe Exp $ */ +/* $OpenBSD: ahci.c,v 1.61 2007/03/04 14:23:08 pascoe Exp $ */ /* * Copyright (c) 2006 David Gwynne <dlg@openbsd.org> @@ -646,6 +646,7 @@ ahci_port_alloc(struct ahci_softc *sc, u_int port) u_int8_t *kva; u_int64_t dva; int i, rc = ENOMEM; + u_int32_t cmd; ap = malloc(sizeof(struct ahci_port), M_DEVBUF, M_NOWAIT); if (ap == NULL) { @@ -655,7 +656,6 @@ ahci_port_alloc(struct ahci_softc *sc, u_int port) } bzero(ap, sizeof(struct ahci_port)); - ap->ap_sc = sc; sc->sc_ports[port] = ap; if (bus_space_subregion(sc->sc_iot, sc->sc_ioh, @@ -665,6 +665,30 @@ ahci_port_alloc(struct ahci_softc *sc, u_int port) goto freeport; } + ap->ap_sc = sc; + + /* Disable port interrupts */ + ahci_pwrite(ap, AHCI_PREG_IE, 0); + + /* Sec 10.1.2 - deinitialise port if it is already running */ + cmd = ahci_pread(ap, AHCI_PREG_CMD); + if (ISSET(cmd, (AHCI_PREG_CMD_ST | AHCI_PREG_CMD_CR | + AHCI_PREG_CMD_FRE | AHCI_PREG_CMD_FR)) || + ISSET(ahci_pread(ap, AHCI_PREG_SCTL), AHCI_PREG_SCTL_DET)) { + int r; + + r = ahci_port_stop(ap, 1); + if (r) { + printf("%s: unable to disable %s, ignoring port %d\n", + DEVNAME(sc), r == 2 ? "CR" : "FR", port); + rc = ENXIO; + goto freeport; + } + + /* Write DET to zero */ + ahci_pwrite(ap, AHCI_PREG_SCTL, 0); + } + /* Allocate RFIS */ ap->ap_dmamem_rfis = ahci_dmamem_alloc(sc, sizeof(struct ahci_rfis)); if (ap->ap_dmamem_rfis == NULL) @@ -677,6 +701,18 @@ ahci_port_alloc(struct ahci_softc *sc, u_int port) ahci_pwrite(ap, AHCI_PREG_FBU, htole32((u_int32_t)(dva >> 32))); ahci_pwrite(ap, AHCI_PREG_FB, htole32((u_int32_t)dva)); + /* Enable FIS reception and activate port. */ + cmd = ahci_pread(ap, AHCI_PREG_CMD) & ~AHCI_PREG_CMD_ICC; + cmd |= AHCI_PREG_CMD_FRE | AHCI_PREG_CMD_POD | AHCI_PREG_CMD_SUD; + ahci_pwrite(ap, AHCI_PREG_CMD, cmd | AHCI_PREG_CMD_ICC_ACTIVE); + + /* Check whether port activated. Skip it if not. */ + cmd = ahci_pread(ap, AHCI_PREG_CMD) & ~AHCI_PREG_CMD_ICC; + if (!ISSET(cmd, AHCI_PREG_CMD_FRE)) { + rc = ENXIO; + goto freeport; + } + /* Allocate a CCB for each command slot */ ap->ap_ccbs = malloc(sizeof(struct ahci_ccb) * sc->sc_ncmds, M_DEVBUF, M_NOWAIT); @@ -720,6 +756,11 @@ nomem: goto freeport; } + /* + * NB: ahci_start assumes a 1:1 mapping of CCB slot number + * to command slot and command table positions in its + * bus_dmasync_calls. + */ ccb->ccb_slot = i; ccb->ccb_port = ap; ccb->ccb_cmd_table = (struct ahci_cmd_table *) @@ -730,8 +771,57 @@ nomem: ahci_put_ccb(ap, ccb); } - rc = 0; + /* Wait for ICC change to complete */ + ahci_pwait_clr(ap, AHCI_PREG_CMD, AHCI_PREG_CMD_ICC); + + /* Reset port */ + rc = ahci_port_portreset(ap); + printf("ahci_portreset returned %d\n", rc); + switch (rc) { + case ENODEV: + printf("%s: ", DEVNAME(sc)); + switch (ahci_pread(ap, AHCI_PREG_SSTS) & AHCI_PREG_SSTS_DET) { + case AHCI_PREG_SSTS_DET_DEV_NE: + printf("device not communicating"); + break; + case AHCI_PREG_SSTS_DET_PHYOFFLINE: + printf("PHY offline"); + break; + default: + printf("no device detected"); + } + printf(" on port %d, disabling.\n", port); + goto freeport; + + case EBUSY: + printf("%s: device on port %d didn't come ready, TFD: 0x%b\n", + DEVNAME(sc), port, ahci_pread(ap, AHCI_PREG_TFD), + AHCI_PFMT_TFD_STS); + + /* Try a soft reset to clear busy */ + rc = ahci_port_softreset(ap); + printf("ahci_portsoftreset returned %d\n", rc); + if (rc) { + printf("%s: unable to communicate with device on port " + "%d, disabling\n", DEVNAME(sc), port); + goto freeport; + } + break; + default: + break; + } + + /* Enable command transfers on port */ + if (ahci_port_start(ap, 0)) { + printf("%s: failed to start command DMA on port %d, " + "disabling\n", DEVNAME(sc), port); + rc = ENXIO; /* couldn't start port */ + } + + /* Flush interrupts for port */ + ahci_pwrite(ap, AHCI_PREG_IS, ahci_pread(ap, AHCI_PREG_IS)); + ahci_write(sc, AHCI_REG_IS, 1 << port); freeport: if (rc != 0) ahci_port_free(sc, port); @@ -745,6 +835,14 @@ ahci_port_free(struct ahci_softc *sc, u_int port) struct ahci_port *ap = sc->sc_ports[port]; struct ahci_ccb *ccb; + /* Ensure port is disabled and its interrupts are flushed */ + if (ap->ap_sc) { + ahci_pwrite(ap, AHCI_PREG_CMD, 0); + ahci_pwrite(ap, AHCI_PREG_IE, 0); + ahci_pwrite(ap, AHCI_PREG_IS, ahci_pread(ap, AHCI_PREG_IS)); + ahci_write(sc, AHCI_REG_IS, 1 << port); + } + if (ap->ap_ccbs) { while ((ccb = ahci_get_ccb(ap)) != NULL) bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap); |