From 71bed1855ff1f0b9702ecedd1ece486ebc915779 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Thu, 4 Jun 2015 16:42:37 +0530 Subject: ARM: DRA7: Add support for manual mode configuration 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 Signed-off-by: Nishanth Menon diff --git a/arch/arm/cpu/armv7/omap5/dra7xx_iodelay.c b/arch/arm/cpu/armv7/omap5/dra7xx_iodelay.c index 4b8ba26..9fa6e69 100644 --- a/arch/arm/cpu/armv7/omap5/dra7xx_iodelay.c +++ b/arch/arm/cpu/armv7/omap5/dra7xx_iodelay.c @@ -13,6 +13,7 @@ #include #include #include +#include #include 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"); } diff --git a/arch/arm/include/asm/arch-omap5/dra7xx_iodelay.h b/arch/arm/include/asm/arch-omap5/dra7xx_iodelay.h index a924629..2f53d85 100644 --- a/arch/arm/include/asm/arch-omap5/dra7xx_iodelay.h +++ b/arch/arm/include/asm/arch-omap5/dra7xx_iodelay.h @@ -35,6 +35,14 @@ #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) @@ -53,6 +61,23 @@ #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 diff --git a/arch/arm/include/asm/arch-omap5/mux_dra7xx.h b/arch/arm/include/asm/arch-omap5/mux_dra7xx.h index 13c288b..2115abb 100644 --- a/arch/arm/include/asm/arch-omap5/mux_dra7xx.h +++ b/arch/arm/include/asm/arch-omap5/mux_dra7xx.h @@ -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)) diff --git a/arch/arm/include/asm/arch-omap5/sys_proto.h b/arch/arm/include/asm/arch-omap5/sys_proto.h index b41bf15..6da8297 100644 --- a/arch/arm/include/asm/arch-omap5/sys_proto.h +++ b/arch/arm/include/asm/arch-omap5/sys_proto.h @@ -18,6 +18,18 @@ 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; -- cgit v0.10.2