Add support for detaching PCI controllers.

This adds support for cardbus ATA/SATA controllers. I get roughly the
same transfer speeds as on true PCI controllers. Nice to be able to add
a couble of "real" disks to a laptop :)
This commit is contained in:
sos 2004-03-15 12:03:48 +00:00
parent a745e2bbf1
commit dc99d6d221
7 changed files with 129 additions and 97 deletions

View File

@ -69,7 +69,6 @@ static struct cdevsw ata_cdevsw = {
static void ata_shutdown(void *, int);
static int ata_getparam(struct ata_device *, u_int8_t);
static void ata_identify_devices(struct ata_channel *);
static void ata_fail_requests(struct ata_channel *ch,struct ata_device *device);
static void ata_boot_attach(void);
static void bswap(int8_t *, int);
static void btrim(int8_t *, int);
@ -710,24 +709,6 @@ ata_identify_devices(struct ata_channel *ch)
}
}
static void
ata_fail_requests(struct ata_channel *ch, struct ata_device *device)
{
struct ata_request *request;
mtx_lock(&ch->queue_mtx);
while ((request = TAILQ_FIRST(&ch->ata_queue))) {
if (device == NULL || request->device == device) {
TAILQ_REMOVE(&ch->ata_queue, request, chain);
request->result = ENXIO;
mtx_unlock(&ch->queue_mtx);
ata_finish(request);
mtx_lock(&ch->queue_mtx);
}
}
mtx_unlock(&ch->queue_mtx);
}
static void
ata_boot_attach(void)
{

View File

@ -332,6 +332,7 @@ struct ata_channel {
#define ATA_ATAPI_DMA_RO 0x08
#define ATA_48BIT_ACTIVE 0x10
#define ATA_IMMEDIATE_MODE 0x20
#define ATA_HWGONE 0x40
struct ata_device device[2]; /* devices on this channel */
#define MASTER 0x00
@ -396,6 +397,7 @@ int ata_controlcmd(struct ata_device *atadev, u_int8_t command, u_int16_t featur
int ata_atapicmd(struct ata_device *atadev, u_int8_t *ccb, caddr_t data, int count, int flags, int timeout);
void ata_queue_request(struct ata_request *request);
void ata_finish(struct ata_request *request);
void ata_fail_requests(struct ata_channel *ch, struct ata_device *device);
char *ata_cmd2str(struct ata_request *request);
/* ata-lowlevel.c: */

View File

@ -512,7 +512,7 @@ ata_cyrix_chipinit(device_t dev)
if (ata_setup_interrupt(dev))
return ENXIO;
if (ctlr->r_io1)
if (ctlr->r_res1)
ctlr->setmode = ata_cyrix_setmode;
else
ctlr->setmode = ata_generic_setmode;
@ -1145,14 +1145,14 @@ ata_promise_chipinit(device_t dev)
switch (ctlr->chip->cfg1) {
case PRNEW:
/* setup clocks */
ATA_OUTB(ctlr->r_io1, 0x11, ATA_INB(ctlr->r_io1, 0x11) | 0x0a);
ATA_OUTB(ctlr->r_res1, 0x11, ATA_INB(ctlr->r_res1, 0x11) | 0x0a);
ctlr->dmainit = ata_promise_new_dmainit;
/* FALLTHROUGH */
case PROLD:
/* enable burst mode */
ATA_OUTB(ctlr->r_io1, 0x1f, ATA_INB(ctlr->r_io1, 0x1f) | 0x01);
ATA_OUTB(ctlr->r_res1, 0x1f, ATA_INB(ctlr->r_res1, 0x1f) | 0x01);
if ((bus_setup_intr(dev, ctlr->r_irq, ATA_INTR_FLAGS,
ata_promise_old_intr, ctlr, &ctlr->handle))) {
@ -1170,21 +1170,23 @@ ata_promise_chipinit(device_t dev)
break;
case PRMIO:
rid = 0x1c;
if (!(ctlr->r_io2 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
0, ~0, 1, RF_ACTIVE)))
ctlr->r_type2 = SYS_RES_MEMORY;
ctlr->r_rid2 = 0x1c;
if (!(ctlr->r_res2 =
bus_alloc_resource(dev, ctlr->r_type2, &ctlr->r_rid2,
0, ~0, 1, RF_ACTIVE)))
return ENXIO;
ctlr->dmainit = ata_promise_mio_dmainit;
ctlr->allocate = ata_promise_mio_allocate;
if (ctlr->chip->cfg2 & PRDUAL) {
ctlr->channels = ((ATA_INL(ctlr->r_io2, 0x48) & 0x01) > 0) +
((ATA_INL(ctlr->r_io2, 0x48) & 0x02) > 0) + 2;
ctlr->channels = ((ATA_INL(ctlr->r_res2, 0x48) & 0x01) > 0) +
((ATA_INL(ctlr->r_res2, 0x48) & 0x02) > 0) + 2;
}
else if (ctlr->chip->cfg2 & PRSATA) {
ATA_OUTL(ctlr->r_io2, 0x06c, 0x00ff0033);
ctlr->channels = ((ATA_INL(ctlr->r_io2, 0x48) & 0x02) > 0) + 3;
ATA_OUTL(ctlr->r_res2, 0x06c, 0x00ff0033);
ctlr->channels = ((ATA_INL(ctlr->r_res2, 0x48) & 0x02) > 0) + 3;
}
else
ctlr->channels = 4;
@ -1208,18 +1210,18 @@ ata_promise_mio_allocate(device_t dev, struct ata_channel *ch)
int i;
for (i = ATA_DATA; i <= ATA_STATUS; i++) {
ch->r_io[i].res = ctlr->r_io2;
ch->r_io[i].res = ctlr->r_res2;
ch->r_io[i].offset = 0x200 + (i << 2) + (ch->unit << 7);
}
ch->r_io[ATA_ALTSTAT].res = ctlr->r_io2;
ch->r_io[ATA_ALTSTAT].res = ctlr->r_res2;
ch->r_io[ATA_ALTSTAT].offset = 0x238 + (ch->unit << 7);
ch->r_io[ATA_BMCMD_PORT].res = ctlr->r_io2;
ch->r_io[ATA_BMCMD_PORT].res = ctlr->r_res2;
ch->r_io[ATA_BMCMD_PORT].offset = 0x260 + (ch->unit << 7);
ch->r_io[ATA_BMDTP_PORT].res = ctlr->r_io2;
ch->r_io[ATA_BMDTP_PORT].res = ctlr->r_res2;
ch->r_io[ATA_BMDTP_PORT].offset = 0x244 + (ch->unit << 7);
ch->r_io[ATA_BMDEVSPEC_0].res = ctlr->r_io2;
ch->r_io[ATA_BMDEVSPEC_0].res = ctlr->r_res2;
ch->r_io[ATA_BMDEVSPEC_0].offset = ((ch->unit + 1) << 2);
ch->r_io[ATA_IDX_ADDR].res = ctlr->r_io2;
ch->r_io[ATA_IDX_ADDR].res = ctlr->r_res2;
ATA_IDX_OUTL(ch, ATA_BMCMD_PORT,
(ATA_IDX_INL(ch, ATA_BMCMD_PORT) & ~0x00003f9f) |
@ -1242,7 +1244,7 @@ ata_promise_old_intr(void *data)
for (unit = 0; unit < 2; unit++) {
if (!(ch = ctlr->interrupt[unit].argument))
continue;
if (ATA_INL(ctlr->r_io1, 0x1c) & (ch->unit ? 0x00004000 : 0x00000400)) {
if (ATA_INL(ctlr->r_res1, 0x1c) & (ch->unit ? 0x00004000 : 0x00000400)) {
if (ch->dma && (ch->dma->flags & ATA_DMA_ACTIVE)) {
int bmstat = ATA_IDX_INB(ch, ATA_BMSTAT_PORT) & ATA_BMSTAT_MASK;
@ -1292,7 +1294,7 @@ ata_promise_mio_intr(void *data)
u_int32_t irq_vector;
int unit;
irq_vector = ATA_INL(ctlr->r_io2, 0x0040);
irq_vector = ATA_INL(ctlr->r_res2, 0x0040);
for (unit = 0; unit < ctlr->channels; unit++) {
if (irq_vector & (1 << (unit + 1))) {
if ((ch = ctlr->interrupt[unit].argument)) {
@ -1398,9 +1400,9 @@ ata_promise_new_dmastart(struct ata_channel *ch)
device_get_softc(device_get_parent(ch->dev));
if (ch->flags & ATA_48BIT_ACTIVE) {
ATA_OUTB(ctlr->r_io1, 0x11,
ATA_INB(ctlr->r_io1, 0x11) | (ch->unit ? 0x08 : 0x02));
ATA_OUTL(ctlr->r_io1, 0x20,
ATA_OUTB(ctlr->r_res1, 0x11,
ATA_INB(ctlr->r_res1, 0x11) | (ch->unit ? 0x08 : 0x02));
ATA_OUTL(ctlr->r_res1, 0x20,
((ch->dma->flags & ATA_DMA_READ) ? 0x05000000 : 0x06000000) |
(ch->dma->cur_iosize >> 1));
}
@ -1421,9 +1423,9 @@ ata_promise_new_dmastop(struct ata_channel *ch)
int error;
if (ch->flags & ATA_48BIT_ACTIVE) {
ATA_OUTB(ctlr->r_io1, 0x11,
ATA_INB(ctlr->r_io1, 0x11) & ~(ch->unit ? 0x08 : 0x02));
ATA_OUTL(ctlr->r_io1, 0x20, 0);
ATA_OUTB(ctlr->r_res1, 0x11,
ATA_INB(ctlr->r_res1, 0x11) & ~(ch->unit ? 0x08 : 0x02));
ATA_OUTL(ctlr->r_res1, 0x20, 0);
}
error = ATA_IDX_INB(ch, ATA_BMSTAT_PORT);
ATA_IDX_OUTB(ch, ATA_BMCMD_PORT,
@ -1630,9 +1632,11 @@ ata_sii_chipinit(device_t dev)
return ENXIO;
}
rid = 0x24;
if (!(ctlr->r_io2 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
0, ~0, 1, RF_ACTIVE)))
ctlr->r_type2 = SYS_RES_MEMORY;
ctlr->r_rid2 = 0x24;
if (!(ctlr->r_res2 =
bus_alloc_resource(dev, ctlr->r_type2, &ctlr->r_rid2,
0, ~0, 1, RF_ACTIVE)))
return ENXIO;
if (ctlr->chip->cfg2 & SIISETCLK) {
@ -1644,23 +1648,12 @@ ata_sii_chipinit(device_t dev)
ctlr->chip->text);
}
if (ctlr->chip->cfg2 & SII4CH)
ctlr->channels = 4;
/* enable interrupt as BIOS might not */
pci_write_config(dev, 0x8a, (pci_read_config(dev, 0x8a, 1) & 0x3f), 1);
/* setup chipset defaults as BIOS might not */
#if 0
pci_write_config(dev, 0xa2, 0x328a, 2);
pci_write_config(dev, 0xa4, 0x328a328a, 4);
pci_write_config(dev, 0xa8, 0x22082208, 4);
pci_write_config(dev, 0xac, 0x40094009, 4);
pci_write_config(dev, 0xe2, 0x328a, 2);
pci_write_config(dev, 0xe4, 0x328a328a, 4);
pci_write_config(dev, 0xe8, 0x22082208, 4);
pci_write_config(dev, 0xec, 0x40094009, 4);
#endif
if (ctlr->chip->cfg2 & SII4CH)
ctlr->channels = 4;
ctlr->allocate = ata_sii_mio_allocate;
if (ctlr->chip->max_dma >= ATA_SA150)
ctlr->setmode = ata_sata_setmode;
@ -1697,22 +1690,22 @@ ata_sii_mio_allocate(device_t dev, struct ata_channel *ch)
int i;
for (i = ATA_DATA; i <= ATA_STATUS; i++) {
ch->r_io[i].res = ctlr->r_io2;
ch->r_io[i].res = ctlr->r_res2;
ch->r_io[i].offset = 0x80 + i + (unit01 << 6) + (unit10 << 9);
}
ch->r_io[ATA_ALTSTAT].res = ctlr->r_io2;
ch->r_io[ATA_ALTSTAT].res = ctlr->r_res2;
ch->r_io[ATA_ALTSTAT].offset = 0x8a + (unit01 << 6) + (unit10 << 9);
ch->r_io[ATA_BMCMD_PORT].res = ctlr->r_io2;
ch->r_io[ATA_BMCMD_PORT].res = ctlr->r_res2;
ch->r_io[ATA_BMCMD_PORT].offset = 0x00 + (unit01 << 3) + (unit10 << 9);
ch->r_io[ATA_BMSTAT_PORT].res = ctlr->r_io2;
ch->r_io[ATA_BMSTAT_PORT].res = ctlr->r_res2;
ch->r_io[ATA_BMSTAT_PORT].offset = 0x02 + (unit01 << 3) + (unit10 << 9);
ch->r_io[ATA_BMDTP_PORT].res = ctlr->r_io2;
ch->r_io[ATA_BMDTP_PORT].res = ctlr->r_res2;
ch->r_io[ATA_BMDTP_PORT].offset = 0x04 + (unit01 << 3) + (unit10 << 9);
ch->r_io[ATA_BMDEVSPEC_0].res = ctlr->r_io2;
ch->r_io[ATA_BMDEVSPEC_0].res = ctlr->r_res2;
ch->r_io[ATA_BMDEVSPEC_0].offset = 0xa1 + (unit01 << 6) + (unit10 << 9);
ch->r_io[ATA_BMDEVSPEC_1].res = ctlr->r_io2;
ch->r_io[ATA_BMDEVSPEC_1].res = ctlr->r_res2;
ch->r_io[ATA_BMDEVSPEC_1].offset = 0x100 + (unit01 << 7) + (unit10 << 9);
ch->r_io[ATA_IDX_ADDR].res = ctlr->r_io2;
ch->r_io[ATA_IDX_ADDR].res = ctlr->r_res2;
if (ctlr->chip->max_dma >= ATA_SA150) {
ch->flags |= ATA_NO_SLAVE;
@ -1723,7 +1716,6 @@ ata_sii_mio_allocate(device_t dev, struct ata_channel *ch)
if (ctlr->chip->cfg2 & SIIBUG)
ch->dma->boundary = 8 * 1024;
return 0;
}

View File

@ -70,8 +70,8 @@ ata_generic_hw(struct ata_channel *ch)
static int
ata_transaction(struct ata_request *request)
{
/* safety check, device might have been detached FIXME SOS */
if (!request->device->param) {
/* safetybelt for HW that went away */
if (!request->device->param || request->device->channel->flags&ATA_HWGONE) {
request->result = ENXIO;
return ATA_OP_FINISHED;
}

View File

@ -154,21 +154,9 @@ static int
ata_pci_attach(device_t dev)
{
struct ata_pci_controller *ctlr = device_get_softc(dev);
u_int8_t class, subclass;
u_int32_t type, cmd;
u_int32_t cmd;
int unit;
/* set up vendor-specific stuff */
type = pci_get_devid(dev);
class = pci_get_class(dev);
subclass = pci_get_subclass(dev);
cmd = pci_read_config(dev, PCIR_COMMAND, 2);
if (!(cmd & (PCIM_CMD_PORTEN | PCIM_CMD_MEMEN))) {
device_printf(dev, "ATA channel disabled by BIOS\n");
return 0;
}
/* do chipset specific setups only needed once */
if (ATA_MASTERDEV(dev) || pci_read_config(dev, 0x18, 4) & IOMASK)
ctlr->channels = 2;
@ -178,18 +166,19 @@ ata_pci_attach(device_t dev)
ctlr->dmainit = ata_pci_dmainit;
ctlr->locking = ata_pci_locknoop;
#ifdef __sparc64__
/* if needed try to enable busmastering */
cmd = pci_read_config(dev, PCIR_COMMAND, 2);
if (!(cmd & PCIM_CMD_BUSMASTEREN)) {
pci_write_config(dev, PCIR_COMMAND, cmd | PCIM_CMD_BUSMASTEREN, 2);
cmd = pci_read_config(dev, PCIR_COMMAND, 2);
}
#endif
/* if busmastering configured get the I/O resource */
if ((cmd & PCIM_CMD_BUSMASTEREN) == PCIM_CMD_BUSMASTEREN) {
int rid = ATA_BMADDR_RID;
ctlr->r_io1 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
0, ~0, 1, RF_ACTIVE);
/* if busmastering mode "stuck" use it */
if ((cmd & PCIM_CMD_BUSMASTEREN) == PCIM_CMD_BUSMASTEREN) {
ctlr->r_type1 = SYS_RES_IOPORT;
ctlr->r_rid1 = ATA_BMADDR_RID;
ctlr->r_res1 = bus_alloc_resource(dev, ctlr->r_type1, &ctlr->r_rid1,
0, ~0, 1, RF_ACTIVE);
}
ctlr->chipinit(dev);
@ -199,9 +188,34 @@ ata_pci_attach(device_t dev)
device_add_child(dev, "ata", ATA_MASTERDEV(dev) ?
unit : devclass_find_free_unit(ata_devclass, 2));
return bus_generic_attach(dev);
}
return bus_generic_attach(dev); }
static int
ata_pci_detach(device_t dev)
{
struct ata_pci_controller *ctlr = device_get_softc(dev);
struct ata_channel *ch;
int unit;
/* mark HW as gone, we dont want to issue commands to HW no longer there */
for (unit = 0; unit < ctlr->channels; unit++) {
if ((ch = ctlr->interrupt[unit].argument))
ch->flags |= ATA_HWGONE;
}
bus_generic_detach(dev);
if (ctlr->r_irq) {
bus_teardown_intr(dev, ctlr->r_irq, ctlr->handle);
bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ctlr->r_irq);
}
if (ctlr->r_res2)
bus_release_resource(dev, ctlr->r_type2, ctlr->r_rid2, ctlr->r_res2);
if (ctlr->r_res1)
bus_release_resource(dev, ctlr->r_type1, ctlr->r_rid1, ctlr->r_res1);
return 0;
}
static int
ata_pci_print_child(device_t dev, device_t child)
@ -420,9 +434,9 @@ ata_pci_allocate(device_t dev, struct ata_channel *ch)
ch->r_io[ATA_ALTSTAT].offset = 0;
ch->r_io[ATA_IDX_ADDR].res = io;
if (ctlr->r_io1) {
if (ctlr->r_res1) {
for (i = ATA_BMCMD_PORT; i <= ATA_BMDTP_PORT; i++) {
ch->r_io[i].res = ctlr->r_io1;
ch->r_io[i].res = ctlr->r_res1;
ch->r_io[i].offset = (i - ATA_BMCMD_PORT)+(ch->unit * ATA_BMIOSIZE);
}
@ -481,6 +495,7 @@ static device_method_t ata_pci_methods[] = {
/* device interface */
DEVMETHOD(device_probe, ata_pci_probe),
DEVMETHOD(device_attach, ata_pci_attach),
DEVMETHOD(device_detach, ata_pci_detach),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
DEVMETHOD(device_suspend, bus_generic_suspend),
DEVMETHOD(device_resume, bus_generic_resume),
@ -514,6 +529,9 @@ ata_pcisub_probe(device_t dev)
device_t *children;
int count, error, i;
/* take care of green memory */
bzero(ch, sizeof(struct ata_channel));
/* find channel number on this controller */
device_get_children(device_get_parent(dev), &children, &count);
for (i = 0; i < count; i++) {
@ -528,6 +546,8 @@ ata_pcisub_probe(device_t dev)
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);
}

View File

@ -40,8 +40,12 @@ struct ata_chip_id {
/* structure describing a PCI ATA controller */
struct ata_pci_controller {
struct resource *r_io1;
struct resource *r_io2;
int r_type1;
int r_rid1;
struct resource *r_res1;
int r_type2;
int r_rid2;
struct resource *r_res2;
struct resource *r_irq;
void *handle;
struct ata_chip_id *chip;

View File

@ -459,6 +459,39 @@ ata_timeout(struct ata_request *request)
return;
}
void
ata_fail_requests(struct ata_channel *ch, struct ata_device *device)
{
struct ata_request *request;
/* fail all requests queued on this channel */
mtx_lock(&ch->queue_mtx);
while ((request = TAILQ_FIRST(&ch->ata_queue))) {
if (!device || request->device == device) {
TAILQ_REMOVE(&ch->ata_queue, request, chain);
request->result = ENXIO;
if (request->callback)
(request->callback)(request);
else
sema_post(&request->done);
}
}
mtx_unlock(&ch->queue_mtx);
/* if we have a request "in flight" fail it as well */
if ((!device || request->device == device) && (request = ch->running)) {
untimeout((timeout_t *)ata_timeout, request, request->timeout_handle);
ATA_UNLOCK_CH(request->device->channel);
request->device->channel->locking(request->device->channel,
ATA_LF_UNLOCK);
request->result = ENXIO;
if (request->callback)
(request->callback)(request);
else
sema_post(&request->done);
}
}
char *
ata_cmd2str(struct ata_request *request)
{