Initial commit
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Gateworks Corporation
|
||||
* Chris Lang <clang@gateworks.com>
|
||||
*
|
||||
* 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/linkage.h>
|
||||
#include <asm/assembler.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
|
||||
#define D_CACHE_LINE_SIZE 32
|
||||
|
||||
.text
|
||||
|
||||
/*
|
||||
* R8 - DMA Start Address
|
||||
* R9 - DMA Length
|
||||
* R10 - DMA Direction
|
||||
* R11 - DMA type
|
||||
* R12 - fiq_buffer Address
|
||||
*/
|
||||
|
||||
.global cns3xxx_fiq_end
|
||||
ENTRY(cns3xxx_fiq_start)
|
||||
str r8, [r13]
|
||||
|
||||
ldmia r12, {r8, r9, r10}
|
||||
and r11, r10, #0x3000000
|
||||
and r10, r10, #0xff
|
||||
|
||||
teq r11, #0x1000000
|
||||
beq cns3xxx_dma_map_area
|
||||
teq r11, #0x2000000
|
||||
beq cns3xxx_dma_unmap_area
|
||||
/* fall through */
|
||||
cns3xxx_dma_flush_range:
|
||||
bic r8, r8, #D_CACHE_LINE_SIZE - 1
|
||||
1:
|
||||
mcr p15, 0, r8, c7, c14, 1 @ clean & invalidate D line
|
||||
add r8, r8, #D_CACHE_LINE_SIZE
|
||||
cmp r8, r9
|
||||
blo 1b
|
||||
/* fall through */
|
||||
cns3xxx_fiq_exit:
|
||||
mov r8, #0
|
||||
str r8, [r12, #8]
|
||||
mcr p15, 0, r8, c7, c10, 4 @ drain write buffer
|
||||
subs pc, lr, #4
|
||||
|
||||
cns3xxx_dma_map_area:
|
||||
add r9, r9, r8
|
||||
teq r10, #DMA_FROM_DEVICE
|
||||
beq cns3xxx_dma_inv_range
|
||||
teq r10, #DMA_TO_DEVICE
|
||||
bne cns3xxx_dma_flush_range
|
||||
/* fall through */
|
||||
cns3xxx_dma_clean_range:
|
||||
bic r8, r8, #D_CACHE_LINE_SIZE - 1
|
||||
1:
|
||||
mcr p15, 0, r8, c7, c10, 1 @ clean D line
|
||||
add r8, r8, #D_CACHE_LINE_SIZE
|
||||
cmp r8, r9
|
||||
blo 1b
|
||||
b cns3xxx_fiq_exit
|
||||
|
||||
cns3xxx_dma_unmap_area:
|
||||
add r9, r9, r8
|
||||
teq r10, #DMA_TO_DEVICE
|
||||
beq cns3xxx_fiq_exit
|
||||
/* fall through */
|
||||
cns3xxx_dma_inv_range:
|
||||
tst r8, #D_CACHE_LINE_SIZE - 1
|
||||
bic r8, r8, #D_CACHE_LINE_SIZE - 1
|
||||
mcrne p15, 0, r8, c7, c10, 1 @ clean D line
|
||||
tst r9, #D_CACHE_LINE_SIZE - 1
|
||||
bic r9, r9, #D_CACHE_LINE_SIZE - 1
|
||||
mcrne p15, 0, r9, c7, c14, 1 @ clean & invalidate D line
|
||||
1:
|
||||
mcr p15, 0, r8, c7, c6, 1 @ invalidate D line
|
||||
add r8, r8, #D_CACHE_LINE_SIZE
|
||||
cmp r8, r9
|
||||
blo 1b
|
||||
b cns3xxx_fiq_exit
|
||||
|
||||
cns3xxx_fiq_end:
|
||||
292
target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/gpio.c
Normal file
292
target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/gpio.c
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Copyright 2012 Gateworks Corporation
|
||||
* Chris Lang <clang@gateworks.com>
|
||||
* Tim Harvey <tharvey@gateworks.com>
|
||||
*
|
||||
* This file 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/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
|
||||
#include <asm/mach/irq.h>
|
||||
|
||||
/*
|
||||
* Registers
|
||||
*/
|
||||
#define GPIO_INPUT 0x04
|
||||
#define GPIO_DIR 0x08
|
||||
#define GPIO_SET 0x10
|
||||
#define GPIO_CLEAR 0x14
|
||||
#define GPIO_INTERRUPT_ENABLE 0x20
|
||||
#define GPIO_INTERRUPT_RAW_STATUS 0x24
|
||||
#define GPIO_INTERRUPT_MASKED_STATUS 0x28
|
||||
#define GPIO_INTERRUPT_MASK 0x2C
|
||||
#define GPIO_INTERRUPT_CLEAR 0x30
|
||||
#define GPIO_INTERRUPT_TRIGGER_METHOD 0x34
|
||||
#define GPIO_INTERRUPT_TRIGGER_BOTH_EDGES 0x38
|
||||
#define GPIO_INTERRUPT_TRIGGER_TYPE 0x3C
|
||||
|
||||
#define GPIO_INTERRUPT_TRIGGER_METHOD_EDGE 0
|
||||
#define GPIO_INTERRUPT_TRIGGER_METHOD_LEVEL 1
|
||||
#define GPIO_INTERRUPT_TRIGGER_EDGE_SINGLE 0
|
||||
#define GPIO_INTERRUPT_TRIGGER_EDGE_BOTH 1
|
||||
#define GPIO_INTERRUPT_TRIGGER_TYPE_RISING 0
|
||||
#define GPIO_INTERRUPT_TRIGGER_TYPE_FALLING 1
|
||||
#define GPIO_INTERRUPT_TRIGGER_TYPE_HIGH 0
|
||||
#define GPIO_INTERRUPT_TRIGGER_TYPE_LOW 1
|
||||
|
||||
struct cns3xxx_gpio_chip {
|
||||
struct gpio_chip chip;
|
||||
struct irq_domain *domain;
|
||||
spinlock_t lock;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
static struct cns3xxx_gpio_chip cns3xxx_gpio_chips[2];
|
||||
static int cns3xxx_gpio_chip_count;
|
||||
|
||||
static inline void
|
||||
__set_direction(struct cns3xxx_gpio_chip *cchip, unsigned pin, int input)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = __raw_readl(cchip->base + GPIO_DIR);
|
||||
if (input)
|
||||
reg &= ~(1 << pin);
|
||||
else
|
||||
reg |= (1 << pin);
|
||||
__raw_writel(reg, cchip->base + GPIO_DIR);
|
||||
}
|
||||
|
||||
/*
|
||||
* GENERIC_GPIO primatives
|
||||
*/
|
||||
static int cns3xxx_gpio_direction_input(struct gpio_chip *chip, unsigned pin)
|
||||
{
|
||||
struct cns3xxx_gpio_chip *cchip =
|
||||
container_of(chip, struct cns3xxx_gpio_chip, chip);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&cchip->lock, flags);
|
||||
__set_direction(cchip, pin, 1);
|
||||
spin_unlock_irqrestore(&cchip->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cns3xxx_gpio_get(struct gpio_chip *chip, unsigned pin)
|
||||
{
|
||||
struct cns3xxx_gpio_chip *cchip =
|
||||
container_of(chip, struct cns3xxx_gpio_chip, chip);
|
||||
int val;
|
||||
|
||||
val = ((__raw_readl(cchip->base + GPIO_INPUT) >> pin) & 0x1);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int cns3xxx_gpio_direction_output(struct gpio_chip *chip, unsigned pin, int level)
|
||||
{
|
||||
struct cns3xxx_gpio_chip *cchip =
|
||||
container_of(chip, struct cns3xxx_gpio_chip, chip);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&cchip->lock, flags);
|
||||
if (level)
|
||||
__raw_writel(1 << pin, cchip->base + GPIO_SET);
|
||||
else
|
||||
__raw_writel(1 << pin, cchip->base + GPIO_CLEAR);
|
||||
__set_direction(cchip, pin, 0);
|
||||
spin_unlock_irqrestore(&cchip->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cns3xxx_gpio_set(struct gpio_chip *chip, unsigned pin,
|
||||
int level)
|
||||
{
|
||||
struct cns3xxx_gpio_chip *cchip =
|
||||
container_of(chip, struct cns3xxx_gpio_chip, chip);
|
||||
|
||||
if (level)
|
||||
__raw_writel(1 << pin, cchip->base + GPIO_SET);
|
||||
else
|
||||
__raw_writel(1 << pin, cchip->base + GPIO_CLEAR);
|
||||
}
|
||||
|
||||
static int cns3xxx_gpio_to_irq(struct gpio_chip *chip, unsigned pin)
|
||||
{
|
||||
struct cns3xxx_gpio_chip *cchip =
|
||||
container_of(chip, struct cns3xxx_gpio_chip, chip);
|
||||
|
||||
return irq_find_mapping(cchip->domain, pin);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* IRQ support
|
||||
*/
|
||||
|
||||
/* one interrupt per GPIO controller (GPIOA/GPIOB)
|
||||
* this is called in task context, with IRQs enabled
|
||||
*/
|
||||
static void cns3xxx_gpio_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct cns3xxx_gpio_chip *cchip = irq_desc_get_handler_data(desc);
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
u16 i;
|
||||
u32 reg;
|
||||
|
||||
chained_irq_enter(chip, desc); /* mask and ack the base interrupt */
|
||||
|
||||
/* see which pin(s) triggered the interrupt */
|
||||
reg = __raw_readl(cchip->base + GPIO_INTERRUPT_RAW_STATUS);
|
||||
for (i = 0; i < 32; i++) {
|
||||
if (reg & (1 << i)) {
|
||||
/* let the generic IRQ layer handle an interrupt */
|
||||
generic_handle_irq(irq_find_mapping(cchip->domain, i));
|
||||
}
|
||||
}
|
||||
|
||||
chained_irq_exit(chip, desc); /* unmask the base interrupt */
|
||||
}
|
||||
|
||||
static int cns3xxx_gpio_irq_set_type(struct irq_data *d, u32 irqtype)
|
||||
{
|
||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||
struct cns3xxx_gpio_chip *cchip = gc->private;
|
||||
u32 gpio = d->hwirq;
|
||||
unsigned long flags;
|
||||
u32 method, edges, type;
|
||||
|
||||
spin_lock_irqsave(&cchip->lock, flags);
|
||||
method = __raw_readl(cchip->base + GPIO_INTERRUPT_TRIGGER_METHOD);
|
||||
edges = __raw_readl(cchip->base + GPIO_INTERRUPT_TRIGGER_BOTH_EDGES);
|
||||
type = __raw_readl(cchip->base + GPIO_INTERRUPT_TRIGGER_TYPE);
|
||||
method &= ~(1 << gpio);
|
||||
edges &= ~(1 << gpio);
|
||||
type &= ~(1 << gpio);
|
||||
|
||||
switch(irqtype) {
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
method |= (GPIO_INTERRUPT_TRIGGER_METHOD_EDGE << gpio);
|
||||
edges |= (GPIO_INTERRUPT_TRIGGER_EDGE_SINGLE << gpio);
|
||||
type |= (GPIO_INTERRUPT_TRIGGER_TYPE_RISING << gpio);
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
method |= (GPIO_INTERRUPT_TRIGGER_METHOD_EDGE << gpio);
|
||||
edges |= (GPIO_INTERRUPT_TRIGGER_EDGE_SINGLE << gpio);
|
||||
type |= (GPIO_INTERRUPT_TRIGGER_TYPE_FALLING << gpio);
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_BOTH:
|
||||
method |= (GPIO_INTERRUPT_TRIGGER_METHOD_EDGE << gpio);
|
||||
edges |= (GPIO_INTERRUPT_TRIGGER_EDGE_BOTH << gpio);
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
method |= (GPIO_INTERRUPT_TRIGGER_METHOD_LEVEL << gpio);
|
||||
type |= (GPIO_INTERRUPT_TRIGGER_TYPE_LOW << gpio);
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
method |= (GPIO_INTERRUPT_TRIGGER_METHOD_LEVEL << gpio);
|
||||
type |= (GPIO_INTERRUPT_TRIGGER_TYPE_HIGH << gpio);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING "No irq type\n");
|
||||
spin_unlock_irqrestore(&cchip->lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
__raw_writel(method, cchip->base + GPIO_INTERRUPT_TRIGGER_METHOD);
|
||||
__raw_writel(edges, cchip->base + GPIO_INTERRUPT_TRIGGER_BOTH_EDGES);
|
||||
__raw_writel(type, cchip->base + GPIO_INTERRUPT_TRIGGER_TYPE);
|
||||
spin_unlock_irqrestore(&cchip->lock, flags);
|
||||
|
||||
if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
|
||||
irq_set_handler_locked(d, handle_level_irq);
|
||||
else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
|
||||
irq_set_handler_locked(d, handle_edge_irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init cns3xxx_gpio_init(int gpio_base, int ngpio,
|
||||
u32 base, int irq, int secondary_irq_base)
|
||||
{
|
||||
struct cns3xxx_gpio_chip *cchip;
|
||||
struct irq_chip_generic *gc;
|
||||
struct irq_chip_type *ct;
|
||||
char gc_label[16];
|
||||
int irq_base;
|
||||
|
||||
if (cns3xxx_gpio_chip_count == ARRAY_SIZE(cns3xxx_gpio_chips))
|
||||
return;
|
||||
|
||||
snprintf(gc_label, sizeof(gc_label), "cns3xxx_gpio%d",
|
||||
cns3xxx_gpio_chip_count);
|
||||
|
||||
cchip = cns3xxx_gpio_chips + cns3xxx_gpio_chip_count;
|
||||
cchip->chip.label = kstrdup(gc_label, GFP_KERNEL);
|
||||
cchip->chip.direction_input = cns3xxx_gpio_direction_input;
|
||||
cchip->chip.get = cns3xxx_gpio_get;
|
||||
cchip->chip.direction_output = cns3xxx_gpio_direction_output;
|
||||
cchip->chip.set = cns3xxx_gpio_set;
|
||||
cchip->chip.to_irq = cns3xxx_gpio_to_irq;
|
||||
cchip->chip.base = gpio_base;
|
||||
cchip->chip.ngpio = ngpio;
|
||||
cchip->chip.can_sleep = 0;
|
||||
spin_lock_init(&cchip->lock);
|
||||
cchip->base = (void __iomem *)base;
|
||||
|
||||
BUG_ON(gpiochip_add(&cchip->chip) < 0);
|
||||
cns3xxx_gpio_chip_count++;
|
||||
|
||||
/* clear GPIO interrupts */
|
||||
__raw_writel(0xffff, cchip->base + GPIO_INTERRUPT_CLEAR);
|
||||
|
||||
irq_base = irq_alloc_descs(-1, secondary_irq_base, ngpio,
|
||||
numa_node_id());
|
||||
if (irq_base < 0)
|
||||
goto out_irqdesc_free;
|
||||
|
||||
cchip->domain = irq_domain_add_legacy(NULL, ngpio, irq_base, 0,
|
||||
&irq_domain_simple_ops, NULL);
|
||||
if (!cchip->domain)
|
||||
goto out_irqdesc_free;
|
||||
|
||||
/*
|
||||
* IRQ chip init
|
||||
*/
|
||||
gc = irq_alloc_generic_chip("cns3xxx_gpio_irq", 1, irq_base,
|
||||
cchip->base, handle_edge_irq);
|
||||
|
||||
gc->private = cchip;
|
||||
|
||||
ct = gc->chip_types;
|
||||
ct->type = IRQ_TYPE_EDGE_FALLING;
|
||||
ct->regs.ack = GPIO_INTERRUPT_CLEAR;
|
||||
ct->chip.irq_ack = irq_gc_ack_set_bit;
|
||||
ct->regs.mask = GPIO_INTERRUPT_ENABLE;
|
||||
ct->chip.irq_enable = irq_gc_mask_set_bit;
|
||||
ct->chip.irq_disable = irq_gc_mask_clr_bit;
|
||||
ct->chip.irq_set_type = cns3xxx_gpio_irq_set_type;
|
||||
ct->handler = handle_edge_irq;
|
||||
|
||||
irq_setup_generic_chip(gc, IRQ_MSK(ngpio), IRQ_GC_INIT_MASK_CACHE,
|
||||
IRQ_NOREQUEST, 0);
|
||||
irq_set_chained_handler(irq, cns3xxx_gpio_irq_handler);
|
||||
irq_set_handler_data(irq, cchip);
|
||||
|
||||
return;
|
||||
|
||||
out_irqdesc_free:
|
||||
irq_free_descs(irq_base, ngpio);
|
||||
}
|
||||
41
target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/headsmp.S
Normal file
41
target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/headsmp.S
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* linux/arch/arm/mach-cns3xxx/headsmp.S
|
||||
*
|
||||
* Cloned from linux/arch/arm/plat-versatile/headsmp.S
|
||||
*
|
||||
* Copyright (c) 2003 ARM Limited
|
||||
* All Rights Reserved
|
||||
*
|
||||
* 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/linkage.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
/*
|
||||
* CNS3XXX specific entry point for secondary CPUs. This provides
|
||||
* a "holding pen" into which all secondary cores are held until we're
|
||||
* ready for them to initialise.
|
||||
*/
|
||||
ENTRY(cns3xxx_secondary_startup)
|
||||
mrc p15, 0, r0, c0, c0, 5
|
||||
and r0, r0, #15
|
||||
adr r4, 1f
|
||||
ldmia r4, {r5, r6}
|
||||
sub r4, r4, r5
|
||||
add r6, r6, r4
|
||||
pen: ldr r7, [r6]
|
||||
cmp r7, r0
|
||||
bne pen
|
||||
|
||||
/*
|
||||
* we've been released from the holding pen: secondary_stack
|
||||
* should now contain the SVC stack for this core
|
||||
*/
|
||||
b secondary_startup
|
||||
ENDPROC(cns3xxx_secondary_startup)
|
||||
|
||||
.align 2
|
||||
1: .long .
|
||||
.long pen_release
|
||||
130
target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/hotplug.c
Normal file
130
target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/hotplug.c
Normal file
@@ -0,0 +1,130 @@
|
||||
/* linux arch/arm/mach-cns3xxx/hotplug.c
|
||||
*
|
||||
* Cloned from linux/arch/arm/mach-realview/hotplug.c
|
||||
*
|
||||
* Copyright (C) 2002 ARM Ltd.
|
||||
* All Rights Reserved
|
||||
*
|
||||
* 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/errno.h>
|
||||
#include <linux/smp.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
extern volatile int pen_release;
|
||||
|
||||
static inline void cpu_enter_lowpower(void)
|
||||
{
|
||||
unsigned int v;
|
||||
|
||||
flush_cache_all();
|
||||
asm volatile(
|
||||
" mcr p15, 0, %1, c7, c5, 0\n"
|
||||
" mcr p15, 0, %1, c7, c10, 4\n"
|
||||
/*
|
||||
* Turn off coherency
|
||||
*/
|
||||
" mrc p15, 0, %0, c1, c0, 1\n"
|
||||
" bic %0, %0, %3\n"
|
||||
" mcr p15, 0, %0, c1, c0, 1\n"
|
||||
" mrc p15, 0, %0, c1, c0, 0\n"
|
||||
" bic %0, %0, %2\n"
|
||||
" mcr p15, 0, %0, c1, c0, 0\n"
|
||||
: "=&r" (v)
|
||||
: "r" (0), "Ir" (CR_C), "Ir" (0x40)
|
||||
: "cc");
|
||||
}
|
||||
|
||||
static inline void cpu_leave_lowpower(void)
|
||||
{
|
||||
unsigned int v;
|
||||
|
||||
asm volatile(
|
||||
"mrc p15, 0, %0, c1, c0, 0\n"
|
||||
" orr %0, %0, %1\n"
|
||||
" mcr p15, 0, %0, c1, c0, 0\n"
|
||||
" mrc p15, 0, %0, c1, c0, 1\n"
|
||||
" orr %0, %0, %2\n"
|
||||
" mcr p15, 0, %0, c1, c0, 1\n"
|
||||
: "=&r" (v)
|
||||
: "Ir" (CR_C), "Ir" (0x40)
|
||||
: "cc");
|
||||
}
|
||||
|
||||
static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
|
||||
{
|
||||
/*
|
||||
* there is no power-control hardware on this platform, so all
|
||||
* we can do is put the core into WFI; this is safe as the calling
|
||||
* code will have already disabled interrupts
|
||||
*/
|
||||
for (;;) {
|
||||
/*
|
||||
* here's the WFI
|
||||
*/
|
||||
asm(".word 0xe320f003\n"
|
||||
:
|
||||
:
|
||||
: "memory", "cc");
|
||||
|
||||
if (pen_release == cpu) {
|
||||
/*
|
||||
* OK, proper wakeup, we're done
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Getting here, means that we have come out of WFI without
|
||||
* having been woken up - this shouldn't happen
|
||||
*
|
||||
* Just note it happening - when we're woken, we can report
|
||||
* its occurrence.
|
||||
*/
|
||||
(*spurious)++;
|
||||
}
|
||||
}
|
||||
|
||||
int platform_cpu_kill(unsigned int cpu)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* platform-specific code to shutdown a CPU
|
||||
*
|
||||
* Called with IRQs disabled
|
||||
*/
|
||||
void platform_cpu_die(unsigned int cpu)
|
||||
{
|
||||
int spurious = 0;
|
||||
|
||||
/*
|
||||
* we're ready for shutdown now, so do it
|
||||
*/
|
||||
cpu_enter_lowpower();
|
||||
platform_do_lowpower(cpu, &spurious);
|
||||
|
||||
/*
|
||||
* bring this CPU back into the world of cache
|
||||
* coherency, and then restore interrupts
|
||||
*/
|
||||
cpu_leave_lowpower();
|
||||
|
||||
if (spurious)
|
||||
pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious);
|
||||
}
|
||||
|
||||
int platform_cpu_disable(unsigned int cpu)
|
||||
{
|
||||
/*
|
||||
* we don't allow CPU 0 to be shutdown (it is still too special
|
||||
* e.g. clock tick interrupts)
|
||||
*/
|
||||
return cpu == 0 ? -EPERM : 0;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* arch/arm/mach-cns3xxx/include/mach/gpio.h
|
||||
*
|
||||
* 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 __ASM_ARCH_CNS3XXX_GPIO_H
|
||||
#define __ASM_ARCH_CNS3XXX_GPIO_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
extern void __init cns3xxx_gpio_init(int gpio_base, int ngpio,
|
||||
u32 base, int irq, int secondary_irq_base);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef __MACH_SMP_H
|
||||
#define __MACH_SMP_H
|
||||
|
||||
extern void smp_dma_map_area(const void *, size_t, int);
|
||||
extern void smp_dma_unmap_area(const void *, size_t, int);
|
||||
extern void smp_dma_flush_range(const void *, const void *);
|
||||
|
||||
#endif
|
||||
1121
target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/laguna.c
Normal file
1121
target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/laguna.c
Normal file
File diff suppressed because it is too large
Load Diff
327
target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/platsmp.c
Normal file
327
target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/platsmp.c
Normal file
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
* linux/arch/arm/mach-cns3xxx/platsmp.c
|
||||
*
|
||||
* Copyright (C) 2002 ARM Ltd.
|
||||
* Copyright 2012 Gateworks Corporation
|
||||
* Chris Lang <clang@gateworks.com>
|
||||
* Tim Harvey <tharvey@gateworks.com>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/smp_scu.h>
|
||||
#include <asm/unified.h>
|
||||
#include <asm/fiq.h>
|
||||
#include <mach/smp.h>
|
||||
#include "cns3xxx.h"
|
||||
|
||||
static struct fiq_handler fh = {
|
||||
.name = "cns3xxx-fiq"
|
||||
};
|
||||
|
||||
struct fiq_req {
|
||||
union {
|
||||
struct {
|
||||
const void *addr;
|
||||
size_t size;
|
||||
} map;
|
||||
struct {
|
||||
const void *addr;
|
||||
size_t size;
|
||||
} unmap;
|
||||
struct {
|
||||
const void *start;
|
||||
const void *end;
|
||||
} flush;
|
||||
};
|
||||
volatile uint flags;
|
||||
void __iomem *reg;
|
||||
} ____cacheline_aligned;
|
||||
|
||||
extern unsigned int fiq_number[2];
|
||||
|
||||
DEFINE_PER_CPU(struct fiq_req, fiq_data);
|
||||
|
||||
#define FIQ_ENABLED 0x80000000
|
||||
#define FIQ_GENERATE 0x00010000
|
||||
#define CNS3XXX_MAP_AREA 0x01000000
|
||||
#define CNS3XXX_UNMAP_AREA 0x02000000
|
||||
#define CNS3XXX_FLUSH_RANGE 0x03000000
|
||||
|
||||
extern void cns3xxx_secondary_startup(void);
|
||||
extern unsigned char cns3xxx_fiq_start, cns3xxx_fiq_end;
|
||||
|
||||
#define SCU_CPU_STATUS 0x08
|
||||
static void __iomem *scu_base;
|
||||
|
||||
static inline void cns3xxx_set_fiq_regs(unsigned int cpu)
|
||||
{
|
||||
struct pt_regs FIQ_regs;
|
||||
struct fiq_req *fiq_req = &per_cpu(fiq_data, !cpu);
|
||||
|
||||
FIQ_regs.ARM_r8 = 0;
|
||||
FIQ_regs.ARM_ip = (unsigned int)fiq_req;
|
||||
FIQ_regs.ARM_sp = (int) MISC_FIQ_CPU(!cpu);
|
||||
fiq_req->reg = MISC_FIQ_CPU(!cpu);
|
||||
|
||||
set_fiq_regs(&FIQ_regs);
|
||||
}
|
||||
|
||||
static void __init cns3xxx_init_fiq(void)
|
||||
{
|
||||
void *fiqhandler_start;
|
||||
unsigned int fiqhandler_length;
|
||||
int ret;
|
||||
|
||||
fiqhandler_start = &cns3xxx_fiq_start;
|
||||
fiqhandler_length = &cns3xxx_fiq_end - &cns3xxx_fiq_start;
|
||||
|
||||
ret = claim_fiq(&fh);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
set_fiq_handler(fiqhandler_start, fiqhandler_length);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write pen_release in a way that is guaranteed to be visible to all
|
||||
* observers, irrespective of whether they're taking part in coherency
|
||||
* or not. This is necessary for the hotplug code to work reliably.
|
||||
*/
|
||||
static void write_pen_release(int val)
|
||||
{
|
||||
pen_release = val;
|
||||
smp_wmb();
|
||||
__cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
|
||||
outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(boot_lock);
|
||||
|
||||
static void cns3xxx_secondary_init(unsigned int cpu)
|
||||
{
|
||||
/*
|
||||
* Setup Secondary Core FIQ regs
|
||||
*/
|
||||
cns3xxx_set_fiq_regs(1);
|
||||
|
||||
/*
|
||||
* let the primary processor know we're out of the
|
||||
* pen, then head off into the C entry point
|
||||
*/
|
||||
write_pen_release(-1);
|
||||
|
||||
/*
|
||||
* Synchronise with the boot thread.
|
||||
*/
|
||||
spin_lock(&boot_lock);
|
||||
spin_unlock(&boot_lock);
|
||||
}
|
||||
|
||||
static int cns3xxx_boot_secondary(unsigned int cpu, struct task_struct *idle)
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
||||
/*
|
||||
* Set synchronisation state between this boot processor
|
||||
* and the secondary one
|
||||
*/
|
||||
spin_lock(&boot_lock);
|
||||
|
||||
/*
|
||||
* The secondary processor is waiting to be released from
|
||||
* the holding pen - release it, then wait for it to flag
|
||||
* that it has been released by resetting pen_release.
|
||||
*
|
||||
* Note that "pen_release" is the hardware CPU ID, whereas
|
||||
* "cpu" is Linux's internal ID.
|
||||
*/
|
||||
write_pen_release(cpu);
|
||||
|
||||
/*
|
||||
* Send the secondary CPU a soft interrupt, thereby causing
|
||||
* the boot monitor to read the system wide flags register,
|
||||
* and branch to the address found there.
|
||||
*/
|
||||
arch_send_wakeup_ipi_mask(cpumask_of(cpu));;
|
||||
|
||||
timeout = jiffies + (1 * HZ);
|
||||
while (time_before(jiffies, timeout)) {
|
||||
smp_rmb();
|
||||
if (pen_release == -1)
|
||||
break;
|
||||
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
/*
|
||||
* now the secondary core is starting up let it run its
|
||||
* calibrations, then wait for it to finish
|
||||
*/
|
||||
spin_unlock(&boot_lock);
|
||||
|
||||
return pen_release != -1 ? -ENOSYS : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialise the CPU possible map early - this describes the CPUs
|
||||
* which may be present or become present in the system.
|
||||
*/
|
||||
static void __init cns3xxx_smp_init_cpus(void)
|
||||
{
|
||||
unsigned int i, ncores;
|
||||
unsigned int status;
|
||||
|
||||
scu_base = (void __iomem *) CNS3XXX_TC11MP_SCU_BASE_VIRT;
|
||||
|
||||
/* for CNS3xxx SCU_CPU_STATUS must be examined instead of SCU_CONFIGURATION
|
||||
* used in scu_get_core_count
|
||||
*/
|
||||
status = __raw_readl(scu_base + SCU_CPU_STATUS);
|
||||
for (i = 0; i < NR_CPUS+1; i++) {
|
||||
if (((status >> (i*2)) & 0x3) == 0)
|
||||
set_cpu_possible(i, true);
|
||||
else
|
||||
break;
|
||||
}
|
||||
ncores = i;
|
||||
}
|
||||
|
||||
static void __init cns3xxx_smp_prepare_cpus(unsigned int max_cpus)
|
||||
{
|
||||
/*
|
||||
* enable SCU
|
||||
*/
|
||||
scu_enable(scu_base);
|
||||
|
||||
/*
|
||||
* Write the address of secondary startup into the
|
||||
* system-wide flags register. The boot monitor waits
|
||||
* until it receives a soft interrupt, and then the
|
||||
* secondary CPU branches to this address.
|
||||
*/
|
||||
__raw_writel(virt_to_phys(cns3xxx_secondary_startup),
|
||||
(void __iomem *)(CNS3XXX_MISC_BASE_VIRT + 0x0600));
|
||||
|
||||
/*
|
||||
* Setup FIQ's for main cpu
|
||||
*/
|
||||
cns3xxx_init_fiq();
|
||||
cns3xxx_set_fiq_regs(0);
|
||||
}
|
||||
|
||||
extern void v6_dma_map_area(const void *, size_t, int);
|
||||
extern void v6_dma_unmap_area(const void *, size_t, int);
|
||||
extern void v6_dma_flush_range(const void *, const void *);
|
||||
extern void v6_flush_kern_dcache_area(void *, size_t);
|
||||
|
||||
void fiq_dma_map_area(const void *addr, size_t size, int dir)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct fiq_req *req;
|
||||
|
||||
raw_local_irq_save(flags);
|
||||
/* currently, not possible to take cpu0 down, so only check cpu1 */
|
||||
if (!cpu_online(1)) {
|
||||
raw_local_irq_restore(flags);
|
||||
v6_dma_map_area(addr, size, dir);
|
||||
return;
|
||||
}
|
||||
|
||||
req = this_cpu_ptr(&fiq_data);
|
||||
req->map.addr = addr;
|
||||
req->map.size = size;
|
||||
req->flags = dir | CNS3XXX_MAP_AREA;
|
||||
smp_mb();
|
||||
|
||||
writel_relaxed(FIQ_GENERATE, req->reg);
|
||||
|
||||
v6_dma_map_area(addr, size, dir);
|
||||
while (req->flags)
|
||||
barrier();
|
||||
|
||||
raw_local_irq_restore(flags);
|
||||
}
|
||||
|
||||
void fiq_dma_unmap_area(const void *addr, size_t size, int dir)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct fiq_req *req;
|
||||
|
||||
raw_local_irq_save(flags);
|
||||
/* currently, not possible to take cpu0 down, so only check cpu1 */
|
||||
if (!cpu_online(1)) {
|
||||
raw_local_irq_restore(flags);
|
||||
v6_dma_unmap_area(addr, size, dir);
|
||||
return;
|
||||
}
|
||||
|
||||
req = this_cpu_ptr(&fiq_data);
|
||||
req->unmap.addr = addr;
|
||||
req->unmap.size = size;
|
||||
req->flags = dir | CNS3XXX_UNMAP_AREA;
|
||||
smp_mb();
|
||||
|
||||
writel_relaxed(FIQ_GENERATE, req->reg);
|
||||
|
||||
v6_dma_unmap_area(addr, size, dir);
|
||||
while (req->flags)
|
||||
barrier();
|
||||
|
||||
raw_local_irq_restore(flags);
|
||||
}
|
||||
|
||||
void fiq_dma_flush_range(const void *start, const void *end)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct fiq_req *req;
|
||||
|
||||
raw_local_irq_save(flags);
|
||||
/* currently, not possible to take cpu0 down, so only check cpu1 */
|
||||
if (!cpu_online(1)) {
|
||||
raw_local_irq_restore(flags);
|
||||
v6_dma_flush_range(start, end);
|
||||
return;
|
||||
}
|
||||
|
||||
req = this_cpu_ptr(&fiq_data);
|
||||
|
||||
req->flush.start = start;
|
||||
req->flush.end = end;
|
||||
req->flags = CNS3XXX_FLUSH_RANGE;
|
||||
smp_mb();
|
||||
|
||||
writel_relaxed(FIQ_GENERATE, req->reg);
|
||||
|
||||
v6_dma_flush_range(start, end);
|
||||
|
||||
while (req->flags)
|
||||
barrier();
|
||||
|
||||
raw_local_irq_restore(flags);
|
||||
}
|
||||
|
||||
void fiq_flush_kern_dcache_area(void *addr, size_t size)
|
||||
{
|
||||
fiq_dma_flush_range(addr, addr + size);
|
||||
}
|
||||
|
||||
struct smp_operations cns3xxx_smp_ops __initdata = {
|
||||
.smp_init_cpus = cns3xxx_smp_init_cpus,
|
||||
.smp_prepare_cpus = cns3xxx_smp_prepare_cpus,
|
||||
.smp_secondary_init = cns3xxx_secondary_init,
|
||||
.smp_boot_secondary = cns3xxx_boot_secondary,
|
||||
};
|
||||
Reference in New Issue
Block a user