diff options
Diffstat (limited to 'arch/arm/mach-at91/at91_pmc_ll.c')
-rw-r--r-- | arch/arm/mach-at91/at91_pmc_ll.c | 184 |
1 files changed, 172 insertions, 12 deletions
diff --git a/arch/arm/mach-at91/at91_pmc_ll.c b/arch/arm/mach-at91/at91_pmc_ll.c index 4d39f57909..0101623c8e 100644 --- a/arch/arm/mach-at91/at91_pmc_ll.c +++ b/arch/arm/mach-at91/at91_pmc_ll.c @@ -1,17 +1,51 @@ // SPDX-License-Identifier: BSD-1-Clause /* * Copyright (c) 2006, Atmel Corporation + * Copyright (C) 2019 Microchip Technology Inc. and its subsidiaries * - * Atmel's name may not be used to endorse or promote products + * Atmel/Microchip's name may not be used to endorse or promote products * derived from this software without specific prior written permission. */ +#define pr_fmt(fmt) "at91pmc: " fmt + #include <common.h> -#include <mach/at91_pmc_ll.h> +#include <mach/at91/hardware.h> +#include <mach/at91/at91_pmc.h> +#include <mach/at91/at91_pmc_ll.h> +#include <mach/at91/early_udelay.h> + +#define SFR_UTMICKTRIM 0x30 /* UTMI Clock Trimming Register */ +#define AT91_UTMICKTRIM_FREQ 0x03 + +#define PMC_GCSR0 0xC0 /* PMCv2 Generic Clock Status Register 0 */ +#define PMC_GCSR1 0xC4 /* PMCv2 Generic Clock Status Register 1 */ #define at91_pmc_write(off, val) writel(val, pmc_base + off) #define at91_pmc_read(off) readl(pmc_base + off) +#define MHZ (1000 * 1000UL) + +static unsigned long at91_pmc_get_main_xtal(void __iomem *pmc_base) +{ + u32 tmp; + + /* Enable a measurement of the Main Crystal Oscillator */ + tmp = at91_pmc_read(AT91_CKGR_MCFR); + tmp |= AT91_PMC_CCSS_XTAL_OSC; + tmp |= AT91_PMC_RCMEAS; + at91_pmc_write(AT91_CKGR_MCFR, tmp); + + do { + tmp = at91_pmc_read(AT91_CKGR_MCFR); + } while (!(tmp & AT91_PMC_MAINRDY)); + + /* read once more like the datasheet says */ + tmp = at91_pmc_read(AT91_CKGR_MCFR) & AT91_PMC_MAINF; + + return tmp * (AT91_SLOW_CLOCK / 16); +} + void at91_pmc_init(void __iomem *pmc_base, unsigned int flags) { u32 tmp; @@ -46,22 +80,16 @@ void at91_pmc_init(void __iomem *pmc_base, unsigned int flags) while (!(at91_pmc_read(AT91_PMC_SR) & AT91_PMC_MOSCS)) ; - if (flags & AT91_PMC_LL_FLAG_MEASURE_XTAL) { - /* Enable a measurement of the Main Crystal Oscillator */ - tmp = at91_pmc_read(AT91_CKGR_MCFR); - tmp |= AT91_PMC_CCSS_XTAL_OSC; - tmp |= AT91_PMC_RCMEAS; - at91_pmc_write(AT91_CKGR_MCFR, tmp); - - while (!(at91_pmc_read(AT91_CKGR_MCFR) & AT91_PMC_MAINRDY)) - ; - } + if (flags & AT91_PMC_LL_FLAG_MEASURE_XTAL) + (void)at91_pmc_get_main_xtal(pmc_base); /* Switch from internal 12MHz RC to the Main Crystal Oscillator */ tmp = at91_pmc_read(AT91_CKGR_MOR); tmp &= ~AT91_PMC_OSCBYPASS; tmp &= ~AT91_PMC_KEY_MASK; tmp |= AT91_PMC_KEY; + if (flags & AT91_PMC_LL_FLAG_MCK_BYPASS) + tmp |= AT91_PMC_OSCBYPASS; at91_pmc_write(AT91_CKGR_MOR, tmp); tmp = at91_pmc_read(AT91_CKGR_MOR); @@ -129,6 +157,17 @@ void at91_pmc_cfg_plla(void __iomem *pmc_base, u32 pmc_pllar, ; } +void at91_pmc_cfg_pllb(void __iomem *pmc_base, u32 pmc_pllbr, + unsigned int __always_unused flags) +{ + /* Always disable PLL before configuring it */ + at91_pmc_write(AT91_CKGR_PLLBR, 0); + at91_pmc_write(AT91_CKGR_PLLBR, pmc_pllbr); + + while (!(at91_pmc_read(AT91_PMC_SR) & AT91_PMC_LOCKB)) + ; +} + void at91_pmc_cfg_mck(void __iomem *pmc_base, u32 pmc_mckr, unsigned int flags) { u32 tmp; @@ -184,3 +223,124 @@ void at91_pmc_cfg_mck(void __iomem *pmc_base, u32 pmc_mckr, unsigned int flags) while (!(at91_pmc_read(AT91_PMC_SR) & AT91_PMC_MCKRDY)) ; } + +static void pmc_configure_utmi_ref_clk(void __iomem *pmc_base, + void __iomem *sfr_base, + unsigned long main_xtal) +{ + unsigned int utmi_ref_clk_freq = 0, tmp; + + /* + * If mainck rate is different from 12 MHz, we have to configure + * the FREQ field of the SFR_UTMICKTRIM register to generate properly + * the utmi clock. + */ + if (main_xtal < (16 + 4) * MHZ) + utmi_ref_clk_freq++; + if (main_xtal < (24 + 10) * MHZ) + utmi_ref_clk_freq++; + if (main_xtal < (48 + 10) * MHZ) + utmi_ref_clk_freq++; + + /* + * Not supported on SAMA5D2 but it's not an issue since MAINCK + * maximum value is 24 MHz. + */ + tmp = readl(sfr_base + SFR_UTMICKTRIM); + tmp &= ~AT91_UTMICKTRIM_FREQ; + tmp |= utmi_ref_clk_freq; + writel(tmp, sfr_base + SFR_UTMICKTRIM); +} + +static void pmc_uckr_clk(void __iomem *pmc_base, + void __iomem *sfr_base, + unsigned long main_xtal) +{ + unsigned int uckr = at91_pmc_read(AT91_CKGR_UCKR); + unsigned int sr; + + if (main_xtal) { + pmc_configure_utmi_ref_clk(pmc_base, sfr_base, + main_xtal); + uckr |= (AT91_PMC_UPLLCOUNT_DEFAULT | + AT91_PMC_UPLLEN | AT91_PMC_BIASEN); + sr = AT91_PMC_LOCKU; + } else { + uckr &= ~(AT91_PMC_UPLLEN | AT91_PMC_BIASEN); + sr = 0; + } + + at91_pmc_write(AT91_CKGR_UCKR, uckr); + + do { + early_udelay(1); + } while ((at91_pmc_read(AT91_PMC_SR) & AT91_PMC_LOCKU) != sr); +} + +static inline unsigned gck_status(unsigned periph_id, + unsigned flags) +{ + if (flags & AT91_PMC_LL_FLAG_GCSR) + return periph_id < 32 ? PMC_GCSR0 : PMC_GCSR1; + + return AT91_PMC_SR; +} + +static inline unsigned gck_ready(unsigned status, + unsigned periph_id, + unsigned flags) +{ + unsigned mask; + + if (flags & AT91_PMC_LL_FLAG_GCSR) + mask = 1 << (periph_id & 0x1f); + else + mask = AT91_PMC_GCKRDY; + + return status & mask; +} + +int at91_pmc_enable_generic_clock(void __iomem *pmc_base, + void __iomem *sfr_base, + unsigned int periph_id, + unsigned int clk_source, unsigned int div, + unsigned int flags) +{ + unsigned long main_xtal; + unsigned int regval, status; + unsigned int timeout = 1000; + + if (periph_id > 0x7f) + return -EINVAL; + + if (div > 0xff) + return -EINVAL; + + main_xtal = at91_pmc_get_main_xtal(pmc_base); + + if ((flags & AT91_PMC_LL_FLAG_PMC_UTMI) && + !(at91_pmc_read(AT91_PMC_SR) & AT91_PMC_LOCKU)) + pmc_uckr_clk(pmc_base, sfr_base, main_xtal); + + at91_pmc_write(AT91_PMC_PCR, periph_id); + regval = at91_pmc_read(AT91_PMC_PCR); + regval &= ~AT91_PMC_GCKCSS; + regval &= ~AT91_PMC_GCKDIV; + + regval |= clk_source; + regval |= AT91_PMC_PCR_CMD | AT91_PMC_GCKDIV_(div) | AT91_PMC_GCK_EN; + + at91_pmc_write(AT91_PMC_PCR, regval); + + for (timeout = 1000; timeout; timeout--) { + early_udelay(1); + + status = at91_pmc_read(gck_status(periph_id, flags)); + if (gck_ready(status, periph_id, flags)) + return 0; + } + + pr_warn("Timeout waiting for GCK ready!\n"); + + return 0; +} |