Skip to content
Snippets Groups Projects
Commit 2e33559f authored by Simon Glass's avatar Simon Glass Committed by Albert ARIBAUD
Browse files

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: default avatarSimon Glass <sjg@chromium.org>
Signed-off-by: default avatarTom Warren <twarren@nvidia.com>
parent c6782270
No related branches found
No related tags found
No related merge requests found
...@@ -68,6 +68,7 @@ enum clock_type_id { ...@@ -68,6 +68,7 @@ enum clock_type_id {
CLOCK_TYPE_MCPT, CLOCK_TYPE_MCPT,
CLOCK_TYPE_PCM, CLOCK_TYPE_PCM,
CLOCK_TYPE_PCMT, CLOCK_TYPE_PCMT,
CLOCK_TYPE_PCMT16, /* CLOCK_TYPE_PCMT with 16-bit divider */
CLOCK_TYPE_PCXTS, CLOCK_TYPE_PCXTS,
CLOCK_TYPE_PDCT, CLOCK_TYPE_PDCT,
...@@ -99,6 +100,7 @@ static enum clock_id clock_source[CLOCK_TYPE_COUNT][CLOCK_MAX_MUX] = { ...@@ -99,6 +100,7 @@ static enum clock_id clock_source[CLOCK_TYPE_COUNT][CLOCK_MAX_MUX] = {
{ CLK(MEMORY), CLK(CGENERAL), CLK(PERIPH), CLK(OSC) }, { CLK(MEMORY), CLK(CGENERAL), CLK(PERIPH), CLK(OSC) },
{ CLK(PERIPH), CLK(CGENERAL), CLK(MEMORY), CLK(NONE) }, { 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(MEMORY), CLK(OSC) },
{ CLK(PERIPH), CLK(CGENERAL), CLK(XCPU), CLK(OSC) }, { CLK(PERIPH), CLK(CGENERAL), CLK(XCPU), CLK(OSC) },
{ CLK(PERIPH), CLK(DISPLAY), CLK(CGENERAL), CLK(OSC) }, { CLK(PERIPH), CLK(DISPLAY), CLK(CGENERAL), CLK(OSC) },
}; };
...@@ -212,8 +214,8 @@ static enum clock_type_id clock_periph_type[PERIPHC_COUNT] = { ...@@ -212,8 +214,8 @@ static enum clock_type_id clock_periph_type[PERIPHC_COUNT] = {
/* 0x08 */ /* 0x08 */
TYPE(PERIPHC_XIO, CLOCK_TYPE_PCMT), TYPE(PERIPHC_XIO, CLOCK_TYPE_PCMT),
TYPE(PERIPHC_I2C1, CLOCK_TYPE_PCMT), TYPE(PERIPHC_I2C1, CLOCK_TYPE_PCMT16),
TYPE(PERIPHC_DVC_I2C, CLOCK_TYPE_PCMT), TYPE(PERIPHC_DVC_I2C, CLOCK_TYPE_PCMT16),
TYPE(PERIPHC_TWC, CLOCK_TYPE_PCMT), TYPE(PERIPHC_TWC, CLOCK_TYPE_PCMT),
TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE),
TYPE(PERIPHC_SPI1, CLOCK_TYPE_PCMT), TYPE(PERIPHC_SPI1, CLOCK_TYPE_PCMT),
...@@ -247,7 +249,7 @@ static enum clock_type_id clock_periph_type[PERIPHC_COUNT] = { ...@@ -247,7 +249,7 @@ static enum clock_type_id clock_periph_type[PERIPHC_COUNT] = {
TYPE(PERIPHC_HDMI, CLOCK_TYPE_PDCT), TYPE(PERIPHC_HDMI, CLOCK_TYPE_PDCT),
TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE),
TYPE(PERIPHC_TVDAC, CLOCK_TYPE_PDCT), TYPE(PERIPHC_TVDAC, CLOCK_TYPE_PDCT),
TYPE(PERIPHC_I2C2, CLOCK_TYPE_PCMT), TYPE(PERIPHC_I2C2, CLOCK_TYPE_PCMT16),
TYPE(PERIPHC_EMC, CLOCK_TYPE_MCPT), TYPE(PERIPHC_EMC, CLOCK_TYPE_MCPT),
/* 0x28 */ /* 0x28 */
...@@ -257,7 +259,7 @@ static enum clock_type_id clock_periph_type[PERIPHC_COUNT] = { ...@@ -257,7 +259,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_NONE, CLOCK_TYPE_NONE), TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE),
TYPE(PERIPHC_SPI4, CLOCK_TYPE_PCMT), TYPE(PERIPHC_SPI4, CLOCK_TYPE_PCMT),
TYPE(PERIPHC_I2C3, CLOCK_TYPE_PCMT), TYPE(PERIPHC_I2C3, CLOCK_TYPE_PCMT16),
TYPE(PERIPHC_SDMMC3, CLOCK_TYPE_PCMT), TYPE(PERIPHC_SDMMC3, CLOCK_TYPE_PCMT),
/* 0x30 */ /* 0x30 */
...@@ -519,14 +521,16 @@ void clock_ll_set_source(enum periph_id periph_id, unsigned source) ...@@ -519,14 +521,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 * 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. * 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 parent_rate clock rate of parent clock in Hz
* @param rate required clock rate for this clock * @param rate required clock rate for this clock
* @return divider which should be used * @return divider which should be used
*/ */
static int clk_div7_1_get_divider(unsigned long parent_rate, static int clk_get_divider(unsigned divider_bits, unsigned long parent_rate,
unsigned long rate) unsigned long rate)
{ {
u64 divider = parent_rate * 2; u64 divider = parent_rate * 2;
unsigned max_divider = 1 << divider_bits;
divider += rate - 1; divider += rate - 1;
do_div(divider, rate); do_div(divider, rate);
...@@ -534,7 +538,7 @@ static int clk_div7_1_get_divider(unsigned long parent_rate, ...@@ -534,7 +538,7 @@ static int clk_div7_1_get_divider(unsigned long parent_rate,
if ((s64)divider - 2 < 0) if ((s64)divider - 2 < 0)
return 0; return 0;
if ((s64)divider - 2 > 255) if ((s64)divider - 2 >= max_divider)
return -1; return -1;
return divider - 2; return divider - 2;
...@@ -572,6 +576,7 @@ unsigned long clock_get_periph_rate(enum periph_id periph_id, ...@@ -572,6 +576,7 @@ unsigned long clock_get_periph_rate(enum periph_id periph_id,
* required child clock rate. This function assumes that a second-stage * 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. * 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 parent_rate clock rate of parent clock in Hz
* @param rate required clock rate for this clock * @param rate required clock rate for this clock
* @param extra_div value for the second-stage divisor (not set if this * @param extra_div value for the second-stage divisor (not set if this
...@@ -579,8 +584,8 @@ unsigned long clock_get_periph_rate(enum periph_id periph_id, ...@@ -579,8 +584,8 @@ unsigned long clock_get_periph_rate(enum periph_id periph_id,
* @return divider which should be used, or -1 if nothing is valid * @return divider which should be used, or -1 if nothing is valid
* *
*/ */
static int find_best_divider(unsigned long parent_rate, unsigned long rate, static int find_best_divider(unsigned divider_bits, unsigned long parent_rate,
int *extra_div) unsigned long rate, int *extra_div)
{ {
int shift; int shift;
int best_divider = -1; int best_divider = -1;
...@@ -589,7 +594,8 @@ static int find_best_divider(unsigned long parent_rate, unsigned long rate, ...@@ -589,7 +594,8 @@ static int find_best_divider(unsigned long parent_rate, unsigned long rate,
/* try dividers from 1 to 256 and find closest match */ /* try dividers from 1 to 256 and find closest match */
for (shift = 0; shift <= 8 && best_error > 0; shift++) { for (shift = 0; shift <= 8 && best_error > 0; shift++) {
unsigned divided_parent = parent_rate >> 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,
rate);
unsigned effective_rate = get_rate_from_divider(divided_parent, unsigned effective_rate = get_rate_from_divider(divided_parent,
divider); divider);
int error = rate - effective_rate; int error = rate - effective_rate;
...@@ -615,10 +621,11 @@ static int find_best_divider(unsigned long parent_rate, unsigned long rate, ...@@ -615,10 +621,11 @@ static int find_best_divider(unsigned long parent_rate, unsigned long rate,
* @param periph_id peripheral to start * @param periph_id peripheral to start
* @param source PLL id of required parent clock * @param source PLL id of required parent clock
* @param mux_bits Set to number of bits in mux register: 2 or 4 * @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) * @return mux value (0-4, or -1 if not found)
*/ */
static int get_periph_clock_source(enum periph_id periph_id, 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 clock_type_id type;
enum periphc_internal_id internal_id; enum periphc_internal_id internal_id;
...@@ -632,11 +639,18 @@ static int get_periph_clock_source(enum periph_id periph_id, ...@@ -632,11 +639,18 @@ static int get_periph_clock_source(enum periph_id periph_id,
type = clock_periph_type[internal_id]; type = clock_periph_type[internal_id];
assert(clock_type_id_isvalid(type)); 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) if (type == CLOCK_TYPE_PCXTS)
*mux_bits = 4; *mux_bits = 4;
else else
*mux_bits = 2; *mux_bits = 2;
if (type == CLOCK_TYPE_PCMT16)
*divider_bits = 16;
else
*divider_bits = 8;
for (mux = 0; mux < CLOCK_MAX_MUX; mux++) for (mux = 0; mux < CLOCK_MAX_MUX; mux++)
if (clock_source[type][mux] == parent) if (clock_source[type][mux] == parent)
...@@ -662,24 +676,22 @@ static int get_periph_clock_source(enum periph_id periph_id, ...@@ -662,24 +676,22 @@ static int get_periph_clock_source(enum periph_id periph_id,
* Adjust peripheral PLL to use the given divider and source. * Adjust peripheral PLL to use the given divider and source.
* *
* @param periph_id peripheral to adjust * @param periph_id peripheral to adjust
* @param parent Required parent clock (for source mux) * @param source Source number (0-3 or 0-7)
* @param divider Required divider in 7.1 format * @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 * @return 0 if ok, -1 on error (requesting a parent clock which is not valid
* for this peripheral) * for this peripheral)
*/ */
static int adjust_periph_pll(enum periph_id periph_id, static int adjust_periph_pll(enum periph_id periph_id, int source,
enum clock_id parent, unsigned divider) int mux_bits, unsigned divider)
{ {
u32 *reg = get_periph_source_reg(periph_id); u32 *reg = get_periph_source_reg(periph_id);
unsigned source;
int mux_bits;
clrsetbits_le32(reg, OUT_CLK_DIVISOR_MASK, clrsetbits_le32(reg, OUT_CLK_DIVISOR_MASK,
divider << OUT_CLK_DIVISOR_SHIFT); divider << OUT_CLK_DIVISOR_SHIFT);
udelay(1); udelay(1);
/* work out the source clock and set it */ /* work out the source clock and set it */
source = get_periph_clock_source(periph_id, parent, &mux_bits);
if (source < 0) if (source < 0)
return -1; return -1;
if (mux_bits == 4) { if (mux_bits == 4) {
...@@ -697,14 +709,21 @@ unsigned clock_adjust_periph_pll_div(enum periph_id periph_id, ...@@ -697,14 +709,21 @@ unsigned clock_adjust_periph_pll_div(enum periph_id periph_id,
enum clock_id parent, unsigned rate, int *extra_div) enum clock_id parent, unsigned rate, int *extra_div)
{ {
unsigned effective_rate; unsigned effective_rate;
int mux_bits, divider_bits, source;
int divider; int divider;
/* work out the source clock and set it */
source = get_periph_clock_source(periph_id, parent, &mux_bits,
&divider_bits);
if (extra_div) if (extra_div)
divider = find_best_divider(pll_rate[parent], rate, extra_div); divider = find_best_divider(divider_bits, pll_rate[parent],
rate, extra_div);
else else
divider = clk_div7_1_get_divider(pll_rate[parent], rate); divider = clk_get_divider(divider_bits, pll_rate[parent],
rate);
assert(divider >= 0); assert(divider >= 0);
if (adjust_periph_pll(periph_id, parent, divider)) if (adjust_periph_pll(periph_id, source, mux_bits, divider))
return -1U; return -1U;
debug("periph %d, rate=%d, reg=%p = %x\n", periph_id, rate, debug("periph %d, rate=%d, reg=%p = %x\n", periph_id, rate,
get_periph_source_reg(periph_id), get_periph_source_reg(periph_id),
......
...@@ -125,9 +125,15 @@ struct clk_rst_ctlr { ...@@ -125,9 +125,15 @@ struct clk_rst_ctlr {
#define OSC_FREQ_SHIFT 30 #define OSC_FREQ_SHIFT 30
#define OSC_FREQ_MASK (3U << OSC_FREQ_SHIFT) #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_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_SHIFT 30
#define OUT_CLK_SOURCE_MASK (3U << OUT_CLK_SOURCE_SHIFT) #define OUT_CLK_SOURCE_MASK (3U << OUT_CLK_SOURCE_SHIFT)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment