Add support for the SPMP8000 SoC and Letcool board
Subsystems (5)
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(-)
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
@@ -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
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.
Signed-off-by Zoltan Devai <[email protected]>---
?arch/arm/common/vic.c | ? ?1 -
?1 files changed, 0 insertions(+), 1 deletions(-)arch/arm/common/vic.c +0
-0
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
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.
Signed-off-by Zoltan Devai <[email protected]>---
?arch/arm/common/vic.c | ? ?1 -
?1 files changed, 0 insertions(+), 1 deletions(-)arch/arm/common/vic.c +0
-0arch/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/
Acked-by Linus Walleij <[email protected]>
Done, will exclude this from the next series.
PATCH 2/9 ARM: SPMP8000: Add machine base files
In reply to Zoltan DevaiAdd 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
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@@ -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.oarch/arm/mach-spmp8000/core.c new file +41
-0@@ -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@@ -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@@ -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@@ -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@@ -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@@ -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
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@@ -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@@ -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, +};
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@@ -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.oarch/arm/mach-spmp8000/core.c new file +47
-0@@ -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_ADDRESSreturns a void__iomempointer 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_initpatches you should be able to reduce this to:
Quoted 7 lines
? ? ? ?static const struct
of_device_idspmp8000_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@@ -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
__initattributes 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@@ -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_configthat 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->boolconversion.
Yeah, just debugging leftover.
Quoted 22 lines
Quoted 22 lines
arch/arm/mach-spmp8000/include/mach/gpio.h new file +15
-0@@ -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_irqRussell 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@@ -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_IRQSI think that if you use my VIC patches then you can actually define
NR_IRQSto 0 as all IRQ descs are dynamically allocated (otherwise you'll be reserving a bunch ofirq_descsthat 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@@ -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-spmp8000then I'd put the headers in there rather than thearch/arm/mach-spmp8000/include/machsubdir 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
-0Same 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@@ -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@@ -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 ? ? ? ?1Nit: 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_RESUMEcase (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, +};
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_ADDRESSreturns a void__iomempointer 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 makeIO_ADDRESSreturn 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;
+ ? ? enumdma_slave_buswidth? ? ? ?dma_width;
+ ? ? int ? ? ? ? ? ? ? ? ? ? ? ? ? ? maxburst; +};dmaengine has
dma_slave_configthat 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@@ -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
PATCH 2/9 ARM: SPMP8000: Add machine base files
In reply to Zoltan DevaiQuoted 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+#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.harch/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@@ -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@@ -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
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+#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_4KDo 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.harch/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@@ -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
#defineAPLL_CFG_S? ? ? ? ? ? ?0x00000002
#defineAPLL_CFG_F? ? ? ? ? ? ?0x00000004
#defineAPLL_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@@ -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 ? ?0x1000These have to be removed. For device drivers, you should normally use
of_iomapto 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_resetis clever, but unfortunately not too common otherwise, so you have to play tricks. I would try locating the device and ioremapping it fromcore.cand then export a function to set the register that is used both by thearch_resetfunction and by the watchdog driver. The latter can still bind to theplatform_device, but it then doesn't have to do an extraof_iomap.
OK
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
@@ -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 useIO_PTRfor 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.
PATCH 2/9 ARM: SPMP8000: Add machine base files
In reply to Zoltan DevaiQuoted 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 addresses0x90005000,0x92005000and0x93007000. 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_desctable inlluart.cand an #ifdefCONFIG_DEBUG_LLin themap_iocall.
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 frommachine_initand 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 todrivers/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 0x04Quoted 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 useBIT()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
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 addresses0x90005000,0x92005000and0x93007000. 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_ADDRESSmacro 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 entire0xf0000000segment 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_desctable inlluart.cand an #ifdefCONFIG_DEBUG_LLin themap_iocall.
Is it worth it ?Yes, it's worth it. Don't do the #ifdef in the
map_iocall 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 frommachine_initand 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.
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 frommachine_initand 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
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
@@ -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 = ÷r_enable_clock, \ + .disable = ÷r_disable_clock, \ + .get_rate = ÷r_get_rate, \ + .set_rate = ÷r_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
@@ -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
PATCH 3/9 ARM: SPMP8000: Add clk support
Quoted 24 lines · Zoltan Devai
Signed-off-by Zoltan Devai <[email protected]>--- 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.harch/arm/mach-spmp8000/clkdev.c new file +8
-0@@ -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?
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
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.
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.
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
@@ -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
@@ -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
PATCH 4/9 ARM: SPMP8000: Add ADC driver
2011/10/9 Zoltan Devai <[email protected]>:
Quoted 4 lines
Signed-off-by Zoltan Devai <[email protected]>---
?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
PATCH 4/9 ARM: SPMP8000: Add ADC driver
Quoted 7 lines · Linus Walleij
2011/10/9 Zoltan Devai <[email protected]>:
Quoted 4 lines
Signed-off-by Zoltan Devai <[email protected]>--- 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/adcthese 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
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
Signed-off-by Zoltan Devai <[email protected]>--- 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/adcthese 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
PATCH 4/9 ARM: SPMP8000: Add ADC driver
In reply to Jonathan CameronQuoted 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.
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
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.
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/armis 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
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/armis 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/adcordrivers/misc/adcfor 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.
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/armis 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/adcordrivers/misc/adcfor 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
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/armis 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/adcordrivers/misc/adcfor 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
PATCH 4/9 ARM: SPMP8000: Add ADC driver
In reply to Arnd BergmannQuoted 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".
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.
PATCH 4/9 ARM: SPMP8000: Add ADC driver
In reply to Mark BrownQuoted 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/armis 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.
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/armis 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 ofdrivers/adcis 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.
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/armis 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 ofdrivers/adcis 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/adcis in trouble now. It will have an in-kernel competitor for similar stuff.Having
drivers/adcanddrivers/iio/adccompete 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/adcshould be moved down todrivers/adcwithout 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
PATCH 4/9 ARM: SPMP8000: Add ADC driver
In reply to Russell King - ARM LinuxQuoted 9 lines · Russell King - ARM Linux
Quoted 3 lines · Mark Brown
At the minute it seems to me that
arch/armis 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 ofdrivers/adcis 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.
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/armis 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.
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/armis 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
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.
PATCH 4/9 ARM: SPMP8000: Add ADC driver
In reply to Russell King - ARM LinuxQuoted 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_datastructure. 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.
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_datawhich 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
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_datawhich 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_datadefinitions 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.
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
@@ -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
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 100644arch/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
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 100644arch/arm/mach-spmp8000/pinmux.cI'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
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
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
@@ -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
PATCH 6/9 ARM: SPMP8000: Add pwm driver
2011/10/9 Zoltan Devai <[email protected]>:
Quoted 5 lines
Signed-off-by Zoltan Devai <[email protected]>---
?arch/arm/mach-spmp8000/pwm.c | ?246 ++++++++++++++++++++++++++++++++++++++++++ ?1 files changed, 246 insertions(+), 0 deletions(-)
?create mode 100644arch/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
PATCH 6/9 ARM: SPMP8000: Add pwm driver
Quoted 12 lines · Linus Walleij
2011/10/9 Zoltan Devai <[email protected]>:
Quoted 5 lines
Signed-off-by Zoltan Devai <[email protected]>---
?arch/arm/mach-spmp8000/pwm.c | ?246 ++++++++++++++++++++++++++++++++++++++++++ ?1 files changed, 246 insertions(+), 0 deletions(-)
?create mode 100644arch/arm/mach-spmp8000/pwm.cAlso 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/anddrivers/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 |
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
@@ -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
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
Signed-off-by Zoltan Devai <[email protected]>--- 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.dtsiarch/arm/boot/dts/spmp8000-letcool.dts new file +2
-0@@ -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
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
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
@@ -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
@@ -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
PATCH 8/9 ARM: SPMP8000: Add support for the Letcool board
Quoted 7 lines · Zoltan Devai
Signed-off-by Zoltan Devai <[email protected]>--- 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
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
Signed-off-by Zoltan Devai <[email protected]>---
?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 100644arch/arm/mach-spmp8000/board_letcool.c
?create mode 100644arch/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.dtsdescribes everything you need to get this board running. TheDT_MACHINE_STARTpart should just be part of thecore.cfile 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
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.dtsdescribes everything you need to get this board running. TheDT_MACHINE_STARTpart should just be part of thecore.cfile 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
PATCH 8/9 ARM: SPMP8000: Add support for the Letcool board
In reply to Zoltan DevaiQuoted 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.
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
@@ -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
@@ -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
PATCH 9/9 ARM: SPMP8000: Add Kconfig and Makefile entries to build the machine
Hi Zoltan,
Quoted 17 lines · Zoltan Devai
Signed-off-by Zoltan Devai <[email protected]>--- arch/arm/Kconfig | 16 ++++++++++++++++ arch/arm/Makefile | 1 + 2 files changed, 17 insertions(+), 0 deletions(-)arch/arm/Kconfig +3
-0@@ -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
PATCH 9/9 ARM: SPMP8000: Add Kconfig and Makefile entries to build the machine
In reply to Zoltan Devai2011/10/9 Zoltan Devai <[email protected]>:
Quoted 4 lines
+config
ARCH_SPMP8000
+ ? ? ? bool "Sunplus SPMP8000"
+ ? ? ? selectCPU_ARM926T
+ ? ? ? selectARM_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
PATCH 9/9 ARM: SPMP8000: Add Kconfig and Makefile entries to build the machine
In reply to Zoltan Devaiarch/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.
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
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
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
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]
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1 @@ +/* empty */
arch/arm/mach-spmp8000/include/mach/hardware.h new file +25 -0
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
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
Signed-off-by Zoltan Devai <[email protected]>
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
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.
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.hisn'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
PATCH v2 1/5 ARM: SPMP8000: Add machine base files
In reply to Arnd Bergmann2011/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
Signed-off-by Zoltan Devai <[email protected]>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
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
Signed-off-by Zoltan Devai <[email protected]>
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
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
Signed-off-by Zoltan Devai <[email protected]>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
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
Signed-off-by Zoltan Devai <[email protected]>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
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
PATCH v2 2/5 ARM: SPMP8000: Add clk support
In reply to a message not in the archiveAdd 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
@@ -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 = ÷r_get_rate, \ + .set_rate = ÷r_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 = ÷r_enable_clock, \ + .disable = ÷r_disable_clock, \ + .get_rate = ÷r_get_rate, \ + .set_rate = ÷r_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
@@ -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
PATCH v2 3/5 ARM: SPMP8000: Add clocksource and clockevent drivers
In reply to a message not in the archiveThis adds a clocksource and clockevent for the SPMP8000 machine.
--- 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
@@ -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 +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
PATCH v2 4/5 ARM: SPMP8000: Add SPMP8000 SoC and Letcool board dts descriptions
In reply to a message not in the archiveThis adds the DT description of the SPMP8000 SoC, the Letcool N350JP board and documentation of required bindings
--- 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
@@ -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
@@ -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
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.orgSigned-off-by Zoltan Devai <[email protected]>---
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.dtsiDocumentation/devicetree/bindings/arm/spmp8000.txt new file +3
-0@@ -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 Unitarch/arm/boot/dts/spmp8000-letcool.dts new file +20
-0@@ -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@@ -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>; + }; + }; +};
PATCH v2 5/5 ARM: SPMP8000: Add Kconfig and Makefile entries
In reply to a message not in the archiveThis 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
@@ -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
@@ -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