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

View File

@@ -0,0 +1,374 @@
/*
* Cavium CNS3xxx I2C Host Controller
*
* Copyright 2010 Cavium Network
* 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/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <linux/wait.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/clk.h>
/*
* We need the memory map
*/
#define I2C_MEM_MAP_ADDR(x) (i2c->base + x)
#define I2C_MEM_MAP_VALUE(x) (*((unsigned int volatile*)I2C_MEM_MAP_ADDR(x)))
#define I2C_CONTROLLER_REG I2C_MEM_MAP_VALUE(0x00)
#define I2C_TIME_OUT_REG I2C_MEM_MAP_VALUE(0x04)
#define I2C_SLAVE_ADDRESS_REG I2C_MEM_MAP_VALUE(0x08)
#define I2C_WRITE_DATA_REG I2C_MEM_MAP_VALUE(0x0C)
#define I2C_READ_DATA_REG I2C_MEM_MAP_VALUE(0x10)
#define I2C_INTERRUPT_STATUS_REG I2C_MEM_MAP_VALUE(0x14)
#define I2C_INTERRUPT_ENABLE_REG I2C_MEM_MAP_VALUE(0x18)
#define I2C_TWI_OUT_DLY_REG I2C_MEM_MAP_VALUE(0x1C)
#define I2C_BUS_ERROR_FLAG (0x1)
#define I2C_ACTION_DONE_FLAG (0x2)
#define CNS3xxx_I2C_ENABLE() (I2C_CONTROLLER_REG) |= ((unsigned int)0x1 << 31)
#define CNS3xxx_I2C_DISABLE() (I2C_CONTROLLER_REG) &= ~((unsigned int)0x1 << 31)
#define CNS3xxx_I2C_ENABLE_INTR() (I2C_INTERRUPT_ENABLE_REG) |= 0x03
#define CNS3xxx_I2C_DISABLE_INTR() (I2C_INTERRUPT_ENABLE_REG) &= 0xfc
#define TWI_TIMEOUT (10*HZ)
#define I2C_100KHZ 100000
#define I2C_200KHZ 200000
#define I2C_300KHZ 300000
#define I2C_400KHZ 400000
#define CNS3xxx_I2C_CLK I2C_100KHZ
#define STATE_DONE 1
#define STATE_ERROR 2
struct cns3xxx_i2c {
struct device *dev;
void __iomem *base; /* virtual */
wait_queue_head_t wait;
struct i2c_adapter adap;
struct i2c_msg *msg;
u8 state; /* see STATE_ */
u8 error; /* see TWI_STATUS register */
int rd_wr_len;
u8 *buf;
};
static u32 cns3xxx_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static int
cns3xxx_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg)
{
struct cns3xxx_i2c *i2c = i2c_get_adapdata(adap);
int i, j;
u8 buf[1] = { 0 };
if (msg->len == 0) {
/*
* We are probably doing a probe for a device here,
* so set the length to one, and data to 0
*/
msg->len = 1;
i2c->buf = buf;
} else {
i2c->buf = msg->buf;
}
if (msg->flags & I2C_M_TEN) {
printk
("%s:%d: Presently the driver does not handle extended addressing\n",
__FUNCTION__, __LINE__);
return -EINVAL;
}
i2c->msg = msg;
for (i = 0; i < msg->len; i++) {
if (msg->len - i >= 4)
i2c->rd_wr_len = 3;
else
i2c->rd_wr_len = msg->len - i - 1;
// Set Data Width and TWI_EN
I2C_CONTROLLER_REG = 0x80000000 | (i2c->rd_wr_len << 2) | (i2c->rd_wr_len);
// Clear Write Reg
I2C_WRITE_DATA_REG = 0;
// Set the slave address
I2C_SLAVE_ADDRESS_REG = (msg->addr << 1);
// Are we Writing
if (!(msg->flags & I2C_M_RD)) {
I2C_CONTROLLER_REG |= (1 << 4);
if (i != 0) {
/*
* We need to set the address in the first byte.
* The base address is going to be in buf[0] and then
* it needs to be incremented by i - 1.
*/
i2c->buf--;
*i2c->buf = buf[0] + i - 1;
if (i2c->rd_wr_len < 3) {
i += i2c->rd_wr_len;
i2c->rd_wr_len++;
I2C_CONTROLLER_REG = 0x80000000 | (1 << 4) | (i2c->rd_wr_len << 2) | (i2c->rd_wr_len);
} else {
i += i2c->rd_wr_len - 1;
}
} else {
i += i2c->rd_wr_len;
buf[0] = *i2c->buf;
}
for (j = 0; j <= i2c->rd_wr_len; j++) {
I2C_WRITE_DATA_REG |= ((*i2c->buf++) << (8 * j));
}
} else {
i += i2c->rd_wr_len;
}
// Start the Transfer
i2c->state = 0; // Clear out the State
i2c->error = 0;
I2C_CONTROLLER_REG |= (1 << 6);
if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) ||
(i2c->state == STATE_DONE), TWI_TIMEOUT)) {
if (i2c->state == STATE_ERROR) {
dev_dbg(i2c->dev, "controller error: 0x%2x", i2c->error);
return -EAGAIN; // try again
}
} else {
dev_err(i2c->dev, "controller timed out "
"waiting for start condition to finish\n");
return -ETIMEDOUT;
}
}
return 0;
}
static int
cns3xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
int i;
int ret;
for (i = 0; i < num; i++)
{
ret = cns3xxx_i2c_xfer_msg(adap, &msgs[i]);
if (ret < 0) {
return ret;
}
}
return num;
}
static struct i2c_algorithm cns3xxx_i2c_algo = {
.master_xfer = cns3xxx_i2c_xfer,
.functionality = cns3xxx_i2c_func,
};
static struct i2c_adapter cns3xxx_i2c_adapter = {
.owner = THIS_MODULE,
.algo = &cns3xxx_i2c_algo,
.algo_data = NULL,
.nr = 0,
.name = "CNS3xxx I2C 0",
.retries = 5,
};
static void cns3xxx_i2c_adapter_init(struct cns3xxx_i2c *i2c)
{
struct clk *clk;
clk = devm_clk_get(i2c->dev, "cpu");
if (WARN_ON(!clk))
return;
/* Disable the I2C */
I2C_CONTROLLER_REG = 0; /* Disabled the I2C */
/* Check the Reg Dump when testing */
I2C_TIME_OUT_REG =
(((((clk_get_rate(clk) / (2 * CNS3xxx_I2C_CLK)) -
1) & 0x3FF) << 8) | (1 << 7) | 0x7F);
I2C_TWI_OUT_DLY_REG |= 0x3;
/* Enable The Interrupt */
CNS3xxx_I2C_ENABLE_INTR();
/* Clear Interrupt Status (0x2 | 0x1) */
I2C_INTERRUPT_STATUS_REG |= (I2C_ACTION_DONE_FLAG | I2C_BUS_ERROR_FLAG);
/* Enable the I2C Controller */
CNS3xxx_I2C_ENABLE();
}
static irqreturn_t cns3xxx_i2c_isr(int irq, void *dev_id)
{
struct cns3xxx_i2c *i2c = dev_id;
int i;
uint32_t stat = I2C_INTERRUPT_STATUS_REG;
/* Clear Interrupt */
I2C_INTERRUPT_STATUS_REG |= 0x1;
if (stat & I2C_BUS_ERROR_FLAG) {
i2c->state = STATE_ERROR;
i2c->error = (I2C_INTERRUPT_STATUS_REG & 0xff00)>>8;
} else {
if (i2c->msg->flags & I2C_M_RD) {
for (i = 0; i <= i2c->rd_wr_len; i++)
{
*i2c->buf++ = ((I2C_READ_DATA_REG >> (8 * i)) & 0xff);
}
}
i2c->state = STATE_DONE;
}
wake_up(&i2c->wait);
return IRQ_HANDLED;
}
static int cns3xxx_i2c_probe(struct platform_device *pdev)
{
struct cns3xxx_i2c *i2c;
struct resource *res, *res2;
int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
printk("%s: IORESOURCE_MEM not defined \n", __FUNCTION__);
return -ENODEV;
}
res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res2) {
printk("%s: IORESOURCE_IRQ not defined \n", __FUNCTION__);
return -ENODEV;
}
i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
if (!request_mem_region(res->start, res->end - res->start + 1,
pdev->name)) {
dev_err(&pdev->dev, "Memory region busy\n");
ret = -EBUSY;
goto request_mem_failed;
}
i2c->dev = &pdev->dev;
i2c->base = ioremap(res->start, res->end - res->start + 1);
if (!i2c->base) {
dev_err(&pdev->dev, "Unable to map registers\n");
ret = -EIO;
goto map_failed;
}
cns3xxx_i2c_adapter_init(i2c);
init_waitqueue_head(&i2c->wait);
ret = request_irq(res2->start, cns3xxx_i2c_isr, 0, pdev->name, i2c);
if (ret) {
dev_err(&pdev->dev, "Cannot claim IRQ\n");
goto request_irq_failed;
}
platform_set_drvdata(pdev, i2c);
i2c->adap = cns3xxx_i2c_adapter;
i2c_set_adapdata(&i2c->adap, i2c);
i2c->adap.dev.parent = &pdev->dev;
/* add i2c adapter to i2c tree */
ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret) {
dev_err(&pdev->dev, "Failed to add adapter\n");
goto add_adapter_failed;
}
return 0;
add_adapter_failed:
free_irq(res2->start, i2c);
request_irq_failed:
iounmap(i2c->base);
map_failed:
release_mem_region(res->start, res->end - res->start + 1);
request_mem_failed:
kfree(i2c);
return ret;
}
static int cns3xxx_i2c_remove(struct platform_device *pdev)
{
struct cns3xxx_i2c *i2c = platform_get_drvdata(pdev);
struct resource *res;
/* disable i2c logic */
CNS3xxx_I2C_DISABLE_INTR();
CNS3xxx_I2C_DISABLE();
/* remove adapter & data */
i2c_del_adapter(&i2c->adap);
platform_set_drvdata(pdev, NULL);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res)
free_irq(res->start, i2c);
iounmap(i2c->base);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res)
release_mem_region(res->start, res->end - res->start + 1);
kfree(i2c);
return 0;
}
static struct platform_driver cns3xxx_i2c_driver = {
.probe = cns3xxx_i2c_probe,
.remove = cns3xxx_i2c_remove,
.driver = {
.owner = THIS_MODULE,
.name = "cns3xxx-i2c",
},
};
static int __init cns3xxx_i2c_init(void)
{
return platform_driver_register(&cns3xxx_i2c_driver);
}
static void __exit cns3xxx_i2c_exit(void)
{
platform_driver_unregister(&cns3xxx_i2c_driver);
}
module_init(cns3xxx_i2c_init);
module_exit(cns3xxx_i2c_exit);
MODULE_AUTHOR("Cavium Networks");
MODULE_DESCRIPTION("Cavium CNS3XXX I2C Controller");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,24 @@
config NET_VENDOR_CAVIUM
bool "Cavium devices"
default y
depends on ARCH_CNS3XXX
---help---
If you have a network (Ethernet) chipset belonging to this class,
say Y.
Note that the answer to this question does not directly affect
the kernel: saying N will just case the configurator to skip all
the questions regarding AMD chipsets. If you say Y, you will be asked
for your specific chipset/driver in the following questions.
if NET_VENDOR_CAVIUM
config CNS3XXX_ETH
tristate "Cavium CNS3xxx Ethernet support"
depends on ARCH_CNS3XXX
select PHYLIB
help
Say Y here if you want to use built-in Ethernet ports
on CNS3XXX processor.
endif

View File

@@ -0,0 +1,5 @@
#
# Makefile for the Cavium ethernet device drivers.
#
obj-$(CONFIG_CNS3XXX_ETH) += cns3xxx_eth.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,448 @@
/*******************************************************************************
*
* CNS3XXX SPI controller driver (master mode only)
*
* Copyright (c) 2008 Cavium Networks
* Copyright 2011 Gateworks Corporation
* Chris Lang <clang@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.
*
* This file is distributed in the hope that it will be useful,
* but AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this file; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA or
* visit http://www.gnu.org/licenses/.
*
* This file may also be available under a different license from Cavium.
* Contact Cavium Networks for more information
*
******************************************************************************/
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/mtd/partitions.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/memory.h>
#include <asm/dma.h>
#include <asm/delay.h>
#include <linux/module.h>
/*
* define access macros
*/
#define SPI_MEM_MAP_VALUE(reg_offset) (*((u32 volatile *)(hw->base + reg_offset)))
#define SPI_CONFIGURATION_REG SPI_MEM_MAP_VALUE(0x00)
#define SPI_SERVICE_STATUS_REG SPI_MEM_MAP_VALUE(0x04)
#define SPI_BIT_RATE_CONTROL_REG SPI_MEM_MAP_VALUE(0x08)
#define SPI_TRANSMIT_CONTROL_REG SPI_MEM_MAP_VALUE(0x0C)
#define SPI_TRANSMIT_BUFFER_REG SPI_MEM_MAP_VALUE(0x10)
#define SPI_RECEIVE_CONTROL_REG SPI_MEM_MAP_VALUE(0x14)
#define SPI_RECEIVE_BUFFER_REG SPI_MEM_MAP_VALUE(0x18)
#define SPI_FIFO_TRANSMIT_CONFIG_REG SPI_MEM_MAP_VALUE(0x1C)
#define SPI_FIFO_TRANSMIT_CONTROL_REG SPI_MEM_MAP_VALUE(0x20)
#define SPI_FIFO_RECEIVE_CONFIG_REG SPI_MEM_MAP_VALUE(0x24)
#define SPI_INTERRUPT_STATUS_REG SPI_MEM_MAP_VALUE(0x28)
#define SPI_INTERRUPT_ENABLE_REG SPI_MEM_MAP_VALUE(0x2C)
#define SPI_TRANSMIT_BUFFER_REG_ADDR (CNS3XXX_SSP_BASE +0x10)
#define SPI_RECEIVE_BUFFER_REG_ADDR (CNS3XXX_SSP_BASE +0x18)
/* Structure for SPI controller of CNS3XXX SOCs */
struct cns3xxx_spi {
/* bitbang has to be first */
struct spi_bitbang bitbang;
struct completion done;
wait_queue_head_t wait;
int len;
int count;
int last_in_message_list;
/* data buffers */
const unsigned char *tx;
unsigned char *rx;
void __iomem *base;
struct spi_master *master;
struct platform_device *pdev;
struct device *dev;
};
static inline u8 cns3xxx_spi_bus_idle(struct cns3xxx_spi *hw)
{
return ((SPI_SERVICE_STATUS_REG & 0x1) ? 0 : 1);
}
static inline u8 cns3xxx_spi_tx_buffer_empty(struct cns3xxx_spi *hw)
{
return ((SPI_INTERRUPT_STATUS_REG & (0x1 << 3)) ? 1 : 0);
}
static inline u8 cns3xxx_spi_rx_buffer_full(struct cns3xxx_spi *hw)
{
return ((SPI_INTERRUPT_STATUS_REG & (0x1 << 2)) ? 1 : 0);
}
u8 cns3xxx_spi_tx_rx(struct cns3xxx_spi *hw, u8 tx_channel, u8 tx_eof,
u32 tx_data, u32 * rx_data)
{
u8 rx_channel;
u8 rx_eof;
while (!cns3xxx_spi_bus_idle(hw)) ; // do nothing
while (!cns3xxx_spi_tx_buffer_empty(hw)) ; // do nothing
SPI_TRANSMIT_CONTROL_REG &= ~(0x7);
SPI_TRANSMIT_CONTROL_REG |= (tx_channel & 0x3) | ((tx_eof & 0x1) << 2);
SPI_TRANSMIT_BUFFER_REG = tx_data;
while (!cns3xxx_spi_rx_buffer_full(hw)) ; // do nothing
rx_channel = SPI_RECEIVE_CONTROL_REG & 0x3;
rx_eof = (SPI_RECEIVE_CONTROL_REG & (0x1 << 2)) ? 1 : 0;
*rx_data = SPI_RECEIVE_BUFFER_REG;
if ((tx_channel != rx_channel) || (tx_eof != rx_eof)) {
return 0;
} else {
return 1;
}
}
u8 cns3xxx_spi_tx(struct cns3xxx_spi *hw, u8 tx_channel, u8 tx_eof, u32 tx_data)
{
while (!cns3xxx_spi_bus_idle(hw)) ; // do nothing
while (!cns3xxx_spi_tx_buffer_empty(hw)) ; // do nothing
SPI_TRANSMIT_CONTROL_REG &= ~(0x7);
SPI_TRANSMIT_CONTROL_REG |= (tx_channel & 0x3) | ((tx_eof & 0x1) << 2);
SPI_TRANSMIT_BUFFER_REG = tx_data;
return 1;
}
static inline struct cns3xxx_spi *to_hw(struct spi_device *sdev)
{
return spi_master_get_devdata(sdev->master);
}
static int cns3xxx_spi_setup_transfer(struct spi_device *spi,
struct spi_transfer *t)
{
return 0;
}
static void cns3xxx_spi_chipselect(struct spi_device *spi, int value)
{
struct cns3xxx_spi *hw = to_hw(spi);
unsigned int spi_config;
switch (value) {
case BITBANG_CS_INACTIVE:
break;
case BITBANG_CS_ACTIVE:
spi_config = SPI_CONFIGURATION_REG;
if (spi->mode & SPI_CPHA)
spi_config |= (0x1 << 13);
else
spi_config &= ~(0x1 << 13);
if (spi->mode & SPI_CPOL)
spi_config |= (0x1 << 14);
else
spi_config &= ~(0x1 << 14);
/* write new configration */
SPI_CONFIGURATION_REG = spi_config;
SPI_TRANSMIT_CONTROL_REG &= ~(0x7);
SPI_TRANSMIT_CONTROL_REG |= (spi->chip_select & 0x3);
break;
}
}
static int cns3xxx_spi_setup(struct spi_device *spi)
{
if (!spi->bits_per_word)
spi->bits_per_word = 8;
return 0;
}
static int cns3xxx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
{
struct cns3xxx_spi *hw = to_hw(spi);
dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", t->tx_buf, t->rx_buf,
t->len);
hw->tx = t->tx_buf;
hw->rx = t->rx_buf;
hw->len = t->len;
hw->count = 0;
hw->last_in_message_list = t->last_in_message_list;
init_completion(&hw->done);
if (hw->tx) {
int i;
u32 rx_data;
for (i = 0; i < (hw->len - 1); i++) {
dev_dbg(&spi->dev,
"[SPI_CNS3XXX_DEBUG] hw->tx[%02d]: 0x%02x\n", i,
hw->tx[i]);
cns3xxx_spi_tx_rx(hw, spi->chip_select, 0, hw->tx[i],
&rx_data);
if (hw->rx) {
hw->rx[i] = rx_data;
dev_dbg(&spi->dev,
"[SPI_CNS3XXX_DEBUG] hw->rx[%02d]: 0x%02x\n",
i, hw->rx[i]);
}
}
if (t->last_in_message_list) {
cns3xxx_spi_tx_rx(hw, spi->chip_select, 1, hw->tx[i],
&rx_data);
if (hw->rx) {
hw->rx[i] = rx_data;
dev_dbg(&spi->dev,
"[SPI_CNS3XXX_DEBUG] hw->rx[%02d]: 0x%02x\n",
i, hw->rx[i]);
}
} else {
cns3xxx_spi_tx_rx(hw, spi->chip_select, 0, hw->tx[i],
&rx_data);
}
goto done;
}
if (hw->rx) {
int i;
u32 rx_data;
for (i = 0; i < (hw->len - 1); i++) {
cns3xxx_spi_tx_rx(hw, spi->chip_select, 0, 0xff, &rx_data);
hw->rx[i] = rx_data;
dev_dbg(&spi->dev,
"[SPI_CNS3XXX_DEBUG] hw->rx[%02d]: 0x%02x\n", i,
hw->rx[i]);
}
if (t->last_in_message_list) {
cns3xxx_spi_tx_rx(hw, spi->chip_select, 1, 0xff, &rx_data);
} else {
cns3xxx_spi_tx_rx(hw, spi->chip_select, 0, 0xff, &rx_data);
}
hw->rx[i] = rx_data;
dev_dbg(&spi->dev, "[SPI_CNS3XXX_DEBUG] hw->rx[%02d]: 0x%02x\n",
i, hw->rx[i]);
}
done:
return hw->len;
}
static void __init cns3xxx_spi_initial(struct cns3xxx_spi *hw)
{
SPI_CONFIGURATION_REG = (((0x0 & 0x3) << 0) | /* 8bits shift length */
(0x0 << 9) | /* SPI mode */
(0x0 << 10) | /* disable FIFO */
(0x1 << 11) | /* SPI master mode */
(0x0 << 12) | /* disable SPI loopback mode */
(0x1 << 13) | /* clock phase */
(0x1 << 14) | /* clock polarity */
(0x0 << 24) | /* disable - SPI data swap */
(0x1 << 29) | /* enable - 2IO Read mode */
(0x0 << 30) | /* disable - SPI high speed read for system boot up */
(0x0 << 31)); /* disable - SPI */
/* Set SPI bit rate PCLK/2 */
SPI_BIT_RATE_CONTROL_REG = 0x1;
/* Set SPI Tx channel 0 */
SPI_TRANSMIT_CONTROL_REG = 0x0;
/* Set Tx FIFO Threshold, Tx FIFO has 2 words */
SPI_FIFO_TRANSMIT_CONFIG_REG &= ~(0x03 << 4);
SPI_FIFO_TRANSMIT_CONFIG_REG |= ((0x0 & 0x03) << 4);
/* Set Rx FIFO Threshold, Rx FIFO has 2 words */
SPI_FIFO_RECEIVE_CONFIG_REG &= ~(0x03 << 4);
SPI_FIFO_RECEIVE_CONFIG_REG |= ((0x0 & 0x03) << 4);
/* Disable all interrupt */
SPI_INTERRUPT_ENABLE_REG = 0x0;
/* Clear spurious interrupt sources */
SPI_INTERRUPT_STATUS_REG = (0x0F << 4);
/* Enable SPI */
SPI_CONFIGURATION_REG |= (0x1 << 31);
return;
}
static int cns3xxx_spi_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct cns3xxx_spi *hw;
struct resource *res;
int err = 0;
printk("%s: setup CNS3XXX SPI Controller\n", __FUNCTION__);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
/* Allocate master with space for cns3xxx_spi */
master = spi_alloc_master(&pdev->dev, sizeof(struct cns3xxx_spi));
if (master == NULL) {
dev_err(&pdev->dev, "No memory for spi_master\n");
err = -ENOMEM;
goto err_nomem;
}
hw = spi_master_get_devdata(master);
memset(hw, 0, sizeof(struct cns3xxx_spi));
hw->master = spi_master_get(master);
hw->dev = &pdev->dev;
hw->base = devm_ioremap_resource(hw->dev, res);
if (IS_ERR(hw->base)) {
dev_err(hw->dev, "Unable to map registers\n");
err = PTR_ERR(hw->base);
goto err_register;
}
platform_set_drvdata(pdev, hw);
init_completion(&hw->done);
/* setup the master state. */
master->num_chipselect = 4;
master->bus_num = 1;
/* setup the state for the bitbang driver */
hw->bitbang.master = hw->master;
hw->bitbang.setup_transfer = cns3xxx_spi_setup_transfer;
hw->bitbang.chipselect = cns3xxx_spi_chipselect;
hw->bitbang.txrx_bufs = cns3xxx_spi_txrx;
hw->bitbang.master->setup = cns3xxx_spi_setup;
dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang);
/* SPI controller initializations */
cns3xxx_spi_initial(hw);
/* register SPI controller */
err = spi_bitbang_start(&hw->bitbang);
if (err) {
dev_err(&pdev->dev, "Failed to register SPI master\n");
goto err_register;
}
return 0;
err_register:
spi_master_put(hw->master);;
err_nomem:
return err;
}
static int cns3xxx_spi_remove(struct platform_device *dev)
{
struct cns3xxx_spi *hw = platform_get_drvdata(dev);
platform_set_drvdata(dev, NULL);
spi_unregister_master(hw->master);
spi_master_put(hw->master);
return 0;
}
#ifdef CONFIG_PM
static int cns3xxx_spi_suspend(struct platform_device *pdev, pm_message_t msg)
{
struct cns3xxx_spi *hw = platform_get_drvdata(pdev);
return 0;
}
static int cns3xxx_spi_resume(struct platform_device *pdev)
{
struct cns3xxx_spi *hw = platform_get_drvdata(pdev);
return 0;
}
#else
#define cns3xxx_spi_suspend NULL
#define cns3xxx_spi_resume NULL
#endif
static struct platform_driver cns3xxx_spi_driver = {
.probe = cns3xxx_spi_probe,
.remove = cns3xxx_spi_remove,
.suspend = cns3xxx_spi_suspend,
.resume = cns3xxx_spi_resume,
.driver = {
.name = "cns3xxx_spi",
.owner = THIS_MODULE,
},
};
static int __init cns3xxx_spi_init(void)
{
return platform_driver_register(&cns3xxx_spi_driver);
}
static void __exit cns3xxx_spi_exit(void)
{
platform_driver_unregister(&cns3xxx_spi_driver);
}
module_init(cns3xxx_spi_init);
module_exit(cns3xxx_spi_exit);
MODULE_AUTHOR("Cavium Networks");
MODULE_DESCRIPTION("CNS3XXX SPI Controller Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:cns3xxx_spi");
EXPORT_SYMBOL_GPL(cns3xxx_spi_tx_rx);

View File

@@ -0,0 +1,26 @@
/*
* arch/arm/mach-cns3xxx/include/mach/platform.h
*
* Copyright 2011 Gateworks Corporation
* Chris Lang <clang@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.
*
*/
#ifndef __ASM_ARCH_PLATFORM_H
#define __ASM_ARCH_PLATFORM_H
#ifndef __ASSEMBLY__
/* Information about built-in Ethernet MAC interfaces */
struct cns3xxx_plat_info {
u8 ports; /* Bitmap of enabled Ports */
u8 hwaddr[4][6];
u32 phy[3];
};
#endif /* __ASM_ARCH_PLATFORM_H */
#endif