Commit 78818e47 authored by Vitaly Wool's avatar Vitaly Wool Committed by Russell King

[ARM] 3466/1: [2/3] Support for Philips PNX4008 platform: chip support

Patch from Vitaly Wool

This patch adds basic chip support for PNX4008 ARM platform.
It's basically the same as the previous one, but with the rmk's
comments taken into account.
Signed-off-by: default avatarVitaly Wool <vwool@ru.mvista.com>
Signed-off-by: default avatarDmitry Pervushin <dpervushin@gmail.com>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent a5a50303
...@@ -270,6 +270,11 @@ config ARCH_AT91RM9200 ...@@ -270,6 +270,11 @@ config ARCH_AT91RM9200
Say Y here if you intend to run this kernel on an Atmel Say Y here if you intend to run this kernel on an Atmel
AT91RM9200-based board. AT91RM9200-based board.
config ARCH_PNX4008
bool "Philips Nexperia PNX4008 Mobile"
help
This enables support for Philips PNX4008 mobile platform.
endchoice endchoice
source "arch/arm/mach-clps711x/Kconfig" source "arch/arm/mach-clps711x/Kconfig"
......
...@@ -116,6 +116,7 @@ endif ...@@ -116,6 +116,7 @@ endif
machine-$(CONFIG_ARCH_REALVIEW) := realview machine-$(CONFIG_ARCH_REALVIEW) := realview
machine-$(CONFIG_ARCH_AT91RM9200) := at91rm9200 machine-$(CONFIG_ARCH_AT91RM9200) := at91rm9200
machine-$(CONFIG_ARCH_EP93XX) := ep93xx machine-$(CONFIG_ARCH_EP93XX) := ep93xx
machine-$(CONFIG_ARCH_PNX4008) := pnx4008
ifeq ($(CONFIG_ARCH_EBSA110),y) ifeq ($(CONFIG_ARCH_EBSA110),y)
# This is what happens if you forget the IOCS16 line. # This is what happens if you forget the IOCS16 line.
......
#
# Makefile for the linux kernel.
#
obj-y := core.o irq.o time.o clock.o gpio.o serial.o dma.o
obj-m :=
obj-n :=
obj- :=
# Power Management
obj-$(CONFIG_PM) += pm.o sleep.o
zreladdr-y := 0x80008000
params_phys-y := 0x80000100
initrd_phys-y := 0x80800000
This diff is collapsed.
/*
* arch/arm/mach-pnx4008/clock.h
*
* Clock control driver for PNX4008 - internal header file
*
* Author: Vitaly Wool <source@mvista.com>
*
* 2006 (c) MontaVista Software, Inc. 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 __ARCH_ARM_PNX4008_CLOCK_H__
#define __ARCH_ARM_PNX4008_CLOCK_H__
struct clk {
struct list_head node;
struct module *owner;
const char *name;
struct clk *parent;
struct clk *propagate_next;
u32 rate;
u32 user_rate;
s8 usecount;
u32 flags;
u32 scale_reg;
u8 enable_shift;
u32 enable_reg;
u8 enable_shift1;
u32 enable_reg1;
u32 parent_switch_reg;
u32(*round_rate) (struct clk *, u32);
int (*set_rate) (struct clk *, u32);
int (*set_parent) (struct clk * clk, struct clk * parent);
};
/* Flags */
#define RATE_PROPAGATES (1<<0)
#define NEEDS_INITIALIZATION (1<<1)
#define PARENT_SET_RATE (1<<2)
#define FIXED_RATE (1<<3)
#endif
/*
* arch/arm/mach-pnx4008/core.c
*
* PNX4008 core startup code
*
* Authors: Vitaly Wool, Dmitry Chigirev,
* Grigory Tolstolytkin, Dmitry Pervushin <source@mvista.com>
*
* Based on reference code received from Philips:
* Copyright (C) 2003 Philips Semiconductors
*
* 2005 (c) MontaVista Software, Inc. 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/types.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/serial_8250.h>
#include <linux/device.h>
#include <linux/spi/spi.h>
#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/setup.h>
#include <asm/mach-types.h>
#include <asm/pgtable.h>
#include <asm/page.h>
#include <asm/system.h>
#include <asm/mach/arch.h>
#include <asm/mach/irq.h>
#include <asm/mach/map.h>
#include <asm/mach/time.h>
#include <asm/arch/irq.h>
#include <asm/arch/clock.h>
#include <asm/arch/dma.h>
struct resource spipnx_0_resources[] = {
{
.start = PNX4008_SPI1_BASE,
.end = PNX4008_SPI1_BASE + SZ_4K,
.flags = IORESOURCE_MEM,
}, {
.start = PER_SPI1_REC_XMIT,
.flags = IORESOURCE_DMA,
}, {
.start = SPI1_INT,
.flags = IORESOURCE_IRQ,
}, {
.flags = 0,
},
};
struct resource spipnx_1_resources[] = {
{
.start = PNX4008_SPI2_BASE,
.end = PNX4008_SPI2_BASE + SZ_4K,
.flags = IORESOURCE_MEM,
}, {
.start = PER_SPI2_REC_XMIT,
.flags = IORESOURCE_DMA,
}, {
.start = SPI2_INT,
.flags = IORESOURCE_IRQ,
}, {
.flags = 0,
}
};
static struct spi_board_info spi_board_info[] __initdata = {
{
.modalias = "m25p80",
.max_speed_hz = 1000000,
.bus_num = 1,
.chip_select = 0,
},
};
static struct platform_device spipnx_1 = {
.name = "spipnx",
.id = 1,
.num_resources = ARRAY_SIZE(spipnx_0_resources),
.resource = spipnx_0_resources,
.dev = {
.coherent_dma_mask = 0xFFFFFFFF,
},
};
static struct platform_device spipnx_2 = {
.name = "spipnx",
.id = 2,
.num_resources = ARRAY_SIZE(spipnx_1_resources),
.resource = spipnx_1_resources,
.dev = {
.coherent_dma_mask = 0xFFFFFFFF,
},
};
static struct plat_serial8250_port platform_serial_ports[] = {
{
.membase = (void *)__iomem(IO_ADDRESS(PNX4008_UART5_BASE)),
.mapbase = (unsigned long)PNX4008_UART5_BASE,
.irq = IIR5_INT,
.uartclk = PNX4008_UART_CLK,
.regshift = 2,
.iotype = UPIO_MEM,
.flags = UPF_BOOT_AUTOCONF | UPF_BUGGY_UART | UPF_SKIP_TEST,
},
{
.membase = (void *)__iomem(IO_ADDRESS(PNX4008_UART3_BASE)),
.mapbase = (unsigned long)PNX4008_UART3_BASE,
.irq = IIR3_INT,
.uartclk = PNX4008_UART_CLK,
.regshift = 2,
.iotype = UPIO_MEM,
.flags = UPF_BOOT_AUTOCONF | UPF_BUGGY_UART | UPF_SKIP_TEST,
},
{}
};
static struct platform_device serial_device = {
.name = "serial8250",
.id = PLAT8250_DEV_PLATFORM,
.dev = {
.platform_data = &platform_serial_ports,
},
};
static struct platform_device *devices[] __initdata = {
&spipnx_1,
&spipnx_2,
&serial_device,
};
extern void pnx4008_uart_init(void);
static void __init pnx4008_init(void)
{
/*disable all START interrupt sources,
and clear all START interrupt flags */
__raw_writel(0, START_INT_ER_REG(SE_PIN_BASE_INT));
__raw_writel(0, START_INT_ER_REG(SE_INT_BASE_INT));
__raw_writel(0xffffffff, START_INT_RSR_REG(SE_PIN_BASE_INT));
__raw_writel(0xffffffff, START_INT_RSR_REG(SE_INT_BASE_INT));
platform_add_devices(devices, ARRAY_SIZE(devices));
spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
/* Switch on the UART clocks */
pnx4008_uart_init();
}
static struct map_desc pnx4008_io_desc[] __initdata = {
{
.virtual = IO_ADDRESS(PNX4008_IRAM_BASE),
.pfn = __phys_to_pfn(PNX4008_IRAM_BASE),
.length = SZ_64K,
.type = MT_DEVICE,
}, {
.virtual = IO_ADDRESS(PNX4008_NDF_FLASH_BASE),
.pfn = __phys_to_pfn(PNX4008_NDF_FLASH_BASE),
.length = SZ_1M - SZ_128K,
.type = MT_DEVICE,
}, {
.virtual = IO_ADDRESS(PNX4008_JPEG_CONFIG_BASE),
.pfn = __phys_to_pfn(PNX4008_JPEG_CONFIG_BASE),
.length = SZ_128K * 3,
.type = MT_DEVICE,
}, {
.virtual = IO_ADDRESS(PNX4008_DMA_CONFIG_BASE),
.pfn = __phys_to_pfn(PNX4008_DMA_CONFIG_BASE),
.length = SZ_1M,
.type = MT_DEVICE,
}, {
.virtual = IO_ADDRESS(PNX4008_AHB2FAB_BASE),
.pfn = __phys_to_pfn(PNX4008_AHB2FAB_BASE),
.length = SZ_1M,
.type = MT_DEVICE,
},
};
void __init pnx4008_map_io(void)
{
iotable_init(pnx4008_io_desc, ARRAY_SIZE(pnx4008_io_desc));
}
extern struct sys_timer pnx4008_timer;
MACHINE_START(PNX4008, "Philips PNX4008")
/* Maintainer: MontaVista Software Inc. */
.phys_io = 0x40090000,
.io_pg_offst = (0xf4090000 >> 18) & 0xfffc,
.boot_params = 0x80000100,
.map_io = pnx4008_map_io,
.init_irq = pnx4008_init_irq,
.init_machine = pnx4008_init,
.timer = &pnx4008_timer,
MACHINE_END
This diff is collapsed.
/*
* arch/arm/mach-pnx4008/gpio.c
*
* PNX4008 GPIO driver
*
* Author: Dmitry Chigirev <source@mvista.com>
*
* Based on reference code by Iwo Mergler and Z.Tabaaloute from Philips:
* Copyright (c) 2005 Koninklijke Philips Electronics N.V.
*
* 2005 (c) MontaVista Software, Inc. 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/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/semaphore.h>
#include <asm/io.h>
#include <asm/arch/platform.h>
#include <asm/arch/gpio.h>
/* register definitions */
#define PIO_VA_BASE IO_ADDRESS(PNX4008_PIO_BASE)
#define PIO_INP_STATE (0x00U)
#define PIO_OUTP_SET (0x04U)
#define PIO_OUTP_CLR (0x08U)
#define PIO_OUTP_STATE (0x0CU)
#define PIO_DRV_SET (0x10U)
#define PIO_DRV_CLR (0x14U)
#define PIO_DRV_STATE (0x18U)
#define PIO_SDINP_STATE (0x1CU)
#define PIO_SDOUTP_SET (0x20U)
#define PIO_SDOUTP_CLR (0x24U)
#define PIO_MUX_SET (0x28U)
#define PIO_MUX_CLR (0x2CU)
#define PIO_MUX_STATE (0x30U)
static inline void gpio_lock(void)
{
local_irq_disable();
}
static inline void gpio_unlock(void)
{
local_irq_enable();
}
/* Inline functions */
static inline int gpio_read_bit(u32 reg, int gpio)
{
u32 bit, val;
int ret = -EFAULT;
if (gpio < 0)
goto out;
bit = GPIO_BIT(gpio);
if (bit) {
val = __raw_readl(PIO_VA_BASE + reg);
ret = (val & bit) ? 1 : 0;
}
out:
return ret;
}
static inline int gpio_set_bit(u32 reg, int gpio)
{
u32 bit, val;
int ret = -EFAULT;
if (gpio < 0)
goto out;
bit = GPIO_BIT(gpio);
if (bit) {
val = __raw_readl(PIO_VA_BASE + reg);
val |= bit;
__raw_writel(val, PIO_VA_BASE + reg);
ret = 0;
}
out:
return ret;
}
/* Very simple access control, bitmap for allocated/free */
static unsigned long access_map[4];
#define INP_INDEX 0
#define OUTP_INDEX 1
#define GPIO_INDEX 2
#define MUX_INDEX 3
/*GPIO to Input Mapping */
static short gpio_to_inp_map[32] = {
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, 10, 11, 12, 13, 14, 24, -1
};
/*GPIO to Mux Mapping */
static short gpio_to_mux_map[32] = {
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, 0, 1, 4, 5, -1
};
/*Output to Mux Mapping */
static short outp_to_mux_map[32] = {
-1, -1, -1, 6, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, 2, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1
};
int pnx4008_gpio_register_pin(unsigned short pin)
{
unsigned long bit = GPIO_BIT(pin);
int ret = -EBUSY; /* Already in use */
gpio_lock();
if (GPIO_ISBID(pin)) {
if (access_map[GPIO_INDEX] & bit)
goto out;
access_map[GPIO_INDEX] |= bit;
} else if (GPIO_ISRAM(pin)) {
if (access_map[GPIO_INDEX] & bit)
goto out;
access_map[GPIO_INDEX] |= bit;
} else if (GPIO_ISMUX(pin)) {
if (access_map[MUX_INDEX] & bit)
goto out;
access_map[MUX_INDEX] |= bit;
} else if (GPIO_ISOUT(pin)) {
if (access_map[OUTP_INDEX] & bit)
goto out;
access_map[OUTP_INDEX] |= bit;
} else if (GPIO_ISIN(pin)) {
if (access_map[INP_INDEX] & bit)
goto out;
access_map[INP_INDEX] |= bit;
} else
goto out;
ret = 0;
out:
gpio_unlock();
return ret;
}
EXPORT_SYMBOL(pnx4008_gpio_register_pin);
int pnx4008_gpio_unregister_pin(unsigned short pin)
{
unsigned long bit = GPIO_BIT(pin);
int ret = -EFAULT; /* Not registered */
gpio_lock();
if (GPIO_ISBID(pin)) {
if (~access_map[GPIO_INDEX] & bit)
goto out;
access_map[GPIO_INDEX] &= ~bit;
} else if (GPIO_ISRAM(pin)) {
if (~access_map[GPIO_INDEX] & bit)
goto out;
access_map[GPIO_INDEX] &= ~bit;
} else if (GPIO_ISMUX(pin)) {
if (~access_map[MUX_INDEX] & bit)
goto out;
access_map[MUX_INDEX] &= ~bit;
} else if (GPIO_ISOUT(pin)) {
if (~access_map[OUTP_INDEX] & bit)
goto out;
access_map[OUTP_INDEX] &= ~bit;
} else if (GPIO_ISIN(pin)) {
if (~access_map[INP_INDEX] & bit)
goto out;
access_map[INP_INDEX] &= ~bit;
} else
goto out;
ret = 0;
out:
gpio_unlock();
return ret;
}
EXPORT_SYMBOL(pnx4008_gpio_unregister_pin);
unsigned long pnx4008_gpio_read_pin(unsigned short pin)
{
unsigned long ret = -EFAULT;
int gpio = GPIO_BIT_MASK(pin);
gpio_lock();
if (GPIO_ISOUT(pin)) {
ret = gpio_read_bit(PIO_OUTP_STATE, gpio);
} else if (GPIO_ISRAM(pin)) {
if (gpio_read_bit(PIO_DRV_STATE, gpio) == 0) {
ret = gpio_read_bit(PIO_SDINP_STATE, gpio);
}
} else if (GPIO_ISBID(pin)) {
ret = gpio_read_bit(PIO_DRV_STATE, gpio);
if (ret > 0)
ret = gpio_read_bit(PIO_OUTP_STATE, gpio);
else if (ret == 0)
ret =
gpio_read_bit(PIO_INP_STATE, gpio_to_inp_map[gpio]);
} else if (GPIO_ISIN(pin)) {
ret = gpio_read_bit(PIO_INP_STATE, gpio);
}
gpio_unlock();
return ret;
}
EXPORT_SYMBOL(pnx4008_gpio_read_pin);
/* Write Value to output */
int pnx4008_gpio_write_pin(unsigned short pin, int output)
{
int gpio = GPIO_BIT_MASK(pin);
int ret = -EFAULT;
gpio_lock();
if (GPIO_ISOUT(pin)) {
printk( "writing '%x' to '%x'\n",
gpio, output ? PIO_OUTP_SET : PIO_OUTP_CLR );
ret = gpio_set_bit(output ? PIO_OUTP_SET : PIO_OUTP_CLR, gpio);
} else if (GPIO_ISRAM(pin)) {
if (gpio_read_bit(PIO_DRV_STATE, gpio) > 0)
ret = gpio_set_bit(output ? PIO_SDOUTP_SET :
PIO_SDOUTP_CLR, gpio);
} else if (GPIO_ISBID(pin)) {
if (gpio_read_bit(PIO_DRV_STATE, gpio) > 0)
ret = gpio_set_bit(output ? PIO_OUTP_SET :
PIO_OUTP_CLR, gpio);
}
gpio_unlock();
return ret;
}
EXPORT_SYMBOL(pnx4008_gpio_write_pin);
/* Value = 1 : Set GPIO pin as output */
/* Value = 0 : Set GPIO pin as input */
int pnx4008_gpio_set_pin_direction(unsigned short pin, int output)
{
int gpio = GPIO_BIT_MASK(pin);
int ret = -EFAULT;
gpio_lock();
if (GPIO_ISBID(pin) || GPIO_ISRAM(pin)) {
ret = gpio_set_bit(output ? PIO_DRV_SET : PIO_DRV_CLR, gpio);
}
gpio_unlock();
return ret;
}
EXPORT_SYMBOL(pnx4008_gpio_set_pin_direction);
/* Read GPIO pin direction: 0= pin used as input, 1= pin used as output*/
int pnx4008_gpio_read_pin_direction(unsigned short pin)
{
int gpio = GPIO_BIT_MASK(pin);
int ret = -EFAULT;
gpio_lock();
if (GPIO_ISBID(pin) || GPIO_ISRAM(pin)) {
ret = gpio_read_bit(PIO_DRV_STATE, gpio);
}
gpio_unlock();
return ret;
}
EXPORT_SYMBOL(pnx4008_gpio_read_pin_direction);
/* Value = 1 : Set pin to muxed function */
/* Value = 0 : Set pin as GPIO */
int pnx4008_gpio_set_pin_mux(unsigned short pin, int output)
{
int gpio = GPIO_BIT_MASK(pin);
int ret = -EFAULT;
gpio_lock();
if (GPIO_ISBID(pin)) {
ret =
gpio_set_bit(output ? PIO_MUX_SET : PIO_MUX_CLR,
gpio_to_mux_map[gpio]);
} else if (GPIO_ISOUT(pin)) {
ret =
gpio_set_bit(output ? PIO_MUX_SET : PIO_MUX_CLR,
outp_to_mux_map[gpio]);
} else if (GPIO_ISMUX(pin)) {
ret = gpio_set_bit(output ? PIO_MUX_SET : PIO_MUX_CLR, gpio);
}
gpio_unlock();
return ret;
}
EXPORT_SYMBOL(pnx4008_gpio_set_pin_mux);
/* Read pin mux function: 0= pin used as GPIO, 1= pin used for muxed function*/
int pnx4008_gpio_read_pin_mux(unsigned short pin)
{
int gpio = GPIO_BIT_MASK(pin);
int ret = -EFAULT;
gpio_lock();
if (GPIO_ISBID(pin)) {
ret = gpio_read_bit(PIO_MUX_STATE, gpio_to_mux_map[gpio]);
} else if (GPIO_ISOUT(pin)) {
ret = gpio_read_bit(PIO_MUX_STATE, outp_to_mux_map[gpio]);
} else if (GPIO_ISMUX(pin)) {
ret = gpio_read_bit(PIO_MUX_STATE, gpio);
}
gpio_unlock();
return ret;
}
EXPORT_SYMBOL(pnx4008_gpio_read_pin_mux);
/*
* arch/arm/mach-pnx4008/irq.c
*
* PNX4008 IRQ controller driver
*
* Author: Dmitry Chigirev <source@mvista.com>
*
* Based on reference code received from Philips:
* Copyright (C) 2003 Philips Semiconductors
*
* 2005 (c) MontaVista Software, Inc. 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/types.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/setup.h>
#include <asm/mach-types.h>
#include <asm/pgtable.h>
#include <asm/page.h>
#include <asm/system.h>
#include <asm/mach/arch.h>
#include <asm/mach/irq.h>
#include <asm/mach/map.h>
#include <asm/arch/irq.h>
static u8 pnx4008_irq_type[NR_IRQS] = PNX4008_IRQ_TYPES;
static void pnx4008_mask_irq(unsigned int irq)
{
__raw_writel(__raw_readl(INTC_ER(irq)) & ~INTC_BIT(irq), INTC_ER(irq)); /* mask interrupt */
}
static void pnx4008_unmask_irq(unsigned int irq)
{
__raw_writel(__raw_readl(INTC_ER(irq)) | INTC_BIT(irq), INTC_ER(irq)); /* unmask interrupt */
}
static void pnx4008_mask_ack_irq(unsigned int irq)
{
__raw_writel(__raw_readl(INTC_ER(irq)) & ~INTC_BIT(irq), INTC_ER(irq)); /* mask interrupt */
__raw_writel(INTC_BIT(irq), INTC_SR(irq)); /* clear interrupt status */
}
static int pnx4008_set_irq_type(unsigned int irq, unsigned int type)
{
switch (type) {
case IRQT_RISING:
__raw_writel(__raw_readl(INTC_ATR(irq)) | INTC_BIT(irq), INTC_ATR(irq)); /*edge sensitive */
__raw_writel(__raw_readl(INTC_APR(irq)) | INTC_BIT(irq), INTC_APR(irq)); /*rising edge */
set_irq_handler(irq, do_edge_IRQ);
break;
case IRQT_FALLING:
__raw_writel(__raw_readl(INTC_ATR(irq)) | INTC_BIT(irq), INTC_ATR(irq)); /*edge sensitive */
__raw_writel(__raw_readl(INTC_APR(irq)) & ~INTC_BIT(irq), INTC_APR(irq)); /*falling edge */
set_irq_handler(irq, do_edge_IRQ);
break;
case IRQT_LOW:
__raw_writel(__raw_readl(INTC_ATR(irq)) & ~INTC_BIT(irq), INTC_ATR(irq)); /*level sensitive */
__raw_writel(__raw_readl(INTC_APR(irq)) & ~INTC_BIT(irq), INTC_APR(irq)); /*low level */
set_irq_handler(irq, do_level_IRQ);
break;
case IRQT_HIGH:
__raw_writel(__raw_readl(INTC_ATR(irq)) & ~INTC_BIT(irq), INTC_ATR(irq)); /*level sensitive */
__raw_writel(__raw_readl(INTC_APR(irq)) | INTC_BIT(irq), INTC_APR(irq)); /* high level */
set_irq_handler(irq, do_level_IRQ);
break;
/* IRQT_BOTHEDGE is not supported */
default:
printk(KERN_ERR "PNX4008 IRQ: Unsupported irq type %d\n", type);
return -1;
}
return 0;
}
static struct irqchip pnx4008_irq_chip = {
.ack = pnx4008_mask_ack_irq,
.mask = pnx4008_mask_irq,
.unmask = pnx4008_unmask_irq,
.set_type = pnx4008_set_irq_type,
};
void __init pnx4008_init_irq(void)
{
unsigned int i;
/* configure and enable IRQ 0,1,30,31 (cascade interrupts) mask all others */
pnx4008_set_irq_type(SUB1_IRQ_N, pnx4008_irq_type[SUB1_IRQ_N]);
pnx4008_set_irq_type(SUB2_IRQ_N, pnx4008_irq_type[SUB2_IRQ_N]);
pnx4008_set_irq_type(SUB1_FIQ_N, pnx4008_irq_type[SUB1_FIQ_N]);
pnx4008_set_irq_type(SUB2_FIQ_N, pnx4008_irq_type[SUB2_FIQ_N]);
__raw_writel((1 << SUB2_FIQ_N) | (1 << SUB1_FIQ_N) |
(1 << SUB2_IRQ_N) | (1 << SUB1_IRQ_N),
INTC_ER(MAIN_BASE_INT));
__raw_writel(0, INTC_ER(SIC1_BASE_INT));
__raw_writel(0, INTC_ER(SIC2_BASE_INT));
/* configure all other IRQ's */
for (i = 0; i < NR_IRQS; i++) {
if (i == SUB2_FIQ_N || i == SUB1_FIQ_N ||
i == SUB2_IRQ_N || i == SUB1_IRQ_N)
continue;
set_irq_flags(i, IRQF_VALID);
set_irq_chip(i, &pnx4008_irq_chip);
pnx4008_set_irq_type(i, pnx4008_irq_type[i]);
}
}
/*
* arch/arm/mach-pnx4008/pm.c
*
* Power Management driver for PNX4008
*
* Authors: Vitaly Wool, Dmitry Chigirev <source@mvista.com>
*
* 2005 (c) MontaVista Software, Inc. 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/pm.h>
#include <linux/rtc.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/pm.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <asm/io.h>
#include <asm/mach-types.h>
#include <asm/cacheflush.h>
#include <asm/arch/pm.h>
#include <asm/arch/clock.h>
#define SRAM_VA IO_ADDRESS(PNX4008_IRAM_BASE)
static void *saved_sram;
static struct clk *pll4_clk;
static inline void pnx4008_standby(void)
{
void (*pnx4008_cpu_standby_ptr) (void);
local_irq_disable();
local_fiq_disable();
clk_disable(pll4_clk);
/*saving portion of SRAM to be used by suspend function. */
memcpy(saved_sram, (void *)SRAM_VA, pnx4008_cpu_standby_sz);
/*make sure SRAM copy gets physically written into SDRAM.
SDRAM will be placed into self-refresh during power down */
flush_cache_all();
/*copy suspend function into SRAM */
memcpy((void *)SRAM_VA, pnx4008_cpu_standby, pnx4008_cpu_standby_sz);
/*do suspend */
pnx4008_cpu_standby_ptr = (void *)SRAM_VA;
pnx4008_cpu_standby_ptr();
/*restoring portion of SRAM that was used by suspend function */
memcpy((void *)SRAM_VA, saved_sram, pnx4008_cpu_standby_sz);
clk_enable(pll4_clk);
local_fiq_enable();
local_irq_enable();
}
static inline void pnx4008_suspend(void)
{
void (*pnx4008_cpu_suspend_ptr) (void);
local_irq_disable();
local_fiq_disable();
clk_disable(pll4_clk);
__raw_writel(0xffffffff, START_INT_RSR_REG(SE_PIN_BASE_INT));
__raw_writel(0xffffffff, START_INT_RSR_REG(SE_INT_BASE_INT));
/*saving portion of SRAM to be used by suspend function. */
memcpy(saved_sram, (void *)SRAM_VA, pnx4008_cpu_suspend_sz);
/*make sure SRAM copy gets physically written into SDRAM.
SDRAM will be placed into self-refresh during power down */
flush_cache_all();
/*copy suspend function into SRAM */
memcpy((void *)SRAM_VA, pnx4008_cpu_suspend, pnx4008_cpu_suspend_sz);
/*do suspend */
pnx4008_cpu_suspend_ptr = (void *)SRAM_VA;
pnx4008_cpu_suspend_ptr();
/*restoring portion of SRAM that was used by suspend function */
memcpy((void *)SRAM_VA, saved_sram, pnx4008_cpu_suspend_sz);
clk_enable(pll4_clk);
local_fiq_enable();
local_irq_enable();
}
static int pnx4008_pm_enter(suspend_state_t state)
{
switch (state) {
case PM_SUSPEND_STANDBY:
pnx4008_standby();
break;
case PM_SUSPEND_MEM:
pnx4008_suspend();
break;
case PM_SUSPEND_DISK:
return -ENOTSUPP;
default:
return -EINVAL;
}
return 0;
}
/*
* Called after processes are frozen, but before we shut down devices.
*/
static int pnx4008_pm_prepare(suspend_state_t state)
{
switch (state) {
case PM_SUSPEND_STANDBY:
case PM_SUSPEND_MEM:
break;
case PM_SUSPEND_DISK:
return -ENOTSUPP;
break;
default:
return -EINVAL;
break;
}
return 0;
}
/*
* Called after devices are re-setup, but before processes are thawed.
*/
static int pnx4008_pm_finish(suspend_state_t state)
{
return 0;
}
/*
* Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk.
*/
static struct pm_ops pnx4008_pm_ops = {
.prepare = pnx4008_pm_prepare,
.enter = pnx4008_pm_enter,
.finish = pnx4008_pm_finish,
};
static int __init pnx4008_pm_init(void)
{
u32 sram_size_to_allocate;
pll4_clk = clk_get(0, "ck_pll4");
if (IS_ERR(pll4_clk)) {
printk(KERN_ERR
"PM Suspend cannot acquire ARM(PLL4) clock control\n");
return PTR_ERR(pll4_clk);
}
if (pnx4008_cpu_standby_sz > pnx4008_cpu_suspend_sz)
sram_size_to_allocate = pnx4008_cpu_standby_sz;
else
sram_size_to_allocate = pnx4008_cpu_suspend_sz;
saved_sram = kmalloc(sram_size_to_allocate, GFP_ATOMIC);
if (!saved_sram) {
printk(KERN_ERR
"PM Suspend: cannot allocate memory to save portion of SRAM\n");
clk_put(pll4_clk);
return -ENOMEM;
}
pm_set_ops(&pnx4008_pm_ops);
return 0;
}
late_initcall(pnx4008_pm_init);
/*
* linux/arch/arm/mach-pnx4008/serial.c
*
* PNX4008 UART initialization
*
* Copyright: MontaVista Software Inc. (c) 2005
*
* 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/kernel.h>
#include <linux/types.h>
#include <asm/io.h>
#include <asm/arch/platform.h>
#include <asm/arch/hardware.h>
#include <linux/serial_core.h>
#include <linux/serial_reg.h>
#include <asm/arch/pm.h>
#include <asm/arch/clock.h>
#define UART_3 0
#define UART_4 1
#define UART_5 2
#define UART_6 3
#define UART_UNKNOWN (-1)
#define UART3_BASE_VA IO_ADDRESS(PNX4008_UART3_BASE)
#define UART4_BASE_VA IO_ADDRESS(PNX4008_UART4_BASE)
#define UART5_BASE_VA IO_ADDRESS(PNX4008_UART5_BASE)
#define UART6_BASE_VA IO_ADDRESS(PNX4008_UART6_BASE)
#define UART_FCR_OFFSET 8
#define UART_FIFO_SIZE 64
void pnx4008_uart_init(void)
{
u32 tmp;
int i = UART_FIFO_SIZE;
__raw_writel(0xC1, UART5_BASE_VA + UART_FCR_OFFSET);
__raw_writel(0xC1, UART3_BASE_VA + UART_FCR_OFFSET);
/* Send a NULL to fix the UART HW bug */
__raw_writel(0x00, UART5_BASE_VA);
__raw_writel(0x00, UART3_BASE_VA);
while (i--) {
tmp = __raw_readl(UART5_BASE_VA);
tmp = __raw_readl(UART3_BASE_VA);
}
__raw_writel(0, UART5_BASE_VA + UART_FCR_OFFSET);
__raw_writel(0, UART3_BASE_VA + UART_FCR_OFFSET);
/* setup wakeup interrupt */
start_int_set_rising_edge(SE_U3_RX_INT);
start_int_ack(SE_U3_RX_INT);
start_int_umask(SE_U3_RX_INT);
start_int_set_rising_edge(SE_U5_RX_INT);
start_int_ack(SE_U5_RX_INT);
start_int_umask(SE_U5_RX_INT);
}
/*
* linux/arch/arm/mach-pnx4008/sleep.S
*
* PNX4008 support for STOP mode and SDRAM self-refresh
*
* Authors: Dmitry Chigirev, Vitaly Wool <source@mvista.com>
*
* 2005 (c) MontaVista Software, Inc. 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/linkage.h>
#include <asm/assembler.h>
#include <asm/hardware.h>
#define PWRMAN_VA_BASE IO_ADDRESS(PNX4008_PWRMAN_BASE)
#define PWR_CTRL_REG_OFFS 0x44
#define SDRAM_CFG_VA_BASE IO_ADDRESS(PNX4008_SDRAM_CFG_BASE)
#define MPMC_STATUS_REG_OFFS 0x4
.text
ENTRY(pnx4008_cpu_suspend)
@this function should be entered in Direct run mode.
@ save registers on stack
stmfd sp!, {r0 - r6, lr}
@ setup Power Manager base address in r4
@ and put it's value in r5
mov r4, #(PWRMAN_VA_BASE & 0xff000000)
orr r4, r4, #(PWRMAN_VA_BASE & 0x00ff0000)
orr r4, r4, #(PWRMAN_VA_BASE & 0x0000ff00)
orr r4, r4, #(PWRMAN_VA_BASE & 0x000000ff)
ldr r5, [r4, #PWR_CTRL_REG_OFFS]
@ setup SDRAM controller base address in r2
@ and put it's value in r3
mov r2, #(SDRAM_CFG_VA_BASE & 0xff000000)
orr r2, r2, #(SDRAM_CFG_VA_BASE & 0x00ff0000)
orr r2, r2, #(SDRAM_CFG_VA_BASE & 0x0000ff00)
orr r2, r2, #(SDRAM_CFG_VA_BASE & 0x000000ff)
ldr r3, [r2, #MPMC_STATUS_REG_OFFS] @extra read - HW bug workaround
@ clear SDRAM self-refresh bit latch
and r5, r5, #(~(1 << 8))
@ clear SDRAM self-refresh bit
and r5, r5, #(~(1 << 9))
str r5, [r4, #PWR_CTRL_REG_OFFS]
@ do save current bit settings in r1
mov r1, r5
@ set SDRAM self-refresh bit
orr r5, r5, #(1 << 9)
str r5, [r4, #PWR_CTRL_REG_OFFS]
@ set SDRAM self-refresh bit latch
orr r5, r5, #(1 << 8)
str r5, [r4, #PWR_CTRL_REG_OFFS]
@ clear SDRAM self-refresh bit latch
and r5, r5, #(~(1 << 8))
str r5, [r4, #PWR_CTRL_REG_OFFS]
@ clear SDRAM self-refresh bit
and r5, r5, #(~(1 << 9))
str r5, [r4, #PWR_CTRL_REG_OFFS]
@ wait for SDRAM to get into self-refresh mode
2: ldr r3, [r2, #MPMC_STATUS_REG_OFFS]
tst r3, #(1 << 2)
beq 2b
@ to prepare SDRAM to get out of self-refresh mode after wakeup
orr r5, r5, #(1 << 7)
str r5, [r4, #PWR_CTRL_REG_OFFS]
@ do enter stop mode
orr r5, r5, #(1 << 0)
str r5, [r4, #PWR_CTRL_REG_OFFS]
nop
nop
nop
nop
nop
nop
nop
nop
nop
@ sleeping now...
@ coming out of STOP mode into Direct Run mode
@ clear STOP mode and SDRAM self-refresh bits
str r1, [r4, #PWR_CTRL_REG_OFFS]
@ wait for SDRAM to get out self-refresh mode
3: ldr r3, [r2, #MPMC_STATUS_REG_OFFS]
tst r3, #5
bne 3b
@ restore regs and return
ldmfd sp!, {r0 - r6, pc}
ENTRY(pnx4008_cpu_suspend_sz)
.word . - pnx4008_cpu_suspend
ENTRY(pnx4008_cpu_standby)
@ save registers on stack
stmfd sp!, {r0 - r6, lr}
@ setup Power Manager base address in r4
@ and put it's value in r5
mov r4, #(PWRMAN_VA_BASE & 0xff000000)
orr r4, r4, #(PWRMAN_VA_BASE & 0x00ff0000)
orr r4, r4, #(PWRMAN_VA_BASE & 0x0000ff00)
orr r4, r4, #(PWRMAN_VA_BASE & 0x000000ff)
ldr r5, [r4, #PWR_CTRL_REG_OFFS]
@ setup SDRAM controller base address in r2
@ and put it's value in r3
mov r2, #(SDRAM_CFG_VA_BASE & 0xff000000)
orr r2, r2, #(SDRAM_CFG_VA_BASE & 0x00ff0000)
orr r2, r2, #(SDRAM_CFG_VA_BASE & 0x0000ff00)
orr r2, r2, #(SDRAM_CFG_VA_BASE & 0x000000ff)
ldr r3, [r2, #MPMC_STATUS_REG_OFFS] @extra read - HW bug workaround
@ clear SDRAM self-refresh bit latch
and r5, r5, #(~(1 << 8))
@ clear SDRAM self-refresh bit
and r5, r5, #(~(1 << 9))
str r5, [r4, #PWR_CTRL_REG_OFFS]
@ do save current bit settings in r1
mov r1, r5
@ set SDRAM self-refresh bit
orr r5, r5, #(1 << 9)
str r5, [r4, #PWR_CTRL_REG_OFFS]
@ set SDRAM self-refresh bit latch
orr r5, r5, #(1 << 8)
str r5, [r4, #PWR_CTRL_REG_OFFS]
@ clear SDRAM self-refresh bit latch
and r5, r5, #(~(1 << 8))
str r5, [r4, #PWR_CTRL_REG_OFFS]
@ clear SDRAM self-refresh bit
and r5, r5, #(~(1 << 9))
str r5, [r4, #PWR_CTRL_REG_OFFS]
@ wait for SDRAM to get into self-refresh mode
2: ldr r3, [r2, #MPMC_STATUS_REG_OFFS]
tst r3, #(1 << 2)
beq 2b
@ set 'get out of self-refresh mode after wakeup' bit
orr r5, r5, #(1 << 7)
str r5, [r4, #PWR_CTRL_REG_OFFS]
mcr p15, 0, r0, c7, c0, 4 @ kinda sleeping now...
@ set SDRAM self-refresh bit latch
orr r5, r5, #(1 << 8)
str r5, [r4, #PWR_CTRL_REG_OFFS]
@ clear SDRAM self-refresh bit latch
and r5, r5, #(~(1 << 8))
str r5, [r4, #PWR_CTRL_REG_OFFS]
@ wait for SDRAM to get out self-refresh mode
3: ldr r3, [r2, #MPMC_STATUS_REG_OFFS]
tst r3, #5
bne 3b
@ restore regs and return
ldmfd sp!, {r0 - r6, pc}
ENTRY(pnx4008_cpu_standby_sz)
.word . - pnx4008_cpu_standby
ENTRY(pnx4008_cache_clean_invalidate)
stmfd sp!, {r0 - r6, lr}
#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache
#else
1: mrc p15, 0, r15, c7, c14, 3 @ test,clean,invalidate
bne 1b
#endif
ldmfd sp!, {r0 - r6, pc}
/*
* arch/arm/mach-pnx4008/time.c
*
* PNX4008 Timers
*
* Authors: Vitaly Wool, Dmitry Chigirev, Grigory Tolstolytkin <source@mvista.com>
*
* 2005 (c) MontaVista Software, Inc. 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/delay.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <asm/system.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/leds.h>
#include <asm/irq.h>
#include <asm/mach/irq.h>
#include <asm/mach/time.h>
#include <linux/time.h>
#include <linux/timex.h>
#include <asm/errno.h>
/*! Note: all timers are UPCOUNTING */
/*!
* Returns number of us since last clock interrupt. Note that interrupts
* will have been disabled by do_gettimeoffset()
*/
static unsigned long pnx4008_gettimeoffset(void)
{
u32 ticks_to_match =
__raw_readl(HSTIM_MATCH0) - __raw_readl(HSTIM_COUNTER);
u32 elapsed = LATCH - ticks_to_match;
return (elapsed * (tick_nsec / 1000)) / LATCH;
}
/*!
* IRQ handler for the timer
*/
static irqreturn_t pnx4008_timer_interrupt(int irq, void *dev_id,
struct pt_regs *regs)
{
if (__raw_readl(HSTIM_INT) & MATCH0_INT) {
write_seqlock(&xtime_lock);
do {
timer_tick(regs);
/*
* this algorithm takes care of possible delay
* for this interrupt handling longer than a normal
* timer period
*/
__raw_writel(__raw_readl(HSTIM_MATCH0) + LATCH,
HSTIM_MATCH0);
__raw_writel(MATCH0_INT, HSTIM_INT); /* clear interrupt */
/*
* The goal is to keep incrementing HSTIM_MATCH0
* register until HSTIM_MATCH0 indicates time after
* what HSTIM_COUNTER indicates.
*/
} while ((signed)
(__raw_readl(HSTIM_MATCH0) -
__raw_readl(HSTIM_COUNTER)) < 0);
write_sequnlock(&xtime_lock);
}
return IRQ_HANDLED;
}
static struct irqaction pnx4008_timer_irq = {
.name = "PNX4008 Tick Timer",
.flags = SA_INTERRUPT | SA_TIMER,
.handler = pnx4008_timer_interrupt
};
/*!
* Set up timer and timer interrupt.
*/
static __init void pnx4008_setup_timer(void)
{
__raw_writel(RESET_COUNT, MSTIM_CTRL);
while (__raw_readl(MSTIM_COUNTER)) ; /* wait for reset to complete. 100% guarantee event */
__raw_writel(0, MSTIM_CTRL); /* stop the timer */
__raw_writel(0, MSTIM_MCTRL);
__raw_writel(RESET_COUNT, HSTIM_CTRL);
while (__raw_readl(HSTIM_COUNTER)) ; /* wait for reset to complete. 100% guarantee event */
__raw_writel(0, HSTIM_CTRL);
__raw_writel(0, HSTIM_MCTRL);
__raw_writel(0, HSTIM_CCR);
__raw_writel(12, HSTIM_PMATCH); /* scale down to 1 MHZ */
__raw_writel(LATCH, HSTIM_MATCH0);
__raw_writel(MR0_INT, HSTIM_MCTRL);
setup_irq(HSTIMER_INT, &pnx4008_timer_irq);
__raw_writel(COUNT_ENAB | DEBUG_EN, HSTIM_CTRL); /*start timer, stop when JTAG active */
}
/* Timer Clock Control in PM register */
#define TIMCLK_CTRL_REG IO_ADDRESS((PNX4008_PWRMAN_BASE + 0xBC))
#define WATCHDOG_CLK_EN 1
#define TIMER_CLK_EN 2 /* HS and MS timers? */
static u32 timclk_ctrl_reg_save;
void pnx4008_timer_suspend(void)
{
timclk_ctrl_reg_save = __raw_readl(TIMCLK_CTRL_REG);
__raw_writel(0, TIMCLK_CTRL_REG); /* disable timers */
}
void pnx4008_timer_resume(void)
{
__raw_writel(timclk_ctrl_reg_save, TIMCLK_CTRL_REG); /* enable timers */
}
struct sys_timer pnx4008_timer = {
.init = pnx4008_setup_timer,
.offset = pnx4008_gettimeoffset,
.suspend = pnx4008_timer_suspend,
.resume = pnx4008_timer_resume,
};
...@@ -121,8 +121,8 @@ config CPU_ARM925T ...@@ -121,8 +121,8 @@ config CPU_ARM925T
# ARM926T # ARM926T
config CPU_ARM926T config CPU_ARM926T
bool "Support ARM926T processor" bool "Support ARM926T processor"
depends on ARCH_INTEGRATOR || ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP16XX || MACH_REALVIEW_EB depends on ARCH_INTEGRATOR || ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP16XX || MACH_REALVIEW_EB || ARCH_PNX4008
default y if ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP16XX default y if ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP16XX || ARCH_PNX4008
select CPU_32v5 select CPU_32v5
select CPU_ABRT_EV5TJ select CPU_ABRT_EV5TJ
select CPU_CACHE_VIVT select CPU_CACHE_VIVT
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment