diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-02-23 00:35:51 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-02-23 00:35:51 +0300 |
commit | 6861eaf79155f0a5544ff989754159f806795c31 (patch) | |
tree | 99f6b7c2ceebced94075ca5e54f5dff09ff7bd4b /drivers/ata | |
parent | f0b2769a0185ccf84842a795b5587afc37274c3d (diff) | |
parent | 8844f0aa8dc42f54dc278c8d4ecbf32e92f2d6f1 (diff) | |
download | linux-6861eaf79155f0a5544ff989754159f806795c31.tar.xz |
Merge tag 'ata-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/dlemoal/libata
Pull ATA updates from Damien Le Moal:
- Small cleanup of the pata_octeon driver to drop a useless platform
callback (Uwe)
- Simplify ata_scsi_cmd_error_handler() code using the fact that
ap->ops->error_handler is NULL most of the time (Wenchao)
- Several patches improving libata error handling. This is in
preparation for supporting the command duration limits (CDL) feature.
The changes allow handling corner cases of ATA NCQ errors which do
not happen with regular drives but will be triggered with CDL drives
(Niklas)
- Simplify the qc_fill_rtf operation (me)
- Improve SCSI command translation for REPORT_SUPPORTED_OPERATION_CODES
command (me)
- Cleanup of libata FUA handling.
This falls short of enabling FUA for ATA drives that support it by
default as there were concerns that old drives would break. The
series however fixes several issues with the FUA support to ensure
that FUA is reported as being supported only for drives that can
handle all possible write cases (NCQ and non-NCQ). A check in the
block layer is also added to ensure that we never see read FUA
commands (current behavior) (me)
- Several patches to move the old PARIDE (parallel port IDE) driver to
libata as pata_parport. Given that this driver also needs protocol
modules, the driver code resides in its own pata_parport directoy
under drivers/ata (Ondrej)
* tag 'ata-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/dlemoal/libata:
ata: pata_parport: Fix ida_alloc return value error check
drivers/block: Move PARIDE protocol modules to drivers/ata/pata_parport
drivers/block: Remove PARIDE core and high-level protocols
ata: pata_parport: add driver (PARIDE replacement)
ata: libata: exclude FUA support for known buggy drives
ata: libata: Fix FUA handling in ata_build_rw_tf()
ata: libata: cleanup fua support detection
ata: libata: Rename and cleanup ata_rwcmd_protocol()
ata: libata: Introduce ata_ncq_supported()
block: add a sanity check for non-write flush/fua bios
ata: libata-scsi: improve ata_scsiop_maint_in()
ata: libata-scsi: do not overwrite SCSI ML and status bytes
ata: libata: move NCQ related ATA_DFLAGs
ata: libata: respect successfully completed commands during errors
ata: libata: read the shared status for successful NCQ commands once
ata: libata: simplify qc_fill_rtf port operation interface
ata: scsi: rename flag ATA_QCFLAG_FAILED to ATA_QCFLAG_EH
ata: libata-eh: Cleanup ata_scsi_cmd_error_handler()
ata: octeon: Drop empty platform remove function
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); } |