
Simon,
-----Original Message----- From: Simon Glass [mailto:sjg@chromium.org] Sent: Friday, February 03, 2012 6:14 PM To: U-Boot Mailing List Cc: Tom Warren; Stephen Warren; linux-tegra@vger.kernel.org; Simon Glass Subject: [PATCH v3 3/9] tegra: Enhance clock support to handle 16-bit clock divisors
I2C ports have a 16-bit clock divisor. Add code to handle this special case so that I2C speeds below 150KHz are supported.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3:
- Add new patch to support 16-bit clock divisors required by I2C
arch/arm/cpu/armv7/tegra2/clock.c | 63 ++++++++++++++++++-------
arch/arm/include/asm/arch-tegra2/clk_rst.h | 10 ++++- 2 files changed, 49 insertions(+), 24 deletions(-)
diff --git a/arch/arm/cpu/armv7/tegra2/clock.c b/arch/arm/cpu/armv7/tegra2/clock.c index 11d2346..e58cb3b 100644 --- a/arch/arm/cpu/armv7/tegra2/clock.c +++ b/arch/arm/cpu/armv7/tegra2/clock.c @@ -67,6 +67,7 @@ enum clock_type_id { CLOCK_TYPE_MCPT, CLOCK_TYPE_PCM, CLOCK_TYPE_PCMT,
- CLOCK_TYPE_PCMT16, /* CLOCK_TYPE_PCMT with 16-bit divider */ CLOCK_TYPE_PCXTS, CLOCK_TYPE_PDCT,
@@ -98,6 +99,7 @@ static enum clock_id clock_source[CLOCK_TYPE_COUNT][CLOCK_MAX_MUX] = { { CLK(MEMORY), CLK(CGENERAL), CLK(PERIPH), CLK(OSC) }, { CLK(PERIPH), CLK(CGENERAL), CLK(MEMORY), CLK(NONE) }, { CLK(PERIPH), CLK(CGENERAL), CLK(MEMORY), CLK(OSC) },
- { CLK(PERIPH), CLK(CGENERAL), CLK(MEMORY), CLK(OSC) }, { CLK(PERIPH), CLK(CGENERAL), CLK(XCPU), CLK(OSC) }, { CLK(PERIPH), CLK(DISPLAY), CLK(CGENERAL), CLK(OSC) },
}; @@ -211,8 +213,8 @@ static enum clock_type_id clock_periph_type[PERIPHC_COUNT] = {
/* 0x08 */ TYPE(PERIPHC_XIO, CLOCK_TYPE_PCMT),
- TYPE(PERIPHC_I2C1, CLOCK_TYPE_PCMT),
- TYPE(PERIPHC_DVC_I2C, CLOCK_TYPE_PCMT),
- TYPE(PERIPHC_I2C1, CLOCK_TYPE_PCMT16),
- TYPE(PERIPHC_DVC_I2C, CLOCK_TYPE_PCMT16), TYPE(PERIPHC_TWC, CLOCK_TYPE_PCMT), TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), TYPE(PERIPHC_SPI1, CLOCK_TYPE_PCMT),
@@ -246,7 +248,7 @@ static enum clock_type_id clock_periph_type[PERIPHC_COUNT] = { TYPE(PERIPHC_HDMI, CLOCK_TYPE_PDCT), TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), TYPE(PERIPHC_TVDAC, CLOCK_TYPE_PDCT),
- TYPE(PERIPHC_I2C2, CLOCK_TYPE_PCMT),
TYPE(PERIPHC_I2C2, CLOCK_TYPE_PCMT16), TYPE(PERIPHC_EMC, CLOCK_TYPE_MCPT),
/* 0x28 */
@@ -256,7 +258,7 @@ static enum clock_type_id clock_periph_type[PERIPHC_COUNT] = { TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), TYPE(PERIPHC_SPI4, CLOCK_TYPE_PCMT),
- TYPE(PERIPHC_I2C3, CLOCK_TYPE_PCMT),
TYPE(PERIPHC_I2C3, CLOCK_TYPE_PCMT16), TYPE(PERIPHC_SDMMC3, CLOCK_TYPE_PCMT),
/* 0x30 */
@@ -518,14 +520,16 @@ void clock_ll_set_source(enum periph_id periph_id, unsigned source)
- Given the parent's rate and the required rate for the children, this
works
- out the peripheral clock divider to use, in 7.1 binary format.
*/
- @param divider_bits number of divider bits (8 or 16)
- @param parent_rate clock rate of parent clock in Hz
- @param rate required clock rate for this clock
- @return divider which should be used
-static int clk_div7_1_get_divider(unsigned long parent_rate,
unsigned long rate)
+static int clk_get_divider(unsigned divider_bits, unsigned long parent_rate,
unsigned long rate)
{ u64 divider = parent_rate * 2;
unsigned max_divider = 1 << divider_bits;
divider += rate - 1; do_div(divider, rate);
@@ -533,7 +537,7 @@ static int clk_div7_1_get_divider(unsigned long parent_rate, if ((s64)divider - 2 < 0) return 0;
- if ((s64)divider - 2 > 255)
if ((s64)divider - 2 >= max_divider) return -1;
return divider - 2;
@@ -571,6 +575,7 @@ unsigned long clock_get_periph_rate(enum periph_id periph_id,
- required child clock rate. This function assumes that a second-stage
- divisor is available which can divide by powers of 2 from 1 to 256.
- @param divider_bits number of divider bits (8 or 16)
- @param parent_rate clock rate of parent clock in Hz
- @param rate required clock rate for this clock
- @param extra_div value for the second-stage divisor (not set if
this @@ -578,8 +583,8 @@ unsigned long clock_get_periph_rate(enum periph_id periph_id,
- @return divider which should be used, or -1 if nothing is valid
*/ -static int find_best_divider(unsigned long parent_rate, unsigned long rate,
int *extra_div)
+static int find_best_divider(unsigned divider_bits, unsigned long parent_rate,
unsigned long rate, int *extra_div)
{ int shift; int best_divider = -1; @@ -588,7 +593,8 @@ static int find_best_divider(unsigned long parent_rate, unsigned long rate, /* try dividers from 1 to 256 and find closest match */ for (shift = 0; shift <= 8 && best_error > 0; shift++) { unsigned divided_parent = parent_rate >> shift;
int divider = clk_div7_1_get_divider(divided_parent, rate);
int divider = clk_get_divider(divider_bits, divided_parent,
unsigned effective_rate = get_rate_from_divider(divided_parent, divider); int error = rate - effective_rate;rate);
@@ -614,10 +620,11 @@ static int find_best_divider(unsigned long parent_rate, unsigned long rate,
- @param periph_id peripheral to start
- @param source PLL id of required parent clock
- @param mux_bits Set to number of bits in mux register: 2 or 4
*/
- @param divider_bits Set to number of divider bits (8 or 16)
- @return mux value (0-4, or -1 if not found)
static int get_periph_clock_source(enum periph_id periph_id,
enum clock_id parent, int *mux_bits)
enum clock_id parent, int *mux_bits, int *divider_bits)
{ enum clock_type_id type; enum periphc_internal_id internal_id; @@ -631,11 +638,18 @@ static int get_periph_clock_source(enum periph_id periph_id, type = clock_periph_type[internal_id]; assert(clock_type_id_isvalid(type));
- /* Special case here for the clock with a 4-bit source mux */
/*
* Special cases here for the clock with a 4-bit source mux and I2C
* with its 16-bit divisor
*/
if (type == CLOCK_TYPE_PCXTS) *mux_bits = 4; else *mux_bits = 2;
if (type == CLOCK_TYPE_PCMT16)
*divider_bits = 16;
else
*divider_bits = 8;
for (mux = 0; mux < CLOCK_MAX_MUX; mux++) if (clock_source[type][mux] == parent) @@ -661,24 +675,22 @@
static int get_periph_clock_source(enum periph_id periph_id,
- Adjust peripheral PLL to use the given divider and source.
- @param periph_id peripheral to adjust
- @param parent Required parent clock (for source mux)
- @param divider Required divider in 7.1 format
- @param source Source number (0-3 or 0-7)
- @param mux_bits Number of mux bits (2 or 4)
- @param divider Required divider in 7.1 or 15.1 format
- @return 0 if ok, -1 on error (requesting a parent clock which is not
valid
for this peripheral)
*/ -static int adjust_periph_pll(enum periph_id periph_id,
enum clock_id parent, unsigned divider)
+static int adjust_periph_pll(enum periph_id periph_id, int source,
int mux_bits, unsigned divider)
{ u32 *reg = get_periph_source_reg(periph_id);
unsigned source;
int mux_bits;
clrsetbits_le32(reg, OUT_CLK_DIVISOR_MASK, divider << OUT_CLK_DIVISOR_SHIFT); udelay(1);
/* work out the source clock and set it */
source = get_periph_clock_source(periph_id, parent, &mux_bits); if (source < 0) return -1; if (mux_bits == 4) {
@@ -696,14 +708,21 @@ unsigned clock_adjust_periph_pll_div(enum periph_id periph_id, enum clock_id parent, unsigned rate, int *extra_div) { unsigned effective_rate;
int mux_bits, divider_bits, source; int divider;
/* work out the source clock and set it */
source = get_periph_clock_source(periph_id, parent, &mux_bits,
÷r_bits);
if (extra_div)
divider = find_best_divider(pll_rate[parent], rate, extra_div);
divider = find_best_divider(divider_bits, pll_rate[parent],
elserate, extra_div);
divider = clk_div7_1_get_divider(pll_rate[parent], rate);
divider = clk_get_divider(divider_bits, pll_rate[parent],
assert(divider >= 0);rate);
- if (adjust_periph_pll(periph_id, parent, divider))
- if (adjust_periph_pll(periph_id, source, mux_bits, divider)) return -1U; debug("periph %d, rate=%d, reg=%p = %x\n", periph_id, rate, get_periph_source_reg(periph_id),
diff --git a/arch/arm/include/asm/arch-tegra2/clk_rst.h b/arch/arm/include/asm/arch-tegra2/clk_rst.h index 0b6e004..415e420 100644 --- a/arch/arm/include/asm/arch-tegra2/clk_rst.h +++ b/arch/arm/include/asm/arch-tegra2/clk_rst.h @@ -125,9 +125,15 @@ struct clk_rst_ctlr { #define OSC_FREQ_SHIFT 30 #define OSC_FREQ_MASK (3U << OSC_FREQ_SHIFT)
-/* CLK_RST_CONTROLLER_CLK_SOURCE_x_OUT_0 */ +/*
- CLK_RST_CONTROLLER_CLK_SOURCE_x_OUT_0 - the mask here is normally 8
+bits
- but can be 16. We could use knowledge we have to restrict the mask
+in
- the 8-bit cases (the divider_bits value returned by
- get_periph_clock_source()) but it does not seem worth it since the
+code
- already checks the ranges of values it is writing, in clk_get_divider().
- */
#define OUT_CLK_DIVISOR_SHIFT 0 -#define OUT_CLK_DIVISOR_MASK (255 << OUT_CLK_DIVISOR_SHIFT) +#define OUT_CLK_DIVISOR_MASK (0xffff << OUT_CLK_DIVISOR_SHIFT)
#define OUT_CLK_SOURCE_SHIFT 30
#define OUT_CLK_SOURCE_MASK (3U << OUT_CLK_SOURCE_SHIFT)
1.7.7.3
This looks reasonable from what I can see looking at the TRM for the CAR controller, so: Acked-by: Tom Warren twarren@nvidia.com
Tom