forked from Minki/linux
ec8510f6fe
Patch from Lennert Buytenhek On the ixp23xx, the microengine thread interrupt sources are numbered 56..119, but their mask/status bits are located in bit positions 64..127 in the various registers in the interrupt controller (bit positions 56..63 are unused.) We don't deal with this, so currently, when asked to enable IRQ 64, we will enable IRQ 56 instead. The only interrupts >= 64 are the thread interrupt sources, and there are no in-tree users of those yet, so this is fortunately not a big problem, but this needs fixing anyway. Signed-off-by: Lennert Buytenhek <buytenh@wantstofly.org> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
444 lines
11 KiB
C
444 lines
11 KiB
C
/*
|
|
* arch/arm/mach-ixp23xx/core.c
|
|
*
|
|
* Core routines for IXP23xx chips
|
|
*
|
|
* Author: Deepak Saxena <dsaxena@plexity.net>
|
|
*
|
|
* Copyright 2005 (c) MontaVista Software, Inc.
|
|
*
|
|
* Based on 2.4 code Copyright 2004 (c) Intel Corporation
|
|
*
|
|
* 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/config.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/serial.h>
|
|
#include <linux/tty.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/serial.h>
|
|
#include <linux/serial_8250.h>
|
|
#include <linux/serial_core.h>
|
|
#include <linux/device.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/time.h>
|
|
#include <linux/timex.h>
|
|
|
|
#include <asm/types.h>
|
|
#include <asm/setup.h>
|
|
#include <asm/memory.h>
|
|
#include <asm/hardware.h>
|
|
#include <asm/mach-types.h>
|
|
#include <asm/irq.h>
|
|
#include <asm/system.h>
|
|
#include <asm/tlbflush.h>
|
|
#include <asm/pgtable.h>
|
|
|
|
#include <asm/mach/map.h>
|
|
#include <asm/mach/time.h>
|
|
#include <asm/mach/irq.h>
|
|
#include <asm/mach/arch.h>
|
|
|
|
|
|
/*************************************************************************
|
|
* Chip specific mappings shared by all IXP23xx systems
|
|
*************************************************************************/
|
|
static struct map_desc ixp23xx_io_desc[] __initdata = {
|
|
{ /* XSI-CPP CSRs */
|
|
.virtual = IXP23XX_XSI2CPP_CSR_VIRT,
|
|
.pfn = __phys_to_pfn(IXP23XX_XSI2CPP_CSR_PHYS),
|
|
.length = IXP23XX_XSI2CPP_CSR_SIZE,
|
|
.type = MT_DEVICE,
|
|
}, { /* Expansion Bus Config */
|
|
.virtual = IXP23XX_EXP_CFG_VIRT,
|
|
.pfn = __phys_to_pfn(IXP23XX_EXP_CFG_PHYS),
|
|
.length = IXP23XX_EXP_CFG_SIZE,
|
|
.type = MT_DEVICE,
|
|
}, { /* UART, Interrupt ctrl, GPIO, timers, NPEs, MACS,.... */
|
|
.virtual = IXP23XX_PERIPHERAL_VIRT,
|
|
.pfn = __phys_to_pfn(IXP23XX_PERIPHERAL_PHYS),
|
|
.length = IXP23XX_PERIPHERAL_SIZE,
|
|
.type = MT_DEVICE,
|
|
}, { /* CAP CSRs */
|
|
.virtual = IXP23XX_CAP_CSR_VIRT,
|
|
.pfn = __phys_to_pfn(IXP23XX_CAP_CSR_PHYS),
|
|
.length = IXP23XX_CAP_CSR_SIZE,
|
|
.type = MT_DEVICE,
|
|
}, { /* MSF CSRs */
|
|
.virtual = IXP23XX_MSF_CSR_VIRT,
|
|
.pfn = __phys_to_pfn(IXP23XX_MSF_CSR_PHYS),
|
|
.length = IXP23XX_MSF_CSR_SIZE,
|
|
.type = MT_DEVICE,
|
|
}, { /* PCI I/O Space */
|
|
.virtual = IXP23XX_PCI_IO_VIRT,
|
|
.pfn = __phys_to_pfn(IXP23XX_PCI_IO_PHYS),
|
|
.length = IXP23XX_PCI_IO_SIZE,
|
|
.type = MT_DEVICE,
|
|
}, { /* PCI Config Space */
|
|
.virtual = IXP23XX_PCI_CFG_VIRT,
|
|
.pfn = __phys_to_pfn(IXP23XX_PCI_CFG_PHYS),
|
|
.length = IXP23XX_PCI_CFG_SIZE,
|
|
.type = MT_DEVICE,
|
|
}, { /* PCI local CFG CSRs */
|
|
.virtual = IXP23XX_PCI_CREG_VIRT,
|
|
.pfn = __phys_to_pfn(IXP23XX_PCI_CREG_PHYS),
|
|
.length = IXP23XX_PCI_CREG_SIZE,
|
|
.type = MT_DEVICE,
|
|
}, { /* PCI MEM Space */
|
|
.virtual = IXP23XX_PCI_MEM_VIRT,
|
|
.pfn = __phys_to_pfn(IXP23XX_PCI_MEM_PHYS),
|
|
.length = IXP23XX_PCI_MEM_SIZE,
|
|
.type = MT_DEVICE,
|
|
}
|
|
};
|
|
|
|
void __init ixp23xx_map_io(void)
|
|
{
|
|
iotable_init(ixp23xx_io_desc, ARRAY_SIZE(ixp23xx_io_desc));
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
* IXP23xx Interrupt Handling
|
|
***************************************************************************/
|
|
enum ixp23xx_irq_type {
|
|
IXP23XX_IRQ_LEVEL, IXP23XX_IRQ_EDGE
|
|
};
|
|
|
|
static void ixp23xx_config_irq(unsigned int, enum ixp23xx_irq_type);
|
|
|
|
static int ixp23xx_irq_set_type(unsigned int irq, unsigned int type)
|
|
{
|
|
int line = irq - IRQ_IXP23XX_GPIO6 + 6;
|
|
u32 int_style;
|
|
enum ixp23xx_irq_type irq_type;
|
|
volatile u32 *int_reg;
|
|
|
|
/*
|
|
* Only GPIOs 6-15 are wired to interrupts on IXP23xx
|
|
*/
|
|
if (line < 6 || line > 15)
|
|
return -EINVAL;
|
|
|
|
switch (type) {
|
|
case IRQT_BOTHEDGE:
|
|
int_style = IXP23XX_GPIO_STYLE_TRANSITIONAL;
|
|
irq_type = IXP23XX_IRQ_EDGE;
|
|
break;
|
|
case IRQT_RISING:
|
|
int_style = IXP23XX_GPIO_STYLE_RISING_EDGE;
|
|
irq_type = IXP23XX_IRQ_EDGE;
|
|
break;
|
|
case IRQT_FALLING:
|
|
int_style = IXP23XX_GPIO_STYLE_FALLING_EDGE;
|
|
irq_type = IXP23XX_IRQ_EDGE;
|
|
break;
|
|
case IRQT_HIGH:
|
|
int_style = IXP23XX_GPIO_STYLE_ACTIVE_HIGH;
|
|
irq_type = IXP23XX_IRQ_LEVEL;
|
|
break;
|
|
case IRQT_LOW:
|
|
int_style = IXP23XX_GPIO_STYLE_ACTIVE_LOW;
|
|
irq_type = IXP23XX_IRQ_LEVEL;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
ixp23xx_config_irq(irq, irq_type);
|
|
|
|
if (line >= 8) { /* pins 8-15 */
|
|
line -= 8;
|
|
int_reg = (volatile u32 *)IXP23XX_GPIO_GPIT2R;
|
|
} else { /* pins 0-7 */
|
|
int_reg = (volatile u32 *)IXP23XX_GPIO_GPIT1R;
|
|
}
|
|
|
|
/*
|
|
* Clear pending interrupts
|
|
*/
|
|
*IXP23XX_GPIO_GPISR = (1 << line);
|
|
|
|
/* Clear the style for the appropriate pin */
|
|
*int_reg &= ~(IXP23XX_GPIO_STYLE_MASK <<
|
|
(line * IXP23XX_GPIO_STYLE_SIZE));
|
|
|
|
/* Set the new style */
|
|
*int_reg |= (int_style << (line * IXP23XX_GPIO_STYLE_SIZE));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ixp23xx_irq_mask(unsigned int irq)
|
|
{
|
|
volatile unsigned long *intr_reg;
|
|
|
|
if (irq >= 56)
|
|
irq += 8;
|
|
|
|
intr_reg = IXP23XX_INTR_EN1 + (irq / 32);
|
|
*intr_reg &= ~(1 << (irq % 32));
|
|
}
|
|
|
|
static void ixp23xx_irq_ack(unsigned int irq)
|
|
{
|
|
int line = irq - IRQ_IXP23XX_GPIO6 + 6;
|
|
|
|
if ((line < 6) || (line > 15))
|
|
return;
|
|
|
|
*IXP23XX_GPIO_GPISR = (1 << line);
|
|
}
|
|
|
|
/*
|
|
* Level triggered interrupts on GPIO lines can only be cleared when the
|
|
* interrupt condition disappears.
|
|
*/
|
|
static void ixp23xx_irq_level_unmask(unsigned int irq)
|
|
{
|
|
volatile unsigned long *intr_reg;
|
|
|
|
ixp23xx_irq_ack(irq);
|
|
|
|
if (irq >= 56)
|
|
irq += 8;
|
|
|
|
intr_reg = IXP23XX_INTR_EN1 + (irq / 32);
|
|
*intr_reg |= (1 << (irq % 32));
|
|
}
|
|
|
|
static void ixp23xx_irq_edge_unmask(unsigned int irq)
|
|
{
|
|
volatile unsigned long *intr_reg;
|
|
|
|
if (irq >= 56)
|
|
irq += 8;
|
|
|
|
intr_reg = IXP23XX_INTR_EN1 + (irq / 32);
|
|
*intr_reg |= (1 << (irq % 32));
|
|
}
|
|
|
|
static struct irqchip ixp23xx_irq_level_chip = {
|
|
.ack = ixp23xx_irq_mask,
|
|
.mask = ixp23xx_irq_mask,
|
|
.unmask = ixp23xx_irq_level_unmask,
|
|
.set_type = ixp23xx_irq_set_type
|
|
};
|
|
|
|
static struct irqchip ixp23xx_irq_edge_chip = {
|
|
.ack = ixp23xx_irq_ack,
|
|
.mask = ixp23xx_irq_mask,
|
|
.unmask = ixp23xx_irq_edge_unmask,
|
|
.set_type = ixp23xx_irq_set_type
|
|
};
|
|
|
|
static void ixp23xx_pci_irq_mask(unsigned int irq)
|
|
{
|
|
*IXP23XX_PCI_XSCALE_INT_ENABLE &= ~(1 << (IRQ_IXP23XX_INTA + 27 - irq));
|
|
}
|
|
|
|
static void ixp23xx_pci_irq_unmask(unsigned int irq)
|
|
{
|
|
*IXP23XX_PCI_XSCALE_INT_ENABLE |= (1 << (IRQ_IXP23XX_INTA + 27 - irq));
|
|
}
|
|
|
|
/*
|
|
* TODO: Should this just be done at ASM level?
|
|
*/
|
|
static void pci_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs)
|
|
{
|
|
u32 pci_interrupt;
|
|
unsigned int irqno;
|
|
struct irqdesc *int_desc;
|
|
|
|
pci_interrupt = *IXP23XX_PCI_XSCALE_INT_STATUS;
|
|
|
|
desc->chip->ack(irq);
|
|
|
|
/* See which PCI_INTA, or PCI_INTB interrupted */
|
|
if (pci_interrupt & (1 << 26)) {
|
|
irqno = IRQ_IXP23XX_INTB;
|
|
} else if (pci_interrupt & (1 << 27)) {
|
|
irqno = IRQ_IXP23XX_INTA;
|
|
} else {
|
|
BUG();
|
|
}
|
|
|
|
int_desc = irq_desc + irqno;
|
|
int_desc->handle(irqno, int_desc, regs);
|
|
|
|
desc->chip->unmask(irq);
|
|
}
|
|
|
|
static struct irqchip ixp23xx_pci_irq_chip = {
|
|
.ack = ixp23xx_pci_irq_mask,
|
|
.mask = ixp23xx_pci_irq_mask,
|
|
.unmask = ixp23xx_pci_irq_unmask
|
|
};
|
|
|
|
static void ixp23xx_config_irq(unsigned int irq, enum ixp23xx_irq_type type)
|
|
{
|
|
switch (type) {
|
|
case IXP23XX_IRQ_LEVEL:
|
|
set_irq_chip(irq, &ixp23xx_irq_level_chip);
|
|
set_irq_handler(irq, do_level_IRQ);
|
|
break;
|
|
case IXP23XX_IRQ_EDGE:
|
|
set_irq_chip(irq, &ixp23xx_irq_edge_chip);
|
|
set_irq_handler(irq, do_edge_IRQ);
|
|
break;
|
|
}
|
|
set_irq_flags(irq, IRQF_VALID);
|
|
}
|
|
|
|
void __init ixp23xx_init_irq(void)
|
|
{
|
|
int irq;
|
|
|
|
/* Route everything to IRQ */
|
|
*IXP23XX_INTR_SEL1 = 0x0;
|
|
*IXP23XX_INTR_SEL2 = 0x0;
|
|
*IXP23XX_INTR_SEL3 = 0x0;
|
|
*IXP23XX_INTR_SEL4 = 0x0;
|
|
|
|
/* Mask all sources */
|
|
*IXP23XX_INTR_EN1 = 0x0;
|
|
*IXP23XX_INTR_EN2 = 0x0;
|
|
*IXP23XX_INTR_EN3 = 0x0;
|
|
*IXP23XX_INTR_EN4 = 0x0;
|
|
|
|
/*
|
|
* Configure all IRQs for level-sensitive operation
|
|
*/
|
|
for (irq = 0; irq <= NUM_IXP23XX_RAW_IRQS; irq++) {
|
|
ixp23xx_config_irq(irq, IXP23XX_IRQ_LEVEL);
|
|
}
|
|
|
|
for (irq = IRQ_IXP23XX_INTA; irq <= IRQ_IXP23XX_INTB; irq++) {
|
|
set_irq_chip(irq, &ixp23xx_pci_irq_chip);
|
|
set_irq_handler(irq, do_level_IRQ);
|
|
set_irq_flags(irq, IRQF_VALID);
|
|
}
|
|
|
|
set_irq_chained_handler(IRQ_IXP23XX_PCI_INT_RPH, pci_handler);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
* Timer-tick functions for IXP23xx
|
|
*************************************************************************/
|
|
#define CLOCK_TICKS_PER_USEC CLOCK_TICK_RATE / (USEC_PER_SEC)
|
|
|
|
static unsigned long next_jiffy_time;
|
|
|
|
static unsigned long
|
|
ixp23xx_gettimeoffset(void)
|
|
{
|
|
unsigned long elapsed;
|
|
|
|
elapsed = *IXP23XX_TIMER_CONT - (next_jiffy_time - LATCH);
|
|
|
|
return elapsed / CLOCK_TICKS_PER_USEC;
|
|
}
|
|
|
|
static irqreturn_t
|
|
ixp23xx_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
|
|
{
|
|
/* Clear Pending Interrupt by writing '1' to it */
|
|
*IXP23XX_TIMER_STATUS = IXP23XX_TIMER1_INT_PEND;
|
|
while ((*IXP23XX_TIMER_CONT - next_jiffy_time) > LATCH) {
|
|
timer_tick(regs);
|
|
next_jiffy_time += LATCH;
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static struct irqaction ixp23xx_timer_irq = {
|
|
.name = "IXP23xx Timer Tick",
|
|
.handler = ixp23xx_timer_interrupt,
|
|
.flags = SA_INTERRUPT | SA_TIMER,
|
|
};
|
|
|
|
void __init ixp23xx_init_timer(void)
|
|
{
|
|
/* Clear Pending Interrupt by writing '1' to it */
|
|
*IXP23XX_TIMER_STATUS = IXP23XX_TIMER1_INT_PEND;
|
|
|
|
/* Setup the Timer counter value */
|
|
*IXP23XX_TIMER1_RELOAD =
|
|
(LATCH & ~IXP23XX_TIMER_RELOAD_MASK) | IXP23XX_TIMER_ENABLE;
|
|
|
|
*IXP23XX_TIMER_CONT = 0;
|
|
next_jiffy_time = LATCH;
|
|
|
|
/* Connect the interrupt handler and enable the interrupt */
|
|
setup_irq(IRQ_IXP23XX_TIMER1, &ixp23xx_timer_irq);
|
|
}
|
|
|
|
struct sys_timer ixp23xx_timer = {
|
|
.init = ixp23xx_init_timer,
|
|
.offset = ixp23xx_gettimeoffset,
|
|
};
|
|
|
|
|
|
/*************************************************************************
|
|
* IXP23xx Platform Initializaion
|
|
*************************************************************************/
|
|
static struct resource ixp23xx_uart_resources[] = {
|
|
{
|
|
.start = IXP23XX_UART1_PHYS,
|
|
.end = IXP23XX_UART1_PHYS + 0x0fff,
|
|
.flags = IORESOURCE_MEM
|
|
}, {
|
|
.start = IXP23XX_UART2_PHYS,
|
|
.end = IXP23XX_UART2_PHYS + 0x0fff,
|
|
.flags = IORESOURCE_MEM
|
|
}
|
|
};
|
|
|
|
static struct plat_serial8250_port ixp23xx_uart_data[] = {
|
|
{
|
|
.mapbase = IXP23XX_UART1_PHYS,
|
|
.membase = (char *)(IXP23XX_UART1_VIRT + 3),
|
|
.irq = IRQ_IXP23XX_UART1,
|
|
.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
|
|
.iotype = UPIO_MEM,
|
|
.regshift = 2,
|
|
.uartclk = IXP23XX_UART_XTAL,
|
|
}, {
|
|
.mapbase = IXP23XX_UART2_PHYS,
|
|
.membase = (char *)(IXP23XX_UART2_VIRT + 3),
|
|
.irq = IRQ_IXP23XX_UART2,
|
|
.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
|
|
.iotype = UPIO_MEM,
|
|
.regshift = 2,
|
|
.uartclk = IXP23XX_UART_XTAL,
|
|
},
|
|
{ },
|
|
};
|
|
|
|
static struct platform_device ixp23xx_uart = {
|
|
.name = "serial8250",
|
|
.id = 0,
|
|
.dev.platform_data = ixp23xx_uart_data,
|
|
.num_resources = 2,
|
|
.resource = ixp23xx_uart_resources,
|
|
};
|
|
|
|
static struct platform_device *ixp23xx_devices[] __initdata = {
|
|
&ixp23xx_uart,
|
|
};
|
|
|
|
void __init ixp23xx_sys_init(void)
|
|
{
|
|
platform_add_devices(ixp23xx_devices, ARRAY_SIZE(ixp23xx_devices));
|
|
}
|