From bf976b51a32e255523c69022c1f21bf9ad527fc5 Mon Sep 17 00:00:00 2001 From: viresh kumar Date: Thu, 1 Apr 2010 12:30:42 +0100 Subject: ARM: 6012/1: ST SPEAr: Added basic header files for SPEAr platform Reviewed-by: Linus Walleij Signed-off-by: Viresh Kumar Signed-off-by: Shiraz Hashim Signed-off-by: Russell King --- arch/arm/plat-spear/include/plat/debug-macro.S | 38 +++++++++++++++++++++++ arch/arm/plat-spear/include/plat/gpio.h | 24 ++++++++++++++ arch/arm/plat-spear/include/plat/io.h | 22 +++++++++++++ arch/arm/plat-spear/include/plat/memory.h | 20 ++++++++++++ arch/arm/plat-spear/include/plat/system.h | 41 ++++++++++++++++++++++++ arch/arm/plat-spear/include/plat/timex.h | 19 ++++++++++++ arch/arm/plat-spear/include/plat/uncompress.h | 43 ++++++++++++++++++++++++++ arch/arm/plat-spear/include/plat/vmalloc.h | 19 ++++++++++++ 8 files changed, 226 insertions(+) create mode 100644 arch/arm/plat-spear/include/plat/debug-macro.S create mode 100644 arch/arm/plat-spear/include/plat/gpio.h create mode 100644 arch/arm/plat-spear/include/plat/io.h create mode 100644 arch/arm/plat-spear/include/plat/memory.h create mode 100644 arch/arm/plat-spear/include/plat/system.h create mode 100644 arch/arm/plat-spear/include/plat/timex.h create mode 100644 arch/arm/plat-spear/include/plat/uncompress.h create mode 100644 arch/arm/plat-spear/include/plat/vmalloc.h (limited to 'arch/arm/plat-spear') diff --git a/arch/arm/plat-spear/include/plat/debug-macro.S b/arch/arm/plat-spear/include/plat/debug-macro.S new file mode 100644 index 000000000000..1670734b7e51 --- /dev/null +++ b/arch/arm/plat-spear/include/plat/debug-macro.S @@ -0,0 +1,38 @@ +/* + * arch/arm/plat-spear/include/plat/debug-macro.S + * + * Debugging macro include header for spear platform + * + * Copyright (C) 2009 ST Microelectronics + * Viresh Kumar + * + * 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 +#include + + .macro addruart, rx + mrc p15, 0, \rx, c1, c0 + tst \rx, #1 @ MMU enabled? + moveq \rx, =SPEAR_DBG_UART_BASE @ Physical base + movne \rx, =VA_SPEAR_DBG_UART_BASE @ Virtual base + .endm + + .macro senduart, rd, rx + strb \rd, [\rx, #UART01x_DR] @ ASC_TX_BUFFER + .endm + + .macro waituart, rd, rx +1001: ldr \rd, [\rx, #UART01x_FR] @ FLAG REGISTER + tst \rd, #UART01x_FR_TXFF @ TX_FULL + bne 1001b + .endm + + .macro busyuart, rd, rx +1002: ldr \rd, [\rx, #UART01x_FR] @ FLAG REGISTER + tst \rd, #UART011_FR_TXFE @ TX_EMPTY + beq 1002b + .endm diff --git a/arch/arm/plat-spear/include/plat/gpio.h b/arch/arm/plat-spear/include/plat/gpio.h new file mode 100644 index 000000000000..b857c91257dd --- /dev/null +++ b/arch/arm/plat-spear/include/plat/gpio.h @@ -0,0 +1,24 @@ +/* + * arch/arm/plat-spear/include/plat/gpio.h + * + * GPIO macros for SPEAr platform + * + * Copyright (C) 2009 ST Microelectronics + * Viresh Kumar + * + * 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 __PLAT_GPIO_H +#define __PLAT_GPIO_H + +#include + +#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 /* __PLAT_GPIO_H */ diff --git a/arch/arm/plat-spear/include/plat/io.h b/arch/arm/plat-spear/include/plat/io.h new file mode 100644 index 000000000000..4d4ba822b3eb --- /dev/null +++ b/arch/arm/plat-spear/include/plat/io.h @@ -0,0 +1,22 @@ +/* + * arch/arm/plat-spear/include/plat/io.h + * + * IO definitions for SPEAr platform + * + * Copyright (C) 2009 ST Microelectronics + * Viresh Kumar + * + * 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 __PLAT_IO_H +#define __PLAT_IO_H + +#define IO_SPACE_LIMIT 0xFFFFFFFF + +#define __io(a) __typesafe_io(a) +#define __mem_pci(a) (a) + +#endif /* __PLAT_IO_H */ diff --git a/arch/arm/plat-spear/include/plat/memory.h b/arch/arm/plat-spear/include/plat/memory.h new file mode 100644 index 000000000000..27a4aba77343 --- /dev/null +++ b/arch/arm/plat-spear/include/plat/memory.h @@ -0,0 +1,20 @@ +/* + * arch/arm/plat-spear/include/plat/memory.h + * + * Memory map for SPEAr platform + * + * Copyright (C) 2009 ST Microelectronics + * Viresh Kumar + * + * 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 __PLAT_MEMORY_H +#define __PLAT_MEMORY_H + +/* Physical DRAM offset */ +#define PHYS_OFFSET UL(0x00000000) + +#endif /* __PLAT_MEMORY_H */ diff --git a/arch/arm/plat-spear/include/plat/system.h b/arch/arm/plat-spear/include/plat/system.h new file mode 100644 index 000000000000..55a4e405d578 --- /dev/null +++ b/arch/arm/plat-spear/include/plat/system.h @@ -0,0 +1,41 @@ +/* + * arch/arm/plat-spear/include/plat/system.h + * + * SPEAr platform specific architecture functions + * + * Copyright (C) 2009 ST Microelectronics + * Viresh Kumar + * + * 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 __PLAT_SYSTEM_H +#define __PLAT_SYSTEM_H + +#include +#include +#include + +static inline void arch_idle(void) +{ + /* + * This should do all the clock switching + * and wait for interrupt tricks + */ + cpu_do_idle(); +} + +static inline void arch_reset(char mode, const char *cmd) +{ + if (mode == 's') { + /* software reset, Jump into ROM at address 0 */ + cpu_reset(0); + } else { + /* hardware reset, Use on-chip reset capability */ + sysctl_soft_reset((void __iomem *)VA_SPEAR_SYS_CTRL_BASE); + } +} + +#endif /* __PLAT_SYSTEM_H */ diff --git a/arch/arm/plat-spear/include/plat/timex.h b/arch/arm/plat-spear/include/plat/timex.h new file mode 100644 index 000000000000..914d09dd50fd --- /dev/null +++ b/arch/arm/plat-spear/include/plat/timex.h @@ -0,0 +1,19 @@ +/* + * arch/arm/plat-spear/include/plat/timex.h + * + * SPEAr platform specific timex definitions + * + * Copyright (C) 2009 ST Microelectronics + * Viresh Kumar + * + * 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 __PLAT_TIMEX_H +#define __PLAT_TIMEX_H + +#define CLOCK_TICK_RATE 48000000 + +#endif /* __PLAT_TIMEX_H */ diff --git a/arch/arm/plat-spear/include/plat/uncompress.h b/arch/arm/plat-spear/include/plat/uncompress.h new file mode 100644 index 000000000000..99ba6789cc97 --- /dev/null +++ b/arch/arm/plat-spear/include/plat/uncompress.h @@ -0,0 +1,43 @@ +/* + * arch/arm/plat-spear/include/plat/uncompress.h + * + * Serial port stubs for kernel decompress status messages + * + * Copyright (C) 2009 ST Microelectronics + * Viresh Kumar + * + * 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 +#include +#include + +#ifndef __PLAT_UNCOMPRESS_H +#define __PLAT_UNCOMPRESS_H +/* + * This does not append a newline + */ +static inline void putc(int c) +{ + void __iomem *base = (void __iomem *)SPEAR_DBG_UART_BASE; + + while (readl(base + UART01x_FR) & UART01x_FR_TXFF) + barrier(); + + writel(c, base + UART01x_DR); +} + +static inline void flush(void) +{ +} + +/* + * nothing to do + */ +#define arch_decomp_setup() +#define arch_decomp_wdog() + +#endif /* __PLAT_UNCOMPRESS_H */ diff --git a/arch/arm/plat-spear/include/plat/vmalloc.h b/arch/arm/plat-spear/include/plat/vmalloc.h new file mode 100644 index 000000000000..09e9372aea21 --- /dev/null +++ b/arch/arm/plat-spear/include/plat/vmalloc.h @@ -0,0 +1,19 @@ +/* + * arch/arm/plat-spear/include/plat/vmalloc.h + * + * Defining Vmalloc area for SPEAr platform + * + * Copyright (C) 2009 ST Microelectronics + * Viresh Kumar + * + * 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 __PLAT_VMALLOC_H +#define __PLAT_VMALLOC_H + +#define VMALLOC_END 0xF0000000 + +#endif /* __PLAT_VMALLOC_H */ -- cgit v1.2.3 From 986435e3596cbae662b86812e4563fbb6013b994 Mon Sep 17 00:00:00 2001 From: viresh kumar Date: Thu, 1 Apr 2010 12:30:49 +0100 Subject: ARM: 6013/1: ST SPEAr: Added source files for SPEAr platform Reviewed-by: Linus Walleij Signed-off-by: Viresh Kumar Signed-off-by: Shiraz Hashim Signed-off-by: Russell King --- arch/arm/plat-spear/Kconfig | 31 +++++ arch/arm/plat-spear/Makefile | 6 + arch/arm/plat-spear/time.c | 292 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 329 insertions(+) create mode 100644 arch/arm/plat-spear/Kconfig create mode 100644 arch/arm/plat-spear/Makefile create mode 100644 arch/arm/plat-spear/time.c (limited to 'arch/arm/plat-spear') diff --git a/arch/arm/plat-spear/Kconfig b/arch/arm/plat-spear/Kconfig new file mode 100644 index 000000000000..1bb3dbce8810 --- /dev/null +++ b/arch/arm/plat-spear/Kconfig @@ -0,0 +1,31 @@ +# +# SPEAr Platform configuration file +# + +if PLAT_SPEAR + +choice + prompt "ST SPEAr Family" + default ARCH_SPEAR3XX + +config ARCH_SPEAR3XX + bool "SPEAr3XX" + select ARM_VIC + select CPU_ARM926T + help + Supports for ARM's SPEAR3XX family + +config ARCH_SPEAR6XX + bool "SPEAr6XX" + select ARM_VIC + select CPU_ARM926T + help + Supports for ARM's SPEAR6XX family + +endchoice + +# Adding SPEAr machine specific configuration files +source "arch/arm/mach-spear3xx/Kconfig" +source "arch/arm/mach-spear6xx/Kconfig" + +endif diff --git a/arch/arm/plat-spear/Makefile b/arch/arm/plat-spear/Makefile new file mode 100644 index 000000000000..96f9ac3d4b81 --- /dev/null +++ b/arch/arm/plat-spear/Makefile @@ -0,0 +1,6 @@ +# +# SPEAr Platform specific Makefile +# + +# Common support +obj-y := clock.o time.o diff --git a/arch/arm/plat-spear/time.c b/arch/arm/plat-spear/time.c new file mode 100644 index 000000000000..a1025d38f383 --- /dev/null +++ b/arch/arm/plat-spear/time.c @@ -0,0 +1,292 @@ +/* + * arch/arm/plat-spear/time.c + * + * Copyright (C) 2009 ST Microelectronics + * Shiraz Hashim + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * We would use TIMER0 and TIMER1 as clockevent and clocksource. + * Timer0 and Timer1 both belong to same gpt block in cpu subbsystem. Further + * they share same functional clock. Any change in one's functional clock will + * also affect other timer. + */ + +#define CLKEVT 0 /* gpt0, channel0 as clockevent */ +#define CLKSRC 1 /* gpt0, channel1 as clocksource */ + +/* Register offsets, x is channel number */ +#define CR(x) ((x) * 0x80 + 0x80) +#define IR(x) ((x) * 0x80 + 0x84) +#define LOAD(x) ((x) * 0x80 + 0x88) +#define COUNT(x) ((x) * 0x80 + 0x8C) + +/* Reg bit definitions */ +#define CTRL_INT_ENABLE 0x0100 +#define CTRL_ENABLE 0x0020 +#define CTRL_ONE_SHOT 0x0010 + +#define CTRL_PRESCALER1 0x0 +#define CTRL_PRESCALER2 0x1 +#define CTRL_PRESCALER4 0x2 +#define CTRL_PRESCALER8 0x3 +#define CTRL_PRESCALER16 0x4 +#define CTRL_PRESCALER32 0x5 +#define CTRL_PRESCALER64 0x6 +#define CTRL_PRESCALER128 0x7 +#define CTRL_PRESCALER256 0x8 + +#define INT_STATUS 0x1 + +static __iomem void *gpt_base; +static struct clk *gpt_clk; + +static void clockevent_set_mode(enum clock_event_mode mode, + struct clock_event_device *clk_event_dev); +static int clockevent_next_event(unsigned long evt, + struct clock_event_device *clk_event_dev); + +/* + * Following clocksource_set_clock and clockevent_set_clock picked + * from arch/mips/kernel/time.c + */ + +void __init clocksource_set_clock(struct clocksource *cs, unsigned int clock) +{ + u64 temp; + u32 shift; + + /* Find a shift value */ + for (shift = 32; shift > 0; shift--) { + temp = (u64) NSEC_PER_SEC << shift; + do_div(temp, clock); + if ((temp >> 32) == 0) + break; + } + cs->shift = shift; + cs->mult = (u32) temp; +} + +void __init clockevent_set_clock(struct clock_event_device *cd, + unsigned int clock) +{ + u64 temp; + u32 shift; + + /* Find a shift value */ + for (shift = 32; shift > 0; shift--) { + temp = (u64) clock << shift; + do_div(temp, NSEC_PER_SEC); + if ((temp >> 32) == 0) + break; + } + cd->shift = shift; + cd->mult = (u32) temp; +} + +static cycle_t clocksource_read_cycles(struct clocksource *cs) +{ + return (cycle_t) readw(gpt_base + COUNT(CLKSRC)); +} + +static struct clocksource clksrc = { + .name = "tmr1", + .rating = 200, /* its a pretty decent clock */ + .read = clocksource_read_cycles, + .mask = 0xFFFF, /* 16 bits */ + .mult = 0, /* to be computed */ + .shift = 0, /* to be computed */ + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static void spear_clocksource_init(void) +{ + u32 tick_rate; + u16 val; + + /* program the prescaler (/256)*/ + writew(CTRL_PRESCALER256, gpt_base + CR(CLKSRC)); + + /* find out actual clock driving Timer */ + tick_rate = clk_get_rate(gpt_clk); + tick_rate >>= CTRL_PRESCALER256; + + writew(0xFFFF, gpt_base + LOAD(CLKSRC)); + + val = readw(gpt_base + CR(CLKSRC)); + val &= ~CTRL_ONE_SHOT; /* autoreload mode */ + val |= CTRL_ENABLE ; + writew(val, gpt_base + CR(CLKSRC)); + + clocksource_set_clock(&clksrc, tick_rate); + + /* register the clocksource */ + clocksource_register(&clksrc); +} + +static struct clock_event_device clkevt = { + .name = "tmr0", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = clockevent_set_mode, + .set_next_event = clockevent_next_event, + .shift = 0, /* to be computed */ +}; + +static void clockevent_set_mode(enum clock_event_mode mode, + struct clock_event_device *clk_event_dev) +{ + u32 period; + u16 val; + + /* stop the timer */ + val = readw(gpt_base + CR(CLKEVT)); + val &= ~CTRL_ENABLE; + writew(val, gpt_base + CR(CLKEVT)); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + period = clk_get_rate(gpt_clk) / HZ; + period >>= CTRL_PRESCALER16; + writew(period, gpt_base + LOAD(CLKEVT)); + + val = readw(gpt_base + CR(CLKEVT)); + val &= ~CTRL_ONE_SHOT; + val |= CTRL_ENABLE | CTRL_INT_ENABLE; + writew(val, gpt_base + CR(CLKEVT)); + + break; + case CLOCK_EVT_MODE_ONESHOT: + val = readw(gpt_base + CR(CLKEVT)); + val |= CTRL_ONE_SHOT; + writew(val, gpt_base + CR(CLKEVT)); + + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_RESUME: + + break; + default: + pr_err("Invalid mode requested\n"); + break; + } +} + +static int clockevent_next_event(unsigned long cycles, + struct clock_event_device *clk_event_dev) +{ + u16 val; + + writew(cycles, gpt_base + LOAD(CLKEVT)); + + val = readw(gpt_base + CR(CLKEVT)); + val |= CTRL_ENABLE | CTRL_INT_ENABLE; + writew(val, gpt_base + CR(CLKEVT)); + + return 0; +} + +static irqreturn_t spear_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = &clkevt; + + writew(INT_STATUS, gpt_base + IR(CLKEVT)); + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct irqaction spear_timer_irq = { + .name = "timer", + .flags = IRQF_DISABLED | IRQF_TIMER, + .handler = spear_timer_interrupt +}; + +static void __init spear_clockevent_init(void) +{ + u32 tick_rate; + + /* program the prescaler */ + writew(CTRL_PRESCALER16, gpt_base + CR(CLKEVT)); + + tick_rate = clk_get_rate(gpt_clk); + tick_rate >>= CTRL_PRESCALER16; + + clockevent_set_clock(&clkevt, tick_rate); + + clkevt.max_delta_ns = clockevent_delta2ns(0xfff0, + &clkevt); + clkevt.min_delta_ns = clockevent_delta2ns(3, &clkevt); + + clkevt.cpumask = cpumask_of(0); + + clockevents_register_device(&clkevt); + + setup_irq(SPEAR_GPT0_CHAN0_IRQ, &spear_timer_irq); +} + +void __init spear_setup_timer(void) +{ + struct clk *pll3_clk; + + if (!request_mem_region(SPEAR_GPT0_BASE, SZ_1K, "gpt0")) { + pr_err("%s:cannot get IO addr\n", __func__); + return; + } + + gpt_base = (void __iomem *)ioremap(SPEAR_GPT0_BASE, SZ_1K); + if (!gpt_base) { + pr_err("%s:ioremap failed for gpt\n", __func__); + goto err_mem; + } + + gpt_clk = clk_get_sys("gpt0", NULL); + if (!gpt_clk) { + pr_err("%s:couldn't get clk for gpt\n", __func__); + goto err_iomap; + } + + pll3_clk = clk_get(NULL, "pll3_48m_clk"); + if (!pll3_clk) { + pr_err("%s:couldn't get PLL3 as parent for gpt\n", __func__); + goto err_iomap; + } + + clk_set_parent(gpt_clk, pll3_clk); + + spear_clockevent_init(); + spear_clocksource_init(); + + return; + +err_iomap: + iounmap(gpt_base); + +err_mem: + release_mem_region(SPEAR_GPT0_BASE, SZ_1K); +} + +struct sys_timer spear_sys_timer = { + .init = spear_setup_timer, +}; -- cgit v1.2.3 From 8c0236fc465c71d98203bcf5609db01b9cf5f70f Mon Sep 17 00:00:00 2001 From: viresh kumar Date: Thu, 1 Apr 2010 12:30:46 +0100 Subject: ARM: 6014/1: ST SPEAr: Added clock framework for SPEAr platform and machines Clock framework for SPEAr is based upon clkdev framework for ARM Reviewed-by: Linus Walleij Signed-off-by: Viresh Kumar Signed-off-by: Russell King --- arch/arm/mach-spear3xx/clock.c | 389 +++++++++++++++++++ arch/arm/mach-spear3xx/include/mach/clkdev.h | 19 + arch/arm/mach-spear3xx/include/mach/misc_regs.h | 0 arch/arm/mach-spear6xx/clock.c | 483 ++++++++++++++++++++++++ arch/arm/mach-spear6xx/include/mach/clkdev.h | 19 + arch/arm/plat-spear/clock.c | 435 +++++++++++++++++++++ arch/arm/plat-spear/include/plat/clkdev.h | 20 + arch/arm/plat-spear/include/plat/clock.h | 126 +++++++ 8 files changed, 1491 insertions(+) create mode 100644 arch/arm/mach-spear3xx/clock.c create mode 100644 arch/arm/mach-spear3xx/include/mach/clkdev.h mode change 100755 => 100644 arch/arm/mach-spear3xx/include/mach/misc_regs.h create mode 100644 arch/arm/mach-spear6xx/clock.c create mode 100644 arch/arm/mach-spear6xx/include/mach/clkdev.h create mode 100644 arch/arm/plat-spear/clock.c create mode 100644 arch/arm/plat-spear/include/plat/clkdev.h create mode 100644 arch/arm/plat-spear/include/plat/clock.h (limited to 'arch/arm/plat-spear') diff --git a/arch/arm/mach-spear3xx/clock.c b/arch/arm/mach-spear3xx/clock.c new file mode 100644 index 000000000000..39f6ccf22294 --- /dev/null +++ b/arch/arm/mach-spear3xx/clock.c @@ -0,0 +1,389 @@ +/* + * arch/arm/mach-spear3xx/clock.c + * + * SPEAr3xx machines clock framework source file + * + * Copyright (C) 2009 ST Microelectronics + * Viresh Kumar + * + * 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 +#include +#include +#include + +/* root clks */ +/* 32 KHz oscillator clock */ +static struct clk osc_32k_clk = { + .flags = ALWAYS_ENABLED, + .rate = 32000, +}; + +/* 24 MHz oscillator clock */ +static struct clk osc_24m_clk = { + .flags = ALWAYS_ENABLED, + .rate = 24000000, +}; + +/* clock derived from 32 KHz osc clk */ +/* rtc clock */ +static struct clk rtc_clk = { + .pclk = &osc_32k_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = RTC_CLK_ENB, + .recalc = &follow_parent, +}; + +/* clock derived from 24 MHz osc clk */ +/* pll1 configuration structure */ +static struct pll_clk_config pll1_config = { + .mode_reg = PLL1_CTR, + .cfg_reg = PLL1_FRQ, +}; + +/* PLL1 clock */ +static struct clk pll1_clk = { + .pclk = &osc_24m_clk, + .en_reg = PLL1_CTR, + .en_reg_bit = PLL_ENABLE, + .recalc = &pll1_clk_recalc, + .private_data = &pll1_config, +}; + +/* PLL3 48 MHz clock */ +static struct clk pll3_48m_clk = { + .flags = ALWAYS_ENABLED, + .pclk = &osc_24m_clk, + .rate = 48000000, +}; + +/* watch dog timer clock */ +static struct clk wdt_clk = { + .flags = ALWAYS_ENABLED, + .pclk = &osc_24m_clk, + .recalc = &follow_parent, +}; + +/* clock derived from pll1 clk */ +/* cpu clock */ +static struct clk cpu_clk = { + .flags = ALWAYS_ENABLED, + .pclk = &pll1_clk, + .recalc = &follow_parent, +}; + +/* ahb configuration structure */ +static struct bus_clk_config ahb_config = { + .reg = CORE_CLK_CFG, + .mask = PLL_HCLK_RATIO_MASK, + .shift = PLL_HCLK_RATIO_SHIFT, +}; + +/* ahb clock */ +static struct clk ahb_clk = { + .flags = ALWAYS_ENABLED, + .pclk = &pll1_clk, + .recalc = &bus_clk_recalc, + .private_data = &ahb_config, +}; + +/* uart configurations */ +static struct aux_clk_config uart_config = { + .synth_reg = UART_CLK_SYNT, +}; + +/* uart parents */ +static struct pclk_info uart_pclk_info[] = { + { + .pclk = &pll1_clk, + .pclk_mask = AUX_CLK_PLL1_MASK, + .scalable = 1, + }, { + .pclk = &pll3_48m_clk, + .pclk_mask = AUX_CLK_PLL3_MASK, + .scalable = 0, + }, +}; + +/* uart parent select structure */ +static struct pclk_sel uart_pclk_sel = { + .pclk_info = uart_pclk_info, + .pclk_count = ARRAY_SIZE(uart_pclk_info), + .pclk_sel_reg = PERIP_CLK_CFG, + .pclk_sel_mask = UART_CLK_MASK, +}; + +/* uart clock */ +static struct clk uart_clk = { + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = UART_CLK_ENB, + .pclk_sel = &uart_pclk_sel, + .pclk_sel_shift = UART_CLK_SHIFT, + .recalc = &aux_clk_recalc, + .private_data = &uart_config, +}; + +/* firda configurations */ +static struct aux_clk_config firda_config = { + .synth_reg = FIRDA_CLK_SYNT, +}; + +/* firda parents */ +static struct pclk_info firda_pclk_info[] = { + { + .pclk = &pll1_clk, + .pclk_mask = AUX_CLK_PLL1_MASK, + .scalable = 1, + }, { + .pclk = &pll3_48m_clk, + .pclk_mask = AUX_CLK_PLL3_MASK, + .scalable = 0, + }, +}; + +/* firda parent select structure */ +static struct pclk_sel firda_pclk_sel = { + .pclk_info = firda_pclk_info, + .pclk_count = ARRAY_SIZE(firda_pclk_info), + .pclk_sel_reg = PERIP_CLK_CFG, + .pclk_sel_mask = FIRDA_CLK_MASK, +}; + +/* firda clock */ +static struct clk firda_clk = { + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = FIRDA_CLK_ENB, + .pclk_sel = &firda_pclk_sel, + .pclk_sel_shift = FIRDA_CLK_SHIFT, + .recalc = &aux_clk_recalc, + .private_data = &firda_config, +}; + +/* gpt parents */ +static struct pclk_info gpt_pclk_info[] = { + { + .pclk = &pll1_clk, + .pclk_mask = AUX_CLK_PLL1_MASK, + .scalable = 1, + }, { + .pclk = &pll3_48m_clk, + .pclk_mask = AUX_CLK_PLL3_MASK, + .scalable = 0, + }, +}; + +/* gpt parent select structure */ +static struct pclk_sel gpt_pclk_sel = { + .pclk_info = gpt_pclk_info, + .pclk_count = ARRAY_SIZE(gpt_pclk_info), + .pclk_sel_reg = PERIP_CLK_CFG, + .pclk_sel_mask = GPT_CLK_MASK, +}; + +/* gpt0 configurations */ +static struct aux_clk_config gpt0_config = { + .synth_reg = PRSC1_CLK_CFG, +}; + +/* gpt0 timer clock */ +static struct clk gpt0_clk = { + .flags = ALWAYS_ENABLED, + .pclk_sel = &gpt_pclk_sel, + .pclk_sel_shift = GPT0_CLK_SHIFT, + .recalc = &gpt_clk_recalc, + .private_data = &gpt0_config, +}; + +/* gpt1 configurations */ +static struct aux_clk_config gpt1_config = { + .synth_reg = PRSC2_CLK_CFG, +}; + +/* gpt1 timer clock */ +static struct clk gpt1_clk = { + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = GPT1_CLK_ENB, + .pclk_sel = &gpt_pclk_sel, + .pclk_sel_shift = GPT1_CLK_SHIFT, + .recalc = &gpt_clk_recalc, + .private_data = &gpt1_config, +}; + +/* gpt2 configurations */ +static struct aux_clk_config gpt2_config = { + .synth_reg = PRSC3_CLK_CFG, +}; + +/* gpt2 timer clock */ +static struct clk gpt2_clk = { + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = GPT2_CLK_ENB, + .pclk_sel = &gpt_pclk_sel, + .pclk_sel_shift = GPT2_CLK_SHIFT, + .recalc = &gpt_clk_recalc, + .private_data = &gpt2_config, +}; + +/* clock derived from pll3 clk */ +/* usbh clock */ +static struct clk usbh_clk = { + .pclk = &pll3_48m_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = USBH_CLK_ENB, + .recalc = &follow_parent, +}; + +/* usbd clock */ +static struct clk usbd_clk = { + .pclk = &pll3_48m_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = USBD_CLK_ENB, + .recalc = &follow_parent, +}; + +/* clcd clock */ +static struct clk clcd_clk = { + .flags = ALWAYS_ENABLED, + .pclk = &pll3_48m_clk, + .recalc = &follow_parent, +}; + +/* clock derived from ahb clk */ +/* apb configuration structure */ +static struct bus_clk_config apb_config = { + .reg = CORE_CLK_CFG, + .mask = HCLK_PCLK_RATIO_MASK, + .shift = HCLK_PCLK_RATIO_SHIFT, +}; + +/* apb clock */ +static struct clk apb_clk = { + .flags = ALWAYS_ENABLED, + .pclk = &ahb_clk, + .recalc = &bus_clk_recalc, + .private_data = &apb_config, +}; + +/* i2c clock */ +static struct clk i2c_clk = { + .pclk = &ahb_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = I2C_CLK_ENB, + .recalc = &follow_parent, +}; + +/* dma clock */ +static struct clk dma_clk = { + .pclk = &ahb_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = DMA_CLK_ENB, + .recalc = &follow_parent, +}; + +/* jpeg clock */ +static struct clk jpeg_clk = { + .pclk = &ahb_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = JPEG_CLK_ENB, + .recalc = &follow_parent, +}; + +/* gmac clock */ +static struct clk gmac_clk = { + .pclk = &ahb_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = GMAC_CLK_ENB, + .recalc = &follow_parent, +}; + +/* smi clock */ +static struct clk smi_clk = { + .pclk = &ahb_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = SMI_CLK_ENB, + .recalc = &follow_parent, +}; + +/* c3 clock */ +static struct clk c3_clk = { + .pclk = &ahb_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = C3_CLK_ENB, + .recalc = &follow_parent, +}; + +/* clock derived from apb clk */ +/* adc clock */ +static struct clk adc_clk = { + .pclk = &apb_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = ADC_CLK_ENB, + .recalc = &follow_parent, +}; + +/* ssp clock */ +static struct clk ssp_clk = { + .pclk = &apb_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = SSP_CLK_ENB, + .recalc = &follow_parent, +}; + +/* gpio clock */ +static struct clk gpio_clk = { + .pclk = &apb_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = GPIO_CLK_ENB, + .recalc = &follow_parent, +}; + +/* array of all spear 3xx clock lookups */ +static struct clk_lookup spear_clk_lookups[] = { + /* root clks */ + { .con_id = "osc_32k_clk", .clk = &osc_32k_clk}, + { .con_id = "osc_24m_clk", .clk = &osc_24m_clk}, + /* clock derived from 32 KHz osc clk */ + { .dev_id = "rtc", .clk = &rtc_clk}, + /* clock derived from 24 MHz osc clk */ + { .con_id = "pll1_clk", .clk = &pll1_clk}, + { .con_id = "pll3_48m_clk", .clk = &pll3_48m_clk}, + { .dev_id = "wdt", .clk = &wdt_clk}, + /* clock derived from pll1 clk */ + { .con_id = "cpu_clk", .clk = &cpu_clk}, + { .con_id = "ahb_clk", .clk = &ahb_clk}, + { .dev_id = "uart", .clk = &uart_clk}, + { .dev_id = "firda", .clk = &firda_clk}, + { .dev_id = "gpt0", .clk = &gpt0_clk}, + { .dev_id = "gpt1", .clk = &gpt1_clk}, + { .dev_id = "gpt2", .clk = &gpt2_clk}, + /* clock derived from pll3 clk */ + { .dev_id = "usbh", .clk = &usbh_clk}, + { .dev_id = "usbd", .clk = &usbd_clk}, + { .dev_id = "clcd", .clk = &clcd_clk}, + /* clock derived from ahb clk */ + { .con_id = "apb_clk", .clk = &apb_clk}, + { .dev_id = "i2c", .clk = &i2c_clk}, + { .dev_id = "dma", .clk = &dma_clk}, + { .dev_id = "jpeg", .clk = &jpeg_clk}, + { .dev_id = "gmac", .clk = &gmac_clk}, + { .dev_id = "smi", .clk = &smi_clk}, + { .dev_id = "c3", .clk = &c3_clk}, + /* clock derived from apb clk */ + { .dev_id = "adc", .clk = &adc_clk}, + { .dev_id = "ssp", .clk = &ssp_clk}, + { .dev_id = "gpio", .clk = &gpio_clk}, +}; + +void __init clk_init(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(spear_clk_lookups); i++) + clk_register(&spear_clk_lookups[i]); + + recalc_root_clocks(); +} diff --git a/arch/arm/mach-spear3xx/include/mach/clkdev.h b/arch/arm/mach-spear3xx/include/mach/clkdev.h new file mode 100644 index 000000000000..a3d07339d9f1 --- /dev/null +++ b/arch/arm/mach-spear3xx/include/mach/clkdev.h @@ -0,0 +1,19 @@ +/* + * arch/arm/mach-spear3xx/include/mach/clkdev.h + * + * Clock Dev framework definitions for SPEAr3xx machine family + * + * Copyright (C) 2009 ST Microelectronics + * Viresh Kumar + * + * 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_CLKDEV_H +#define __MACH_CLKDEV_H + +#include + +#endif /* __MACH_CLKDEV_H */ diff --git a/arch/arm/mach-spear3xx/include/mach/misc_regs.h b/arch/arm/mach-spear3xx/include/mach/misc_regs.h old mode 100755 new mode 100644 diff --git a/arch/arm/mach-spear6xx/clock.c b/arch/arm/mach-spear6xx/clock.c new file mode 100644 index 000000000000..13e27c769685 --- /dev/null +++ b/arch/arm/mach-spear6xx/clock.c @@ -0,0 +1,483 @@ +/* + * arch/arm/mach-spear6xx/clock.c + * + * SPEAr6xx machines clock framework source file + * + * Copyright (C) 2009 ST Microelectronics + * Viresh Kumar + * + * 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 +#include +#include +#include + +/* root clks */ +/* 32 KHz oscillator clock */ +static struct clk osc_32k_clk = { + .flags = ALWAYS_ENABLED, + .rate = 32000, +}; + +/* 30 MHz oscillator clock */ +static struct clk osc_30m_clk = { + .flags = ALWAYS_ENABLED, + .rate = 30000000, +}; + +/* clock derived from 32 KHz osc clk */ +/* rtc clock */ +static struct clk rtc_clk = { + .pclk = &osc_32k_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = RTC_CLK_ENB, + .recalc = &follow_parent, +}; + +/* clock derived from 30 MHz osc clk */ +/* pll1 configuration structure */ +static struct pll_clk_config pll1_config = { + .mode_reg = PLL1_CTR, + .cfg_reg = PLL1_FRQ, +}; + +/* PLL1 clock */ +static struct clk pll1_clk = { + .pclk = &osc_30m_clk, + .en_reg = PLL1_CTR, + .en_reg_bit = PLL_ENABLE, + .recalc = &pll1_clk_recalc, + .private_data = &pll1_config, +}; + +/* PLL3 48 MHz clock */ +static struct clk pll3_48m_clk = { + .flags = ALWAYS_ENABLED, + .pclk = &osc_30m_clk, + .rate = 48000000, +}; + +/* watch dog timer clock */ +static struct clk wdt_clk = { + .flags = ALWAYS_ENABLED, + .pclk = &osc_30m_clk, + .recalc = &follow_parent, +}; + +/* clock derived from pll1 clk */ +/* cpu clock */ +static struct clk cpu_clk = { + .flags = ALWAYS_ENABLED, + .pclk = &pll1_clk, + .recalc = &follow_parent, +}; + +/* ahb configuration structure */ +static struct bus_clk_config ahb_config = { + .reg = CORE_CLK_CFG, + .mask = PLL_HCLK_RATIO_MASK, + .shift = PLL_HCLK_RATIO_SHIFT, +}; + +/* ahb clock */ +static struct clk ahb_clk = { + .flags = ALWAYS_ENABLED, + .pclk = &pll1_clk, + .recalc = &bus_clk_recalc, + .private_data = &ahb_config, +}; + +/* uart parents */ +static struct pclk_info uart_pclk_info[] = { + { + .pclk = &pll1_clk, + .pclk_mask = AUX_CLK_PLL1_MASK, + .scalable = 1, + }, { + .pclk = &pll3_48m_clk, + .pclk_mask = AUX_CLK_PLL3_MASK, + .scalable = 0, + }, +}; + +/* uart parent select structure */ +static struct pclk_sel uart_pclk_sel = { + .pclk_info = uart_pclk_info, + .pclk_count = ARRAY_SIZE(uart_pclk_info), + .pclk_sel_reg = PERIP_CLK_CFG, + .pclk_sel_mask = UART_CLK_MASK, +}; + +/* uart configurations */ +static struct aux_clk_config uart_config = { + .synth_reg = UART_CLK_SYNT, +}; + +/* uart0 clock */ +static struct clk uart0_clk = { + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = UART0_CLK_ENB, + .pclk_sel = &uart_pclk_sel, + .pclk_sel_shift = UART_CLK_SHIFT, + .recalc = &aux_clk_recalc, + .private_data = &uart_config, +}; + +/* uart1 clock */ +static struct clk uart1_clk = { + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = UART1_CLK_ENB, + .pclk_sel = &uart_pclk_sel, + .pclk_sel_shift = UART_CLK_SHIFT, + .recalc = &aux_clk_recalc, + .private_data = &uart_config, +}; + +/* firda configurations */ +static struct aux_clk_config firda_config = { + .synth_reg = FIRDA_CLK_SYNT, +}; + +/* firda parents */ +static struct pclk_info firda_pclk_info[] = { + { + .pclk = &pll1_clk, + .pclk_mask = AUX_CLK_PLL1_MASK, + .scalable = 1, + }, { + .pclk = &pll3_48m_clk, + .pclk_mask = AUX_CLK_PLL3_MASK, + .scalable = 0, + }, +}; + +/* firda parent select structure */ +static struct pclk_sel firda_pclk_sel = { + .pclk_info = firda_pclk_info, + .pclk_count = ARRAY_SIZE(firda_pclk_info), + .pclk_sel_reg = PERIP_CLK_CFG, + .pclk_sel_mask = FIRDA_CLK_MASK, +}; + +/* firda clock */ +static struct clk firda_clk = { + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = FIRDA_CLK_ENB, + .pclk_sel = &firda_pclk_sel, + .pclk_sel_shift = FIRDA_CLK_SHIFT, + .recalc = &aux_clk_recalc, + .private_data = &firda_config, +}; + +/* clcd configurations */ +static struct aux_clk_config clcd_config = { + .synth_reg = CLCD_CLK_SYNT, +}; + +/* clcd parents */ +static struct pclk_info clcd_pclk_info[] = { + { + .pclk = &pll1_clk, + .pclk_mask = AUX_CLK_PLL1_MASK, + .scalable = 1, + }, { + .pclk = &pll3_48m_clk, + .pclk_mask = AUX_CLK_PLL3_MASK, + .scalable = 0, + }, +}; + +/* clcd parent select structure */ +static struct pclk_sel clcd_pclk_sel = { + .pclk_info = clcd_pclk_info, + .pclk_count = ARRAY_SIZE(clcd_pclk_info), + .pclk_sel_reg = PERIP_CLK_CFG, + .pclk_sel_mask = CLCD_CLK_MASK, +}; + +/* clcd clock */ +static struct clk clcd_clk = { + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = CLCD_CLK_ENB, + .pclk_sel = &clcd_pclk_sel, + .pclk_sel_shift = CLCD_CLK_SHIFT, + .recalc = &aux_clk_recalc, + .private_data = &clcd_config, +}; + +/* gpt parents */ +static struct pclk_info gpt_pclk_info[] = { + { + .pclk = &pll1_clk, + .pclk_mask = AUX_CLK_PLL1_MASK, + .scalable = 1, + }, { + .pclk = &pll3_48m_clk, + .pclk_mask = AUX_CLK_PLL3_MASK, + .scalable = 0, + }, +}; + +/* gpt parent select structure */ +static struct pclk_sel gpt_pclk_sel = { + .pclk_info = gpt_pclk_info, + .pclk_count = ARRAY_SIZE(gpt_pclk_info), + .pclk_sel_reg = PERIP_CLK_CFG, + .pclk_sel_mask = GPT_CLK_MASK, +}; + +/* gpt0_1 configurations */ +static struct aux_clk_config gpt0_1_config = { + .synth_reg = PRSC1_CLK_CFG, +}; + +/* gpt0 ARM1 subsystem timer clock */ +static struct clk gpt0_clk = { + .flags = ALWAYS_ENABLED, + .pclk_sel = &gpt_pclk_sel, + .pclk_sel_shift = GPT0_CLK_SHIFT, + .recalc = &gpt_clk_recalc, + .private_data = &gpt0_1_config, +}; + +/* gpt1 timer clock */ +static struct clk gpt1_clk = { + .flags = ALWAYS_ENABLED, + .pclk_sel = &gpt_pclk_sel, + .pclk_sel_shift = GPT1_CLK_SHIFT, + .recalc = &gpt_clk_recalc, + .private_data = &gpt0_1_config, +}; + +/* gpt2 configurations */ +static struct aux_clk_config gpt2_config = { + .synth_reg = PRSC2_CLK_CFG, +}; + +/* gpt2 timer clock */ +static struct clk gpt2_clk = { + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = GPT2_CLK_ENB, + .pclk_sel = &gpt_pclk_sel, + .pclk_sel_shift = GPT2_CLK_SHIFT, + .recalc = &gpt_clk_recalc, + .private_data = &gpt2_config, +}; + +/* gpt3 configurations */ +static struct aux_clk_config gpt3_config = { + .synth_reg = PRSC3_CLK_CFG, +}; + +/* gpt3 timer clock */ +static struct clk gpt3_clk = { + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = GPT3_CLK_ENB, + .pclk_sel = &gpt_pclk_sel, + .pclk_sel_shift = GPT3_CLK_SHIFT, + .recalc = &gpt_clk_recalc, + .private_data = &gpt3_config, +}; + +/* clock derived from pll3 clk */ +/* usbh0 clock */ +static struct clk usbh0_clk = { + .pclk = &pll3_48m_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = USBH0_CLK_ENB, + .recalc = &follow_parent, +}; + +/* usbh1 clock */ +static struct clk usbh1_clk = { + .pclk = &pll3_48m_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = USBH1_CLK_ENB, + .recalc = &follow_parent, +}; + +/* usbd clock */ +static struct clk usbd_clk = { + .pclk = &pll3_48m_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = USBD_CLK_ENB, + .recalc = &follow_parent, +}; + +/* clock derived from ahb clk */ +/* apb configuration structure */ +static struct bus_clk_config apb_config = { + .reg = CORE_CLK_CFG, + .mask = HCLK_PCLK_RATIO_MASK, + .shift = HCLK_PCLK_RATIO_SHIFT, +}; + +/* apb clock */ +static struct clk apb_clk = { + .flags = ALWAYS_ENABLED, + .pclk = &ahb_clk, + .recalc = &bus_clk_recalc, + .private_data = &apb_config, +}; + +/* i2c clock */ +static struct clk i2c_clk = { + .pclk = &ahb_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = I2C_CLK_ENB, + .recalc = &follow_parent, +}; + +/* dma clock */ +static struct clk dma_clk = { + .pclk = &ahb_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = DMA_CLK_ENB, + .recalc = &follow_parent, +}; + +/* jpeg clock */ +static struct clk jpeg_clk = { + .pclk = &ahb_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = JPEG_CLK_ENB, + .recalc = &follow_parent, +}; + +/* gmac clock */ +static struct clk gmac_clk = { + .pclk = &ahb_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = GMAC_CLK_ENB, + .recalc = &follow_parent, +}; + +/* smi clock */ +static struct clk smi_clk = { + .pclk = &ahb_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = SMI_CLK_ENB, + .recalc = &follow_parent, +}; + +/* fsmc clock */ +static struct clk fsmc_clk = { + .pclk = &ahb_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = FSMC_CLK_ENB, + .recalc = &follow_parent, +}; + +/* clock derived from apb clk */ +/* adc clock */ +static struct clk adc_clk = { + .pclk = &apb_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = ADC_CLK_ENB, + .recalc = &follow_parent, +}; + +/* ssp0 clock */ +static struct clk ssp0_clk = { + .pclk = &apb_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = SSP0_CLK_ENB, + .recalc = &follow_parent, +}; + +/* ssp1 clock */ +static struct clk ssp1_clk = { + .pclk = &apb_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = SSP1_CLK_ENB, + .recalc = &follow_parent, +}; + +/* ssp2 clock */ +static struct clk ssp2_clk = { + .pclk = &apb_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = SSP2_CLK_ENB, + .recalc = &follow_parent, +}; + +/* gpio0 ARM subsystem clock */ +static struct clk gpio0_clk = { + .flags = ALWAYS_ENABLED, + .pclk = &apb_clk, + .recalc = &follow_parent, +}; + +/* gpio1 clock */ +static struct clk gpio1_clk = { + .pclk = &apb_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = GPIO1_CLK_ENB, + .recalc = &follow_parent, +}; + +/* gpio2 clock */ +static struct clk gpio2_clk = { + .pclk = &apb_clk, + .en_reg = PERIP1_CLK_ENB, + .en_reg_bit = GPIO2_CLK_ENB, + .recalc = &follow_parent, +}; + +/* array of all spear 6xx clock lookups */ +static struct clk_lookup spear_clk_lookups[] = { + /* root clks */ + { .con_id = "osc_32k_clk", .clk = &osc_32k_clk}, + { .con_id = "osc_30m_clk", .clk = &osc_30m_clk}, + /* clock derived from 32 KHz os clk */ + { .dev_id = "rtc", .clk = &rtc_clk}, + /* clock derived from 30 MHz os clk */ + { .con_id = "pll1_clk", .clk = &pll1_clk}, + { .con_id = "pll3_48m_clk", .clk = &pll3_48m_clk}, + { .dev_id = "wdt", .clk = &wdt_clk}, + /* clock derived from pll1 clk */ + { .con_id = "cpu_clk", .clk = &cpu_clk}, + { .con_id = "ahb_clk", .clk = &ahb_clk}, + { .dev_id = "uart0", .clk = &uart0_clk}, + { .dev_id = "uart1", .clk = &uart1_clk}, + { .dev_id = "firda", .clk = &firda_clk}, + { .dev_id = "clcd", .clk = &clcd_clk}, + { .dev_id = "gpt0", .clk = &gpt0_clk}, + { .dev_id = "gpt1", .clk = &gpt1_clk}, + { .dev_id = "gpt2", .clk = &gpt2_clk}, + { .dev_id = "gpt3", .clk = &gpt3_clk}, + /* clock derived from pll3 clk */ + { .dev_id = "usbh0", .clk = &usbh0_clk}, + { .dev_id = "usbh1", .clk = &usbh1_clk}, + { .dev_id = "usbd", .clk = &usbd_clk}, + /* clock derived from ahb clk */ + { .con_id = "apb_clk", .clk = &apb_clk}, + { .dev_id = "i2c", .clk = &i2c_clk}, + { .dev_id = "dma", .clk = &dma_clk}, + { .dev_id = "jpeg", .clk = &jpeg_clk}, + { .dev_id = "gmac", .clk = &gmac_clk}, + { .dev_id = "smi", .clk = &smi_clk}, + { .dev_id = "fsmc", .clk = &fsmc_clk}, + /* clock derived from apb clk */ + { .dev_id = "adc", .clk = &adc_clk}, + { .dev_id = "ssp0", .clk = &ssp0_clk}, + { .dev_id = "ssp1", .clk = &ssp1_clk}, + { .dev_id = "ssp2", .clk = &ssp2_clk}, + { .dev_id = "gpio0", .clk = &gpio0_clk}, + { .dev_id = "gpio1", .clk = &gpio1_clk}, + { .dev_id = "gpio2", .clk = &gpio2_clk}, +}; + +void __init clk_init(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(spear_clk_lookups); i++) + clk_register(&spear_clk_lookups[i]); + + recalc_root_clocks(); +} diff --git a/arch/arm/mach-spear6xx/include/mach/clkdev.h b/arch/arm/mach-spear6xx/include/mach/clkdev.h new file mode 100644 index 000000000000..05676bf440d3 --- /dev/null +++ b/arch/arm/mach-spear6xx/include/mach/clkdev.h @@ -0,0 +1,19 @@ +/* + * arch/arm/mach-spear6xx/include/mach/clkdev.h + * + * Clock Dev framework definitions for SPEAr6xx machine family + * + * Copyright (C) 2009 ST Microelectronics + * Viresh Kumar + * + * 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_CLKDEV_H +#define __MACH_CLKDEV_H + +#include + +#endif /* __MACH_CLKDEV_H */ diff --git a/arch/arm/plat-spear/clock.c b/arch/arm/plat-spear/clock.c new file mode 100644 index 000000000000..ee4f90e534d8 --- /dev/null +++ b/arch/arm/plat-spear/clock.c @@ -0,0 +1,435 @@ +/* + * arch/arm/plat-spear/clock.c + * + * Clock framework for SPEAr platform + * + * Copyright (C) 2009 ST Microelectronics + * Viresh Kumar + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_SPINLOCK(clocks_lock); +static LIST_HEAD(root_clks); + +static void propagate_rate(struct list_head *); + +static int generic_clk_enable(struct clk *clk) +{ + unsigned int val; + + if (!clk->en_reg) + return -EFAULT; + + val = readl(clk->en_reg); + if (unlikely(clk->flags & RESET_TO_ENABLE)) + val &= ~(1 << clk->en_reg_bit); + else + val |= 1 << clk->en_reg_bit; + + writel(val, clk->en_reg); + + return 0; +} + +static void generic_clk_disable(struct clk *clk) +{ + unsigned int val; + + if (!clk->en_reg) + return; + + val = readl(clk->en_reg); + if (unlikely(clk->flags & RESET_TO_ENABLE)) + val |= 1 << clk->en_reg_bit; + else + val &= ~(1 << clk->en_reg_bit); + + writel(val, clk->en_reg); +} + +/* generic clk ops */ +static struct clkops generic_clkops = { + .enable = generic_clk_enable, + .disable = generic_clk_disable, +}; + +/* + * clk_enable - inform the system when the clock source should be running. + * @clk: clock source + * + * If the clock can not be enabled/disabled, this should return success. + * + * Returns success (0) or negative errno. + */ +int clk_enable(struct clk *clk) +{ + unsigned long flags; + int ret = 0; + + if (!clk || IS_ERR(clk)) + return -EFAULT; + + spin_lock_irqsave(&clocks_lock, flags); + if (clk->usage_count == 0) { + if (clk->ops && clk->ops->enable) + ret = clk->ops->enable(clk); + } + clk->usage_count++; + spin_unlock_irqrestore(&clocks_lock, flags); + + return ret; +} +EXPORT_SYMBOL(clk_enable); + +/* + * clk_disable - inform the system when the clock source is no longer required. + * @clk: clock source + * + * Inform the system that a clock source is no longer required by + * a driver and may be shut down. + * + * Implementation detail: if the clock source is shared between + * multiple drivers, clk_enable() calls must be balanced by the + * same number of clk_disable() calls for the clock source to be + * disabled. + */ +void clk_disable(struct clk *clk) +{ + unsigned long flags; + + if (!clk || IS_ERR(clk)) + return; + + WARN_ON(clk->usage_count == 0); + + spin_lock_irqsave(&clocks_lock, flags); + clk->usage_count--; + if (clk->usage_count == 0) { + if (clk->ops && clk->ops->disable) + clk->ops->disable(clk); + } + spin_unlock_irqrestore(&clocks_lock, flags); +} +EXPORT_SYMBOL(clk_disable); + +/** + * clk_get_rate - obtain the current clock rate (in Hz) for a clock source. + * This is only valid once the clock source has been enabled. + * @clk: clock source + */ +unsigned long clk_get_rate(struct clk *clk) +{ + unsigned long flags, rate; + + spin_lock_irqsave(&clocks_lock, flags); + rate = clk->rate; + spin_unlock_irqrestore(&clocks_lock, flags); + + return rate; +} +EXPORT_SYMBOL(clk_get_rate); + +/** + * clk_set_parent - set the parent clock source for this clock + * @clk: clock source + * @parent: parent clock source + * + * Returns success (0) or negative errno. + */ +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + int i, found = 0, val = 0; + unsigned long flags; + + if (!clk || IS_ERR(clk) || !parent || IS_ERR(parent)) + return -EFAULT; + if (clk->usage_count) + return -EBUSY; + if (!clk->pclk_sel) + return -EPERM; + if (clk->pclk == parent) + return 0; + + for (i = 0; i < clk->pclk_sel->pclk_count; i++) { + if (clk->pclk_sel->pclk_info[i].pclk == parent) { + found = 1; + break; + } + } + + if (!found) + return -EINVAL; + + spin_lock_irqsave(&clocks_lock, flags); + /* reflect parent change in hardware */ + val = readl(clk->pclk_sel->pclk_sel_reg); + val &= ~(clk->pclk_sel->pclk_sel_mask << clk->pclk_sel_shift); + val |= clk->pclk_sel->pclk_info[i].pclk_mask << clk->pclk_sel_shift; + writel(val, clk->pclk_sel->pclk_sel_reg); + spin_unlock_irqrestore(&clocks_lock, flags); + + /* reflect parent change in software */ + clk->recalc(clk); + propagate_rate(&clk->children); + return 0; +} +EXPORT_SYMBOL(clk_set_parent); + +/* registers clock in platform clock framework */ +void clk_register(struct clk_lookup *cl) +{ + struct clk *clk = cl->clk; + unsigned long flags; + + if (!clk || IS_ERR(clk)) + return; + + spin_lock_irqsave(&clocks_lock, flags); + + INIT_LIST_HEAD(&clk->children); + if (clk->flags & ALWAYS_ENABLED) + clk->ops = NULL; + else if (!clk->ops) + clk->ops = &generic_clkops; + + /* root clock don't have any parents */ + if (!clk->pclk && !clk->pclk_sel) { + list_add(&clk->sibling, &root_clks); + /* add clocks with only one parent to parent's children list */ + } else if (clk->pclk && !clk->pclk_sel) { + list_add(&clk->sibling, &clk->pclk->children); + } else { + /* add clocks with > 1 parent to 1st parent's children list */ + list_add(&clk->sibling, + &clk->pclk_sel->pclk_info[0].pclk->children); + } + spin_unlock_irqrestore(&clocks_lock, flags); + + /* add clock to arm clockdev framework */ + clkdev_add(cl); +} + +/** + * propagate_rate - recalculate and propagate all clocks in list head + * + * Recalculates all root clocks in list head, which if the clock's .recalc is + * set correctly, should also propagate their rates. + */ +static void propagate_rate(struct list_head *lhead) +{ + struct clk *clkp, *_temp; + + list_for_each_entry_safe(clkp, _temp, lhead, sibling) { + if (clkp->recalc) + clkp->recalc(clkp); + propagate_rate(&clkp->children); + } +} + +/* returns current programmed clocks clock info structure */ +static struct pclk_info *pclk_info_get(struct clk *clk) +{ + unsigned int mask, i; + unsigned long flags; + struct pclk_info *info = NULL; + + spin_lock_irqsave(&clocks_lock, flags); + mask = (readl(clk->pclk_sel->pclk_sel_reg) >> clk->pclk_sel_shift) + & clk->pclk_sel->pclk_sel_mask; + + for (i = 0; i < clk->pclk_sel->pclk_count; i++) { + if (clk->pclk_sel->pclk_info[i].pclk_mask == mask) + info = &clk->pclk_sel->pclk_info[i]; + } + spin_unlock_irqrestore(&clocks_lock, flags); + + return info; +} + +/* + * Set pclk as cclk's parent and add clock sibling node to current parents + * children list + */ +static void change_parent(struct clk *cclk, struct clk *pclk) +{ + unsigned long flags; + + spin_lock_irqsave(&clocks_lock, flags); + list_del(&cclk->sibling); + list_add(&cclk->sibling, &pclk->children); + + cclk->pclk = pclk; + spin_unlock_irqrestore(&clocks_lock, flags); +} + +/* + * calculates current programmed rate of pll1 + * + * In normal mode + * rate = (2 * M[15:8] * Fin)/(N * 2^P) + * + * In Dithered mode + * rate = (2 * M[15:0] * Fin)/(256 * N * 2^P) + */ +void pll1_clk_recalc(struct clk *clk) +{ + struct pll_clk_config *config = clk->private_data; + unsigned int num = 2, den = 0, val, mode = 0; + unsigned long flags; + + spin_lock_irqsave(&clocks_lock, flags); + mode = (readl(config->mode_reg) >> PLL_MODE_SHIFT) & + PLL_MODE_MASK; + + val = readl(config->cfg_reg); + /* calculate denominator */ + den = (val >> PLL_DIV_P_SHIFT) & PLL_DIV_P_MASK; + den = 1 << den; + den *= (val >> PLL_DIV_N_SHIFT) & PLL_DIV_N_MASK; + + /* calculate numerator & denominator */ + if (!mode) { + /* Normal mode */ + num *= (val >> PLL_NORM_FDBK_M_SHIFT) & PLL_NORM_FDBK_M_MASK; + } else { + /* Dithered mode */ + num *= (val >> PLL_DITH_FDBK_M_SHIFT) & PLL_DITH_FDBK_M_MASK; + den *= 256; + } + + clk->rate = (((clk->pclk->rate/10000) * num) / den) * 10000; + spin_unlock_irqrestore(&clocks_lock, flags); +} + +/* calculates current programmed rate of ahb or apb bus */ +void bus_clk_recalc(struct clk *clk) +{ + struct bus_clk_config *config = clk->private_data; + unsigned int div; + unsigned long flags; + + spin_lock_irqsave(&clocks_lock, flags); + div = ((readl(config->reg) >> config->shift) & config->mask) + 1; + clk->rate = (unsigned long)clk->pclk->rate / div; + spin_unlock_irqrestore(&clocks_lock, flags); +} + +/* + * calculates current programmed rate of auxiliary synthesizers + * used by: UART, FIRDA + * + * Fout from synthesizer can be given from two equations: + * Fout1 = (Fin * X/Y)/2 + * Fout2 = Fin * X/Y + * + * Selection of eqn 1 or 2 is programmed in register + */ +void aux_clk_recalc(struct clk *clk) +{ + struct aux_clk_config *config = clk->private_data; + struct pclk_info *pclk_info = NULL; + unsigned int num = 1, den = 1, val, eqn; + unsigned long flags; + + /* get current programmed parent */ + pclk_info = pclk_info_get(clk); + if (!pclk_info) { + spin_lock_irqsave(&clocks_lock, flags); + clk->pclk = NULL; + clk->rate = 0; + spin_unlock_irqrestore(&clocks_lock, flags); + return; + } + + change_parent(clk, pclk_info->pclk); + + spin_lock_irqsave(&clocks_lock, flags); + if (pclk_info->scalable) { + val = readl(config->synth_reg); + + eqn = (val >> AUX_EQ_SEL_SHIFT) & AUX_EQ_SEL_MASK; + if (eqn == AUX_EQ1_SEL) + den *= 2; + + /* calculate numerator */ + num = (val >> AUX_XSCALE_SHIFT) & AUX_XSCALE_MASK; + + /* calculate denominator */ + den *= (val >> AUX_YSCALE_SHIFT) & AUX_YSCALE_MASK; + val = (((clk->pclk->rate/10000) * num) / den) * 10000; + } else + val = clk->pclk->rate; + + clk->rate = val; + spin_unlock_irqrestore(&clocks_lock, flags); +} + +/* + * calculates current programmed rate of gpt synthesizers + * Fout from synthesizer can be given from below equations: + * Fout= Fin/((2 ^ (N+1)) * (M+1)) + */ +void gpt_clk_recalc(struct clk *clk) +{ + struct aux_clk_config *config = clk->private_data; + struct pclk_info *pclk_info = NULL; + unsigned int div = 1, val; + unsigned long flags; + + pclk_info = pclk_info_get(clk); + if (!pclk_info) { + spin_lock_irqsave(&clocks_lock, flags); + clk->pclk = NULL; + clk->rate = 0; + spin_unlock_irqrestore(&clocks_lock, flags); + return; + } + + change_parent(clk, pclk_info->pclk); + + spin_lock_irqsave(&clocks_lock, flags); + if (pclk_info->scalable) { + val = readl(config->synth_reg); + div += (val >> GPT_MSCALE_SHIFT) & GPT_MSCALE_MASK; + div *= 1 << (((val >> GPT_NSCALE_SHIFT) & GPT_NSCALE_MASK) + 1); + } + + clk->rate = (unsigned long)clk->pclk->rate / div; + spin_unlock_irqrestore(&clocks_lock, flags); +} + +/* + * Used for clocks that always have same value as the parent clock divided by a + * fixed divisor + */ +void follow_parent(struct clk *clk) +{ + unsigned long flags; + + spin_lock_irqsave(&clocks_lock, flags); + clk->rate = clk->pclk->rate; + spin_unlock_irqrestore(&clocks_lock, flags); +} + +/** + * recalc_root_clocks - recalculate and propagate all root clocks + * + * Recalculates all root clocks (clocks with no parent), which if the + * clock's .recalc is set correctly, should also propagate their rates. + */ +void recalc_root_clocks(void) +{ + propagate_rate(&root_clks); +} diff --git a/arch/arm/plat-spear/include/plat/clkdev.h b/arch/arm/plat-spear/include/plat/clkdev.h new file mode 100644 index 000000000000..a2d0112fcaf7 --- /dev/null +++ b/arch/arm/plat-spear/include/plat/clkdev.h @@ -0,0 +1,20 @@ +/* + * arch/arm/plat-spear/include/plat/clkdev.h + * + * Clock Dev framework definitions for SPEAr platform + * + * Copyright (C) 2009 ST Microelectronics + * Viresh Kumar + * + * 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 __PLAT_CLKDEV_H +#define __PLAT_CLKDEV_H + +#define __clk_get(clk) ({ 1; }) +#define __clk_put(clk) do { } while (0) + +#endif /* __PLAT_CLKDEV_H */ diff --git a/arch/arm/plat-spear/include/plat/clock.h b/arch/arm/plat-spear/include/plat/clock.h new file mode 100644 index 000000000000..298bafc0a52f --- /dev/null +++ b/arch/arm/plat-spear/include/plat/clock.h @@ -0,0 +1,126 @@ +/* + * arch/arm/plat-spear/include/plat/clock.h + * + * Clock framework definitions for SPEAr platform + * + * Copyright (C) 2009 ST Microelectronics + * Viresh Kumar + * + * 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 __PLAT_CLOCK_H +#define __PLAT_CLOCK_H + +#include +#include +#include + +/* clk structure flags */ +#define ALWAYS_ENABLED (1 << 0) /* clock always enabled */ +#define RESET_TO_ENABLE (1 << 1) /* reset register bit to enable clk */ + +/** + * struct clkops - clock operations + * @enable: pointer to clock enable function + * @disable: pointer to clock disable function + */ +struct clkops { + int (*enable) (struct clk *); + void (*disable) (struct clk *); +}; + +/** + * struct pclk_info - parents info + * @pclk: pointer to parent clk + * @pclk_mask: value to be written for selecting this parent + * @scalable: Is parent scalable (1 - YES, 0 - NO) + */ +struct pclk_info { + struct clk *pclk; + u8 pclk_mask; + u8 scalable; +}; + +/** + * struct pclk_sel - parents selection configuration + * @pclk_info: pointer to array of parent clock info + * @pclk_count: number of parents + * @pclk_sel_reg: register for selecting a parent + * @pclk_sel_mask: mask for selecting parent (can be used to clear bits also) + */ +struct pclk_sel { + struct pclk_info *pclk_info; + u8 pclk_count; + unsigned int *pclk_sel_reg; + unsigned int pclk_sel_mask; +}; + +/** + * struct clk - clock structure + * @usage_count: num of users who enabled this clock + * @flags: flags for clock properties + * @rate: programmed clock rate in Hz + * @en_reg: clk enable/disable reg + * @en_reg_bit: clk enable/disable bit + * @ops: clk enable/disable ops - generic_clkops selected if NULL + * @recalc: pointer to clock rate recalculate function + * @pclk: current parent clk + * @pclk_sel: pointer to parent selection structure + * @pclk_sel_shift: register shift for selecting parent of this clock + * @children: list for childrens or this clock + * @sibling: node for list of clocks having same parents + * @private_data: clock specific private data + */ +struct clk { + unsigned int usage_count; + unsigned int flags; + unsigned long rate; + unsigned int *en_reg; + u8 en_reg_bit; + const struct clkops *ops; + void (*recalc) (struct clk *); + + struct clk *pclk; + struct pclk_sel *pclk_sel; + unsigned int pclk_sel_shift; + + struct list_head children; + struct list_head sibling; + void *private_data; +}; + +/* pll configuration structure */ +struct pll_clk_config { + unsigned int *mode_reg; + unsigned int *cfg_reg; +}; + +/* ahb and apb bus configuration structure */ +struct bus_clk_config { + unsigned int *reg; + unsigned int mask; + unsigned int shift; +}; + +/* + * Aux clk configuration structure: applicable to GPT, UART and FIRDA + */ +struct aux_clk_config { + unsigned int *synth_reg; +}; + +/* platform specific clock functions */ +void clk_register(struct clk_lookup *cl); +void recalc_root_clocks(void); + +/* clock recalc functions */ +void follow_parent(struct clk *clk); +void pll1_clk_recalc(struct clk *clk); +void bus_clk_recalc(struct clk *clk); +void gpt_clk_recalc(struct clk *clk); +void aux_clk_recalc(struct clk *clk); + +#endif /* __PLAT_CLOCK_H */ -- cgit v1.2.3 From 70f4c0bf9e4d067744ee453bc37c0c4adcea6e53 Mon Sep 17 00:00:00 2001 From: viresh kumar Date: Thu, 1 Apr 2010 12:31:29 +0100 Subject: ARM: 6020/1: ST SPEAr: Adding gpio pad multiplexing support GPIO Pads in spear platform are are multiplexed in various machines. This patch adds support for this pad multiplexing. Reviewed-by: Linus Walleij Signed-off-by: Viresh Kumar Signed-off-by: Russell King --- arch/arm/mach-spear3xx/include/mach/generic.h | 161 +++++++++- arch/arm/mach-spear3xx/spear300.c | 358 +++++++++++++++++++++ arch/arm/mach-spear3xx/spear300_evb.c | 22 ++ arch/arm/mach-spear3xx/spear310.c | 129 ++++++++ arch/arm/mach-spear3xx/spear310_evb.c | 30 ++ arch/arm/mach-spear3xx/spear320.c | 374 +++++++++++++++++++++ arch/arm/mach-spear3xx/spear320_evb.c | 27 ++ arch/arm/mach-spear3xx/spear3xx.c | 447 ++++++++++++++++++++++++++ arch/arm/plat-spear/Makefile | 2 +- arch/arm/plat-spear/include/plat/padmux.h | 92 ++++++ arch/arm/plat-spear/padmux.c | 164 ++++++++++ 11 files changed, 1798 insertions(+), 8 deletions(-) create mode 100644 arch/arm/plat-spear/include/plat/padmux.h create mode 100644 arch/arm/plat-spear/padmux.c (limited to 'arch/arm/plat-spear') diff --git a/arch/arm/mach-spear3xx/include/mach/generic.h b/arch/arm/mach-spear3xx/include/mach/generic.h index aeea8453a9e2..af7e02c909a3 100644 --- a/arch/arm/mach-spear3xx/include/mach/generic.h +++ b/arch/arm/mach-spear3xx/include/mach/generic.h @@ -19,7 +19,9 @@ #include #include #include +#include +/* spear3xx declarations */ /* * Each GPT has 2 timer channels * Following GPT channels will be used as clock source and clockevent @@ -34,25 +36,170 @@ extern struct amba_device uart_device; extern struct sys_timer spear_sys_timer; /* Add spear3xx family function declarations here */ +void __init clk_init(void); void __init spear3xx_map_io(void); void __init spear3xx_init_irq(void); void __init spear3xx_init(void); -void __init spear300_init(void); -void __init spear310_init(void); -void __init spear320_init(void); -void __init clk_init(void); +void spear_pmx_init(struct pmx_driver *pmx_driver, uint base, uint size); -/* Add spear300 machine device structure declarations here */ +/* pad mux declarations */ +#define PMX_FIRDA_MASK (1 << 14) +#define PMX_I2C_MASK (1 << 13) +#define PMX_SSP_CS_MASK (1 << 12) +#define PMX_SSP_MASK (1 << 11) +#define PMX_MII_MASK (1 << 10) +#define PMX_GPIO_PIN0_MASK (1 << 9) +#define PMX_GPIO_PIN1_MASK (1 << 8) +#define PMX_GPIO_PIN2_MASK (1 << 7) +#define PMX_GPIO_PIN3_MASK (1 << 6) +#define PMX_GPIO_PIN4_MASK (1 << 5) +#define PMX_GPIO_PIN5_MASK (1 << 4) +#define PMX_UART0_MODEM_MASK (1 << 3) +#define PMX_UART0_MASK (1 << 2) +#define PMX_TIMER_3_4_MASK (1 << 1) +#define PMX_TIMER_1_2_MASK (1 << 0) + +/* pad mux devices */ +extern struct pmx_dev pmx_firda; +extern struct pmx_dev pmx_i2c; +extern struct pmx_dev pmx_ssp_cs; +extern struct pmx_dev pmx_ssp; +extern struct pmx_dev pmx_mii; +extern struct pmx_dev pmx_gpio_pin0; +extern struct pmx_dev pmx_gpio_pin1; +extern struct pmx_dev pmx_gpio_pin2; +extern struct pmx_dev pmx_gpio_pin3; +extern struct pmx_dev pmx_gpio_pin4; +extern struct pmx_dev pmx_gpio_pin5; +extern struct pmx_dev pmx_uart0_modem; +extern struct pmx_dev pmx_uart0; +extern struct pmx_dev pmx_timer_3_4; +extern struct pmx_dev pmx_timer_1_2; + +#if defined(CONFIG_MACH_SPEAR310) || defined(CONFIG_MACH_SPEAR320) +/* padmux plgpio devices */ +extern struct pmx_dev pmx_plgpio_0_1; +extern struct pmx_dev pmx_plgpio_2_3; +extern struct pmx_dev pmx_plgpio_4_5; +extern struct pmx_dev pmx_plgpio_6_9; +extern struct pmx_dev pmx_plgpio_10_27; +extern struct pmx_dev pmx_plgpio_28; +extern struct pmx_dev pmx_plgpio_29; +extern struct pmx_dev pmx_plgpio_30; +extern struct pmx_dev pmx_plgpio_31; +extern struct pmx_dev pmx_plgpio_32; +extern struct pmx_dev pmx_plgpio_33; +extern struct pmx_dev pmx_plgpio_34_36; +extern struct pmx_dev pmx_plgpio_37_42; +extern struct pmx_dev pmx_plgpio_43_44_47_48; +extern struct pmx_dev pmx_plgpio_45_46_49_50; +#endif + +extern struct pmx_driver pmx_driver; + +/* spear300 declarations */ #ifdef CONFIG_MACH_SPEAR300 +/* Add spear300 machine device structure declarations here */ extern struct amba_device gpio1_device; + +/* pad mux modes */ +extern struct pmx_mode nand_mode; +extern struct pmx_mode nor_mode; +extern struct pmx_mode photo_frame_mode; +extern struct pmx_mode lend_ip_phone_mode; +extern struct pmx_mode hend_ip_phone_mode; +extern struct pmx_mode lend_wifi_phone_mode; +extern struct pmx_mode hend_wifi_phone_mode; +extern struct pmx_mode ata_pabx_wi2s_mode; +extern struct pmx_mode ata_pabx_i2s_mode; +extern struct pmx_mode caml_lcdw_mode; +extern struct pmx_mode camu_lcd_mode; +extern struct pmx_mode camu_wlcd_mode; +extern struct pmx_mode caml_lcd_mode; + +/* pad mux devices */ +extern struct pmx_dev pmx_fsmc_2_chips; +extern struct pmx_dev pmx_fsmc_4_chips; +extern struct pmx_dev pmx_keyboard; +extern struct pmx_dev pmx_clcd; +extern struct pmx_dev pmx_telecom_gpio; +extern struct pmx_dev pmx_telecom_tdm; +extern struct pmx_dev pmx_telecom_spi_cs_i2c_clk; +extern struct pmx_dev pmx_telecom_camera; +extern struct pmx_dev pmx_telecom_dac; +extern struct pmx_dev pmx_telecom_i2s; +extern struct pmx_dev pmx_telecom_boot_pins; +extern struct pmx_dev pmx_telecom_sdio_4bit; +extern struct pmx_dev pmx_telecom_sdio_8bit; +extern struct pmx_dev pmx_gpio1; + +void spear300_pmx_init(void); + +/* Add spear300 machine function declarations here */ +void __init spear300_init(void); + #endif /* CONFIG_MACH_SPEAR300 */ -/* Add spear310 machine device structure declarations here */ +/* spear310 declarations */ #ifdef CONFIG_MACH_SPEAR310 +/* Add spear310 machine device structure declarations here */ + +/* pad mux devices */ +extern struct pmx_dev pmx_emi_cs_0_1_4_5; +extern struct pmx_dev pmx_emi_cs_2_3; +extern struct pmx_dev pmx_uart1; +extern struct pmx_dev pmx_uart2; +extern struct pmx_dev pmx_uart3_4_5; +extern struct pmx_dev pmx_fsmc; +extern struct pmx_dev pmx_rs485_0_1; +extern struct pmx_dev pmx_tdm0; + +void spear310_pmx_init(void); + +/* Add spear310 machine function declarations here */ +void __init spear310_init(void); + #endif /* CONFIG_MACH_SPEAR310 */ -/* Add spear320 machine device structure declarations here */ +/* spear320 declarations */ #ifdef CONFIG_MACH_SPEAR320 +/* Add spear320 machine device structure declarations here */ + +/* pad mux modes */ +extern struct pmx_mode auto_net_smii_mode; +extern struct pmx_mode auto_net_mii_mode; +extern struct pmx_mode auto_exp_mode; +extern struct pmx_mode small_printers_mode; + +/* pad mux devices */ +extern struct pmx_dev pmx_clcd; +extern struct pmx_dev pmx_emi; +extern struct pmx_dev pmx_fsmc; +extern struct pmx_dev pmx_spp; +extern struct pmx_dev pmx_sdio; +extern struct pmx_dev pmx_i2s; +extern struct pmx_dev pmx_uart1; +extern struct pmx_dev pmx_uart1_modem; +extern struct pmx_dev pmx_uart2; +extern struct pmx_dev pmx_touchscreen; +extern struct pmx_dev pmx_can; +extern struct pmx_dev pmx_sdio_led; +extern struct pmx_dev pmx_pwm0; +extern struct pmx_dev pmx_pwm1; +extern struct pmx_dev pmx_pwm2; +extern struct pmx_dev pmx_pwm3; +extern struct pmx_dev pmx_ssp1; +extern struct pmx_dev pmx_ssp2; +extern struct pmx_dev pmx_mii1; +extern struct pmx_dev pmx_smii0; +extern struct pmx_dev pmx_smii1; +extern struct pmx_dev pmx_i2c1; + +void spear320_pmx_init(void); + +/* Add spear320 machine function declarations here */ +void __init spear320_init(void); + #endif /* CONFIG_MACH_SPEAR320 */ #endif /* __MACH_GENERIC_H */ diff --git a/arch/arm/mach-spear3xx/spear300.c b/arch/arm/mach-spear3xx/spear300.c index 63aca8fc3ebb..66e7fcd8baf8 100644 --- a/arch/arm/mach-spear3xx/spear300.c +++ b/arch/arm/mach-spear3xx/spear300.c @@ -18,6 +18,357 @@ #include #include +/* pad multiplexing support */ +/* muxing registers */ +#define PAD_MUX_CONFIG_REG 0x00 +#define MODE_CONFIG_REG 0x04 + +/* modes */ +#define NAND_MODE (1 << 0) +#define NOR_MODE (1 << 1) +#define PHOTO_FRAME_MODE (1 << 2) +#define LEND_IP_PHONE_MODE (1 << 3) +#define HEND_IP_PHONE_MODE (1 << 4) +#define LEND_WIFI_PHONE_MODE (1 << 5) +#define HEND_WIFI_PHONE_MODE (1 << 6) +#define ATA_PABX_WI2S_MODE (1 << 7) +#define ATA_PABX_I2S_MODE (1 << 8) +#define CAML_LCDW_MODE (1 << 9) +#define CAMU_LCD_MODE (1 << 10) +#define CAMU_WLCD_MODE (1 << 11) +#define CAML_LCD_MODE (1 << 12) +#define ALL_MODES 0x1FFF + +struct pmx_mode nand_mode = { + .id = NAND_MODE, + .name = "nand mode", + .mask = 0x00, +}; + +struct pmx_mode nor_mode = { + .id = NOR_MODE, + .name = "nor mode", + .mask = 0x01, +}; + +struct pmx_mode photo_frame_mode = { + .id = PHOTO_FRAME_MODE, + .name = "photo frame mode", + .mask = 0x02, +}; + +struct pmx_mode lend_ip_phone_mode = { + .id = LEND_IP_PHONE_MODE, + .name = "lend ip phone mode", + .mask = 0x03, +}; + +struct pmx_mode hend_ip_phone_mode = { + .id = HEND_IP_PHONE_MODE, + .name = "hend ip phone mode", + .mask = 0x04, +}; + +struct pmx_mode lend_wifi_phone_mode = { + .id = LEND_WIFI_PHONE_MODE, + .name = "lend wifi phone mode", + .mask = 0x05, +}; + +struct pmx_mode hend_wifi_phone_mode = { + .id = HEND_WIFI_PHONE_MODE, + .name = "hend wifi phone mode", + .mask = 0x06, +}; + +struct pmx_mode ata_pabx_wi2s_mode = { + .id = ATA_PABX_WI2S_MODE, + .name = "ata pabx wi2s mode", + .mask = 0x07, +}; + +struct pmx_mode ata_pabx_i2s_mode = { + .id = ATA_PABX_I2S_MODE, + .name = "ata pabx i2s mode", + .mask = 0x08, +}; + +struct pmx_mode caml_lcdw_mode = { + .id = CAML_LCDW_MODE, + .name = "caml lcdw mode", + .mask = 0x0C, +}; + +struct pmx_mode camu_lcd_mode = { + .id = CAMU_LCD_MODE, + .name = "camu lcd mode", + .mask = 0x0D, +}; + +struct pmx_mode camu_wlcd_mode = { + .id = CAMU_WLCD_MODE, + .name = "camu wlcd mode", + .mask = 0x0E, +}; + +struct pmx_mode caml_lcd_mode = { + .id = CAML_LCD_MODE, + .name = "caml lcd mode", + .mask = 0x0F, +}; + +/* devices */ +struct pmx_dev_mode pmx_fsmc_2_chips_modes[] = { + { + .ids = NAND_MODE | NOR_MODE | PHOTO_FRAME_MODE | + ATA_PABX_WI2S_MODE | ATA_PABX_I2S_MODE, + .mask = PMX_FIRDA_MASK, + }, +}; + +struct pmx_dev pmx_fsmc_2_chips = { + .name = "fsmc_2_chips", + .modes = pmx_fsmc_2_chips_modes, + .mode_count = ARRAY_SIZE(pmx_fsmc_2_chips_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_fsmc_4_chips_modes[] = { + { + .ids = NAND_MODE | NOR_MODE | PHOTO_FRAME_MODE | + ATA_PABX_WI2S_MODE | ATA_PABX_I2S_MODE, + .mask = PMX_FIRDA_MASK | PMX_UART0_MASK, + }, +}; + +struct pmx_dev pmx_fsmc_4_chips = { + .name = "fsmc_4_chips", + .modes = pmx_fsmc_4_chips_modes, + .mode_count = ARRAY_SIZE(pmx_fsmc_4_chips_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_keyboard_modes[] = { + { + .ids = LEND_IP_PHONE_MODE | HEND_IP_PHONE_MODE | + LEND_WIFI_PHONE_MODE | HEND_WIFI_PHONE_MODE | + CAML_LCDW_MODE | CAMU_LCD_MODE | CAMU_WLCD_MODE | + CAML_LCD_MODE, + .mask = 0x0, + }, +}; + +struct pmx_dev pmx_keyboard = { + .name = "keyboard", + .modes = pmx_keyboard_modes, + .mode_count = ARRAY_SIZE(pmx_keyboard_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_clcd_modes[] = { + { + .ids = PHOTO_FRAME_MODE, + .mask = PMX_TIMER_1_2_MASK | PMX_TIMER_3_4_MASK , + }, { + .ids = HEND_IP_PHONE_MODE | HEND_WIFI_PHONE_MODE | + CAMU_LCD_MODE | CAML_LCD_MODE, + .mask = PMX_TIMER_3_4_MASK, + }, +}; + +struct pmx_dev pmx_clcd = { + .name = "clcd", + .modes = pmx_clcd_modes, + .mode_count = ARRAY_SIZE(pmx_clcd_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_telecom_gpio_modes[] = { + { + .ids = PHOTO_FRAME_MODE | CAMU_LCD_MODE | CAML_LCD_MODE, + .mask = PMX_MII_MASK, + }, { + .ids = LEND_IP_PHONE_MODE | LEND_WIFI_PHONE_MODE, + .mask = PMX_MII_MASK | PMX_TIMER_1_2_MASK | PMX_TIMER_3_4_MASK, + }, { + .ids = ATA_PABX_I2S_MODE | CAML_LCDW_MODE | CAMU_WLCD_MODE, + .mask = PMX_MII_MASK | PMX_TIMER_3_4_MASK, + }, { + .ids = HEND_IP_PHONE_MODE | HEND_WIFI_PHONE_MODE, + .mask = PMX_MII_MASK | PMX_TIMER_1_2_MASK, + }, { + .ids = ATA_PABX_WI2S_MODE, + .mask = PMX_MII_MASK | PMX_TIMER_1_2_MASK | PMX_TIMER_3_4_MASK + | PMX_UART0_MODEM_MASK, + }, +}; + +struct pmx_dev pmx_telecom_gpio = { + .name = "telecom_gpio", + .modes = pmx_telecom_gpio_modes, + .mode_count = ARRAY_SIZE(pmx_telecom_gpio_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_telecom_tdm_modes[] = { + { + .ids = PHOTO_FRAME_MODE | LEND_IP_PHONE_MODE | + HEND_IP_PHONE_MODE | LEND_WIFI_PHONE_MODE + | HEND_WIFI_PHONE_MODE | ATA_PABX_WI2S_MODE + | ATA_PABX_I2S_MODE | CAML_LCDW_MODE | CAMU_LCD_MODE + | CAMU_WLCD_MODE | CAML_LCD_MODE, + .mask = PMX_UART0_MODEM_MASK | PMX_SSP_CS_MASK, + }, +}; + +struct pmx_dev pmx_telecom_tdm = { + .name = "telecom_tdm", + .modes = pmx_telecom_tdm_modes, + .mode_count = ARRAY_SIZE(pmx_telecom_tdm_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_telecom_spi_cs_i2c_clk_modes[] = { + { + .ids = LEND_IP_PHONE_MODE | HEND_IP_PHONE_MODE | + LEND_WIFI_PHONE_MODE | HEND_WIFI_PHONE_MODE + | ATA_PABX_WI2S_MODE | ATA_PABX_I2S_MODE | + CAML_LCDW_MODE | CAML_LCD_MODE, + .mask = PMX_TIMER_1_2_MASK | PMX_TIMER_3_4_MASK, + }, +}; + +struct pmx_dev pmx_telecom_spi_cs_i2c_clk = { + .name = "telecom_spi_cs_i2c_clk", + .modes = pmx_telecom_spi_cs_i2c_clk_modes, + .mode_count = ARRAY_SIZE(pmx_telecom_spi_cs_i2c_clk_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_telecom_camera_modes[] = { + { + .ids = CAML_LCDW_MODE | CAML_LCD_MODE, + .mask = PMX_MII_MASK, + }, { + .ids = CAMU_LCD_MODE | CAMU_WLCD_MODE, + .mask = PMX_TIMER_1_2_MASK | PMX_TIMER_3_4_MASK | PMX_MII_MASK, + }, +}; + +struct pmx_dev pmx_telecom_camera = { + .name = "telecom_camera", + .modes = pmx_telecom_camera_modes, + .mode_count = ARRAY_SIZE(pmx_telecom_camera_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_telecom_dac_modes[] = { + { + .ids = ATA_PABX_I2S_MODE | CAML_LCDW_MODE | CAMU_LCD_MODE + | CAMU_WLCD_MODE | CAML_LCD_MODE, + .mask = PMX_TIMER_1_2_MASK, + }, +}; + +struct pmx_dev pmx_telecom_dac = { + .name = "telecom_dac", + .modes = pmx_telecom_dac_modes, + .mode_count = ARRAY_SIZE(pmx_telecom_dac_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_telecom_i2s_modes[] = { + { + .ids = LEND_IP_PHONE_MODE | HEND_IP_PHONE_MODE + | LEND_WIFI_PHONE_MODE | HEND_WIFI_PHONE_MODE | + ATA_PABX_I2S_MODE | CAML_LCDW_MODE | CAMU_LCD_MODE + | CAMU_WLCD_MODE | CAML_LCD_MODE, + .mask = PMX_UART0_MODEM_MASK, + }, +}; + +struct pmx_dev pmx_telecom_i2s = { + .name = "telecom_i2s", + .modes = pmx_telecom_i2s_modes, + .mode_count = ARRAY_SIZE(pmx_telecom_i2s_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_telecom_boot_pins_modes[] = { + { + .ids = NAND_MODE | NOR_MODE, + .mask = PMX_UART0_MODEM_MASK | PMX_TIMER_1_2_MASK | + PMX_TIMER_3_4_MASK, + }, +}; + +struct pmx_dev pmx_telecom_boot_pins = { + .name = "telecom_boot_pins", + .modes = pmx_telecom_boot_pins_modes, + .mode_count = ARRAY_SIZE(pmx_telecom_boot_pins_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_telecom_sdio_4bit_modes[] = { + { + .ids = PHOTO_FRAME_MODE | LEND_IP_PHONE_MODE | + HEND_IP_PHONE_MODE | LEND_WIFI_PHONE_MODE | + HEND_WIFI_PHONE_MODE | CAML_LCDW_MODE | CAMU_LCD_MODE | + CAMU_WLCD_MODE | CAML_LCD_MODE | ATA_PABX_WI2S_MODE | + ATA_PABX_I2S_MODE, + .mask = PMX_GPIO_PIN0_MASK | PMX_GPIO_PIN1_MASK | + PMX_GPIO_PIN2_MASK | PMX_GPIO_PIN3_MASK | + PMX_GPIO_PIN4_MASK | PMX_GPIO_PIN5_MASK, + }, +}; + +struct pmx_dev pmx_telecom_sdio_4bit = { + .name = "telecom_sdio_4bit", + .modes = pmx_telecom_sdio_4bit_modes, + .mode_count = ARRAY_SIZE(pmx_telecom_sdio_4bit_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_telecom_sdio_8bit_modes[] = { + { + .ids = PHOTO_FRAME_MODE | LEND_IP_PHONE_MODE | + HEND_IP_PHONE_MODE | LEND_WIFI_PHONE_MODE | + HEND_WIFI_PHONE_MODE | CAML_LCDW_MODE | CAMU_LCD_MODE | + CAMU_WLCD_MODE | CAML_LCD_MODE, + .mask = PMX_GPIO_PIN0_MASK | PMX_GPIO_PIN1_MASK | + PMX_GPIO_PIN2_MASK | PMX_GPIO_PIN3_MASK | + PMX_GPIO_PIN4_MASK | PMX_GPIO_PIN5_MASK | PMX_MII_MASK, + }, +}; + +struct pmx_dev pmx_telecom_sdio_8bit = { + .name = "telecom_sdio_8bit", + .modes = pmx_telecom_sdio_8bit_modes, + .mode_count = ARRAY_SIZE(pmx_telecom_sdio_8bit_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_gpio1_modes[] = { + { + .ids = PHOTO_FRAME_MODE, + .mask = PMX_UART0_MODEM_MASK | PMX_TIMER_1_2_MASK | + PMX_TIMER_3_4_MASK, + }, +}; + +struct pmx_dev pmx_gpio1 = { + .name = "arm gpio1", + .modes = pmx_gpio1_modes, + .mode_count = ARRAY_SIZE(pmx_gpio1_modes), + .enb_on_reset = 1, +}; + +/* pmx driver structure */ +struct pmx_driver pmx_driver = { + .mode_reg = {.offset = MODE_CONFIG_REG, .mask = 0x0000000f}, + .mux_reg = {.offset = PAD_MUX_CONFIG_REG, .mask = 0x00007fff}, +}; + /* Add spear300 specific devices here */ /* arm gpio1 device registeration */ static struct pl061_platform_data gpio1_plat_data = { @@ -38,8 +389,15 @@ struct amba_device gpio1_device = { .irq = {IRQ_GEN_RAS_1, NO_IRQ}, }; +/* spear300 routines */ void __init spear300_init(void) { /* call spear3xx family common init function */ spear3xx_init(); } + +void spear300_pmx_init(void) +{ + spear_pmx_init(&pmx_driver, SPEAR300_SOC_CONFIG_BASE, + SPEAR300_SOC_CONFIG_SIZE); +} diff --git a/arch/arm/mach-spear3xx/spear300_evb.c b/arch/arm/mach-spear3xx/spear300_evb.c index 1272a385c208..bb21db152a23 100644 --- a/arch/arm/mach-spear3xx/spear300_evb.c +++ b/arch/arm/mach-spear3xx/spear300_evb.c @@ -16,6 +16,22 @@ #include #include +/* padmux devices to enable */ +static struct pmx_dev *pmx_devs[] = { + /* spear3xx specific devices */ + &pmx_i2c, + &pmx_ssp_cs, + &pmx_ssp, + &pmx_mii, + &pmx_uart0, + + /* spear300 specific devices */ + &pmx_fsmc_2_chips, + &pmx_clcd, + &pmx_telecom_sdio_4bit, + &pmx_gpio1, +}; + static struct amba_device *amba_devs[] __initdata = { /* spear3xx specific devices */ &gpio_device, @@ -38,6 +54,12 @@ static void __init spear300_evb_init(void) /* call spear300 machine init function */ spear300_init(); + /* padmux initialization */ + pmx_driver.mode = &photo_frame_mode; + pmx_driver.devs = pmx_devs; + pmx_driver.devs_count = ARRAY_SIZE(pmx_devs); + spear300_pmx_init(); + /* Add Platform Devices */ platform_add_devices(plat_devs, ARRAY_SIZE(plat_devs)); diff --git a/arch/arm/mach-spear3xx/spear310.c b/arch/arm/mach-spear3xx/spear310.c index 6eb62f9de7ee..dd5a57282711 100644 --- a/arch/arm/mach-spear3xx/spear310.c +++ b/arch/arm/mach-spear3xx/spear310.c @@ -16,10 +16,139 @@ #include #include +/* pad multiplexing support */ +/* muxing registers */ +#define PAD_MUX_CONFIG_REG 0x08 + +/* devices */ +struct pmx_dev_mode pmx_emi_cs_0_1_4_5_modes[] = { + { + .ids = 0x00, + .mask = PMX_TIMER_3_4_MASK, + }, +}; + +struct pmx_dev pmx_emi_cs_0_1_4_5 = { + .name = "emi_cs_0_1_4_5", + .modes = pmx_emi_cs_0_1_4_5_modes, + .mode_count = ARRAY_SIZE(pmx_emi_cs_0_1_4_5_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_emi_cs_2_3_modes[] = { + { + .ids = 0x00, + .mask = PMX_TIMER_1_2_MASK, + }, +}; + +struct pmx_dev pmx_emi_cs_2_3 = { + .name = "emi_cs_2_3", + .modes = pmx_emi_cs_2_3_modes, + .mode_count = ARRAY_SIZE(pmx_emi_cs_2_3_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_uart1_modes[] = { + { + .ids = 0x00, + .mask = PMX_FIRDA_MASK, + }, +}; + +struct pmx_dev pmx_uart1 = { + .name = "uart1", + .modes = pmx_uart1_modes, + .mode_count = ARRAY_SIZE(pmx_uart1_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_uart2_modes[] = { + { + .ids = 0x00, + .mask = PMX_TIMER_1_2_MASK, + }, +}; + +struct pmx_dev pmx_uart2 = { + .name = "uart2", + .modes = pmx_uart2_modes, + .mode_count = ARRAY_SIZE(pmx_uart2_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_uart3_4_5_modes[] = { + { + .ids = 0x00, + .mask = PMX_UART0_MODEM_MASK, + }, +}; + +struct pmx_dev pmx_uart3_4_5 = { + .name = "uart3_4_5", + .modes = pmx_uart3_4_5_modes, + .mode_count = ARRAY_SIZE(pmx_uart3_4_5_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_fsmc_modes[] = { + { + .ids = 0x00, + .mask = PMX_SSP_CS_MASK, + }, +}; + +struct pmx_dev pmx_fsmc = { + .name = "fsmc", + .modes = pmx_fsmc_modes, + .mode_count = ARRAY_SIZE(pmx_fsmc_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_rs485_0_1_modes[] = { + { + .ids = 0x00, + .mask = PMX_MII_MASK, + }, +}; + +struct pmx_dev pmx_rs485_0_1 = { + .name = "rs485_0_1", + .modes = pmx_rs485_0_1_modes, + .mode_count = ARRAY_SIZE(pmx_rs485_0_1_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_tdm0_modes[] = { + { + .ids = 0x00, + .mask = PMX_MII_MASK, + }, +}; + +struct pmx_dev pmx_tdm0 = { + .name = "tdm0", + .modes = pmx_tdm0_modes, + .mode_count = ARRAY_SIZE(pmx_tdm0_modes), + .enb_on_reset = 1, +}; + +/* pmx driver structure */ +struct pmx_driver pmx_driver = { + .mux_reg = {.offset = PAD_MUX_CONFIG_REG, .mask = 0x00007fff}, +}; + /* Add spear310 specific devices here */ +/* spear310 routines */ void __init spear310_init(void) { /* call spear3xx family common init function */ spear3xx_init(); } + +void spear310_pmx_init(void) +{ + spear_pmx_init(&pmx_driver, SPEAR310_SOC_CONFIG_BASE, + SPEAR310_SOC_CONFIG_SIZE); +} diff --git a/arch/arm/mach-spear3xx/spear310_evb.c b/arch/arm/mach-spear3xx/spear310_evb.c index e781b2b7f137..7facf6643199 100644 --- a/arch/arm/mach-spear3xx/spear310_evb.c +++ b/arch/arm/mach-spear3xx/spear310_evb.c @@ -16,6 +16,30 @@ #include #include +/* padmux devices to enable */ +static struct pmx_dev *pmx_devs[] = { + /* spear3xx specific devices */ + &pmx_i2c, + &pmx_ssp, + &pmx_gpio_pin0, + &pmx_gpio_pin1, + &pmx_gpio_pin2, + &pmx_gpio_pin3, + &pmx_gpio_pin4, + &pmx_gpio_pin5, + &pmx_uart0, + + /* spear310 specific devices */ + &pmx_emi_cs_0_1_4_5, + &pmx_emi_cs_2_3, + &pmx_uart1, + &pmx_uart2, + &pmx_uart3_4_5, + &pmx_fsmc, + &pmx_rs485_0_1, + &pmx_tdm0, +}; + static struct amba_device *amba_devs[] __initdata = { /* spear3xx specific devices */ &gpio_device, @@ -37,6 +61,12 @@ static void __init spear310_evb_init(void) /* call spear310 machine init function */ spear310_init(); + /* padmux initialization */ + pmx_driver.mode = NULL; + pmx_driver.devs = pmx_devs; + pmx_driver.devs_count = ARRAY_SIZE(pmx_devs); + spear310_pmx_init(); + /* Add Platform Devices */ platform_add_devices(plat_devs, ARRAY_SIZE(plat_devs)); diff --git a/arch/arm/mach-spear3xx/spear320.c b/arch/arm/mach-spear3xx/spear320.c index 72d45489dcb0..2cedf5eb9ec9 100644 --- a/arch/arm/mach-spear3xx/spear320.c +++ b/arch/arm/mach-spear3xx/spear320.c @@ -16,10 +16,384 @@ #include #include +/* pad multiplexing support */ +/* muxing registers */ +#define PAD_MUX_CONFIG_REG 0x0C +#define MODE_CONFIG_REG 0x10 + +/* modes */ +#define AUTO_NET_SMII_MODE (1 << 0) +#define AUTO_NET_MII_MODE (1 << 1) +#define AUTO_EXP_MODE (1 << 2) +#define SMALL_PRINTERS_MODE (1 << 3) +#define ALL_MODES 0xF + +struct pmx_mode auto_net_smii_mode = { + .id = AUTO_NET_SMII_MODE, + .name = "Automation Networking SMII Mode", + .mask = 0x00, +}; + +struct pmx_mode auto_net_mii_mode = { + .id = AUTO_NET_MII_MODE, + .name = "Automation Networking MII Mode", + .mask = 0x01, +}; + +struct pmx_mode auto_exp_mode = { + .id = AUTO_EXP_MODE, + .name = "Automation Expanded Mode", + .mask = 0x02, +}; + +struct pmx_mode small_printers_mode = { + .id = SMALL_PRINTERS_MODE, + .name = "Small Printers Mode", + .mask = 0x03, +}; + +/* devices */ +struct pmx_dev_mode pmx_clcd_modes[] = { + { + .ids = AUTO_NET_SMII_MODE, + .mask = 0x0, + }, +}; + +struct pmx_dev pmx_clcd = { + .name = "clcd", + .modes = pmx_clcd_modes, + .mode_count = ARRAY_SIZE(pmx_clcd_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_emi_modes[] = { + { + .ids = AUTO_EXP_MODE, + .mask = PMX_TIMER_1_2_MASK | PMX_TIMER_3_4_MASK, + }, +}; + +struct pmx_dev pmx_emi = { + .name = "emi", + .modes = pmx_emi_modes, + .mode_count = ARRAY_SIZE(pmx_emi_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_fsmc_modes[] = { + { + .ids = ALL_MODES, + .mask = 0x0, + }, +}; + +struct pmx_dev pmx_fsmc = { + .name = "fsmc", + .modes = pmx_fsmc_modes, + .mode_count = ARRAY_SIZE(pmx_fsmc_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_spp_modes[] = { + { + .ids = SMALL_PRINTERS_MODE, + .mask = 0x0, + }, +}; + +struct pmx_dev pmx_spp = { + .name = "spp", + .modes = pmx_spp_modes, + .mode_count = ARRAY_SIZE(pmx_spp_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_sdio_modes[] = { + { + .ids = AUTO_NET_SMII_MODE | AUTO_NET_MII_MODE | + SMALL_PRINTERS_MODE, + .mask = PMX_TIMER_1_2_MASK | PMX_TIMER_3_4_MASK, + }, +}; + +struct pmx_dev pmx_sdio = { + .name = "sdio", + .modes = pmx_sdio_modes, + .mode_count = ARRAY_SIZE(pmx_sdio_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_i2s_modes[] = { + { + .ids = AUTO_NET_SMII_MODE | AUTO_NET_MII_MODE, + .mask = PMX_UART0_MODEM_MASK, + }, +}; + +struct pmx_dev pmx_i2s = { + .name = "i2s", + .modes = pmx_i2s_modes, + .mode_count = ARRAY_SIZE(pmx_i2s_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_uart1_modes[] = { + { + .ids = ALL_MODES, + .mask = PMX_GPIO_PIN0_MASK | PMX_GPIO_PIN1_MASK, + }, +}; + +struct pmx_dev pmx_uart1 = { + .name = "uart1", + .modes = pmx_uart1_modes, + .mode_count = ARRAY_SIZE(pmx_uart1_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_uart1_modem_modes[] = { + { + .ids = AUTO_EXP_MODE, + .mask = PMX_TIMER_1_2_MASK | PMX_TIMER_3_4_MASK | + PMX_SSP_CS_MASK, + }, { + .ids = SMALL_PRINTERS_MODE, + .mask = PMX_GPIO_PIN3_MASK | PMX_GPIO_PIN4_MASK | + PMX_GPIO_PIN5_MASK | PMX_SSP_CS_MASK, + }, +}; + +struct pmx_dev pmx_uart1_modem = { + .name = "uart1_modem", + .modes = pmx_uart1_modem_modes, + .mode_count = ARRAY_SIZE(pmx_uart1_modem_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_uart2_modes[] = { + { + .ids = ALL_MODES, + .mask = PMX_FIRDA_MASK, + }, +}; + +struct pmx_dev pmx_uart2 = { + .name = "uart2", + .modes = pmx_uart2_modes, + .mode_count = ARRAY_SIZE(pmx_uart2_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_touchscreen_modes[] = { + { + .ids = AUTO_NET_SMII_MODE, + .mask = PMX_SSP_CS_MASK, + }, +}; + +struct pmx_dev pmx_touchscreen = { + .name = "touchscreen", + .modes = pmx_touchscreen_modes, + .mode_count = ARRAY_SIZE(pmx_touchscreen_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_can_modes[] = { + { + .ids = AUTO_NET_SMII_MODE | AUTO_NET_MII_MODE | AUTO_EXP_MODE, + .mask = PMX_GPIO_PIN2_MASK | PMX_GPIO_PIN3_MASK | + PMX_GPIO_PIN4_MASK | PMX_GPIO_PIN5_MASK, + }, +}; + +struct pmx_dev pmx_can = { + .name = "can", + .modes = pmx_can_modes, + .mode_count = ARRAY_SIZE(pmx_can_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_sdio_led_modes[] = { + { + .ids = AUTO_NET_SMII_MODE | AUTO_NET_MII_MODE, + .mask = PMX_SSP_CS_MASK, + }, +}; + +struct pmx_dev pmx_sdio_led = { + .name = "sdio_led", + .modes = pmx_sdio_led_modes, + .mode_count = ARRAY_SIZE(pmx_sdio_led_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_pwm0_modes[] = { + { + .ids = AUTO_NET_SMII_MODE | AUTO_NET_MII_MODE, + .mask = PMX_UART0_MODEM_MASK, + }, { + .ids = AUTO_EXP_MODE | SMALL_PRINTERS_MODE, + .mask = PMX_MII_MASK, + }, +}; + +struct pmx_dev pmx_pwm0 = { + .name = "pwm0", + .modes = pmx_pwm0_modes, + .mode_count = ARRAY_SIZE(pmx_pwm0_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_pwm1_modes[] = { + { + .ids = AUTO_NET_SMII_MODE | AUTO_NET_MII_MODE, + .mask = PMX_UART0_MODEM_MASK, + }, { + .ids = AUTO_EXP_MODE | SMALL_PRINTERS_MODE, + .mask = PMX_MII_MASK, + }, +}; + +struct pmx_dev pmx_pwm1 = { + .name = "pwm1", + .modes = pmx_pwm1_modes, + .mode_count = ARRAY_SIZE(pmx_pwm1_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_pwm2_modes[] = { + { + .ids = AUTO_NET_SMII_MODE | AUTO_NET_MII_MODE, + .mask = PMX_SSP_CS_MASK, + }, { + .ids = AUTO_EXP_MODE | SMALL_PRINTERS_MODE, + .mask = PMX_MII_MASK, + }, +}; + +struct pmx_dev pmx_pwm2 = { + .name = "pwm2", + .modes = pmx_pwm2_modes, + .mode_count = ARRAY_SIZE(pmx_pwm2_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_pwm3_modes[] = { + { + .ids = AUTO_EXP_MODE | SMALL_PRINTERS_MODE | AUTO_NET_SMII_MODE, + .mask = PMX_MII_MASK, + }, +}; + +struct pmx_dev pmx_pwm3 = { + .name = "pwm3", + .modes = pmx_pwm3_modes, + .mode_count = ARRAY_SIZE(pmx_pwm3_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_ssp1_modes[] = { + { + .ids = SMALL_PRINTERS_MODE | AUTO_NET_SMII_MODE, + .mask = PMX_MII_MASK, + }, +}; + +struct pmx_dev pmx_ssp1 = { + .name = "ssp1", + .modes = pmx_ssp1_modes, + .mode_count = ARRAY_SIZE(pmx_ssp1_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_ssp2_modes[] = { + { + .ids = AUTO_NET_SMII_MODE, + .mask = PMX_MII_MASK, + }, +}; + +struct pmx_dev pmx_ssp2 = { + .name = "ssp2", + .modes = pmx_ssp2_modes, + .mode_count = ARRAY_SIZE(pmx_ssp2_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_mii1_modes[] = { + { + .ids = AUTO_NET_MII_MODE, + .mask = 0x0, + }, +}; + +struct pmx_dev pmx_mii1 = { + .name = "mii1", + .modes = pmx_mii1_modes, + .mode_count = ARRAY_SIZE(pmx_mii1_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_smii0_modes[] = { + { + .ids = AUTO_NET_SMII_MODE | AUTO_EXP_MODE | SMALL_PRINTERS_MODE, + .mask = PMX_MII_MASK, + }, +}; + +struct pmx_dev pmx_smii0 = { + .name = "smii0", + .modes = pmx_smii0_modes, + .mode_count = ARRAY_SIZE(pmx_smii0_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_smii1_modes[] = { + { + .ids = AUTO_NET_SMII_MODE | SMALL_PRINTERS_MODE, + .mask = PMX_MII_MASK, + }, +}; + +struct pmx_dev pmx_smii1 = { + .name = "smii1", + .modes = pmx_smii1_modes, + .mode_count = ARRAY_SIZE(pmx_smii1_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_i2c1_modes[] = { + { + .ids = AUTO_EXP_MODE, + .mask = 0x0, + }, +}; + +struct pmx_dev pmx_i2c1 = { + .name = "i2c1", + .modes = pmx_i2c1_modes, + .mode_count = ARRAY_SIZE(pmx_i2c1_modes), + .enb_on_reset = 1, +}; + +/* pmx driver structure */ +struct pmx_driver pmx_driver = { + .mode_reg = {.offset = MODE_CONFIG_REG, .mask = 0x00000007}, + .mux_reg = {.offset = PAD_MUX_CONFIG_REG, .mask = 0x00007fff}, +}; + /* Add spear320 specific devices here */ +/* spear320 routines */ void __init spear320_init(void) { /* call spear3xx family common init function */ spear3xx_init(); } + +void spear320_pmx_init(void) +{ + spear_pmx_init(&pmx_driver, SPEAR320_SOC_CONFIG_BASE, + SPEAR320_SOC_CONFIG_SIZE); +} diff --git a/arch/arm/mach-spear3xx/spear320_evb.c b/arch/arm/mach-spear3xx/spear320_evb.c index 85bc4d226212..62ac685a4135 100644 --- a/arch/arm/mach-spear3xx/spear320_evb.c +++ b/arch/arm/mach-spear3xx/spear320_evb.c @@ -16,6 +16,27 @@ #include #include +/* padmux devices to enable */ +static struct pmx_dev *pmx_devs[] = { + /* spear3xx specific devices */ + &pmx_i2c, + &pmx_ssp, + &pmx_mii, + &pmx_uart0, + + /* spear320 specific devices */ + &pmx_fsmc, + &pmx_sdio, + &pmx_i2s, + &pmx_uart1, + &pmx_uart2, + &pmx_can, + &pmx_pwm0, + &pmx_pwm1, + &pmx_pwm2, + &pmx_mii1, +}; + static struct amba_device *amba_devs[] __initdata = { /* spear3xx specific devices */ &gpio_device, @@ -37,6 +58,12 @@ static void __init spear320_evb_init(void) /* call spear320 machine init function */ spear320_init(); + /* padmux initialization */ + pmx_driver.mode = &auto_net_mii_mode; + pmx_driver.devs = pmx_devs; + pmx_driver.devs_count = ARRAY_SIZE(pmx_devs); + spear320_pmx_init(); + /* Add Platform Devices */ platform_add_devices(plat_devs, ARRAY_SIZE(plat_devs)); diff --git a/arch/arm/mach-spear3xx/spear3xx.c b/arch/arm/mach-spear3xx/spear3xx.c index 82ebcd30465e..e87313aeae20 100644 --- a/arch/arm/mach-spear3xx/spear3xx.c +++ b/arch/arm/mach-spear3xx/spear3xx.c @@ -99,3 +99,450 @@ void __init spear3xx_map_io(void) /* This will initialize clock framework */ clk_init(); } + +/* pad multiplexing support */ +/* devices */ +struct pmx_dev_mode pmx_firda_modes[] = { + { + .ids = 0xffffffff, + .mask = PMX_FIRDA_MASK, + }, +}; + +struct pmx_dev pmx_firda = { + .name = "firda", + .modes = pmx_firda_modes, + .mode_count = ARRAY_SIZE(pmx_firda_modes), + .enb_on_reset = 0, +}; + +struct pmx_dev_mode pmx_i2c_modes[] = { + { + .ids = 0xffffffff, + .mask = PMX_I2C_MASK, + }, +}; + +struct pmx_dev pmx_i2c = { + .name = "i2c", + .modes = pmx_i2c_modes, + .mode_count = ARRAY_SIZE(pmx_i2c_modes), + .enb_on_reset = 0, +}; + +struct pmx_dev_mode pmx_ssp_cs_modes[] = { + { + .ids = 0xffffffff, + .mask = PMX_SSP_CS_MASK, + }, +}; + +struct pmx_dev pmx_ssp_cs = { + .name = "ssp_chip_selects", + .modes = pmx_ssp_cs_modes, + .mode_count = ARRAY_SIZE(pmx_ssp_cs_modes), + .enb_on_reset = 0, +}; + +struct pmx_dev_mode pmx_ssp_modes[] = { + { + .ids = 0xffffffff, + .mask = PMX_SSP_MASK, + }, +}; + +struct pmx_dev pmx_ssp = { + .name = "ssp", + .modes = pmx_ssp_modes, + .mode_count = ARRAY_SIZE(pmx_ssp_modes), + .enb_on_reset = 0, +}; + +struct pmx_dev_mode pmx_mii_modes[] = { + { + .ids = 0xffffffff, + .mask = PMX_MII_MASK, + }, +}; + +struct pmx_dev pmx_mii = { + .name = "mii", + .modes = pmx_mii_modes, + .mode_count = ARRAY_SIZE(pmx_mii_modes), + .enb_on_reset = 0, +}; + +struct pmx_dev_mode pmx_gpio_pin0_modes[] = { + { + .ids = 0xffffffff, + .mask = PMX_GPIO_PIN0_MASK, + }, +}; + +struct pmx_dev pmx_gpio_pin0 = { + .name = "gpio_pin0", + .modes = pmx_gpio_pin0_modes, + .mode_count = ARRAY_SIZE(pmx_gpio_pin0_modes), + .enb_on_reset = 0, +}; + +struct pmx_dev_mode pmx_gpio_pin1_modes[] = { + { + .ids = 0xffffffff, + .mask = PMX_GPIO_PIN1_MASK, + }, +}; + +struct pmx_dev pmx_gpio_pin1 = { + .name = "gpio_pin1", + .modes = pmx_gpio_pin1_modes, + .mode_count = ARRAY_SIZE(pmx_gpio_pin1_modes), + .enb_on_reset = 0, +}; + +struct pmx_dev_mode pmx_gpio_pin2_modes[] = { + { + .ids = 0xffffffff, + .mask = PMX_GPIO_PIN2_MASK, + }, +}; + +struct pmx_dev pmx_gpio_pin2 = { + .name = "gpio_pin2", + .modes = pmx_gpio_pin2_modes, + .mode_count = ARRAY_SIZE(pmx_gpio_pin2_modes), + .enb_on_reset = 0, +}; + +struct pmx_dev_mode pmx_gpio_pin3_modes[] = { + { + .ids = 0xffffffff, + .mask = PMX_GPIO_PIN3_MASK, + }, +}; + +struct pmx_dev pmx_gpio_pin3 = { + .name = "gpio_pin3", + .modes = pmx_gpio_pin3_modes, + .mode_count = ARRAY_SIZE(pmx_gpio_pin3_modes), + .enb_on_reset = 0, +}; + +struct pmx_dev_mode pmx_gpio_pin4_modes[] = { + { + .ids = 0xffffffff, + .mask = PMX_GPIO_PIN4_MASK, + }, +}; + +struct pmx_dev pmx_gpio_pin4 = { + .name = "gpio_pin4", + .modes = pmx_gpio_pin4_modes, + .mode_count = ARRAY_SIZE(pmx_gpio_pin4_modes), + .enb_on_reset = 0, +}; + +struct pmx_dev_mode pmx_gpio_pin5_modes[] = { + { + .ids = 0xffffffff, + .mask = PMX_GPIO_PIN5_MASK, + }, +}; + +struct pmx_dev pmx_gpio_pin5 = { + .name = "gpio_pin5", + .modes = pmx_gpio_pin5_modes, + .mode_count = ARRAY_SIZE(pmx_gpio_pin5_modes), + .enb_on_reset = 0, +}; + +struct pmx_dev_mode pmx_uart0_modem_modes[] = { + { + .ids = 0xffffffff, + .mask = PMX_UART0_MODEM_MASK, + }, +}; + +struct pmx_dev pmx_uart0_modem = { + .name = "uart0_modem", + .modes = pmx_uart0_modem_modes, + .mode_count = ARRAY_SIZE(pmx_uart0_modem_modes), + .enb_on_reset = 0, +}; + +struct pmx_dev_mode pmx_uart0_modes[] = { + { + .ids = 0xffffffff, + .mask = PMX_UART0_MASK, + }, +}; + +struct pmx_dev pmx_uart0 = { + .name = "uart0", + .modes = pmx_uart0_modes, + .mode_count = ARRAY_SIZE(pmx_uart0_modes), + .enb_on_reset = 0, +}; + +struct pmx_dev_mode pmx_timer_3_4_modes[] = { + { + .ids = 0xffffffff, + .mask = PMX_TIMER_3_4_MASK, + }, +}; + +struct pmx_dev pmx_timer_3_4 = { + .name = "timer_3_4", + .modes = pmx_timer_3_4_modes, + .mode_count = ARRAY_SIZE(pmx_timer_3_4_modes), + .enb_on_reset = 0, +}; + +struct pmx_dev_mode pmx_timer_1_2_modes[] = { + { + .ids = 0xffffffff, + .mask = PMX_TIMER_1_2_MASK, + }, +}; + +struct pmx_dev pmx_timer_1_2 = { + .name = "timer_1_2", + .modes = pmx_timer_1_2_modes, + .mode_count = ARRAY_SIZE(pmx_timer_1_2_modes), + .enb_on_reset = 0, +}; + +#if defined(CONFIG_MACH_SPEAR310) || defined(CONFIG_MACH_SPEAR320) +/* plgpios devices */ +struct pmx_dev_mode pmx_plgpio_0_1_modes[] = { + { + .ids = 0x00, + .mask = PMX_FIRDA_MASK, + }, +}; + +struct pmx_dev pmx_plgpio_0_1 = { + .name = "plgpio 0 and 1", + .modes = pmx_plgpio_0_1_modes, + .mode_count = ARRAY_SIZE(pmx_plgpio_0_1_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_plgpio_2_3_modes[] = { + { + .ids = 0x00, + .mask = PMX_UART0_MASK, + }, +}; + +struct pmx_dev pmx_plgpio_2_3 = { + .name = "plgpio 2 and 3", + .modes = pmx_plgpio_2_3_modes, + .mode_count = ARRAY_SIZE(pmx_plgpio_2_3_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_plgpio_4_5_modes[] = { + { + .ids = 0x00, + .mask = PMX_I2C_MASK, + }, +}; + +struct pmx_dev pmx_plgpio_4_5 = { + .name = "plgpio 4 and 5", + .modes = pmx_plgpio_4_5_modes, + .mode_count = ARRAY_SIZE(pmx_plgpio_4_5_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_plgpio_6_9_modes[] = { + { + .ids = 0x00, + .mask = PMX_SSP_MASK, + }, +}; + +struct pmx_dev pmx_plgpio_6_9 = { + .name = "plgpio 6 to 9", + .modes = pmx_plgpio_6_9_modes, + .mode_count = ARRAY_SIZE(pmx_plgpio_6_9_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_plgpio_10_27_modes[] = { + { + .ids = 0x00, + .mask = PMX_MII_MASK, + }, +}; + +struct pmx_dev pmx_plgpio_10_27 = { + .name = "plgpio 10 to 27", + .modes = pmx_plgpio_10_27_modes, + .mode_count = ARRAY_SIZE(pmx_plgpio_10_27_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_plgpio_28_modes[] = { + { + .ids = 0x00, + .mask = PMX_GPIO_PIN0_MASK, + }, +}; + +struct pmx_dev pmx_plgpio_28 = { + .name = "plgpio 28", + .modes = pmx_plgpio_28_modes, + .mode_count = ARRAY_SIZE(pmx_plgpio_28_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_plgpio_29_modes[] = { + { + .ids = 0x00, + .mask = PMX_GPIO_PIN1_MASK, + }, +}; + +struct pmx_dev pmx_plgpio_29 = { + .name = "plgpio 29", + .modes = pmx_plgpio_29_modes, + .mode_count = ARRAY_SIZE(pmx_plgpio_29_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_plgpio_30_modes[] = { + { + .ids = 0x00, + .mask = PMX_GPIO_PIN2_MASK, + }, +}; + +struct pmx_dev pmx_plgpio_30 = { + .name = "plgpio 30", + .modes = pmx_plgpio_30_modes, + .mode_count = ARRAY_SIZE(pmx_plgpio_30_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_plgpio_31_modes[] = { + { + .ids = 0x00, + .mask = PMX_GPIO_PIN3_MASK, + }, +}; + +struct pmx_dev pmx_plgpio_31 = { + .name = "plgpio 31", + .modes = pmx_plgpio_31_modes, + .mode_count = ARRAY_SIZE(pmx_plgpio_31_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_plgpio_32_modes[] = { + { + .ids = 0x00, + .mask = PMX_GPIO_PIN4_MASK, + }, +}; + +struct pmx_dev pmx_plgpio_32 = { + .name = "plgpio 32", + .modes = pmx_plgpio_32_modes, + .mode_count = ARRAY_SIZE(pmx_plgpio_32_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_plgpio_33_modes[] = { + { + .ids = 0x00, + .mask = PMX_GPIO_PIN5_MASK, + }, +}; + +struct pmx_dev pmx_plgpio_33 = { + .name = "plgpio 33", + .modes = pmx_plgpio_33_modes, + .mode_count = ARRAY_SIZE(pmx_plgpio_33_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_plgpio_34_36_modes[] = { + { + .ids = 0x00, + .mask = PMX_SSP_CS_MASK, + }, +}; + +struct pmx_dev pmx_plgpio_34_36 = { + .name = "plgpio 34 to 36", + .modes = pmx_plgpio_34_36_modes, + .mode_count = ARRAY_SIZE(pmx_plgpio_34_36_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_plgpio_37_42_modes[] = { + { + .ids = 0x00, + .mask = PMX_UART0_MODEM_MASK, + }, +}; + +struct pmx_dev pmx_plgpio_37_42 = { + .name = "plgpio 37 to 42", + .modes = pmx_plgpio_37_42_modes, + .mode_count = ARRAY_SIZE(pmx_plgpio_37_42_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_plgpio_43_44_47_48_modes[] = { + { + .ids = 0x00, + .mask = PMX_TIMER_1_2_MASK, + }, +}; + +struct pmx_dev pmx_plgpio_43_44_47_48 = { + .name = "plgpio 43, 44, 47 and 48", + .modes = pmx_plgpio_43_44_47_48_modes, + .mode_count = ARRAY_SIZE(pmx_plgpio_43_44_47_48_modes), + .enb_on_reset = 1, +}; + +struct pmx_dev_mode pmx_plgpio_45_46_49_50_modes[] = { + { + .ids = 0x00, + .mask = PMX_TIMER_3_4_MASK, + }, +}; + +struct pmx_dev pmx_plgpio_45_46_49_50 = { + .name = "plgpio 45, 46, 49 and 50", + .modes = pmx_plgpio_45_46_49_50_modes, + .mode_count = ARRAY_SIZE(pmx_plgpio_45_46_49_50_modes), + .enb_on_reset = 1, +}; + +#endif + +/* spear padmux initialization function */ +void spear_pmx_init(struct pmx_driver *pmx_driver, uint base, uint size) +{ + int ret = 0; + + /* pad mux initialization */ + pmx_driver->base = ioremap(base, size); + if (!pmx_driver->base) { + ret = -ENOMEM; + goto pmx_fail; + } + + ret = pmx_register(pmx_driver); + iounmap(pmx_driver->base); + +pmx_fail: + if (ret) + printk(KERN_ERR "padmux: registeration failed. err no: %d\n", + ret); +} diff --git a/arch/arm/plat-spear/Makefile b/arch/arm/plat-spear/Makefile index 96f9ac3d4b81..6f4ad5e9462e 100644 --- a/arch/arm/plat-spear/Makefile +++ b/arch/arm/plat-spear/Makefile @@ -3,4 +3,4 @@ # # Common support -obj-y := clock.o time.o +obj-y := clock.o padmux.o time.o diff --git a/arch/arm/plat-spear/include/plat/padmux.h b/arch/arm/plat-spear/include/plat/padmux.h new file mode 100644 index 000000000000..877f3adcf610 --- /dev/null +++ b/arch/arm/plat-spear/include/plat/padmux.h @@ -0,0 +1,92 @@ +/* + * arch/arm/plat-spear/include/plat/padmux.h + * + * SPEAr platform specific gpio pads muxing file + * + * Copyright (C) 2009 ST Microelectronics + * Viresh Kumar + * + * 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 __PLAT_PADMUX_H +#define __PLAT_PADMUX_H + +#include + +/* + * struct pmx_reg: configuration structure for mode reg and mux reg + * + * offset: offset of mode reg + * mask: mask of mode reg + */ +struct pmx_reg { + u32 offset; + u32 mask; +}; + +/* + * struct pmx_dev_mode: configuration structure every group of modes of a device + * + * ids: all modes for this configuration + * mask: mask for supported mode + */ +struct pmx_dev_mode { + u32 ids; + u32 mask; +}; + +/* + * struct pmx_mode: mode definition structure + * + * name: mode name + * mask: mode mask + */ +struct pmx_mode { + char *name; + u32 id; + u32 mask; +}; + +/* + * struct pmx_dev: device definition structure + * + * name: device name + * modes: device configuration array for different modes supported + * mode_count: size of modes array + * is_active: is peripheral active/enabled + * enb_on_reset: if 1, mask bits to be cleared in reg otherwise to be set in reg + */ +struct pmx_dev { + char *name; + struct pmx_dev_mode *modes; + u8 mode_count; + bool is_active; + bool enb_on_reset; +}; + +/* + * struct pmx_driver: driver definition structure + * + * mode: mode to be set + * devs: array of pointer to pmx devices + * devs_count: ARRAY_SIZE of devs + * base: base address of soc config registers + * mode_reg: structure of mode config register + * mux_reg: structure of device mux config register + */ +struct pmx_driver { + struct pmx_mode *mode; + struct pmx_dev **devs; + u8 devs_count; + u32 *base; + struct pmx_reg mode_reg; + struct pmx_reg mux_reg; +}; + +/* pmx functions */ +int pmx_register(struct pmx_driver *driver); + +#endif /* __PLAT_PADMUX_H */ diff --git a/arch/arm/plat-spear/padmux.c b/arch/arm/plat-spear/padmux.c new file mode 100644 index 000000000000..d2aab3adcdeb --- /dev/null +++ b/arch/arm/plat-spear/padmux.c @@ -0,0 +1,164 @@ +/* + * arch/arm/plat-spear/include/plat/padmux.c + * + * SPEAr platform specific gpio pads muxing source file + * + * Copyright (C) 2009 ST Microelectronics + * Viresh Kumar + * + * 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 +#include +#include +#include + +/* + * struct pmx: pmx definition structure + * + * base: base address of configuration registers + * mode_reg: mode configurations + * mux_reg: muxing configurations + * active_mode: pointer to current active mode + */ +struct pmx { + u32 base; + struct pmx_reg mode_reg; + struct pmx_reg mux_reg; + struct pmx_mode *active_mode; +}; + +static struct pmx *pmx; + +/** + * pmx_mode_set - Enables an multiplexing mode + * @mode - pointer to pmx mode + * + * It will set mode of operation in hardware. + * Returns -ve on Err otherwise 0 + */ +static int pmx_mode_set(struct pmx_mode *mode) +{ + u32 val; + + if (!mode->name) + return -EFAULT; + + pmx->active_mode = mode; + + val = readl(pmx->base + pmx->mode_reg.offset); + val &= ~pmx->mode_reg.mask; + val |= mode->mask & pmx->mode_reg.mask; + writel(val, pmx->base + pmx->mode_reg.offset); + + return 0; +} + +/** + * pmx_devs_enable - Enables list of devices + * @devs - pointer to pmx device array + * @count - number of devices to enable + * + * It will enable pads for all required peripherals once and only once. + * If peripheral is not supported by current mode then request is rejected. + * Conflicts between peripherals are not handled and peripherals will be + * enabled in the order they are present in pmx_dev array. + * In case of conflicts last peripheral enalbed will be present. + * Returns -ve on Err otherwise 0 + */ +static int pmx_devs_enable(struct pmx_dev **devs, u8 count) +{ + u32 val, i, mask; + + if (!count) + return -EINVAL; + + val = readl(pmx->base + pmx->mux_reg.offset); + for (i = 0; i < count; i++) { + u8 j = 0; + + if (!devs[i]->name || !devs[i]->modes) { + printk(KERN_ERR "padmux: dev name or modes is null\n"); + continue; + } + /* check if peripheral exists in active mode */ + if (pmx->active_mode) { + bool found = false; + for (j = 0; j < devs[i]->mode_count; j++) { + if (devs[i]->modes[j].ids & + pmx->active_mode->id) { + found = true; + break; + } + } + if (found == false) { + printk(KERN_ERR "%s device not available in %s"\ + "mode\n", devs[i]->name, + pmx->active_mode->name); + continue; + } + } + + /* enable peripheral */ + mask = devs[i]->modes[j].mask & pmx->mux_reg.mask; + if (devs[i]->enb_on_reset) + val &= ~mask; + else + val |= mask; + + devs[i]->is_active = true; + } + writel(val, pmx->base + pmx->mux_reg.offset); + kfree(pmx); + + /* this will ensure that multiplexing can't be changed now */ + pmx = (struct pmx *)-1; + + return 0; +} + +/** + * pmx_register - registers a platform requesting pad mux feature + * @driver - pointer to driver structure containing driver specific parameters + * + * Also this must be called only once. This will allocate memory for pmx + * structure, will call pmx_mode_set, will call pmx_devs_enable. + * Returns -ve on Err otherwise 0 + */ +int pmx_register(struct pmx_driver *driver) +{ + int ret = 0; + + if (pmx) + return -EPERM; + if (!driver->base || !driver->devs) + return -EFAULT; + + pmx = kzalloc(sizeof(*pmx), GFP_KERNEL); + if (!pmx) + return -ENOMEM; + + pmx->base = (u32)driver->base; + pmx->mode_reg.offset = driver->mode_reg.offset; + pmx->mode_reg.mask = driver->mode_reg.mask; + pmx->mux_reg.offset = driver->mux_reg.offset; + pmx->mux_reg.mask = driver->mux_reg.mask; + + /* choose mode to enable */ + if (driver->mode) { + ret = pmx_mode_set(driver->mode); + if (ret) + goto pmx_fail; + } + ret = pmx_devs_enable(driver->devs, driver->devs_count); + if (ret) + goto pmx_fail; + + return 0; + +pmx_fail: + return ret; +} -- cgit v1.2.3 From 4c18e77f718cdda33363132127db4df795eadbd9 Mon Sep 17 00:00:00 2001 From: viresh kumar Date: Mon, 3 May 2010 09:24:30 +0100 Subject: ARM: 6091/1: ST SPEAr: Adding support for shared irq layer Multiple peripherals in SPEAr share common hardware interrupt lines. This patch adds support for a shared irq layer, which registers hardware irqs by itself and exposes virtual irq numbers to peripherals. Signed-off-by: Viresh Kumar Signed-off-by: Russell King --- arch/arm/mach-spear3xx/include/mach/irqs.h | 101 +++++++++++++++-- arch/arm/mach-spear3xx/include/mach/spear300.h | 16 +++ arch/arm/mach-spear3xx/include/mach/spear310.h | 24 ++++ arch/arm/mach-spear3xx/include/mach/spear320.h | 26 +++++ arch/arm/mach-spear3xx/spear300.c | 67 ++++++++++- arch/arm/mach-spear3xx/spear310.c | 148 ++++++++++++++++++++++++ arch/arm/mach-spear3xx/spear320.c | 150 +++++++++++++++++++++++++ arch/arm/mach-spear6xx/include/mach/irqs.h | 1 + arch/arm/plat-spear/Makefile | 2 + arch/arm/plat-spear/include/plat/shirq.h | 73 ++++++++++++ arch/arm/plat-spear/shirq.c | 118 +++++++++++++++++++ 11 files changed, 718 insertions(+), 8 deletions(-) create mode 100644 arch/arm/plat-spear/include/plat/shirq.h create mode 100644 arch/arm/plat-spear/shirq.c (limited to 'arch/arm/plat-spear') diff --git a/arch/arm/mach-spear3xx/include/mach/irqs.h b/arch/arm/mach-spear3xx/include/mach/irqs.h index fe980e07b6ab..7f940b818473 100644 --- a/arch/arm/mach-spear3xx/include/mach/irqs.h +++ b/arch/arm/mach-spear3xx/include/mach/irqs.h @@ -14,7 +14,7 @@ #ifndef __MACH_IRQS_H #define __MACH_IRQS_H -/* IRQ definitions */ +/* SPEAr3xx IRQ definitions */ #define IRQ_HW_ACCEL_MOD_0 0 #define IRQ_INTRCOMM_RAS_ARM 1 #define IRQ_CPU_GPT1_1 2 @@ -50,16 +50,103 @@ #define IRQ_HW_ACCEL_MOD_1 31 #define IRQ_VIC_END 32 -#define SPEAR_GPIO_INT_BASE IRQ_VIC_END +#define VIRQ_START IRQ_VIC_END +/* SPEAr300 Virtual irq definitions */ #ifdef CONFIG_MACH_SPEAR300 -#define SPEAR_GPIO1_INT_BASE (SPEAR_GPIO_INT_BASE + 8) -#define SPEAR_GPIO_INT_END (SPEAR_GPIO1_INT_BASE + 8) +/* IRQs sharing IRQ_GEN_RAS_1 */ +#define VIRQ_IT_PERS_S (VIRQ_START + 0) +#define VIRQ_IT_CHANGE_S (VIRQ_START + 1) +#define VIRQ_I2S (VIRQ_START + 2) +#define VIRQ_TDM (VIRQ_START + 3) +#define VIRQ_CAMERA_L (VIRQ_START + 4) +#define VIRQ_CAMERA_F (VIRQ_START + 5) +#define VIRQ_CAMERA_V (VIRQ_START + 6) +#define VIRQ_KEYBOARD (VIRQ_START + 7) +#define VIRQ_GPIO1 (VIRQ_START + 8) + +/* IRQs sharing IRQ_GEN_RAS_3 */ +#define IRQ_CLCD IRQ_GEN_RAS_3 + +/* IRQs sharing IRQ_INTRCOMM_RAS_ARM */ +#define IRQ_SDIO IRQ_INTRCOMM_RAS_ARM + +/* GPIO pins virtual irqs */ +#define SPEAR_GPIO_INT_BASE (VIRQ_START + 9) +#define SPEAR_GPIO1_INT_BASE (SPEAR_GPIO_INT_BASE + 8) +#define SPEAR_GPIO_INT_END (SPEAR_GPIO1_INT_BASE + 8) + +/* SPEAr310 Virtual irq definitions */ +#elif defined(CONFIG_MACH_SPEAR310) +/* IRQs sharing IRQ_GEN_RAS_1 */ +#define VIRQ_SMII0 (VIRQ_START + 0) +#define VIRQ_SMII1 (VIRQ_START + 1) +#define VIRQ_SMII2 (VIRQ_START + 2) +#define VIRQ_SMII3 (VIRQ_START + 3) +#define VIRQ_WAKEUP_SMII0 (VIRQ_START + 4) +#define VIRQ_WAKEUP_SMII1 (VIRQ_START + 5) +#define VIRQ_WAKEUP_SMII2 (VIRQ_START + 6) +#define VIRQ_WAKEUP_SMII3 (VIRQ_START + 7) + +/* IRQs sharing IRQ_GEN_RAS_2 */ +#define VIRQ_UART1 (VIRQ_START + 8) +#define VIRQ_UART2 (VIRQ_START + 9) +#define VIRQ_UART3 (VIRQ_START + 10) +#define VIRQ_UART4 (VIRQ_START + 11) +#define VIRQ_UART5 (VIRQ_START + 12) + +/* IRQs sharing IRQ_GEN_RAS_3 */ +#define VIRQ_EMI (VIRQ_START + 13) +#define VIRQ_PLGPIO (VIRQ_START + 14) + +/* IRQs sharing IRQ_INTRCOMM_RAS_ARM */ +#define VIRQ_TDM_HDLC (VIRQ_START + 15) +#define VIRQ_RS485_0 (VIRQ_START + 16) +#define VIRQ_RS485_1 (VIRQ_START + 17) + +/* GPIO pins virtual irqs */ +#define SPEAR_GPIO_INT_BASE (VIRQ_START + 18) + +/* SPEAr320 Virtual irq definitions */ #else -#define SPEAR_GPIO_INT_END (SPEAR_GPIO_INT_BASE + 8) +/* IRQs sharing IRQ_GEN_RAS_1 */ +#define VIRQ_EMI (VIRQ_START + 0) +#define VIRQ_CLCD (VIRQ_START + 1) +#define VIRQ_SPP (VIRQ_START + 2) + +/* IRQs sharing IRQ_GEN_RAS_2 */ +#define IRQ_SDIO IRQ_GEN_RAS_2 + +/* IRQs sharing IRQ_GEN_RAS_3 */ +#define VIRQ_PLGPIO (VIRQ_START + 3) +#define VIRQ_I2S_PLAY (VIRQ_START + 4) +#define VIRQ_I2S_REC (VIRQ_START + 5) + +/* IRQs sharing IRQ_INTRCOMM_RAS_ARM */ +#define VIRQ_CANU (VIRQ_START + 6) +#define VIRQ_CANL (VIRQ_START + 7) +#define VIRQ_UART1 (VIRQ_START + 8) +#define VIRQ_UART2 (VIRQ_START + 9) +#define VIRQ_SSP1 (VIRQ_START + 10) +#define VIRQ_SSP2 (VIRQ_START + 11) +#define VIRQ_SMII0 (VIRQ_START + 12) +#define VIRQ_MII1_SMII1 (VIRQ_START + 13) +#define VIRQ_WAKEUP_SMII0 (VIRQ_START + 14) +#define VIRQ_WAKEUP_MII1_SMII1 (VIRQ_START + 15) +#define VIRQ_I2C (VIRQ_START + 16) + +/* GPIO pins virtual irqs */ +#define SPEAR_GPIO_INT_BASE (VIRQ_START + 17) + +#endif + +/* PLGPIO Virtual IRQs */ +#if defined(CONFIG_MACH_SPEAR310) || defined(CONFIG_MACH_SPEAR320) +#define SPEAR_PLGPIO_INT_BASE (SPEAR_GPIO_INT_BASE + 8) +#define SPEAR_GPIO_INT_END (SPEAR_PLGPIO_INT_BASE + 102) #endif -#define VIRTUAL_IRQS (SPEAR_GPIO_INT_END - IRQ_VIC_END) -#define NR_IRQS (IRQ_VIC_END + VIRTUAL_IRQS) +#define VIRQ_END SPEAR_GPIO_INT_END +#define NR_IRQS VIRQ_END #endif /* __MACH_IRQS_H */ diff --git a/arch/arm/mach-spear3xx/include/mach/spear300.h b/arch/arm/mach-spear3xx/include/mach/spear300.h index 40c16f385cfa..ccaa76522ee2 100644 --- a/arch/arm/mach-spear3xx/include/mach/spear300.h +++ b/arch/arm/mach-spear3xx/include/mach/spear300.h @@ -20,6 +20,22 @@ #define SPEAR300_TELECOM_BASE 0x50000000 #define SPEAR300_TELECOM_SIZE 0x10000000 +/* Interrupt registers offsets and masks */ +#define SPEAR300_TELECOM_REG_SIZE 0x00010000 +#define INT_ENB_MASK_REG 0x54 +#define INT_STS_MASK_REG 0x58 +#define IT_PERS_S_IRQ_MASK (1 << 0) +#define IT_CHANGE_S_IRQ_MASK (1 << 1) +#define I2S_IRQ_MASK (1 << 2) +#define TDM_IRQ_MASK (1 << 3) +#define CAMERA_L_IRQ_MASK (1 << 4) +#define CAMERA_F_IRQ_MASK (1 << 5) +#define CAMERA_V_IRQ_MASK (1 << 6) +#define KEYBOARD_IRQ_MASK (1 << 7) +#define GPIO1_IRQ_MASK (1 << 8) + +#define SHIRQ_RAS1_MASK 0x1FF + #define SPEAR300_CLCD_BASE 0x60000000 #define SPEAR300_CLCD_SIZE 0x10000000 diff --git a/arch/arm/mach-spear3xx/include/mach/spear310.h b/arch/arm/mach-spear3xx/include/mach/spear310.h index d4f58d6469d9..b27bb8af3309 100644 --- a/arch/arm/mach-spear3xx/include/mach/spear310.h +++ b/arch/arm/mach-spear3xx/include/mach/spear310.h @@ -40,6 +40,30 @@ #define SPEAR310_SOC_CONFIG_BASE 0xB4000000 #define SPEAR310_SOC_CONFIG_SIZE 0x00000070 +/* Interrupt registers offsets and masks */ +#define INT_STS_MASK_REG 0x04 +#define SMII0_IRQ_MASK (1 << 0) +#define SMII1_IRQ_MASK (1 << 1) +#define SMII2_IRQ_MASK (1 << 2) +#define SMII3_IRQ_MASK (1 << 3) +#define WAKEUP_SMII0_IRQ_MASK (1 << 4) +#define WAKEUP_SMII1_IRQ_MASK (1 << 5) +#define WAKEUP_SMII2_IRQ_MASK (1 << 6) +#define WAKEUP_SMII3_IRQ_MASK (1 << 7) +#define UART1_IRQ_MASK (1 << 8) +#define UART2_IRQ_MASK (1 << 9) +#define UART3_IRQ_MASK (1 << 10) +#define UART4_IRQ_MASK (1 << 11) +#define UART5_IRQ_MASK (1 << 12) +#define EMI_IRQ_MASK (1 << 13) +#define TDM_HDLC_IRQ_MASK (1 << 14) +#define RS485_0_IRQ_MASK (1 << 15) +#define RS485_1_IRQ_MASK (1 << 16) + +#define SHIRQ_RAS1_MASK 0x000FF +#define SHIRQ_RAS2_MASK 0x01F00 +#define SHIRQ_RAS3_MASK 0x02000 +#define SHIRQ_INTRCOMM_RAS_MASK 0x1C000 #endif /* __MACH_SPEAR310_H */ diff --git a/arch/arm/mach-spear3xx/include/mach/spear320.h b/arch/arm/mach-spear3xx/include/mach/spear320.h index e8ad2ed82e24..cacf17a958cd 100644 --- a/arch/arm/mach-spear3xx/include/mach/spear320.h +++ b/arch/arm/mach-spear3xx/include/mach/spear320.h @@ -64,6 +64,32 @@ #define SPEAR320_SOC_CONFIG_BASE 0xB4000000 #define SPEAR320_SOC_CONFIG_SIZE 0x00000070 +/* Interrupt registers offsets and masks */ +#define INT_STS_MASK_REG 0x04 +#define INT_CLR_MASK_REG 0x04 +#define INT_ENB_MASK_REG 0x08 +#define GPIO_IRQ_MASK (1 << 0) +#define I2S_PLAY_IRQ_MASK (1 << 1) +#define I2S_REC_IRQ_MASK (1 << 2) +#define EMI_IRQ_MASK (1 << 7) +#define CLCD_IRQ_MASK (1 << 8) +#define SPP_IRQ_MASK (1 << 9) +#define SDIO_IRQ_MASK (1 << 10) +#define CAN_U_IRQ_MASK (1 << 11) +#define CAN_L_IRQ_MASK (1 << 12) +#define UART1_IRQ_MASK (1 << 13) +#define UART2_IRQ_MASK (1 << 14) +#define SSP1_IRQ_MASK (1 << 15) +#define SSP2_IRQ_MASK (1 << 16) +#define SMII0_IRQ_MASK (1 << 17) +#define MII1_SMII1_IRQ_MASK (1 << 18) +#define WAKEUP_SMII0_IRQ_MASK (1 << 19) +#define WAKEUP_MII1_SMII1_IRQ_MASK (1 << 20) +#define I2C1_IRQ_MASK (1 << 21) + +#define SHIRQ_RAS1_MASK 0x000380 +#define SHIRQ_RAS3_MASK 0x000007 +#define SHIRQ_INTRCOMM_RAS_MASK 0x3FF800 #endif /* __MACH_SPEAR320_H */ diff --git a/arch/arm/mach-spear3xx/spear300.c b/arch/arm/mach-spear3xx/spear300.c index 66e7fcd8baf8..3560f8c1e723 100644 --- a/arch/arm/mach-spear3xx/spear300.c +++ b/arch/arm/mach-spear3xx/spear300.c @@ -17,6 +17,7 @@ #include #include #include +#include /* pad multiplexing support */ /* muxing registers */ @@ -386,14 +387,78 @@ struct amba_device gpio1_device = { .end = SPEAR300_GPIO_BASE + SPEAR300_GPIO_SIZE - 1, .flags = IORESOURCE_MEM, }, - .irq = {IRQ_GEN_RAS_1, NO_IRQ}, + .irq = {VIRQ_GPIO1, NO_IRQ}, +}; + +/* spear3xx shared irq */ +struct shirq_dev_config shirq_ras1_config[] = { + { + .virq = VIRQ_IT_PERS_S, + .enb_mask = IT_PERS_S_IRQ_MASK, + .status_mask = IT_PERS_S_IRQ_MASK, + }, { + .virq = VIRQ_IT_CHANGE_S, + .enb_mask = IT_CHANGE_S_IRQ_MASK, + .status_mask = IT_CHANGE_S_IRQ_MASK, + }, { + .virq = VIRQ_I2S, + .enb_mask = I2S_IRQ_MASK, + .status_mask = I2S_IRQ_MASK, + }, { + .virq = VIRQ_TDM, + .enb_mask = TDM_IRQ_MASK, + .status_mask = TDM_IRQ_MASK, + }, { + .virq = VIRQ_CAMERA_L, + .enb_mask = CAMERA_L_IRQ_MASK, + .status_mask = CAMERA_L_IRQ_MASK, + }, { + .virq = VIRQ_CAMERA_F, + .enb_mask = CAMERA_F_IRQ_MASK, + .status_mask = CAMERA_F_IRQ_MASK, + }, { + .virq = VIRQ_CAMERA_V, + .enb_mask = CAMERA_V_IRQ_MASK, + .status_mask = CAMERA_V_IRQ_MASK, + }, { + .virq = VIRQ_KEYBOARD, + .enb_mask = KEYBOARD_IRQ_MASK, + .status_mask = KEYBOARD_IRQ_MASK, + }, { + .virq = VIRQ_GPIO1, + .enb_mask = GPIO1_IRQ_MASK, + .status_mask = GPIO1_IRQ_MASK, + }, +}; + +struct spear_shirq shirq_ras1 = { + .irq = IRQ_GEN_RAS_1, + .dev_config = shirq_ras1_config, + .dev_count = ARRAY_SIZE(shirq_ras1_config), + .regs = { + .enb_reg = INT_ENB_MASK_REG, + .status_reg = INT_STS_MASK_REG, + .status_reg_mask = SHIRQ_RAS1_MASK, + .clear_reg = -1, + }, }; /* spear300 routines */ void __init spear300_init(void) { + int ret = 0; + /* call spear3xx family common init function */ spear3xx_init(); + + /* shared irq registeration */ + shirq_ras1.regs.base = + ioremap(SPEAR300_TELECOM_BASE, SPEAR300_TELECOM_REG_SIZE); + if (shirq_ras1.regs.base) { + ret = spear_shirq_register(&shirq_ras1); + if (ret) + printk(KERN_ERR "Error registering Shared IRQ\n"); + } } void spear300_pmx_init(void) diff --git a/arch/arm/mach-spear3xx/spear310.c b/arch/arm/mach-spear3xx/spear310.c index dd5a57282711..96a1ab824bac 100644 --- a/arch/arm/mach-spear3xx/spear310.c +++ b/arch/arm/mach-spear3xx/spear310.c @@ -15,6 +15,7 @@ #include #include #include +#include /* pad multiplexing support */ /* muxing registers */ @@ -140,11 +141,158 @@ struct pmx_driver pmx_driver = { /* Add spear310 specific devices here */ +/* spear3xx shared irq */ +struct shirq_dev_config shirq_ras1_config[] = { + { + .virq = VIRQ_SMII0, + .status_mask = SMII0_IRQ_MASK, + }, { + .virq = VIRQ_SMII1, + .status_mask = SMII1_IRQ_MASK, + }, { + .virq = VIRQ_SMII2, + .status_mask = SMII2_IRQ_MASK, + }, { + .virq = VIRQ_SMII3, + .status_mask = SMII3_IRQ_MASK, + }, { + .virq = VIRQ_WAKEUP_SMII0, + .status_mask = WAKEUP_SMII0_IRQ_MASK, + }, { + .virq = VIRQ_WAKEUP_SMII1, + .status_mask = WAKEUP_SMII1_IRQ_MASK, + }, { + .virq = VIRQ_WAKEUP_SMII2, + .status_mask = WAKEUP_SMII2_IRQ_MASK, + }, { + .virq = VIRQ_WAKEUP_SMII3, + .status_mask = WAKEUP_SMII3_IRQ_MASK, + }, +}; + +struct spear_shirq shirq_ras1 = { + .irq = IRQ_GEN_RAS_1, + .dev_config = shirq_ras1_config, + .dev_count = ARRAY_SIZE(shirq_ras1_config), + .regs = { + .enb_reg = -1, + .status_reg = INT_STS_MASK_REG, + .status_reg_mask = SHIRQ_RAS1_MASK, + .clear_reg = -1, + }, +}; + +struct shirq_dev_config shirq_ras2_config[] = { + { + .virq = VIRQ_UART1, + .status_mask = UART1_IRQ_MASK, + }, { + .virq = VIRQ_UART2, + .status_mask = UART2_IRQ_MASK, + }, { + .virq = VIRQ_UART3, + .status_mask = UART3_IRQ_MASK, + }, { + .virq = VIRQ_UART4, + .status_mask = UART4_IRQ_MASK, + }, { + .virq = VIRQ_UART5, + .status_mask = UART5_IRQ_MASK, + }, +}; + +struct spear_shirq shirq_ras2 = { + .irq = IRQ_GEN_RAS_2, + .dev_config = shirq_ras2_config, + .dev_count = ARRAY_SIZE(shirq_ras2_config), + .regs = { + .enb_reg = -1, + .status_reg = INT_STS_MASK_REG, + .status_reg_mask = SHIRQ_RAS2_MASK, + .clear_reg = -1, + }, +}; + +struct shirq_dev_config shirq_ras3_config[] = { + { + .virq = VIRQ_EMI, + .status_mask = EMI_IRQ_MASK, + }, +}; + +struct spear_shirq shirq_ras3 = { + .irq = IRQ_GEN_RAS_3, + .dev_config = shirq_ras3_config, + .dev_count = ARRAY_SIZE(shirq_ras3_config), + .regs = { + .enb_reg = -1, + .status_reg = INT_STS_MASK_REG, + .status_reg_mask = SHIRQ_RAS3_MASK, + .clear_reg = -1, + }, +}; + +struct shirq_dev_config shirq_intrcomm_ras_config[] = { + { + .virq = VIRQ_TDM_HDLC, + .status_mask = TDM_HDLC_IRQ_MASK, + }, { + .virq = VIRQ_RS485_0, + .status_mask = RS485_0_IRQ_MASK, + }, { + .virq = VIRQ_RS485_1, + .status_mask = RS485_1_IRQ_MASK, + }, +}; + +struct spear_shirq shirq_intrcomm_ras = { + .irq = IRQ_INTRCOMM_RAS_ARM, + .dev_config = shirq_intrcomm_ras_config, + .dev_count = ARRAY_SIZE(shirq_intrcomm_ras_config), + .regs = { + .enb_reg = -1, + .status_reg = INT_STS_MASK_REG, + .status_reg_mask = SHIRQ_INTRCOMM_RAS_MASK, + .clear_reg = -1, + }, +}; + /* spear310 routines */ void __init spear310_init(void) { + void __iomem *base; + int ret = 0; + /* call spear3xx family common init function */ spear3xx_init(); + + /* shared irq registeration */ + base = ioremap(SPEAR310_SOC_CONFIG_BASE, SPEAR310_SOC_CONFIG_SIZE); + if (base) { + /* shirq 1 */ + shirq_ras1.regs.base = base; + ret = spear_shirq_register(&shirq_ras1); + if (ret) + printk(KERN_ERR "Error registering Shared IRQ 1\n"); + + /* shirq 2 */ + shirq_ras2.regs.base = base; + ret = spear_shirq_register(&shirq_ras2); + if (ret) + printk(KERN_ERR "Error registering Shared IRQ 2\n"); + + /* shirq 3 */ + shirq_ras3.regs.base = base; + ret = spear_shirq_register(&shirq_ras3); + if (ret) + printk(KERN_ERR "Error registering Shared IRQ 3\n"); + + /* shirq 4 */ + shirq_intrcomm_ras.regs.base = base; + ret = spear_shirq_register(&shirq_intrcomm_ras); + if (ret) + printk(KERN_ERR "Error registering Shared IRQ 4\n"); + } } void spear310_pmx_init(void) diff --git a/arch/arm/mach-spear3xx/spear320.c b/arch/arm/mach-spear3xx/spear320.c index 2cedf5eb9ec9..6a1219549369 100644 --- a/arch/arm/mach-spear3xx/spear320.c +++ b/arch/arm/mach-spear3xx/spear320.c @@ -15,6 +15,7 @@ #include #include #include +#include /* pad multiplexing support */ /* muxing registers */ @@ -385,11 +386,160 @@ struct pmx_driver pmx_driver = { /* Add spear320 specific devices here */ +/* spear3xx shared irq */ +struct shirq_dev_config shirq_ras1_config[] = { + { + .virq = VIRQ_EMI, + .status_mask = EMI_IRQ_MASK, + .clear_mask = EMI_IRQ_MASK, + }, { + .virq = VIRQ_CLCD, + .status_mask = CLCD_IRQ_MASK, + .clear_mask = CLCD_IRQ_MASK, + }, { + .virq = VIRQ_SPP, + .status_mask = SPP_IRQ_MASK, + .clear_mask = SPP_IRQ_MASK, + }, +}; + +struct spear_shirq shirq_ras1 = { + .irq = IRQ_GEN_RAS_1, + .dev_config = shirq_ras1_config, + .dev_count = ARRAY_SIZE(shirq_ras1_config), + .regs = { + .enb_reg = -1, + .status_reg = INT_STS_MASK_REG, + .status_reg_mask = SHIRQ_RAS1_MASK, + .clear_reg = INT_CLR_MASK_REG, + .reset_to_clear = 1, + }, +}; + +struct shirq_dev_config shirq_ras3_config[] = { + { + .virq = VIRQ_PLGPIO, + .enb_mask = GPIO_IRQ_MASK, + .status_mask = GPIO_IRQ_MASK, + .clear_mask = GPIO_IRQ_MASK, + }, { + .virq = VIRQ_I2S_PLAY, + .enb_mask = I2S_PLAY_IRQ_MASK, + .status_mask = I2S_PLAY_IRQ_MASK, + .clear_mask = I2S_PLAY_IRQ_MASK, + }, { + .virq = VIRQ_I2S_REC, + .enb_mask = I2S_REC_IRQ_MASK, + .status_mask = I2S_REC_IRQ_MASK, + .clear_mask = I2S_REC_IRQ_MASK, + }, +}; + +struct spear_shirq shirq_ras3 = { + .irq = IRQ_GEN_RAS_3, + .dev_config = shirq_ras3_config, + .dev_count = ARRAY_SIZE(shirq_ras3_config), + .regs = { + .enb_reg = INT_ENB_MASK_REG, + .reset_to_enb = 1, + .status_reg = INT_STS_MASK_REG, + .status_reg_mask = SHIRQ_RAS3_MASK, + .clear_reg = INT_CLR_MASK_REG, + .reset_to_clear = 1, + }, +}; + +struct shirq_dev_config shirq_intrcomm_ras_config[] = { + { + .virq = VIRQ_CANU, + .status_mask = CAN_U_IRQ_MASK, + .clear_mask = CAN_U_IRQ_MASK, + }, { + .virq = VIRQ_CANL, + .status_mask = CAN_L_IRQ_MASK, + .clear_mask = CAN_L_IRQ_MASK, + }, { + .virq = VIRQ_UART1, + .status_mask = UART1_IRQ_MASK, + .clear_mask = UART1_IRQ_MASK, + }, { + .virq = VIRQ_UART2, + .status_mask = UART2_IRQ_MASK, + .clear_mask = UART2_IRQ_MASK, + }, { + .virq = VIRQ_SSP1, + .status_mask = SSP1_IRQ_MASK, + .clear_mask = SSP1_IRQ_MASK, + }, { + .virq = VIRQ_SSP2, + .status_mask = SSP2_IRQ_MASK, + .clear_mask = SSP2_IRQ_MASK, + }, { + .virq = VIRQ_SMII0, + .status_mask = SMII0_IRQ_MASK, + .clear_mask = SMII0_IRQ_MASK, + }, { + .virq = VIRQ_MII1_SMII1, + .status_mask = MII1_SMII1_IRQ_MASK, + .clear_mask = MII1_SMII1_IRQ_MASK, + }, { + .virq = VIRQ_WAKEUP_SMII0, + .status_mask = WAKEUP_SMII0_IRQ_MASK, + .clear_mask = WAKEUP_SMII0_IRQ_MASK, + }, { + .virq = VIRQ_WAKEUP_MII1_SMII1, + .status_mask = WAKEUP_MII1_SMII1_IRQ_MASK, + .clear_mask = WAKEUP_MII1_SMII1_IRQ_MASK, + }, { + .virq = VIRQ_I2C, + .status_mask = I2C1_IRQ_MASK, + .clear_mask = I2C1_IRQ_MASK, + }, +}; + +struct spear_shirq shirq_intrcomm_ras = { + .irq = IRQ_INTRCOMM_RAS_ARM, + .dev_config = shirq_intrcomm_ras_config, + .dev_count = ARRAY_SIZE(shirq_intrcomm_ras_config), + .regs = { + .enb_reg = -1, + .status_reg = INT_STS_MASK_REG, + .status_reg_mask = SHIRQ_INTRCOMM_RAS_MASK, + .clear_reg = INT_CLR_MASK_REG, + .reset_to_clear = 1, + }, +}; + /* spear320 routines */ void __init spear320_init(void) { + void __iomem *base; + int ret = 0; + /* call spear3xx family common init function */ spear3xx_init(); + + /* shared irq registeration */ + base = ioremap(SPEAR320_SOC_CONFIG_BASE, SPEAR320_SOC_CONFIG_SIZE); + if (base) { + /* shirq 1 */ + shirq_ras1.regs.base = base; + ret = spear_shirq_register(&shirq_ras1); + if (ret) + printk(KERN_ERR "Error registering Shared IRQ 1\n"); + + /* shirq 3 */ + shirq_ras3.regs.base = base; + ret = spear_shirq_register(&shirq_ras3); + if (ret) + printk(KERN_ERR "Error registering Shared IRQ 3\n"); + + /* shirq 4 */ + shirq_intrcomm_ras.regs.base = base; + ret = spear_shirq_register(&shirq_intrcomm_ras); + if (ret) + printk(KERN_ERR "Error registering Shared IRQ 4\n"); + } } void spear320_pmx_init(void) diff --git a/arch/arm/mach-spear6xx/include/mach/irqs.h b/arch/arm/mach-spear6xx/include/mach/irqs.h index 08a3abced750..8f214b03d75d 100644 --- a/arch/arm/mach-spear6xx/include/mach/irqs.h +++ b/arch/arm/mach-spear6xx/include/mach/irqs.h @@ -85,6 +85,7 @@ #define IRQ_VIC_END 64 +/* GPIO pins virtual irqs */ #define SPEAR_GPIO_INT_BASE IRQ_VIC_END #define SPEAR_GPIO0_INT_BASE SPEAR_GPIO_INT_BASE #define SPEAR_GPIO1_INT_BASE (SPEAR_GPIO0_INT_BASE + 8) diff --git a/arch/arm/plat-spear/Makefile b/arch/arm/plat-spear/Makefile index 6f4ad5e9462e..eb89540aeda9 100644 --- a/arch/arm/plat-spear/Makefile +++ b/arch/arm/plat-spear/Makefile @@ -4,3 +4,5 @@ # Common support obj-y := clock.o padmux.o time.o + +obj-$(CONFIG_ARCH_SPEAR3XX) += shirq.o diff --git a/arch/arm/plat-spear/include/plat/shirq.h b/arch/arm/plat-spear/include/plat/shirq.h new file mode 100644 index 000000000000..03ed8b585dcf --- /dev/null +++ b/arch/arm/plat-spear/include/plat/shirq.h @@ -0,0 +1,73 @@ +/* + * arch/arm/plat-spear/include/plat/shirq.h + * + * SPEAr platform shared irq layer header file + * + * Copyright (C) 2009 ST Microelectronics + * Viresh Kumar + * + * 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 __PLAT_SHIRQ_H +#define __PLAT_SHIRQ_H + +#include +#include + +/* + * struct shirq_dev_config: shared irq device configuration + * + * virq: virtual irq number of device + * enb_mask: enable mask of device + * status_mask: status mask of device + * clear_mask: clear mask of device + */ +struct shirq_dev_config { + u32 virq; + u32 enb_mask; + u32 status_mask; + u32 clear_mask; +}; + +/* + * struct shirq_regs: shared irq register configuration + * + * base: base address of shared irq register + * enb_reg: enable register offset + * reset_to_enb: val 1 indicates, we need to clear bit for enabling interrupt + * status_reg: status register offset + * status_reg_mask: status register valid mask + * clear_reg: clear register offset + * reset_to_clear: val 1 indicates, we need to clear bit for clearing interrupt + */ +struct shirq_regs { + void __iomem *base; + u32 enb_reg; + u32 reset_to_enb; + u32 status_reg; + u32 status_reg_mask; + u32 clear_reg; + u32 reset_to_clear; +}; + +/* + * struct spear_shirq: shared irq structure + * + * irq: hardware irq number + * dev_config: array of device config structures which are using "irq" line + * dev_count: size of dev_config array + * regs: register configuration for shared irq block + */ +struct spear_shirq { + u32 irq; + struct shirq_dev_config *dev_config; + u32 dev_count; + struct shirq_regs regs; +}; + +int spear_shirq_register(struct spear_shirq *shirq); + +#endif /* __PLAT_SHIRQ_H */ diff --git a/arch/arm/plat-spear/shirq.c b/arch/arm/plat-spear/shirq.c new file mode 100644 index 000000000000..2172d6946aea --- /dev/null +++ b/arch/arm/plat-spear/shirq.c @@ -0,0 +1,118 @@ +/* + * arch/arm/plat-spear/shirq.c + * + * SPEAr platform shared irq layer source file + * + * Copyright (C) 2009 ST Microelectronics + * Viresh Kumar + * + * 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 +#include +#include +#include +#include + +struct spear_shirq *shirq; +static DEFINE_SPINLOCK(lock); + +static void shirq_irq_mask(unsigned irq) +{ + struct spear_shirq *shirq = get_irq_chip_data(irq); + u32 val, id = irq - shirq->dev_config[0].virq; + unsigned long flags; + + if ((shirq->regs.enb_reg == -1) || shirq->dev_config[id].enb_mask == -1) + return; + + spin_lock_irqsave(&lock, flags); + val = readl(shirq->regs.base + shirq->regs.enb_reg); + if (shirq->regs.reset_to_enb) + val |= shirq->dev_config[id].enb_mask; + else + val &= ~(shirq->dev_config[id].enb_mask); + writel(val, shirq->regs.base + shirq->regs.enb_reg); + spin_unlock_irqrestore(&lock, flags); +} + +static void shirq_irq_unmask(unsigned irq) +{ + struct spear_shirq *shirq = get_irq_chip_data(irq); + u32 val, id = irq - shirq->dev_config[0].virq; + unsigned long flags; + + if ((shirq->regs.enb_reg == -1) || shirq->dev_config[id].enb_mask == -1) + return; + + spin_lock_irqsave(&lock, flags); + val = readl(shirq->regs.base + shirq->regs.enb_reg); + if (shirq->regs.reset_to_enb) + val &= ~(shirq->dev_config[id].enb_mask); + else + val |= shirq->dev_config[id].enb_mask; + writel(val, shirq->regs.base + shirq->regs.enb_reg); + spin_unlock_irqrestore(&lock, flags); +} + +static struct irq_chip shirq_chip = { + .name = "spear_shirq", + .ack = shirq_irq_mask, + .mask = shirq_irq_mask, + .unmask = shirq_irq_unmask, +}; + +static void shirq_handler(unsigned irq, struct irq_desc *desc) +{ + u32 i, val, mask; + struct spear_shirq *shirq = get_irq_data(irq); + + desc->chip->ack(irq); + while ((val = readl(shirq->regs.base + shirq->regs.status_reg) & + shirq->regs.status_reg_mask)) { + for (i = 0; (i < shirq->dev_count) && val; i++) { + if (!(shirq->dev_config[i].status_mask & val)) + continue; + + generic_handle_irq(shirq->dev_config[i].virq); + + /* clear interrupt */ + val &= ~shirq->dev_config[i].status_mask; + if ((shirq->regs.clear_reg == -1) || + shirq->dev_config[i].clear_mask == -1) + continue; + mask = readl(shirq->regs.base + shirq->regs.clear_reg); + if (shirq->regs.reset_to_clear) + mask &= ~shirq->dev_config[i].clear_mask; + else + mask |= shirq->dev_config[i].clear_mask; + writel(mask, shirq->regs.base + shirq->regs.clear_reg); + } + } + desc->chip->unmask(irq); +} + +int spear_shirq_register(struct spear_shirq *shirq) +{ + int i; + + if (!shirq || !shirq->dev_config || !shirq->regs.base) + return -EFAULT; + + if (!shirq->dev_count) + return -EINVAL; + + set_irq_chained_handler(shirq->irq, shirq_handler); + for (i = 0; i < shirq->dev_count; i++) { + set_irq_chip(shirq->dev_config[i].virq, &shirq_chip); + set_irq_handler(shirq->dev_config[i].virq, handle_simple_irq); + set_irq_flags(shirq->dev_config[i].virq, IRQF_VALID); + set_irq_chip_data(shirq->dev_config[i].virq, shirq); + } + + set_irq_data(shirq->irq, shirq); + return 0; +} -- cgit v1.2.3