Skip to contentSkip to conversation

Add support for the SPMP8000 SoC and Letcool board

PATCH New Zoltan Devai 12 participants 78 messages Oct 9, 2011 – Nov 3, 2011 1 message not in the archive

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

Hi,

This series adds support for the Sunplus SPMP8000 SoC, an ARM926EJ-S based MCU, which is most commonly found in handheld game consoles and digital cams. Most of the work was done during GSoC 2011, with Greg KH as my mentor.

The board I'm using for development is the Letcool N350JP handheld.

The patches are based on Arnds for-next branch with Jamie Iles' vic-dt patches on top.

No static platform devices are used, everything is inited from DT, with some hacks for drivers that don't have any bindings yet. (Documentation for own, custom bindings will follow in next version.)

So far, these peripherals are supported, with drivers not submitted yet:
gpio, fb, cpufreq, slave-dma, mmc, ASoC (i2s, codec, pcm, card).

Reviews, instructions for mainlining it are very welcome.

Thanks, Cheers,
Zoltan

 arch/arm/Kconfig                                  |   16 +
 arch/arm/Makefile                                 |    1 +
 arch/arm/boot/dts/spmp8000-letcool.dts            |  163 ++++++
 arch/arm/boot/dts/spmp8000.dtsi                   |  169 ++++++
 arch/arm/common/vic.c                             |    1 -
 arch/arm/mach-spmp8000/Makefile                   |   11 +
 arch/arm/mach-spmp8000/Makefile.boot              |    1 +
 arch/arm/mach-spmp8000/adc.c                      |  465 ++++++++++++++++
 arch/arm/mach-spmp8000/board_letcool.c            |  154 ++++++
 arch/arm/mach-spmp8000/clkdev.c                   |  586 +++++++++++++++++++++
 arch/arm/mach-spmp8000/clock.c                    |  155 ++++++
 arch/arm/mach-spmp8000/core.c                     |  103 ++++
 arch/arm/mach-spmp8000/include/mach/clock.h       |   37 ++
 arch/arm/mach-spmp8000/include/mach/core.h        |   29 +
 arch/arm/mach-spmp8000/include/mach/debug-macro.S |   19 +
 arch/arm/mach-spmp8000/include/mach/dma.h         |   45 ++
 arch/arm/mach-spmp8000/include/mach/entry-macro.S |   14 +
 arch/arm/mach-spmp8000/include/mach/gpio.h        |   21 +
 arch/arm/mach-spmp8000/include/mach/hardware.h    |   27 +
 arch/arm/mach-spmp8000/include/mach/io.h          |   20 +
 arch/arm/mach-spmp8000/include/mach/irqs.h        |   21 +
 arch/arm/mach-spmp8000/include/mach/memory.h      |   16 +
 arch/arm/mach-spmp8000/include/mach/regs-timer.h  |   32 ++
 arch/arm/mach-spmp8000/include/mach/scu.h         |  146 +++++
 arch/arm/mach-spmp8000/include/mach/spmp8000adc.h |   29 +
 arch/arm/mach-spmp8000/include/mach/spmp8000fb.h  |   32 ++
 arch/arm/mach-spmp8000/include/mach/system.h      |   45 ++
 arch/arm/mach-spmp8000/include/mach/timex.h       |   17 +
 arch/arm/mach-spmp8000/include/mach/uncompress.h  |   37 ++
 arch/arm/mach-spmp8000/include/mach/vmalloc.h     |   16 +
 arch/arm/mach-spmp8000/pinmux.c                   |  131 +++++
 arch/arm/mach-spmp8000/pwm.c                      |  246 +++++++++
 arch/arm/mach-spmp8000/timer.c                    |  160 ++++++
 33 files changed, 2964 insertions(+), 1 deletions(-)
#1 lore raw

PATCH 1/9 ARM: vic: Don't write to the read-only register VIC_IRQ_STATUS

This is unneeded and causes an abort on the SPMP8000 platform.

---
 arch/arm/common/vic.c |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)
arch/arm/common/vic.c +0 -1
index b22b83d..651c77d 100644
@@ -274,7 +274,6 @@ static void __init vic_disable(void __iomem *base)
 	writel(0, base + VIC_INT_SELECT);
 	writel(0, base + VIC_INT_ENABLE);
 	writel(~0, base + VIC_INT_ENABLE_CLEAR);
-	writel(0, base + VIC_IRQ_STATUS);
 	writel(0, base + VIC_ITCR);
 	writel(~0, base + VIC_INT_SOFT_CLEAR);
 }
Signature · 1 line
1.7.4.1
#2 lore raw

PATCH 1/9 ARM: vic: Don't write to the read-only register VIC_IRQ_STATUS

2011/10/9 Zoltan Devai <[email protected]>:

Quoted 12 lines

This is unneeded and causes an abort on the SPMP8000 platform.

---
?arch/arm/common/vic.c | ? ?1 -
?1 files changed, 0 insertions(+), 1 deletions(-)

arch/arm/common/vic.c +0 -0
index b22b83d..651c77d 100644

arch/arm/common/vic.c static void __init vic_disable(void __iomem *base) · line 279

Quoted 7 lines
arch/arm/common/vic.c +0 -1
@@ -274,7 +274,6 @@ static void __init vic_disable(void __iomem *base)
 ? ? ? ?writel(0, base + VIC_INT_SELECT);
 ? ? ? ?writel(0, base + VIC_INT_ENABLE);
 ? ? ? ?writel(~0, base + VIC_INT_ENABLE_CLEAR);
- ? ? ? writel(0, base + VIC_IRQ_STATUS);
 ? ? ? ?writel(0, base + VIC_ITCR);
 ? ? ? ?writel(~0, base + VIC_INT_SOFT_CLEAR);
 ?}

Indeed.

This patch stands on its own, please put it into Russell's patch tracker at http://www.arm.linux.org.uk/developer/patches/

Yours,
Linus Walleij

#3 lore raw

PATCH 1/9 ARM: vic: Don't write to the read-only register VIC_IRQ_STATUS

2011/10/10 Linus Walleij <[email protected]>:

Quoted 24 lines

2011/10/9 Zoltan Devai <[email protected]>:

Quoted 12 lines

This is unneeded and causes an abort on the SPMP8000 platform.

---
?arch/arm/common/vic.c | ? ?1 -
?1 files changed, 0 insertions(+), 1 deletions(-)

arch/arm/common/vic.c +0 -0
index b22b83d..651c77d 100644

arch/arm/common/vic.c static void __init vic_disable(void __iomem *base) · line 279

Quoted 7 lines
arch/arm/common/vic.c +0 -1
@@ -274,7 +274,6 @@ static void __init vic_disable(void __iomem *base)
 ? ? ? ?writel(0, base + VIC_INT_SELECT);
 ? ? ? ?writel(0, base + VIC_INT_ENABLE);
 ? ? ? ?writel(~0, base + VIC_INT_ENABLE_CLEAR);
- ? ? ? writel(0, base + VIC_IRQ_STATUS);
 ? ? ? ?writel(0, base + VIC_ITCR);
 ? ? ? ?writel(~0, base + VIC_INT_SOFT_CLEAR);
 ?}

Indeed.

Quoted 4 lines

This patch stands on its own, please put it into Russell's patch tracker at http://www.arm.linux.org.uk/developer/patches/

Done, will exclude this from the next series.

#4 lore raw

PATCH 2/9 ARM: SPMP8000: Add machine base files

In reply to Zoltan Devai

Add the files for the basic support of the SPMP8000 SoC

---
 arch/arm/mach-spmp8000/Makefile                   |   11 ++
 arch/arm/mach-spmp8000/Makefile.boot              |    1 +
 arch/arm/mach-spmp8000/core.c                     |  103 +++++++++++++
 arch/arm/mach-spmp8000/include/mach/core.h        |   29 ++++
 arch/arm/mach-spmp8000/include/mach/debug-macro.S |   19 +++
 arch/arm/mach-spmp8000/include/mach/dma.h         |   45 ++++++
 arch/arm/mach-spmp8000/include/mach/entry-macro.S |   14 ++
 arch/arm/mach-spmp8000/include/mach/gpio.h        |   21 +++
 arch/arm/mach-spmp8000/include/mach/hardware.h    |   27 ++++
 arch/arm/mach-spmp8000/include/mach/io.h          |   20 +++
 arch/arm/mach-spmp8000/include/mach/irqs.h        |   21 +++
 arch/arm/mach-spmp8000/include/mach/memory.h      |   16 ++
 arch/arm/mach-spmp8000/include/mach/regs-timer.h  |   32 ++++
 arch/arm/mach-spmp8000/include/mach/scu.h         |  146 +++++++++++++++++++
 arch/arm/mach-spmp8000/include/mach/system.h      |   45 ++++++
 arch/arm/mach-spmp8000/include/mach/timex.h       |   17 +++
 arch/arm/mach-spmp8000/include/mach/uncompress.h  |   37 +++++
 arch/arm/mach-spmp8000/include/mach/vmalloc.h     |   16 ++
 arch/arm/mach-spmp8000/timer.c                    |  160 +++++++++++++++++++++
 19 files changed, 780 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-spmp8000/Makefile
 create mode 100644 arch/arm/mach-spmp8000/Makefile.boot
 create mode 100644 arch/arm/mach-spmp8000/core.c
 create mode 100644 arch/arm/mach-spmp8000/include/mach/core.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/debug-macro.S
 create mode 100644 arch/arm/mach-spmp8000/include/mach/dma.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/entry-macro.S
 create mode 100644 arch/arm/mach-spmp8000/include/mach/gpio.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/hardware.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/io.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/irqs.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/memory.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/regs-timer.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/scu.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/system.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/timex.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/uncompress.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/vmalloc.h
 create mode 100644 arch/arm/mach-spmp8000/timer.c
arch/arm/mach-spmp8000/Makefile new file +11 -0
new file mode 100644
index 0000000..9d35cca
@@ -0,0 +1,11 @@
+#
+# Makefile for the linux kernel.
+#
+obj-y 			:= core.o
+obj-y			+= timer.o
+obj-y			+= pinmux.o
+obj-y			+= clock.o
+obj-y			+= clkdev.o
+obj-y			+= board_letcool.o
+obj-y			+= adc.o
+obj-y			+= pwm.o
arch/arm/mach-spmp8000/Makefile.boot new file +1 -0
new file mode 100644
index 0000000..dae9661
@@ -0,0 +1 @@
+zreladdr-y	:= 0x00008000
arch/arm/mach-spmp8000/core.c new file +103 -0

103 lines not on this page · raw message

arch/arm/mach-spmp8000/include/mach/core.h new file +29 -0
new file mode 100644
index 0000000..fa1d522
@@ -0,0 +1,29 @@
+/*
+ * SPMP8000 generic includes
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_GENERIC_H__
+#define __MACH_SPMP8000_GENERIC_H__
+
+/* core.c */
+extern void __init spmp8000_map_io(void);
+extern void __init spmp8000_init_irq(void);
+extern struct sys_timer spmp8000_sys_timer;
+
+/* clkdev.c */
+extern void __init spmp8000_init_clkdev(void);
+extern void spmp8000_update_arm_freqs(void); /* For cpufreq driver */
+
+/* pinmux.c */
+extern void spmp8000_pinmux_pgc_set(int pc, unsigned int conf);
+extern unsigned int spmp8000_pinmux_pgc_get(int pc);
+extern void spmp8000_pinmux_pgs_set(unsigned int pg, unsigned int func);
+extern int spmp8000_pinmux_pgs_get(int pg);
+
+#endif /* __MACH_SPMP8000_GENERIC_H__ */
arch/arm/mach-spmp8000/include/mach/debug-macro.S new file +19 -0
new file mode 100644
index 0000000..698c37e
@@ -0,0 +1,19 @@
+/*
+ * SPMP8000 debug-macro.S
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+	.macro  addruart, rp, rv
+	mov     \rp, #0x92000000
+	add	\rp, #0x00B00000
+	add	\rp, #0x00004000
+	orr	\rv, \rp, #0xF0000000
+	.endm
+
+#define UART_SHIFT 2
+#include <asm/hardware/debug-8250.S>
arch/arm/mach-spmp8000/include/mach/dma.h new file +45 -0

45 lines not on this page · raw message

arch/arm/mach-spmp8000/include/mach/entry-macro.S new file +14 -0
new file mode 100644
index 0000000..3d26088
@@ -0,0 +1,14 @@
+/*
+ * SPMP8000 entry-macro.S
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+	.macro  disable_fiq
+	.endm
+
+	.macro  arch_ret_to_user, tmp1, tmp2
+	.endm
arch/arm/mach-spmp8000/include/mach/gpio.h new file +21 -0
new file mode 100644
index 0000000..3eafac2
@@ -0,0 +1,21 @@
+/*
+ * SPMP8000 machines gpio support
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_GPIO_H__
+#define __MACH_SPMP8000_GPIO_H__
+
+#include <asm-generic/gpio.h>
+
+#define gpio_get_value		__gpio_get_value
+#define gpio_set_value		__gpio_set_value
+#define gpio_cansleep		__gpio_cansleep
+#define gpio_to_irq		__gpio_to_irq
+
+#endif /* __MACH_SPMP8000_GPIO_H__ */
arch/arm/mach-spmp8000/include/mach/hardware.h new file +27 -0
new file mode 100644
index 0000000..998a184
@@ -0,0 +1,27 @@
+/*
+ * SPMP8000 hardware.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_HARDWARE_H__
+#define __MACH_SPMP8000_HARDWARE_H__
+
+/* Vitual to physical translation of statically mapped space */
+#define IO_ADDRESS(x)		((x) | 0xF0000000)
+
+/* Needed for the static mappings and uncompress.h */
+#define SPMP8000_UARTC0_BASE	0x92B04000
+#define SPMP8000_UARTC0_SIZE	SZ_4K
+#define SPMP8000_SCU_C_BASE	0x92005000
+#define SPMP8000_SCU_C_SIZE	SZ_4K
+#define SPMP8000_SCU_A_BASE	0x93007000
+#define SPMP8000_SCU_A_SIZE	SZ_4K
+#define SPMP8000_SCU_B_BASE	0x90005000
+#define SPMP8000_SCU_B_SIZE	SZ_4K
+
+#endif /* __MACH_SPMP8000_HARDWARE_H__ */
arch/arm/mach-spmp8000/include/mach/io.h new file +20 -0
new file mode 100644
index 0000000..a8ea8f0
@@ -0,0 +1,20 @@
+/*
+ * SPMP8000 io.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_IO_H__
+#define __MACH_SPMP8000_IO_H__
+
+/* FIXME This should go away */
+#define IO_SPACE_LIMIT  0
+
+#define __mem_pci(a)    (a)
+#define __io(a)         __typesafe_io(a)
+
+#endif /* __MACH_SPMP8000_IO_H__ */
arch/arm/mach-spmp8000/include/mach/irqs.h new file +21 -0
new file mode 100644
index 0000000..7f305d3
@@ -0,0 +1,21 @@
+/*
+ * SPMP8000 irqs.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_IRQS_H__
+#define __MACH_SPMP8000_IRQS_H__
+
+#define SPMP8000_HW_IRQS		64
+#define SPMP8000_GPIO_IRQS_START	SPMP8000_HW_IRQS
+#define SPMP8000_GPIO_IRQS		(16 + 32)
+#define SPMP8000_TOTAL_IRQS		(SPMP8000_HW_IRQS + SPMP8000_GPIO_IRQS)
+
+#define NR_IRQS				SPMP8000_TOTAL_IRQS
+
+#endif /* __MACH_SPMP8000_IRQS_H__ */
arch/arm/mach-spmp8000/include/mach/memory.h new file +16 -0
new file mode 100644
index 0000000..d2f1089
@@ -0,0 +1,16 @@
+/*
+ * SPMP8000 memory.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_MEMORY_H__
+#define __MACH_SPMP8000_MEMORY_H__
+
+#define PLAT_PHYS_OFFSET	UL(0x00000000)
+
+#endif /* __MACH_SPMP8000_MEMORY_H__ */
arch/arm/mach-spmp8000/include/mach/regs-timer.h new file +32 -0
new file mode 100644
index 0000000..90735df
@@ -0,0 +1,32 @@
+/*
+ * SPMP8000 timer support
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * These timer reg definitions are used by the timer and pwm drivers
+ */
+#ifndef __MACH_SPMP8000_REGS_TIMER_H__
+#define __MACH_SPMP8000_REGS_TIMER_H__
+
+#define SPMP8000_TMRB(tnum)	(tnum * 0x20)
+#define SPMP8000_TMRB_CTR	0x00
+#define SPMP8000_TMRB_CTR_TE	BIT(0)
+#define SPMP8000_TMRB_CTR_IE	BIT(1)
+#define SPMP8000_TMRB_CTR_OE	BIT(2)
+#define SPMP8000_TMRB_CTR_PWMON	BIT(3)
+#define SPMP8000_TMRB_CTR_UD	BIT(4)
+#define SPMP8000_TMRB_CTR_UDS	BIT(5)
+#define SPMP8000_TMRB_CTR_OM	BIT(6)
+#define SPMP8000_TMRB_CTR_ES_SH	8
+#define SPMP8000_TMRB_CTR_M_SH	10
+#define SPMP8000_TMRB_PSR	0x04
+#define SPMP8000_TMRB_LDR	0x08
+#define SPMP8000_TMRB_VLR	0x08
+#define SPMP8000_TMRB_ISR	0x0C
+#define SPMP8000_TMRB_CMP	0x10
+
+#endif /* __MACH_SPMP8000_REGS_TIMER_H__ */
arch/arm/mach-spmp8000/include/mach/scu.h new file +146 -0

146 lines not on this page · raw message

arch/arm/mach-spmp8000/include/mach/system.h new file +45 -0

45 lines not on this page · raw message

arch/arm/mach-spmp8000/include/mach/timex.h new file +17 -0
new file mode 100644
index 0000000..77069c2
@@ -0,0 +1,17 @@
+/*
+ * SPMP8000 timex.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_TIMEX_H__
+#define __MACH_SPMP8000_TIMEX_H__
+
+/* TODO this should go away */
+#define CLOCK_TICK_RATE		(100 * HZ)
+
+#endif /* __MACH_SPMP8000_TIMEX_H__ */
arch/arm/mach-spmp8000/include/mach/uncompress.h new file +37 -0
new file mode 100644
index 0000000..105778b
@@ -0,0 +1,37 @@
+/*
+ * SPMP8000 uncompress.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * Based on the mach-kirkwood implementation
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/serial_reg.h>
+#include <mach/hardware.h>
+
+#define SERIAL_BASE	((volatile unsigned char *)SPMP8000_UARTC0_BASE)
+
+static void putc(const char c)
+{
+	volatile unsigned char *base = SERIAL_BASE;
+	int i;
+
+	for (i = 0; i < 0x1000; i++) {
+		if (base[UART_LSR << 2] & UART_LSR_THRE)
+			break;
+		barrier();
+	}
+
+	base[UART_TX << 2] = c;
+}
+
+static void flush(void)
+{
+}
+
+#define arch_decomp_setup()
+#define arch_decomp_wdog()
arch/arm/mach-spmp8000/include/mach/vmalloc.h new file +16 -0
new file mode 100644
index 0000000..ff40d1c
@@ -0,0 +1,16 @@
+/*
+ * SPMP8000 vmalloc.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_VMALLOC_H__
+#define __MACH_SPMP8000_VMALLOC_H__
+
+#define VMALLOC_END	0xF0000000UL
+
+#endif /* __MACH_SPMP8000_VMALLOC_H__ */
arch/arm/mach-spmp8000/timer.c new file +160 -0

160 lines not on this page · raw message

Signature · 1 line
1.7.4.1
#5 lore raw

PATCH 2/9 ARM: SPMP8000: Add machine base files

Hi Zoltan,

A couple of minor comments inline but this looks really good!

Jamie

On Sun, Oct 09, 2011 at 06:36:05PM +0200, Zoltan Devai wrote:
[...]

arch/arm/mach-spmp8000/Makefile line 3

Quoted 9 lines
arch/arm/mach-spmp8000/Makefile new file +3 -0
new file mode 100644
index 0000000..9d35cca
@@ -0,0 +1,11 @@
+#
+# Makefile for the linux kernel.
+#

Nit: I don't think this comment adds a great deal of value.

Quoted 56 lines
arch/arm/mach-spmp8000/Makefile new file +8 -0
+obj-y 			:= core.o
+obj-y			+= timer.o
+obj-y			+= pinmux.o
+obj-y			+= clock.o
+obj-y			+= clkdev.o
+obj-y			+= board_letcool.o
+obj-y			+= adc.o
+obj-y			+= pwm.o
arch/arm/mach-spmp8000/core.c new file +41 -0
new file mode 100644
index 0000000..ba05614
@@ -0,0 +1,103 @@
+/*
+ * SPMP8000 machines core functions
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <asm/mach/map.h>
+#include <asm/hardware/vic.h>
+#include <mach/hardware.h>
+#include <mach/scu.h>
+
+/* Static mappings:
+ * SCU: timer needs clk support which is inited in init_early
+ * UART: needed for earlyprintk
+ */
+static struct map_desc io_desc[] __initdata = {
+	{
+		.virtual	= IO_ADDRESS(SPMP8000_SCU_A_BASE),
+		.pfn		= __phys_to_pfn(SPMP8000_SCU_A_BASE),
+		.length		= SPMP8000_SCU_A_SIZE,
+		.type		= MT_DEVICE,
+	}, {
+		.virtual	= IO_ADDRESS(SPMP8000_SCU_B_BASE),
+		.pfn		= __phys_to_pfn(SPMP8000_SCU_B_BASE),
+		.length		= SPMP8000_SCU_B_SIZE,
+		.type		= MT_DEVICE,
+	}, {
+		.virtual	= IO_ADDRESS(SPMP8000_SCU_C_BASE),
+		.pfn		= __phys_to_pfn(SPMP8000_SCU_C_BASE),
+		.length		= SPMP8000_SCU_C_SIZE,
+		.type		= MT_DEVICE,
+	},
+#ifdef CONFIG_DEBUG_LL

arch/arm/mach-spmp8000/core.c line 51

Quoted 10 lines
arch/arm/mach-spmp8000/core.c new file +10 -0
+	{
+		.virtual	= IO_ADDRESS(SPMP8000_UARTC0_BASE),
+		.pfn		= __phys_to_pfn(SPMP8000_UARTC0_BASE),
+		.length		= SPMP8000_UARTC0_SIZE,
+		.type		= MT_DEVICE,
+	},
+#endif
+};
+
+#define IO_PTR(x)	((void __iomem *)IO_ADDRESS(x))

Generally IO_ADDRESS returns a void __iomem pointer so it would be easier if you could have the cast in there (though it may need to be enclosed in #ifndef __ASSEMBLY__ conditionals).

Quoted 10 lines
arch/arm/mach-spmp8000/core.c new file +10 -0
+/* Make the lookup of SCU base and clk enable registers easy for the clk
+ * code, as they are not at the same offset in each controller */
+struct scu_reg_t scu_regs[3] = {
+	{	/* SCU_A */
+		.base = IO_PTR(SPMP8000_SCU_A_BASE),
+		.clken = IO_PTR(SPMP8000_SCU_A_BASE) + SCU_A_PERI_CLKEN,
+	},
+	{	/* SCU_B */
+		.base = IO_PTR(SPMP8000_SCU_B_BASE),
+		.clken = IO_PTR(SPMP8000_SCU_B_BASE) + SCU_B_PERI_CLKEN,

arch/arm/mach-spmp8000/core.c line 71

Quoted 10 lines
arch/arm/mach-spmp8000/core.c new file +10 -0
+	},
+	{	/* SCU_C */
+		.base = IO_PTR(SPMP8000_SCU_C_BASE),
+		.clken = IO_PTR(SPMP8000_SCU_C_BASE) + SCU_C_PERI_CLKEN,
+	},
+};
+
+/* DMA controller names to be used for the filter
+ * function of the DMA-Engine API. */
+char *spmp8000_dma_controller_names[] = {

const char * const?

Quoted 21 lines
arch/arm/mach-spmp8000/core.c new file +21 -0
+	"93010000.dma",		/* APBDMA_A */
+	"92b00000.dma",		/* APBDMA_C */
+};
+
+void __init spmp8000_map_io(void)
+{
+	iotable_init(io_desc, ARRAY_SIZE(io_desc));
+}
+
+void __init spmp8000_init_irq(void)
+{
+	struct device_node *np;
+	int ret;
+
+	np = of_find_compatible_node(NULL, NULL, "arm,pl192");
+	if (!np)
+		panic("Can't find VIC0 interrupt controller\n");
+
+	ret = vic_of_init(np, NULL);
+	if (ret)
+		panic("Can't init VIC0 interrupt controller\n");

arch/arm/mach-spmp8000/core.c line 102

Quoted 10 lines
arch/arm/mach-spmp8000/core.c new file +10 -0
+
+	np = of_find_compatible_node(np, NULL, "arm,pl192");
+	if (!np)
+		panic("Can't find VIC1 interrupt controller\n");
+
+	ret = vic_of_init(np, NULL);
+	if (ret)
+		panic("Can't init VIC1 interrupt controller\n");
+
+	of_node_put(np);

So with Rob Herring's of_irq_init patches you should be able to reduce this to:

	static const struct of_device_id spmp8000_vic_matches[] = {
		{ .compatible = "arm,pl192", .data = vic_of_init },
	};
	
	of_irq_init(spmp8000_vic_matches);

and that will handle both VIC's and ordering.

Quoted 13 lines
arch/arm/mach-spmp8000/core.c new file +1 -0
+}
arch/arm/mach-spmp8000/include/mach/core.h new file +6 -0
new file mode 100644
index 0000000..fa1d522
@@ -0,0 +1,29 @@
+/*
+ * SPMP8000 generic includes
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public

arch/arm/mach-spmp8000/include/mach/core.h line 16

Quoted 10 lines
arch/arm/mach-spmp8000/include/mach/core.h new file +10 -0
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_GENERIC_H__
+#define __MACH_SPMP8000_GENERIC_H__
+
+/* core.c */
+extern void __init spmp8000_map_io(void);
+extern void __init spmp8000_init_irq(void);

Nit: you don't need the __init attributes in the header files.

Quoted 13 lines
arch/arm/mach-spmp8000/include/mach/core.h new file +13 -0
+extern struct sys_timer spmp8000_sys_timer;
+
+/* clkdev.c */
+extern void __init spmp8000_init_clkdev(void);
+extern void spmp8000_update_arm_freqs(void); /* For cpufreq driver */
+
+/* pinmux.c */
+extern void spmp8000_pinmux_pgc_set(int pc, unsigned int conf);
+extern unsigned int spmp8000_pinmux_pgc_get(int pc);
+extern void spmp8000_pinmux_pgs_set(unsigned int pg, unsigned int func);
+extern int spmp8000_pinmux_pgs_get(int pg);
+
+#endif /* __MACH_SPMP8000_GENERIC_H__ */

[...]

Quoted 7 lines
arch/arm/mach-spmp8000/include/mach/dma.h new file +1 -0
new file mode 100644
index 0000000..44bc9f2
@@ -0,0 +1,45 @@
+/*

arch/arm/mach-spmp8000/include/mach/dma.h line 11

Quoted 10 lines
arch/arm/mach-spmp8000/include/mach/dma.h new file +10 -0
+ * SPMP8000 dma.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/module.h>
+#include <linux/dmaengine.h>

It may be worth splitting the DMA part out into a separate patch unless this really is required for basic functionality.

Quoted 7 lines
arch/arm/mach-spmp8000/include/mach/dma.h new file +7 -0
+
+#ifndef __MACH_SPMP8000_DMA_H__
+#define __MACH_SPMP8000_DMA_H__
+
+enum spmp8000_dma_controller {
+	SPMP8000_APBDMA_A	= 0,
+	SPMP8000_APBDMA_C,

arch/arm/mach-spmp8000/include/mach/dma.h line 28

Quoted 10 lines
arch/arm/mach-spmp8000/include/mach/dma.h new file +10 -0
+};
+
+extern char *spmp8000_dma_controller_names[];
+
+struct spmp8000_dma_params {
+	char				*dma_controller;
+	dma_addr_t			dma_address;
+	enum dma_slave_buswidth		dma_width;
+	int				maxburst;
+};

dmaengine has dma_slave_config that could be used for this, but I don't see any of this file being used in this series so perhaps it's worth adding in after the core stuff has been merged?

Quoted 4 lines
arch/arm/mach-spmp8000/include/mach/dma.h new file +4 -0
+
+/* Driver parameters */
+#define SPMP8000_APBDMA_MAX_PERIODS	64
+

arch/arm/mach-spmp8000/include/mach/dma.h line 42

Quoted 10 lines
arch/arm/mach-spmp8000/include/mach/dma.h new file +10 -0
+static bool spmp8000_dma_filter(struct dma_chan *chan, const char *name)
+{
+	int ret;
+
+	ret = strcmp(dev_name(chan->device->dev), name);
+
+	if (ret)
+		return false;
+
+	return true;

I think you can reduce this function to just

	return !strcmp(dev_name(chan->device->dev), name);

and the compiler will do the right thing with int->bool conversion.

Quoted 16 lines
arch/arm/mach-spmp8000/include/mach/gpio.h new file +9 -0
new file mode 100644
index 0000000..3eafac2
@@ -0,0 +1,21 @@
+/*
+ * SPMP8000 machines gpio support
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */

arch/arm/mach-spmp8000/include/mach/gpio.h line 19

Quoted 10 lines
arch/arm/mach-spmp8000/include/mach/gpio.h new file +10 -0
+
+#ifndef __MACH_SPMP8000_GPIO_H__
+#define __MACH_SPMP8000_GPIO_H__
+
+#include <asm-generic/gpio.h>
+
+#define gpio_get_value		__gpio_get_value
+#define gpio_set_value		__gpio_set_value
+#define gpio_cansleep		__gpio_cansleep
+#define gpio_to_irq		__gpio_to_irq

Russell has a patch (ARM: gpio: make trivial GPIOLIB implementation the default) in for-next that means you shouldn't need these definitions anymore.

Quoted 16 lines
arch/arm/mach-spmp8000/include/mach/irqs.h new file +9 -0
new file mode 100644
index 0000000..7f305d3
@@ -0,0 +1,21 @@
+/*
+ * SPMP8000 irqs.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */

arch/arm/mach-spmp8000/include/mach/irqs.h line 19

Quoted 10 lines
arch/arm/mach-spmp8000/include/mach/irqs.h new file +10 -0
+
+#ifndef __MACH_SPMP8000_IRQS_H__
+#define __MACH_SPMP8000_IRQS_H__
+
+#define SPMP8000_HW_IRQS		64
+#define SPMP8000_GPIO_IRQS_START	SPMP8000_HW_IRQS
+#define SPMP8000_GPIO_IRQS		(16 + 32)
+#define SPMP8000_TOTAL_IRQS		(SPMP8000_HW_IRQS + SPMP8000_GPIO_IRQS)
+
+#define NR_IRQS				SPMP8000_TOTAL_IRQS

I think that if you use my VIC patches then you can actually define NR_IRQS to 0 as all IRQ descs are dynamically allocated (otherwise you'll be reserving a bunch of irq_descs that never get used).

Quoted 8 lines
arch/arm/mach-spmp8000/include/mach/regs-timer.h new file +1 -0
new file mode 100644
index 0000000..90735df
@@ -0,0 +1,32 @@
+/*

arch/arm/mach-spmp8000/include/mach/regs-timer.h line 11

Quoted 10 lines
arch/arm/mach-spmp8000/include/mach/regs-timer.h new file +10 -0
+ * SPMP8000 timer support
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * These timer reg definitions are used by the timer and pwm drivers
+ */

If these definitions are only being used inside arch/arm/mach-spmp8000 then I'd put the headers in there rather than the arch/arm/mach-spmp8000/include/mach subdir so that the visibility is restricted to the mach code.

arch/arm/mach-spmp8000/include/mach/scu.h

Quoted 26 lines
arch/arm/mach-spmp8000/include/mach/regs-timer.h new file +21 -0
+#ifndef __MACH_SPMP8000_REGS_TIMER_H__
+#define __MACH_SPMP8000_REGS_TIMER_H__
+
+#define SPMP8000_TMRB(tnum)	(tnum * 0x20)
+#define SPMP8000_TMRB_CTR	0x00
+#define SPMP8000_TMRB_CTR_TE	BIT(0)
+#define SPMP8000_TMRB_CTR_IE	BIT(1)
+#define SPMP8000_TMRB_CTR_OE	BIT(2)
+#define SPMP8000_TMRB_CTR_PWMON	BIT(3)
+#define SPMP8000_TMRB_CTR_UD	BIT(4)
+#define SPMP8000_TMRB_CTR_UDS	BIT(5)
+#define SPMP8000_TMRB_CTR_OM	BIT(6)
+#define SPMP8000_TMRB_CTR_ES_SH	8
+#define SPMP8000_TMRB_CTR_M_SH	10
+#define SPMP8000_TMRB_PSR	0x04
+#define SPMP8000_TMRB_LDR	0x08
+#define SPMP8000_TMRB_VLR	0x08
+#define SPMP8000_TMRB_ISR	0x0C
+#define SPMP8000_TMRB_CMP	0x10
+
+#endif /* __MACH_SPMP8000_REGS_TIMER_H__ */
arch/arm/mach-spmp8000/include/mach/scu.h new file +0 -0
new file mode 100644
index 0000000..e3a98d4

Same here as the timer regs stuff.

Quoted 13 lines
arch/arm/mach-spmp8000/include/mach/scu.h new file +12 -0
@@ -0,0 +1,146 @@
+/*
+ * SPMP8000 System Control Unit register definitions
+ * SCUs are a random collection of reset, clock, gpio, pinmux, etc. controllers
+ * so that these definitions are needed at several places.
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+

arch/arm/mach-spmp8000/include/mach/scu.h line 22

Quoted 10 lines
arch/arm/mach-spmp8000/include/mach/scu.h new file +10 -0
+#include <linux/bitops.h>
+#include <mach/hardware.h>
+
+#ifndef __MACH_SPMP8000_SCU_H__
+#define __MACH_SPMP8000_SCU_H__
+
+/* Used by to the clock code to directly access the SCU base and clock
+ * enabling registers, without looking up the according registers first.
+ */
+struct scu_reg_t {

Generally speaking _t isn't used for structs.

arch/arm/mach-spmp8000/include/mach/scu.h line 24

Quoted 2 lines
arch/arm/mach-spmp8000/include/mach/scu.h new file +2 -0
+	void *base;
+	void *clken;

Shouldn't these be void __iomem *?

arch/arm/mach-spmp8000/include/mach/scu.h line 36

Quoted 12 lines
arch/arm/mach-spmp8000/include/mach/scu.h new file +12 -0
+};
+
+extern struct scu_reg_t scu_regs[];
+
+#define REG_SCU_BASE(x)		scu_regs[x].base
+#define REG_SCU_CLKEN(x)	scu_regs[x].clken
+#define REG_SCU_A_ID		0
+#define REG_SCU_B_ID		1
+#define REG_SCU_C_ID		2
+#define REG_SCU_A(x)		(scu_regs[REG_SCU_A_ID].base + x)
+#define REG_SCU_B(x)		(scu_regs[REG_SCU_B_ID].base + x)
+#define REG_SCU_C(x)		(scu_regs[REG_SCU_C_ID].base + x)

macros that assume there is a variable in scope with a given name (scu_regs) are generally frowned upon as it's difficult to see what's going on and can be a little error prone.

Quoted 35 lines
arch/arm/mach-spmp8000/include/mach/system.h new file +28 -0
new file mode 100644
index 0000000..be53ff3
@@ -0,0 +1,45 @@
+/*
+ * SPMP8000 system.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_SYSTEM_H__
+#define __MACH_SPMP8000_SYSTEM_H__
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+
+#define SPMP8000_WDT_BASE	0x90001000
+#define SPMP8000_WDT_SIZE	0x1000
+
+#define SPMP8000_WDT_CTR	0x00
+#define SPMP8000_WDT_CTR_TE	BIT(0)
+#define SPMP8000_WDT_CTR_RE	BIT(3)
+
+static inline void arch_idle(void)
+{
+	cpu_do_idle();
+}
+

arch/arm/mach-spmp8000/include/mach/system.h line 38

Quoted 10 lines
arch/arm/mach-spmp8000/include/mach/system.h new file +10 -0
+static inline void arch_reset(char mode, const char *cmd)
+{
+	void *base;
+
+	base = ioremap(SPMP8000_WDT_BASE, SPMP8000_WDT_SIZE);
+	if (!base) {
+		pr_err("spmp8000: Can't ioremap watchdog regs for reset. "
+			"Halt.");
+		while (1);
+	}

It may be worth doing the ioremap earlier when the system is in a known good state with all functions available rather than at reset time.

Quoted 5 lines
arch/arm/mach-spmp8000/include/mach/system.h new file +5 -0
+
+	/* Trigger a watchdog reset */
+	writel(SPMP8000_WDT_CTR_RE | SPMP8000_WDT_CTR_TE,
+			base + SPMP8000_WDT_CTR);
+}

[...]

Quoted 41 lines
arch/arm/mach-spmp8000/timer.c new file +34 -0
new file mode 100644
index 0000000..7d5d0eb
@@ -0,0 +1,160 @@
+/*
+ * SPMP8000 machines timer functions
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/clockchips.h>
+#include <linux/clk.h>
+#include <asm/mach/time.h>
+
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#include <mach/hardware.h>
+#include <mach/regs-timer.h>
+#include <mach/irqs.h>
+
+static unsigned long clkrate;
+static const unsigned int tickrate = 1012500;
+
+/* The TIMER_B block is used as system timer
+ * T2: Clocksource
+ * T1: Clockevent

arch/arm/mach-spmp8000/timer.c line 44

Quoted 10 lines
arch/arm/mach-spmp8000/timer.c new file +10 -0
+ * T0: PWM for LCD backlight
+ * T3,4: Could be used as additional clockevent devices
+ * Timer constraints:
+ * - WDT: Can't clear the irq directly, only by resetting the whole counter
+ *   in the ISR, which means that IRQs will jitter
+ * - Timer_B: Can't reset the timer value, so at start, the first IRQ
+ *   will happen at some random time.
+ */
+#define		CS_TIMER	2
+#define		CE_TIMER	1

Nit: removing the extra spaces between the define and the CS_TIMER... might make it easier to grep for.

Quoted 7 lines
arch/arm/mach-spmp8000/timer.c new file +7 -0
+static void __iomem *cs_base;
+static void __iomem *ce_base;
+
+static void tmrb_set_mode(enum clock_event_mode mode,
+				struct clock_event_device *dev)
+{
+	switch (mode) {

arch/arm/mach-spmp8000/timer.c line 61

Quoted 10 lines
arch/arm/mach-spmp8000/timer.c new file +10 -0
+	case CLOCK_EVT_MODE_PERIODIC:
+		writel((tickrate / HZ), ce_base + SPMP8000_TMRB_LDR);
+		/* Configure as periodic, down counter, IEN, enable timer */
+		writel(SPMP8000_TMRB_CTR_TE | SPMP8000_TMRB_CTR_IE |
+				(1 << SPMP8000_TMRB_CTR_M_SH),
+				ce_base + SPMP8000_TMRB_CTR);
+		break;
+	case CLOCK_EVT_MODE_ONESHOT:
+		BUG();
+		break;

This case can be folded into the CLOCK_EVT_MODE_RESUME case (and changed to be a default case).

Quoted 50 lines
arch/arm/mach-spmp8000/timer.c new file +50 -0
+	case CLOCK_EVT_MODE_SHUTDOWN:
+	case CLOCK_EVT_MODE_UNUSED:
+		/* Disable timer */
+		writel(0, ce_base + SPMP8000_TMRB_CTR);
+		break;
+	case CLOCK_EVT_MODE_RESUME:
+		BUG();
+		break;
+	}
+}
+
+static struct clock_event_device tmrb1_clkevt = {
+	.name		= "tmrb1",
+	.features	= CLOCK_EVT_FEAT_PERIODIC,
+	.rating		= 200,
+	.set_mode	= tmrb_set_mode,
+};
+
+static irqreturn_t tmrb1_isr(int irq, void *dev_id)
+{
+	tmrb1_clkevt.event_handler(&tmrb1_clkevt);
+
+	/* Clear IRQ */
+	writel(0, ce_base + SPMP8000_TMRB_ISR);
+
+	return IRQ_HANDLED;
+};
+
+static struct irqaction tmrb1_irq = {
+	.name		= "tmrb1_irq",
+	.flags		= IRQF_TIMER | IRQF_IRQPOLL,
+	.handler	= tmrb1_isr,
+};
+
+static void __init spmp8000_sys_timer_init(void)
+{
+	struct device_node *np;
+	unsigned int irq;
+	struct clk *clk;
+	void *tmrb_base;
+
+	np = of_find_compatible_node(NULL, NULL, "sunplus,spmp8000-timer");
+	if (!np)
+		panic("spmp8000: unable to find timer node in dtb\n");
+
+	tmrb_base = of_iomap(np, 0);
+	if (!tmrb_base)
+		panic("spmp8000: unable to map timer cpu registers\n");
+
+	irq = of_irq_to_resource(np, CE_TIMER, NULL);

arch/arm/mach-spmp8000/timer.c line 121

Quoted 10 lines
arch/arm/mach-spmp8000/timer.c new file +10 -0
+	if (irq == NO_IRQ)
+		panic("spmp8000: unable to get interrupts of timer\n");
+
+	of_node_put(np);
+
+	cs_base = tmrb_base + SPMP8000_TMRB(CS_TIMER);
+	ce_base = tmrb_base + SPMP8000_TMRB(CE_TIMER);
+
+	clk = clk_get(NULL, "arm_apb");
+	if (IS_ERR_OR_NULL(clk))

NULL can actually be a valid clk cookie so this should just be IS_ERR(clk).

arch/arm/mach-spmp8000/timer.c line 124

Quoted 3 lines
arch/arm/mach-spmp8000/timer.c new file +3 -0
+		panic("spmp8000: Can't get clock for timer device");
+
+	clk_enable(clk);

Should probably check for success/failure here.

Quoted 36 lines
arch/arm/mach-spmp8000/timer.c new file +36 -0
+	clkrate = clk_get_rate(clk);
+
+	/* Clocksource */
+	/* Disable timer */
+	writel(0, cs_base + SPMP8000_TMRB_CTR);
+
+	/* Reset counter value
+	 * Not really possible unless setting end-1 LDR value and waiting
+	 * until the counter reaches that */
+
+	/* Prescale timer */
+	writel((clkrate / tickrate) - 1, cs_base + SPMP8000_TMRB_PSR);
+
+	/* Register the clocksource */
+	clocksource_mmio_init(cs_base + SPMP8000_TMRB_VLR, "tmrb2",
+				tickrate, 200, 16, clocksource_mmio_readl_up);
+
+	/* Configure as free running (0 - 0xFFFF), up counter, enable timer */
+	writel(SPMP8000_TMRB_CTR_TE | SPMP8000_TMRB_CTR_UD,
+		cs_base + SPMP8000_TMRB_CTR);
+
+	/* Clockevent */
+	setup_irq(irq, &tmrb1_irq);
+
+	/* Disable timer */
+	writel(0, ce_base + SPMP8000_TMRB_CTR);
+
+	/* Prescale timer */
+	writel((clkrate / tickrate) - 1, ce_base + SPMP8000_TMRB_PSR);
+
+	clockevents_register_device(&tmrb1_clkevt);
+}
+
+struct sys_timer spmp8000_sys_timer = {
+	.init		= spmp8000_sys_timer_init,
+};
#6 lore raw

PATCH 2/9 ARM: SPMP8000: Add machine base files

2011/10/9 Jamie Iles <[email protected]>:

Quoted 1 line

A couple of minor comments inline but this looks really good!

Thanks for reviewing! Comments inline.

Quoted 13 lines · Zoltan Devai

[...]

arch/arm/mach-spmp8000/Makefile line 3

Quoted 9 lines
arch/arm/mach-spmp8000/Makefile new file +3 -0
new file mode 100644
index 0000000..9d35cca
@@ -0,0 +1,11 @@
+#
+# Makefile for the linux kernel.
+#

Nit: I don't think this comment adds a great deal of value.

Yep, but this seemed like a common pattern :)
I can't imagine anything that would add value, so will just skip the header.

Quoted 62 lines
Quoted 62 lines
arch/arm/mach-spmp8000/Makefile new file +8 -0
+obj-y ? ? ? ? ? ? ? ? ? ? ? ?:= core.o
+obj-y ? ? ? ? ? ? ? ? ? ? ? ?+= timer.o
+obj-y ? ? ? ? ? ? ? ? ? ? ? ?+= pinmux.o
+obj-y ? ? ? ? ? ? ? ? ? ? ? ?+= clock.o
+obj-y ? ? ? ? ? ? ? ? ? ? ? ?+= clkdev.o
+obj-y ? ? ? ? ? ? ? ? ? ? ? ?+= board_letcool.o
+obj-y ? ? ? ? ? ? ? ? ? ? ? ?+= adc.o
+obj-y ? ? ? ? ? ? ? ? ? ? ? ?+= pwm.o
arch/arm/mach-spmp8000/core.c new file +47 -0
new file mode 100644
index 0000000..ba05614
@@ -0,0 +1,103 @@
+/*
+ * SPMP8000 machines core functions
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <asm/mach/map.h>
+#include <asm/hardware/vic.h>
+#include <mach/hardware.h>
+#include <mach/scu.h>
+
+/* Static mappings:
+ * SCU: timer needs clk support which is inited in init_early
+ * UART: needed for earlyprintk
+ */
+static struct map_desc io_desc[] __initdata = {
+ ? ? {
+ ? ? ? ? ? ? .virtual ? ? ? ?= IO_ADDRESS(SPMP8000_SCU_A_BASE),
+ ? ? ? ? ? ? .pfn ? ? ? ? ? ?= __phys_to_pfn(SPMP8000_SCU_A_BASE),
+ ? ? ? ? ? ? .length ? ? ? ? = SPMP8000_SCU_A_SIZE,
+ ? ? ? ? ? ? .type ? ? ? ? ? = MT_DEVICE,
+ ? ? }, {
+ ? ? ? ? ? ? .virtual ? ? ? ?= IO_ADDRESS(SPMP8000_SCU_B_BASE),
+ ? ? ? ? ? ? .pfn ? ? ? ? ? ?= __phys_to_pfn(SPMP8000_SCU_B_BASE),
+ ? ? ? ? ? ? .length ? ? ? ? = SPMP8000_SCU_B_SIZE,
+ ? ? ? ? ? ? .type ? ? ? ? ? = MT_DEVICE,
+ ? ? }, {
+ ? ? ? ? ? ? .virtual ? ? ? ?= IO_ADDRESS(SPMP8000_SCU_C_BASE),
+ ? ? ? ? ? ? .pfn ? ? ? ? ? ?= __phys_to_pfn(SPMP8000_SCU_C_BASE),
+ ? ? ? ? ? ? .length ? ? ? ? = SPMP8000_SCU_C_SIZE,
+ ? ? ? ? ? ? .type ? ? ? ? ? = MT_DEVICE,
+ ? ? },
+#ifdef CONFIG_DEBUG_LL
+ ? ? {
+ ? ? ? ? ? ? .virtual ? ? ? ?= IO_ADDRESS(SPMP8000_UARTC0_BASE),
+ ? ? ? ? ? ? .pfn ? ? ? ? ? ?= __phys_to_pfn(SPMP8000_UARTC0_BASE),
+ ? ? ? ? ? ? .length ? ? ? ? = SPMP8000_UARTC0_SIZE,
+ ? ? ? ? ? ? .type ? ? ? ? ? = MT_DEVICE,
+ ? ? },
Quoted 8 lines

arch/arm/mach-spmp8000/core.c

Quoted 4 lines
arch/arm/mach-spmp8000/core.c new file +4 -0
+#endif
+};
+
+#define IO_PTR(x) ? ?((void __iomem *)IO_ADDRESS(x))

Generally IO_ADDRESS returns a void __iomem pointer so it would be easier if you could have the cast in there (though it may need to be enclosed in #ifndef __ASSEMBLY__ conditionals).

Every instance I've seen returns an int, mainly because it's used for the static mappings above.
So, if I make IO_ADDRESS return void __iomem, I'd have to make a macro for casting it back to int, and we'd be where we started.

Quoted 14 lines
Quoted 14 lines
arch/arm/mach-spmp8000/core.c new file +14 -0
+/* Make the lookup of SCU base and clk enable registers easy for the clk
+ * code, as they are not at the same offset in each controller */
+struct scu_reg_t scu_regs[3] = {
+ ? ? { ? ? ? /* SCU_A */
+ ? ? ? ? ? ? .base = IO_PTR(SPMP8000_SCU_A_BASE),
+ ? ? ? ? ? ? .clken = IO_PTR(SPMP8000_SCU_A_BASE) + SCU_A_PERI_CLKEN,
+ ? ? },
+ ? ? { ? ? ? /* SCU_B */
+ ? ? ? ? ? ? .base = IO_PTR(SPMP8000_SCU_B_BASE),
+ ? ? ? ? ? ? .clken = IO_PTR(SPMP8000_SCU_B_BASE) + SCU_B_PERI_CLKEN,
+ ? ? },
+ ? ? { ? ? ? /* SCU_C */
+ ? ? ? ? ? ? .base = IO_PTR(SPMP8000_SCU_C_BASE),
+ ? ? ? ? ? ? .clken = IO_PTR(SPMP8000_SCU_C_BASE) + SCU_C_PERI_CLKEN,
Quoted 8 lines

arch/arm/mach-spmp8000/core.c line 71

Quoted 6 lines
arch/arm/mach-spmp8000/core.c new file +6 -0
+ ? ? },
+};
+
+/* DMA controller names to be used for the filter
+ * function of the DMA-Engine API. */
+char *spmp8000_dma_controller_names[] = {

const char * const?

Sure.

Quoted 35 lines
Quoted 21 lines
arch/arm/mach-spmp8000/core.c new file +21 -0
+ ? ? "93010000.dma", ? ? ? ? /* APBDMA_A */
+ ? ? "92b00000.dma", ? ? ? ? /* APBDMA_C */
+};
+
+void __init spmp8000_map_io(void)
+{
+ ? ? iotable_init(io_desc, ARRAY_SIZE(io_desc));
+}
+
+void __init spmp8000_init_irq(void)
+{
+ ? ? struct device_node *np;
+ ? ? int ret;
+
+ ? ? np = of_find_compatible_node(NULL, NULL, "arm,pl192");
+ ? ? if (!np)
+ ? ? ? ? ? ? panic("Can't find VIC0 interrupt controller\n");
+
+ ? ? ret = vic_of_init(np, NULL);
+ ? ? if (ret)
+ ? ? ? ? ? ? panic("Can't init VIC0 interrupt controller\n");

arch/arm/mach-spmp8000/core.c line 102

Quoted 10 lines
arch/arm/mach-spmp8000/core.c new file +10 -0
+
+ ? ? np = of_find_compatible_node(np, NULL, "arm,pl192");
+ ? ? if (!np)
+ ? ? ? ? ? ? panic("Can't find VIC1 interrupt controller\n");
+
+ ? ? ret = vic_of_init(np, NULL);
+ ? ? if (ret)
+ ? ? ? ? ? ? panic("Can't init VIC1 interrupt controller\n");
+
+ ? ? of_node_put(np);

So with Rob Herring's of_irq_init patches you should be able to reduce this to:

Quoted 7 lines

? ? ? ?static const struct of_device_id spmp8000_vic_matches[] = { ? ? ? ? ? ? ? ?{ .compatible = "arm,pl192", .data = vic_of_init }, ? ? ? ?};

? ? ? ?of_irq_init(spmp8000_vic_matches);

and that will handle both VIC's and ordering.

Works like a charm, thanks for the hint.

Quoted 17 lines
Quoted 17 lines
arch/arm/mach-spmp8000/core.c new file +1 -0
+}
arch/arm/mach-spmp8000/include/mach/core.h new file +10 -0
new file mode 100644
index 0000000..fa1d522
@@ -0,0 +1,29 @@
+/*
+ * SPMP8000 generic includes
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
Quoted 8 lines

arch/arm/mach-spmp8000/include/mach/core.h line 16

Quoted 6 lines
arch/arm/mach-spmp8000/include/mach/core.h new file +6 -0
+#ifndef __MACH_SPMP8000_GENERIC_H__
+#define __MACH_SPMP8000_GENERIC_H__
+
+/* core.c */
+extern void __init spmp8000_map_io(void);
+extern void __init spmp8000_init_irq(void);

Nit: you don't need the __init attributes in the header files.

Copy-paste it is. Removed.

Quoted 26 lines
Quoted 13 lines
arch/arm/mach-spmp8000/include/mach/core.h new file +13 -0
+extern struct sys_timer spmp8000_sys_timer;
+
+/* clkdev.c */
+extern void __init spmp8000_init_clkdev(void);
+extern void spmp8000_update_arm_freqs(void); /* For cpufreq driver */
+
+/* pinmux.c */
+extern void spmp8000_pinmux_pgc_set(int pc, unsigned int conf);
+extern unsigned int spmp8000_pinmux_pgc_get(int pc);
+extern void spmp8000_pinmux_pgs_set(unsigned int pg, unsigned int func);
+extern int spmp8000_pinmux_pgs_get(int pg);
+
+#endif /* __MACH_SPMP8000_GENERIC_H__ */

[...]

Quoted 12 lines
arch/arm/mach-spmp8000/include/mach/dma.h new file +6 -0
new file mode 100644
index 0000000..44bc9f2
@@ -0,0 +1,45 @@
+/*
+ * SPMP8000 dma.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
Quoted 8 lines

arch/arm/mach-spmp8000/include/mach/dma.h line 11

Quoted 5 lines
arch/arm/mach-spmp8000/include/mach/dma.h new file +5 -0
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/module.h>
+#include <linux/dmaengine.h>

It may be worth splitting the DMA part out into a separate patch unless this really is required for basic functionality.

I'll split these out for v2.

Quoted 13 lines
Quoted 13 lines
arch/arm/mach-spmp8000/include/mach/dma.h new file +13 -0
+
+#ifndef __MACH_SPMP8000_DMA_H__
+#define __MACH_SPMP8000_DMA_H__
+
+enum spmp8000_dma_controller {
+ ? ? SPMP8000_APBDMA_A ? ? ? = 0,
+ ? ? SPMP8000_APBDMA_C,
+};
+
+extern char *spmp8000_dma_controller_names[];
+
+struct spmp8000_dma_params {
+ ? ? char ? ? ? ? ? ? ? ? ? ? ? ? ? ?*dma_controller;
Quoted 8 lines

arch/arm/mach-spmp8000/include/mach/dma.h line 28

Quoted 4 lines
arch/arm/mach-spmp8000/include/mach/dma.h new file +4 -0
+ ? ? dma_addr_t ? ? ? ? ? ? ? ? ? ? ?dma_address;
+ ? ? enum dma_slave_buswidth ? ? ? ? dma_width;
+ ? ? int ? ? ? ? ? ? ? ? ? ? ? ? ? ? maxburst;
+};

dmaengine has dma_slave_config that could be used for this, but I don't see any of this file being used in this series so perhaps it's worth adding in after the core stuff has been merged?

Will split.
This struct is used by the i2s driver to pass info to the pcm driver on how to set up the dma controller. Seems to be the way to do.

Quoted 17 lines
Quoted 4 lines
arch/arm/mach-spmp8000/include/mach/dma.h new file +4 -0
+
+/* Driver parameters */
+#define SPMP8000_APBDMA_MAX_PERIODS ?64
+

arch/arm/mach-spmp8000/include/mach/dma.h

Quoted 10 lines
arch/arm/mach-spmp8000/include/mach/dma.h new file +10 -0
+static bool spmp8000_dma_filter(struct dma_chan *chan, const char *name)
+{
+ ? ? int ret;
+
+ ? ? ret = strcmp(dev_name(chan->device->dev), name);
+
+ ? ? if (ret)
+ ? ? ? ? ? ? return false;
+
+ ? ? return true;

I think you can reduce this function to just

Quoted 3 lines

? ? ? ?return !strcmp(dev_name(chan->device->dev), name);

and the compiler will do the right thing with int->bool conversion.

Yeah, just debugging leftover.

Quoted 22 lines
Quoted 22 lines
arch/arm/mach-spmp8000/include/mach/gpio.h new file +15 -0
new file mode 100644
index 0000000..3eafac2
@@ -0,0 +1,21 @@
+/*
+ * SPMP8000 machines gpio support
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_GPIO_H__
+#define __MACH_SPMP8000_GPIO_H__
+
+#include <asm-generic/gpio.h>
+
Quoted 8 lines

arch/arm/mach-spmp8000/include/mach/gpio.h line 19

Quoted 4 lines
arch/arm/mach-spmp8000/include/mach/gpio.h new file +4 -0
+#define gpio_get_value ? ? ? ? ? ? ? __gpio_get_value
+#define gpio_set_value ? ? ? ? ? ? ? __gpio_set_value
+#define gpio_cansleep ? ? ? ? ? ? ? ?__gpio_cansleep
+#define gpio_to_irq ? ? ? ? ?__gpio_to_irq

Russell has a patch (ARM: gpio: make trivial GPIOLIB implementation the default) in for-next that means you shouldn't need these definitions anymore.

Which tree and branch should I base my work on ? I'm quite confused by the all the options, and it seems like if I choose some devel-stable tree, then I don't get the new features and my work is outdated from the beginning, but for-next branches are quite diverged.
Is there a good strategy ?

Quoted 22 lines
Quoted 22 lines
arch/arm/mach-spmp8000/include/mach/irqs.h new file +15 -0
new file mode 100644
index 0000000..7f305d3
@@ -0,0 +1,21 @@
+/*
+ * SPMP8000 irqs.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_IRQS_H__
+#define __MACH_SPMP8000_IRQS_H__
+
+#define SPMP8000_HW_IRQS ? ? ? ? ? ? 64
+#define SPMP8000_GPIO_IRQS_START ? ? SPMP8000_HW_IRQS
Quoted 8 lines

arch/arm/mach-spmp8000/include/mach/irqs.h line 19

Quoted 4 lines
arch/arm/mach-spmp8000/include/mach/irqs.h new file +4 -0
+#define SPMP8000_GPIO_IRQS ? ? ? ? ? (16 + 32)
+#define SPMP8000_TOTAL_IRQS ? ? ? ? ?(SPMP8000_HW_IRQS + SPMP8000_GPIO_IRQS)
+
+#define NR_IRQS ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?SPMP8000_TOTAL_IRQS

I think that if you use my VIC patches then you can actually define NR_IRQS to 0 as all IRQ descs are dynamically allocated (otherwise you'll be reserving a bunch of irq_descs that never get used).

OK. Tried with 0, works.

Quoted 15 lines
Quoted 15 lines
arch/arm/mach-spmp8000/include/mach/regs-timer.h new file +8 -0
new file mode 100644
index 0000000..90735df
@@ -0,0 +1,32 @@
+/*
+ * SPMP8000 timer support
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
Quoted 8 lines

arch/arm/mach-spmp8000/include/mach/regs-timer.h line 11

Quoted 3 lines
arch/arm/mach-spmp8000/include/mach/regs-timer.h new file +3 -0
+ *
+ * These timer reg definitions are used by the timer and pwm drivers
+ */

If these definitions are only being used inside arch/arm/mach-spmp8000 then I'd put the headers in there rather than the arch/arm/mach-spmp8000/include/mach subdir so that the visibility is restricted to the mach code.

OK.
Depends on the final place of the PWM driver.

Quoted 20 lines
Quoted 20 lines
arch/arm/mach-spmp8000/include/mach/regs-timer.h new file +20 -0
+#ifndef __MACH_SPMP8000_REGS_TIMER_H__
+#define __MACH_SPMP8000_REGS_TIMER_H__
+
+#define SPMP8000_TMRB(tnum) ?(tnum * 0x20)
+#define SPMP8000_TMRB_CTR ? ?0x00
+#define SPMP8000_TMRB_CTR_TE BIT(0)
+#define SPMP8000_TMRB_CTR_IE BIT(1)
+#define SPMP8000_TMRB_CTR_OE BIT(2)
+#define SPMP8000_TMRB_CTR_PWMON ? ? ?BIT(3)
+#define SPMP8000_TMRB_CTR_UD BIT(4)
+#define SPMP8000_TMRB_CTR_UDS ? ? ? ?BIT(5)
+#define SPMP8000_TMRB_CTR_OM BIT(6)
+#define SPMP8000_TMRB_CTR_ES_SH ? ? ?8
+#define SPMP8000_TMRB_CTR_M_SH ? ? ? 10
+#define SPMP8000_TMRB_PSR ? ?0x04
+#define SPMP8000_TMRB_LDR ? ?0x08
+#define SPMP8000_TMRB_VLR ? ?0x08
+#define SPMP8000_TMRB_ISR ? ?0x0C
+#define SPMP8000_TMRB_CMP ? ?0x10
+
Quoted 8 lines

arch/arm/mach-spmp8000/include/mach/scu.h

Quoted 6 lines
arch/arm/mach-spmp8000/include/mach/regs-timer.h new file +1 -0
+#endif /* __MACH_SPMP8000_REGS_TIMER_H__ */
arch/arm/mach-spmp8000/include/mach/scu.h new file +0 -0
new file mode 100644
index 0000000..e3a98d4

Same here as the timer regs stuff.

OK, this can definitely move.

Quoted 17 lines
Quoted 17 lines
arch/arm/mach-spmp8000/include/mach/scu.h new file +16 -0
@@ -0,0 +1,146 @@
+/*
+ * SPMP8000 System Control Unit register definitions
+ * SCUs are a random collection of reset, clock, gpio, pinmux, etc. controllers
+ * so that these definitions are needed at several places.
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/bitops.h>
+#include <mach/hardware.h>
+
+#ifndef __MACH_SPMP8000_SCU_H__
Quoted 8 lines

arch/arm/mach-spmp8000/include/mach/scu.h line 22

Quoted 6 lines
arch/arm/mach-spmp8000/include/mach/scu.h new file +6 -0
+#define __MACH_SPMP8000_SCU_H__
+
+/* Used by to the clock code to directly access the SCU base and clock
+ * enabling registers, without looking up the according registers first.
+ */
+struct scu_reg_t {

Generally speaking _t isn't used for structs.

OK.

Quoted 4 lines

arch/arm/mach-spmp8000/include/mach/scu.h line 24

Quoted 2 lines
arch/arm/mach-spmp8000/include/mach/scu.h new file +2 -0
+ ? ? void *base;
+ ? ? void *clken;

Shouldn't these be void __iomem *?

Yes.

Quoted 8 lines
Quoted 8 lines
arch/arm/mach-spmp8000/include/mach/scu.h new file +8 -0
+};
+
+extern struct scu_reg_t scu_regs[];
+
+#define REG_SCU_BASE(x) ? ? ? ? ? ? ?scu_regs[x].base
+#define REG_SCU_CLKEN(x) ? ? scu_regs[x].clken
+#define REG_SCU_A_ID ? ? ? ? 0
+#define REG_SCU_B_ID ? ? ? ? 1
Quoted 8 lines

arch/arm/mach-spmp8000/include/mach/scu.h line 36

Quoted 4 lines
arch/arm/mach-spmp8000/include/mach/scu.h new file +4 -0
+#define REG_SCU_C_ID ? ? ? ? 2
+#define REG_SCU_A(x) ? ? ? ? (scu_regs[REG_SCU_A_ID].base + x)
+#define REG_SCU_B(x) ? ? ? ? (scu_regs[REG_SCU_B_ID].base + x)
+#define REG_SCU_C(x) ? ? ? ? (scu_regs[REG_SCU_C_ID].base + x)

macros that assume there is a variable in scope with a given name (scu_regs) are generally frowned upon as it's difficult to see what's going on and can be a little error prone.

Yes, this isn't really nice. Will work out another way.

Quoted 40 lines
Quoted 40 lines
arch/arm/mach-spmp8000/include/mach/system.h new file +33 -0
new file mode 100644
index 0000000..be53ff3
@@ -0,0 +1,45 @@
+/*
+ * SPMP8000 system.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_SYSTEM_H__
+#define __MACH_SPMP8000_SYSTEM_H__
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+
+#define SPMP8000_WDT_BASE ? ?0x90001000
+#define SPMP8000_WDT_SIZE ? ?0x1000
+
+#define SPMP8000_WDT_CTR ? ? 0x00
+#define SPMP8000_WDT_CTR_TE ?BIT(0)
+#define SPMP8000_WDT_CTR_RE ?BIT(3)
+
+static inline void arch_idle(void)
+{
+ ? ? cpu_do_idle();
+}
+
+static inline void arch_reset(char mode, const char *cmd)
+{
+ ? ? void *base;
+
+ ? ? base = ioremap(SPMP8000_WDT_BASE, SPMP8000_WDT_SIZE);
Quoted 8 lines

arch/arm/mach-spmp8000/include/mach/system.h line 38

Quoted 5 lines
arch/arm/mach-spmp8000/include/mach/system.h new file +5 -0
+ ? ? if (!base) {
+ ? ? ? ? ? ? pr_err("spmp8000: Can't ioremap watchdog regs for reset. "
+ ? ? ? ? ? ? ? ? ? ? "Halt.");
+ ? ? ? ? ? ? while (1);
+ ? ? }

It may be worth doing the ioremap earlier when the system is in a known good state with all functions available rather than at reset time.

Any suggestion where the best place would be ?
I can only think of either the timer init or the board init, but neither seemed to be appropriate.

Quoted 52 lines
Quoted 5 lines
arch/arm/mach-spmp8000/include/mach/system.h new file +5 -0
+
+ ? ? /* Trigger a watchdog reset */
+ ? ? writel(SPMP8000_WDT_CTR_RE | SPMP8000_WDT_CTR_TE,
+ ? ? ? ? ? ? ? ? ? ? base + SPMP8000_WDT_CTR);
+}

[...]

Quoted 46 lines
arch/arm/mach-spmp8000/timer.c new file +39 -0
new file mode 100644
index 0000000..7d5d0eb
@@ -0,0 +1,160 @@
+/*
+ * SPMP8000 machines timer functions
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/clockchips.h>
+#include <linux/clk.h>
+#include <asm/mach/time.h>
+
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#include <mach/hardware.h>
+#include <mach/regs-timer.h>
+#include <mach/irqs.h>
+
+static unsigned long clkrate;
+static const unsigned int tickrate = 1012500;
+
+/* The TIMER_B block is used as system timer
+ * T2: Clocksource
+ * T1: Clockevent
+ * T0: PWM for LCD backlight
+ * T3,4: Could be used as additional clockevent devices
+ * Timer constraints:
+ * - WDT: Can't clear the irq directly, only by resetting the whole counter
+ * ? in the ISR, which means that IRQs will jitter
Quoted 8 lines

arch/arm/mach-spmp8000/timer.c line 44

Quoted 5 lines
arch/arm/mach-spmp8000/timer.c new file +5 -0
+ * - Timer_B: Can't reset the timer value, so at start, the first IRQ
+ * ? will happen at some random time.
+ */
+#define ? ? ? ? ? ? ?CS_TIMER ? ? ? ?2
+#define ? ? ? ? ? ? ?CE_TIMER ? ? ? ?1

Nit: removing the extra spaces between the define and the CS_TIMER... might make it easier to grep for.

OK.

Quoted 12 lines
Quoted 12 lines
arch/arm/mach-spmp8000/timer.c new file +12 -0
+static void __iomem *cs_base;
+static void __iomem *ce_base;
+
+static void tmrb_set_mode(enum clock_event_mode mode,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct clock_event_device *dev)
+{
+ ? ? switch (mode) {
+ ? ? case CLOCK_EVT_MODE_PERIODIC:
+ ? ? ? ? ? ? writel((tickrate / HZ), ce_base + SPMP8000_TMRB_LDR);
+ ? ? ? ? ? ? /* Configure as periodic, down counter, IEN, enable timer */
+ ? ? ? ? ? ? writel(SPMP8000_TMRB_CTR_TE | SPMP8000_TMRB_CTR_IE |
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? (1 << SPMP8000_TMRB_CTR_M_SH),
Quoted 8 lines

arch/arm/mach-spmp8000/timer.c line 61

Quoted 5 lines
arch/arm/mach-spmp8000/timer.c new file +5 -0
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ce_base + SPMP8000_TMRB_CTR);
+ ? ? ? ? ? ? break;
+ ? ? case CLOCK_EVT_MODE_ONESHOT:
+ ? ? ? ? ? ? BUG();
+ ? ? ? ? ? ? break;

This case can be folded into the CLOCK_EVT_MODE_RESUME case (and changed to be a default case).

OK.

Quoted 55 lines
Quoted 55 lines
arch/arm/mach-spmp8000/timer.c new file +55 -0
+ ? ? case CLOCK_EVT_MODE_SHUTDOWN:
+ ? ? case CLOCK_EVT_MODE_UNUSED:
+ ? ? ? ? ? ? /* Disable timer */
+ ? ? ? ? ? ? writel(0, ce_base + SPMP8000_TMRB_CTR);
+ ? ? ? ? ? ? break;
+ ? ? case CLOCK_EVT_MODE_RESUME:
+ ? ? ? ? ? ? BUG();
+ ? ? ? ? ? ? break;
+ ? ? }
+}
+
+static struct clock_event_device tmrb1_clkevt = {
+ ? ? .name ? ? ? ? ? = "tmrb1",
+ ? ? .features ? ? ? = CLOCK_EVT_FEAT_PERIODIC,
+ ? ? .rating ? ? ? ? = 200,
+ ? ? .set_mode ? ? ? = tmrb_set_mode,
+};
+
+static irqreturn_t tmrb1_isr(int irq, void *dev_id)
+{
+ ? ? tmrb1_clkevt.event_handler(&tmrb1_clkevt);
+
+ ? ? /* Clear IRQ */
+ ? ? writel(0, ce_base + SPMP8000_TMRB_ISR);
+
+ ? ? return IRQ_HANDLED;
+};
+
+static struct irqaction tmrb1_irq = {
+ ? ? .name ? ? ? ? ? = "tmrb1_irq",
+ ? ? .flags ? ? ? ? ?= IRQF_TIMER | IRQF_IRQPOLL,
+ ? ? .handler ? ? ? ?= tmrb1_isr,
+};
+
+static void __init spmp8000_sys_timer_init(void)
+{
+ ? ? struct device_node *np;
+ ? ? unsigned int irq;
+ ? ? struct clk *clk;
+ ? ? void *tmrb_base;
+
+ ? ? np = of_find_compatible_node(NULL, NULL, "sunplus,spmp8000-timer");
+ ? ? if (!np)
+ ? ? ? ? ? ? panic("spmp8000: unable to find timer node in dtb\n");
+
+ ? ? tmrb_base = of_iomap(np, 0);
+ ? ? if (!tmrb_base)
+ ? ? ? ? ? ? panic("spmp8000: unable to map timer cpu registers\n");
+
+ ? ? irq = of_irq_to_resource(np, CE_TIMER, NULL);
+ ? ? if (irq == NO_IRQ)
+ ? ? ? ? ? ? panic("spmp8000: unable to get interrupts of timer\n");
+
+ ? ? of_node_put(np);
+
Quoted 8 lines

arch/arm/mach-spmp8000/timer.c line 121

Quoted 5 lines
arch/arm/mach-spmp8000/timer.c new file +5 -0
+ ? ? cs_base = tmrb_base + SPMP8000_TMRB(CS_TIMER);
+ ? ? ce_base = tmrb_base + SPMP8000_TMRB(CE_TIMER);
+
+ ? ? clk = clk_get(NULL, "arm_apb");
+ ? ? if (IS_ERR_OR_NULL(clk))

NULL can actually be a valid clk cookie so this should just be IS_ERR(clk).

OK.

Quoted 5 lines

arch/arm/mach-spmp8000/timer.c line 124

Quoted 3 lines
arch/arm/mach-spmp8000/timer.c new file +3 -0
+ ? ? ? ? ? ? panic("spmp8000: Can't get clock for timer device");
+
+ ? ? clk_enable(clk);

Should probably check for success/failure here.

OK.

Quoted 37 lines
Quoted 36 lines
arch/arm/mach-spmp8000/timer.c new file +36 -0
+ ? ? clkrate = clk_get_rate(clk);
+
+ ? ? /* Clocksource */
+ ? ? /* Disable timer */
+ ? ? writel(0, cs_base + SPMP8000_TMRB_CTR);
+
+ ? ? /* Reset counter value
+ ? ? ?* Not really possible unless setting end-1 LDR value and waiting
+ ? ? ?* until the counter reaches that */
+
+ ? ? /* Prescale timer */
+ ? ? writel((clkrate / tickrate) - 1, cs_base + SPMP8000_TMRB_PSR);
+
+ ? ? /* Register the clocksource */
+ ? ? clocksource_mmio_init(cs_base + SPMP8000_TMRB_VLR, "tmrb2",
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? tickrate, 200, 16, clocksource_mmio_readl_up);
+
+ ? ? /* Configure as free running (0 - 0xFFFF), up counter, enable timer */
+ ? ? writel(SPMP8000_TMRB_CTR_TE | SPMP8000_TMRB_CTR_UD,
+ ? ? ? ? ? ? cs_base + SPMP8000_TMRB_CTR);
+
+ ? ? /* Clockevent */
+ ? ? setup_irq(irq, &tmrb1_irq);
+
+ ? ? /* Disable timer */
+ ? ? writel(0, ce_base + SPMP8000_TMRB_CTR);
+
+ ? ? /* Prescale timer */
+ ? ? writel((clkrate / tickrate) - 1, ce_base + SPMP8000_TMRB_PSR);
+
+ ? ? clockevents_register_device(&tmrb1_clkevt);
+}
+
+struct sys_timer spmp8000_sys_timer = {
+ ? ? .init ? ? ? ? ? = spmp8000_sys_timer_init,
+};
#7 lore raw

PATCH 2/9 ARM: SPMP8000: Add machine base files

Quoted 5 lines · Zoltan Devai

2011/10/9 Jamie Iles <[email protected]>:

Quoted 1 line

A couple of minor comments inline but this looks really good!

Thanks for reviewing! Comments inline.

Quoted 1 line · Zoltan Devai

[...]

Quoted 15 lines
Quoted 7 lines
Quoted 7 lines

+#ifdef CONFIG_DEBUG_LL
+ ? ? {
+ ? ? ? ? ? ? .virtual ? ? ? ?= IO_ADDRESS(SPMP8000_UARTC0_BASE),
+ ? ? ? ? ? ? .pfn ? ? ? ? ? ?= __phys_to_pfn(SPMP8000_UARTC0_BASE),
+ ? ? ? ? ? ? .length ? ? ? ? = SPMP8000_UARTC0_SIZE,
+ ? ? ? ? ? ? .type ? ? ? ? ? = MT_DEVICE,
+ ? ? },

Quoted 8 lines

arch/arm/mach-spmp8000/core.c

Quoted 4 lines
arch/arm/mach-spmp8000/core.c new file +4 -0
+#endif
+};
+
+#define IO_PTR(x) ? ?((void __iomem *)IO_ADDRESS(x))

Generally IO_ADDRESS returns a void __iomem pointer so it would be easier if you could have the cast in there (though it may need to be enclosed in #ifndef __ASSEMBLY__ conditionals).

Quoted 4 lines

Every instance I've seen returns an int, mainly because it's used for the static mappings above.
So, if I make IO_ADDRESS return void __iomem, I'd have to make a macro for casting it back to int, and we'd be where we started.

Yes, you're quite right!

[...]

Quoted 21 lines
Quoted 13 lines
Quoted 13 lines
arch/arm/mach-spmp8000/include/mach/dma.h new file +13 -0
+
+#ifndef __MACH_SPMP8000_DMA_H__
+#define __MACH_SPMP8000_DMA_H__
+
+enum spmp8000_dma_controller {
+ ? ? SPMP8000_APBDMA_A ? ? ? = 0,
+ ? ? SPMP8000_APBDMA_C,
+};
+
+extern char *spmp8000_dma_controller_names[];
+
+struct spmp8000_dma_params {
+ ? ? char ? ? ? ? ? ? ? ? ? ? ? ? ? ?*dma_controller;
Quoted 8 lines
Quoted 4 lines

+ ? ? dma_addr_t ? ? ? ? ? ? ? ? ? ? ?dma_address;
+ ? ? enum dma_slave_buswidth ? ? ? ? dma_width;
+ ? ? int ? ? ? ? ? ? ? ? ? ? ? ? ? ? maxburst; +};

dmaengine has dma_slave_config that could be used for this, but I don't see any of this file being used in this series so perhaps it's worth adding in after the core stuff has been merged?

Quoted 3 lines

Will split.
This struct is used by the i2s driver to pass info to the pcm driver on how to set up the dma controller. Seems to be the way to do.

OK, so this is platform data for the i2s driver?

Quoted 6 lines

Which tree and branch should I base my work on ? I'm quite confused by the all the options, and it seems like if I choose some devel-stable tree, then I don't get the new features and my work is outdated from the beginning, but for-next branches are quite diverged.
Is there a good strategy ?

That's a tricky one. I have a similar problem, and rightly or wrongly I generally put most of the stuff I can based of off an -rc tag from Linus' tree then merge in the others during testing. As long as those get merged first you don't need to do anything else, but it does get a bit fiddly.

Quoted 48 lines
Quoted 40 lines
Quoted 40 lines
arch/arm/mach-spmp8000/include/mach/system.h new file +33 -0
new file mode 100644
index 0000000..be53ff3
@@ -0,0 +1,45 @@
+/*
+ * SPMP8000 system.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_SYSTEM_H__
+#define __MACH_SPMP8000_SYSTEM_H__
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+
+#define SPMP8000_WDT_BASE ? ?0x90001000
+#define SPMP8000_WDT_SIZE ? ?0x1000
+
+#define SPMP8000_WDT_CTR ? ? 0x00
+#define SPMP8000_WDT_CTR_TE ?BIT(0)
+#define SPMP8000_WDT_CTR_RE ?BIT(3)
+
+static inline void arch_idle(void)
+{
+ ? ? cpu_do_idle();
+}
+
+static inline void arch_reset(char mode, const char *cmd)
+{
+ ? ? void *base;
+
+ ? ? base = ioremap(SPMP8000_WDT_BASE, SPMP8000_WDT_SIZE);
Quoted 8 lines

arch/arm/mach-spmp8000/include/mach/system.h line 38

Quoted 5 lines
arch/arm/mach-spmp8000/include/mach/system.h new file +5 -0
+ ? ? if (!base) {
+ ? ? ? ? ? ? pr_err("spmp8000: Can't ioremap watchdog regs for reset. "
+ ? ? ? ? ? ? ? ? ? ? "Halt.");
+ ? ? ? ? ? ? while (1);
+ ? ? }

It may be worth doing the ioremap earlier when the system is in a known good state with all functions available rather than at reset time.

Quoted 3 lines

Any suggestion where the best place would be ?
I can only think of either the timer init or the board init, but neither seemed to be appropriate.

Personally I think having a soc init function that does this sort of stuff and gets called from the board init would be suitable for this sort of thing. If the ioremap() fails you could always fall back to a soft reset.

Jamie

#8 lore raw

PATCH 2/9 ARM: SPMP8000: Add machine base files

In reply to Zoltan Devai
Quoted 12 lines · Zoltan Devai
arch/arm/mach-spmp8000/core.c new file +12 -0
+
+/* Static mappings:
+ * SCU: timer needs clk support which is inited in init_early
+ * UART: needed for earlyprintk
+ */
+static struct map_desc io_desc[] __initdata = {
+	{
+		.virtual	= IO_ADDRESS(SPMP8000_SCU_A_BASE),
+		.pfn		= __phys_to_pfn(SPMP8000_SCU_A_BASE),
+		.length		= SPMP8000_SCU_A_SIZE,
+		.type		= MT_DEVICE,
+	}, {

arch/arm/mach-spmp8000/core.c line 40

Quoted 10 lines
arch/arm/mach-spmp8000/core.c new file +10 -0
+		.virtual	= IO_ADDRESS(SPMP8000_SCU_B_BASE),
+		.pfn		= __phys_to_pfn(SPMP8000_SCU_B_BASE),
+		.length		= SPMP8000_SCU_B_SIZE,
+		.type		= MT_DEVICE,
+	}, {
+		.virtual	= IO_ADDRESS(SPMP8000_SCU_C_BASE),
+		.pfn		= __phys_to_pfn(SPMP8000_SCU_C_BASE),
+		.length		= SPMP8000_SCU_C_SIZE,
+		.type		= MT_DEVICE,
+	},

With Nicolas Pitre's rework of the MMIO space handling, I think it would be nice to just use a single area that spans all of the devices so you can do an optimized ioremap and huge TLBs. Unfortunately that series won't make it into 3.2, but if you just set up a large area now, it will automatically work.

arch/arm/mach-spmp8000/core.c line 49

Quoted 10 lines
arch/arm/mach-spmp8000/core.c new file +9 -0
+#ifdef CONFIG_DEBUG_LL
+	{
+		.virtual	= IO_ADDRESS(SPMP8000_UARTC0_BASE),
+		.pfn		= __phys_to_pfn(SPMP8000_UARTC0_BASE),
+		.length		= SPMP8000_UARTC0_SIZE,
+		.type		= MT_DEVICE,
+	},
+#endif
+};

And it would be nice to move this into a separate file that handles the early debug code, as prima2 does.

Quoted 23 lines
arch/arm/mach-spmp8000/include/mach/dma.h new file +23 -0
+#ifndef __MACH_SPMP8000_DMA_H__
+#define __MACH_SPMP8000_DMA_H__
+
+enum spmp8000_dma_controller {
+	SPMP8000_APBDMA_A	= 0,
+	SPMP8000_APBDMA_C,
+};
+
+extern char *spmp8000_dma_controller_names[];
+
+struct spmp8000_dma_params {
+	char				*dma_controller;
+	dma_addr_t			dma_address;
+	enum dma_slave_buswidth		dma_width;
+	int				maxburst;
+};
+
+/* Driver parameters */
+#define SPMP8000_APBDMA_MAX_PERIODS	64
+
+static bool spmp8000_dma_filter(struct dma_chan *chan, const char *name)
+{
+	int ret;

arch/arm/mach-spmp8000/include/mach/dma.h line 45

Quoted 10 lines
arch/arm/mach-spmp8000/include/mach/dma.h new file +10 -0
+
+	ret = strcmp(dev_name(chan->device->dev), name);
+
+	if (ret)
+		return false;
+
+	return true;
+}
+
+#endif /* __MACH_SPMP8000_DMA_H__ */

Please separate the dma controller out for now into a separate patch. We really need to have proper device tree bindings so we can avoid the silly filter functions and do something better. That needs some more discussion and any help on the interface is very much appreciated.

Quoted 4 lines
arch/arm/mach-spmp8000/include/mach/gpio.h new file +1 -0
new file mode 100644
 
+#ifndef __MACH_SPMP8000_GPIO_H__

arch/arm/mach-spmp8000/include/mach/gpio.h line 21

Quoted 10 lines
arch/arm/mach-spmp8000/include/mach/gpio.h new file +10 -0
+#define __MACH_SPMP8000_GPIO_H__
+
+#include <asm-generic/gpio.h>
+
+#define gpio_get_value		__gpio_get_value
+#define gpio_set_value		__gpio_set_value
+#define gpio_cansleep		__gpio_cansleep
+#define gpio_to_irq		__gpio_to_irq
+
+#endif /* __MACH_SPMP8000_GPIO_H__ */

I think this can become an empty file now, with Russell's latest cleanup going into 3.2.

arch/arm/mach-spmp8000/include/mach/gpio.h

Quoted 2 lines
arch/arm/mach-spmp8000/include/mach/gpio.h new file +2 -0
+/* Vitual to physical translation of statically mapped space */
+#define IO_ADDRESS(x)		((x) | 0xF0000000)

Better make this return a 'void __iomem *', e.g. by doing

#define IO_ADDRESS(x) (((x) & 0x0fffffff) + ((void __iomem *)0xf0000000)

arch/arm/mach-spmp8000/include/mach/gpio.h

Quoted 9 lines
arch/arm/mach-spmp8000/include/mach/gpio.h new file +9 -0
+/* Needed for the static mappings and uncompress.h */
+#define SPMP8000_UARTC0_BASE	0x92B04000
+#define SPMP8000_UARTC0_SIZE	SZ_4K
+#define SPMP8000_SCU_C_BASE	0x92005000
+#define SPMP8000_SCU_C_SIZE	SZ_4K
+#define SPMP8000_SCU_A_BASE	0x93007000
+#define SPMP8000_SCU_A_SIZE	SZ_4K
+#define SPMP8000_SCU_B_BASE	0x90005000
+#define SPMP8000_SCU_B_SIZE	SZ_4K

Do you need these all so early that you cannot get the location from the device tree?

arch/arm/mach-spmp8000/include/mach/gpio.h

Quoted 6 lines
arch/arm/mach-spmp8000/include/mach/gpio.h new file +6 -0
+/* FIXME This should go away */
+#define IO_SPACE_LIMIT  0
+
+#define __mem_pci(a)    (a)
+#define __io(a)         __typesafe_io(a)
+

I think you should better define __io() to NULL.

Quoted 24 lines

+++ b/arch/arm/mach-spmp8000/include/mach/regs-timer.h

arch/arm/mach-spmp8000/include/mach/gpio.h new file +22 -0
@@ -0,0 +1,32 @@
+/*
+ * SPMP8000 timer support
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * These timer reg definitions are used by the timer and pwm drivers
+ */
+#ifndef __MACH_SPMP8000_REGS_TIMER_H__
+#define __MACH_SPMP8000_REGS_TIMER_H__
+
+#define SPMP8000_TMRB(tnum)	(tnum * 0x20)
+#define SPMP8000_TMRB_CTR	0x00
+#define SPMP8000_TMRB_CTR_TE	BIT(0)
+#define SPMP8000_TMRB_CTR_IE	BIT(1)
+#define SPMP8000_TMRB_CTR_OE	BIT(2)
+#define SPMP8000_TMRB_CTR_PWMON	BIT(3)
+#define SPMP8000_TMRB_CTR_UD	BIT(4)
+#define SPMP8000_TMRB_CTR_UDS	BIT(5)

arch/arm/mach-spmp8000/include/mach/gpio.h line 32

Quoted 10 lines
arch/arm/mach-spmp8000/include/mach/gpio.h new file +10 -0
+#define SPMP8000_TMRB_CTR_OM	BIT(6)
+#define SPMP8000_TMRB_CTR_ES_SH	8
+#define SPMP8000_TMRB_CTR_M_SH	10
+#define SPMP8000_TMRB_PSR	0x04
+#define SPMP8000_TMRB_LDR	0x08
+#define SPMP8000_TMRB_VLR	0x08
+#define SPMP8000_TMRB_ISR	0x0C
+#define SPMP8000_TMRB_CMP	0x10
+
+#endif /* __MACH_SPMP8000_REGS_TIMER_H__ */

No need for this header, just move the definitions into the timer driver.

Quoted 16 lines
arch/arm/mach-spmp8000/include/mach/scu.h new file +9 -0
new file mode 100644
index 0000000..e3a98d4
@@ -0,0 +1,146 @@
+/*
+ * SPMP8000 System Control Unit register definitions
+ * SCUs are a random collection of reset, clock, gpio, pinmux, etc. controllers
+ * so that these definitions are needed at several places.
 
+struct scu_reg_t {
+	void *base;
+	void *clken;
+};
+

arch/arm/mach-spmp8000/include/mach/scu.h line 36

Quoted 10 lines
arch/arm/mach-spmp8000/include/mach/scu.h new file +10 -0
+extern struct scu_reg_t scu_regs[];
+
+#define REG_SCU_BASE(x)		scu_regs[x].base
+#define REG_SCU_CLKEN(x)	scu_regs[x].clken
+#define REG_SCU_A_ID		0
+#define REG_SCU_B_ID		1
+#define REG_SCU_C_ID		2
+#define REG_SCU_A(x)		(scu_regs[REG_SCU_A_ID].base + x)
+#define REG_SCU_B(x)		(scu_regs[REG_SCU_B_ID].base + x)
+#define REG_SCU_C(x)		(scu_regs[REG_SCU_C_ID].base + x)

I would prefer having this in core.c, so the base addresses can remain private to that file instead of exporting them to all of the kernel.

arch/arm/mach-spmp8000/include/mach/scu.h line 46

Quoted 9 lines
arch/arm/mach-spmp8000/include/mach/scu.h new file +9 -0
+#define SCU_A_PERI_RST		0x00
+#define SCU_A_PERI_CLKEN	0x04
+#define SCU_A_PERI_DGCLKEN	0x0C
+
+#define SCU_A_APLL_CFG		0x44
+#define APLL_CFG_P		BIT(0)
+#define APLL_CFG_S		BIT(1)
+#define APLL_CFG_F		BIT(2)
+#define APLL_CFG_E		BIT(3)

The BIT() definition is really just mean for kernel-internal bitops, not so much for device registers.

My recommended style of this is

#define APLL_CFG_P 0x00000001
#define APLL_CFG_S 0x00000002
#define APLL_CFG_F 0x00000004
#define APLL_CFG_E 0x00000008

Quoted 14 lines
arch/arm/mach-spmp8000/include/mach/system.h new file +8 -0
new file mode 100644
index 0000000..be53ff3
@@ -0,0 +1,45 @@
+/*
+ * SPMP8000 system.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.

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

Quoted 10 lines
arch/arm/mach-spmp8000/include/mach/system.h new file +10 -0
+ */
+
+#ifndef __MACH_SPMP8000_SYSTEM_H__
+#define __MACH_SPMP8000_SYSTEM_H__
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+
+#define SPMP8000_WDT_BASE	0x90001000
+#define SPMP8000_WDT_SIZE	0x1000

These have to be removed. For device drivers, you should normally use of_iomap to get at the registers.

Quoted 14 lines
arch/arm/mach-spmp8000/include/mach/system.h new file +14 -0
+#define SPMP8000_WDT_CTR	0x00
+#define SPMP8000_WDT_CTR_TE	BIT(0)
+#define SPMP8000_WDT_CTR_RE	BIT(3)
+
+static inline void arch_idle(void)
+{
+	cpu_do_idle();
+}
+
+static inline void arch_reset(char mode, const char *cmd)
+{
+	void *base;
+
+	base = ioremap(SPMP8000_WDT_BASE, SPMP8000_WDT_SIZE);

arch/arm/mach-spmp8000/include/mach/system.h line 43

Quoted 10 lines
arch/arm/mach-spmp8000/include/mach/system.h new file +10 -0
+	if (!base) {
+		pr_err("spmp8000: Can't ioremap watchdog regs for reset. "
+			"Halt.");
+		while (1);
+	}
+
+	/* Trigger a watchdog reset */
+	writel(SPMP8000_WDT_CTR_RE | SPMP8000_WDT_CTR_TE,
+			base + SPMP8000_WDT_CTR);
+}

Using the watchdog for arch_reset is clever, but unfortunately not too common otherwise, so you have to play tricks. I would try locating the device and ioremapping it from core.c and then export a function to set the register that is used both by the arch_reset function and by the watchdog driver. The latter can still bind to the platform_device, but it then doesn't have to do an extra of_iomap.

Arnd

#9 lore raw

PATCH 2/9 ARM: SPMP8000: Add machine base files

2011/10/11 Arnd Bergmann <[email protected]>:

Quoted 21 lines · Zoltan Devai
Quoted 20 lines
(quoted diff) +20 -0
+
+/* Static mappings:
+ * SCU: timer needs clk support which is inited in init_early
+ * UART: needed for earlyprintk
+ */
+static struct map_desc io_desc[] __initdata = {
+ ? ? {
+ ? ? ? ? ? ? .virtual ? ? ? ?= IO_ADDRESS(SPMP8000_SCU_A_BASE),
+ ? ? ? ? ? ? .pfn ? ? ? ? ? ?= __phys_to_pfn(SPMP8000_SCU_A_BASE),
+ ? ? ? ? ? ? .length ? ? ? ? = SPMP8000_SCU_A_SIZE,
+ ? ? ? ? ? ? .type ? ? ? ? ? = MT_DEVICE,
+ ? ? }, {
+ ? ? ? ? ? ? .virtual ? ? ? ?= IO_ADDRESS(SPMP8000_SCU_B_BASE),
+ ? ? ? ? ? ? .pfn ? ? ? ? ? ?= __phys_to_pfn(SPMP8000_SCU_B_BASE),
+ ? ? ? ? ? ? .length ? ? ? ? = SPMP8000_SCU_B_SIZE,
+ ? ? ? ? ? ? .type ? ? ? ? ? = MT_DEVICE,
+ ? ? }, {
+ ? ? ? ? ? ? .virtual ? ? ? ?= IO_ADDRESS(SPMP8000_SCU_C_BASE),
+ ? ? ? ? ? ? .pfn ? ? ? ? ? ?= __phys_to_pfn(SPMP8000_SCU_C_BASE),
+ ? ? ? ? ? ? .length ? ? ? ? = SPMP8000_SCU_C_SIZE,
Quoted 8 lines
Quoted 2 lines

+ ? ? ? ? ? ? .type ? ? ? ? ? = MT_DEVICE,
+ ? ? },

With Nicolas Pitre's rework of the MMIO space handling, I think it would be nice to just use a single area that spans all of the devices so you can do an optimized ioremap and huge TLBs. Unfortunately that series won't make it into 3.2, but if you just set up a large area now, it will automatically work.

What do you mean by 'all of the devices' ?
These three, or all MMIO peripherals ?
These are at addresses 0x90005000, 0x92005000 and 0x93007000. So either a 50 or 256 MB area, but both seem like a waste of virtual memory space.

Quoted 4 lines
Quoted 4 lines

+#ifdef CONFIG_DEBUG_LL
+ ? ? {
+ ? ? ? ? ? ? .virtual ? ? ? ?= IO_ADDRESS(SPMP8000_UARTC0_BASE),
+ ? ? ? ? ? ? .pfn ? ? ? ? ? ?= __phys_to_pfn(SPMP8000_UARTC0_BASE),

Quoted 8 lines

Review

Quoted 6 lines

+ ? ? ? ? ? ? .length ? ? ? ? = SPMP8000_UARTC0_SIZE,
+ ? ? ? ? ? ? .type ? ? ? ? ? = MT_DEVICE,
+ ? ? },

(quoted diff) +2 -0
+#endif
+};

And it would be nice to move this into a separate file that handles the early debug code, as prima2 does.

Prima2 doesn't have other static mappings. In my case, that would mean a separate map_desc table in lluart.c and an #ifdef CONFIG_DEBUG_LL in the map_io call.
Is it worth it ?

BTW, the static mappings are only needed to be able to set up the clk stuff in init_early. That in turn is only needed for the timer code to determinate its input clock.
If I would hard-code the timer clock rate in its driver (the bootloader is very unlikely to change), then I could init the clk driver from machine_init and get rid of these mappings.
How about that ?

BTW2,
shouldn't the timer driver also go to drivers/clocksource ?

Quoted 7 lines
Quoted 7 lines
arch/arm/mach-spmp8000/include/mach/dma.h new file +7 -0
+#ifndef __MACH_SPMP8000_DMA_H__
+#define __MACH_SPMP8000_DMA_H__
+
+enum spmp8000_dma_controller {
+ ? ? SPMP8000_APBDMA_A ? ? ? = 0,
+ ? ? SPMP8000_APBDMA_C,
+};

...

Quoted 4 lines

Please separate the dma controller out for now into a separate patch. We really need to have proper device tree bindings so we can avoid the silly filter functions and do something better. That needs some more discussion and any help on the interface is very much appreciated.

OK

Quoted 17 lines
Quoted 4 lines
arch/arm/mach-spmp8000/include/mach/gpio.h new file +1 -0
new file mode 100644
 
+#ifndef __MACH_SPMP8000_GPIO_H__

arch/arm/mach-spmp8000/include/mach/gpio.h line 21

Quoted 10 lines
arch/arm/mach-spmp8000/include/mach/gpio.h new file +10 -0
+#define __MACH_SPMP8000_GPIO_H__
+
+#include <asm-generic/gpio.h>
+
+#define gpio_get_value ? ? ? ? ? ? ? __gpio_get_value
+#define gpio_set_value ? ? ? ? ? ? ? __gpio_set_value
+#define gpio_cansleep ? ? ? ? ? ? ? ?__gpio_cansleep
+#define gpio_to_irq ? ? ? ? ?__gpio_to_irq
+
+#endif /* __MACH_SPMP8000_GPIO_H__ */

I think this can become an empty file now, with Russell's latest cleanup going into 3.2.

I didn't find that in either your or Russell's tree, where should I look for it ?

Quoted 6 lines

arch/arm/mach-spmp8000/include/mach/gpio.h

Quoted 2 lines
arch/arm/mach-spmp8000/include/mach/gpio.h new file +2 -0
+/* Vitual to physical translation of statically mapped space */
+#define IO_ADDRESS(x) ? ? ? ? ? ? ? ?((x) | 0xF0000000)

Better make this return a 'void __iomem *', e.g. by doing

#define IO_ADDRESS(x) ? (((x) & 0x0fffffff) + ((void __iomem *)0xf0000000)

This is used by map_desc, so it needs to return an int.
I use IO_PTR for the pointer version, but maybe the naming is misleading.

Quoted 4 lines
Quoted 4 lines
arch/arm/mach-spmp8000/include/mach/gpio.h new file +4 -0
+/* Needed for the static mappings and uncompress.h */
+#define SPMP8000_UARTC0_BASE 0x92B04000
+#define SPMP8000_UARTC0_SIZE SZ_4K
+#define SPMP8000_SCU_C_BASE ?0x92005000
Quoted 8 lines

arch/arm/mach-spmp8000/include/mach/gpio.h

Quoted 5 lines
arch/arm/mach-spmp8000/include/mach/gpio.h new file +5 -0
+#define SPMP8000_SCU_C_SIZE ?SZ_4K
+#define SPMP8000_SCU_A_BASE ?0x93007000
+#define SPMP8000_SCU_A_SIZE ?SZ_4K
+#define SPMP8000_SCU_B_BASE ?0x90005000
+#define SPMP8000_SCU_B_SIZE ?SZ_4K

Do you need these all so early that you cannot get the location from the device tree?

See the reason above

Quoted 8 lines

arch/arm/mach-spmp8000/include/mach/gpio.h

Quoted 6 lines
arch/arm/mach-spmp8000/include/mach/gpio.h new file +6 -0
+/* FIXME This should go away */
+#define IO_SPACE_LIMIT ?0
+
+#define __mem_pci(a) ? ?(a)
+#define __io(a) ? ? ? ? __typesafe_io(a)
+

I think you should better define __io() to NULL.

OK

Quoted 28 lines
Quoted 28 lines

+++ b/arch/arm/mach-spmp8000/include/mach/regs-timer.h

arch/arm/mach-spmp8000/include/mach/gpio.h new file +26 -0
@@ -0,0 +1,32 @@
+/*
+ * SPMP8000 timer support
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * These timer reg definitions are used by the timer and pwm drivers
+ */
+#ifndef __MACH_SPMP8000_REGS_TIMER_H__
+#define __MACH_SPMP8000_REGS_TIMER_H__
+
+#define SPMP8000_TMRB(tnum) ?(tnum * 0x20)
+#define SPMP8000_TMRB_CTR ? ?0x00
+#define SPMP8000_TMRB_CTR_TE BIT(0)
+#define SPMP8000_TMRB_CTR_IE BIT(1)
+#define SPMP8000_TMRB_CTR_OE BIT(2)
+#define SPMP8000_TMRB_CTR_PWMON ? ? ?BIT(3)
+#define SPMP8000_TMRB_CTR_UD BIT(4)
+#define SPMP8000_TMRB_CTR_UDS ? ? ? ?BIT(5)
+#define SPMP8000_TMRB_CTR_OM BIT(6)
+#define SPMP8000_TMRB_CTR_ES_SH ? ? ?8
+#define SPMP8000_TMRB_CTR_M_SH ? ? ? 10
+#define SPMP8000_TMRB_PSR ? ?0x04
Quoted 8 lines

arch/arm/mach-spmp8000/include/mach/gpio.h line 32

Quoted 6 lines
arch/arm/mach-spmp8000/include/mach/gpio.h new file +6 -0
+#define SPMP8000_TMRB_LDR ? ?0x08
+#define SPMP8000_TMRB_VLR ? ?0x08
+#define SPMP8000_TMRB_ISR ? ?0x0C
+#define SPMP8000_TMRB_CMP ? ?0x10
+
+#endif /* __MACH_SPMP8000_REGS_TIMER_H__ */

No need for this header, just move the definitions into the timer driver.

It's also used by the pwm driver (pwm is just a timer with a HW output pin) as the header indicates. Not a lots of lines, so I can include it in both if you think that's more appropriate.

Quoted 22 lines
Quoted 22 lines
arch/arm/mach-spmp8000/include/mach/scu.h new file +15 -0
new file mode 100644
index 0000000..e3a98d4
@@ -0,0 +1,146 @@
+/*
+ * SPMP8000 System Control Unit register definitions
+ * SCUs are a random collection of reset, clock, gpio, pinmux, etc. controllers
+ * so that these definitions are needed at several places.
 
+struct scu_reg_t {
+ ? ? void *base;
+ ? ? void *clken;
+};
+
+extern struct scu_reg_t scu_regs[];
+
+#define REG_SCU_BASE(x) ? ? ? ? ? ? ?scu_regs[x].base
+#define REG_SCU_CLKEN(x) ? ? scu_regs[x].clken
+#define REG_SCU_A_ID ? ? ? ? 0
+#define REG_SCU_B_ID ? ? ? ? 1
Quoted 8 lines

arch/arm/mach-spmp8000/include/mach/scu.h

Quoted 4 lines
arch/arm/mach-spmp8000/include/mach/scu.h new file +4 -0
+#define REG_SCU_C_ID ? ? ? ? 2
+#define REG_SCU_A(x) ? ? ? ? (scu_regs[REG_SCU_A_ID].base + x)
+#define REG_SCU_B(x) ? ? ? ? (scu_regs[REG_SCU_B_ID].base + x)
+#define REG_SCU_C(x) ? ? ? ? (scu_regs[REG_SCU_C_ID].base + x)

I would prefer having ?this in core.c, so the base addresses can remain private to that file instead of exporting them to all of the kernel.

I'll get rid of these macros altogether, they're not really nice.

Quoted 13 lines

arch/arm/mach-spmp8000/include/mach/scu.h

Quoted 9 lines
arch/arm/mach-spmp8000/include/mach/scu.h new file +9 -0
+#define SCU_A_PERI_RST ? ? ? ? ? ? ? 0x00
+#define SCU_A_PERI_CLKEN ? ? 0x04
+#define SCU_A_PERI_DGCLKEN ? 0x0C
+
+#define SCU_A_APLL_CFG ? ? ? ? ? ? ? 0x44
+#define APLL_CFG_P ? ? ? ? ? BIT(0)
+#define APLL_CFG_S ? ? ? ? ? BIT(1)
+#define APLL_CFG_F ? ? ? ? ? BIT(2)
+#define APLL_CFG_E ? ? ? ? ? BIT(3)

The BIT() definition is really just mean for kernel-internal bitops, not so much for device registers.

Quoted 6 lines

My recommended style of this is

#define APLL_CFG_P ? ? ? ? ? ? ?0x00000001
#define APLL_CFG_S ? ? ? ? ? ? ?0x00000002
#define APLL_CFG_F ? ? ? ? ? ? ?0x00000004
#define APLL_CFG_E ? ? ? ? ? ? ?0x00000008

Are you sure about this ?
I recall reading a recommendation to use BIT() as its less error-prone, easier matched with the datasheet and properly typed. Also:
kernel$ grep -R " BIT(" drivers/ | wc -l
2186

Quoted 53 lines
Quoted 14 lines
arch/arm/mach-spmp8000/include/mach/system.h new file +8 -0
new file mode 100644
index 0000000..be53ff3
@@ -0,0 +1,45 @@
+/*
+ * SPMP8000 system.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.

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

Quoted 10 lines
arch/arm/mach-spmp8000/include/mach/system.h new file +10 -0
+ */
+
+#ifndef __MACH_SPMP8000_SYSTEM_H__
+#define __MACH_SPMP8000_SYSTEM_H__
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+
+#define SPMP8000_WDT_BASE ? ?0x90001000
+#define SPMP8000_WDT_SIZE ? ?0x1000

These have to be removed. For device drivers, you should normally use of_iomap to get at the registers.

Quoted 14 lines
arch/arm/mach-spmp8000/include/mach/system.h new file +14 -0
+#define SPMP8000_WDT_CTR ? ? 0x00
+#define SPMP8000_WDT_CTR_TE ?BIT(0)
+#define SPMP8000_WDT_CTR_RE ?BIT(3)
+
+static inline void arch_idle(void)
+{
+ ? ? cpu_do_idle();
+}
+
+static inline void arch_reset(char mode, const char *cmd)
+{
+ ? ? void *base;
+
+ ? ? base = ioremap(SPMP8000_WDT_BASE, SPMP8000_WDT_SIZE);

arch/arm/mach-spmp8000/include/mach/system.h line 42

Quoted 10 lines
arch/arm/mach-spmp8000/include/mach/system.h new file +10 -0
+ ? ? if (!base) {
+ ? ? ? ? ? ? pr_err("spmp8000: Can't ioremap watchdog regs for reset. "
+ ? ? ? ? ? ? ? ? ? ? "Halt.");
+ ? ? ? ? ? ? while (1);
+ ? ? }
+
+ ? ? /* Trigger a watchdog reset */
+ ? ? writel(SPMP8000_WDT_CTR_RE | SPMP8000_WDT_CTR_TE,
+ ? ? ? ? ? ? ? ? ? ? base + SPMP8000_WDT_CTR);
+}
Quoted 7 lines

Using the watchdog for arch_reset is clever, but unfortunately not too common otherwise, so you have to play tricks. I would try locating the device and ioremapping it from core.c and then export a function to set the register that is used both by the arch_reset function and by the watchdog driver. The latter can still bind to the platform_device, but it then doesn't have to do an extra of_iomap.

OK

#10 lore raw

PATCH 2/9 ARM: SPMP8000: Add machine base files

Quoted 5 lines · Zoltan Devai

2011/10/11 Arnd Bergmann <[email protected]>:

Quoted 2 lines

I think this can become an empty file now, with Russell's latest cleanup going into 3.2.

I didn't find that in either your or Russell's tree, where should I look for it ?

It's definitely there - this is how asm/gpio.h is changed:

arch/arm/include/asm/gpio.h +19 -0
index 166a7a3..11ad0bf 100644
@@ -4,4 +4,23 @@
 /* not all ARM platforms necessarily support this API ... */
 #include <mach/gpio.h>
 
+#ifndef __ARM_GPIOLIB_COMPLEX
+/* Note: this may rely upon the value of ARCH_NR_GPIOS set in mach/gpio.h */
+#include <asm-generic/gpio.h>
+
+/* The trivial gpiolib dispatchers */
+#define gpio_get_value  __gpio_get_value
+#define gpio_set_value  __gpio_set_value
+#define gpio_cansleep   __gpio_cansleep
+#endif
+
+/*
+ * Provide a default gpio_to_irq() which should satisfy every case.
+ * However, some platforms want to do this differently, so allow them
+ * to override it.
+ */
+#ifndef gpio_to_irq
+#define gpio_to_irq    __gpio_to_irq
+#endif
+
 #endif /* _ARCH_ARM_GPIO_H */

So, for the case where everything goes through gpiolib and there's no platform accelerators for SoC GPIOs, there's no need to have anything in mach/gpio.h. If a platform does want to do something special, it needs to define __ARM_GPIOLIB_COMPLEX.

Quoted 8 lines
Quoted 6 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+/* Vitual to physical translation of statically mapped space */
+#define IO_ADDRESS(x) ? ? ? ? ? ? ? ?((x) | 0xF0000000)

Better make this return a 'void __iomem *', e.g. by doing

#define IO_ADDRESS(x) ? (((x) & 0x0fffffff) + ((void __iomem *)0xf0000000)

This is used by map_desc, so it needs to return an int.
I use IO_PTR for the pointer version, but maybe the naming is misleading.

Much preferred to have macros returning virtual addresses as a pointer, and then have the map_desc stuff cast. This means if you end up using IO_ADDRESS() with readl() etc, then you're going to be correct no matter what.

Quoted 9 lines
Quoted 8 lines

Review

Quoted 6 lines
(quoted diff) +6 -0
+/* FIXME This should go away */
+#define IO_SPACE_LIMIT ?0
+
+#define __mem_pci(a) ? ?(a)
+#define __io(a) ? ? ? ? __typesafe_io(a)
+

I think you should better define __io() to NULL.

OK

Or if you don't have any PCI/ISA IO, don't define __io at all, which means inb() et.al. will be undefined (note: the 8250 driver still wants these.) Also select NO_IOPORT as well in the config if you don't support PCI/ISA IO.

#11 lore raw

PATCH 2/9 ARM: SPMP8000: Add machine base files

In reply to Zoltan Devai
Quoted 30 lines · Zoltan Devai

2011/10/11 Arnd Bergmann <[email protected]>:

Quoted 21 lines · Zoltan Devai
Quoted 20 lines
arch/arm/mach-spmp8000/core.c new file +20 -0
+
+/* Static mappings:
+ * SCU: timer needs clk support which is inited in init_early
+ * UART: needed for earlyprintk
+ */
+static struct map_desc io_desc[] __initdata = {
+     {
+             .virtual        = IO_ADDRESS(SPMP8000_SCU_A_BASE),
+             .pfn            = __phys_to_pfn(SPMP8000_SCU_A_BASE),
+             .length         = SPMP8000_SCU_A_SIZE,
+             .type           = MT_DEVICE,
+     }, {
+             .virtual        = IO_ADDRESS(SPMP8000_SCU_B_BASE),
+             .pfn            = __phys_to_pfn(SPMP8000_SCU_B_BASE),
+             .length         = SPMP8000_SCU_B_SIZE,
+             .type           = MT_DEVICE,
+     }, {
+             .virtual        = IO_ADDRESS(SPMP8000_SCU_C_BASE),
+             .pfn            = __phys_to_pfn(SPMP8000_SCU_C_BASE),
+             .length         = SPMP8000_SCU_C_SIZE,
Quoted 8 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+             .type           = MT_DEVICE,
+     },

With Nicolas Pitre's rework of the MMIO space handling, I think it would be nice to just use a single area that spans all of the devices so you can do an optimized ioremap and huge TLBs. Unfortunately that series won't make it into 3.2, but if you just set up a large area now, it will automatically work.

Quoted 5 lines

What do you mean by 'all of the devices' ?
These three, or all MMIO peripherals ?
These are at addresses 0x90005000, 0x92005000 and 0x93007000. So either a 50 or 256 MB area, but both seem like a waste of virtual memory space.

I was thinking of mapping everything that your IO_ADDRESS macro covers, which should be something like 240 MB starting at 0x90000000/0xf0000000. On most ARM9 platforms, virtual memory is not really contraint at all because there is not all that much physical memory. Also, you currently reserve the entire 0xf0000000 segment for MMIO ranges, so I would expect that you can keep doing that after the cleanup.

Quoted 12 lines
Quoted 4 lines
Quoted 4 lines
arch/arm/mach-spmp8000/core.c new file +4 -0
+#ifdef CONFIG_DEBUG_LL
+     {
+             .virtual        = IO_ADDRESS(SPMP8000_UARTC0_BASE),
+             .pfn            = __phys_to_pfn(SPMP8000_UARTC0_BASE),
Quoted 8 lines

arch/arm/mach-spmp8000/core.c line 49

Quoted 6 lines
arch/arm/mach-spmp8000/core.c new file +5 -0
+             .length         = SPMP8000_UARTC0_SIZE,
+             .type           = MT_DEVICE,
+     },
+#endif
+};

And it would be nice to move this into a separate file that handles the early debug code, as prima2 does.

Quoted 4 lines

Prima2 doesn't have other static mappings. In my case, that would mean a separate map_desc table in lluart.c and an #ifdef CONFIG_DEBUG_LL in the map_io call.
Is it worth it ?

Yes, it's worth it. Don't do the #ifdef in the map_io call though, do it in the header file that declares the function prototype.

Quoted 7 lines

BTW, the static mappings are only needed to be able to set up the clk stuff in init_early. That in turn is only needed for the timer code to determinate its input clock.
If I would hard-code the timer clock rate in its driver (the bootloader is very unlikely to change), then I could init the clk driver from machine_init and get rid of these mappings.
How about that ?

Maybe you can just pass the clock rate in the device tree then. If the boot loader changes it, it can still pass the correct rate and you don't need the early mappings for functional reasons any more.

Quoted 2 lines

BTW2,
shouldn't the timer driver also go to drivers/clocksource ?

Yes, good point. Please move it there.

Quoted 23 lines
Quoted 15 lines
Quoted 15 lines
arch/arm/mach-spmp8000/include/mach/regs-timer.h new file +15 -0
+#ifndef __MACH_SPMP8000_REGS_TIMER_H__
+#define __MACH_SPMP8000_REGS_TIMER_H__
+
+#define SPMP8000_TMRB(tnum)  (tnum * 0x20)
+#define SPMP8000_TMRB_CTR    0x00
+#define SPMP8000_TMRB_CTR_TE BIT(0)
+#define SPMP8000_TMRB_CTR_IE BIT(1)
+#define SPMP8000_TMRB_CTR_OE BIT(2)
+#define SPMP8000_TMRB_CTR_PWMON      BIT(3)
+#define SPMP8000_TMRB_CTR_UD BIT(4)
+#define SPMP8000_TMRB_CTR_UDS        BIT(5)
+#define SPMP8000_TMRB_CTR_OM BIT(6)
+#define SPMP8000_TMRB_CTR_ES_SH      8
+#define SPMP8000_TMRB_CTR_M_SH       10
+#define SPMP8000_TMRB_PSR    0x04
Quoted 8 lines

arch/arm/mach-spmp8000/include/mach/regs-timer.h line 32

Quoted 6 lines
arch/arm/mach-spmp8000/include/mach/regs-timer.h new file +6 -0
+#define SPMP8000_TMRB_LDR    0x08
+#define SPMP8000_TMRB_VLR    0x08
+#define SPMP8000_TMRB_ISR    0x0C
+#define SPMP8000_TMRB_CMP    0x10
+
+#endif /* __MACH_SPMP8000_REGS_TIMER_H__ */

No need for this header, just move the definitions into the timer driver.

Quoted 3 lines

It's also used by the pwm driver (pwm is just a timer with a HW output pin) as the header indicates. Not a lots of lines, so I can include it in both if you think that's more appropriate.

Normally you need to have proper locking around register accesses when a piece of hardware is used by two drivers. My feeling is that the abstraction should be on a higher level because of this, either exporting functions from the timer driver that are used by the pwm driver, or having one low-level mfd driver exporting simple functions that are used by both of them.

Quoted 9 lines
Quoted 9 lines

The BIT() definition is really just mean for kernel-internal bitops, not so much for device registers.

My recommended style of this is

#define APLL_CFG_P              0x00000001
#define APLL_CFG_S              0x00000002
#define APLL_CFG_F              0x00000004
#define APLL_CFG_E              0x00000008
Quoted 6 lines

Are you sure about this ?
I recall reading a recommendation to use BIT() as its less error-prone, easier matched with the datasheet and properly typed. Also:
kernel$ grep -R " BIT(" drivers/ | wc -l
2186

Fair enough. My preference is still the other way (maybe with an added 'ul' for type safety), but that may be mostly because I got bitten too much by IBM hardware specifications numbering the bits in the opposite way. Just decide for yourself here.

Arnd

#12 lore raw

PATCH 2/9 ARM: SPMP8000: Add machine base files

Quoted 72 lines · Arnd Bergmann
Quoted 30 lines · Zoltan Devai

2011/10/11 Arnd Bergmann <[email protected]>:

Quoted 21 lines · Zoltan Devai
Quoted 20 lines
arch/arm/mach-spmp8000/core.c new file +20 -0
+
+/* Static mappings:
+ * SCU: timer needs clk support which is inited in init_early
+ * UART: needed for earlyprintk
+ */
+static struct map_desc io_desc[] __initdata = {
+     {
+             .virtual        = IO_ADDRESS(SPMP8000_SCU_A_BASE),
+             .pfn            = __phys_to_pfn(SPMP8000_SCU_A_BASE),
+             .length         = SPMP8000_SCU_A_SIZE,
+             .type           = MT_DEVICE,
+     }, {
+             .virtual        = IO_ADDRESS(SPMP8000_SCU_B_BASE),
+             .pfn            = __phys_to_pfn(SPMP8000_SCU_B_BASE),
+             .length         = SPMP8000_SCU_B_SIZE,
+             .type           = MT_DEVICE,
+     }, {
+             .virtual        = IO_ADDRESS(SPMP8000_SCU_C_BASE),
+             .pfn            = __phys_to_pfn(SPMP8000_SCU_C_BASE),
+             .length         = SPMP8000_SCU_C_SIZE,
Quoted 8 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+             .type           = MT_DEVICE,
+     },

With Nicolas Pitre's rework of the MMIO space handling, I think it would be nice to just use a single area that spans all of the devices so you can do an optimized ioremap and huge TLBs. Unfortunately that series won't make it into 3.2, but if you just set up a large area now, it will automatically work.

Quoted 5 lines

What do you mean by 'all of the devices' ?
These three, or all MMIO peripherals ?
These are at addresses 0x90005000, 0x92005000 and 0x93007000. So either a 50 or 256 MB area, but both seem like a waste of virtual memory space.

I was thinking of mapping everything that your IO_ADDRESS macro covers, which should be something like 240 MB starting at 0x90000000/0xf0000000. On most ARM9 platforms, virtual memory is not really contraint at all because there is not all that much physical memory. Also, you currently reserve the entire 0xf0000000 segment for MMIO ranges, so I would expect that you can keep doing that after the cleanup.

Quoted 12 lines
Quoted 4 lines
Quoted 4 lines
arch/arm/mach-spmp8000/core.c new file +4 -0
+#ifdef CONFIG_DEBUG_LL
+     {
+             .virtual        = IO_ADDRESS(SPMP8000_UARTC0_BASE),
+             .pfn            = __phys_to_pfn(SPMP8000_UARTC0_BASE),
Quoted 8 lines

arch/arm/mach-spmp8000/core.c line 49

Quoted 6 lines
arch/arm/mach-spmp8000/core.c new file +5 -0
+             .length         = SPMP8000_UARTC0_SIZE,
+             .type           = MT_DEVICE,
+     },
+#endif
+};

And it would be nice to move this into a separate file that handles the early debug code, as prima2 does.

Quoted 4 lines

Prima2 doesn't have other static mappings. In my case, that would mean a separate map_desc table in lluart.c and an #ifdef CONFIG_DEBUG_LL in the map_io call.
Is it worth it ?

Yes, it's worth it. Don't do the #ifdef in the map_io call though, do it in the header file that declares the function prototype.

Quoted 7 lines

BTW, the static mappings are only needed to be able to set up the clk stuff in init_early. That in turn is only needed for the timer code to determinate its input clock.
If I would hard-code the timer clock rate in its driver (the bootloader is very unlikely to change), then I could init the clk driver from machine_init and get rid of these mappings.
How about that ?

Quoted 3 lines

Maybe you can just pass the clock rate in the device tree then. If the boot loader changes it, it can still pass the correct rate and you don't need the early mappings for functional reasons any more.

please don't update the DT from the bootloader, I'll recommand you to limit your bootloader to just update the strict minimun (same as ATAG) and touch noting else

Best Regards,
J.

#13 lore raw

PATCH 2/9 ARM: SPMP8000: Add machine base files

Quoted 10 lines · Jean-Christophe PLAGNIOL-VILLARD
Quoted 9 lines · Arnd Bergmann
Quoted 7 lines · Zoltan Devai

BTW, the static mappings are only needed to be able to set up the clk stuff in init_early. That in turn is only needed for the timer code to determinate its input clock.
If I would hard-code the timer clock rate in its driver (the bootloader is very unlikely to change), then I could init the clk driver from machine_init and get rid of these mappings.
How about that ?

Quoted 7 lines
Quoted 3 lines

Maybe you can just pass the clock rate in the device tree then. If the boot loader changes it, it can still pass the correct rate and you don't need the early mappings for functional reasons any more.

please don't update the DT from the bootloader, I'll recommand you to limit your bootloader to just update the strict minimun (same as ATAG) and touch noting else

Why? Do you think it's better to hardcode it and have no way to update it? Note that the more likely scenario even with clock rate in the device tree would be that the value is hardcoded in the board's file and would not be touched by the boot loader. Only if the boot loader actually changes the physical clock rate (which Zoltan described as very unlikely), it would also have to go the full length and update the device tree as well.

Arnd

#14 lore raw

PATCH 3/9 ARM: SPMP8000: Add clk support

In reply to Zoltan Devai
---
 arch/arm/mach-spmp8000/clkdev.c             |  586 +++++++++++++++++++++++++++
 arch/arm/mach-spmp8000/clock.c              |  155 +++++++
 arch/arm/mach-spmp8000/include/mach/clock.h |   37 ++
 3 files changed, 778 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-spmp8000/clkdev.c
 create mode 100644 arch/arm/mach-spmp8000/clock.c
 create mode 100644 arch/arm/mach-spmp8000/include/mach/clock.h
arch/arm/mach-spmp8000/clkdev.c new file +586 -0
new file mode 100644
index 0000000..c492d20
@@ -0,0 +1,586 @@
+/*
+ * SPMP8000 machines clock support
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <mach/clock.h>
+#include <mach/scu.h>
+
+/* The SoC doesn't support anything else, so no need to make these machine
+ * configurable.
+ */
+#define XTAL_FREQ		27000000UL
+#define XTAL_32K_FREQ		32768UL
+#define APLL_48K_FREQ		73728000UL
+#define APLL_44K_FREQ		67737600UL
+
+static unsigned long spll_get_rate(struct clk *clk)
+{
+	unsigned int f, r, od;
+	unsigned long osc;
+	u32 cfg;
+
+	cfg = readl(REG_SCU_B(SCU_B_SPLL_CFG));
+	osc = XTAL_FREQ;
+
+	/* SPLL off ? */
+	if ((cfg & (SPLL_CFG_XR | SPLL_CFG_PD | SPLL_CFG_BP)) ||
+		(~cfg & (SPLL_CFG_PL | SPLL_CFG_XE | SPLL_CFG_SE))) {
+		return 0;
+	}
+
+	f = (cfg & SPLL_CFG_F_MASK) >> SPLL_CFG_F_SHIFT;
+	r = (cfg & SPLL_CFG_R_MASK) >> SPLL_CFG_R_SHIFT;
+	od = !!(cfg & SPLL_CFG_OD);
+	clk->rate = (osc * (f + 1) * 2) / ((r + 1) * (od + 1));
+
+	return clk->rate;
+}
+
+static unsigned long pll_mux_get_rate(int ratesel, struct clk *clk)
+{
+	int pll_rate;
+
+	switch (ratesel) {
+	case 0:
+		clk->rate = XTAL_FREQ;
+		break;
+	case 1:
+		clk->rate = XTAL_32K_FREQ;
+		break;
+	case 2:
+		pll_rate = clk_get_rate(clk->parent);
+		clk->rate = pll_rate / 2;
+		break;
+	case 3:
+		pll_rate = clk_get_rate(clk->parent);
+		clk->rate = pll_rate / 3;
+		break;
+	}
+
+	return clk->rate;
+}
+
+static unsigned long ref_arm_get_rate(struct clk *clk)
+{
+	int ratesel;
+	u32 spll_cfg;
+
+	spll_cfg = readl(REG_SCU_B(SCU_B_SPLL_CFG));
+	ratesel = (spll_cfg & SPLL_CFG_ASEL_MASK) >> SPLL_CFG_ASEL_SHIFT;
+	return pll_mux_get_rate(ratesel, clk);
+}
+
+static unsigned long ref_ceva_get_rate(struct clk *clk)
+{
+	int ratesel;
+	u32 spll_cfg;
+
+	spll_cfg = readl(REG_SCU_B(SCU_B_SPLL_CFG));
+	ratesel = (spll_cfg & SPLL_CFG_CSEL_MASK) >> SPLL_CFG_CSEL_SHIFT;
+	return pll_mux_get_rate(ratesel, clk);
+}
+
+static unsigned long apll_get_rate(void)
+{
+	u32 cfg = readl(REG_SCU_A(SCU_A_APLL_CFG));
+
+	if ((cfg & APLL_CFG_R) || (~cfg & (APLL_CFG_P | APLL_CFG_E)) ||
+		(((cfg & APLL_CFG_AS_MASK) >> APLL_CFG_AS_SHIFT)
+			!= APLL_CFG_AS_MAGIC)) {
+		return 0;
+	}
+
+	/* Not dealing with the input oscillator frequency as the settings
+	 * for non-27Mhz are unknown, and all platforms use that anyway */
+	if (cfg & APLL_CFG_S)
+		return APLL_44K_FREQ;
+	else
+		return APLL_48K_FREQ;
+}
+
+static void apll_enable(struct clk *clk)
+{
+	u32 cfg = readl(REG_SCU_A(SCU_A_APLL_CFG));
+
+	/* Store new config with reset, then disable reset */
+	cfg |= APLL_CFG_R;
+	cfg |= APLL_CFG_E | APLL_CFG_P | APLL_CFG_F;
+	cfg |= APLL_CFG_AS_MAGIC << APLL_CFG_AS_SHIFT;
+
+	writel(cfg, REG_SCU_A(SCU_A_APLL_CFG));
+
+	cfg &= ~APLL_CFG_R;
+	writel(cfg, REG_SCU_A(SCU_A_APLL_CFG));
+}
+
+static void apll_disable(struct clk *clk)
+{
+	u32 cfg = readl(REG_SCU_A(SCU_A_APLL_CFG));
+
+	cfg &= ~APLL_CFG_P;
+	cfg |= APLL_CFG_E;
+
+	writel(cfg, REG_SCU_A(SCU_A_APLL_CFG));
+}
+
+static int apll_set_rate(unsigned long rate)
+{
+	u32 cfg;
+
+	cfg = readl(REG_SCU_A(SCU_A_APLL_CFG));
+
+	switch (rate) {
+	case 67737600:
+		cfg |= APLL_CFG_S;
+		break;
+	case 73728000:
+		cfg &= ~APLL_CFG_S;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	writel(cfg, REG_SCU_A(SCU_A_APLL_CFG));
+
+	return 0;
+}
+
+static void i2s_mck_switch(u32 mask, int enable)
+{
+	u32 cfg = readl(REG_SCU_A(SCU_A_APLL_CFG));
+	if (enable)
+		cfg &= ~mask;
+	else
+		cfg |= mask;
+
+	writel(cfg, REG_SCU_A(SCU_A_APLL_CFG));
+}
+
+static void i2stx_mck_enable(struct clk *clk)
+{
+	i2s_mck_switch(APLL_CFG_DS, 1);
+}
+
+static void i2stx_mck_disable(struct clk *clk)
+{
+	i2s_mck_switch(APLL_CFG_DS, 0);
+}
+
+static void i2srx_mck_enable(struct clk *clk)
+{
+	i2s_mck_switch(APLL_CFG_AS, 1);
+}
+
+static void i2srx_mck_disable(struct clk *clk)
+{
+	i2s_mck_switch(APLL_CFG_AS, 0);
+}
+
+static const int apll_dividers[7] = {
+	3, 6, 9, 12, 18, 24, 32,
+};
+
+static unsigned long i2s_mck_get_rate(struct clk *clk, u32 msk, u32 sh)
+{
+	unsigned long apll_rate = apll_get_rate();
+	u32 cfg = readl(REG_SCU_A(SCU_A_APLL_CFG));
+	int divider;
+
+	divider = (cfg & msk) >> sh;
+
+	if (divider == ARRAY_SIZE(apll_dividers))
+		return 0;
+
+	divider = apll_dividers[divider];
+
+	return apll_rate / divider;
+}
+
+static unsigned long i2stx_mck_get_rate(struct clk *clk)
+{
+	return i2s_mck_get_rate(clk, APLL_CFG_DAR_MASK, APLL_CFG_DAR_SHIFT);
+}
+
+static unsigned long i2srx_mck_get_rate(struct clk *clk)
+{
+	return i2s_mck_get_rate(clk, APLL_CFG_ADR_MASK, APLL_CFG_ADR_SHIFT);
+}
+
+static int i2s_mck_set_rate(struct clk *clk, unsigned long rate,
+					u32 msk, u32 sh)
+{
+	int i = ARRAY_SIZE(apll_dividers) - 1;
+	unsigned long apll_rate;
+	int divider = -1;
+	u32 cfg;
+
+	/* Set up APLL */
+	if (rate % 8000)
+		apll_rate = APLL_44K_FREQ;
+	else
+		apll_rate = APLL_48K_FREQ;
+
+	apll_set_rate(apll_rate);
+
+	cfg = readl(REG_SCU_A(SCU_A_APLL_CFG));
+
+	/* Get the biggest possible divider for MCK */
+	while (i >= 0) {
+		if (apll_rate / apll_dividers[i] == rate) {
+			divider = apll_dividers[i];
+			break;
+		}
+
+		i--;
+	}
+
+	if (divider < 0)
+		return -EINVAL;
+
+	cfg &= ~msk;
+	cfg |= (i << sh);
+
+	writel(cfg, REG_SCU_A(SCU_A_APLL_CFG));
+
+	return 0;
+}
+
+static int i2stx_mck_set_rate(struct clk *clk, unsigned long rate)
+{
+	return i2s_mck_set_rate(clk, rate,
+					APLL_CFG_DAR_MASK, APLL_CFG_DAR_SHIFT);
+}
+
+static int i2srx_mck_set_rate(struct clk *clk, unsigned long rate)
+{
+	return i2s_mck_set_rate(clk, rate,
+					APLL_CFG_ADR_MASK, APLL_CFG_ADR_SHIFT);
+}
+
+static int divider_set_clock(struct clk *clk, int on)
+{
+	u32 divider;
+
+	if (!(clk->flags & CLK_DIVIDER_WITH_ENABLE))
+		return -EINVAL;
+
+	divider = readl(REG_SCU_BASE(clk->scu) + clk->dividerreg);
+	if (on)
+		divider |= DIVIDER_ENABLE_BIT;
+	else
+		divider = 0;
+	writel(divider, REG_SCU_BASE(clk->scu) + clk->dividerreg);
+
+	return 0;
+}
+
+static void divider_enable_clock(struct clk *clk)
+{
+	divider_set_clock(clk, 1);
+}
+
+static void divider_disable_clock(struct clk *clk)
+{
+	divider_set_clock(clk, 0);
+}
+
+static unsigned long divider_get_rate(struct clk *clk)
+{
+	u32 divider;
+	unsigned long parent_rate = clk_get_rate(clk->parent);
+
+	if (!parent_rate) {
+		clk->rate = 0;
+		return clk->rate;
+	}
+
+	divider = readl(REG_SCU_BASE(clk->scu) + clk->dividerreg);
+	if ((clk->flags & CLK_DIVIDER_WITH_ENABLE) &&
+		!(divider & DIVIDER_ENABLE_BIT)) {
+		clk->rate = 0UL;
+		return clk->rate;
+	}
+
+	clk->rate = parent_rate / ((divider & clk->divmask) + 1);
+
+	return clk->rate;
+}
+
+static int divider_set_rate(struct clk *clk, unsigned long rate)
+{
+	unsigned long parent_rate = clk_get_rate(clk->parent);
+	u32 divider, divider_old;
+
+	if (unlikely(!parent_rate || rate > parent_rate)) {
+		clk->rate = 0;
+		pr_debug("parent rate not sufficient\n");
+		return -EINVAL;
+	}
+
+	divider = (parent_rate / rate) - 1;
+
+	if (divider > clk->divmask) {
+		pr_debug("input clock too high\n");
+		return -EINVAL;
+	};
+
+	divider_old = readl(REG_SCU_BASE(clk->scu) + clk->dividerreg);
+	writel(0, REG_SCU_BASE(clk->scu) + clk->dividerreg);
+	writel(divider, REG_SCU_BASE(clk->scu) + clk->dividerreg);
+
+	/* Re-enable clock if it was enabled before */
+	if (divider_old & DIVIDER_ENABLE_BIT)
+		writel(divider | DIVIDER_ENABLE_BIT,
+			REG_SCU_BASE(clk->scu) + clk->dividerreg);
+
+	clk->rate = parent_rate / (divider + 1);
+
+	return 0;
+}
+static void periph_enable_clock(struct clk *clk)
+{
+	u32 scu_reg = readl(REG_SCU_CLKEN(clk->scu));
+
+	scu_reg |= (1 << clk->scu_periph_id);
+
+	writel(scu_reg, REG_SCU_CLKEN(clk->scu));
+}
+
+static void periph_disable_clock(struct clk *clk)
+{
+	u32 scu_reg = readl(REG_SCU_CLKEN(clk->scu));
+
+	scu_reg &= ~(1 << clk->scu_periph_id);
+
+	writel(scu_reg, REG_SCU_CLKEN(clk->scu));
+}
+
+void spmp8000_update_arm_freqs(void)
+{
+	writel(7, REG_SCU_B(SCU_B_UPDATE_ARM_RATIO));
+}
+EXPORT_SYMBOL(spmp8000_update_arm_freqs);
+
+static struct clk clk_spll = {
+	.get_rate = spll_get_rate,
+	.flags = CLK_FIXED_RATE,
+};
+
+static struct clk clk_ref_arm = {
+	.parent = &clk_spll,
+	.get_rate = ref_arm_get_rate,
+};
+
+static struct clk clk_ref_ceva = {
+	.parent = &clk_spll,
+	.get_rate = ref_ceva_get_rate,
+};
+
+static struct clk clk_apll = {
+	.name = "clk_apll",
+	.enable = apll_enable,
+	.disable = apll_disable,
+};
+
+static struct clk clk_i2stx_mck = {
+	.name = "clk_i2stx_mck",
+	.parent = &clk_apll,
+	.enable = i2stx_mck_enable,
+	.disable = i2stx_mck_disable,
+	.get_rate = i2stx_mck_get_rate,
+	.set_rate = i2stx_mck_set_rate,
+};
+
+static struct clk clk_i2srx_mck = {
+	.name = "clk_i2srx_mck",
+	.parent = &clk_apll,
+	.enable = i2srx_mck_enable,
+	.disable = i2srx_mck_disable,
+	.get_rate = i2srx_mck_get_rate,
+	.set_rate = i2srx_mck_set_rate,
+};
+
+#define SYSTEM_CLK(__name, __parent, __scu, __divider, __divmask, __flags) \
+static struct clk clk_##__name = { \
+	.name = "clk_" #__name, \
+	.parent = &clk_##__parent, \
+	.scu = REG_SCU_##__scu##_ID, \
+	.dividerreg = __divider, \
+	.divmask = __divmask, \
+	.flags = __flags, \
+	.enable = &divider_enable_clock, \
+	.disable = &divider_disable_clock, \
+	.get_rate = &divider_get_rate, \
+	.set_rate = &divider_set_rate, \
+}
+
+SYSTEM_CLK(arm, ref_arm, B, SCU_B_ARM_RATIO, 0x3F, 0);
+SYSTEM_CLK(arm_ahb, arm, B, SCU_B_ARM_AHB_RATIO, 0x3F, 0);
+SYSTEM_CLK(arm_apb, arm_ahb, B, SCU_B_ARM_APB_RATIO, 0x3F, 0);
+SYSTEM_CLK(ceva, ref_ceva, C, SCU_C_CEVA_RATIO, 0x3F, 0);
+SYSTEM_CLK(ceva_ahb, ceva, C, SCU_C_CEVA_AHB_RATIO, 0x3F, 0);
+SYSTEM_CLK(ceva_apb, ceva_ahb, C, SCU_C_CEVA_APB_RATIO, 0x3F, 0);
+SYSTEM_CLK(sys, ref_ceva, C, SCU_C_SYS_RATIO, 0x3F, 0);
+SYSTEM_CLK(sys_ahb, sys, C, SCU_C_SYS_AHB_RATIO, 0x3F, 0);
+SYSTEM_CLK(sys_apb, sys_ahb, C, SCU_C_SYS_APB_RATIO, 0x3F, 0);
+
+SYSTEM_CLK(uart, ref_ceva, A, SCU_A_UART_CFG, 0xFF, CLK_DIVIDER_WITH_ENABLE);
+SYSTEM_CLK(lcd, ref_ceva, A, SCU_A_LCD_CLK_CFG, 0xFF, CLK_DIVIDER_WITH_ENABLE);
+SYSTEM_CLK(csi, ref_ceva, A, SCU_A_CSI_CLK_CFG, 0xFF, CLK_DIVIDER_WITH_ENABLE);
+SYSTEM_CLK(i2srx_bck, i2srx_mck, A, SCU_A_I2S_BCK_CFG, 0xFF,
+		CLK_DIVIDER_WITH_ENABLE);
+SYSTEM_CLK(i2stx_bck, i2stx_mck, A, SCU_A_I2S_BCK_CFG, 0xFF,
+		CLK_DIVIDER_WITH_ENABLE);
+
+
+/* Peripherals */
+#define PERIPH_CLK(__name, __parent, __scu, __id) \
+static struct clk clk_##__name = { \
+	.name = "clk_" #__name, \
+	.parent = &__parent, \
+	.scu = REG_SCU_##__scu##_ID, \
+	.scu_periph_id = __id, \
+	.enable = periph_enable_clock, \
+	.disable = periph_disable_clock, \
+}
+
+/* Not needed yet for any driver */
+#if 0
+PERIPH_CLK(drm, clk_sys_ahb, A, 2);
+PERIPH_CLK(usb_host, clk_sys_ahb, A, 3);
+PERIPH_CLK(usb_device, clk_sys_ahb, A, 4);
+PERIPH_CLK(scu_a, clk_sys_ahb, A, 6);
+PERIPH_CLK(tvout, clk_sys, A, 7);
+PERIPH_CLK(csi_ctrl, clk_sys, A, 10);
+PERIPH_CLK(nand0, clk_sys_ahb, A, 11);
+PERIPH_CLK(nand1, clk_sys_ahb, A, 12);
+PERIPH_CLK(ecc, clk_sys_ahb, A, 13);
+PERIPH_CLK(uart_con, clk_sys_ahb, A, 15);
+PERIPH_CLK(aahbm212, clk_sys, A, 16);
+PERIPH_CLK(nand_abt, clk_sys_ahb, A, 20);
+PERIPH_CLK(rt_abt212, clk_sys, A, 22);
+PERIPH_CLK(cahbm212, clk_sys, A, 23);
+
+PERIPH_CLK(tcm_bist, clk_arm, B, 0);
+PERIPH_CLK(tcm_ctrl, clk_arm, B, 1);
+PERIPH_CLK(ahb2ahb, clk_arm, B, 2);
+PERIPH_CLK(ahbsw, clk_arm, B, 3);
+PERIPH_CLK(vic0, clk_arm_ahb, B, 4);
+PERIPH_CLK(vic1, clk_arm_ahb, B, 5);
+PERIPH_CLK(dpm, clk_arm_ahb, B, 6);
+PERIPH_CLK(apbb, clk_arm_ahb, B, 7);
+PERIPH_CLK(arm926, clk_arm, B, 8);
+PERIPH_CLK(wdt, clk_arm_apb, B, 10);
+PERIPH_CLK(uartapb, clk_arm_apb, B, 11);
+PERIPH_CLK(i2c, clk_arm_apb, B, 12);
+PERIPH_CLK(rand, clk_arm_apb, B, 13);
+PERIPH_CLK(gpio0, clk_arm_apb, B, 14);
+PERIPH_CLK(rtc, clk_arm_apb, B, 15);
+
+PERIPH_CLK(fabricc, clk_sys, C, 0);
+PERIPH_CLK(dmac0, clk_sys, C, 1);
+PERIPH_CLK(dmac1, clk_sys, C, 2);
+PERIPH_CLK(dram_ctrl, clk_sys, C, 4);
+PERIPH_CLK(scu_c, clk_sys, C, 5);
+PERIPH_CLK(i2c_ctrl, clk_sys, C, 6);
+PERIPH_CLK(2d, clk_sys, C, 8);
+PERIPH_CLK(extmem, clk_sys, C, 9);
+PERIPH_CLK(cf, clk_sys_apb, C, 10);
+PERIPH_CLK(ms, clk_sys_apb, C, 11);
+PERIPH_CLK(intmem, clk_sys, C, 12);
+PERIPH_CLK(uartc0, clk_sys_apb, C, 13);
+PERIPH_CLK(uartc1, clk_sys_apb, C, 14);
+PERIPH_CLK(uartc2, clk_sys_apb, C, 15);
+PERIPH_CLK(ssp0, clk_sys_apb, C, 16);
+PERIPH_CLK(ssp1, clk_sys_apb, C, 17);
+PERIPH_CLK(sd1, clk_sys_apb, C, 19);
+PERIPH_CLK(i2c_sys, clk_sys_apb, C, 20);
+PERIPH_CLK(scale, clk_sys, C, 21);
+PERIPH_CLK(scaleabt, clk_sys, C, 22);
+PERIPH_CLK(ti2c, clk_sys_apb, C, 23);
+PERIPH_CLK(fabric_a, clk_sys, C, 24);
+PERIPH_CLK(cxmp_sl, clk_sys, C, 25);
+PERIPH_CLK(cxmd_sl, clk_sys, C, 26);
+#endif
+
+PERIPH_CLK(rt_abt, clk_sys, A, 21);
+/* Make the parent rt_abt to auto-enable it when enabling the lcdc clock */
+PERIPH_CLK(lcd_ctrl, clk_rt_abt, A, 1);
+PERIPH_CLK(apbdma_a, clk_rt_abt, A, 9);
+PERIPH_CLK(apll_ctrl, clk_sys_ahb, A, 14);
+PERIPH_CLK(i2stx_ctrl, clk_apbdma_a, A, 17);
+PERIPH_CLK(i2srx_ctrl, clk_apbdma_a, A, 18);
+PERIPH_CLK(saacc, clk_sys_apb, A, 19);
+
+PERIPH_CLK(tmrb, clk_arm_apb, B, 9);
+PERIPH_CLK(apbdma_c, clk_sys_apb, C, 7);
+PERIPH_CLK(sd0, clk_sys_apb, C, 18);
+
+/* TODO use common macro once available */
+#define _DEFINE_CLKDEV_CON(n) \
+	{ \
+		.dev_id = NULL, \
+		.con_id = #n, \
+		.clk = &clk_##n, \
+	}
+
+#define _DEFINE_CLKDEV_DEV(d, c) \
+	{ \
+		.dev_id = d, \
+		.con_id = NULL, \
+		.clk = &clk_##c, \
+	}
+
+static struct clk_lookup lookups[] = {
+	_DEFINE_CLKDEV_CON(arm),
+	_DEFINE_CLKDEV_CON(arm_ahb),
+	_DEFINE_CLKDEV_CON(arm_apb),
+	_DEFINE_CLKDEV_CON(ceva),
+	_DEFINE_CLKDEV_CON(ceva_ahb),
+	_DEFINE_CLKDEV_CON(ceva_apb),
+	_DEFINE_CLKDEV_CON(sys),
+	_DEFINE_CLKDEV_CON(sys_ahb),
+	_DEFINE_CLKDEV_CON(sys_apb),
+	_DEFINE_CLKDEV_CON(lcd),
+	_DEFINE_CLKDEV_CON(apbdma_a),
+	_DEFINE_CLKDEV_CON(saacc),
+	_DEFINE_CLKDEV_CON(i2stx_mck),
+	_DEFINE_CLKDEV_CON(i2srx_mck),
+	_DEFINE_CLKDEV_CON(i2stx_bck),
+	_DEFINE_CLKDEV_CON(i2srx_bck),
+	_DEFINE_CLKDEV_DEV("uart.0", uart),
+	_DEFINE_CLKDEV_DEV("uart.1", uart),
+	_DEFINE_CLKDEV_DEV("uart.2", uart),
+	_DEFINE_CLKDEV_DEV("93000000.fb", lcd_ctrl),
+	_DEFINE_CLKDEV_DEV("90000000.pwm", tmrb),
+	_DEFINE_CLKDEV_DEV("92b00000.dma", apbdma_c),
+	_DEFINE_CLKDEV_DEV("92b0b000.mmc", sd0),
+	_DEFINE_CLKDEV_DEV("93010000.dma", apbdma_a),
+	_DEFINE_CLKDEV_DEV("93012000.i2s", i2stx_ctrl),
+	_DEFINE_CLKDEV_DEV("9301d000.i2s", i2srx_ctrl),
+};
+
+void __init spmp8000_init_clkdev(void)
+{
+	clkdev_add_table(lookups, ARRAY_SIZE(lookups));
+
+	/* Enable the apll control registers here, as the according clock
+	 * isn't exported, but used by the mck/bck clocks. If we wouldn't
+	 * enable it here, the freq of the pll couldn't be set up before
+	 * enabling one of its child clocks.
+	 * The PLL clock itself will be auto-enabled on demand by them.
+	 */
+	clk_enable(&clk_apll_ctrl);
+}
arch/arm/mach-spmp8000/clock.c new file +155 -0

155 lines not on this page · raw message

arch/arm/mach-spmp8000/include/mach/clock.h new file +37 -0
new file mode 100644
index 0000000..e5cdbd2
@@ -0,0 +1,37 @@
+/*
+ * SPMP8000 machines clock support
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#ifndef __MACH_SPMP8000_CLOCK_H__
+#define __MACH_SPMP8000_CLOCK_H__
+
+struct clk {
+	char		*name;
+	unsigned long	rate;
+	unsigned int	id;
+	unsigned int	refcount;
+	int		scu;
+	int		scu_periph_id;
+	int		dividerreg;
+	int		divmask;
+	unsigned long	flags;
+/* Either really fixed rate (crystal) or which we don't change for sure */
+#define CLK_FIXED_RATE			BIT(0)
+/* SPMP8000 has two types of dividers, one of them needs enabling */
+#define CLK_DIVIDER_WITH_ENABLE		BIT(1)
+
+	struct clk	*parent;
+	unsigned long	(*get_rate)(struct clk *clk);
+	unsigned long	(*round_rate) (struct clk *, u32);
+	int		(*set_rate) (struct clk *, unsigned long);
+	int		(*set_parent) (struct clk *clk, struct clk *parent);
+	void		(*enable) (struct clk *);
+	void		(*disable) (struct clk *);
+};
+
+#endif /* __MACH_SPMP8000_CLOCK_H__ */
Signature · 1 line
1.7.4.1
#15 lore raw

PATCH 3/9 ARM: SPMP8000: Add clk support

Quoted 24 lines · Zoltan Devai
---
 arch/arm/mach-spmp8000/clkdev.c             |  586 +++++++++++++++++++++++++++
 arch/arm/mach-spmp8000/clock.c              |  155 +++++++
 arch/arm/mach-spmp8000/include/mach/clock.h |   37 ++
 3 files changed, 778 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-spmp8000/clkdev.c
 create mode 100644 arch/arm/mach-spmp8000/clock.c
 create mode 100644 arch/arm/mach-spmp8000/include/mach/clock.h
arch/arm/mach-spmp8000/clkdev.c new file +8 -0
new file mode 100644
index 0000000..c492d20
@@ -0,0 +1,586 @@
+/*
+ * SPMP8000 machines clock support
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.

arch/arm/mach-spmp8000/clkdev.c line 18

Quoted 10 lines
arch/arm/mach-spmp8000/clkdev.c new file +10 -0
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <mach/clock.h>
+#include <mach/scu.h>

I find the use of 'scu' for something which isn't the SMP Coherence Unit rather confusing - and I suspect you will too if/when spmp8000 has SMP support.

arch/arm/mach-spmp8000/clkdev.c line 284

Quoted 13 lines
arch/arm/mach-spmp8000/clkdev.c new file +13 -0
+static int divider_set_clock(struct clk *clk, int on)
+{
+	u32 divider;
+
+	if (!(clk->flags & CLK_DIVIDER_WITH_ENABLE))
+		return -EINVAL;
+
+	divider = readl(REG_SCU_BASE(clk->scu) + clk->dividerreg);
+	if (on)
+		divider |= DIVIDER_ENABLE_BIT;
+	else
+		divider = 0;
+	writel(divider, REG_SCU_BASE(clk->scu) + clk->dividerreg);

When you enable, you preserve the other bits in the register. When you disable, you zero the whole register. That looks rather odd - either you want to preserve the other bits or you don't.

arch/arm/mach-spmp8000/clkdev.c line 297

Quoted 13 lines
arch/arm/mach-spmp8000/clkdev.c new file +13 -0
+
+	return 0;
+}
+
+static void divider_enable_clock(struct clk *clk)
+{
+	divider_set_clock(clk, 1);
+}
+
+static void divider_disable_clock(struct clk *clk)
+{
+	divider_set_clock(clk, 0);
+}

afaics, this is the only place divider_set_clock() is called from - which returns an int which is never checked. Is there any point to it returning an int?

Quoted 6 lines
arch/arm/mach-spmp8000/clkdev.c new file +6 -0
+
+static unsigned long divider_get_rate(struct clk *clk)
+{
+	u32 divider;
+	unsigned long parent_rate = clk_get_rate(clk->parent);
+

arch/arm/mach-spmp8000/clkdev.c line 313

Quoted 10 lines
arch/arm/mach-spmp8000/clkdev.c new file +10 -0
+	if (!parent_rate) {
+		clk->rate = 0;
+		return clk->rate;
+	}
+
+	divider = readl(REG_SCU_BASE(clk->scu) + clk->dividerreg);
+	if ((clk->flags & CLK_DIVIDER_WITH_ENABLE) &&
+		!(divider & DIVIDER_ENABLE_BIT)) {
+		clk->rate = 0UL;
+		return clk->rate;

You really should return the rate which you're going to get when the clock is enabled here, not zero because it happens to be disabled at the current point in time.

Quoted 7 lines
arch/arm/mach-spmp8000/clkdev.c new file +7 -0
+	}
+
+	clk->rate = parent_rate / ((divider & clk->divmask) + 1);
+
+	return clk->rate;
+}
+

arch/arm/mach-spmp8000/clkdev.c line 330

Quoted 10 lines
arch/arm/mach-spmp8000/clkdev.c new file +10 -0
+static int divider_set_rate(struct clk *clk, unsigned long rate)
+{
+	unsigned long parent_rate = clk_get_rate(clk->parent);
+	u32 divider, divider_old;
+
+	if (unlikely(!parent_rate || rate > parent_rate)) {
+		clk->rate = 0;
+		pr_debug("parent rate not sufficient\n");
+		return -EINVAL;
+	}

You should set to the maximum (parent rate) in that case.

arch/arm/mach-spmp8000/clkdev.c line 337

Quoted 7 lines
arch/arm/mach-spmp8000/clkdev.c new file +7 -0
+
+	divider = (parent_rate / rate) - 1;
+
+	if (divider > clk->divmask) {
+		pr_debug("input clock too high\n");
+		return -EINVAL;
+	};

Set to the minimum rate here. The trailing ';' is not required.

Quoted 14 lines
arch/arm/mach-spmp8000/clkdev.c new file +14 -0
+
+	divider_old = readl(REG_SCU_BASE(clk->scu) + clk->dividerreg);
+	writel(0, REG_SCU_BASE(clk->scu) + clk->dividerreg);
+	writel(divider, REG_SCU_BASE(clk->scu) + clk->dividerreg);
+
+	/* Re-enable clock if it was enabled before */
+	if (divider_old & DIVIDER_ENABLE_BIT)
+		writel(divider | DIVIDER_ENABLE_BIT,
+			REG_SCU_BASE(clk->scu) + clk->dividerreg);
+
+	clk->rate = parent_rate / (divider + 1);
+
+	return 0;
+}

...

arch/arm/mach-spmp8000/clkdev.c

Quoted 10 lines
arch/arm/mach-spmp8000/clkdev.c new file +10 -0
+static void __clk_disable(struct clk *clk)
+{
+	BUG_ON(clk->refcount == 0);
+
+	if (!(--clk->refcount) && clk->disable) {
+		clk->disable(clk);
+		if (clk->parent)
+			__clk_disable(clk->parent);
+	}
+}

What if the clock has a parent but no disable function?

arch/arm/mach-spmp8000/clkdev.c

Quoted 12 lines
arch/arm/mach-spmp8000/clkdev.c new file +12 -0
+
+static int __clk_enable(struct clk *clk)
+{
+	int ret = 0;
+
+	if (clk->refcount++ == 0 && clk->enable) {
+		if (clk->parent)
+			ret = __clk_enable(clk->parent);
+		if (ret)
+			return ret;
+		else
+			clk->enable(clk);

What if the clock has a parent but no enable function?

#16 lore raw

PATCH 3/9 ARM: SPMP8000: Add clk support

2011/10/13 Russell King - ARM Linux <[email protected]>:

Quoted 3 lines · Zoltan Devai
Quoted 2 lines
arch/arm/mach-spmp8000/clkdev.c new file +0 -0

...

Quoted 5 lines
Quoted 1 line

+#include <mach/scu.h>

I find the use of 'scu' for something which isn't the SMP Coherence Unit rather confusing - and I suspect you will too if/when spmp8000 has SMP support.

Well, Sunplus decided to name these devices 'SCU', which are a collection of reset, clock, debug, pinmux and gpio stuff.
The datasheet doesn't even tell what SCU stands for, but I'd prefer to keep the naming to be in sync with the documentation.

I'll fix the issues pointed out by your other comments in the next version.

Thanks,
Zoltan

#17 lore raw

PATCH 3/9 ARM: SPMP8000: Add clk support

Quoted 10 lines · Zoltan Devai

2011/10/13 Russell King - ARM Linux <[email protected]>:

Quoted 3 lines

I find the use of 'scu' for something which isn't the SMP Coherence Unit rather confusing - and I suspect you will too if/when spmp8000 has SMP support.

Well, Sunplus decided to name these devices 'SCU', which are a collection of reset, clock, debug, pinmux and gpio stuff.
The datasheet doesn't even tell what SCU stands for, but I'd prefer to keep the naming to be in sync with the documentation.

System Control Unit. It's a fairly common name to use for the core system glue logic.

#18 lore raw

PATCH 3/9 ARM: SPMP8000: Add clk support

Quoted 4 lines · Mark Brown
Quoted 3 lines · Zoltan Devai

2011/10/13 Russell King - ARM Linux <[email protected]>:

Quoted 1 line

I find the use of 'scu' for something which isn't the SMP Coherence Unit

Sorry, that should've been 'snoop control unit' to give its proper terminology.

Quoted 10 lines
Quoted 7 lines
Quoted 2 lines

rather confusing - and I suspect you will too if/when spmp8000 has SMP support.

Well, Sunplus decided to name these devices 'SCU', which are a collection of reset, clock, debug, pinmux and gpio stuff.
The datasheet doesn't even tell what SCU stands for, but I'd prefer to keep the naming to be in sync with the documentation.

System Control Unit. It's a fairly common name to use for the core system glue logic.

So, what do we do about this confusing overloading of a TLA ? It's already had me searching through these patches for some reference to SMP stuff and wondering if its differing SMP implementation.

#19 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

In reply to Zoltan Devai
---
 arch/arm/mach-spmp8000/adc.c                      |  465 +++++++++++++++++++++
 arch/arm/mach-spmp8000/include/mach/spmp8000adc.h |   29 ++
 2 files changed, 494 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-spmp8000/adc.c
 create mode 100644 arch/arm/mach-spmp8000/include/mach/spmp8000adc.h
arch/arm/mach-spmp8000/adc.c new file +465 -0
new file mode 100644
index 0000000..c517116
@@ -0,0 +1,465 @@
+/*
+ * SPMP8000 ADC support
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include <mach/spmp8000adc.h>
+#include <mach/scu.h>
+
+#define SARCTRL			0x00
+#define SARCTRL_BUSY		BIT(21)
+#define SARCTRL_REF		BIT(17)
+#define SARCTRL_TOG_EN		BIT(16)
+#define SARCTRL_DIVNUM_OFF	8
+#define SARCTRL_DIVNUM_MASK	0xFF00
+#define SARCTRL_BACK2INT	BIT(7)
+#define SARCTRL_TPS_OFF		5
+#define SARCTRL_TPS_MASK	(3 << 5)
+#define SARCTRL_TPS_NOP		0
+#define SARCTRL_TPS_TEST	1
+#define SARCTRL_TPS_TP		2
+#define SARCTRL_TPS_INT		3
+#define SARCTRL_SARS_OFF	2
+#define SARCTRL_SARS_MASK	(7 << 2)
+#define SARCTRL_SARS_TPXP	0
+#define SARCTRL_SARS_TPXN	1
+#define SARCTRL_SARS_TPYP	2
+#define SARCTRL_SARS_TPYN	3
+#define SARCTRL_SARS_SARIN4	4
+#define SARCTRL_SARS_SARIN5	5
+#define SARCTRL_SARS_SARIN6	6
+#define SARCTRL_SARS_SARIN7	7
+#define SARCTRL_MANCON		BIT(1)
+#define SARCTRL_AUTOCON		BIT(0)
+
+#define SARCONDLY		0x04
+#define SARAUTODLY		0x08
+#define SARDEBTIME		0x0C
+#define SARDEBTIME_DEBDLY_OFF	0
+#define SARDEBTIME_DEBDLY_MASK	0xFFFF
+#define SARDEBTIME_CHKDLY_OFF	16
+#define SARDEBTIME_CHKDLY_MASK	0xFFFF0000UL
+#define SARPNL			0x10
+#define SARAUX			0x14
+#define SARINTEN		0x18
+#define SARINTF			0x1C
+#define SARINT_NAUX		BIT(3)
+#define SARINT_NPNL		BIT(2)
+#define SARINT_PENUP		BIT(1)
+#define SARINT_PENDOWN		BIT(0)
+
+#define SAR_CLKRATE		400000UL
+#define ADC_CHANNELS		8
+
+struct spmp8000_adc {
+	void __iomem		*reg_base;
+	struct device		*dev;
+	struct clk		*clk_apbdma;
+	struct clk		*clk_saacc;
+	spinlock_t		lock;
+	/* These are callbacks that get served with the measurement data
+	 *@their according channel interrupt. */
+	struct spmp8000adc_client_chan	*chan[ADC_CHANNELS];
+	/* Same for the touch-panel data */
+	struct spmp8000adc_client_tp	*tp;
+};
+
+static struct spmp8000_adc	*adc_dev;
+
+static int spmp8000adc_should_run(struct spmp8000_adc *adc)
+{
+	int i = ADC_CHANNELS - 1;
+
+	while (i-- >= 0) {
+		if (adc->chan[i])
+			return 1;
+	}
+
+	return 0;
+}
+
+/* Starts or stops the ADC controller */
+static void spmp8000adc_ctrl(struct spmp8000_adc *adc, int start, int chan)
+{
+	u32			ctrl_reg;
+
+	ctrl_reg = readl(adc->reg_base + SARCTRL);
+
+	if (start) {
+		ctrl_reg &= ~SARCTRL_SARS_MASK;
+		ctrl_reg |= (chan << SARCTRL_SARS_OFF);
+		ctrl_reg |= (SARCTRL_TPS_TEST << SARCTRL_TPS_OFF) |
+				SARCTRL_MANCON;
+	} else
+		ctrl_reg &= ~SARCTRL_MANCON;
+
+	writel(ctrl_reg, adc->reg_base + SARCTRL);
+}
+
+/* Request an ADC channel to be measured. Measurement starts automatically
+ * when at least one channel is requested. IRQ code loops around all channels
+ * to be measured.
+ */
+int spmp8000adc_request_channel(unsigned int chan,
+				struct spmp8000adc_client_chan *c)
+{
+	struct spmp8000_adc	*adc = adc_dev;
+	unsigned long		flags;
+
+	if (chan > ADC_CHANNELS - 1)
+		return -EINVAL;
+
+	if (!c)
+		return -EINVAL;
+
+	spin_lock_irqsave(&adc->lock, flags);
+
+	if (adc->chan[chan]) {
+		spin_unlock_irqrestore(&adc->lock, flags);
+		return -EBUSY;
+	}
+
+	adc->chan[chan] = c;
+
+	if (spmp8000adc_should_run(adc))
+		spmp8000adc_ctrl(adc, 1, chan);
+
+	spin_unlock_irqrestore(&adc->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(spmp8000adc_request_channel);
+
+/* Release an ADC channel. If no more channels are to be measured,
+ * the controller is shut down
+ */
+int spmp8000adc_release_channel(unsigned int chan)
+{
+	struct spmp8000_adc	*adc = adc_dev;
+	unsigned long		flags;
+
+	if (chan > ADC_CHANNELS - 1)
+		return -EINVAL;
+
+	spin_lock_irqsave(&adc->lock, flags);
+
+	if (!spmp8000adc_should_run(adc))
+		spmp8000adc_ctrl(adc, 0, 0);
+
+	adc->chan[chan] = NULL;
+
+	spin_unlock_irqrestore(&adc->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(spmp8000adc_release_channel);
+
+/* Request touch panel measurement */
+int spmp8000adc_request_tp(struct spmp8000adc_client_tp *tp)
+{
+	struct spmp8000_adc	*adc = adc_dev;
+	unsigned long		flags;
+	u32 ctrl_reg;
+
+	spin_lock_irqsave(&adc->lock, flags);
+
+	if (adc->tp) {
+		spin_unlock_irqrestore(&adc->lock, flags);
+		return -EBUSY;
+	}
+
+	adc->tp = tp;
+
+	ctrl_reg = readl(adc->reg_base + SARCTRL);
+	ctrl_reg |= SARCTRL_AUTOCON;
+	writel(ctrl_reg, adc->reg_base + SARCTRL);
+	spin_unlock_irqrestore(&adc->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(spmp8000adc_request_tp);
+
+/* Relase touch panel measurement */
+void spmp8000adc_release_tp(void)
+{
+	struct spmp8000_adc	*adc = adc_dev;
+	unsigned long		flags;
+	u32 ctrl_reg;
+
+	spin_lock_irqsave(&adc->lock, flags);
+
+	ctrl_reg = readl(adc->reg_base + SARCTRL);
+	ctrl_reg &= ~SARCTRL_AUTOCON;
+	writel(ctrl_reg, adc->reg_base + SARCTRL);
+	adc->tp = NULL;
+
+	spin_unlock_irqrestore(&adc->lock, flags);
+}
+EXPORT_SYMBOL(spmp8000adc_release_tp);
+
+static irqreturn_t spmp8000adc_irq(int irq, void *dev)
+{
+	struct spmp8000_adc		*adc = dev;
+	void __iomem			*regs = adc->reg_base;
+	u32				ints, ctrl;
+	s32				val_tp;
+	s16				val_chan;
+	int curchan;
+
+	ints = readl(regs + SARINTF);
+
+	dev_vdbg(adc->dev, "irq: %08X\n", ints);
+
+	if (unlikely(!ints))
+		return IRQ_NONE;
+
+	if (unlikely(ints & SARINT_PENDOWN))
+		dev_dbg(adc->dev, "IRQ: Pen down\n");
+
+	if (unlikely(ints & SARINT_PENUP))
+		dev_dbg(adc->dev, "IRQ: Pen up\n");
+
+	/* Channel measurement end in manual mode */
+	if (ints & SARINT_NAUX) {
+		spin_lock(&adc->lock);
+
+		ctrl = readl(regs + SARCTRL);
+		curchan = (ctrl & SARCTRL_SARS_MASK) >> SARCTRL_SARS_OFF;
+		dev_vdbg(adc->dev, "irq aux chan: %d\n", curchan);
+
+		/* The read also clears the interrupt */
+		val_chan = readl(regs + SARAUX);
+		if (adc->chan[curchan])
+			adc->chan[curchan]->cb(val_chan,
+						adc->chan[curchan]->priv);
+
+		if (!spmp8000adc_should_run(adc)) {
+			spin_unlock(&adc->lock);
+			return IRQ_HANDLED;
+		}
+
+		/* Select next channel */
+		curchan++;
+		if (curchan == ADC_CHANNELS - 1)
+			curchan = 0;
+
+		/* Set up next measurement */
+		spmp8000adc_ctrl(adc, 1, curchan);
+
+		spin_unlock(&adc->lock);
+	}
+
+	/* Touch panel measurement end in automatic mode */
+	if (ints & SARINT_NPNL) {
+		/* The read also clears the interrupt */
+		val_tp = readl(regs + SARPNL);
+		if (adc->tp)
+			adc->tp->cb(val_tp, adc->tp->priv);
+	}
+
+	return IRQ_HANDLED;
+}
+
+
+static int __devinit spmp8000adc_probe(struct platform_device *pdev)
+{
+	struct spmp8000_adc		*adc;
+	struct clk			*clk_apbdma, *clk_saacc;
+	unsigned long			clkrate;
+	struct resource			*r;
+	int ret, irq;
+
+	if (adc_dev) {
+		dev_err(&pdev->dev, "ADC driver already initialized\n");
+		return -EINVAL;
+	}
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r)
+		return -ENODEV;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	r = request_mem_region(r->start, resource_size(r), pdev->name);
+	if (!r) {
+		dev_err(&pdev->dev, "Failed to request base memory region\n");
+		return -EBUSY;
+	}
+
+	adc = kzalloc(sizeof(struct spmp8000_adc), GFP_KERNEL);
+	if (!adc) {
+		ret = -ENOMEM;
+		goto out_release_mem;
+	}
+
+	adc->reg_base = ioremap(r->start, resource_size(r));
+	if (!adc->reg_base) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+
+	clk_apbdma = clk_get(&pdev->dev, "apbdma_a");
+	if (IS_ERR_OR_NULL(clk_apbdma)) {
+		dev_err(&pdev->dev, "Can't get data path clock\n");
+		ret = -ENODEV;
+		goto out_iounmap;
+	}
+
+	clk_saacc = clk_get(&pdev->dev, "saacc");
+	if (IS_ERR_OR_NULL(clk_saacc)) {
+		dev_err(&pdev->dev, "Can't get controller clock\n");
+		ret = -ENODEV;
+		goto out_put_clk_apbdma;
+	}
+
+	clk_enable(clk_apbdma);
+	clk_enable(clk_saacc);
+
+	clkrate = clk_get_rate(clk_saacc);
+
+	clkrate = DIV_ROUND_UP(clkrate, SAR_CLKRATE);	/* Target clkrate */
+	clkrate = DIV_ROUND_UP(clkrate, 2) - 1;		/* Divisor */
+
+	dev_dbg(&pdev->dev, "clock rate: %ld\n", clk_get_rate(clk_saacc) /
+		((clkrate + 1) * 2));
+
+	if (clkrate > 255) {
+		dev_err(&pdev->dev, "Input clockrate too high\n");
+		ret = -EINVAL;
+		goto out_clk_disable;
+	}
+
+	spin_lock_init(&adc->lock);
+	adc->dev = &pdev->dev;
+	adc->clk_apbdma = clk_apbdma;
+	adc->clk_saacc = clk_saacc;
+
+	ret = request_irq(irq, spmp8000adc_irq, 0, dev_name(&pdev->dev), adc);
+	if (ret)
+		goto out_clk_disable;
+
+	adc_dev = adc;
+	platform_set_drvdata(pdev, adc);
+
+	/* Set internal clock divider */
+	writel(clkrate << SARCTRL_DIVNUM_OFF, adc->reg_base + SARCTRL);
+
+	/* Set conversion time defaults */
+	writel(0x10010, adc->reg_base + SARAUTODLY);
+	writel(0x1000, adc->reg_base + SARDEBTIME);
+	writel(0xFF, adc->reg_base + SARCONDLY);
+
+	/* Clear pending interrupts */
+	writel(0xF, adc->reg_base + SARINTF);
+
+	/* Enable interrupts */
+	writel(0xF, adc->reg_base + SARINTEN);
+
+	dev_info(&pdev->dev, "registered\n");
+
+	return 0;
+
+out_clk_disable:
+	clk_disable(clk_saacc);
+	clk_disable(clk_apbdma);
+	clk_put(clk_saacc);
+out_put_clk_apbdma:
+	clk_put(clk_apbdma);
+out_iounmap:
+	iounmap(adc->reg_base);
+out_free:
+	kfree(adc);
+out_release_mem:
+	release_mem_region(r->start, resource_size(r));
+
+	return ret;
+}
+
+static int __devexit spmp8000adc_remove(struct platform_device *pdev)
+{
+	struct spmp8000_adc		*adc;
+	struct resource			*r;
+	int irq;
+
+	adc = platform_get_drvdata(pdev);
+
+	/* Disable and clear remaining interrupts */
+	writel(0, adc->reg_base + SARCTRL);
+	writel(0xF, adc->reg_base + SARINTEN);
+	writel(0xF, adc->reg_base + SARINTF);
+
+	clk_disable(adc->clk_saacc);
+	clk_disable(adc->clk_apbdma);
+	clk_put(adc->clk_saacc);
+	clk_put(adc->clk_apbdma);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	free_irq(irq, adc);
+
+	iounmap(adc->reg_base);
+
+	kfree(adc);
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r)
+		return -ENODEV;
+
+	release_mem_region(r->start, resource_size(r));
+
+	platform_set_drvdata(pdev, NULL);
+
+	dev_info(&pdev->dev, "registered\n");
+
+	return 0;
+}
+
+static const struct of_device_id spmp8000_adc_match[] __devinitdata = {
+	{ .compatible = "sunplus,spmp8000-adc" },
+	{},
+};
+
+static struct platform_driver spmp8000adc_driver = {
+	.driver		= {
+		.name	= "spmp8000-adc",
+		.owner	= THIS_MODULE,
+		.of_match_table = spmp8000_adc_match,
+	},
+	.probe		= spmp8000adc_probe,
+	.remove		= __devexit_p(spmp8000adc_remove),
+};
+
+
+static int __init spmp8000adc_init(void)
+{
+	return platform_driver_register(&spmp8000adc_driver);
+}
+module_init(spmp8000adc_init);
+
+static void __exit spmp8000adc_exit(void)
+{
+	platform_driver_unregister(&spmp8000adc_driver);
+}
+module_exit(spmp8000adc_exit);
+
+MODULE_AUTHOR("Zoltan Devai <[email protected]>");
+MODULE_DESCRIPTION("ADC driver for Sunplus SPMP8000 SoC");
+MODULE_LICENSE("GPL");
arch/arm/mach-spmp8000/include/mach/spmp8000adc.h new file +29 -0
new file mode 100644
index 0000000..df5dfad
@@ -0,0 +1,29 @@
+/*
+ * SPMP8000 ADC support
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#ifndef __MACH_SPMP8000_ADC_H__
+#define __MACH_SPMP8000_ADC_H__
+
+struct spmp8000adc_client_chan {
+	void	(*cb)(s16, void*); /* Callback for every measurement value */
+	void	*priv;		/* Private data for client passed to cb */
+};
+
+struct spmp8000adc_client_tp {
+	void	(*cb)(s32, void*); /* Callback for every measurement value */
+	void	*priv;		/* Private data for client passed to cb */
+};
+
+extern int spmp8000adc_request_channel(unsigned int chan,
+				struct spmp8000adc_client_chan *c);
+extern int spmp8000adc_release_channel(unsigned int chan);
+extern int spmp8000adc_request_tp(struct spmp8000adc_client_tp *tp);
+extern void spmp8000adc_release_tp(void);
+
+#endif /* __MACH_SPMP8000_ADC_H__ */
Signature · 1 line
1.7.4.1
#20 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

2011/10/9 Zoltan Devai <[email protected]>:

Quoted 4 lines

---
?arch/arm/mach-spmp8000/adc.c ? ? ? ? ? ? ? ? ? ? ?| ?465 +++++++++++++++++++++ ?arch/arm/mach-spmp8000/include/mach/spmp8000adc.h | ? 29 ++

I think we stopped stuffing misc drivers under arch/arm/*

And stuffing them under drivers/misc/* won't be popular either.

IIO has some ADCs in drivers/staging/iio/adc these seem all to be intended to be used from userspace.

There are a few in-kernel ADCs all over the kernel tree for various specific purposes.

I don't know where we should go with this kind of stuff, really :-/

Yours,
Linus Walleij

#21 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

Quoted 7 lines · Linus Walleij

2011/10/9 Zoltan Devai <[email protected]>:

Quoted 4 lines
---
 arch/arm/mach-spmp8000/adc.c                      |  465 +++++++++++++++++++++
 arch/arm/mach-spmp8000/include/mach/spmp8000adc.h |   29 ++
Quoted 6 lines

I think we stopped stuffing misc drivers under arch/arm/*

And stuffing them under drivers/misc/* won't be popular either.

IIO has some ADCs in drivers/staging/iio/adc these seem all to be intended to be used from userspace.

Yeah, we've had various discussions about adding support for in kernel users. Its always fallen on the fact that no one has had the time to write the code to allow it all to be linked up. Right now it's a case of drivers providing their own interfaces if they want to allow this.
Mark Brown has raised this issue before so I've cc'd him.

For reference of those not following the original thread (based on a quick scan of the driver code in question).

Driver has two callback sets:

1) Touch panel reads (I guess going straight to an input driver) (1 of these only). This is annoyingly tied up with the more general adc registers.
2) General purpose adc callbacks.

Both are driven off an interrupt, but data ready
appears to manually driven (so software triggered sampling then data ready signal when sampling is done for a single channel).

So what are these callbacks used for? Note they all run as interrupt top halves...

Options at current time are:

mfd supporting input device and either hwmon or iio device.

A driver that hosts both a hwmon / iio and an input device.

Be brave (+ have a lot of time) and propose patches adding the relevant tying together code to allow iio drivers to be queried in kernel. It shouldn't be that bad actually if you solve the how to work out which device / channel you want in a consistent way.

It really depends on what those general adc callbacks get used for.

Quoted 14 lines

There are a few in-kernel ADCs all over the kernel tree for various specific purposes.

I don't know where we should go with this kind of stuff, really :-/

Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

#22 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

Quoted 42 lines · Jonathan Cameron
Quoted 7 lines · Linus Walleij

2011/10/9 Zoltan Devai <[email protected]>:

Quoted 4 lines
---
 arch/arm/mach-spmp8000/adc.c                      |  465 +++++++++++++++++++++
 arch/arm/mach-spmp8000/include/mach/spmp8000adc.h |   29 ++
Quoted 6 lines

I think we stopped stuffing misc drivers under arch/arm/*

And stuffing them under drivers/misc/* won't be popular either.

IIO has some ADCs in drivers/staging/iio/adc these seem all to be intended to be used from userspace.

Yeah, we've had various discussions about adding support for in kernel users. Its always fallen on the fact that no one has had the time to write the code to allow it all to be linked up. Right now it's a case of drivers providing their own interfaces if they want to allow this.
Mark Brown has raised this issue before so I've cc'd him.

For reference of those not following the original thread (based on a quick scan of the driver code in question).

Driver has two callback sets:

1) Touch panel reads (I guess going straight to an input driver) (1 of these only). This is annoyingly tied up with the more general adc registers.
2) General purpose adc callbacks.

Both are driven off an interrupt, but data ready
appears to manually driven (so software triggered sampling then data ready signal when sampling is done for a single channel).

So what are these callbacks used for? Note they all run as interrupt top halves...

Options at current time are:

mfd supporting input device and either hwmon or iio device.

Quoted 8 lines

A driver that hosts both a hwmon / iio and an input device.

Be brave (+ have a lot of time) and propose patches adding the relevant tying together code to allow iio drivers to be queried in kernel. It shouldn't be that bad actually if you solve the how to work out which device / channel you want in a consistent way.

It really depends on what those general adc callbacks get used for.

Ah, just taken a look at your dts file.

Looks like some are used for 'analog-keys'. So input? If so this should probably be an entirely input driver for now.

Quoted 21 lines
Quoted 8 lines

There are a few in-kernel ADCs all over the kernel tree for various specific purposes.

I don't know where we should go with this kind of stuff, really :-/

Quoted 7 lines

Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

#23 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

In reply to Jonathan Cameron
Quoted 1 line · Jonathan Cameron

It really depends on what those general adc callbacks get used for.

If it's a general purpose ADC it's up to the board designer - you sometimes see things like battery or supply monitoring but really it could be any low volume analogue input.

#24 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

2011/10/10 Mark Brown <[email protected]>:

Quoted 7 lines · Jonathan Cameron
Quoted 2 lines

It really depends on what those general adc callbacks get used for.

If it's a general purpose ADC it's up to the board designer - you sometimes see things like battery or supply monitoring but really it could be any low volume analogue input.

It's really up to the board designer. In this case, all real-world scenarios use the adc for analog-keys AND battery monitoring.

Jonathan, your analysis of the driver is completely correct:
Currently, only the not-yet-submitted analog-keys input driver uses this, but I'd like to add battery monitoring shortly, so this has at least two users.

Where should the adc driver go in this case ?

Zoltan

#25 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

Quoted 1 line · Zoltan Devai

Where should the adc driver go in this case ?

At the minute it seems to me that arch/arm is as good a place as any really - currently this code is getting dumped wherever the main device is.

#26 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

Quoted 7 lines · Mark Brown
Quoted 1 line · Zoltan Devai

Where should the adc driver go in this case ?

At the minute it seems to me that arch/arm is as good a place as any really - currently this code is getting dumped wherever the main device is.

My feeling is that there is a general class of drivers involving the gpio, pinmux, pwm, led, adc and dac: these are basically all things you do with a single pin. Right now we have subsystems for some of them, adding some others and don't have anything for adc basides iio (which only partially fits here).

Since there is no externally visible interface for this kind of adc driver, we can always fix it later, which helps a lot. How about putting it into drivers/adc or drivers/misc/adc for now and waiting for others to join it? We can then later make it a proper subsystem along the lines of gpio and pwm and move interfaces for input, hwmon and iio into the subsystem.

Arnd

#27 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

Quoted 11 lines · Arnd Bergmann
Quoted 3 lines · Mark Brown

At the minute it seems to me that arch/arm is as good a place as any really - currently this code is getting dumped wherever the main device is.

My feeling is that there is a general class of drivers involving the gpio, pinmux, pwm, led, adc and dac: these are basically all things you do with a single pin. Right now we have subsystems for some of them, adding some others and don't have anything for adc basides iio (which only partially fits here).

We've been round this loop repeatedly before, trying to push all this stuff into pins really doesn't map terribly well. For example, SoCs will frequently have PWM controllers which are isolated IP blocks within the device and may each have multiple mappings out of the device onto pins. In the case of DACs, ADCs and PWM you definitely don't have a one to one mapping with a single pin - they can be differential inputs and outputs, or entirely internal to a device. Trying to push everything into a single namespace just makes things confusing.

What we *do* have a pattern of is clk API type lookups based on a device/name pair.

Quoted 6 lines

Since there is no externally visible interface for this kind of adc driver, we can always fix it later, which helps a lot. How about putting it into drivers/adc or drivers/misc/adc for now and waiting for others to join it? We can then later make it a proper subsystem along the lines of gpio and pwm and move interfaces for input, hwmon and iio into the subsystem.

I'm not sure that IIO isn't the kernel subsystem we're looking for here
- as I keep saying when this comes up it's just representing bare DACs and ADCs pretty directly which looks like a generic subsystem to me.

#28 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

Quoted 13 lines · Mark Brown
Quoted 11 lines · Arnd Bergmann
Quoted 3 lines · Mark Brown

At the minute it seems to me that arch/arm is as good a place as any really - currently this code is getting dumped wherever the main device is.

My feeling is that there is a general class of drivers involving the gpio, pinmux, pwm, led, adc and dac: these are basically all things you do with a single pin. Right now we have subsystems for some of them, adding some others and don't have anything for adc basides iio (which only partially fits here).

Quoted 8 lines

We've been round this loop repeatedly before, trying to push all this stuff into pins really doesn't map terribly well. For example, SoCs will frequently have PWM controllers which are isolated IP blocks within the device and may each have multiple mappings out of the device onto pins. In the case of DACs, ADCs and PWM you definitely don't have a one to one mapping with a single pin - they can be differential inputs and outputs, or entirely internal to a device. Trying to push everything into a single namespace just makes things confusing.

I didn't mean to suggest that they should be the same subsystem, but it would be nice to use similar in-kernel interfaces so that a driver author doesn't have to learn about six different interfaces for a new soc. If we add an adc subsystem, I would definitely recommend looking at how the others work.

Quoted 10 lines
Quoted 6 lines

Since there is no externally visible interface for this kind of adc driver, we can always fix it later, which helps a lot. How about putting it into drivers/adc or drivers/misc/adc for now and waiting for others to join it? We can then later make it a proper subsystem along the lines of gpio and pwm and move interfaces for input, hwmon and iio into the subsystem.

I'm not sure that IIO isn't the kernel subsystem we're looking for here
- as I keep saying when this comes up it's just representing bare DACs and ADCs pretty directly which looks like a generic subsystem to me.

Possible, yes. Until now, IIO is a subsystem for user-level access not for kernel access though, so it's not the right place yet. If we decide to let IIO handle all ADC input, do you think it would also be the right place to do PWM output, rather than having a separate subsystem for that?

Arnd

#29 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

Quoted 23 lines · Arnd Bergmann
Quoted 13 lines · Mark Brown
Quoted 11 lines · Arnd Bergmann
Quoted 3 lines · Mark Brown

At the minute it seems to me that arch/arm is as good a place as any really - currently this code is getting dumped wherever the main device is.

My feeling is that there is a general class of drivers involving the gpio, pinmux, pwm, led, adc and dac: these are basically all things you do with a single pin. Right now we have subsystems for some of them, adding some others and don't have anything for adc basides iio (which only partially fits here).

Quoted 8 lines

We've been round this loop repeatedly before, trying to push all this stuff into pins really doesn't map terribly well. For example, SoCs will frequently have PWM controllers which are isolated IP blocks within the device and may each have multiple mappings out of the device onto pins. In the case of DACs, ADCs and PWM you definitely don't have a one to one mapping with a single pin - they can be differential inputs and outputs, or entirely internal to a device. Trying to push everything into a single namespace just makes things confusing.

Quoted 5 lines

I didn't mean to suggest that they should be the same subsystem, but it would be nice to use similar in-kernel interfaces so that a driver author doesn't have to learn about six different interfaces for a new soc. If we add an adc subsystem, I would definitely recommend looking at how the others work.

Yup, I'm reading the clk api stuff right now.

Quoted 8 lines
Quoted 8 lines
Quoted 7 lines

Since there is no externally visible interface for this kind of adc driver, we can always fix it later, which helps a lot. How about putting it into drivers/adc or drivers/misc/adc for now and waiting for others to join it? We can then later make it a proper subsystem along the lines of gpio and pwm and move interfaces for input, hwmon and iio into the subsystem.

Quoted 6 lines
Quoted 3 lines

I'm not sure that IIO isn't the kernel subsystem we're looking for here
- as I keep saying when this comes up it's just representing bare DACs and ADCs pretty directly which looks like a generic subsystem to me.

Possible, yes. Until now, IIO is a subsystem for user-level access not for kernel access though, so it's not the right place yet.

Technically proof of concept code for kernel access went out this morning
(sorry, didn't cc you as you hadn't entered this thread yet). It's
all of 10 lines of code right now. But yeah, its userspace focussed.

https://lkml.org/lkml/2011/10/11/141

Covers basic hwmon type usage (polled single channel read). I'm not nearly as sure how to handle complex channel scans. There's a lot of infrastructure involved in those.

Quoted 3 lines

If we decide to let IIO handle all ADC input, do you think it would also be the right place to do PWM output, rather than having a separate subsystem for that?

That's one I've been avoiding talking about. The fuzzy region is between DDS (direct digital synthesis - waveform generator) chips which we have
been taking in IIO so far. We don't actually handle these that well
at the moment. Our core channel registration code doesn't handle the weird routing these can do - single underlying generator does several synchronized waveforms.

Pwm is small compact and nice. Perhaps a similar shim to that I proposed to hwmon alongside that patch earlier. So simple pwm only device sits in pwm, but we have a general purpose driver sitting on IIO that can provide pwm from an appropriate channel of a more complex device. It would need hooks that aren't there at the moment and more of the DDS control to become part of the IIO core.

We may ultimately want to hive off more of IIO into a layer sitting at same level as other users. It's tricky to know where to make such divisions though.

Jonathan

#30 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

In reply to Arnd Bergmann
Quoted 11 lines · Arnd Bergmann
Quoted 3 lines · Mark Brown

I'm not sure that IIO isn't the kernel subsystem we're looking for here
- as I keep saying when this comes up it's just representing bare DACs and ADCs pretty directly which looks like a generic subsystem to me.

Possible, yes. Until now, IIO is a subsystem for user-level access not for kernel access though, so it's not the right place yet. If we decide to let IIO handle all ADC input, do you think it would also be the right place to do PWM output, rather than having a separate subsystem for that?

Off the top of my head I'd expect PWM to be a separate thing which could optionally synthesize stuff onto an IIO device (bitbanging the PWM essentially). The specialized PWM stuff tends to be "output this steady state for an indefinite period" type interface rather than "here's a batch of samples, output them".

#31 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

Quoted 13 lines · Mark Brown
Quoted 11 lines · Arnd Bergmann
Quoted 3 lines · Mark Brown

I'm not sure that IIO isn't the kernel subsystem we're looking for here
- as I keep saying when this comes up it's just representing bare DACs and ADCs pretty directly which looks like a generic subsystem to me.

Possible, yes. Until now, IIO is a subsystem for user-level access not for kernel access though, so it's not the right place yet. If we decide to let IIO handle all ADC input, do you think it would also be the right place to do PWM output, rather than having a separate subsystem for that?

Quoted 5 lines

Off the top of my head I'd expect PWM to be a separate thing which could optionally synthesize stuff onto an IIO device (bitbanging the PWM essentially). The specialized PWM stuff tends to be "output this steady state for an indefinite period" type interface rather than "here's a batch of samples, output them".

Same can be true of dds chips, with the addition that they often have gpio type switching between a set of predefined frequencies (FM basically).
+ weird waveform choices sometimes.

#32 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

In reply to Mark Brown
Quoted 7 lines · Mark Brown
Quoted 1 line · Zoltan Devai

Where should the adc driver go in this case ?

At the minute it seems to me that arch/arm is as good a place as any really - currently this code is getting dumped wherever the main device is.

No it isn't - we want drivers out of arch/arm (it's already been a topic of flame for Linus, so it's something that we should try _really_ hard to avoid.)

As this is clearly a device driver (it has a device driver struct, it relies upon a struct device, etc) then it needs to live outside of arch/arm/ - and I think Arnd's suggestion of drivers/adc is probably the right place for it to move to (even though its probably the first to create the directory.) More stuff can come along in the future, and then its all together to start creating a common API in there.

The other thing I'd say is that things like the callback definitions and driver data should not be in some arch/arm/ include file - if it's private to the ADC, it should go in drivers/adc too. If not, then somewhere in include/linux (maybe include/linux/adc) is a better place.

What we *do* need to avoid is having the spmp8000 ADC callback function data structure, the foo ADC callback function data structure, the bar ADC callback function data structure and so forth. We need to think *NOW* about defining a common ADC callback structure so that we don't spawn a new problem which'll take effort to solve.

#33 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

Quoted 5 lines · Russell King - ARM Linux
Quoted 4 lines · Mark Brown
Quoted 1 line · Zoltan Devai

Where should the adc driver go in this case ?

Quoted 7 lines
Quoted 3 lines

At the minute it seems to me that arch/arm is as good a place as any really - currently this code is getting dumped wherever the main device is.

No it isn't - we want drivers out of arch/arm (it's already been a topic of flame for Linus, so it's something that we should try _really_ hard to avoid.)

Amen to that.

Quoted 6 lines

As this is clearly a device driver (it has a device driver struct, it relies upon a struct device, etc) then it needs to live outside of arch/arm/ - and I think Arnd's suggestion of drivers/adc is probably the right place for it to move to (even though its probably the first to create the directory.) ?More stuff can come along in the future, and then its all together to start creating a common API in there.

That sort of suggest that the drivers/staging/iio/adc is in trouble now. It will have an in-kernel competitor for similar stuff.

Having drivers/adc and drivers/iio/adc compete about taking drivers on preference as to whether they were intended for userspace or kernelspace does not seem like a good thing.

#34 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

Quoted 24 lines · Linus Walleij
Quoted 5 lines · Russell King - ARM Linux
Quoted 4 lines · Mark Brown
Quoted 1 line · Zoltan Devai

Where should the adc driver go in this case ?

Quoted 7 lines
Quoted 3 lines

At the minute it seems to me that arch/arm is as good a place as any really - currently this code is getting dumped wherever the main device is.

No it isn't - we want drivers out of arch/arm (it's already been a topic of flame for Linus, so it's something that we should try _really_ hard to avoid.)

Amen to that.

Quoted 6 lines

As this is clearly a device driver (it has a device driver struct, it relies upon a struct device, etc) then it needs to live outside of arch/arm/ - and I think Arnd's suggestion of drivers/adc is probably the right place for it to move to (even though its probably the first to create the directory.) More stuff can come along in the future, and then its all together to start creating a common API in there.

Quoted 8 lines

That sort of suggest that the drivers/staging/iio/adc is in trouble now. It will have an in-kernel competitor for similar stuff.

Having drivers/adc and drivers/iio/adc compete about taking drivers on preference as to whether they were intended for userspace or kernelspace does not seem like a good thing.

The problem with a straight adc directory is the fact that many of these devices are combined input and output. The also tend to have numerous things that to a casual don't look like ADC's (light sensors etc). We only did the grouping in IIO for the sake of not having a huge flat directory. Many of the drivers overlap between the different categories.

Quoted 11 lines
Quoted 2 lines

From a quick review of the IIO ADC subsystem I cannot really

find a big problem with it other than it brings the entire IIO subsystem along, including the userspace interfaces I guess.

The circumstances seem to suggest that drivers/iio/adc should be moved down to drivers/adc without the rest of the iio stuff, with a in-kernel API only and then have iio use that (this is probably true for some more drivers in IIO like dac, accel, addac, gyro...).

Quoted 7 lines

So the concept of IIO stuff would change from the userspace-first centric view to the kernelspace-first centric, with optional userspace interfaces in the form of IIO.

I'm not sure about what Jonathan thinks about that though.

That wouldn't be too hard to do. It's just a case of breaking iio_register_device function in two. Right now (with the in kernel RFC) on top. The only thing needed for in kernel access is that the configured struct iio_dev gets added to the list of available devices.

I think we need to maintain the form of struct iio_chan_spec (or whatever does the same job) with the intent of it directly mapping to userspace interfaces (keeps things clean and makes sure there is enough information to actually use the channel for anything).

The key thing is the unified specification of channel capabilities and the ability to read them. We have one way of doing that which has developed against a 'lot' of drivers.

The nasty corner is optimized reading of channel scans. They tend to be irrelevant for memory mapped device but are vital for many devices on low bandwidth buses. Right now we leave that entirely to the individual drivers (they push this data out rather than it being pulled from the core like everything else). It may be possible to flip this around with out too much pain, but I'm not sure. The other nasty bits are:
* hardware buffering
* triggered capture control - we really care about this for IIO and I don't think anyone else does. Could maybe get away with using device tree or similar to specify defaults for this and not provide any means of changing it for devices other than those with full IIO userspace in place.

As you can imagine I'm a little wary of sending IIO through another refactoring right now (proposal to move the core out of staging is scheduled for my next free afternoon!). So in principal in favour of this approach but in practice not sure I'll be in a position to do it (need to eat and my funding source is about to run out - awaiting decision on more).

So my personal preference would be to add the relevant access functions etc to IIO as is. Two models:

1) Pull from drivers - (give me reading now).
Done - we need to nick the clkdev mapping approach to join everything up and play a few tricks to get a unique consistent name.

2) Push from triggered capture - (here is a reading). Easiest option from our point of view is to provide this as a 'buffer' implementation. Normally we'd notify userspace of new data, here we notify in kernel users (or just push it into their callbacks).

Post that we start to make IIO userspace interfaces optional.

It gives a progression towards what you are suggesting and doesn't hold us up in staging for another long cycle.

There are exceptions. I'd argue any element that is really built for say hwmon or input ought to register directly with those subsytems. That includes the touchscreen element of the adc that started this conversation. No point in adding overhead and generality to a non general element.

Sorry for quick reply, mad day.

Jonathan

#35 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

In reply to Russell King - ARM Linux
Quoted 9 lines · Russell King - ARM Linux
Quoted 3 lines · Mark Brown

At the minute it seems to me that arch/arm is as good a place as any really - currently this code is getting dumped wherever the main device is.

No it isn't - we want drivers out of arch/arm (it's already been a topic of flame for Linus, so it's something that we should try _really_ hard to avoid.)

I said it was as good a place as any, I didn't say it was a good place. Currently we don't have a good place for this code but just dumping the code somewhere else isn't really helping anything either unless there's some meaningful subsytem (or at least effort towards a subsystem) to look after it, it's bookkeeping.

Quoted 6 lines

As this is clearly a device driver (it has a device driver struct, it relies upon a struct device, etc) then it needs to live outside of arch/arm/ - and I think Arnd's suggestion of drivers/adc is probably the right place for it to move to (even though its probably the first to create the directory.) More stuff can come along in the future, and then its all together to start creating a common API in there.

Creating drivers/adc just seems like a sucky idea. We've already got at least one ongoing effort at creating a general purpose ADC/DAC interface in the form of the IIO subsystem (which is currently mostly targeted at higher volume devices and userspace only but the abstractions needed aren't terribly different depending on the data volumes) and does have the rather substantial advantage that there's people actually trying to make a subsystem. On the other hand it's in staging for now so it'd create pain trying to merge the in-kernel clients that go along with these devices which doesn't seem constructive.

There's also the fact that ADCs and DACs are pretty much the same thing to software with the data line driven from the opposite end of the link sO if we've got a subsystem for ADCs only it seems clear we're doing something wrong.

Quoted 5 lines

What we *do* need to avoid is having the spmp8000 ADC callback function data structure, the foo ADC callback function data structure, the bar ADC callback function data structure and so forth. We need to think *NOW* about defining a common ADC callback structure so that we don't spawn a new problem which'll take effort to solve.

The cat's already well out of the bag on that one, we've got a bunch of these things in tree already - the Cragganmore system I've got sitting here on my desk has three auxiliary ADCs I'm aware of, all of which are supported in mainline, and they're far from the only ones.

I think we should just carry on as we are while the IIO work proceeds, either we'll decide that that's the way forward and we can then kick all the ADCs into there or we'll get fed up, decide that's never going to work then go and can do something else.

#36 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

Quoted 7 lines · Mark Brown
Quoted 5 lines · Russell King - ARM Linux
Quoted 3 lines · Mark Brown

At the minute it seems to me that arch/arm is as good a place as any really - currently this code is getting dumped wherever the main device is.

Quoted 5 lines
Quoted 3 lines

No it isn't - we want drivers out of arch/arm (it's already been a topic of flame for Linus, so it's something that we should try _really_ hard to avoid.)

I said it was as good a place as any, I didn't say it was a good place.

I'm saying it is a *BAD* place. I'm saying that we've been flamed over this several times before. We need to change our behaviour RIGHT NOW, not continue on ignoring the problem, and demonstrating to Linus that we don't take his concerns seriously.

If we can't find a place for it that's outside of arch/arm, then it doesn't go in.

Quoted 4 lines

I think we should just carry on as we are while the IIO work proceeds, either we'll decide that that's the way forward and we can then kick all the ADCs into there or we'll get fed up, decide that's never going to work then go and can do something else.

You mean like other stuff which is already in the kernel gets fixed? It doesn't get fixed. We persist with per-driver private data structures which multiply all over the place.

Look at what happened with MTD and the flash_platform_data structure. That got ignored and instead people just continued merrily creating their own private crap time and time again.

We can not continue like this. We can not continue to be soo permissive.

I'm now saying a big NO to this - I don't care that the "cat is already out of the bag" - that's a defeatest attitude. I'm saying no *now* so that there _is_ some kind of movement towards fixing this problem. I don't care whether that means it shares an existing ADC drivers callback data structures or what - I just don't want to see yet another driver private data structure being created for NO REASON what so ever.

Or maybe you'd like to be on the receiving end of another Linus flame?

Some people would like to avoid such things - maybe you feed off them, that's your decision. I personally am on the side of the folk who'd like to avoid being on the receiving end of the next Linus flame.

So. No new drivers in arch/arm. And I'm going to be saying no to any new per-driver data structures in mach/*.h for stuff which should be generic which haven't been justified for why they need to be different from someone elses.

#37 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

Quoted 14 lines · Russell King - ARM Linux
Quoted 7 lines · Mark Brown
Quoted 5 lines · Russell King - ARM Linux
Quoted 3 lines · Mark Brown

At the minute it seems to me that arch/arm is as good a place as any really - currently this code is getting dumped wherever the main device is.

Quoted 5 lines
Quoted 3 lines

No it isn't - we want drivers out of arch/arm (it's already been a topic of flame for Linus, so it's something that we should try really hard to avoid.)

I said it was as good a place as any, I didn't say it was a good place.

Quoted 7 lines

I'm saying it is a BAD place. I'm saying that we've been flamed over this several times before. We need to change our behaviour RIGHT NOW, not continue on ignoring the problem, and demonstrating to Linus that we don't take his concerns seriously.

If we can't find a place for it that's outside of arch/arm, then it doesn't go in.

How about deferring the decision for now and putting it into drivers/staging/adc, with a single TODO item listing "Work out proper API"? After all, the reason this driver doesn't fit anywhere is that there is no established subsystem for it, at least not outside of staging.

AFAICT, the driver is not essential for spmp8000 systems, but you would want to have it there in practice. It makes little sense to put the driver into drivers/adc and then later change the interface fundamentally again if we decide to go with drivers/iio/adc providing the common abstraction lation.

Of course, if the spmp8000 adc driver gets put into staging, all its dependencies (input, battery) also have to go there because no regular driver must link against interfaces provided by a staging driver.

Arnd

#38 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

Quoted 4 lines · Arnd Bergmann

Of course, if the spmp8000 adc driver gets put into staging, all its dependencies (input, battery) also have to go there because no regular driver must link against interfaces provided by a staging driver.

Yes, that's the big problem with this - it blocks the actual drivers. Putting it in drivers/misc would be another option that sidesteps that.

#39 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

In reply to Russell King - ARM Linux
Quoted 8 lines · Russell King - ARM Linux
Quoted 1 line · Mark Brown

I said it was as good a place as any, I didn't say it was a good place.

I'm saying it is a *BAD* place. I'm saying that we've been flamed over this several times before. We need to change our behaviour RIGHT NOW, not continue on ignoring the problem, and demonstrating to Linus that we don't take his concerns seriously.

I think we're agreeing here...

Quoted 5 lines
Quoted 4 lines

I think we should just carry on as we are while the IIO work proceeds, either we'll decide that that's the way forward and we can then kick all the ADCs into there or we'll get fed up, decide that's never going to work then go and can do something else.

Quoted 7 lines

You mean like other stuff which is already in the kernel gets fixed? It doesn't get fixed. We persist with per-driver private data structures which multiply all over the place.

Look at what happened with MTD and the flash_platform_data structure. That got ignored and instead people just continued merrily creating their own private crap time and time again.

The situation here is a bit different; there's people working on handling these devices already but we can't point people at it as something they can use yet. As soon as we've got something to point people at we should absolutely be going and actively pushing them towards it but right now we don't have that option.

Quoted 5 lines

We can not continue like this. We can not continue to be soo permissive.

I'm now saying a big NO to this - I don't care that the "cat is already out of the bag" - that's a defeatest attitude. I'm saying no *now* so that there _is_ some kind of movement towards fixing this problem. I

Half the problem that's being pointed out is that there is already movement towards a solution for these devices, it's just not quite at the point where it can be used for this class of devices yet. The point with the existing drivers being there is that this isn't a new driver setting a precedent, if we had chosen to merge the driver we'd simply be taking a decision to avoid creating churn that disrupts the existing work.

Quoted 3 lines

don't care whether that means it shares an existing ADC drivers callback data structures or what - I just don't want to see yet another driver private data structure being created for NO REASON what so ever.

Well, what do we do then? I guess so long as it's outside arch/arm you don't mind too much.

Quoted 5 lines

Or maybe you'd like to be on the receiving end of another Linus flame?

Some people would like to avoid such things - maybe you feed off them, that's your decision. I personally am on the side of the folk who'd like to avoid being on the receiving end of the next Linus flame.

On the other hand we also don't want to overreact and we don't want to irritate people by just putting stuff into a different directory without actually improving it. The goal isn't to troll Linus, the goal is to be pragmatic about what we're actually achieving. It's similar to the decision that was taken to allow platforms to proceed without the generic clk API or device tree bindings for the clk API.

Quoted 4 lines

So. No new drivers in arch/arm. And I'm going to be saying no to any new per-driver data structures in mach/*.h for stuff which should be generic which haven't been justified for why they need to be different from someone elses.

The driver specific data structures should probably just be moving to include/linux/platform_data which is the approved location for that sort of thing.

#40 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

Quoted 8 lines · Mark Brown
Quoted 4 lines

So. No new drivers in arch/arm. And I'm going to be saying no to any new per-driver data structures in mach/*.h for stuff which should be generic which haven't been justified for why they need to be different from someone elses.

The driver specific data structures should probably just be moving to include/linux/platform_data which is the approved location for that sort of thing.

Note that we also need someone to watch over that and say no to silly platform_data definitions being put in there, e.g. using wildly different ways of expressing the same things in almost-identical drivers, or using function pointers where a simple data structure is enough.

Arnd

#41 lore raw

PATCH 4/9 ARM: SPMP8000: Add ADC driver

Quoted 10 lines · Arnd Bergmann
Quoted 3 lines · Mark Brown

The driver specific data structures should probably just be moving to include/linux/platform_data which is the approved location for that sort of thing.

Note that we also need someone to watch over that and say no to silly platform_data definitions being put in there, e.g. using wildly different ways of expressing the same things in almost-identical drivers, or using function pointers where a simple data structure is enough.

Yeah, I'm not sure I entirely understand the plan with it though even without any structure it doesn't make much difference (in that everything is being reviewed by the subsystem maintainers just as it was before we dumped the files in a single directory). I'm tempted to say the device tree folks should be looking at it as an awful lot of what they're doing is exactly the same thing.

#42 lore raw

PATCH 5/9 ARM: SPMP8000: Add pinmux driver

In reply to Zoltan Devai
---
 arch/arm/mach-spmp8000/pinmux.c |  131 +++++++++++++++++++++++++++++++++++++++
 1 files changed, 131 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-spmp8000/pinmux.c
arch/arm/mach-spmp8000/pinmux.c new file +131 -0
new file mode 100644
index 0000000..61174d5
@@ -0,0 +1,131 @@
+/*
+ * SPMP8000 machines pinmux functions
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+
+#include <mach/scu.h>
+
+static int pgc_to_regs(int pc, void __iomem **reg, int *off, int *mask_bits)
+{
+	if (pc > 52) {
+		pr_err("Invalid pad group control number\n");
+		return -EINVAL;
+	}
+
+	*mask_bits = 1;
+
+	/* The pin controls are nicely scattered around */
+	if (pc < 10) {
+		*reg = REG_SCU_B(SCU_B_PGC0);
+		*off = pc * 3;
+		*mask_bits = 3;
+	} else if (pc == 10) {
+		*reg = REG_SCU_B(SCU_B_PGC1);
+		*off = 0;
+	} else if (pc < 42) {
+		*reg = REG_SCU_B(SCU_B_PGC2);
+		*off = pc - 10;
+	} else {
+		*reg = REG_SCU_B(SCU_B_PGC3);
+		*off = pc - 42;
+	}
+
+	return 0;
+}
+
+/**
+ * Set the control function (usually pull-up/down) of a pin group
+ * @pc Pin control group number
+ * @conf Control function state
+ */
+void spmp8000_pinmux_pgc_set(int pc, unsigned int conf)
+{
+	void __iomem *pgc_reg;
+	int ret, pgc_off, mask_bits;
+	u32 reg;
+
+	ret = pgc_to_regs(pc, &pgc_reg, &pgc_off, &mask_bits);
+	if (ret)
+		return;
+
+	pr_debug("pgc_set: %d, reg: %p, off: %d, mask: %d\n", pc, pgc_reg,
+		pgc_off, mask_bits);
+
+	reg = readl(pgc_reg);
+	reg &= ~(((1 <<  mask_bits) - 1) << pgc_off);
+	reg |= (conf << pgc_off);
+	writel(reg, pgc_reg);
+}
+
+/**
+ * Get the control function (usually pull-up/down) of a pin group
+ * @pc Pin control group number
+ * @returns Current function control state
+ */
+unsigned int spmp8000_pinmux_pgc_get(int pc)
+{
+	void __iomem *pgc_reg;
+	int ret, pgc_off, mask_bits;
+	u32 reg;
+
+	ret = pgc_to_regs(pc, &pgc_reg, &pgc_off, &mask_bits);
+	if (ret)
+		return ret;
+
+	reg = readl(pgc_reg);
+	reg >>= pgc_off;
+	reg &= (1 <<  mask_bits) - 1;
+	return reg;
+}
+
+static inline int pgs_to_reg(unsigned int pg)
+{
+	return (pg / 16) * 4;
+}
+
+static inline int pgs_to_off(unsigned int pg)
+{
+	return (pg % 16) * 2;
+}
+
+/**
+ * Set the function of a pin group
+ * @pg Pin group
+ * @func Function
+ */
+void spmp8000_pinmux_pgs_set(unsigned int pg, unsigned int func)
+{
+	u32 reg;
+
+	if ((pg > 63) || (func > 3)) {
+		pr_err("Invalid pad group function select\n");
+		return;
+	}
+
+	reg = readl(REG_SCU_B(SCU_B_PGS0 + pgs_to_reg(pg)));
+	reg &= ~(3 << pgs_to_off(pg));
+	reg |= (func << pgs_to_off(pg));
+	writel(reg, REG_SCU_B(SCU_B_PGS0 + pgs_to_reg(pg)));
+}
+/**
+ * Get the current function associated to the pin group
+ * @pg Pin group
+ * @returns The current function of the pin group
+ */
+int spmp8000_pinmux_pgs_get(int pg)
+{
+	u32 reg;
+
+	reg = readl(REG_SCU_B(SCU_B_PGS0 + pgs_to_reg(pg)));
+	reg = (reg >> pgs_to_off(pg)) & 3;
+	return reg;
+}
Signature · 1 line
1.7.4.1
#43 lore raw

PATCH 5/9 ARM: SPMP8000: Add pinmux driver

2011/10/9 Zoltan Devai <[email protected]>:

Quoted 3 lines

?arch/arm/mach-spmp8000/pinmux.c | ?131 +++++++++++++++++++++++++++++++++++++++ ?1 files changed, 131 insertions(+), 0 deletions(-)
?create mode 100644 arch/arm/mach-spmp8000/pinmux.c

I'm creating this new pin control and pin mux subsystem, set for next merge window. Can you check it out and see if it does what you need?

http://marc.info/?l=linux-kernel&m=131805699106465&w=2

Thanks,
Linus Walleij

#44 lore raw

PATCH 5/9 ARM: SPMP8000: Add pinmux driver

2011/10/10 Linus Walleij <[email protected]>:

Quoted 9 lines

2011/10/9 Zoltan Devai <[email protected]>:

Quoted 3 lines

?arch/arm/mach-spmp8000/pinmux.c | ?131 +++++++++++++++++++++++++++++++++++++++ ?1 files changed, 131 insertions(+), 0 deletions(-)
?create mode 100644 arch/arm/mach-spmp8000/pinmux.c

I'm creating this new pin control and pin mux subsystem, set for next merge window. Can you check it out and see if it does what you need?

yes. Zoltan, you do need to follow Linus W's new pinmux framework.

And Linus, can you help to take care prima2 pinmux driver v3 i sent yesterday and make sure it is merged in the window too. our lots of device drivers depend on it, so i'd like it is ready before we upstream lots of other drivers.

Quoted 5 lines

http://marc.info/?l=linux-kernel&m=131805699106465&w=2

Thanks,
Linus Walleij

-barry

#45 lore raw

PATCH 5/9 ARM: SPMP8000: Add pinmux driver

Quoted 4 lines · Barry Song

And Linus, can you help to take care prima2 pinmux driver v3 i sent yesterday and make sure it is merged in the window too. our lots of device drivers depend on it, so i'd like it is ready before we upstream lots of other drivers.

It is taken care of and queued for the merge window :-)

If it gets merged in the end is up to Torvalds.

Yours,
Linus Walleij

#46 lore raw

PATCH 6/9 ARM: SPMP8000: Add pwm driver

In reply to Zoltan Devai
---
 arch/arm/mach-spmp8000/pwm.c |  246 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 246 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-spmp8000/pwm.c
arch/arm/mach-spmp8000/pwm.c new file +246 -0
new file mode 100644
index 0000000..a6418bf
@@ -0,0 +1,246 @@
+/*
+ * SPMP8000 machines PWM support
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <asm/div64.h>
+
+#include <mach/regs-timer.h>
+
+static DEFINE_MUTEX(pwm_lock);
+static LIST_HEAD(pwm_list);
+
+struct pwm_device {
+	const char	*label;
+	void __iomem	*regbase;
+	unsigned int	use_count;
+	unsigned int	pwm_id;
+	unsigned long	clkrate;
+	struct device	*dev;
+	struct list_head	node;
+};
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	unsigned long long c;
+	unsigned long period_cycles, duty_cycles, prescale;
+
+	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+		return -EINVAL;
+
+	c = pwm->clkrate;
+	c = c * period_ns;
+	do_div(c, 1000000000);
+	period_cycles = c;
+
+	prescale = (period_cycles - 1) / 65536;
+
+	if (prescale > 65535)
+		return -EINVAL;
+
+	if (prescale < 1)
+		prescale = 1;
+
+	period_cycles /= prescale;
+
+	if (period_cycles > 65536)
+		return -EINVAL;
+
+	c = (unsigned long long)period_cycles * duty_ns;
+	do_div(c, period_ns);
+	duty_cycles = c;
+
+	dev_dbg(pwm->dev, "period_ns: %d, duty_ns: %d, prescale: %ld,"
+			  " period: %ld, duty: %ld\n",
+			  period_ns, duty_ns, prescale, period_cycles,
+			  duty_cycles);
+
+	writel(prescale - 1, pwm->regbase + SPMP8000_TMRB_PSR);
+	writel(period_cycles, pwm->regbase + SPMP8000_TMRB_LDR);
+	writel(duty_cycles, pwm->regbase + SPMP8000_TMRB_CMP);
+
+	return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+	writel(SPMP8000_TMRB_CTR_TE | SPMP8000_TMRB_CTR_OE |
+		SPMP8000_TMRB_CTR_PWMON | SPMP8000_TMRB_CTR_UD |
+		(1 << SPMP8000_TMRB_CTR_M_SH),
+		pwm->regbase + SPMP8000_TMRB_CTR);
+	return 0;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+	writel(0, pwm->regbase + SPMP8000_TMRB_CTR);
+}
+EXPORT_SYMBOL(pwm_disable);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+	struct pwm_device *pwm;
+	int found = 0;
+
+	mutex_lock(&pwm_lock);
+
+	list_for_each_entry(pwm, &pwm_list, node) {
+		if (pwm->pwm_id == pwm_id) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		if (pwm->use_count == 0) {
+			pwm->use_count++;
+			pwm->label = label;
+		} else {
+			pwm = ERR_PTR(-EBUSY);
+		}
+	} else {
+		pwm = ERR_PTR(-ENOENT);
+	}
+
+	mutex_unlock(&pwm_lock);
+	return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+
+	if (pwm->use_count) {
+		pwm->use_count--;
+		pwm->label = NULL;
+	} else {
+		pr_warning("PWM device already freed\n");
+	}
+
+	mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL(pwm_free);
+
+static inline void __add_pwm(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+	list_add_tail(&pwm->node, &pwm_list);
+	mutex_unlock(&pwm_lock);
+}
+
+static int __devinit pwm_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	const unsigned int *id;
+	struct pwm_device *pwm;
+	void __iomem *base;
+	struct clk *clk;
+
+	id = of_get_property(np, "id", NULL);
+	if (!id) {
+		dev_dbg(&pdev->dev, "No id specified\n");
+		return -ENODEV;
+	}
+
+	base = of_iomap(np, 0);
+	if (!base) {
+		dev_dbg(&pdev->dev, "Can't map registers");
+		return -ENOMEM;
+	}
+
+	clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(clk)) {
+		dev_dbg(&pdev->dev, "Unable to get pwm device clock");
+		return -ENODEV;
+	}
+
+	pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
+	if (pwm == NULL) {
+		dev_dbg(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	pwm->dev = &pdev->dev;
+	pwm->use_count = 0;
+	pwm->pwm_id = *id;
+	pwm->regbase = base;
+	pwm->clkrate = clk_get_rate(clk);
+
+	dev_dbg(&pdev->dev, "register pwm.%d, regbase %p, clkrate: %ld\n",
+		pwm->pwm_id, pwm->regbase, pwm->clkrate);
+
+	dev_info(&pdev->dev, "pwm %d registered\n", pwm->pwm_id);
+
+	__add_pwm(pwm);
+
+	platform_set_drvdata(pdev, pwm);
+	return 0;
+}
+
+static int __devexit pwm_remove(struct platform_device *pdev)
+{
+	struct pwm_device *pwm;
+
+	pwm = platform_get_drvdata(pdev);
+	if (pwm == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm_lock);
+	list_del(&pwm->node);
+	mutex_unlock(&pwm_lock);
+
+	kfree(pwm);
+	return 0;
+}
+
+static const __devinitconst struct of_device_id spmp8000_pwm_match[] = {
+	{
+		.compatible = "sunplus,spmp8000-pwm",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, spmp8000_pwm_match);
+
+static struct platform_driver pwm_driver = {
+	.driver		= {
+		.name	= "spmp8000-pwm",
+		.owner	= THIS_MODULE,
+		.of_match_table = spmp8000_pwm_match,
+	},
+	.probe		= pwm_probe,
+	.remove		= __devexit_p(pwm_remove),
+};
+
+static int __init pwm_init(void)
+{
+	return platform_driver_register(&pwm_driver);
+}
+arch_initcall(pwm_init);
+
+static void __exit pwm_exit(void)
+{
+	platform_driver_unregister(&pwm_driver);
+}
+module_exit(pwm_exit);
+
+MODULE_AUTHOR("Zoltan Devai <[email protected]>");
+MODULE_LICENSE("GPL");
Signature · 1 line
1.7.4.1
#47 lore raw

PATCH 6/9 ARM: SPMP8000: Add pwm driver

2011/10/9 Zoltan Devai <[email protected]>:

Quoted 5 lines

---
?arch/arm/mach-spmp8000/pwm.c | ?246 ++++++++++++++++++++++++++++++++++++++++++ ?1 files changed, 246 insertions(+), 0 deletions(-)
?create mode 100644 arch/arm/mach-spmp8000/pwm.c

Also PWM should have its own subsystem, sadly that is an area where discussions about the patches for generic PWM in drivers/pwm/ by Sascha Hauer seem to have stalled recently. :-(

Those we have tend to live in drivers/misc/ and drivers/mfd/ which is not good.

Kudos to Sascha for trying to solve the mess, we need more subsystems...

Yours,
Linus Walleij

#48 lore raw

PATCH 6/9 ARM: SPMP8000: Add pwm driver

Quoted 12 lines · Linus Walleij

2011/10/9 Zoltan Devai <[email protected]>:

Quoted 5 lines

---
?arch/arm/mach-spmp8000/pwm.c | ?246 ++++++++++++++++++++++++++++++++++++++++++ ?1 files changed, 246 insertions(+), 0 deletions(-)
?create mode 100644 arch/arm/mach-spmp8000/pwm.c

Also PWM should have its own subsystem, sadly that is an area where discussions about the patches for generic PWM in drivers/pwm/ by Sascha Hauer seem to have stalled recently. :-(

Quoted 5 lines

Those we have tend to live in drivers/misc/ and drivers/mfd/ which is not good.

Kudos to Sascha for trying to solve the mess, we need more subsystems...

Thanks, I'll respin these patches when I find the time. In the mean time if anyone wants to work on this my latest branch is here:

git://git.pengutronix.de/git/imx/linux-2.6.git pwmlib

It contains the pwm framework and converts most in tree drivers to the new framework.

Sascha

Signature · 4 lines
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
#49 lore raw

PATCH 7/9 ARM: SPMP8000: Add dts file of SPMP8000 SoC and Letcool board

In reply to Zoltan Devai
---
 arch/arm/boot/dts/spmp8000-letcool.dts |  163 ++++++++++++++++++++++++++++++
 arch/arm/boot/dts/spmp8000.dtsi        |  169 ++++++++++++++++++++++++++++++++
 2 files changed, 332 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/boot/dts/spmp8000-letcool.dts
 create mode 100644 arch/arm/boot/dts/spmp8000.dtsi
arch/arm/boot/dts/spmp8000-letcool.dts new file +163 -0
new file mode 100644
index 0000000..270f00e
@@ -0,0 +1,163 @@
+/dts-v1/;
+/include/ "spmp8000.dtsi"
+/ {
+	model = "Letcool N350JP handheld game console";
+	compatible = "gameware,letcool", "sunplus,spmp8000";
+
+	memory {
+		reg = <0x00000000 0x02000000>;
+	};
+
+	chosen {
+		bootargs = "console=tty0 mem=32M root=/dev/mmcblk0p1 console=ttyS0,115200 rootdelay=5 loglevel=8 earlyprintk";
+		linux,stdout-path = &uart0;
+	};
+
+	pwm-backlight {
+		compatible = "pwm-backlight";
+		reg = <0 0>;	/* Hack to pass platform data as AUXDATA */
+	};
+
+	plat {
+		plat-apb {
+			mmc at 92B0B000 {
+				cd-gpios = <&gpio0 9 0>;
+			};
+		};
+	};
+
+	armapb {
+		gpio at 9000A000 {
+				polarity = <0>;
+				sticky = <0>;
+				direction = <0xFFFFFFFF>;
+				debounce-val = <100>;
+				debounce = <0xFFFFFFFF>;
+		};
+	
+		gpio at 9000A004 {
+				polarity = <0>;
+				sticky = <0>;
+				direction = <0xFFFFFFFF>;
+				debounce-val = <100>;
+				debounce = <0xFFFFFFFF>;
+		};
+	};
+
+	gpio-keys {
+		compatible = "gpio-keys";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		autorepeat;
+
+		up {
+			label = "D-Pad Up";
+			gpios = <&gpio0 0 0>;
+			linux,code = <103>; /* KEY_UP */
+			gpio-key,wakeup;
+		};
+
+		right {
+			label = "D-Pad Right";
+			gpios = <&gpio0 1 0>;
+			linux,code = <106>; /* KEY_RIGHT */
+			gpio-key,wakeup;
+		};
+
+		down {
+			label = "D-Pad Down";
+			gpios = <&gpio0 2 0>;
+			linux,code = <108>; /* KEY_DOWN */
+			gpio-key,wakeup;
+		};
+
+		left {
+			label = "D-Pad Left";
+			gpios = <&gpio0 3 0>;
+			linux,code = <105>; /* KEY_LEFT */
+			gpio-key,wakeup;
+		};
+
+		button-x {
+			label = "Button X";
+			gpios = <&gpio0 4 0>;
+			linux,code = <45>; /* KEY_X */
+			gpio-key,wakeup;
+		};
+
+		button-a {
+			label = "Button A";
+			gpios = <&gpio0 5 0>;
+			linux,code = <30>; /* KEY_A */
+			gpio-key,wakeup;
+		};
+
+		button-y {
+			label = "Button Y";
+			gpios = <&gpio0 6 0>;
+			linux,code = <21>; /* KEY_Y */
+			gpio-key,wakeup;
+		};
+
+		button-b {
+			label = "Button B";
+			gpios = <&gpio0 7 0>;
+			linux,code = <48>; /* KEY_B */
+			gpio-key,wakeup;
+		};
+	};
+
+	analog-keys {
+		compatible = "spmp8000-ak";
+
+		channel-1 {
+			adc-channel = <1>;
+			idle-value = <0>;
+			tolerance = <10>;
+
+			button-left {
+				adc-value = <0x00B0>;
+				linux,code = <0x110>; /* BTN_LEFT */
+			};
+
+			button-right {
+				adc-value = <0xFEAB>;
+				linux,code = <0x111>; /* BTN_RIGHT */
+			};
+
+			button-volup {
+				adc-value = <0x015E>;
+				linux,code = <115>; /* KEY_VOLUMEUP */
+			};
+
+			button-voldown {
+				adc-value = <0xFF56>;
+				linux,code = <114>; /* KEY_VOLUMEDOWN */
+			};
+		};
+
+		channel-3 {
+			adc-channel = <3>;
+			idle-value = <5>;
+			tolerance = <10>;
+
+			button-select {
+				adc-value = <0x0113>;
+				linux,code = <0x13a>; /* BTN_SELECT */
+			};
+
+			button-start {
+				adc-value = <0xFEF7>;
+				linux,code = <0x13b>; /* BTN_START */
+			};
+		};
+
+	};
+
+	snd-card {
+		compatible = "spmp8000-sndcard";
+		hp-detect-gpios = <&gpio0 8 0>;
+		spk-enable-gpios = <&gpio3 12 0>;
+	};
+
+};
arch/arm/boot/dts/spmp8000.dtsi new file +169 -0

169 lines not on this page · raw message

Signature · 1 line
1.7.4.1
#50 lore raw

PATCH 7/9 ARM: SPMP8000: Add dts file of SPMP8000 SoC and Letcool board

Hi Zoltan,

Just minor comments again. Also, if you could cc all device tree stuff to devicetree-discuss at lists.ozlabs.org then you'll get a wider audience for these bits.

Jamie

Quoted 16 lines · Zoltan Devai
---
 arch/arm/boot/dts/spmp8000-letcool.dts |  163 ++++++++++++++++++++++++++++++
 arch/arm/boot/dts/spmp8000.dtsi        |  169 ++++++++++++++++++++++++++++++++
 2 files changed, 332 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/boot/dts/spmp8000-letcool.dts
 create mode 100644 arch/arm/boot/dts/spmp8000.dtsi
arch/arm/boot/dts/spmp8000-letcool.dts new file +2 -0
new file mode 100644
index 0000000..270f00e
@@ -0,0 +1,163 @@
+/dts-v1/;
+/include/ "spmp8000.dtsi"

arch/arm/boot/dts/spmp8000-letcool.dts line 12

Quoted 10 lines
arch/arm/boot/dts/spmp8000-letcool.dts new file +10 -0
+/ {
+	model = "Letcool N350JP handheld game console";
+	compatible = "gameware,letcool", "sunplus,spmp8000";
+
+	memory {
+		reg = <0x00000000 0x02000000>;
+	};
+
+	chosen {
+		bootargs = "console=tty0 mem=32M root=/dev/mmcblk0p1 console=ttyS0,115200 rootdelay=5 loglevel=8 earlyprintk";

It's preferred to keep bootargs in the bootloader if at all possible.

Quoted 157 lines
arch/arm/boot/dts/spmp8000-letcool.dts new file +151 -0
+		linux,stdout-path = &uart0;
+	};
+
+	pwm-backlight {
+		compatible = "pwm-backlight";
+		reg = <0 0>;	/* Hack to pass platform data as AUXDATA */
+	};
+
+	plat {
+		plat-apb {
+			mmc at 92B0B000 {
+				cd-gpios = <&gpio0 9 0>;
+			};
+		};
+	};
+
+	armapb {
+		gpio at 9000A000 {
+				polarity = <0>;
+				sticky = <0>;
+				direction = <0xFFFFFFFF>;
+				debounce-val = <100>;
+				debounce = <0xFFFFFFFF>;
+		};
+	
+		gpio at 9000A004 {
+				polarity = <0>;
+				sticky = <0>;
+				direction = <0xFFFFFFFF>;
+				debounce-val = <100>;
+				debounce = <0xFFFFFFFF>;
+		};
+	};
+
+	gpio-keys {
+		compatible = "gpio-keys";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		autorepeat;
+
+		up {
+			label = "D-Pad Up";
+			gpios = <&gpio0 0 0>;
+			linux,code = <103>; /* KEY_UP */
+			gpio-key,wakeup;
+		};
+
+		right {
+			label = "D-Pad Right";
+			gpios = <&gpio0 1 0>;
+			linux,code = <106>; /* KEY_RIGHT */
+			gpio-key,wakeup;
+		};
+
+		down {
+			label = "D-Pad Down";
+			gpios = <&gpio0 2 0>;
+			linux,code = <108>; /* KEY_DOWN */
+			gpio-key,wakeup;
+		};
+
+		left {
+			label = "D-Pad Left";
+			gpios = <&gpio0 3 0>;
+			linux,code = <105>; /* KEY_LEFT */
+			gpio-key,wakeup;
+		};
+
+		button-x {
+			label = "Button X";
+			gpios = <&gpio0 4 0>;
+			linux,code = <45>; /* KEY_X */
+			gpio-key,wakeup;
+		};
+
+		button-a {
+			label = "Button A";
+			gpios = <&gpio0 5 0>;
+			linux,code = <30>; /* KEY_A */
+			gpio-key,wakeup;
+		};
+
+		button-y {
+			label = "Button Y";
+			gpios = <&gpio0 6 0>;
+			linux,code = <21>; /* KEY_Y */
+			gpio-key,wakeup;
+		};
+
+		button-b {
+			label = "Button B";
+			gpios = <&gpio0 7 0>;
+			linux,code = <48>; /* KEY_B */
+			gpio-key,wakeup;
+		};
+	};
+
+	analog-keys {
+		compatible = "spmp8000-ak";
+
+		channel-1 {
+			adc-channel = <1>;
+			idle-value = <0>;
+			tolerance = <10>;
+
+			button-left {
+				adc-value = <0x00B0>;
+				linux,code = <0x110>; /* BTN_LEFT */
+			};
+
+			button-right {
+				adc-value = <0xFEAB>;
+				linux,code = <0x111>; /* BTN_RIGHT */
+			};
+
+			button-volup {
+				adc-value = <0x015E>;
+				linux,code = <115>; /* KEY_VOLUMEUP */
+			};
+
+			button-voldown {
+				adc-value = <0xFF56>;
+				linux,code = <114>; /* KEY_VOLUMEDOWN */
+			};
+		};
+
+		channel-3 {
+			adc-channel = <3>;
+			idle-value = <5>;
+			tolerance = <10>;
+
+			button-select {
+				adc-value = <0x0113>;
+				linux,code = <0x13a>; /* BTN_SELECT */
+			};
+
+			button-start {
+				adc-value = <0xFEF7>;
+				linux,code = <0x13b>; /* BTN_START */
+			};
+		};
+
+	};
+
+	snd-card {
+		compatible = "spmp8000-sndcard";
+		hp-detect-gpios = <&gpio0 8 0>;
+		spk-enable-gpios = <&gpio3 12 0>;
+	};
+
+};
arch/arm/boot/dts/spmp8000.dtsi new file +0 -0
new file mode 100644
index 0000000..74764b2c6

arch/arm/boot/dts/spmp8000.dtsi line 10

Quoted 10 lines
arch/arm/boot/dts/spmp8000.dtsi new file +10 -0
@@ -0,0 +1,169 @@
+/ {
+	compatible = "sunplus,spmp8000";
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	armapb {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0x90000000 0x90000000 0x10000>;

I'm not sure you need a ranges property like this as it isn't doing any translation. I think it would be nicer to have ranges = <0 0x90000000 0x10000> and use bus local addresses for the child nodes.

arch/arm/boot/dts/spmp8000.dtsi line 22

Quoted 11 lines
arch/arm/boot/dts/spmp8000.dtsi new file +11 -0
+		timer at 90000000 {
+			compatible = "sunplus,spmp8000-timer";
+			reg = <0x90000000 0x1000>;
+			interrupt-parent = <&vic0>;
+			interrupts = <7 8 9>;
+		};
+
+		pwm at 90000000 {
+			compatible = "sunplus,spmp8000-pwm";
+			reg = <0x90000000 0x1000>;
+			id = <0>;

This is probably a little too generic. sunplus,pwm-id ?

arch/arm/boot/dts/spmp8000.dtsi line 26

Quoted 4 lines
arch/arm/boot/dts/spmp8000.dtsi new file +4 -0
+		};
+
+		gpio3: gpio at 90005070 {
+			compatible = "basic-mmio-gpio";	/* No DT binding yet */

I did start work on a binding
(http://lists.ozlabs.org/pipermail/devicetree-discuss/2011-August/007137.html)
but it hasn't moved along too far. Thinking about it a little more though I'm not sure that the generic binding is quite the right approach if we need to handle gpio's that can generate interrupts too though.

arch/arm/boot/dts/spmp8000.dtsi line 39

Quoted 13 lines
arch/arm/boot/dts/spmp8000.dtsi new file +13 -0
+			reg = <0x90005070 0xC>;
+			gpio-controller;
+			#gpio-cells = <2>;
+		};
+
+		gpio0: gpio at 9000A000 {
+			compatible = "sunplus,spmp8000-gpio";
+			reg = <0x9000A000 0x1000>;
+			interrupt-parent = <&vic1>;
+			interrupts = <0>;
+			gpio-controller;
+			#gpio-cells = <2>;
+			bits = <16>;

sunplus,nr-gpios?

Quoted 35 lines
arch/arm/boot/dts/spmp8000.dtsi new file +35 -0
+			interrupt-controller;
+			#interrupt-cells = <1>;
+		};
+
+		gpio1: gpio at 9000A004 {
+			compatible = "sunplus,spmp8000-gpio";
+			reg = <0x9000A004 0x1000>;
+			interrupt-parent = <&vic1>;
+			interrupts = <1>;
+			gpio-controller;
+			#gpio-cells = <2>;
+			bits = <32>;
+			interrupt-controller;
+			#interrupt-cells = <1>;
+		};
+	};
+
+	armahb {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0x90010000 0x90010000 0x20000>;
+
+		vic0: interrupt-controller at 90010000 {
+			compatible = "arm,pl192";
+			interrupt-controller;
+			#interrupt-cells = <1>;
+			reg = <0x90010000 0x1000>;
+		};
+
+		vic1: interrupt-controller at 900020000 {
+			compatible = "arm,pl192";
+			interrupt-controller;
+			#interrupt-cells = <1>;
+			reg = <0x90020000 0x1000>;

arch/arm/boot/dts/spmp8000.dtsi line 84

Quoted 10 lines
arch/arm/boot/dts/spmp8000.dtsi new file +10 -0
+		};
+	};
+
+	plat {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0x92000000 0x92000000 0x1000000>;
+
+		plat-apb {

Why are there 2 levels of nesting here? Can't the child nodes of plat-apb just be under plat?

Quoted 93 lines
arch/arm/boot/dts/spmp8000.dtsi new file +85 -0
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0x92B00000 0x92B00000 0x10000>;
+
+			dma at 92B00000 {
+				compatible = "sunplus,spmp8000-apbdma-c";
+				reg = <0x92B00000 0x1000>;
+				interrupt-parent = <&vic1>;
+				interrupts = <28 29 30 31>;
+			};
+
+			uart0: uart-c0 at 92B04000 {
+				compatible = "ns16550a";
+				reg = <0x92B04000 0x1000>;
+				clock-frequency = <2076923>;
+				interrupt-parent = <&vic1>;
+				interrupts = <24>;
+				current-speed = <115200>;
+				reg-shift = <2>;
+			};
+
+			mmc at 92B0B000 {
+				compatible = "sunplus,mmc";
+				reg = <0x92B0B000 0x1000>;
+				interrupt-parent = <&vic1>;
+				interrupts = <8>;
+			};
+		};
+	};
+
+	axi {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0x93000000 0x93000000 0x800000>;
+
+		fb at 93000000 {
+			compatible = "sunplus,spmp8000-lcd";
+			reg = <0x93000000 0x1000>;
+			interrupt-parent = <&vic0>;
+			interrupts = <23>;
+		};
+
+		axi-apb {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0x93010000 0x93010000 0x10000>;
+
+			dma at 93010000 {
+				compatible = "sunplus,spmp8000-apbdma-a";
+				reg = <0x93010000 0x1000>;
+				interrupt-parent = <&vic0>;
+				interrupts = <24 25 26 27>;
+			};
+
+			i2s at 93012000 {
+				compatible = "sunplus,spmp8000-i2s-tx";
+				reg = <0x93012000 0x1000>;
+				interrupt-parent = <&vic0>;
+				interrupts = <13>;
+			};
+
+			i2s at 9301D000 {
+				compatible = "sunplus,spmp8000-i2s-rx";
+				reg = <0x9301D000 0x1000>;
+				interrupt-parent = <&vic0>;
+				interrupts = <14>;
+			};
+
+			adc at 9301F000 {
+				compatible = "sunplus,spmp8000-adc";
+				reg = <0x9301F000 0x20>;
+				interrupt-parent = <&vic1>;
+				interrupts = <27>;
+			};
+
+			codec at 9301F020 {
+				compatible = "sunplus,spmp8000-codec";
+				reg = <0x9301F020 0x20>;
+			};
+		};
+	};
+};
Signature · 7 lines
1.7.4.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
#51 lore raw

PATCH 8/9 ARM: SPMP8000: Add support for the Letcool board

In reply to Zoltan Devai
---
 arch/arm/mach-spmp8000/board_letcool.c           |  154 ++++++++++++++++++++++
 arch/arm/mach-spmp8000/include/mach/spmp8000fb.h |   32 +++++
 2 files changed, 186 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-spmp8000/board_letcool.c
 create mode 100644 arch/arm/mach-spmp8000/include/mach/spmp8000fb.h
arch/arm/mach-spmp8000/board_letcool.c new file +154 -0
new file mode 100644
index 0000000..8d6c081
@@ -0,0 +1,154 @@
+/*
+ * Letcool board support file
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/pwm_backlight.h>
+#include <linux/input.h>
+#include <linux/basic_mmio_gpio.h>
+#include <linux/gpio_keys.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <asm/hardware/vic.h>
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+
+#include <mach/core.h>
+#include <mach/gpio.h>
+#include <mach/scu.h>
+#include <mach/spmp8000fb.h>
+
+/* Backlight */
+static struct platform_pwm_backlight_data backlight_letcool = {
+	.pwm_id		= 0,
+	.max_brightness	= 100,
+	.dft_brightness	= 20,
+	.pwm_period_ns	= 100000,
+};
+
+/* Framebuffer */
+static struct spmp8000fb_pdata spmp8000fb_letcool = {
+	.data_fmt = TFT_FMT_RGB565,
+	.mode = {
+		.name = "A035QN02V6",
+		.refresh = 60,
+		.xres = 320,
+		.yres = 240,
+		.pixclock = KHZ2PICOS(5000),
+		.left_margin = 8,
+		.right_margin = 8,
+		.upper_margin = 2,
+		.lower_margin = 2,
+		.hsync_len = 1,
+		.vsync_len = 1,
+	},
+};
+
+/* GPIO3 */
+static struct resource spmp8000_gpio3_resources[] = {
+	[0] = {
+		.name = "dat",
+		.start = SPMP8000_SCU_B_BASE + SCU_B_GPIO3_I,
+		.end = SPMP8000_SCU_B_BASE + SCU_B_GPIO3_I + 3,
+		.flags = IORESOURCE_MEM,
+	},
+	[1] = {
+		.name = "set",
+		.start = SPMP8000_SCU_B_BASE + SCU_B_GPIO3_O,
+		.end = SPMP8000_SCU_B_BASE + SCU_B_GPIO3_O + 3,
+		.flags = IORESOURCE_MEM,
+	},
+	[2] = {
+		.name = "dirin",
+		.start = SPMP8000_SCU_B_BASE + SCU_B_GPIO3_E,
+		.end = SPMP8000_SCU_B_BASE + SCU_B_GPIO3_E + 3,
+		.flags = IORESOURCE_MEM,
+	},
+};
+
+static struct of_dev_auxdata letcool_auxdata_lookup[] __initdata = {
+	OF_DEV_AUXDATA("pwm-backlight", 0, "pwm-backlight",
+			&backlight_letcool),
+	OF_DEV_AUXDATA("sunplus,spmp8000-lcd", 0x93000000, "93000000.fb",
+			&spmp8000fb_letcool),
+	OF_DEV_AUXDATA("basic-mmio-gpio", 0x90005070, "basic-mmio-gpio",
+			NULL),
+	{ /* sentinel */ },
+};
+
+static void __init letcool_dt_init(void)
+{
+	struct device_node *gpio3_np;
+	struct platform_device *gpio3_pdev;
+
+	/* Static board setup stuff */
+
+	/* Switch off pull-down on SD Card detect pin */
+	spmp8000_pinmux_pgc_set(47, 0);
+
+	/* Set GPIO0 pins as GPIO */
+	spmp8000_pinmux_pgs_set(31, 2);
+	spmp8000_pinmux_pgs_set(32, 2);
+	spmp8000_pinmux_pgs_set(37, 1);
+	spmp8000_pinmux_pgs_set(38, 1);
+
+	/* Set up SAR-GPIOs for the ADC power supply */
+	writel(0xCC, REG_SCU_A(SCU_A_SAR_GPIO_CTRL));
+	writel(0x3, REG_SCU_A(SCU_A_SAR_GPIO_OEN));
+	writel(0xC, REG_SCU_A(SCU_A_SAR_GPIO_O));
+
+	/* Switch UART_MISC_TX and RX to GPIO3 pins */
+	spmp8000_pinmux_pgs_set(43, 2);
+	spmp8000_pinmux_pgs_set(44, 2);
+
+	/* Codec errata fix */
+	writel(SCU_A_CODEC_CFG_VAL, REG_SCU_A(SCU_A_CODEC_CFG));
+
+	/* DT and platform device setup */
+
+	/* Create platform devces */
+	of_platform_populate(NULL, of_default_bus_match_table,
+			     letcool_auxdata_lookup, NULL);
+
+	/* Fix-up GPIO3 platform device resources to make gpio-generic work.
+	 * Should be removed once it has DT bindings. */
+	gpio3_np = of_find_compatible_node(NULL, NULL, "basic-mmio-gpio");
+	if (!gpio3_np) {
+		pr_err("letcool: Can't find gpio3 DT node\n");
+		return;
+	}
+
+	gpio3_pdev = of_find_device_by_node(gpio3_np);
+	if (!gpio3_pdev) {
+		pr_err("letcool: Can't find gpio3 platform device\n");
+		return;
+	}
+
+	gpio3_pdev->num_resources = ARRAY_SIZE(spmp8000_gpio3_resources);
+	gpio3_pdev->resource = spmp8000_gpio3_resources;
+}
+
+static const char *letcool_dt_match[] __initconst = {
+	"gameware,letcool",
+	NULL,
+};
+
+DT_MACHINE_START(LETCOOL, "Letcool N350JP")
+	.map_io		= spmp8000_map_io,
+	.init_early	= spmp8000_init_clkdev,
+	.init_irq	= spmp8000_init_irq,
+	.handle_irq	= vic_handle_irq,
+	.nr_irqs	= SPMP8000_TOTAL_IRQS,
+	.init_machine	= letcool_dt_init,
+	.timer		= &spmp8000_sys_timer,
+	.dt_compat	= letcool_dt_match,
+MACHINE_END
arch/arm/mach-spmp8000/include/mach/spmp8000fb.h new file +32 -0
new file mode 100644
index 0000000..525e23f
@@ -0,0 +1,32 @@
+/*
+ * SPMP8000 machines LCD controller support
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_FB_H__
+#define __MACH_SPMP8000_FB_H__
+
+#include <linux/fb.h>
+
+#define TFT_FMT_RGB565		0
+#define TFT_FMT_SRGB		1
+#define TFT_FMT_RGBDM		2
+#define TFT_FMT_CCIR601		3
+#define TFT_FMT_CCIR656		4
+#define TFT_FMT_RGB24B		5
+#define TFT_FMT_CCIR_720M	(1 << 8)
+
+struct spmp8000fb_pdata {
+	u32			data_fmt;
+	u32			xres_virtual;
+	u32			yres_virtual;
+	u32			bpp;
+	struct fb_videomode	mode;
+};
+
+#endif /* __MACH_SPMP8000_FB_H__ */
Signature · 1 line
1.7.4.1
#52 lore raw

PATCH 8/9 ARM: SPMP8000: Add support for the Letcool board

Quoted 7 lines · Zoltan Devai
---
 arch/arm/mach-spmp8000/board_letcool.c           |  154 ++++++++++++++++++++++
 arch/arm/mach-spmp8000/include/mach/spmp8000fb.h |   32 +++++
 2 files changed, 186 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-spmp8000/board_letcool.c
 create mode 100644 arch/arm/mach-spmp8000/include/mach/spmp8000fb.h

Hi Zoltan,

We basically stopped doing board files for new platforms. Please get rid of this and make sure that the spmp8000-letcool.dts describes everything you need to get this board running. The DT_MACHINE_START part should just be part of the core.c file and not mention the board name, since its intended to apply to all boards with this SOC.

Arnd

#53 lore raw

PATCH 8/9 ARM: SPMP8000: Add support for the Letcool board

2011/10/11 Arnd Bergmann <[email protected]>:

Quoted 9 lines · Zoltan Devai
Quoted 7 lines

---
?arch/arm/mach-spmp8000/board_letcool.c ? ? ? ? ? | ?154 ++++++++++++++++++++++ ?arch/arm/mach-spmp8000/include/mach/spmp8000fb.h | ? 32 +++++
?2 files changed, 186 insertions(+), 0 deletions(-)
?create mode 100644 arch/arm/mach-spmp8000/board_letcool.c
?create mode 100644 arch/arm/mach-spmp8000/include/mach/spmp8000fb.h

Quoted 8 lines

Hi Zoltan,

We basically stopped doing board files for new platforms. Please get rid of this and make sure that the spmp8000-letcool.dts describes everything you need to get this board running. The DT_MACHINE_START part should just be part of the core.c file and not mention the board name, since its intended to apply to all boards with this SOC.

Hi,

The board file only contains platform data for drivers which don't handle DT. Should I strip those off ?

Does that also mean that no fully working ARM board will be allowed into mainline until all required drivers do DT ?

Zoltan

#54 lore raw

PATCH 8/9 ARM: SPMP8000: Add support for the Letcool board

Quoted 9 lines · Zoltan Devai
Quoted 9 lines

Hi Zoltan,

We basically stopped doing board files for new platforms. Please get rid of this and make sure that the spmp8000-letcool.dts describes everything you need to get this board running. The DT_MACHINE_START part should just be part of the core.c file and not mention the board name, since its intended to apply to all boards with this SOC.

Quoted 4 lines

Hi,

The board file only contains platform data for drivers which don't handle DT. Should I strip those off ?

Well, ideally you should add support for DT probing to those drivers. With all the new helpers we recently added, that should not require more code than what you have in the board file that you lose in the process.

Quoted 2 lines

Does that also mean that no fully working ARM board will be allowed into mainline until all required drivers do DT ?

Yes, sorry that this causes more work for you.

The overall strategy is more complex, it depends a lot on the state that the platform is in:

1. For new platforms (prima2, zynq, picoxcell, highbank, spmp8000, ...), there is a single machine description and all devices are probed through the device tree. Board files are no longer allowed.

2a For actively maintained platforms (omap, pxa, msm, samsung, at91, imx, ...), the expectation is that the maintainers work on converting the drivers to device tree probing, but they can still add the occasional board file for important boards. Once all on board (not necessarily on-chip) devices can be probed using device tree, new boards files are not allowed any more, and existing board files should be removed (either all at once or gradually as dts files get added).

2b After an existing platform has lost its board files, we also want to remove the on-soc devices from platform files by moving them into the device tree. The method is the same: you get to add new socs only if you work on infrastructure to get rid of the code again and move it into dts files. Once the infrastructure is there, the existing soc descriptions should gradually go away.

3. For legacy platforms (rpc, sa1100, h720x, ...), I don't expect anyone to realistically work on device tree conversion. If someone sends a new board file, that would likely be accepted. Those platforms are also simple enough that doing everything using board files is not a signficant overhead.

Arnd

#55 lore raw

PATCH 8/9 ARM: SPMP8000: Add support for the Letcool board

In reply to Zoltan Devai
Quoted 6 lines · Zoltan Devai
arch/arm/mach-spmp8000/board_letcool.c new file +6 -0
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/pwm_backlight.h>
+#include <linux/input.h>
+#include <linux/basic_mmio_gpio.h>

arch/arm/mach-spmp8000/board_letcool.c line 26

Quoted 10 lines
arch/arm/mach-spmp8000/board_letcool.c new file +10 -0
+#include <linux/gpio_keys.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <asm/hardware/vic.h>
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+
+#include <mach/core.h>
+#include <mach/gpio.h>

In addition to Arnd's comments - please use linux/gpio.h rather than mach/gpio.h or asm/gpio.h throughout your patches. This helps to make them future-proof.

#56 lore raw

PATCH 9/9 ARM: SPMP8000: Add Kconfig and Makefile entries to build the machine

In reply to Zoltan Devai
---
 arch/arm/Kconfig  |   16 ++++++++++++++++
 arch/arm/Makefile |    1 +
 2 files changed, 17 insertions(+), 0 deletions(-)
arch/arm/Kconfig +16 -0
index 9426e26..f34f10d 100644
@@ -831,6 +831,22 @@ config ARCH_SHARK
 	  Support for the StrongARM based Digital DNARD machine, also known
 	  as "Shark" (<http://www.shark-linux.de/shark.html>).
 
+config ARCH_SPMP8000
+	bool "Sunplus SPMP8000"
+	select CPU_ARM926T
+	select ARM_VIC
+	select GENERIC_IRQ_CHIP
+	select MULTI_IRQ_HANDLER
+	select CLKSRC_MMIO
+	select GENERIC_CLOCKEVENTS
+	select ARCH_WANT_OPTIONAL_GPIOLIB
+	select USE_OF
+	select CLKDEV_LOOKUP
+	select HAVE_PWM
+	select ARCH_HAS_CPUFREQ
+	help
+	  Support for Sunplus SPMP8000A/8010A/8016A SoC.
+
 config ARCH_TCC_926
 	bool "Telechips TCC ARM926-based systems"
 	select CLKSRC_MMIO
arch/arm/Makefile +1 -0
index e0ac6eb..f837a3b 100644
@@ -180,6 +180,7 @@ machine-$(CONFIG_ARCH_EXYNOS4)		:= exynos4
 machine-$(CONFIG_ARCH_SA1100)		:= sa1100
 machine-$(CONFIG_ARCH_SHARK)		:= shark
 machine-$(CONFIG_ARCH_SHMOBILE) 	:= shmobile
+machine-$(CONFIG_ARCH_SPMP8000)		:= spmp8000
 machine-$(CONFIG_ARCH_TCC8K)		:= tcc8k
 machine-$(CONFIG_ARCH_TEGRA)		:= tegra
 machine-$(CONFIG_ARCH_U300)		:= u300
Signature · 1 line
1.7.4.1
#57 lore raw

PATCH 9/9 ARM: SPMP8000: Add Kconfig and Makefile entries to build the machine

Hi Zoltan,

Quoted 17 lines · Zoltan Devai
---
 arch/arm/Kconfig  |   16 ++++++++++++++++
 arch/arm/Makefile |    1 +
 2 files changed, 17 insertions(+), 0 deletions(-)
arch/arm/Kconfig +3 -0
index 9426e26..f34f10d 100644
@@ -831,6 +831,22 @@ config ARCH_SHARK
 	  Support for the StrongARM based Digital DNARD machine, also known
 	  as "Shark" (<http://www.shark-linux.de/shark.html>).
 
+config ARCH_SPMP8000
+	bool "Sunplus SPMP8000"
+	select CPU_ARM926T

arch/arm/Kconfig config ARCH_SHARK · line 846

Quoted 10 lines
arch/arm/Kconfig +10 -0
+	select ARM_VIC
+	select GENERIC_IRQ_CHIP
+	select MULTI_IRQ_HANDLER
+	select CLKSRC_MMIO
+	select GENERIC_CLOCKEVENTS
+	select ARCH_WANT_OPTIONAL_GPIOLIB
+	select USE_OF
+	select CLKDEV_LOOKUP
+	select HAVE_PWM
+	select ARCH_HAS_CPUFREQ

I can't see anything using cpufreq yet so perhaps this can be removed for now?

Jamie

#58 lore raw

PATCH 9/9 ARM: SPMP8000: Add Kconfig and Makefile entries to build the machine

In reply to Zoltan Devai

2011/10/9 Zoltan Devai <[email protected]>:

Quoted 4 lines

+config ARCH_SPMP8000
+ ? ? ? bool "Sunplus SPMP8000"
+ ? ? ? select CPU_ARM926T
+ ? ? ? select ARM_VIC

It's quite common for ARM926EJ-S systems to have TCM, if you don't know whether your system has it just try select HAVE_TCM for fun and see if it is detected on boot.

Yours,
Linus Walleij

#59 lore raw

PATCH 9/9 ARM: SPMP8000: Add Kconfig and Makefile entries to build the machine

In reply to Zoltan Devai

arch/arm/Kconfig config ARCH_SHARK · line 846

Quoted 13 lines · Zoltan Devai
arch/arm/Kconfig +13 -0
+config ARCH_SPMP8000
+	bool "Sunplus SPMP8000"
+	select CPU_ARM926T
+	select ARM_VIC
+	select GENERIC_IRQ_CHIP
+	select MULTI_IRQ_HANDLER
+	select CLKSRC_MMIO
+	select GENERIC_CLOCKEVENTS
+	select ARCH_WANT_OPTIONAL_GPIOLIB
+	select USE_OF
+	select CLKDEV_LOOKUP
+	select HAVE_PWM
+	select ARCH_HAS_CPUFREQ

Please arrange these select statements in alphabetical order - it helps avoid the 'add to the end' conflict problem.

#60 lore raw
In reply to Zoltan Devai
Quoted 10 lines · Zoltan Devai

Hi,

This series adds support for the Sunplus SPMP8000 SoC, an ARM926EJ-S based MCU, which is most commonly found in handheld game consoles and digital cams. Most of the work was done during GSoC 2011, with Greg KH as my mentor.

The board I'm using for development is the Letcool N350JP handheld.

The patches are based on Arnds for-next branch with Jamie Iles' vic-dt patches on top.

Excellent! Can I take that as a Tested-by from you for that series?

Jamie

#61 lore raw

2011/10/10 Jamie Iles <[email protected]>:

Quoted 7 lines · Zoltan Devai
Quoted 6 lines

Hi,

This series adds support for the Sunplus SPMP8000 SoC, an ARM926EJ-S based MCU, which is most commonly found in handheld game consoles and digital cams. Most of the work was done during GSoC 2011, with Greg KH as my mentor.

Quoted 6 lines
Quoted 4 lines

The board I'm using for development is the Letcool N350JP handheld.

The patches are based on Arnds for-next branch with Jamie Iles' vic-dt patches on top.

Excellent! ?Can I take that as a Tested-by from you for that series?

Yes, I've been using it without problems.
Do you need an explicit reply to that thread with the Tested-by ?

Z

#62 lore raw
Quoted 8 lines · Zoltan Devai

2011/10/10 Jamie Iles <[email protected]>:

Quoted 7 lines · Zoltan Devai
Quoted 6 lines

Hi,

This series adds support for the Sunplus SPMP8000 SoC, an ARM926EJ-S based MCU, which is most commonly found in handheld game consoles and digital cams. Most of the work was done during GSoC 2011, with Greg KH as my mentor.

Quoted 8 lines
Quoted 6 lines
Quoted 4 lines

The board I'm using for development is the Letcool N350JP handheld.

The patches are based on Arnds for-next branch with Jamie Iles' vic-dt patches on top.

Excellent! ?Can I take that as a Tested-by from you for that series?

Yes, I've been using it without problems.
Do you need an explicit reply to that thread with the Tested-by ?

No that's fine, I'll add it for the next posting/pull request.

Thanks,

Jamie

#63 lore raw
In reply to Zoltan Devai
Quoted 9 lines · Zoltan Devai

This series adds support for the Sunplus SPMP8000 SoC, an ARM926EJ-S based MCU, which is most commonly found in handheld game consoles and digital cams. Most of the work was done during GSoC 2011, with Greg KH as my mentor.

The board I'm using for development is the Letcool N350JP handheld.

The patches are based on Arnds for-next branch with Jamie Iles' vic-dt patches on top.

Quoted 8 lines

No static platform devices are used, everything is inited from DT, with some hacks for drivers that don't have any bindings yet. (Documentation for own, custom bindings will follow in next version.)

So far, these peripherals are supported, with drivers not submitted yet:
gpio, fb, cpufreq, slave-dma, mmc, ASoC (i2s, codec, pcm, card).

Reviews, instructions for mainlining it are very welcome.

Hi Zoltan,

Looks basically ok, but there are the usual smaller things I found that are worth improving. I like very much seeing an ARM926 port done in the new style, especially since these tend to be much simpler than the cortex-a9 platforms that most people are working on these days. This gives us the chance to experiment with a complete port without having to do heaps of new device tree bindings etc. It will also be valuable to have this when working on single-zimage patches for ARMv5.

I would however prefer to leave the port for linux-3.3 at this point, since we are close to the merge window and there are a lot of details for you to work on still.
In particular, I would hope that by the time of the 3.3 merge window we will have a proper common clockdev layer that can simplify a significant portion of your code.

As Linus Walleij mentioned already, it would be good to move the device drivers out of the platform directory into subsystems below drivers/ and I hope that by 3.3 that also includes the pinmux, clkdev, and timer drivers, possibly also pwm and adc.

My hope is that in the long run, a relatively simple platform like this can become a single core.c file plus the optional debug_ll support, after all the header files are consolidated and the drivers are moved out. You won't have to wait for that though: If the code looks ok, we can merge it in whatever the closest is we get to the ideal at the time.

Arnd

A message that is not in the archive (sent off-list or not yet received): [email protected]

#65 lore raw

PATCH v2 1/5 ARM: SPMP8000: Add machine base files

Files needed for the basic machine support of SPMP8000

---
 arch/arm/mach-spmp8000/Makefile                   |    4 +
 arch/arm/mach-spmp8000/Makefile.boot              |    1 +
 arch/arm/mach-spmp8000/core.c                     |   79 +++++++++++++++++++++
 arch/arm/mach-spmp8000/core.h                     |   27 +++++++
 arch/arm/mach-spmp8000/include/mach/debug-macro.S |   19 +++++
 arch/arm/mach-spmp8000/include/mach/entry-macro.S |   14 ++++
 arch/arm/mach-spmp8000/include/mach/gpio.h        |    1 +
 arch/arm/mach-spmp8000/include/mach/hardware.h    |   25 +++++++
 arch/arm/mach-spmp8000/include/mach/io.h          |   17 +++++
 arch/arm/mach-spmp8000/include/mach/irqs.h        |   21 ++++++
 arch/arm/mach-spmp8000/include/mach/memory.h      |    1 +
 arch/arm/mach-spmp8000/include/mach/system.h      |   43 +++++++++++
 arch/arm/mach-spmp8000/include/mach/timex.h       |   17 +++++
 arch/arm/mach-spmp8000/include/mach/uncompress.h  |   37 ++++++++++
 arch/arm/mach-spmp8000/include/mach/vmalloc.h     |   16 ++++
 arch/arm/mach-spmp8000/lluart.c                   |   24 ++++++
 16 files changed, 346 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-spmp8000/Makefile
 create mode 100644 arch/arm/mach-spmp8000/Makefile.boot
 create mode 100644 arch/arm/mach-spmp8000/core.c
 create mode 100644 arch/arm/mach-spmp8000/core.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/debug-macro.S
 create mode 100644 arch/arm/mach-spmp8000/include/mach/entry-macro.S
 create mode 100644 arch/arm/mach-spmp8000/include/mach/gpio.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/hardware.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/io.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/irqs.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/memory.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/system.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/timex.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/uncompress.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/vmalloc.h
 create mode 100644 arch/arm/mach-spmp8000/lluart.c
arch/arm/mach-spmp8000/Makefile new file +4 -0
new file mode 100644
index 0000000..15e0238
@@ -0,0 +1,4 @@
+obj-y 			:= core.o
+obj-y			+= clkdev.o
+obj-y			+= clock.o
+obj-$(CONFIG_DEBUG_LL)	+= lluart.o
arch/arm/mach-spmp8000/Makefile.boot new file +1 -0
new file mode 100644
index 0000000..dae9661
@@ -0,0 +1 @@
+zreladdr-y	:= 0x00008000
arch/arm/mach-spmp8000/core.c new file +79 -0

79 lines not on this page · raw message

arch/arm/mach-spmp8000/core.h new file +27 -0
new file mode 100644
index 0000000..1e84a49
@@ -0,0 +1,27 @@
+/*
+ * SPMP8000 generic includes
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_CORE_H__
+#define __MACH_SPMP8000_CORE_H__
+
+/* drivers/clksrc/spmp8000_tmrb.c */
+extern void spmp8000_sys_timer_init(void);
+
+/* clkdev.c */
+extern void spmp8000_init_clkdev(void);
+
+/* lluart.c */
+#ifdef CONFIG_DEBUG_LL
+extern void spmp8000_map_lluart(void);
+#else
+#define spmp8000_map_lluart	NULL
+#endif
+
+#endif /* __MACH_SPMP8000_CORE_H__ */
arch/arm/mach-spmp8000/include/mach/debug-macro.S new file +19 -0
new file mode 100644
index 0000000..698c37e
@@ -0,0 +1,19 @@
+/*
+ * SPMP8000 debug-macro.S
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+	.macro  addruart, rp, rv
+	mov     \rp, #0x92000000
+	add	\rp, #0x00B00000
+	add	\rp, #0x00004000
+	orr	\rv, \rp, #0xF0000000
+	.endm
+
+#define UART_SHIFT 2
+#include <asm/hardware/debug-8250.S>
arch/arm/mach-spmp8000/include/mach/entry-macro.S new file +14 -0
new file mode 100644
index 0000000..3d26088
@@ -0,0 +1,14 @@
+/*
+ * SPMP8000 entry-macro.S
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+	.macro  disable_fiq
+	.endm
+
+	.macro  arch_ret_to_user, tmp1, tmp2
+	.endm
arch/arm/mach-spmp8000/include/mach/gpio.h new file +1 -0
new file mode 100644
index 0000000..40a8c17
@@ -0,0 +1 @@
+/* empty */
arch/arm/mach-spmp8000/include/mach/hardware.h new file +25 -0
new file mode 100644
index 0000000..bf65e47
@@ -0,0 +1,25 @@
+/*
+ * SPMP8000 hardware.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_HARDWARE_H__
+#define __MACH_SPMP8000_HARDWARE_H__
+
+/* Used by WDT driver and arch_reset */
+extern void *spmp8000_wdt_base;
+
+/* Physical to virtual translation of static mappings */
+#define SPMP8000_P2V(x)		((x) | 0xF0000000)
+#define IO_ADDRESS(x)		((void __iomem *)SPMP8000_P2V(x))
+
+/* Needed by lluart.c and uncompress.h */
+#define SPMP8000_UARTC0_BASE	0x92B04000
+#define SPMP8000_UARTC0_SIZE	SZ_4K
+
+#endif /* __MACH_SPMP8000_HARDWARE_H__ */
arch/arm/mach-spmp8000/include/mach/io.h new file +17 -0
new file mode 100644
index 0000000..464b78c
@@ -0,0 +1,17 @@
+/*
+ * SPMP8000 io.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_IO_H__
+#define __MACH_SPMP8000_IO_H__
+
+#define __mem_pci(a)	(a)
+#define __io(a)		NULL /* Only for 8250 UART driver */
+
+#endif /* __MACH_SPMP8000_IO_H__ */
arch/arm/mach-spmp8000/include/mach/irqs.h new file +21 -0
new file mode 100644
index 0000000..7becde7
@@ -0,0 +1,21 @@
+/*
+ * SPMP8000 irqs.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_IRQS_H__
+#define __MACH_SPMP8000_IRQS_H__
+
+#define SPMP8000_HW_IRQS		64
+#define SPMP8000_GPIO_IRQS_START	SPMP8000_HW_IRQS
+#define SPMP8000_GPIO_IRQS		(16 + 32)
+#define SPMP8000_TOTAL_IRQS		(SPMP8000_HW_IRQS + SPMP8000_GPIO_IRQS)
+
+#define NR_IRQS				0
+
+#endif /* __MACH_SPMP8000_IRQS_H__ */
arch/arm/mach-spmp8000/include/mach/memory.h new file +1 -0
new file mode 100644
index 0000000..40a8c17
@@ -0,0 +1 @@
+/* empty */
arch/arm/mach-spmp8000/include/mach/system.h new file +43 -0

43 lines not on this page · raw message

arch/arm/mach-spmp8000/include/mach/timex.h new file +17 -0
new file mode 100644
index 0000000..795deff
@@ -0,0 +1,17 @@
+/*
+ * SPMP8000 timex.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_TIMEX_H__
+#define __MACH_SPMP8000_TIMEX_H__
+
+/* TODO this should go away */
+#define CLOCK_TICK_RATE		(50 * HZ)
+
+#endif /* __MACH_SPMP8000_TIMEX_H__ */
arch/arm/mach-spmp8000/include/mach/uncompress.h new file +37 -0
new file mode 100644
index 0000000..105778b
@@ -0,0 +1,37 @@
+/*
+ * SPMP8000 uncompress.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * Based on the mach-kirkwood implementation
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/serial_reg.h>
+#include <mach/hardware.h>
+
+#define SERIAL_BASE	((volatile unsigned char *)SPMP8000_UARTC0_BASE)
+
+static void putc(const char c)
+{
+	volatile unsigned char *base = SERIAL_BASE;
+	int i;
+
+	for (i = 0; i < 0x1000; i++) {
+		if (base[UART_LSR << 2] & UART_LSR_THRE)
+			break;
+		barrier();
+	}
+
+	base[UART_TX << 2] = c;
+}
+
+static void flush(void)
+{
+}
+
+#define arch_decomp_setup()
+#define arch_decomp_wdog()
arch/arm/mach-spmp8000/include/mach/vmalloc.h new file +16 -0
new file mode 100644
index 0000000..ff40d1c
@@ -0,0 +1,16 @@
+/*
+ * SPMP8000 vmalloc.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_VMALLOC_H__
+#define __MACH_SPMP8000_VMALLOC_H__
+
+#define VMALLOC_END	0xF0000000UL
+
+#endif /* __MACH_SPMP8000_VMALLOC_H__ */
arch/arm/mach-spmp8000/lluart.c new file +24 -0
new file mode 100644
index 0000000..4b90514
@@ -0,0 +1,24 @@
+/*
+ * Static memory mapping for DEBUG_LL
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <asm/page.h>
+#include <asm/mach/map.h>
+#include <mach/hardware.h>
+
+void __init spmp8000_map_lluart(void)
+{
+	struct map_desc spmp8000_lluart_map = {
+		.virtual        = SPMP8000_P2V(SPMP8000_UARTC0_BASE),
+		.pfn            = __phys_to_pfn(SPMP8000_UARTC0_BASE),
+		.length         = SPMP8000_UARTC0_SIZE,
+		.type           = MT_DEVICE,
+	};
+
+	iotable_init(&spmp8000_lluart_map, 1);
+}
Signature · 1 line
1.7.4.1
#66 lore raw

PATCH v2 1/5 ARM: SPMP8000: Add machine base files

Quoted 3 lines · On Wednesday 19 October 2011 18:01:54 Zoltan Devai wrote:

Files needed for the basic machine support of SPMP8000

Hi Zoltan,

This version looks very nice, only a few small things remain that I noticed reading through it again:

arch/arm/mach-spmp8000/core.c line 24

Quoted 3 lines
arch/arm/mach-spmp8000/core.c new file +3 -0
+
+void __iomem *spmp8000_wdt_base;
+EXPORT_SYMBOL(spmp8000_wdt_base);

This should really be EXPORT_SYMBOL_GPL.

Quoted 1 line

+#define SERIAL_BASE ((volatile unsigned char *)SPMP8000_UARTC0_BASE)

drop this, then

arch/arm/mach-spmp8000/include/mach/uncompress.h line 20

Quoted 3 lines
arch/arm/mach-spmp8000/include/mach/uncompress.h new file +3 -0
+static void putc(const char c)
+{
+	volatile unsigned char *base = SERIAL_BASE;

make this a "unsigned char __iomem *".

arch/arm/mach-spmp8000/include/mach/uncompress.h line 30

Quoted 10 lines
arch/arm/mach-spmp8000/include/mach/uncompress.h new file +10 -0
+	int i;
+
+	for (i = 0; i < 0x1000; i++) {
+		if (base[UART_LSR << 2] & UART_LSR_THRE)
+			break;
+		barrier();
+	}
+
+	base[UART_TX << 2] = c;
+}

and use readb_relaxed/writeb_relaxed to do the access. You should never just dereference volatile pointers to do an MMIO access.

Best check your code by running 'sparse' over it (make C=1). It will warn about things like this. Try to fix all warnings that sparse reports.

Arnd

#67 lore raw

PATCH v2 1/5 ARM: SPMP8000: Add machine base files

Quoted 10 lines · Arnd Bergmann
Quoted 1 line

+#define SERIAL_BASE ((volatile unsigned char *)SPMP8000_UARTC0_BASE)

drop this, then

arch/arm/mach-spmp8000/include/mach/uncompress.h line 20

Quoted 3 lines
arch/arm/mach-spmp8000/include/mach/uncompress.h new file +3 -0
+static void putc(const char c)
+{
+	volatile unsigned char *base = SERIAL_BASE;

make this a "unsigned char __iomem *".

Quoted 13 lines

arch/arm/mach-spmp8000/include/mach/uncompress.h line 30

Quoted 10 lines
arch/arm/mach-spmp8000/include/mach/uncompress.h new file +10 -0
+	int i;
+
+	for (i = 0; i < 0x1000; i++) {
+		if (base[UART_LSR << 2] & UART_LSR_THRE)
+			break;
+		barrier();
+	}
+
+	base[UART_TX << 2] = c;
+}

and use readb_relaxed/writeb_relaxed to do the access. You should never just dereference volatile pointers to do an MMIO access.

Note that uncompress.h isn't part of the kernel proper, and so the MMIO accessors may not work (depends whether the arch out-of-lines them.) Moreover, as the MMU is setup with 1:1 mappings, arguably it's not IOMEM anyway.

#68 lore raw

PATCH v2 1/5 ARM: SPMP8000: Add machine base files

2011/10/22 Russell King - ARM Linux <[email protected]>:

Quoted 25 lines · Arnd Bergmann
Quoted 10 lines
Quoted 1 line

+#define SERIAL_BASE ? ? ? ?((volatile unsigned char *)SPMP8000_UARTC0_BASE)

drop this, then

arch/arm/mach-spmp8000/include/mach/uncompress.h

Quoted 3 lines
arch/arm/mach-spmp8000/include/mach/uncompress.h new file +3 -0
+static void putc(const char c)
+{
+ ? volatile unsigned char *base = SERIAL_BASE;

make this a "unsigned char __iomem *".

Quoted 13 lines

Review

Quoted 10 lines

+ ? int i;

(quoted diff) +9 -0
+
+ ? for (i = 0; i < 0x1000; i++) {
+ ? ? ? ? ? if (base[UART_LSR << 2] & UART_LSR_THRE)
+ ? ? ? ? ? ? ? ? ? break;
+ ? ? ? ? ? barrier();
+ ? }
+
+ ? base[UART_TX << 2] = c;
+}

and use readb_relaxed/writeb_relaxed to do the access. You should never just dereference volatile pointers to do an MMIO access.

Quoted 4 lines

Note that uncompress.h isn't part of the kernel proper, and so the MMIO accessors may not work (depends whether the arch out-of-lines them.) ?Moreover, as the MMU is setup with 1:1 mappings, arguably it's not IOMEM anyway.

What's the preferred method then ?
The current uncompress.h files use all possible methods. This is certainly also a question for the uncompress.h cleanup series.

Z

#69 lore raw

PATCH v2 1/5 ARM: SPMP8000: Add machine base files

In reply to Arnd Bergmann

2011/10/19 Arnd Bergmann <[email protected]>:

Quoted 9 lines · On Wednesday 19 October 2011 18:01:54 Zoltan Devai wrote:
Quoted 3 lines

Files needed for the basic machine support of SPMP8000

Hi Zoltan,

This version looks very nice, only a few small things remain that I noticed reading through it again:

OK, will correct these.

What's next then ?
I assume I'd need some more review on at least the dts file. Also, can the timer code go through your tree into drivers/clocksource ?

Thanks,
Zoltan

#70 lore raw

PATCH v2 1/5 ARM: SPMP8000: Add machine base files

Quoted 6 lines · Zoltan Devai

2011/10/19 Arnd Bergmann <[email protected]>:

Quoted 5 lines · On Wednesday 19 October 2011 18:01:54 Zoltan Devai wrote:
Quoted 3 lines

Files needed for the basic machine support of SPMP8000

Quoted 7 lines
Quoted 4 lines

Hi Zoltan,

This version looks very nice, only a few small things remain that I noticed reading through it again:

OK, will correct these.

What's next then ?

We talked about it at the ARM subarchitecture maintainer summit, and I think I'll just merge your platform code for 3.2 if you send the fixed version in time. I'm not going to merge your clk support patch (2/5) though, since there is a lot of work going into the common clk framework right now, and I don't want to cause more work going into rewriting your code.

Please instead replace that patch with a stubbed-out version that builds but doesn't actually do anything. Obviously you won't be able to run on actual hardware then but you still have the patch you did and know how to apply it for testing ;-).

Quoted 1 line

I assume I'd need some more review on at least the dts file.

It seems all good to me, but I would definitely appreciate Grant and Rob to take a closer look.

Quoted 1 line

Also, can the timer code go through your tree into drivers/clocksource ?

It can, but it needs an Acked-by from John and/or Thomas in that case. Alternatively, feel free to stick my acked-by on the patch and have Thomas handle the patch.

Arnd

#71 lore raw

PATCH v2 1/5 ARM: SPMP8000: Add machine base files

Hi Arnd,

Quoted 11 lines · Arnd Bergmann
Quoted 10 lines · Zoltan Devai

2011/10/19 Arnd Bergmann <[email protected]>:

Quoted 9 lines · On Wednesday 19 October 2011 18:01:54 Zoltan Devai wrote:
Quoted 3 lines

Files needed for the basic machine support of SPMP8000

Hi Zoltan,

This version looks very nice, only a few small things remain that I noticed reading through it again:

Quoted 7 lines
Quoted 3 lines

OK, will correct these.

What's next then ?

We talked about it at the ARM subarchitecture maintainer summit, and I think I'll just merge your platform code for 3.2 if you send the fixed version in time. I'm not going to merge your clk support patch (2/5) though, since

Zoltan uses my device tree bindings and multi IRQ handler for the VIC which hasn't yet been merged (and I haven't had any Tested-by/Acked-by's) from anyone other than Zoltan yet.

I suppose the device tree binding and multi IRQ handler patches /could/ go in without the other platform conversions though they haven't had any time in -next.

Jamie

#72 lore raw

PATCH v2 1/5 ARM: SPMP8000: Add machine base files

2011/10/24 Jamie Iles <[email protected]>:

Quoted 20 lines · Arnd Bergmann
Quoted 11 lines · Zoltan Devai
Quoted 10 lines

2011/10/19 Arnd Bergmann <[email protected]>:

Quoted 9 lines · On Wednesday 19 October 2011 18:01:54 Zoltan Devai wrote:
Quoted 3 lines

Files needed for the basic machine support of SPMP8000

Hi Zoltan,

This version looks very nice, only a few small things remain that I noticed reading through it again:

Quoted 7 lines
Quoted 3 lines

OK, will correct these.

What's next then ?

We talked about it at the ARM subarchitecture maintainer summit, and I think I'll just merge your platform code for 3.2 if you send the fixed version in time. I'm not going to merge your clk support patch (2/5) though, since

Quoted 7 lines

Zoltan uses my device tree bindings and multi IRQ handler for the VIC which hasn't yet been merged (and I haven't had any Tested-by/Acked-by's) from anyone other than Zoltan yet.

I suppose the device tree binding and multi IRQ handler patches /could/ go in without the other platform conversions though they haven't had any time in -next.

Arnd, Russell,

What about Jamie's VIC patches ?
Should I wait for those to get in somehow, or rebase SPMP8000 to not depend on those ?

Thanks,
Zoltan

#73 lore raw

PATCH v2 1/5 ARM: SPMP8000: Add machine base files

Quoted 12 lines · Zoltan Devai

2011/10/24 Jamie Iles <[email protected]>:

Quoted 11 lines · Arnd Bergmann
Quoted 5 lines · Zoltan Devai
Quoted 1 line

2011/10/19 Arnd Bergmann <[email protected]>:

We talked about it at the ARM subarchitecture maintainer summit, and I think I'll just merge your platform code for 3.2 if you send the fixed version in time. I'm not going to merge your clk support patch (2/5) though, since

Zoltan uses my device tree bindings and multi IRQ handler for the VIC which hasn't yet been merged (and I haven't had any Tested-by/Acked-by's) from anyone other than Zoltan yet.

Quoted 8 lines
Quoted 3 lines

I suppose the device tree binding and multi IRQ handler patches could go in without the other platform conversions though they haven't had any time in -next.

Arnd, Russell,

What about Jamie's VIC patches ?
Should I wait for those to get in somehow, or rebase SPMP8000 to not depend on those ?

Sorry I didn't get back to you on this any more. As you may have noticed, Linus merged a large number of patches through my tree today and yours were not a part of that.

It's too late for 3.2 now, but everything looks good for 3.3. I'll wait for Jamie's patches to get stable first and then put your code into the arm-soc tree.

There are other problems with the process in your series: you should never cherry-pick a kernel from another branch if you depend on them. Instead, make sure that the patches are queued up for the next merge window and not changing any more (they are now obviously, as Linus merged them).

Arnd

#74 lore raw

PATCH v2 2/5 ARM: SPMP8000: Add clk support

In reply to a message not in the archive

Add support for the clk API

---
 arch/arm/mach-spmp8000/clkdev.c |  611 +++++++++++++++++++++++++++++++++++++++
 arch/arm/mach-spmp8000/clock.c  |  149 ++++++++++
 arch/arm/mach-spmp8000/clock.h  |   32 ++
 3 files changed, 792 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-spmp8000/clkdev.c
 create mode 100644 arch/arm/mach-spmp8000/clock.c
 create mode 100644 arch/arm/mach-spmp8000/clock.h
arch/arm/mach-spmp8000/clkdev.c new file +611 -0
new file mode 100644
index 0000000..470ce1a
@@ -0,0 +1,611 @@
+/*
+ * SPMP8000 machines clock support
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+
+#include "clock.h"
+
+/* System Control Unit registers */
+#define SCU_A_PERI_CLKEN	0x04
+
+#define SCU_A_APLL_CFG		0x44
+#define APLL_CFG_P		BIT(0)
+#define APLL_CFG_S		BIT(1)
+#define APLL_CFG_F		BIT(2)
+#define APLL_CFG_E		BIT(3)
+#define APLL_CFG_AS_MASK	0x3F0
+#define APLL_CFG_AS_SHIFT	4
+#define APLL_CFG_AS_MAGIC	0x12
+#define APLL_CFG_C		BIT(10)
+#define APLL_CFG_R		BIT(11)
+#define APLL_CFG_DAR_SHIFT	16
+#define APLL_CFG_DAR_MASK	(7 << APLL_CFG_DAR_SHIFT)
+#define APLL_CFG_DS		BIT(19)
+#define APLL_CFG_ADR_SHIFT	24
+#define APLL_CFG_ADR_MASK	(7 << APLL_CFG_ADR_SHIFT)
+#define APLL_CFG_AS		BIT(27)
+
+#define SCU_A_LCD_CLK_CFG	0x80
+#define LCD_CLK_CFG_RATIO_MASK	0xFF
+#define LCD_CLK_CFG_EN		BIT(8)
+#define SCU_A_CSI_CLK_CFG	0x84
+#define CSI_CLK_CFG_RATIO_MASK	0xFF
+#define CSI_CLK_CFG_EN		BIT(8)
+#define SCU_A_I2S_BCK_CFG	0x90
+#define I2S_BCK_CFG_RATIO_MASK	0xFF
+#define I2S_BCK_CFG_EN		BIT(8)
+#define SCU_A_UART_CFG		0x94
+#define UART_CFG_RATIO_MASK	0xFF
+#define UART_CFG_EN		BIT(8)
+#define DIVIDER_ENABLE_BIT	BIT(8)
+
+#define SCU_B_PERI_CLKEN	0x20
+#define SCU_B_UPDATE_ARM_RATIO	0x28
+
+#define SCU_B_SPLL_CFG		0x04
+#define SPLL_CFG_R_MASK		3
+#define SPLL_CFG_R_SHIFT	0
+#define SPLL_CFG_F_MASK		0xFC
+#define SPLL_CFG_F_SHIFT	2
+#define SPLL_CFG_BS		BIT(8)
+#define SPLL_CFG_OD		BIT(9)
+#define SPLL_CFG_BP		BIT(10)
+#define SPLL_CFG_PD		BIT(11)
+#define SPLL_CFG_CSEL_MASK	(BIT(13) | BIT(12))
+#define SPLL_CFG_CSEL_SHIFT	12
+#define SPLL_CFG_ASEL_MASK	(BIT(15) | BIT(14))
+#define SPLL_CFG_ASEL_SHIFT	14
+#define SPLL_CFG_SE		BIT(17)
+#define SPLL_CFG_XE		BIT(18)
+#define SPLL_CFG_AE		BIT(19)
+#define SPLL_CFG_XR		BIT(20)
+#define SPLL_CFG_PL		BIT(31)
+
+#define SCU_B_ARM_RATIO		0xD0
+#define SCU_B_ARM_AHB_RATIO	0xD4
+#define SCU_B_ARM_APB_RATIO	0xD8
+#define SCU_B_SYS_CNT_EN	0xDC
+#define SYS_CNT_EN_SYS		BIT(0)
+#define SYS_CNT_EN_SYS_RT	BIT(1)
+#define SYS_CNT_EN_SYS_AHB	BIT(2)
+#define SYS_CNT_EN_SYS_APB	BIT(3)
+#define SYS_CNT_EN_SYS_CHECK	BIT(31)
+
+#define SCU_C_PERI_CLKEN	0x04
+
+#define SCU_C_UPDATE_SYS_RATIO	0x28
+#define SCU_C_SYS_RATIO		0x100
+#define SCU_C_SYS_RT_RATIO	0x104
+#define SCU_C_SYS_AHB_RATIO	0x108
+#define SCU_C_SYS_APB_RATIO	0x10C
+#define SCU_C_CEVA_RATIO	0x110
+#define SCU_C_CEVA_AHB_RATIO	0x114
+#define SCU_C_CEVA_APB_RATIO	0x118
+#define SCU_C_CEVA_CNT_EN	0x11C
+#define CEVA_CNT_EN_CEVA	BIT(0)
+#define CEVA_CNT_EN_CEVA_AHB	BIT(1)
+#define CEVA_CNT_EN_CEVA_APB	BIT(2)
+#define CEVA_CNT_EN_CEVA_CHECK	BIT(31)
+
+/* The SoC doesn't support anything else, no need to make these machine
+ * configurable.
+ */
+#define XTAL_FREQ		27000000UL
+#define XTAL_32K_FREQ		32768UL
+#define APLL_48K_FREQ		73728000UL
+#define APLL_44K_FREQ		67737600UL
+
+static void __iomem *scu_a_base;
+static void __iomem *scu_b_base;
+static void __iomem *scu_c_base;
+
+static unsigned long spll_get_rate(struct clk *clk)
+{
+	unsigned int f, r, od;
+	unsigned long osc;
+	u32 cfg;
+
+	cfg = readl(scu_b_base + SCU_B_SPLL_CFG);
+	osc = XTAL_FREQ;
+
+	f = (cfg & SPLL_CFG_F_MASK) >> SPLL_CFG_F_SHIFT;
+	r = (cfg & SPLL_CFG_R_MASK) >> SPLL_CFG_R_SHIFT;
+	od = !!(cfg & SPLL_CFG_OD);
+	clk->rate = (osc * (f + 1) * 2) / ((r + 1) * (od + 1));
+
+	return clk->rate;
+}
+
+static unsigned long pll_mux_get_rate(int ratesel, struct clk *clk)
+{
+	int pll_rate;
+
+	switch (ratesel) {
+	case 0:
+		clk->rate = XTAL_FREQ;
+		break;
+	case 1:
+		clk->rate = XTAL_32K_FREQ;
+		break;
+	case 2:
+		pll_rate = clk_get_rate(clk->parent);
+		clk->rate = pll_rate / 2;
+		break;
+	case 3:
+		pll_rate = clk_get_rate(clk->parent);
+		clk->rate = pll_rate / 3;
+		break;
+	}
+
+	return clk->rate;
+}
+
+static unsigned long ref_arm_get_rate(struct clk *clk)
+{
+	int ratesel;
+	u32 spll_cfg;
+
+	spll_cfg = readl(scu_b_base + SCU_B_SPLL_CFG);
+	ratesel = (spll_cfg & SPLL_CFG_ASEL_MASK) >> SPLL_CFG_ASEL_SHIFT;
+	return pll_mux_get_rate(ratesel, clk);
+}
+
+static unsigned long ref_ceva_get_rate(struct clk *clk)
+{
+	int ratesel;
+	u32 spll_cfg;
+
+	spll_cfg = readl(scu_b_base + SCU_B_SPLL_CFG);
+	ratesel = (spll_cfg & SPLL_CFG_CSEL_MASK) >> SPLL_CFG_CSEL_SHIFT;
+	return pll_mux_get_rate(ratesel, clk);
+}
+
+static unsigned long apll_get_rate(void)
+{
+	u32 cfg = readl(scu_a_base + SCU_A_APLL_CFG);
+
+	if ((cfg & APLL_CFG_R) || (~cfg & (APLL_CFG_P | APLL_CFG_E)) ||
+		(((cfg & APLL_CFG_AS_MASK) >> APLL_CFG_AS_SHIFT)
+			!= APLL_CFG_AS_MAGIC)) {
+		return 0;
+	}
+
+	/* Not dealing with the input oscillator frequency as the settings
+	 * for non-27Mhz are unknown, and all platforms use that anyway */
+	if (cfg & APLL_CFG_S)
+		return APLL_44K_FREQ;
+	else
+		return APLL_48K_FREQ;
+}
+
+static void apll_enable(struct clk *clk)
+{
+	u32 cfg = readl(scu_a_base + SCU_A_APLL_CFG);
+
+	/* Store new config with reset, then disable reset */
+	cfg |= APLL_CFG_R;
+	cfg |= APLL_CFG_E | APLL_CFG_P | APLL_CFG_F;
+	cfg |= APLL_CFG_AS_MAGIC << APLL_CFG_AS_SHIFT;
+
+	writel(cfg, scu_a_base + SCU_A_APLL_CFG);
+
+	cfg &= ~APLL_CFG_R;
+	writel(cfg, scu_a_base + SCU_A_APLL_CFG);
+}
+
+static void apll_disable(struct clk *clk)
+{
+	u32 cfg = readl(scu_a_base + SCU_A_APLL_CFG);
+
+	cfg &= ~APLL_CFG_P;
+	cfg |= APLL_CFG_E;
+
+	writel(cfg, scu_a_base + SCU_A_APLL_CFG);
+}
+
+static int apll_set_rate(unsigned long rate)
+{
+	u32 cfg;
+
+	cfg = readl(scu_a_base + SCU_A_APLL_CFG);
+
+	switch (rate) {
+	case 67737600:
+		cfg |= APLL_CFG_S;
+		break;
+	case 73728000:
+		cfg &= ~APLL_CFG_S;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	writel(cfg, scu_a_base + SCU_A_APLL_CFG);
+
+	return 0;
+}
+
+static void i2s_mck_switch(u32 mask, int enable)
+{
+	u32 cfg = readl(scu_a_base + SCU_A_APLL_CFG);
+	if (enable)
+		cfg &= ~mask;
+	else
+		cfg |= mask;
+
+	writel(cfg, scu_a_base + SCU_A_APLL_CFG);
+}
+
+static void i2stx_mck_enable(struct clk *clk)
+{
+	i2s_mck_switch(APLL_CFG_DS, 1);
+}
+
+static void i2stx_mck_disable(struct clk *clk)
+{
+	i2s_mck_switch(APLL_CFG_DS, 0);
+}
+
+static void i2srx_mck_enable(struct clk *clk)
+{
+	i2s_mck_switch(APLL_CFG_AS, 1);
+}
+
+static void i2srx_mck_disable(struct clk *clk)
+{
+	i2s_mck_switch(APLL_CFG_AS, 0);
+}
+
+static const int apll_dividers[7] = {
+	3, 6, 9, 12, 18, 24, 32,
+};
+
+static unsigned long i2s_mck_get_rate(struct clk *clk, u32 msk, u32 sh)
+{
+	unsigned long apll_rate = apll_get_rate();
+	u32 cfg = readl(scu_a_base + SCU_A_APLL_CFG);
+	int divider;
+
+	divider = (cfg & msk) >> sh;
+
+	if (divider == ARRAY_SIZE(apll_dividers))
+		return 0;
+
+	divider = apll_dividers[divider];
+
+	return apll_rate / divider;
+}
+
+static unsigned long i2stx_mck_get_rate(struct clk *clk)
+{
+	return i2s_mck_get_rate(clk, APLL_CFG_DAR_MASK, APLL_CFG_DAR_SHIFT);
+}
+
+static unsigned long i2srx_mck_get_rate(struct clk *clk)
+{
+	return i2s_mck_get_rate(clk, APLL_CFG_ADR_MASK, APLL_CFG_ADR_SHIFT);
+}
+
+static int i2s_mck_set_rate(struct clk *clk, unsigned long rate,
+					u32 msk, u32 sh)
+{
+	int i = ARRAY_SIZE(apll_dividers) - 1;
+	unsigned long apll_rate;
+	int divider = -1;
+	u32 cfg;
+
+	/* Set up APLL */
+	if (rate % 8000)
+		apll_rate = APLL_44K_FREQ;
+	else
+		apll_rate = APLL_48K_FREQ;
+
+	apll_set_rate(apll_rate);
+
+	cfg = readl(scu_a_base + SCU_A_APLL_CFG);
+
+	/* Get the biggest possible divider for MCK */
+	while (i >= 0) {
+		if (apll_rate / apll_dividers[i] == rate) {
+			divider = apll_dividers[i];
+			break;
+		}
+
+		i--;
+	}
+
+	if (divider < 0)
+		return -EINVAL;
+
+	cfg &= ~msk;
+	cfg |= (i << sh);
+
+	writel(cfg, scu_a_base + SCU_A_APLL_CFG);
+
+	return 0;
+}
+
+static int i2stx_mck_set_rate(struct clk *clk, unsigned long rate)
+{
+	return i2s_mck_set_rate(clk, rate,
+					APLL_CFG_DAR_MASK, APLL_CFG_DAR_SHIFT);
+}
+
+static int i2srx_mck_set_rate(struct clk *clk, unsigned long rate)
+{
+	return i2s_mck_set_rate(clk, rate,
+					APLL_CFG_ADR_MASK, APLL_CFG_ADR_SHIFT);
+}
+
+static void divider_set_clock(struct clk *clk, int on)
+{
+	u32 divider;
+
+	divider = readl(clk->scu_base + clk->scu_divreg);
+	if (on)
+		divider |= DIVIDER_ENABLE_BIT;
+	else
+		divider &= ~DIVIDER_ENABLE_BIT;
+	writel(divider, clk->scu_base + clk->scu_divreg);
+}
+
+static void divider_enable_clock(struct clk *clk)
+{
+	divider_set_clock(clk, 1);
+}
+
+static void divider_disable_clock(struct clk *clk)
+{
+	divider_set_clock(clk, 0);
+}
+
+static unsigned long divider_get_rate(struct clk *clk)
+{
+	u32 divider;
+	unsigned long parent_rate = clk_get_rate(clk->parent);
+
+	if (!parent_rate) {
+		clk->rate = 0;
+		return clk->rate;
+	}
+
+	divider = readl(clk->scu_base + clk->scu_divreg);
+	clk->rate = parent_rate / ((divider & clk->divmask) + 1);
+
+	return clk->rate;
+}
+
+static int divider_set_rate(struct clk *clk, unsigned long rate)
+{
+	u32 divider, divider_old;
+	unsigned long parent_rate = clk_get_rate(clk->parent);
+
+	if (unlikely(!parent_rate || rate > parent_rate)) {
+		clk->rate = parent_rate;
+		pr_debug("spmp8000: clk: parent rate < requested rate\n");
+		return 0;
+	}
+
+	divider = (parent_rate / rate) - 1;
+
+	if (divider > clk->divmask) {
+		pr_debug("spmp8000: clk: input clock / requested > max div\n");
+		divider = clk->divmask;
+	}
+
+	divider_old = readl(clk->scu_base + clk->scu_divreg);
+
+	/* Order of writes is important: 0 -> divider value -> enable bit */
+	writel(0, clk->scu_base + clk->scu_divreg);
+	writel(divider, clk->scu_base + clk->scu_divreg);
+
+	/* Re-enable clock if it was enabled before */
+	if (divider_old & DIVIDER_ENABLE_BIT)
+		writel(divider | DIVIDER_ENABLE_BIT,
+				clk->scu_base + clk->scu_divreg);
+
+	clk->rate = parent_rate / (divider + 1);
+
+	return 0;
+}
+static void periph_enable_clock(struct clk *clk)
+{
+	u32 scu_reg = readl(clk->scu_base + clk->scu_clkreg);
+
+	scu_reg |= (1 << clk->clkreg_off);
+
+	writel(scu_reg, clk->scu_base + clk->scu_clkreg);
+}
+
+static void periph_disable_clock(struct clk *clk)
+{
+	u32 scu_reg = readl(clk->scu_base + clk->scu_clkreg);
+
+	scu_reg &= ~(1 << clk->clkreg_off);
+
+	writel(scu_reg, clk->scu_base + clk->scu_clkreg);
+}
+
+void spmp8000_update_arm_freqs(void)
+{
+	writel(7, scu_b_base + SCU_B_UPDATE_ARM_RATIO);
+}
+EXPORT_SYMBOL(spmp8000_update_arm_freqs);
+
+static struct clk clk_spll = {
+	.get_rate = spll_get_rate,
+};
+
+static struct clk clk_ref_arm = {
+	.parent = &clk_spll,
+	.get_rate = ref_arm_get_rate,
+};
+
+static struct clk clk_ref_ceva = {
+	.parent = &clk_spll,
+	.get_rate = ref_ceva_get_rate,
+};
+
+static struct clk clk_apll = {
+	.name = "clk_apll",
+	.enable = apll_enable,
+	.disable = apll_disable,
+};
+
+static struct clk clk_i2stx_mck = {
+	.name = "clk_i2stx_mck",
+	.parent = &clk_apll,
+	.enable = i2stx_mck_enable,
+	.disable = i2stx_mck_disable,
+	.get_rate = i2stx_mck_get_rate,
+	.set_rate = i2stx_mck_set_rate,
+};
+
+static struct clk clk_i2srx_mck = {
+	.name = "clk_i2srx_mck",
+	.parent = &clk_apll,
+	.enable = i2srx_mck_enable,
+	.disable = i2srx_mck_disable,
+	.get_rate = i2srx_mck_get_rate,
+	.set_rate = i2srx_mck_set_rate,
+};
+
+#define SYS_CLK_DIV(__name, __parent, __scu_base, __divreg) \
+static struct clk __name = { \
+	.name = #__name, \
+	.parent = __parent, \
+	.scu_base = __scu_base, \
+	.scu_divreg = __divreg, \
+	.divmask = 0x3F, \
+	.get_rate = &divider_get_rate, \
+	.set_rate = &divider_set_rate, \
+}
+
+SYS_CLK_DIV(clk_arm, &clk_ref_arm, &scu_b_base, SCU_B_ARM_RATIO);
+SYS_CLK_DIV(clk_arm_ahb, &clk_arm, &scu_b_base, SCU_B_ARM_AHB_RATIO);
+SYS_CLK_DIV(clk_arm_apb, &clk_arm_ahb, &scu_b_base, SCU_B_ARM_APB_RATIO);
+SYS_CLK_DIV(clk_ceva, &clk_ref_ceva, &scu_c_base, SCU_C_CEVA_RATIO);
+SYS_CLK_DIV(clk_ceva_ahb, &clk_ceva, &scu_c_base, SCU_C_CEVA_AHB_RATIO);
+SYS_CLK_DIV(clk_ceva_apb, &clk_ceva_ahb, &scu_c_base, SCU_C_CEVA_APB_RATIO);
+SYS_CLK_DIV(clk_sys, &clk_ref_ceva, &scu_c_base, SCU_C_SYS_RATIO);
+SYS_CLK_DIV(clk_sys_ahb, &clk_sys, &scu_c_base, SCU_C_SYS_AHB_RATIO);
+SYS_CLK_DIV(clk_sys_apb, &clk_sys_ahb, &scu_c_base, SCU_C_SYS_APB_RATIO);
+
+#define SYS_CLK_DIV_EN(__name, __parent, __scu_base, __divreg) \
+static struct clk __name = { \
+	.name = #__name, \
+	.parent = __parent, \
+	.scu_base = __scu_base, \
+	.scu_divreg = __divreg, \
+	.divmask = 0xFF, \
+	.enable = &divider_enable_clock, \
+	.disable = &divider_disable_clock, \
+	.get_rate = &divider_get_rate, \
+	.set_rate = &divider_set_rate, \
+}
+
+SYS_CLK_DIV_EN(clk_uart, &clk_ref_ceva, &scu_a_base, SCU_A_UART_CFG);
+SYS_CLK_DIV_EN(clk_lcd, &clk_ref_ceva, &scu_a_base, SCU_A_LCD_CLK_CFG);
+SYS_CLK_DIV_EN(clk_i2srx_bck, &clk_i2srx_mck, &scu_a_base, SCU_A_I2S_BCK_CFG);
+SYS_CLK_DIV_EN(clk_i2stx_bck, &clk_i2stx_mck, &scu_a_base, SCU_A_I2S_BCK_CFG);
+
+/* Peripherals */
+#define PERIPH_CLK(__name, __parent, __scu_base, __clkreg_off) \
+static struct clk __name = { \
+	.name = #__name, \
+	.parent = __parent, \
+	.scu_base = __scu_base, \
+	.clkreg_off = __clkreg_off, \
+	.enable = periph_enable_clock, \
+	.disable = periph_disable_clock, \
+}
+
+PERIPH_CLK(clk_rt_abt, &clk_sys, &scu_a_base, 21);
+/* Make the parent rt_abt to auto-enable it when enabling the lcdc clock */
+PERIPH_CLK(clk_lcd_ctrl, &clk_rt_abt, &scu_a_base, 1);
+PERIPH_CLK(clk_apbdma_a, &clk_rt_abt, &scu_a_base, 9);
+PERIPH_CLK(clk_apll_ctrl, &clk_sys_ahb, &scu_a_base, 14);
+PERIPH_CLK(clk_i2stx_ctrl, &clk_apbdma_a, &scu_a_base, 17);
+PERIPH_CLK(clk_i2srx_ctrl, &clk_apbdma_a, &scu_a_base, 18);
+PERIPH_CLK(clk_saacc, &clk_sys_apb, &scu_a_base, 19);
+
+PERIPH_CLK(clk_tmrb, &clk_arm_apb, &scu_b_base, 9);
+PERIPH_CLK(clk_apbdma_c, &clk_sys_apb, &scu_c_base, 7);
+PERIPH_CLK(clk_sd0, &clk_sys_apb, &scu_c_base, 18);
+
+static struct clk_lookup lookups[] = {
+	CLKDEV_INIT(NULL, "arm", &clk_arm),
+	CLKDEV_INIT(NULL, "arm_ahb", &clk_arm_ahb),
+	CLKDEV_INIT(NULL, "arm_apb", &clk_arm_apb),
+	CLKDEV_INIT(NULL, "ceva", &clk_ceva),
+	CLKDEV_INIT(NULL, "ceva_ahb", &clk_ceva_ahb),
+	CLKDEV_INIT(NULL, "ceva_apb", &clk_ceva_apb),
+	CLKDEV_INIT(NULL, "sys", &clk_sys),
+	CLKDEV_INIT(NULL, "sys_ahb", &clk_sys_ahb),
+	CLKDEV_INIT(NULL, "sys_apb", &clk_sys_apb),
+	CLKDEV_INIT(NULL, "lcd", &clk_lcd),
+	CLKDEV_INIT(NULL, "apbdma_a", &clk_apbdma_a),
+	CLKDEV_INIT(NULL, "saacc", &clk_saacc),
+	CLKDEV_INIT(NULL, "i2stx_mck", &clk_i2stx_mck),
+	CLKDEV_INIT(NULL, "i2srx_mck", &clk_i2srx_mck),
+	CLKDEV_INIT(NULL, "i2stx_bck", &clk_i2stx_bck),
+	CLKDEV_INIT(NULL, "i2srx_bck", &clk_i2srx_bck),
+	CLKDEV_INIT("uart.0", NULL, &clk_uart),
+	CLKDEV_INIT("uart.1", NULL, &clk_uart),
+	CLKDEV_INIT("uart.2", NULL, &clk_uart),
+	CLKDEV_INIT("93000000.fb", NULL, &clk_lcd_ctrl),
+	CLKDEV_INIT("90000000.pwm", NULL, &clk_tmrb),
+	CLKDEV_INIT("92b00000.dma", NULL, &clk_apbdma_c),
+	CLKDEV_INIT("92b0b000.mmc", NULL, &clk_sd0),
+	CLKDEV_INIT("93010000.dma", NULL, &clk_apbdma_a),
+	CLKDEV_INIT("93012000.i2s", NULL, &clk_i2stx_ctrl),
+	CLKDEV_INIT("9301d000.i2s", NULL, &clk_i2srx_ctrl),
+};
+
+void __init *spmp8000_map_scu(const char *compatible)
+{
+	struct device_node *np;
+	void __iomem *addr;
+
+	np = of_find_compatible_node(NULL, NULL, compatible);
+	if (!np)
+		panic("spmp8000: unable to find %s node in dtb\n",
+			compatible);
+
+	addr = of_iomap(np, 0);
+	if (!addr)
+		panic("spmp8000: unable to map %s registers\n",
+			compatible);
+
+	return addr;
+}
+
+void __init spmp8000_init_clkdev(void)
+{
+	scu_a_base = spmp8000_map_scu("sunplus,spmp8000-scua");
+	scu_b_base = spmp8000_map_scu("sunplus,spmp8000-scub");
+	scu_c_base = spmp8000_map_scu("sunplus,spmp8000-scuc");
+
+	clkdev_add_table(lookups, ARRAY_SIZE(lookups));
+
+	/* Enable the apll control registers here, as the according clock
+	 * isn't exported, but used by the mck/bck clocks. If we wouldn't
+	 * enable it here, the freq of the pll couldn't be set up before
+	 * enabling one of its child clocks.
+	 * The PLL clock itself will be auto-enabled on demand by them.
+	 */
+	clk_enable(&clk_apll_ctrl);
+}
arch/arm/mach-spmp8000/clock.c new file +149 -0

149 lines not on this page · raw message

arch/arm/mach-spmp8000/clock.h new file +32 -0
new file mode 100644
index 0000000..d20759a
@@ -0,0 +1,32 @@
+/*
+ * SPMP8000 machines clock support
+ *
+ * Copyright (C) 2011 Zoltan Devai <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#ifndef __MACH_SPMP8000_CLOCK_H__
+#define __MACH_SPMP8000_CLOCK_H__
+
+struct clk {
+	char		*name;
+	unsigned long	rate;
+	unsigned int	id;
+	unsigned int	refcount;
+	void __iomem	*scu_base;
+	int		scu_clkreg;
+	unsigned int	clkreg_off;
+	int		scu_divreg;
+	int		divmask;
+	struct clk	*parent;
+	unsigned long	(*get_rate)(struct clk *clk);
+	unsigned long	(*round_rate) (struct clk *, u32);
+	int		(*set_rate) (struct clk *, unsigned long);
+	int		(*set_parent) (struct clk *clk, struct clk *parent);
+	void		(*enable) (struct clk *);
+	void		(*disable) (struct clk *);
+};
+
+#endif /* __MACH_SPMP8000_CLOCK_H__ */
Signature · 1 line
1.7.4.1
#75 lore raw

PATCH v2 3/5 ARM: SPMP8000: Add clocksource and clockevent drivers

In reply to a message not in the archive

This adds a clocksource and clockevent for the SPMP8000 machine.

Cc John Stultz <[email protected]>
Cc Thomas Gleixner <[email protected]>
Cc Grant Likely <[email protected]>
---
 drivers/clocksource/Makefile        |    3 +-
 drivers/clocksource/spmp8000_tmrb.c |  159 +++++++++++++++++++++++++++++++++++
 2 files changed, 161 insertions(+), 1 deletions(-)
 create mode 100644 drivers/clocksource/spmp8000_tmrb.c
drivers/clocksource/Makefile +2 -1
index 8d81a1d..adb575e 100644
@@ -9,4 +9,5 @@ obj-$(CONFIG_SH_TIMER_TMU)	+= sh_tmu.o
 obj-$(CONFIG_CLKBLD_I8253)	+= i8253.o
 obj-$(CONFIG_CLKSRC_MMIO)	+= mmio.o
 obj-$(CONFIG_DW_APB_TIMER)	+= dw_apb_timer.o
-obj-$(CONFIG_CLKSRC_DBX500_PRCMU)	+= clksrc-dbx500-prcmu.o
\ No newline at end of file
+obj-$(CONFIG_CLKSRC_DBX500_PRCMU)	+= clksrc-dbx500-prcmu.o
+obj-$(CONFIG_ARCH_SPMP8000)	+= spmp8000_tmrb.o
drivers/clocksource/spmp8000_tmrb.c new file +159 -0

159 lines not on this page · raw message

Signature · 1 line
1.7.4.1
#76 lore raw

PATCH v2 4/5 ARM: SPMP8000: Add SPMP8000 SoC and Letcool board dts descriptions

In reply to a message not in the archive

This adds the DT description of the SPMP8000 SoC, the Letcool N350JP board and documentation of required bindings

Cc Grant Likely <[email protected]>
CC devicetree-discuss at lists.ozlabs.org
---
 Documentation/devicetree/bindings/arm/spmp8000.txt |   25 +++++
 arch/arm/boot/dts/spmp8000-letcool.dts             |   20 ++++
 arch/arm/boot/dts/spmp8000.dtsi                    |   93 ++++++++++++++++++++
 3 files changed, 138 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/spmp8000.txt
 create mode 100644 arch/arm/boot/dts/spmp8000-letcool.dts
 create mode 100644 arch/arm/boot/dts/spmp8000.dtsi
Documentation/devicetree/bindings/arm/spmp8000.txt new file +25 -0
new file mode 100644
index 0000000..c392b09
@@ -0,0 +1,25 @@
+Sunplus SPMP8000 device tree bindings
+=====================================
+
+Required root node properties:
+	- compatible:
+		- "gameware,letcool" : Letcool N350JO handheld game console board
+		- "sunplus,spmp8000" : A board based on the SPMP8000 SoC
+
+Timer required properties:
+	- compatible = "sunplus,spmp8000-timer"
+	- interrupt-parent : The interrupt controller node this timer belongs to
+	- interrupts : IRQs of the timer
+	- clock-freq : The frequency in HZ of the timer.
+	- reg : The register bank for the timer.
+
+Watchdog required properties:
+	- compatible = "sunplus,spmp8000-watchdof"
+	- interrupt-parent : The interrupt controller node this timer belongs to
+	- interrupts : IRQs of the watchdog
+	- reg : The register bank for the watchdog controller
+
+SCU required properties:
+	- compatible: "sunplus,spmp8000-scua" or "sunplus,spmp8000-scuab" or
+		      "sunplus,spmp8000-scuc"
+	- reg: The register bank of the System Control Unit
arch/arm/boot/dts/spmp8000-letcool.dts new file +20 -0
new file mode 100644
index 0000000..424f2aa
@@ -0,0 +1,20 @@
+/dts-v1/;
+/include/ "spmp8000.dtsi"
+/ {
+	model = "Letcool N350JP handheld game console";
+	compatible = "gameware,letcool", "sunplus,spmp8000";
+
+	memory {
+		reg = <0x00000000 0x02000000>;
+	};
+
+	chosen {
+		linux,stdout-path = &uart0;
+	};
+
+	armapb {
+		timer at 90000000 {
+			clock-freq = <15187500>;
+		};
+	};
+};
arch/arm/boot/dts/spmp8000.dtsi new file +93 -0

93 lines not on this page · raw message

Signature · 1 line
1.7.4.1
#77 lore raw

PATCH v2 4/5 ARM: SPMP8000: Add SPMP8000 SoC and Letcool board dts descriptions

Quoted 7 lines · Zoltan Devai

This adds the DT description of the SPMP8000 SoC, the Letcool N350JP board and documentation of required bindings

Cc Grant Likely <[email protected]>
CC devicetree-discuss at lists.ozlabs.org

---

Only a couple of minor things below. Otherwise looks good.

Quoted 17 lines
 Documentation/devicetree/bindings/arm/spmp8000.txt |   25 +++++
 arch/arm/boot/dts/spmp8000-letcool.dts             |   20 ++++
 arch/arm/boot/dts/spmp8000.dtsi                    |   93 ++++++++++++++++++++
 3 files changed, 138 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/spmp8000.txt
 create mode 100644 arch/arm/boot/dts/spmp8000-letcool.dts
 create mode 100644 arch/arm/boot/dts/spmp8000.dtsi
Documentation/devicetree/bindings/arm/spmp8000.txt new file +3 -0
new file mode 100644
index 0000000..c392b09
@@ -0,0 +1,25 @@
+Sunplus SPMP8000 device tree bindings
+=====================================
+

Documentation/devicetree/bindings/arm/spmp8000.txt line 13

Quoted 10 lines
Documentation/devicetree/bindings/arm/spmp8000.txt new file +10 -0
+Required root node properties:
+	- compatible:
+		- "gameware,letcool" : Letcool N350JO handheld game console board
+		- "sunplus,spmp8000" : A board based on the SPMP8000 SoC
+
+Timer required properties:
+	- compatible = "sunplus,spmp8000-timer"
+	- interrupt-parent : The interrupt controller node this timer belongs to
+	- interrupts : IRQs of the timer
+	- clock-freq : The frequency in HZ of the timer.

"clock-frequency" is what is commonly used.

Documentation/devicetree/bindings/arm/spmp8000.txt line 17

Quoted 4 lines
Documentation/devicetree/bindings/arm/spmp8000.txt new file +4 -0
+	- reg : The register bank for the timer.
+
+Watchdog required properties:
+	- compatible = "sunplus,spmp8000-watchdof"

s/watchdof/watchdog/

Quoted 98 lines
Documentation/devicetree/bindings/arm/spmp8000.txt new file +8 -0
+	- interrupt-parent : The interrupt controller node this timer belongs to
+	- interrupts : IRQs of the watchdog
+	- reg : The register bank for the watchdog controller
+
+SCU required properties:
+	- compatible: "sunplus,spmp8000-scua" or "sunplus,spmp8000-scuab" or
+		      "sunplus,spmp8000-scuc"
+	- reg: The register bank of the System Control Unit
arch/arm/boot/dts/spmp8000-letcool.dts new file +20 -0
new file mode 100644
index 0000000..424f2aa
@@ -0,0 +1,20 @@
+/dts-v1/;
+/include/ "spmp8000.dtsi"
+/ {
+	model = "Letcool N350JP handheld game console";
+	compatible = "gameware,letcool", "sunplus,spmp8000";
+
+	memory {
+		reg = <0x00000000 0x02000000>;
+	};
+
+	chosen {
+		linux,stdout-path = &uart0;
+	};
+
+	armapb {
+		timer at 90000000 {
+			clock-freq = <15187500>;
+		};
+	};
+};
arch/arm/boot/dts/spmp8000.dtsi new file +58 -0
new file mode 100644
index 0000000..15b25fa
@@ -0,0 +1,93 @@
+/ {
+	compatible = "sunplus,spmp8000";
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	armapb {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0 0x90000000 0x10000>;
+
+		timer at 90000000 {
+			compatible = "sunplus,spmp8000-timer";
+			reg = <0x0 0x1000>;
+			interrupt-parent = <&vic0>;
+			interrupts = <7 8 9>;
+		};
+
+		watchdog at 90001000 {
+			compatible = "sunplus,spmp8000-wdt";
+			reg = <0x1000 0x1000>;
+			interrupt-parent = <&vic0>;
+			interrupts = <10 11 12>;
+		};
+
+		scu-b at 90005000 {
+			compatible = "sunplus,spmp8000-scub";
+			reg = <0x5000 0x1000>;
+		};
+	};
+
+	armahb {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0 0x90010000 0x20000>;
+
+		vic0: interrupt-controller at 90010000 {
+			compatible = "arm,pl192";
+			interrupt-controller;
+			#interrupt-cells = <1>;
+			reg = <0x0 0x1000>;
+		};
+
+		vic1: interrupt-controller at 900020000 {
+			compatible = "arm,pl192";
+			interrupt-controller;
+			#interrupt-cells = <1>;
+			reg = <0x10000 0x1000>;
+		};
+	};
+
+	plat {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0 0x92000000 0x1000000>;
+

arch/arm/boot/dts/spmp8000.dtsi line 68

Quoted 10 lines
arch/arm/boot/dts/spmp8000.dtsi new file +10 -0
+		scu-c at 92005000 {
+			compatible = "sunplus,spmp8000-scuc";
+			reg = <0x5000 0x1000>;
+		};
+
+		plat-apb {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0 0xB00000 0x10000>;

lower case for hex is preferred.

Quoted 25 lines
arch/arm/boot/dts/spmp8000.dtsi new file +25 -0
+
+			uart0: uart-c0 at 92B04000 {
+				compatible = "ns16550a";
+				reg = <0x4000 0x1000>;
+				clock-frequency = <2076923>;
+				interrupt-parent = <&vic1>;
+				interrupts = <24>;
+				current-speed = <115200>;
+				reg-shift = <2>;
+			};
+		};
+	};
+
+	axi {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0 0x93000000 0x20000>;
+
+		scu-a at 93007000 {
+			compatible = "sunplus,spmp8000-scua";
+			reg = <0x7000 0x1000>;
+		};
+	};
+};
#78 lore raw

PATCH v2 5/5 ARM: SPMP8000: Add Kconfig and Makefile entries

In reply to a message not in the archive

This adds the necessary Kconfig and Makefile entries to build the SPMP8000 machine

---
 arch/arm/Kconfig  |   16 ++++++++++++++++
 arch/arm/Makefile |    1 +
 2 files changed, 17 insertions(+), 0 deletions(-)
arch/arm/Kconfig +16 -0
index 9426e26..bfd8317 100644
@@ -831,6 +831,22 @@ config ARCH_SHARK
 	  Support for the StrongARM based Digital DNARD machine, also known
 	  as "Shark" (<http://www.shark-linux.de/shark.html>).
 
+config ARCH_SPMP8000
+	bool "Sunplus SPMP8000"
+	select ARCH_WANT_OPTIONAL_GPIOLIB
+	select ARM_PATCH_PHYS_VIRT
+	select ARM_VIC
+	select CLKDEV_LOOKUP
+	select CLKSRC_MMIO
+	select CPU_ARM926T
+	select GENERIC_CLOCKEVENTS
+	select GENERIC_IRQ_CHIP
+	select MULTI_IRQ_HANDLER
+	select NO_IOPORT
+	select USE_OF
+	help
+	  Support for Sunplus SPMP8000A/8010A/8016A SoC.
+
 config ARCH_TCC_926
 	bool "Telechips TCC ARM926-based systems"
 	select CLKSRC_MMIO
arch/arm/Makefile +1 -0
index e0ac6eb..f837a3b 100644
@@ -180,6 +180,7 @@ machine-$(CONFIG_ARCH_EXYNOS4)		:= exynos4
 machine-$(CONFIG_ARCH_SA1100)		:= sa1100
 machine-$(CONFIG_ARCH_SHARK)		:= shark
 machine-$(CONFIG_ARCH_SHMOBILE) 	:= shmobile
+machine-$(CONFIG_ARCH_SPMP8000)		:= spmp8000
 machine-$(CONFIG_ARCH_TCC8K)		:= tcc8k
 machine-$(CONFIG_ARCH_TEGRA)		:= tegra
 machine-$(CONFIG_ARCH_U300)		:= u300
Signature · 1 line
1.7.4.1