2005-04-17 00:20:36 +02:00
|
|
|
/*
|
|
|
|
* linux/arch/arm/mach-integrator/core.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 2000-2003 Deep Blue Solutions Ltd
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2, as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*/
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/spinlock.h>
|
|
|
|
#include <linux/interrupt.h>
|
2006-07-01 23:32:32 +02:00
|
|
|
#include <linux/irq.h>
|
2005-04-17 00:20:36 +02:00
|
|
|
#include <linux/sched.h>
|
2005-06-18 11:15:46 +02:00
|
|
|
#include <linux/smp.h>
|
2006-03-27 00:13:39 +02:00
|
|
|
#include <linux/termios.h>
|
2006-01-07 14:52:45 +01:00
|
|
|
#include <linux/amba/bus.h>
|
2006-03-27 00:13:39 +02:00
|
|
|
#include <linux/amba/serial.h>
|
2010-01-15 19:10:33 +01:00
|
|
|
#include <linux/clocksource.h>
|
2010-01-15 22:49:23 +01:00
|
|
|
#include <linux/clockchips.h>
|
2008-09-06 13:10:45 +02:00
|
|
|
#include <linux/io.h>
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2008-11-08 21:08:08 +01:00
|
|
|
#include <asm/clkdev.h>
|
|
|
|
#include <mach/clkdev.h>
|
2008-08-05 17:14:15 +02:00
|
|
|
#include <mach/hardware.h>
|
2010-01-14 20:59:37 +01:00
|
|
|
#include <mach/platform.h>
|
2005-04-17 00:20:36 +02:00
|
|
|
#include <asm/irq.h>
|
2005-06-29 16:15:54 +02:00
|
|
|
#include <asm/hardware/arm_timer.h>
|
2008-08-05 17:14:15 +02:00
|
|
|
#include <mach/cm.h>
|
2005-04-17 00:20:36 +02:00
|
|
|
#include <asm/system.h>
|
|
|
|
#include <asm/leds.h>
|
|
|
|
#include <asm/mach/time.h>
|
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
|
2006-03-27 00:13:39 +02:00
|
|
|
static struct amba_pl010_data integrator_uart_data;
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
static struct amba_device rtc_device = {
|
|
|
|
.dev = {
|
2009-01-06 19:44:43 +01:00
|
|
|
.init_name = "mb:15",
|
2005-04-17 00:20:36 +02:00
|
|
|
},
|
|
|
|
.res = {
|
|
|
|
.start = INTEGRATOR_RTC_BASE,
|
|
|
|
.end = INTEGRATOR_RTC_BASE + SZ_4K - 1,
|
|
|
|
.flags = IORESOURCE_MEM,
|
|
|
|
},
|
|
|
|
.irq = { IRQ_RTCINT, NO_IRQ },
|
|
|
|
.periphid = 0x00041030,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct amba_device uart0_device = {
|
|
|
|
.dev = {
|
2009-01-06 19:44:43 +01:00
|
|
|
.init_name = "mb:16",
|
2006-03-27 00:13:39 +02:00
|
|
|
.platform_data = &integrator_uart_data,
|
2005-04-17 00:20:36 +02:00
|
|
|
},
|
|
|
|
.res = {
|
|
|
|
.start = INTEGRATOR_UART0_BASE,
|
|
|
|
.end = INTEGRATOR_UART0_BASE + SZ_4K - 1,
|
|
|
|
.flags = IORESOURCE_MEM,
|
|
|
|
},
|
|
|
|
.irq = { IRQ_UARTINT0, NO_IRQ },
|
|
|
|
.periphid = 0x0041010,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct amba_device uart1_device = {
|
|
|
|
.dev = {
|
2009-01-06 19:44:43 +01:00
|
|
|
.init_name = "mb:17",
|
2006-03-27 00:13:39 +02:00
|
|
|
.platform_data = &integrator_uart_data,
|
2005-04-17 00:20:36 +02:00
|
|
|
},
|
|
|
|
.res = {
|
|
|
|
.start = INTEGRATOR_UART1_BASE,
|
|
|
|
.end = INTEGRATOR_UART1_BASE + SZ_4K - 1,
|
|
|
|
.flags = IORESOURCE_MEM,
|
|
|
|
},
|
|
|
|
.irq = { IRQ_UARTINT1, NO_IRQ },
|
|
|
|
.periphid = 0x0041010,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct amba_device kmi0_device = {
|
|
|
|
.dev = {
|
2009-01-06 19:44:43 +01:00
|
|
|
.init_name = "mb:18",
|
2005-04-17 00:20:36 +02:00
|
|
|
},
|
|
|
|
.res = {
|
|
|
|
.start = KMI0_BASE,
|
|
|
|
.end = KMI0_BASE + SZ_4K - 1,
|
|
|
|
.flags = IORESOURCE_MEM,
|
|
|
|
},
|
|
|
|
.irq = { IRQ_KMIINT0, NO_IRQ },
|
|
|
|
.periphid = 0x00041050,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct amba_device kmi1_device = {
|
|
|
|
.dev = {
|
2009-01-06 19:44:43 +01:00
|
|
|
.init_name = "mb:19",
|
2005-04-17 00:20:36 +02:00
|
|
|
},
|
|
|
|
.res = {
|
|
|
|
.start = KMI1_BASE,
|
|
|
|
.end = KMI1_BASE + SZ_4K - 1,
|
|
|
|
.flags = IORESOURCE_MEM,
|
|
|
|
},
|
|
|
|
.irq = { IRQ_KMIINT1, NO_IRQ },
|
|
|
|
.periphid = 0x00041050,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct amba_device *amba_devs[] __initdata = {
|
|
|
|
&rtc_device,
|
|
|
|
&uart0_device,
|
|
|
|
&uart1_device,
|
|
|
|
&kmi0_device,
|
|
|
|
&kmi1_device,
|
|
|
|
};
|
|
|
|
|
2008-11-08 21:08:08 +01:00
|
|
|
/*
|
|
|
|
* These are fixed clocks.
|
|
|
|
*/
|
|
|
|
static struct clk clk24mhz = {
|
|
|
|
.rate = 24000000,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct clk uartclk = {
|
|
|
|
.rate = 14745600,
|
|
|
|
};
|
|
|
|
|
2009-05-18 18:26:08 +02:00
|
|
|
static struct clk_lookup lookups[] = {
|
2008-11-08 21:08:08 +01:00
|
|
|
{ /* UART0 */
|
|
|
|
.dev_id = "mb:16",
|
|
|
|
.clk = &uartclk,
|
|
|
|
}, { /* UART1 */
|
|
|
|
.dev_id = "mb:17",
|
|
|
|
.clk = &uartclk,
|
|
|
|
}, { /* KMI0 */
|
|
|
|
.dev_id = "mb:18",
|
|
|
|
.clk = &clk24mhz,
|
|
|
|
}, { /* KMI1 */
|
|
|
|
.dev_id = "mb:19",
|
|
|
|
.clk = &clk24mhz,
|
|
|
|
}, { /* MMCI - IntegratorCP */
|
|
|
|
.dev_id = "mb:1c",
|
|
|
|
.clk = &uartclk,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
static int __init integrator_init(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2010-01-12 13:28:00 +01:00
|
|
|
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
|
2008-11-08 21:08:08 +01:00
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
|
|
|
|
struct amba_device *d = amba_devs[i];
|
|
|
|
amba_device_register(d, &iomem_resource);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
arch_initcall(integrator_init);
|
|
|
|
|
2006-03-27 00:13:39 +02:00
|
|
|
/*
|
|
|
|
* On the Integrator platform, the port RTS and DTR are provided by
|
|
|
|
* bits in the following SC_CTRLS register bits:
|
|
|
|
* RTS DTR
|
|
|
|
* UART0 7 6
|
|
|
|
* UART1 5 4
|
|
|
|
*/
|
2010-01-17 21:45:12 +01:00
|
|
|
#define SC_CTRLC IO_ADDRESS(INTEGRATOR_SC_CTRLC)
|
|
|
|
#define SC_CTRLS IO_ADDRESS(INTEGRATOR_SC_CTRLS)
|
2006-03-27 00:13:39 +02:00
|
|
|
|
|
|
|
static void integrator_uart_set_mctrl(struct amba_device *dev, void __iomem *base, unsigned int mctrl)
|
|
|
|
{
|
|
|
|
unsigned int ctrls = 0, ctrlc = 0, rts_mask, dtr_mask;
|
|
|
|
|
|
|
|
if (dev == &uart0_device) {
|
|
|
|
rts_mask = 1 << 4;
|
|
|
|
dtr_mask = 1 << 5;
|
|
|
|
} else {
|
|
|
|
rts_mask = 1 << 6;
|
|
|
|
dtr_mask = 1 << 7;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mctrl & TIOCM_RTS)
|
|
|
|
ctrlc |= rts_mask;
|
|
|
|
else
|
|
|
|
ctrls |= rts_mask;
|
|
|
|
|
|
|
|
if (mctrl & TIOCM_DTR)
|
|
|
|
ctrlc |= dtr_mask;
|
|
|
|
else
|
|
|
|
ctrls |= dtr_mask;
|
|
|
|
|
|
|
|
__raw_writel(ctrls, SC_CTRLS);
|
|
|
|
__raw_writel(ctrlc, SC_CTRLC);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct amba_pl010_data integrator_uart_data = {
|
|
|
|
.set_mctrl = integrator_uart_set_mctrl,
|
|
|
|
};
|
|
|
|
|
2010-01-17 21:45:12 +01:00
|
|
|
#define CM_CTRL IO_ADDRESS(INTEGRATOR_HDR_CTRL)
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
static DEFINE_SPINLOCK(cm_lock);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* cm_control - update the CM_CTRL register.
|
|
|
|
* @mask: bits to change
|
|
|
|
* @set: bits to set
|
|
|
|
*/
|
|
|
|
void cm_control(u32 mask, u32 set)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&cm_lock, flags);
|
|
|
|
val = readl(CM_CTRL) & ~mask;
|
|
|
|
writel(val | set, CM_CTRL);
|
|
|
|
spin_unlock_irqrestore(&cm_lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT_SYMBOL(cm_control);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Where is the timer (VA)?
|
|
|
|
*/
|
2010-01-17 21:45:12 +01:00
|
|
|
#define TIMER0_VA_BASE IO_ADDRESS(INTEGRATOR_TIMER0_BASE)
|
|
|
|
#define TIMER1_VA_BASE IO_ADDRESS(INTEGRATOR_TIMER1_BASE)
|
|
|
|
#define TIMER2_VA_BASE IO_ADDRESS(INTEGRATOR_TIMER2_BASE)
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* How long is the timer interval?
|
|
|
|
*/
|
|
|
|
#define TIMER_INTERVAL (TICKS_PER_uSEC * mSEC_10)
|
|
|
|
#if TIMER_INTERVAL >= 0x100000
|
|
|
|
#define TICKS2USECS(x) (256 * (x) / TICKS_PER_uSEC)
|
|
|
|
#elif TIMER_INTERVAL >= 0x10000
|
|
|
|
#define TICKS2USECS(x) (16 * (x) / TICKS_PER_uSEC)
|
|
|
|
#else
|
|
|
|
#define TICKS2USECS(x) ((x) / TICKS_PER_uSEC)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static unsigned long timer_reload;
|
|
|
|
|
2010-01-15 19:10:33 +01:00
|
|
|
static void __iomem * const clksrc_base = (void __iomem *)TIMER2_VA_BASE;
|
|
|
|
|
|
|
|
static cycle_t timersp_read(struct clocksource *cs)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2010-01-15 19:10:33 +01:00
|
|
|
return ~(readl(clksrc_base + TIMER_VALUE) & 0xffff);
|
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2010-01-15 19:10:33 +01:00
|
|
|
static struct clocksource clocksource_timersp = {
|
|
|
|
.name = "timer2",
|
|
|
|
.rating = 200,
|
|
|
|
.read = timersp_read,
|
|
|
|
.mask = CLOCKSOURCE_MASK(16),
|
|
|
|
.shift = 16,
|
|
|
|
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
|
|
|
};
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2010-01-15 19:10:33 +01:00
|
|
|
static void integrator_clocksource_init(u32 khz)
|
|
|
|
{
|
|
|
|
struct clocksource *cs = &clocksource_timersp;
|
|
|
|
void __iomem *base = clksrc_base;
|
|
|
|
u32 ctrl = TIMER_CTRL_ENABLE;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2010-01-15 19:10:33 +01:00
|
|
|
if (khz >= 1500) {
|
|
|
|
khz /= 16;
|
|
|
|
ctrl = TIMER_CTRL_DIV16;
|
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2010-01-15 19:10:33 +01:00
|
|
|
writel(ctrl, base + TIMER_CTRL);
|
|
|
|
writel(0xffff, base + TIMER_LOAD);
|
|
|
|
|
|
|
|
cs->mult = clocksource_khz2mult(khz, cs->shift);
|
|
|
|
clocksource_register(cs);
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
2010-01-15 22:49:23 +01:00
|
|
|
static void __iomem * const clkevt_base = (void __iomem *)TIMER1_VA_BASE;
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
/*
|
|
|
|
* IRQ handler for the timer
|
|
|
|
*/
|
2010-01-15 22:49:23 +01:00
|
|
|
static irqreturn_t integrator_timer_interrupt(int irq, void *dev_id)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2010-01-15 22:49:23 +01:00
|
|
|
struct clock_event_device *evt = dev_id;
|
|
|
|
|
|
|
|
/* clear the interrupt */
|
|
|
|
writel(1, clkevt_base + TIMER_INTCLR);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2010-01-15 22:49:23 +01:00
|
|
|
evt->event_handler(evt);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
2010-01-15 22:49:23 +01:00
|
|
|
static void clkevt_set_mode(enum clock_event_mode mode, struct clock_event_device *evt)
|
|
|
|
{
|
|
|
|
u32 ctrl = readl(clkevt_base + TIMER_CTRL) & ~TIMER_CTRL_ENABLE;
|
|
|
|
|
|
|
|
BUG_ON(mode == CLOCK_EVT_MODE_ONESHOT);
|
|
|
|
|
|
|
|
if (mode == CLOCK_EVT_MODE_PERIODIC) {
|
|
|
|
writel(ctrl, clkevt_base + TIMER_CTRL);
|
|
|
|
writel(timer_reload, clkevt_base + TIMER_LOAD);
|
|
|
|
ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
writel(ctrl, clkevt_base + TIMER_CTRL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int clkevt_set_next_event(unsigned long next, struct clock_event_device *evt)
|
|
|
|
{
|
|
|
|
unsigned long ctrl = readl(clkevt_base + TIMER_CTRL);
|
|
|
|
|
|
|
|
writel(ctrl & ~TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL);
|
|
|
|
writel(next, clkevt_base + TIMER_LOAD);
|
|
|
|
writel(ctrl | TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct clock_event_device integrator_clockevent = {
|
|
|
|
.name = "timer1",
|
|
|
|
.shift = 34,
|
|
|
|
.features = CLOCK_EVT_FEAT_PERIODIC,
|
|
|
|
.set_mode = clkevt_set_mode,
|
|
|
|
.set_next_event = clkevt_set_next_event,
|
|
|
|
.rating = 300,
|
|
|
|
.cpumask = cpu_all_mask,
|
|
|
|
};
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
static struct irqaction integrator_timer_irq = {
|
2010-01-15 22:49:23 +01:00
|
|
|
.name = "timer",
|
2007-05-08 09:35:39 +02:00
|
|
|
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
|
2005-06-26 18:06:36 +02:00
|
|
|
.handler = integrator_timer_interrupt,
|
2010-01-15 22:49:23 +01:00
|
|
|
.dev_id = &integrator_clockevent,
|
2005-04-17 00:20:36 +02:00
|
|
|
};
|
|
|
|
|
2010-01-15 22:49:23 +01:00
|
|
|
static void integrator_clockevent_init(u32 khz, unsigned int ctrl)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2010-01-15 22:49:23 +01:00
|
|
|
struct clock_event_device *evt = &integrator_clockevent;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2010-01-15 22:49:23 +01:00
|
|
|
if (khz * 1000 > 0x100000 * HZ) {
|
|
|
|
khz /= 256;
|
|
|
|
ctrl |= TIMER_CTRL_DIV256;
|
|
|
|
} else if (khz * 1000 > 0x10000 * HZ) {
|
|
|
|
khz /= 16;
|
|
|
|
ctrl |= TIMER_CTRL_DIV16;
|
|
|
|
}
|
2010-01-15 19:10:33 +01:00
|
|
|
|
2010-01-15 22:49:23 +01:00
|
|
|
timer_reload = khz * 1000 / HZ;
|
|
|
|
writel(ctrl, clkevt_base + TIMER_CTRL);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2010-01-15 22:49:23 +01:00
|
|
|
evt->irq = IRQ_TIMERINT1;
|
|
|
|
evt->mult = div_sc(khz, NSEC_PER_MSEC, evt->shift);
|
|
|
|
evt->max_delta_ns = clockevent_delta2ns(0xffff, evt);
|
|
|
|
evt->min_delta_ns = clockevent_delta2ns(0xf, evt);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2010-01-15 22:49:23 +01:00
|
|
|
setup_irq(IRQ_TIMERINT1, &integrator_timer_irq);
|
|
|
|
clockevents_register_device(evt);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up timer(s).
|
|
|
|
*/
|
2010-01-15 23:12:46 +01:00
|
|
|
void __init integrator_time_init(u32 khz, unsigned int ctrl)
|
2010-01-15 22:49:23 +01:00
|
|
|
{
|
2005-06-29 16:15:54 +02:00
|
|
|
writel(0, TIMER0_VA_BASE + TIMER_CTRL);
|
|
|
|
writel(0, TIMER1_VA_BASE + TIMER_CTRL);
|
|
|
|
writel(0, TIMER2_VA_BASE + TIMER_CTRL);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2010-01-15 23:12:46 +01:00
|
|
|
integrator_clocksource_init(khz);
|
|
|
|
integrator_clockevent_init(khz, ctrl);
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|