ARM: DRA7: Add support for manual mode configuration
authorLokesh Vutla <lokeshvutla@ti.com>
Thu, 4 Jun 2015 11:12:37 +0000 (16:42 +0530)
committerTom Rini <trini@konsulko.com>
Fri, 12 Jun 2015 17:02:05 +0000 (13:02 -0400)
In addition to the regular mux configuration, certain pins of DRA7
require to have "manual mode" also programmed, when predefined
delay characteristics cannot be used for the interface.

struct iodelay_cfg_entry is introduced for populating
manual mode IO timings.
For configuring manual mode, along with the normal pad
configuration do the following steps:
- Select MODESELECT field of each assocaited PAD.
  CTRL_CORE_PAD_XXX[8]:MODESELECT = 1(Enable MANUAL_MODE macro along with mux)
- Populate A_DELAY, G_DELAY values that are specified in DATA MANUAL.
  And pass the offset of the CFG_XXX register in iodelay_cfg_entry.

Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com>
Signed-off-by: Nishanth Menon <nm@ti.com>
arch/arm/cpu/armv7/omap5/dra7xx_iodelay.c
arch/arm/include/asm/arch-omap5/dra7xx_iodelay.h
arch/arm/include/asm/arch-omap5/mux_dra7xx.h
arch/arm/include/asm/arch-omap5/sys_proto.h

index 4b8ba26fd66c95067748765f49dad8f1541dd9c5..9fa6e6991f0ef7d168ed53f3903e9a16e8448568 100644 (file)
@@ -13,6 +13,7 @@
 #include <asm/arch/omap.h>
 #include <asm/arch/sys_proto.h>
 #include <asm/arch/clock.h>
+#include <asm/arch/mux_dra7xx.h>
 #include <asm/omap_common.h>
 
 static int isolate_io(u32 isolate)
@@ -80,7 +81,94 @@ static int update_delay_mechanism(u32 base)
        return 0;
 }
 
-void __recalibrate_iodelay(struct pad_conf_entry const *pad, int npads)
+static u32 calculate_delay(u32 base, u16 offset, u16 den)
+{
+       u16 refclk_period, dly_cnt, ref_cnt;
+       u32 reg, q, r;
+
+       refclk_period = readl(base + CFG_REG_2_OFFSET) &
+                             CFG_REG_REFCLK_PERIOD_MASK;
+
+       reg = readl(base + offset);
+       dly_cnt = (reg & CFG_REG_DLY_CNT_MASK) >> CFG_REG_DLY_CNT_SHIFT;
+       ref_cnt = (reg & CFG_REG_REF_CNT_MASK) >> CFG_REG_REF_CNT_SHIFT;
+
+       if (!dly_cnt || !den)
+               return 0;
+
+       /*
+        * To avoid overflow and integer truncation, delay value
+        * is calculated as quotient + remainder.
+        */
+       q = 5 * ((ref_cnt * refclk_period) / (dly_cnt * den));
+       r = (10 * ((ref_cnt * refclk_period) % (dly_cnt * den))) /
+               (2 * dly_cnt * den);
+
+       return q + r;
+}
+
+static u32 get_cfg_reg(u16 a_delay, u16 g_delay, u32 cpde, u32 fpde)
+{
+       u32 g_delay_coarse, g_delay_fine;
+       u32 a_delay_coarse, a_delay_fine;
+       u32 c_elements, f_elements;
+       u32 total_delay, reg = 0;
+
+       g_delay_coarse = g_delay / 920;
+       g_delay_fine = ((g_delay % 920) * 10) / 60;
+
+       a_delay_coarse = a_delay / cpde;
+       a_delay_fine = ((a_delay % cpde) * 10) / fpde;
+
+       c_elements = g_delay_coarse + a_delay_coarse;
+       f_elements = (g_delay_fine + a_delay_fine) / 10;
+
+       if (f_elements > 22) {
+               total_delay = c_elements * cpde + f_elements * fpde;
+
+               c_elements = total_delay / cpde;
+               f_elements = (total_delay % cpde) / fpde;
+       }
+
+       reg = (c_elements << CFG_X_COARSE_DLY_SHIFT) & CFG_X_COARSE_DLY_MASK;
+       reg |= (f_elements << CFG_X_FINE_DLY_SHIFT) & CFG_X_FINE_DLY_MASK;
+       reg |= CFG_X_SIGNATURE << CFG_X_SIGNATURE_SHIFT;
+       reg |= CFG_X_LOCK << CFG_X_LOCK_SHIFT;
+
+       return reg;
+}
+
+static int do_set_iodelay(u32 base, struct iodelay_cfg_entry const *array,
+                          int niodelays)
+{
+       struct iodelay_cfg_entry *iodelay = (struct iodelay_cfg_entry *)array;
+       u32 reg, cpde, fpde, i;
+
+       if (!niodelays)
+               return 0;
+
+       cpde = calculate_delay((*ctrl)->iodelay_config_base, CFG_REG_3_OFFSET,
+                              88);
+       if (!cpde)
+               return ERR_CPDE;
+
+       fpde = calculate_delay((*ctrl)->iodelay_config_base, CFG_REG_4_OFFSET,
+                              264);
+       if (!fpde)
+               return ERR_FPDE;
+
+       for (i = 0; i < niodelays; i++, iodelay++) {
+               reg = get_cfg_reg(iodelay->a_delay, iodelay->g_delay, cpde,
+                                 fpde);
+               writel(reg, base + iodelay->offset);
+       }
+
+       return 0;
+}
+
+void __recalibrate_iodelay(struct pad_conf_entry const *pad, int npads,
+                          struct iodelay_cfg_entry const *iodelay,
+                          int niodelays)
 {
        int ret = 0;
 
@@ -109,6 +197,11 @@ void __recalibrate_iodelay(struct pad_conf_entry const *pad, int npads)
        /* Configure Mux settings */
        do_set_mux32((*ctrl)->control_padconf_core_base, pad, npads);
 
+       /* Configure Manual IO timing modes */
+       ret = do_set_iodelay((*ctrl)->iodelay_config_base, iodelay, niodelays);
+       if (ret)
+               goto err;
+
        ret = isolate_io(DEISOLATE_IO);
 
 err:
@@ -133,6 +226,12 @@ err:
        case ERR_DEISOLATE_IO:
                puts("IODELAY: De-isolation of Device IOs failed\n");
                break;
+       case ERR_CPDE:
+               puts("IODELAY: CPDE calculation failed\n");
+               break;
+       case ERR_FPDE:
+               puts("IODELAY: FPDE calculation failed\n");
+               break;
        default:
                debug("IODELAY: IO delay recalibration successfully completed\n");
        }
index a924629a35df1b1c04c07b534eab0fea8119a854..2f53d85283b8d2138ac350ac28fde71042ffe5f1 100644 (file)
 #define CFG_IODELAY_UNLOCK_KEY         0x0000AAAA
 #define CFG_IODELAY_LOCK_KEY           0x0000AAAB
 
+/* CONFIG_REG_3/4 */
+#define CFG_REG_3_OFFSET       0x18
+#define CFG_REG_4_OFFSET       0x1C
+#define CFG_REG_DLY_CNT_SHIFT  16
+#define CFG_REG_DLY_CNT_MASK   (0xFFFF << 16)
+#define CFG_REG_REF_CNT_SHIFT  0
+#define CFG_REG_REF_CNT_MASK   (0xFFFF << 0)
+
 /* CTRL_CORE_SMA_SW_0 */
 #define CTRL_ISOLATE_SHIFT             2
 #define CTRL_ISOLATE_MASK              (1 << 2)
 #define ERR_DEISOLATE_IO               0x2
 #define ERR_ISOLATE_IO                 0x4
 #define ERR_UPDATE_DELAY               0x8
+#define ERR_CPDE                       0x3
+#define ERR_FPDE                       0x5
+
+/* CFG_XXX */
+#define CFG_X_SIGNATURE_SHIFT          12
+#define CFG_X_SIGNATURE_MASK           (0x3F << 12)
+#define CFG_X_LOCK_SHIFT               10
+#define CFG_X_LOCK_MASK                        (0x1 << 10)
+#define CFG_X_COARSE_DLY_SHIFT         5
+#define CFG_X_COARSE_DLY_MASK          (0x1F << 5)
+#define CFG_X_FINE_DLY_SHIFT           0
+#define CFG_X_FINE_DLY_MASK            (0x1F << 0)
+#define CFG_X_SIGNATURE                        0x29
+#define CFG_X_LOCK                     1
+
+void __recalibrate_iodelay(struct pad_conf_entry const *pad, int npads,
+                          struct iodelay_cfg_entry const *iodelay,
+                          int niodelays);
 
-void __recalibrate_iodelay(struct pad_conf_entry const *array, int npads);
 #endif
index 13c288b5d31296b472e4f34922b9f7397c6772f7..2115abb5553e59f2c2475cd8de4fbbebfa4633b0 100644 (file)
@@ -61,6 +61,8 @@
 #define MODE_SELECT            (1 << 8)
 #define DELAYMODE_SHIFT                4
 
+#define MANUAL_MODE    MODE_SELECT
+
 #define VIRTUAL_MODE0  (MODE_SELECT | (0x0 << DELAYMODE_SHIFT))
 #define VIRTUAL_MODE1  (MODE_SELECT | (0x1 << DELAYMODE_SHIFT))
 #define VIRTUAL_MODE2  (MODE_SELECT | (0x2 << DELAYMODE_SHIFT))
index b41bf15a08cf61e172fcbdb4defd5c916097b14a..6da8297c7292ec5329196fab082d45f93d9b12a5 100644 (file)
 
 DECLARE_GLOBAL_DATA_PTR;
 
+/*
+ * Structure for Iodelay configuration registers.
+ * Theoretical max for g_delay is 21560 ps.
+ * Theoretical max for a_delay is 1/3rd of g_delay max.
+ * So using u16 for both a/g_delay.
+ */
+struct iodelay_cfg_entry {
+       u16 offset;
+       u16 a_delay;
+       u16 g_delay;
+};
+
 struct pad_conf_entry {
        u32 offset;
        u32 val;