summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-at91/at91_pmc_ll.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-at91/at91_pmc_ll.c')
-rw-r--r--arch/arm/mach-at91/at91_pmc_ll.c184
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;
+}