Initial commit

This commit is contained in:
domenico
2025-06-24 15:51:28 +02:00
commit 22031d9dab
6862 changed files with 1462554 additions and 0 deletions

View File

@@ -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:

View 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);
}

View 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

View 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;
}

View File

@@ -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

View File

@@ -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

File diff suppressed because it is too large Load Diff

View 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,
};