diff options
Diffstat (limited to 'drivers/ata/pata_legacy.c')
| -rw-r--r-- | drivers/ata/pata_legacy.c | 268 |
1 files changed, 123 insertions, 145 deletions
diff --git a/drivers/ata/pata_legacy.c b/drivers/ata/pata_legacy.c index 7af4b29cc42..bce2a8ca467 100644 --- a/drivers/ata/pata_legacy.c +++ b/drivers/ata/pata_legacy.c @@ -1,6 +1,6 @@ /* * pata-legacy.c - Legacy port PATA/SATA controller driver. - * Copyright 2005/2006 Red Hat <alan@redhat.com>, all rights reserved. + * Copyright 2005/2006 Red Hat, all rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,6 +25,13 @@ * http://www.ryston.cz/petr/vlb/pdc20230b.html * http://www.ryston.cz/petr/vlb/pdc20230c.html * http://www.ryston.cz/petr/vlb/pdc20630.html + * QDI65x0: + * http://www.ryston.cz/petr/vlb/qd6500.html + * http://www.ryston.cz/petr/vlb/qd6580.html + * + * QDI65x0 probe code based on drivers/ide/legacy/qd65xx.c + * Rewritten from the work of Colten Edwards <pje120@cs.usask.ca> by + * Samuel Thibault <samuel.thibault@ens-lyon.org> * * Unsupported but docs exist: * Appian/Adaptec AIC25VL01/Cirrus Logic PD7220 @@ -35,7 +42,10 @@ * the MPIIX where the tuning is PCI side but the IDE is "ISA side". * * Specific support is included for the ht6560a/ht6560b/opti82c611a/ - * opti82c465mv/promise 20230c/20630/winbond83759A + * opti82c465mv/promise 20230c/20630/qdi65x0/winbond83759A + * + * Support for the Winbond 83759A when operating in advanced mode. + * Multichip mode is not currently supported. * * Use the autospeed and pio_mask options with: * Appian ADI/2 aka CLPD7220 or AIC25VL01. @@ -48,6 +58,7 @@ * */ +#include <linux/async.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> @@ -68,15 +79,6 @@ static int all; module_param(all, int, 0444); MODULE_PARM_DESC(all, "Grab all legacy port devices, even if PCI(0=off, 1=on)"); -struct legacy_data { - unsigned long timing; - u8 clock[2]; - u8 last; - int fast; - struct platform_device *platform_dev; - -}; - enum controller { BIOS = 0, SNOOP = 1, @@ -93,6 +95,14 @@ enum controller { UNKNOWN = -1 }; +struct legacy_data { + unsigned long timing; + u8 clock[2]; + u8 last; + int fast; + enum controller type; + struct platform_device *platform_dev; +}; struct legacy_probe { unsigned char *name; @@ -108,6 +118,7 @@ struct legacy_controller { struct ata_port_operations *ops; unsigned int pio_mask; unsigned int flags; + unsigned int pflags; int (*setup)(struct platform_device *, struct legacy_probe *probe, struct legacy_data *data); }; @@ -125,13 +136,25 @@ static int ht6560a; /* HT 6560A on primary 1, second 2, both 3 */ static int ht6560b; /* HT 6560A on primary 1, second 2, both 3 */ static int opti82c611a; /* Opti82c611A on primary 1, sec 2, both 3 */ static int opti82c46x; /* Opti 82c465MV present(pri/sec autodetect) */ -static int qdi; /* Set to probe QDI controllers */ -static int winbond; /* Set to probe Winbond controllers, - give I/O port if non standard */ static int autospeed; /* Chip present which snoops speed changes */ -static int pio_mask = 0x1F; /* PIO range for autospeed devices */ +static int pio_mask = ATA_PIO4; /* PIO range for autospeed devices */ static int iordy_mask = 0xFFFFFFFF; /* Use iordy if available */ +/* Set to probe QDI controllers */ +#ifdef CONFIG_PATA_QDI_MODULE +static int qdi = 1; +#else +static int qdi; +#endif + +#ifdef CONFIG_PATA_WINBOND_VLB_MODULE +static int winbond = 1; /* Set to probe Winbond controllers, + give I/O port if non standard */ +#else +static int winbond; /* Set to probe Winbond controllers, + give I/O port if non standard */ +#endif + /** * legacy_probe_add - Add interface to probe list * @port: Controller port @@ -194,15 +217,12 @@ static int legacy_set_mode(struct ata_link *link, struct ata_device **unused) { struct ata_device *dev; - ata_link_for_each_dev(dev, link) { - if (ata_dev_enabled(dev)) { - ata_dev_printk(dev, KERN_INFO, - "configured for PIO\n"); - dev->pio_mode = XFER_PIO_0; - dev->xfer_mode = XFER_PIO_0; - dev->xfer_shift = ATA_SHIFT_PIO; - dev->flags |= ATA_DFLAG_PIO; - } + ata_for_each_dev(dev, link, ENABLED) { + ata_dev_info(dev, "configured for PIO\n"); + dev->pio_mode = XFER_PIO_0; + dev->xfer_mode = XFER_PIO_0; + dev->xfer_shift = ATA_SHIFT_PIO; + dev->flags |= ATA_DFLAG_PIO; } return 0; } @@ -286,9 +306,12 @@ static void pdc20230_set_piomode(struct ata_port *ap, struct ata_device *adev) static unsigned int pdc_data_xfer_vlb(struct ata_device *dev, unsigned char *buf, unsigned int buflen, int rw) { - if (ata_id_has_dword_io(dev->id)) { - struct ata_port *ap = dev->link->ap; - int slop = buflen & 3; + int slop = buflen & 3; + struct ata_port *ap = dev->link->ap; + + /* 32bit I/O capable *and* we need to write a whole number of dwords */ + if (ata_id_has_dword_io(dev->id) && (slop == 0 || slop == 3) + && (ap->pflags & ATA_PFLAG_PIO32)) { unsigned long flags; local_irq_save(flags); @@ -305,7 +328,7 @@ static unsigned int pdc_data_xfer_vlb(struct ata_device *dev, iowrite32_rep(ap->ioaddr.data_addr, buf, buflen >> 2); if (unlikely(slop)) { - u32 pad; + __le32 pad; if (rw == READ) { pad = cpu_to_le32(ioread32(ap->ioaddr.data_addr)); memcpy(buf + buflen - slop, &pad, slop); @@ -343,8 +366,8 @@ static void ht6560a_set_piomode(struct ata_port *ap, struct ata_device *adev) /* Get the timing data in cycles. For now play safe at 50Mhz */ ata_timing_compute(adev, adev->pio_mode, &t, 20000, 1000); - active = FIT(t.active, 2, 15); - recover = FIT(t.recover, 4, 15); + active = clamp_val(t.active, 2, 15); + recover = clamp_val(t.recover, 4, 15); inb(0x3E6); inb(0x3E6); @@ -377,9 +400,8 @@ static void ht6560b_set_piomode(struct ata_port *ap, struct ata_device *adev) /* Get the timing data in cycles. For now play safe at 50Mhz */ ata_timing_compute(adev, adev->pio_mode, &t, 20000, 1000); - active = FIT(t.active, 2, 15); - recover = FIT(t.recover, 2, 16); - recover &= 0x15; + active = clamp_val(t.active, 2, 15); + recover = clamp_val(t.recover, 2, 16) & 0x0F; inb(0x3E6); inb(0x3E6); @@ -462,9 +484,9 @@ static void opti82c611a_set_piomode(struct ata_port *ap, ata_timing_merge(&t, &tp, &t, ATA_TIMING_SETUP); } - active = FIT(t.active, 2, 17) - 2; - recover = FIT(t.recover, 1, 16) - 1; - setup = FIT(t.setup, 1, 4) - 1; + active = clamp_val(t.active, 2, 17) - 2; + recover = clamp_val(t.recover, 1, 16) - 1; + setup = clamp_val(t.setup, 1, 4) - 1; /* Select the right timing bank for write timing */ rc = ioread8(ap->ioaddr.lbal_addr); @@ -520,7 +542,7 @@ static void opti82c46x_set_piomode(struct ata_port *ap, struct ata_device *adev) u8 sysclk; /* Get the clock */ - sysclk = opti_syscfg(0xAC) & 0xC0; /* BIOS set */ + sysclk = (opti_syscfg(0xAC) & 0xC0) >> 6; /* BIOS set */ /* Enter configuration mode */ ioread16(ap->ioaddr.error_addr); @@ -541,9 +563,9 @@ static void opti82c46x_set_piomode(struct ata_port *ap, struct ata_device *adev) ata_timing_merge(&t, &tp, &t, ATA_TIMING_SETUP); } - active = FIT(t.active, 2, 17) - 2; - recover = FIT(t.recover, 1, 16) - 1; - setup = FIT(t.setup, 1, 4) - 1; + active = clamp_val(t.active, 2, 17) - 2; + recover = clamp_val(t.recover, 1, 16) - 1; + setup = clamp_val(t.setup, 1, 4) - 1; /* Select the right timing bank for write timing */ rc = ioread8(ap->ioaddr.lbal_addr); @@ -613,41 +635,20 @@ static struct ata_port_operations opti82c46x_port_ops = { .qc_issue = opti82c46x_qc_issue, }; -static void qdi6500_set_piomode(struct ata_port *ap, struct ata_device *adev) -{ - struct ata_timing t; - struct legacy_data *ld_qdi = ap->host->private_data; - int active, recovery; - u8 timing; - - /* Get the timing data in cycles */ - ata_timing_compute(adev, adev->pio_mode, &t, 30303, 1000); - - if (ld_qdi->fast) { - active = 8 - FIT(t.active, 1, 8); - recovery = 18 - FIT(t.recover, 3, 18); - } else { - active = 9 - FIT(t.active, 2, 9); - recovery = 15 - FIT(t.recover, 0, 15); - } - timing = (recovery << 4) | active | 0x08; - - ld_qdi->clock[adev->devno] = timing; - - outb(timing, ld_qdi->timing); -} - /** - * qdi6580dp_set_piomode - PIO setup for dual channel + * qdi65x0_set_piomode - PIO setup for QDI65x0 * @ap: Port * @adev: Device - * @irq: interrupt line + * + * In single channel mode the 6580 has one clock per device and we can + * avoid the requirement to clock switch. We also have to load the timing + * into the right clock according to whether we are master or slave. * * In dual channel mode the 6580 has one clock per channel and we have * to software clockswitch in qc_issue. */ -static void qdi6580dp_set_piomode(struct ata_port *ap, struct ata_device *adev) +static void qdi65x0_set_piomode(struct ata_port *ap, struct ata_device *adev) { struct ata_timing t; struct legacy_data *ld_qdi = ap->host->private_data; @@ -658,55 +659,23 @@ static void qdi6580dp_set_piomode(struct ata_port *ap, struct ata_device *adev) ata_timing_compute(adev, adev->pio_mode, &t, 30303, 1000); if (ld_qdi->fast) { - active = 8 - FIT(t.active, 1, 8); - recovery = 18 - FIT(t.recover, 3, 18); + active = 8 - clamp_val(t.active, 1, 8); + recovery = 18 - clamp_val(t.recover, 3, 18); } else { - active = 9 - FIT(t.active, 2, 9); - recovery = 15 - FIT(t.recover, 0, 15); + active = 9 - clamp_val(t.active, 2, 9); + recovery = 15 - clamp_val(t.recover, 0, 15); } timing = (recovery << 4) | active | 0x08; - ld_qdi->clock[adev->devno] = timing; - outb(timing, ld_qdi->timing + 2 * ap->port_no); - /* Clear the FIFO */ - if (adev->class != ATA_DEV_ATA) - outb(0x5F, ld_qdi->timing + 3); -} - -/** - * qdi6580_set_piomode - PIO setup for single channel - * @ap: Port - * @adev: Device - * - * In single channel mode the 6580 has one clock per device and we can - * avoid the requirement to clock switch. We also have to load the timing - * into the right clock according to whether we are master or slave. - */ - -static void qdi6580_set_piomode(struct ata_port *ap, struct ata_device *adev) -{ - struct ata_timing t; - struct legacy_data *ld_qdi = ap->host->private_data; - int active, recovery; - u8 timing; - - /* Get the timing data in cycles */ - ata_timing_compute(adev, adev->pio_mode, &t, 30303, 1000); + if (ld_qdi->type == QDI6580) + outb(timing, ld_qdi->timing + 2 * adev->devno); + else + outb(timing, ld_qdi->timing + 2 * ap->port_no); - if (ld_qdi->fast) { - active = 8 - FIT(t.active, 1, 8); - recovery = 18 - FIT(t.recover, 3, 18); - } else { - active = 9 - FIT(t.active, 2, 9); - recovery = 15 - FIT(t.recover, 0, 15); - } - timing = (recovery << 4) | active | 0x08; - ld_qdi->clock[adev->devno] = timing; - outb(timing, ld_qdi->timing + 2 * adev->devno); /* Clear the FIFO */ - if (adev->class != ATA_DEV_ATA) - outb(0x5F, ld_qdi->timing + 3); + if (ld_qdi->type != QDI6500 && adev->class != ATA_DEV_ATA) + outb(0x5F, (ld_qdi->timing & 0xFFF0) + 3); } /** @@ -739,21 +708,20 @@ static unsigned int vlb32_data_xfer(struct ata_device *adev, unsigned char *buf, struct ata_port *ap = adev->link->ap; int slop = buflen & 3; - if (ata_id_has_dword_io(adev->id)) { + if (ata_id_has_dword_io(adev->id) && (slop == 0 || slop == 3) + && (ap->pflags & ATA_PFLAG_PIO32)) { if (rw == WRITE) iowrite32_rep(ap->ioaddr.data_addr, buf, buflen >> 2); else ioread32_rep(ap->ioaddr.data_addr, buf, buflen >> 2); if (unlikely(slop)) { - u32 pad; + __le32 pad; if (rw == WRITE) { memcpy(&pad, buf + buflen - slop, slop); - pad = le32_to_cpu(pad); - iowrite32(pad, ap->ioaddr.data_addr); + iowrite32(le32_to_cpu(pad), ap->ioaddr.data_addr); } else { - pad = ioread32(ap->ioaddr.data_addr); - pad = cpu_to_le32(pad); + pad = cpu_to_le32(ioread32(ap->ioaddr.data_addr)); memcpy(buf + buflen - slop, &pad, slop); } } @@ -773,20 +741,21 @@ static int qdi_port(struct platform_device *dev, static struct ata_port_operations qdi6500_port_ops = { .inherits = &legacy_base_port_ops, - .set_piomode = qdi6500_set_piomode, + .set_piomode = qdi65x0_set_piomode, .qc_issue = qdi_qc_issue, .sff_data_xfer = vlb32_data_xfer, }; static struct ata_port_operations qdi6580_port_ops = { .inherits = &legacy_base_port_ops, - .set_piomode = qdi6580_set_piomode, + .set_piomode = qdi65x0_set_piomode, .sff_data_xfer = vlb32_data_xfer, }; static struct ata_port_operations qdi6580dp_port_ops = { .inherits = &legacy_base_port_ops, - .set_piomode = qdi6580dp_set_piomode, + .set_piomode = qdi65x0_set_piomode, + .qc_issue = qdi_qc_issue, .sff_data_xfer = vlb32_data_xfer, }; @@ -830,8 +799,8 @@ static void winbond_set_piomode(struct ata_port *ap, struct ata_device *adev) else ata_timing_compute(adev, adev->pio_mode, &t, 30303, 1000); - active = (FIT(t.active, 3, 17) - 1) & 0x0F; - recovery = (FIT(t.recover, 1, 15) + 1) & 0x0F; + active = (clamp_val(t.active, 3, 17) - 1) & 0x0F; + recovery = (clamp_val(t.recover, 1, 15) + 1) & 0x0F; timing = (active << 4) | recovery; winbond_writecfg(ld_winbond->timing, timing, reg); @@ -842,7 +811,7 @@ static void winbond_set_piomode(struct ata_port *ap, struct ata_device *adev) reg |= 0x08; /* FIFO off */ if (!ata_pio_need_iordy(adev)) reg |= 0x02; /* IORDY off */ - reg |= (FIT(t.setup, 0, 3) << 6); + reg |= (clamp_val(t.setup, 0, 3) << 6); winbond_writecfg(ld_winbond->timing, timing + 1, reg); } @@ -862,28 +831,31 @@ static struct ata_port_operations winbond_port_ops = { }; static struct legacy_controller controllers[] = { - {"BIOS", &legacy_port_ops, 0x1F, - ATA_FLAG_NO_IORDY, NULL }, - {"Snooping", &simple_port_ops, 0x1F, - 0 , NULL }, - {"PDC20230", &pdc20230_port_ops, 0x7, - ATA_FLAG_NO_IORDY, NULL }, - {"HT6560A", &ht6560a_port_ops, 0x07, - ATA_FLAG_NO_IORDY, NULL }, - {"HT6560B", &ht6560b_port_ops, 0x1F, - ATA_FLAG_NO_IORDY, NULL }, - {"OPTI82C611A", &opti82c611a_port_ops, 0x0F, - 0 , NULL }, - {"OPTI82C46X", &opti82c46x_port_ops, 0x0F, - 0 , NULL }, - {"QDI6500", &qdi6500_port_ops, 0x07, - ATA_FLAG_NO_IORDY, qdi_port }, - {"QDI6580", &qdi6580_port_ops, 0x1F, - 0 , qdi_port }, - {"QDI6580DP", &qdi6580dp_port_ops, 0x1F, - 0 , qdi_port }, - {"W83759A", &winbond_port_ops, 0x1F, - 0 , winbond_port } + {"BIOS", &legacy_port_ops, ATA_PIO4, + ATA_FLAG_NO_IORDY, 0, NULL }, + {"Snooping", &simple_port_ops, ATA_PIO4, + 0, 0, NULL }, + {"PDC20230", &pdc20230_port_ops, ATA_PIO2, + ATA_FLAG_NO_IORDY, + ATA_PFLAG_PIO32 | ATA_PFLAG_PIO32CHANGE, NULL }, + {"HT6560A", &ht6560a_port_ops, ATA_PIO2, + ATA_FLAG_NO_IORDY, 0, NULL }, + {"HT6560B", &ht6560b_port_ops, ATA_PIO4, + ATA_FLAG_NO_IORDY, 0, NULL }, + {"OPTI82C611A", &opti82c611a_port_ops, ATA_PIO3, + 0, 0, NULL }, + {"OPTI82C46X", &opti82c46x_port_ops, ATA_PIO3, + 0, 0, NULL }, + {"QDI6500", &qdi6500_port_ops, ATA_PIO2, + ATA_FLAG_NO_IORDY, + ATA_PFLAG_PIO32 | ATA_PFLAG_PIO32CHANGE, qdi_port }, + {"QDI6580", &qdi6580_port_ops, ATA_PIO4, + 0, ATA_PFLAG_PIO32 | ATA_PFLAG_PIO32CHANGE, qdi_port }, + {"QDI6580DP", &qdi6580dp_port_ops, ATA_PIO4, + 0, ATA_PFLAG_PIO32 | ATA_PFLAG_PIO32CHANGE, qdi_port }, + {"W83759A", &winbond_port_ops, ATA_PIO4, + 0, ATA_PFLAG_PIO32 | ATA_PFLAG_PIO32CHANGE, + winbond_port } }; /** @@ -944,7 +916,6 @@ static __init int probe_chip_type(struct legacy_probe *probe) local_irq_restore(flags); return BIOS; } - local_irq_restore(flags); } if (ht6560a & mask) @@ -1002,6 +973,7 @@ static __init int legacy_init_one(struct legacy_probe *probe) ctrl_addr = devm_ioport_map(&pdev->dev, io + 0x0206, 1); if (!io_addr || !ctrl_addr) goto fail; + ld->type = probe->type; if (controller->setup) if (controller->setup(pdev, probe, ld) < 0) goto fail; @@ -1013,6 +985,7 @@ static __init int legacy_init_one(struct legacy_probe *probe) ap->ops = ops; ap->pio_mask = pio_modes; ap->flags |= ATA_FLAG_SLAVE_POSS | iordy; + ap->pflags |= controller->pflags; ap->ioaddr.cmd_addr = io_addr; ap->ioaddr.altstatus_addr = ctrl_addr; ap->ioaddr.ctl_addr = ctrl_addr; @@ -1025,18 +998,20 @@ static __init int legacy_init_one(struct legacy_probe *probe) &legacy_sht); if (ret) goto fail; + async_synchronize_full(); ld->platform_dev = pdev; /* Nothing found means we drop the port as its probably not there */ ret = -ENODEV; - ata_link_for_each_dev(dev, &ap->link) { + ata_for_each_dev(dev, &ap->link, ALL) { if (!ata_dev_absent(dev)) { legacy_host[probe->slot] = host; ld->platform_dev = pdev; return 0; } } + ata_host_detach(host); fail: platform_device_unregister(pdev); return ret; @@ -1283,6 +1258,8 @@ MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for legacy ATA"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); +MODULE_ALIAS("pata_qdi"); +MODULE_ALIAS("pata_winbond"); module_param(probe_all, int, 0); module_param(autospeed, int, 0); @@ -1291,6 +1268,7 @@ module_param(ht6560b, int, 0); module_param(opti82c611a, int, 0); module_param(opti82c46x, int, 0); module_param(qdi, int, 0); +module_param(winbond, int, 0); module_param(pio_mask, int, 0); module_param(iordy_mask, int, 0); |
