diff options
Diffstat (limited to 'configs/platform-energymicro-efm32gg-dk3750/patches/linux-3.8-rc5/0015-ARM-new-platform-for-Energy-Micro-s-EFM32-Cortex-M3-.patch')
-rw-r--r-- | configs/platform-energymicro-efm32gg-dk3750/patches/linux-3.8-rc5/0015-ARM-new-platform-for-Energy-Micro-s-EFM32-Cortex-M3-.patch | 629 |
1 files changed, 629 insertions, 0 deletions
diff --git a/configs/platform-energymicro-efm32gg-dk3750/patches/linux-3.8-rc5/0015-ARM-new-platform-for-Energy-Micro-s-EFM32-Cortex-M3-.patch b/configs/platform-energymicro-efm32gg-dk3750/patches/linux-3.8-rc5/0015-ARM-new-platform-for-Energy-Micro-s-EFM32-Cortex-M3-.patch new file mode 100644 index 0000000..304e80a --- /dev/null +++ b/configs/platform-energymicro-efm32gg-dk3750/patches/linux-3.8-rc5/0015-ARM-new-platform-for-Energy-Micro-s-EFM32-Cortex-M3-.patch @@ -0,0 +1,629 @@ +From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@pengutronix.de> +Date: Thu, 17 Nov 2011 14:36:23 +0100 +Subject: [PATCH] ARM: new platform for Energy Micro's EFM32 Cortex-M3 SoCs +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> +--- + arch/arm/Kconfig | 13 +- + arch/arm/Kconfig.debug | 16 +++ + arch/arm/Makefile | 1 + + arch/arm/boot/dts/efm32gg-dk3750.dts | 69 ++++++++++ + arch/arm/mach-efm32/Makefile | 3 + + arch/arm/mach-efm32/Makefile.boot | 1 + + arch/arm/mach-efm32/clk.c | 38 ++++++ + arch/arm/mach-efm32/cmu.h | 11 ++ + arch/arm/mach-efm32/common.h | 1 + + arch/arm/mach-efm32/dtmachine.c | 43 ++++++ + arch/arm/mach-efm32/include/mach/debug-macro.S | 48 +++++++ + arch/arm/mach-efm32/include/mach/entry-macro.S | 12 ++ + arch/arm/mach-efm32/include/mach/io.h | 6 + + arch/arm/mach-efm32/include/mach/irqs.h | 6 + + arch/arm/mach-efm32/include/mach/system.h | 18 +++ + arch/arm/mach-efm32/include/mach/timex.h | 7 + + arch/arm/mach-efm32/time.c | 170 ++++++++++++++++++++++++ + 17 files changed, 462 insertions(+), 1 deletion(-) + create mode 100644 arch/arm/boot/dts/efm32gg-dk3750.dts + create mode 100644 arch/arm/mach-efm32/Makefile + create mode 100644 arch/arm/mach-efm32/Makefile.boot + create mode 100644 arch/arm/mach-efm32/clk.c + create mode 100644 arch/arm/mach-efm32/cmu.h + create mode 100644 arch/arm/mach-efm32/common.h + create mode 100644 arch/arm/mach-efm32/dtmachine.c + create mode 100644 arch/arm/mach-efm32/include/mach/debug-macro.S + create mode 100644 arch/arm/mach-efm32/include/mach/entry-macro.S + create mode 100644 arch/arm/mach-efm32/include/mach/io.h + create mode 100644 arch/arm/mach-efm32/include/mach/irqs.h + create mode 100644 arch/arm/mach-efm32/include/mach/system.h + create mode 100644 arch/arm/mach-efm32/include/mach/timex.h + create mode 100644 arch/arm/mach-efm32/time.c + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 9d09a41..029f074 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -418,6 +418,17 @@ config ARCH_EBSA110 + Ethernet interface, two PCMCIA sockets, two serial ports and a + parallel port. + ++config ARCH_EFM32 ++ bool "EnergyMicro Cortex M3 Platform" ++ depends on !MMU ++ select ARM_NVIC ++ select CLKSRC_MMIO ++ select CPU_V7M ++ select GENERIC_CLOCKEVENTS ++ select HAVE_CLK ++ select NO_DMA ++ select NO_IOPORT ++ + config ARCH_EP93XX + bool "EP93xx-based" + select ARCH_HAS_HOLES_MEMORYMODEL +@@ -1785,7 +1796,7 @@ config FORCE_MAX_ZONEORDER + int "Maximum zone order" if ARCH_SHMOBILE + range 11 64 if ARCH_SHMOBILE + default "12" if SOC_AM33XX +- default "9" if SA1111 ++ default "9" if SA1111 || ARCH_EFM32 + default "11" + help + The kernel memory allocator divides physically contiguous memory +diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug +index 661030d..e0f675a 100644 +--- a/arch/arm/Kconfig.debug ++++ b/arch/arm/Kconfig.debug +@@ -156,6 +156,22 @@ choice + Say Y here if you want the debug print routines to direct + their output to the serial port in the DC21285 (Footbridge). + ++ config DEBUG_EFM32_USART1 ++ bool "Kernel low-level debugging messages via USART1" ++ depends on ARCH_EFM32 ++ help ++ Say Y here if you want the debug print routines to direct ++ their output to the second USART port on efm32 based ++ machines. ++ ++ config DEBUG_EFM32_UART1 ++ bool "Kernel low-level debugging messages via UART1" ++ depends on ARCH_EFM32 ++ help ++ Say Y here if you want the debug print routines to direct ++ their output to the second UART port on efm32 based ++ machines. ++ + config DEBUG_FOOTBRIDGE_COM1 + bool "Kernel low-level debugging messages via footbridge 8250 at PCI COM1" + depends on FOOTBRIDGE +diff --git a/arch/arm/Makefile b/arch/arm/Makefile +index 69a5955..374e25a 100644 +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -146,6 +146,7 @@ machine-$(CONFIG_ARCH_CNS3XXX) += cns3xxx + machine-$(CONFIG_ARCH_DAVINCI) += davinci + machine-$(CONFIG_ARCH_DOVE) += dove + machine-$(CONFIG_ARCH_EBSA110) += ebsa110 ++machine-$(CONFIG_ARCH_EFM32) += efm32 + machine-$(CONFIG_ARCH_EP93XX) += ep93xx + machine-$(CONFIG_ARCH_GEMINI) += gemini + machine-$(CONFIG_ARCH_H720X) += h720x +diff --git a/arch/arm/boot/dts/efm32gg-dk3750.dts b/arch/arm/boot/dts/efm32gg-dk3750.dts +new file mode 100644 +index 0000000..3fc2369 +--- /dev/null ++++ b/arch/arm/boot/dts/efm32gg-dk3750.dts +@@ -0,0 +1,69 @@ ++/dts-v1/; ++/include/ "skeleton.dtsi" ++ ++/ { ++ model = "Energy Micro Giant Gecko Development Kit"; ++ compatible = "efm32,dk3750"; ++ ++ aliases { ++ serial4 = &uart4; ++ }; ++ ++ nvic: nv-interrupt-controller@0xe0000000 { ++ compatible = "arm,cortex-m3-nvic"; ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ reg = <0xe000e000 0x4000>; ++ }; ++ ++ chosen { ++ bootargs = "console=ttyefm4,115200 init=/linuxrc ignore_loglevel ihash_entries=64 dhash_entries=64 earlyprintk uclinux.physaddr=0x8c000000 root=/dev/mtdblock0"; ++ }; ++ ++ memory { ++ reg = <0x88000000 0x400000>; ++ }; ++ ++ soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "simple-bus"; ++ interrupt-parent = <&nvic>; ++ ranges; ++ ++ spi@0x4000c400 { /* USART1 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "efm32,spi"; ++ reg = <0x4000c400 0x400>; ++ interrupts = <15>; ++ status = "ok"; ++ ++ ks8851@0 { ++ compatible = "ks8851"; ++ spi-max-frequency = <6000000>; ++ reg = <0>; ++ interrupt-parent = <&boardfpga>; ++ interrupts = <4>; ++ status = "ok"; ++ }; ++ }; ++ ++ uart4: uart@0x4000e400 { /* UART1 */ ++ compatible = "efm32,uart"; ++ reg = <0x4000e400 0x400>; ++ interrupts = <22>; ++ location = <2>; ++ status = "ok"; ++ }; ++ ++ boardfpga: boardfpga@0x80000000 { ++ compatible = "efm32board"; ++ reg = <0x80000000 0x400>; ++ /* this is a lie, gpio_even = 1 */ ++ interrupts = <1>; ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; ++ }; ++}; +diff --git a/arch/arm/mach-efm32/Makefile b/arch/arm/mach-efm32/Makefile +new file mode 100644 +index 0000000..10a3426 +--- /dev/null ++++ b/arch/arm/mach-efm32/Makefile +@@ -0,0 +1,3 @@ ++obj-y += clk.o time.o ++ ++obj-$(CONFIG_OF) += dtmachine.o +diff --git a/arch/arm/mach-efm32/Makefile.boot b/arch/arm/mach-efm32/Makefile.boot +new file mode 100644 +index 0000000..385e93a +--- /dev/null ++++ b/arch/arm/mach-efm32/Makefile.boot +@@ -0,0 +1 @@ ++dtb-$(CONFIG_MACH_EFM32GG_DK3750) += efm32gg-dk3750.dtb +diff --git a/arch/arm/mach-efm32/clk.c b/arch/arm/mach-efm32/clk.c +new file mode 100644 +index 0000000..5b158ab +--- /dev/null ++++ b/arch/arm/mach-efm32/clk.c +@@ -0,0 +1,38 @@ ++#include <linux/clk.h> ++#include <linux/export.h> ++#include <linux/io.h> ++ ++#include "cmu.h" ++ ++struct clk *clk_get(struct device *dev, const char *id) ++{ ++ return NULL; ++} ++EXPORT_SYMBOL(clk_get); ++ ++int clk_enable(struct clk *clk) ++{ ++ return 0; ++} ++EXPORT_SYMBOL(clk_enable); ++ ++void clk_disable(struct clk *clk) ++{ ++} ++EXPORT_SYMBOL(clk_disable); ++ ++unsigned long clk_get_rate(struct clk *clk) ++{ ++ u32 cmu_status = __raw_readl(CMU_STATUS); ++ ++ if (cmu_status & CMU_STATUS_HFRCOSEL) ++ return 14000000; ++ else ++ return 48000000; ++} ++EXPORT_SYMBOL(clk_get_rate); ++ ++void clk_put(struct clk *clk) ++{ ++} ++EXPORT_SYMBOL(clk_put); +diff --git a/arch/arm/mach-efm32/cmu.h b/arch/arm/mach-efm32/cmu.h +new file mode 100644 +index 0000000..05ef676 +--- /dev/null ++++ b/arch/arm/mach-efm32/cmu.h +@@ -0,0 +1,11 @@ ++#include "common.h" ++ ++#define CMU_OSCENCMD IOMEM(0x400c8020) ++#define CMU_OSCENCMD_HFXOEN 0x00000004 ++ ++#define CMU_CMD IOMEM(0x400C8024) ++#define CMU_CMD_HFCLKSEL_HFXO 0x00000002 ++ ++#define CMU_STATUS IOMEM(0x400c802c) ++#define CMU_STATUS_HFRCOSEL 0x00000400 ++#define CMU_STATUS_HFXOSEL 0x00000800 +diff --git a/arch/arm/mach-efm32/common.h b/arch/arm/mach-efm32/common.h +new file mode 100644 +index 0000000..d2ff797 +--- /dev/null ++++ b/arch/arm/mach-efm32/common.h +@@ -0,0 +1 @@ ++extern struct sys_timer efm32_timer; +diff --git a/arch/arm/mach-efm32/dtmachine.c b/arch/arm/mach-efm32/dtmachine.c +new file mode 100644 +index 0000000..a64c4a4 +--- /dev/null ++++ b/arch/arm/mach-efm32/dtmachine.c +@@ -0,0 +1,43 @@ ++#include <linux/kernel.h> ++#include <linux/pinctrl/machine.h> ++#include <linux/irqdomain.h> ++#include <linux/of_platform.h> ++#include <linux/of_irq.h> ++ ++#include <asm/hardware/nvic.h> ++#include <asm/mach/arch.h> ++#include <asm/mach/time.h> ++ ++#include "common.h" ++#include "devices.h" ++ ++static const struct of_device_id efm32_irq_match[] __initconst = { ++ { ++ .compatible = "arm,cortex-m3-nvic", ++ .data = nvic_of_init, ++ }, { ++ /* sentinel */ ++ } ++}; ++ ++static void __init efm32_init_irq(void) ++{ ++ of_irq_init(efm32_irq_match); ++} ++ ++static void __init efm32_init(void) ++{ ++ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); ++} ++ ++static const char *const efm32gg_compat[] __initconst = { ++ "efm32,dk3750", ++ NULL ++}; ++ ++DT_MACHINE_START(EFM32DT, "EFM32 (Device Tree Support)") ++ .init_irq = efm32_init_irq, ++ .timer = &efm32_timer, ++ .init_machine = efm32_init, ++ .dt_compat = efm32gg_compat, ++MACHINE_END +diff --git a/arch/arm/mach-efm32/include/mach/debug-macro.S b/arch/arm/mach-efm32/include/mach/debug-macro.S +new file mode 100644 +index 0000000..c58915c +--- /dev/null ++++ b/arch/arm/mach-efm32/include/mach/debug-macro.S +@@ -0,0 +1,48 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#define UARTn_CMD 0x000c ++#define UARTn_CMD_TXEN 0x0004 ++ ++#define UARTn_STATUS 0x0010 ++#define UARTn_STATUS_TXC 0x0020 ++#define UARTn_STATUS_TXBL 0x0040 ++ ++#define UARTn_TXDATA 0x0034 ++ ++ .macro addruart, rx, tmp ++#if defined(CONFIG_DEBUG_EFM32_USART1) ++ ldr \rx, =(0x4000c400) /* USART1 */ ++#elif defined(CONFIG_DEBUG_EFM32_UART1) ++ ldr \rx, =(0x4000e400) /* UART1 */ ++#else ++#error "No debug port configured" ++#endif ++ /* ++ * enable TX. The driver might disable that to save energy. We ++ * don't care about disabling at the end as during debug power ++ * consumption isn't that important. ++ */ ++ ldr \tmp, =(UARTn_CMD_TXEN) ++ str \tmp, [\rx, #UARTn_CMD] ++ .endm ++ ++ ++ .macro senduart,rd,rx ++ strb \rd, [\rx, #UARTn_TXDATA] ++ .endm ++ ++ .macro waituart,rd,rx ++1001: ldr \rd, [\rx, #UARTn_STATUS] ++ tst \rd, #UARTn_STATUS_TXBL ++ beq 1001b ++ .endm ++ ++ .macro busyuart,rd,rx ++1001: ldr \rd, [\rx, UARTn_STATUS] ++ tst \rd, #UARTn_STATUS_TXC ++ bne 1001b ++ .endm +diff --git a/arch/arm/mach-efm32/include/mach/entry-macro.S b/arch/arm/mach-efm32/include/mach/entry-macro.S +new file mode 100644 +index 0000000..75b304a +--- /dev/null ++++ b/arch/arm/mach-efm32/include/mach/entry-macro.S +@@ -0,0 +1,12 @@ ++/* ++ * ++ */ ++#include <asm/hardware/gic.h> ++ ++ .macro get_irqnr_preamble, base, tmp ++ ldr \base, =gic_cpu_base_addr ++ ldr \base, [\base] ++ .endm ++ ++ .macro arch_ret_to_user, tmp1, tmp2 ++ .endm +diff --git a/arch/arm/mach-efm32/include/mach/io.h b/arch/arm/mach-efm32/include/mach/io.h +new file mode 100644 +index 0000000..bc3519b +--- /dev/null ++++ b/arch/arm/mach-efm32/include/mach/io.h +@@ -0,0 +1,6 @@ ++#ifndef __MACH_IO_H__ ++#define __MACH_IO_H__ ++ ++#define __mem_pci(a) (a) ++ ++#endif /* __MACH_IO_H__ */ +diff --git a/arch/arm/mach-efm32/include/mach/irqs.h b/arch/arm/mach-efm32/include/mach/irqs.h +new file mode 100644 +index 0000000..e33ed12 +--- /dev/null ++++ b/arch/arm/mach-efm32/include/mach/irqs.h +@@ -0,0 +1,6 @@ ++#ifndef __MACH_IRQS_H__ ++#define __MACH_IRQS_H__ ++ ++#define NR_IRQS 82 ++ ++#endif /* __MACH_IRQS_H__ */ +diff --git a/arch/arm/mach-efm32/include/mach/system.h b/arch/arm/mach-efm32/include/mach/system.h +new file mode 100644 +index 0000000..619222c +--- /dev/null ++++ b/arch/arm/mach-efm32/include/mach/system.h +@@ -0,0 +1,18 @@ ++#ifndef __MACH_SYSTEM_H__ ++#define __MACH_SYSTEM_H__ ++ ++#include <asm/io.h> ++ ++static inline void arch_idle(void) ++{ ++ cpu_do_idle(); ++} ++ ++static inline void arch_reset(char mode, const char *cmd) ++{ ++ /* XXX: move this to (say) cpuv7m_reset */ ++ dsb(); ++ __raw_writel(0x05fa0004, (void __iomem *)0xe000ed0c); ++ dsb(); ++} ++#endif /* __MACH_SYSTEM_H__ */ +diff --git a/arch/arm/mach-efm32/include/mach/timex.h b/arch/arm/mach-efm32/include/mach/timex.h +new file mode 100644 +index 0000000..b408dce +--- /dev/null ++++ b/arch/arm/mach-efm32/include/mach/timex.h +@@ -0,0 +1,7 @@ ++#ifndef __MACH_TIMEX_H__ ++#define __MACH_TIMEX_H__ ++ ++/* just a bogus value */ ++#define CLOCK_TICK_RATE 12345678 ++ ++#endif /* __MACH_TIMEX_H__ */ +diff --git a/arch/arm/mach-efm32/time.c b/arch/arm/mach-efm32/time.c +new file mode 100644 +index 0000000..f91a3ef +--- /dev/null ++++ b/arch/arm/mach-efm32/time.c +@@ -0,0 +1,170 @@ ++#include <linux/kernel.h> ++#include <linux/clocksource.h> ++#include <linux/clockchips.h> ++#include <linux/irq.h> ++#include <linux/interrupt.h> ++#include <linux/stringify.h> ++ ++#include <asm/mach/time.h> ++ ++#include "common.h" ++ ++#define BASEADDR_TIMER(n) IOMEM(0x40010000 + (n) * 0x400) ++ ++#define TIMERn_CTRL 0x00 ++#define TIMERn_CTRL_PRESC(val) (((val) & 0xf) << 24) ++#define TIMERn_CTRL_PRESC_1024 TIMERn_CTRL_PRESC(10) ++#define TIMERn_CTRL_CLKSEL(val) (((val) & 0x3) << 16) ++#define TIMERn_CTRL_CLKSEL_PRESCHFPERCLK TIMERn_CTRL_CLKSEL(0) ++#define TIMERn_CTRL_OSMEN 0x00000010 ++#define TIMERn_CTRL_MODE(val) (((val) & 0x3) << 0) ++#define TIMERn_CTRL_MODE_UP TIMERn_CTRL_MODE(0) ++#define TIMERn_CTRL_MODE_DOWN TIMERn_CTRL_MODE(1) ++ ++#define TIMERn_CMD 0x04 ++#define TIMERn_CMD_START 0x1 ++#define TIMERn_CMD_STOP 0x2 ++ ++#define TIMERn_IEN 0x0c ++#define TIMERn_IF 0x10 ++#define TIMERn_IFS 0x14 ++#define TIMERn_IFC 0x18 ++#define TIMERn_IRQ_UF 0x2 ++#define TIMERn_IRQ_OF 0x1 ++ ++#define TIMERn_TOP 0x1c ++#define TIMERn_CNT 0x24 ++ ++#define TIMER_CLOCKSOURCE 1 ++#define TIMER_CLOCKEVENT 2 ++#define IRQ_CLOCKEVENT NVIC_IRQ(13) ++ ++static void efm32_timer_write(unsigned timerno, u32 val, unsigned offset) ++{ ++ __raw_writel(val, BASEADDR_TIMER(timerno) + offset); ++} ++ ++static void efm32_clock_event_set_mode(enum clock_event_mode mode, ++ struct clock_event_device *unused) ++{ ++ switch (mode) { ++ case CLOCK_EVT_MODE_PERIODIC: ++ efm32_timer_write(TIMER_CLOCKEVENT, ++ TIMERn_CMD_STOP, TIMERn_CMD); ++ efm32_timer_write(TIMER_CLOCKEVENT, 137, TIMERn_TOP); ++ efm32_timer_write(TIMER_CLOCKEVENT, ++ TIMERn_CTRL_PRESC_1024 | ++ TIMERn_CTRL_CLKSEL_PRESCHFPERCLK | ++ TIMERn_CTRL_MODE_DOWN, TIMERn_CTRL); ++ efm32_timer_write(TIMER_CLOCKEVENT, ++ TIMERn_CMD_START, TIMERn_CMD); ++ break; ++ ++ case CLOCK_EVT_MODE_ONESHOT: ++ efm32_timer_write(TIMER_CLOCKEVENT, ++ TIMERn_CMD_STOP, TIMERn_CMD); ++ efm32_timer_write(TIMER_CLOCKEVENT, ++ TIMERn_CTRL_PRESC_1024 | ++ TIMERn_CTRL_CLKSEL_PRESCHFPERCLK | ++ TIMERn_CTRL_OSMEN | ++ TIMERn_CTRL_MODE_DOWN, TIMERn_CTRL); ++ break; ++ ++ case CLOCK_EVT_MODE_UNUSED: ++ case CLOCK_EVT_MODE_SHUTDOWN: ++ efm32_timer_write(TIMER_CLOCKEVENT, TIMERn_CMD_STOP, ++ TIMERn_CMD); ++ break; ++ ++ case CLOCK_EVT_MODE_RESUME: ++ break; ++ } ++} ++ ++static int efm32_clock_event_set_next_event(unsigned long evt, ++ struct clock_event_device *unused) ++{ ++ efm32_timer_write(TIMER_CLOCKEVENT, TIMERn_CMD_STOP, TIMERn_CMD); ++ efm32_timer_write(TIMER_CLOCKEVENT, evt, TIMERn_CNT); ++ efm32_timer_write(TIMER_CLOCKEVENT, TIMERn_CMD_START, TIMERn_CMD); ++ ++ return 0; ++} ++ ++static struct clock_event_device efm32_clock_event_device = { ++ .name = "efm32 clockevent (" __stringify(TIMER_CLOCKEVENT) ")", ++ .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_MODE_PERIODIC, ++ .set_mode = efm32_clock_event_set_mode, ++ .set_next_event = efm32_clock_event_set_next_event, ++ .rating = 200, ++}; ++ ++static irqreturn_t efm32_clock_event_handler(int irq, void *dev_id) ++{ ++ struct clock_event_device *evt = &efm32_clock_event_device; ++ ++ /* ack irq */ ++ efm32_timer_write(TIMER_CLOCKEVENT, TIMERn_IRQ_UF, TIMERn_IFC); ++ ++ evt->event_handler(evt); ++ ++ return IRQ_HANDLED; ++} ++ ++static struct irqaction efm32_clock_event_irq = { ++ .name = "efm32 clockevent", ++ .flags = IRQF_TIMER, ++ .handler = efm32_clock_event_handler, ++ .dev_id = &efm32_clock_event_device, ++}; ++ ++/* ++ * XXX: use clk_ API to get frequency and enabling of the clocks used. ++ * Here the reset defaults are used: ++ * - freq_{HFPERCLK} = freq_{HFCLK} ++ * (CMU_HFPERCLKDIV_HFPERCLKDIV = 0x0) ++ * - freq_{HFCLK} = freq_{HFRCO} ++ * (CMU_CTRL_HFCLKDIV = 0x0, CMU_STATUS_HFRCOSEL = 0x1) ++ * - freq_{HFRCO} = 14MHz ++ * (CMU_HFRCOCTRL_BAND = 0x3) ++ * ++ * So the HFPERCLK runs at 14MHz. The timer has an additional prescaler ++ * programmed to /1024. This make the timer run at ++ * ++ * 14 MHz / 1024 = 13671.875 Hz ++ * ++ * When HFXO is used HFPERCLK runs at 48 MHz, so the timer runs at ++ * ++ * 48 MHz / 1024 = 46875 Hz ++ * ++ */ ++static void __init efm32_timer_init(void) ++{ ++ /* enable CMU_HFPERCLKEN0_TIMERn for clocksource via bit-band */ ++ __raw_writel(1, IOMEM(0x43900894 + 4 * TIMER_CLOCKSOURCE)); ++ ++ efm32_timer_write(TIMER_CLOCKSOURCE, ++ TIMERn_CTRL_PRESC_1024 | ++ TIMERn_CTRL_CLKSEL_PRESCHFPERCLK | ++ TIMERn_CTRL_MODE_UP, TIMERn_CTRL); ++ efm32_timer_write(TIMER_CLOCKSOURCE, TIMERn_CMD_START, TIMERn_CMD); ++ ++ clocksource_mmio_init(BASEADDR_TIMER(TIMER_CLOCKSOURCE) + TIMERn_CNT, ++ "efm32 timer", 46875, 200, 16, ++ clocksource_mmio_readl_up); ++ ++ /* enable CMU_HFPERCLKEN0_TIMERn for clockevent via bit-band */ ++ __raw_writel(1, IOMEM(0x43900894 + 4 * TIMER_CLOCKEVENT)); ++ ++ efm32_timer_write(TIMER_CLOCKEVENT, TIMERn_IRQ_UF, TIMERn_IEN); ++ ++ setup_irq(IRQ_CLOCKEVENT, &efm32_clock_event_irq); ++ ++ /* XXX: tune min_delta */ ++ clockevents_config_and_register(&efm32_clock_event_device, ++ 46875, 0xf, 0xffff); ++} ++ ++struct sys_timer efm32_timer = { ++ .init = efm32_timer_init, ++}; |