|
|
@@ -1,21 +1,7 @@
|
|
|
/*
|
|
|
- * File : clock.c
|
|
|
- * This file is part of RT-Thread RTOS
|
|
|
- * COPYRIGHT (C) 2006, RT-Thread Development Team
|
|
|
+ * Copyright (c) 2006-2021, RT-Thread Development Team
|
|
|
*
|
|
|
- * This program is free software; you can redistribute it and/or modify
|
|
|
- * it under the terms of the GNU General Public License as published by
|
|
|
- * the Free Software Foundation; either version 2 of the License, or
|
|
|
- * (at your option) any later version.
|
|
|
- *
|
|
|
- * This program is distributed in the hope that it will be useful,
|
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
- * GNU General Public License for more details.
|
|
|
- *
|
|
|
- * You should have received a copy of the GNU General Public License along
|
|
|
- * with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
+ * SPDX-License-Identifier: Apache-2.0
|
|
|
*
|
|
|
* Change Logs:
|
|
|
* Date Author Notes
|
|
|
@@ -28,269 +14,269 @@
|
|
|
static rt_list_t clocks;
|
|
|
|
|
|
struct clk {
|
|
|
- char name[32];
|
|
|
- rt_uint32_t rate_hz;
|
|
|
- struct clk *parent;
|
|
|
- rt_list_t node;
|
|
|
+ char name[32];
|
|
|
+ rt_uint32_t rate_hz;
|
|
|
+ struct clk *parent;
|
|
|
+ rt_list_t node;
|
|
|
};
|
|
|
|
|
|
static struct clk clk32k = {
|
|
|
- "clk32k",
|
|
|
- AT91_SLOW_CLOCK,
|
|
|
- RT_NULL,
|
|
|
- {RT_NULL, RT_NULL},
|
|
|
+ "clk32k",
|
|
|
+ AT91_SLOW_CLOCK,
|
|
|
+ RT_NULL,
|
|
|
+ {RT_NULL, RT_NULL},
|
|
|
};
|
|
|
|
|
|
static struct clk main_clk = {
|
|
|
- "main",
|
|
|
- 0,
|
|
|
- RT_NULL,
|
|
|
- {RT_NULL, RT_NULL},
|
|
|
+ "main",
|
|
|
+ 0,
|
|
|
+ RT_NULL,
|
|
|
+ {RT_NULL, RT_NULL},
|
|
|
};
|
|
|
|
|
|
static struct clk plla = {
|
|
|
- "plla",
|
|
|
- 0,
|
|
|
- RT_NULL,
|
|
|
- {RT_NULL, RT_NULL},
|
|
|
+ "plla",
|
|
|
+ 0,
|
|
|
+ RT_NULL,
|
|
|
+ {RT_NULL, RT_NULL},
|
|
|
};
|
|
|
|
|
|
static struct clk mck = {
|
|
|
- "mck",
|
|
|
- 0,
|
|
|
- RT_NULL,
|
|
|
- {RT_NULL, RT_NULL},
|
|
|
+ "mck",
|
|
|
+ 0,
|
|
|
+ RT_NULL,
|
|
|
+ {RT_NULL, RT_NULL},
|
|
|
};
|
|
|
|
|
|
static struct clk uhpck = {
|
|
|
- "uhpck",
|
|
|
- 0,
|
|
|
- RT_NULL,
|
|
|
- {RT_NULL, RT_NULL},
|
|
|
+ "uhpck",
|
|
|
+ 0,
|
|
|
+ RT_NULL,
|
|
|
+ {RT_NULL, RT_NULL},
|
|
|
};
|
|
|
|
|
|
static struct clk pllb = {
|
|
|
- "pllb",
|
|
|
- 0,
|
|
|
- &main_clk,
|
|
|
- {RT_NULL, RT_NULL},
|
|
|
+ "pllb",
|
|
|
+ 0,
|
|
|
+ &main_clk,
|
|
|
+ {RT_NULL, RT_NULL},
|
|
|
};
|
|
|
|
|
|
static struct clk udpck = {
|
|
|
- "udpck",
|
|
|
- 0,
|
|
|
- &pllb,
|
|
|
- {RT_NULL, RT_NULL},
|
|
|
+ "udpck",
|
|
|
+ 0,
|
|
|
+ &pllb,
|
|
|
+ {RT_NULL, RT_NULL},
|
|
|
};
|
|
|
|
|
|
static struct clk *const standard_pmc_clocks[] = {
|
|
|
- // four primary clocks
|
|
|
- &clk32k,
|
|
|
- &main_clk,
|
|
|
- &plla,
|
|
|
+ // four primary clocks
|
|
|
+ &clk32k,
|
|
|
+ &main_clk,
|
|
|
+ &plla,
|
|
|
|
|
|
- // MCK
|
|
|
- &mck
|
|
|
+ // MCK
|
|
|
+ &mck
|
|
|
};
|
|
|
|
|
|
// clocks cannot be de-registered no refcounting necessary
|
|
|
struct clk *clk_get(const char *id)
|
|
|
{
|
|
|
- struct clk *clk;
|
|
|
- rt_list_t *list;
|
|
|
-
|
|
|
- for (list = (&clocks)->next; list != &clocks; list = list->next)
|
|
|
- {
|
|
|
- clk = (struct clk *)rt_list_entry(list, struct clk, node);
|
|
|
- if (rt_strcmp(id, clk->name) == 0)
|
|
|
- return clk;
|
|
|
- }
|
|
|
-
|
|
|
- return RT_NULL;
|
|
|
+ struct clk *clk;
|
|
|
+ rt_list_t *list;
|
|
|
+
|
|
|
+ for (list = (&clocks)->next; list != &clocks; list = list->next)
|
|
|
+ {
|
|
|
+ clk = (struct clk *)rt_list_entry(list, struct clk, node);
|
|
|
+ if (rt_strcmp(id, clk->name) == 0)
|
|
|
+ return clk;
|
|
|
+ }
|
|
|
+
|
|
|
+ return RT_NULL;
|
|
|
}
|
|
|
|
|
|
rt_uint32_t clk_get_rate(struct clk *clk)
|
|
|
{
|
|
|
- rt_uint32_t rate;
|
|
|
-
|
|
|
- for (;;) {
|
|
|
- rate = clk->rate_hz;
|
|
|
- if (rate || !clk->parent)
|
|
|
- break;
|
|
|
- clk = clk->parent;
|
|
|
- }
|
|
|
- return rate;
|
|
|
+ rt_uint32_t rate;
|
|
|
+
|
|
|
+ for (;;) {
|
|
|
+ rate = clk->rate_hz;
|
|
|
+ if (rate || !clk->parent)
|
|
|
+ break;
|
|
|
+ clk = clk->parent;
|
|
|
+ }
|
|
|
+ return rate;
|
|
|
}
|
|
|
|
|
|
static rt_uint32_t at91_pll_rate(struct clk *pll, rt_uint32_t freq, rt_uint32_t reg)
|
|
|
{
|
|
|
- unsigned mul, div;
|
|
|
+ unsigned mul, div;
|
|
|
|
|
|
- div = reg & 0xff;
|
|
|
- mul = (reg >> 16) & 0x7ff;
|
|
|
- if (div && mul) {
|
|
|
- freq /= div;
|
|
|
- freq *= mul + 1;
|
|
|
- } else
|
|
|
- freq = 0;
|
|
|
+ div = reg & 0xff;
|
|
|
+ mul = (reg >> 16) & 0x7ff;
|
|
|
+ if (div && mul) {
|
|
|
+ freq /= div;
|
|
|
+ freq *= mul + 1;
|
|
|
+ } else
|
|
|
+ freq = 0;
|
|
|
|
|
|
- return freq;
|
|
|
+ return freq;
|
|
|
}
|
|
|
|
|
|
static unsigned at91_pll_calc(unsigned main_freq, unsigned out_freq)
|
|
|
{
|
|
|
- unsigned i, div = 0, mul = 0, diff = 1 << 30;
|
|
|
- unsigned ret = (out_freq > 155000000) ? 0xbe00 : 0x3e00;
|
|
|
-
|
|
|
- //PLL output max 240 MHz (or 180 MHz per errata)
|
|
|
- if (out_freq > 240000000)
|
|
|
- goto fail;
|
|
|
-
|
|
|
- for (i = 1; i < 256; i++) {
|
|
|
- int diff1;
|
|
|
- unsigned input, mul1;
|
|
|
-
|
|
|
- //
|
|
|
- // PLL input between 1MHz and 32MHz per spec, but lower
|
|
|
- // frequences seem necessary in some cases so allow 100K.
|
|
|
- // Warning: some newer products need 2MHz min.
|
|
|
- //
|
|
|
- input = main_freq / i;
|
|
|
- if (input < 100000)
|
|
|
- continue;
|
|
|
- if (input > 32000000)
|
|
|
- continue;
|
|
|
-
|
|
|
- mul1 = out_freq / input;
|
|
|
- if (mul1 > 2048)
|
|
|
- continue;
|
|
|
- if (mul1 < 2)
|
|
|
- goto fail;
|
|
|
-
|
|
|
- diff1 = out_freq - input * mul1;
|
|
|
- if (diff1 < 0)
|
|
|
- diff1 = -diff1;
|
|
|
- if (diff > diff1) {
|
|
|
- diff = diff1;
|
|
|
- div = i;
|
|
|
- mul = mul1;
|
|
|
- if (diff == 0)
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- if (i == 256 && diff > (out_freq >> 5))
|
|
|
- goto fail;
|
|
|
- return ret | ((mul - 1) << 16) | div;
|
|
|
+ unsigned i, div = 0, mul = 0, diff = 1 << 30;
|
|
|
+ unsigned ret = (out_freq > 155000000) ? 0xbe00 : 0x3e00;
|
|
|
+
|
|
|
+ //PLL output max 240 MHz (or 180 MHz per errata)
|
|
|
+ if (out_freq > 240000000)
|
|
|
+ goto fail;
|
|
|
+
|
|
|
+ for (i = 1; i < 256; i++) {
|
|
|
+ int diff1;
|
|
|
+ unsigned input, mul1;
|
|
|
+
|
|
|
+ //
|
|
|
+ // PLL input between 1MHz and 32MHz per spec, but lower
|
|
|
+ // frequences seem necessary in some cases so allow 100K.
|
|
|
+ // Warning: some newer products need 2MHz min.
|
|
|
+ //
|
|
|
+ input = main_freq / i;
|
|
|
+ if (input < 100000)
|
|
|
+ continue;
|
|
|
+ if (input > 32000000)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ mul1 = out_freq / input;
|
|
|
+ if (mul1 > 2048)
|
|
|
+ continue;
|
|
|
+ if (mul1 < 2)
|
|
|
+ goto fail;
|
|
|
+
|
|
|
+ diff1 = out_freq - input * mul1;
|
|
|
+ if (diff1 < 0)
|
|
|
+ diff1 = -diff1;
|
|
|
+ if (diff > diff1) {
|
|
|
+ diff = diff1;
|
|
|
+ div = i;
|
|
|
+ mul = mul1;
|
|
|
+ if (diff == 0)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (i == 256 && diff > (out_freq >> 5))
|
|
|
+ goto fail;
|
|
|
+ return ret | ((mul - 1) << 16) | div;
|
|
|
fail:
|
|
|
- return 0;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static rt_uint32_t at91_usb_rate(struct clk *pll, rt_uint32_t freq, rt_uint32_t reg)
|
|
|
{
|
|
|
- if (pll == &pllb && (reg & AT91_PMC_USB96M))
|
|
|
- return freq / 2;
|
|
|
- else
|
|
|
- return freq;
|
|
|
+ if (pll == &pllb && (reg & AT91_PMC_USB96M))
|
|
|
+ return freq / 2;
|
|
|
+ else
|
|
|
+ return freq;
|
|
|
}
|
|
|
|
|
|
|
|
|
// PLLB generated USB full speed clock init
|
|
|
static void at91_pllb_usbfs_clock_init(rt_uint32_t main_clock)
|
|
|
{
|
|
|
- rt_uint32_t at91_pllb_usb_init;
|
|
|
- //
|
|
|
- // USB clock init: choose 48 MHz PLLB value,
|
|
|
- // disable 48MHz clock during usb peripheral suspend.
|
|
|
- //
|
|
|
- // REVISIT: assumes MCK doesn't derive from PLLB!
|
|
|
- //
|
|
|
- uhpck.parent = &pllb;
|
|
|
-
|
|
|
- at91_pllb_usb_init = at91_pll_calc(main_clock, 48000000 * 2) | AT91_PMC_USB96M;
|
|
|
- pllb.rate_hz = at91_pll_rate(&pllb, main_clock, at91_pllb_usb_init);
|
|
|
-
|
|
|
- at91_sys_write(AT91_CKGR_PLLBR, 0);
|
|
|
-
|
|
|
- udpck.rate_hz = at91_usb_rate(&pllb, pllb.rate_hz, at91_pllb_usb_init);
|
|
|
- uhpck.rate_hz = at91_usb_rate(&pllb, pllb.rate_hz, at91_pllb_usb_init);
|
|
|
+ rt_uint32_t at91_pllb_usb_init;
|
|
|
+ //
|
|
|
+ // USB clock init: choose 48 MHz PLLB value,
|
|
|
+ // disable 48MHz clock during usb peripheral suspend.
|
|
|
+ //
|
|
|
+ // REVISIT: assumes MCK doesn't derive from PLLB!
|
|
|
+ //
|
|
|
+ uhpck.parent = &pllb;
|
|
|
+
|
|
|
+ at91_pllb_usb_init = at91_pll_calc(main_clock, 48000000 * 2) | AT91_PMC_USB96M;
|
|
|
+ pllb.rate_hz = at91_pll_rate(&pllb, main_clock, at91_pllb_usb_init);
|
|
|
+
|
|
|
+ at91_sys_write(AT91_CKGR_PLLBR, 0);
|
|
|
+
|
|
|
+ udpck.rate_hz = at91_usb_rate(&pllb, pllb.rate_hz, at91_pllb_usb_init);
|
|
|
+ uhpck.rate_hz = at91_usb_rate(&pllb, pllb.rate_hz, at91_pllb_usb_init);
|
|
|
}
|
|
|
|
|
|
static struct clk *at91_css_to_clk(unsigned long css)
|
|
|
{
|
|
|
- switch (css) {
|
|
|
- case AT91_PMC_CSS_SLOW:
|
|
|
- return &clk32k;
|
|
|
- case AT91_PMC_CSS_MAIN:
|
|
|
- return &main_clk;
|
|
|
- case AT91_PMC_CSS_PLLA:
|
|
|
- return &plla;
|
|
|
- case AT91_PMC_CSS_PLLB:
|
|
|
- return &pllb;
|
|
|
- }
|
|
|
-
|
|
|
- return RT_NULL;
|
|
|
+ switch (css) {
|
|
|
+ case AT91_PMC_CSS_SLOW:
|
|
|
+ return &clk32k;
|
|
|
+ case AT91_PMC_CSS_MAIN:
|
|
|
+ return &main_clk;
|
|
|
+ case AT91_PMC_CSS_PLLA:
|
|
|
+ return &plla;
|
|
|
+ case AT91_PMC_CSS_PLLB:
|
|
|
+ return &pllb;
|
|
|
+ }
|
|
|
+
|
|
|
+ return RT_NULL;
|
|
|
}
|
|
|
|
|
|
#define false 0
|
|
|
#define true 1
|
|
|
int at91_clock_init(rt_uint32_t main_clock)
|
|
|
{
|
|
|
- unsigned tmp, freq, mckr;
|
|
|
- int i;
|
|
|
- int pll_overclock = false;
|
|
|
-
|
|
|
- //
|
|
|
- // When the bootloader initialized the main oscillator correctly,
|
|
|
- // there's no problem using the cycle counter. But if it didn't,
|
|
|
- // or when using oscillator bypass mode, we must be told the speed
|
|
|
- // of the main clock.
|
|
|
- //
|
|
|
- if (!main_clock) {
|
|
|
- do {
|
|
|
- tmp = at91_sys_read(AT91_CKGR_MCFR);
|
|
|
- } while (!(tmp & AT91_PMC_MAINRDY));
|
|
|
- main_clock = (tmp & AT91_PMC_MAINF) * (AT91_SLOW_CLOCK / 16);
|
|
|
- }
|
|
|
- main_clk.rate_hz = main_clock;
|
|
|
-
|
|
|
- // report if PLLA is more than mildly overclocked
|
|
|
- plla.rate_hz = at91_pll_rate(&plla, main_clock, at91_sys_read(AT91_CKGR_PLLAR));
|
|
|
- if (plla.rate_hz > 209000000)
|
|
|
- pll_overclock = true;
|
|
|
- if (pll_overclock)
|
|
|
- ;//rt_kprintf("Clocks: PLLA overclocked, %ld MHz\n", plla.rate_hz / 1000000);
|
|
|
-
|
|
|
- at91_pllb_usbfs_clock_init(main_clock);
|
|
|
-
|
|
|
- //
|
|
|
- // MCK and CPU derive from one of those primary clocks.
|
|
|
- // For now, assume this parentage won't change.
|
|
|
- //
|
|
|
- mckr = at91_sys_read(AT91_PMC_MCKR);
|
|
|
- mck.parent = at91_css_to_clk(mckr & AT91_PMC_CSS);
|
|
|
- freq = mck.parent->rate_hz;
|
|
|
- freq /= (1 << ((mckr & AT91_PMC_PRES) >> 2)); // prescale
|
|
|
-
|
|
|
- mck.rate_hz = freq / (1 << ((mckr & AT91_PMC_MDIV) >> 8)); // mdiv
|
|
|
-
|
|
|
- // Register the PMC's standard clocks
|
|
|
- rt_list_init(&clocks);
|
|
|
- for (i = 0; i < ARRAY_SIZE(standard_pmc_clocks); i++)
|
|
|
- rt_list_insert_after(&clocks, &standard_pmc_clocks[i]->node);
|
|
|
-
|
|
|
- rt_list_insert_after(&clocks, &pllb.node);
|
|
|
- rt_list_insert_after(&clocks, &uhpck.node);
|
|
|
- rt_list_insert_after(&clocks, &udpck.node);
|
|
|
-
|
|
|
- // MCK and CPU clock are "always on"
|
|
|
- //clk_enable(&mck);
|
|
|
-
|
|
|
- //rt_kprintf("Clocks: CPU %u MHz, master %u MHz, main %u.%03u MHz\n",
|
|
|
- // freq / 1000000, (unsigned) mck.rate_hz / 1000000,
|
|
|
- // (unsigned) main_clock / 1000000,
|
|
|
- // ((unsigned) main_clock % 1000000) / 1000); //cause blocked
|
|
|
-
|
|
|
- return 0;
|
|
|
+ unsigned tmp, freq, mckr;
|
|
|
+ int i;
|
|
|
+ int pll_overclock = false;
|
|
|
+
|
|
|
+ //
|
|
|
+ // When the bootloader initialized the main oscillator correctly,
|
|
|
+ // there's no problem using the cycle counter. But if it didn't,
|
|
|
+ // or when using oscillator bypass mode, we must be told the speed
|
|
|
+ // of the main clock.
|
|
|
+ //
|
|
|
+ if (!main_clock) {
|
|
|
+ do {
|
|
|
+ tmp = at91_sys_read(AT91_CKGR_MCFR);
|
|
|
+ } while (!(tmp & AT91_PMC_MAINRDY));
|
|
|
+ main_clock = (tmp & AT91_PMC_MAINF) * (AT91_SLOW_CLOCK / 16);
|
|
|
+ }
|
|
|
+ main_clk.rate_hz = main_clock;
|
|
|
+
|
|
|
+ // report if PLLA is more than mildly overclocked
|
|
|
+ plla.rate_hz = at91_pll_rate(&plla, main_clock, at91_sys_read(AT91_CKGR_PLLAR));
|
|
|
+ if (plla.rate_hz > 209000000)
|
|
|
+ pll_overclock = true;
|
|
|
+ if (pll_overclock)
|
|
|
+ ;//rt_kprintf("Clocks: PLLA overclocked, %ld MHz\n", plla.rate_hz / 1000000);
|
|
|
+
|
|
|
+ at91_pllb_usbfs_clock_init(main_clock);
|
|
|
+
|
|
|
+ //
|
|
|
+ // MCK and CPU derive from one of those primary clocks.
|
|
|
+ // For now, assume this parentage won't change.
|
|
|
+ //
|
|
|
+ mckr = at91_sys_read(AT91_PMC_MCKR);
|
|
|
+ mck.parent = at91_css_to_clk(mckr & AT91_PMC_CSS);
|
|
|
+ freq = mck.parent->rate_hz;
|
|
|
+ freq /= (1 << ((mckr & AT91_PMC_PRES) >> 2)); // prescale
|
|
|
+
|
|
|
+ mck.rate_hz = freq / (1 << ((mckr & AT91_PMC_MDIV) >> 8)); // mdiv
|
|
|
+
|
|
|
+ // Register the PMC's standard clocks
|
|
|
+ rt_list_init(&clocks);
|
|
|
+ for (i = 0; i < ARRAY_SIZE(standard_pmc_clocks); i++)
|
|
|
+ rt_list_insert_after(&clocks, &standard_pmc_clocks[i]->node);
|
|
|
+
|
|
|
+ rt_list_insert_after(&clocks, &pllb.node);
|
|
|
+ rt_list_insert_after(&clocks, &uhpck.node);
|
|
|
+ rt_list_insert_after(&clocks, &udpck.node);
|
|
|
+
|
|
|
+ // MCK and CPU clock are "always on"
|
|
|
+ //clk_enable(&mck);
|
|
|
+
|
|
|
+ //rt_kprintf("Clocks: CPU %u MHz, master %u MHz, main %u.%03u MHz\n",
|
|
|
+ // freq / 1000000, (unsigned) mck.rate_hz / 1000000,
|
|
|
+ // (unsigned) main_clock / 1000000,
|
|
|
+ // ((unsigned) main_clock % 1000000) / 1000); //cause blocked
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
*/
|
|
|
|
|
|
@@ -298,6 +284,6 @@ int at91_clock_init(rt_uint32_t main_clock)
|
|
|
|
|
|
void rt_hw_clock_init(void)
|
|
|
{
|
|
|
- //at91_clock_init(18432000);
|
|
|
+ //at91_clock_init(18432000);
|
|
|
}
|
|
|
|