Skip to contentSkip to conversation

ARM: new architecture for Energy Micro's EFM32 Cortex-M3 SoCs

RFC PATCH New Uwe Kleine-König 7 participants 16 messages Dec 21, 2011 – Jan 17, 2012

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

RFC PATCH 1/1 ARM: new architecture for Energy Micro's EFM32 Cortex-M3 SoCs

---
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
arch/arm/Kconfig +10 -1
index 8555445..68de356 100644
@@ -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
arch/arm/Kconfig.debug +8 -0
index c5213e7..cdec99b 100644
@@ -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
arch/arm/Makefile +1 -0
index 042fdec..b955c46 100644
@@ -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
arch/arm/boot/dts/efm32gg-dk3750.dts new file +41 -0

41 lines not on this page · raw message

arch/arm/mach-efm32/Makefile new file +3 -0
new file mode 100644
index 0000000..10a3426
@@ -0,0 +1,3 @@
+obj-y += clk.o time.o
+
+obj-$(CONFIG_OF) += dtmachine.o
arch/arm/mach-efm32/Makefile.boot new file +1 -0
new file mode 100644
index 0000000..385e93a
@@ -0,0 +1 @@
+dtb-$(CONFIG_MACH_EFM32GG_DK3750) += efm32gg-dk3750.dtb
arch/arm/mach-efm32/clk.c new file +30 -0
new file mode 100644
index 0000000..86fadc8
@@ -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);
arch/arm/mach-efm32/common.h new file +7 -0
new file mode 100644
index 0000000..f545224
@@ -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;
arch/arm/mach-efm32/dtmachine.c new file +47 -0

47 lines not on this page · raw message

arch/arm/mach-efm32/include/mach/debug-macro.S new file +32 -0
new file mode 100644
index 0000000..cbf37f3
@@ -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
arch/arm/mach-efm32/include/mach/entry-macro.S new file +12 -0
new file mode 100644
index 0000000..75b304a
@@ -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
arch/arm/mach-efm32/include/mach/io.h new file +7 -0
new file mode 100644
index 0000000..00b6af6
@@ -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__ */
arch/arm/mach-efm32/include/mach/irqs.h new file +6 -0
new file mode 100644
index 0000000..5fa84db
@@ -0,0 +1,6 @@
+#ifndef __MACH_IRQS_H__
+#define __MACH_IRQS_H__
+
+#define NR_IRQS			37
+
+#endif /* __MACH_IRQS_H__ */
arch/arm/mach-efm32/include/mach/system.h new file +18 -0
new file mode 100644
index 0000000..619222c
@@ -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__ */
arch/arm/mach-efm32/include/mach/timex.h new file +7 -0
new file mode 100644
index 0000000..b408dce
@@ -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__ */
arch/arm/mach-efm32/time.c new file +166 -0

166 lines not on this page · raw message

arch/arm/mm/Kconfig +1 -1
index 2bc1a68..5b81e46 100644
@@ -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
Signature · 1 line
1.7.7.3
#1 lore raw

RFC PATCH 1/1 ARM: new architecture for Energy Micro's EFM32 Cortex-M3 SoCs

Hi Uwe,

Just nits inline, but otherwise looks nice! Certainly seems like an interesting platform!

Jamie

On Wed, Dec 21, 2011 at 04:13:48PM +0100, Uwe Kleine-K?nig wrote:
[...]

Quoted 13 lines
arch/arm/boot/dts/efm32gg-dk3750.dts new file +7 -0
new file mode 100644
index 0000000..a4aa4f3
@@ -0,0 +1,41 @@
+/dts-v1/;
+/include/ "skeleton.dtsi"
+
+/ {
+	model = "Energy Micro Giant Gecko Development Kit";
+	compatible = "efm32,dk3750";
+

arch/arm/boot/dts/efm32gg-dk3750.dts line 17

Quoted 10 lines
arch/arm/boot/dts/efm32gg-dk3750.dts new file +10 -0
+	aliases {
+		serial1 = &uart1;
+	};
+
+	nvic: nv-interrupt-controller at 0xe0000000  {
+		compatible = "arm,cortex-m3-nvic";
+		interrupt-controller;
+		#interrupt-cells = <1>;
+		reg = <0xe0000000 0x4000>;
+	};

I guess there's some patches somewhere else for the nvic binding, but I thought that the nvic had a reasonable number of features like edge/level etc and so would have more than one intererupt cell...

arch/arm/boot/dts/efm32gg-dk3750.dts line 23

Quoted 5 lines
arch/arm/boot/dts/efm32gg-dk3750.dts new file +5 -0
+	chosen {
+		bootargs = "console=ttyefm1,115200 init=/linuxrc ignore_loglevel ihash_entries=64 dhash_entries=64";
+	};
+
+	memory {

I think "memory at 80000000" is preferred here.

Quoted 25 lines
arch/arm/boot/dts/efm32gg-dk3750.dts new file +18 -0
+		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";
+		};
+	};
+};
arch/arm/mach-efm32/common.h new file +0 -0
new file mode 100644
index 0000000..f545224

arch/arm/mach-efm32/common.h line 5

Quoted 5 lines
arch/arm/mach-efm32/common.h new file +5 -0
@@ -0,0 +1,7 @@
+#ifdef __ASSEMBLER__
+#define IOMEM(addr)     (addr)
+#else
+#define IOMEM(addr)     ((void __force __iomem *)(addr))
+#endif

It looks like the only users of IOMEM are for the timers. Is it possible to get the timer information from the device tree then remove this?

Quoted 14 lines
arch/arm/mach-efm32/common.h new file +1 -0
+extern struct sys_timer efm32_timer;
arch/arm/mach-efm32/dtmachine.c new file +7 -0
new file mode 100644
index 0000000..42d091c
@@ -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>

arch/arm/mach-efm32/dtmachine.c line 17

Quoted 10 lines
arch/arm/mach-efm32/dtmachine.c new file +10 -0
+#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);
+}

If the nvic driver is new then I would have expected that to have device tree and irq domain support internally. From the entry macros it looks like you're using the GIC macros, so is it pretty much GIC compatible?

arch/arm/mach-efm32/dtmachine.c line 30

Quoted 13 lines
arch/arm/mach-efm32/dtmachine.c new file +13 -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;

This doesn't appear to be used.

arch/arm/mach-efm32/dtmachine.c line 32

Quoted 2 lines
arch/arm/mach-efm32/dtmachine.c new file +2 -0
+
+	of_irq_init(efm32_irq_match);

Shouldn't this be in a .init_irq function()?

arch/arm/mach-efm32/dtmachine.c line 43

Quoted 11 lines
arch/arm/mach-efm32/dtmachine.c new file +11 -0
+
+	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,

This doesn't exist...

Quoted 11 lines
arch/arm/mach-efm32/dtmachine.c new file +4 -0
+	.timer = &efm32_timer,
+	.init_machine = efm32_init,
+	.dt_compat = efm32gg_compat,
+MACHINE_END
arch/arm/mach-efm32/include/mach/io.h new file +0 -0
new file mode 100644
index 0000000..00b6af6

arch/arm/mach-efm32/include/mach/io.h line 4

Quoted 4 lines
arch/arm/mach-efm32/include/mach/io.h new file +4 -0
@@ -0,0 +1,7 @@
+#ifndef __MACH_IO_H__
+#define __MACH_IO_H__
+
+#define __io(a)			__typesafe_io(a)

Do you support io ports on this platform? If not then perhaps select NO_IPORT and define __io(a) to 0?

arch/arm/mach-efm32/include/mach/irqs.h line 4

Quoted 13 lines
arch/arm/mach-efm32/include/mach/io.h new file +3 -0
+#define __mem_pci(a)		(a)
+
+#endif /* __MACH_IO_H__ */
arch/arm/mach-efm32/include/mach/irqs.h new file +4 -0
new file mode 100644
index 0000000..5fa84db
@@ -0,0 +1,6 @@
+#ifndef __MACH_IRQS_H__
+#define __MACH_IRQS_H__
+
+#define NR_IRQS			37

Is it possible to use sparse irq and have the nvic driver allocate the irq descs? I guess multi-platform is less likely on platforms with small amounts of RAM though...

Quoted 16 lines
arch/arm/mach-efm32/include/mach/irqs.h new file +2 -0
+
+#endif /* __MACH_IRQS_H__ */
arch/arm/mach-efm32/include/mach/system.h new file +8 -0
new file mode 100644
index 0000000..619222c
@@ -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();

arch/arm/mach-efm32/include/mach/system.h line 18

Quoted 10 lines
arch/arm/mach-efm32/include/mach/system.h new file +10 -0
+}
+
+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__ */

I guess this would need to be rebased on Russell and Nicolas' series' that remove arch_idle() and arch_reset() from here and moves the reset into the common.c portion and part of the machine desc.

Quoted 6 lines
arch/arm/mach-efm32/time.c new file +0 -0
new file mode 100644
index 0000000..65f93a3

arch/arm/mach-efm32/time.c line 10

Quoted 10 lines
arch/arm/mach-efm32/time.c new file +10 -0
@@ -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>

Perhaps:

#include "common.h"

to make it clear it's a local include?

arch/arm/mach-efm32/time.c line 12

Quoted 2 lines
arch/arm/mach-efm32/time.c new file +2 -0
+
+#define BASEADDR_TIMER(n)		IOMEM(0x40010000 + (n) * 0x400)

Is it not possible to get this from the device tree rather than hard coding it?

Quoted 22 lines
arch/arm/mach-efm32/time.c new file +22 -0
+
+#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
+

arch/arm/mach-efm32/time.c line 44

Quoted 10 lines
arch/arm/mach-efm32/time.c new file +10 -0
+#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);

I believe writel_relaxed() is preferred where possible over __raw_writel().

Quoted 103 lines
arch/arm/mach-efm32/time.c new file +103 -0
+}
+
+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);
+

arch/arm/mach-efm32/time.c line 157

Quoted 10 lines
arch/arm/mach-efm32/time.c new file +10 -0
+	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);

Do you need to use setup_irq() here? For picoxcell I used device tree and so irq_of_parse_and_map() but that seemed fine for getting a timer up and running.

Jamie

#2 lore raw

RFC PATCH 1/1 ARM: new architecture for Energy Micro's EFM32 Cortex-M3 SoCs

Hi Jamie,

Quoted 2 lines · Jamie Iles

Just nits inline, but otherwise looks nice! Certainly seems like an interesting platform!

Yes it is. I hope to get the silicon version of it in January, this will have 4 MiB of RAM and so might be better suited for Linux. (Currently when booting with dt I get a prompt but starting a program makes the kernel BUG because there isn't enough memory.)

Quoted 21 lines · Uwe Kleine-K?nig

[...]

Quoted 19 lines
arch/arm/boot/dts/efm32gg-dk3750.dts new file +13 -0
new file mode 100644
index 0000000..a4aa4f3
@@ -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";
Quoted 8 lines

arch/arm/boot/dts/efm32gg-dk3750.dts line 17

Quoted 4 lines
arch/arm/boot/dts/efm32gg-dk3750.dts new file +4 -0
+		interrupt-controller;
+		#interrupt-cells = <1>;
+		reg = <0xe0000000 0x4000>;
+	};

I guess there's some patches somewhere else for the nvic binding, but I thought that the nvic had a reasonable number of features like edge/level etc and so would have more than one intererupt cell...

This dt stuff is all quite new to me. I will check what needs to be done here. Support for the nvic is part of Catalin's patches that need some more love to be included in mainline.

Quoted 7 lines

arch/arm/boot/dts/efm32gg-dk3750.dts line 23

Quoted 5 lines
arch/arm/boot/dts/efm32gg-dk3750.dts new file +5 -0
+	chosen {
+		bootargs = "console=ttyefm1,115200 init=/linuxrc ignore_loglevel ihash_entries=64 dhash_entries=64";
+	};
+
+	memory {

I think "memory at 80000000" is preferred here.

ok.

Quoted 24 lines
Quoted 24 lines
arch/arm/boot/dts/efm32gg-dk3750.dts new file +18 -0
+		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";
+		};
+	};
+};
arch/arm/mach-efm32/common.h new file +0 -0
new file mode 100644
index 0000000..f545224
Quoted 8 lines

arch/arm/mach-efm32/common.h line 5

Quoted 6 lines
arch/arm/mach-efm32/common.h new file +5 -0
@@ -0,0 +1,7 @@
+#ifdef __ASSEMBLER__
+#define IOMEM(addr)     (addr)
+#else
+#define IOMEM(addr)     ((void __force __iomem *)(addr))
+#endif

It looks like the only users of IOMEM are for the timers. Is it

I still wait for this to be moved to generic ARM code.

Quoted 2 lines

possible to get the timer information from the device tree then remove this?

Probably yes. Though I'll have to wait for the bigger machine to test putting more into dt.

Quoted 20 lines
Quoted 20 lines
arch/arm/mach-efm32/common.h new file +1 -0
+extern struct sys_timer efm32_timer;
arch/arm/mach-efm32/dtmachine.c new file +13 -0
new file mode 100644
index 0000000..42d091c
@@ -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,
Quoted 8 lines

arch/arm/mach-efm32/dtmachine.c line 17

Quoted 4 lines
arch/arm/mach-efm32/dtmachine.c new file +4 -0
+		struct device_node *interrupt_parent)
+{
+	irq_domain_add_simple(np, 0);
+}

If the nvic driver is new then I would have expected that to have device tree and irq domain support internally. From the entry macros it looks like you're using the GIC macros, so is it pretty much GIC compatible?

Yeah, the relevant register offsets are identical. I think the nvic has less power management capabilities and supports less irqs. I will address this when I rework the nvic patch.

Quoted 7 lines
Quoted 7 lines
arch/arm/mach-efm32/dtmachine.c new file +7 -0
+
+static const struct of_device_id efm32_irq_match[] __initconst = {
+	{
+		.compatible = "arm,cortex-m3-nvic",
+		.data = efm32_nvic_add_irq_domain,
+	}, {
+		/* sentinel */
Quoted 8 lines

arch/arm/mach-efm32/dtmachine.c line 30

Quoted 6 lines
arch/arm/mach-efm32/dtmachine.c new file +6 -0
+	}
+};
+
+static void __init efm32_init(void)
+{
+	int ret;

This doesn't appear to be used.

yeah, that's only used in my tree that has a few more in the init routine.

Quoted 4 lines

arch/arm/mach-efm32/dtmachine.c line 32

Quoted 2 lines
arch/arm/mach-efm32/dtmachine.c new file +2 -0
+
+	of_irq_init(efm32_irq_match);

Shouldn't this be in a .init_irq function()?

I think I copied that from imx. So if you're right imx needs fixing, too.

Quoted 5 lines
Quoted 5 lines
arch/arm/mach-efm32/dtmachine.c new file +5 -0
+
+	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+}
+
+static const char *const efm32gg_compat[] __initconst = {
Quoted 8 lines

arch/arm/mach-efm32/dtmachine.c line 43

Quoted 6 lines
arch/arm/mach-efm32/dtmachine.c new file +6 -0
+	"efm32,dk3750",
+	NULL
+};
+
+DT_MACHINE_START(EFM32DT, "EFM32 (Device Tree Support)")
+	.init_irq = nvic_init,

This doesn't exist...

yeah, see above.

Quoted 59 lines
Quoted 11 lines
arch/arm/mach-efm32/dtmachine.c new file +4 -0
+	.timer = &efm32_timer,
+	.init_machine = efm32_init,
+	.dt_compat = efm32gg_compat,
+MACHINE_END
arch/arm/mach-efm32/include/mach/io.h new file +0 -0
new file mode 100644
index 0000000..00b6af6

arch/arm/mach-efm32/include/mach/io.h line 4

Quoted 4 lines
arch/arm/mach-efm32/include/mach/io.h new file +4 -0
@@ -0,0 +1,7 @@
+#ifndef __MACH_IO_H__
+#define __MACH_IO_H__
+
+#define __io(a)			__typesafe_io(a)

Do you support io ports on this platform? If not then perhaps select NO_IPORT and define __io(a) to 0?

arch/arm/mach-efm32/include/mach/irqs.h line 4

Quoted 13 lines
arch/arm/mach-efm32/include/mach/io.h new file +3 -0
+#define __mem_pci(a)		(a)
+
+#endif /* __MACH_IO_H__ */
arch/arm/mach-efm32/include/mach/irqs.h new file +4 -0
new file mode 100644
index 0000000..5fa84db
@@ -0,0 +1,6 @@
+#ifndef __MACH_IRQS_H__
+#define __MACH_IRQS_H__
+
+#define NR_IRQS			37

Is it possible to use sparse irq and have the nvic driver allocate the irq descs? I guess multi-platform is less likely on platforms with small amounts of RAM though...

Quoted 22 lines
arch/arm/mach-efm32/include/mach/irqs.h new file +2 -0
+
+#endif /* __MACH_IRQS_H__ */
arch/arm/mach-efm32/include/mach/system.h new file +14 -0
new file mode 100644
index 0000000..619222c
@@ -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();
Quoted 8 lines

arch/arm/mach-efm32/include/mach/system.h line 18

Quoted 4 lines
arch/arm/mach-efm32/include/mach/system.h new file +4 -0
+	__raw_writel(0x05fa0004, (void __iomem *)0xe000ed0c);
+	dsb();
+}
+#endif /* __MACH_SYSTEM_H__ */

I guess this would need to be rebased on Russell and Nicolas' series' that remove arch_idle() and arch_reset() from here and moves the reset into the common.c portion and part of the machine desc.

Yeah, I'm aware of these changes.

Quoted 19 lines
Quoted 6 lines
arch/arm/mach-efm32/time.c new file +0 -0
new file mode 100644
index 0000000..65f93a3

arch/arm/mach-efm32/time.c line 10

Quoted 10 lines
arch/arm/mach-efm32/time.c new file +10 -0
@@ -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>

Perhaps:

Quoted 3 lines

#include "common.h"

to make it clear it's a local include?

good catch.

Quoted 152 lines

arch/arm/mach-efm32/time.c line 12

Quoted 3 lines
arch/arm/mach-efm32/time.c new file +2 -0
+
+#define BASEADDR_TIMER(n)		IOMEM(0x40010000 + (n) * 0x400)

Is it not possible to get this from the device tree rather than hard coding it?

Quoted 22 lines
arch/arm/mach-efm32/time.c new file +22 -0
+
+#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
+

arch/arm/mach-efm32/time.c line 44

Quoted 10 lines
arch/arm/mach-efm32/time.c new file +10 -0
+#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);

I believe writel_relaxed() is preferred where possible over __raw_writel().

Quoted 109 lines
arch/arm/mach-efm32/time.c new file +109 -0
+}
+
+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));
Quoted 8 lines

arch/arm/mach-efm32/time.c line 157

Quoted 4 lines
arch/arm/mach-efm32/time.c new file +4 -0
+
+	efm32_timer_write(TIMER_CLOCKEVENT, TIMERn_IRQ_UF, TIMERn_IEN);
+
+	setup_irq(IRQ_CLOCKEVENT, &efm32_clock_event_irq);

Do you need to use setup_irq() here? For picoxcell I used device tree and so irq_of_parse_and_map() but that seemed fine for getting a timer up and running.

Ok, I will evaluate that.

Thanks for your feedback,
Uwe

Signature · 2 lines
Pengutronix e.K.                           | Uwe Kleine-K?nig            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
#3 lore raw

RFC PATCH 1/1 ARM: new architecture for Energy Micro's EFM32 Cortex-M3 SoCs

In reply to Jamie Iles
Quoted 4 lines · Jamie Iles
Quoted 1 line

+#define __io(a) __typesafe_io(a)

Do you support io ports on this platform? If not then perhaps select NO_IPORT and define __io(a) to 0?

Do not do that. Define NO_IOPORT and *do* *not* define __io() at all to make drivers which use PCI IO stuff fail at build time. Don't try to frig it into doing a NULL pointer deref.

#4 lore raw

RFC PATCH 1/1 ARM: new architecture for Energy Micro's EFM32 Cortex-M3 SoCs

Quoted 9 lines · Russell King - ARM Linux
Quoted 4 lines · Jamie Iles
Quoted 1 line

+#define __io(a) __typesafe_io(a)

Do you support io ports on this platform? If not then perhaps select NO_IPORT and define __io(a) to 0?

Do not do that. Define NO_IOPORT and *do* *not* define __io() at all to make drivers which use PCI IO stuff fail at build time. Don't try to frig it into doing a NULL pointer deref.

Fair point. I said that because I remember that the 8250 driver uses ioport accessors even for !HAVE_IOPORT and that some patches to remove that were nacked, but I don't remember the details. But yes, not defining __io would be much better!

Jamie

#5 lore raw

RFC PATCH 1/1 ARM: new architecture for Energy Micro's EFM32 Cortex-M3 SoCs

In reply to Russell King - ARM Linux
Quoted 9 lines · Russell King - ARM Linux
Quoted 4 lines · Jamie Iles
Quoted 1 line

+#define __io(a) __typesafe_io(a)

Do you support io ports on this platform? If not then perhaps select NO_IPORT and define __io(a) to 0?

Do not do that. Define NO_IOPORT and *do* *not* define __io() at all to make drivers which use PCI IO stuff fail at build time. Don't try to frig it into doing a NULL pointer deref.

... especially since this is a !MMU platform and therefore NULL pointer derefs won't work as intended.

Nicolas

#6 lore raw

RFC PATCH 1/1 ARM: new architecture for Energy Micro's EFM32 Cortex-M3 SoCs

In reply to Uwe Kleine-König
Quoted 10 lines · Uwe Kleine-K?nig

---
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.

Quoted 6 lines

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.

Hi Uwe,

It's great to see you managed to get the port done using the device tree. You mentioned earlier that you were worried about the object size overhead in doing so. Do you have any numbers already about the actual memory consumption of the DT based port compared to your earlier one?

arch/arm/boot/dts/efm32gg-dk3750.dts line 21

Quoted 3 lines
arch/arm/boot/dts/efm32gg-dk3750.dts new file +3 -0
+	chosen {
+		bootargs = "console=ttyefm1,115200 init=/linuxrc ignore_loglevel ihash_entries=64 dhash_entries=64";
+	};

I don't know how we do this on other boards, but is it intentional to leave the bootargs in the device tree source? I would expect that they normally would have to be changed by the boot loader.

Quoted 4 lines
arch/arm/boot/dts/efm32gg-dk3750.dts new file +4 -0
+	soc {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "simple-bus";

arch/arm/boot/dts/efm32gg-dk3750.dts line 40

Quoted 10 lines
arch/arm/boot/dts/efm32gg-dk3750.dts new file +10 -0
+		interrupt-parent = <&nvic>;
+		ranges;
+
+		uart1: uart at 0x4000c400 { /* USART1 */
+			compatible = "efm32,usart";
+			reg = <0x4000c400 0x400>;
+			interrupts = <15>;
+			status = "ok";
+		};
+	};

This will have to grow to include the other devices for inclusion, right?

arch/arm/mach-efm32/Makefile line 3

Quoted 9 lines
arch/arm/mach-efm32/Makefile new file +3 -0
new file mode 100644
index 0000000..10a3426
@@ -0,0 +1,3 @@
+obj-y += clk.o time.o
+
+obj-$(CONFIG_OF) += dtmachine.o

I think you can rename the dtmachine.c file into common.c or similar and build it unconditionally, as we do on the other new platforms.

arch/arm/mach-efm32/include/mach/io.h line 7

Quoted 13 lines
arch/arm/mach-efm32/include/mach/io.h new file +7 -0
new file mode 100644
index 0000000..00b6af6
@@ -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__ */

Can you already build with __io not defined? That should catch any drivers that try to access the PC-style I/O ports.

arch/arm/mach-efm32/include/mach/io.h

Quoted 7 lines
arch/arm/mach-efm32/include/mach/io.h new file +7 -0
+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();
+}

I'd like to hear other opinions on this one. It's clearly an ugly hack to just poke a random MMIO register for reset, but we could decide that it's useful enough to keep the implementation just like this because a cleaner implementation using a device_node and io_iomap just causes overhead that you don't want to spend a system that is so limited on memory.

As Jamie mentioned, using writel_relaxed() would be better here and cause no overhead.

arch/arm/mach-efm32/include/mach/io.h

Quoted 2 lines
arch/arm/mach-efm32/include/mach/io.h new file +2 -0
+/* just a bogus value */
+#define CLOCK_TICK_RATE	12345678

I think you should actually pick a number that results in no rounding in the jiffies code. 1000000 would be a reasonable choice, as would any other multiple of HZ.

arch/arm/mach-efm32/time.c

Quoted 5 lines
arch/arm/mach-efm32/time.c new file +0 -0
new file mode 100644
index 0000000..65f93a3

I think Jamie already said everything necessary about this file.

arch/arm/mm/Kconfig config CPU_V7 · line 438

Quoted 11 lines
arch/arm/mm/Kconfig +1 -1
@@ -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

Hmm, since you *have* to enable this option for a successful build, wouldn't it make more sense to 'select' this from ARCH_EFM32.

Arnd

#7 lore raw

RFC PATCH 1/1 ARM: new architecture for Energy Micro's EFM32 Cortex-M3 SoCs

Quoted 8 lines · Arnd Bergmann
Quoted 8 lines
arch/arm/mach-efm32/include/mach/io.h new file +2 -0
new file mode 100644
index 0000000..00b6af6
@@ -0,0 +1,7 @@
+#ifndef __MACH_IO_H__
+#define __MACH_IO_H__
Quoted 8 lines

arch/arm/mach-efm32/include/mach/io.h line 7

Quoted 5 lines
arch/arm/mach-efm32/include/mach/io.h new file +5 -0
+
+#define __io(a)			__typesafe_io(a)
+#define __mem_pci(a)		(a)
+
+#endif /* __MACH_IO_H__ */

Can you already build with __io not defined? That should catch any drivers that try to access the PC-style I/O ports.

And don't forget to select NO_IOPORT in the configuration as well.

Quoted 6 lines
Quoted 6 lines
arch/arm/mm/Kconfig +0 -0
@@ -432,7 +432,7 @@ config CPU_V7
 # ARMv7
 config CPU_V7M
 	bool "Support ARMv7-M processors"
Quoted 8 lines

arch/arm/mm/Kconfig config CPU_V7 · line 438

Quoted 5 lines
arch/arm/mm/Kconfig +1 -1
-	depends on MACH_REALVIEW_EB && EXPERIMENTAL
+	depends on (MACH_REALVIEW_EB && EXPERIMENTAL) || ARCH_EFM32
 	select THUMB2_KERNEL
 	select ARM_THUMB
 	select CPU_32v7M

Hmm, since you *have* to enable this option for a successful build, wouldn't it make more sense to 'select' this from ARCH_EFM32.

It'd help if Catalin conformed to the well established way in the rest of the CPU specific configuration. Rather than making this depend on realview etc, make the presentation of the prompt conditional on realview etc. Then ARCH_EFM32 can simply select CPU_V7M without generating any warnings.

In other words:

config CPU_whatever
bool "whatever" if <symbols-which-may-have-this-cpu>

and elsewhere:

config PLATFORM_which_always_has_CPU_whatever
select CPU_whatever

#8 lore raw

RFC PATCH 1/1 ARM: new architecture for Energy Micro's EFM32 Cortex-M3 SoCs

In reply to Uwe Kleine-König

Hi Uwe,

2011/12/21 Uwe Kleine-K?nig <[email protected]>:

Quoted 1 line

If you're interested in the details I can publish my tree I think.

Definitely interested - it could be very nice if you can publish that tree.

Thanks!
Ohad.

#9 lore raw

RFC PATCH 1/1 ARM: new architecture for Energy Micro's EFM32 Cortex-M3 SoCs

Quoted 6 lines · Ohad Ben-Cohen

Hi Uwe,

2011/12/21 Uwe Kleine-K?nig <[email protected]>:

Quoted 1 line

If you're interested in the details I can publish my tree I think.

Definitely interested - it could be very nice if you can publish that tree.

OK, it's online now at

	http://git.pengutronix.de/?p=ukl/linux-2.6.git;a=shortlog;h=refs/heads/efm32
	git://git.pengutronix.de/git/ukl/linux.git efm32

Please note that this is work in progress and has some rough edges. ... but at least it boots on my machine.

Best regards
Uwe

Signature · 2 lines
Pengutronix e.K.                           | Uwe Kleine-K?nig            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
#11 lore raw

RFC PATCH 1/1 ARM: new architecture for Energy Micro's EFM32 Cortex-M3 SoCs

In reply to Uwe Kleine-König
Quoted 6 lines · Uwe Kleine-K?nig
Quoted 5 lines · Ohad Ben-Cohen

Hi Uwe,

2011/12/21 Uwe Kleine-K?nig <[email protected]>:

Quoted 1 line

If you're interested in the details I can publish my tree I think.

Quoted 8 lines
Quoted 1 line

Definitely interested - it could be very nice if you can publish that tree.

OK, it's online now at

	http://git.pengutronix.de/?p=ukl/linux-2.6.git;a=shortlog;h=refs/heads/efm32
	git://git.pengutronix.de/git/ukl/linux.git efm32

Please note that this is work in progress and has some rough edges. ... but at least it boots on my machine.

Some of your patches seem weird, like this:

        for (i = 0; i < ARRAY_SIZE(cache_policies); i++) {

- int len = strlen(cache_policies[i].policy);
-
- if (memcmp(p, cache_policies[i].policy, len) == 0) {
+ if (strcmp(p, cache_policies[i].policy) == 0) {

Please compare the two, specifically how many bytes of the strings in each case are compared. I think you'll find it's not an equivalent change, and you need to discard the patch.

This looks buggy:
+#if defined(CONFIG_CPU_V7M)
+ .macro setmode, mode, reg
+ .endm
+#elif defined(CONFIG_THUMB2_KERNEL)

        .macro  setmode, mode, reg
        mov     \reg, #\mode
        msr     cpsr_c, \reg

as it does nothing about the interrupt mask.

The VFP stuff - adding 'clean' which is kernel state to the _user_ _exported_ VFP hardware structure is a bad idea. So this needlessly causes a variation in the kernels userspace API. Please find somewhere else to keep kernel internal state. (As that patch comes from Catalin, then that comment is directed to Catalin.)

In "mtd/maps: uclinux: fix sparse warnings and coding style":

-struct map_info uclinux_ram_map = {

+static struct map_info uclinux_ram_map = {
        .name = "RAM",
-       .phys = (unsigned long)&_ebss,
-       .size = 0,
+       .phys = (resource_size_t)&_ebss,
+       .bankwidth = 4,

This doesn't match the description - it's a functional change. Basic rule of kernel patch generation: functional changes must _never_ _ever_, not in a million years, be mixed with such patches.

+ *virt = (__force void *)(map->virt + from);

This says "wrong" to me loudly - by the mere fact that you're using __force. If you're having to do that, leave the sparse warning in place. Sparse is _not_ a tool which must produce zero warnings for kernel code. Sparse is an auditing tool, and as such there are valid reasons to ignore warnings. This is one of them.

No driver should ever use __force within its code or definitions. That priviledge is reserved for stuff like arch code inside IO accessors where it really knows that it's safe.

In "ARM: new architecture for ..." I expected to find a new arch/* directory. What you mean is "new platform" or "new machine" not "new architecture". Maybe "new sub-architecture". Good to see you're selecting NO_IOPORT here though.

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

Ok, so EFM32 may or may not have a V7M CPU. Cool. What does it have when it doesn't have a V7M CPU? (Please fix it, like all the other CPU stuff in this file, so that the prompt appears for those which _may_ have it, but it's selected by those which must _always_ have it.)

Your code in arch/arm/mach-efm32/include/mach/system.h won't be called as of this merge window - that's something you should consider fixing.

In your serial driver, you really need to deal with which modes you can't set sanely: ask Alan Cox how to do this. POSIX has a requirement that termios modes which can't be set should be reported back. (eg, if you can only do one stop bit, don't blindly ignore the request for two stop bits.)

#12 lore raw

RFC PATCH 1/1 ARM: new architecture for Energy Micro's EFM32 Cortex-M3 SoCs

Quoted 15 lines · Russell King - ARM Linux
Quoted 14 lines · Uwe Kleine-K?nig
Quoted 6 lines · Ohad Ben-Cohen

Hi Uwe,

2011/12/21 Uwe Kleine-K?nig <[email protected]>:

Quoted 1 line

If you're interested in the details I can publish my tree I think.

Definitely interested - it could be very nice if you can publish that tree.

OK, it's online now at

	http://git.pengutronix.de/?p=ukl/linux-2.6.git;a=shortlog;h=refs/heads/efm32
	git://git.pengutronix.de/git/ukl/linux.git efm32

Please note that this is work in progress and has some rough edges. ... but at least it boots on my machine.

...

Quoted 10 lines

This looks buggy:

(quoted diff) +4 -0
+#if defined(CONFIG_CPU_V7M)
+       .macro  setmode, mode, reg
+       .endm
+#elif defined(CONFIG_THUMB2_KERNEL)
        .macro  setmode, mode, reg
        mov     \reg, #\mode
        msr     cpsr_c, \reg

as it does nothing about the interrupt mask.

The interrupt mask is not part of CPSR on the M profile, so setmode didn't make much sense. Looking through the kernel, there are only two calls to setmode that would disable interrupts, so we need to make sure they are indeed disabled (though given how the M profile works, with priorities, we could already be safe; it needs checking though).

Quoted 5 lines

The VFP stuff - adding 'clean' which is kernel state to the _user_ _exported_ VFP hardware structure is a bad idea. So this needlessly causes a variation in the kernels userspace API. Please find somewhere else to keep kernel internal state. (As that patch comes from Catalin, then that comment is directed to Catalin.)

Are you sure we export vfp_hard_struct to user? That's a kernel-only structure (and it's not by any means stable, given the number of #ifdef's it has). I would also argue that 'clean' is a hardware state (inferred from the exception return value).

Signature · 1 line
Catalin
#13 lore raw

RFC PATCH 1/1 ARM: new architecture for Energy Micro's EFM32 Cortex-M3 SoCs

Quoted 11 lines · Catalin Marinas
Quoted 5 lines · Russell King - ARM Linux

The VFP stuff - adding 'clean' which is kernel state to the _user_ _exported_ VFP hardware structure is a bad idea. So this needlessly causes a variation in the kernels userspace API. Please find somewhere else to keep kernel internal state. (As that patch comes from Catalin, then that comment is directed to Catalin.)

Are you sure we export vfp_hard_struct to user? That's a kernel-only structure (and it's not by any means stable, given the number of #ifdef's it has). I would also argue that 'clean' is a hardware state (inferred from the exception return value).

Actually, looking at arch/arm/kernel/ptrace.c, we only export the fpregs and fpscr, so this should be fine.

Still, I don't see why we need this 'clean' state, when normal VFP doesn't need it. Maybe you could explain why it's necessary?

#14 lore raw

RFC PATCH 1/1 ARM: new architecture for Energy Micro's EFM32 Cortex-M3 SoCs

Quoted 13 lines · Russell King - ARM Linux
Quoted 11 lines · Catalin Marinas
Quoted 5 lines · Russell King - ARM Linux

The VFP stuff - adding 'clean' which is kernel state to the _user_ _exported_ VFP hardware structure is a bad idea. So this needlessly causes a variation in the kernels userspace API. Please find somewhere else to keep kernel internal state. (As that patch comes from Catalin, then that comment is directed to Catalin.)

Are you sure we export vfp_hard_struct to user? That's a kernel-only structure (and it's not by any means stable, given the number of #ifdef's it has). I would also argue that 'clean' is a hardware state (inferred from the exception return value).

Quoted 5 lines

Actually, looking at arch/arm/kernel/ptrace.c, we only export the fpregs and fpscr, so this should be fine.

Still, I don't see why we need this 'clean' state, when normal VFP doesn't need it. Maybe you could explain why it's necessary?

The FP support on the M profile is a bit different (in terms of control registers) from the A/R profiles. The M profile has built-in knowledge of the AAPCS for automatically preservation of certain registers during exceptions and it can also do lazy saving of the S0-S15 FP registers. In general the M processors are meant to be simpler to use without a complex OS (especially if thread switching is done synchronously at SVC time rather than during interrupts).

For example, when user space gets an exception, the M core saves R0-R3, R12, LR, PC, xPSR on the user stack automatically. It also preserves space for S0-S15 and FPSCR but does not save them (it remembers the address though for lazy saving if new thread uses the FP). If another thread tries to use the FP for the first time, it saves the old FP state and initialises a new one for the current thread automatically. But once a thread touched the FP, its state is no longer 'clean' and it is automatically loaded (non-lazily) from the stack when switching to such thread while saving the old one on the previous stack. When switching between two threads that have never used the FP, the processor does not do any FP state switching.

However, it does not save S16-S31 as the AAPCS specifies that they are caller-saved. Linux needs to do this and it uses the 'clean' state to detect whether a thread used the FP or not. The disadvantage is that if a thread ever used FP, its state is always saved/restored at context switch.

Of course, we can change all this and disable the hardware automatic saving while implementing a pure software lazy switching solution using the CPACR (there is no FPEXC). But at the time this was the simplest implementation, given that Cortex-M3 doesn't even have an FP unit (it came with Cortex-M4) and most M3 software around didn't touch the FP at all.

BTW, if you are fine with trying to get the M support into mainline, I'm happy to revisit the code (probably with Uwe's help given that I don't have much time available).

Signature · 1 line
Catalin
#15 lore raw

RFC PATCH 1/1 ARM: new architecture for Energy Micro's EFM32 Cortex-M3 SoCs

In reply to Russell King - ARM Linux

Hello Russell,

I'm glad you're replying though my tree was just intended to show a prove of concept, not a tree ready to be merged.

Quoted 24 lines · Russell King - ARM Linux
Quoted 6 lines · Uwe Kleine-K?nig
Quoted 5 lines · Ohad Ben-Cohen

Hi Uwe,

2011/12/21 Uwe Kleine-K?nig <[email protected]>:

Quoted 1 line

If you're interested in the details I can publish my tree I think.

Quoted 8 lines
Quoted 1 line

Definitely interested - it could be very nice if you can publish that tree.

OK, it's online now at

	http://git.pengutronix.de/?p=ukl/linux-2.6.git;a=shortlog;h=refs/heads/efm32
	git://git.pengutronix.de/git/ukl/linux.git efm32

Please note that this is work in progress and has some rough edges. ... but at least it boots on my machine.

Some of your patches seem weird, like this:

        for (i = 0; i < ARRAY_SIZE(cache_policies); i++) {
(quoted diff) +1 -3
-               int len = strlen(cache_policies[i].policy);
-
-               if (memcmp(p, cache_policies[i].policy, len) == 0) {
+               if (strcmp(p, cache_policies[i].policy) == 0) {
Quoted 3 lines

Please compare the two, specifically how many bytes of the strings in each case are compared. I think you'll find it's not an equivalent change, and you need to discard the patch.

OK, so you say it is intended that I can do

	early_cachepolicy("writebackyourmother");

with the same result as

	early_cachepolicy("writeback");

? Maybe a comment would be great to have in place then about why this is done. I couldn't find a user apart from

	early_param("cachepolicy", early_cachepolicy);

and this parameter isn't described in
Documentation/kernel-parameters.txt.

I would understand if only strlen(p) bytes were checked which would allow to shorten the parameter. (But then

cachepolicy=

would have a meaning (though I didn't test -- I think it would select uncached) which is strange.) All in all I put the patch there as a reminder for me to ask if everything the function does is intended.

Quoted 10 lines

This looks buggy:

(quoted diff) +4 -0
+#if defined(CONFIG_CPU_V7M)
+       .macro  setmode, mode, reg
+       .endm
+#elif defined(CONFIG_THUMB2_KERNEL)
        .macro  setmode, mode, reg
        mov     \reg, #\mode
        msr     cpsr_c, \reg

as it does nothing about the interrupt mask.

right, good catch.

Quoted 16 lines

The VFP stuff - adding 'clean' which is kernel state to the _user_ _exported_ VFP hardware structure is a bad idea. So this needlessly causes a variation in the kernels userspace API. Please find somewhere else to keep kernel internal state. (As that patch comes from Catalin, then that comment is directed to Catalin.)

In "mtd/maps: uclinux: fix sparse warnings and coding style":

(quoted diff) +3 -3
-struct map_info uclinux_ram_map = {
+static struct map_info uclinux_ram_map = {
        .name = "RAM",
-       .phys = (unsigned long)&_ebss,
-       .size = 0,
+       .phys = (resource_size_t)&_ebss,
+       .bankwidth = 4,
Quoted 2 lines

This doesn't match the description - it's a functional change. Basic rule of kernel patch generation: functional changes must _never_ _ever_, not in

I stumbled about that, too, but a deeper look shows that

	.bankwidth = 4,

was introduced in place of the removed

	mapp->bankwidth = 4;

in uclinux_mtd_init(). I didn't check the details because it's not my patch and I don't intend (yet) to mainline it myself. (Marc: hint hint!)

Quoted 10 lines

a million years, be mixed with such patches.

+ *virt = (__force void *)(map->virt + from);

This says "wrong" to me loudly - by the mere fact that you're using __force. If you're having to do that, leave the sparse warning in place. Sparse is _not_ a tool which must produce zero warnings for kernel code. Sparse is an auditing tool, and as such there are valid reasons to ignore warnings. This is one of them.

Quoted 8 lines

No driver should ever use __force within its code or definitions. That priviledge is reserved for stuff like arch code inside IO accessors where it really knows that it's safe.

In "ARM: new architecture for ..." I expected to find a new arch/* directory. What you mean is "new platform" or "new machine" not "new architecture". Maybe "new sub-architecture". Good to see you're selecting NO_IOPORT here though.

Yeah, I learned NO_IOPORT from the first post of the "new architecture" patch :-)

Quoted 8 lines

config CPU_V7M
bool "Support ARMv7-M processors"

arch/arm/mm/Kconfig +1 -1
-       depends on MACH_REALVIEW_EB && EXPERIMENTAL
+       depends on (MACH_REALVIEW_EB && EXPERIMENTAL) || ARCH_EFM32
        select THUMB2_KERNEL
        select ARM_THUMB
        select CPU_32v7M
Quoted 4 lines

Ok, so EFM32 may or may not have a V7M CPU. Cool. What does it have when it doesn't have a V7M CPU? (Please fix it, like all the other CPU stuff in this file, so that the prompt appears for those which _may_ have it, but it's selected by those which must _always_ have it.)

Yeah, this was pointed out in the review of my first post, too. Didn't come around yet to fix that.

Quoted 2 lines

Your code in arch/arm/mach-efm32/include/mach/system.h won't be called as of this merge window - that's something you should consider fixing.

yeah, I'm aware of this change and intend to update the base when 3.3-rc1 is out.

Quoted 5 lines

In your serial driver, you really need to deal with which modes you can't set sanely: ask Alan Cox how to do this. POSIX has a requirement that termios modes which can't be set should be reported back. (eg, if you can only do one stop bit, don't blindly ignore the request for two stop bits.)

Ah, I thought CSTOPB means "use 1 stop bit" but it seems to request 2. Will fix and send out a v3.

Thanks for your time
Uwe

Signature · 2 lines
Pengutronix e.K.                           | Uwe Kleine-K?nig            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |