diff options
Diffstat (limited to 'drivers/ata')
35 files changed, 5815 insertions, 190 deletions
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 0b6358a5b315..b56fba76b43f 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -1144,6 +1144,20 @@ config PATA_WINBOND_VLB Support for the Winbond W83759A controller on Vesa Local Bus systems. +config PATA_PARPORT + tristate "Parallel port IDE device support" + depends on PARPORT_PC + help + There are many external CD-ROM and disk devices that connect through + your computer's parallel port. Most of them are actually IDE devices + using a parallel port IDE adapter. This option enables the + PATA_PARPORT subsystem which contains drivers for many of these + external drives. + Read <file:Documentation/admin-guide/blockdev/paride.rst> for more + information. + +source "drivers/ata/pata_parport/Kconfig" + comment "Generic fallback / legacy drivers" config PATA_ACPI diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 4ee5c0761d90..20e6645ab737 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -112,6 +112,8 @@ obj-$(CONFIG_PATA_RZ1000) += pata_rz1000.o obj-$(CONFIG_PATA_PXA) += pata_pxa.o +obj-$(CONFIG_PATA_PARPORT) += pata_parport/ + # Should be last but two libata driver obj-$(CONFIG_PATA_ACPI) += pata_acpi.o # Should be last but one libata driver diff --git a/drivers/ata/acard-ahci.c b/drivers/ata/acard-ahci.c index 7654a40c12b4..993eadd173da 100644 --- a/drivers/ata/acard-ahci.c +++ b/drivers/ata/acard-ahci.c @@ -57,7 +57,7 @@ struct acard_sg { }; static enum ata_completion_errors acard_ahci_qc_prep(struct ata_queued_cmd *qc); -static bool acard_ahci_qc_fill_rtf(struct ata_queued_cmd *qc); +static void acard_ahci_qc_fill_rtf(struct ata_queued_cmd *qc); static int acard_ahci_port_start(struct ata_port *ap); static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); @@ -248,7 +248,7 @@ static enum ata_completion_errors acard_ahci_qc_prep(struct ata_queued_cmd *qc) return AC_ERR_OK; } -static bool acard_ahci_qc_fill_rtf(struct ata_queued_cmd *qc) +static void acard_ahci_qc_fill_rtf(struct ata_queued_cmd *qc) { struct ahci_port_priv *pp = qc->ap->private_data; u8 *rx_fis = pp->rx_fis; @@ -263,13 +263,11 @@ static bool acard_ahci_qc_fill_rtf(struct ata_queued_cmd *qc) * Setup FIS. */ if (qc->tf.protocol == ATA_PROT_PIO && qc->dma_dir == DMA_FROM_DEVICE && - !(qc->flags & ATA_QCFLAG_FAILED)) { + !(qc->flags & ATA_QCFLAG_EH)) { ata_tf_from_fis(rx_fis + RX_FIS_PIO_SETUP, &qc->result_tf); qc->result_tf.status = (rx_fis + RX_FIS_PIO_SETUP)[15]; } else ata_tf_from_fis(rx_fis + RX_FIS_D2H_REG, &qc->result_tf); - - return true; } static int acard_ahci_port_start(struct ata_port *ap) diff --git a/drivers/ata/ahci_octeon.c b/drivers/ata/ahci_octeon.c index b9460b91288f..5021ab3ede49 100644 --- a/drivers/ata/ahci_octeon.c +++ b/drivers/ata/ahci_octeon.c @@ -73,11 +73,6 @@ static int ahci_octeon_probe(struct platform_device *pdev) return 0; } -static int ahci_octeon_remove(struct platform_device *pdev) -{ - return 0; -} - static const struct of_device_id octeon_ahci_match[] = { { .compatible = "cavium,octeon-7130-sata-uctl", }, { /* sentinel */ } @@ -86,7 +81,6 @@ MODULE_DEVICE_TABLE(of, octeon_ahci_match); static struct platform_driver ahci_octeon_driver = { .probe = ahci_octeon_probe, - .remove = ahci_octeon_remove, .driver = { .name = "octeon-ahci", .of_match_table = octeon_ahci_match, diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 29acc35bf4a6..8f216de76648 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -55,7 +55,8 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state, static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); -static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc); +static void ahci_qc_fill_rtf(struct ata_queued_cmd *qc); +static void ahci_qc_ncq_fill_rtf(struct ata_port *ap, u64 done_mask); static int ahci_port_start(struct ata_port *ap); static void ahci_port_stop(struct ata_port *ap); static enum ata_completion_errors ahci_qc_prep(struct ata_queued_cmd *qc); @@ -157,6 +158,7 @@ struct ata_port_operations ahci_ops = { .qc_prep = ahci_qc_prep, .qc_issue = ahci_qc_issue, .qc_fill_rtf = ahci_qc_fill_rtf, + .qc_ncq_fill_rtf = ahci_qc_ncq_fill_rtf, .freeze = ahci_freeze, .thaw = ahci_thaw, @@ -1847,18 +1849,47 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) ata_port_abort(ap); } -static void ahci_handle_port_interrupt(struct ata_port *ap, - void __iomem *port_mmio, u32 status) +static void ahci_qc_complete(struct ata_port *ap, void __iomem *port_mmio) { struct ata_eh_info *ehi = &ap->link.eh_info; struct ahci_port_priv *pp = ap->private_data; - struct ahci_host_priv *hpriv = ap->host->private_data; - int resetting = !!(ap->pflags & ATA_PFLAG_RESETTING); u32 qc_active = 0; int rc; + /* + * pp->active_link is not reliable once FBS is enabled, both + * PORT_SCR_ACT and PORT_CMD_ISSUE should be checked because + * NCQ and non-NCQ commands may be in flight at the same time. + */ + if (pp->fbs_enabled) { + if (ap->qc_active) { + qc_active = readl(port_mmio + PORT_SCR_ACT); + qc_active |= readl(port_mmio + PORT_CMD_ISSUE); + } + } else { + /* pp->active_link is valid iff any command is in flight */ + if (ap->qc_active && pp->active_link->sactive) + qc_active = readl(port_mmio + PORT_SCR_ACT); + else + qc_active = readl(port_mmio + PORT_CMD_ISSUE); + } + + rc = ata_qc_complete_multiple(ap, qc_active); + if (unlikely(rc < 0 && !(ap->pflags & ATA_PFLAG_RESETTING))) { + ehi->err_mask |= AC_ERR_HSM; + ehi->action |= ATA_EH_RESET; + ata_port_freeze(ap); + } +} + +static void ahci_handle_port_interrupt(struct ata_port *ap, + void __iomem *port_mmio, u32 status) +{ + struct ahci_port_priv *pp = ap->private_data; + struct ahci_host_priv *hpriv = ap->host->private_data; + /* ignore BAD_PMP while resetting */ - if (unlikely(resetting)) + if (unlikely(ap->pflags & ATA_PFLAG_RESETTING)) status &= ~PORT_IRQ_BAD_PMP; if (sata_lpm_ignore_phy_events(&ap->link)) { @@ -1867,6 +1898,12 @@ static void ahci_handle_port_interrupt(struct ata_port *ap, } if (unlikely(status & PORT_IRQ_ERROR)) { + /* + * Before getting the error notification, we may have + * received SDB FISes notifying successful completions. + * Handle these first and then handle the error. + */ + ahci_qc_complete(ap, port_mmio); ahci_error_intr(ap, status); return; } @@ -1903,32 +1940,8 @@ static void ahci_handle_port_interrupt(struct ata_port *ap, } } - /* pp->active_link is not reliable once FBS is enabled, both - * PORT_SCR_ACT and PORT_CMD_ISSUE should be checked because - * NCQ and non-NCQ commands may be in flight at the same time. - */ - if (pp->fbs_enabled) { - if (ap->qc_active) { - qc_active = readl(port_mmio + PORT_SCR_ACT); - qc_active |= readl(port_mmio + PORT_CMD_ISSUE); - } - } else { - /* pp->active_link is valid iff any command is in flight */ - if (ap->qc_active && pp->active_link->sactive) - qc_active = readl(port_mmio + PORT_SCR_ACT); - else - qc_active = readl(port_mmio + PORT_CMD_ISSUE); - } - - - rc = ata_qc_complete_multiple(ap, qc_active); - - /* while resetting, invalid completions are expected */ - if (unlikely(rc < 0 && !resetting)) { - ehi->err_mask |= AC_ERR_HSM; - ehi->action |= ATA_EH_RESET; - ata_port_freeze(ap); - } + /* Handle completed commands */ + ahci_qc_complete(ap, port_mmio); } static void ahci_port_intr(struct ata_port *ap) @@ -2053,11 +2066,18 @@ unsigned int ahci_qc_issue(struct ata_queued_cmd *qc) } EXPORT_SYMBOL_GPL(ahci_qc_issue); -static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc) +static void ahci_qc_fill_rtf(struct ata_queued_cmd *qc) { struct ahci_port_priv *pp = qc->ap->private_data; u8 *rx_fis = pp->rx_fis; + /* + * rtf may already be filled (e.g. for successful NCQ commands). + * If that is the case, we have nothing to do. + */ + if (qc->flags & ATA_QCFLAG_RTF_FILLED) + return; + if (pp->fbs_enabled) rx_fis += qc->dev->link->pmp * AHCI_RX_FIS_SZ; @@ -2068,9 +2088,12 @@ static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc) * Setup FIS. */ if (qc->tf.protocol == ATA_PROT_PIO && qc->dma_dir == DMA_FROM_DEVICE && - !(qc->flags & ATA_QCFLAG_FAILED)) { + !(qc->flags & ATA_QCFLAG_EH)) { ata_tf_from_fis(rx_fis + RX_FIS_PIO_SETUP, &qc->result_tf); qc->result_tf.status = (rx_fis + RX_FIS_PIO_SETUP)[15]; + qc->flags |= ATA_QCFLAG_RTF_FILLED; + return; + } /* * For NCQ commands, we never get a D2H FIS, so reading the D2H Register @@ -2080,15 +2103,85 @@ static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc) * instead. However, the SDB FIS does not contain the LBA, so we can't * use the ata_tf_from_fis() helper. */ - } else if (ata_is_ncq(qc->tf.protocol)) { + if (ata_is_ncq(qc->tf.protocol)) { const u8 *fis = rx_fis + RX_FIS_SDB; + /* + * Successful NCQ commands have been filled already. + * A failed NCQ command will read the status here. + * (Note that a failed NCQ command will get a more specific + * error when reading the NCQ Command Error log.) + */ qc->result_tf.status = fis[2]; qc->result_tf.error = fis[3]; - } else - ata_tf_from_fis(rx_fis + RX_FIS_D2H_REG, &qc->result_tf); + qc->flags |= ATA_QCFLAG_RTF_FILLED; + return; + } + + ata_tf_from_fis(rx_fis + RX_FIS_D2H_REG, &qc->result_tf); + qc->flags |= ATA_QCFLAG_RTF_FILLED; +} + +static void ahci_qc_ncq_fill_rtf(struct ata_port *ap, u64 done_mask) +{ + struct ahci_port_priv *pp = ap->private_data; + const u8 *fis; + + /* No outstanding commands. */ + if (!ap->qc_active) + return; + + /* + * FBS not enabled, so read status and error once, since they are shared + * for all QCs. + */ + if (!pp->fbs_enabled) { + u8 status, error; + + /* No outstanding NCQ commands. */ + if (!pp->active_link->sactive) + return; + + fis = pp->rx_fis + RX_FIS_SDB; + status = fis[2]; + error = fis[3]; - return true; + while (done_mask) { + struct ata_queued_cmd *qc; + unsigned int tag = __ffs64(done_mask); + + qc = ata_qc_from_tag(ap, tag); + if (qc && ata_is_ncq(qc->tf.protocol)) { + qc->result_tf.status = status; + qc->result_tf.error = error; + qc->flags |= ATA_QCFLAG_RTF_FILLED; + } + done_mask &= ~(1ULL << tag); + } + + return; + } + + /* + * FBS enabled, so read the status and error for each QC, since the QCs + * can belong to different PMP links. (Each PMP link has its own FIS + * Receive Area.) + */ + while (done_mask) { + struct ata_queued_cmd *qc; + unsigned int tag = __ffs64(done_mask); + + qc = ata_qc_from_tag(ap, tag); + if (qc && ata_is_ncq(qc->tf.protocol)) { + fis = pp->rx_fis; + fis += qc->dev->link->pmp * AHCI_RX_FIS_SZ; + fis += RX_FIS_SDB; + qc->result_tf.status = fis[2]; + qc->result_tf.error = fis[3]; + qc->flags |= ATA_QCFLAG_RTF_FILLED; + } + done_mask &= ~(1ULL << tag); + } } static void ahci_freeze(struct ata_port *ap) @@ -2138,7 +2231,7 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) struct ata_port *ap = qc->ap; /* make DMA engine forget about the failed command */ - if (qc->flags & ATA_QCFLAG_FAILED) + if (qc->flags & ATA_QCFLAG_EH) ahci_kick_engine(ap); } diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index c4c89d24f84c..14c17c3bda4e 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -552,7 +552,7 @@ static const u8 ata_rw_cmds[] = { 0, 0, 0, - ATA_CMD_WRITE_MULTI_FUA_EXT, + 0, /* pio */ ATA_CMD_PIO_READ, ATA_CMD_PIO_WRITE, @@ -574,17 +574,18 @@ static const u8 ata_rw_cmds[] = { }; /** - * ata_rwcmd_protocol - set taskfile r/w commands and protocol - * @tf: command to examine and configure - * @dev: device tf belongs to + * ata_set_rwcmd_protocol - set taskfile r/w command and protocol + * @dev: target device for the taskfile + * @tf: taskfile to examine and configure * - * Examine the device configuration and tf->flags to calculate - * the proper read/write commands and protocol to use. + * Examine the device configuration and tf->flags to determine + * the proper read/write command and protocol to use for @tf. * * LOCKING: * caller. */ -static int ata_rwcmd_protocol(struct ata_taskfile *tf, struct ata_device *dev) +static bool ata_set_rwcmd_protocol(struct ata_device *dev, + struct ata_taskfile *tf) { u8 cmd; @@ -607,11 +608,12 @@ static int ata_rwcmd_protocol(struct ata_taskfile *tf, struct ata_device *dev) } cmd = ata_rw_cmds[index + fua + lba48 + write]; - if (cmd) { - tf->command = cmd; - return 0; - } - return -1; + if (!cmd) + return false; + + tf->command = cmd; + + return true; } /** @@ -725,7 +727,8 @@ int ata_build_rw_tf(struct ata_queued_cmd *qc, u64 block, u32 n_block, } else if (dev->flags & ATA_DFLAG_LBA) { tf->flags |= ATA_TFLAG_LBA; - if (lba_28_ok(block, n_block)) { + /* We need LBA48 for FUA writes */ + if (!(tf->flags & ATA_TFLAG_FUA) && lba_28_ok(block, n_block)) { /* use LBA28 */ tf->device |= (block >> 24) & 0xf; } else if (lba_48_ok(block, n_block)) { @@ -740,11 +743,12 @@ int ata_build_rw_tf(struct ata_queued_cmd *qc, u64 block, u32 n_block, tf->hob_lbah = (block >> 40) & 0xff; tf->hob_lbam = (block >> 32) & 0xff; tf->hob_lbal = (block >> 24) & 0xff; - } else + } else { /* request too large even for LBA48 */ return -ERANGE; + } - if (unlikely(ata_rwcmd_protocol(tf, dev) < 0)) + if (unlikely(!ata_set_rwcmd_protocol(dev, tf))) return -EINVAL; tf->nsect = n_block & 0xff; @@ -762,7 +766,7 @@ int ata_build_rw_tf(struct ata_queued_cmd *qc, u64 block, u32 n_block, if (!lba_28_ok(block, n_block)) return -ERANGE; - if (unlikely(ata_rwcmd_protocol(tf, dev) < 0)) + if (unlikely(!ata_set_rwcmd_protocol(dev, tf))) return -EINVAL; /* Convert LBA to CHS */ @@ -1590,7 +1594,7 @@ static unsigned ata_exec_internal_sg(struct ata_device *dev, ap->ops->post_internal_cmd(qc); /* perform minimal error analysis */ - if (qc->flags & ATA_QCFLAG_FAILED) { + if (qc->flags & ATA_QCFLAG_EH) { if (qc->result_tf.status & (ATA_ERR | ATA_DF)) qc->err_mask |= AC_ERR_DEV; @@ -2420,6 +2424,28 @@ static void ata_dev_config_chs(struct ata_device *dev) dev->heads, dev->sectors); } +static void ata_dev_config_fua(struct ata_device *dev) +{ + /* Ignore FUA support if its use is disabled globally */ + if (!libata_fua) + goto nofua; + + /* Ignore devices without support for WRITE DMA FUA EXT */ + if (!(dev->flags & ATA_DFLAG_LBA48) || !ata_id_has_fua(dev->id)) + goto nofua; + + /* Ignore known bad devices and devices that lack NCQ support */ + if (!ata_ncq_supported(dev) || (dev->horkage & ATA_HORKAGE_NO_FUA)) + goto nofua; + + dev->flags |= ATA_DFLAG_FUA; + + return; + +nofua: + dev->flags &= ~ATA_DFLAG_FUA; +} + static void ata_dev_config_devslp(struct ata_device *dev) { u8 *sata_setting = dev->link->ap->sector_buf; @@ -2508,7 +2534,8 @@ static void ata_dev_print_features(struct ata_device *dev) return; ata_dev_info(dev, - "Features:%s%s%s%s%s%s\n", + "Features:%s%s%s%s%s%s%s\n", + dev->flags & ATA_DFLAG_FUA ? " FUA" : "", dev->flags & ATA_DFLAG_TRUSTED ? " Trust" : "", dev->flags & ATA_DFLAG_DA ? " Dev-Attention" : "", dev->flags & ATA_DFLAG_DEVSLP ? " Dev-Sleep" : "", @@ -2669,6 +2696,7 @@ int ata_dev_configure(struct ata_device *dev) ata_dev_config_chs(dev); } + ata_dev_config_fua(dev); ata_dev_config_devslp(dev); ata_dev_config_sense_reporting(dev); ata_dev_config_zac(dev); @@ -4106,6 +4134,12 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { */ { "SATADOM-ML 3ME", NULL, ATA_HORKAGE_NO_LOG_DIR }, + /* Buggy FUA */ + { "Maxtor", "BANC1G10", ATA_HORKAGE_NO_FUA }, + { "WDC*WD2500J*", NULL, ATA_HORKAGE_NO_FUA }, + { "OCZ-VERTEX*", NULL, ATA_HORKAGE_NO_FUA }, + { "INTEL*SSDSC2CT*", NULL, ATA_HORKAGE_NO_FUA }, + /* End Marker */ { } }; @@ -4686,10 +4720,10 @@ void ata_qc_complete(struct ata_queued_cmd *qc) /* XXX: New EH and old EH use different mechanisms to * synchronize EH with regular execution path. * - * In new EH, a failed qc is marked with ATA_QCFLAG_FAILED. + * In new EH, a qc owned by EH is marked with ATA_QCFLAG_EH. * Normal execution path is responsible for not accessing a - * failed qc. libata core enforces the rule by returning NULL - * from ata_qc_from_tag() for failed qcs. + * qc owned by EH. libata core enforces the rule by returning NULL + * from ata_qc_from_tag() for qcs owned by EH. * * Old EH depends on ata_qc_complete() nullifying completion * requests if ATA_QCFLAG_EH_SCHEDULED is set. Old EH does @@ -4701,7 +4735,7 @@ void ata_qc_complete(struct ata_queued_cmd *qc) struct ata_eh_info *ehi = &dev->link->eh_info; if (unlikely(qc->err_mask)) - qc->flags |= ATA_QCFLAG_FAILED; + qc->flags |= ATA_QCFLAG_EH; /* * Finish internal commands without any further processing @@ -4718,7 +4752,7 @@ void ata_qc_complete(struct ata_queued_cmd *qc) * Non-internal qc has failed. Fill the result TF and * summon EH. */ - if (unlikely(qc->flags & ATA_QCFLAG_FAILED)) { + if (unlikely(qc->flags & ATA_QCFLAG_EH)) { fill_result_tf(qc); trace_ata_qc_complete_failed(qc); ata_qc_schedule_eh(qc); @@ -6217,6 +6251,7 @@ static const struct ata_force_param force_tbl[] __initconst = { force_horkage_onoff(lpm, ATA_HORKAGE_NOLPM), force_horkage_onoff(setxfer, ATA_HORKAGE_NOSETXFER), force_horkage_on(dump_id, ATA_HORKAGE_DUMP_ID), + force_horkage_onoff(fua, ATA_HORKAGE_NO_FUA), force_horkage_on(disable, ATA_HORKAGE_DISABLE), }; diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 34303ce67c14..a6c901811802 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -565,17 +565,23 @@ void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap, { int i; unsigned long flags; + struct scsi_cmnd *scmd, *tmp; + int nr_timedout = 0; /* make sure sff pio task is not running */ ata_sff_flush_pio_task(ap); + if (!ap->ops->error_handler) + return; + /* synchronize with host lock and sort out timeouts */ - /* For new EH, all qcs are finished in one of three ways - + /* + * For new EH, all qcs are finished in one of three ways - * normal completion, error completion, and SCSI timeout. * Both completions can race against SCSI timeout. When normal * completion wins, the qc never reaches EH. When error - * completion wins, the qc has ATA_QCFLAG_FAILED set. + * completion wins, the qc has ATA_QCFLAG_EH set. * * When SCSI timeout wins, things are a bit more complex. * Normal or error completion can occur after the timeout but @@ -584,64 +590,61 @@ void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap, * timed out iff its associated qc is active and not failed. */ spin_lock_irqsave(ap->lock, flags); - if (ap->ops->error_handler) { - struct scsi_cmnd *scmd, *tmp; - int nr_timedout = 0; - - /* This must occur under the ap->lock as we don't want - a polled recovery to race the real interrupt handler - - The lost_interrupt handler checks for any completed but - non-notified command and completes much like an IRQ handler. - We then fall into the error recovery code which will treat - this as if normal completion won the race */ - - if (ap->ops->lost_interrupt) - ap->ops->lost_interrupt(ap); + /* + * This must occur under the ap->lock as we don't want + * a polled recovery to race the real interrupt handler + * + * The lost_interrupt handler checks for any completed but + * non-notified command and completes much like an IRQ handler. + * + * We then fall into the error recovery code which will treat + * this as if normal completion won the race + */ + if (ap->ops->lost_interrupt) + ap->ops->lost_interrupt(ap); - list_for_each_entry_safe(scmd, tmp, eh_work_q, eh_entry) { - struct ata_queued_cmd *qc; + list_for_each_entry_safe(scmd, tmp, eh_work_q, eh_entry) { + struct ata_queued_cmd *qc; - ata_qc_for_each_raw(ap, qc, i) { - if (qc->flags & ATA_QCFLAG_ACTIVE && - qc->scsicmd == scmd) - break; - } + ata_qc_for_each_raw(ap, qc, i) { + if (qc->flags & ATA_QCFLAG_ACTIVE && + qc->scsicmd == scmd) + break; + } - if (i < ATA_MAX_QUEUE) { - /* the scmd has an associated qc */ - if (!(qc->flags & ATA_QCFLAG_FAILED)) { - /* which hasn't failed yet, timeout */ - qc->err_mask |= AC_ERR_TIMEOUT; - qc->flags |= ATA_QCFLAG_FAILED; - nr_timedout++; - } - } else { - /* Normal completion occurred after - * SCSI timeout but before this point. - * Successfully complete it. - */ - scmd->retries = scmd->allowed; - scsi_eh_finish_cmd(scmd, &ap->eh_done_q); + if (i < ATA_MAX_QUEUE) { + /* the scmd has an associated qc */ + if (!(qc->flags & ATA_QCFLAG_EH)) { + /* which hasn't failed yet, timeout */ + qc->err_mask |= AC_ERR_TIMEOUT; + qc->flags |= ATA_QCFLAG_EH; + nr_timedout++; } + } else { + /* Normal completion occurred after + * SCSI timeout but before this point. + * Successfully complete it. + */ + scmd->retries = scmd->allowed; + scsi_eh_finish_cmd(scmd, &ap->eh_done_q); } + } - /* If we have timed out qcs. They belong to EH from - * this point but the state of the controller is - * unknown. Freeze the port to make sure the IRQ - * handler doesn't diddle with those qcs. This must - * be done atomically w.r.t. setting QCFLAG_FAILED. - */ - if (nr_timedout) - __ata_port_freeze(ap); + /* + * If we have timed out qcs. They belong to EH from + * this point but the state of the controller is + * unknown. Freeze the port to make sure the IRQ + * handler doesn't diddle with those qcs. This must + * be done atomically w.r.t. setting ATA_QCFLAG_EH. + */ + if (nr_timedout) + __ata_port_freeze(ap); + /* initialize eh_tries */ + ap->eh_tries = ATA_EH_MAX_TRIES; - /* initialize eh_tries */ - ap->eh_tries = ATA_EH_MAX_TRIES; - } spin_unlock_irqrestore(ap->lock, flags); - } EXPORT_SYMBOL(ata_scsi_cmd_error_handler); @@ -911,12 +914,12 @@ void ata_qc_schedule_eh(struct ata_queued_cmd *qc) WARN_ON(!ap->ops->error_handler); - qc->flags |= ATA_QCFLAG_FAILED; + qc->flags |= ATA_QCFLAG_EH; ata_eh_set_pending(ap, 1); /* The following will fail if timeout has already expired. * ata_scsi_error() takes care of such scmds on EH entry. - * Note that ATA_QCFLAG_FAILED is unconditionally set after + * Note that ATA_QCFLAG_EH is unconditionally set after * this function completes. */ blk_abort_request(scsi_cmd_to_rq(qc->scsicmd)); @@ -994,7 +997,7 @@ static int ata_do_link_abort(struct ata_port *ap, struct ata_link *link) /* include internal tag in iteration */ ata_qc_for_each_with_internal(ap, qc, tag) { if (qc && (!link || qc->dev->link == link)) { - qc->flags |= ATA_QCFLAG_FAILED; + qc->flags |= ATA_QCFLAG_EH; ata_qc_complete(qc); nr_aborted++; } @@ -1954,7 +1957,7 @@ static void ata_eh_link_autopsy(struct ata_link *link) all_err_mask |= ehc->i.err_mask; ata_qc_for_each_raw(ap, qc, tag) { - if (!(qc->flags & ATA_QCFLAG_FAILED) || + if (!(qc->flags & ATA_QCFLAG_EH) || qc->flags & ATA_QCFLAG_RETRY || ata_dev_phys_link(qc->dev) != link) continue; @@ -2232,7 +2235,7 @@ static void ata_eh_link_report(struct ata_link *link) desc = ehc->i.desc; ata_qc_for_each_raw(ap, qc, tag) { - if (!(qc->flags & ATA_QCFLAG_FAILED) || + if (!(qc->flags & ATA_QCFLAG_EH) || ata_dev_phys_link(qc->dev) != link || ((qc->flags & ATA_QCFLAG_QUIET) && qc->err_mask == AC_ERR_DEV)) @@ -2298,7 +2301,7 @@ static void ata_eh_link_report(struct ata_link *link) char data_buf[20] = ""; char cdb_buf[70] = ""; - if (!(qc->flags & ATA_QCFLAG_FAILED) || + if (!(qc->flags & ATA_QCFLAG_EH) || ata_dev_phys_link(qc->dev) != link || !qc->err_mask) continue; @@ -3802,7 +3805,7 @@ void ata_eh_finish(struct ata_port *ap) /* retry or finish qcs */ ata_qc_for_each_raw(ap, qc, tag) { - if (!(qc->flags & ATA_QCFLAG_FAILED)) + if (!(qc->flags & ATA_QCFLAG_EH)) continue; if (qc->err_mask) { diff --git a/drivers/ata/libata-sata.c b/drivers/ata/libata-sata.c index 18ef14e749a0..f3e7396e3191 100644 --- a/drivers/ata/libata-sata.c +++ b/drivers/ata/libata-sata.c @@ -655,6 +655,9 @@ int ata_qc_complete_multiple(struct ata_port *ap, u64 qc_active) return -EINVAL; } + if (ap->ops->qc_ncq_fill_rtf) + ap->ops->qc_ncq_fill_rtf(ap, done_mask); + while (done_mask) { struct ata_queued_cmd *qc; unsigned int tag = __ffs64(done_mask); @@ -1429,7 +1432,7 @@ void ata_eh_analyze_ncq_error(struct ata_link *link) /* has LLDD analyzed already? */ ata_qc_for_each_raw(ap, qc, tag) { - if (!(qc->flags & ATA_QCFLAG_FAILED)) + if (!(qc->flags & ATA_QCFLAG_EH)) continue; if (qc->err_mask) @@ -1477,7 +1480,7 @@ void ata_eh_analyze_ncq_error(struct ata_link *link) } ata_qc_for_each_raw(ap, qc, tag) { - if (!(qc->flags & ATA_QCFLAG_FAILED) || + if (!(qc->flags & ATA_QCFLAG_EH) || ata_dev_phys_link(qc->dev) != link) continue; diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index cbb3a7a50816..aa338f10cef2 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1654,7 +1654,8 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc) struct ata_port *ap = qc->ap; struct scsi_cmnd *cmd = qc->scsicmd; u8 *cdb = cmd->cmnd; - int need_sense = (qc->err_mask != 0); + int need_sense = (qc->err_mask != 0) && + !(qc->flags & ATA_QCFLAG_SENSE_VALID); /* For ATA pass thru (SAT) commands, generate a sense block if * user mandated it or if there's an error. Note that if we @@ -1668,12 +1669,11 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc) if (((cdb[0] == ATA_16) || (cdb[0] == ATA_12)) && ((cdb[2] & 0x20) || need_sense)) ata_gen_passthru_sense(qc); - else if (qc->flags & ATA_QCFLAG_SENSE_VALID) - cmd->result = SAM_STAT_CHECK_CONDITION; else if (need_sense) ata_gen_ata_sense(qc); else - cmd->result = SAM_STAT_GOOD; + /* Keep the SCSI ML and status byte, clear host byte. */ + cmd->result &= 0x0000ffff; if (need_sense && !ap->ops->error_handler) ata_dump_status(ap, &qc->result_tf); @@ -2240,30 +2240,6 @@ static unsigned int ata_msense_rw_recovery(u8 *buf, bool changeable) return sizeof(def_rw_recovery_mpage); } -/* - * We can turn this into a real blacklist if it's needed, for now just - * blacklist any Maxtor BANC1G10 revision firmware - */ -static int ata_dev_supports_fua(u16 *id) -{ - unsigned char model[ATA_ID_PROD_LEN + 1], fw[ATA_ID_FW_REV_LEN + 1]; - - if (!libata_fua) - return 0; - if (!ata_id_has_fua(id)) - return 0; - - ata_id_c_string(id, model, ATA_ID_PROD, sizeof(model)); - ata_id_c_string(id, fw, ATA_ID_FW_REV, sizeof(fw)); - - if (strcmp(model, "Maxtor")) - return 1; - if (strcmp(fw, "BANC1G10")) - return 1; - - return 0; /* blacklisted */ -} - /** * ata_scsiop_mode_sense - Simulate MODE SENSE 6, 10 commands * @args: device IDENTIFY data / SCSI command of interest. @@ -2287,7 +2263,7 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf) }; u8 pg, spg; unsigned int ebd, page_control, six_byte; - u8 dpofua, bp = 0xff; + u8 dpofua = 0, bp = 0xff; u16 fp; six_byte = (scsicmd[0] == MODE_SENSE); @@ -2350,9 +2326,7 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf) goto invalid_fld; } - dpofua = 0; - if (ata_dev_supports_fua(args->id) && (dev->flags & ATA_DFLAG_LBA48) && - (!(dev->flags & ATA_DFLAG_PIO) || dev->multi_count)) + if (dev->flags & ATA_DFLAG_FUA) dpofua = 1 << 4; if (six_byte) { @@ -3266,11 +3240,12 @@ static unsigned int ata_scsiop_maint_in(struct ata_scsi_args *args, u8 *rbuf) u8 supported = 0; unsigned int err = 0; - if (cdb[2] != 1) { + if (cdb[2] != 1 && cdb[2] != 3) { ata_dev_warn(dev, "invalid command format %d\n", cdb[2]); err = 2; goto out; } + switch (cdb[3]) { case INQUIRY: case MODE_SENSE: diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 153f49e00713..cd82d3b5ed14 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -1377,14 +1377,10 @@ EXPORT_SYMBOL_GPL(ata_sff_qc_issue); * * LOCKING: * spin_lock_irqsave(host lock) - * - * RETURNS: - * true indicating that result TF is successfully filled. */ -bool ata_sff_qc_fill_rtf(struct ata_queued_cmd *qc) +void ata_sff_qc_fill_rtf(struct ata_queued_cmd *qc) { qc->ap->ops->sff_tf_read(qc->ap, &qc->result_tf); - return true; } EXPORT_SYMBOL_GPL(ata_sff_qc_fill_rtf); @@ -2073,7 +2069,7 @@ void ata_sff_error_handler(struct ata_port *ap) unsigned long flags; qc = __ata_qc_from_tag(ap, ap->link.active_tag); - if (qc && !(qc->flags & ATA_QCFLAG_FAILED)) + if (qc && !(qc->flags & ATA_QCFLAG_EH)) qc = NULL; spin_lock_irqsave(ap->lock, flags); @@ -2796,7 +2792,7 @@ void ata_bmdma_error_handler(struct ata_port *ap) bool thaw = false; qc = __ata_qc_from_tag(ap, ap->link.active_tag); - if (qc && !(qc->flags & ATA_QCFLAG_FAILED)) + if (qc && !(qc->flags & ATA_QCFLAG_EH)) qc = NULL; /* reset PIO HSM and stop DMA engine */ diff --git a/drivers/ata/libata-trace.c b/drivers/ata/libata-trace.c index e0e4d0d5a100..9b5363fd0ab0 100644 --- a/drivers/ata/libata-trace.c +++ b/drivers/ata/libata-trace.c @@ -142,7 +142,7 @@ libata_trace_parse_qc_flags(struct trace_seq *p, unsigned int qc_flags) trace_seq_printf(p, "QUIET "); if (qc_flags & ATA_QCFLAG_RETRY) trace_seq_printf(p, "RETRY "); - if (qc_flags & ATA_QCFLAG_FAILED) + if (qc_flags & ATA_QCFLAG_EH) trace_seq_printf(p, "FAILED "); if (qc_flags & ATA_QCFLAG_SENSE_VALID) trace_seq_printf(p, "SENSE_VALID "); diff --git a/drivers/ata/pata_parport/Kconfig b/drivers/ata/pata_parport/Kconfig new file mode 100644 index 000000000000..0893a13e7979 --- /dev/null +++ b/drivers/ata/pata_parport/Kconfig @@ -0,0 +1,141 @@ +# SPDX-License-Identifier: GPL-2.0 + +comment "Parallel IDE protocol modules" + depends on PATA_PARPORT + +config PATA_PARPORT_ATEN + tristate "ATEN EH-100 protocol" + depends on PATA_PARPORT + help + This option enables support for the ATEN EH-100 parallel port IDE + protocol. This protocol is used in some inexpensive low performance + parallel port kits made in Hong Kong. + +config PATA_PARPORT_BPCK + tristate "MicroSolutions backpack (Series 5) protocol" + depends on PATA_PARPORT + help + This option enables support for the Micro Solutions BACKPACK + parallel port Series 5 IDE protocol. (Most BACKPACK drives made + before 1999 were Series 5) Series 5 drives will NOT always have the + Series noted on the bottom of the drive. Series 6 drivers will. + + In other words, if your BACKPACK drive doesn't say "Series 6" on the + bottom, enable this option. + +config PATA_PARPORT_BPCK6 + tristate "MicroSolutions backpack (Series 6) protocol" + depends on (PATA_PARPORT) && !64BIT + help + This option enables support for the Micro Solutions BACKPACK + parallel port Series 6 IDE protocol. (Most BACKPACK drives made + after 1999 were Series 6) Series 6 drives will have the Series noted + on the bottom of the drive. Series 5 drivers don't always have it + noted. + + In other words, if your BACKPACK drive says "Series 6" on the + bottom, enable this option. + +config PATA_PARPORT_COMM + tristate "DataStor Commuter protocol" + depends on PATA_PARPORT + help + This option enables support for the Commuter parallel port IDE + protocol from DataStor. + +config PATA_PARPORT_DSTR + tristate "DataStor EP-2000 protocol" + depends on PATA_PARPORT + help + This option enables support for the EP-2000 parallel port IDE + protocol from DataStor + +config PATA_PARPORT_FIT2 + tristate "FIT TD-2000 protocol" + depends on PATA_PARPORT + help + This option enables support for the TD-2000 parallel port IDE + protocol from Fidelity International Technology. This is a simple + (low speed) adapter that is used in some portable hard drives. + +config PATA_PARPORT_FIT3 + tristate "FIT TD-3000 protocol" + depends on PATA_PARPORT + help + This option enables support for the TD-3000 parallel port IDE + protocol from Fidelity International Technology. This protocol is + used in newer models of their portable disk, CD-ROM and PD/CD + devices. + +config PATA_PARPORT_EPAT + tristate "Shuttle EPAT/EPEZ protocol" + depends on PATA_PARPORT + help + This option enables support for the EPAT parallel port IDE protocol. + EPAT is a parallel port IDE adapter manufactured by Shuttle + Technology and widely used in devices from major vendors such as + Hewlett-Packard, SyQuest, Imation and Avatar. + +config PATA_PARPORT_EPATC8 + bool "Support c7/c8 chips" + depends on PATA_PARPORT_EPAT + help + This option enables support for the newer Shuttle EP1284 (aka c7 and + c8) chip. You need this if you are using any recent Imation SuperDisk + (LS-120) drive. + +config PATA_PARPORT_EPIA + tristate "Shuttle EPIA protocol" + depends on PATA_PARPORT + help + This option enables support for the (obsolete) EPIA parallel port + IDE protocol from Shuttle Technology. This adapter can still be + found in some no-name kits. + +config PATA_PARPORT_FRIQ + tristate "Freecom IQ ASIC-2 protocol" + depends on PATA_PARPORT + help + This option enables support for version 2 of the Freecom IQ parallel + port IDE adapter. This adapter is used by the Maxell Superdisk + drive. + +config PATA_PARPORT_FRPW + tristate "FreeCom power protocol" + depends on PATA_PARPORT + help + This option enables support for the Freecom power parallel port IDE + protocol. + +config PATA_PARPORT_KBIC + tristate "KingByte KBIC-951A/971A protocols" + depends on PATA_PARPORT + help + This option enables support for the KBIC-951A and KBIC-971A parallel + port IDE protocols from KingByte Information Corp. KingByte's + adapters appear in many no-name portable disk and CD-ROM products, + especially in Europe. + +config PATA_PARPORT_KTTI + tristate "KT PHd protocol" + depends on PATA_PARPORT + help + This option enables support for the "PHd" parallel port IDE protocol + from KT Technology. This is a simple (low speed) adapter that is + used in some 2.5" portable hard drives. + +config PATA_PARPORT_ON20 + tristate "OnSpec 90c20 protocol" + depends on PATA_PARPORT + help + This option enables support for the (obsolete) 90c20 parallel port + IDE protocol from OnSpec (often marketed under the ValuStore brand + name). + +config PATA_PARPORT_ON26 + tristate "OnSpec 90c26 protocol" + depends on PATA_PARPORT + help + This option enables support for the 90c26 parallel port IDE protocol + from OnSpec Electronics (often marketed under the ValuStore brand + name). diff --git a/drivers/ata/pata_parport/Makefile b/drivers/ata/pata_parport/Makefile new file mode 100644 index 000000000000..0932c8d55b91 --- /dev/null +++ b/drivers/ata/pata_parport/Makefile @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_PATA_PARPORT) += pata_parport.o + +obj-$(CONFIG_PATA_PARPORT_ATEN) += aten.o +obj-$(CONFIG_PATA_PARPORT_BPCK) += bpck.o +obj-$(CONFIG_PATA_PARPORT_COMM) += comm.o +obj-$(CONFIG_PATA_PARPORT_DSTR) += dstr.o +obj-$(CONFIG_PATA_PARPORT_KBIC) += kbic.o +obj-$(CONFIG_PATA_PARPORT_EPAT) += epat.o +obj-$(CONFIG_PATA_PARPORT_EPIA) += epia.o +obj-$(CONFIG_PATA_PARPORT_FRPW) += frpw.o +obj-$(CONFIG_PATA_PARPORT_FRIQ) += friq.o +obj-$(CONFIG_PATA_PARPORT_FIT2) += fit2.o +obj-$(CONFIG_PATA_PARPORT_FIT3) += fit3.o +obj-$(CONFIG_PATA_PARPORT_ON20) += on20.o +obj-$(CONFIG_PATA_PARPORT_ON26) += on26.o +obj-$(CONFIG_PATA_PARPORT_KTTI) += ktti.o +obj-$(CONFIG_PATA_PARPORT_BPCK6) += bpck6.o diff --git a/drivers/ata/pata_parport/aten.c b/drivers/ata/pata_parport/aten.c new file mode 100644 index 000000000000..b66508bedbd0 --- /dev/null +++ b/drivers/ata/pata_parport/aten.c @@ -0,0 +1,162 @@ +/* + aten.c (c) 1997-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU General Public License. + + aten.c is a low-level protocol driver for the ATEN EH-100 + parallel port adapter. The EH-100 supports 4-bit and 8-bit + modes only. There is also an EH-132 which supports EPP mode + transfers. The EH-132 is not yet supported. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.05 init_proto, release_proto + +*/ + +#define ATEN_VERSION "1.01" + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/wait.h> +#include <linux/types.h> +#include <asm/io.h> + +#include <linux/pata_parport.h> + +#define j44(a,b) ((((a>>4)&0x0f)|(b&0xf0))^0x88) + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int cont_map[2] = { 0x08, 0x20 }; + +static void aten_write_regr( PIA *pi, int cont, int regr, int val) + +{ int r; + + r = regr + cont_map[cont] + 0x80; + + w0(r); w2(0xe); w2(6); w0(val); w2(7); w2(6); w2(0xc); +} + +static int aten_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, r; + + r = regr + cont_map[cont] + 0x40; + + switch (pi->mode) { + + case 0: w0(r); w2(0xe); w2(6); + w2(7); w2(6); w2(0); + a = r1(); w0(0x10); b = r1(); w2(0xc); + return j44(a,b); + + case 1: r |= 0x10; + w0(r); w2(0xe); w2(6); w0(0xff); + w2(0x27); w2(0x26); w2(0x20); + a = r0(); + w2(0x26); w2(0xc); + return a; + } + return -1; +} + +static void aten_read_block( PIA *pi, char * buf, int count ) + +{ int k, a, b, c, d; + + switch (pi->mode) { + + case 0: w0(0x48); w2(0xe); w2(6); + for (k=0;k<count/2;k++) { + w2(7); w2(6); w2(2); + a = r1(); w0(0x58); b = r1(); + w2(0); d = r1(); w0(0x48); c = r1(); + buf[2*k] = j44(c,d); + buf[2*k+1] = j44(a,b); + } + w2(0xc); + break; + + case 1: w0(0x58); w2(0xe); w2(6); + for (k=0;k<count/2;k++) { + w2(0x27); w2(0x26); w2(0x22); + a = r0(); w2(0x20); b = r0(); + buf[2*k] = b; buf[2*k+1] = a; + } + w2(0x26); w2(0xc); + break; + } +} + +static void aten_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + w0(0x88); w2(0xe); w2(6); + for (k=0;k<count/2;k++) { + w0(buf[2*k+1]); w2(0xe); w2(6); + w0(buf[2*k]); w2(7); w2(6); + } + w2(0xc); +} + +static void aten_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + w2(0xc); +} + +static void aten_disconnect ( PIA *pi ) + +{ w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void aten_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[2] = {"4-bit","8-bit"}; + + printk("%s: aten %s, ATEN EH-100 at 0x%x, ", + pi->device,ATEN_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static struct pi_protocol aten = { + .owner = THIS_MODULE, + .name = "aten", + .max_mode = 2, + .epp_first = 2, + .default_delay = 1, + .max_units = 1, + .write_regr = aten_write_regr, + .read_regr = aten_read_regr, + .write_block = aten_write_block, + .read_block = aten_read_block, + .connect = aten_connect, + .disconnect = aten_disconnect, + .log_adapter = aten_log_adapter, +}; + +static int __init aten_init(void) +{ + return paride_register(&aten); +} + +static void __exit aten_exit(void) +{ + paride_unregister( &aten ); +} + +MODULE_LICENSE("GPL"); +module_init(aten_init) +module_exit(aten_exit) diff --git a/drivers/ata/pata_parport/bpck.c b/drivers/ata/pata_parport/bpck.c new file mode 100644 index 000000000000..5fb3cf9ba11d --- /dev/null +++ b/drivers/ata/pata_parport/bpck.c @@ -0,0 +1,477 @@ +/* + bpck.c (c) 1996-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU General Public License. + + bpck.c is a low-level protocol driver for the MicroSolutions + "backpack" parallel port IDE adapter. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.05 init_proto, release_proto, pi->delay + 1.02 GRG 1998.08.15 default pi->delay returned to 4 + +*/ + +#define BPCK_VERSION "1.02" + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <asm/io.h> + +#include <linux/pata_parport.h> + +#undef r2 +#undef w2 +#undef PC + +#define PC pi->private +#define r2() (PC=(in_p(2) & 0xff)) +#define w2(byte) {out_p(2,byte); PC = byte;} +#define t2(pat) {PC ^= pat; out_p(2,PC);} +#define e2() {PC &= 0xfe; out_p(2,PC);} +#define o2() {PC |= 1; out_p(2,PC);} + +#define j44(l,h) (((l>>3)&0x7)|((l>>4)&0x8)|((h<<1)&0x70)|(h&0x80)) + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set + cont = 2 - use internal bpck register addressing +*/ + +static int cont_map[3] = { 0x40, 0x48, 0 }; + +static int bpck_read_regr( PIA *pi, int cont, int regr ) + +{ int r, l, h; + + r = regr + cont_map[cont]; + + switch (pi->mode) { + + case 0: w0(r & 0xf); w0(r); t2(2); t2(4); + l = r1(); + t2(4); + h = r1(); + return j44(l,h); + + case 1: w0(r & 0xf); w0(r); t2(2); + e2(); t2(0x20); + t2(4); h = r0(); + t2(1); t2(0x20); + return h; + + case 2: + case 3: + case 4: w0(r); w2(9); w2(0); w2(0x20); + h = r4(); + w2(0); + return h; + + } + return -1; +} + +static void bpck_write_regr( PIA *pi, int cont, int regr, int val ) + +{ int r; + + r = regr + cont_map[cont]; + + switch (pi->mode) { + + case 0: + case 1: w0(r); + t2(2); + w0(val); + o2(); t2(4); t2(1); + break; + + case 2: + case 3: + case 4: w0(r); w2(9); w2(0); + w0(val); w2(1); w2(3); w2(0); + break; + + } +} + +/* These macros access the bpck registers in native addressing */ + +#define WR(r,v) bpck_write_regr(pi,2,r,v) +#define RR(r) (bpck_read_regr(pi,2,r)) + +static void bpck_write_block( PIA *pi, char * buf, int count ) + +{ int i; + + switch (pi->mode) { + + case 0: WR(4,0x40); + w0(0x40); t2(2); t2(1); + for (i=0;i<count;i++) { w0(buf[i]); t2(4); } + WR(4,0); + break; + + case 1: WR(4,0x50); + w0(0x40); t2(2); t2(1); + for (i=0;i<count;i++) { w0(buf[i]); t2(4); } + WR(4,0x10); + break; + + case 2: WR(4,0x48); + w0(0x40); w2(9); w2(0); w2(1); + for (i=0;i<count;i++) w4(buf[i]); + w2(0); + WR(4,8); + break; + + case 3: WR(4,0x48); + w0(0x40); w2(9); w2(0); w2(1); + for (i=0;i<count/2;i++) w4w(((u16 *)buf)[i]); + w2(0); + WR(4,8); + break; + + case 4: WR(4,0x48); + w0(0x40); w2(9); w2(0); w2(1); + for (i=0;i<count/4;i++) w4l(((u32 *)buf)[i]); + w2(0); + WR(4,8); + break; + } +} + +static void bpck_read_block( PIA *pi, char * buf, int count ) + +{ int i, l, h; + + switch (pi->mode) { + + case 0: WR(4,0x40); + w0(0x40); t2(2); + for (i=0;i<count;i++) { + t2(4); l = r1(); + t2(4); h = r1(); + buf[i] = j44(l,h); + } + WR(4,0); + break; + + case 1: WR(4,0x50); + w0(0x40); t2(2); t2(0x20); + for(i=0;i<count;i++) { t2(4); buf[i] = r0(); } + t2(1); t2(0x20); + WR(4,0x10); + break; + + case 2: WR(4,0x48); + w0(0x40); w2(9); w2(0); w2(0x20); + for (i=0;i<count;i++) buf[i] = r4(); + w2(0); + WR(4,8); + break; + + case 3: WR(4,0x48); + w0(0x40); w2(9); w2(0); w2(0x20); + for (i=0;i<count/2;i++) ((u16 *)buf)[i] = r4w(); + w2(0); + WR(4,8); + break; + + case 4: WR(4,0x48); + w0(0x40); w2(9); w2(0); w2(0x20); + for (i=0;i<count/4;i++) ((u32 *)buf)[i] = r4l(); + w2(0); + WR(4,8); + break; + + } +} + +static int bpck_probe_unit ( PIA *pi ) + +{ int o1, o0, f7, id; + int t, s; + + id = pi->unit; + s = 0; + w2(4); w2(0xe); r2(); t2(2); + o1 = r1()&0xf8; + o0 = r0(); + w0(255-id); w2(4); w0(id); + t2(8); t2(8); t2(8); + t2(2); t = r1()&0xf8; + f7 = ((id % 8) == 7); + if ((f7) || (t != o1)) { t2(2); s = r1()&0xf8; } + if ((t == o1) && ((!f7) || (s == o1))) { + w2(0x4c); w0(o0); + return 0; + } + t2(8); w0(0); t2(2); w2(0x4c); w0(o0); + return 1; +} + +static void bpck_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + w0(0xff-pi->unit); w2(4); w0(pi->unit); + t2(8); t2(8); t2(8); + t2(2); t2(2); + + switch (pi->mode) { + + case 0: t2(8); WR(4,0); + break; + + case 1: t2(8); WR(4,0x10); + break; + + case 2: + case 3: + case 4: w2(0); WR(4,8); + break; + + } + + WR(5,8); + + if (pi->devtype == PI_PCD) { + WR(0x46,0x10); /* fiddle with ESS logic ??? */ + WR(0x4c,0x38); + WR(0x4d,0x88); + WR(0x46,0xa0); + WR(0x41,0); + WR(0x4e,8); + } +} + +static void bpck_disconnect ( PIA *pi ) + +{ w0(0); + if (pi->mode >= 2) { w2(9); w2(0); } else t2(2); + w2(0x4c); w0(pi->saved_r0); +} + +static void bpck_force_spp ( PIA *pi ) + +/* This fakes the EPP protocol to turn off EPP ... */ + +{ pi->saved_r0 = r0(); + w0(0xff-pi->unit); w2(4); w0(pi->unit); + t2(8); t2(8); t2(8); + t2(2); t2(2); + + w2(0); + w0(4); w2(9); w2(0); + w0(0); w2(1); w2(3); w2(0); + w0(0); w2(9); w2(0); + w2(0x4c); w0(pi->saved_r0); +} + +#define TEST_LEN 16 + +static int bpck_test_proto( PIA *pi, char * scratch, int verbose ) + +{ int i, e, l, h, om; + char buf[TEST_LEN]; + + bpck_force_spp(pi); + + switch (pi->mode) { + + case 0: bpck_connect(pi); + WR(0x13,0x7f); + w0(0x13); t2(2); + for(i=0;i<TEST_LEN;i++) { + t2(4); l = r1(); + t2(4); h = r1(); + buf[i] = j44(l,h); + } + bpck_disconnect(pi); + break; + + case 1: bpck_connect(pi); + WR(0x13,0x7f); + w0(0x13); t2(2); t2(0x20); + for(i=0;i<TEST_LEN;i++) { t2(4); buf[i] = r0(); } + t2(1); t2(0x20); + bpck_disconnect(pi); + break; + + case 2: + case 3: + case 4: om = pi->mode; + pi->mode = 0; + bpck_connect(pi); + WR(7,3); + WR(4,8); + bpck_disconnect(pi); + + pi->mode = om; + bpck_connect(pi); + w0(0x13); w2(9); w2(1); w0(0); w2(3); w2(0); w2(0xe0); + + switch (pi->mode) { + case 2: for (i=0;i<TEST_LEN;i++) buf[i] = r4(); + break; + case 3: for (i=0;i<TEST_LEN/2;i++) ((u16 *)buf)[i] = r4w(); + break; + case 4: for (i=0;i<TEST_LEN/4;i++) ((u32 *)buf)[i] = r4l(); + break; + } + + w2(0); + WR(7,0); + bpck_disconnect(pi); + + break; + + } + + if (verbose) { + printk("%s: bpck: 0x%x unit %d mode %d: ", + pi->device,pi->port,pi->unit,pi->mode); + for (i=0;i<TEST_LEN;i++) printk("%3d",buf[i]); + printk("\n"); + } + + e = 0; + for (i=0;i<TEST_LEN;i++) if (buf[i] != (i+1)) e++; + return e; +} + +static void bpck_read_eeprom ( PIA *pi, char * buf ) + +{ int i, j, k, p, v, f, om, od; + + bpck_force_spp(pi); + + om = pi->mode; od = pi->delay; + pi->mode = 0; pi->delay = 6; + + bpck_connect(pi); + + WR(4,0); + for (i=0;i<64;i++) { + WR(6,8); + WR(6,0xc); + p = 0x100; + for (k=0;k<9;k++) { + f = (((i + 0x180) & p) != 0) * 2; + WR(6,f+0xc); + WR(6,f+0xd); + WR(6,f+0xc); + p = (p >> 1); + } + for (j=0;j<2;j++) { + v = 0; + for (k=0;k<8;k++) { + WR(6,0xc); + WR(6,0xd); + WR(6,0xc); + f = RR(0); + v = 2*v + (f == 0x84); + } + buf[2*i+1-j] = v; + } + } + WR(6,8); + WR(6,0); + WR(5,8); + + bpck_disconnect(pi); + + if (om >= 2) { + bpck_connect(pi); + WR(7,3); + WR(4,8); + bpck_disconnect(pi); + } + + pi->mode = om; pi->delay = od; +} + +static int bpck_test_port ( PIA *pi ) /* check for 8-bit port */ + +{ int i, r, m; + + w2(0x2c); i = r0(); w0(255-i); r = r0(); w0(i); + m = -1; + if (r == i) m = 2; + if (r == (255-i)) m = 0; + + w2(0xc); i = r0(); w0(255-i); r = r0(); w0(i); + if (r != (255-i)) m = -1; + + if (m == 0) { w2(6); w2(0xc); r = r0(); w0(0xaa); w0(r); w0(0xaa); } + if (m == 2) { w2(0x26); w2(0xc); } + + if (m == -1) return 0; + return 5; +} + +static void bpck_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[5] = { "4-bit","8-bit","EPP-8", + "EPP-16","EPP-32" }; + +#ifdef DUMP_EEPROM + int i; +#endif + + bpck_read_eeprom(pi,scratch); + +#ifdef DUMP_EEPROM + if (verbose) { + for(i=0;i<128;i++) + if ((scratch[i] < ' ') || (scratch[i] > '~')) + scratch[i] = '.'; + printk("%s: bpck EEPROM: %64.64s\n",pi->device,scratch); + printk("%s: %64.64s\n",pi->device,&scratch[64]); + } +#endif + + printk("%s: bpck %s, backpack %8.8s unit %d", + pi->device,BPCK_VERSION,&scratch[110],pi->unit); + printk(" at 0x%x, mode %d (%s), delay %d\n",pi->port, + pi->mode,mode_string[pi->mode],pi->delay); +} + +static struct pi_protocol bpck = { + .owner = THIS_MODULE, + .name = "bpck", + .max_mode = 5, + .epp_first = 2, + .default_delay = 4, + .max_units = 255, + .write_regr = bpck_write_regr, + .read_regr = bpck_read_regr, + .write_block = bpck_write_block, + .read_block = bpck_read_block, + .connect = bpck_connect, + .disconnect = bpck_disconnect, + .test_port = bpck_test_port, + .probe_unit = bpck_probe_unit, + .test_proto = bpck_test_proto, + .log_adapter = bpck_log_adapter, +}; + +static int __init bpck_init(void) +{ + return paride_register(&bpck); +} + +static void __exit bpck_exit(void) +{ + paride_unregister(&bpck); +} + +MODULE_LICENSE("GPL"); +module_init(bpck_init) +module_exit(bpck_exit) diff --git a/drivers/ata/pata_parport/bpck6.c b/drivers/ata/pata_parport/bpck6.c new file mode 100644 index 000000000000..d897e2a28efe --- /dev/null +++ b/drivers/ata/pata_parport/bpck6.c @@ -0,0 +1,267 @@ +/* + backpack.c (c) 2001 Micro Solutions Inc. + Released under the terms of the GNU General Public license + + backpack.c is a low-level protocol driver for the Micro Solutions + "BACKPACK" parallel port IDE adapter + (Works on Series 6 drives) + + Written by: Ken Hahn (linux-dev@micro-solutions.com) + Clive Turvey (linux-dev@micro-solutions.com) + +*/ + +/* + This is Ken's linux wrapper for the PPC library + Version 1.0.0 is the backpack driver for which source is not available + Version 2.0.0 is the first to have source released + Version 2.0.1 is the "Cox-ified" source code + Version 2.0.2 - fixed version string usage, and made ppc functions static +*/ + + +#define BACKPACK_VERSION "2.0.2" + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <asm/io.h> +#include <linux/parport.h> + +#include "ppc6lnx.c" +#include <linux/pata_parport.h> + +/* PARAMETERS */ +static bool verbose; /* set this to 1 to see debugging messages and whatnot */ + + +#define PPCSTRUCT(pi) ((Interface *)(pi->private)) + +/****************************************************************/ +/* + ATAPI CDROM DRIVE REGISTERS +*/ +#define ATAPI_DATA 0 /* data port */ +#define ATAPI_ERROR 1 /* error register (read) */ +#define ATAPI_FEATURES 1 /* feature register (write) */ +#define ATAPI_INT_REASON 2 /* interrupt reason register */ +#define ATAPI_COUNT_LOW 4 /* byte count register (low) */ +#define ATAPI_COUNT_HIGH 5 /* byte count register (high) */ +#define ATAPI_DRIVE_SEL 6 /* drive select register */ +#define ATAPI_STATUS 7 /* status port (read) */ +#define ATAPI_COMMAND 7 /* command port (write) */ +#define ATAPI_ALT_STATUS 0x0e /* alternate status reg (read) */ +#define ATAPI_DEVICE_CONTROL 0x0e /* device control (write) */ +/****************************************************************/ + +static int bpck6_read_regr(PIA *pi, int cont, int reg) +{ + unsigned int out; + + /* check for bad settings */ + if (reg<0 || reg>7 || cont<0 || cont>2) + { + return(-1); + } + out=ppc6_rd_port(PPCSTRUCT(pi),cont?reg|8:reg); + return(out); +} + +static void bpck6_write_regr(PIA *pi, int cont, int reg, int val) +{ + /* check for bad settings */ + if (reg>=0 && reg<=7 && cont>=0 && cont<=1) + { + ppc6_wr_port(PPCSTRUCT(pi),cont?reg|8:reg,(u8)val); + } +} + +static void bpck6_write_block( PIA *pi, char * buf, int len ) +{ + ppc6_wr_port16_blk(PPCSTRUCT(pi),ATAPI_DATA,buf,(u32)len>>1); +} + +static void bpck6_read_block( PIA *pi, char * buf, int len ) +{ + ppc6_rd_port16_blk(PPCSTRUCT(pi),ATAPI_DATA,buf,(u32)len>>1); +} + +static void bpck6_connect ( PIA *pi ) +{ + if(verbose) + { + printk(KERN_DEBUG "connect\n"); + } + + if(pi->mode >=2) + { + PPCSTRUCT(pi)->mode=4+pi->mode-2; + } + else if(pi->mode==1) + { + PPCSTRUCT(pi)->mode=3; + } + else + { + PPCSTRUCT(pi)->mode=1; + } + + ppc6_open(PPCSTRUCT(pi)); + ppc6_wr_extout(PPCSTRUCT(pi),0x3); +} + +static void bpck6_disconnect ( PIA *pi ) +{ + if(verbose) + { + printk("disconnect\n"); + } + ppc6_wr_extout(PPCSTRUCT(pi),0x0); + ppc6_close(PPCSTRUCT(pi)); +} + +static int bpck6_test_port ( PIA *pi ) /* check for 8-bit port */ +{ + if(verbose) + { + printk(KERN_DEBUG "PARPORT indicates modes=%x for lp=0x%lx\n", + ((struct pardevice*)(pi->pardev))->port->modes, + ((struct pardevice *)(pi->pardev))->port->base); + } + + /*copy over duplicate stuff.. initialize state info*/ + PPCSTRUCT(pi)->ppc_id=pi->unit; + PPCSTRUCT(pi)->lpt_addr=pi->port; + + /* look at the parport device to see if what modes we can use */ + if(((struct pardevice *)(pi->pardev))->port->modes & + (PARPORT_MODE_EPP) + ) + { + return 5; /* Can do EPP*/ + } + else if(((struct pardevice *)(pi->pardev))->port->modes & + (PARPORT_MODE_TRISTATE) + ) + { + return 2; + } + else /*Just flat SPP*/ + { + return 1; + } +} + +static int bpck6_probe_unit ( PIA *pi ) +{ + int out; + + if(verbose) + { + printk(KERN_DEBUG "PROBE UNIT %x on port:%x\n",pi->unit,pi->port); + } + + /*SET PPC UNIT NUMBER*/ + PPCSTRUCT(pi)->ppc_id=pi->unit; + + /*LOWER DOWN TO UNIDIRECTIONAL*/ + PPCSTRUCT(pi)->mode=1; + + out=ppc6_open(PPCSTRUCT(pi)); + + if(verbose) + { + printk(KERN_DEBUG "ppc_open returned %2x\n",out); + } + + if(out) + { + ppc6_close(PPCSTRUCT(pi)); + if(verbose) + { + printk(KERN_DEBUG "leaving probe\n"); + } + return(1); + } + else + { + if(verbose) + { + printk(KERN_DEBUG "Failed open\n"); + } + return(0); + } +} + +static void bpck6_log_adapter( PIA *pi, char * scratch, int verbose ) +{ + char *mode_string[5]= + {"4-bit","8-bit","EPP-8","EPP-16","EPP-32"}; + + printk("%s: BACKPACK Protocol Driver V"BACKPACK_VERSION"\n",pi->device); + printk("%s: Copyright 2001 by Micro Solutions, Inc., DeKalb IL.\n",pi->device); + printk("%s: BACKPACK %s, Micro Solutions BACKPACK Drive at 0x%x\n", + pi->device,BACKPACK_VERSION,pi->port); + printk("%s: Unit: %d Mode:%d (%s) Delay %d\n",pi->device, + pi->unit,pi->mode,mode_string[pi->mode],pi->delay); +} + +static int bpck6_init_proto(PIA *pi) +{ + Interface *p = kzalloc(sizeof(Interface), GFP_KERNEL); + + if (p) { + pi->private = (unsigned long)p; + return 0; + } + + printk(KERN_ERR "%s: ERROR COULDN'T ALLOCATE MEMORY\n", pi->device); + return -1; +} + +static void bpck6_release_proto(PIA *pi) +{ + kfree((void *)(pi->private)); +} + +static struct pi_protocol bpck6 = { + .owner = THIS_MODULE, + .name = "bpck6", + .max_mode = 5, + .epp_first = 2, /* 2-5 use epp (need 8 ports) */ + .max_units = 255, + .write_regr = bpck6_write_regr, + .read_regr = bpck6_read_regr, + .write_block = bpck6_write_block, + .read_block = bpck6_read_block, + .connect = bpck6_connect, + .disconnect = bpck6_disconnect, + .test_port = bpck6_test_port, + .probe_unit = bpck6_probe_unit, + .log_adapter = bpck6_log_adapter, + .init_proto = bpck6_init_proto, + .release_proto = bpck6_release_proto, +}; + +static int __init bpck6_init(void) +{ + printk(KERN_INFO "bpck6: BACKPACK Protocol Driver V"BACKPACK_VERSION"\n"); + printk(KERN_INFO "bpck6: Copyright 2001 by Micro Solutions, Inc., DeKalb IL. USA\n"); + if(verbose) + printk(KERN_DEBUG "bpck6: verbose debug enabled.\n"); + return paride_register(&bpck6); +} + +static void __exit bpck6_exit(void) +{ + paride_unregister(&bpck6); +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Micro Solutions Inc."); +MODULE_DESCRIPTION("BACKPACK Protocol module, compatible with PARIDE"); +module_param(verbose, bool, 0644); +module_init(bpck6_init) +module_exit(bpck6_exit) diff --git a/drivers/ata/pata_parport/comm.c b/drivers/ata/pata_parport/comm.c new file mode 100644 index 000000000000..1775e7ed9336 --- /dev/null +++ b/drivers/ata/pata_parport/comm.c @@ -0,0 +1,218 @@ +/* + comm.c (c) 1997-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU General Public License. + + comm.c is a low-level protocol driver for some older models + of the DataStor "Commuter" parallel to IDE adapter. Some of + the parallel port devices marketed by Arista currently + use this adapter. +*/ + +/* Changes: + + 1.01 GRG 1998.05.05 init_proto, release_proto + +*/ + +#define COMM_VERSION "1.01" + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <asm/io.h> + +#include <linux/pata_parport.h> + +/* mode codes: 0 nybble reads, 8-bit writes + 1 8-bit reads and writes + 2 8-bit EPP mode +*/ + +#define j44(a,b) (((a>>3)&0x0f)|((b<<1)&0xf0)) + +#define P1 w2(5);w2(0xd);w2(0xd);w2(5);w2(4); +#define P2 w2(5);w2(7);w2(7);w2(5);w2(4); + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int cont_map[2] = { 0x08, 0x10 }; + +static int comm_read_regr( PIA *pi, int cont, int regr ) + +{ int l, h, r; + + r = regr + cont_map[cont]; + + switch (pi->mode) { + + case 0: w0(r); P1; w0(0); + w2(6); l = r1(); w0(0x80); h = r1(); w2(4); + return j44(l,h); + + case 1: w0(r+0x20); P1; + w0(0); w2(0x26); h = r0(); w2(4); + return h; + + case 2: + case 3: + case 4: w3(r+0x20); (void)r1(); + w2(0x24); h = r4(); w2(4); + return h; + + } + return -1; +} + +static void comm_write_regr( PIA *pi, int cont, int regr, int val ) + +{ int r; + + r = regr + cont_map[cont]; + + switch (pi->mode) { + + case 0: + case 1: w0(r); P1; w0(val); P2; + break; + + case 2: + case 3: + case 4: w3(r); (void)r1(); w4(val); + break; + } +} + +static void comm_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + w2(4); w0(0xff); w2(6); + w2(4); w0(0xaa); w2(6); + w2(4); w0(0x00); w2(6); + w2(4); w0(0x87); w2(6); + w2(4); w0(0xe0); w2(0xc); w2(0xc); w2(4); +} + +static void comm_disconnect ( PIA *pi ) + +{ w2(0); w2(0); w2(0); w2(4); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void comm_read_block( PIA *pi, char * buf, int count ) + +{ int i, l, h; + + switch (pi->mode) { + + case 0: w0(0x48); P1; + for(i=0;i<count;i++) { + w0(0); w2(6); l = r1(); + w0(0x80); h = r1(); w2(4); + buf[i] = j44(l,h); + } + break; + + case 1: w0(0x68); P1; w0(0); + for(i=0;i<count;i++) { + w2(0x26); buf[i] = r0(); w2(0x24); + } + w2(4); + break; + + case 2: w3(0x68); (void)r1(); w2(0x24); + for (i=0;i<count;i++) buf[i] = r4(); + w2(4); + break; + + case 3: w3(0x68); (void)r1(); w2(0x24); + for (i=0;i<count/2;i++) ((u16 *)buf)[i] = r4w(); + w2(4); + break; + + case 4: w3(0x68); (void)r1(); w2(0x24); + for (i=0;i<count/4;i++) ((u32 *)buf)[i] = r4l(); + w2(4); + break; + + } +} + +/* NB: Watch out for the byte swapped writes ! */ + +static void comm_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + switch (pi->mode) { + + case 0: + case 1: w0(0x68); P1; + for (k=0;k<count;k++) { + w2(5); w0(buf[k^1]); w2(7); + } + w2(5); w2(4); + break; + + case 2: w3(0x48); (void)r1(); + for (k=0;k<count;k++) w4(buf[k^1]); + break; + + case 3: w3(0x48); (void)r1(); + for (k=0;k<count/2;k++) w4w(pi_swab16(buf,k)); + break; + + case 4: w3(0x48); (void)r1(); + for (k=0;k<count/4;k++) w4l(pi_swab32(buf,k)); + break; + + + } +} + +static void comm_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[5] = {"4-bit","8-bit","EPP-8","EPP-16","EPP-32"}; + + printk("%s: comm %s, DataStor Commuter at 0x%x, ", + pi->device,COMM_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static struct pi_protocol comm = { + .owner = THIS_MODULE, + .name = "comm", + .max_mode = 5, + .epp_first = 2, + .default_delay = 1, + .max_units = 1, + .write_regr = comm_write_regr, + .read_regr = comm_read_regr, + .write_block = comm_write_block, + .read_block = comm_read_block, + .connect = comm_connect, + .disconnect = comm_disconnect, + .log_adapter = comm_log_adapter, +}; + +static int __init comm_init(void) +{ + return paride_register(&comm); +} + +static void __exit comm_exit(void) +{ + paride_unregister(&comm); +} + +MODULE_LICENSE("GPL"); +module_init(comm_init) +module_exit(comm_exit) diff --git a/drivers/ata/pata_parport/dstr.c b/drivers/ata/pata_parport/dstr.c new file mode 100644 index 000000000000..edf414d186a6 --- /dev/null +++ b/drivers/ata/pata_parport/dstr.c @@ -0,0 +1,233 @@ +/* + dstr.c (c) 1997-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU General Public License. + + dstr.c is a low-level protocol driver for the + DataStor EP2000 parallel to IDE adapter chip. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.06 init_proto, release_proto + +*/ + +#define DSTR_VERSION "1.01" + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <asm/io.h> + +#include <linux/pata_parport.h> + +/* mode codes: 0 nybble reads, 8-bit writes + 1 8-bit reads and writes + 2 8-bit EPP mode + 3 EPP-16 + 4 EPP-32 +*/ + +#define j44(a,b) (((a>>3)&0x07)|((~a>>4)&0x08)|((b<<1)&0x70)|((~b)&0x80)) + +#define P1 w2(5);w2(0xd);w2(5);w2(4); +#define P2 w2(5);w2(7);w2(5);w2(4); +#define P3 w2(6);w2(4);w2(6);w2(4); + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int cont_map[2] = { 0x20, 0x40 }; + +static int dstr_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, r; + + r = regr + cont_map[cont]; + + w0(0x81); P1; + if (pi->mode) { w0(0x11); } else { w0(1); } + P2; w0(r); P1; + + switch (pi->mode) { + + case 0: w2(6); a = r1(); w2(4); w2(6); b = r1(); w2(4); + return j44(a,b); + + case 1: w0(0); w2(0x26); a = r0(); w2(4); + return a; + + case 2: + case 3: + case 4: w2(0x24); a = r4(); w2(4); + return a; + + } + return -1; +} + +static void dstr_write_regr( PIA *pi, int cont, int regr, int val ) + +{ int r; + + r = regr + cont_map[cont]; + + w0(0x81); P1; + if (pi->mode >= 2) { w0(0x11); } else { w0(1); } + P2; w0(r); P1; + + switch (pi->mode) { + + case 0: + case 1: w0(val); w2(5); w2(7); w2(5); w2(4); + break; + + case 2: + case 3: + case 4: w4(val); + break; + } +} + +#define CCP(x) w0(0xff);w2(0xc);w2(4);\ + w0(0xaa);w0(0x55);w0(0);w0(0xff);w0(0x87);w0(0x78);\ + w0(x);w2(5);w2(4); + +static void dstr_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + w2(4); CCP(0xe0); w0(0xff); +} + +static void dstr_disconnect ( PIA *pi ) + +{ CCP(0x30); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void dstr_read_block( PIA *pi, char * buf, int count ) + +{ int k, a, b; + + w0(0x81); P1; + if (pi->mode) { w0(0x19); } else { w0(9); } + P2; w0(0x82); P1; P3; w0(0x20); P1; + + switch (pi->mode) { + + case 0: for (k=0;k<count;k++) { + w2(6); a = r1(); w2(4); + w2(6); b = r1(); w2(4); + buf[k] = j44(a,b); + } + break; + + case 1: w0(0); + for (k=0;k<count;k++) { + w2(0x26); buf[k] = r0(); w2(0x24); + } + w2(4); + break; + + case 2: w2(0x24); + for (k=0;k<count;k++) buf[k] = r4(); + w2(4); + break; + + case 3: w2(0x24); + for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w(); + w2(4); + break; + + case 4: w2(0x24); + for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l(); + w2(4); + break; + + } +} + +static void dstr_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + w0(0x81); P1; + if (pi->mode) { w0(0x19); } else { w0(9); } + P2; w0(0x82); P1; P3; w0(0x20); P1; + + switch (pi->mode) { + + case 0: + case 1: for (k=0;k<count;k++) { + w2(5); w0(buf[k]); w2(7); + } + w2(5); w2(4); + break; + + case 2: w2(0xc5); + for (k=0;k<count;k++) w4(buf[k]); + w2(0xc4); + break; + + case 3: w2(0xc5); + for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]); + w2(0xc4); + break; + + case 4: w2(0xc5); + for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]); + w2(0xc4); + break; + + } +} + + +static void dstr_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[5] = {"4-bit","8-bit","EPP-8", + "EPP-16","EPP-32"}; + + printk("%s: dstr %s, DataStor EP2000 at 0x%x, ", + pi->device,DSTR_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static struct pi_protocol dstr = { + .owner = THIS_MODULE, + .name = "dstr", + .max_mode = 5, + .epp_first = 2, + .default_delay = 1, + .max_units = 1, + .write_regr = dstr_write_regr, + .read_regr = dstr_read_regr, + .write_block = dstr_write_block, + .read_block = dstr_read_block, + .connect = dstr_connect, + .disconnect = dstr_disconnect, + .log_adapter = dstr_log_adapter, +}; + +static int __init dstr_init(void) +{ + return paride_register(&dstr); +} + +static void __exit dstr_exit(void) +{ + paride_unregister(&dstr); +} + +MODULE_LICENSE("GPL"); +module_init(dstr_init) +module_exit(dstr_exit) diff --git a/drivers/ata/pata_parport/epat.c b/drivers/ata/pata_parport/epat.c new file mode 100644 index 000000000000..6ce2dee7657f --- /dev/null +++ b/drivers/ata/pata_parport/epat.c @@ -0,0 +1,340 @@ +/* + epat.c (c) 1997-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU General Public License. + + This is the low level protocol driver for the EPAT parallel + to IDE adapter from Shuttle Technologies. This adapter is + used in many popular parallel port disk products such as the + SyQuest EZ drives, the Avatar Shark and the Imation SuperDisk. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.06 init_proto, release_proto + 1.02 Joshua b. Jore CPP(renamed), epat_connect, epat_disconnect + +*/ + +#define EPAT_VERSION "1.02" + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <asm/io.h> + +#include <linux/pata_parport.h> + +#define j44(a,b) (((a>>4)&0x0f)+(b&0xf0)) +#define j53(a,b) (((a>>3)&0x1f)+((b<<4)&0xe0)) + +static int epatc8; + +module_param(epatc8, int, 0); +MODULE_PARM_DESC(epatc8, "support for the Shuttle EP1284 chip, " + "used in any recent Imation SuperDisk (LS-120) drive."); + +/* cont = 0 IDE register file + cont = 1 IDE control registers + cont = 2 internal EPAT registers +*/ + +static int cont_map[3] = { 0x18, 0x10, 0 }; + +static void epat_write_regr( PIA *pi, int cont, int regr, int val) + +{ int r; + + r = regr + cont_map[cont]; + + switch (pi->mode) { + + case 0: + case 1: + case 2: w0(0x60+r); w2(1); w0(val); w2(4); + break; + + case 3: + case 4: + case 5: w3(0x40+r); w4(val); + break; + + } +} + +static int epat_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, r; + + r = regr + cont_map[cont]; + + switch (pi->mode) { + + case 0: w0(r); w2(1); w2(3); + a = r1(); w2(4); b = r1(); + return j44(a,b); + + case 1: w0(0x40+r); w2(1); w2(4); + a = r1(); b = r2(); w0(0xff); + return j53(a,b); + + case 2: w0(0x20+r); w2(1); w2(0x25); + a = r0(); w2(4); + return a; + + case 3: + case 4: + case 5: w3(r); w2(0x24); a = r4(); w2(4); + return a; + + } + return -1; /* never gets here */ +} + +static void epat_read_block( PIA *pi, char * buf, int count ) + +{ int k, ph, a, b; + + switch (pi->mode) { + + case 0: w0(7); w2(1); w2(3); w0(0xff); + ph = 0; + for(k=0;k<count;k++) { + if (k == count-1) w0(0xfd); + w2(6+ph); a = r1(); + if (a & 8) b = a; + else { w2(4+ph); b = r1(); } + buf[k] = j44(a,b); + ph = 1 - ph; + } + w0(0); w2(4); + break; + + case 1: w0(0x47); w2(1); w2(5); w0(0xff); + ph = 0; + for(k=0;k<count;k++) { + if (k == count-1) w0(0xfd); + w2(4+ph); + a = r1(); b = r2(); + buf[k] = j53(a,b); + ph = 1 - ph; + } + w0(0); w2(4); + break; + + case 2: w0(0x27); w2(1); w2(0x25); w0(0); + ph = 0; + for(k=0;k<count-1;k++) { + w2(0x24+ph); + buf[k] = r0(); + ph = 1 - ph; + } + w2(0x26); w2(0x27); buf[count-1] = r0(); + w2(0x25); w2(4); + break; + + case 3: w3(0x80); w2(0x24); + for(k=0;k<count-1;k++) buf[k] = r4(); + w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4(); + w2(4); + break; + + case 4: w3(0x80); w2(0x24); + for(k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w(); + buf[count-2] = r4(); + w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4(); + w2(4); + break; + + case 5: w3(0x80); w2(0x24); + for(k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l(); + for(k=count-4;k<count-1;k++) buf[k] = r4(); + w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4(); + w2(4); + break; + + } +} + +static void epat_write_block( PIA *pi, char * buf, int count ) + +{ int ph, k; + + switch (pi->mode) { + + case 0: + case 1: + case 2: w0(0x67); w2(1); w2(5); + ph = 0; + for(k=0;k<count;k++) { + w0(buf[k]); + w2(4+ph); + ph = 1 - ph; + } + w2(7); w2(4); + break; + + case 3: w3(0xc0); + for(k=0;k<count;k++) w4(buf[k]); + w2(4); + break; + + case 4: w3(0xc0); + for(k=0;k<(count/2);k++) w4w(((u16 *)buf)[k]); + w2(4); + break; + + case 5: w3(0xc0); + for(k=0;k<(count/4);k++) w4l(((u32 *)buf)[k]); + w2(4); + break; + + } +} + +/* these macros access the EPAT registers in native addressing */ + +#define WR(r,v) epat_write_regr(pi,2,r,v) +#define RR(r) (epat_read_regr(pi,2,r)) + +/* and these access the IDE task file */ + +#define WRi(r,v) epat_write_regr(pi,0,r,v) +#define RRi(r) (epat_read_regr(pi,0,r)) + +/* FIXME: the CPP stuff should be fixed to handle multiple EPATs on a chain */ + +#define CPP(x) w2(4);w0(0x22);w0(0xaa);w0(0x55);w0(0);w0(0xff);\ + w0(0x87);w0(0x78);w0(x);w2(4);w2(5);w2(4);w0(0xff); + +static void epat_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + + /* Initialize the chip */ + CPP(0); + + if (epatc8) { + CPP(0x40);CPP(0xe0); + w0(0);w2(1);w2(4); + WR(0x8,0x12);WR(0xc,0x14);WR(0x12,0x10); + WR(0xe,0xf);WR(0xf,4); + /* WR(0xe,0xa);WR(0xf,4); */ + WR(0xe,0xd);WR(0xf,0); + /* CPP(0x30); */ + } + + /* Connect to the chip */ + CPP(0xe0); + w0(0);w2(1);w2(4); /* Idle into SPP */ + if (pi->mode >= 3) { + w0(0);w2(1);w2(4);w2(0xc); + /* Request EPP */ + w0(0x40);w2(6);w2(7);w2(4);w2(0xc);w2(4); + } + + if (!epatc8) { + WR(8,0x10); WR(0xc,0x14); WR(0xa,0x38); WR(0x12,0x10); + } +} + +static void epat_disconnect (PIA *pi) +{ CPP(0x30); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static int epat_test_proto( PIA *pi, char * scratch, int verbose ) + +{ int k, j, f, cc; + int e[2] = {0,0}; + + epat_connect(pi); + cc = RR(0xd); + epat_disconnect(pi); + + epat_connect(pi); + for (j=0;j<2;j++) { + WRi(6,0xa0+j*0x10); + for (k=0;k<256;k++) { + WRi(2,k^0xaa); + WRi(3,k^0x55); + if (RRi(2) != (k^0xaa)) e[j]++; + } + } + epat_disconnect(pi); + + f = 0; + epat_connect(pi); + WR(0x13,1); WR(0x13,0); WR(0xa,0x11); + epat_read_block(pi,scratch,512); + + for (k=0;k<256;k++) { + if ((scratch[2*k] & 0xff) != k) f++; + if ((scratch[2*k+1] & 0xff) != (0xff-k)) f++; + } + epat_disconnect(pi); + + if (verbose) { + printk("%s: epat: port 0x%x, mode %d, ccr %x, test=(%d,%d,%d)\n", + pi->device,pi->port,pi->mode,cc,e[0],e[1],f); + } + + return (e[0] && e[1]) || f; +} + +static void epat_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ int ver; + char *mode_string[6] = + {"4-bit","5/3","8-bit","EPP-8","EPP-16","EPP-32"}; + + epat_connect(pi); + WR(0xa,0x38); /* read the version code */ + ver = RR(0xb); + epat_disconnect(pi); + + printk("%s: epat %s, Shuttle EPAT chip %x at 0x%x, ", + pi->device,EPAT_VERSION,ver,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static struct pi_protocol epat = { + .owner = THIS_MODULE, + .name = "epat", + .max_mode = 6, + .epp_first = 3, + .default_delay = 1, + .max_units = 1, + .write_regr = epat_write_regr, + .read_regr = epat_read_regr, + .write_block = epat_write_block, + .read_block = epat_read_block, + .connect = epat_connect, + .disconnect = epat_disconnect, + .test_proto = epat_test_proto, + .log_adapter = epat_log_adapter, +}; + +static int __init epat_init(void) +{ +#ifdef CONFIG_PARIDE_EPATC8 + epatc8 = 1; +#endif + return paride_register(&epat); +} + +static void __exit epat_exit(void) +{ + paride_unregister(&epat); +} + +MODULE_LICENSE("GPL"); +module_init(epat_init) +module_exit(epat_exit) diff --git a/drivers/ata/pata_parport/epia.c b/drivers/ata/pata_parport/epia.c new file mode 100644 index 000000000000..417d5a3c7f72 --- /dev/null +++ b/drivers/ata/pata_parport/epia.c @@ -0,0 +1,316 @@ +/* + epia.c (c) 1997-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU General Public License. + + epia.c is a low-level protocol driver for Shuttle Technologies + EPIA parallel to IDE adapter chip. This device is now obsolete + and has been replaced with the EPAT chip, which is supported + by epat.c, however, some devices based on EPIA are still + available. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.06 init_proto, release_proto + 1.02 GRG 1998.06.17 support older versions of EPIA + +*/ + +#define EPIA_VERSION "1.02" + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <asm/io.h> + +#include <linux/pata_parport.h> + +/* mode codes: 0 nybble reads on port 1, 8-bit writes + 1 5/3 reads on ports 1 & 2, 8-bit writes + 2 8-bit reads and writes + 3 8-bit EPP mode + 4 16-bit EPP + 5 32-bit EPP +*/ + +#define j44(a,b) (((a>>4)&0x0f)+(b&0xf0)) +#define j53(a,b) (((a>>3)&0x1f)+((b<<4)&0xe0)) + +/* cont = 0 IDE register file + cont = 1 IDE control registers +*/ + +static int cont_map[2] = { 0, 0x80 }; + +static int epia_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, r; + + regr += cont_map[cont]; + + switch (pi->mode) { + + case 0: r = regr^0x39; + w0(r); w2(1); w2(3); w0(r); + a = r1(); w2(1); b = r1(); w2(4); + return j44(a,b); + + case 1: r = regr^0x31; + w0(r); w2(1); w0(r&0x37); + w2(3); w2(5); w0(r|0xf0); + a = r1(); b = r2(); w2(4); + return j53(a,b); + + case 2: r = regr^0x29; + w0(r); w2(1); w2(0X21); w2(0x23); + a = r0(); w2(4); + return a; + + case 3: + case 4: + case 5: w3(regr); w2(0x24); a = r4(); w2(4); + return a; + + } + return -1; +} + +static void epia_write_regr( PIA *pi, int cont, int regr, int val) + +{ int r; + + regr += cont_map[cont]; + + switch (pi->mode) { + + case 0: + case 1: + case 2: r = regr^0x19; + w0(r); w2(1); w0(val); w2(3); w2(4); + break; + + case 3: + case 4: + case 5: r = regr^0x40; + w3(r); w4(val); w2(4); + break; + } +} + +#define WR(r,v) epia_write_regr(pi,0,r,v) +#define RR(r) (epia_read_regr(pi,0,r)) + +/* The use of register 0x84 is entirely unclear - it seems to control + some EPP counters ... currently we know about 3 different block + sizes: the standard 512 byte reads and writes, 12 byte writes and + 2048 byte reads (the last two being used in the CDrom drivers. +*/ + +static void epia_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + + w2(4); w0(0xa0); w0(0x50); w0(0xc0); w0(0x30); w0(0xa0); w0(0); + w2(1); w2(4); + if (pi->mode >= 3) { + w0(0xa); w2(1); w2(4); w0(0x82); w2(4); w2(0xc); w2(4); + w2(0x24); w2(0x26); w2(4); + } + WR(0x86,8); +} + +static void epia_disconnect ( PIA *pi ) + +{ /* WR(0x84,0x10); */ + w0(pi->saved_r0); + w2(1); w2(4); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void epia_read_block( PIA *pi, char * buf, int count ) + +{ int k, ph, a, b; + + switch (pi->mode) { + + case 0: w0(0x81); w2(1); w2(3); w0(0xc1); + ph = 1; + for (k=0;k<count;k++) { + w2(2+ph); a = r1(); + w2(4+ph); b = r1(); + buf[k] = j44(a,b); + ph = 1 - ph; + } + w0(0); w2(4); + break; + + case 1: w0(0x91); w2(1); w0(0x10); w2(3); + w0(0x51); w2(5); w0(0xd1); + ph = 1; + for (k=0;k<count;k++) { + w2(4+ph); + a = r1(); b = r2(); + buf[k] = j53(a,b); + ph = 1 - ph; + } + w0(0); w2(4); + break; + + case 2: w0(0x89); w2(1); w2(0x23); w2(0x21); + ph = 1; + for (k=0;k<count;k++) { + w2(0x24+ph); + buf[k] = r0(); + ph = 1 - ph; + } + w2(6); w2(4); + break; + + case 3: if (count > 512) WR(0x84,3); + w3(0); w2(0x24); + for (k=0;k<count;k++) buf[k] = r4(); + w2(4); WR(0x84,0); + break; + + case 4: if (count > 512) WR(0x84,3); + w3(0); w2(0x24); + for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w(); + w2(4); WR(0x84,0); + break; + + case 5: if (count > 512) WR(0x84,3); + w3(0); w2(0x24); + for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l(); + w2(4); WR(0x84,0); + break; + + } +} + +static void epia_write_block( PIA *pi, char * buf, int count ) + +{ int ph, k, last, d; + + switch (pi->mode) { + + case 0: + case 1: + case 2: w0(0xa1); w2(1); w2(3); w2(1); w2(5); + ph = 0; last = 0x8000; + for (k=0;k<count;k++) { + d = buf[k]; + if (d != last) { last = d; w0(d); } + w2(4+ph); + ph = 1 - ph; + } + w2(7); w2(4); + break; + + case 3: if (count < 512) WR(0x84,1); + w3(0x40); + for (k=0;k<count;k++) w4(buf[k]); + if (count < 512) WR(0x84,0); + break; + + case 4: if (count < 512) WR(0x84,1); + w3(0x40); + for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]); + if (count < 512) WR(0x84,0); + break; + + case 5: if (count < 512) WR(0x84,1); + w3(0x40); + for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]); + if (count < 512) WR(0x84,0); + break; + + } + +} + +static int epia_test_proto( PIA *pi, char * scratch, int verbose ) + +{ int j, k, f; + int e[2] = {0,0}; + + epia_connect(pi); + for (j=0;j<2;j++) { + WR(6,0xa0+j*0x10); + for (k=0;k<256;k++) { + WR(2,k^0xaa); + WR(3,k^0x55); + if (RR(2) != (k^0xaa)) e[j]++; + } + WR(2,1); WR(3,1); + } + epia_disconnect(pi); + + f = 0; + epia_connect(pi); + WR(0x84,8); + epia_read_block(pi,scratch,512); + for (k=0;k<256;k++) { + if ((scratch[2*k] & 0xff) != ((k+1) & 0xff)) f++; + if ((scratch[2*k+1] & 0xff) != ((-2-k) & 0xff)) f++; + } + WR(0x84,0); + epia_disconnect(pi); + + if (verbose) { + printk("%s: epia: port 0x%x, mode %d, test=(%d,%d,%d)\n", + pi->device,pi->port,pi->mode,e[0],e[1],f); + } + + return (e[0] && e[1]) || f; + +} + + +static void epia_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[6] = {"4-bit","5/3","8-bit", + "EPP-8","EPP-16","EPP-32"}; + + printk("%s: epia %s, Shuttle EPIA at 0x%x, ", + pi->device,EPIA_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static struct pi_protocol epia = { + .owner = THIS_MODULE, + .name = "epia", + .max_mode = 6, + .epp_first = 3, + .default_delay = 1, + .max_units = 1, + .write_regr = epia_write_regr, + .read_regr = epia_read_regr, + .write_block = epia_write_block, + .read_block = epia_read_block, + .connect = epia_connect, + .disconnect = epia_disconnect, + .test_proto = epia_test_proto, + .log_adapter = epia_log_adapter, +}; + +static int __init epia_init(void) +{ + return paride_register(&epia); +} + +static void __exit epia_exit(void) +{ + paride_unregister(&epia); +} + +MODULE_LICENSE("GPL"); +module_init(epia_init) +module_exit(epia_exit) diff --git a/drivers/ata/pata_parport/fit2.c b/drivers/ata/pata_parport/fit2.c new file mode 100644 index 000000000000..3c7a1069b026 --- /dev/null +++ b/drivers/ata/pata_parport/fit2.c @@ -0,0 +1,151 @@ +/* + fit2.c (c) 1998 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU General Public License. + + fit2.c is a low-level protocol driver for the older version + of the Fidelity International Technology parallel port adapter. + This adapter is used in their TransDisk 2000 and older TransDisk + 3000 portable hard-drives. As far as I can tell, this device + supports 4-bit mode _only_. + + Newer models of the FIT products use an enhanced protocol. + The "fit3" protocol module should support current drives. + +*/ + +#define FIT2_VERSION "1.0" + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <asm/io.h> + +#include <linux/pata_parport.h> + +#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0)) + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set + +NB: The FIT adapter does not appear to use the control registers. +So, we map ALT_STATUS to STATUS and NO-OP writes to the device +control register - this means that IDE reset will not work on these +devices. + +*/ + +static void fit2_write_regr( PIA *pi, int cont, int regr, int val) + +{ if (cont == 1) return; + w2(0xc); w0(regr); w2(4); w0(val); w2(5); w0(0); w2(4); +} + +static int fit2_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, r; + + if (cont) { + if (regr != 6) return 0xff; + r = 7; + } else r = regr + 0x10; + + w2(0xc); w0(r); w2(4); w2(5); + w0(0); a = r1(); + w0(1); b = r1(); + w2(4); + + return j44(a,b); + +} + +static void fit2_read_block( PIA *pi, char * buf, int count ) + +{ int k, a, b, c, d; + + w2(0xc); w0(0x10); + + for (k=0;k<count/4;k++) { + + w2(4); w2(5); + w0(0); a = r1(); w0(1); b = r1(); + w0(3); c = r1(); w0(2); d = r1(); + buf[4*k+0] = j44(a,b); + buf[4*k+1] = j44(d,c); + + w2(4); w2(5); + a = r1(); w0(3); b = r1(); + w0(1); c = r1(); w0(0); d = r1(); + buf[4*k+2] = j44(d,c); + buf[4*k+3] = j44(a,b); + + } + + w2(4); + +} + +static void fit2_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + + w2(0xc); w0(0); + for (k=0;k<count/2;k++) { + w2(4); w0(buf[2*k]); + w2(5); w0(buf[2*k+1]); + } + w2(4); +} + +static void fit2_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + w2(0xcc); +} + +static void fit2_disconnect ( PIA *pi ) + +{ w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void fit2_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ printk("%s: fit2 %s, FIT 2000 adapter at 0x%x, delay %d\n", + pi->device,FIT2_VERSION,pi->port,pi->delay); + +} + +static struct pi_protocol fit2 = { + .owner = THIS_MODULE, + .name = "fit2", + .max_mode = 1, + .epp_first = 2, + .default_delay = 1, + .max_units = 1, + .write_regr = fit2_write_regr, + .read_regr = fit2_read_regr, + .write_block = fit2_write_block, + .read_block = fit2_read_block, + .connect = fit2_connect, + .disconnect = fit2_disconnect, + .log_adapter = fit2_log_adapter, +}; + +static int __init fit2_init(void) +{ + return paride_register(&fit2); +} + +static void __exit fit2_exit(void) +{ + paride_unregister(&fit2); +} + +MODULE_LICENSE("GPL"); +module_init(fit2_init) +module_exit(fit2_exit) diff --git a/drivers/ata/pata_parport/fit3.c b/drivers/ata/pata_parport/fit3.c new file mode 100644 index 000000000000..cd95f4f0edc2 --- /dev/null +++ b/drivers/ata/pata_parport/fit3.c @@ -0,0 +1,211 @@ +/* + fit3.c (c) 1998 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU General Public License. + + fit3.c is a low-level protocol driver for newer models + of the Fidelity International Technology parallel port adapter. + This adapter is used in their TransDisk 3000 portable + hard-drives, as well as CD-ROM, PD-CD and other devices. + + The TD-2000 and certain older devices use a different protocol. + Try the fit2 protocol module with them. + + NB: The FIT adapters do not appear to support the control + registers. So, we map ALT_STATUS to STATUS and NO-OP writes + to the device control register - this means that IDE reset + will not work on these devices. + +*/ + +#define FIT3_VERSION "1.0" + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <asm/io.h> + +#include <linux/pata_parport.h> + +#define j44(a,b) (((a>>3)&0x0f)|((b<<1)&0xf0)) + +#define w7(byte) {out_p(7,byte);} +#define r7() (in_p(7) & 0xff) + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set + +*/ + +static void fit3_write_regr( PIA *pi, int cont, int regr, int val) + +{ if (cont == 1) return; + + switch (pi->mode) { + + case 0: + case 1: w2(0xc); w0(regr); w2(0x8); w2(0xc); + w0(val); w2(0xd); + w0(0); w2(0xc); + break; + + case 2: w2(0xc); w0(regr); w2(0x8); w2(0xc); + w4(val); w4(0); + w2(0xc); + break; + + } +} + +static int fit3_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b; + + if (cont) { + if (regr != 6) return 0xff; + regr = 7; + } + + switch (pi->mode) { + + case 0: w2(0xc); w0(regr + 0x10); w2(0x8); w2(0xc); + w2(0xd); a = r1(); + w2(0xf); b = r1(); + w2(0xc); + return j44(a,b); + + case 1: w2(0xc); w0(regr + 0x90); w2(0x8); w2(0xc); + w2(0xec); w2(0xee); w2(0xef); a = r0(); + w2(0xc); + return a; + + case 2: w2(0xc); w0(regr + 0x90); w2(0x8); w2(0xc); + w2(0xec); + a = r4(); b = r4(); + w2(0xc); + return a; + + } + return -1; + +} + +static void fit3_read_block( PIA *pi, char * buf, int count ) + +{ int k, a, b, c, d; + + switch (pi->mode) { + + case 0: w2(0xc); w0(0x10); w2(0x8); w2(0xc); + for (k=0;k<count/2;k++) { + w2(0xd); a = r1(); + w2(0xf); b = r1(); + w2(0xc); c = r1(); + w2(0xe); d = r1(); + buf[2*k ] = j44(a,b); + buf[2*k+1] = j44(c,d); + } + w2(0xc); + break; + + case 1: w2(0xc); w0(0x90); w2(0x8); w2(0xc); + w2(0xec); w2(0xee); + for (k=0;k<count/2;k++) { + w2(0xef); a = r0(); + w2(0xee); b = r0(); + buf[2*k ] = a; + buf[2*k+1] = b; + } + w2(0xec); + w2(0xc); + break; + + case 2: w2(0xc); w0(0x90); w2(0x8); w2(0xc); + w2(0xec); + for (k=0;k<count;k++) buf[k] = r4(); + w2(0xc); + break; + + } +} + +static void fit3_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + switch (pi->mode) { + + case 0: + case 1: w2(0xc); w0(0); w2(0x8); w2(0xc); + for (k=0;k<count/2;k++) { + w0(buf[2*k ]); w2(0xd); + w0(buf[2*k+1]); w2(0xc); + } + break; + + case 2: w2(0xc); w0(0); w2(0x8); w2(0xc); + for (k=0;k<count;k++) w4(buf[k]); + w2(0xc); + break; + } +} + +static void fit3_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + w2(0xc); w0(0); w2(0xa); + if (pi->mode == 2) { + w2(0xc); w0(0x9); w2(0x8); w2(0xc); + } +} + +static void fit3_disconnect ( PIA *pi ) + +{ w2(0xc); w0(0xa); w2(0x8); w2(0xc); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void fit3_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[3] = {"4-bit","8-bit","EPP"}; + + printk("%s: fit3 %s, FIT 3000 adapter at 0x%x, " + "mode %d (%s), delay %d\n", + pi->device,FIT3_VERSION,pi->port, + pi->mode,mode_string[pi->mode],pi->delay); + +} + +static struct pi_protocol fit3 = { + .owner = THIS_MODULE, + .name = "fit3", + .max_mode = 3, + .epp_first = 2, + .default_delay = 1, + .max_units = 1, + .write_regr = fit3_write_regr, + .read_regr = fit3_read_regr, + .write_block = fit3_write_block, + .read_block = fit3_read_block, + .connect = fit3_connect, + .disconnect = fit3_disconnect, + .log_adapter = fit3_log_adapter, +}; + +static int __init fit3_init(void) +{ + return paride_register(&fit3); +} + +static void __exit fit3_exit(void) +{ + paride_unregister(&fit3); +} + +MODULE_LICENSE("GPL"); +module_init(fit3_init) +module_exit(fit3_exit) diff --git a/drivers/ata/pata_parport/friq.c b/drivers/ata/pata_parport/friq.c new file mode 100644 index 000000000000..da1d0cb016d6 --- /dev/null +++ b/drivers/ata/pata_parport/friq.c @@ -0,0 +1,276 @@ +/* + friq.c (c) 1998 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU General Public License + + friq.c is a low-level protocol driver for the Freecom "IQ" + parallel port IDE adapter. Early versions of this adapter + use the 'frpw' protocol. + + Freecom uses this adapter in a battery powered external + CD-ROM drive. It is also used in LS-120 drives by + Maxell and Panasonic, and other devices. + + The battery powered drive requires software support to + control the power to the drive. This module enables the + drive power when the high level driver (pcd) is loaded + and disables it when the module is unloaded. Note, if + the friq module is built in to the kernel, the power + will never be switched off, so other means should be + used to conserve battery power. + +*/ + +/* Changes: + + 1.01 GRG 1998.12.20 Added support for soft power switch +*/ + +#define FRIQ_VERSION "1.01" + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <asm/io.h> + +#include <linux/pata_parport.h> + +#define CMD(x) w2(4);w0(0xff);w0(0xff);w0(0x73);w0(0x73);\ + w0(0xc9);w0(0xc9);w0(0x26);w0(0x26);w0(x);w0(x); + +#define j44(l,h) (((l>>4)&0x0f)|(h&0xf0)) + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int cont_map[2] = { 0x08, 0x10 }; + +static int friq_read_regr( PIA *pi, int cont, int regr ) + +{ int h,l,r; + + r = regr + cont_map[cont]; + + CMD(r); + w2(6); l = r1(); + w2(4); h = r1(); + w2(4); + + return j44(l,h); + +} + +static void friq_write_regr( PIA *pi, int cont, int regr, int val) + +{ int r; + + r = regr + cont_map[cont]; + + CMD(r); + w0(val); + w2(5);w2(7);w2(5);w2(4); +} + +static void friq_read_block_int( PIA *pi, char * buf, int count, int regr ) + +{ int h, l, k, ph; + + switch(pi->mode) { + + case 0: CMD(regr); + for (k=0;k<count;k++) { + w2(6); l = r1(); + w2(4); h = r1(); + buf[k] = j44(l,h); + } + w2(4); + break; + + case 1: ph = 2; + CMD(regr+0xc0); + w0(0xff); + for (k=0;k<count;k++) { + w2(0xa4 + ph); + buf[k] = r0(); + ph = 2 - ph; + } + w2(0xac); w2(0xa4); w2(4); + break; + + case 2: CMD(regr+0x80); + for (k=0;k<count-2;k++) buf[k] = r4(); + w2(0xac); w2(0xa4); + buf[count-2] = r4(); + buf[count-1] = r4(); + w2(4); + break; + + case 3: CMD(regr+0x80); + for (k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w(); + w2(0xac); w2(0xa4); + buf[count-2] = r4(); + buf[count-1] = r4(); + w2(4); + break; + + case 4: CMD(regr+0x80); + for (k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l(); + buf[count-4] = r4(); + buf[count-3] = r4(); + w2(0xac); w2(0xa4); + buf[count-2] = r4(); + buf[count-1] = r4(); + w2(4); + break; + + } +} + +static void friq_read_block( PIA *pi, char * buf, int count) + +{ friq_read_block_int(pi,buf,count,0x08); +} + +static void friq_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + switch(pi->mode) { + + case 0: + case 1: CMD(8); w2(5); + for (k=0;k<count;k++) { + w0(buf[k]); + w2(7);w2(5); + } + w2(4); + break; + + case 2: CMD(0xc8); w2(5); + for (k=0;k<count;k++) w4(buf[k]); + w2(4); + break; + + case 3: CMD(0xc8); w2(5); + for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]); + w2(4); + break; + + case 4: CMD(0xc8); w2(5); + for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]); + w2(4); + break; + } +} + +static void friq_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + w2(4); +} + +static void friq_disconnect ( PIA *pi ) + +{ CMD(0x20); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static int friq_test_proto( PIA *pi, char * scratch, int verbose ) + +{ int j, k, r; + int e[2] = {0,0}; + + pi->saved_r0 = r0(); + w0(0xff); udelay(20); CMD(0x3d); /* turn the power on */ + udelay(500); + w0(pi->saved_r0); + + friq_connect(pi); + for (j=0;j<2;j++) { + friq_write_regr(pi,0,6,0xa0+j*0x10); + for (k=0;k<256;k++) { + friq_write_regr(pi,0,2,k^0xaa); + friq_write_regr(pi,0,3,k^0x55); + if (friq_read_regr(pi,0,2) != (k^0xaa)) e[j]++; + } + } + friq_disconnect(pi); + + friq_connect(pi); + friq_read_block_int(pi,scratch,512,0x10); + r = 0; + for (k=0;k<128;k++) if (scratch[k] != k) r++; + friq_disconnect(pi); + + if (verbose) { + printk("%s: friq: port 0x%x, mode %d, test=(%d,%d,%d)\n", + pi->device,pi->port,pi->mode,e[0],e[1],r); + } + + return (r || (e[0] && e[1])); +} + + +static void friq_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[6] = {"4-bit","8-bit", + "EPP-8","EPP-16","EPP-32"}; + + printk("%s: friq %s, Freecom IQ ASIC-2 adapter at 0x%x, ", pi->device, + FRIQ_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + + pi->private = 1; + friq_connect(pi); + CMD(0x9e); /* disable sleep timer */ + friq_disconnect(pi); + +} + +static void friq_release_proto( PIA *pi) +{ + if (pi->private) { /* turn off the power */ + friq_connect(pi); + CMD(0x1d); CMD(0x1e); + friq_disconnect(pi); + pi->private = 0; + } +} + +static struct pi_protocol friq = { + .owner = THIS_MODULE, + .name = "friq", + .max_mode = 5, + .epp_first = 2, + .default_delay = 1, + .max_units = 1, + .write_regr = friq_write_regr, + .read_regr = friq_read_regr, + .write_block = friq_write_block, + .read_block = friq_read_block, + .connect = friq_connect, + .disconnect = friq_disconnect, + .test_proto = friq_test_proto, + .log_adapter = friq_log_adapter, + .release_proto = friq_release_proto, +}; + +static int __init friq_init(void) +{ + return paride_register(&friq); +} + +static void __exit friq_exit(void) +{ + paride_unregister(&friq); +} + +MODULE_LICENSE("GPL"); +module_init(friq_init) +module_exit(friq_exit) diff --git a/drivers/ata/pata_parport/frpw.c b/drivers/ata/pata_parport/frpw.c new file mode 100644 index 000000000000..7bc8fa16d5d8 --- /dev/null +++ b/drivers/ata/pata_parport/frpw.c @@ -0,0 +1,313 @@ +/* + frpw.c (c) 1996-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU General Public License + + frpw.c is a low-level protocol driver for the Freecom "Power" + parallel port IDE adapter. + + Some applications of this adapter may require a "printer" reset + prior to loading the driver. This can be done by loading and + unloading the "lp" driver, or it can be done by this driver + if you define FRPW_HARD_RESET. The latter is not recommended + as it may upset devices on other ports. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.06 init_proto, release_proto + fix chip detect + added EPP-16 and EPP-32 + 1.02 GRG 1998.09.23 added hard reset to initialisation process + 1.03 GRG 1998.12.14 made hard reset conditional + +*/ + +#define FRPW_VERSION "1.03" + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <asm/io.h> + +#include <linux/pata_parport.h> + +#define cec4 w2(0xc);w2(0xe);w2(0xe);w2(0xc);w2(4);w2(4);w2(4); +#define j44(l,h) (((l>>4)&0x0f)|(h&0xf0)) + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int cont_map[2] = { 0x08, 0x10 }; + +static int frpw_read_regr( PIA *pi, int cont, int regr ) + +{ int h,l,r; + + r = regr + cont_map[cont]; + + w2(4); + w0(r); cec4; + w2(6); l = r1(); + w2(4); h = r1(); + w2(4); + + return j44(l,h); + +} + +static void frpw_write_regr( PIA *pi, int cont, int regr, int val) + +{ int r; + + r = regr + cont_map[cont]; + + w2(4); w0(r); cec4; + w0(val); + w2(5);w2(7);w2(5);w2(4); +} + +static void frpw_read_block_int( PIA *pi, char * buf, int count, int regr ) + +{ int h, l, k, ph; + + switch(pi->mode) { + + case 0: w2(4); w0(regr); cec4; + for (k=0;k<count;k++) { + w2(6); l = r1(); + w2(4); h = r1(); + buf[k] = j44(l,h); + } + w2(4); + break; + + case 1: ph = 2; + w2(4); w0(regr + 0xc0); cec4; + w0(0xff); + for (k=0;k<count;k++) { + w2(0xa4 + ph); + buf[k] = r0(); + ph = 2 - ph; + } + w2(0xac); w2(0xa4); w2(4); + break; + + case 2: w2(4); w0(regr + 0x80); cec4; + for (k=0;k<count;k++) buf[k] = r4(); + w2(0xac); w2(0xa4); + w2(4); + break; + + case 3: w2(4); w0(regr + 0x80); cec4; + for (k=0;k<count-2;k++) buf[k] = r4(); + w2(0xac); w2(0xa4); + buf[count-2] = r4(); + buf[count-1] = r4(); + w2(4); + break; + + case 4: w2(4); w0(regr + 0x80); cec4; + for (k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w(); + w2(0xac); w2(0xa4); + buf[count-2] = r4(); + buf[count-1] = r4(); + w2(4); + break; + + case 5: w2(4); w0(regr + 0x80); cec4; + for (k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l(); + buf[count-4] = r4(); + buf[count-3] = r4(); + w2(0xac); w2(0xa4); + buf[count-2] = r4(); + buf[count-1] = r4(); + w2(4); + break; + + } +} + +static void frpw_read_block( PIA *pi, char * buf, int count) + +{ frpw_read_block_int(pi,buf,count,0x08); +} + +static void frpw_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + switch(pi->mode) { + + case 0: + case 1: + case 2: w2(4); w0(8); cec4; w2(5); + for (k=0;k<count;k++) { + w0(buf[k]); + w2(7);w2(5); + } + w2(4); + break; + + case 3: w2(4); w0(0xc8); cec4; w2(5); + for (k=0;k<count;k++) w4(buf[k]); + w2(4); + break; + + case 4: w2(4); w0(0xc8); cec4; w2(5); + for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]); + w2(4); + break; + + case 5: w2(4); w0(0xc8); cec4; w2(5); + for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]); + w2(4); + break; + } +} + +static void frpw_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + w2(4); +} + +static void frpw_disconnect ( PIA *pi ) + +{ w2(4); w0(0x20); cec4; + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +/* Stub logic to see if PNP string is available - used to distinguish + between the Xilinx and ASIC implementations of the Freecom adapter. +*/ + +static int frpw_test_pnp ( PIA *pi ) + +/* returns chip_type: 0 = Xilinx, 1 = ASIC */ + +{ int olddelay, a, b; + +#ifdef FRPW_HARD_RESET + w0(0); w2(8); udelay(50); w2(0xc); /* parallel bus reset */ + mdelay(1500); +#endif + + olddelay = pi->delay; + pi->delay = 10; + + pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + + w2(4); w0(4); w2(6); w2(7); + a = r1() & 0xff; w2(4); b = r1() & 0xff; + w2(0xc); w2(0xe); w2(4); + + pi->delay = olddelay; + w0(pi->saved_r0); + w2(pi->saved_r2); + + return ((~a&0x40) && (b&0x40)); +} + +/* We use the pi->private to remember the result of the PNP test. + To make this work, private = port*2 + chip. Yes, I know it's + a hack :-( +*/ + +static int frpw_test_proto( PIA *pi, char * scratch, int verbose ) + +{ int j, k, r; + int e[2] = {0,0}; + + if ((pi->private>>1) != pi->port) + pi->private = frpw_test_pnp(pi) + 2*pi->port; + + if (((pi->private%2) == 0) && (pi->mode > 2)) { + if (verbose) + printk("%s: frpw: Xilinx does not support mode %d\n", + pi->device, pi->mode); + return 1; + } + + if (((pi->private%2) == 1) && (pi->mode == 2)) { + if (verbose) + printk("%s: frpw: ASIC does not support mode 2\n", + pi->device); + return 1; + } + + frpw_connect(pi); + for (j=0;j<2;j++) { + frpw_write_regr(pi,0,6,0xa0+j*0x10); + for (k=0;k<256;k++) { + frpw_write_regr(pi,0,2,k^0xaa); + frpw_write_regr(pi,0,3,k^0x55); + if (frpw_read_regr(pi,0,2) != (k^0xaa)) e[j]++; + } + } + frpw_disconnect(pi); + + frpw_connect(pi); + frpw_read_block_int(pi,scratch,512,0x10); + r = 0; + for (k=0;k<128;k++) if (scratch[k] != k) r++; + frpw_disconnect(pi); + + if (verbose) { + printk("%s: frpw: port 0x%x, chip %ld, mode %d, test=(%d,%d,%d)\n", + pi->device,pi->port,(pi->private%2),pi->mode,e[0],e[1],r); + } + + return (r || (e[0] && e[1])); +} + + +static void frpw_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[6] = {"4-bit","8-bit","EPP", + "EPP-8","EPP-16","EPP-32"}; + + printk("%s: frpw %s, Freecom (%s) adapter at 0x%x, ", pi->device, + FRPW_VERSION,((pi->private%2) == 0)?"Xilinx":"ASIC",pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static struct pi_protocol frpw = { + .owner = THIS_MODULE, + .name = "frpw", + .max_mode = 6, + .epp_first = 2, + .default_delay = 2, + .max_units = 1, + .write_regr = frpw_write_regr, + .read_regr = frpw_read_regr, + .write_block = frpw_write_block, + .read_block = frpw_read_block, + .connect = frpw_connect, + .disconnect = frpw_disconnect, + .test_proto = frpw_test_proto, + .log_adapter = frpw_log_adapter, +}; + +static int __init frpw_init(void) +{ + return paride_register(&frpw); +} + +static void __exit frpw_exit(void) +{ + paride_unregister(&frpw); +} + +MODULE_LICENSE("GPL"); +module_init(frpw_init) +module_exit(frpw_exit) diff --git a/drivers/ata/pata_parport/kbic.c b/drivers/ata/pata_parport/kbic.c new file mode 100644 index 000000000000..f0960eb68635 --- /dev/null +++ b/drivers/ata/pata_parport/kbic.c @@ -0,0 +1,305 @@ +/* + kbic.c (c) 1997-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU General Public License. + + This is a low-level driver for the KBIC-951A and KBIC-971A + parallel to IDE adapter chips from KingByte Information Systems. + + The chips are almost identical, however, the wakeup code + required for the 971A interferes with the correct operation of + the 951A, so this driver registers itself twice, once for + each chip. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.06 init_proto, release_proto + +*/ + +#define KBIC_VERSION "1.01" + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <asm/io.h> + +#include <linux/pata_parport.h> + +#define r12w() (delay_p,inw(pi->port+1)&0xffff) + +#define j44(a,b) ((((a>>4)&0x0f)|(b&0xf0))^0x88) +#define j53(w) (((w>>3)&0x1f)|((w>>4)&0xe0)) + + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int cont_map[2] = { 0x80, 0x40 }; + +static int kbic_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, s; + + s = cont_map[cont]; + + switch (pi->mode) { + + case 0: w0(regr|0x18|s); w2(4); w2(6); w2(4); w2(1); w0(8); + a = r1(); w0(0x28); b = r1(); w2(4); + return j44(a,b); + + case 1: w0(regr|0x38|s); w2(4); w2(6); w2(4); w2(5); w0(8); + a = r12w(); w2(4); + return j53(a); + + case 2: w0(regr|0x08|s); w2(4); w2(6); w2(4); w2(0xa5); w2(0xa1); + a = r0(); w2(4); + return a; + + case 3: + case 4: + case 5: w0(0x20|s); w2(4); w2(6); w2(4); w3(regr); + a = r4(); b = r4(); w2(4); w2(0); w2(4); + return a; + + } + return -1; +} + +static void kbic_write_regr( PIA *pi, int cont, int regr, int val) + +{ int s; + + s = cont_map[cont]; + + switch (pi->mode) { + + case 0: + case 1: + case 2: w0(regr|0x10|s); w2(4); w2(6); w2(4); + w0(val); w2(5); w2(4); + break; + + case 3: + case 4: + case 5: w0(0x20|s); w2(4); w2(6); w2(4); w3(regr); + w4(val); w4(val); + w2(4); w2(0); w2(4); + break; + + } +} + +static void k951_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + w2(4); +} + +static void k951_disconnect ( PIA *pi ) + +{ w0(pi->saved_r0); + w2(pi->saved_r2); +} + +#define CCP(x) w2(0xc4);w0(0xaa);w0(0x55);w0(0);w0(0xff);w0(0x87);\ + w0(0x78);w0(x);w2(0xc5);w2(0xc4);w0(0xff); + +static void k971_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + CCP(0x20); + w2(4); +} + +static void k971_disconnect ( PIA *pi ) + +{ CCP(0x30); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +/* counts must be congruent to 0 MOD 4, but all known applications + have this property. +*/ + +static void kbic_read_block( PIA *pi, char * buf, int count ) + +{ int k, a, b; + + switch (pi->mode) { + + case 0: w0(0x98); w2(4); w2(6); w2(4); + for (k=0;k<count/2;k++) { + w2(1); w0(8); a = r1(); + w0(0x28); b = r1(); + buf[2*k] = j44(a,b); + w2(5); b = r1(); + w0(8); a = r1(); + buf[2*k+1] = j44(a,b); + w2(4); + } + break; + + case 1: w0(0xb8); w2(4); w2(6); w2(4); + for (k=0;k<count/4;k++) { + w0(0xb8); + w2(4); w2(5); + w0(8); buf[4*k] = j53(r12w()); + w0(0xb8); buf[4*k+1] = j53(r12w()); + w2(4); w2(5); + buf[4*k+3] = j53(r12w()); + w0(8); buf[4*k+2] = j53(r12w()); + } + w2(4); + break; + + case 2: w0(0x88); w2(4); w2(6); w2(4); + for (k=0;k<count/2;k++) { + w2(0xa0); w2(0xa1); buf[2*k] = r0(); + w2(0xa5); buf[2*k+1] = r0(); + } + w2(4); + break; + + case 3: w0(0xa0); w2(4); w2(6); w2(4); w3(0); + for (k=0;k<count;k++) buf[k] = r4(); + w2(4); w2(0); w2(4); + break; + + case 4: w0(0xa0); w2(4); w2(6); w2(4); w3(0); + for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w(); + w2(4); w2(0); w2(4); + break; + + case 5: w0(0xa0); w2(4); w2(6); w2(4); w3(0); + for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l(); + w2(4); w2(0); w2(4); + break; + + + } +} + +static void kbic_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + switch (pi->mode) { + + case 0: + case 1: + case 2: w0(0x90); w2(4); w2(6); w2(4); + for(k=0;k<count/2;k++) { + w0(buf[2*k+1]); w2(0); w2(4); + w0(buf[2*k]); w2(5); w2(4); + } + break; + + case 3: w0(0xa0); w2(4); w2(6); w2(4); w3(0); + for(k=0;k<count/2;k++) { + w4(buf[2*k+1]); + w4(buf[2*k]); + } + w2(4); w2(0); w2(4); + break; + + case 4: w0(0xa0); w2(4); w2(6); w2(4); w3(0); + for(k=0;k<count/2;k++) w4w(pi_swab16(buf,k)); + w2(4); w2(0); w2(4); + break; + + case 5: w0(0xa0); w2(4); w2(6); w2(4); w3(0); + for(k=0;k<count/4;k++) w4l(pi_swab32(buf,k)); + w2(4); w2(0); w2(4); + break; + + } + +} + +static void kbic_log_adapter( PIA *pi, char * scratch, + int verbose, char * chip ) + +{ char *mode_string[6] = {"4-bit","5/3","8-bit", + "EPP-8","EPP_16","EPP-32"}; + + printk("%s: kbic %s, KingByte %s at 0x%x, ", + pi->device,KBIC_VERSION,chip,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void k951_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ kbic_log_adapter(pi,scratch,verbose,"KBIC-951A"); +} + +static void k971_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ kbic_log_adapter(pi,scratch,verbose,"KBIC-971A"); +} + +static struct pi_protocol k951 = { + .owner = THIS_MODULE, + .name = "k951", + .max_mode = 6, + .epp_first = 3, + .default_delay = 1, + .max_units = 1, + .write_regr = kbic_write_regr, + .read_regr = kbic_read_regr, + .write_block = kbic_write_block, + .read_block = kbic_read_block, + .connect = k951_connect, + .disconnect = k951_disconnect, + .log_adapter = k951_log_adapter, +}; + +static struct pi_protocol k971 = { + .owner = THIS_MODULE, + .name = "k971", + .max_mode = 6, + .epp_first = 3, + .default_delay = 1, + .max_units = 1, + .write_regr = kbic_write_regr, + .read_regr = kbic_read_regr, + .write_block = kbic_write_block, + .read_block = kbic_read_block, + .connect = k971_connect, + .disconnect = k971_disconnect, + .log_adapter = k971_log_adapter, +}; + +static int __init kbic_init(void) +{ + int rv; + + rv = paride_register(&k951); + if (rv < 0) + return rv; + rv = paride_register(&k971); + if (rv < 0) + paride_unregister(&k951); + return rv; +} + +static void __exit kbic_exit(void) +{ + paride_unregister(&k951); + paride_unregister(&k971); +} + +MODULE_LICENSE("GPL"); +module_init(kbic_init) +module_exit(kbic_exit) diff --git a/drivers/ata/pata_parport/ktti.c b/drivers/ata/pata_parport/ktti.c new file mode 100644 index 000000000000..fc4f707fed1f --- /dev/null +++ b/drivers/ata/pata_parport/ktti.c @@ -0,0 +1,128 @@ +/* + ktti.c (c) 1998 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU General Public License. + + ktti.c is a low-level protocol driver for the KT Technology + parallel port adapter. This adapter is used in the "PHd" + portable hard-drives. As far as I can tell, this device + supports 4-bit mode _only_. + +*/ + +#define KTTI_VERSION "1.0" + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <asm/io.h> + +#include <linux/pata_parport.h> + +#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0)) + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int cont_map[2] = { 0x10, 0x08 }; + +static void ktti_write_regr( PIA *pi, int cont, int regr, int val) + +{ int r; + + r = regr + cont_map[cont]; + + w0(r); w2(0xb); w2(0xa); w2(3); w2(6); + w0(val); w2(3); w0(0); w2(6); w2(0xb); +} + +static int ktti_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, r; + + r = regr + cont_map[cont]; + + w0(r); w2(0xb); w2(0xa); w2(9); w2(0xc); w2(9); + a = r1(); w2(0xc); b = r1(); w2(9); w2(0xc); w2(9); + return j44(a,b); + +} + +static void ktti_read_block( PIA *pi, char * buf, int count ) + +{ int k, a, b; + + for (k=0;k<count/2;k++) { + w0(0x10); w2(0xb); w2(0xa); w2(9); w2(0xc); w2(9); + a = r1(); w2(0xc); b = r1(); w2(9); + buf[2*k] = j44(a,b); + a = r1(); w2(0xc); b = r1(); w2(9); + buf[2*k+1] = j44(a,b); + } +} + +static void ktti_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + for (k=0;k<count/2;k++) { + w0(0x10); w2(0xb); w2(0xa); w2(3); w2(6); + w0(buf[2*k]); w2(3); + w0(buf[2*k+1]); w2(6); + w2(0xb); + } +} + +static void ktti_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + w2(0xb); w2(0xa); w0(0); w2(3); w2(6); +} + +static void ktti_disconnect ( PIA *pi ) + +{ w2(0xb); w2(0xa); w0(0xa0); w2(3); w2(4); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void ktti_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ printk("%s: ktti %s, KT adapter at 0x%x, delay %d\n", + pi->device,KTTI_VERSION,pi->port,pi->delay); + +} + +static struct pi_protocol ktti = { + .owner = THIS_MODULE, + .name = "ktti", + .max_mode = 1, + .epp_first = 2, + .default_delay = 1, + .max_units = 1, + .write_regr = ktti_write_regr, + .read_regr = ktti_read_regr, + .write_block = ktti_write_block, + .read_block = ktti_read_block, + .connect = ktti_connect, + .disconnect = ktti_disconnect, + .log_adapter = ktti_log_adapter, +}; + +static int __init ktti_init(void) +{ + return paride_register(&ktti); +} + +static void __exit ktti_exit(void) +{ + paride_unregister(&ktti); +} + +MODULE_LICENSE("GPL"); +module_init(ktti_init) +module_exit(ktti_exit) diff --git a/drivers/ata/pata_parport/on20.c b/drivers/ata/pata_parport/on20.c new file mode 100644 index 000000000000..995fc41e3122 --- /dev/null +++ b/drivers/ata/pata_parport/on20.c @@ -0,0 +1,153 @@ +/* + on20.c (c) 1996-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU General Public License. + + on20.c is a low-level protocol driver for the + Onspec 90c20 parallel to IDE adapter. +*/ + +/* Changes: + + 1.01 GRG 1998.05.06 init_proto, release_proto + +*/ + +#define ON20_VERSION "1.01" + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <asm/io.h> + +#include <linux/pata_parport.h> + +#define op(f) w2(4);w0(f);w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4); +#define vl(v) w2(4);w0(v);w2(5);w2(7);w2(5);w2(4); + +#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0)) + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int on20_read_regr( PIA *pi, int cont, int regr ) + +{ int h,l, r ; + + r = (regr<<2) + 1 + cont; + + op(1); vl(r); op(0); + + switch (pi->mode) { + + case 0: w2(4); w2(6); l = r1(); + w2(4); w2(6); h = r1(); + w2(4); w2(6); w2(4); w2(6); w2(4); + return j44(l,h); + + case 1: w2(4); w2(0x26); r = r0(); + w2(4); w2(0x26); w2(4); + return r; + + } + return -1; +} + +static void on20_write_regr( PIA *pi, int cont, int regr, int val ) + +{ int r; + + r = (regr<<2) + 1 + cont; + + op(1); vl(r); + op(0); vl(val); + op(0); vl(val); +} + +static void on20_connect ( PIA *pi) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + + w2(4);w0(0);w2(0xc);w2(4);w2(6);w2(4);w2(6);w2(4); + if (pi->mode) { op(2); vl(8); op(2); vl(9); } + else { op(2); vl(0); op(2); vl(8); } +} + +static void on20_disconnect ( PIA *pi ) + +{ w2(4);w0(7);w2(4);w2(0xc);w2(4); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void on20_read_block( PIA *pi, char * buf, int count ) + +{ int k, l, h; + + op(1); vl(1); op(0); + + for (k=0;k<count;k++) + if (pi->mode) { + w2(4); w2(0x26); buf[k] = r0(); + } else { + w2(6); l = r1(); w2(4); + w2(6); h = r1(); w2(4); + buf[k] = j44(l,h); + } + w2(4); +} + +static void on20_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + op(1); vl(1); op(0); + + for (k=0;k<count;k++) { w2(5); w0(buf[k]); w2(7); } + w2(4); +} + +static void on20_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[2] = {"4-bit","8-bit"}; + + printk("%s: on20 %s, OnSpec 90c20 at 0x%x, ", + pi->device,ON20_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static struct pi_protocol on20 = { + .owner = THIS_MODULE, + .name = "on20", + .max_mode = 2, + .epp_first = 2, + .default_delay = 1, + .max_units = 1, + .write_regr = on20_write_regr, + .read_regr = on20_read_regr, + .write_block = on20_write_block, + .read_block = on20_read_block, + .connect = on20_connect, + .disconnect = on20_disconnect, + .log_adapter = on20_log_adapter, +}; + +static int __init on20_init(void) +{ + return paride_register(&on20); +} + +static void __exit on20_exit(void) +{ + paride_unregister(&on20); +} + +MODULE_LICENSE("GPL"); +module_init(on20_init) +module_exit(on20_exit) diff --git a/drivers/ata/pata_parport/on26.c b/drivers/ata/pata_parport/on26.c new file mode 100644 index 000000000000..35f1c481a782 --- /dev/null +++ b/drivers/ata/pata_parport/on26.c @@ -0,0 +1,319 @@ +/* + on26.c (c) 1997-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU General Public License. + + on26.c is a low-level protocol driver for the + OnSpec 90c26 parallel to IDE adapter chip. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.06 init_proto, release_proto + 1.02 GRG 1998.09.23 updates for the -E rev chip + 1.03 GRG 1998.12.14 fix for slave drives + 1.04 GRG 1998.12.20 yet another bug fix + +*/ + +#define ON26_VERSION "1.04" + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <asm/io.h> + +#include <linux/pata_parport.h> + +/* mode codes: 0 nybble reads, 8-bit writes + 1 8-bit reads and writes + 2 8-bit EPP mode + 3 EPP-16 + 4 EPP-32 +*/ + +#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0)) + +#define P1 w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4); +#define P2 w2(5);w2(7);w2(5);w2(4); + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int on26_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, r; + + r = (regr<<2) + 1 + cont; + + switch (pi->mode) { + + case 0: w0(1); P1; w0(r); P2; w0(0); P1; + w2(6); a = r1(); w2(4); + w2(6); b = r1(); w2(4); + w2(6); w2(4); w2(6); w2(4); + return j44(a,b); + + case 1: w0(1); P1; w0(r); P2; w0(0); P1; + w2(0x26); a = r0(); w2(4); w2(0x26); w2(4); + return a; + + case 2: + case 3: + case 4: w3(1); w3(1); w2(5); w4(r); w2(4); + w3(0); w3(0); w2(0x24); a = r4(); w2(4); + w2(0x24); (void)r4(); w2(4); + return a; + + } + return -1; +} + +static void on26_write_regr( PIA *pi, int cont, int regr, int val ) + +{ int r; + + r = (regr<<2) + 1 + cont; + + switch (pi->mode) { + + case 0: + case 1: w0(1); P1; w0(r); P2; w0(0); P1; + w0(val); P2; w0(val); P2; + break; + + case 2: + case 3: + case 4: w3(1); w3(1); w2(5); w4(r); w2(4); + w3(0); w3(0); + w2(5); w4(val); w2(4); + w2(5); w4(val); w2(4); + break; + } +} + +#define CCP(x) w0(0xfe);w0(0xaa);w0(0x55);w0(0);w0(0xff);\ + w0(0x87);w0(0x78);w0(x);w2(4);w2(5);w2(4);w0(0xff); + +static void on26_connect ( PIA *pi ) + +{ int x; + + pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + + CCP(0x20); + x = 8; if (pi->mode) x = 9; + + w0(2); P1; w0(8); P2; + w0(2); P1; w0(x); P2; +} + +static void on26_disconnect ( PIA *pi ) + +{ if (pi->mode >= 2) { w3(4); w3(4); w3(4); w3(4); } + else { w0(4); P1; w0(4); P1; } + CCP(0x30); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +#define RESET_WAIT 200 + +static int on26_test_port( PIA *pi) /* hard reset */ + +{ int i, m, d, x=0, y=0; + + pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + + d = pi->delay; + m = pi->mode; + pi->delay = 5; + pi->mode = 0; + + w2(0xc); + + CCP(0x30); CCP(0); + + w0(0xfe);w0(0xaa);w0(0x55);w0(0);w0(0xff); + i = ((r1() & 0xf0) << 4); w0(0x87); + i |= (r1() & 0xf0); w0(0x78); + w0(0x20);w2(4);w2(5); + i |= ((r1() & 0xf0) >> 4); + w2(4);w0(0xff); + + if (i == 0xb5f) { + + w0(2); P1; w0(0); P2; + w0(3); P1; w0(0); P2; + w0(2); P1; w0(8); P2; udelay(100); + w0(2); P1; w0(0xa); P2; udelay(100); + w0(2); P1; w0(8); P2; udelay(1000); + + on26_write_regr(pi,0,6,0xa0); + + for (i=0;i<RESET_WAIT;i++) { + on26_write_regr(pi,0,6,0xa0); + x = on26_read_regr(pi,0,7); + on26_write_regr(pi,0,6,0xb0); + y = on26_read_regr(pi,0,7); + if (!((x&0x80)||(y&0x80))) break; + mdelay(100); + } + + if (i == RESET_WAIT) + printk("on26: Device reset failed (%x,%x)\n",x,y); + + w0(4); P1; w0(4); P1; + } + + CCP(0x30); + + pi->delay = d; + pi->mode = m; + w0(pi->saved_r0); + w2(pi->saved_r2); + + return 5; +} + + +static void on26_read_block( PIA *pi, char * buf, int count ) + +{ int k, a, b; + + switch (pi->mode) { + + case 0: w0(1); P1; w0(1); P2; w0(2); P1; w0(0x18); P2; w0(0); P1; + udelay(10); + for (k=0;k<count;k++) { + w2(6); a = r1(); + w2(4); b = r1(); + buf[k] = j44(a,b); + } + w0(2); P1; w0(8); P2; + break; + + case 1: w0(1); P1; w0(1); P2; w0(2); P1; w0(0x19); P2; w0(0); P1; + udelay(10); + for (k=0;k<count/2;k++) { + w2(0x26); buf[2*k] = r0(); + w2(0x24); buf[2*k+1] = r0(); + } + w0(2); P1; w0(9); P2; + break; + + case 2: w3(1); w3(1); w2(5); w4(1); w2(4); + w3(0); w3(0); w2(0x24); + udelay(10); + for (k=0;k<count;k++) buf[k] = r4(); + w2(4); + break; + + case 3: w3(1); w3(1); w2(5); w4(1); w2(4); + w3(0); w3(0); w2(0x24); + udelay(10); + for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w(); + w2(4); + break; + + case 4: w3(1); w3(1); w2(5); w4(1); w2(4); + w3(0); w3(0); w2(0x24); + udelay(10); + for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l(); + w2(4); + break; + + } +} + +static void on26_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + switch (pi->mode) { + + case 0: + case 1: w0(1); P1; w0(1); P2; + w0(2); P1; w0(0x18+pi->mode); P2; w0(0); P1; + udelay(10); + for (k=0;k<count/2;k++) { + w2(5); w0(buf[2*k]); + w2(7); w0(buf[2*k+1]); + } + w2(5); w2(4); + w0(2); P1; w0(8+pi->mode); P2; + break; + + case 2: w3(1); w3(1); w2(5); w4(1); w2(4); + w3(0); w3(0); w2(0xc5); + udelay(10); + for (k=0;k<count;k++) w4(buf[k]); + w2(0xc4); + break; + + case 3: w3(1); w3(1); w2(5); w4(1); w2(4); + w3(0); w3(0); w2(0xc5); + udelay(10); + for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]); + w2(0xc4); + break; + + case 4: w3(1); w3(1); w2(5); w4(1); w2(4); + w3(0); w3(0); w2(0xc5); + udelay(10); + for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]); + w2(0xc4); + break; + + } + +} + +static void on26_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[5] = {"4-bit","8-bit","EPP-8", + "EPP-16","EPP-32"}; + + printk("%s: on26 %s, OnSpec 90c26 at 0x%x, ", + pi->device,ON26_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static struct pi_protocol on26 = { + .owner = THIS_MODULE, + .name = "on26", + .max_mode = 5, + .epp_first = 2, + .default_delay = 1, + .max_units = 1, + .write_regr = on26_write_regr, + .read_regr = on26_read_regr, + .write_block = on26_write_block, + .read_block = on26_read_block, + .connect = on26_connect, + .disconnect = on26_disconnect, + .test_port = on26_test_port, + .log_adapter = on26_log_adapter, +}; + +static int __init on26_init(void) +{ + return paride_register(&on26); +} + +static void __exit on26_exit(void) +{ + paride_unregister(&on26); +} + +MODULE_LICENSE("GPL"); +module_init(on26_init) +module_exit(on26_exit) diff --git a/drivers/ata/pata_parport/pata_parport.c b/drivers/ata/pata_parport/pata_parport.c new file mode 100644 index 000000000000..294a266a0dda --- /dev/null +++ b/drivers/ata/pata_parport/pata_parport.c @@ -0,0 +1,761 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2023 Ondrej Zary + * based on paride.c by Grant R. Guenther <grant@torque.net> + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/parport.h> +#include <linux/pata_parport.h> + +#define DRV_NAME "pata_parport" + +static DEFINE_IDR(parport_list); +static DEFINE_IDR(protocols); +static DEFINE_IDA(pata_parport_bus_dev_ids); +static DEFINE_MUTEX(pi_mutex); + +static bool probe = true; +module_param(probe, bool, 0644); +MODULE_PARM_DESC(probe, "Enable automatic device probing (0=off, 1=on [default])"); + +/* + * libata drivers cannot sleep so this driver claims parport before activating + * the ata host and keeps it claimed (and protocol connected) until the ata + * host is removed. Unfortunately, this means that you cannot use any chained + * devices (neither other pata_parport devices nor a printer). + */ +static void pi_connect(struct pi_adapter *pi) +{ + parport_claim_or_block(pi->pardev); + pi->proto->connect(pi); +} + +static void pi_disconnect(struct pi_adapter *pi) +{ + pi->proto->disconnect(pi); + parport_release(pi->pardev); +} + +static void pata_parport_dev_select(struct ata_port *ap, unsigned int device) +{ + struct pi_adapter *pi = ap->host->private_data; + u8 tmp; + + if (device == 0) + tmp = ATA_DEVICE_OBS; + else + tmp = ATA_DEVICE_OBS | ATA_DEV1; + + pi->proto->write_regr(pi, 0, ATA_REG_DEVICE, tmp); + ata_sff_pause(ap); +} + +static bool pata_parport_devchk(struct ata_port *ap, unsigned int device) +{ + struct pi_adapter *pi = ap->host->private_data; + u8 nsect, lbal; + + pata_parport_dev_select(ap, device); + + pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 0x55); + pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0xaa); + + pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 0xaa); + pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0x55); + + pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 055); + pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0xaa); + + nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT); + lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL); + + return (nsect == 0x55) && (lbal == 0xaa); +} + +static int pata_parport_bus_softreset(struct ata_port *ap, unsigned int devmask, + unsigned long deadline) +{ + struct pi_adapter *pi = ap->host->private_data; + + /* software reset. causes dev0 to be selected */ + pi->proto->write_regr(pi, 1, 6, ap->ctl); + udelay(20); + pi->proto->write_regr(pi, 1, 6, ap->ctl | ATA_SRST); + udelay(20); + pi->proto->write_regr(pi, 1, 6, ap->ctl); + ap->last_ctl = ap->ctl; + + /* wait the port to become ready */ + return ata_sff_wait_after_reset(&ap->link, devmask, deadline); +} + +static int pata_parport_softreset(struct ata_link *link, unsigned int *classes, + unsigned long deadline) +{ + struct ata_port *ap = link->ap; + unsigned int devmask = 0; + int rc; + u8 err; + + /* determine if device 0/1 are present */ + if (pata_parport_devchk(ap, 0)) + devmask |= (1 << 0); + if (pata_parport_devchk(ap, 1)) + devmask |= (1 << 1); + + /* select device 0 again */ + pata_parport_dev_select(ap, 0); + + /* issue bus reset */ + rc = pata_parport_bus_softreset(ap, devmask, deadline); + if (rc && rc != -ENODEV) { + ata_link_err(link, "SRST failed (errno=%d)\n", rc); + return rc; + } + + /* determine by signature whether we have ATA or ATAPI devices */ + classes[0] = ata_sff_dev_classify(&link->device[0], + devmask & (1 << 0), &err); + if (err != 0x81) + classes[1] = ata_sff_dev_classify(&link->device[1], + devmask & (1 << 1), &err); + + return 0; +} + +static u8 pata_parport_check_status(struct ata_port *ap) +{ + struct pi_adapter *pi = ap->host->private_data; + + return pi->proto->read_regr(pi, 0, ATA_REG_STATUS); +} + +static u8 pata_parport_check_altstatus(struct ata_port *ap) +{ + struct pi_adapter *pi = ap->host->private_data; + + return pi->proto->read_regr(pi, 1, 6); +} + +static void pata_parport_tf_load(struct ata_port *ap, + const struct ata_taskfile *tf) +{ + struct pi_adapter *pi = ap->host->private_data; + + if (tf->ctl != ap->last_ctl) { + pi->proto->write_regr(pi, 1, 6, tf->ctl); + ap->last_ctl = tf->ctl; + ata_wait_idle(ap); + } + + if (tf->flags & ATA_TFLAG_ISADDR) { + if (tf->flags & ATA_TFLAG_LBA48) { + pi->proto->write_regr(pi, 0, ATA_REG_FEATURE, + tf->hob_feature); + pi->proto->write_regr(pi, 0, ATA_REG_NSECT, + tf->hob_nsect); + pi->proto->write_regr(pi, 0, ATA_REG_LBAL, + tf->hob_lbal); + pi->proto->write_regr(pi, 0, ATA_REG_LBAM, + tf->hob_lbam); + pi->proto->write_regr(pi, 0, ATA_REG_LBAH, + tf->hob_lbah); + } + pi->proto->write_regr(pi, 0, ATA_REG_FEATURE, tf->feature); + pi->proto->write_regr(pi, 0, ATA_REG_NSECT, tf->nsect); + pi->proto->write_regr(pi, 0, ATA_REG_LBAL, tf->lbal); + pi->proto->write_regr(pi, 0, ATA_REG_LBAM, tf->lbam); + pi->proto->write_regr(pi, 0, ATA_REG_LBAH, tf->lbah); + } + + if (tf->flags & ATA_TFLAG_DEVICE) + pi->proto->write_regr(pi, 0, ATA_REG_DEVICE, tf->device); + + ata_wait_idle(ap); +} + +static void pata_parport_tf_read(struct ata_port *ap, struct ata_taskfile *tf) +{ + struct pi_adapter *pi = ap->host->private_data; + + tf->status = pi->proto->read_regr(pi, 0, ATA_REG_STATUS); + tf->error = pi->proto->read_regr(pi, 0, ATA_REG_ERR); + tf->nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT); + tf->lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL); + tf->lbam = pi->proto->read_regr(pi, 0, ATA_REG_LBAM); + tf->lbah = pi->proto->read_regr(pi, 0, ATA_REG_LBAH); + tf->device = pi->proto->read_regr(pi, 0, ATA_REG_DEVICE); + + if (tf->flags & ATA_TFLAG_LBA48) { + pi->proto->write_regr(pi, 1, 6, tf->ctl | ATA_HOB); + tf->hob_feature = pi->proto->read_regr(pi, 0, ATA_REG_ERR); + tf->hob_nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT); + tf->hob_lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL); + tf->hob_lbam = pi->proto->read_regr(pi, 0, ATA_REG_LBAM); + tf->hob_lbah = pi->proto->read_regr(pi, 0, ATA_REG_LBAH); + pi->proto->write_regr(pi, 1, 6, tf->ctl); + ap->last_ctl = tf->ctl; + } +} + +static void pata_parport_exec_command(struct ata_port *ap, + const struct ata_taskfile *tf) +{ + struct pi_adapter *pi = ap->host->private_data; + + pi->proto->write_regr(pi, 0, ATA_REG_CMD, tf->command); + ata_sff_pause(ap); +} + +static unsigned int pata_parport_data_xfer(struct ata_queued_cmd *qc, + unsigned char *buf, unsigned int buflen, int rw) +{ + struct ata_port *ap = qc->dev->link->ap; + struct pi_adapter *pi = ap->host->private_data; + + if (rw == READ) + pi->proto->read_block(pi, buf, buflen); + else + pi->proto->write_block(pi, buf, buflen); + + return buflen; +} + +static void pata_parport_drain_fifo(struct ata_queued_cmd *qc) +{ + int count; + struct ata_port *ap; + struct pi_adapter *pi; + char junk[2]; + + /* We only need to flush incoming data when a command was running */ + if (qc == NULL || qc->dma_dir == DMA_TO_DEVICE) + return; + + ap = qc->ap; + pi = ap->host->private_data; + /* Drain up to 64K of data before we give up this recovery method */ + for (count = 0; (pata_parport_check_status(ap) & ATA_DRQ) + && count < 65536; count += 2) { + pi->proto->read_block(pi, junk, 2); + } + + if (count) + ata_port_dbg(ap, "drained %d bytes to clear DRQ\n", count); +} + +static struct ata_port_operations pata_parport_port_ops = { + .inherits = &ata_sff_port_ops, + + .softreset = pata_parport_softreset, + .hardreset = NULL, + + .sff_dev_select = pata_parport_dev_select, + .sff_check_status = pata_parport_check_status, + .sff_check_altstatus = pata_parport_check_altstatus, + .sff_tf_load = pata_parport_tf_load, + .sff_tf_read = pata_parport_tf_read, + .sff_exec_command = pata_parport_exec_command, + .sff_data_xfer = pata_parport_data_xfer, + .sff_drain_fifo = pata_parport_drain_fifo, +}; + +static const struct ata_port_info pata_parport_port_info = { + .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_PIO_POLLING, + .pio_mask = ATA_PIO0, + /* No DMA */ + .port_ops = &pata_parport_port_ops, +}; + +static void pi_release(struct pi_adapter *pi) +{ + parport_unregister_device(pi->pardev); + if (pi->proto->release_proto) + pi->proto->release_proto(pi); + module_put(pi->proto->owner); +} + +static int default_test_proto(struct pi_adapter *pi, char *scratch) +{ + int j, k; + int e[2] = { 0, 0 }; + + pi->proto->connect(pi); + + for (j = 0; j < 2; j++) { + pi->proto->write_regr(pi, 0, 6, 0xa0 + j * 0x10); + for (k = 0; k < 256; k++) { + pi->proto->write_regr(pi, 0, 2, k ^ 0xaa); + pi->proto->write_regr(pi, 0, 3, k ^ 0x55); + if (pi->proto->read_regr(pi, 0, 2) != (k ^ 0xaa)) + e[j]++; + } + } + pi->proto->disconnect(pi); + + dev_dbg(&pi->dev, "%s: port 0x%x, mode %d, test=(%d,%d)\n", + pi->proto->name, pi->port, pi->mode, e[0], e[1]); + + return e[0] && e[1]; /* not here if both > 0 */ +} + +static int pi_test_proto(struct pi_adapter *pi, char *scratch) +{ + int res; + + parport_claim_or_block(pi->pardev); + if (pi->proto->test_proto) + res = pi->proto->test_proto(pi, scratch, 1); + else + res = default_test_proto(pi, scratch); + parport_release(pi->pardev); + + return res; +} + +static bool pi_probe_mode(struct pi_adapter *pi, int max, char *scratch) +{ + int best, range; + + if (pi->mode != -1) { + if (pi->mode >= max) + return false; + range = 3; + if (pi->mode >= pi->proto->epp_first) + range = 8; + if (range == 8 && pi->port % 8) + return false; + return !pi_test_proto(pi, scratch); + } + best = -1; + for (pi->mode = 0; pi->mode < max; pi->mode++) { + range = 3; + if (pi->mode >= pi->proto->epp_first) + range = 8; + if (range == 8 && pi->port % 8) + break; + if (!pi_test_proto(pi, scratch)) + best = pi->mode; + } + pi->mode = best; + return best > -1; +} + +static bool pi_probe_unit(struct pi_adapter *pi, int unit, char *scratch) +{ + int max, s, e; + + s = unit; + e = s + 1; + + if (s == -1) { + s = 0; + e = pi->proto->max_units; + } + + if (pi->proto->test_port) { + parport_claim_or_block(pi->pardev); + max = pi->proto->test_port(pi); + parport_release(pi->pardev); + } else { + max = pi->proto->max_mode; + } + + if (pi->proto->probe_unit) { + parport_claim_or_block(pi->pardev); + for (pi->unit = s; pi->unit < e; pi->unit++) { + if (pi->proto->probe_unit(pi)) { + parport_release(pi->pardev); + return pi_probe_mode(pi, max, scratch); + } + } + parport_release(pi->pardev); + return false; + } + + return pi_probe_mode(pi, max, scratch); +} + +static void pata_parport_dev_release(struct device *dev) +{ + struct pi_adapter *pi = container_of(dev, struct pi_adapter, dev); + + kfree(pi); +} + +static void pata_parport_bus_release(struct device *dev) +{ + /* nothing to do here but required to avoid warning on device removal */ +} + +static struct bus_type pata_parport_bus_type = { + .name = DRV_NAME, +}; + +static struct device pata_parport_bus = { + .init_name = DRV_NAME, + .release = pata_parport_bus_release, +}; + +static struct scsi_host_template pata_parport_sht = { + PATA_PARPORT_SHT("pata_parport") +}; + +struct pi_device_match { + struct parport *parport; + struct pi_protocol *proto; +}; + +static int pi_find_dev(struct device *dev, void *data) +{ + struct pi_adapter *pi = container_of(dev, struct pi_adapter, dev); + struct pi_device_match *match = data; + + return pi->pardev->port == match->parport && pi->proto == match->proto; +} + +static struct pi_adapter *pi_init_one(struct parport *parport, + struct pi_protocol *pr, int mode, int unit, int delay) +{ + struct pardev_cb par_cb = { }; + char scratch[512]; + const struct ata_port_info *ppi[] = { &pata_parport_port_info }; + struct ata_host *host; + struct pi_adapter *pi; + struct pi_device_match match = { .parport = parport, .proto = pr }; + int id; + + /* + * Abort if there's a device already registered on the same parport + * using the same protocol. + */ + if (bus_for_each_dev(&pata_parport_bus_type, NULL, &match, pi_find_dev)) + return NULL; + + pi = kzalloc(sizeof(struct pi_adapter), GFP_KERNEL); + if (!pi) + return NULL; + + /* set up pi->dev before pi_probe_unit() so it can use dev_printk() */ + pi->dev.parent = &pata_parport_bus; + pi->dev.bus = &pata_parport_bus_type; + pi->dev.driver = &pr->driver; + pi->dev.release = pata_parport_dev_release; + id = ida_alloc(&pata_parport_bus_dev_ids, GFP_KERNEL); + if (id < 0) + return NULL; /* pata_parport_dev_release will do kfree(pi) */ + pi->dev.id = id; + dev_set_name(&pi->dev, "pata_parport.%u", pi->dev.id); + if (device_register(&pi->dev)) { + put_device(&pi->dev); + goto out_ida_free; + } + + pi->proto = pr; + + if (!try_module_get(pi->proto->owner)) + goto out_unreg_dev; + if (pi->proto->init_proto && pi->proto->init_proto(pi) < 0) + goto out_module_put; + + pi->delay = (delay == -1) ? pi->proto->default_delay : delay; + pi->mode = mode; + pi->port = parport->base; + + par_cb.private = pi; + pi->pardev = parport_register_dev_model(parport, DRV_NAME, &par_cb, + pi->dev.id); + if (!pi->pardev) + goto out_module_put; + + if (!pi_probe_unit(pi, unit, scratch)) { + dev_info(&pi->dev, "Adapter not found\n"); + goto out_unreg_parport; + } + + pi->proto->log_adapter(pi, scratch, 1); + + host = ata_host_alloc_pinfo(&pi->pardev->dev, ppi, 1); + if (!host) + goto out_unreg_parport; + dev_set_drvdata(&pi->dev, host); + host->private_data = pi; + + ata_port_desc(host->ports[0], "port %s", pi->pardev->port->name); + ata_port_desc(host->ports[0], "protocol %s", pi->proto->name); + + pi_connect(pi); + if (ata_host_activate(host, 0, NULL, 0, &pata_parport_sht)) + goto out_unreg_parport; + + return pi; + +out_unreg_parport: + pi_disconnect(pi); + parport_unregister_device(pi->pardev); + if (pi->proto->release_proto) + pi->proto->release_proto(pi); +out_module_put: + module_put(pi->proto->owner); +out_unreg_dev: + device_unregister(&pi->dev); +out_ida_free: + ida_free(&pata_parport_bus_dev_ids, pi->dev.id); + return NULL; +} + +int pata_parport_register_driver(struct pi_protocol *pr) +{ + int error; + struct parport *parport; + int port_num; + + pr->driver.bus = &pata_parport_bus_type; + pr->driver.name = pr->name; + error = driver_register(&pr->driver); + if (error) + return error; + + mutex_lock(&pi_mutex); + error = idr_alloc(&protocols, pr, 0, 0, GFP_KERNEL); + if (error < 0) { + driver_unregister(&pr->driver); + mutex_unlock(&pi_mutex); + return error; + } + + pr_info("pata_parport: protocol %s registered\n", pr->name); + + if (probe) { + /* probe all parports using this protocol */ + idr_for_each_entry(&parport_list, parport, port_num) + pi_init_one(parport, pr, -1, 0, -1); + } + mutex_unlock(&pi_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(pata_parport_register_driver); + +void pata_parport_unregister_driver(struct pi_protocol *pr) +{ + struct pi_protocol *pr_iter; + int id = -1; + + mutex_lock(&pi_mutex); + idr_for_each_entry(&protocols, pr_iter, id) { + if (pr_iter == pr) + break; + } + idr_remove(&protocols, id); + mutex_unlock(&pi_mutex); + driver_unregister(&pr->driver); +} +EXPORT_SYMBOL_GPL(pata_parport_unregister_driver); + +static ssize_t new_device_store(struct bus_type *bus, const char *buf, + size_t count) +{ + char port[12] = "auto"; + char protocol[8] = "auto"; + int mode = -1, unit = -1, delay = -1; + struct pi_protocol *pr, *pr_wanted; + struct device_driver *drv; + struct parport *parport; + int port_num, port_wanted, pr_num; + bool ok = false; + + if (sscanf(buf, "%11s %7s %d %d %d", + port, protocol, &mode, &unit, &delay) < 1) + return -EINVAL; + + if (sscanf(port, "parport%u", &port_wanted) < 1) { + if (strcmp(port, "auto")) { + pr_err("invalid port name %s\n", port); + return -EINVAL; + } + port_wanted = -1; + } + + drv = driver_find(protocol, &pata_parport_bus_type); + if (!drv) { + if (strcmp(protocol, "auto")) { + pr_err("protocol %s not found\n", protocol); + return -EINVAL; + } + pr_wanted = NULL; + } else { + pr_wanted = container_of(drv, struct pi_protocol, driver); + } + + mutex_lock(&pi_mutex); + /* walk all parports */ + idr_for_each_entry(&parport_list, parport, port_num) { + if (port_num == port_wanted || port_wanted == -1) { + parport = parport_find_number(port_num); + if (!parport) { + pr_err("no such port %s\n", port); + mutex_unlock(&pi_mutex); + return -ENODEV; + } + /* walk all protocols */ + idr_for_each_entry(&protocols, pr, pr_num) { + if (pr == pr_wanted || !pr_wanted) + if (pi_init_one(parport, pr, mode, unit, + delay)) + ok = true; + } + parport_put_port(parport); + } + } + mutex_unlock(&pi_mutex); + if (!ok) + return -ENODEV; + + return count; +} +static BUS_ATTR_WO(new_device); + +static void pi_remove_one(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + struct pi_adapter *pi = host->private_data; + + ata_host_detach(host); + pi_disconnect(pi); + pi_release(pi); + device_unregister(dev); + ida_free(&pata_parport_bus_dev_ids, dev->id); + /* pata_parport_dev_release will do kfree(pi) */ +} + +static ssize_t delete_device_store(struct bus_type *bus, const char *buf, + size_t count) +{ + struct device *dev; + + mutex_lock(&pi_mutex); + dev = bus_find_device_by_name(bus, NULL, buf); + if (!dev) { + mutex_unlock(&pi_mutex); + return -ENODEV; + } + + pi_remove_one(dev); + mutex_unlock(&pi_mutex); + + return count; +} +static BUS_ATTR_WO(delete_device); + +static void pata_parport_attach(struct parport *port) +{ + struct pi_protocol *pr; + int pr_num, id; + + mutex_lock(&pi_mutex); + id = idr_alloc(&parport_list, port, port->number, port->number, + GFP_KERNEL); + if (id < 0) { + mutex_unlock(&pi_mutex); + return; + } + + if (probe) { + /* probe this port using all protocols */ + idr_for_each_entry(&protocols, pr, pr_num) + pi_init_one(port, pr, -1, 0, -1); + } + mutex_unlock(&pi_mutex); +} + +static int pi_remove_port(struct device *dev, void *p) +{ + struct ata_host *host = dev_get_drvdata(dev); + struct pi_adapter *pi = host->private_data; + + if (pi->pardev->port == p) + pi_remove_one(dev); + + return 0; +} + +static void pata_parport_detach(struct parport *port) +{ + mutex_lock(&pi_mutex); + bus_for_each_dev(&pata_parport_bus_type, NULL, port, pi_remove_port); + idr_remove(&parport_list, port->number); + mutex_unlock(&pi_mutex); +} + +static struct parport_driver pata_parport_driver = { + .name = DRV_NAME, + .match_port = pata_parport_attach, + .detach = pata_parport_detach, + .devmodel = true, +}; + +static __init int pata_parport_init(void) +{ + int error; + + error = bus_register(&pata_parport_bus_type); + if (error) { + pr_err("failed to register pata_parport bus, error: %d\n", error); + return error; + } + + error = device_register(&pata_parport_bus); + if (error) { + pr_err("failed to register pata_parport bus, error: %d\n", error); + goto out_unregister_bus; + } + + error = bus_create_file(&pata_parport_bus_type, &bus_attr_new_device); + if (error) { + pr_err("unable to create sysfs file, error: %d\n", error); + goto out_unregister_dev; + } + + error = bus_create_file(&pata_parport_bus_type, &bus_attr_delete_device); + if (error) { + pr_err("unable to create sysfs file, error: %d\n", error); + goto out_remove_new; + } + + error = parport_register_driver(&pata_parport_driver); + if (error) { + pr_err("unable to register parport driver, error: %d\n", error); + goto out_remove_del; + } + + return 0; + +out_remove_del: + bus_remove_file(&pata_parport_bus_type, &bus_attr_delete_device); +out_remove_new: + bus_remove_file(&pata_parport_bus_type, &bus_attr_new_device); +out_unregister_dev: + device_unregister(&pata_parport_bus); +out_unregister_bus: + bus_unregister(&pata_parport_bus_type); + return error; +} + +static __exit void pata_parport_exit(void) +{ + parport_unregister_driver(&pata_parport_driver); + bus_remove_file(&pata_parport_bus_type, &bus_attr_new_device); + bus_remove_file(&pata_parport_bus_type, &bus_attr_delete_device); + device_unregister(&pata_parport_bus); + bus_unregister(&pata_parport_bus_type); +} + +MODULE_AUTHOR("Ondrej Zary"); +MODULE_DESCRIPTION("driver for parallel port ATA adapters"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("paride"); + +module_init(pata_parport_init); +module_exit(pata_parport_exit); diff --git a/drivers/ata/pata_parport/ppc6lnx.c b/drivers/ata/pata_parport/ppc6lnx.c new file mode 100644 index 000000000000..5e5521d3b1dd --- /dev/null +++ b/drivers/ata/pata_parport/ppc6lnx.c @@ -0,0 +1,726 @@ +/* + ppc6lnx.c (c) 2001 Micro Solutions Inc. + Released under the terms of the GNU General Public license + + ppc6lnx.c is a par of the protocol driver for the Micro Solutions + "BACKPACK" parallel port IDE adapter + (Works on Series 6 drives) + +*/ + +//*************************************************************************** + +// PPC 6 Code in C sanitized for LINUX +// Original x86 ASM by Ron, Converted to C by Clive + +//*************************************************************************** + + +#define port_stb 1 +#define port_afd 2 +#define cmd_stb port_afd +#define port_init 4 +#define data_stb port_init +#define port_sel 8 +#define port_int 16 +#define port_dir 0x20 + +#define ECR_EPP 0x80 +#define ECR_BI 0x20 + +//*************************************************************************** + +// 60772 Commands + +#define ACCESS_REG 0x00 +#define ACCESS_PORT 0x40 + +#define ACCESS_READ 0x00 +#define ACCESS_WRITE 0x20 + +// 60772 Command Prefix + +#define CMD_PREFIX_SET 0xe0 // Special command that modifies the next command's operation +#define CMD_PREFIX_RESET 0xc0 // Resets current cmd modifier reg bits + #define PREFIX_IO16 0x01 // perform 16-bit wide I/O + #define PREFIX_FASTWR 0x04 // enable PPC mode fast-write + #define PREFIX_BLK 0x08 // enable block transfer mode + +// 60772 Registers + +#define REG_STATUS 0x00 // status register + #define STATUS_IRQA 0x01 // Peripheral IRQA line + #define STATUS_EEPROM_DO 0x40 // Serial EEPROM data bit +#define REG_VERSION 0x01 // PPC version register (read) +#define REG_HWCFG 0x02 // Hardware Config register +#define REG_RAMSIZE 0x03 // Size of RAM Buffer + #define RAMSIZE_128K 0x02 +#define REG_EEPROM 0x06 // EEPROM control register + #define EEPROM_SK 0x01 // eeprom SK bit + #define EEPROM_DI 0x02 // eeprom DI bit + #define EEPROM_CS 0x04 // eeprom CS bit + #define EEPROM_EN 0x08 // eeprom output enable +#define REG_BLKSIZE 0x08 // Block transfer len (24 bit) + +//*************************************************************************** + +typedef struct ppc_storage { + u16 lpt_addr; // LPT base address + u8 ppc_id; + u8 mode; // operating mode + // 0 = PPC Uni SW + // 1 = PPC Uni FW + // 2 = PPC Bi SW + // 3 = PPC Bi FW + // 4 = EPP Byte + // 5 = EPP Word + // 6 = EPP Dword + u8 ppc_flags; + u8 org_data; // original LPT data port contents + u8 org_ctrl; // original LPT control port contents + u8 cur_ctrl; // current control port contents +} Interface; + +//*************************************************************************** + +// ppc_flags + +#define fifo_wait 0x10 + +//*************************************************************************** + +// DONT CHANGE THESE LEST YOU BREAK EVERYTHING - BIT FIELD DEPENDENCIES + +#define PPCMODE_UNI_SW 0 +#define PPCMODE_UNI_FW 1 +#define PPCMODE_BI_SW 2 +#define PPCMODE_BI_FW 3 +#define PPCMODE_EPP_BYTE 4 +#define PPCMODE_EPP_WORD 5 +#define PPCMODE_EPP_DWORD 6 + +//*************************************************************************** + +static int ppc6_select(Interface *ppc); +static void ppc6_deselect(Interface *ppc); +static void ppc6_send_cmd(Interface *ppc, u8 cmd); +static void ppc6_wr_data_byte(Interface *ppc, u8 data); +static u8 ppc6_rd_data_byte(Interface *ppc); +static u8 ppc6_rd_port(Interface *ppc, u8 port); +static void ppc6_wr_port(Interface *ppc, u8 port, u8 data); +static void ppc6_rd_data_blk(Interface *ppc, u8 *data, long count); +static void ppc6_wait_for_fifo(Interface *ppc); +static void ppc6_wr_data_blk(Interface *ppc, u8 *data, long count); +static void ppc6_rd_port16_blk(Interface *ppc, u8 port, u8 *data, long length); +static void ppc6_wr_port16_blk(Interface *ppc, u8 port, u8 *data, long length); +static void ppc6_wr_extout(Interface *ppc, u8 regdata); +static int ppc6_open(Interface *ppc); +static void ppc6_close(Interface *ppc); + +//*************************************************************************** + +static int ppc6_select(Interface *ppc) +{ + u8 i, j, k; + + i = inb(ppc->lpt_addr + 1); + + if (i & 1) + outb(i, ppc->lpt_addr + 1); + + ppc->org_data = inb(ppc->lpt_addr); + + ppc->org_ctrl = inb(ppc->lpt_addr + 2) & 0x5F; // readback ctrl + + ppc->cur_ctrl = ppc->org_ctrl; + + ppc->cur_ctrl |= port_sel; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + if (ppc->org_data == 'b') + outb('x', ppc->lpt_addr); + + outb('b', ppc->lpt_addr); + outb('p', ppc->lpt_addr); + outb(ppc->ppc_id, ppc->lpt_addr); + outb(~ppc->ppc_id,ppc->lpt_addr); + + ppc->cur_ctrl &= ~port_sel; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + ppc->cur_ctrl = (ppc->cur_ctrl & port_int) | port_init; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + i = ppc->mode & 0x0C; + + if (i == 0) + i = (ppc->mode & 2) | 1; + + outb(i, ppc->lpt_addr); + + ppc->cur_ctrl |= port_sel; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + // DELAY + + ppc->cur_ctrl |= port_afd; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + j = ((i & 0x08) << 4) | ((i & 0x07) << 3); + + k = inb(ppc->lpt_addr + 1) & 0xB8; + + if (j == k) + { + ppc->cur_ctrl &= ~port_afd; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + k = (inb(ppc->lpt_addr + 1) & 0xB8) ^ 0xB8; + + if (j == k) + { + if (i & 4) // EPP + ppc->cur_ctrl &= ~(port_sel | port_init); + else // PPC/ECP + ppc->cur_ctrl &= ~port_sel; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + return(1); + } + } + + outb(ppc->org_ctrl, ppc->lpt_addr + 2); + + outb(ppc->org_data, ppc->lpt_addr); + + return(0); // FAIL +} + +//*************************************************************************** + +static void ppc6_deselect(Interface *ppc) +{ + if (ppc->mode & 4) // EPP + ppc->cur_ctrl |= port_init; + else // PPC/ECP + ppc->cur_ctrl |= port_sel; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + outb(ppc->org_data, ppc->lpt_addr); + + outb((ppc->org_ctrl | port_sel), ppc->lpt_addr + 2); + + outb(ppc->org_ctrl, ppc->lpt_addr + 2); +} + +//*************************************************************************** + +static void ppc6_send_cmd(Interface *ppc, u8 cmd) +{ + switch(ppc->mode) + { + case PPCMODE_UNI_SW : + case PPCMODE_UNI_FW : + case PPCMODE_BI_SW : + case PPCMODE_BI_FW : + { + outb(cmd, ppc->lpt_addr); + + ppc->cur_ctrl ^= cmd_stb; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + break; + } + + case PPCMODE_EPP_BYTE : + case PPCMODE_EPP_WORD : + case PPCMODE_EPP_DWORD : + { + outb(cmd, ppc->lpt_addr + 3); + + break; + } + } +} + +//*************************************************************************** + +static void ppc6_wr_data_byte(Interface *ppc, u8 data) +{ + switch(ppc->mode) + { + case PPCMODE_UNI_SW : + case PPCMODE_UNI_FW : + case PPCMODE_BI_SW : + case PPCMODE_BI_FW : + { + outb(data, ppc->lpt_addr); + + ppc->cur_ctrl ^= data_stb; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + break; + } + + case PPCMODE_EPP_BYTE : + case PPCMODE_EPP_WORD : + case PPCMODE_EPP_DWORD : + { + outb(data, ppc->lpt_addr + 4); + + break; + } + } +} + +//*************************************************************************** + +static u8 ppc6_rd_data_byte(Interface *ppc) +{ + u8 data = 0; + + switch(ppc->mode) + { + case PPCMODE_UNI_SW : + case PPCMODE_UNI_FW : + { + ppc->cur_ctrl = (ppc->cur_ctrl & ~port_stb) ^ data_stb; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + // DELAY + + data = inb(ppc->lpt_addr + 1); + + data = ((data & 0x80) >> 1) | ((data & 0x38) >> 3); + + ppc->cur_ctrl |= port_stb; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + // DELAY + + data |= inb(ppc->lpt_addr + 1) & 0xB8; + + break; + } + + case PPCMODE_BI_SW : + case PPCMODE_BI_FW : + { + ppc->cur_ctrl |= port_dir; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + ppc->cur_ctrl = (ppc->cur_ctrl | port_stb) ^ data_stb; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + data = inb(ppc->lpt_addr); + + ppc->cur_ctrl &= ~port_stb; + + outb(ppc->cur_ctrl,ppc->lpt_addr + 2); + + ppc->cur_ctrl &= ~port_dir; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + break; + } + + case PPCMODE_EPP_BYTE : + case PPCMODE_EPP_WORD : + case PPCMODE_EPP_DWORD : + { + outb((ppc->cur_ctrl | port_dir),ppc->lpt_addr + 2); + + data = inb(ppc->lpt_addr + 4); + + outb(ppc->cur_ctrl,ppc->lpt_addr + 2); + + break; + } + } + + return(data); +} + +//*************************************************************************** + +static u8 ppc6_rd_port(Interface *ppc, u8 port) +{ + ppc6_send_cmd(ppc,(u8)(port | ACCESS_PORT | ACCESS_READ)); + + return(ppc6_rd_data_byte(ppc)); +} + +//*************************************************************************** + +static void ppc6_wr_port(Interface *ppc, u8 port, u8 data) +{ + ppc6_send_cmd(ppc,(u8)(port | ACCESS_PORT | ACCESS_WRITE)); + + ppc6_wr_data_byte(ppc, data); +} + +//*************************************************************************** + +static void ppc6_rd_data_blk(Interface *ppc, u8 *data, long count) +{ + switch(ppc->mode) + { + case PPCMODE_UNI_SW : + case PPCMODE_UNI_FW : + { + while(count) + { + u8 d; + + ppc->cur_ctrl = (ppc->cur_ctrl & ~port_stb) ^ data_stb; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + // DELAY + + d = inb(ppc->lpt_addr + 1); + + d = ((d & 0x80) >> 1) | ((d & 0x38) >> 3); + + ppc->cur_ctrl |= port_stb; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + // DELAY + + d |= inb(ppc->lpt_addr + 1) & 0xB8; + + *data++ = d; + count--; + } + + break; + } + + case PPCMODE_BI_SW : + case PPCMODE_BI_FW : + { + ppc->cur_ctrl |= port_dir; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + ppc->cur_ctrl |= port_stb; + + while(count) + { + ppc->cur_ctrl ^= data_stb; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + *data++ = inb(ppc->lpt_addr); + count--; + } + + ppc->cur_ctrl &= ~port_stb; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + ppc->cur_ctrl &= ~port_dir; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + break; + } + + case PPCMODE_EPP_BYTE : + { + outb((ppc->cur_ctrl | port_dir), ppc->lpt_addr + 2); + + // DELAY + + while(count) + { + *data++ = inb(ppc->lpt_addr + 4); + count--; + } + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + break; + } + + case PPCMODE_EPP_WORD : + { + outb((ppc->cur_ctrl | port_dir), ppc->lpt_addr + 2); + + // DELAY + + while(count > 1) + { + *((u16 *)data) = inw(ppc->lpt_addr + 4); + data += 2; + count -= 2; + } + + while(count) + { + *data++ = inb(ppc->lpt_addr + 4); + count--; + } + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + break; + } + + case PPCMODE_EPP_DWORD : + { + outb((ppc->cur_ctrl | port_dir),ppc->lpt_addr + 2); + + // DELAY + + while(count > 3) + { + *((u32 *)data) = inl(ppc->lpt_addr + 4); + data += 4; + count -= 4; + } + + while(count) + { + *data++ = inb(ppc->lpt_addr + 4); + count--; + } + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + break; + } + } + +} + +//*************************************************************************** + +static void ppc6_wait_for_fifo(Interface *ppc) +{ + int i; + + if (ppc->ppc_flags & fifo_wait) + { + for(i=0; i<20; i++) + inb(ppc->lpt_addr + 1); + } +} + +//*************************************************************************** + +static void ppc6_wr_data_blk(Interface *ppc, u8 *data, long count) +{ + switch(ppc->mode) + { + case PPCMODE_UNI_SW : + case PPCMODE_BI_SW : + { + while(count--) + { + outb(*data++, ppc->lpt_addr); + + ppc->cur_ctrl ^= data_stb; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + } + + break; + } + + case PPCMODE_UNI_FW : + case PPCMODE_BI_FW : + { + u8 this, last; + + ppc6_send_cmd(ppc,(CMD_PREFIX_SET | PREFIX_FASTWR)); + + ppc->cur_ctrl |= port_stb; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + last = *data; + + outb(last, ppc->lpt_addr); + + while(count) + { + this = *data++; + count--; + + if (this == last) + { + ppc->cur_ctrl ^= data_stb; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + } + else + { + outb(this, ppc->lpt_addr); + + last = this; + } + } + + ppc->cur_ctrl &= ~port_stb; + + outb(ppc->cur_ctrl, ppc->lpt_addr + 2); + + ppc6_send_cmd(ppc,(CMD_PREFIX_RESET | PREFIX_FASTWR)); + + break; + } + + case PPCMODE_EPP_BYTE : + { + while(count) + { + outb(*data++,ppc->lpt_addr + 4); + count--; + } + + ppc6_wait_for_fifo(ppc); + + break; + } + + case PPCMODE_EPP_WORD : + { + while(count > 1) + { + outw(*((u16 *)data),ppc->lpt_addr + 4); + data += 2; + count -= 2; + } + + while(count) + { + outb(*data++,ppc->lpt_addr + 4); + count--; + } + + ppc6_wait_for_fifo(ppc); + + break; + } + + case PPCMODE_EPP_DWORD : + { + while(count > 3) + { + outl(*((u32 *)data),ppc->lpt_addr + 4); + data += 4; + count -= 4; + } + + while(count) + { + outb(*data++,ppc->lpt_addr + 4); + count--; + } + + ppc6_wait_for_fifo(ppc); + + break; + } + } +} + +//*************************************************************************** + +static void ppc6_rd_port16_blk(Interface *ppc, u8 port, u8 *data, long length) +{ + length = length << 1; + + ppc6_send_cmd(ppc, (REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE)); + ppc6_wr_data_byte(ppc,(u8)length); + ppc6_wr_data_byte(ppc,(u8)(length >> 8)); + ppc6_wr_data_byte(ppc,0); + + ppc6_send_cmd(ppc, (CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK)); + + ppc6_send_cmd(ppc, (u8)(port | ACCESS_PORT | ACCESS_READ)); + + ppc6_rd_data_blk(ppc, data, length); + + ppc6_send_cmd(ppc, (CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK)); +} + +//*************************************************************************** + +static void ppc6_wr_port16_blk(Interface *ppc, u8 port, u8 *data, long length) +{ + length = length << 1; + + ppc6_send_cmd(ppc, (REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE)); + ppc6_wr_data_byte(ppc,(u8)length); + ppc6_wr_data_byte(ppc,(u8)(length >> 8)); + ppc6_wr_data_byte(ppc,0); + + ppc6_send_cmd(ppc, (CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK)); + + ppc6_send_cmd(ppc, (u8)(port | ACCESS_PORT | ACCESS_WRITE)); + + ppc6_wr_data_blk(ppc, data, length); + + ppc6_send_cmd(ppc, (CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK)); +} + +//*************************************************************************** + +static void ppc6_wr_extout(Interface *ppc, u8 regdata) +{ + ppc6_send_cmd(ppc,(REG_VERSION | ACCESS_REG | ACCESS_WRITE)); + + ppc6_wr_data_byte(ppc, (u8)((regdata & 0x03) << 6)); +} + +//*************************************************************************** + +static int ppc6_open(Interface *ppc) +{ + int ret; + + ret = ppc6_select(ppc); + + if (ret == 0) + return(ret); + + ppc->ppc_flags &= ~fifo_wait; + + ppc6_send_cmd(ppc, (ACCESS_REG | ACCESS_WRITE | REG_RAMSIZE)); + ppc6_wr_data_byte(ppc, RAMSIZE_128K); + + ppc6_send_cmd(ppc, (ACCESS_REG | ACCESS_READ | REG_VERSION)); + + if ((ppc6_rd_data_byte(ppc) & 0x3F) == 0x0C) + ppc->ppc_flags |= fifo_wait; + + return(ret); +} + +//*************************************************************************** + +static void ppc6_close(Interface *ppc) +{ + ppc6_deselect(ppc); +} + +//*************************************************************************** + diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index b9a4f68b371d..b052c5a65c17 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -566,7 +566,7 @@ static unsigned int sata_fsl_qc_issue(struct ata_queued_cmd *qc) return 0; } -static bool sata_fsl_qc_fill_rtf(struct ata_queued_cmd *qc) +static void sata_fsl_qc_fill_rtf(struct ata_queued_cmd *qc) { struct sata_fsl_port_priv *pp = qc->ap->private_data; struct sata_fsl_host_priv *host_priv = qc->ap->host->private_data; @@ -577,7 +577,6 @@ static bool sata_fsl_qc_fill_rtf(struct ata_queued_cmd *qc) cd = pp->cmdentry + tag; ata_tf_from_fis(cd->sfis, &qc->result_tf); - return true; } static int sata_fsl_scr_write(struct ata_link *link, @@ -1042,7 +1041,7 @@ static void sata_fsl_error_handler(struct ata_port *ap) static void sata_fsl_post_internal_cmd(struct ata_queued_cmd *qc) { - if (qc->flags & ATA_QCFLAG_FAILED) + if (qc->flags & ATA_QCFLAG_EH) qc->err_mask |= AC_ERR_OTHER; if (qc->err_mask) { diff --git a/drivers/ata/sata_inic162x.c b/drivers/ata/sata_inic162x.c index 11e518f0111c..2833c722118d 100644 --- a/drivers/ata/sata_inic162x.c +++ b/drivers/ata/sata_inic162x.c @@ -566,7 +566,7 @@ static void inic_tf_read(struct ata_port *ap, struct ata_taskfile *tf) tf->status = readb(port_base + PORT_TF_COMMAND); } -static bool inic_qc_fill_rtf(struct ata_queued_cmd *qc) +static void inic_qc_fill_rtf(struct ata_queued_cmd *qc) { struct ata_taskfile *rtf = &qc->result_tf; struct ata_taskfile tf; @@ -580,12 +580,10 @@ static bool inic_qc_fill_rtf(struct ata_queued_cmd *qc) */ inic_tf_read(qc->ap, &tf); - if (!(tf.status & ATA_ERR)) - return false; - - rtf->status = tf.status; - rtf->error = tf.error; - return true; + if (tf.status & ATA_ERR) { + rtf->status = tf.status; + rtf->error = tf.error; + } } static void inic_freeze(struct ata_port *ap) @@ -672,7 +670,7 @@ static void inic_error_handler(struct ata_port *ap) static void inic_post_internal_cmd(struct ata_queued_cmd *qc) { /* make DMA engine forget about the failed command */ - if (qc->flags & ATA_QCFLAG_FAILED) + if (qc->flags & ATA_QCFLAG_EH) inic_reset_port(inic_port_base(qc->ap)); } diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c index 9cd7d8b71361..4e60e6c4c35a 100644 --- a/drivers/ata/sata_promise.c +++ b/drivers/ata/sata_promise.c @@ -828,7 +828,7 @@ static void pdc_post_internal_cmd(struct ata_queued_cmd *qc) struct ata_port *ap = qc->ap; /* make DMA engine forget about the failed command */ - if (qc->flags & ATA_QCFLAG_FAILED) + if (qc->flags & ATA_QCFLAG_EH) pdc_reset_port(ap); } diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index 2fef6ce93f07..22cc9e9789dd 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -328,7 +328,7 @@ static int sil24_scr_write(struct ata_link *link, unsigned sc_reg, u32 val); static int sil24_qc_defer(struct ata_queued_cmd *qc); static enum ata_completion_errors sil24_qc_prep(struct ata_queued_cmd *qc); static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc); -static bool sil24_qc_fill_rtf(struct ata_queued_cmd *qc); +static void sil24_qc_fill_rtf(struct ata_queued_cmd *qc); static void sil24_pmp_attach(struct ata_port *ap); static void sil24_pmp_detach(struct ata_port *ap); static void sil24_freeze(struct ata_port *ap); @@ -901,10 +901,9 @@ static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc) return 0; } -static bool sil24_qc_fill_rtf(struct ata_queued_cmd *qc) +static void sil24_qc_fill_rtf(struct ata_queued_cmd *qc) { sil24_read_tf(qc->ap, qc->hw_tag, &qc->result_tf); - return true; } static void sil24_pmp_attach(struct ata_port *ap) @@ -1185,7 +1184,7 @@ static void sil24_post_internal_cmd(struct ata_queued_cmd *qc) struct ata_port *ap = qc->ap; /* make DMA engine forget about the failed command */ - if ((qc->flags & ATA_QCFLAG_FAILED) && sil24_init_port(ap)) + if ((qc->flags & ATA_QCFLAG_EH) && sil24_init_port(ap)) ata_eh_freeze_port(ap); } diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c index ab70cbc78f96..a92c60455b1d 100644 --- a/drivers/ata/sata_sx4.c +++ b/drivers/ata/sata_sx4.c @@ -866,7 +866,7 @@ static void pdc_post_internal_cmd(struct ata_queued_cmd *qc) struct ata_port *ap = qc->ap; /* make DMA engine forget about the failed command */ - if (qc->flags & ATA_QCFLAG_FAILED) + if (qc->flags & ATA_QCFLAG_EH) pdc_reset_port(ap); } |