diff options
Diffstat (limited to 'arch/arm/mach-prima2')
23 files changed, 1307 insertions, 0 deletions
diff --git a/arch/arm/mach-prima2/Makefile b/arch/arm/mach-prima2/Makefile new file mode 100644 index 00000000000..7af7fc05d56 --- /dev/null +++ b/arch/arm/mach-prima2/Makefile @@ -0,0 +1,7 @@ +obj-y := timer.o +obj-y += irq.o +obj-y += clock.o +obj-y += rstc.o +obj-y += prima2.o +obj-$(CONFIG_DEBUG_LL) += lluart.o +obj-$(CONFIG_CACHE_L2X0) += l2x0.o diff --git a/arch/arm/mach-prima2/Makefile.boot b/arch/arm/mach-prima2/Makefile.boot new file mode 100644 index 00000000000..d023db3ae4f --- /dev/null +++ b/arch/arm/mach-prima2/Makefile.boot @@ -0,0 +1,3 @@ +zreladdr-y := 0x00008000 +params_phys-y := 0x00000100 +initrd_phys-y := 0x00800000 diff --git a/arch/arm/mach-prima2/clock.c b/arch/arm/mach-prima2/clock.c new file mode 100644 index 00000000000..f9a2aaf63f7 --- /dev/null +++ b/arch/arm/mach-prima2/clock.c @@ -0,0 +1,509 @@ +/* + * Clock tree for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/module.h> +#include <linux/bitops.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/clkdev.h> +#include <linux/clk.h> +#include <linux/spinlock.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <asm/mach/map.h> +#include <mach/map.h> + +#define SIRFSOC_CLKC_CLK_EN0 0x0000 +#define SIRFSOC_CLKC_CLK_EN1 0x0004 +#define SIRFSOC_CLKC_REF_CFG 0x0014 +#define SIRFSOC_CLKC_CPU_CFG 0x0018 +#define SIRFSOC_CLKC_MEM_CFG 0x001c +#define SIRFSOC_CLKC_SYS_CFG 0x0020 +#define SIRFSOC_CLKC_IO_CFG 0x0024 +#define SIRFSOC_CLKC_DSP_CFG 0x0028 +#define SIRFSOC_CLKC_GFX_CFG 0x002c +#define SIRFSOC_CLKC_MM_CFG 0x0030 +#define SIRFSOC_LKC_LCD_CFG 0x0034 +#define SIRFSOC_CLKC_MMC_CFG 0x0038 +#define SIRFSOC_CLKC_PLL1_CFG0 0x0040 +#define SIRFSOC_CLKC_PLL2_CFG0 0x0044 +#define SIRFSOC_CLKC_PLL3_CFG0 0x0048 +#define SIRFSOC_CLKC_PLL1_CFG1 0x004c +#define SIRFSOC_CLKC_PLL2_CFG1 0x0050 +#define SIRFSOC_CLKC_PLL3_CFG1 0x0054 +#define SIRFSOC_CLKC_PLL1_CFG2 0x0058 +#define SIRFSOC_CLKC_PLL2_CFG2 0x005c +#define SIRFSOC_CLKC_PLL3_CFG2 0x0060 + +#define SIRFSOC_CLOCK_VA_BASE SIRFSOC_VA(0x005000) + +#define KHZ 1000 +#define MHZ (KHZ * KHZ) + +struct clk_ops { + unsigned long (*get_rate)(struct clk *clk); + long (*round_rate)(struct clk *clk, unsigned long rate); + int (*set_rate)(struct clk *clk, unsigned long rate); + int (*enable)(struct clk *clk); + int (*disable)(struct clk *clk); + struct clk *(*get_parent)(struct clk *clk); + int (*set_parent)(struct clk *clk, struct clk *parent); +}; + +struct clk { + struct clk *parent; /* parent clk */ + unsigned long rate; /* clock rate in Hz */ + signed char usage; /* clock enable count */ + signed char enable_bit; /* enable bit: 0 ~ 63 */ + unsigned short regofs; /* register offset */ + struct clk_ops *ops; /* clock operation */ +}; + +static DEFINE_SPINLOCK(clocks_lock); + +static inline unsigned long clkc_readl(unsigned reg) +{ + return readl(SIRFSOC_CLOCK_VA_BASE + reg); +} + +static inline void clkc_writel(u32 val, unsigned reg) +{ + writel(val, SIRFSOC_CLOCK_VA_BASE + reg); +} + +/* + * osc_rtc - real time oscillator - 32.768KHz + * osc_sys - high speed oscillator - 26MHz + */ + +static struct clk clk_rtc = { + .rate = 32768, +}; + +static struct clk clk_osc = { + .rate = 26 * MHZ, +}; + +/* + * std pll + */ +static unsigned long std_pll_get_rate(struct clk *clk) +{ + unsigned long fin = clk_get_rate(clk->parent); + u32 regcfg2 = clk->regofs + SIRFSOC_CLKC_PLL1_CFG2 - + SIRFSOC_CLKC_PLL1_CFG0; + + if (clkc_readl(regcfg2) & BIT(2)) { + /* pll bypass mode */ + clk->rate = fin; + } else { + /* fout = fin * nf / nr / od */ + u32 cfg0 = clkc_readl(clk->regofs); + u32 nf = (cfg0 & (BIT(13) - 1)) + 1; + u32 nr = ((cfg0 >> 13) & (BIT(6) - 1)) + 1; + u32 od = ((cfg0 >> 19) & (BIT(4) - 1)) + 1; + WARN_ON(fin % MHZ); + clk->rate = fin / MHZ * nf / nr / od * MHZ; + } + + return clk->rate; +} + +static int std_pll_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned long fin, nf, nr, od, reg; + + /* + * fout = fin * nf / (nr * od); + * set od = 1, nr = fin/MHz, so fout = nf * MHz + */ + + nf = rate / MHZ; + if (unlikely((rate % MHZ) || nf > BIT(13) || nf < 1)) + return -EINVAL; + + fin = clk_get_rate(clk->parent); + BUG_ON(fin < MHZ); + + nr = fin / MHZ; + BUG_ON((fin % MHZ) || nr > BIT(6)); + + od = 1; + + reg = (nf - 1) | ((nr - 1) << 13) | ((od - 1) << 19); + clkc_writel(reg, clk->regofs); + + reg = clk->regofs + SIRFSOC_CLKC_PLL1_CFG1 - SIRFSOC_CLKC_PLL1_CFG0; + clkc_writel((nf >> 1) - 1, reg); + + reg = clk->regofs + SIRFSOC_CLKC_PLL1_CFG2 - SIRFSOC_CLKC_PLL1_CFG0; + while (!(clkc_readl(reg) & BIT(6))) + cpu_relax(); + + clk->rate = 0; /* set to zero will force recalculation */ + return 0; +} + +static struct clk_ops std_pll_ops = { + .get_rate = std_pll_get_rate, + .set_rate = std_pll_set_rate, +}; + +static struct clk clk_pll1 = { + .parent = &clk_osc, + .regofs = SIRFSOC_CLKC_PLL1_CFG0, + .ops = &std_pll_ops, +}; + +static struct clk clk_pll2 = { + .parent = &clk_osc, + .regofs = SIRFSOC_CLKC_PLL2_CFG0, + .ops = &std_pll_ops, +}; + +static struct clk clk_pll3 = { + .parent = &clk_osc, + .regofs = SIRFSOC_CLKC_PLL3_CFG0, + .ops = &std_pll_ops, +}; + +/* + * clock domains - cpu, mem, sys/io + */ + +static struct clk clk_mem; + +static struct clk *dmn_get_parent(struct clk *clk) +{ + struct clk *clks[] = { + &clk_osc, &clk_rtc, &clk_pll1, &clk_pll2, &clk_pll3 + }; + u32 cfg = clkc_readl(clk->regofs); + WARN_ON((cfg & (BIT(3) - 1)) > 4); + return clks[cfg & (BIT(3) - 1)]; +} + +static int dmn_set_parent(struct clk *clk, struct clk *parent) +{ + const struct clk *clks[] = { + &clk_osc, &clk_rtc, &clk_pll1, &clk_pll2, &clk_pll3 + }; + u32 cfg = clkc_readl(clk->regofs); + int i; + for (i = 0; i < ARRAY_SIZE(clks); i++) { + if (clks[i] == parent) { + cfg &= ~(BIT(3) - 1); + clkc_writel(cfg | i, clk->regofs); + /* BIT(3) - switching status: 1 - busy, 0 - done */ + while (clkc_readl(clk->regofs) & BIT(3)) + cpu_relax(); + return 0; + } + } + return -EINVAL; +} + +static unsigned long dmn_get_rate(struct clk *clk) +{ + unsigned long fin = clk_get_rate(clk->parent); + u32 cfg = clkc_readl(clk->regofs); + if (cfg & BIT(24)) { + /* fcd bypass mode */ + clk->rate = fin; + } else { + /* + * wait count: bit[19:16], hold count: bit[23:20] + */ + u32 wait = (cfg >> 16) & (BIT(4) - 1); + u32 hold = (cfg >> 20) & (BIT(4) - 1); + + clk->rate = fin / (wait + hold + 2); + } + + return clk->rate; +} + +static int dmn_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned long fin; + unsigned ratio, wait, hold, reg; + unsigned bits = (clk == &clk_mem) ? 3 : 4; + + fin = clk_get_rate(clk->parent); + ratio = fin / rate; + + if (unlikely(ratio < 2 || ratio > BIT(bits + 1))) + return -EINVAL; + + WARN_ON(fin % rate); + + wait = (ratio >> 1) - 1; + hold = ratio - wait - 2; + + reg = clkc_readl(clk->regofs); + reg &= ~(((BIT(bits) - 1) << 16) | ((BIT(bits) - 1) << 20)); + reg |= (wait << 16) | (hold << 20) | BIT(25); + clkc_writel(reg, clk->regofs); + + /* waiting FCD been effective */ + while (clkc_readl(clk->regofs) & BIT(25)) + cpu_relax(); + + clk->rate = 0; /* set to zero will force recalculation */ + + return 0; +} + +/* + * cpu clock has no FCD register in Prima2, can only change pll + */ +static int cpu_set_rate(struct clk *clk, unsigned long rate) +{ + int ret1, ret2; + struct clk *cur_parent, *tmp_parent; + + cur_parent = dmn_get_parent(clk); + BUG_ON(cur_parent == NULL || cur_parent->usage > 1); + + /* switch to tmp pll before setting parent clock's rate */ + tmp_parent = cur_parent == &clk_pll1 ? &clk_pll2 : &clk_pll1; + ret1 = dmn_set_parent(clk, tmp_parent); + BUG_ON(ret1); + + ret2 = clk_set_rate(cur_parent, rate); + + ret1 = dmn_set_parent(clk, cur_parent); + + clk->rate = 0; /* set to zero will force recalculation */ + + return ret2 ? ret2 : ret1; +} + +static struct clk_ops cpu_ops = { + .get_parent = dmn_get_parent, + .set_parent = dmn_set_parent, + .set_rate = cpu_set_rate, +}; + +static struct clk clk_cpu = { + .parent = &clk_pll1, + .regofs = SIRFSOC_CLKC_CPU_CFG, + .ops = &cpu_ops, +}; + + +static struct clk_ops msi_ops = { + .set_rate = dmn_set_rate, + .get_rate = dmn_get_rate, + .set_parent = dmn_set_parent, + .get_parent = dmn_get_parent, +}; + +static struct clk clk_mem = { + .parent = &clk_pll2, + .regofs = SIRFSOC_CLKC_MEM_CFG, + .ops = &msi_ops, +}; + +static struct clk clk_sys = { + .parent = &clk_pll3, + .regofs = SIRFSOC_CLKC_SYS_CFG, + .ops = &msi_ops, +}; + +static struct clk clk_io = { + .parent = &clk_pll3, + .regofs = SIRFSOC_CLKC_IO_CFG, + .ops = &msi_ops, +}; + +/* + * on-chip clock sets + */ +static struct clk_lookup onchip_clks[] = { + { + .dev_id = "rtc", + .clk = &clk_rtc, + }, { + .dev_id = "osc", + .clk = &clk_osc, + }, { + .dev_id = "pll1", + .clk = &clk_pll1, + }, { + .dev_id = "pll2", + .clk = &clk_pll2, + }, { + .dev_id = "pll3", + .clk = &clk_pll3, + }, { + .dev_id = "cpu", + .clk = &clk_cpu, + }, { + .dev_id = "mem", + .clk = &clk_mem, + }, { + .dev_id = "sys", + .clk = &clk_sys, + }, { + .dev_id = "io", + .clk = &clk_io, + }, +}; + +int clk_enable(struct clk *clk) +{ + unsigned long flags; + + if (unlikely(IS_ERR_OR_NULL(clk))) + return -EINVAL; + + if (clk->parent) + clk_enable(clk->parent); + + spin_lock_irqsave(&clocks_lock, flags); + if (!clk->usage++ && clk->ops && clk->ops->enable) + clk->ops->enable(clk); + spin_unlock_irqrestore(&clocks_lock, flags); + return 0; +} +EXPORT_SYMBOL(clk_enable); + +void clk_disable(struct clk *clk) +{ + unsigned long flags; + + if (unlikely(IS_ERR_OR_NULL(clk))) + return; + + WARN_ON(!clk->usage); + + spin_lock_irqsave(&clocks_lock, flags); + if (--clk->usage == 0 && clk->ops && clk->ops->disable) + clk->ops->disable(clk); + spin_unlock_irqrestore(&clocks_lock, flags); + + if (clk->parent) + clk_disable(clk->parent); +} +EXPORT_SYMBOL(clk_disable); + +unsigned long clk_get_rate(struct clk *clk) +{ + if (unlikely(IS_ERR_OR_NULL(clk))) + return 0; + + if (clk->rate) + return clk->rate; + + if (clk->ops && clk->ops->get_rate) + return clk->ops->get_rate(clk); + + return clk_get_rate(clk->parent); +} +EXPORT_SYMBOL(clk_get_rate); + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + if (unlikely(IS_ERR_OR_NULL(clk))) + return 0; + + if (clk->ops && clk->ops->round_rate) + return clk->ops->round_rate(clk, rate); + + return 0; +} +EXPORT_SYMBOL(clk_round_rate); + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + if (unlikely(IS_ERR_OR_NULL(clk))) + return -EINVAL; + + if (!clk->ops || !clk->ops->set_rate) + return -EINVAL; + + return clk->ops->set_rate(clk, rate); +} +EXPORT_SYMBOL(clk_set_rate); + +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + int ret; + unsigned long flags; + + if (unlikely(IS_ERR_OR_NULL(clk))) + return -EINVAL; + + if (!clk->ops || !clk->ops->set_parent) + return -EINVAL; + + spin_lock_irqsave(&clocks_lock, flags); + ret = clk->ops->set_parent(clk, parent); + if (!ret) { + parent->usage += clk->usage; + clk->parent->usage -= clk->usage; + BUG_ON(clk->parent->usage < 0); + clk->parent = parent; + } + spin_unlock_irqrestore(&clocks_lock, flags); + return ret; +} +EXPORT_SYMBOL(clk_set_parent); + +struct clk *clk_get_parent(struct clk *clk) +{ + unsigned long flags; + + if (unlikely(IS_ERR_OR_NULL(clk))) + return NULL; + + if (!clk->ops || !clk->ops->get_parent) + return clk->parent; + + spin_lock_irqsave(&clocks_lock, flags); + clk->parent = clk->ops->get_parent(clk); + spin_unlock_irqrestore(&clocks_lock, flags); + return clk->parent; +} +EXPORT_SYMBOL(clk_get_parent); + +static void __init sirfsoc_clk_init(void) +{ + clkdev_add_table(onchip_clks, ARRAY_SIZE(onchip_clks)); +} + +static struct of_device_id clkc_ids[] = { + { .compatible = "sirf,prima2-clkc" }, +}; + +void __init sirfsoc_of_clk_init(void) +{ + struct device_node *np; + struct resource res; + struct map_desc sirfsoc_clkc_iodesc = { + .virtual = SIRFSOC_CLOCK_VA_BASE, + .type = MT_DEVICE, + }; + + np = of_find_matching_node(NULL, clkc_ids); + if (!np) + panic("unable to find compatible clkc node in dtb\n"); + + if (of_address_to_resource(np, 0, &res)) + panic("unable to find clkc range in dtb"); + of_node_put(np); + + sirfsoc_clkc_iodesc.pfn = __phys_to_pfn(res.start); + sirfsoc_clkc_iodesc.length = 1 + res.end - res.start; + + iotable_init(&sirfsoc_clkc_iodesc, 1); + + sirfsoc_clk_init(); +} diff --git a/arch/arm/mach-prima2/common.h b/arch/arm/mach-prima2/common.h new file mode 100644 index 00000000000..83e5d212811 --- /dev/null +++ b/arch/arm/mach-prima2/common.h @@ -0,0 +1,26 @@ +/* + * This file contains common function prototypes to avoid externs in the c files. + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#ifndef __MACH_PRIMA2_COMMON_H__ +#define __MACH_PRIMA2_COMMON_H__ + +#include <linux/init.h> +#include <asm/mach/time.h> + +extern struct sys_timer sirfsoc_timer; + +extern void __init sirfsoc_of_irq_init(void); +extern void __init sirfsoc_of_clk_init(void); + +#ifndef CONFIG_DEBUG_LL +static inline void sirfsoc_map_lluart(void) {} +#else +extern void __init sirfsoc_map_lluart(void); +#endif + +#endif diff --git a/arch/arm/mach-prima2/include/mach/clkdev.h b/arch/arm/mach-prima2/include/mach/clkdev.h new file mode 100644 index 00000000000..66932518b1b --- /dev/null +++ b/arch/arm/mach-prima2/include/mach/clkdev.h @@ -0,0 +1,15 @@ +/* + * arch/arm/mach-prima2/include/mach/clkdev.h + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#ifndef __MACH_CLKDEV_H +#define __MACH_CLKDEV_H + +#define __clk_get(clk) ({ 1; }) +#define __clk_put(clk) do { } while (0) + +#endif diff --git a/arch/arm/mach-prima2/include/mach/debug-macro.S b/arch/arm/mach-prima2/include/mach/debug-macro.S new file mode 100644 index 00000000000..bf75106333f --- /dev/null +++ b/arch/arm/mach-prima2/include/mach/debug-macro.S @@ -0,0 +1,29 @@ +/* + * arch/arm/mach-prima2/include/mach/debug-macro.S + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <mach/hardware.h> +#include <mach/uart.h> + + .macro addruart, rp, rv + ldr \rp, =SIRFSOC_UART1_PA_BASE @ physical + ldr \rv, =SIRFSOC_UART1_VA_BASE @ virtual + .endm + + .macro senduart,rd,rx + str \rd, [\rx, #SIRFSOC_UART_TXFIFO_DATA] + .endm + + .macro busyuart,rd,rx + .endm + + .macro waituart,rd,rx +1001: ldr \rd, [\rx, #SIRFSOC_UART_TXFIFO_STATUS] + tst \rd, #SIRFSOC_UART1_TXFIFO_EMPTY + beq 1001b + .endm + diff --git a/arch/arm/mach-prima2/include/mach/entry-macro.S b/arch/arm/mach-prima2/include/mach/entry-macro.S new file mode 100644 index 00000000000..1c8a50f102a --- /dev/null +++ b/arch/arm/mach-prima2/include/mach/entry-macro.S @@ -0,0 +1,29 @@ +/* + * arch/arm/mach-prima2/include/mach/entry-macro.S + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <mach/hardware.h> + +#define SIRFSOC_INT_ID 0x38 + + .macro get_irqnr_preamble, base, tmp + ldr \base, =sirfsoc_intc_base + ldr \base, [\base] + .endm + + .macro get_irqnr_and_base, irqnr, irqstat, base, tmp + ldr \irqnr, [\base, #SIRFSOC_INT_ID] @ Get the highest priority irq + cmp \irqnr, #0x40 @ the irq num can't be larger than 0x3f + movges \irqnr, #0 + .endm + + .macro disable_fiq + .endm + + .macro arch_ret_to_user, tmp1, tmp2 + .endm + diff --git a/arch/arm/mach-prima2/include/mach/hardware.h b/arch/arm/mach-prima2/include/mach/hardware.h new file mode 100644 index 00000000000..105b96964f2 --- /dev/null +++ b/arch/arm/mach-prima2/include/mach/hardware.h @@ -0,0 +1,15 @@ +/* + * arch/arm/mach-prima2/include/mach/hardware.h + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#ifndef __MACH_HARDWARE_H__ +#define __MACH_HARDWARE_H__ + +#include <asm/sizes.h> +#include <mach/map.h> + +#endif diff --git a/arch/arm/mach-prima2/include/mach/io.h b/arch/arm/mach-prima2/include/mach/io.h new file mode 100644 index 00000000000..6c31e9ec279 --- /dev/null +++ b/arch/arm/mach-prima2/include/mach/io.h @@ -0,0 +1,16 @@ +/* + * arch/arm/mach-prima2/include/mach/io.h + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#ifndef __MACH_PRIMA2_IO_H +#define __MACH_PRIMA2_IO_H + +#define IO_SPACE_LIMIT ((resource_size_t)0) + +#define __mem_pci(a) (a) + +#endif diff --git a/arch/arm/mach-prima2/include/mach/irqs.h b/arch/arm/mach-prima2/include/mach/irqs.h new file mode 100644 index 00000000000..bb354f952fd --- /dev/null +++ b/arch/arm/mach-prima2/include/mach/irqs.h @@ -0,0 +1,17 @@ +/* + * arch/arm/mach-prima2/include/mach/irqs.h + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#ifndef __ASM_ARCH_IRQS_H +#define __ASM_ARCH_IRQS_H + +#define SIRFSOC_INTENAL_IRQ_START 0 +#define SIRFSOC_INTENAL_IRQ_END 59 + +#define NR_IRQS 220 + +#endif diff --git a/arch/arm/mach-prima2/include/mach/map.h b/arch/arm/mach-prima2/include/mach/map.h new file mode 100644 index 00000000000..66b1ae2e553 --- /dev/null +++ b/arch/arm/mach-prima2/include/mach/map.h @@ -0,0 +1,16 @@ +/* + * memory & I/O static mapping definitions for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#ifndef __MACH_PRIMA2_MAP_H__ +#define __MACH_PRIMA2_MAP_H__ + +#include <mach/vmalloc.h> + +#define SIRFSOC_VA(x) (VMALLOC_END + ((x) & 0x00FFF000)) + +#endif diff --git a/arch/arm/mach-prima2/include/mach/memory.h b/arch/arm/mach-prima2/include/mach/memory.h new file mode 100644 index 00000000000..368cd5a0601 --- /dev/null +++ b/arch/arm/mach-prima2/include/mach/memory.h @@ -0,0 +1,21 @@ +/* + * arch/arm/mach-prima2/include/mach/memory.h + * + * Copyright (c) 2010 – 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#ifndef __ASM_ARCH_MEMORY_H +#define __ASM_ARCH_MEMORY_H + +#define PLAT_PHYS_OFFSET UL(0x00000000) + +/* + * Restrict DMA-able region to workaround silicon limitation. + * The limitation restricts buffers available for DMA to SD/MMC + * hardware to be below 256MB + */ +#define ARM_DMA_ZONE_SIZE (SZ_256M) + +#endif diff --git a/arch/arm/mach-prima2/include/mach/system.h b/arch/arm/mach-prima2/include/mach/system.h new file mode 100644 index 00000000000..0dbd257ad16 --- /dev/null +++ b/arch/arm/mach-prima2/include/mach/system.h @@ -0,0 +1,29 @@ +/* + * arch/arm/mach-prima2/include/mach/system.h + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#ifndef __MACH_SYSTEM_H__ +#define __MACH_SYSTEM_H__ + +#include <linux/bitops.h> +#include <mach/hardware.h> + +#define SIRFSOC_SYS_RST_BIT BIT(31) + +extern void __iomem *sirfsoc_rstc_base; + +static inline void arch_idle(void) +{ + cpu_do_idle(); +} + +static inline void arch_reset(char mode, const char *cmd) +{ + writel(SIRFSOC_SYS_RST_BIT, sirfsoc_rstc_base); +} + +#endif diff --git a/arch/arm/mach-prima2/include/mach/timex.h b/arch/arm/mach-prima2/include/mach/timex.h new file mode 100644 index 00000000000..d6f98a75e56 --- /dev/null +++ b/arch/arm/mach-prima2/include/mach/timex.h @@ -0,0 +1,14 @@ +/* + * arch/arm/mach-prima2/include/mach/timex.h + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#ifndef __MACH_TIMEX_H__ +#define __MACH_TIMEX_H__ + +#define CLOCK_TICK_RATE 1000000 + +#endif diff --git a/arch/arm/mach-prima2/include/mach/uart.h b/arch/arm/mach-prima2/include/mach/uart.h new file mode 100644 index 00000000000..c98b4d5ac24 --- /dev/null +++ b/arch/arm/mach-prima2/include/mach/uart.h @@ -0,0 +1,23 @@ +/* + * arch/arm/mach-prima2/include/mach/uart.h + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#ifndef __MACH_PRIMA2_SIRFSOC_UART_H +#define __MACH_PRIMA2_SIRFSOC_UART_H + +/* UART-1: used as serial debug port */ +#define SIRFSOC_UART1_PA_BASE 0xb0060000 +#define SIRFSOC_UART1_VA_BASE SIRFSOC_VA(0x060000) +#define SIRFSOC_UART1_SIZE SZ_4K + +#define SIRFSOC_UART_TXFIFO_STATUS 0x0114 +#define SIRFSOC_UART_TXFIFO_DATA 0x0118 + +#define SIRFSOC_UART1_TXFIFO_FULL (1 << 5) +#define SIRFSOC_UART1_TXFIFO_EMPTY (1 << 6) + +#endif diff --git a/arch/arm/mach-prima2/include/mach/uncompress.h b/arch/arm/mach-prima2/include/mach/uncompress.h new file mode 100644 index 00000000000..83125c6a30b --- /dev/null +++ b/arch/arm/mach-prima2/include/mach/uncompress.h @@ -0,0 +1,40 @@ +/* + * arch/arm/mach-prima2/include/mach/uncompress.h + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#ifndef __ASM_ARCH_UNCOMPRESS_H +#define __ASM_ARCH_UNCOMPRESS_H + +#include <linux/io.h> +#include <mach/hardware.h> +#include <mach/uart.h> + +void arch_decomp_setup(void) +{ +} + +#define arch_decomp_wdog() + +static __inline__ void putc(char c) +{ + /* + * during kernel decompression, all mappings are flat: + * virt_addr == phys_addr + */ + while (__raw_readl(SIRFSOC_UART1_PA_BASE + SIRFSOC_UART_TXFIFO_STATUS) + & SIRFSOC_UART1_TXFIFO_FULL) + barrier(); + + __raw_writel(c, SIRFSOC_UART1_PA_BASE + SIRFSOC_UART_TXFIFO_DATA); +} + +static inline void flush(void) +{ +} + +#endif + diff --git a/arch/arm/mach-prima2/include/mach/vmalloc.h b/arch/arm/mach-prima2/include/mach/vmalloc.h new file mode 100644 index 00000000000..c9f90fec78e --- /dev/null +++ b/arch/arm/mach-prima2/include/mach/vmalloc.h @@ -0,0 +1,16 @@ +/* + * arch/arm/ach-prima2/include/mach/vmalloc.h + * + * Copyright (c) 2010 – 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#ifndef __MACH_VMALLOC_H +#define __MACH_VMALLOC_H + +#include <linux/const.h> + +#define VMALLOC_END _AC(0xFEC00000, UL) + +#endif diff --git a/arch/arm/mach-prima2/irq.c b/arch/arm/mach-prima2/irq.c new file mode 100644 index 00000000000..c3404cbb6ff --- /dev/null +++ b/arch/arm/mach-prima2/irq.c @@ -0,0 +1,71 @@ +/* + * interrupt controller support for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/init.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <mach/hardware.h> +#include <asm/mach/irq.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#define SIRFSOC_INT_RISC_MASK0 0x0018 +#define SIRFSOC_INT_RISC_MASK1 0x001C +#define SIRFSOC_INT_RISC_LEVEL0 0x0020 +#define SIRFSOC_INT_RISC_LEVEL1 0x0024 + +void __iomem *sirfsoc_intc_base; + +static __init void +sirfsoc_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num) +{ + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + + gc = irq_alloc_generic_chip("SIRFINTC", 1, irq_start, base, handle_level_irq); + ct = gc->chip_types; + + ct->chip.irq_mask = irq_gc_mask_clr_bit; + ct->chip.irq_unmask = irq_gc_mask_set_bit; + ct->regs.mask = SIRFSOC_INT_RISC_MASK0; + + irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST, 0); +} + +static __init void sirfsoc_irq_init(void) +{ + sirfsoc_alloc_gc(sirfsoc_intc_base, 0, 32); + sirfsoc_alloc_gc(sirfsoc_intc_base + 4, 32, SIRFSOC_INTENAL_IRQ_END - 32); + + writel_relaxed(0, sirfsoc_intc_base + SIRFSOC_INT_RISC_LEVEL0); + writel_relaxed(0, sirfsoc_intc_base + SIRFSOC_INT_RISC_LEVEL1); + + writel_relaxed(0, sirfsoc_intc_base + SIRFSOC_INT_RISC_MASK0); + writel_relaxed(0, sirfsoc_intc_base + SIRFSOC_INT_RISC_MASK1); +} + +static struct of_device_id intc_ids[] = { + { .compatible = "sirf,prima2-intc" }, +}; + +void __init sirfsoc_of_irq_init(void) +{ + struct device_node *np; + + np = of_find_matching_node(NULL, intc_ids); + if (!np) + panic("unable to find compatible intc node in dtb\n"); + + sirfsoc_intc_base = of_iomap(np, 0); + if (!sirfsoc_intc_base) + panic("unable to map intc cpu registers\n"); + + of_node_put(np); + + sirfsoc_irq_init(); +} diff --git a/arch/arm/mach-prima2/l2x0.c b/arch/arm/mach-prima2/l2x0.c new file mode 100644 index 00000000000..9cda2057bcf --- /dev/null +++ b/arch/arm/mach-prima2/l2x0.c @@ -0,0 +1,59 @@ +/* + * l2 cache initialization for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/errno.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <asm/hardware/cache-l2x0.h> +#include <mach/memory.h> + +#define L2X0_ADDR_FILTERING_START 0xC00 +#define L2X0_ADDR_FILTERING_END 0xC04 + +static struct of_device_id l2x_ids[] = { + { .compatible = "arm,pl310-cache" }, +}; + +static int __init sirfsoc_of_l2x_init(void) +{ + struct device_node *np; + void __iomem *sirfsoc_l2x_base; + + np = of_find_matching_node(NULL, l2x_ids); + if (!np) + panic("unable to find compatible l2x node in dtb\n"); + + sirfsoc_l2x_base = of_iomap(np, 0); + if (!sirfsoc_l2x_base) + panic("unable to map l2x cpu registers\n"); + + of_node_put(np); + + if (!(readl_relaxed(sirfsoc_l2x_base + L2X0_CTRL) & 1)) { + /* + * set the physical memory windows L2 cache will cover + */ + writel_relaxed(PLAT_PHYS_OFFSET + 1024 * 1024 * 1024, + sirfsoc_l2x_base + L2X0_ADDR_FILTERING_END); + writel_relaxed(PLAT_PHYS_OFFSET | 0x1, + sirfsoc_l2x_base + L2X0_ADDR_FILTERING_START); + + writel_relaxed(0, + sirfsoc_l2x_base + L2X0_TAG_LATENCY_CTRL); + writel_relaxed(0, + sirfsoc_l2x_base + L2X0_DATA_LATENCY_CTRL); + } + l2x0_init((void __iomem *)sirfsoc_l2x_base, 0x00040000, + 0x00000000); + + return 0; +} +early_initcall(sirfsoc_of_l2x_init); diff --git a/arch/arm/mach-prima2/lluart.c b/arch/arm/mach-prima2/lluart.c new file mode 100644 index 00000000000..a89f9b3c8cc --- /dev/null +++ b/arch/arm/mach-prima2/lluart.c @@ -0,0 +1,25 @@ +/* + * Static memory mapping for DEBUG_LL + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/kernel.h> +#include <asm/page.h> +#include <asm/mach/map.h> +#include <mach/map.h> +#include <mach/uart.h> + +void __init sirfsoc_map_lluart(void) +{ + struct map_desc sirfsoc_lluart_map = { + .virtual = SIRFSOC_UART1_VA_BASE, + .pfn = __phys_to_pfn(SIRFSOC_UART1_PA_BASE), + .length = SIRFSOC_UART1_SIZE, + .type = MT_DEVICE, + }; + + iotable_init(&sirfsoc_lluart_map, 1); +} diff --git a/arch/arm/mach-prima2/prima2.c b/arch/arm/mach-prima2/prima2.c new file mode 100644 index 00000000000..f57124bdd14 --- /dev/null +++ b/arch/arm/mach-prima2/prima2.c @@ -0,0 +1,41 @@ +/* + * Defines machines for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include "common.h" + +static struct of_device_id sirfsoc_of_bus_ids[] __initdata = { + { .compatible = "simple-bus", }, + {}, +}; + +void __init sirfsoc_mach_init(void) +{ + of_platform_bus_probe(NULL, sirfsoc_of_bus_ids, NULL); +} + +static const char *prima2cb_dt_match[] __initdata = { + "sirf,prima2-cb", + NULL +}; + +MACHINE_START(PRIMA2_EVB, "prima2cb") + /* Maintainer: Barry Song <baohua.song@csr.com> */ + .boot_params = 0x00000100, + .init_early = sirfsoc_of_clk_init, + .map_io = sirfsoc_map_lluart, + .init_irq = sirfsoc_of_irq_init, + .timer = &sirfsoc_timer, + .init_machine = sirfsoc_mach_init, + .dt_compat = prima2cb_dt_match, +MACHINE_END diff --git a/arch/arm/mach-prima2/rstc.c b/arch/arm/mach-prima2/rstc.c new file mode 100644 index 00000000000..d074786e83d --- /dev/null +++ b/arch/arm/mach-prima2/rstc.c @@ -0,0 +1,69 @@ +/* + * reset controller for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/kernel.h> +#include <linux/mutex.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/of.h> +#include <linux/of_address.h> + +void __iomem *sirfsoc_rstc_base; +static DEFINE_MUTEX(rstc_lock); + +static struct of_device_id rstc_ids[] = { + { .compatible = "sirf,prima2-rstc" }, +}; + +static int __init sirfsoc_of_rstc_init(void) +{ + struct device_node *np; + + np = of_find_matching_node(NULL, rstc_ids); + if (!np) + panic("unable to find compatible rstc node in dtb\n"); + + sirfsoc_rstc_base = of_iomap(np, 0); + if (!sirfsoc_rstc_base) + panic("unable to map rstc cpu registers\n"); + + of_node_put(np); + + return 0; +} +early_initcall(sirfsoc_of_rstc_init); + +int sirfsoc_reset_device(struct device *dev) +{ + const unsigned int *prop = of_get_property(dev->of_node, "reset-bit", NULL); + unsigned int reset_bit; + + if (!prop) + return -ENODEV; + + reset_bit = be32_to_cpup(prop); + + mutex_lock(&rstc_lock); + + /* + * Writing 1 to this bit resets corresponding block. Writing 0 to this + * bit de-asserts reset signal of the corresponding block. + * datasheet doesn't require explicit delay between the set and clear + * of reset bit. it could be shorter if tests pass. + */ + writel(readl(sirfsoc_rstc_base + (reset_bit / 32) * 4) | reset_bit, + sirfsoc_rstc_base + (reset_bit / 32) * 4); + msleep(10); + writel(readl(sirfsoc_rstc_base + (reset_bit / 32) * 4) & ~reset_bit, + sirfsoc_rstc_base + (reset_bit / 32) * 4); + + mutex_unlock(&rstc_lock); + + return 0; +} diff --git a/arch/arm/mach-prima2/timer.c b/arch/arm/mach-prima2/timer.c new file mode 100644 index 00000000000..44027f34a88 --- /dev/null +++ b/arch/arm/mach-prima2/timer.c @@ -0,0 +1,217 @@ +/* + * System timer for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/clockchips.h> +#include <linux/clocksource.h> +#include <linux/bitops.h> +#include <linux/irq.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <mach/map.h> +#include <asm/mach/time.h> + +#define SIRFSOC_TIMER_COUNTER_LO 0x0000 +#define SIRFSOC_TIMER_COUNTER_HI 0x0004 +#define SIRFSOC_TIMER_MATCH_0 0x0008 +#define SIRFSOC_TIMER_MATCH_1 0x000C +#define SIRFSOC_TIMER_MATCH_2 0x0010 +#define SIRFSOC_TIMER_MATCH_3 0x0014 +#define SIRFSOC_TIMER_MATCH_4 0x0018 +#define SIRFSOC_TIMER_MATCH_5 0x001C +#define SIRFSOC_TIMER_STATUS 0x0020 +#define SIRFSOC_TIMER_INT_EN 0x0024 +#define SIRFSOC_TIMER_WATCHDOG_EN 0x0028 +#define SIRFSOC_TIMER_DIV 0x002C +#define SIRFSOC_TIMER_LATCH 0x0030 +#define SIRFSOC_TIMER_LATCHED_LO 0x0034 +#define SIRFSOC_TIMER_LATCHED_HI 0x0038 + +#define SIRFSOC_TIMER_WDT_INDEX 5 + +#define SIRFSOC_TIMER_LATCH_BIT BIT(0) + +static void __iomem *sirfsoc_timer_base; +static void __init sirfsoc_of_timer_map(void); + +/* timer0 interrupt handler */ +static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *ce = dev_id; + + WARN_ON(!(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_STATUS) & BIT(0))); + + /* clear timer0 interrupt */ + writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS); + + ce->event_handler(ce); + + return IRQ_HANDLED; +} + +/* read 64-bit timer counter */ +static cycle_t sirfsoc_timer_read(struct clocksource *cs) +{ + u64 cycles; + + /* latch the 64-bit timer counter */ + writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH); + cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_HI); + cycles = (cycles << 32) | readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO); + + return cycles; +} + +static int sirfsoc_timer_set_next_event(unsigned long delta, + struct clock_event_device *ce) +{ + unsigned long now, next; + + writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH); + now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO); + next = now + delta; + writel_relaxed(next, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0); + writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH); + now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO); + + return next - now > delta ? -ETIME : 0; +} + +static void sirfsoc_timer_set_mode(enum clock_event_mode mode, + struct clock_event_device *ce) +{ + u32 val = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN); + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + WARN_ON(1); + break; + case CLOCK_EVT_MODE_ONESHOT: + writel_relaxed(val | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN); + break; + case CLOCK_EVT_MODE_SHUTDOWN: + writel_relaxed(val & ~BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN); + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_RESUME: + break; + } +} + +static struct clock_event_device sirfsoc_clockevent = { + .name = "sirfsoc_clockevent", + .rating = 200, + .features = CLOCK_EVT_FEAT_ONESHOT, + .set_mode = sirfsoc_timer_set_mode, + .set_next_event = sirfsoc_timer_set_next_event, +}; + +static struct clocksource sirfsoc_clocksource = { + .name = "sirfsoc_clocksource", + .rating = 200, + .mask = CLOCKSOURCE_MASK(64), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .read = sirfsoc_timer_read, +}; + +static struct irqaction sirfsoc_timer_irq = { + .name = "sirfsoc_timer0", + .flags = IRQF_TIMER, + .irq = 0, + .handler = sirfsoc_timer_interrupt, + .dev_id = &sirfsoc_clockevent, +}; + +/* Overwrite weak default sched_clock with more precise one */ +unsigned long long notrace sched_clock(void) +{ + static int is_mapped = 0; + + /* + * sched_clock is called earlier than .init of sys_timer + * if we map timer memory in .init of sys_timer, system + * will panic due to illegal memory access + */ + if(!is_mapped) { + sirfsoc_of_timer_map(); + is_mapped = 1; + } + + return sirfsoc_timer_read(NULL) * (NSEC_PER_SEC / CLOCK_TICK_RATE); +} + +static void __init sirfsoc_clockevent_init(void) +{ + clockevents_calc_mult_shift(&sirfsoc_clockevent, CLOCK_TICK_RATE, 60); + + sirfsoc_clockevent.max_delta_ns = + clockevent_delta2ns(-2, &sirfsoc_clockevent); + sirfsoc_clockevent.min_delta_ns = + clockevent_delta2ns(2, &sirfsoc_clockevent); + + sirfsoc_clockevent.cpumask = cpumask_of(0); + clockevents_register_device(&sirfsoc_clockevent); +} + +/* initialize the kernel jiffy timer source */ +static void __init sirfsoc_timer_init(void) +{ + unsigned long rate; + + /* timer's input clock is io clock */ + struct clk *clk = clk_get_sys("io", NULL); + + BUG_ON(IS_ERR(clk)); + + rate = clk_get_rate(clk); + + BUG_ON(rate < CLOCK_TICK_RATE); + BUG_ON(rate % CLOCK_TICK_RATE); + + writel_relaxed(rate / CLOCK_TICK_RATE / 2 - 1, sirfsoc_timer_base + SIRFSOC_TIMER_DIV); + writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO); + writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI); + writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS); + + BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, CLOCK_TICK_RATE)); + + BUG_ON(setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq)); + + sirfsoc_clockevent_init(); +} + +static struct of_device_id timer_ids[] = { + { .compatible = "sirf,prima2-tick" }, +}; + +static void __init sirfsoc_of_timer_map(void) +{ + struct device_node *np; + const unsigned int *intspec; + + np = of_find_matching_node(NULL, timer_ids); + if (!np) + panic("unable to find compatible timer node in dtb\n"); + sirfsoc_timer_base = of_iomap(np, 0); + if (!sirfsoc_timer_base) + panic("unable to map timer cpu registers\n"); + + /* Get the interrupts property */ + intspec = of_get_property(np, "interrupts", NULL); + BUG_ON(!intspec); + sirfsoc_timer_irq.irq = be32_to_cpup(intspec); + + of_node_put(np); +} + +struct sys_timer sirfsoc_timer = { + .init = sirfsoc_timer_init, +}; |