pata_legacy: multiple updates (rediff as asked)
PATCH 1/1 pata_legacy: multiple updates (rediff as asked)
- Fix probe logic to support multiple devices better
- Fold in qdi and winbond support
- Fix promise 202C30 probe
- Restructure
drivers/ata/pata_legacy.c +722 -154
@@ -28,7 +28,6 @@* * Unsupported but docs exist: * Appian/Adaptec AIC25VL01/Cirrus Logic PD7220 -* Winbond W83759A * * This driver handles legacy (that is "ISA/VLB side") IDE ports found * on PC class systems. There are three hybrid devices that are exceptions
@@ -36,7 +35,7 @@* 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 +* opti82c465mv/promise 20230c/20630/winbond83759A * * Use the autospeed and pio_mask options with: * Appian ADI/2 aka CLPD7220 or AIC25VL01.
@@ -47,9 +46,6 @@* For now use autospeed and pio_mask as above with the W83759A. This may * change. * -* TODO -* Merge existing pata_qdi driver -* */ #include <linux/kernel.h>
@@ -64,12 +60,13 @@#include <linux/platform_device.h> #define DRV_NAME "pata_legacy" -#define DRV_VERSION "0.5.5" +#define DRV_VERSION "0.6.5"#define NR_HOST 6 -static int legacy_port[NR_HOST ] = {0x1f0 ,0x170 ,0x1e8 ,0x168 ,0x1e0 ,0x160 }; -static int legacy_irq[NR_HOST ] = {14 ,15 ,11 ,10 ,8 ,12 }; +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;
@@ -80,21 +77,107 @@ }; +enum controller { +BIOS =0 , +SNOOP =1 , +PDC20230 =2 , +HT6560A =3 , +HT6560B =4 , +OPTI611A =5 , +OPTI46X =6 , +QDI6500 =7 , +QDI6580 =8 , +QDI6580DP =9 ,/* Dual channel mode is different */ +W83759A =10 , + +UNKNOWN =-1 +}; + + +struct legacy_probe { +unsigned char *name; +unsigned long port; +unsigned int irq; +unsigned int slot; +enum controller type; +unsigned long private; +}; + +struct legacy_controller { +const char *name; +struct ata_port_operations *ops; +unsigned int pio_mask; +unsigned int flags; +int (*setup)(struct platform_device *,struct legacy_probe *probe, +struct legacy_data *data); +}; + +static int legacy_port[NR_HOST ] = {0x1f0 ,0x170 ,0x1e8 ,0x168 ,0x1e0 ,0x160 }; + +static struct legacy_probe probe_list[NR_HOST ];static struct legacy_data legacy_data[NR_HOST ];static struct ata_host *legacy_host[NR_HOST ];static int nr_legacy_host; -static int probe_all;/* Set to check all ISA port ranges */ -static int ht6560a;/* HT 6560A on primary 1, secondary 2, both 3 */ -static int ht6560b;/* HT 6560A on primary 1, secondary 2, both 3 */ -static int opti82c611a;/* Opti82c611A on primary 1, secondary 2, both 3 */ -static int opti82c46x;/* Opti 82c465MV present (pri/sec autodetect) */ -static int autospeed;/* Chip present which snoops speed changes */ -static int pio_mask =0x1F ;/* PIO range for autospeed devices */ +static int probe_all;/* Set to check all ISA port ranges */ +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 stdanard */ +static int autospeed;/* Chip present which snoops speed changes */ +static int pio_mask =0x1F ;/* PIO range for autospeed devices */ static int iordy_mask =0xFFFFFFFF ;/* Use iordy if available */ /** +* legacy_probe_add - Add interface to probe list +* @port: Controller port +* @irq: IRQ number +* @type: Controller type +* @private: Controller specific info +* +* Add an entry into the probe list for ATA controllers. This is used +* to add the default ISA slots and then to build up the table +* further according to other ISA/VLB/Weird device scans +* +* An I/O port list is used to keep ordering stable and sane, as we +* don't have any good way to talk about ordering otherwise +*/ + +static int legacy_probe_add (unsigned long port,unsigned int irq, +enum controller type,unsigned private) +{ +struct legacy_probe *lp = &probe_list[0 ]; +int i; +struct legacy_probe *free =NULL ; + +for (i =0 ; i <NR_HOST ; i++) { +if (lp->port ==0 && free ==NULL ) + free = lp; +/* Matching port, or the correct slot for ordering */ +if (lp->port == port || legacy_port[i] == port) { + free = lp; +break ; + } + lp++; + } +if (free ==NULL ) { +printk (KERN_ERR "pata_legacy: Too many interfaces.\n" ); +return -1 ; + } +/* Fill in the entry for later probing */ + free->port = port; + free->irq = irq; + free->type = type; + free->private = private; +return 0 ; +} + + +/** * legacy_set_mode - mode setting * @link: IDE link * @unused: Device that failed when error is returned
@@ -113,7 +196,8 @@ata_link_for_each_dev (dev, link) {if (ata_dev_enabled (dev)) { -ata_dev_printk (dev,KERN_INFO ,"configured for PIO\n" ); +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 ;
@@ -171,7 +255,7 @@ .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, - .port_start = ata_port_start, + .port_start = ata_sff_port_start, };static struct ata_port_operations legacy_port_ops = {
@@ -198,15 +282,16 @@ .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, - .port_start = ata_port_start, + .port_start = ata_sff_port_start, };/* * Promise 20230C and 20620 support * -* This controller supports PIO0 to PIO2. We set PIO timings conservatively to -* allow for 50MHz Vesa Local Bus. The 20620 DMA support is weird being DMA to -* controller and PIO'd to the host and not supported. +* This controller supports PIO0 to PIO2. We set PIO timings +* conservatively to allow for 50MHz Vesa Local Bus. The 20620 DMA +* support is weird being DMA to controller and PIO'd to the host +* and not supported. */ static void pdc20230_set_piomode (struct ata_port *ap,struct ata_device *adev)
@@ -221,8 +306,7 @@local_irq_save (flags);/* Unlock the control interface */ - do - { + do { inb(0x1F5); outb(inb(0x1F2) | 0x80, 0x1F2); inb(0x1F2);
@@ -231,7 +315,7 @@inb (0x1F2 );inb (0x1F2 ); } -while ((inb (0x1F2 ) &0x80 ) && --tries); +while ((inb (0x1F2 ) &0x80 ) && --tries);local_irq_restore (flags);
@@ -310,14 +394,14 @@ .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, - .port_start = ata_port_start, + .port_start = ata_sff_port_start, };/* * Holtek 6560A support * -* This controller supports PIO0 to PIO2 (no IORDY even though higher timings -* can be loaded). +* This controller supports PIO0 to PIO2 (no IORDY even though higher +* timings can be loaded). */ static void ht6560a_set_piomode (struct ata_port *ap,struct ata_device *adev)
@@ -364,14 +448,14 @@ .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, - .port_start = ata_port_start, + .port_start = ata_sff_port_start, };/* * Holtek 6560B support * -* This controller supports PIO0 to PIO4. We honour the BIOS/jumper FIFO setting -* unless we see an ATAPI device in which case we force it off. +* This controller supports PIO0 to PIO4. We honour the BIOS/jumper FIFO +* setting unless we see an ATAPI device in which case we force it off. * * FIXME: need to implement 2nd channel support. */
@@ -398,7 +482,7 @@if (adev->class !=ATA_DEV_ATA ) {u8 rconf =inb (0x3E6 );if (rconf &0x24 ) { - rconf &= ~0x24 ; + rconf &= ~0x24 ;outb (rconf,0x3E6 ); } }
@@ -423,13 +507,13 @@ .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, - .data_xfer = ata_data_xfer, /* FIXME: Check 32bit and noirq */ + .data_xfer = ata_data_xfer, /* FIXME: Check 32bit and noirq */ .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, - .port_start = ata_port_start, + .port_start = ata_sff_port_start, };/*
@@ -462,7 +546,8 @@* This controller supports PIO0 to PIO3. */ -static void opti82c611a_set_piomode (struct ata_port *ap,struct ata_device *adev) +static void opti82c611a_set_piomode (struct ata_port *ap, +struct ata_device *adev) {u8 active, recover, setup;struct ata_timing t;
@@ -549,7 +634,7 @@ .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, - .port_start = ata_port_start, + .port_start = ata_sff_port_start, };/*
@@ -681,77 +766,396 @@ .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, - .port_start = ata_port_start, + .port_start = ata_sff_port_start, }; +static void qdi6500_set_piomode (struct ata_port *ap,struct ata_device *adev) +{ +struct ata_timing t; +struct legacy_data *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 (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 ; + + qdi->clock[adev->devno] = timing; + +outb (timing, qdi->timing); +}/** -* legacy_init_one - attach a legacy interface -* @port: port number -* @io: I/O port start -* @ctrl: control port +* qdi6580dp_set_piomode - PIO setup for dual channel +* @ap: Port +* @adev: Device * @irq: interrupt line * -* Register an ISA bus IDE interface. Such interfaces are PIO and we -* assume do not support IRQ sharing. +* In dual channel mode the 6580 has one clock per channel and we have +* to software clockswitch in qc_issue_prot. */ -static __init int legacy_init_one (int port,unsigned long io,unsigned long ctrl,int irq) +static void qdi6580dp_set_piomode (struct ata_port *ap,struct ata_device *adev) { -struct legacy_data *ld = &legacy_data[nr_legacy_host]; -struct ata_host *host; -struct ata_port *ap; -struct platform_device *pdev; -struct ata_port_operations *ops = &legacy_port_ops; -void __iomem *io_addr, *ctrl_addr; -int pio_modes = pio_mask; -u32 mask = (1 << port); -u32 iordy = (iordy_mask & mask) ?0 :ATA_FLAG_NO_IORDY ; -int ret; +struct ata_timing t; +struct legacy_data *qdi = ap->host->private_data; +int active, recovery; +u8 timing; - pdev =platform_device_register_simple (DRV_NAME , nr_legacy_host,NULL ,0 ); -if (IS_ERR (pdev)) -return PTR_ERR (pdev); +/* Get the timing data in cycles */ +ata_timing_compute (adev, adev->pio_mode, &t,30303 ,1000 ); - ret = -EBUSY ; -if (devm_request_region (&pdev->dev, io,8 ,"pata_legacy" ) ==NULL || -devm_request_region (&pdev->dev, ctrl,1 ,"pata_legacy" ) ==NULL ) - goto fail; +if (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 ; - ret = -ENOMEM ; - io_addr =devm_ioport_map (&pdev->dev, io,8 ); - ctrl_addr =devm_ioport_map (&pdev->dev, ctrl,1 ); -if (!io_addr || !ctrl_addr) - goto fail; + qdi->clock[adev->devno] = timing; -if (ht6560a & mask) { - ops = &ht6560a_port_ops; - pio_modes =0x07 ; - iordy =ATA_FLAG_NO_IORDY ; - } -if (ht6560b & mask) { - ops = &ht6560b_port_ops; - pio_modes =0x1F ; - } -if (opti82c611a & mask) { - ops = &opti82c611a_port_ops; - pio_modes =0x0F ; +outb (timing, qdi->timing +2 * ap->port_no); +/* Clear the FIFO */ +if (adev->class !=ATA_DEV_ATA ) +outb (0x5F , 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 *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 (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 ); } -if (opti82c46x & mask) { - ops = &opti82c46x_port_ops; - pio_modes =0x0F ; + timing = (recovery <<4 ) | active |0x08 ; + qdi->clock[adev->devno] = timing; +outb (timing, qdi->timing +2 * adev->devno); +/* Clear the FIFO */ +if (adev->class !=ATA_DEV_ATA ) +outb (0x5F , qdi->timing +3 ); +} + +/** +* qdi_qc_issue_prot - command issue +* @qc: command pending +* +* Called when the libata layer is about to issue a command. We wrap +* this interface so that we can load the correct ATA timings. +*/ + +static unsigned int qdi_qc_issue_prot (struct ata_queued_cmd *qc) +{ +struct ata_port *ap = qc->ap; +struct ata_device *adev = qc->dev; +struct legacy_data *qdi = ap->host->private_data; + +if (qdi->clock[adev->devno] != qdi->last) { +if (adev->pio_mode) { + qdi->last = qdi->clock[adev->devno]; +outb (qdi->clock[adev->devno], qdi->timing + +2 * ap->port_no); + } } +return ata_qc_issue_prot (qc); +} -/* Probe for automatically detectable controllers */ +static void vlb32_data_xfer (struct ata_device *adev,unsigned char *buf,unsigned int buflen,int write_data) +{ +struct ata_port *ap = adev->link->ap; +int slop = buflen &3 ; -if (io ==0x1F0 && ops == &legacy_port_ops) { -unsigned long flags; +if (ata_id_has_dword_io (adev->id)) { +if (write_data) +iowrite32_rep (ap->ioaddr.data_addr, buf, buflen >>2 ); +else +ioread32_rep (ap->ioaddr.data_addr, buf, buflen >>2 ); -local_irq_save (flags); +if (unlikely (slop)) { +u32 pad; +if (write_data) { +memcpy (&pad, buf + buflen - slop, slop); + pad =le32_to_cpu (pad); +iowrite32 (pad, ap->ioaddr.data_addr); + }else { + pad =ioread32 (ap->ioaddr.data_addr); + pad =cpu_to_le32 (pad); +memcpy (buf + buflen - slop, &pad, slop); + } + } + }else +ata_data_xfer (adev, buf, buflen, write_data); +} + +static int qdi_port (struct platform_device *dev, +struct legacy_probe *lp,struct legacy_data *ld) +{ +if (devm_request_region (&dev->dev, lp->private,4 ,"qdi" ) ==NULL ) +return -EBUSY ; + ld->timing = lp->private; +return 0 ; +} + +static struct ata_port_operations qdi6500_port_ops = { + .set_piomode = qdi6500_set_piomode, + + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .freeze = ata_bmdma_freeze, + .thaw = ata_bmdma_thaw, + .error_handler = ata_bmdma_error_handler, + .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, + + .qc_prep = ata_qc_prep, + .qc_issue = qdi_qc_issue_prot, + + .data_xfer = vlb32_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + .irq_on = ata_irq_on, + + .port_start = ata_sff_port_start, +}; + +static struct ata_port_operations qdi6580_port_ops = { + .set_piomode = qdi6580_set_piomode, + + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .freeze = ata_bmdma_freeze, + .thaw = ata_bmdma_thaw, + .error_handler = ata_bmdma_error_handler, + .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, + + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + + .data_xfer = vlb32_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + .irq_on = ata_irq_on, + .port_start = ata_sff_port_start, +}; + +static struct ata_port_operations qdi6580dp_port_ops = { + .set_piomode = qdi6580dp_set_piomode, + + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .freeze = ata_bmdma_freeze, + .thaw = ata_bmdma_thaw, + .error_handler = ata_bmdma_error_handler, + .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, + + .qc_prep = ata_qc_prep, + .qc_issue = qdi_qc_issue_prot, + + .data_xfer = vlb32_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + .irq_on = ata_irq_on, + + .port_start = ata_sff_port_start, +}; + +static DEFINE_SPINLOCK (winbond_lock); + +static void winbond_writecfg (unsigned long port,u8 reg,u8 val) +{ +unsigned long flags; +spin_lock_irqsave (&winbond_lock, flags); +outb (reg, port +0x01 ); +outb (val, port +0x02 ); +spin_unlock_irqrestore (&winbond_lock, flags); +} + +static u8 winbond_readcfg (unsigned long port,u8 reg) +{ +u8 val; + +unsigned long flags; +spin_lock_irqsave (&winbond_lock, flags); +outb (reg, port +0x01 ); + val =inb (port +0x02 ); +spin_unlock_irqrestore (&winbond_lock, flags); + +return val; +} + +static void winbond_set_piomode (struct ata_port *ap,struct ata_device *adev) +{ +struct ata_timing t; +struct legacy_data *winbond = ap->host->private_data; +int active, recovery; +u8 reg; +int timing =0x88 + (ap->port_no *4 ) + (adev->devno *2 ); + + reg =winbond_readcfg (winbond->timing,0x81 ); + +/* Get the timing data in cycles */ +if (reg &0x40 )/* Fast VLB bus, assume 50MHz */ +ata_timing_compute (adev, adev->pio_mode, &t,20000 ,1000 ); +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 ; + timing = (active <<4 ) | recovery; +winbond_writecfg (winbond->timing, timing, reg); + +/* Load the setup timing */ + + reg =0x35 ; +if (adev->class !=ATA_DEV_ATA ) + reg |=0x08 ;/* FIFO off */ +if (!ata_pio_need_iordy (adev)) + reg |=0x02 ;/* IORDY off */ + reg |= (FIT (t.setup,0 ,3 ) <<6 ); +winbond_writecfg (winbond->timing, timing +1 , reg); +} + +static int winbond_port (struct platform_device *dev, +struct legacy_probe *lp,struct legacy_data *ld) +{ +if (devm_request_region (&dev->dev, lp->private,4 ,"winbond" ) ==NULL ) +return -EBUSY ; + ld->timing = lp->private; +return 0 ; +} + +static struct ata_port_operations winbond_port_ops = { + .set_piomode = winbond_set_piomode, + + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .freeze = ata_bmdma_freeze, + .thaw = ata_bmdma_thaw, + .error_handler = ata_bmdma_error_handler, + .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, + + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + + .data_xfer = vlb32_data_xfer, + + .irq_clear = ata_bmdma_irq_clear, + .irq_on = ata_irq_on, + + .port_start = ata_sff_port_start, +}; + +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 } +}; + +/** +* probe_chip_type - Discover controller +* @probe: Probe entry to check +* +* Probe an ATA port and identify the type of controller. We don't +* check if the controller appears to be driveless at this point. +*/ + +static __init int probe_chip_type (struct legacy_probe *probe) +{ +int mask =1 << probe->slot; + +if (winbond && (probe->port ==0x1F0 || probe->port ==0x170 )) { +u8 reg =winbond_readcfg (winbond,0x81 ); + reg |=0x80 ;/* jumpered mode off */ +winbond_writecfg (winbond,0x81 , reg); + reg =winbond_readcfg (winbond,0x83 ); + reg |=0xF0 ;/* local control */ +winbond_writecfg (winbond,0x83 , reg); + reg =winbond_readcfg (winbond,0x85 ); + reg |=0xF0 ;/* programmable timing */ +winbond_writecfg (winbond,0x85 , reg); + + reg =winbond_readcfg (winbond,0x81 ); + +if (reg & mask) +return W83759A ; + } +if (probe->port ==0x1F0 ) { +unsigned long flags; +local_irq_save (flags);/* Probes */ -inb (0x1F5 );outb (inb (0x1F2 ) |0x80 ,0x1F2 ); +inb (0x1F5 );inb (0x1F2 );inb (0x3F6 );inb (0x3F6 );
@@ -760,29 +1164,83 @@if ((inb (0x1F2 ) &0x80 ) ==0 ) {/* PDC20230c or 20630 ? */ -printk (KERN_INFO "PDC20230-C/20630 VLB ATA controller detected.\n" ); - pio_modes =0x07 ; - ops = &pdc20230_port_ops; - iordy =ATA_FLAG_NO_IORDY ; +printk (KERN_INFO "PDC20230-C/20630 VLB ATA controller" +" detected.\n" );udelay (100 );inb (0x1F5 ); +local_irq_restore (flags); +return PDC20230 ; }else {outb (0x55 ,0x1F2 );inb (0x1F2 );inb (0x1F2 ); -if (inb (0x1F2 ) ==0x00 ) { -printk (KERN_INFO "PDC20230-B VLB ATA controller detected.\n" ); - } +if (inb (0x1F2 ) ==0x00 ) +printk (KERN_INFO "PDC20230-B VLB ATA " +"controller detected.\n" ); +local_irq_restore (flags); +return BIOS ; }local_irq_restore (flags); } +if (ht6560a & mask) +return HT6560A ; +if (ht6560b & mask) +return HT6560B ; +if (opti82c611a & mask) +return OPTI611A ; +if (opti82c46x & mask) +return OPTI46X ; +if (autospeed & mask) +return SNOOP ; +return BIOS ; +} + -/* Chip does mode setting by command snooping */ -if (ops == &legacy_port_ops && (autospeed & mask)) - ops = &simple_port_ops; +/** +* legacy_init_one - attach a legacy interface +* @pl: probe record +* +* Register an ISA bus IDE interface. Such interfaces are PIO and we +* assume do not support IRQ sharing. +*/ + +static __init int legacy_init_one (struct legacy_probe *probe) +{ +struct legacy_controller *controller = &controllers[probe->type]; +int pio_modes = controller->pio_mask; +unsigned long io = probe->port; +u32 mask = (1 << probe->slot); +struct ata_port_operations *ops = controller->ops; +struct legacy_data *ld = &legacy_data[probe->slot]; +struct ata_host *host =NULL ; +struct ata_port *ap; +struct platform_device *pdev; +struct ata_device *dev; +void __iomem *io_addr, *ctrl_addr; +u32 iordy = (iordy_mask & mask) ?0 :ATA_FLAG_NO_IORDY ; +int ret; + + iordy |= controller->flags; + + pdev =platform_device_register_simple (DRV_NAME , probe->slot,NULL ,0 ); +if (IS_ERR (pdev)) +return PTR_ERR (pdev); + + ret = -EBUSY ; +if (devm_request_region (&pdev->dev, io,8 ,"pata_legacy" ) ==NULL || +devm_request_region (&pdev->dev, io +0x0206 ,1 , +"pata_legacy" ) ==NULL ) + goto fail; ret = -ENOMEM ; + io_addr =devm_ioport_map (&pdev->dev, io,8 ); + ctrl_addr =devm_ioport_map (&pdev->dev, io +0x0206 ,1 ); +if (!io_addr || !ctrl_addr) + goto fail; +if (controller->setup) +if (controller->setup (pdev, probe, ld) <0 ) + goto fail; host =ata_host_alloc (&pdev->dev,1 );if (!host) goto fail;
@@ -795,19 +1253,29 @@ ap->ioaddr.altstatus_addr = ctrl_addr; ap->ioaddr.ctl_addr = ctrl_addr;ata_std_ports (&ap->ioaddr); - ap->private_data = ld; + ap->host->private_data = ld; -ata_port_desc (ap,"cmd 0x%lx ctl 0x%lx" , io, ctrl); +ata_port_desc (ap,"cmd 0x%lx ctl 0x%lx" , io, io +0x0206 ); - ret =ata_host_activate (host, irq, ata_interrupt,0 , &legacy_sht); + ret =ata_host_activate (host, probe->irq, ata_interrupt,0 , + &legacy_sht);if (ret) goto fail; - - legacy_host[nr_legacy_host++] =dev_get_drvdata (&pdev->dev); ld->platform_dev = pdev; -return 0 ; +/* Nothing found means we drop the port as its probably not there */ + + ret = -ENODEV ; +ata_link_for_each_dev (dev, &ap->link) { +if (!ata_dev_absent (dev)) { + legacy_host[probe->slot] = host; + ld->platform_dev = pdev; +return 0 ; + } + } fail: +if (host) +ata_host_detach (host);platform_device_unregister (pdev);return ret; }
@@ -818,13 +1286,15 @@* @master: set this if we find an ATA master * @master: set this if we find an ATA secondary * -* A small number of vendors implemented early PCI ATA interfaces on bridge logic -* without the ATA interface being PCI visible. Where we have a matching PCI driver -* we must skip the relevant device here. If we don't know about it then the legacy -* driver is the right driver anyway. +* A small number of vendors implemented early PCI ATA interfaces +* on bridge logic without the ATA interface being PCI visible. +* Where we have a matching PCI driver we must skip the relevant +* device here. If we don't know about it then the legacy driver +* is the right driver anyway. */ -static void legacy_check_special_cases (struct pci_dev *p,int *primary,int *secondary) +static void __init legacy_check_special_cases (struct pci_dev *p,int *primary, +int *secondary) {/* Cyrix CS5510 pre SFF MWDMA ATA on the bridge */ if (p->vendor ==0x1078 && p->device ==0x0000 ) {
@@ -840,7 +1310,8 @@if (p->vendor ==0x8086 && p->device ==0x1234 ) {u16 r;pci_read_config_word (p,0x6C , &r); -if (r &0x8000 ) {/* ATA port enabled */ +if (r &0x8000 ) { +/* ATA port enabled */ if (r &0x4000 ) *secondary =1 ;else
@@ -850,6 +1321,114 @@ } } +static __init void probe_opti_vlb (void ) +{ +/* If an OPTI 82C46X is present find out where the channels are */ +static const char *optis[4 ] = { +"3/463MV" ,"5MV" , +"5MVA" ,"5MVB" + }; +u8 chans =1 ; +u8 ctrl = (opti_syscfg (0x30 ) &0xC0 ) >>6 ; + + opti82c46x =3 ;/* Assume master and slave first */ + printk(KERN_INFO DRV_NAME ": Opti 82C46%s chipset support.\n", + optis[ctrl]); +if (ctrl ==3 ) + chans = (opti_syscfg (0x3F ) &0x20 ) ?2 :1 ; + ctrl =opti_syscfg (0xAC ); +/* Check enabled and this port is the 465MV port. On the +MVB we may have two channels */ +if (ctrl &8 ) { +if (chans ==2 ) { +legacy_probe_add (0x1F0 ,14 ,OPTI46X ,0 ); +legacy_probe_add (0x170 ,15 ,OPTI46X ,0 ); + } +if (ctrl &4 ) +legacy_probe_add (0x170 ,15 ,OPTI46X ,0 ); +else +legacy_probe_add (0x1F0 ,14 ,OPTI46X ,0 ); + }else +legacy_probe_add (0x1F0 ,14 ,OPTI46X ,0 ); +} + +static __init void qdi65_identify_port (u8 r,u8 res,unsigned long port) +{ +static const unsigned long ide_port[2 ] = {0x170 ,0x1F0 }; +/* Check card type */ +if ((r &0xF0 ) ==0xC0 ) { +/* QD6500: single channel */ +if (r &8 ) +/* Disabled ? */ +return ; +legacy_probe_add (ide_port[r &0x01 ],14 + (r &0x01 ), +QDI6500 , port); + } +if (((r &0xF0 ) ==0xA0 ) || (r &0xF0 ) ==0x50 ) { +/* QD6580: dual channel */ +if (!request_region (port +2 ,2 ,"pata_qdi" )) { +release_region (port,2 ); +return ; + } + res =inb (port +3 ); +/* Single channel mode ? */ +if (res &1 ) +legacy_probe_add (ide_port[r &0x01 ],14 + (r &0x01 ), +QDI6580 , port); +else {/* Dual channel mode */ +legacy_probe_add (0x1F0 ,14 ,QDI6580DP , port); +/* port + 0x02, r & 0x04 */ +legacy_probe_add (0x170 ,15 ,QDI6580DP , port +2 ); + } +release_region (port +2 ,2 ); + } +} + +static __init void probe_qdi_vlb (void ) +{ +unsigned long flags; +static const unsigned long qd_port[2 ] = {0x30 ,0xB0 }; +int i; + +/* +* Check each possible QD65xx base address +*/ + +for (i =0 ; i <2 ; i++) { +unsigned long port = qd_port[i]; +u8 r, res; + + +if (request_region (port,2 ,"pata_qdi" )) { +/* Check for a card */ +local_irq_save (flags); +/* I have no h/w that needs this delay but it +is present in the historic code */ + r =inb (port); +udelay (1 ); +outb (0x19 , port); +udelay (1 ); + res =inb (port); +udelay (1 ); +outb (r, port); +udelay (1 ); +local_irq_restore (flags); + +/* Fail */ +if (res ==0x19 ) { +release_region (port,2 ); +continue ; + } +/* Passes the presence test */ + r =inb (port +1 ); +udelay (1 ); +/* Check port agrees with port set */ +if ((r &2 ) >>1 == i) +qdi65_identify_port (r, res, port); +release_region (port,2 ); + } + } +}/** * legacy_init - attach legacy interfaces
@@ -867,15 +1446,17 @@int ct =0 ;int primary =0 ;int secondary =0 ; -int last_port =NR_HOST ; +int pci_present =0 ; +struct legacy_probe *pl = &probe_list[0 ]; +int slot =0 ;struct pci_dev *p =NULL ;for_each_pci_dev (p) {int r; -/* Check for any overlap of the system ATA mappings. Native mode controllers -stuck on these addresses or some devices in 'raid' mode won't be found by -the storage class test */ +/* Check for any overlap of the system ATA mappings. Native +mode controllers stuck on these addresses or some devices +in 'raid' mode won't be found by the storage class test */ for (r =0 ; r <6 ; r++) {if (pci_resource_start (p, r) ==0x1f0 ) primary =1 ;
@@ -885,49 +1466,39 @@/* Check for special cases */ legacy_check_special_cases (p, &primary, &secondary); -/* If PCI bus is present then don't probe for tertiary legacy ports */ -if (probe_all ==0 ) - last_port =2 ; +/* If PCI bus is present then don't probe for tertiary +legacy ports */ + pci_present =1 ; } - -/* If an OPTI 82C46X is present find out where the channels are */ -if (opti82c46x) { -static const char *optis[4 ] = { -"3/463MV" ,"5MV" , -"5MVA" ,"5MVB" - }; -u8 chans =1 ; -u8 ctrl = (opti_syscfg (0x30 ) &0xC0 ) >>6 ; - - opti82c46x =3 ;/* Assume master and slave first */ - printk(KERN_INFO DRV_NAME ": Opti 82C46%s chipset support.\n", optis[ctrl]); -if (ctrl ==3 ) - chans = (opti_syscfg (0x3F ) &0x20 ) ?2 :1 ; - ctrl =opti_syscfg (0xAC ); -/* Check enabled and this port is the 465MV port. On the -MVB we may have two channels */ -if (ctrl &8 ) { -if (ctrl &4 ) - opti82c46x =2 ;/* Slave */ -else - opti82c46x =1 ;/* Master */ -if (chans ==2 ) - opti82c46x =3 ;/* Master and Slave */ - }/* Slave only */ -else if (chans ==1 ) - opti82c46x =1 ; + +if (winbond ==1 ) + winbond =0x130 ;/* Default port, alt is 1B0 */ + +if (primary ==0 || all) +legacy_probe_add (0x1F0 ,14 ,UNKNOWN ,0 ); +if (secondary ==0 || all) +legacy_probe_add (0x170 ,15 ,UNKNOWN ,0 ); + +if (probe_all || !pci_present) { +/* ISA/VLB extra ports */ +legacy_probe_add (0x1E8 ,11 ,UNKNOWN ,0 ); +legacy_probe_add (0x168 ,10 ,UNKNOWN ,0 ); +legacy_probe_add (0x1E0 ,8 ,UNKNOWN ,0 ); +legacy_probe_add (0x160 ,12 ,UNKNOWN ,0 ); } -for (i =0 ; i < last_port; i++) { -/* Skip primary if we have seen a PCI one */ -if (i ==0 && primary ==1 ) -continue ; -/* Skip secondary if we have seen a PCI one */ -if (i ==1 && secondary ==1 ) +if (opti82c46x) +probe_opti_vlb (); +if (qdi) +probe_qdi_vlb (); + +for (i =0 ; i <NR_HOST ; i++, pl++) { +if (pl->port ==0 )continue ; -if (legacy_init_one (i, legacy_port[i], - legacy_port[i] +0x0206 , - legacy_irq[i]) ==0 ) +if (pl->type ==UNKNOWN ) + pl->type =probe_chip_type (pl); + pl->slot = slot++; +if (legacy_init_one (pl) ==0 ) ct++; }if (ct !=0 )
@@ -941,11 +1512,8 @@for (i =0 ; i < nr_legacy_host; i++) {struct legacy_data *ld = &legacy_data[i]; -ata_host_detach (legacy_host[i]);platform_device_unregister (ld->platform_dev); -if (ld->timing) -release_region (ld->timing,2 ); } }
@@ -960,9 +1528,9 @@module_param (ht6560b, int,0 );module_param (opti82c611a, int,0 );module_param (opti82c46x, int,0 ); +module_param (qdi, int,0 );module_param (pio_mask, int,0 );module_param (iordy_mask, int,0 );module_init (legacy_init);module_exit (legacy_exit); -
Quoted 4 lines · Alan Cox
- Fix probe logic to support multiple devices better
- Fold in qdi and winbond support
- Fix promise 202C30 probe
- Restructure
hrm, some of this is already in #upstream (and thus -mm, though maybe akpm has not yet regenerated his stuff).
patch(1) seems to think I already have this stuff, and indeed the most recent changesets in pata_legacy are
commit b832548773b0cd98216534caa31b9ed7607c4e76
Author: Alan Cox <[email protected]> Date: Sat Jan 19 15:47:23 2008 +0000
pata_legacy: Merge winbond support
This puts winbond VLB in with the other ISA/VLB support and means
we can
lose pata_winbond.c. With all the VLB/ISA probe in one space (and
out of
the core libata) this makes legacy probing work sanely.
Also switch to devm_ for resource handling on the ports post probe
commit defc9cd826e4a99f550504a744f9407b518828ae
Author: Alan Cox <[email protected]> Date: Thu Jan 10 14:33:10 2008 -0800
pata_legacy: resychronize with upstream changes and resubmit
Update the legacy driver so it can handle VLB ports nicely, and has an internal structure for nailing new ISA/VLB forms in. Anyone got an
ALI14xx
and a spare day ;)
Also adds an "all" parameter so you can load this driver after all
the PCI
ones in a boot time kernel and tell it to grab anything ST412
compatible
even if it is an unknown PCI device. That allows libata to offer
the same
"just get me a disk somehow" fallback that old IDE did.
Obsoletes pata_qdi.