Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus
[linux-drm-fsl-dcu.git] / arch / mips / pic32 / pic32mzda / early_clk.c
diff --git a/arch/mips/pic32/pic32mzda/early_clk.c b/arch/mips/pic32/pic32mzda/early_clk.c
new file mode 100644 (file)
index 0000000..96c090e
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Joshua Henderson <joshua.henderson@microchip.com>
+ * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope 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.
+ */
+#include <asm/mach-pic32/pic32.h>
+
+#include "pic32mzda.h"
+
+/* Oscillators, PLL & clocks */
+#define ICLK_MASK      0x00000080
+#define PLLDIV_MASK    0x00000007
+#define CUROSC_MASK    0x00000007
+#define PLLMUL_MASK    0x0000007F
+#define PB_MASK                0x00000007
+#define FRC1           0
+#define FRC2           7
+#define SPLL           1
+#define POSC           2
+#define FRC_CLK                8000000
+
+#define PIC32_POSC_FREQ        24000000
+
+#define OSCCON         0x0000
+#define SPLLCON                0x0020
+#define PB1DIV         0x0140
+
+u32 pic32_get_sysclk(void)
+{
+       u32 osc_freq = 0;
+       u32 pllclk;
+       u32 frcdivn;
+       u32 osccon;
+       u32 spllcon;
+       int curr_osc;
+
+       u32 plliclk;
+       u32 pllidiv;
+       u32 pllodiv;
+       u32 pllmult;
+       u32 frcdiv;
+
+       void __iomem *osc_base = ioremap(PIC32_BASE_OSC, 0x200);
+
+       osccon = __raw_readl(osc_base + OSCCON);
+       spllcon = __raw_readl(osc_base + SPLLCON);
+
+       plliclk = (spllcon & ICLK_MASK);
+       pllidiv = ((spllcon >> 8) & PLLDIV_MASK) + 1;
+       pllodiv = ((spllcon >> 24) & PLLDIV_MASK);
+       pllmult = ((spllcon >> 16) & PLLMUL_MASK) + 1;
+       frcdiv = ((osccon >> 24) & PLLDIV_MASK);
+
+       pllclk = plliclk ? FRC_CLK : PIC32_POSC_FREQ;
+       frcdivn = ((1 << frcdiv) + 1) + (128 * (frcdiv == 7));
+
+       if (pllodiv < 2)
+               pllodiv = 2;
+       else if (pllodiv < 5)
+               pllodiv = (1 << pllodiv);
+       else
+               pllodiv = 32;
+
+       curr_osc = (int)((osccon >> 12) & CUROSC_MASK);
+
+       switch (curr_osc) {
+       case FRC1:
+       case FRC2:
+               osc_freq = FRC_CLK / frcdivn;
+               break;
+       case SPLL:
+               osc_freq = ((pllclk / pllidiv) * pllmult) / pllodiv;
+               break;
+       case POSC:
+               osc_freq = PIC32_POSC_FREQ;
+               break;
+       default:
+               break;
+       }
+
+       iounmap(osc_base);
+
+       return osc_freq;
+}
+
+u32 pic32_get_pbclk(int bus)
+{
+       u32 clk_freq;
+       void __iomem *osc_base = ioremap(PIC32_BASE_OSC, 0x200);
+       u32 pbxdiv = PB1DIV + ((bus - 1) * 0x10);
+       u32 pbdiv = (__raw_readl(osc_base + pbxdiv) & PB_MASK) + 1;
+
+       iounmap(osc_base);
+
+       clk_freq = pic32_get_sysclk();
+
+       return clk_freq / pbdiv;
+}