|
@@ -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] = {
|
|
{ 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] = {
|
|
|
|
|
|
/* 0x08 */
|
|
/* 0x08 */
|
|
TYPE(PERIPHC_XIO, CLOCK_TYPE_PCMT),
|
|
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_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] = {
|
|
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] = {
|
|
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)
|
|
* 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,
|
|
|
|
- unsigned long rate)
|
|
|
|
|
|
+static int clk_get_divider(unsigned divider_bits, unsigned long parent_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,
|
|
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,
|
|
* 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,
|
|
* @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,
|
|
|
|
- int *extra_div)
|
|
|
|
|
|
+static int find_best_divider(unsigned divider_bits, unsigned long parent_rate,
|
|
|
|
+ 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,
|
|
/* 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,
|
|
* @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,
|
|
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,
|
|
* 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 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
|
|
* @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,
|
|
|
|
- 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);
|
|
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,
|
|
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,
|
|
|
|
+ ÷r_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),
|