diff options
Diffstat (limited to 'arch/arm/mach-integrator/core.c')
| -rw-r--r-- | arch/arm/mach-integrator/core.c | 347 |
1 files changed, 137 insertions, 210 deletions
diff --git a/arch/arm/mach-integrator/core.c b/arch/arm/mach-integrator/core.c index 20071a2767c..e3f3aca43ef 100644 --- a/arch/arm/mach-integrator/core.c +++ b/arch/arm/mach-integrator/core.c @@ -11,114 +11,39 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/device.h> +#include <linux/export.h> #include <linux/spinlock.h> #include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/memblock.h> #include <linux/sched.h> #include <linux/smp.h> #include <linux/amba/bus.h> +#include <linux/amba/serial.h> +#include <linux/io.h> +#include <linux/stat.h> +#include <linux/of.h> +#include <linux/of_address.h> -#include <asm/hardware.h> -#include <asm/irq.h> -#include <asm/io.h> -#include <asm/hardware/arm_timer.h> -#include <asm/arch/cm.h> -#include <asm/system.h> -#include <asm/leds.h> +#include <asm/mach-types.h> #include <asm/mach/time.h> +#include <asm/pgtable.h> +#include "hardware.h" +#include "cm.h" #include "common.h" -static struct amba_device rtc_device = { - .dev = { - .bus_id = "mb:15", - }, - .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 = { - .bus_id = "mb:16", - }, - .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 = { - .bus_id = "mb:17", - }, - .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 = { - .bus_id = "mb:18", - }, - .res = { - .start = KMI0_BASE, - .end = KMI0_BASE + SZ_4K - 1, - .flags = IORESOURCE_MEM, - }, - .irq = { IRQ_KMIINT0, NO_IRQ }, - .periphid = 0x00041050, -}; +static DEFINE_RAW_SPINLOCK(cm_lock); +static void __iomem *cm_base; -static struct amba_device kmi1_device = { - .dev = { - .bus_id = "mb:19", - }, - .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, -}; - -static int __init integrator_init(void) +/** + * cm_get - get the value from the CM_CTRL register + */ +u32 cm_get(void) { - int i; - - for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { - struct amba_device *d = amba_devs[i]; - amba_device_register(d, &iomem_resource); - } - - return 0; + return readl(cm_base + INTEGRATOR_HDR_CTRL_OFFSET); } -arch_initcall(integrator_init); - -#define CM_CTRL IO_ADDRESS(INTEGRATOR_HDR_BASE) + INTEGRATOR_HDR_CTRL_OFFSET - -static DEFINE_SPINLOCK(cm_lock); - /** * cm_control - update the CM_CTRL register. * @mask: bits to change @@ -129,147 +54,149 @@ 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); + raw_spin_lock_irqsave(&cm_lock, flags); + val = readl(cm_base + INTEGRATOR_HDR_CTRL_OFFSET) & ~mask; + writel(val | set, cm_base + INTEGRATOR_HDR_CTRL_OFFSET); + raw_spin_unlock_irqrestore(&cm_lock, flags); } -EXPORT_SYMBOL(cm_control); - -/* - * Where is the timer (VA)? - */ -#define TIMER0_VA_BASE (IO_ADDRESS(INTEGRATOR_CT_BASE)+0x00000000) -#define TIMER1_VA_BASE (IO_ADDRESS(INTEGRATOR_CT_BASE)+0x00000100) -#define TIMER2_VA_BASE (IO_ADDRESS(INTEGRATOR_CT_BASE)+0x00000200) -#define VA_IC_BASE IO_ADDRESS(INTEGRATOR_IC_BASE) - -/* - * 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; +static const char *integrator_arch_str(u32 id) +{ + switch ((id >> 16) & 0xff) { + case 0x00: + return "ASB little-endian"; + case 0x01: + return "AHB little-endian"; + case 0x03: + return "AHB-Lite system bus, bi-endian"; + case 0x04: + return "AHB"; + case 0x08: + return "AHB system bus, ASB processor bus"; + default: + return "Unknown"; + } +} -/* - * Returns number of ms since last clock interrupt. Note that interrupts - * will have been disabled by do_gettimeoffset() - */ -unsigned long integrator_gettimeoffset(void) +static const char *integrator_fpga_str(u32 id) { - unsigned long ticks1, ticks2, status; + switch ((id >> 12) & 0xf) { + case 0x01: + return "XC4062"; + case 0x02: + return "XC4085"; + case 0x03: + return "XVC600"; + case 0x04: + return "EPM7256AE (Altera PLD)"; + default: + return "Unknown"; + } +} - /* - * Get the current number of ticks. Note that there is a race - * condition between us reading the timer and checking for - * an interrupt. We get around this by ensuring that the - * counter has not reloaded between our two reads. - */ - ticks2 = readl(TIMER1_VA_BASE + TIMER_VALUE) & 0xffff; - do { - ticks1 = ticks2; - status = __raw_readl(VA_IC_BASE + IRQ_RAW_STATUS); - ticks2 = readl(TIMER1_VA_BASE + TIMER_VALUE) & 0xffff; - } while (ticks2 > ticks1); +void cm_clear_irqs(void) +{ + /* disable core module IRQs */ + writel(0xffffffffU, cm_base + INTEGRATOR_HDR_IC_OFFSET + + IRQ_ENABLE_CLEAR); +} - /* - * Number of ticks since last interrupt. - */ - ticks1 = timer_reload - ticks2; +static const struct of_device_id cm_match[] = { + { .compatible = "arm,core-module-integrator"}, + { }, +}; - /* - * Interrupt pending? If so, we've reloaded once already. - */ - if (status & (1 << IRQ_TIMERINT1)) - ticks1 += timer_reload; +void cm_init(void) +{ + struct device_node *cm = of_find_matching_node(NULL, cm_match); + u32 val; - /* - * Convert the ticks to usecs - */ - return TICKS2USECS(ticks1); + if (!cm) { + pr_crit("no core module node found in device tree\n"); + return; + } + cm_base = of_iomap(cm, 0); + if (!cm_base) { + pr_crit("could not remap core module\n"); + return; + } + cm_clear_irqs(); + val = readl(cm_base + INTEGRATOR_HDR_ID_OFFSET); + pr_info("Detected ARM core module:\n"); + pr_info(" Manufacturer: %02x\n", (val >> 24)); + pr_info(" Architecture: %s\n", integrator_arch_str(val)); + pr_info(" FPGA: %s\n", integrator_fpga_str(val)); + pr_info(" Build: %02x\n", (val >> 4) & 0xFF); + pr_info(" Rev: %c\n", ('A' + (val & 0x03))); } /* - * IRQ handler for the timer + * We need to stop things allocating the low memory; ideally we need a + * better implementation of GFP_DMA which does not assume that DMA-able + * memory starts at zero. */ -static irqreturn_t -integrator_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +void __init integrator_reserve(void) { - write_seqlock(&xtime_lock); + memblock_reserve(PHYS_OFFSET, __pa(swapper_pg_dir) - PHYS_OFFSET); +} - /* - * clear the interrupt - */ - writel(1, TIMER1_VA_BASE + TIMER_INTCLR); +/* + * To reset, we hit the on-board reset register in the system FPGA + */ +void integrator_restart(enum reboot_mode mode, const char *cmd) +{ + cm_control(CM_CTRL_RESET, CM_CTRL_RESET); +} - /* - * the clock tick routines are only processed on the - * primary CPU - */ - if (hard_smp_processor_id() == 0) { - timer_tick(regs); -#ifdef CONFIG_SMP - smp_send_timer(); -#endif - } +static u32 integrator_id; -#ifdef CONFIG_SMP - /* - * this is the ARM equivalent of the APIC timer interrupt - */ - update_process_times(user_mode(regs)); -#endif /* CONFIG_SMP */ +static ssize_t intcp_get_manf(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%02x\n", integrator_id >> 24); +} - write_sequnlock(&xtime_lock); +static struct device_attribute intcp_manf_attr = + __ATTR(manufacturer, S_IRUGO, intcp_get_manf, NULL); - return IRQ_HANDLED; +static ssize_t intcp_get_arch(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%s\n", integrator_arch_str(integrator_id)); } -static struct irqaction integrator_timer_irq = { - .name = "Integrator Timer Tick", - .flags = SA_INTERRUPT | SA_TIMER, - .handler = integrator_timer_interrupt, -}; +static struct device_attribute intcp_arch_attr = + __ATTR(architecture, S_IRUGO, intcp_get_arch, NULL); -/* - * Set up timer interrupt, and return the current time in seconds. - */ -void __init integrator_time_init(unsigned long reload, unsigned int ctrl) +static ssize_t intcp_get_fpga(struct device *dev, + struct device_attribute *attr, + char *buf) { - unsigned int timer_ctrl = TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC; + return sprintf(buf, "%s\n", integrator_fpga_str(integrator_id)); +} - timer_reload = reload; - timer_ctrl |= ctrl; +static struct device_attribute intcp_fpga_attr = + __ATTR(fpga, S_IRUGO, intcp_get_fpga, NULL); - if (timer_reload > 0x100000) { - timer_reload >>= 8; - timer_ctrl |= TIMER_CTRL_DIV256; - } else if (timer_reload > 0x010000) { - timer_reload >>= 4; - timer_ctrl |= TIMER_CTRL_DIV16; - } +static ssize_t intcp_get_build(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%02x\n", (integrator_id >> 4) & 0xFF); +} + +static struct device_attribute intcp_build_attr = + __ATTR(build, S_IRUGO, intcp_get_build, NULL); - /* - * Initialise to a known state (all timers off) - */ - writel(0, TIMER0_VA_BASE + TIMER_CTRL); - writel(0, TIMER1_VA_BASE + TIMER_CTRL); - writel(0, TIMER2_VA_BASE + TIMER_CTRL); - writel(timer_reload, TIMER1_VA_BASE + TIMER_LOAD); - writel(timer_reload, TIMER1_VA_BASE + TIMER_VALUE); - writel(timer_ctrl, TIMER1_VA_BASE + TIMER_CTRL); - /* - * Make irqs happen for the system timer - */ - setup_irq(IRQ_TIMERINT1, &integrator_timer_irq); +void integrator_init_sysfs(struct device *parent, u32 id) +{ + integrator_id = id; + device_create_file(parent, &intcp_manf_attr); + device_create_file(parent, &intcp_arch_attr); + device_create_file(parent, &intcp_fpga_attr); + device_create_file(parent, &intcp_build_attr); } |
