[PATCH] libata: implement dummy port
authorTejun Heo <htejun@gmail.com>
Thu, 10 Aug 2006 07:59:12 +0000 (16:59 +0900)
committerTejun Heo <htejun@gmail.com>
Thu, 10 Aug 2006 07:59:12 +0000 (16:59 +0900)
Implement dummy port which can be requested by setting appropriate bit
in probe_ent->dummy_port_mask.  The dummy port is used as placeholder
for stolen legacy port.  This allows libata to guarantee that
index_of(ap) == ap->port_no == actual_device_port_no, and thus to
remove error-prone ap->hard_port_no.

As it's used only when one port of a legacy controller is reserved by
some other entity (e.g. IDE), the focus is on keeping the added *code*
complexity at minimum, so dummy port allocates all libata core
resources and acts as a normal port.  It just has all dummy port_ops.

This patch only implements dummy port.  The following patch will make
libata use it for stolen legacy ports.

Signed-off-by: Tejun Heo <htejun@gmail.com>
drivers/scsi/libata-core.c
include/linux/libata.h

index 3634279..f2e7e2f 100644 (file)
@@ -5311,7 +5311,6 @@ static struct ata_port * ata_port_add(const struct ata_probe_ent *ent,
 {
        struct Scsi_Host *shost;
        struct ata_port *ap;
-       int rc;
 
        DPRINTK("ENTER\n");
 
@@ -5333,15 +5332,7 @@ static struct ata_port * ata_port_add(const struct ata_probe_ent *ent,
        ata_port_init(ap, host_set, ent, port_no);
        ata_port_init_shost(ap, shost);
 
-       rc = ap->ops->port_start(ap);
-       if (rc)
-               goto err_out;
-
        return ap;
-
-err_out:
-       scsi_host_put(shost);
-       return NULL;
 }
 
 /**
@@ -5415,11 +5406,27 @@ int ata_device_add(const struct ata_probe_ent *ent)
                if (!ap)
                        goto err_out;
 
+               host_set->ports[i] = ap;
+
+               /* dummy? */
+               if (ent->dummy_port_mask & (1 << i)) {
+                       ata_port_printk(ap, KERN_INFO, "DUMMY\n");
+                       ap->ops = &ata_dummy_port_ops;
+                       continue;
+               }
+
+               /* start port */
+               rc = ap->ops->port_start(ap);
+               if (rc) {
+                       host_set->ports[i] = NULL;
+                       scsi_host_put(ap->host);
+                       goto err_out;
+               }
+
                /* Report the secondary IRQ for second channel legacy */
                if (i == 1 && ent->irq2)
                        irq_line = ent->irq2;
 
-               host_set->ports[i] = ap;
                xfer_mode_mask =(ap->udma_mask << ATA_SHIFT_UDMA) |
                                (ap->mwdma_mask << ATA_SHIFT_MWDMA) |
                                (ap->pio_mask << ATA_SHIFT_PIO);
@@ -5940,6 +5947,39 @@ u32 ata_wait_register(void __iomem *reg, u32 mask, u32 val,
        return tmp;
 }
 
+/*
+ * Dummy port_ops
+ */
+static void ata_dummy_noret(struct ata_port *ap)       { }
+static int ata_dummy_ret0(struct ata_port *ap)         { return 0; }
+static void ata_dummy_qc_noret(struct ata_queued_cmd *qc) { }
+
+static u8 ata_dummy_check_status(struct ata_port *ap)
+{
+       return ATA_DRDY;
+}
+
+static unsigned int ata_dummy_qc_issue(struct ata_queued_cmd *qc)
+{
+       return AC_ERR_SYSTEM;
+}
+
+const struct ata_port_operations ata_dummy_port_ops = {
+       .port_disable           = ata_port_disable,
+       .check_status           = ata_dummy_check_status,
+       .check_altstatus        = ata_dummy_check_status,
+       .dev_select             = ata_noop_dev_select,
+       .qc_prep                = ata_noop_qc_prep,
+       .qc_issue               = ata_dummy_qc_issue,
+       .freeze                 = ata_dummy_noret,
+       .thaw                   = ata_dummy_noret,
+       .error_handler          = ata_dummy_noret,
+       .post_internal_cmd      = ata_dummy_qc_noret,
+       .irq_clear              = ata_dummy_noret,
+       .port_start             = ata_dummy_ret0,
+       .port_stop              = ata_dummy_noret,
+};
+
 /*
  * libata is essentially a library of internal helper functions for
  * low-level ATA host controller drivers.  As such, the API/ABI is
@@ -5950,6 +5990,7 @@ u32 ata_wait_register(void __iomem *reg, u32 mask, u32 val,
 EXPORT_SYMBOL_GPL(sata_deb_timing_normal);
 EXPORT_SYMBOL_GPL(sata_deb_timing_hotplug);
 EXPORT_SYMBOL_GPL(sata_deb_timing_long);
+EXPORT_SYMBOL_GPL(ata_dummy_port_ops);
 EXPORT_SYMBOL_GPL(ata_std_bios_param);
 EXPORT_SYMBOL_GPL(ata_std_ports);
 EXPORT_SYMBOL_GPL(ata_host_set_init);
index 4504776..30bfe8f 100644 (file)
@@ -353,6 +353,7 @@ struct ata_probe_ent {
        struct ata_ioports      port[ATA_MAX_PORTS];
        unsigned int            n_ports;
        unsigned int            hard_port_no;
+       unsigned int            dummy_port_mask;
        unsigned int            pio_mask;
        unsigned int            mwdma_mask;
        unsigned int            udma_mask;
@@ -652,6 +653,8 @@ extern const unsigned long sata_deb_timing_normal[];
 extern const unsigned long sata_deb_timing_hotplug[];
 extern const unsigned long sata_deb_timing_long[];
 
+extern const struct ata_port_operations ata_dummy_port_ops;
+
 static inline const unsigned long *
 sata_ehc_deb_timing(struct ata_eh_context *ehc)
 {
@@ -661,6 +664,11 @@ sata_ehc_deb_timing(struct ata_eh_context *ehc)
                return sata_deb_timing_normal;
 }
 
+static inline int ata_port_is_dummy(struct ata_port *ap)
+{
+       return ap->ops == &ata_dummy_port_ops;
+}
+
 extern void ata_port_probe(struct ata_port *);
 extern void __sata_phy_reset(struct ata_port *ap);
 extern void sata_phy_reset(struct ata_port *ap);