diff options
Diffstat (limited to 'arch/arm/cpu/armv7/tegra2/clock.c')
| -rw-r--r-- | arch/arm/cpu/armv7/tegra2/clock.c | 121 | 
1 files changed, 99 insertions, 22 deletions
| diff --git a/arch/arm/cpu/armv7/tegra2/clock.c b/arch/arm/cpu/armv7/tegra2/clock.c index 11d2346d8..39376ab86 100644 --- a/arch/arm/cpu/armv7/tegra2/clock.c +++ b/arch/arm/cpu/armv7/tegra2/clock.c @@ -28,6 +28,7 @@  #include <asm/arch/tegra2.h>  #include <common.h>  #include <div64.h> +#include <fdtdec.h>  /*   * This is our record of the current clock rate of each clock. We don't @@ -67,6 +68,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 +100,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 +214,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 +249,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 +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_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 +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   * 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 +538,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 +576,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 +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   *   */ -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 +594,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, +					      rate);  		unsigned effective_rate = get_rate_from_divider(divided_parent,  						       divider);  		int error = rate - effective_rate; @@ -614,10 +621,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 +639,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 +676,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 +709,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], +					    rate, extra_div);  	else -		divider = clk_div7_1_get_divider(pll_rate[parent], rate); +		divider = clk_get_divider(divider_bits, pll_rate[parent], +					  rate);  	assert(divider >= 0); -	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), @@ -918,6 +938,63 @@ void clock_ll_start_uart(enum periph_id periph_id)  	reset_set_enable(periph_id, 0);  } +#ifdef CONFIG_OF_CONTROL +/* + * Convert a device tree clock ID to our peripheral ID. They are mostly + * the same but we are very cautious so we check that a valid clock ID is + * provided. + * + * @param clk_id	Clock ID according to tegra2 device tree binding + * @return peripheral ID, or PERIPH_ID_NONE if the clock ID is invalid + */ +static enum periph_id clk_id_to_periph_id(int clk_id) +{ +	if (clk_id > 95) +		return PERIPH_ID_NONE; + +	switch (clk_id) { +	case 1: +	case 2: +	case 7: +	case 10: +	case 20: +	case 30: +	case 35: +	case 49: +	case 56: +	case 74: +	case 76: +	case 77: +	case 78: +	case 79: +	case 80: +	case 81: +	case 82: +	case 83: +	case 91: +	case 95: +		return PERIPH_ID_NONE; +	default: +		return clk_id; +	} +} + +int clock_decode_periph_id(const void *blob, int node) +{ +	enum periph_id id; +	u32 cell[2]; +	int err; + +	err = fdtdec_get_int_array(blob, node, "clocks", cell, +				   ARRAY_SIZE(cell)); +	if (err) +		return -1; +	id = clk_id_to_periph_id(cell[1]); +	assert(clock_periph_id_isvalid(id)); +	return id; +} +#endif /* CONFIG_OF_CONTROL */ +  int clock_verify(void)  {  	struct clk_pll *pll = get_pll(CLOCK_ID_PERIPH); |