summaryrefslogtreecommitdiff
path: root/arch/arm/cpu/armv7
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/cpu/armv7')
-rw-r--r--arch/arm/cpu/armv7/sunxi/Makefile12
-rw-r--r--arch/arm/cpu/armv7/sunxi/clock.c25
-rw-r--r--arch/arm/cpu/armv7/sunxi/clock_sun4i.c188
-rw-r--r--arch/arm/cpu/armv7/sunxi/timer.c113
4 files changed, 338 insertions, 0 deletions
diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile
new file mode 100644
index 0000000..440d266
--- /dev/null
+++ b/arch/arm/cpu/armv7/sunxi/Makefile
@@ -0,0 +1,12 @@
+#
+# (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
+#
+# Based on some other Makefile
+# (C) Copyright 2000-2003
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+obj-y += timer.o
+obj-y += clock.o
+obj-$(CONFIG_SUN7I) += clock_sun4i.o
diff --git a/arch/arm/cpu/armv7/sunxi/clock.c b/arch/arm/cpu/armv7/sunxi/clock.c
new file mode 100644
index 0000000..47fb70f
--- /dev/null
+++ b/arch/arm/cpu/armv7/sunxi/clock.c
@@ -0,0 +1,25 @@
+/*
+ * (C) Copyright 2007-2012
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Tom Cubie <tangliang@allwinnertech.com>
+ *
+ * (C) Copyright 2013 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/sys_proto.h>
+
+int clock_init(void)
+{
+#ifdef CONFIG_SPL_BUILD
+ clock_init_safe();
+#endif
+ clock_init_uart();
+
+ return 0;
+}
diff --git a/arch/arm/cpu/armv7/sunxi/clock_sun4i.c b/arch/arm/cpu/armv7/sunxi/clock_sun4i.c
new file mode 100644
index 0000000..5a7da3c
--- /dev/null
+++ b/arch/arm/cpu/armv7/sunxi/clock_sun4i.c
@@ -0,0 +1,188 @@
+/*
+ * sun4i, sun5i and sun7i specific clock code
+ *
+ * (C) Copyright 2007-2012
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Tom Cubie <tangliang@allwinnertech.com>
+ *
+ * (C) Copyright 2013 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/sys_proto.h>
+
+#ifdef CONFIG_SPL_BUILD
+void clock_init_safe(void)
+{
+ struct sunxi_ccm_reg * const ccm =
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+
+ /* Set safe defaults until PMU is configured */
+ writel(AXI_DIV_1 << AXI_DIV_SHIFT |
+ AHB_DIV_2 << AHB_DIV_SHIFT |
+ APB0_DIV_1 << APB0_DIV_SHIFT |
+ CPU_CLK_SRC_OSC24M << CPU_CLK_SRC_SHIFT,
+ &ccm->cpu_ahb_apb0_cfg);
+ writel(PLL1_CFG_DEFAULT, &ccm->pll1_cfg);
+ sdelay(200);
+ writel(AXI_DIV_1 << AXI_DIV_SHIFT |
+ AHB_DIV_2 << AHB_DIV_SHIFT |
+ APB0_DIV_1 << APB0_DIV_SHIFT |
+ CPU_CLK_SRC_PLL1 << CPU_CLK_SRC_SHIFT,
+ &ccm->cpu_ahb_apb0_cfg);
+#ifdef CONFIG_SUN7I
+ writel(0x1 << AHB_GATE_OFFSET_DMA | readl(&ccm->ahb_gate0),
+ &ccm->ahb_gate0);
+#endif
+ writel(PLL6_CFG_DEFAULT, &ccm->pll6_cfg);
+}
+#endif
+
+void clock_init_uart(void)
+{
+ struct sunxi_ccm_reg *const ccm =
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+
+ /* uart clock source is apb1 */
+ writel(APB1_CLK_SRC_OSC24M|
+ APB1_CLK_RATE_N_1|
+ APB1_CLK_RATE_M(1),
+ &ccm->apb1_clk_div_cfg);
+
+ /* open the clock for uart */
+ setbits_le32(&ccm->apb1_gate,
+ CLK_GATE_OPEN << (APB1_GATE_UART_SHIFT+CONFIG_CONS_INDEX-1));
+}
+
+int clock_twi_onoff(int port, int state)
+{
+ struct sunxi_ccm_reg *const ccm =
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+
+ if (port > 2)
+ return -1;
+
+ /* set the apb clock gate for twi */
+ if (state)
+ setbits_le32(&ccm->apb1_gate,
+ CLK_GATE_OPEN << (APB1_GATE_TWI_SHIFT+port));
+ else
+ clrbits_le32(&ccm->apb1_gate,
+ CLK_GATE_OPEN << (APB1_GATE_TWI_SHIFT+port));
+
+ return 0;
+}
+
+#ifdef CONFIG_SPL_BUILD
+#define PLL1_CFG(N, K, M, P) ( 1 << CCM_PLL1_CFG_ENABLE_SHIFT | \
+ 0 << CCM_PLL1_CFG_VCO_RST_SHIFT | \
+ 8 << CCM_PLL1_CFG_VCO_BIAS_SHIFT | \
+ 0 << CCM_PLL1_CFG_PLL4_EXCH_SHIFT | \
+ 16 << CCM_PLL1_CFG_BIAS_CUR_SHIFT | \
+ (P)<< CCM_PLL1_CFG_DIVP_SHIFT | \
+ 2 << CCM_PLL1_CFG_LCK_TMR_SHIFT | \
+ (N)<< CCM_PLL1_CFG_FACTOR_N_SHIFT | \
+ (K)<< CCM_PLL1_CFG_FACTOR_K_SHIFT | \
+ 0 << CCM_PLL1_CFG_SIG_DELT_PAT_IN_SHIFT | \
+ 0 << CCM_PLL1_CFG_SIG_DELT_PAT_EN_SHIFT | \
+ (M)<< CCM_PLL1_CFG_FACTOR_M_SHIFT)
+
+static struct {
+ u32 pll1_cfg;
+ unsigned int freq;
+} pll1_para[] = {
+ /* This array must be ordered by frequency. */
+ { PLL1_CFG(16, 0, 0, 0), 384000000 },
+ { PLL1_CFG(16, 1, 0, 0), 768000000 },
+ { PLL1_CFG(20, 1, 0, 0), 960000000 },
+ { PLL1_CFG(21, 1, 0, 0), 1008000000},
+ { PLL1_CFG(22, 1, 0, 0), 1056000000},
+ { PLL1_CFG(23, 1, 0, 0), 1104000000},
+ { PLL1_CFG(24, 1, 0, 0), 1152000000},
+ { PLL1_CFG(25, 1, 0, 0), 1200000000},
+ { PLL1_CFG(26, 1, 0, 0), 1248000000},
+ { PLL1_CFG(27, 1, 0, 0), 1296000000},
+ { PLL1_CFG(28, 1, 0, 0), 1344000000},
+ { PLL1_CFG(29, 1, 0, 0), 1392000000},
+ { PLL1_CFG(30, 1, 0, 0), 1440000000},
+ { PLL1_CFG(31, 1, 0, 0), 1488000000},
+ /* Final catchall entry */
+ { PLL1_CFG(31, 1, 0, 0), ~0},
+};
+
+void clock_set_pll1(unsigned int hz)
+{
+ int i = 0;
+ int axi, ahb, apb0;
+ struct sunxi_ccm_reg * const ccm =
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+
+ /* Find target frequency */
+ while (pll1_para[i].freq < hz)
+ i++;
+
+ hz = pll1_para[i].freq;
+
+ /* Calculate system clock divisors */
+ axi = DIV_ROUND_UP(hz, 432000000); /* Max 450MHz */
+ ahb = DIV_ROUND_UP(hz/axi, 204000000); /* Max 250MHz */
+ apb0 = 2; /* Max 150MHz */
+
+ printf("CPU: %uHz, AXI/AHB/APB: %d/%d/%d\n", hz, axi, ahb, apb0);
+
+ /* Map divisors to register values */
+ axi = axi - 1;
+ if (ahb > 4)
+ ahb = 3;
+ else if (ahb > 2)
+ ahb = 2;
+ else if (ahb > 1)
+ ahb = 1;
+ else
+ ahb = 0;
+
+ apb0 = apb0 - 1;
+
+ /* Switch to 24MHz clock while changing PLL1 */
+ writel(AXI_DIV_1 << AXI_DIV_SHIFT |
+ AHB_DIV_2 << AHB_DIV_SHIFT |
+ APB0_DIV_1 << APB0_DIV_SHIFT |
+ CPU_CLK_SRC_OSC24M << CPU_CLK_SRC_SHIFT,
+ &ccm->cpu_ahb_apb0_cfg);
+ sdelay(20);
+
+ /* Configure sys clock divisors */
+ writel(axi << AXI_DIV_SHIFT |
+ ahb << AHB_DIV_SHIFT |
+ apb0 << APB0_DIV_SHIFT |
+ CPU_CLK_SRC_OSC24M << CPU_CLK_SRC_SHIFT,
+ &ccm->cpu_ahb_apb0_cfg);
+
+ /* Configure PLL1 at the desired frequency */
+ writel(pll1_para[i].pll1_cfg, &ccm->pll1_cfg);
+ sdelay(200);
+
+ /* Switch CPU to PLL1 */
+ writel(axi << AXI_DIV_SHIFT |
+ ahb << AHB_DIV_SHIFT |
+ apb0 << APB0_DIV_SHIFT |
+ CPU_CLK_SRC_PLL1 << CPU_CLK_SRC_SHIFT,
+ &ccm->cpu_ahb_apb0_cfg);
+ sdelay(20);
+}
+#endif
+
+unsigned int clock_get_pll6(void)
+{
+ struct sunxi_ccm_reg *const ccm =
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+ uint32_t rval = readl(&ccm->pll6_cfg);
+ int n = ((rval & CCM_PLL6_CTRL_N_MASK) >> CCM_PLL6_CTRL_N_SHIFT);
+ int k = ((rval & CCM_PLL6_CTRL_K_MASK) >> CCM_PLL6_CTRL_K_SHIFT) + 1;
+ return 24000000 * n * k / 2;
+}
diff --git a/arch/arm/cpu/armv7/sunxi/timer.c b/arch/arm/cpu/armv7/sunxi/timer.c
new file mode 100644
index 0000000..3626389
--- /dev/null
+++ b/arch/arm/cpu/armv7/sunxi/timer.c
@@ -0,0 +1,113 @@
+/*
+ * (C) Copyright 2007-2011
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Tom Cubie <tangliang@allwinnertech.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/timer.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define TIMER_MODE (0x0 << 7) /* continuous mode */
+#define TIMER_DIV (0x0 << 4) /* pre scale 1 */
+#define TIMER_SRC (0x1 << 2) /* osc24m */
+#define TIMER_RELOAD (0x1 << 1) /* reload internal value */
+#define TIMER_EN (0x1 << 0) /* enable timer */
+
+#define TIMER_CLOCK (24 * 1000 * 1000)
+#define COUNT_TO_USEC(x) ((x) / 24)
+#define USEC_TO_COUNT(x) ((x) * 24)
+#define TICKS_PER_HZ (TIMER_CLOCK / CONFIG_SYS_HZ)
+#define TICKS_TO_HZ(x) ((x) / TICKS_PER_HZ)
+
+#define TIMER_LOAD_VAL 0xffffffff
+
+#define TIMER_NUM 0 /* we use timer 0 */
+
+/* read the 32-bit timer */
+static ulong read_timer(void)
+{
+ struct sunxi_timer_reg *timers =
+ (struct sunxi_timer_reg *)SUNXI_TIMER_BASE;
+ struct sunxi_timer *timer = &timers->timer[TIMER_NUM];
+
+ /*
+ * The hardware timer counts down, therefore we invert to
+ * produce an incrementing timer.
+ */
+ return ~readl(&timer->val);
+}
+
+/* init timer register */
+int timer_init(void)
+{
+ struct sunxi_timer_reg *timers =
+ (struct sunxi_timer_reg *)SUNXI_TIMER_BASE;
+ struct sunxi_timer *timer = &timers->timer[TIMER_NUM];
+ writel(TIMER_LOAD_VAL, &timer->inter);
+ writel(TIMER_MODE | TIMER_DIV | TIMER_SRC | TIMER_RELOAD | TIMER_EN,
+ &timer->ctl);
+
+ return 0;
+}
+
+/* timer without interrupts */
+ulong get_timer(ulong base)
+{
+ return get_timer_masked() - base;
+}
+
+ulong get_timer_masked(void)
+{
+ /* current tick value */
+ ulong now = TICKS_TO_HZ(read_timer());
+
+ if (now >= gd->arch.lastinc) /* normal (non rollover) */
+ gd->arch.tbl += (now - gd->arch.lastinc);
+ else {
+ /* rollover */
+ gd->arch.tbl += (TICKS_TO_HZ(TIMER_LOAD_VAL)
+ - gd->arch.lastinc) + now;
+ }
+ gd->arch.lastinc = now;
+
+ return gd->arch.tbl;
+}
+
+/* delay x useconds */
+void __udelay(unsigned long usec)
+{
+ long tmo = USEC_TO_COUNT(usec);
+ ulong now, last = read_timer();
+
+ while (tmo > 0) {
+ now = read_timer();
+ if (now > last) /* normal (non rollover) */
+ tmo -= now - last;
+ else /* rollover */
+ tmo -= TIMER_LOAD_VAL - last + now;
+ last = now;
+ }
+}
+
+/*
+ * This function is derived from PowerPC code (read timebase as long long).
+ * On ARM it just returns the timer value.
+ */
+unsigned long long get_ticks(void)
+{
+ return get_timer(0);
+}
+
+/*
+ * This function is derived from PowerPC code (timebase clock frequency).
+ * On ARM it returns the number of timer ticks per second.
+ */
+ulong get_tbclk(void)
+{
+ return CONFIG_SYS_HZ;
+}