These patches have been updated to -current as of the past weekend. They increase the robustness of SATA disk errors (media errors and drive going away such as drives that spin up and down). Also has code to support hot swap on both controllers. This is only HW support and does not address other ata/FreeBSD issues. That isn't quite right ... some ata changes have been made to avoid panics in probes etc. to support the HW changes. It would be nice to get this into FreeBSD in some form then I can help with other issues. I have -stable code to read and write the Intel RAID meta-data but not Adaptec. My latest -stable patches supports Intel/ Promise SATA hot-swap and the Intel RAID meta-data with ata-raid. Doug A. Index: sys/dev/ata/ata-all.c =================================================================== RCS file: /cvs/src/sys/dev/ata/ata-all.c,v retrieving revision 1.208 diff -u -p -r1.208 ata-all.c --- sys/dev/ata/ata-all.c 13 Apr 2004 09:44:20 -0000 1.208 +++ sys/dev/ata/ata-all.c 4 May 2004 18:16:49 -0000 _at__at_ -119,7 +119,6 _at__at_ ata_probe(device_t dev) ch->device[SLAVE].channel = ch; ch->device[SLAVE].unit = ATA_SLAVE; ch->device[SLAVE].mode = ATA_PIO; - ch->dev = dev; ch->state = ATA_IDLE; /* initialise device(s) on this channel */ _at__at_ -308,6 +307,19 _at__at_ ata_reinit(struct ata_channel *ch) return 0; } +static void +ata_reinit_task(void *data, int dummy) +{ + ata_reinit(data); +} + +void +ata_queue_reinit(struct ata_channel *ch) +{ + TASK_INIT(&ch->task, 0, ata_reinit_task, ch); + taskqueue_enqueue(taskqueue_thread, &ch->task); +} + int ata_suspend(device_t dev) { Index: sys/dev/ata/ata-all.h =================================================================== RCS file: /cvs/src/sys/dev/ata/ata-all.h,v retrieving revision 1.78 diff -u -p -r1.78 ata-all.h --- sys/dev/ata/ata-all.h 13 Apr 2004 09:44:20 -0000 1.78 +++ sys/dev/ata/ata-all.h 4 May 2004 18:16:49 -0000 _at__at_ -363,6 +363,10 _at__at_ struct ata_channel { struct mtx queue_mtx; /* queue lock */ TAILQ_HEAD(, ata_request) ata_queue; /* head of ATA queue */ struct ata_request *running; /* currently running request */ + int sata_master_idx; + int sata_slave_idx; + struct task task; /* task management */ + struct callout_handle timeout_handle; /* poll channel */ }; /* disk bay/enclosure related */ _at__at_ -398,6 +402,7 _at__at_ int ata_limit_mode(struct ata_device *at /* ata-queue.c: */ int ata_reinit(struct ata_channel *ch); +void ata_queue_reinit(struct ata_channel *ch); void ata_start(struct ata_channel *ch); int ata_controlcmd(struct ata_device *atadev, u_int8_t command, u_int16_t feature, u_int64_t lba, u_int16_t count); int ata_atapicmd(struct ata_device *atadev, u_int8_t *ccb, caddr_t data, int count, int flags, int timeout); Index: sys/dev/ata/ata-chipset.c =================================================================== RCS file: /cvs/src/sys/dev/ata/ata-chipset.c,v retrieving revision 1.70 diff -u -p -r1.70 ata-chipset.c --- sys/dev/ata/ata-chipset.c 24 Apr 2004 15:54:20 -0000 1.70 +++ sys/dev/ata/ata-chipset.c 4 May 2004 18:16:49 -0000 _at__at_ -81,6 +81,11 _at__at_ static int ata_highpoint_check_80pin(str static int ata_intel_chipinit(device_t); static void ata_intel_old_setmode(struct ata_device *, int); static void ata_intel_new_setmode(struct ata_device *, int); +static int ata_intel_sata_allocate(device_t, struct ata_channel *); +static void ata_intel_map_sata_ports(device_t, struct ata_channel *); +static void ata_intel_sata_intr(void *); +static void ata_intel_sata_status(struct ata_channel *); +static void ata_intel_sata_reset(struct ata_channel *); static int ata_national_chipinit(device_t); static void ata_national_setmode(struct ata_device *, int); static int ata_nvidia_chipinit(device_t); _at__at_ -827,11 +832,11 _at__at_ ata_intel_ident(device_t dev) { ATA_I82801DB, 0, 0, 0x00, ATA_UDMA5, "Intel ICH4" }, { ATA_I82801DB_1, 0, 0, 0x00, ATA_UDMA5, "Intel ICH4" }, { ATA_I82801EB, 0, 0, 0x00, ATA_UDMA5, "Intel ICH5" }, - { ATA_I82801EB_1, 0, 0, 0x00, ATA_SA150, "Intel ICH5" }, - { ATA_I82801EB_2, 0, 0, 0x00, ATA_SA150, "Intel ICH5" }, - { ATA_I6300ESB, 0, 0, 0x00, ATA_UDMA5, "Intel ICH5" }, - { ATA_I6300ESB_1, 0, 0, 0x00, ATA_SA150, "Intel ICH5" }, - { ATA_I6300ESB_2, 0, 0, 0x00, ATA_SA150, "Intel ICH5" }, + { ATA_I82801EB_1, 0, 0, ISATA, ATA_SA150, "Intel ICH5" }, + { ATA_I82801EB_2, 0, 0, ISATA, ATA_SA150, "Intel ICH5" }, + { ATA_I6300ESB, 0, 0, 0x00, ATA_UDMA5, "Intel ICH6" }, + { ATA_I6300ESB_1, 0, 0, ISATA, ATA_SA150, "Intel ICH6" }, + { ATA_I6300ESB_2, 0, 0, ISATA, ATA_SA150, "Intel ICH6" }, { 0, 0, 0, 0, 0, 0}}; char buffer[64]; _at__at_ -849,6 +854,35 _at__at_ static int ata_intel_chipinit(device_t dev) { struct ata_pci_controller *ctlr = device_get_softc(dev); + int rid = ATA_IRQ_RID; + + if (ctlr->chip->cfg2 & ISATA) { + ctlr->allocate = ata_intel_sata_allocate; + + /* Clear SATA error registers */ + pci_write_config(dev, 0xa0, 0x54, 4); + pci_write_config(dev, 0xa4, pci_read_config(dev, 0xa4, 4), 4); + pci_write_config(dev, 0xa0, 0x64, 4); + pci_write_config(dev, 0xa4, pci_read_config(dev, 0xa4, 4), 4); + + if (!ata_legacy(dev)) { + if (!(ctlr->r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_SHAREABLE + | RF_ACTIVE))) { + device_printf(dev, "unable to map interrupt\n"); + return ENXIO; + } + if ((bus_setup_intr(dev, ctlr->r_irq, ATA_INTR_FLAGS, + ata_intel_sata_intr, ctlr, &ctlr->handle))) { + device_printf(dev, "unable to setup interrupt\n"); + return ENXIO; + } + } + + ctlr->setmode = ata_sata_setmode; + + return 0; + } if (ata_setup_interrupt(dev)) return ENXIO; _at__at_ -944,6 +978,228 _at__at_ ata_intel_new_setmode(struct ata_device atadev->mode = mode; } +static int +ata_intel_sata_allocate(device_t dev, struct ata_channel *ch) +{ + struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev)); + struct resource *io = NULL, *altio = NULL; + int i, rid; + + rid = ATA_IOADDR_RID; + io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, + 0, ~0, ATA_IOSIZE, RF_ACTIVE); + if (!io) + return ENXIO; + + rid = ATA_ALTADDR_RID; + altio = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, + 0, ~0, ATA_ALTIOSIZE, RF_ACTIVE); + if (!altio) { + bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, io); + return ENXIO; + } + + for (i = ATA_DATA; i <= ATA_STATUS; i ++) { + ch->r_io[i].res = io; + ch->r_io[i].offset = i; + } + ch->r_io[ATA_ALTSTAT].res = altio; + ch->r_io[ATA_ALTSTAT].offset = ata_legacy(device_get_parent(dev)) ? 0 : 2; + ch->r_io[ATA_IDX_ADDR].res = io; + + if (ctlr->r_res1) { + for (i = ATA_BMCMD_PORT; i <= ATA_BMDTP_PORT; i++) { + ch->r_io[i].res = ctlr->r_res1; + ch->r_io[i].offset = (i - ATA_BMCMD_PORT)+(ch->unit * ATA_BMIOSIZE); + } + + /* if simplex controller, only allow DMA on primary channel */ + ATA_IDX_OUTB(ch, ATA_BMSTAT_PORT, ATA_IDX_INB(ch, ATA_BMSTAT_PORT) & + (ATA_BMSTAT_DMA_MASTER | ATA_BMSTAT_DMA_SLAVE)); + if (ch->unit > 0 && + (ATA_IDX_INB(ch, ATA_BMSTAT_PORT) & ATA_BMSTAT_DMA_SIMPLEX)) + device_printf(dev, "simplex device, DMA on primary only\n"); + else + ctlr->dmainit(ch); + } + + ch->reset = ata_intel_sata_reset; + ata_intel_map_sata_ports(dev, ch); + + ata_generic_hw(ch); + + return 0; +} + +void +ata_intel_map_sata_ports(device_t dev, struct ata_channel *ch) +{ + device_t parent = device_get_parent(dev); + int unit = ch->unit; + int sata_config; + + sata_config = pci_read_config(parent, 0x90, 1); + + switch (sata_config & 7) { + case 0: /* SATA P0 on pri master SATA P1 on sec master */ + if (unit == 0) { + ch->sata_master_idx = 1; + device_printf(dev, "SATA P0 primary master\n"); + } + if (unit == 1) { + ch->sata_master_idx = 2; + device_printf(dev, "SATA P1 secondary master\n"); + } + break; + case 1: /* SATA P0 on sec master SATA P1 on pri master */ + if (unit == 0) { + ch->sata_master_idx = 2; + device_printf(dev, "SATA P0 secondary master\n"); + } + if (unit == 1) { + ch->sata_master_idx = 0; + device_printf(dev, "SATA P1 primary master\n"); + } + break; + case 4: /* SATA P0 on pri master SATA P1 on pri slave */ + if (unit == 0) { + ch->sata_master_idx = 1; + ch->sata_slave_idx = 2; + device_printf(dev, "SATA P0 primary master\n"); + device_printf(dev, "SATA P1 primary slave\n"); + } + break; + case 5: /* SATA P0 on pri slave SATA P1 on pri master */ + if (unit == 0) { + ch->sata_master_idx = 2; + ch->sata_slave_idx = 1; + device_printf(dev, "SATA P0 primary slave\n"); + device_printf(dev, "SATA P1 primary master\n"); + } + break; + case 6: /* SATA P0 on sec master SATA P1 on sec slave */ + if (unit == 1) { + ch->sata_master_idx = 1; + ch->sata_slave_idx = 2; + device_printf(dev, "SATA P0 secondary master\n"); + device_printf(dev, "SATA P1 secondary slave\n"); + } + break; + case 7: /* SATA P0 on sec slave SATA P1 on sec master */ + if (unit == 1) { + ch->sata_master_idx = 2; + ch->sata_slave_idx = 1; + device_printf(dev, "SATA P0 secondary slave\n"); + device_printf(dev, "SATA P1 secondary master\n"); + } + break; + } +} + +static void +ata_intel_sata_status(struct ata_channel *ch) +{ + device_t parent; + struct ata_pci_controller *ctlr; + u_int32_t sstatus, serror, scontrol; + int index = 0, device, base; + + if (!ch->dev) /* Channel not ready */ + return; + parent = device_get_parent(ch->dev); + ctlr = device_get_softc(parent); + + ch->locking(ch, ATA_LF_LOCK); + untimeout((timeout_t *)ata_intel_sata_status, ch, ch->timeout_handle); + + index = ch->sata_master_idx | ch->sata_slave_idx;; + + for (device = 1; device <= 2; device++) { + if (index & device) { + if (device == 1) + base = 0x50; + else + base = 0x60; + + pci_write_config(parent, 0xa0, base, 4); + sstatus = pci_read_config(parent, 0xa4, 4); + + pci_write_config(parent, 0xa0, base + 4, 4); + serror = pci_read_config(parent, 0xa4, 4); + + pci_write_config(parent, 0xa0, base + 8, 4); + scontrol = pci_read_config(parent, 0xa4, 4); + + if (serror) { + if (sstatus == 0x05) { /* Drive left reset port */ + pci_write_config(parent, 0x92, + pci_read_config(parent, 0x92, 2) & ~device, 2); + pci_write_config(parent, 0x92, + pci_read_config(parent, 0x92, 2) | device, 2); + } + + /* Loop until SATA port is okay otherwise we can hang */ + for (;;) { + pci_write_config(parent, 0xa0, base, 4); + sstatus = pci_read_config(parent, 0xa4, 4); + + pci_write_config(parent, 0xa0, base + 4, 4); + serror = pci_read_config(parent, 0xa4, 4); + /* Acknowledge serror */ + pci_write_config(parent, 0xa4, serror, 4); + + pci_write_config(parent, 0xa0, base + 8, 4); + scontrol = pci_read_config(parent, 0xa4, 4); + device_printf(ch->dev, + "Intel SATA P%d status %x error %x scontrol %x %d %x\n", + device - 1, sstatus, serror, scontrol, + ch->unit, ch->devices); + if (!serror && sstatus != 5) + break; + + DELAY(100000); + } + ata_queue_reinit(ch); + } + } + } + ch->timeout_handle = timeout((timeout_t*)ata_intel_sata_status, + ch, 10 * hz); + + ch->locking(ch, ATA_LF_UNLOCK); +} + +static void +ata_intel_sata_intr(void *data) +{ + struct ata_pci_controller *ctlr = data; + struct ata_channel *ch; + int unit; + + /* implement this as a toggle instead to balance load XXX */ + for (unit = 0; unit < 2; unit++) { + if (!(ch = ctlr->interrupt[unit].argument)) + continue; + ata_intel_sata_status(ch); + if (ch->dma && (ch->dma->flags & ATA_DMA_ACTIVE)) { + int bmstat = ATA_IDX_INB(ch, ATA_BMSTAT_PORT) & ATA_BMSTAT_MASK; + + if ((bmstat & (ATA_BMSTAT_ACTIVE | ATA_BMSTAT_INTERRUPT)) != + ATA_BMSTAT_INTERRUPT) + continue; + ATA_IDX_OUTB(ch, ATA_BMSTAT_PORT, bmstat & ~ATA_BMSTAT_ERROR); + DELAY(1); + } + ctlr->interrupt[unit].function(ch); + } +} + +static void +ata_intel_sata_reset(struct ata_channel *ch) +{ + ata_intel_sata_status(ch); +} + /* * National chipset support functions */ _at__at_ -1182,6 +1438,8 _at__at_ ata_promise_chipinit(device_t dev) struct ata_pci_controller *ctlr = device_get_softc(dev); int rid = ATA_IRQ_RID; + mtx_init(&ctlr->mtx, "ATA controller lock", MTX_DEF, 0); + if (!(ctlr->r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE))) { device_printf(dev, "unable to map interrupt\n"); _at__at_ -1238,7 +1496,7 _at__at_ ata_promise_chipinit(device_t dev) ((ATA_INL(ctlr->r_res2, 0x48) & 0x02) > 0) + 2; } else if (ctlr->chip->cfg2 & PRSATA) { - ATA_OUTL(ctlr->r_res2, 0x06c, 0x00ff0033); + ATA_OUTL(ctlr->r_res2, 0x06c, 0x000000ff); ctlr->channels = ((ATA_INL(ctlr->r_res2, 0x48) & 0x02) > 0) + 3; } else _at__at_ -1302,6 +1560,7 _at__at_ ata_promise_mio_allocate(device_t dev, s ch->r_io[ATA_IDX_ADDR].res = ctlr->r_res2; ch->flags |= ATA_USE_16BIT; ch->reset = ata_promise_mio_reset; + ch->reset(ch); ctlr->dmainit(ch); ata_generic_hw(ch); _at__at_ -1317,13 +1576,50 _at__at_ ata_promise_mio_intr(void *data) { struct ata_pci_controller *ctlr = data; struct ata_channel *ch; - u_int32_t vector = ATA_INL(ctlr->r_res2, 0x00040); + u_int32_t vector, plug_status; + int offset = (ctlr->chip->cfg2 & PRSX4X) ? 0x000c0000 : 0; int unit; + if (ctlr->chip->cfg2 & PRSATA) { + /* Check for all drive changes first */ + plug_status = ATA_INL(ctlr->r_res2, 0x06c); + for (unit = 0; unit < ctlr->channels; unit++) { + if (plug_status & (1 << unit)) { + /* drive gone */ + ATA_OUTL(ctlr->r_res2, 0x06c, 1 << unit); + if ((ch = ctlr->interrupt[unit].argument)) { + device_printf(ch->dev, + "device on port %d gone\n", unit + 1); + ata_queue_reinit(ch); + } + } + + if (plug_status & (1 << (unit + 4))) { + /* drive connected */ + ATA_OUTL(ctlr->r_res2, 0x06c, 1 << (unit + 4)); + if ((ch = ctlr->interrupt[unit].argument)) { + device_printf(ch->dev, + "device on port %d connected\n", unit + 1); + ata_queue_reinit(ch); + } + } + } + } + + vector = ATA_INL(ctlr->r_res2, 0x00040); for (unit = 0; unit < ctlr->channels; unit++) { if (vector & (1 << (unit + 1))) { if ((ch = ctlr->interrupt[unit].argument)) + if (ATA_INL(ctlr->r_res2, offset + ((ch->unit + 1) << 2)) + & 1 << (ch->unit + 16)) + ch->reset(ch); ctlr->interrupt[unit].function(ch); + ATA_OUTL(ctlr->r_res2, offset + 0x0260 + (ch->unit << 7), + (ATA_INL(ctlr->r_res2, + offset + 0x0260 + (ch->unit << 7)) & + ~0x00003f9f) | (ch->unit + 1)); + + ATA_OUTL(ctlr->r_res2, offset + ((ch->unit + 1) << 2), 0x1); } } } _at__at_ -1389,7 +1685,32 _at__at_ ata_promise_mio_reset(struct ata_channel struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(ch->dev)); int offset = (ctlr->chip->cfg2 & PRSX4X) ? 0x000c0000 : 0; + u_int32_t control; + + mtx_lock(&ctlr->mtx); + control = ATA_INL(ctlr->r_res2, 0x0048); + /* channel reset */ + control &= ~(1 << (ch->unit + 12)); + ATA_OUTL(ctlr->r_res2, 0x0048, control); + control |= 1 << (ch->unit + 12); + ATA_OUTL(ctlr->r_res2, 0x0048, control); + + DELAY(1000); /* DJA XXX */ + mtx_unlock(&ctlr->mtx); + + if (ctlr->chip->cfg2 & PRSATA) { + /* clear plug status that bounces during hard reset */ + ATA_OUTL(ctlr->r_res2, 0x06c, (1 << ch->unit) | (1 << (ch->unit + 4))); + } + + /* ATA module reset */ + ATA_OUTL(ctlr->r_res2, offset + 0x0260 + (ch->unit << 7), 0x800); + + /* Define which sequencer to interrupt on */ + ATA_OUTL(ctlr->r_res2, offset + 0x0260 + (ch->unit << 7), + 0x80008000 | (ch->unit + 1)); + /* Define which sequencer to interrupt on */ ATA_OUTL(ctlr->r_res2, offset + 0x0260 + (ch->unit << 7), (ATA_INL(ctlr->r_res2, offset + 0x0260 + (ch->unit << 7)) & ~0x00003f9f) | (ch->unit + 1)); Index: sys/dev/ata/ata-lowlevel.c =================================================================== RCS file: /cvs/src/sys/dev/ata/ata-lowlevel.c,v retrieving revision 1.34 diff -u -p -r1.34 ata-lowlevel.c --- sys/dev/ata/ata-lowlevel.c 27 Apr 2004 15:52:08 -0000 1.34 +++ sys/dev/ata/ata-lowlevel.c 4 May 2004 18:16:49 -0000 _at__at_ -573,6 +573,10 _at__at_ ata_generic_reset(struct ata_channel *ch if (!mask) return; + /* reset host end of channel (if supported) */ + if (ch->reset) + ch->reset(ch); + /* reset (both) devices on this channel */ ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_MASTER); DELAY(10); Index: sys/dev/ata/ata-pci.c =================================================================== RCS file: /cvs/src/sys/dev/ata/ata-pci.c,v retrieving revision 1.82 diff -u -p -r1.82 ata-pci.c --- sys/dev/ata/ata-pci.c 27 Apr 2004 12:54:59 -0000 1.82 +++ sys/dev/ata/ata-pci.c 4 May 2004 18:16:49 -0000 _at__at_ -523,12 +523,15 _at__at_ ata_pcisub_probe(device_t dev) } free(children, M_TEMP); + ch->dev = dev; if ((error = ctlr->allocate(dev, ch))) return error; ch->device[MASTER].setmode = ctlr->setmode; ch->device[SLAVE].setmode = ctlr->setmode; ch->locking = ctlr->locking; + if (ch->reset) + ch->reset(ch); return ata_probe(dev); } Index: sys/dev/ata/ata-pci.h =================================================================== RCS file: /cvs/src/sys/dev/ata/ata-pci.h,v retrieving revision 1.30 diff -u -p -r1.30 ata-pci.h --- sys/dev/ata/ata-pci.h 21 Apr 2004 20:03:26 -0000 1.30 +++ sys/dev/ata/ata-pci.h 4 May 2004 18:16:49 -0000 _at__at_ -61,6 +61,7 _at__at_ struct ata_pci_controller { void *argument; } interrupt[8]; /* SOS max ch# for now XXX */ void *driver; + struct mtx mtx; }; /* defines for known chipset PCI id's */ _at__at_ -251,6 +252,8 _at__at_ struct ata_pci_controller { #define HPT374 3 #define HPTOLD 0x01 +#define ISATA 1 + #define PROLD 0 #define PRNEW 1 #define PRTX 2 Index: sys/dev/ata/ata-queue.c =================================================================== RCS file: /cvs/src/sys/dev/ata/ata-queue.c,v retrieving revision 1.26 diff -u -p -r1.26 ata-queue.c --- sys/dev/ata/ata-queue.c 13 Apr 2004 09:44:20 -0000 1.26 +++ sys/dev/ata/ata-queue.c 4 May 2004 18:16:49 -0000 _at__at_ -454,6 +454,7 _at__at_ ata_timeout(struct ata_request *request) printf("\n"); } + request->device->channel->hw.reset(request->device->channel); /* now simulate the missing interrupt */ request->flags |= ATA_R_TIMEOUT; request->device->channel->hw.interrupt(request->device->channel);Received on Tue May 04 2004 - 09:25:44 UTC
This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:37:53 UTC