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
|
||||
1123
target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/laguna.c
Normal file
1123
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,
|
||||
};
|
||||
374
target/linux/cns3xxx/files/drivers/i2c/busses/i2c-cns3xxx.c
Normal file
374
target/linux/cns3xxx/files/drivers/i2c/busses/i2c-cns3xxx.c
Normal 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");
|
||||
@@ -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
|
||||
@@ -0,0 +1,5 @@
|
||||
#
|
||||
# Makefile for the Cavium ethernet device drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_CNS3XXX_ETH) += cns3xxx_eth.o
|
||||
1325
target/linux/cns3xxx/files/drivers/net/ethernet/cavium/cns3xxx_eth.c
Normal file
1325
target/linux/cns3xxx/files/drivers/net/ethernet/cavium/cns3xxx_eth.c
Normal file
File diff suppressed because it is too large
Load Diff
448
target/linux/cns3xxx/files/drivers/spi/spi-cns3xxx.c
Normal file
448
target/linux/cns3xxx/files/drivers/spi/spi-cns3xxx.c
Normal 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);
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user