From: Bjorn Helgaas Add ACPI-based floppy controller enumeration. This fixes one problem exposed when I removed the unconditional ACPI PCI IRQ routing. ACPI-based enumeration can be disabled with "floppy=no_acpi". That should only be required if your BIOS supplies incorrect ACPI _CRS information about I/O ports, IRQs, or DMA channels. Signed-off-by: Bjorn Helgaas Signed-off-by: Andrew Morton --- 25-akpm/drivers/block/floppy.c | 152 +++++++++++++++++++++++++++++++++++++++++ 1 files changed, 152 insertions(+) diff -puN drivers/block/floppy.c~acpi-based-floppy-controller-enumeration drivers/block/floppy.c --- 25/drivers/block/floppy.c~acpi-based-floppy-controller-enumeration Fri Aug 20 15:12:56 2004 +++ 25-akpm/drivers/block/floppy.c Fri Aug 20 15:12:56 2004 @@ -181,6 +181,13 @@ static int print_unex = 1; #include #include /* for invalidate_buffers() */ +#ifdef CONFIG_ACPI_BUS +#include +#include + +static int no_acpi; +#endif + /* * PS/2 floppies have much slower step rates than regular floppies. * It's been recommended that take about 1/4 of the default speed @@ -4150,6 +4157,9 @@ static struct param_table { {"slow", NULL, &slow_floppy, 1, 0}, {"unexpected_interrupts", NULL, &print_unex, 1, 0}, {"no_unexpected_interrupts", NULL, &print_unex, 0, 0}, +#ifdef CONFIG_ACPI_BUS + {"no_acpi", NULL, &no_acpi, 1, 0}, +#endif {"L40SX", NULL, &print_unex, 0, 0} EXTRA_FLOPPY_PARAMS @@ -4222,11 +4232,153 @@ static struct kobject *floppy_find(dev_t return get_disk(disks[drive]); } +#ifdef CONFIG_ACPI_BUS +static int acpi_floppies; + +struct floppy_resources { + unsigned int port1; + unsigned int nr_ports1; + unsigned int port2; + unsigned int nr_ports2; + unsigned int irq; + unsigned int dma_channel; +}; + +static acpi_status acpi_floppy_resource(struct acpi_resource *res, void *data) +{ + struct floppy_resources *floppy = (struct floppy_resources *) data; + struct acpi_resource_io *io; + struct acpi_resource_irq *irq; + struct acpi_resource_ext_irq *ext_irq; + struct acpi_resource_dma *dma; + + if (res->id == ACPI_RSTYPE_IO) { + io = &res->data.io; + if (io->range_length) { + printk("%s: %d ioports at 0x%x\n", __FUNCTION__, + io->range_length, io->min_base_address); + if (floppy->port1) { + floppy->port2 = io->min_base_address; + floppy->nr_ports2 = io->range_length; + } else { + floppy->port1 = io->min_base_address; + floppy->nr_ports1 = io->range_length; + } + } + } else if (res->id == ACPI_RSTYPE_IRQ) { + irq = &res->data.irq; + if (irq->number_of_interrupts > 0) + floppy->irq = acpi_register_gsi(irq->interrupts[0], + irq->edge_level, irq->active_high_low); + } else if (res->id == ACPI_RSTYPE_EXT_IRQ) { + ext_irq = &res->data.extended_irq; + if (ext_irq->number_of_interrupts > 0) + floppy->irq = acpi_register_gsi(ext_irq->interrupts[0], + ext_irq->edge_level, ext_irq->active_high_low); + } else if (res->id == ACPI_RSTYPE_DMA) { + dma = &res->data.dma; + if (dma->number_of_channels > 0) + floppy->dma_channel = dma->channels[0]; + } + return AE_OK; +} + +static int acpi_floppy_add(struct acpi_device *device) +{ + struct floppy_resources floppy; + acpi_status status; + + memset(&floppy, 0, sizeof(floppy)); + status = acpi_walk_resources(device->handle, METHOD_NAME__CRS, + acpi_floppy_resource, &floppy); + if (ACPI_FAILURE(status)) + return -ENODEV; + + printk("%s: controller ACPI %s at I/O 0x%x-0x%x", + DEVICE_NAME, device->pnp.bus_id, + floppy.port1, floppy.port1 + floppy.nr_ports1 - 1); + if (floppy.nr_ports2) + printk(", 0x%x-0x%x", + floppy.port2, floppy.port2 + floppy.nr_ports2 - 1); + printk(" irq %d dma channel %d\n", floppy.irq, floppy.dma_channel); + + /* + * The driver assumes I/O port regions like 0x3f0-0x3f5 and 0x3f7, + * but it ignores the first two ports (i.e., 0x3f0 and 0x3f1), + * which are for PS/2 systems. Since no PS/2 systems have + * ACPI, we should get regions like 0x3f2-0x3f5 and 0x3f7. + * Some boxes don't report 0x3f7 at all, so try to make sense + * of whatever we get. + */ + if (floppy.nr_ports1 == 4) { /* 0x3f2-0x3f5 */ + floppy.port1 -= 2; + floppy.nr_ports1 = 6; + } else if (floppy.nr_ports1 == 6) { /* 0x3f2-0x3f7 */ + floppy.port1 -= 2; + floppy.nr_ports1 = 6; + floppy.nr_ports2 = 1; + } else if (floppy.nr_ports1 == 8) { /* 0x3f0-0x3f7 */ + floppy.nr_ports1 = 6; + floppy.nr_ports2 = 1; + } + + if (floppy.nr_ports2 == 0) + printk(KERN_WARNING "%s: no FD_DIR/DCR port in %s _CRS, assuming 0x%x\n", + DEVICE_NAME, device->pnp.bus_id, floppy.port1 + 7); + + if (acpi_floppies == 0) { + FDC1 = floppy.port1; + FLOPPY_IRQ = floppy.irq; + FLOPPY_DMA = floppy.dma_channel; + } else if (acpi_floppies == 1) { + FDC2 = floppy.port1; + if (floppy.irq != FLOPPY_IRQ || floppy.dma_channel != FLOPPY_DMA) + printk(KERN_WARNING "%s: different IRQ/DMA info for %s; may not work\n", + DEVICE_NAME, device->pnp.bus_id); + } else { + printk(KERN_ERR "%s: only 2 controllers supported; %s ignored\n", + DEVICE_NAME, device->pnp.bus_id); + return -ENODEV; + } + + acpi_floppies++; + return 0; +} + +static int acpi_floppy_remove(struct acpi_device *device, int type) +{ + printk(KERN_ERR "%s: remove ACPI %s not supported\n", DEVICE_NAME, + device->pnp.bus_id); + return -EINVAL; +} + +static struct acpi_driver acpi_floppy_driver = { + .name = "floppy", + .ids = "PNP0700", + .ops = { + .add = acpi_floppy_add, + .remove = acpi_floppy_remove, + }, +}; + +static int acpi_floppy_init(void) +{ + if (no_acpi) + return -ENODEV; + return acpi_bus_register_driver(&acpi_floppy_driver); +} +#endif + int __init floppy_init(void) { int i, unit, drive; int err, dr; +#ifdef CONFIG_ACPI_BUS + if (acpi_floppy_init() == 0) + return -ENODEV; +#endif + raw_cmd = NULL; for (dr = 0; dr < N_DRIVE; dr++) { _