diff options
Diffstat (limited to 'drivers/mfd')
-rw-r--r-- | drivers/mfd/db8500-prcmu.c | 238 | ||||
-rw-r--r-- | drivers/mfd/dbx500-prcmu-regs.h | 19 |
2 files changed, 257 insertions, 0 deletions
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 24413a3a67b..eb026cf7efd 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -467,6 +467,25 @@ struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = { CLK_MGT_ENTRY(UICCCLK, PLL_FIX, false), }; +struct dsiclk { + u32 divsel_mask; + u32 divsel_shift; + u32 divsel; +}; + +static struct dsiclk dsiclk[2] = { + { + .divsel_mask = PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_MASK, + .divsel_shift = PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_SHIFT, + .divsel = PRCM_DSI_PLLOUT_SEL_PHI, + }, + { + .divsel_mask = PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_MASK, + .divsel_shift = PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_SHIFT, + .divsel = PRCM_DSI_PLLOUT_SEL_PHI, + } +}; + struct dsiescclk { u32 en; u32 div_mask; @@ -1561,6 +1580,66 @@ static int request_sga_clock(u8 clock, bool enable) return ret; } +static inline bool plldsi_locked(void) +{ + return (readl(PRCM_PLLDSI_LOCKP) & + (PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP10 | + PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP3)) == + (PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP10 | + PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP3); +} + +static int request_plldsi(bool enable) +{ + int r = 0; + u32 val; + + writel(PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMP, (enable ? + PRCM_MMIP_LS_CLAMP_CLR : PRCM_MMIP_LS_CLAMP_SET)); + + val = readl(PRCM_PLLDSI_ENABLE); + if (enable) + val |= PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE; + else + val &= ~PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE; + writel(val, PRCM_PLLDSI_ENABLE); + + if (enable) { + unsigned int i; + bool locked = plldsi_locked(); + + for (i = 10; !locked && (i > 0); --i) { + udelay(100); + locked = plldsi_locked(); + } + if (locked) { + writel(PRCM_APE_RESETN_DSIPLL_RESETN, + PRCM_APE_RESETN_SET); + } else { + writel(PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMP, + PRCM_MMIP_LS_CLAMP_SET); + val &= ~PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE; + writel(val, PRCM_PLLDSI_ENABLE); + r = -EAGAIN; + } + } else { + writel(PRCM_APE_RESETN_DSIPLL_RESETN, PRCM_APE_RESETN_CLR); + } + return r; +} + +static int request_dsiclk(u8 n, bool enable) +{ + u32 val; + + val = readl(PRCM_DSI_PLLOUT_SEL); + val &= ~dsiclk[n].divsel_mask; + val |= ((enable ? dsiclk[n].divsel : PRCM_DSI_PLLOUT_SEL_OFF) << + dsiclk[n].divsel_shift); + writel(val, PRCM_DSI_PLLOUT_SEL); + return 0; +} + static int request_dsiescclk(u8 n, bool enable) { u32 val; @@ -1587,8 +1666,12 @@ int db8500_prcmu_request_clock(u8 clock, bool enable) return request_clock(clock, enable); else if (clock == PRCMU_TIMCLK) return request_timclk(enable); + else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK)) + return request_dsiclk((clock - PRCMU_DSI0CLK), enable); else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK)) return request_dsiescclk((clock - PRCMU_DSI0ESCCLK), enable); + else if (clock == PRCMU_PLLDSI) + return request_plldsi(enable); else if (clock == PRCMU_SYSCLK) return request_sysclk(enable); else if (clock == PRCMU_PLLSOC1) @@ -1674,6 +1757,30 @@ static unsigned long clock_rate(u8 clock) return 0; } +static unsigned long dsiclk_rate(u8 n) +{ + u32 divsel; + u32 div = 1; + + divsel = readl(PRCM_DSI_PLLOUT_SEL); + divsel = ((divsel & dsiclk[n].divsel_mask) >> dsiclk[n].divsel_shift); + + if (divsel == PRCM_DSI_PLLOUT_SEL_OFF) + divsel = dsiclk[n].divsel; + + switch (divsel) { + case PRCM_DSI_PLLOUT_SEL_PHI_4: + div *= 2; + case PRCM_DSI_PLLOUT_SEL_PHI_2: + div *= 2; + case PRCM_DSI_PLLOUT_SEL_PHI: + return pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK), + PLL_RAW) / div; + default: + return 0; + } +} + static unsigned long dsiescclk_rate(u8 n) { u32 div; @@ -1697,6 +1804,11 @@ unsigned long prcmu_clock_rate(u8 clock) return pll_rate(PRCM_PLLSOC1_FREQ, ROOT_CLOCK_RATE, PLL_RAW); else if (clock == PRCMU_PLLDDR) return pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, PLL_RAW); + else if (clock == PRCMU_PLLDSI) + return pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK), + PLL_RAW); + else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK)) + return dsiclk_rate(clock - PRCMU_DSI0CLK); else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK)) return dsiescclk_rate(clock - PRCMU_DSI0ESCCLK); else @@ -1760,6 +1872,60 @@ static long round_clock_rate(u8 clock, unsigned long rate) return rounded_rate; } +#define MIN_PLL_VCO_RATE 600000000ULL +#define MAX_PLL_VCO_RATE 1680640000ULL + +static long round_plldsi_rate(unsigned long rate) +{ + long rounded_rate = 0; + unsigned long src_rate; + unsigned long rem; + u32 r; + + src_rate = clock_rate(PRCMU_HDMICLK); + rem = rate; + + for (r = 7; (rem > 0) && (r > 0); r--) { + u64 d; + + d = (r * rate); + (void)do_div(d, src_rate); + if (d < 6) + d = 6; + else if (d > 255) + d = 255; + d *= src_rate; + if (((2 * d) < (r * MIN_PLL_VCO_RATE)) || + ((r * MAX_PLL_VCO_RATE) < (2 * d))) + continue; + (void)do_div(d, r); + if (rate < d) { + if (rounded_rate == 0) + rounded_rate = (long)d; + break; + } + if ((rate - d) < rem) { + rem = (rate - d); + rounded_rate = (long)d; + } + } + return rounded_rate; +} + +static long round_dsiclk_rate(unsigned long rate) +{ + u32 div; + unsigned long src_rate; + long rounded_rate; + + src_rate = pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK), + PLL_RAW); + div = clock_divider(src_rate, rate); + rounded_rate = (src_rate / ((div > 2) ? 4 : div)); + + return rounded_rate; +} + static long round_dsiescclk_rate(unsigned long rate) { u32 div; @@ -1777,6 +1943,10 @@ long prcmu_round_clock_rate(u8 clock, unsigned long rate) { if (clock < PRCMU_NUM_REG_CLOCKS) return round_clock_rate(clock, rate); + else if (clock == PRCMU_PLLDSI) + return round_plldsi_rate(rate); + else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK)) + return round_dsiclk_rate(rate); else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK)) return round_dsiescclk_rate(rate); else @@ -1832,6 +2002,70 @@ static void set_clock_rate(u8 clock, unsigned long rate) spin_unlock_irqrestore(&clk_mgt_lock, flags); } +static int set_plldsi_rate(unsigned long rate) +{ + unsigned long src_rate; + unsigned long rem; + u32 pll_freq = 0; + u32 r; + + src_rate = clock_rate(PRCMU_HDMICLK); + rem = rate; + + for (r = 7; (rem > 0) && (r > 0); r--) { + u64 d; + u64 hwrate; + + d = (r * rate); + (void)do_div(d, src_rate); + if (d < 6) + d = 6; + else if (d > 255) + d = 255; + hwrate = (d * src_rate); + if (((2 * hwrate) < (r * MIN_PLL_VCO_RATE)) || + ((r * MAX_PLL_VCO_RATE) < (2 * hwrate))) + continue; + (void)do_div(hwrate, r); + if (rate < hwrate) { + if (pll_freq == 0) + pll_freq = (((u32)d << PRCM_PLL_FREQ_D_SHIFT) | + (r << PRCM_PLL_FREQ_R_SHIFT)); + break; + } + if ((rate - hwrate) < rem) { + rem = (rate - hwrate); + pll_freq = (((u32)d << PRCM_PLL_FREQ_D_SHIFT) | + (r << PRCM_PLL_FREQ_R_SHIFT)); + } + } + if (pll_freq == 0) + return -EINVAL; + + pll_freq |= (1 << PRCM_PLL_FREQ_N_SHIFT); + writel(pll_freq, PRCM_PLLDSI_FREQ); + + return 0; +} + +static void set_dsiclk_rate(u8 n, unsigned long rate) +{ + u32 val; + u32 div; + + div = clock_divider(pll_rate(PRCM_PLLDSI_FREQ, + clock_rate(PRCMU_HDMICLK), PLL_RAW), rate); + + dsiclk[n].divsel = (div == 1) ? PRCM_DSI_PLLOUT_SEL_PHI : + (div == 2) ? PRCM_DSI_PLLOUT_SEL_PHI_2 : + /* else */ PRCM_DSI_PLLOUT_SEL_PHI_4; + + val = readl(PRCM_DSI_PLLOUT_SEL); + val &= ~dsiclk[n].divsel_mask; + val |= (dsiclk[n].divsel << dsiclk[n].divsel_shift); + writel(val, PRCM_DSI_PLLOUT_SEL); +} + static void set_dsiescclk_rate(u8 n, unsigned long rate) { u32 val; @@ -1848,6 +2082,10 @@ int prcmu_set_clock_rate(u8 clock, unsigned long rate) { if (clock < PRCMU_NUM_REG_CLOCKS) set_clock_rate(clock, rate); + else if (clock == PRCMU_PLLDSI) + return set_plldsi_rate(rate); + else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK)) + set_dsiclk_rate((clock - PRCMU_DSI0CLK), rate); else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK)) set_dsiescclk_rate((clock - PRCMU_DSI0ESCCLK), rate); return 0; diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h index e8d70d0d145..9d0bc7200c0 100644 --- a/drivers/mfd/dbx500-prcmu-regs.h +++ b/drivers/mfd/dbx500-prcmu-regs.h @@ -131,6 +131,8 @@ #define PRCM_MMIP_LS_CLAMP_SET (_PRCMU_BASE + 0x420) #define PRCM_MMIP_LS_CLAMP_CLR (_PRCMU_BASE + 0x424) +#define PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMP BIT(11) + /* PRCMU clock/PLL/reset registers */ #define PRCM_PLLSOC0_FREQ (_PRCMU_BASE + 0x080) #define PRCM_PLLSOC1_FREQ (_PRCMU_BASE + 0x084) @@ -153,6 +155,21 @@ #define PRCM_APE_RESETN_SET (_PRCMU_BASE + 0x1E4) #define PRCM_APE_RESETN_CLR (_PRCMU_BASE + 0x1E8) +#define PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE BIT(0) + +#define PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP10 BIT(0) +#define PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP3 BIT(1) + +#define PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_SHIFT 0 +#define PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_MASK BITS(0, 2) +#define PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_SHIFT 8 +#define PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_MASK BITS(8, 10) + +#define PRCM_DSI_PLLOUT_SEL_OFF 0 +#define PRCM_DSI_PLLOUT_SEL_PHI 1 +#define PRCM_DSI_PLLOUT_SEL_PHI_2 2 +#define PRCM_DSI_PLLOUT_SEL_PHI_4 3 + #define PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_SHIFT 0 #define PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_MASK BITS(0, 7) #define PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_DIV_SHIFT 8 @@ -163,6 +180,8 @@ #define PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_EN BIT(25) #define PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_EN BIT(26) +#define PRCM_APE_RESETN_DSIPLL_RESETN BIT(14) + #define PRCM_CLKOCR (_PRCMU_BASE + 0x1CC) #define PRCM_CLKOCR_CLKOUT0_REF_CLK (1 << 0) #define PRCM_CLKOCR_CLKOUT0_MASK BITS(0, 13) |