Add EBSA110's PCMCIA slot support.

Cc: Russell King <[email protected]>
Signed-off-by: Bartlomiej Zolnierkiewicz <[email protected]>
---
Cross compile tested only.

 drivers/ata/pata_pcmcia.c |  297 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 294 insertions(+), 3 deletions(-)

Index: b/drivers/ata/pata_pcmcia.c
===================================================================
--- a/drivers/ata/pata_pcmcia.c 2017-03-14 18:32:21.667263033 +0100
+++ b/drivers/ata/pata_pcmcia.c 2017-03-14 18:41:48.995277320 +0100
@@ -164,6 +164,286 @@ static struct ata_port_operations pcmcia
        .sff_drain_fifo = pcmcia_8bit_drain_fifo,
 };
 
+#ifdef CONFIG_ARCH_EBSA110
+static void pmcmia_ebsa110_dev_select(struct ata_port *ap, unsigned int device)
+{
+       u8 tmp;
+
+       if (device == 0)
+               tmp = ATA_DEVICE_OBS;
+       else
+               tmp = ATA_DEVICE_OBS | ATA_DEV1;
+
+       __outb16(tmp, (unsigned long)ap->ioaddr.device_addr);
+       ata_sff_pause(ap);      /* needed; also flushes, for mmio */
+}
+
+static void pcmcia_ebsa110_set_devctl(struct ata_port *ap, u8 ctl)
+{
+       __outb16(ctl, (unsigned long)ap->ioaddr.ctl_addr);
+}
+
+static u8 pcmcia_ebsa110_check_status(struct ata_port *ap)
+{
+       return __inb16((unsigned long)ap->ioaddr.status_addr);
+}
+
+static u8 pcmcia_ebsa110_check_altstatus(struct ata_port *ap)
+{
+       return __inb16((unsigned long)ap->ioaddr.altstatus_addr);
+}
+
+static void pcmcia_ebsa110_tf_load(struct ata_port *ap,
+                                  const struct ata_taskfile *tf)
+{
+       struct ata_ioports *ioaddr = &ap->ioaddr;
+       unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;
+
+       if (tf->ctl != ap->last_ctl) {
+               if (ioaddr->ctl_addr)
+                       __outb16(tf->ctl, (unsigned long)ioaddr->ctl_addr);
+               ap->last_ctl = tf->ctl;
+               ata_wait_idle(ap);
+       }
+
+       if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) {
+               WARN_ON_ONCE(!ioaddr->ctl_addr);
+               __outb16(tf->hob_feature, (unsigned long)ioaddr->feature_addr);
+               __outb16(tf->hob_nsect, (unsigned long)ioaddr->nsect_addr);
+               __outb16(tf->hob_lbal, (unsigned long)ioaddr->lbal_addr);
+               __outb16(tf->hob_lbam, (unsigned long)ioaddr->lbam_addr);
+               __outb16(tf->hob_lbah, (unsigned long)ioaddr->lbah_addr);
+               VPRINTK("hob: feat 0x%X nsect 0x%X, lba 0x%X 0x%X 0x%X\n",
+                       tf->hob_feature,
+                       tf->hob_nsect,
+                       tf->hob_lbal,
+                       tf->hob_lbam,
+                       tf->hob_lbah);
+       }
+
+       if (is_addr) {
+               __outb16(tf->feature, (unsigned long)ioaddr->feature_addr);
+               __outb16(tf->nsect, (unsigned long)ioaddr->nsect_addr);
+               __outb16(tf->lbal, (unsigned long)ioaddr->lbal_addr);
+               __outb16(tf->lbam, (unsigned long)ioaddr->lbam_addr);
+               __outb16(tf->lbah, (unsigned long)ioaddr->lbah_addr);
+               VPRINTK("feat 0x%X nsect 0x%X lba 0x%X 0x%X 0x%X\n",
+                       tf->feature,
+                       tf->nsect,
+                       tf->lbal,
+                       tf->lbam,
+                       tf->lbah);
+       }
+
+       if (tf->flags & ATA_TFLAG_DEVICE) {
+               __outb16(tf->device, (unsigned long)ioaddr->device_addr);
+               VPRINTK("device 0x%X\n", tf->device);
+       }
+
+       ata_wait_idle(ap);
+}
+
+static void pcmcia_ebsa110_tf_read(struct ata_port *ap,
+                                  struct ata_taskfile *tf)
+{
+       struct ata_ioports *ioaddr = &ap->ioaddr;
+
+       tf->command = ata_sff_check_status(ap);
+       tf->feature = __inb16((unsigned long)ioaddr->error_addr);
+       tf->nsect = __inb16((unsigned long)ioaddr->nsect_addr);
+       tf->lbal = __inb16((unsigned long)ioaddr->lbal_addr);
+       tf->lbam = __inb16((unsigned long)ioaddr->lbam_addr);
+       tf->lbah = __inb16((unsigned long)ioaddr->lbah_addr);
+       tf->device = __inb16((unsigned long)ioaddr->device_addr);
+
+       if (tf->flags & ATA_TFLAG_LBA48) {
+               if (likely(ioaddr->ctl_addr)) {
+                       __outb16(tf->ctl | ATA_HOB, (unsigned 
long)ioaddr->ctl_addr);
+                       tf->hob_feature = __inb16((unsigned 
long)ioaddr->error_addr);
+                       tf->hob_nsect = __inb16((unsigned 
long)ioaddr->nsect_addr);
+                       tf->hob_lbal = __inb16((unsigned 
long)ioaddr->lbal_addr);
+                       tf->hob_lbam = __inb16((unsigned 
long)ioaddr->lbam_addr);
+                       tf->hob_lbah = __inb16((unsigned 
long)ioaddr->lbah_addr);
+                       __outb16(tf->ctl, (unsigned long)ioaddr->ctl_addr);
+                       ap->last_ctl = tf->ctl;
+               } else
+                       WARN_ON_ONCE(1);
+       }
+}
+
+static void pcmcia_ebsa110_exec_command(struct ata_port *ap,
+                                        const struct ata_taskfile *tf)
+{
+       DPRINTK("ata%u: cmd 0x%X\n", ap->print_id, tf->command);
+
+       __outb16(tf->command, (unsigned long)ap->ioaddr.command_addr);
+       ata_sff_pause(ap);
+}
+
+static unsigned int pata_pcmcia_ebsa110_devchk(struct ata_port *ap,
+                                             unsigned int device)
+{
+       struct ata_ioports *ioaddr = &ap->ioaddr;
+       u8 nsect, lbal;
+
+       ap->ops->sff_dev_select(ap, device);
+
+       __outb16(0x55, (unsigned long)ioaddr->nsect_addr);
+       __outb16(0xaa, (unsigned long)ioaddr->lbal_addr);
+
+       __outb16(0xaa, (unsigned long)ioaddr->nsect_addr);
+       __outb16(0x55, (unsigned long)ioaddr->lbal_addr);
+
+       __outb16(0x55, (unsigned long)ioaddr->nsect_addr);
+       __outb16(0xaa, (unsigned long)ioaddr->lbal_addr);
+
+       nsect = __inb16((unsigned long)ioaddr->nsect_addr);
+       lbal = __inb16((unsigned long)ioaddr->lbal_addr);
+
+       if ((nsect == 0x55) && (lbal == 0xaa))
+               return 1;       /* we found a device */
+
+       return 0;               /* nothing found */
+}
+
+static int pata_pcmcia_ebsa110_wait_after_reset(struct ata_link *link,
+                                               unsigned int devmask,
+                                               unsigned long deadline)
+{
+       struct ata_port *ap = link->ap;
+       struct ata_ioports *ioaddr = &ap->ioaddr;
+       unsigned int dev0 = devmask & (1 << 0);
+       unsigned int dev1 = devmask & (1 << 1);
+       int rc, ret = 0;
+
+       ata_msleep(ap, ATA_WAIT_AFTER_RESET);
+
+       /* always check readiness of the master device */
+       rc = ata_sff_wait_ready(link, deadline);
+       /* -ENODEV means the odd clown forgot the D7 pulldown resistor
+        * and TF status is 0xff, bail out on it too.
+        */
+       if (rc)
+               return rc;
+
+       /* if device 1 was found in ata_devchk, wait for register
+        * access briefly, then wait for BSY to clear.
+        */
+       if (dev1) {
+               int i;
+
+               ap->ops->sff_dev_select(ap, 1);
+
+               /* Wait for register access.  Some ATAPI devices fail
+                * to set nsect/lbal after reset, so don't waste too
+                * much time on it.  We're gonna wait for !BSY anyway.
+                */
+               for (i = 0; i < 2; i++) {
+                       u8 nsect, lbal;
+
+                       nsect = __inb16((unsigned long)ioaddr->nsect_addr);
+                       lbal = __inb16((unsigned long)ioaddr->lbal_addr);
+                       if ((nsect == 1) && (lbal == 1))
+                               break;
+                       ata_msleep(ap, 50);     /* give drive a breather */
+               }
+
+               rc = ata_sff_wait_ready(link, deadline);
+               if (rc) {
+                       if (rc != -ENODEV)
+                               return rc;
+                       ret = rc;
+               }
+       }
+
+       /* is all this really necessary? */
+       ap->ops->sff_dev_select(ap, 0);
+       if (dev1)
+               ap->ops->sff_dev_select(ap, 1);
+       if (dev0)
+               ap->ops->sff_dev_select(ap, 0);
+
+       return ret;
+}
+
+static int pata_pcmcia_ebsa110_bus_softreset(struct ata_port *ap,
+                                            unsigned int devmask,
+                                            unsigned long deadline)
+{
+       struct ata_ioports *ioaddr = &ap->ioaddr;
+
+       DPRINTK("ata%u: bus reset via SRST\n", ap->print_id);
+
+       /* software reset.  causes dev0 to be selected */
+       __outb16(ap->ctl, (unsigned long)ioaddr->ctl_addr);
+       udelay(20);     /* FIXME: flush */
+       __outb16(ap->ctl | ATA_SRST, (unsigned long)ioaddr->ctl_addr);
+       udelay(20);     /* FIXME: flush */
+       __outb16(ap->ctl, (unsigned long)ioaddr->ctl_addr);
+       ap->last_ctl = ap->ctl;
+
+       /* wait the port to become ready */
+       return pata_pcmcia_ebsa110_wait_after_reset(&ap->link, devmask,
+                                                   deadline);
+}
+
+static int pata_pcmcia_ebsa110_softreset(struct ata_link *link,
+                                        unsigned int *classes,
+                                        unsigned long deadline)
+{
+       struct ata_port *ap = link->ap;
+       unsigned int slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS;
+       unsigned int devmask = 0;
+       int rc;
+       u8 err;
+
+       DPRINTK("ENTER\n");
+
+       /* determine if device 0/1 are present */
+       if (pata_pcmcia_ebsa110_devchk(ap, 0))
+               devmask |= (1 << 0);
+       if (slave_possible && pata_pcmcia_ebsa110_devchk(ap, 1))
+               devmask |= (1 << 1);
+
+       /* select device 0 again */
+       ap->ops->sff_dev_select(ap, 0);
+
+       /* issue bus reset */
+       DPRINTK("about to softreset, devmask=%x\n", devmask);
+       rc = pata_pcmcia_ebsa110_bus_softreset(ap, devmask, deadline);
+       /* if link is occupied, -ENODEV too is an error */
+       if (rc && (rc != -ENODEV || sata_scr_valid(link))) {
+               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 (slave_possible && err != 0x81)
+               classes[1] = ata_sff_dev_classify(&link->device[1],
+                                                 devmask & (1 << 1), &err);
+
+       DPRINTK("EXIT, classes[0]=%u [1]=%u\n", classes[0], classes[1]);
+       return 0;
+}
+
+static struct ata_port_operations pcmcia_ebsa110_port_ops = {
+       .inherits               = &ata_sff_port_ops,
+       .sff_dev_select         = pmcmia_ebsa110_dev_select,
+       .sff_set_devctl         = pcmcia_ebsa110_set_devctl,
+       .sff_check_status       = pcmcia_ebsa110_check_status,
+       .sff_check_altstatus    = pcmcia_ebsa110_check_altstatus,
+       .sff_tf_load            = pcmcia_ebsa110_tf_load,
+       .sff_tf_read            = pcmcia_ebsa110_tf_read,
+       .sff_exec_command       = pcmcia_ebsa110_exec_command,
+       .sff_data_xfer          = ata_sff_data_xfer_noirq,
+       .softreset              = pata_pcmcia_ebsa110_softreset,
+       .cable_detect           = ata_cable_40wire,
+       .set_mode               = pcmcia_set_mode,
+};
+#include <asm/mach-types.h>
+#endif
 
 static int pcmcia_check_one_config(struct pcmcia_device *pdev, void *priv_data)
 {
@@ -242,9 +522,20 @@ static int pcmcia_init_one(struct pcmcia
                goto failed;
 
        /* Success. Disable the IRQ nIEN line, do quirks */
-       iowrite8(0x02, ctl_addr);
-       if (is_kme)
-               iowrite8(0x81, ctl_addr + 0x01);
+#ifdef CONFIG_ARCH_EBSA110
+       if (machine_is_ebsa110()) {
+               ops = &pcmcia_ebsa110_port_ops;
+
+               __outb16(0x02, (unsigned long)ctl_addr);
+               if (is_kme)
+                       __outb16(0x81, (unsigned long)ctl_addr + 0x01);
+       } else
+#endif
+       {
+               iowrite8(0x02, ctl_addr);
+               if (is_kme)
+                       iowrite8(0x81, ctl_addr + 0x01);
+       }
 
        /* FIXME: Could be more ports at base + 0x10 but we only deal with
           one right now */

Reply via email to