Add ide-timing functionality to libata.
authorAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 21 Oct 2005 23:01:32 +0000 (19:01 -0400)
committerJeff Garzik <jgarzik@pobox.com>
Fri, 21 Oct 2005 23:01:32 +0000 (19:01 -0400)
This is needed for full AMD and VIA drivers and possibly more. Functions
to turn actual clocking and cycle timings into register values. Also to
merge shared timings to compute an optimal timing set.

Built from the drivers/ide version by Vojtech Pavlik

Signed-off-by: Alan Cox <alan@redhat.com>
Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
drivers/scsi/libata-core.c
include/linux/libata.h

index 09639e7aaa7145559ade55c2ea3aed942c7cd09c..9269fd9b814f9546aadc8f0bae84553bc430fa2d 100644 (file)
@@ -1538,6 +1538,152 @@ void ata_port_disable(struct ata_port *ap)
        ap->flags |= ATA_FLAG_PORT_DISABLED;
 }
 
+/*
+ * This mode timing computation functionality is ported over from
+ * drivers/ide/ide-timing.h and was originally written by Vojtech Pavlik
+ */
+/*
+ * PIO 0-5, MWDMA 0-2 and UDMA 0-6 timings (in nanoseconds).
+ * These were taken from ATA/ATAPI-6 standard, rev 0a, except
+ * for PIO 5, which is a nonstandard extension and UDMA6, which
+ * is currently supported only by Maxtor drives. 
+ */
+
+static const struct ata_timing ata_timing[] = {
+
+       { XFER_UDMA_6,     0,   0,   0,   0,   0,   0,   0,  15 },
+       { XFER_UDMA_5,     0,   0,   0,   0,   0,   0,   0,  20 },
+       { XFER_UDMA_4,     0,   0,   0,   0,   0,   0,   0,  30 },
+       { XFER_UDMA_3,     0,   0,   0,   0,   0,   0,   0,  45 },
+
+       { XFER_UDMA_2,     0,   0,   0,   0,   0,   0,   0,  60 },
+       { XFER_UDMA_1,     0,   0,   0,   0,   0,   0,   0,  80 },
+       { XFER_UDMA_0,     0,   0,   0,   0,   0,   0,   0, 120 },
+
+/*     { XFER_UDMA_SLOW,  0,   0,   0,   0,   0,   0,   0, 150 }, */
+                                          
+       { XFER_MW_DMA_2,  25,   0,   0,   0,  70,  25, 120,   0 },
+       { XFER_MW_DMA_1,  45,   0,   0,   0,  80,  50, 150,   0 },
+       { XFER_MW_DMA_0,  60,   0,   0,   0, 215, 215, 480,   0 },
+                                          
+       { XFER_SW_DMA_2,  60,   0,   0,   0, 120, 120, 240,   0 },
+       { XFER_SW_DMA_1,  90,   0,   0,   0, 240, 240, 480,   0 },
+       { XFER_SW_DMA_0, 120,   0,   0,   0, 480, 480, 960,   0 },
+
+/*     { XFER_PIO_5,     20,  50,  30, 100,  50,  30, 100,   0 }, */
+       { XFER_PIO_4,     25,  70,  25, 120,  70,  25, 120,   0 },
+       { XFER_PIO_3,     30,  80,  70, 180,  80,  70, 180,   0 },
+
+       { XFER_PIO_2,     30, 290,  40, 330, 100,  90, 240,   0 },
+       { XFER_PIO_1,     50, 290,  93, 383, 125, 100, 383,   0 },
+       { XFER_PIO_0,     70, 290, 240, 600, 165, 150, 600,   0 },
+
+/*     { XFER_PIO_SLOW, 120, 290, 240, 960, 290, 240, 960,   0 }, */
+
+       { 0xFF }
+};
+
+#define ENOUGH(v,unit)         (((v)-1)/(unit)+1)
+#define EZ(v,unit)             ((v)?ENOUGH(v,unit):0)
+
+static void ata_timing_quantize(const struct ata_timing *t, struct ata_timing *q, int T, int UT)
+{
+       q->setup   = EZ(t->setup   * 1000,  T);
+       q->act8b   = EZ(t->act8b   * 1000,  T);
+       q->rec8b   = EZ(t->rec8b   * 1000,  T);
+       q->cyc8b   = EZ(t->cyc8b   * 1000,  T);
+       q->active  = EZ(t->active  * 1000,  T);
+       q->recover = EZ(t->recover * 1000,  T);
+       q->cycle   = EZ(t->cycle   * 1000,  T);
+       q->udma    = EZ(t->udma    * 1000, UT);
+}
+
+void ata_timing_merge(const struct ata_timing *a, const struct ata_timing *b,
+                     struct ata_timing *m, unsigned int what)
+{
+       if (what & ATA_TIMING_SETUP  ) m->setup   = max(a->setup,   b->setup);
+       if (what & ATA_TIMING_ACT8B  ) m->act8b   = max(a->act8b,   b->act8b);
+       if (what & ATA_TIMING_REC8B  ) m->rec8b   = max(a->rec8b,   b->rec8b);
+       if (what & ATA_TIMING_CYC8B  ) m->cyc8b   = max(a->cyc8b,   b->cyc8b);
+       if (what & ATA_TIMING_ACTIVE ) m->active  = max(a->active,  b->active);
+       if (what & ATA_TIMING_RECOVER) m->recover = max(a->recover, b->recover);
+       if (what & ATA_TIMING_CYCLE  ) m->cycle   = max(a->cycle,   b->cycle);
+       if (what & ATA_TIMING_UDMA   ) m->udma    = max(a->udma,    b->udma);
+}
+
+static const struct ata_timing* ata_timing_find_mode(unsigned short speed)
+{
+       const struct ata_timing *t;
+
+       for (t = ata_timing; t->mode != speed; t++)
+               if (t->mode != 0xFF)
+                       return NULL;
+       return t; 
+}
+
+int ata_timing_compute(struct ata_device *adev, unsigned short speed,
+                      struct ata_timing *t, int T, int UT)
+{
+       const struct ata_timing *s;
+       struct ata_timing p;
+
+       /*
+        * Find the mode. 
+       */
+
+       if (!(s = ata_timing_find_mode(speed)))
+               return -EINVAL;
+
+       /*
+        * If the drive is an EIDE drive, it can tell us it needs extended
+        * PIO/MW_DMA cycle timing.
+        */
+
+       if (adev->id[ATA_ID_FIELD_VALID] & 2) { /* EIDE drive */
+               memset(&p, 0, sizeof(p));
+               if(speed >= XFER_PIO_0 && speed <= XFER_SW_DMA_0) {
+                       if (speed <= XFER_PIO_2) p.cycle = p.cyc8b = adev->id[ATA_ID_EIDE_PIO];
+                                           else p.cycle = p.cyc8b = adev->id[ATA_ID_EIDE_PIO_IORDY];
+               } else if(speed >= XFER_MW_DMA_0 && speed <= XFER_MW_DMA_2) {
+                       p.cycle = adev->id[ATA_ID_EIDE_DMA_MIN];
+               }
+               ata_timing_merge(&p, t, t, ATA_TIMING_CYCLE | ATA_TIMING_CYC8B);
+       }
+
+       /*
+        * Convert the timing to bus clock counts.
+        */
+
+       ata_timing_quantize(s, t, T, UT);
+
+       /*
+        * Even in DMA/UDMA modes we still use PIO access for IDENTIFY, S.M.A.R.T
+        * and some other commands. We have to ensure that the DMA cycle timing is
+        * slower/equal than the fastest PIO timing.
+        */
+
+       if (speed > XFER_PIO_4) {
+               ata_timing_compute(adev, adev->pio_mode, &p, T, UT);
+               ata_timing_merge(&p, t, t, ATA_TIMING_ALL);
+       }
+
+       /*
+        * Lenghten active & recovery time so that cycle time is correct.
+        */
+
+       if (t->act8b + t->rec8b < t->cyc8b) {
+               t->act8b += (t->cyc8b - (t->act8b + t->rec8b)) / 2;
+               t->rec8b = t->cyc8b - t->act8b;
+       }
+
+       if (t->active + t->recover < t->cycle) {
+               t->active += (t->cycle - (t->active + t->recover)) / 2;
+               t->recover = t->cycle - t->active;
+       }
+
+       return 0;
+}
+
 static struct {
        unsigned int shift;
        u8 base;
@@ -4764,6 +4910,9 @@ EXPORT_SYMBOL_GPL(ata_dev_id_string);
 EXPORT_SYMBOL_GPL(ata_dev_config);
 EXPORT_SYMBOL_GPL(ata_scsi_simulate);
 
+EXPORT_SYMBOL_GPL(ata_timing_compute);
+EXPORT_SYMBOL_GPL(ata_timing_merge);
+
 #ifdef CONFIG_PCI
 EXPORT_SYMBOL_GPL(pci_test_config_bits);
 EXPORT_SYMBOL_GPL(ata_pci_host_stop);
index 0261c55f34837a4049e9651770cbabb7304efa9b..0e214f8c8f9f6f6f85387e79de788725551aa0ef 100644 (file)
@@ -388,6 +388,19 @@ struct ata_port_info {
        struct ata_port_operations      *port_ops;
 };
 
+struct ata_timing {
+       unsigned short mode;            /* ATA mode */
+       unsigned short setup;           /* t1 */
+       unsigned short act8b;           /* t2 for 8-bit I/O */
+       unsigned short rec8b;           /* t2i for 8-bit I/O */
+       unsigned short cyc8b;           /* t0 for 8-bit I/O */
+       unsigned short active;          /* t2 or tD */
+       unsigned short recover;         /* t2i or tK */
+       unsigned short cycle;           /* t0 */
+       unsigned short udma;            /* t2CYCTYP/2 */
+};
+
+#define FIT(v,vmin,vmax)       max_t(short,min_t(short,v,vmax),vmin)
 
 extern void ata_port_probe(struct ata_port *);
 extern void __sata_phy_reset(struct ata_port *ap);
@@ -451,6 +464,32 @@ extern int ata_std_bios_param(struct scsi_device *sdev,
                              sector_t capacity, int geom[]);
 extern int ata_scsi_slave_config(struct scsi_device *sdev);
 
+/*
+ * Timing helpers
+ */
+extern int ata_timing_compute(struct ata_device *, unsigned short,
+                             struct ata_timing *, int, int);
+extern void ata_timing_merge(const struct ata_timing *,
+                            const struct ata_timing *, struct ata_timing *,
+                            unsigned int);
+
+enum {
+       ATA_TIMING_SETUP        = (1 << 0),
+       ATA_TIMING_ACT8B        = (1 << 1),
+       ATA_TIMING_REC8B        = (1 << 2),
+       ATA_TIMING_CYC8B        = (1 << 3),
+       ATA_TIMING_8BIT         = ATA_TIMING_ACT8B | ATA_TIMING_REC8B |
+                                 ATA_TIMING_CYC8B,
+       ATA_TIMING_ACTIVE       = (1 << 4),
+       ATA_TIMING_RECOVER      = (1 << 5),
+       ATA_TIMING_CYCLE        = (1 << 6),
+       ATA_TIMING_UDMA         = (1 << 7),
+       ATA_TIMING_ALL          = ATA_TIMING_SETUP | ATA_TIMING_ACT8B |
+                                 ATA_TIMING_REC8B | ATA_TIMING_CYC8B |
+                                 ATA_TIMING_ACTIVE | ATA_TIMING_RECOVER |
+                                 ATA_TIMING_CYCLE | ATA_TIMING_UDMA,
+};
+
 
 #ifdef CONFIG_PCI
 struct pci_bits {