[RFC PATCH] ARM: new architecture for Energy Micro's EFM32 Cortex-M3 SoCs
Uwe Kleine-König
u.kleine-koenig at pengutronix.de
Wed Dec 21 10:13:48 EST 2011
Signed-off-by: Uwe Kleine-König <u.kleine-koenig at pengutronix.de>
---
Hello,
currently this relies on the bootloader to enable all necessary clocks
because there is no functional clk API, yet.
Other than that my machine comes up, but the 1 MiB of RAM is very tight,
so I have to stick to XIP.
Also note that this depends on ARMv7M support that is picked from
Catalin's repository. I didn't come around yet to rework them into a
shape acceptable for mainline. I ported them to 3.2-rc and needed a few
more hacks (e.g. I don't have a bootloader, so I added a little
assembler bootloader that is included at the start of my xipImage).
If you're interested in the details I can publish my tree I think.
Best regards
Uwe
arch/arm/Kconfig | 11 ++-
arch/arm/Kconfig.debug | 8 +
arch/arm/Makefile | 1 +
arch/arm/boot/dts/efm32gg-dk3750.dts | 41 ++++++
arch/arm/mach-efm32/Makefile | 3 +
arch/arm/mach-efm32/Makefile.boot | 1 +
arch/arm/mach-efm32/clk.c | 30 +++++
arch/arm/mach-efm32/common.h | 7 +
arch/arm/mach-efm32/dtmachine.c | 47 +++++++
arch/arm/mach-efm32/include/mach/debug-macro.S | 32 +++++
arch/arm/mach-efm32/include/mach/entry-macro.S | 12 ++
arch/arm/mach-efm32/include/mach/io.h | 7 +
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 | 166 ++++++++++++++++++++++++
arch/arm/mm/Kconfig | 2 +-
17 files changed, 397 insertions(+), 2 deletions(-)
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/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 8555445..68de356 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -400,6 +400,15 @@ 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 CPU_V7M
+ select ARM_NVIC
+ select GENERIC_CLOCKEVENTS
+ select NO_DMA
+ select CLKSRC_MMIO
+
config ARCH_EP93XX
bool "EP93xx-based"
select CPU_ARM920T
@@ -1699,7 +1708,7 @@ source "mm/Kconfig"
config FORCE_MAX_ZONEORDER
int "Maximum zone order" if ARCH_SHMOBILE
range 11 64 if ARCH_SHMOBILE
- 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 c5213e7..cdec99b 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -128,6 +128,14 @@ choice
Say Y here if you want the debug print routines to direct
their output to the second serial port on these devices.
+ 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 serial port on efm32 based
+ machines.
+
config DEBUG_HIGHBANK_UART
bool "Kernel low-level debugging messages via Highbank UART"
depends on ARCH_HIGHBANK
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 042fdec..b955c46 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -142,6 +142,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..a4aa4f3
--- /dev/null
+++ b/arch/arm/boot/dts/efm32gg-dk3750.dts
@@ -0,0 +1,41 @@
+/dts-v1/;
+/include/ "skeleton.dtsi"
+
+/ {
+ model = "Energy Micro Giant Gecko Development Kit";
+ compatible = "efm32,dk3750";
+
+ aliases {
+ serial1 = &uart1;
+ };
+
+ nvic: nv-interrupt-controller at 0xe0000000 {
+ compatible = "arm,cortex-m3-nvic";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ reg = <0xe0000000 0x4000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyefm1,115200 init=/linuxrc ignore_loglevel ihash_entries=64 dhash_entries=64";
+ };
+
+ memory {
+ reg = <0x80000000 0x100000>;
+ };
+
+ soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ interrupt-parent = <&nvic>;
+ ranges;
+
+ uart1: uart at 0x4000c400 { /* USART1 */
+ compatible = "efm32,usart";
+ reg = <0x4000c400 0x400>;
+ interrupts = <15>;
+ status = "ok";
+ };
+ };
+};
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..86fadc8
--- /dev/null
+++ b/arch/arm/mach-efm32/clk.c
@@ -0,0 +1,30 @@
+#include <linux/clk.h>
+#include <linux/export.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)
+{
+ return 14000000;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+void clk_put(struct clk *clk)
+{
+}
+EXPORT_SYMBOL(clk_put);
diff --git a/arch/arm/mach-efm32/common.h b/arch/arm/mach-efm32/common.h
new file mode 100644
index 0000000..f545224
--- /dev/null
+++ b/arch/arm/mach-efm32/common.h
@@ -0,0 +1,7 @@
+#ifdef __ASSEMBLER__
+#define IOMEM(addr) (addr)
+#else
+#define IOMEM(addr) ((void __force __iomem *)(addr))
+#endif
+
+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..42d091c
--- /dev/null
+++ b/arch/arm/mach-efm32/dtmachine.c
@@ -0,0 +1,47 @@
+#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"
+
+static void __init efm32_nvic_add_irq_domain(struct device_node *np,
+ struct device_node *interrupt_parent)
+{
+ irq_domain_add_simple(np, 0);
+}
+
+static const struct of_device_id efm32_irq_match[] __initconst = {
+ {
+ .compatible = "arm,cortex-m3-nvic",
+ .data = efm32_nvic_add_irq_domain,
+ }, {
+ /* sentinel */
+ }
+};
+
+static void __init efm32_init(void)
+{
+ int ret;
+
+ of_irq_init(efm32_irq_match);
+
+ 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 = nvic_init,
+ .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..cbf37f3
--- /dev/null
+++ b/arch/arm/mach-efm32/include/mach/debug-macro.S
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+ .macro addruart,rx,tmp
+ mov \rx, #0x40000000
+ orr \rx, \rx, #0x0000C400 /* USART1 */
+ .endm
+
+#define USARTn_DR 0x34
+#define USARTn_FR 0x10
+#define USARTn_FR_TXFF 0x0020
+
+ .macro senduart,rd,rx
+ strb \rd, [\rx, #USARTn_DR]
+ .endm
+
+ .macro waituart,rd,rx
+1001: ldr \rd, [\rx, #USARTn_FR]
+ tst \rd, #USARTn_FR_TXFF
+ it eq
+ beq 1001b
+ .endm
+
+ .macro busyuart,rd,rx
+1001: ldr \rd, [\rx, USARTn_FR]
+ tst \rd, USARTn_FR_TXFF
+ it ne
+ 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..00b6af6
--- /dev/null
+++ b/arch/arm/mach-efm32/include/mach/io.h
@@ -0,0 +1,7 @@
+#ifndef __MACH_IO_H__
+#define __MACH_IO_H__
+
+#define __io(a) __typesafe_io(a)
+#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..5fa84db
--- /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 37
+
+#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..65f93a3
--- /dev/null
+++ b/arch/arm/mach-efm32/time.c
@@ -0,0 +1,166 @@
+#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 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)
+{
+ /* writing START to CMD restarts a running timer, too */
+ efm32_timer_write(TIMER_CLOCKEVENT, evt, TIMERn_TOP);
+ 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
+ *
+ */
+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", 13672, 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,
+ 13672, 0xf, 0xffff);
+}
+
+struct sys_timer efm32_timer = {
+ .init = efm32_timer_init,
+};
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index 2bc1a68..5b81e46 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -432,7 +432,7 @@ config CPU_V7
# ARMv7
config CPU_V7M
bool "Support ARMv7-M processors"
- depends on MACH_REALVIEW_EB && EXPERIMENTAL
+ depends on (MACH_REALVIEW_EB && EXPERIMENTAL) || ARCH_EFM32
select THUMB2_KERNEL
select ARM_THUMB
select CPU_32v7M
--
1.7.7.3
More information about the linux-arm-kernel
mailing list