diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-orion/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-orion/common.c | 16 | ||||
-rw-r--r-- | arch/arm/mach-orion/common.h | 6 | ||||
-rw-r--r-- | arch/arm/mach-orion/time.c | 181 | ||||
-rw-r--r-- | arch/arm/plat-orion/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/plat-orion/time.c | 203 |
6 files changed, 222 insertions, 188 deletions
diff --git a/arch/arm/mach-orion/Makefile b/arch/arm/mach-orion/Makefile index f91d937a73e..d894caa5060 100644 --- a/arch/arm/mach-orion/Makefile +++ b/arch/arm/mach-orion/Makefile @@ -1,4 +1,4 @@ -obj-y += common.o addr-map.o pci.o gpio.o irq.o time.o +obj-y += common.o addr-map.o pci.o gpio.o irq.o obj-$(CONFIG_MACH_DB88F5281) += db88f5281-setup.o obj-$(CONFIG_MACH_RD88F5182) += rd88f5182-setup.o obj-$(CONFIG_MACH_KUROBOX_PRO) += kurobox_pro-setup.o diff --git a/arch/arm/mach-orion/common.c b/arch/arm/mach-orion/common.c index a32fe8e108b..86d7f7ccfae 100644 --- a/arch/arm/mach-orion/common.c +++ b/arch/arm/mach-orion/common.c @@ -23,8 +23,11 @@ #include <asm/timex.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> +#include <asm/mach/time.h> #include <asm/arch/hardware.h> +#include <asm/arch/orion.h> #include <asm/arch/platform.h> +#include <asm/plat-orion/time.h> #include "common.h" /***************************************************************************** @@ -296,6 +299,19 @@ void __init orion_sata_init(struct mv_sata_platform_data *sata_data) } /***************************************************************************** + * Time handling + ****************************************************************************/ + +static void orion_timer_init(void) +{ + orion_time_init(IRQ_ORION_BRIDGE, ORION_TCLK); +} + +struct sys_timer orion_timer = { + .init = orion_timer_init, +}; + +/***************************************************************************** * General ****************************************************************************/ diff --git a/arch/arm/mach-orion/common.h b/arch/arm/mach-orion/common.h index 9a5afefac4d..3898e1b78ee 100644 --- a/arch/arm/mach-orion/common.h +++ b/arch/arm/mach-orion/common.h @@ -8,6 +8,7 @@ void __init orion_map_io(void); void __init orion_init_irq(void); void __init orion_init(void); +extern struct sys_timer orion_timer; /* * Enumerations and functions for Orion windows mapping. Used by Orion core @@ -57,11 +58,6 @@ void __init orion_gpio_set_valid_pins(u32 pins); void gpio_display(void); /* debug */ /* - * Orion system timer (clocksource + clockevnt, /mach-orion/time.c) - */ -extern struct sys_timer orion_timer; - -/* * Pull in Orion Ethernet platform_data, used by machine-setup */ diff --git a/arch/arm/mach-orion/time.c b/arch/arm/mach-orion/time.c deleted file mode 100644 index bd4262da4f4..00000000000 --- a/arch/arm/mach-orion/time.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * arch/arm/mach-orion/time.c - * - * Core time functions for Marvell Orion System On Chip - * - * Maintainer: Tzachi Perelstein <tzachi@marvell.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include <linux/kernel.h> -#include <linux/clockchips.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <asm/mach/time.h> -#include <asm/arch/orion.h> -#include "common.h" - -/* - * Timer0: clock_event_device, Tick. - * Timer1: clocksource, Free running. - * WatchDog: Not used. - * - * Timers are counting down. - */ -#define CLOCKEVENT 0 -#define CLOCKSOURCE 1 - -/* - * Timers bits - */ -#define BRIDGE_INT_TIMER(x) (1 << ((x) + 1)) -#define TIMER_EN(x) (1 << ((x) * 2)) -#define TIMER_RELOAD_EN(x) (1 << (((x) * 2) + 1)) -#define BRIDGE_INT_TIMER_WD (1 << 3) -#define TIMER_WD_EN (1 << 4) -#define TIMER_WD_RELOAD_EN (1 << 5) - -static cycle_t orion_clksrc_read(void) -{ - return (0xffffffff - orion_read(TIMER_VAL(CLOCKSOURCE))); -} - -static struct clocksource orion_clksrc = { - .name = "orion_clocksource", - .shift = 20, - .rating = 300, - .read = orion_clksrc_read, - .mask = CLOCKSOURCE_MASK(32), - .flags = CLOCK_SOURCE_IS_CONTINUOUS, -}; - -static int -orion_clkevt_next_event(unsigned long delta, struct clock_event_device *dev) -{ - unsigned long flags; - - if (delta == 0) - return -ETIME; - - local_irq_save(flags); - - /* - * Clear and enable timer interrupt bit - */ - orion_write(BRIDGE_CAUSE, ~BRIDGE_INT_TIMER(CLOCKEVENT)); - orion_setbits(BRIDGE_MASK, BRIDGE_INT_TIMER(CLOCKEVENT)); - - /* - * Setup new timer value - */ - orion_write(TIMER_VAL(CLOCKEVENT), delta); - - /* - * Disable auto reload and kickoff the timer - */ - orion_clrbits(TIMER_CTRL, TIMER_RELOAD_EN(CLOCKEVENT)); - orion_setbits(TIMER_CTRL, TIMER_EN(CLOCKEVENT)); - - local_irq_restore(flags); - - return 0; -} - -static void -orion_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev) -{ - unsigned long flags; - - local_irq_save(flags); - - if (mode == CLOCK_EVT_MODE_PERIODIC) { - /* - * Setup latch cycles in timer and enable reload interrupt. - */ - orion_write(TIMER_VAL_RELOAD(CLOCKEVENT), LATCH); - orion_write(TIMER_VAL(CLOCKEVENT), LATCH); - orion_setbits(BRIDGE_MASK, BRIDGE_INT_TIMER(CLOCKEVENT)); - orion_setbits(TIMER_CTRL, TIMER_RELOAD_EN(CLOCKEVENT) | - TIMER_EN(CLOCKEVENT)); - } else { - /* - * Disable timer and interrupt - */ - orion_clrbits(BRIDGE_MASK, BRIDGE_INT_TIMER(CLOCKEVENT)); - orion_write(BRIDGE_CAUSE, ~BRIDGE_INT_TIMER(CLOCKEVENT)); - orion_clrbits(TIMER_CTRL, TIMER_RELOAD_EN(CLOCKEVENT) | - TIMER_EN(CLOCKEVENT)); - } - - local_irq_restore(flags); -} - -static struct clock_event_device orion_clkevt = { - .name = "orion_tick", - .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, - .shift = 32, - .rating = 300, - .cpumask = CPU_MASK_CPU0, - .set_next_event = orion_clkevt_next_event, - .set_mode = orion_clkevt_mode, -}; - -static irqreturn_t orion_timer_interrupt(int irq, void *dev_id) -{ - /* - * Clear cause bit and do event - */ - orion_write(BRIDGE_CAUSE, ~BRIDGE_INT_TIMER(CLOCKEVENT)); - orion_clkevt.event_handler(&orion_clkevt); - return IRQ_HANDLED; -} - -static struct irqaction orion_timer_irq = { - .name = "orion_tick", - .flags = IRQF_DISABLED | IRQF_TIMER, - .handler = orion_timer_interrupt -}; - -static void orion_timer_init(void) -{ - /* - * Setup clocksource free running timer (no interrupt on reload) - */ - orion_write(TIMER_VAL(CLOCKSOURCE), 0xffffffff); - orion_write(TIMER_VAL_RELOAD(CLOCKSOURCE), 0xffffffff); - orion_clrbits(BRIDGE_MASK, BRIDGE_INT_TIMER(CLOCKSOURCE)); - orion_setbits(TIMER_CTRL, TIMER_RELOAD_EN(CLOCKSOURCE) | - TIMER_EN(CLOCKSOURCE)); - - /* - * Register clocksource - */ - orion_clksrc.mult = - clocksource_hz2mult(CLOCK_TICK_RATE, orion_clksrc.shift); - - clocksource_register(&orion_clksrc); - - /* - * Connect and enable tick handler - */ - setup_irq(IRQ_ORION_BRIDGE, &orion_timer_irq); - - /* - * Register clockevent - */ - orion_clkevt.mult = - div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC, orion_clkevt.shift); - orion_clkevt.max_delta_ns = - clockevent_delta2ns(0xfffffffe, &orion_clkevt); - orion_clkevt.min_delta_ns = - clockevent_delta2ns(1, &orion_clkevt); - - clockevents_register_device(&orion_clkevt); -} - -struct sys_timer orion_timer = { - .init = orion_timer_init, -}; diff --git a/arch/arm/plat-orion/Makefile b/arch/arm/plat-orion/Makefile index b33ecb60183..198f3dde2be 100644 --- a/arch/arm/plat-orion/Makefile +++ b/arch/arm/plat-orion/Makefile @@ -2,7 +2,7 @@ # Makefile for the linux kernel. # -obj-y := irq.o pcie.o +obj-y := irq.o pcie.o time.o obj-m := obj-n := obj- := diff --git a/arch/arm/plat-orion/time.c b/arch/arm/plat-orion/time.c new file mode 100644 index 00000000000..28b5285446e --- /dev/null +++ b/arch/arm/plat-orion/time.c @@ -0,0 +1,203 @@ +/* + * arch/arm/plat-orion/time.c + * + * Marvell Orion SoC timer handling. + * + * 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. + * + * Timer 0 is used as free-running clocksource, while timer 1 is + * used as clock_event_device. + */ + +#include <linux/kernel.h> +#include <linux/clockchips.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <asm/mach/time.h> +#include <asm/arch/hardware.h> + +/* + * Number of timer ticks per jiffy. + */ +static u32 ticks_per_jiffy; + + +/* + * Timer block registers. + */ +#define TIMER_CTRL (TIMER_VIRT_BASE + 0x0000) +#define TIMER0_EN 0x0001 +#define TIMER0_RELOAD_EN 0x0002 +#define TIMER1_EN 0x0004 +#define TIMER1_RELOAD_EN 0x0008 +#define TIMER0_RELOAD (TIMER_VIRT_BASE + 0x0010) +#define TIMER0_VAL (TIMER_VIRT_BASE + 0x0014) +#define TIMER1_RELOAD (TIMER_VIRT_BASE + 0x0018) +#define TIMER1_VAL (TIMER_VIRT_BASE + 0x001c) + + +/* + * Clocksource handling. + */ +static cycle_t orion_clksrc_read(void) +{ + return 0xffffffff - readl(TIMER0_VAL); +} + +static struct clocksource orion_clksrc = { + .name = "orion_clocksource", + .shift = 20, + .rating = 300, + .read = orion_clksrc_read, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + + + +/* + * Clockevent handling. + */ +static int +orion_clkevt_next_event(unsigned long delta, struct clock_event_device *dev) +{ + unsigned long flags; + u32 u; + + if (delta == 0) + return -ETIME; + + local_irq_save(flags); + + /* + * Clear and enable clockevent timer interrupt. + */ + writel(~BRIDGE_INT_TIMER1, BRIDGE_CAUSE); + + u = readl(BRIDGE_MASK); + u |= BRIDGE_INT_TIMER1; + writel(u, BRIDGE_MASK); + + /* + * Setup new clockevent timer value. + */ + writel(delta, TIMER1_VAL); + + /* + * Enable the timer. + */ + u = readl(TIMER_CTRL); + u = (u & ~TIMER1_RELOAD_EN) | TIMER1_EN; + writel(u, TIMER_CTRL); + + local_irq_restore(flags); + + return 0; +} + +static void +orion_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev) +{ + unsigned long flags; + u32 u; + + local_irq_save(flags); + if (mode == CLOCK_EVT_MODE_PERIODIC) { + /* + * Setup timer to fire at 1/HZ intervals. + */ + writel(ticks_per_jiffy - 1, TIMER1_RELOAD); + writel(ticks_per_jiffy - 1, TIMER1_VAL); + + /* + * Enable timer interrupt. + */ + u = readl(BRIDGE_MASK); + writel(u | BRIDGE_INT_TIMER1, BRIDGE_MASK); + + /* + * Enable timer. + */ + u = readl(TIMER_CTRL); + writel(u | TIMER1_EN | TIMER1_RELOAD_EN, TIMER_CTRL); + } else { + /* + * Disable timer. + */ + u = readl(TIMER_CTRL); + writel(u & ~TIMER1_EN, TIMER_CTRL); + + /* + * Disable timer interrupt. + */ + u = readl(BRIDGE_MASK); + writel(u & ~BRIDGE_INT_TIMER1, BRIDGE_MASK); + + /* + * ACK pending timer interrupt. + */ + writel(~BRIDGE_INT_TIMER1, BRIDGE_CAUSE); + + } + local_irq_restore(flags); +} + +static struct clock_event_device orion_clkevt = { + .name = "orion_tick", + .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, + .shift = 32, + .rating = 300, + .cpumask = CPU_MASK_CPU0, + .set_next_event = orion_clkevt_next_event, + .set_mode = orion_clkevt_mode, +}; + +static irqreturn_t orion_timer_interrupt(int irq, void *dev_id) +{ + /* + * ACK timer interrupt and call event handler. + */ + writel(~BRIDGE_INT_TIMER1, BRIDGE_CAUSE); + orion_clkevt.event_handler(&orion_clkevt); + + return IRQ_HANDLED; +} + +static struct irqaction orion_timer_irq = { + .name = "orion_tick", + .flags = IRQF_DISABLED | IRQF_TIMER, + .handler = orion_timer_interrupt +}; + +void __init orion_time_init(unsigned int irq, unsigned int tclk) +{ + u32 u; + + ticks_per_jiffy = (tclk + HZ/2) / HZ; + + + /* + * Setup free-running clocksource timer (interrupts + * disabled.) + */ + writel(0xffffffff, TIMER0_VAL); + writel(0xffffffff, TIMER0_RELOAD); + u = readl(BRIDGE_MASK); + writel(u & ~BRIDGE_INT_TIMER0, BRIDGE_MASK); + u = readl(TIMER_CTRL); + writel(u | TIMER0_EN | TIMER0_RELOAD_EN, TIMER_CTRL); + orion_clksrc.mult = clocksource_hz2mult(tclk, orion_clksrc.shift); + clocksource_register(&orion_clksrc); + + + /* + * Setup clockevent timer (interrupt-driven.) + */ + setup_irq(irq, &orion_timer_irq); + orion_clkevt.mult = div_sc(tclk, NSEC_PER_SEC, orion_clkevt.shift); + orion_clkevt.max_delta_ns = clockevent_delta2ns(0xfffffffe, &orion_clkevt); + orion_clkevt.min_delta_ns = clockevent_delta2ns(1, &orion_clkevt); + clockevents_register_device(&orion_clkevt); +} |