Commit 9ef9dc69 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of master.kernel.org:/home/rmk/linux-2.6-arm

* 'for-linus' of master.kernel.org:/home/rmk/linux-2.6-arm: (44 commits)
  [ARM] 4822/1: RealView: Change the REALVIEW_MPCORE configuration option
  [ARM] 4821/1: RealView: Remove the platform dependencies from localtimer.c
  [ARM] 4820/1: RealView: Select the timer IRQ at run-time
  [ARM] 4819/1: RealView: Fix entry-macro.S to work with multiple platforms
  [ARM] 4818/1: RealView: Add core-tile detection
  [ARM] 4817/1: RealView: Move the AMBA resource definitions to realview_eb.c
  [ARM] 4816/1: RealView: Move the platform-specific definitions into board-eb.h
  [ARM] 4815/1: RealView: Add clockevents suport for the local timers
  [ARM] 4814/1: RealView: Add broadcasting clockevents support for ARM11MPCore
  [ARM] 4813/1: Add SMP helper functions for clockevents support
  [ARM] 4812/1: RealView: clockevents support for the RealView platforms
  [ARM] 4811/1: RealView: clocksource support for the RealView platforms
  [ARM] 4736/1: Export atags to userspace and allow kexec to use customised atags
  [ARM] 4798/1: pcm027: fix missing header file
  [ARM] 4803/1: pxa: fix building issue of poodle.c caused by patch 4737/1
  [ARM] 4801/1: pxa: fix building issues of missing pxa2xx-regs.h
  [ARM] pxa: introduce sysdev for pxa3xx static memory controller
  [ARM] pxa: add preliminary suspend/resume code for pxa3xx
  [ARM] pxa: introduce sysdev for GPIO register saving/restoring
  [ARM] pxa: introduce sysdev for IRQ register saving/restoring
  ...
parents 2c8296f8 0d899e1b
...@@ -35,6 +35,11 @@ config GENERIC_CLOCKEVENTS ...@@ -35,6 +35,11 @@ config GENERIC_CLOCKEVENTS
bool bool
default n default n
config GENERIC_CLOCKEVENTS_BROADCAST
bool
depends on GENERIC_CLOCKEVENTS
default y if SMP && !LOCAL_TIMERS
config MMU config MMU
bool bool
default y default y
...@@ -187,6 +192,8 @@ config ARCH_REALVIEW ...@@ -187,6 +192,8 @@ config ARCH_REALVIEW
bool "ARM Ltd. RealView family" bool "ARM Ltd. RealView family"
select ARM_AMBA select ARM_AMBA
select ICST307 select ICST307
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
help help
This enables support for ARM Ltd RealView boards. This enables support for ARM Ltd RealView boards.
...@@ -623,7 +630,7 @@ source "kernel/time/Kconfig" ...@@ -623,7 +630,7 @@ source "kernel/time/Kconfig"
config SMP config SMP
bool "Symmetric Multi-Processing (EXPERIMENTAL)" bool "Symmetric Multi-Processing (EXPERIMENTAL)"
depends on EXPERIMENTAL && REALVIEW_MPCORE depends on EXPERIMENTAL && REALVIEW_EB_ARM11MP
help help
This enables support for systems with more than one CPU. If you have This enables support for systems with more than one CPU. If you have
a system with only one CPU, like most personal computers, say N. If a system with only one CPU, like most personal computers, say N. If
...@@ -656,7 +663,7 @@ config HOTPLUG_CPU ...@@ -656,7 +663,7 @@ config HOTPLUG_CPU
config LOCAL_TIMERS config LOCAL_TIMERS
bool "Use local timer interrupts" bool "Use local timer interrupts"
depends on SMP && REALVIEW_MPCORE depends on SMP && REALVIEW_EB_ARM11MP
default y default y
help help
Enable support for local timers on SMP platforms, rather then the Enable support for local timers on SMP platforms, rather then the
...@@ -912,6 +919,13 @@ config KEXEC ...@@ -912,6 +919,13 @@ config KEXEC
initially work for you. It may help to enable device hotplugging initially work for you. It may help to enable device hotplugging
support. support.
config ATAGS_PROC
bool "Export atags in procfs"
default n
help
Should the atags used to boot the kernel be exported in an "atags"
file in procfs. Useful with kexec.
endmenu endmenu
if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_IMX || ARCH_PXA) if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_IMX || ARCH_PXA)
......
...@@ -69,9 +69,7 @@ void __init ioctime_init(void) ...@@ -69,9 +69,7 @@ void __init ioctime_init(void)
static irqreturn_t static irqreturn_t
ioc_timer_interrupt(int irq, void *dev_id) ioc_timer_interrupt(int irq, void *dev_id)
{ {
write_seqlock(&xtime_lock);
timer_tick(); timer_tick();
write_sequnlock(&xtime_lock);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
......
This diff is collapsed.
...@@ -20,6 +20,7 @@ obj-$(CONFIG_PCI) += bios32.o isa.o ...@@ -20,6 +20,7 @@ obj-$(CONFIG_PCI) += bios32.o isa.o
obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
obj-$(CONFIG_KPROBES) += kprobes.o kprobes-decode.o obj-$(CONFIG_KPROBES) += kprobes.o kprobes-decode.o
obj-$(CONFIG_ATAGS_PROC) += atags.o
obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o
obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o
......
#include <linux/slab.h>
#include <linux/kexec.h>
#include <linux/proc_fs.h>
#include <asm/setup.h>
#include <asm/types.h>
#include <asm/page.h>
struct buffer {
size_t size;
char *data;
};
static struct buffer tags_buffer;
static int
read_buffer(char* page, char** start, off_t off, int count,
int* eof, void* data)
{
struct buffer *buffer = (struct buffer *)data;
if (off >= buffer->size) {
*eof = 1;
return 0;
}
count = min((int) (buffer->size - off), count);
memcpy(page, &buffer->data[off], count);
return count;
}
static int
create_proc_entries(void)
{
struct proc_dir_entry* tags_entry;
tags_entry = create_proc_read_entry("atags", 0400, &proc_root, read_buffer, &tags_buffer);
if (!tags_entry)
return -ENOMEM;
return 0;
}
static char __initdata atags_copy_buf[KEXEC_BOOT_PARAMS_SIZE];
static char __initdata *atags_copy;
void __init save_atags(const struct tag *tags)
{
atags_copy = atags_copy_buf;
memcpy(atags_copy, tags, KEXEC_BOOT_PARAMS_SIZE);
}
static int __init init_atags_procfs(void)
{
struct tag *tag;
int error;
if (!atags_copy) {
printk(KERN_WARNING "Exporting ATAGs: No saved tags found\n");
return -EIO;
}
for (tag = (struct tag *) atags_copy; tag->hdr.size; tag = tag_next(tag))
;
tags_buffer.size = ((char *) tag - atags_copy) + sizeof(tag->hdr);
tags_buffer.data = kmalloc(tags_buffer.size, GFP_KERNEL);
if (tags_buffer.data == NULL)
return -ENOMEM;
memcpy(tags_buffer.data, atags_copy, tags_buffer.size);
error = create_proc_entries();
if (error) {
printk(KERN_ERR "Exporting ATAGs: not enough memory\n");
kfree(tags_buffer.data);
tags_buffer.size = 0;
tags_buffer.data = NULL;
}
return error;
}
arch_initcall(init_atags_procfs);
#ifdef CONFIG_ATAGS_PROC
extern void save_atags(struct tag *tags);
#else
static inline void save_atags(struct tag *tags) { }
#endif
...@@ -21,6 +21,7 @@ extern void setup_mm_for_reboot(char mode); ...@@ -21,6 +21,7 @@ extern void setup_mm_for_reboot(char mode);
extern unsigned long kexec_start_address; extern unsigned long kexec_start_address;
extern unsigned long kexec_indirection_page; extern unsigned long kexec_indirection_page;
extern unsigned long kexec_mach_type; extern unsigned long kexec_mach_type;
extern unsigned long kexec_boot_atags;
/* /*
* Provide a dummy crash_notes definition while crash dump arrives to arm. * Provide a dummy crash_notes definition while crash dump arrives to arm.
...@@ -62,6 +63,7 @@ void machine_kexec(struct kimage *image) ...@@ -62,6 +63,7 @@ void machine_kexec(struct kimage *image)
kexec_start_address = image->start; kexec_start_address = image->start;
kexec_indirection_page = page_list; kexec_indirection_page = page_list;
kexec_mach_type = machine_arch_type; kexec_mach_type = machine_arch_type;
kexec_boot_atags = image->start - KEXEC_ARM_ZIMAGE_OFFSET + KEXEC_ARM_ATAGS_OFFSET;
/* copy our kernel relocation code to the control code page */ /* copy our kernel relocation code to the control code page */
memcpy(reboot_code_buffer, memcpy(reboot_code_buffer,
......
...@@ -7,23 +7,6 @@ ...@@ -7,23 +7,6 @@
.globl relocate_new_kernel .globl relocate_new_kernel
relocate_new_kernel: relocate_new_kernel:
/* Move boot params back to where the kernel expects them */
ldr r0,kexec_boot_params_address
teq r0,#0
beq 8f
ldr r1,kexec_boot_params_copy
mov r6,#KEXEC_BOOT_PARAMS_SIZE/4
7:
ldr r5,[r1],#4
str r5,[r0],#4
subs r6,r6,#1
bne 7b
8:
/* Boot params moved, now go on with the kernel */
ldr r0,kexec_indirection_page ldr r0,kexec_indirection_page
ldr r1,kexec_start_address ldr r1,kexec_start_address
...@@ -67,7 +50,7 @@ relocate_new_kernel: ...@@ -67,7 +50,7 @@ relocate_new_kernel:
mov lr,r1 mov lr,r1
mov r0,#0 mov r0,#0
ldr r1,kexec_mach_type ldr r1,kexec_mach_type
ldr r2,kexec_boot_params_address ldr r2,kexec_boot_atags
mov pc,lr mov pc,lr
.globl kexec_start_address .globl kexec_start_address
...@@ -82,14 +65,9 @@ kexec_indirection_page: ...@@ -82,14 +65,9 @@ kexec_indirection_page:
kexec_mach_type: kexec_mach_type:
.long 0x0 .long 0x0
/* phy addr where new kernel will expect to find boot params */ /* phy addr of the atags for the new kernel */
.globl kexec_boot_params_address .globl kexec_boot_atags
kexec_boot_params_address: kexec_boot_atags:
.long 0x0
/* phy addr where old kernel put a copy of orig boot params */
.globl kexec_boot_params_copy
kexec_boot_params_copy:
.long 0x0 .long 0x0
relocate_new_kernel_end: relocate_new_kernel_end:
......
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/kexec.h>
#include <asm/cpu.h> #include <asm/cpu.h>
#include <asm/elf.h> #include <asm/elf.h>
...@@ -39,6 +38,7 @@ ...@@ -39,6 +38,7 @@
#include <asm/mach/time.h> #include <asm/mach/time.h>
#include "compat.h" #include "compat.h"
#include "atags.h"
#ifndef MEM_SIZE #ifndef MEM_SIZE
#define MEM_SIZE (16*1024*1024) #define MEM_SIZE (16*1024*1024)
...@@ -62,6 +62,7 @@ extern int root_mountflags; ...@@ -62,6 +62,7 @@ extern int root_mountflags;
extern void _stext, _text, _etext, __data_start, _edata, _end; extern void _stext, _text, _etext, __data_start, _edata, _end;
unsigned int processor_id; unsigned int processor_id;
EXPORT_SYMBOL(processor_id);
unsigned int __machine_arch_type; unsigned int __machine_arch_type;
EXPORT_SYMBOL(__machine_arch_type); EXPORT_SYMBOL(__machine_arch_type);
...@@ -784,23 +785,6 @@ static int __init customize_machine(void) ...@@ -784,23 +785,6 @@ static int __init customize_machine(void)
} }
arch_initcall(customize_machine); arch_initcall(customize_machine);
#ifdef CONFIG_KEXEC
/* Physical addr of where the boot params should be for this machine */
extern unsigned long kexec_boot_params_address;
/* Physical addr of the buffer into which the boot params are copied */
extern unsigned long kexec_boot_params_copy;
/* Pointer to the boot params buffer, for manipulation and display */
unsigned long kexec_boot_params;
EXPORT_SYMBOL(kexec_boot_params);
/* The buffer itself - make sure it is sized correctly */
static unsigned long kexec_boot_params_buf[(KEXEC_BOOT_PARAMS_SIZE + 3) / 4];
#endif
void __init setup_arch(char **cmdline_p) void __init setup_arch(char **cmdline_p)
{ {
struct tag *tags = (struct tag *)&init_tags; struct tag *tags = (struct tag *)&init_tags;
...@@ -819,18 +803,6 @@ void __init setup_arch(char **cmdline_p) ...@@ -819,18 +803,6 @@ void __init setup_arch(char **cmdline_p)
else if (mdesc->boot_params) else if (mdesc->boot_params)
tags = phys_to_virt(mdesc->boot_params); tags = phys_to_virt(mdesc->boot_params);
#ifdef CONFIG_KEXEC
kexec_boot_params_copy = virt_to_phys(kexec_boot_params_buf);
kexec_boot_params = (unsigned long)kexec_boot_params_buf;
if (__atags_pointer) {
kexec_boot_params_address = __atags_pointer;
memcpy((void *)kexec_boot_params, tags, KEXEC_BOOT_PARAMS_SIZE);
} else if (mdesc->boot_params) {
kexec_boot_params_address = mdesc->boot_params;
memcpy((void *)kexec_boot_params, tags, KEXEC_BOOT_PARAMS_SIZE);
}
#endif
/* /*
* If we have the old style parameters, convert them to * If we have the old style parameters, convert them to
* a tag list. * a tag list.
...@@ -846,6 +818,7 @@ void __init setup_arch(char **cmdline_p) ...@@ -846,6 +818,7 @@ void __init setup_arch(char **cmdline_p)
if (tags->hdr.tag == ATAG_CORE) { if (tags->hdr.tag == ATAG_CORE) {
if (meminfo.nr_banks != 0) if (meminfo.nr_banks != 0)
squash_mem_tags(tags); squash_mem_tags(tags);
save_atags(tags);
parse_tags(tags); parse_tags(tags);
} }
......
...@@ -290,6 +290,11 @@ asmlinkage void __cpuinit secondary_start_kernel(void) ...@@ -290,6 +290,11 @@ asmlinkage void __cpuinit secondary_start_kernel(void)
local_irq_enable(); local_irq_enable();
local_fiq_enable(); local_fiq_enable();
/*
* Setup local timer for this CPU.
*/
local_timer_setup(cpu);
calibrate_delay(); calibrate_delay();
smp_store_cpu_info(cpu); smp_store_cpu_info(cpu);
...@@ -299,11 +304,6 @@ asmlinkage void __cpuinit secondary_start_kernel(void) ...@@ -299,11 +304,6 @@ asmlinkage void __cpuinit secondary_start_kernel(void)
*/ */
cpu_set(cpu, cpu_online_map); cpu_set(cpu, cpu_online_map);
/*
* Setup local timer for this CPU.
*/
local_timer_setup(cpu);
/* /*
* OK, it's off to the idle thread for us * OK, it's off to the idle thread for us
*/ */
...@@ -454,6 +454,27 @@ int smp_call_function(void (*func)(void *info), void *info, int retry, ...@@ -454,6 +454,27 @@ int smp_call_function(void (*func)(void *info), void *info, int retry,
} }
EXPORT_SYMBOL_GPL(smp_call_function); EXPORT_SYMBOL_GPL(smp_call_function);
int smp_call_function_single(int cpu, void (*func)(void *info), void *info,
int retry, int wait)
{
/* prevent preemption and reschedule on another processor */
int current_cpu = get_cpu();
int ret = 0;
if (cpu == current_cpu) {
local_irq_disable();
func(info);
local_irq_enable();
} else
ret = smp_call_function_on_cpu(func, info, retry, wait,
cpumask_of_cpu(cpu));
put_cpu();
return ret;
}
EXPORT_SYMBOL_GPL(smp_call_function_single);
void show_ipi_list(struct seq_file *p) void show_ipi_list(struct seq_file *p)
{ {
unsigned int cpu; unsigned int cpu;
...@@ -481,8 +502,7 @@ void show_local_irqs(struct seq_file *p) ...@@ -481,8 +502,7 @@ void show_local_irqs(struct seq_file *p)
static void ipi_timer(void) static void ipi_timer(void)
{ {
irq_enter(); irq_enter();
profile_tick(CPU_PROFILING); local_timer_interrupt();
update_process_times(user_mode(get_irq_regs()));
irq_exit(); irq_exit();
} }
...@@ -621,6 +641,11 @@ void smp_send_timer(void) ...@@ -621,6 +641,11 @@ void smp_send_timer(void)
send_ipi_message(mask, IPI_TIMER); send_ipi_message(mask, IPI_TIMER);
} }
void smp_timer_broadcast(cpumask_t mask)
{
send_ipi_message(mask, IPI_TIMER);
}
void smp_send_stop(void) void smp_send_stop(void)
{ {
cpumask_t mask = cpu_online_map; cpumask_t mask = cpu_online_map;
......
...@@ -253,6 +253,36 @@ config AT91_TIMER_HZ ...@@ -253,6 +253,36 @@ config AT91_TIMER_HZ
system clock (of at least several MHz), rounding is less of a system clock (of at least several MHz), rounding is less of a
problem so it can be safer to use a decimal values like 100. problem so it can be safer to use a decimal values like 100.
choice
prompt "Select a UART for early kernel messages"
config AT91_EARLY_DBGU
bool "DBGU"
config AT91_EARLY_USART0
bool "USART0"
config AT91_EARLY_USART1
bool "USART1"
config AT91_EARLY_USART2
bool "USART2"
depends on ! ARCH_AT91X40
config AT91_EARLY_USART3
bool "USART3"
depends on (ARCH_AT91RM9200 || ARCH_AT91SAM9RL || ARCH_AT91SAM9260)
config AT91_EARLY_USART4
bool "USART4"
depends on ARCH_AT91SAM9260
config AT91_EARLY_USART5
bool "USART5"
depends on ARCH_AT91SAM9260
endchoice
endmenu endmenu
endif endif
...@@ -49,8 +49,6 @@ static irqreturn_t at91sam926x_timer_interrupt(int irq, void *dev_id) ...@@ -49,8 +49,6 @@ static irqreturn_t at91sam926x_timer_interrupt(int irq, void *dev_id)
volatile long nr_ticks; volatile long nr_ticks;
if (at91_sys_read(AT91_PIT_SR) & AT91_PIT_PITS) { /* This is a shared interrupt */ if (at91_sys_read(AT91_PIT_SR) & AT91_PIT_PITS) { /* This is a shared interrupt */
write_seqlock(&xtime_lock);
/* Get number to ticks performed before interrupt and clear PIT interrupt */ /* Get number to ticks performed before interrupt and clear PIT interrupt */
nr_ticks = PIT_PICNT(at91_sys_read(AT91_PIT_PIVR)); nr_ticks = PIT_PICNT(at91_sys_read(AT91_PIT_PIVR));
do { do {
...@@ -58,7 +56,6 @@ static irqreturn_t at91sam926x_timer_interrupt(int irq, void *dev_id) ...@@ -58,7 +56,6 @@ static irqreturn_t at91sam926x_timer_interrupt(int irq, void *dev_id)
nr_ticks--; nr_ticks--;
} while (nr_ticks); } while (nr_ticks);
write_sequnlock(&xtime_lock);
return IRQ_HANDLED; return IRQ_HANDLED;
} else } else
return IRQ_NONE; /* not handled */ return IRQ_NONE; /* not handled */
......
...@@ -47,6 +47,9 @@ extern void at91_irq_resume(void); ...@@ -47,6 +47,9 @@ extern void at91_irq_resume(void);
#define AT91RM9200_BGA 4 /* AT91RM9200 BGA package has 4 banks */ #define AT91RM9200_BGA 4 /* AT91RM9200 BGA package has 4 banks */
struct at91_gpio_bank { struct at91_gpio_bank {
unsigned chipbase; /* bank's first GPIO number */
void __iomem *regbase; /* base of register bank */
struct at91_gpio_bank *next; /* bank sharing same IRQ/clock/... */
unsigned short id; /* peripheral ID */ unsigned short id; /* peripheral ID */
unsigned long offset; /* offset from system peripheral base */ unsigned long offset; /* offset from system peripheral base */
struct clk *clock; /* associated clock */ struct clk *clock; /* associated clock */
......
...@@ -33,12 +33,10 @@ static int gpio_banks; ...@@ -33,12 +33,10 @@ static int gpio_banks;
static inline void __iomem *pin_to_controller(unsigned pin) static inline void __iomem *pin_to_controller(unsigned pin)
{ {
void __iomem *sys_base = (void __iomem *) AT91_VA_BASE_SYS;
pin -= PIN_BASE; pin -= PIN_BASE;
pin /= 32; pin /= 32;
if (likely(pin < gpio_banks)) if (likely(pin < gpio_banks))
return sys_base + gpio[pin].offset; return gpio[pin].regbase;
return NULL; return NULL;
} }
...@@ -294,11 +292,11 @@ void at91_gpio_suspend(void) ...@@ -294,11 +292,11 @@ void at91_gpio_suspend(void)
int i; int i;
for (i = 0; i < gpio_banks; i++) { for (i = 0; i < gpio_banks; i++) {
u32 pio = gpio[i].offset; void __iomem *pio = gpio[i].regbase;
backups[i] = at91_sys_read(pio + PIO_IMR); backups[i] = __raw_readl(pio + PIO_IMR);
at91_sys_write(pio + PIO_IDR, backups[i]); __raw_writel(backups[i], pio + PIO_IDR);
at91_sys_write(pio + PIO_IER, wakeups[i]); __raw_writel(wakeups[i], pio + PIO_IER);
if (!wakeups[i]) if (!wakeups[i])
clk_disable(gpio[i].clock); clk_disable(gpio[i].clock);
...@@ -315,13 +313,13 @@ void at91_gpio_resume(void) ...@@ -315,13 +313,13 @@ void at91_gpio_resume(void)
int i; int i;
for (i = 0; i < gpio_banks; i++) { for (i = 0; i < gpio_banks; i++) {
u32 pio = gpio[i].offset; void __iomem *pio = gpio[i].regbase;
if (!wakeups[i]) if (!wakeups[i])
clk_enable(gpio[i].clock); clk_enable(gpio[i].clock);
at91_sys_write(pio + PIO_IDR, wakeups[i]); __raw_writel(wakeups[i], pio + PIO_IDR);
at91_sys_write(pio + PIO_IER, backups[i]); __raw_writel(backups[i], pio + PIO_IER);
} }
} }
...@@ -361,7 +359,13 @@ static void gpio_irq_unmask(unsigned pin) ...@@ -361,7 +359,13 @@ static void gpio_irq_unmask(unsigned pin)
static int gpio_irq_type(unsigned pin, unsigned type) static int gpio_irq_type(unsigned pin, unsigned type)
{ {
return (type == IRQT_BOTHEDGE) ? 0 : -EINVAL; switch (type) {
case IRQ_TYPE_NONE:
case IRQ_TYPE_EDGE_BOTH:
return 0;
default:
return -EINVAL;
}
} }
static struct irq_chip gpio_irqchip = { static struct irq_chip gpio_irqchip = {
...@@ -376,20 +380,30 @@ static void gpio_irq_handler(unsigned irq, struct irq_desc *desc) ...@@ -376,20 +380,30 @@ static void gpio_irq_handler(unsigned irq, struct irq_desc *desc)
{ {
unsigned pin; unsigned pin;
struct irq_desc *gpio; struct irq_desc *gpio;
struct at91_gpio_bank *bank;
void __iomem *pio; void __iomem *pio;
u32 isr; u32 isr;
pio = get_irq_chip_data(irq); bank = get_irq_chip_data(irq);
pio = bank->regbase;
/* temporarily mask (level sensitive) parent IRQ */ /* temporarily mask (level sensitive) parent IRQ */
desc->chip->ack(irq); desc->chip->ack(irq);
for (;;) { for (;;) {
/* reading ISR acks the pending (edge triggered) GPIO interrupt */ /* Reading ISR acks pending (edge triggered) GPIO interrupts.
* When there none are pending, we're finished unless we need
* to process multiple banks (like ID_PIOCDE on sam9263).
*/
isr = __raw_readl(pio + PIO_ISR) & __raw_readl(pio + PIO_IMR); isr = __raw_readl(pio + PIO_ISR) & __raw_readl(pio + PIO_IMR);
if (!isr) if (!isr) {
break; if (!bank->next)
break;
bank = bank->next;
pio = bank->regbase;
continue;
}
pin = (unsigned) get_irq_data(irq); pin = bank->chipbase;
gpio = &irq_desc[pin]; gpio = &irq_desc[pin];
while (isr) { while (isr) {
...@@ -481,24 +495,21 @@ postcore_initcall(at91_gpio_debugfs_init); ...@@ -481,24 +495,21 @@ postcore_initcall(at91_gpio_debugfs_init);
*/ */
void __init at91_gpio_irq_setup(void) void __init at91_gpio_irq_setup(void)
{ {
unsigned pioc, pin; unsigned pioc, pin;
struct at91_gpio_bank *this, *prev;
for (pioc = 0, pin = PIN_BASE; for (pioc = 0, pin = PIN_BASE, this = gpio, prev = NULL;
pioc < gpio_banks; pioc++ < gpio_banks;
pioc++) { prev = this, this++) {
void __iomem *controller; unsigned id = this->id;
unsigned id = gpio[pioc].id;
unsigned i; unsigned i;
clk_enable(gpio[pioc].clock); /* enable PIO controller's clock */ /* enable PIO controller's clock */
clk_enable(this->clock);
controller = (void __iomem *) AT91_VA_BASE_SYS + gpio[pioc].offset;
__raw_writel(~0, controller + PIO_IDR);
set_irq_data(id, (void *) pin); __raw_writel(~0, this->regbase + PIO_IDR);
set_irq_chip_data(id, controller);
for (i = 0; i < 32; i++, pin++) { for (i = 0, pin = this->chipbase; i < 32; i++, pin++) {
/* /*
* Can use the "simple" and not "edge" handler since it's * Can use the "simple" and not "edge" handler since it's
* shorter, and the AIC handles interrupts sanely. * shorter, and the AIC handles interrupts sanely.
...@@ -508,6 +519,14 @@ void __init at91_gpio_irq_setup(void) ...@@ -508,6 +519,14 @@ void __init at91_gpio_irq_setup(void)
set_irq_flags(pin, IRQF_VALID); set_irq_flags(pin, IRQF_VALID);
} }
/* The toplevel handler handles one bank of GPIOs, except
* AT91SAM9263_ID_PIOCDE handles three... PIOC is first in
* the list, so we only set up that handler.
*/
if (prev && prev->next == this)
continue;
set_irq_chip_data(id, this);
set_irq_chained_handler(id, gpio_irq_handler); set_irq_chained_handler(id, gpio_irq_handler);
} }
pr_info("AT91: %d gpio irqs in %d banks\n", pin - PIN_BASE, gpio_banks); pr_info("AT91: %d gpio irqs in %d banks\n", pin - PIN_BASE, gpio_banks);
...@@ -518,8 +537,20 @@ void __init at91_gpio_irq_setup(void) ...@@ -518,8 +537,20 @@ void __init at91_gpio_irq_setup(void)
*/ */
void __init at91_gpio_init(struct at91_gpio_bank *data, int nr_banks) void __init at91_gpio_init(struct at91_gpio_bank *data, int nr_banks)
{ {
unsigned i;
struct at91_gpio_bank *last;
BUG_ON(nr_banks > MAX_GPIO_BANKS); BUG_ON(nr_banks > MAX_GPIO_BANKS);
gpio = data; gpio = data;
gpio_banks = nr_banks; gpio_banks = nr_banks;
for (i = 0, last = NULL; i < nr_banks; i++, last = data, data++) {
data->chipbase = PIN_BASE + i * 32;
data->regbase = data->offset + (void __iomem *)AT91_VA_BASE_SYS;
/* AT91SAM9263_ID_PIOCDE groups PIOC, PIOD, PIOE */
if (last && last->id == data->id)
last->next = data;
}
} }
...@@ -189,6 +189,20 @@ config IXP4XX_INDIRECT_PCI ...@@ -189,6 +189,20 @@ config IXP4XX_INDIRECT_PCI
need to use the indirect method instead. If you don't know need to use the indirect method instead. If you don't know
what you need, leave this option unselected. what you need, leave this option unselected.
config IXP4XX_QMGR
tristate "IXP4xx Queue Manager support"
help
This driver supports IXP4xx built-in hardware queue manager
and is automatically selected by Ethernet and HSS drivers.
config IXP4XX_NPE
tristate "IXP4xx Network Processor Engine support"
select HOTPLUG
select FW_LOADER
help
This driver supports IXP4xx built-in network coprocessors
and is automatically selected by Ethernet and HSS drivers.
endmenu endmenu
endif endif
...@@ -23,10 +23,12 @@ obj-$(CONFIG_MACH_AVILA) += avila-setup.o ...@@ -23,10 +23,12 @@ obj-$(CONFIG_MACH_AVILA) += avila-setup.o
obj-$(CONFIG_MACH_IXDPG425) += coyote-setup.o obj-$(CONFIG_MACH_IXDPG425) += coyote-setup.o
obj-$(CONFIG_ARCH_ADI_COYOTE) += coyote-setup.o obj-$(CONFIG_ARCH_ADI_COYOTE) += coyote-setup.o
obj-$(CONFIG_MACH_GTWX5715) += gtwx5715-setup.o obj-$(CONFIG_MACH_GTWX5715) += gtwx5715-setup.o
obj-$(CONFIG_MACH_NSLU2) += nslu2-setup.o nslu2-power.o obj-$(CONFIG_MACH_NSLU2) += nslu2-setup.o
obj-$(CONFIG_MACH_NAS100D) += nas100d-setup.o nas100d-power.o obj-$(CONFIG_MACH_NAS100D) += nas100d-setup.o
obj-$(CONFIG_MACH_DSMG600) += dsmg600-setup.o dsmg600-power.o obj-$(CONFIG_MACH_DSMG600) += dsmg600-setup.o
obj-$(CONFIG_MACH_GATEWAY7001) += gateway7001-setup.o obj-$(CONFIG_MACH_GATEWAY7001) += gateway7001-setup.o
obj-$(CONFIG_MACH_WG302V2) += wg302v2-setup.o obj-$(CONFIG_MACH_WG302V2) += wg302v2-setup.o
obj-$(CONFIG_PCI) += $(obj-pci-$(CONFIG_PCI)) common-pci.o obj-$(CONFIG_PCI) += $(obj-pci-$(CONFIG_PCI)) common-pci.o
obj-$(CONFIG_IXP4XX_QMGR) += ixp4xx_qmgr.o
obj-$(CONFIG_IXP4XX_NPE) += ixp4xx_npe.o
/*
* arch/arm/mach-ixp4xx/dsmg600-power.c
*
* DSM-G600 Power/Reset driver
* Author: Michael Westerhof <mwester@dls.net>
*
* Based on nslu2-power.c
* Copyright (C) 2005 Tower Technologies
* Author: Alessandro Zummo <a.zummo@towertech.it>
*
* which was based on nslu2-io.c
* Copyright (C) 2004 Karen Spearel
*
* Maintainers: http://www.nslu2-linux.org/
*
* 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/module.h>
#include <linux/reboot.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
#include <asm/mach-types.h>
extern void ctrl_alt_del(void);
/* This is used to make sure the power-button pusher is serious. The button
* must be held until the value of this counter reaches zero.
*/
static volatile int power_button_countdown;
/* Must hold the button down for at least this many counts to be processed */
#define PBUTTON_HOLDDOWN_COUNT 4 /* 2 secs */
static void dsmg600_power_handler(unsigned long data);
static DEFINE_TIMER(dsmg600_power_timer, dsmg600_power_handler, 0, 0);
static void dsmg600_power_handler(unsigned long data)
{
/* This routine is called twice per second to check the
* state of the power button.
*/
if (*IXP4XX_GPIO_GPINR & DSMG600_PB_BM) {
/* IO Pin is 1 (button pushed) */
if (power_button_countdown == 0) {
/* Signal init to do the ctrlaltdel action, this will bypass
* init if it hasn't started and do a kernel_restart.
*/
ctrl_alt_del();
/* Change the state of the power LED to "blink" */
gpio_line_set(DSMG600_LED_PWR_GPIO, IXP4XX_GPIO_LOW);
}
power_button_countdown--;
} else {
power_button_countdown = PBUTTON_HOLDDOWN_COUNT;
}
mod_timer(&dsmg600_power_timer, jiffies + msecs_to_jiffies(500));
}
static irqreturn_t dsmg600_reset_handler(int irq, void *dev_id)
{
/* This is the paper-clip reset, it shuts the machine down directly. */
machine_power_off();
return IRQ_HANDLED;
}
static int __init dsmg600_power_init(void)
{
if (!(machine_is_dsmg600()))
return 0;
if (request_irq(DSMG600_RB_IRQ, &dsmg600_reset_handler,
IRQF_DISABLED | IRQF_TRIGGER_LOW, "DSM-G600 reset button",
NULL) < 0) {
printk(KERN_DEBUG "Reset Button IRQ %d not available\n",
DSMG600_RB_IRQ);
return -EIO;
}
/* The power button on the D-Link DSM-G600 is on GPIO 15, but
* it cannot handle interrupts on that GPIO line. So we'll
* have to poll it with a kernel timer.
*/
/* Make sure that the power button GPIO is set up as an input */
gpio_line_config(DSMG600_PB_GPIO, IXP4XX_GPIO_IN);
/* Set the initial value for the power button IRQ handler */
power_button_countdown = PBUTTON_HOLDDOWN_COUNT;
mod_timer(&dsmg600_power_timer, jiffies + msecs_to_jiffies(500));
return 0;
}
static void __exit dsmg600_power_exit(void)
{
if (!(machine_is_dsmg600()))
return;
del_timer_sync(&dsmg600_power_timer);
free_irq(DSMG600_RB_IRQ, NULL);
}
module_init(dsmg600_power_init);
module_exit(dsmg600_power_exit);
MODULE_AUTHOR("Michael Westerhof <mwester@dls.net>");
MODULE_DESCRIPTION("DSM-G600 Power/Reset driver");
MODULE_LICENSE("GPL");
/* /*
* DSM-G600 board-setup * DSM-G600 board-setup
* *
* Copyright (C) 2008 Rod Whitby <rod@whitby.id.au>
* Copyright (C) 2006 Tower Technologies * Copyright (C) 2006 Tower Technologies
* Author: Alessandro Zummo <a.zummo@towertech.it>
* *
* based ixdp425-setup.c: * based on ixdp425-setup.c:
* Copyright (C) 2003-2004 MontaVista Software, Inc. * Copyright (C) 2003-2004 MontaVista Software, Inc.
* based on nslu2-power.c:
* Copyright (C) 2005 Tower Technologies
* based on nslu2-io.c:
* Copyright (C) 2004 Karen Spearel
* *
* Author: Alessandro Zummo <a.zummo@towertech.it> * Author: Alessandro Zummo <a.zummo@towertech.it>
* Author: Michael Westerhof <mwester@dls.net>
* Author: Rod Whitby <rod@whitby.id.au>
* Maintainers: http://www.nslu2-linux.org/ * Maintainers: http://www.nslu2-linux.org/
*/ */
#include <linux/kernel.h> #include <linux/irq.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
#include <linux/serial.h> #include <linux/serial.h>
#include <linux/serial_8250.h> #include <linux/serial_8250.h>
#include <linux/leds.h>
#include <linux/reboot.h>
#include <linux/i2c.h>
#include <linux/i2c-gpio.h> #include <linux/i2c-gpio.h>
#include <asm/mach-types.h> #include <asm/mach-types.h>
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
#include <asm/mach/flash.h> #include <asm/mach/flash.h>
#include <asm/mach/time.h> #include <asm/mach/time.h>
#include <asm/gpio.h>
static struct flash_platform_data dsmg600_flash_data = { static struct flash_platform_data dsmg600_flash_data = {
.map_name = "cfi_probe", .map_name = "cfi_probe",
...@@ -51,29 +63,34 @@ static struct platform_device dsmg600_i2c_gpio = { ...@@ -51,29 +63,34 @@ static struct platform_device dsmg600_i2c_gpio = {
}, },
}; };
#ifdef CONFIG_LEDS_CLASS static struct i2c_board_info __initdata dsmg600_i2c_board_info [] = {
static struct resource dsmg600_led_resources[] = { {
I2C_BOARD_INFO("rtc-pcf8563", 0x51),
},
};
static struct gpio_led dsmg600_led_pins[] = {
{ {
.name = "power", .name = "power",
.start = DSMG600_LED_PWR_GPIO, .gpio = DSMG600_LED_PWR_GPIO,
.end = DSMG600_LED_PWR_GPIO,
.flags = IXP4XX_GPIO_HIGH,
}, },
{ {
.name = "wlan", .name = "wlan",
.start = DSMG600_LED_WLAN_GPIO, .gpio = DSMG600_LED_WLAN_GPIO,
.end = DSMG600_LED_WLAN_GPIO, .active_low = true,
.flags = IXP4XX_GPIO_LOW,
}, },
}; };
static struct gpio_led_platform_data dsmg600_led_data = {
.num_leds = ARRAY_SIZE(dsmg600_led_pins),
.leds = dsmg600_led_pins,
};
static struct platform_device dsmg600_leds = { static struct platform_device dsmg600_leds = {
.name = "IXP4XX-GPIO-LED", .name = "leds-gpio",
.id = -1, .id = -1,
.num_resources = ARRAY_SIZE(dsmg600_led_resources), .dev.platform_data = &dsmg600_led_data,
.resource = dsmg600_led_resources,
}; };
#endif
static struct resource dsmg600_uart_resources[] = { static struct resource dsmg600_uart_resources[] = {
{ {
...@@ -121,6 +138,7 @@ static struct platform_device dsmg600_uart = { ...@@ -121,6 +138,7 @@ static struct platform_device dsmg600_uart = {
static struct platform_device *dsmg600_devices[] __initdata = { static struct platform_device *dsmg600_devices[] __initdata = {
&dsmg600_i2c_gpio, &dsmg600_i2c_gpio,
&dsmg600_flash, &dsmg600_flash,
&dsmg600_leds,
}; };
static void dsmg600_power_off(void) static void dsmg600_power_off(void)
...@@ -132,6 +150,57 @@ static void dsmg600_power_off(void) ...@@ -132,6 +150,57 @@ static void dsmg600_power_off(void)
gpio_line_set(DSMG600_PO_GPIO, IXP4XX_GPIO_HIGH); gpio_line_set(DSMG600_PO_GPIO, IXP4XX_GPIO_HIGH);
} }
/* This is used to make sure the power-button pusher is serious. The button
* must be held until the value of this counter reaches zero.
*/
static int power_button_countdown;
/* Must hold the button down for at least this many counts to be processed */
#define PBUTTON_HOLDDOWN_COUNT 4 /* 2 secs */
static void dsmg600_power_handler(unsigned long data);
static DEFINE_TIMER(dsmg600_power_timer, dsmg600_power_handler, 0, 0);
static void dsmg600_power_handler(unsigned long data)
{
/* This routine is called twice per second to check the
* state of the power button.
*/
if (gpio_get_value(DSMG600_PB_GPIO)) {
/* IO Pin is 1 (button pushed) */
if (power_button_countdown > 0)
power_button_countdown--;
} else {
/* Done on button release, to allow for auto-power-on mods. */
if (power_button_countdown == 0) {
/* Signal init to do the ctrlaltdel action,
* this will bypass init if it hasn't started
* and do a kernel_restart.
*/
ctrl_alt_del();
/* Change the state of the power LED to "blink" */
gpio_line_set(DSMG600_LED_PWR_GPIO, IXP4XX_GPIO_LOW);
} else {
power_button_countdown = PBUTTON_HOLDDOWN_COUNT;
}
}
mod_timer(&dsmg600_power_timer, jiffies + msecs_to_jiffies(500));
}
static irqreturn_t dsmg600_reset_handler(int irq, void *dev_id)
{
/* This is the paper-clip reset, it shuts the machine down directly. */
machine_power_off();
return IRQ_HANDLED;
}
static void __init dsmg600_timer_init(void) static void __init dsmg600_timer_init(void)
{ {
/* The xtal on this machine is non-standard. */ /* The xtal on this machine is non-standard. */
...@@ -156,7 +225,8 @@ static void __init dsmg600_init(void) ...@@ -156,7 +225,8 @@ static void __init dsmg600_init(void)
dsmg600_flash_resource.end = dsmg600_flash_resource.end =
IXP4XX_EXP_BUS_BASE(0) + ixp4xx_exp_bus_size - 1; IXP4XX_EXP_BUS_BASE(0) + ixp4xx_exp_bus_size - 1;
pm_power_off = dsmg600_power_off; i2c_register_board_info(0, dsmg600_i2c_board_info,
ARRAY_SIZE(dsmg600_i2c_board_info));
/* The UART is required on the DSM-G600 (Redboot cannot use the /* The UART is required on the DSM-G600 (Redboot cannot use the
* NIC) -- do it here so that it does *not* get removed if * NIC) -- do it here so that it does *not* get removed if
...@@ -166,10 +236,28 @@ static void __init dsmg600_init(void) ...@@ -166,10 +236,28 @@ static void __init dsmg600_init(void)
platform_add_devices(dsmg600_devices, ARRAY_SIZE(dsmg600_devices)); platform_add_devices(dsmg600_devices, ARRAY_SIZE(dsmg600_devices));
#ifdef CONFIG_LEDS_CLASS pm_power_off = dsmg600_power_off;
/* We don't care whether or not this works. */
(void)platform_device_register(&dsmg600_leds); if (request_irq(gpio_to_irq(DSMG600_RB_GPIO), &dsmg600_reset_handler,
#endif IRQF_DISABLED | IRQF_TRIGGER_LOW,
"DSM-G600 reset button", NULL) < 0) {
printk(KERN_DEBUG "Reset Button IRQ %d not available\n",
gpio_to_irq(DSMG600_RB_GPIO));
}
/* The power button on the D-Link DSM-G600 is on GPIO 15, but
* it cannot handle interrupts on that GPIO line. So we'll
* have to poll it with a kernel timer.
*/
/* Make sure that the power button GPIO is set up as an input */
gpio_line_config(DSMG600_PB_GPIO, IXP4XX_GPIO_IN);
/* Set the initial value for the power button IRQ handler */
power_button_countdown = PBUTTON_HOLDDOWN_COUNT;
mod_timer(&dsmg600_power_timer, jiffies + msecs_to_jiffies(500));
} }
MACHINE_START(DSMG600, "D-Link DSM-G600 RevA") MACHINE_START(DSMG600, "D-Link DSM-G600 RevA")
......
...@@ -177,6 +177,31 @@ static struct platform_device ixdp425_uart = { ...@@ -177,6 +177,31 @@ static struct platform_device ixdp425_uart = {
.resource = ixdp425_uart_resources .resource = ixdp425_uart_resources
}; };
/* Built-in 10/100 Ethernet MAC interfaces */
static struct eth_plat_info ixdp425_plat_eth[] = {
{
.phy = 0,
.rxq = 3,
.txreadyq = 20,
}, {
.phy = 1,
.rxq = 4,
.txreadyq = 21,
}
};
static struct platform_device ixdp425_eth[] = {
{
.name = "ixp4xx_eth",
.id = IXP4XX_ETH_NPEB,
.dev.platform_data = ixdp425_plat_eth,
}, {
.name = "ixp4xx_eth",
.id = IXP4XX_ETH_NPEC,
.dev.platform_data = ixdp425_plat_eth + 1,
}
};
static struct platform_device *ixdp425_devices[] __initdata = { static struct platform_device *ixdp425_devices[] __initdata = {
&ixdp425_i2c_gpio, &ixdp425_i2c_gpio,
&ixdp425_flash, &ixdp425_flash,
...@@ -184,7 +209,9 @@ static struct platform_device *ixdp425_devices[] __initdata = { ...@@ -184,7 +209,9 @@ static struct platform_device *ixdp425_devices[] __initdata = {
defined(CONFIG_MTD_NAND_PLATFORM_MODULE) defined(CONFIG_MTD_NAND_PLATFORM_MODULE)
&ixdp425_flash_nand, &ixdp425_flash_nand,
#endif #endif
&ixdp425_uart &ixdp425_uart,
&ixdp425_eth[0],
&ixdp425_eth[1],
}; };
static void __init ixdp425_init(void) static void __init ixdp425_init(void)
......
This diff is collapsed.
/*
* Intel IXP4xx Queue Manager driver for Linux
*
* Copyright (C) 2007 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*/
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/arch/qmgr.h>
#define DEBUG 0
struct qmgr_regs __iomem *qmgr_regs;
static struct resource *mem_res;
static spinlock_t qmgr_lock;
static u32 used_sram_bitmap[4]; /* 128 16-dword pages */
static void (*irq_handlers[HALF_QUEUES])(void *pdev);
static void *irq_pdevs[HALF_QUEUES];
void qmgr_set_irq(unsigned int queue, int src,
void (*handler)(void *pdev), void *pdev)
{
u32 __iomem *reg = &qmgr_regs->irqsrc[queue / 8]; /* 8 queues / u32 */
int bit = (queue % 8) * 4; /* 3 bits + 1 reserved bit per queue */
unsigned long flags;
src &= 7;
spin_lock_irqsave(&qmgr_lock, flags);
__raw_writel((__raw_readl(reg) & ~(7 << bit)) | (src << bit), reg);
irq_handlers[queue] = handler;
irq_pdevs[queue] = pdev;
spin_unlock_irqrestore(&qmgr_lock, flags);
}
static irqreturn_t qmgr_irq1(int irq, void *pdev)
{
int i;
u32 val = __raw_readl(&qmgr_regs->irqstat[0]);
__raw_writel(val, &qmgr_regs->irqstat[0]); /* ACK */
for (i = 0; i < HALF_QUEUES; i++)
if (val & (1 << i))
irq_handlers[i](irq_pdevs[i]);
return val ? IRQ_HANDLED : 0;
}
void qmgr_enable_irq(unsigned int queue)
{
unsigned long flags;
spin_lock_irqsave(&qmgr_lock, flags);
__raw_writel(__raw_readl(&qmgr_regs->irqen[0]) | (1 << queue),
&qmgr_regs->irqen[0]);
spin_unlock_irqrestore(&qmgr_lock, flags);
}
void qmgr_disable_irq(unsigned int queue)
{
unsigned long flags;
spin_lock_irqsave(&qmgr_lock, flags);
__raw_writel(__raw_readl(&qmgr_regs->irqen[0]) & ~(1 << queue),
&qmgr_regs->irqen[0]);
spin_unlock_irqrestore(&qmgr_lock, flags);
}
static inline void shift_mask(u32 *mask)
{
mask[3] = mask[3] << 1 | mask[2] >> 31;
mask[2] = mask[2] << 1 | mask[1] >> 31;
mask[1] = mask[1] << 1 | mask[0] >> 31;
mask[0] <<= 1;
}
int qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */,
unsigned int nearly_empty_watermark,
unsigned int nearly_full_watermark)
{
u32 cfg, addr = 0, mask[4]; /* in 16-dwords */
int err;
if (queue >= HALF_QUEUES)
return -ERANGE;
if ((nearly_empty_watermark | nearly_full_watermark) & ~7)
return -EINVAL;
switch (len) {
case 16:
cfg = 0 << 24;
mask[0] = 0x1;
break;
case 32:
cfg = 1 << 24;
mask[0] = 0x3;
break;
case 64:
cfg = 2 << 24;
mask[0] = 0xF;
break;
case 128:
cfg = 3 << 24;
mask[0] = 0xFF;
break;
default:
return -EINVAL;
}
cfg |= nearly_empty_watermark << 26;
cfg |= nearly_full_watermark << 29;
len /= 16; /* in 16-dwords: 1, 2, 4 or 8 */
mask[1] = mask[2] = mask[3] = 0;
if (!try_module_get(THIS_MODULE))
return -ENODEV;
spin_lock_irq(&qmgr_lock);
if (__raw_readl(&qmgr_regs->sram[queue])) {
err = -EBUSY;
goto err;
}
while (1) {
if (!(used_sram_bitmap[0] & mask[0]) &&
!(used_sram_bitmap[1] & mask[1]) &&
!(used_sram_bitmap[2] & mask[2]) &&
!(used_sram_bitmap[3] & mask[3]))
break; /* found free space */
addr++;
shift_mask(mask);
if (addr + len > ARRAY_SIZE(qmgr_regs->sram)) {
printk(KERN_ERR "qmgr: no free SRAM space for"
" queue %i\n", queue);
err = -ENOMEM;
goto err;
}
}
used_sram_bitmap[0] |= mask[0];
used_sram_bitmap[1] |= mask[1];
used_sram_bitmap[2] |= mask[2];
used_sram_bitmap[3] |= mask[3];
__raw_writel(cfg | (addr << 14), &qmgr_regs->sram[queue]);
spin_unlock_irq(&qmgr_lock);
#if DEBUG
printk(KERN_DEBUG "qmgr: requested queue %i, addr = 0x%02X\n",
queue, addr);
#endif
return 0;
err:
spin_unlock_irq(&qmgr_lock);
module_put(THIS_MODULE);
return err;
}
void qmgr_release_queue(unsigned int queue)
{
u32 cfg, addr, mask[4];
BUG_ON(queue >= HALF_QUEUES); /* not in valid range */
spin_lock_irq(&qmgr_lock);
cfg = __raw_readl(&qmgr_regs->sram[queue]);
addr = (cfg >> 14) & 0xFF;
BUG_ON(!addr); /* not requested */
switch ((cfg >> 24) & 3) {
case 0: mask[0] = 0x1; break;
case 1: mask[0] = 0x3; break;
case 2: mask[0] = 0xF; break;
case 3: mask[0] = 0xFF; break;
}
while (addr--)
shift_mask(mask);
__raw_writel(0, &qmgr_regs->sram[queue]);
used_sram_bitmap[0] &= ~mask[0];
used_sram_bitmap[1] &= ~mask[1];
used_sram_bitmap[2] &= ~mask[2];
used_sram_bitmap[3] &= ~mask[3];
irq_handlers[queue] = NULL; /* catch IRQ bugs */
spin_unlock_irq(&qmgr_lock);
module_put(THIS_MODULE);
#if DEBUG
printk(KERN_DEBUG "qmgr: released queue %i\n", queue);
#endif
}
static int qmgr_init(void)
{
int i, err;
mem_res = request_mem_region(IXP4XX_QMGR_BASE_PHYS,
IXP4XX_QMGR_REGION_SIZE,
"IXP4xx Queue Manager");
if (mem_res == NULL)
return -EBUSY;
qmgr_regs = ioremap(IXP4XX_QMGR_BASE_PHYS, IXP4XX_QMGR_REGION_SIZE);
if (qmgr_regs == NULL) {
err = -ENOMEM;
goto error_map;
}
/* reset qmgr registers */
for (i = 0; i < 4; i++) {
__raw_writel(0x33333333, &qmgr_regs->stat1[i]);
__raw_writel(0, &qmgr_regs->irqsrc[i]);
}
for (i = 0; i < 2; i++) {
__raw_writel(0, &qmgr_regs->stat2[i]);
__raw_writel(0xFFFFFFFF, &qmgr_regs->irqstat[i]); /* clear */
__raw_writel(0, &qmgr_regs->irqen[i]);
}
for (i = 0; i < QUEUES; i++)
__raw_writel(0, &qmgr_regs->sram[i]);
err = request_irq(IRQ_IXP4XX_QM1, qmgr_irq1, 0,
"IXP4xx Queue Manager", NULL);
if (err) {
printk(KERN_ERR "qmgr: failed to request IRQ%i\n",
IRQ_IXP4XX_QM1);
goto error_irq;
}
used_sram_bitmap[0] = 0xF; /* 4 first pages reserved for config */
spin_lock_init(&qmgr_lock);
printk(KERN_INFO "IXP4xx Queue Manager initialized.\n");
return 0;
error_irq:
iounmap(qmgr_regs);
error_map:
release_mem_region(IXP4XX_QMGR_BASE_PHYS, IXP4XX_QMGR_REGION_SIZE);
return err;
}
static void qmgr_remove(void)
{
free_irq(IRQ_IXP4XX_QM1, NULL);
synchronize_irq(IRQ_IXP4XX_QM1);
iounmap(qmgr_regs);
release_mem_region(IXP4XX_QMGR_BASE_PHYS, IXP4XX_QMGR_REGION_SIZE);
}
module_init(qmgr_init);
module_exit(qmgr_remove);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Krzysztof Halasa");
EXPORT_SYMBOL(qmgr_regs);
EXPORT_SYMBOL(qmgr_set_irq);
EXPORT_SYMBOL(qmgr_enable_irq);
EXPORT_SYMBOL(qmgr_disable_irq);
EXPORT_SYMBOL(qmgr_request_queue);
EXPORT_SYMBOL(qmgr_release_queue);
/*
* arch/arm/mach-ixp4xx/nas100d-power.c
*
* NAS 100d Power/Reset driver
*
* Copyright (C) 2005 Tower Technologies
*
* based on nas100d-io.c
* Copyright (C) 2004 Karen Spearel
*
* Author: Alessandro Zummo <a.zummo@towertech.it>
* Maintainers: http://www.nslu2-linux.org/
*
* 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/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/reboot.h>
#include <asm/mach-types.h>
static irqreturn_t nas100d_reset_handler(int irq, void *dev_id)
{
/* Signal init to do the ctrlaltdel action, this will bypass init if
* it hasn't started and do a kernel_restart.
*/
ctrl_alt_del();
return IRQ_HANDLED;
}
static int __init nas100d_power_init(void)
{
if (!(machine_is_nas100d()))
return 0;
set_irq_type(NAS100D_RB_IRQ, IRQT_LOW);
if (request_irq(NAS100D_RB_IRQ, &nas100d_reset_handler,
IRQF_DISABLED, "NAS100D reset button", NULL) < 0) {
printk(KERN_DEBUG "Reset Button IRQ %d not available\n",
NAS100D_RB_IRQ);
return -EIO;
}
return 0;
}
static void __exit nas100d_power_exit(void)
{
if (!(machine_is_nas100d()))
return;
free_irq(NAS100D_RB_IRQ, NULL);
}
module_init(nas100d_power_init);
module_exit(nas100d_power_exit);
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("NAS100D Power/Reset driver");
MODULE_LICENSE("GPL");
...@@ -3,8 +3,14 @@ ...@@ -3,8 +3,14 @@
* *
* NAS 100d board-setup * NAS 100d board-setup
* *
* based ixdp425-setup.c: * Copyright (C) 2008 Rod Whitby <rod@whitby.id.au>
*
* based on ixdp425-setup.c:
* Copyright (C) 2003-2004 MontaVista Software, Inc. * Copyright (C) 2003-2004 MontaVista Software, Inc.
* based on nas100d-power.c:
* Copyright (C) 2005 Tower Technologies
* based on nas100d-io.c
* Copyright (C) 2004 Karen Spearel
* *
* Author: Alessandro Zummo <a.zummo@towertech.it> * Author: Alessandro Zummo <a.zummo@towertech.it>
* Author: Rod Whitby <rod@whitby.id.au> * Author: Rod Whitby <rod@whitby.id.au>
...@@ -12,15 +18,22 @@ ...@@ -12,15 +18,22 @@
* *
*/ */
#include <linux/kernel.h> #include <linux/if_ether.h>
#include <linux/irq.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
#include <linux/serial.h> #include <linux/serial.h>
#include <linux/serial_8250.h> #include <linux/serial_8250.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/reboot.h>
#include <linux/i2c.h>
#include <linux/i2c-gpio.h> #include <linux/i2c-gpio.h>
#include <asm/mach-types.h> #include <asm/mach-types.h>
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
#include <asm/mach/flash.h> #include <asm/mach/flash.h>
#include <asm/io.h>
#include <asm/gpio.h>
static struct flash_platform_data nas100d_flash_data = { static struct flash_platform_data nas100d_flash_data = {
.map_name = "cfi_probe", .map_name = "cfi_probe",
...@@ -39,35 +52,40 @@ static struct platform_device nas100d_flash = { ...@@ -39,35 +52,40 @@ static struct platform_device nas100d_flash = {
.resource = &nas100d_flash_resource, .resource = &nas100d_flash_resource,
}; };
#ifdef CONFIG_LEDS_IXP4XX static struct i2c_board_info __initdata nas100d_i2c_board_info [] = {
static struct resource nas100d_led_resources[] = { {
I2C_BOARD_INFO("rtc-pcf8563", 0x51),
},
};
static struct gpio_led nas100d_led_pins[] = {
{ {
.name = "wlan", /* green led */ .name = "wlan", /* green led */
.start = 0, .gpio = NAS100D_LED_WLAN_GPIO,
.end = 0, .active_low = true,
.flags = IXP4XX_GPIO_LOW,
}, },
{ {
.name = "ready", /* blue power led (off is flashing!) */ .name = "power", /* blue power led (off=flashing) */
.start = 15, .gpio = NAS100D_LED_PWR_GPIO,
.end = 15, .active_low = true,
.flags = IXP4XX_GPIO_LOW,
}, },
{ {
.name = "disk", /* yellow led */ .name = "disk", /* yellow led */
.start = 3, .gpio = NAS100D_LED_DISK_GPIO,
.end = 3, .active_low = true,
.flags = IXP4XX_GPIO_LOW,
}, },
}; };
static struct gpio_led_platform_data nas100d_led_data = {
.num_leds = ARRAY_SIZE(nas100d_led_pins),
.leds = nas100d_led_pins,
};
static struct platform_device nas100d_leds = { static struct platform_device nas100d_leds = {
.name = "IXP4XX-GPIO-LED", .name = "leds-gpio",
.id = -1, .id = -1,
.num_resources = ARRAY_SIZE(nas100d_led_resources), .dev.platform_data = &nas100d_led_data,
.resource = nas100d_led_resources,
}; };
#endif
static struct i2c_gpio_platform_data nas100d_i2c_gpio_data = { static struct i2c_gpio_platform_data nas100d_i2c_gpio_data = {
.sda_pin = NAS100D_SDA_PIN, .sda_pin = NAS100D_SDA_PIN,
...@@ -125,12 +143,28 @@ static struct platform_device nas100d_uart = { ...@@ -125,12 +143,28 @@ static struct platform_device nas100d_uart = {
.resource = nas100d_uart_resources, .resource = nas100d_uart_resources,
}; };
/* Built-in 10/100 Ethernet MAC interfaces */
static struct eth_plat_info nas100d_plat_eth[] = {
{
.phy = 0,
.rxq = 3,
.txreadyq = 20,
}
};
static struct platform_device nas100d_eth[] = {
{
.name = "ixp4xx_eth",
.id = IXP4XX_ETH_NPEB,
.dev.platform_data = nas100d_plat_eth,
}
};
static struct platform_device *nas100d_devices[] __initdata = { static struct platform_device *nas100d_devices[] __initdata = {
&nas100d_i2c_gpio, &nas100d_i2c_gpio,
&nas100d_flash, &nas100d_flash,
#ifdef CONFIG_LEDS_IXP4XX
&nas100d_leds, &nas100d_leds,
#endif &nas100d_eth[0],
}; };
static void nas100d_power_off(void) static void nas100d_power_off(void)
...@@ -144,8 +178,63 @@ static void nas100d_power_off(void) ...@@ -144,8 +178,63 @@ static void nas100d_power_off(void)
gpio_line_set(NAS100D_PO_GPIO, IXP4XX_GPIO_HIGH); gpio_line_set(NAS100D_PO_GPIO, IXP4XX_GPIO_HIGH);
} }
/* This is used to make sure the power-button pusher is serious. The button
* must be held until the value of this counter reaches zero.
*/
static int power_button_countdown;
/* Must hold the button down for at least this many counts to be processed */
#define PBUTTON_HOLDDOWN_COUNT 4 /* 2 secs */
static void nas100d_power_handler(unsigned long data);
static DEFINE_TIMER(nas100d_power_timer, nas100d_power_handler, 0, 0);
static void nas100d_power_handler(unsigned long data)
{
/* This routine is called twice per second to check the
* state of the power button.
*/
if (gpio_get_value(NAS100D_PB_GPIO)) {
/* IO Pin is 1 (button pushed) */
if (power_button_countdown > 0)
power_button_countdown--;
} else {
/* Done on button release, to allow for auto-power-on mods. */
if (power_button_countdown == 0) {
/* Signal init to do the ctrlaltdel action,
* this will bypass init if it hasn't started
* and do a kernel_restart.
*/
ctrl_alt_del();
/* Change the state of the power LED to "blink" */
gpio_line_set(NAS100D_LED_PWR_GPIO, IXP4XX_GPIO_LOW);
} else {
power_button_countdown = PBUTTON_HOLDDOWN_COUNT;
}
}
mod_timer(&nas100d_power_timer, jiffies + msecs_to_jiffies(500));
}
static irqreturn_t nas100d_reset_handler(int irq, void *dev_id)
{
/* This is the paper-clip reset, it shuts the machine down directly. */
machine_power_off();
return IRQ_HANDLED;
}
static void __init nas100d_init(void) static void __init nas100d_init(void)
{ {
DECLARE_MAC_BUF(mac_buf);
uint8_t __iomem *f;
int i;
ixp4xx_sys_init(); ixp4xx_sys_init();
/* gpio 14 and 15 are _not_ clocks */ /* gpio 14 and 15 are _not_ clocks */
...@@ -155,7 +244,8 @@ static void __init nas100d_init(void) ...@@ -155,7 +244,8 @@ static void __init nas100d_init(void)
nas100d_flash_resource.end = nas100d_flash_resource.end =
IXP4XX_EXP_BUS_BASE(0) + ixp4xx_exp_bus_size - 1; IXP4XX_EXP_BUS_BASE(0) + ixp4xx_exp_bus_size - 1;
pm_power_off = nas100d_power_off; i2c_register_board_info(0, nas100d_i2c_board_info,
ARRAY_SIZE(nas100d_i2c_board_info));
/* /*
* This is only useful on a modified machine, but it is valuable * This is only useful on a modified machine, but it is valuable
...@@ -165,6 +255,48 @@ static void __init nas100d_init(void) ...@@ -165,6 +255,48 @@ static void __init nas100d_init(void)
(void)platform_device_register(&nas100d_uart); (void)platform_device_register(&nas100d_uart);
platform_add_devices(nas100d_devices, ARRAY_SIZE(nas100d_devices)); platform_add_devices(nas100d_devices, ARRAY_SIZE(nas100d_devices));
pm_power_off = nas100d_power_off;
if (request_irq(gpio_to_irq(NAS100D_RB_GPIO), &nas100d_reset_handler,
IRQF_DISABLED | IRQF_TRIGGER_LOW,
"NAS100D reset button", NULL) < 0) {
printk(KERN_DEBUG "Reset Button IRQ %d not available\n",
gpio_to_irq(NAS100D_RB_GPIO));
}
/* The power button on the Iomega NAS100d is on GPIO 14, but
* it cannot handle interrupts on that GPIO line. So we'll
* have to poll it with a kernel timer.
*/
/* Make sure that the power button GPIO is set up as an input */
gpio_line_config(NAS100D_PB_GPIO, IXP4XX_GPIO_IN);
/* Set the initial value for the power button IRQ handler */
power_button_countdown = PBUTTON_HOLDDOWN_COUNT;
mod_timer(&nas100d_power_timer, jiffies + msecs_to_jiffies(500));
/*
* Map in a portion of the flash and read the MAC address.
* Since it is stored in BE in the flash itself, we need to
* byteswap it if we're in LE mode.
*/
f = ioremap(IXP4XX_EXP_BUS_BASE(0), 0x1000000);
if (f) {
for (i = 0; i < 6; i++)
#ifdef __ARMEB__
nas100d_plat_eth[0].hwaddr[i] = readb(f + 0xFC0FD8 + i);
#else
nas100d_plat_eth[0].hwaddr[i] = readb(f + 0xFC0FD8 + (i^3));
#endif
iounmap(f);
}
printk(KERN_INFO "NAS100D: Using MAC address %s for port 0\n",
print_mac(mac_buf, nas100d_plat_eth[0].hwaddr));
} }
MACHINE_START(NAS100D, "Iomega NAS 100d") MACHINE_START(NAS100D, "Iomega NAS 100d")
......
/*
* arch/arm/mach-ixp4xx/nslu2-power.c
*
* NSLU2 Power/Reset driver
*
* Copyright (C) 2005 Tower Technologies
*
* based on nslu2-io.c
* Copyright (C) 2004 Karen Spearel
*
* Author: Alessandro Zummo <a.zummo@towertech.it>
* Maintainers: http://www.nslu2-linux.org/
*
* 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/module.h>
#include <linux/reboot.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <asm/mach-types.h>
static irqreturn_t nslu2_power_handler(int irq, void *dev_id)
{
/* Signal init to do the ctrlaltdel action, this will bypass init if
* it hasn't started and do a kernel_restart.
*/
ctrl_alt_del();
return IRQ_HANDLED;
}
static irqreturn_t nslu2_reset_handler(int irq, void *dev_id)
{
/* This is the paper-clip reset, it shuts the machine down directly.
*/
machine_power_off();
return IRQ_HANDLED;
}
static int __init nslu2_power_init(void)
{
if (!(machine_is_nslu2()))
return 0;
*IXP4XX_GPIO_GPISR = 0x20400000; /* read the 2 irqs to clr */
set_irq_type(NSLU2_RB_IRQ, IRQT_LOW);
set_irq_type(NSLU2_PB_IRQ, IRQT_HIGH);
if (request_irq(NSLU2_RB_IRQ, &nslu2_reset_handler,
IRQF_DISABLED, "NSLU2 reset button", NULL) < 0) {
printk(KERN_DEBUG "Reset Button IRQ %d not available\n",
NSLU2_RB_IRQ);
return -EIO;
}
if (request_irq(NSLU2_PB_IRQ, &nslu2_power_handler,
IRQF_DISABLED, "NSLU2 power button", NULL) < 0) {
printk(KERN_DEBUG "Power Button IRQ %d not available\n",
NSLU2_PB_IRQ);
return -EIO;
}
return 0;
}
static void __exit nslu2_power_exit(void)
{
if (!(machine_is_nslu2()))
return;
free_irq(NSLU2_RB_IRQ, NULL);
free_irq(NSLU2_PB_IRQ, NULL);
}
module_init(nslu2_power_init);
module_exit(nslu2_power_exit);
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("NSLU2 Power/Reset driver");
MODULE_LICENSE("GPL");
...@@ -3,27 +3,35 @@ ...@@ -3,27 +3,35 @@
* *
* NSLU2 board-setup * NSLU2 board-setup
* *
* based ixdp425-setup.c: * Copyright (C) 2008 Rod Whitby <rod@whitby.id.au>
*
* based on ixdp425-setup.c:
* Copyright (C) 2003-2004 MontaVista Software, Inc. * Copyright (C) 2003-2004 MontaVista Software, Inc.
* based on nslu2-power.c:
* Copyright (C) 2005 Tower Technologies
* *
* Author: Mark Rakes <mrakes at mac.com> * Author: Mark Rakes <mrakes at mac.com>
* Author: Rod Whitby <rod@whitby.id.au> * Author: Rod Whitby <rod@whitby.id.au>
* Author: Alessandro Zummo <a.zummo@towertech.it>
* Maintainers: http://www.nslu2-linux.org/ * Maintainers: http://www.nslu2-linux.org/
* *
* Fixed missing init_time in MACHINE_START kas11 10/22/04
* Changed to conform to new style __init ixdp425 kas11 10/22/04
*/ */
#include <linux/kernel.h> #include <linux/if_ether.h>
#include <linux/irq.h>
#include <linux/serial.h> #include <linux/serial.h>
#include <linux/serial_8250.h> #include <linux/serial_8250.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/reboot.h>
#include <linux/i2c.h>
#include <linux/i2c-gpio.h> #include <linux/i2c-gpio.h>
#include <asm/mach-types.h> #include <asm/mach-types.h>
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
#include <asm/mach/flash.h> #include <asm/mach/flash.h>
#include <asm/mach/time.h> #include <asm/mach/time.h>
#include <asm/io.h>
#include <asm/gpio.h>
static struct flash_platform_data nslu2_flash_data = { static struct flash_platform_data nslu2_flash_data = {
.map_name = "cfi_probe", .map_name = "cfi_probe",
...@@ -47,41 +55,43 @@ static struct i2c_gpio_platform_data nslu2_i2c_gpio_data = { ...@@ -47,41 +55,43 @@ static struct i2c_gpio_platform_data nslu2_i2c_gpio_data = {
.scl_pin = NSLU2_SCL_PIN, .scl_pin = NSLU2_SCL_PIN,
}; };
#ifdef CONFIG_LEDS_IXP4XX static struct i2c_board_info __initdata nslu2_i2c_board_info [] = {
static struct resource nslu2_led_resources[] = { {
I2C_BOARD_INFO("rtc-x1205", 0x6f),
},
};
static struct gpio_led nslu2_led_pins[] = {
{ {
.name = "ready", /* green led */ .name = "ready", /* green led */
.start = NSLU2_LED_GRN_GPIO, .gpio = NSLU2_LED_GRN_GPIO,
.end = NSLU2_LED_GRN_GPIO,
.flags = IXP4XX_GPIO_HIGH,
}, },
{ {
.name = "status", /* red led */ .name = "status", /* red led */
.start = NSLU2_LED_RED_GPIO, .gpio = NSLU2_LED_RED_GPIO,
.end = NSLU2_LED_RED_GPIO,
.flags = IXP4XX_GPIO_HIGH,
}, },
{ {
.name = "disk-1", .name = "disk-1",
.start = NSLU2_LED_DISK1_GPIO, .gpio = NSLU2_LED_DISK1_GPIO,
.end = NSLU2_LED_DISK1_GPIO, .active_low = true,
.flags = IXP4XX_GPIO_LOW,
}, },
{ {
.name = "disk-2", .name = "disk-2",
.start = NSLU2_LED_DISK2_GPIO, .gpio = NSLU2_LED_DISK2_GPIO,
.end = NSLU2_LED_DISK2_GPIO, .active_low = true,
.flags = IXP4XX_GPIO_LOW,
}, },
}; };
static struct gpio_led_platform_data nslu2_led_data = {
.num_leds = ARRAY_SIZE(nslu2_led_pins),
.leds = nslu2_led_pins,
};
static struct platform_device nslu2_leds = { static struct platform_device nslu2_leds = {
.name = "IXP4XX-GPIO-LED", .name = "leds-gpio",
.id = -1, .id = -1,
.num_resources = ARRAY_SIZE(nslu2_led_resources), .dev.platform_data = &nslu2_led_data,
.resource = nslu2_led_resources,
}; };
#endif
static struct platform_device nslu2_i2c_gpio = { static struct platform_device nslu2_i2c_gpio = {
.name = "i2c-gpio", .name = "i2c-gpio",
...@@ -140,13 +150,29 @@ static struct platform_device nslu2_uart = { ...@@ -140,13 +150,29 @@ static struct platform_device nslu2_uart = {
.resource = nslu2_uart_resources, .resource = nslu2_uart_resources,
}; };
/* Built-in 10/100 Ethernet MAC interfaces */
static struct eth_plat_info nslu2_plat_eth[] = {
{
.phy = 1,
.rxq = 3,
.txreadyq = 20,
}
};
static struct platform_device nslu2_eth[] = {
{
.name = "ixp4xx_eth",
.id = IXP4XX_ETH_NPEB,
.dev.platform_data = nslu2_plat_eth,
}
};
static struct platform_device *nslu2_devices[] __initdata = { static struct platform_device *nslu2_devices[] __initdata = {
&nslu2_i2c_gpio, &nslu2_i2c_gpio,
&nslu2_flash, &nslu2_flash,
&nslu2_beeper, &nslu2_beeper,
#ifdef CONFIG_LEDS_IXP4XX
&nslu2_leds, &nslu2_leds,
#endif &nslu2_eth[0],
}; };
static void nslu2_power_off(void) static void nslu2_power_off(void)
...@@ -160,6 +186,25 @@ static void nslu2_power_off(void) ...@@ -160,6 +186,25 @@ static void nslu2_power_off(void)
gpio_line_set(NSLU2_PO_GPIO, IXP4XX_GPIO_HIGH); gpio_line_set(NSLU2_PO_GPIO, IXP4XX_GPIO_HIGH);
} }
static irqreturn_t nslu2_power_handler(int irq, void *dev_id)
{
/* Signal init to do the ctrlaltdel action, this will bypass init if
* it hasn't started and do a kernel_restart.
*/
ctrl_alt_del();
return IRQ_HANDLED;
}
static irqreturn_t nslu2_reset_handler(int irq, void *dev_id)
{
/* This is the paper-clip reset, it shuts the machine down directly.
*/
machine_power_off();
return IRQ_HANDLED;
}
static void __init nslu2_timer_init(void) static void __init nslu2_timer_init(void)
{ {
/* The xtal on this machine is non-standard. */ /* The xtal on this machine is non-standard. */
...@@ -175,13 +220,18 @@ static struct sys_timer nslu2_timer = { ...@@ -175,13 +220,18 @@ static struct sys_timer nslu2_timer = {
static void __init nslu2_init(void) static void __init nslu2_init(void)
{ {
DECLARE_MAC_BUF(mac_buf);
uint8_t __iomem *f;
int i;
ixp4xx_sys_init(); ixp4xx_sys_init();
nslu2_flash_resource.start = IXP4XX_EXP_BUS_BASE(0); nslu2_flash_resource.start = IXP4XX_EXP_BUS_BASE(0);
nslu2_flash_resource.end = nslu2_flash_resource.end =
IXP4XX_EXP_BUS_BASE(0) + ixp4xx_exp_bus_size - 1; IXP4XX_EXP_BUS_BASE(0) + ixp4xx_exp_bus_size - 1;
pm_power_off = nslu2_power_off; i2c_register_board_info(0, nslu2_i2c_board_info,
ARRAY_SIZE(nslu2_i2c_board_info));
/* /*
* This is only useful on a modified machine, but it is valuable * This is only useful on a modified machine, but it is valuable
...@@ -191,6 +241,43 @@ static void __init nslu2_init(void) ...@@ -191,6 +241,43 @@ static void __init nslu2_init(void)
(void)platform_device_register(&nslu2_uart); (void)platform_device_register(&nslu2_uart);
platform_add_devices(nslu2_devices, ARRAY_SIZE(nslu2_devices)); platform_add_devices(nslu2_devices, ARRAY_SIZE(nslu2_devices));
pm_power_off = nslu2_power_off;
if (request_irq(gpio_to_irq(NSLU2_RB_GPIO), &nslu2_reset_handler,
IRQF_DISABLED | IRQF_TRIGGER_LOW,
"NSLU2 reset button", NULL) < 0) {
printk(KERN_DEBUG "Reset Button IRQ %d not available\n",
gpio_to_irq(NSLU2_RB_GPIO));
}
if (request_irq(gpio_to_irq(NSLU2_PB_GPIO), &nslu2_power_handler,
IRQF_DISABLED | IRQF_TRIGGER_HIGH,
"NSLU2 power button", NULL) < 0) {
printk(KERN_DEBUG "Power Button IRQ %d not available\n",
gpio_to_irq(NSLU2_PB_GPIO));
}
/*
* Map in a portion of the flash and read the MAC address.
* Since it is stored in BE in the flash itself, we need to
* byteswap it if we're in LE mode.
*/
f = ioremap(IXP4XX_EXP_BUS_BASE(0), 0x40000);
if (f) {
for (i = 0; i < 6; i++)
#ifdef __ARMEB__
nslu2_plat_eth[0].hwaddr[i] = readb(f + 0x3FFB0 + i);
#else
nslu2_plat_eth[0].hwaddr[i] = readb(f + 0x3FFB0 + (i^3));
#endif
iounmap(f);
}
printk(KERN_INFO "NSLU2: Using MAC address %s for port 0\n",
print_mac(mac_buf, nslu2_plat_eth[0].hwaddr));
} }
MACHINE_START(NSLU2, "Linksys NSLU2") MACHINE_START(NSLU2, "Linksys NSLU2")
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
obj-y += clock.o devices.o generic.o irq.o dma.o time.o obj-y += clock.o devices.o generic.o irq.o dma.o time.o
obj-$(CONFIG_PXA25x) += pxa25x.o obj-$(CONFIG_PXA25x) += pxa25x.o
obj-$(CONFIG_PXA27x) += pxa27x.o obj-$(CONFIG_PXA27x) += pxa27x.o
obj-$(CONFIG_PXA3xx) += pxa3xx.o mfp.o obj-$(CONFIG_PXA3xx) += pxa3xx.o mfp.o smemc.o
obj-$(CONFIG_CPU_PXA300) += pxa300.o obj-$(CONFIG_CPU_PXA300) += pxa300.o
obj-$(CONFIG_CPU_PXA320) += pxa320.o obj-$(CONFIG_CPU_PXA320) += pxa320.o
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <asm/mach/map.h> #include <asm/mach/map.h>
#include <asm/arch/pxa-regs.h> #include <asm/arch/pxa-regs.h>
#include <asm/arch/pxa2xx-regs.h>
#include <asm/arch/pxafb.h> #include <asm/arch/pxafb.h>
#include <asm/arch/ohci.h> #include <asm/arch/ohci.h>
#include <asm/arch/mmc.h> #include <asm/arch/mmc.h>
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <asm/arch/mmc.h> #include <asm/arch/mmc.h>
#include <asm/arch/irda.h> #include <asm/arch/irda.h>
#include <asm/arch/i2c.h> #include <asm/arch/i2c.h>
#include <asm/arch/ohci.h>
#include "devices.h" #include "devices.h"
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/sysdev.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/irq.h> #include <asm/irq.h>
...@@ -226,3 +227,59 @@ void __init pxa_map_io(void) ...@@ -226,3 +227,59 @@ void __init pxa_map_io(void)
iotable_init(standard_io_desc, ARRAY_SIZE(standard_io_desc)); iotable_init(standard_io_desc, ARRAY_SIZE(standard_io_desc));
get_clk_frequency_khz(1); get_clk_frequency_khz(1);
} }
#ifdef CONFIG_PM
static unsigned long saved_gplr[4];
static unsigned long saved_gpdr[4];
static unsigned long saved_grer[4];
static unsigned long saved_gfer[4];
static int pxa_gpio_suspend(struct sys_device *dev, pm_message_t state)
{
int i, gpio;
for (gpio = 0, i = 0; gpio < pxa_last_gpio; gpio += 32, i++) {
saved_gplr[i] = GPLR(gpio);
saved_gpdr[i] = GPDR(gpio);
saved_grer[i] = GRER(gpio);
saved_gfer[i] = GFER(gpio);
/* Clear GPIO transition detect bits */
GEDR(gpio) = GEDR(gpio);
}
return 0;
}
static int pxa_gpio_resume(struct sys_device *dev)
{
int i, gpio;
for (gpio = 0, i = 0; gpio < pxa_last_gpio; gpio += 32, i++) {
/* restore level with set/clear */
GPSR(gpio) = saved_gplr[i];
GPCR(gpio) = ~saved_gplr[i];
GRER(gpio) = saved_grer[i];
GFER(gpio) = saved_gfer[i];
GPDR(gpio) = saved_gpdr[i];
}
return 0;
}
#else
#define pxa_gpio_suspend NULL
#define pxa_gpio_resume NULL
#endif
struct sysdev_class pxa_gpio_sysclass = {
.name = "gpio",
.suspend = pxa_gpio_suspend,
.resume = pxa_gpio_resume,
};
static int __init pxa_gpio_init(void)
{
return sysdev_class_register(&pxa_gpio_sysclass);
}
core_initcall(pxa_gpio_init);
...@@ -52,3 +52,6 @@ extern unsigned pxa3xx_get_memclk_frequency_10khz(void); ...@@ -52,3 +52,6 @@ extern unsigned pxa3xx_get_memclk_frequency_10khz(void);
#define pxa3xx_get_clk_frequency_khz(x) (0) #define pxa3xx_get_clk_frequency_khz(x) (0)
#define pxa3xx_get_memclk_frequency_10khz() (0) #define pxa3xx_get_memclk_frequency_10khz() (0)
#endif #endif
extern struct sysdev_class pxa_irq_sysclass;
extern struct sysdev_class pxa_gpio_sysclass;
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/sysdev.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/irq.h> #include <asm/irq.h>
...@@ -321,3 +322,64 @@ void __init pxa_init_irq_set_wake(int (*set_wake)(unsigned int, unsigned int)) ...@@ -321,3 +322,64 @@ void __init pxa_init_irq_set_wake(int (*set_wake)(unsigned int, unsigned int))
pxa_low_gpio_chip.set_wake = set_wake; pxa_low_gpio_chip.set_wake = set_wake;
pxa_muxed_gpio_chip.set_wake = set_wake; pxa_muxed_gpio_chip.set_wake = set_wake;
} }
#ifdef CONFIG_PM
static unsigned long saved_icmr[2];
static int pxa_irq_suspend(struct sys_device *dev, pm_message_t state)
{
switch (dev->id) {
case 0:
saved_icmr[0] = ICMR;
ICMR = 0;
break;
#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx)
case 1:
saved_icmr[1] = ICMR2;
ICMR2 = 0;
break;
#endif
default:
return -EINVAL;
}
return 0;
}
static int pxa_irq_resume(struct sys_device *dev)
{
switch (dev->id) {
case 0:
ICMR = saved_icmr[0];
ICLR = 0;
ICCR = 1;
break;
#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx)
case 1:
ICMR2 = saved_icmr[1];
ICLR2 = 0;
break;
#endif
default:
return -EINVAL;
}
return 0;
}
#else
#define pxa_irq_suspend NULL
#define pxa_irq_resume NULL
#endif
struct sysdev_class pxa_irq_sysclass = {
.name = "irq",
.suspend = pxa_irq_suspend,
.resume = pxa_irq_resume,
};
static int __init pxa_irq_init(void)
{
return sysdev_class_register(&pxa_irq_sysclass);
}
core_initcall(pxa_irq_init);
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/arch/mfp.h> #include <asm/arch/mfp.h>
#include <asm/arch/mfp-pxa3xx.h> #include <asm/arch/mfp-pxa3xx.h>
#include <asm/arch/pxa3xx-regs.h>
/* mfp_spin_lock is used to ensure that MFP register configuration /* mfp_spin_lock is used to ensure that MFP register configuration
* (most likely a read-modify-write operation) is atomic, and that * (most likely a read-modify-write operation) is atomic, and that
...@@ -223,11 +224,19 @@ static int pxa3xx_mfp_resume(struct sys_device *d) ...@@ -223,11 +224,19 @@ static int pxa3xx_mfp_resume(struct sys_device *d)
struct pxa3xx_mfp_pin *p = &mfp_table[pin]; struct pxa3xx_mfp_pin *p = &mfp_table[pin];
__mfp_config_run(p); __mfp_config_run(p);
} }
/* clear RDH bit when MFP settings are restored
*
* NOTE: the last 3 bits DxS are write-1-to-clear so carefully
* preserve them here in case they will be referenced later
*/
ASCR &= ~(ASCR_RDH | ASCR_D1S | ASCR_D2S | ASCR_D3S);
return 0; return 0;
} }
static struct sysdev_class mfp_sysclass = { static struct sysdev_class mfp_sysclass = {
set_kset_name("mfp"), .name = "mfp",
.suspend = pxa3xx_mfp_suspend, .suspend = pxa3xx_mfp_suspend,
.resume = pxa3xx_mfp_resume, .resume = pxa3xx_mfp_resume,
}; };
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
#include <asm/arch/hardware.h> #include <asm/arch/hardware.h>
#include <asm/arch/pxa-regs.h> #include <asm/arch/pxa-regs.h>
#include <asm/arch/pxa2xx-regs.h>
#include <asm/arch/pxa2xx_spi.h> #include <asm/arch/pxa2xx_spi.h>
#include <asm/arch/pcm027.h> #include <asm/arch/pcm027.h>
#include "generic.h" #include "generic.h"
......
...@@ -164,7 +164,7 @@ static struct resource poodlets_resources[] = { ...@@ -164,7 +164,7 @@ static struct resource poodlets_resources[] = {
}, },
}; };
static unsigned long poodle_get_hsync_len(void) static unsigned long poodle_get_hsync_invperiod(void)
{ {
return 0; return 0;
} }
...@@ -174,9 +174,9 @@ static void poodle_null_hsync(void) ...@@ -174,9 +174,9 @@ static void poodle_null_hsync(void)
} }
static struct corgits_machinfo poodle_ts_machinfo = { static struct corgits_machinfo poodle_ts_machinfo = {
.get_hsync_len = poodle_get_hsync_len, .get_hsync_invperiod = poodle_get_hsync_invperiod,
.put_hsync = poodle_null_hsync, .put_hsync = poodle_null_hsync,
.wait_hsync = poodle_null_hsync, .wait_hsync = poodle_null_hsync,
}; };
static struct platform_device poodle_ts_device = { static struct platform_device poodle_ts_device = {
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/sysdev.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/arch/irqs.h> #include <asm/arch/irqs.h>
...@@ -141,11 +142,6 @@ static struct clk pxa25x_clks[] = { ...@@ -141,11 +142,6 @@ static struct clk pxa25x_clks[] = {
#define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x #define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x
#define RESTORE(x) x = sleep_save[SLEEP_SAVE_##x] #define RESTORE(x) x = sleep_save[SLEEP_SAVE_##x]
#define RESTORE_GPLEVEL(n) do { \
GPSR##n = sleep_save[SLEEP_SAVE_GPLR##n]; \
GPCR##n = ~sleep_save[SLEEP_SAVE_GPLR##n]; \
} while (0)
/* /*
* List of global PXA peripheral registers to preserve. * List of global PXA peripheral registers to preserve.
* More ones like CP and general purpose register values are preserved * More ones like CP and general purpose register values are preserved
...@@ -153,10 +149,6 @@ static struct clk pxa25x_clks[] = { ...@@ -153,10 +149,6 @@ static struct clk pxa25x_clks[] = {
*/ */
enum { SLEEP_SAVE_START = 0, enum { SLEEP_SAVE_START = 0,
SLEEP_SAVE_GPLR0, SLEEP_SAVE_GPLR1, SLEEP_SAVE_GPLR2,
SLEEP_SAVE_GPDR0, SLEEP_SAVE_GPDR1, SLEEP_SAVE_GPDR2,
SLEEP_SAVE_GRER0, SLEEP_SAVE_GRER1, SLEEP_SAVE_GRER2,
SLEEP_SAVE_GFER0, SLEEP_SAVE_GFER1, SLEEP_SAVE_GFER2,
SLEEP_SAVE_PGSR0, SLEEP_SAVE_PGSR1, SLEEP_SAVE_PGSR2, SLEEP_SAVE_PGSR0, SLEEP_SAVE_PGSR1, SLEEP_SAVE_PGSR2,
SLEEP_SAVE_GAFR0_L, SLEEP_SAVE_GAFR0_U, SLEEP_SAVE_GAFR0_L, SLEEP_SAVE_GAFR0_U,
...@@ -165,7 +157,6 @@ enum { SLEEP_SAVE_START = 0, ...@@ -165,7 +157,6 @@ enum { SLEEP_SAVE_START = 0,
SLEEP_SAVE_PSTR, SLEEP_SAVE_PSTR,
SLEEP_SAVE_ICMR,
SLEEP_SAVE_CKEN, SLEEP_SAVE_CKEN,
SLEEP_SAVE_SIZE SLEEP_SAVE_SIZE
...@@ -174,17 +165,12 @@ enum { SLEEP_SAVE_START = 0, ...@@ -174,17 +165,12 @@ enum { SLEEP_SAVE_START = 0,
static void pxa25x_cpu_pm_save(unsigned long *sleep_save) static void pxa25x_cpu_pm_save(unsigned long *sleep_save)
{ {
SAVE(GPLR0); SAVE(GPLR1); SAVE(GPLR2);
SAVE(GPDR0); SAVE(GPDR1); SAVE(GPDR2);
SAVE(GRER0); SAVE(GRER1); SAVE(GRER2);
SAVE(GFER0); SAVE(GFER1); SAVE(GFER2);
SAVE(PGSR0); SAVE(PGSR1); SAVE(PGSR2); SAVE(PGSR0); SAVE(PGSR1); SAVE(PGSR2);
SAVE(GAFR0_L); SAVE(GAFR0_U); SAVE(GAFR0_L); SAVE(GAFR0_U);
SAVE(GAFR1_L); SAVE(GAFR1_U); SAVE(GAFR1_L); SAVE(GAFR1_U);
SAVE(GAFR2_L); SAVE(GAFR2_U); SAVE(GAFR2_L); SAVE(GAFR2_U);
SAVE(ICMR); ICMR = 0;
SAVE(CKEN); SAVE(CKEN);
SAVE(PSTR); SAVE(PSTR);
...@@ -198,22 +184,14 @@ static void pxa25x_cpu_pm_restore(unsigned long *sleep_save) ...@@ -198,22 +184,14 @@ static void pxa25x_cpu_pm_restore(unsigned long *sleep_save)
PSPR = 0; PSPR = 0;
/* restore registers */ /* restore registers */
RESTORE_GPLEVEL(0); RESTORE_GPLEVEL(1); RESTORE_GPLEVEL(2);
RESTORE(GPDR0); RESTORE(GPDR1); RESTORE(GPDR2);
RESTORE(GAFR0_L); RESTORE(GAFR0_U); RESTORE(GAFR0_L); RESTORE(GAFR0_U);
RESTORE(GAFR1_L); RESTORE(GAFR1_U); RESTORE(GAFR1_L); RESTORE(GAFR1_U);
RESTORE(GAFR2_L); RESTORE(GAFR2_U); RESTORE(GAFR2_L); RESTORE(GAFR2_U);
RESTORE(GRER0); RESTORE(GRER1); RESTORE(GRER2);
RESTORE(GFER0); RESTORE(GFER1); RESTORE(GFER2);
RESTORE(PGSR0); RESTORE(PGSR1); RESTORE(PGSR2); RESTORE(PGSR0); RESTORE(PGSR1); RESTORE(PGSR2);
PSSR = PSSR_RDH | PSSR_PH; PSSR = PSSR_RDH | PSSR_PH;
RESTORE(CKEN); RESTORE(CKEN);
ICLR = 0;
ICCR = 1;
RESTORE(ICMR);
RESTORE(PSTR); RESTORE(PSTR);
} }
...@@ -304,9 +282,17 @@ static struct platform_device *pxa25x_devices[] __initdata = { ...@@ -304,9 +282,17 @@ static struct platform_device *pxa25x_devices[] __initdata = {
&pxa25x_device_assp, &pxa25x_device_assp,
}; };
static struct sys_device pxa25x_sysdev[] = {
{
.cls = &pxa_irq_sysclass,
}, {
.cls = &pxa_gpio_sysclass,
},
};
static int __init pxa25x_init(void) static int __init pxa25x_init(void)
{ {
int ret = 0; int i, ret = 0;
/* Only add HWUART for PXA255/26x; PXA210/250/27x do not have it. */ /* Only add HWUART for PXA255/26x; PXA210/250/27x do not have it. */
if (cpu_is_pxa25x()) if (cpu_is_pxa25x())
...@@ -320,9 +306,18 @@ static int __init pxa25x_init(void) ...@@ -320,9 +306,18 @@ static int __init pxa25x_init(void)
pxa25x_init_pm(); pxa25x_init_pm();
for (i = 0; i < ARRAY_SIZE(pxa25x_sysdev); i++) {
ret = sysdev_register(&pxa25x_sysdev[i]);
if (ret)
pr_err("failed to register sysdev[%d]\n", i);
}
ret = platform_add_devices(pxa25x_devices, ret = platform_add_devices(pxa25x_devices,
ARRAY_SIZE(pxa25x_devices)); ARRAY_SIZE(pxa25x_devices));
if (ret)
return ret;
} }
/* Only add HWUART for PXA255/26x; PXA210/250/27x do not have it. */ /* Only add HWUART for PXA255/26x; PXA210/250/27x do not have it. */
if (cpu_is_pxa25x()) if (cpu_is_pxa25x())
ret = platform_device_register(&pxa_device_hwuart); ret = platform_device_register(&pxa_device_hwuart);
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/sysdev.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/irq.h> #include <asm/irq.h>
...@@ -171,11 +172,6 @@ static struct clk pxa27x_clks[] = { ...@@ -171,11 +172,6 @@ static struct clk pxa27x_clks[] = {
#define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x #define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x
#define RESTORE(x) x = sleep_save[SLEEP_SAVE_##x] #define RESTORE(x) x = sleep_save[SLEEP_SAVE_##x]
#define RESTORE_GPLEVEL(n) do { \
GPSR##n = sleep_save[SLEEP_SAVE_GPLR##n]; \
GPCR##n = ~sleep_save[SLEEP_SAVE_GPLR##n]; \
} while (0)
/* /*
* List of global PXA peripheral registers to preserve. * List of global PXA peripheral registers to preserve.
* More ones like CP and general purpose register values are preserved * More ones like CP and general purpose register values are preserved
...@@ -183,10 +179,6 @@ static struct clk pxa27x_clks[] = { ...@@ -183,10 +179,6 @@ static struct clk pxa27x_clks[] = {
*/ */
enum { SLEEP_SAVE_START = 0, enum { SLEEP_SAVE_START = 0,
SLEEP_SAVE_GPLR0, SLEEP_SAVE_GPLR1, SLEEP_SAVE_GPLR2, SLEEP_SAVE_GPLR3,
SLEEP_SAVE_GPDR0, SLEEP_SAVE_GPDR1, SLEEP_SAVE_GPDR2, SLEEP_SAVE_GPDR3,
SLEEP_SAVE_GRER0, SLEEP_SAVE_GRER1, SLEEP_SAVE_GRER2, SLEEP_SAVE_GRER3,
SLEEP_SAVE_GFER0, SLEEP_SAVE_GFER1, SLEEP_SAVE_GFER2, SLEEP_SAVE_GFER3,
SLEEP_SAVE_PGSR0, SLEEP_SAVE_PGSR1, SLEEP_SAVE_PGSR2, SLEEP_SAVE_PGSR3, SLEEP_SAVE_PGSR0, SLEEP_SAVE_PGSR1, SLEEP_SAVE_PGSR2, SLEEP_SAVE_PGSR3,
SLEEP_SAVE_GAFR0_L, SLEEP_SAVE_GAFR0_U, SLEEP_SAVE_GAFR0_L, SLEEP_SAVE_GAFR0_U,
...@@ -196,7 +188,6 @@ enum { SLEEP_SAVE_START = 0, ...@@ -196,7 +188,6 @@ enum { SLEEP_SAVE_START = 0,
SLEEP_SAVE_PSTR, SLEEP_SAVE_PSTR,
SLEEP_SAVE_ICMR,
SLEEP_SAVE_CKEN, SLEEP_SAVE_CKEN,
SLEEP_SAVE_MDREFR, SLEEP_SAVE_MDREFR,
...@@ -208,10 +199,6 @@ enum { SLEEP_SAVE_START = 0, ...@@ -208,10 +199,6 @@ enum { SLEEP_SAVE_START = 0,
void pxa27x_cpu_pm_save(unsigned long *sleep_save) void pxa27x_cpu_pm_save(unsigned long *sleep_save)
{ {
SAVE(GPLR0); SAVE(GPLR1); SAVE(GPLR2); SAVE(GPLR3);
SAVE(GPDR0); SAVE(GPDR1); SAVE(GPDR2); SAVE(GPDR3);
SAVE(GRER0); SAVE(GRER1); SAVE(GRER2); SAVE(GRER3);
SAVE(GFER0); SAVE(GFER1); SAVE(GFER2); SAVE(GFER3);
SAVE(PGSR0); SAVE(PGSR1); SAVE(PGSR2); SAVE(PGSR3); SAVE(PGSR0); SAVE(PGSR1); SAVE(PGSR2); SAVE(PGSR3);
SAVE(GAFR0_L); SAVE(GAFR0_U); SAVE(GAFR0_L); SAVE(GAFR0_U);
...@@ -223,12 +210,8 @@ void pxa27x_cpu_pm_save(unsigned long *sleep_save) ...@@ -223,12 +210,8 @@ void pxa27x_cpu_pm_save(unsigned long *sleep_save)
SAVE(PWER); SAVE(PCFR); SAVE(PRER); SAVE(PWER); SAVE(PCFR); SAVE(PRER);
SAVE(PFER); SAVE(PKWR); SAVE(PFER); SAVE(PKWR);
SAVE(ICMR); ICMR = 0;
SAVE(CKEN); SAVE(CKEN);
SAVE(PSTR); SAVE(PSTR);
/* Clear GPIO transition detect bits */
GEDR0 = GEDR0; GEDR1 = GEDR1; GEDR2 = GEDR2; GEDR3 = GEDR3;
} }
void pxa27x_cpu_pm_restore(unsigned long *sleep_save) void pxa27x_cpu_pm_restore(unsigned long *sleep_save)
...@@ -237,15 +220,10 @@ void pxa27x_cpu_pm_restore(unsigned long *sleep_save) ...@@ -237,15 +220,10 @@ void pxa27x_cpu_pm_restore(unsigned long *sleep_save)
PSPR = 0; PSPR = 0;
/* restore registers */ /* restore registers */
RESTORE_GPLEVEL(0); RESTORE_GPLEVEL(1);
RESTORE_GPLEVEL(2); RESTORE_GPLEVEL(3);
RESTORE(GPDR0); RESTORE(GPDR1); RESTORE(GPDR2); RESTORE(GPDR3);
RESTORE(GAFR0_L); RESTORE(GAFR0_U); RESTORE(GAFR0_L); RESTORE(GAFR0_U);
RESTORE(GAFR1_L); RESTORE(GAFR1_U); RESTORE(GAFR1_L); RESTORE(GAFR1_U);
RESTORE(GAFR2_L); RESTORE(GAFR2_U); RESTORE(GAFR2_L); RESTORE(GAFR2_U);
RESTORE(GAFR3_L); RESTORE(GAFR3_U); RESTORE(GAFR3_L); RESTORE(GAFR3_U);
RESTORE(GRER0); RESTORE(GRER1); RESTORE(GRER2); RESTORE(GRER3);
RESTORE(GFER0); RESTORE(GFER1); RESTORE(GFER2); RESTORE(GFER3);
RESTORE(PGSR0); RESTORE(PGSR1); RESTORE(PGSR2); RESTORE(PGSR3); RESTORE(PGSR0); RESTORE(PGSR1); RESTORE(PGSR2); RESTORE(PGSR3);
RESTORE(MDREFR); RESTORE(MDREFR);
...@@ -256,9 +234,6 @@ void pxa27x_cpu_pm_restore(unsigned long *sleep_save) ...@@ -256,9 +234,6 @@ void pxa27x_cpu_pm_restore(unsigned long *sleep_save)
RESTORE(CKEN); RESTORE(CKEN);
ICLR = 0;
ICCR = 1;
RESTORE(ICMR);
RESTORE(PSTR); RESTORE(PSTR);
} }
...@@ -409,9 +384,22 @@ static struct platform_device *devices[] __initdata = { ...@@ -409,9 +384,22 @@ static struct platform_device *devices[] __initdata = {
&pxa27x_device_ssp3, &pxa27x_device_ssp3,
}; };
static struct sys_device pxa27x_sysdev[] = {
{
.id = 0,
.cls = &pxa_irq_sysclass,
}, {
.id = 1,
.cls = &pxa_irq_sysclass,
}, {
.cls = &pxa_gpio_sysclass,
},
};
static int __init pxa27x_init(void) static int __init pxa27x_init(void)
{ {
int ret = 0; int i, ret = 0;
if (cpu_is_pxa27x()) { if (cpu_is_pxa27x()) {
clks_register(pxa27x_clks, ARRAY_SIZE(pxa27x_clks)); clks_register(pxa27x_clks, ARRAY_SIZE(pxa27x_clks));
...@@ -420,8 +408,15 @@ static int __init pxa27x_init(void) ...@@ -420,8 +408,15 @@ static int __init pxa27x_init(void)
pxa27x_init_pm(); pxa27x_init_pm();
for (i = 0; i < ARRAY_SIZE(pxa27x_sysdev); i++) {
ret = sysdev_register(&pxa27x_sysdev[i]);
if (ret)
pr_err("failed to register sysdev[%d]\n", i);
}
ret = platform_add_devices(devices, ARRAY_SIZE(devices)); ret = platform_add_devices(devices, ARRAY_SIZE(devices));
} }
return ret; return ret;
} }
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/sysdev.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/arch/pxa3xx-regs.h> #include <asm/arch/pxa3xx-regs.h>
...@@ -39,6 +40,7 @@ ...@@ -39,6 +40,7 @@
#define RO_CLK 60000000 #define RO_CLK 60000000
#define ACCR_D0CS (1 << 26) #define ACCR_D0CS (1 << 26)
#define ACCR_PCCE (1 << 11)
/* crystal frequency to static memory controller multiplier (SMCFS) */ /* crystal frequency to static memory controller multiplier (SMCFS) */
static unsigned char smcfs_mult[8] = { 6, 0, 8, 0, 0, 16, }; static unsigned char smcfs_mult[8] = { 6, 0, 8, 0, 0, 16, };
...@@ -203,7 +205,6 @@ static struct clk pxa3xx_clks[] = { ...@@ -203,7 +205,6 @@ static struct clk pxa3xx_clks[] = {
}; };
#ifdef CONFIG_PM #ifdef CONFIG_PM
#define SLEEP_SAVE_SIZE 4
#define ISRAM_START 0x5c000000 #define ISRAM_START 0x5c000000
#define ISRAM_SIZE SZ_256K #define ISRAM_SIZE SZ_256K
...@@ -211,25 +212,29 @@ static struct clk pxa3xx_clks[] = { ...@@ -211,25 +212,29 @@ static struct clk pxa3xx_clks[] = {
static void __iomem *sram; static void __iomem *sram;
static unsigned long wakeup_src; static unsigned long wakeup_src;
static void pxa3xx_cpu_pm_save(unsigned long *sleep_save) #define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x
{ #define RESTORE(x) x = sleep_save[SLEEP_SAVE_##x]
pr_debug("PM: CKENA=%08x CKENB=%08x\n", CKENA, CKENB);
if (CKENA & (1 << CKEN_USBH)) { enum { SLEEP_SAVE_START = 0,
printk(KERN_ERR "PM: USB host clock not stopped?\n"); SLEEP_SAVE_CKENA,
CKENA &= ~(1 << CKEN_USBH); SLEEP_SAVE_CKENB,
} SLEEP_SAVE_ACCR,
// CKENA |= 1 << (CKEN_ISC & 31);
/* SLEEP_SAVE_SIZE,
* Low power modes require the HSIO2 clock to be enabled. };
*/
CKENB |= 1 << (CKEN_HSIO2 & 31); static void pxa3xx_cpu_pm_save(unsigned long *sleep_save)
{
SAVE(CKENA);
SAVE(CKENB);
SAVE(ACCR);
} }
static void pxa3xx_cpu_pm_restore(unsigned long *sleep_save) static void pxa3xx_cpu_pm_restore(unsigned long *sleep_save)
{ {
CKENB &= ~(1 << (CKEN_HSIO2 & 31)); RESTORE(ACCR);
RESTORE(CKENA);
RESTORE(CKENB);
} }
/* /*
...@@ -265,6 +270,46 @@ static void pxa3xx_cpu_standby(unsigned int pwrmode) ...@@ -265,6 +270,46 @@ static void pxa3xx_cpu_standby(unsigned int pwrmode)
printk("PM: AD2D0SR=%08x ASCR=%08x\n", AD2D0SR, ASCR); printk("PM: AD2D0SR=%08x ASCR=%08x\n", AD2D0SR, ASCR);
} }
/*
* NOTE: currently, the OBM (OEM Boot Module) binary comes along with
* PXA3xx development kits assumes that the resuming process continues
* with the address stored within the first 4 bytes of SDRAM. The PSPR
* register is used privately by BootROM and OBM, and _must_ be set to
* 0x5c014000 for the moment.
*/
static void pxa3xx_cpu_pm_suspend(void)
{
volatile unsigned long *p = (volatile void *)0xc0000000;
unsigned long saved_data = *p;
extern void pxa3xx_cpu_suspend(void);
extern void pxa3xx_cpu_resume(void);
/* resuming from D2 requires the HSIO2/BOOT/TPM clocks enabled */
CKENA |= (1 << CKEN_BOOT) | (1 << CKEN_TPM);
CKENB |= 1 << (CKEN_HSIO2 & 0x1f);
/* clear and setup wakeup source */
AD3SR = ~0;
AD3ER = wakeup_src;
ASCR = ASCR;
ARSR = ARSR;
PCFR |= (1u << 13); /* L1_DIS */
PCFR &= ~((1u << 12) | (1u << 1)); /* L0_EN | SL_ROD */
PSPR = 0x5c014000;
/* overwrite with the resume address */
*p = virt_to_phys(pxa3xx_cpu_resume);
pxa3xx_cpu_suspend();
*p = saved_data;
AD3ER = 0;
}
static void pxa3xx_cpu_pm_enter(suspend_state_t state) static void pxa3xx_cpu_pm_enter(suspend_state_t state)
{ {
/* /*
...@@ -279,6 +324,7 @@ static void pxa3xx_cpu_pm_enter(suspend_state_t state) ...@@ -279,6 +324,7 @@ static void pxa3xx_cpu_pm_enter(suspend_state_t state)
break; break;
case PM_SUSPEND_MEM: case PM_SUSPEND_MEM:
pxa3xx_cpu_pm_suspend();
break; break;
} }
} }
...@@ -452,9 +498,21 @@ static struct platform_device *devices[] __initdata = { ...@@ -452,9 +498,21 @@ static struct platform_device *devices[] __initdata = {
&pxa3xx_device_ssp4, &pxa3xx_device_ssp4,
}; };
static struct sys_device pxa3xx_sysdev[] = {
{
.id = 0,
.cls = &pxa_irq_sysclass,
}, {
.id = 1,
.cls = &pxa_irq_sysclass,
}, {
.cls = &pxa_gpio_sysclass,
},
};
static int __init pxa3xx_init(void) static int __init pxa3xx_init(void)
{ {
int ret = 0; int i, ret = 0;
if (cpu_is_pxa3xx()) { if (cpu_is_pxa3xx()) {
clks_register(pxa3xx_clks, ARRAY_SIZE(pxa3xx_clks)); clks_register(pxa3xx_clks, ARRAY_SIZE(pxa3xx_clks));
...@@ -464,9 +522,16 @@ static int __init pxa3xx_init(void) ...@@ -464,9 +522,16 @@ static int __init pxa3xx_init(void)
pxa3xx_init_pm(); pxa3xx_init_pm();
return platform_add_devices(devices, ARRAY_SIZE(devices)); for (i = 0; i < ARRAY_SIZE(pxa3xx_sysdev); i++) {
ret = sysdev_register(&pxa3xx_sysdev[i]);
if (ret)
pr_err("failed to register sysdev[%d]\n", i);
}
ret = platform_add_devices(devices, ARRAY_SIZE(devices));
} }
return 0;
return ret;
} }
subsys_initcall(pxa3xx_init); subsys_initcall(pxa3xx_init);
...@@ -50,6 +50,108 @@ pxa_cpu_save_sp: ...@@ -50,6 +50,108 @@ pxa_cpu_save_sp:
str r0, [r1] str r0, [r1]
ldr pc, [sp], #4 ldr pc, [sp], #4
#ifdef CONFIG_PXA3xx
/*
* pxa3xx_cpu_suspend() - forces CPU into sleep state (S2D3C4)
*
* NOTE: unfortunately, pxa_cpu_save_cp can not be reused here since
* the auxiliary control register address is different between pxa3xx
* and pxa{25x,27x}
*/
ENTRY(pxa3xx_cpu_suspend)
#ifndef CONFIG_IWMMXT
mra r2, r3, acc0
#endif
stmfd sp!, {r2 - r12, lr} @ save registers on stack
mrc p14, 0, r3, c6, c0, 0 @ clock configuration, for turbo mode
mrc p15, 0, r4, c15, c1, 0 @ CP access reg
mrc p15, 0, r5, c13, c0, 0 @ PID
mrc p15, 0, r6, c3, c0, 0 @ domain ID
mrc p15, 0, r7, c2, c0, 0 @ translation table base addr
mrc p15, 0, r8, c1, c0, 1 @ auxiliary control reg
mrc p15, 0, r9, c1, c0, 0 @ control reg
bic r3, r3, #2 @ clear frequency change bit
@ store them plus current virtual stack ptr on stack
mov r10, sp
stmfd sp!, {r3 - r10}
@ store physical address of stack pointer
mov r0, sp
bl sleep_phys_sp
ldr r1, =sleep_save_sp
str r0, [r1]
@ clean data cache
bl xsc3_flush_kern_cache_all
mov r0, #0x06 @ S2D3C4 mode
mcr p14, 0, r0, c7, c0, 0 @ enter sleep
20: b 20b @ waiting for sleep
.data
.align 5
/*
* pxa3xx_cpu_resume
*/
ENTRY(pxa3xx_cpu_resume)
mov r0, #PSR_I_BIT | PSR_F_BIT | SVC_MODE @ set SVC, irqs off
msr cpsr_c, r0
ldr r0, sleep_save_sp @ stack phys addr
ldmfd r0, {r3 - r9, sp} @ CP regs + virt stack ptr
mov r1, #0
mcr p15, 0, r1, c7, c7, 0 @ invalidate I & D caches, BTB
mcr p15, 0, r1, c7, c10, 4 @ drain write (&fill) buffer
mcr p15, 0, r1, c7, c5, 4 @ flush prefetch buffer
mcr p15, 0, r1, c8, c7, 0 @ invalidate I & D TLBs
mcr p14, 0, r3, c6, c0, 0 @ clock configuration, turbo mode.
mcr p15, 0, r4, c15, c1, 0 @ CP access reg
mcr p15, 0, r5, c13, c0, 0 @ PID
mcr p15, 0, r6, c3, c0, 0 @ domain ID
mcr p15, 0, r7, c2, c0, 0 @ translation table base addr
mcr p15, 0, r8, c1, c0, 1 @ auxiliary control reg
@ temporarily map resume_turn_on_mmu into the page table,
@ otherwise prefetch abort occurs after MMU is turned on
mov r1, r7
bic r1, r1, #0x00ff
bic r1, r1, #0x3f00
ldr r2, =0x542e
adr r3, resume_turn_on_mmu
mov r3, r3, lsr #20
orr r4, r2, r3, lsl #20
ldr r5, [r1, r3, lsl #2]
str r4, [r1, r3, lsl #2]
@ Mapping page table address in the page table
mov r6, r1, lsr #20
orr r7, r2, r6, lsl #20
ldr r8, [r1, r6, lsl #2]
str r7, [r1, r6, lsl #2]
ldr r2, =pxa3xx_resume_after_mmu @ absolute virtual address
b resume_turn_on_mmu @ cache align execution
.text
pxa3xx_resume_after_mmu:
/* restore the temporary mapping */
str r5, [r1, r3, lsl #2]
str r8, [r1, r6, lsl #2]
b resume_after_mmu
#endif /* CONFIG_PXA3xx */
#ifdef CONFIG_PXA27x #ifdef CONFIG_PXA27x
/* /*
* pxa27x_cpu_suspend() * pxa27x_cpu_suspend()
......
This diff is collapsed.
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <asm/mach/irq.h> #include <asm/mach/irq.h>
#include <asm/arch/pxa-regs.h> #include <asm/arch/pxa-regs.h>
#include <asm/arch/pxa2xx-regs.h>
#include <asm/arch/irda.h> #include <asm/arch/irda.h>
#include <asm/arch/mmc.h> #include <asm/arch/mmc.h>
#include <asm/arch/ohci.h> #include <asm/arch/ohci.h>
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/arch/pxa-regs.h> #include <asm/arch/pxa-regs.h>
#include <asm/arch/pxa2xx-regs.h>
#include <asm/arch/irda.h> #include <asm/arch/irda.h>
#include <asm/arch/mmc.h> #include <asm/arch/mmc.h>
#include <asm/arch/udc.h> #include <asm/arch/udc.h>
......
This diff is collapsed.
...@@ -4,6 +4,5 @@ ...@@ -4,6 +4,5 @@
obj-y := core.o clock.o obj-y := core.o clock.o
obj-$(CONFIG_MACH_REALVIEW_EB) += realview_eb.o obj-$(CONFIG_MACH_REALVIEW_EB) += realview_eb.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o obj-$(CONFIG_SMP) += platsmp.o headsmp.o localtimer.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
#define __ASM_ARCH_HARDWARE_H #define __ASM_ARCH_HARDWARE_H
#include <asm/sizes.h> #include <asm/sizes.h>
#include <asm/arch/platform.h>
/* macro to get at IO space when running virtually */ /* macro to get at IO space when running virtually */
#define IO_ADDRESS(x) ((((x) & 0x0effffff) | (((x) >> 4) & 0x0f000000)) + 0xf0000000) #define IO_ADDRESS(x) ((((x) & 0x0effffff) | (((x) >> 4) & 0x0f000000)) + 0xf0000000)
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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