From 0deac0bf66110fe70507c5dcec661c9e85fa07ca Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 15 Mar 2010 20:36:22 +0000 Subject: ARM: Arrange for COMMON_CLKDEV to select HAVE_CLK If support for COMMON_CLKDEV is enabled, we have CLK support. Signed-off-by: Russell King --- arch/arm/common/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'arch/arm/common') diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig index 4efbb9df044..b64bb78d784 100644 --- a/arch/arm/common/Kconfig +++ b/arch/arm/common/Kconfig @@ -40,3 +40,4 @@ config SHARP_SCOOP config COMMON_CLKDEV bool + select HAVE_CLK -- cgit v1.2.3-70-g09d2 From e4bf5becccf4685754c4d8e4485bb2ff7d28147f Mon Sep 17 00:00:00 2001 From: viresh kumar Date: Tue, 9 Mar 2010 11:54:30 +0100 Subject: ARM: 5979/1: CLKDEV: return from clk_find as soon as we get maching clk clk_find function looks for clock with matching dev_id and con_id. We should return from clk_find as soon as we get a clock with matching dev_id and con_id. This can be done by comparing variable "match" with 3. Acked-by: Uwe Kleine-Konig Signed-off-by: Viresh Kumar Signed-off-by: Russell King --- arch/arm/common/clkdev.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'arch/arm/common') diff --git a/arch/arm/common/clkdev.c b/arch/arm/common/clkdev.c index 6416d5b5020..4f8390dd6ca 100644 --- a/arch/arm/common/clkdev.c +++ b/arch/arm/common/clkdev.c @@ -52,12 +52,13 @@ static struct clk *clk_find(const char *dev_id, const char *con_id) continue; match += 1; } - if (match == 0) - continue; if (match > best) { clk = p->clk; - best = match; + if (match != 3) + best = match; + else + break; } } return clk; -- cgit v1.2.3-70-g09d2 From b0c4c898833250ed143e615c32ea1be0e2155fdc Mon Sep 17 00:00:00 2001 From: Hartley Sweeten Date: Fri, 2 Apr 2010 18:04:47 +0100 Subject: ARM: 6025/1: vic: factor out common init code This factors out the common initialization code for the two vic vendors into easier maintainable functions. Tested-by: Linus Walleij Tested-by: Alessandro Rubini Cc: Andrea Gallo Cc: Ben Dooks Signed-off-by: H Hartley Sweeten Signed-off-by: Russell King --- arch/arm/common/vic.c | 107 ++++++++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 55 deletions(-) (limited to 'arch/arm/common') diff --git a/arch/arm/common/vic.c b/arch/arm/common/vic.c index 1cf999ade4b..ba65f6eedca 100644 --- a/arch/arm/common/vic.c +++ b/arch/arm/common/vic.c @@ -266,13 +266,53 @@ static int vic_set_wake(unsigned int irq, unsigned int on) #endif /* CONFIG_PM */ static struct irq_chip vic_chip = { - .name = "VIC", - .ack = vic_ack_irq, - .mask = vic_mask_irq, - .unmask = vic_unmask_irq, - .set_wake = vic_set_wake, + .name = "VIC", + .ack = vic_ack_irq, + .mask = vic_mask_irq, + .unmask = vic_unmask_irq, + .set_wake = vic_set_wake, }; +static void __init vic_disable(void __iomem *base) +{ + writel(0, base + VIC_INT_SELECT); + writel(0, base + VIC_INT_ENABLE); + writel(~0, base + VIC_INT_ENABLE_CLEAR); + writel(0, base + VIC_IRQ_STATUS); + writel(0, base + VIC_ITCR); + writel(~0, base + VIC_INT_SOFT_CLEAR); +} + +static void __init vic_clear_interrupts(void __iomem *base) +{ + unsigned int i; + + writel(0, base + VIC_PL190_VECT_ADDR); + for (i = 0; i < 19; i++) { + unsigned int value; + + value = readl(base + VIC_PL190_VECT_ADDR); + writel(value, base + VIC_PL190_VECT_ADDR); + } +} + +static void __init vic_set_irq_sources(void __iomem *base, + unsigned int irq_start, u32 vic_sources) +{ + unsigned int i; + + for (i = 0; i < 32; i++) { + if (vic_sources & (1 << i)) { + unsigned int irq = irq_start + i; + + set_irq_chip(irq, &vic_chip); + set_irq_chip_data(irq, base); + set_irq_handler(irq, handle_level_irq); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + } + } +} + /* * The PL190 cell from ARM has been modified by ST to handle 64 interrupts. * The original cell has 32 interrupts, while the modified one has 64, @@ -287,13 +327,7 @@ static void __init vic_init_st(void __iomem *base, unsigned int irq_start, int vic_2nd_block = ((unsigned long)base & ~PAGE_MASK) != 0; /* Disable all interrupts initially. */ - - writel(0, base + VIC_INT_SELECT); - writel(0, base + VIC_INT_ENABLE); - writel(~0, base + VIC_INT_ENABLE_CLEAR); - writel(0, base + VIC_IRQ_STATUS); - writel(0, base + VIC_ITCR); - writel(~0, base + VIC_INT_SOFT_CLEAR); + vic_disable(base); /* * Make sure we clear all existing interrupts. The vector registers @@ -302,13 +336,8 @@ static void __init vic_init_st(void __iomem *base, unsigned int irq_start, * the second base address, which is 0x20 in the page */ if (vic_2nd_block) { - writel(0, base + VIC_PL190_VECT_ADDR); - for (i = 0; i < 19; i++) { - unsigned int value; + vic_clear_interrupts(base); - value = readl(base + VIC_PL190_VECT_ADDR); - writel(value, base + VIC_PL190_VECT_ADDR); - } /* ST has 16 vectors as well, but we don't enable them by now */ for (i = 0; i < 16; i++) { void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4); @@ -318,16 +347,7 @@ static void __init vic_init_st(void __iomem *base, unsigned int irq_start, writel(32, base + VIC_PL190_DEF_VECT_ADDR); } - for (i = 0; i < 32; i++) { - if (vic_sources & (1 << i)) { - unsigned int irq = irq_start + i; - - set_irq_chip(irq, &vic_chip); - set_irq_chip_data(irq, base); - set_irq_handler(irq, handle_level_irq); - set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); - } - } + vic_set_irq_sources(base, irq_start, vic_sources); } /** @@ -365,37 +385,14 @@ void __init vic_init(void __iomem *base, unsigned int irq_start, } /* Disable all interrupts initially. */ + vic_disable(base); - writel(0, base + VIC_INT_SELECT); - writel(0, base + VIC_INT_ENABLE); - writel(~0, base + VIC_INT_ENABLE_CLEAR); - writel(0, base + VIC_IRQ_STATUS); - writel(0, base + VIC_ITCR); - writel(~0, base + VIC_INT_SOFT_CLEAR); - - /* - * Make sure we clear all existing interrupts - */ - writel(0, base + VIC_PL190_VECT_ADDR); - for (i = 0; i < 19; i++) { - unsigned int value; - - value = readl(base + VIC_PL190_VECT_ADDR); - writel(value, base + VIC_PL190_VECT_ADDR); - } + /* Make sure we clear all existing interrupts */ + vic_clear_interrupts(base); vic_init2(base); - for (i = 0; i < 32; i++) { - if (vic_sources & (1 << i)) { - unsigned int irq = irq_start + i; - - set_irq_chip(irq, &vic_chip); - set_irq_chip_data(irq, base); - set_irq_handler(irq, handle_level_irq); - set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); - } - } + vic_set_irq_sources(base, irq_start, vic_sources); vic_pm_register(base, irq_start, resume_sources); } -- cgit v1.2.3-70-g09d2 From 39c0cb02db5b8fdfac76d506b7a008b70bc960e9 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 16 Jan 2010 16:27:28 +0000 Subject: ARM: ICST: merge common ICST VCO structures The structures for the ICST307 and ICST525 VCO devices are identical, so merge them together. Signed-off-by: Russell King --- arch/arm/common/icst307.c | 14 +++++------ arch/arm/common/icst525.c | 14 +++++------ arch/arm/include/asm/hardware/icst.h | 32 ++++++++++++++++++++++++++ arch/arm/include/asm/hardware/icst307.h | 21 ++++------------- arch/arm/include/asm/hardware/icst525.h | 21 ++++------------- arch/arm/mach-integrator/clock.c | 5 ++-- arch/arm/mach-integrator/cpu.c | 10 ++++---- arch/arm/mach-integrator/impd1.c | 4 ++-- arch/arm/mach-integrator/include/mach/clkdev.h | 6 ++--- arch/arm/mach-integrator/integrator_cp.c | 4 ++-- arch/arm/mach-realview/clock.c | 4 ++-- arch/arm/mach-realview/clock.h | 7 +++--- arch/arm/mach-realview/core.c | 4 ++-- arch/arm/mach-versatile/clock.c | 4 ++-- arch/arm/mach-versatile/clock.h | 7 +++--- arch/arm/mach-versatile/core.c | 4 ++-- 16 files changed, 85 insertions(+), 76 deletions(-) create mode 100644 arch/arm/include/asm/hardware/icst.h (limited to 'arch/arm/common') diff --git a/arch/arm/common/icst307.c b/arch/arm/common/icst307.c index 6d094c15754..2eebd960c01 100644 --- a/arch/arm/common/icst307.c +++ b/arch/arm/common/icst307.c @@ -24,7 +24,7 @@ */ static unsigned char s2div[8] = { 10, 2, 8, 4, 5, 7, 3, 6 }; -unsigned long icst307_khz(const struct icst307_params *p, struct icst307_vco vco) +unsigned long icst307_khz(const struct icst_params *p, struct icst_vco vco) { return p->ref * 2 * (vco.v + 8) / ((vco.r + 2) * s2div[vco.s]); } @@ -36,10 +36,10 @@ EXPORT_SYMBOL(icst307_khz); */ static unsigned char idx2s[8] = { 1, 6, 3, 4, 7, 5, 2, 0 }; -struct icst307_vco -icst307_khz_to_vco(const struct icst307_params *p, unsigned long freq) +struct icst_vco +icst307_khz_to_vco(const struct icst_params *p, unsigned long freq) { - struct icst307_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max }; + struct icst_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max }; unsigned long f; unsigned int i = 0, rd, best = (unsigned int)-1; @@ -96,10 +96,10 @@ icst307_khz_to_vco(const struct icst307_params *p, unsigned long freq) EXPORT_SYMBOL(icst307_khz_to_vco); -struct icst307_vco -icst307_ps_to_vco(const struct icst307_params *p, unsigned long period) +struct icst_vco +icst307_ps_to_vco(const struct icst_params *p, unsigned long period) { - struct icst307_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max }; + struct icst_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max }; unsigned long f, ps; unsigned int i = 0, rd, best = (unsigned int)-1; diff --git a/arch/arm/common/icst525.c b/arch/arm/common/icst525.c index 3d377c5bdef..fd5c2e7ed17 100644 --- a/arch/arm/common/icst525.c +++ b/arch/arm/common/icst525.c @@ -21,7 +21,7 @@ */ static unsigned char s2div[8] = { 10, 2, 8, 4, 5, 7, 9, 6 }; -unsigned long icst525_khz(const struct icst525_params *p, struct icst525_vco vco) +unsigned long icst525_khz(const struct icst_params *p, struct icst_vco vco) { return p->ref * 2 * (vco.v + 8) / ((vco.r + 2) * s2div[vco.s]); } @@ -33,10 +33,10 @@ EXPORT_SYMBOL(icst525_khz); */ static unsigned char idx2s[] = { 1, 3, 4, 7, 5, 2, 6, 0 }; -struct icst525_vco -icst525_khz_to_vco(const struct icst525_params *p, unsigned long freq) +struct icst_vco +icst525_khz_to_vco(const struct icst_params *p, unsigned long freq) { - struct icst525_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max }; + struct icst_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max }; unsigned long f; unsigned int i = 0, rd, best = (unsigned int)-1; @@ -94,10 +94,10 @@ icst525_khz_to_vco(const struct icst525_params *p, unsigned long freq) EXPORT_SYMBOL(icst525_khz_to_vco); -struct icst525_vco -icst525_ps_to_vco(const struct icst525_params *p, unsigned long period) +struct icst_vco +icst525_ps_to_vco(const struct icst_params *p, unsigned long period) { - struct icst525_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max }; + struct icst_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max }; unsigned long f, ps; unsigned int i = 0, rd, best = (unsigned int)-1; diff --git a/arch/arm/include/asm/hardware/icst.h b/arch/arm/include/asm/hardware/icst.h new file mode 100644 index 00000000000..65b1edd4452 --- /dev/null +++ b/arch/arm/include/asm/hardware/icst.h @@ -0,0 +1,32 @@ +/* + * arch/arm/include/asm/hardware/icst.h + * + * Copyright (C) 2003 Deep Blue Solutions, 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. + * + * Support functions for calculating clocks/divisors for the ICST + * clock generators. See http://www.icst.com/ for more information + * on these devices. + */ +#ifndef ASMARM_HARDWARE_ICST_H +#define ASMARM_HARDWARE_ICST_H + +struct icst_params { + unsigned long ref; + unsigned long vco_max; /* inclusive */ + unsigned short vd_min; /* inclusive */ + unsigned short vd_max; /* inclusive */ + unsigned char rd_min; /* inclusive */ + unsigned char rd_max; /* inclusive */ +}; + +struct icst_vco { + unsigned short v; + unsigned char r; + unsigned char s; +}; + +#endif diff --git a/arch/arm/include/asm/hardware/icst307.h b/arch/arm/include/asm/hardware/icst307.h index 554f128a104..85932e902e9 100644 --- a/arch/arm/include/asm/hardware/icst307.h +++ b/arch/arm/include/asm/hardware/icst307.h @@ -16,23 +16,10 @@ #ifndef ASMARM_HARDWARE_ICST307_H #define ASMARM_HARDWARE_ICST307_H -struct icst307_params { - unsigned long ref; - unsigned long vco_max; /* inclusive */ - unsigned short vd_min; /* inclusive */ - unsigned short vd_max; /* inclusive */ - unsigned char rd_min; /* inclusive */ - unsigned char rd_max; /* inclusive */ -}; +#include -struct icst307_vco { - unsigned short v; - unsigned char r; - unsigned char s; -}; - -unsigned long icst307_khz(const struct icst307_params *p, struct icst307_vco vco); -struct icst307_vco icst307_khz_to_vco(const struct icst307_params *p, unsigned long freq); -struct icst307_vco icst307_ps_to_vco(const struct icst307_params *p, unsigned long period); +unsigned long icst307_khz(const struct icst_params *p, struct icst_vco vco); +struct icst_vco icst307_khz_to_vco(const struct icst_params *p, unsigned long freq); +struct icst_vco icst307_ps_to_vco(const struct icst_params *p, unsigned long period); #endif diff --git a/arch/arm/include/asm/hardware/icst525.h b/arch/arm/include/asm/hardware/icst525.h index 58f0dc43e2e..170deb2b605 100644 --- a/arch/arm/include/asm/hardware/icst525.h +++ b/arch/arm/include/asm/hardware/icst525.h @@ -14,23 +14,10 @@ #ifndef ASMARM_HARDWARE_ICST525_H #define ASMARM_HARDWARE_ICST525_H -struct icst525_params { - unsigned long ref; - unsigned long vco_max; /* inclusive */ - unsigned short vd_min; /* inclusive */ - unsigned short vd_max; /* inclusive */ - unsigned char rd_min; /* inclusive */ - unsigned char rd_max; /* inclusive */ -}; +#include -struct icst525_vco { - unsigned short v; - unsigned char r; - unsigned char s; -}; - -unsigned long icst525_khz(const struct icst525_params *p, struct icst525_vco vco); -struct icst525_vco icst525_khz_to_vco(const struct icst525_params *p, unsigned long freq); -struct icst525_vco icst525_ps_to_vco(const struct icst525_params *p, unsigned long period); +unsigned long icst525_khz(const struct icst_params *p, struct icst_vco vco); +struct icst_vco icst525_khz_to_vco(const struct icst_params *p, unsigned long freq); +struct icst_vco icst525_ps_to_vco(const struct icst_params *p, unsigned long period); #endif diff --git a/arch/arm/mach-integrator/clock.c b/arch/arm/mach-integrator/clock.c index 989ecf5f5c4..bb70b64a656 100644 --- a/arch/arm/mach-integrator/clock.c +++ b/arch/arm/mach-integrator/clock.c @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -36,7 +37,7 @@ EXPORT_SYMBOL(clk_get_rate); long clk_round_rate(struct clk *clk, unsigned long rate) { - struct icst525_vco vco; + struct icst_vco vco; vco = icst525_khz_to_vco(clk->params, rate / 1000); return icst525_khz(clk->params, vco) * 1000; } @@ -47,7 +48,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate) int ret = -EIO; if (clk->setvco) { - struct icst525_vco vco; + struct icst_vco vco; vco = icst525_khz_to_vco(clk->params, rate / 1000); clk->rate = icst525_khz(clk->params, vco) * 1000; diff --git a/arch/arm/mach-integrator/cpu.c b/arch/arm/mach-integrator/cpu.c index 7f1b73b776a..c4e5debeb48 100644 --- a/arch/arm/mach-integrator/cpu.c +++ b/arch/arm/mach-integrator/cpu.c @@ -31,7 +31,7 @@ static struct cpufreq_driver integrator_driver; #define CM_STAT IO_ADDRESS(INTEGRATOR_HDR_STAT) #define CM_LOCK IO_ADDRESS(INTEGRATOR_HDR_LOCK) -static const struct icst525_params lclk_params = { +static const struct icst_params lclk_params = { .ref = 24000, .vco_max = 320000, .vd_min = 8, @@ -40,7 +40,7 @@ static const struct icst525_params lclk_params = { .rd_max = 24, }; -static const struct icst525_params cclk_params = { +static const struct icst_params cclk_params = { .ref = 24000, .vco_max = 320000, .vd_min = 12, @@ -54,7 +54,7 @@ static const struct icst525_params cclk_params = { */ static int integrator_verify_policy(struct cpufreq_policy *policy) { - struct icst525_vco vco; + struct icst_vco vco; cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, @@ -80,7 +80,7 @@ static int integrator_set_target(struct cpufreq_policy *policy, { cpumask_t cpus_allowed; int cpu = policy->cpu; - struct icst525_vco vco; + struct icst_vco vco; struct cpufreq_freqs freqs; u_int cm_osc; @@ -156,7 +156,7 @@ static unsigned int integrator_get(unsigned int cpu) cpumask_t cpus_allowed; unsigned int current_freq; u_int cm_osc; - struct icst525_vco vco; + struct icst_vco vco; cpus_allowed = current->cpus_allowed; diff --git a/arch/arm/mach-integrator/impd1.c b/arch/arm/mach-integrator/impd1.c index 0058c937719..dfb961b67af 100644 --- a/arch/arm/mach-integrator/impd1.c +++ b/arch/arm/mach-integrator/impd1.c @@ -40,7 +40,7 @@ struct impd1_module { struct clk_lookup *clks[3]; }; -static const struct icst525_params impd1_vco_params = { +static const struct icst_params impd1_vco_params = { .ref = 24000, /* 24 MHz */ .vco_max = 200000, /* 200 MHz */ .vd_min = 12, @@ -49,7 +49,7 @@ static const struct icst525_params impd1_vco_params = { .rd_max = 120, }; -static void impd1_setvco(struct clk *clk, struct icst525_vco vco) +static void impd1_setvco(struct clk *clk, struct icst_vco vco) { struct impd1_module *impd1 = clk->data; int vconr = clk - impd1->vcos; diff --git a/arch/arm/mach-integrator/include/mach/clkdev.h b/arch/arm/mach-integrator/include/mach/clkdev.h index 9293e410832..89ea9380399 100644 --- a/arch/arm/mach-integrator/include/mach/clkdev.h +++ b/arch/arm/mach-integrator/include/mach/clkdev.h @@ -2,14 +2,14 @@ #define __ASM_MACH_CLKDEV_H #include -#include +#include struct clk { unsigned long rate; struct module *owner; - const struct icst525_params *params; + const struct icst_params *params; void *data; - void (*setvco)(struct clk *, struct icst525_vco vco); + void (*setvco)(struct clk *, struct icst_vco vco); }; static inline int __clk_get(struct clk *clk) diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c index c0161df9d9a..15bfbe2e1df 100644 --- a/arch/arm/mach-integrator/integrator_cp.c +++ b/arch/arm/mach-integrator/integrator_cp.c @@ -268,7 +268,7 @@ static void __init intcp_init_irq(void) #define CM_LOCK IO_ADDRESS(INTEGRATOR_HDR_LOCK) #define CM_AUXOSC IO_ADDRESS(INTEGRATOR_HDR_BASE + 0x1c) -static const struct icst525_params cp_auxvco_params = { +static const struct icst_params cp_auxvco_params = { .ref = 24000, .vco_max = 320000, .vd_min = 8, @@ -277,7 +277,7 @@ static const struct icst525_params cp_auxvco_params = { .rd_max = 65, }; -static void cp_auxvco_set(struct clk *clk, struct icst525_vco vco) +static void cp_auxvco_set(struct clk *clk, struct icst_vco vco) { u32 val; diff --git a/arch/arm/mach-realview/clock.c b/arch/arm/mach-realview/clock.c index a7043115de7..83646579bc4 100644 --- a/arch/arm/mach-realview/clock.c +++ b/arch/arm/mach-realview/clock.c @@ -41,7 +41,7 @@ EXPORT_SYMBOL(clk_get_rate); long clk_round_rate(struct clk *clk, unsigned long rate) { - struct icst307_vco vco; + struct icst_vco vco; vco = icst307_khz_to_vco(clk->params, rate / 1000); return icst307_khz(clk->params, vco) * 1000; } @@ -52,7 +52,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate) int ret = -EIO; if (clk->setvco) { - struct icst307_vco vco; + struct icst_vco vco; vco = icst307_khz_to_vco(clk->params, rate / 1000); clk->rate = icst307_khz(clk->params, vco) * 1000; diff --git a/arch/arm/mach-realview/clock.h b/arch/arm/mach-realview/clock.h index ebbb0f06b60..fa64c854258 100644 --- a/arch/arm/mach-realview/clock.h +++ b/arch/arm/mach-realview/clock.h @@ -8,12 +8,13 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include + struct module; -struct icst307_params; struct clk { unsigned long rate; - const struct icst307_params *params; + const struct icst_params *params; void *data; - void (*setvco)(struct clk *, struct icst307_vco vco); + void (*setvco)(struct clk *, struct icst_vco vco); }; diff --git a/arch/arm/mach-realview/core.c b/arch/arm/mach-realview/core.c index 90bd4ef71b2..ac504745fed 100644 --- a/arch/arm/mach-realview/core.c +++ b/arch/arm/mach-realview/core.c @@ -273,7 +273,7 @@ struct mmci_platform_data realview_mmc1_plat_data = { /* * Clock handling */ -static const struct icst307_params realview_oscvco_params = { +static const struct icst_params realview_oscvco_params = { .ref = 24000, .vco_max = 200000, .vd_min = 4 + 8, @@ -282,7 +282,7 @@ static const struct icst307_params realview_oscvco_params = { .rd_max = 127 + 2, }; -static void realview_oscvco_set(struct clk *clk, struct icst307_vco vco) +static void realview_oscvco_set(struct clk *clk, struct icst_vco vco) { void __iomem *sys_lock = __io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_LOCK_OFFSET; void __iomem *sys_osc; diff --git a/arch/arm/mach-versatile/clock.c b/arch/arm/mach-versatile/clock.c index c50a44ea7ee..530e16aa7ad 100644 --- a/arch/arm/mach-versatile/clock.c +++ b/arch/arm/mach-versatile/clock.c @@ -42,7 +42,7 @@ EXPORT_SYMBOL(clk_get_rate); long clk_round_rate(struct clk *clk, unsigned long rate) { - struct icst307_vco vco; + struct icst_vco vco; vco = icst307_khz_to_vco(clk->params, rate / 1000); return icst307_khz(clk->params, vco) * 1000; } @@ -53,7 +53,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate) int ret = -EIO; if (clk->setvco) { - struct icst307_vco vco; + struct icst_vco vco; vco = icst307_khz_to_vco(clk->params, rate / 1000); clk->rate = icst307_khz(clk->params, vco) * 1000; diff --git a/arch/arm/mach-versatile/clock.h b/arch/arm/mach-versatile/clock.h index 03468fdc3e5..aed7e22d102 100644 --- a/arch/arm/mach-versatile/clock.h +++ b/arch/arm/mach-versatile/clock.h @@ -8,13 +8,14 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include + struct module; -struct icst307_params; struct clk { unsigned long rate; - const struct icst307_params *params; + const struct icst_params *params; u32 oscoff; void *data; - void (*setvco)(struct clk *, struct icst307_vco vco); + void (*setvco)(struct clk *, struct icst_vco vco); }; diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c index b77bc407b77..ded71343553 100644 --- a/arch/arm/mach-versatile/core.c +++ b/arch/arm/mach-versatile/core.c @@ -379,7 +379,7 @@ static struct mmci_platform_data mmc0_plat_data = { /* * Clock handling */ -static const struct icst307_params versatile_oscvco_params = { +static const struct icst_params versatile_oscvco_params = { .ref = 24000, .vco_max = 200000, .vd_min = 4 + 8, @@ -388,7 +388,7 @@ static const struct icst307_params versatile_oscvco_params = { .rd_max = 127 + 2, }; -static void versatile_oscvco_set(struct clk *clk, struct icst307_vco vco) +static void versatile_oscvco_set(struct clk *clk, struct icst_vco vco) { void __iomem *sys = __io_address(VERSATILE_SYS_BASE); void __iomem *sys_lock = sys + VERSATILE_SYS_LOCK_OFFSET; -- cgit v1.2.3-70-g09d2 From 643761ac43dfe4bea1195e966fc65a9403cdcb9f Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 16 Jan 2010 17:15:07 +0000 Subject: ARM: ICST: icst.*_ps_to_vco() functions are unused, remove them These functions were originally implemented for the CLCD driver before we had clk API support. Since the CLCD driver does not use these anymore, we can remove them. Signed-off-by: Russell King --- arch/arm/common/icst307.c | 64 -------------------------------- arch/arm/common/icst525.c | 65 --------------------------------- arch/arm/include/asm/hardware/icst307.h | 1 - arch/arm/include/asm/hardware/icst525.h | 1 - 4 files changed, 131 deletions(-) (limited to 'arch/arm/common') diff --git a/arch/arm/common/icst307.c b/arch/arm/common/icst307.c index 2eebd960c01..f78f5b5880c 100644 --- a/arch/arm/common/icst307.c +++ b/arch/arm/common/icst307.c @@ -95,67 +95,3 @@ icst307_khz_to_vco(const struct icst_params *p, unsigned long freq) } EXPORT_SYMBOL(icst307_khz_to_vco); - -struct icst_vco -icst307_ps_to_vco(const struct icst_params *p, unsigned long period) -{ - struct icst_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max }; - unsigned long f, ps; - unsigned int i = 0, rd, best = (unsigned int)-1; - - ps = 1000000000UL / p->vco_max; - - /* - * First, find the PLL output divisor such - * that the PLL output is within spec. - */ - do { - f = period / s2div[idx2s[i]]; - - /* - * f must be between 6MHz and 200MHz (3.3 or 5V) - */ - if (f >= ps && f < 1000000000UL / 6000 + 1) - break; - } while (i < ARRAY_SIZE(idx2s)); - - if (i >= ARRAY_SIZE(idx2s)) - return vco; - - vco.s = idx2s[i]; - - ps = 500000000UL / p->ref; - - /* - * Now find the closest divisor combination - * which gives a PLL output of 'f'. - */ - for (rd = p->rd_min; rd <= p->rd_max; rd++) { - unsigned long f_in_div, f_pll; - unsigned int vd; - int f_diff; - - f_in_div = ps * rd; - - vd = (f_in_div + f / 2) / f; - if (vd < p->vd_min || vd > p->vd_max) - continue; - - f_pll = (f_in_div + vd / 2) / vd; - f_diff = f_pll - f; - if (f_diff < 0) - f_diff = -f_diff; - - if ((unsigned)f_diff < best) { - vco.v = vd - 8; - vco.r = rd - 2; - if (f_diff == 0) - break; - best = f_diff; - } - } - - return vco; -} - -EXPORT_SYMBOL(icst307_ps_to_vco); diff --git a/arch/arm/common/icst525.c b/arch/arm/common/icst525.c index fd5c2e7ed17..eadf983ee43 100644 --- a/arch/arm/common/icst525.c +++ b/arch/arm/common/icst525.c @@ -93,68 +93,3 @@ icst525_khz_to_vco(const struct icst_params *p, unsigned long freq) } EXPORT_SYMBOL(icst525_khz_to_vco); - -struct icst_vco -icst525_ps_to_vco(const struct icst_params *p, unsigned long period) -{ - struct icst_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max }; - unsigned long f, ps; - unsigned int i = 0, rd, best = (unsigned int)-1; - - ps = 1000000000UL / p->vco_max; - - /* - * First, find the PLL output divisor such - * that the PLL output is within spec. - */ - do { - f = period / s2div[idx2s[i]]; - - /* - * f must be between 10MHz and - * 320MHz (5V) or 200MHz (3V) - */ - if (f >= ps && f < 100000) - break; - } while (i < ARRAY_SIZE(idx2s)); - - if (i >= ARRAY_SIZE(idx2s)) - return vco; - - vco.s = idx2s[i]; - - ps = 500000000UL / p->ref; - - /* - * Now find the closest divisor combination - * which gives a PLL output of 'f'. - */ - for (rd = p->rd_min; rd <= p->rd_max; rd++) { - unsigned long f_in_div, f_pll; - unsigned int vd; - int f_diff; - - f_in_div = ps * rd; - - vd = (f_in_div + f / 2) / f; - if (vd < p->vd_min || vd > p->vd_max) - continue; - - f_pll = (f_in_div + vd / 2) / vd; - f_diff = f_pll - f; - if (f_diff < 0) - f_diff = -f_diff; - - if ((unsigned)f_diff < best) { - vco.v = vd - 8; - vco.r = rd - 2; - if (f_diff == 0) - break; - best = f_diff; - } - } - - return vco; -} - -EXPORT_SYMBOL(icst525_ps_to_vco); diff --git a/arch/arm/include/asm/hardware/icst307.h b/arch/arm/include/asm/hardware/icst307.h index 85932e902e9..8d0820a654d 100644 --- a/arch/arm/include/asm/hardware/icst307.h +++ b/arch/arm/include/asm/hardware/icst307.h @@ -20,6 +20,5 @@ unsigned long icst307_khz(const struct icst_params *p, struct icst_vco vco); struct icst_vco icst307_khz_to_vco(const struct icst_params *p, unsigned long freq); -struct icst_vco icst307_ps_to_vco(const struct icst_params *p, unsigned long period); #endif diff --git a/arch/arm/include/asm/hardware/icst525.h b/arch/arm/include/asm/hardware/icst525.h index 170deb2b605..42a274e59fa 100644 --- a/arch/arm/include/asm/hardware/icst525.h +++ b/arch/arm/include/asm/hardware/icst525.h @@ -18,6 +18,5 @@ unsigned long icst525_khz(const struct icst_params *p, struct icst_vco vco); struct icst_vco icst525_khz_to_vco(const struct icst_params *p, unsigned long freq); -struct icst_vco icst525_ps_to_vco(const struct icst_params *p, unsigned long period); #endif -- cgit v1.2.3-70-g09d2 From 4de2edbd15ff70c77e1d018611d06f7801fc2b72 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 16 Jan 2010 18:08:47 +0000 Subject: ARM: ICST: provide definitions for max/min VCO frequencies Signed-off-by: Russell King --- arch/arm/common/icst307.c | 2 +- arch/arm/common/icst525.c | 2 +- arch/arm/include/asm/hardware/icst307.h | 7 +++++++ arch/arm/include/asm/hardware/icst525.h | 8 ++++++++ arch/arm/mach-integrator/cpu.c | 4 ++-- arch/arm/mach-integrator/impd1.c | 2 +- arch/arm/mach-integrator/integrator_cp.c | 2 +- arch/arm/mach-realview/core.c | 2 +- arch/arm/mach-versatile/core.c | 2 +- 9 files changed, 23 insertions(+), 8 deletions(-) (limited to 'arch/arm/common') diff --git a/arch/arm/common/icst307.c b/arch/arm/common/icst307.c index f78f5b5880c..f6063c95e25 100644 --- a/arch/arm/common/icst307.c +++ b/arch/arm/common/icst307.c @@ -53,7 +53,7 @@ icst307_khz_to_vco(const struct icst_params *p, unsigned long freq) /* * f must be between 6MHz and 200MHz (3.3 or 5V) */ - if (f > 6000 && f <= p->vco_max) + if (f > ICST307_VCO_MIN && f <= p->vco_max) break; } while (i < ARRAY_SIZE(idx2s)); diff --git a/arch/arm/common/icst525.c b/arch/arm/common/icst525.c index eadf983ee43..34dc2e1b9ef 100644 --- a/arch/arm/common/icst525.c +++ b/arch/arm/common/icst525.c @@ -51,7 +51,7 @@ icst525_khz_to_vco(const struct icst_params *p, unsigned long freq) * f must be between 10MHz and * 320MHz (5V) or 200MHz (3V) */ - if (f > 10000 && f <= p->vco_max) + if (f > ICST525_VCO_MIN && f <= p->vco_max) break; } while (i < ARRAY_SIZE(idx2s)); diff --git a/arch/arm/include/asm/hardware/icst307.h b/arch/arm/include/asm/hardware/icst307.h index 8d0820a654d..0c4e37e3fde 100644 --- a/arch/arm/include/asm/hardware/icst307.h +++ b/arch/arm/include/asm/hardware/icst307.h @@ -21,4 +21,11 @@ unsigned long icst307_khz(const struct icst_params *p, struct icst_vco vco); struct icst_vco icst307_khz_to_vco(const struct icst_params *p, unsigned long freq); +/* + * ICST307 VCO frequency must be between 6MHz and 200MHz (3.3 or 5V). + * This frequency is pre-output divider. + */ +#define ICST307_VCO_MIN 6000 +#define ICST307_VCO_MAX 200000 + #endif diff --git a/arch/arm/include/asm/hardware/icst525.h b/arch/arm/include/asm/hardware/icst525.h index 42a274e59fa..3b72c132e24 100644 --- a/arch/arm/include/asm/hardware/icst525.h +++ b/arch/arm/include/asm/hardware/icst525.h @@ -19,4 +19,12 @@ unsigned long icst525_khz(const struct icst_params *p, struct icst_vco vco); struct icst_vco icst525_khz_to_vco(const struct icst_params *p, unsigned long freq); +/* + * ICST525 VCO frequency must be between 10MHz and 200MHz (3V) or 320MHz (5V). + * This frequency is pre-output divider. + */ +#define ICST525_VCO_MIN 10000 +#define ICST525_VCO_MAX_3V 200000 +#define ICST525_VCO_MAX_5V 320000 + #endif diff --git a/arch/arm/mach-integrator/cpu.c b/arch/arm/mach-integrator/cpu.c index c4e5debeb48..1671b4a363b 100644 --- a/arch/arm/mach-integrator/cpu.c +++ b/arch/arm/mach-integrator/cpu.c @@ -33,7 +33,7 @@ static struct cpufreq_driver integrator_driver; static const struct icst_params lclk_params = { .ref = 24000, - .vco_max = 320000, + .vco_max = ICST525_VCO_MAX_5V, .vd_min = 8, .vd_max = 132, .rd_min = 24, @@ -42,7 +42,7 @@ static const struct icst_params lclk_params = { static const struct icst_params cclk_params = { .ref = 24000, - .vco_max = 320000, + .vco_max = ICST525_VCO_MAX_5V, .vd_min = 12, .vd_max = 160, .rd_min = 24, diff --git a/arch/arm/mach-integrator/impd1.c b/arch/arm/mach-integrator/impd1.c index dfb961b67af..5aca7ebea3c 100644 --- a/arch/arm/mach-integrator/impd1.c +++ b/arch/arm/mach-integrator/impd1.c @@ -42,7 +42,7 @@ struct impd1_module { static const struct icst_params impd1_vco_params = { .ref = 24000, /* 24 MHz */ - .vco_max = 200000, /* 200 MHz */ + .vco_max = ICST525_VCO_MAX_3V, .vd_min = 12, .vd_max = 519, .rd_min = 3, diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c index 15bfbe2e1df..27f95106b47 100644 --- a/arch/arm/mach-integrator/integrator_cp.c +++ b/arch/arm/mach-integrator/integrator_cp.c @@ -270,7 +270,7 @@ static void __init intcp_init_irq(void) static const struct icst_params cp_auxvco_params = { .ref = 24000, - .vco_max = 320000, + .vco_max = ICST525_VCO_MAX_5V, .vd_min = 8, .vd_max = 263, .rd_min = 3, diff --git a/arch/arm/mach-realview/core.c b/arch/arm/mach-realview/core.c index ac504745fed..5a850f0dc36 100644 --- a/arch/arm/mach-realview/core.c +++ b/arch/arm/mach-realview/core.c @@ -275,7 +275,7 @@ struct mmci_platform_data realview_mmc1_plat_data = { */ static const struct icst_params realview_oscvco_params = { .ref = 24000, - .vco_max = 200000, + .vco_max = ICST307_VCO_MAX, .vd_min = 4 + 8, .vd_max = 511 + 8, .rd_min = 1 + 2, diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c index ded71343553..f8ed561ec69 100644 --- a/arch/arm/mach-versatile/core.c +++ b/arch/arm/mach-versatile/core.c @@ -381,7 +381,7 @@ static struct mmci_platform_data mmc0_plat_data = { */ static const struct icst_params versatile_oscvco_params = { .ref = 24000, - .vco_max = 200000, + .vco_max = ICST307_VCO_MAX, .vd_min = 4 + 8, .vd_max = 511 + 8, .rd_min = 1 + 2, -- cgit v1.2.3-70-g09d2 From 64fceb1dcd1aa6a9f2e53cf8830b38bb007b375b Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 16 Jan 2010 17:28:44 +0000 Subject: ARM: ICST: use Hz instead of kHz This makes the ICST support fit more nicely with the clk API, eliminating the need to *1000 and /1000 in places. Signed-off-by: Russell King --- arch/arm/common/icst307.c | 8 ++++---- arch/arm/common/icst525.c | 8 ++++---- arch/arm/include/asm/hardware/icst307.h | 8 ++++---- arch/arm/include/asm/hardware/icst525.h | 10 +++++----- arch/arm/mach-integrator/clock.c | 8 ++++---- arch/arm/mach-integrator/cpu.c | 22 +++++++++++----------- arch/arm/mach-integrator/impd1.c | 6 +++--- arch/arm/mach-integrator/integrator_cp.c | 2 +- arch/arm/mach-realview/clock.c | 8 ++++---- arch/arm/mach-realview/core.c | 2 +- arch/arm/mach-versatile/clock.c | 8 ++++---- arch/arm/mach-versatile/core.c | 2 +- 12 files changed, 46 insertions(+), 46 deletions(-) (limited to 'arch/arm/common') diff --git a/arch/arm/common/icst307.c b/arch/arm/common/icst307.c index f6063c95e25..66c69e597b7 100644 --- a/arch/arm/common/icst307.c +++ b/arch/arm/common/icst307.c @@ -24,12 +24,12 @@ */ static unsigned char s2div[8] = { 10, 2, 8, 4, 5, 7, 3, 6 }; -unsigned long icst307_khz(const struct icst_params *p, struct icst_vco vco) +unsigned long icst307_hz(const struct icst_params *p, struct icst_vco vco) { return p->ref * 2 * (vco.v + 8) / ((vco.r + 2) * s2div[vco.s]); } -EXPORT_SYMBOL(icst307_khz); +EXPORT_SYMBOL(icst307_hz); /* * Ascending divisor S values. @@ -37,7 +37,7 @@ EXPORT_SYMBOL(icst307_khz); static unsigned char idx2s[8] = { 1, 6, 3, 4, 7, 5, 2, 0 }; struct icst_vco -icst307_khz_to_vco(const struct icst_params *p, unsigned long freq) +icst307_hz_to_vco(const struct icst_params *p, unsigned long freq) { struct icst_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max }; unsigned long f; @@ -94,4 +94,4 @@ icst307_khz_to_vco(const struct icst_params *p, unsigned long freq) return vco; } -EXPORT_SYMBOL(icst307_khz_to_vco); +EXPORT_SYMBOL(icst307_hz_to_vco); diff --git a/arch/arm/common/icst525.c b/arch/arm/common/icst525.c index 34dc2e1b9ef..c1d22b7c476 100644 --- a/arch/arm/common/icst525.c +++ b/arch/arm/common/icst525.c @@ -21,12 +21,12 @@ */ static unsigned char s2div[8] = { 10, 2, 8, 4, 5, 7, 9, 6 }; -unsigned long icst525_khz(const struct icst_params *p, struct icst_vco vco) +unsigned long icst525_hz(const struct icst_params *p, struct icst_vco vco) { return p->ref * 2 * (vco.v + 8) / ((vco.r + 2) * s2div[vco.s]); } -EXPORT_SYMBOL(icst525_khz); +EXPORT_SYMBOL(icst525_hz); /* * Ascending divisor S values. @@ -34,7 +34,7 @@ EXPORT_SYMBOL(icst525_khz); static unsigned char idx2s[] = { 1, 3, 4, 7, 5, 2, 6, 0 }; struct icst_vco -icst525_khz_to_vco(const struct icst_params *p, unsigned long freq) +icst525_hz_to_vco(const struct icst_params *p, unsigned long freq) { struct icst_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max }; unsigned long f; @@ -92,4 +92,4 @@ icst525_khz_to_vco(const struct icst_params *p, unsigned long freq) return vco; } -EXPORT_SYMBOL(icst525_khz_to_vco); +EXPORT_SYMBOL(icst525_hz_to_vco); diff --git a/arch/arm/include/asm/hardware/icst307.h b/arch/arm/include/asm/hardware/icst307.h index 0c4e37e3fde..0f096181e83 100644 --- a/arch/arm/include/asm/hardware/icst307.h +++ b/arch/arm/include/asm/hardware/icst307.h @@ -18,14 +18,14 @@ #include -unsigned long icst307_khz(const struct icst_params *p, struct icst_vco vco); -struct icst_vco icst307_khz_to_vco(const struct icst_params *p, unsigned long freq); +unsigned long icst307_hz(const struct icst_params *p, struct icst_vco vco); +struct icst_vco icst307_hz_to_vco(const struct icst_params *p, unsigned long freq); /* * ICST307 VCO frequency must be between 6MHz and 200MHz (3.3 or 5V). * This frequency is pre-output divider. */ -#define ICST307_VCO_MIN 6000 -#define ICST307_VCO_MAX 200000 +#define ICST307_VCO_MIN 6000000 +#define ICST307_VCO_MAX 200000000 #endif diff --git a/arch/arm/include/asm/hardware/icst525.h b/arch/arm/include/asm/hardware/icst525.h index 3b72c132e24..1000a6096fc 100644 --- a/arch/arm/include/asm/hardware/icst525.h +++ b/arch/arm/include/asm/hardware/icst525.h @@ -16,15 +16,15 @@ #include -unsigned long icst525_khz(const struct icst_params *p, struct icst_vco vco); -struct icst_vco icst525_khz_to_vco(const struct icst_params *p, unsigned long freq); +unsigned long icst525_hz(const struct icst_params *p, struct icst_vco vco); +struct icst_vco icst525_hz_to_vco(const struct icst_params *p, unsigned long freq); /* * ICST525 VCO frequency must be between 10MHz and 200MHz (3V) or 320MHz (5V). * This frequency is pre-output divider. */ -#define ICST525_VCO_MIN 10000 -#define ICST525_VCO_MAX_3V 200000 -#define ICST525_VCO_MAX_5V 320000 +#define ICST525_VCO_MIN 10000000 +#define ICST525_VCO_MAX_3V 200000000 +#define ICST525_VCO_MAX_5V 320000000 #endif diff --git a/arch/arm/mach-integrator/clock.c b/arch/arm/mach-integrator/clock.c index bb70b64a656..a4f80d33429 100644 --- a/arch/arm/mach-integrator/clock.c +++ b/arch/arm/mach-integrator/clock.c @@ -38,8 +38,8 @@ EXPORT_SYMBOL(clk_get_rate); long clk_round_rate(struct clk *clk, unsigned long rate) { struct icst_vco vco; - vco = icst525_khz_to_vco(clk->params, rate / 1000); - return icst525_khz(clk->params, vco) * 1000; + vco = icst525_hz_to_vco(clk->params, rate); + return icst525_hz(clk->params, vco); } EXPORT_SYMBOL(clk_round_rate); @@ -50,8 +50,8 @@ int clk_set_rate(struct clk *clk, unsigned long rate) if (clk->setvco) { struct icst_vco vco; - vco = icst525_khz_to_vco(clk->params, rate / 1000); - clk->rate = icst525_khz(clk->params, vco) * 1000; + vco = icst525_hz_to_vco(clk->params, rate); + clk->rate = icst525_hz(clk->params, vco); clk->setvco(clk, vco); ret = 0; } diff --git a/arch/arm/mach-integrator/cpu.c b/arch/arm/mach-integrator/cpu.c index 1671b4a363b..3ebb785f30c 100644 --- a/arch/arm/mach-integrator/cpu.c +++ b/arch/arm/mach-integrator/cpu.c @@ -32,7 +32,7 @@ static struct cpufreq_driver integrator_driver; #define CM_LOCK IO_ADDRESS(INTEGRATOR_HDR_LOCK) static const struct icst_params lclk_params = { - .ref = 24000, + .ref = 24000000, .vco_max = ICST525_VCO_MAX_5V, .vd_min = 8, .vd_max = 132, @@ -41,7 +41,7 @@ static const struct icst_params lclk_params = { }; static const struct icst_params cclk_params = { - .ref = 24000, + .ref = 24000000, .vco_max = ICST525_VCO_MAX_5V, .vd_min = 12, .vd_max = 160, @@ -60,11 +60,11 @@ static int integrator_verify_policy(struct cpufreq_policy *policy) policy->cpuinfo.min_freq, policy->cpuinfo.max_freq); - vco = icst525_khz_to_vco(&cclk_params, policy->max); - policy->max = icst525_khz(&cclk_params, vco); + vco = icst525_hz_to_vco(&cclk_params, policy->max * 1000); + policy->max = icst525_hz(&cclk_params, vco) / 1000; - vco = icst525_khz_to_vco(&cclk_params, policy->min); - policy->min = icst525_khz(&cclk_params, vco); + vco = icst525_hz_to_vco(&cclk_params, policy->min * 1000); + policy->min = icst525_hz(&cclk_params, vco) / 1000; cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, @@ -106,17 +106,17 @@ static int integrator_set_target(struct cpufreq_policy *policy, } vco.v = cm_osc & 255; vco.r = 22; - freqs.old = icst525_khz(&cclk_params, vco); + freqs.old = icst525_hz(&cclk_params, vco) / 1000; - /* icst525_khz_to_vco rounds down -- so we need the next + /* icst525_hz_to_vco rounds down -- so we need the next * larger freq in case of CPUFREQ_RELATION_L. */ if (relation == CPUFREQ_RELATION_L) target_freq += 999; if (target_freq > policy->max) target_freq = policy->max; - vco = icst525_khz_to_vco(&cclk_params, target_freq); - freqs.new = icst525_khz(&cclk_params, vco); + vco = icst525_hz_to_vco(&cclk_params, target_freq * 1000); + freqs.new = icst525_hz(&cclk_params, vco) / 1000; freqs.cpu = policy->cpu; @@ -174,7 +174,7 @@ static unsigned int integrator_get(unsigned int cpu) vco.v = cm_osc & 255; vco.r = 22; - current_freq = icst525_khz(&cclk_params, vco); /* current freq */ + current_freq = icst525_hz(&cclk_params, vco) / 1000; /* current freq */ set_cpus_allowed(current, cpus_allowed); diff --git a/arch/arm/mach-integrator/impd1.c b/arch/arm/mach-integrator/impd1.c index 5aca7ebea3c..ecce3eb8fe0 100644 --- a/arch/arm/mach-integrator/impd1.c +++ b/arch/arm/mach-integrator/impd1.c @@ -41,7 +41,7 @@ struct impd1_module { }; static const struct icst_params impd1_vco_params = { - .ref = 24000, /* 24 MHz */ + .ref = 24000000, /* 24 MHz */ .vco_max = ICST525_VCO_MAX_3V, .vd_min = 12, .vd_max = 519, @@ -73,8 +73,8 @@ static void impd1_setvco(struct clk *clk, struct icst_vco vco) vco.r = (val >> 9) & 0x7f; vco.s = (val >> 16) & 7; - pr_debug("IM-PD1: VCO%d clock is %ld kHz\n", - vconr, icst525_khz(&impd1_vco_params, vco)); + pr_debug("IM-PD1: VCO%d clock is %ld Hz\n", + vconr, icst525_hz(&impd1_vco_params, vco)); #endif } diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c index 27f95106b47..335af99acb7 100644 --- a/arch/arm/mach-integrator/integrator_cp.c +++ b/arch/arm/mach-integrator/integrator_cp.c @@ -269,7 +269,7 @@ static void __init intcp_init_irq(void) #define CM_AUXOSC IO_ADDRESS(INTEGRATOR_HDR_BASE + 0x1c) static const struct icst_params cp_auxvco_params = { - .ref = 24000, + .ref = 24000000, .vco_max = ICST525_VCO_MAX_5V, .vd_min = 8, .vd_max = 263, diff --git a/arch/arm/mach-realview/clock.c b/arch/arm/mach-realview/clock.c index 83646579bc4..2ba0667d18f 100644 --- a/arch/arm/mach-realview/clock.c +++ b/arch/arm/mach-realview/clock.c @@ -42,8 +42,8 @@ EXPORT_SYMBOL(clk_get_rate); long clk_round_rate(struct clk *clk, unsigned long rate) { struct icst_vco vco; - vco = icst307_khz_to_vco(clk->params, rate / 1000); - return icst307_khz(clk->params, vco) * 1000; + vco = icst307_hz_to_vco(clk->params, rate); + return icst307_hz(clk->params, vco); } EXPORT_SYMBOL(clk_round_rate); @@ -54,8 +54,8 @@ int clk_set_rate(struct clk *clk, unsigned long rate) if (clk->setvco) { struct icst_vco vco; - vco = icst307_khz_to_vco(clk->params, rate / 1000); - clk->rate = icst307_khz(clk->params, vco) * 1000; + vco = icst307_hz_to_vco(clk->params, rate); + clk->rate = icst307_hz(clk->params, vco); clk->setvco(clk, vco); ret = 0; } diff --git a/arch/arm/mach-realview/core.c b/arch/arm/mach-realview/core.c index 5a850f0dc36..02d48371c09 100644 --- a/arch/arm/mach-realview/core.c +++ b/arch/arm/mach-realview/core.c @@ -274,7 +274,7 @@ struct mmci_platform_data realview_mmc1_plat_data = { * Clock handling */ static const struct icst_params realview_oscvco_params = { - .ref = 24000, + .ref = 24000000, .vco_max = ICST307_VCO_MAX, .vd_min = 4 + 8, .vd_max = 511 + 8, diff --git a/arch/arm/mach-versatile/clock.c b/arch/arm/mach-versatile/clock.c index 530e16aa7ad..82753be453d 100644 --- a/arch/arm/mach-versatile/clock.c +++ b/arch/arm/mach-versatile/clock.c @@ -43,8 +43,8 @@ EXPORT_SYMBOL(clk_get_rate); long clk_round_rate(struct clk *clk, unsigned long rate) { struct icst_vco vco; - vco = icst307_khz_to_vco(clk->params, rate / 1000); - return icst307_khz(clk->params, vco) * 1000; + vco = icst307_hz_to_vco(clk->params, rate); + return icst307_hz(clk->params, vco); } EXPORT_SYMBOL(clk_round_rate); @@ -55,8 +55,8 @@ int clk_set_rate(struct clk *clk, unsigned long rate) if (clk->setvco) { struct icst_vco vco; - vco = icst307_khz_to_vco(clk->params, rate / 1000); - clk->rate = icst307_khz(clk->params, vco) * 1000; + vco = icst307_hz_to_vco(clk->params, rate); + clk->rate = icst307_hz(clk->params, vco); clk->setvco(clk, vco); ret = 0; } diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c index f8ed561ec69..ff5d24f80b2 100644 --- a/arch/arm/mach-versatile/core.c +++ b/arch/arm/mach-versatile/core.c @@ -380,7 +380,7 @@ static struct mmci_platform_data mmc0_plat_data = { * Clock handling */ static const struct icst_params versatile_oscvco_params = { - .ref = 24000, + .ref = 24000000, .vco_max = ICST307_VCO_MAX, .vd_min = 4 + 8, .vd_max = 511 + 8, -- cgit v1.2.3-70-g09d2 From e73a46a3d3df22b379d8adcb59999eafea1d60d5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 16 Jan 2010 19:49:39 +0000 Subject: ARM: ICST: move minimum VCO frequency to icst_params Signed-off-by: Russell King --- arch/arm/common/icst307.c | 2 +- arch/arm/common/icst525.c | 2 +- arch/arm/include/asm/hardware/icst.h | 1 + arch/arm/mach-integrator/cpu.c | 2 ++ arch/arm/mach-integrator/impd1.c | 1 + arch/arm/mach-integrator/integrator_cp.c | 1 + arch/arm/mach-realview/core.c | 1 + arch/arm/mach-versatile/core.c | 1 + 8 files changed, 9 insertions(+), 2 deletions(-) (limited to 'arch/arm/common') diff --git a/arch/arm/common/icst307.c b/arch/arm/common/icst307.c index 66c69e597b7..312485fab4a 100644 --- a/arch/arm/common/icst307.c +++ b/arch/arm/common/icst307.c @@ -53,7 +53,7 @@ icst307_hz_to_vco(const struct icst_params *p, unsigned long freq) /* * f must be between 6MHz and 200MHz (3.3 or 5V) */ - if (f > ICST307_VCO_MIN && f <= p->vco_max) + if (f > p->vco_min && f <= p->vco_max) break; } while (i < ARRAY_SIZE(idx2s)); diff --git a/arch/arm/common/icst525.c b/arch/arm/common/icst525.c index c1d22b7c476..da58965da31 100644 --- a/arch/arm/common/icst525.c +++ b/arch/arm/common/icst525.c @@ -51,7 +51,7 @@ icst525_hz_to_vco(const struct icst_params *p, unsigned long freq) * f must be between 10MHz and * 320MHz (5V) or 200MHz (3V) */ - if (f > ICST525_VCO_MIN && f <= p->vco_max) + if (f > p->vco_min && f <= p->vco_max) break; } while (i < ARRAY_SIZE(idx2s)); diff --git a/arch/arm/include/asm/hardware/icst.h b/arch/arm/include/asm/hardware/icst.h index 65b1edd4452..6707c6a9132 100644 --- a/arch/arm/include/asm/hardware/icst.h +++ b/arch/arm/include/asm/hardware/icst.h @@ -17,6 +17,7 @@ struct icst_params { unsigned long ref; unsigned long vco_max; /* inclusive */ + unsigned long vco_min; /* exclusive */ unsigned short vd_min; /* inclusive */ unsigned short vd_max; /* inclusive */ unsigned char rd_min; /* inclusive */ diff --git a/arch/arm/mach-integrator/cpu.c b/arch/arm/mach-integrator/cpu.c index 3ebb785f30c..569306b8fdb 100644 --- a/arch/arm/mach-integrator/cpu.c +++ b/arch/arm/mach-integrator/cpu.c @@ -34,6 +34,7 @@ static struct cpufreq_driver integrator_driver; static const struct icst_params lclk_params = { .ref = 24000000, .vco_max = ICST525_VCO_MAX_5V, + .vco_min = ICST525_VCO_MIN, .vd_min = 8, .vd_max = 132, .rd_min = 24, @@ -43,6 +44,7 @@ static const struct icst_params lclk_params = { static const struct icst_params cclk_params = { .ref = 24000000, .vco_max = ICST525_VCO_MAX_5V, + .vco_min = ICST525_VCO_MIN, .vd_min = 12, .vd_max = 160, .rd_min = 24, diff --git a/arch/arm/mach-integrator/impd1.c b/arch/arm/mach-integrator/impd1.c index ecce3eb8fe0..036cfb45273 100644 --- a/arch/arm/mach-integrator/impd1.c +++ b/arch/arm/mach-integrator/impd1.c @@ -43,6 +43,7 @@ struct impd1_module { static const struct icst_params impd1_vco_params = { .ref = 24000000, /* 24 MHz */ .vco_max = ICST525_VCO_MAX_3V, + .vco_min = ICST525_VCO_MIN, .vd_min = 12, .vd_max = 519, .rd_min = 3, diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c index 335af99acb7..34c120ad129 100644 --- a/arch/arm/mach-integrator/integrator_cp.c +++ b/arch/arm/mach-integrator/integrator_cp.c @@ -271,6 +271,7 @@ static void __init intcp_init_irq(void) static const struct icst_params cp_auxvco_params = { .ref = 24000000, .vco_max = ICST525_VCO_MAX_5V, + .vco_min = ICST525_VCO_MIN, .vd_min = 8, .vd_max = 263, .rd_min = 3, diff --git a/arch/arm/mach-realview/core.c b/arch/arm/mach-realview/core.c index 02d48371c09..3ac44138bf7 100644 --- a/arch/arm/mach-realview/core.c +++ b/arch/arm/mach-realview/core.c @@ -276,6 +276,7 @@ struct mmci_platform_data realview_mmc1_plat_data = { static const struct icst_params realview_oscvco_params = { .ref = 24000000, .vco_max = ICST307_VCO_MAX, + .vco_min = ICST307_VCO_MIN, .vd_min = 4 + 8, .vd_max = 511 + 8, .rd_min = 1 + 2, diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c index ff5d24f80b2..c4bf6802df5 100644 --- a/arch/arm/mach-versatile/core.c +++ b/arch/arm/mach-versatile/core.c @@ -382,6 +382,7 @@ static struct mmci_platform_data mmc0_plat_data = { static const struct icst_params versatile_oscvco_params = { .ref = 24000000, .vco_max = ICST307_VCO_MAX, + .vco_min = ICST307_VCO_MIN, .vd_min = 4 + 8, .vd_max = 511 + 8, .rd_min = 1 + 2, -- cgit v1.2.3-70-g09d2 From 232eaf7f268f765b52170bec42bfa0c5825aa239 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 16 Jan 2010 19:46:19 +0000 Subject: ARM: ICST: indirect s2div and idx2s arrays via icst_params Signed-off-by: Russell King --- arch/arm/common/icst307.c | 18 +++++++++++------- arch/arm/common/icst525.c | 18 +++++++++++------- arch/arm/include/asm/hardware/icst.h | 2 ++ arch/arm/include/asm/hardware/icst307.h | 3 +++ arch/arm/include/asm/hardware/icst525.h | 3 +++ arch/arm/mach-integrator/cpu.c | 4 ++++ arch/arm/mach-integrator/impd1.c | 2 ++ arch/arm/mach-integrator/integrator_cp.c | 2 ++ arch/arm/mach-realview/core.c | 2 ++ arch/arm/mach-versatile/core.c | 2 ++ 10 files changed, 42 insertions(+), 14 deletions(-) (limited to 'arch/arm/common') diff --git a/arch/arm/common/icst307.c b/arch/arm/common/icst307.c index 312485fab4a..8332c0726e7 100644 --- a/arch/arm/common/icst307.c +++ b/arch/arm/common/icst307.c @@ -22,11 +22,13 @@ /* * Divisors for each OD setting. */ -static unsigned char s2div[8] = { 10, 2, 8, 4, 5, 7, 3, 6 }; +const unsigned char icst307_s2div[8] = { 10, 2, 8, 4, 5, 7, 3, 6 }; + +EXPORT_SYMBOL(icst307_s2div); unsigned long icst307_hz(const struct icst_params *p, struct icst_vco vco) { - return p->ref * 2 * (vco.v + 8) / ((vco.r + 2) * s2div[vco.s]); + return p->ref * 2 * (vco.v + 8) / ((vco.r + 2) * p->s2div[vco.s]); } EXPORT_SYMBOL(icst307_hz); @@ -34,7 +36,9 @@ EXPORT_SYMBOL(icst307_hz); /* * Ascending divisor S values. */ -static unsigned char idx2s[8] = { 1, 6, 3, 4, 7, 5, 2, 0 }; +const unsigned char icst307_idx2s[8] = { 1, 6, 3, 4, 7, 5, 2, 0 }; + +EXPORT_SYMBOL(icst307_idx2s); struct icst_vco icst307_hz_to_vco(const struct icst_params *p, unsigned long freq) @@ -48,19 +52,19 @@ icst307_hz_to_vco(const struct icst_params *p, unsigned long freq) * that the PLL output is within spec. */ do { - f = freq * s2div[idx2s[i]]; + f = freq * p->s2div[p->idx2s[i]]; /* * f must be between 6MHz and 200MHz (3.3 or 5V) */ if (f > p->vco_min && f <= p->vco_max) break; - } while (i < ARRAY_SIZE(idx2s)); + } while (i < 8); - if (i >= ARRAY_SIZE(idx2s)) + if (i >= 8) return vco; - vco.s = idx2s[i]; + vco.s = p->idx2s[i]; /* * Now find the closest divisor combination diff --git a/arch/arm/common/icst525.c b/arch/arm/common/icst525.c index da58965da31..4180255eb07 100644 --- a/arch/arm/common/icst525.c +++ b/arch/arm/common/icst525.c @@ -19,11 +19,13 @@ /* * Divisors for each OD setting. */ -static unsigned char s2div[8] = { 10, 2, 8, 4, 5, 7, 9, 6 }; +const unsigned char icst525_s2div[8] = { 10, 2, 8, 4, 5, 7, 9, 6 }; + +EXPORT_SYMBOL(icst525_s2div); unsigned long icst525_hz(const struct icst_params *p, struct icst_vco vco) { - return p->ref * 2 * (vco.v + 8) / ((vco.r + 2) * s2div[vco.s]); + return p->ref * 2 * (vco.v + 8) / ((vco.r + 2) * p->s2div[vco.s]); } EXPORT_SYMBOL(icst525_hz); @@ -31,7 +33,9 @@ EXPORT_SYMBOL(icst525_hz); /* * Ascending divisor S values. */ -static unsigned char idx2s[] = { 1, 3, 4, 7, 5, 2, 6, 0 }; +const unsigned char icst525_idx2s[8] = { 1, 3, 4, 7, 5, 2, 6, 0 }; + +EXPORT_SYMBOL(icst525_idx2s); struct icst_vco icst525_hz_to_vco(const struct icst_params *p, unsigned long freq) @@ -45,7 +49,7 @@ icst525_hz_to_vco(const struct icst_params *p, unsigned long freq) * that the PLL output is within spec. */ do { - f = freq * s2div[idx2s[i]]; + f = freq * p->s2div[p->idx2s[i]]; /* * f must be between 10MHz and @@ -53,12 +57,12 @@ icst525_hz_to_vco(const struct icst_params *p, unsigned long freq) */ if (f > p->vco_min && f <= p->vco_max) break; - } while (i < ARRAY_SIZE(idx2s)); + } while (i < 8); - if (i >= ARRAY_SIZE(idx2s)) + if (i >= 8) return vco; - vco.s = idx2s[i]; + vco.s = p->idx2s[i]; /* * Now find the closest divisor combination diff --git a/arch/arm/include/asm/hardware/icst.h b/arch/arm/include/asm/hardware/icst.h index 6707c6a9132..4d403680fa4 100644 --- a/arch/arm/include/asm/hardware/icst.h +++ b/arch/arm/include/asm/hardware/icst.h @@ -22,6 +22,8 @@ struct icst_params { unsigned short vd_max; /* inclusive */ unsigned char rd_min; /* inclusive */ unsigned char rd_max; /* inclusive */ + const unsigned char *s2div; /* chip specific s2div array */ + const unsigned char *idx2s; /* chip specific idx2s array */ }; struct icst_vco { diff --git a/arch/arm/include/asm/hardware/icst307.h b/arch/arm/include/asm/hardware/icst307.h index 0f096181e83..d76fc6e09e4 100644 --- a/arch/arm/include/asm/hardware/icst307.h +++ b/arch/arm/include/asm/hardware/icst307.h @@ -28,4 +28,7 @@ struct icst_vco icst307_hz_to_vco(const struct icst_params *p, unsigned long fre #define ICST307_VCO_MIN 6000000 #define ICST307_VCO_MAX 200000000 +extern const unsigned char icst307_s2div[]; +extern const unsigned char icst307_idx2s[]; + #endif diff --git a/arch/arm/include/asm/hardware/icst525.h b/arch/arm/include/asm/hardware/icst525.h index 1000a6096fc..2f9b95386b1 100644 --- a/arch/arm/include/asm/hardware/icst525.h +++ b/arch/arm/include/asm/hardware/icst525.h @@ -27,4 +27,7 @@ struct icst_vco icst525_hz_to_vco(const struct icst_params *p, unsigned long fre #define ICST525_VCO_MAX_3V 200000000 #define ICST525_VCO_MAX_5V 320000000 +extern const unsigned char icst525_s2div[]; +extern const unsigned char icst525_idx2s[]; + #endif diff --git a/arch/arm/mach-integrator/cpu.c b/arch/arm/mach-integrator/cpu.c index 569306b8fdb..9481c5408bf 100644 --- a/arch/arm/mach-integrator/cpu.c +++ b/arch/arm/mach-integrator/cpu.c @@ -39,6 +39,8 @@ static const struct icst_params lclk_params = { .vd_max = 132, .rd_min = 24, .rd_max = 24, + .s2div = icst525_s2div, + .idx2s = icst525_idx2s, }; static const struct icst_params cclk_params = { @@ -49,6 +51,8 @@ static const struct icst_params cclk_params = { .vd_max = 160, .rd_min = 24, .rd_max = 24, + .s2div = icst525_s2div, + .idx2s = icst525_idx2s, }; /* diff --git a/arch/arm/mach-integrator/impd1.c b/arch/arm/mach-integrator/impd1.c index 036cfb45273..e24cfc357ff 100644 --- a/arch/arm/mach-integrator/impd1.c +++ b/arch/arm/mach-integrator/impd1.c @@ -48,6 +48,8 @@ static const struct icst_params impd1_vco_params = { .vd_max = 519, .rd_min = 3, .rd_max = 120, + .s2div = icst525_s2div, + .idx2s = icst525_idx2s, }; static void impd1_setvco(struct clk *clk, struct icst_vco vco) diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c index 34c120ad129..9997d1f6c18 100644 --- a/arch/arm/mach-integrator/integrator_cp.c +++ b/arch/arm/mach-integrator/integrator_cp.c @@ -276,6 +276,8 @@ static const struct icst_params cp_auxvco_params = { .vd_max = 263, .rd_min = 3, .rd_max = 65, + .s2div = icst525_s2div, + .idx2s = icst525_idx2s, }; static void cp_auxvco_set(struct clk *clk, struct icst_vco vco) diff --git a/arch/arm/mach-realview/core.c b/arch/arm/mach-realview/core.c index 3ac44138bf7..26d44caad54 100644 --- a/arch/arm/mach-realview/core.c +++ b/arch/arm/mach-realview/core.c @@ -281,6 +281,8 @@ static const struct icst_params realview_oscvco_params = { .vd_max = 511 + 8, .rd_min = 1 + 2, .rd_max = 127 + 2, + .s2div = icst307_s2div, + .idx2s = icst307_idx2s, }; static void realview_oscvco_set(struct clk *clk, struct icst_vco vco) diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c index c4bf6802df5..51d7aeb6a50 100644 --- a/arch/arm/mach-versatile/core.c +++ b/arch/arm/mach-versatile/core.c @@ -387,6 +387,8 @@ static const struct icst_params versatile_oscvco_params = { .vd_max = 511 + 8, .rd_min = 1 + 2, .rd_max = 127 + 2, + .s2div = icst307_s2div, + .idx2s = icst307_idx2s, }; static void versatile_oscvco_set(struct clk *clk, struct icst_vco vco) -- cgit v1.2.3-70-g09d2 From c5a0adb51002e51a4254cb7f0ab7190d41d8b930 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 16 Jan 2010 20:16:10 +0000 Subject: ARM: ICST: kill duplicate icst code The only difference between ICST307 and ICST525 are the two arrays for calculating the S parameter; the code is now identical. Merge the two files and kill the duplicated code. Signed-off-by: Russell King --- arch/arm/Kconfig | 6 +- arch/arm/common/Kconfig | 5 +- arch/arm/common/Makefile | 3 +- arch/arm/common/icst.c | 100 ++++++++++++++++++++++++++++++ arch/arm/common/icst307.c | 101 ------------------------------- arch/arm/common/icst525.c | 99 ------------------------------ arch/arm/include/asm/hardware/icst.h | 24 ++++++++ arch/arm/include/asm/hardware/icst307.h | 34 ----------- arch/arm/include/asm/hardware/icst525.h | 33 ---------- arch/arm/mach-integrator/clock.c | 10 +-- arch/arm/mach-integrator/cpu.c | 20 +++--- arch/arm/mach-integrator/impd1.c | 2 +- arch/arm/mach-integrator/integrator_cp.c | 2 +- arch/arm/mach-realview/clock.c | 10 +-- arch/arm/mach-realview/core.c | 2 +- arch/arm/mach-realview/realview_eb.c | 1 - arch/arm/mach-realview/realview_pb1176.c | 1 - arch/arm/mach-realview/realview_pb11mp.c | 1 - arch/arm/mach-realview/realview_pba8.c | 1 - arch/arm/mach-versatile/clock.c | 10 +-- arch/arm/mach-versatile/core.c | 2 +- 21 files changed, 158 insertions(+), 309 deletions(-) create mode 100644 arch/arm/common/icst.c delete mode 100644 arch/arm/common/icst307.c delete mode 100644 arch/arm/common/icst525.c delete mode 100644 arch/arm/include/asm/hardware/icst307.h delete mode 100644 arch/arm/include/asm/hardware/icst525.h (limited to 'arch/arm/common') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 86fecec1600..3c436fdbe2a 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -240,7 +240,7 @@ config ARCH_INTEGRATOR select ARCH_HAS_CPUFREQ select HAVE_CLK select COMMON_CLKDEV - select ICST525 + select ICST select GENERIC_TIME select GENERIC_CLOCKEVENTS help @@ -251,7 +251,7 @@ config ARCH_REALVIEW select ARM_AMBA select HAVE_CLK select COMMON_CLKDEV - select ICST307 + select ICST select GENERIC_TIME select GENERIC_CLOCKEVENTS select ARCH_WANT_OPTIONAL_GPIOLIB @@ -264,7 +264,7 @@ config ARCH_VERSATILE select ARM_VIC select HAVE_CLK select COMMON_CLKDEV - select ICST307 + select ICST select GENERIC_TIME select GENERIC_CLOCKEVENTS select ARCH_WANT_OPTIONAL_GPIOLIB diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig index 4efbb9df044..323d2d28952 100644 --- a/arch/arm/common/Kconfig +++ b/arch/arm/common/Kconfig @@ -12,10 +12,7 @@ config ARM_VIC_NR The maximum number of VICs available in the system, for power management. -config ICST525 - bool - -config ICST307 +config ICST bool config SA1111 diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index 76be7ff2a7c..5e8ad0d6c91 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -4,8 +4,7 @@ obj-$(CONFIG_ARM_GIC) += gic.o obj-$(CONFIG_ARM_VIC) += vic.o -obj-$(CONFIG_ICST525) += icst525.o -obj-$(CONFIG_ICST307) += icst307.o +obj-$(CONFIG_ICST) += icst.o obj-$(CONFIG_SA1111) += sa1111.o obj-$(CONFIG_PCI_HOST_VIA82C505) += via82c505.o obj-$(CONFIG_DMABOUNCE) += dmabounce.o diff --git a/arch/arm/common/icst.c b/arch/arm/common/icst.c new file mode 100644 index 00000000000..9a7f09cff30 --- /dev/null +++ b/arch/arm/common/icst.c @@ -0,0 +1,100 @@ +/* + * linux/arch/arm/common/icst307.c + * + * Copyright (C) 2003 Deep Blue Solutions, 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. + * + * Support functions for calculating clocks/divisors for the ICST307 + * clock generators. See http://www.icst.com/ for more information + * on these devices. + * + * This is an almost identical implementation to the ICST525 clock generator. + * The s2div and idx2s files are different + */ +#include +#include + +#include + +/* + * Divisors for each OD setting. + */ +const unsigned char icst307_s2div[8] = { 10, 2, 8, 4, 5, 7, 3, 6 }; +const unsigned char icst525_s2div[8] = { 10, 2, 8, 4, 5, 7, 9, 6 }; +EXPORT_SYMBOL(icst307_s2div); +EXPORT_SYMBOL(icst525_s2div); + +unsigned long icst_hz(const struct icst_params *p, struct icst_vco vco) +{ + return p->ref * 2 * (vco.v + 8) / ((vco.r + 2) * p->s2div[vco.s]); +} + +EXPORT_SYMBOL(icst_hz); + +/* + * Ascending divisor S values. + */ +const unsigned char icst307_idx2s[8] = { 1, 6, 3, 4, 7, 5, 2, 0 }; +const unsigned char icst525_idx2s[8] = { 1, 3, 4, 7, 5, 2, 6, 0 }; +EXPORT_SYMBOL(icst307_idx2s); +EXPORT_SYMBOL(icst525_idx2s); + +struct icst_vco +icst_hz_to_vco(const struct icst_params *p, unsigned long freq) +{ + struct icst_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max }; + unsigned long f; + unsigned int i = 0, rd, best = (unsigned int)-1; + + /* + * First, find the PLL output divisor such + * that the PLL output is within spec. + */ + do { + f = freq * p->s2div[p->idx2s[i]]; + + if (f > p->vco_min && f <= p->vco_max) + break; + } while (i < 8); + + if (i >= 8) + return vco; + + vco.s = p->idx2s[i]; + + /* + * Now find the closest divisor combination + * which gives a PLL output of 'f'. + */ + for (rd = p->rd_min; rd <= p->rd_max; rd++) { + unsigned long fref_div, f_pll; + unsigned int vd; + int f_diff; + + fref_div = (2 * p->ref) / rd; + + vd = (f + fref_div / 2) / fref_div; + if (vd < p->vd_min || vd > p->vd_max) + continue; + + f_pll = fref_div * vd; + f_diff = f_pll - f; + if (f_diff < 0) + f_diff = -f_diff; + + if ((unsigned)f_diff < best) { + vco.v = vd - 8; + vco.r = rd - 2; + if (f_diff == 0) + break; + best = f_diff; + } + } + + return vco; +} + +EXPORT_SYMBOL(icst_hz_to_vco); diff --git a/arch/arm/common/icst307.c b/arch/arm/common/icst307.c deleted file mode 100644 index 8332c0726e7..00000000000 --- a/arch/arm/common/icst307.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * linux/arch/arm/common/icst307.c - * - * Copyright (C) 2003 Deep Blue Solutions, 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. - * - * Support functions for calculating clocks/divisors for the ICST307 - * clock generators. See http://www.icst.com/ for more information - * on these devices. - * - * This is an almost identical implementation to the ICST525 clock generator. - * The s2div and idx2s files are different - */ -#include -#include - -#include - -/* - * Divisors for each OD setting. - */ -const unsigned char icst307_s2div[8] = { 10, 2, 8, 4, 5, 7, 3, 6 }; - -EXPORT_SYMBOL(icst307_s2div); - -unsigned long icst307_hz(const struct icst_params *p, struct icst_vco vco) -{ - return p->ref * 2 * (vco.v + 8) / ((vco.r + 2) * p->s2div[vco.s]); -} - -EXPORT_SYMBOL(icst307_hz); - -/* - * Ascending divisor S values. - */ -const unsigned char icst307_idx2s[8] = { 1, 6, 3, 4, 7, 5, 2, 0 }; - -EXPORT_SYMBOL(icst307_idx2s); - -struct icst_vco -icst307_hz_to_vco(const struct icst_params *p, unsigned long freq) -{ - struct icst_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max }; - unsigned long f; - unsigned int i = 0, rd, best = (unsigned int)-1; - - /* - * First, find the PLL output divisor such - * that the PLL output is within spec. - */ - do { - f = freq * p->s2div[p->idx2s[i]]; - - /* - * f must be between 6MHz and 200MHz (3.3 or 5V) - */ - if (f > p->vco_min && f <= p->vco_max) - break; - } while (i < 8); - - if (i >= 8) - return vco; - - vco.s = p->idx2s[i]; - - /* - * Now find the closest divisor combination - * which gives a PLL output of 'f'. - */ - for (rd = p->rd_min; rd <= p->rd_max; rd++) { - unsigned long fref_div, f_pll; - unsigned int vd; - int f_diff; - - fref_div = (2 * p->ref) / rd; - - vd = (f + fref_div / 2) / fref_div; - if (vd < p->vd_min || vd > p->vd_max) - continue; - - f_pll = fref_div * vd; - f_diff = f_pll - f; - if (f_diff < 0) - f_diff = -f_diff; - - if ((unsigned)f_diff < best) { - vco.v = vd - 8; - vco.r = rd - 2; - if (f_diff == 0) - break; - best = f_diff; - } - } - - return vco; -} - -EXPORT_SYMBOL(icst307_hz_to_vco); diff --git a/arch/arm/common/icst525.c b/arch/arm/common/icst525.c deleted file mode 100644 index 4180255eb07..00000000000 --- a/arch/arm/common/icst525.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * linux/arch/arm/common/icst525.c - * - * Copyright (C) 2003 Deep Blue Solutions, 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. - * - * Support functions for calculating clocks/divisors for the ICST525 - * clock generators. See http://www.icst.com/ for more information - * on these devices. - */ -#include -#include - -#include - -/* - * Divisors for each OD setting. - */ -const unsigned char icst525_s2div[8] = { 10, 2, 8, 4, 5, 7, 9, 6 }; - -EXPORT_SYMBOL(icst525_s2div); - -unsigned long icst525_hz(const struct icst_params *p, struct icst_vco vco) -{ - return p->ref * 2 * (vco.v + 8) / ((vco.r + 2) * p->s2div[vco.s]); -} - -EXPORT_SYMBOL(icst525_hz); - -/* - * Ascending divisor S values. - */ -const unsigned char icst525_idx2s[8] = { 1, 3, 4, 7, 5, 2, 6, 0 }; - -EXPORT_SYMBOL(icst525_idx2s); - -struct icst_vco -icst525_hz_to_vco(const struct icst_params *p, unsigned long freq) -{ - struct icst_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max }; - unsigned long f; - unsigned int i = 0, rd, best = (unsigned int)-1; - - /* - * First, find the PLL output divisor such - * that the PLL output is within spec. - */ - do { - f = freq * p->s2div[p->idx2s[i]]; - - /* - * f must be between 10MHz and - * 320MHz (5V) or 200MHz (3V) - */ - if (f > p->vco_min && f <= p->vco_max) - break; - } while (i < 8); - - if (i >= 8) - return vco; - - vco.s = p->idx2s[i]; - - /* - * Now find the closest divisor combination - * which gives a PLL output of 'f'. - */ - for (rd = p->rd_min; rd <= p->rd_max; rd++) { - unsigned long fref_div, f_pll; - unsigned int vd; - int f_diff; - - fref_div = (2 * p->ref) / rd; - - vd = (f + fref_div / 2) / fref_div; - if (vd < p->vd_min || vd > p->vd_max) - continue; - - f_pll = fref_div * vd; - f_diff = f_pll - f; - if (f_diff < 0) - f_diff = -f_diff; - - if ((unsigned)f_diff < best) { - vco.v = vd - 8; - vco.r = rd - 2; - if (f_diff == 0) - break; - best = f_diff; - } - } - - return vco; -} - -EXPORT_SYMBOL(icst525_hz_to_vco); diff --git a/arch/arm/include/asm/hardware/icst.h b/arch/arm/include/asm/hardware/icst.h index 4d403680fa4..10382a3dcec 100644 --- a/arch/arm/include/asm/hardware/icst.h +++ b/arch/arm/include/asm/hardware/icst.h @@ -32,4 +32,28 @@ struct icst_vco { unsigned char s; }; +unsigned long icst_hz(const struct icst_params *p, struct icst_vco vco); +struct icst_vco icst_hz_to_vco(const struct icst_params *p, unsigned long freq); + +/* + * ICST307 VCO frequency must be between 6MHz and 200MHz (3.3 or 5V). + * This frequency is pre-output divider. + */ +#define ICST307_VCO_MIN 6000000 +#define ICST307_VCO_MAX 200000000 + +extern const unsigned char icst307_s2div[]; +extern const unsigned char icst307_idx2s[]; + +/* + * ICST525 VCO frequency must be between 10MHz and 200MHz (3V) or 320MHz (5V). + * This frequency is pre-output divider. + */ +#define ICST525_VCO_MIN 10000000 +#define ICST525_VCO_MAX_3V 200000000 +#define ICST525_VCO_MAX_5V 320000000 + +extern const unsigned char icst525_s2div[]; +extern const unsigned char icst525_idx2s[]; + #endif diff --git a/arch/arm/include/asm/hardware/icst307.h b/arch/arm/include/asm/hardware/icst307.h deleted file mode 100644 index d76fc6e09e4..00000000000 --- a/arch/arm/include/asm/hardware/icst307.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * arch/arm/include/asm/hardware/icst307.h - * - * Copyright (C) 2003 Deep Blue Solutions, 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. - * - * Support functions for calculating clocks/divisors for the ICS307 - * clock generators. See http://www.icst.com/ for more information - * on these devices. - * - * This file is similar to the icst525.h file - */ -#ifndef ASMARM_HARDWARE_ICST307_H -#define ASMARM_HARDWARE_ICST307_H - -#include - -unsigned long icst307_hz(const struct icst_params *p, struct icst_vco vco); -struct icst_vco icst307_hz_to_vco(const struct icst_params *p, unsigned long freq); - -/* - * ICST307 VCO frequency must be between 6MHz and 200MHz (3.3 or 5V). - * This frequency is pre-output divider. - */ -#define ICST307_VCO_MIN 6000000 -#define ICST307_VCO_MAX 200000000 - -extern const unsigned char icst307_s2div[]; -extern const unsigned char icst307_idx2s[]; - -#endif diff --git a/arch/arm/include/asm/hardware/icst525.h b/arch/arm/include/asm/hardware/icst525.h deleted file mode 100644 index 2f9b95386b1..00000000000 --- a/arch/arm/include/asm/hardware/icst525.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * arch/arm/include/asm/hardware/icst525.h - * - * Copyright (C) 2003 Deep Blue Solutions, 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. - * - * Support functions for calculating clocks/divisors for the ICST525 - * clock generators. See http://www.icst.com/ for more information - * on these devices. - */ -#ifndef ASMARM_HARDWARE_ICST525_H -#define ASMARM_HARDWARE_ICST525_H - -#include - -unsigned long icst525_hz(const struct icst_params *p, struct icst_vco vco); -struct icst_vco icst525_hz_to_vco(const struct icst_params *p, unsigned long freq); - -/* - * ICST525 VCO frequency must be between 10MHz and 200MHz (3V) or 320MHz (5V). - * This frequency is pre-output divider. - */ -#define ICST525_VCO_MIN 10000000 -#define ICST525_VCO_MAX_3V 200000000 -#define ICST525_VCO_MAX_5V 320000000 - -extern const unsigned char icst525_s2div[]; -extern const unsigned char icst525_idx2s[]; - -#endif diff --git a/arch/arm/mach-integrator/clock.c b/arch/arm/mach-integrator/clock.c index a4f80d33429..52fc294eac7 100644 --- a/arch/arm/mach-integrator/clock.c +++ b/arch/arm/mach-integrator/clock.c @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include @@ -38,8 +38,8 @@ EXPORT_SYMBOL(clk_get_rate); long clk_round_rate(struct clk *clk, unsigned long rate) { struct icst_vco vco; - vco = icst525_hz_to_vco(clk->params, rate); - return icst525_hz(clk->params, vco); + vco = icst_hz_to_vco(clk->params, rate); + return icst_hz(clk->params, vco); } EXPORT_SYMBOL(clk_round_rate); @@ -50,8 +50,8 @@ int clk_set_rate(struct clk *clk, unsigned long rate) if (clk->setvco) { struct icst_vco vco; - vco = icst525_hz_to_vco(clk->params, rate); - clk->rate = icst525_hz(clk->params, vco); + vco = icst_hz_to_vco(clk->params, rate); + clk->rate = icst_hz(clk->params, vco); clk->setvco(clk, vco); ret = 0; } diff --git a/arch/arm/mach-integrator/cpu.c b/arch/arm/mach-integrator/cpu.c index 9481c5408bf..1cb222daa06 100644 --- a/arch/arm/mach-integrator/cpu.c +++ b/arch/arm/mach-integrator/cpu.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include static struct cpufreq_driver integrator_driver; @@ -66,11 +66,11 @@ static int integrator_verify_policy(struct cpufreq_policy *policy) policy->cpuinfo.min_freq, policy->cpuinfo.max_freq); - vco = icst525_hz_to_vco(&cclk_params, policy->max * 1000); - policy->max = icst525_hz(&cclk_params, vco) / 1000; + vco = icst_hz_to_vco(&cclk_params, policy->max * 1000); + policy->max = icst_hz(&cclk_params, vco) / 1000; - vco = icst525_hz_to_vco(&cclk_params, policy->min * 1000); - policy->min = icst525_hz(&cclk_params, vco) / 1000; + vco = icst_hz_to_vco(&cclk_params, policy->min * 1000); + policy->min = icst_hz(&cclk_params, vco) / 1000; cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, @@ -112,17 +112,17 @@ static int integrator_set_target(struct cpufreq_policy *policy, } vco.v = cm_osc & 255; vco.r = 22; - freqs.old = icst525_hz(&cclk_params, vco) / 1000; + freqs.old = icst_hz(&cclk_params, vco) / 1000; - /* icst525_hz_to_vco rounds down -- so we need the next + /* icst_hz_to_vco rounds down -- so we need the next * larger freq in case of CPUFREQ_RELATION_L. */ if (relation == CPUFREQ_RELATION_L) target_freq += 999; if (target_freq > policy->max) target_freq = policy->max; - vco = icst525_hz_to_vco(&cclk_params, target_freq * 1000); - freqs.new = icst525_hz(&cclk_params, vco) / 1000; + vco = icst_hz_to_vco(&cclk_params, target_freq * 1000); + freqs.new = icst_hz(&cclk_params, vco) / 1000; freqs.cpu = policy->cpu; @@ -180,7 +180,7 @@ static unsigned int integrator_get(unsigned int cpu) vco.v = cm_osc & 255; vco.r = 22; - current_freq = icst525_hz(&cclk_params, vco) / 1000; /* current freq */ + current_freq = icst_hz(&cclk_params, vco) / 1000; /* current freq */ set_cpus_allowed(current, cpus_allowed); diff --git a/arch/arm/mach-integrator/impd1.c b/arch/arm/mach-integrator/impd1.c index e24cfc357ff..3e211021685 100644 --- a/arch/arm/mach-integrator/impd1.c +++ b/arch/arm/mach-integrator/impd1.c @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c index 9997d1f6c18..a9ab8fda399 100644 --- a/arch/arm/mach-integrator/integrator_cp.c +++ b/arch/arm/mach-integrator/integrator_cp.c @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/arch/arm/mach-realview/clock.c b/arch/arm/mach-realview/clock.c index 2ba0667d18f..18c545921e4 100644 --- a/arch/arm/mach-realview/clock.c +++ b/arch/arm/mach-realview/clock.c @@ -18,7 +18,7 @@ #include #include -#include +#include #include "clock.h" @@ -42,8 +42,8 @@ EXPORT_SYMBOL(clk_get_rate); long clk_round_rate(struct clk *clk, unsigned long rate) { struct icst_vco vco; - vco = icst307_hz_to_vco(clk->params, rate); - return icst307_hz(clk->params, vco); + vco = icst_hz_to_vco(clk->params, rate); + return icst_hz(clk->params, vco); } EXPORT_SYMBOL(clk_round_rate); @@ -54,8 +54,8 @@ int clk_set_rate(struct clk *clk, unsigned long rate) if (clk->setvco) { struct icst_vco vco; - vco = icst307_hz_to_vco(clk->params, rate); - clk->rate = icst307_hz(clk->params, vco); + vco = icst_hz_to_vco(clk->params, rate); + clk->rate = icst_hz(clk->params, vco); clk->setvco(clk, vco); ret = 0; } diff --git a/arch/arm/mach-realview/core.c b/arch/arm/mach-realview/core.c index 26d44caad54..a8c215a406a 100644 --- a/arch/arm/mach-realview/core.c +++ b/arch/arm/mach-realview/core.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include #include diff --git a/arch/arm/mach-realview/realview_eb.c b/arch/arm/mach-realview/realview_eb.c index 7d857d30055..39d953c89d9 100644 --- a/arch/arm/mach-realview/realview_eb.c +++ b/arch/arm/mach-realview/realview_eb.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include diff --git a/arch/arm/mach-realview/realview_pb1176.c b/arch/arm/mach-realview/realview_pb1176.c index 44392e51dd5..a93aac5f473 100644 --- a/arch/arm/mach-realview/realview_pb1176.c +++ b/arch/arm/mach-realview/realview_pb1176.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include diff --git a/arch/arm/mach-realview/realview_pb11mp.c b/arch/arm/mach-realview/realview_pb11mp.c index 3e02731af95..c7c656e235c 100644 --- a/arch/arm/mach-realview/realview_pb11mp.c +++ b/arch/arm/mach-realview/realview_pb11mp.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include diff --git a/arch/arm/mach-realview/realview_pba8.c b/arch/arm/mach-realview/realview_pba8.c index fe4e25c4201..3e3aaa3a2bc 100644 --- a/arch/arm/mach-realview/realview_pba8.c +++ b/arch/arm/mach-realview/realview_pba8.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include diff --git a/arch/arm/mach-versatile/clock.c b/arch/arm/mach-versatile/clock.c index 82753be453d..adc67d771c7 100644 --- a/arch/arm/mach-versatile/clock.c +++ b/arch/arm/mach-versatile/clock.c @@ -19,7 +19,7 @@ #include #include -#include +#include #include "clock.h" @@ -43,8 +43,8 @@ EXPORT_SYMBOL(clk_get_rate); long clk_round_rate(struct clk *clk, unsigned long rate) { struct icst_vco vco; - vco = icst307_hz_to_vco(clk->params, rate); - return icst307_hz(clk->params, vco); + vco = icst_hz_to_vco(clk->params, rate); + return icst_hz(clk->params, vco); } EXPORT_SYMBOL(clk_round_rate); @@ -55,8 +55,8 @@ int clk_set_rate(struct clk *clk, unsigned long rate) if (clk->setvco) { struct icst_vco vco; - vco = icst307_hz_to_vco(clk->params, rate); - clk->rate = icst307_hz(clk->params, vco); + vco = icst_hz_to_vco(clk->params, rate); + clk->rate = icst_hz(clk->params, vco); clk->setvco(clk, vco); ret = 0; } diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c index 51d7aeb6a50..3c67691d4e7 100644 --- a/arch/arm/mach-versatile/core.c +++ b/arch/arm/mach-versatile/core.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include -- cgit v1.2.3-70-g09d2 From 75070612c49c0bb976bb9221d051b20316ad1bc7 Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Fri, 14 May 2010 15:18:54 +0100 Subject: ARM: 6132/1: PL330: Add common core driver PL330 is a configurable DMA controller PrimeCell device. The register map of the device is well defined. The configuration of a particular implementation can be read from the six configuration registers CR0-4,Dn. This patch implements a driver for the specification:- http://infocenter.arm.com/help/topic/com.arm.doc.ddi0424a/DDI0424A_dmac_pl330_r0p0_trm.pdf The exported interface should be sufficient to implement a driver for any DMA API. Signed-off-by: Jassi Brar Signed-off-by: Russell King --- arch/arm/common/Kconfig | 3 + arch/arm/common/Makefile | 1 + arch/arm/common/pl330.c | 1966 +++++++++++++++++++++++++++++++++ arch/arm/include/asm/hardware/pl330.h | 217 ++++ 4 files changed, 2187 insertions(+) create mode 100644 arch/arm/common/pl330.c create mode 100644 arch/arm/include/asm/hardware/pl330.h (limited to 'arch/arm/common') diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig index b64bb78d784..cf905909188 100644 --- a/arch/arm/common/Kconfig +++ b/arch/arm/common/Kconfig @@ -18,6 +18,9 @@ config ICST525 config ICST307 bool +config PL330 + bool + config SA1111 bool select DMABOUNCE if !ARCH_PXA diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index 76be7ff2a7c..df20c265020 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_ARM_GIC) += gic.o obj-$(CONFIG_ARM_VIC) += vic.o obj-$(CONFIG_ICST525) += icst525.o obj-$(CONFIG_ICST307) += icst307.o +obj-$(CONFIG_PL330) += pl330.o obj-$(CONFIG_SA1111) += sa1111.o obj-$(CONFIG_PCI_HOST_VIA82C505) += via82c505.o obj-$(CONFIG_DMABOUNCE) += dmabounce.o diff --git a/arch/arm/common/pl330.c b/arch/arm/common/pl330.c new file mode 100644 index 00000000000..5ebbab6242a --- /dev/null +++ b/arch/arm/common/pl330.c @@ -0,0 +1,1966 @@ +/* linux/arch/arm/common/pl330.c + * + * Copyright (C) 2010 Samsung Electronics Co Ltd. + * Jaswinder Singh + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Register and Bit field Definitions */ +#define DS 0x0 +#define DS_ST_STOP 0x0 +#define DS_ST_EXEC 0x1 +#define DS_ST_CMISS 0x2 +#define DS_ST_UPDTPC 0x3 +#define DS_ST_WFE 0x4 +#define DS_ST_ATBRR 0x5 +#define DS_ST_QBUSY 0x6 +#define DS_ST_WFP 0x7 +#define DS_ST_KILL 0x8 +#define DS_ST_CMPLT 0x9 +#define DS_ST_FLTCMP 0xe +#define DS_ST_FAULT 0xf + +#define DPC 0x4 +#define INTEN 0x20 +#define ES 0x24 +#define INTSTATUS 0x28 +#define INTCLR 0x2c +#define FSM 0x30 +#define FSC 0x34 +#define FTM 0x38 + +#define _FTC 0x40 +#define FTC(n) (_FTC + (n)*0x4) + +#define _CS 0x100 +#define CS(n) (_CS + (n)*0x8) +#define CS_CNS (1 << 21) + +#define _CPC 0x104 +#define CPC(n) (_CPC + (n)*0x8) + +#define _SA 0x400 +#define SA(n) (_SA + (n)*0x20) + +#define _DA 0x404 +#define DA(n) (_DA + (n)*0x20) + +#define _CC 0x408 +#define CC(n) (_CC + (n)*0x20) + +#define CC_SRCINC (1 << 0) +#define CC_DSTINC (1 << 14) +#define CC_SRCPRI (1 << 8) +#define CC_DSTPRI (1 << 22) +#define CC_SRCNS (1 << 9) +#define CC_DSTNS (1 << 23) +#define CC_SRCIA (1 << 10) +#define CC_DSTIA (1 << 24) +#define CC_SRCBRSTLEN_SHFT 4 +#define CC_DSTBRSTLEN_SHFT 18 +#define CC_SRCBRSTSIZE_SHFT 1 +#define CC_DSTBRSTSIZE_SHFT 15 +#define CC_SRCCCTRL_SHFT 11 +#define CC_SRCCCTRL_MASK 0x7 +#define CC_DSTCCTRL_SHFT 25 +#define CC_DRCCCTRL_MASK 0x7 +#define CC_SWAP_SHFT 28 + +#define _LC0 0x40c +#define LC0(n) (_LC0 + (n)*0x20) + +#define _LC1 0x410 +#define LC1(n) (_LC1 + (n)*0x20) + +#define DBGSTATUS 0xd00 +#define DBG_BUSY (1 << 0) + +#define DBGCMD 0xd04 +#define DBGINST0 0xd08 +#define DBGINST1 0xd0c + +#define CR0 0xe00 +#define CR1 0xe04 +#define CR2 0xe08 +#define CR3 0xe0c +#define CR4 0xe10 +#define CRD 0xe14 + +#define PERIPH_ID 0xfe0 +#define PCELL_ID 0xff0 + +#define CR0_PERIPH_REQ_SET (1 << 0) +#define CR0_BOOT_EN_SET (1 << 1) +#define CR0_BOOT_MAN_NS (1 << 2) +#define CR0_NUM_CHANS_SHIFT 4 +#define CR0_NUM_CHANS_MASK 0x7 +#define CR0_NUM_PERIPH_SHIFT 12 +#define CR0_NUM_PERIPH_MASK 0x1f +#define CR0_NUM_EVENTS_SHIFT 17 +#define CR0_NUM_EVENTS_MASK 0x1f + +#define CR1_ICACHE_LEN_SHIFT 0 +#define CR1_ICACHE_LEN_MASK 0x7 +#define CR1_NUM_ICACHELINES_SHIFT 4 +#define CR1_NUM_ICACHELINES_MASK 0xf + +#define CRD_DATA_WIDTH_SHIFT 0 +#define CRD_DATA_WIDTH_MASK 0x7 +#define CRD_WR_CAP_SHIFT 4 +#define CRD_WR_CAP_MASK 0x7 +#define CRD_WR_Q_DEP_SHIFT 8 +#define CRD_WR_Q_DEP_MASK 0xf +#define CRD_RD_CAP_SHIFT 12 +#define CRD_RD_CAP_MASK 0x7 +#define CRD_RD_Q_DEP_SHIFT 16 +#define CRD_RD_Q_DEP_MASK 0xf +#define CRD_DATA_BUFF_SHIFT 20 +#define CRD_DATA_BUFF_MASK 0x3ff + +#define PART 0x330 +#define DESIGNER 0x41 +#define REVISION 0x0 +#define INTEG_CFG 0x0 +#define PERIPH_ID_VAL ((PART << 0) | (DESIGNER << 12) \ + | (REVISION << 20) | (INTEG_CFG << 24)) + +#define PCELL_ID_VAL 0xb105f00d + +#define PL330_STATE_STOPPED (1 << 0) +#define PL330_STATE_EXECUTING (1 << 1) +#define PL330_STATE_WFE (1 << 2) +#define PL330_STATE_FAULTING (1 << 3) +#define PL330_STATE_COMPLETING (1 << 4) +#define PL330_STATE_WFP (1 << 5) +#define PL330_STATE_KILLING (1 << 6) +#define PL330_STATE_FAULT_COMPLETING (1 << 7) +#define PL330_STATE_CACHEMISS (1 << 8) +#define PL330_STATE_UPDTPC (1 << 9) +#define PL330_STATE_ATBARRIER (1 << 10) +#define PL330_STATE_QUEUEBUSY (1 << 11) +#define PL330_STATE_INVALID (1 << 15) + +#define PL330_STABLE_STATES (PL330_STATE_STOPPED | PL330_STATE_EXECUTING \ + | PL330_STATE_WFE | PL330_STATE_FAULTING) + +#define CMD_DMAADDH 0x54 +#define CMD_DMAEND 0x00 +#define CMD_DMAFLUSHP 0x35 +#define CMD_DMAGO 0xa0 +#define CMD_DMALD 0x04 +#define CMD_DMALDP 0x25 +#define CMD_DMALP 0x20 +#define CMD_DMALPEND 0x28 +#define CMD_DMAKILL 0x01 +#define CMD_DMAMOV 0xbc +#define CMD_DMANOP 0x18 +#define CMD_DMARMB 0x12 +#define CMD_DMASEV 0x34 +#define CMD_DMAST 0x08 +#define CMD_DMASTP 0x29 +#define CMD_DMASTZ 0x0c +#define CMD_DMAWFE 0x36 +#define CMD_DMAWFP 0x30 +#define CMD_DMAWMB 0x13 + +#define SZ_DMAADDH 3 +#define SZ_DMAEND 1 +#define SZ_DMAFLUSHP 2 +#define SZ_DMALD 1 +#define SZ_DMALDP 2 +#define SZ_DMALP 2 +#define SZ_DMALPEND 2 +#define SZ_DMAKILL 1 +#define SZ_DMAMOV 6 +#define SZ_DMANOP 1 +#define SZ_DMARMB 1 +#define SZ_DMASEV 2 +#define SZ_DMAST 1 +#define SZ_DMASTP 2 +#define SZ_DMASTZ 1 +#define SZ_DMAWFE 2 +#define SZ_DMAWFP 2 +#define SZ_DMAWMB 1 +#define SZ_DMAGO 6 + +#define BRST_LEN(ccr) ((((ccr) >> CC_SRCBRSTLEN_SHFT) & 0xf) + 1) +#define BRST_SIZE(ccr) (1 << (((ccr) >> CC_SRCBRSTSIZE_SHFT) & 0x7)) + +#define BYTE_TO_BURST(b, ccr) ((b) / BRST_SIZE(ccr) / BRST_LEN(ccr)) +#define BURST_TO_BYTE(c, ccr) ((c) * BRST_SIZE(ccr) * BRST_LEN(ccr)) + +/* + * With 256 bytes, we can do more than 2.5MB and 5MB xfers per req + * at 1byte/burst for P<->M and M<->M respectively. + * For typical scenario, at 1word/burst, 10MB and 20MB xfers per req + * should be enough for P<->M and M<->M respectively. + */ +#define MCODE_BUFF_PER_REQ 256 + +/* + * Mark a _pl330_req as free. + * We do it by writing DMAEND as the first instruction + * because no valid request is going to have DMAEND as + * its first instruction to execute. + */ +#define MARK_FREE(req) do { \ + _emit_END(0, (req)->mc_cpu); \ + (req)->mc_len = 0; \ + } while (0) + +/* If the _pl330_req is available to the client */ +#define IS_FREE(req) (*((u8 *)((req)->mc_cpu)) == CMD_DMAEND) + +/* Use this _only_ to wait on transient states */ +#define UNTIL(t, s) while (!(_state(t) & (s))) cpu_relax(); + +#ifdef PL330_DEBUG_MCGEN +static unsigned cmd_line; +#define PL330_DBGCMD_DUMP(off, x...) do { \ + printk("%x:", cmd_line); \ + printk(x); \ + cmd_line += off; \ + } while (0) +#define PL330_DBGMC_START(addr) (cmd_line = addr) +#else +#define PL330_DBGCMD_DUMP(off, x...) do {} while (0) +#define PL330_DBGMC_START(addr) do {} while (0) +#endif + +struct _xfer_spec { + u32 ccr; + struct pl330_req *r; + struct pl330_xfer *x; +}; + +enum dmamov_dst { + SAR = 0, + CCR, + DAR, +}; + +enum pl330_dst { + SRC = 0, + DST, +}; + +enum pl330_cond { + SINGLE, + BURST, + ALWAYS, +}; + +struct _pl330_req { + u32 mc_bus; + void *mc_cpu; + /* Number of bytes taken to setup MC for the req */ + u32 mc_len; + struct pl330_req *r; + /* Hook to attach to DMAC's list of reqs with due callback */ + struct list_head rqd; +}; + +/* ToBeDone for tasklet */ +struct _pl330_tbd { + bool reset_dmac; + bool reset_mngr; + u8 reset_chan; +}; + +/* A DMAC Thread */ +struct pl330_thread { + u8 id; + int ev; + /* If the channel is not yet acquired by any client */ + bool free; + /* Parent DMAC */ + struct pl330_dmac *dmac; + /* Only two at a time */ + struct _pl330_req req[2]; + /* Index of the last submitted request */ + unsigned lstenq; +}; + +enum pl330_dmac_state { + UNINIT, + INIT, + DYING, +}; + +/* A DMAC */ +struct pl330_dmac { + spinlock_t lock; + /* Holds list of reqs with due callbacks */ + struct list_head req_done; + /* Pointer to platform specific stuff */ + struct pl330_info *pinfo; + /* Maximum possible events/irqs */ + int events[32]; + /* BUS address of MicroCode buffer */ + u32 mcode_bus; + /* CPU address of MicroCode buffer */ + void *mcode_cpu; + /* List of all Channel threads */ + struct pl330_thread *channels; + /* Pointer to the MANAGER thread */ + struct pl330_thread *manager; + /* To handle bad news in interrupt */ + struct tasklet_struct tasks; + struct _pl330_tbd dmac_tbd; + /* State of DMAC operation */ + enum pl330_dmac_state state; +}; + +static inline void _callback(struct pl330_req *r, enum pl330_op_err err) +{ + if (r && r->xfer_cb) + r->xfer_cb(r->token, err); +} + +static inline bool _queue_empty(struct pl330_thread *thrd) +{ + return (IS_FREE(&thrd->req[0]) && IS_FREE(&thrd->req[1])) + ? true : false; +} + +static inline bool _queue_full(struct pl330_thread *thrd) +{ + return (IS_FREE(&thrd->req[0]) || IS_FREE(&thrd->req[1])) + ? false : true; +} + +static inline bool is_manager(struct pl330_thread *thrd) +{ + struct pl330_dmac *pl330 = thrd->dmac; + + /* MANAGER is indexed at the end */ + if (thrd->id == pl330->pinfo->pcfg.num_chan) + return true; + else + return false; +} + +/* If manager of the thread is in Non-Secure mode */ +static inline bool _manager_ns(struct pl330_thread *thrd) +{ + struct pl330_dmac *pl330 = thrd->dmac; + + return (pl330->pinfo->pcfg.mode & DMAC_MODE_NS) ? true : false; +} + +static inline u32 get_id(struct pl330_info *pi, u32 off) +{ + void __iomem *regs = pi->base; + u32 id = 0; + + id |= (readb(regs + off + 0x0) << 0); + id |= (readb(regs + off + 0x4) << 8); + id |= (readb(regs + off + 0x8) << 16); + id |= (readb(regs + off + 0xc) << 24); + + return id; +} + +static inline u32 _emit_ADDH(unsigned dry_run, u8 buf[], + enum pl330_dst da, u16 val) +{ + if (dry_run) + return SZ_DMAADDH; + + buf[0] = CMD_DMAADDH; + buf[0] |= (da << 1); + *((u16 *)&buf[1]) = val; + + PL330_DBGCMD_DUMP(SZ_DMAADDH, "\tDMAADDH %s %u\n", + da == 1 ? "DA" : "SA", val); + + return SZ_DMAADDH; +} + +static inline u32 _emit_END(unsigned dry_run, u8 buf[]) +{ + if (dry_run) + return SZ_DMAEND; + + buf[0] = CMD_DMAEND; + + PL330_DBGCMD_DUMP(SZ_DMAEND, "\tDMAEND\n"); + + return SZ_DMAEND; +} + +static inline u32 _emit_FLUSHP(unsigned dry_run, u8 buf[], u8 peri) +{ + if (dry_run) + return SZ_DMAFLUSHP; + + buf[0] = CMD_DMAFLUSHP; + + peri &= 0x1f; + peri <<= 3; + buf[1] = peri; + + PL330_DBGCMD_DUMP(SZ_DMAFLUSHP, "\tDMAFLUSHP %u\n", peri >> 3); + + return SZ_DMAFLUSHP; +} + +static inline u32 _emit_LD(unsigned dry_run, u8 buf[], enum pl330_cond cond) +{ + if (dry_run) + return SZ_DMALD; + + buf[0] = CMD_DMALD; + + if (cond == SINGLE) + buf[0] |= (0 << 1) | (1 << 0); + else if (cond == BURST) + buf[0] |= (1 << 1) | (1 << 0); + + PL330_DBGCMD_DUMP(SZ_DMALD, "\tDMALD%c\n", + cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'A')); + + return SZ_DMALD; +} + +static inline u32 _emit_LDP(unsigned dry_run, u8 buf[], + enum pl330_cond cond, u8 peri) +{ + if (dry_run) + return SZ_DMALDP; + + buf[0] = CMD_DMALDP; + + if (cond == BURST) + buf[0] |= (1 << 1); + + peri &= 0x1f; + peri <<= 3; + buf[1] = peri; + + PL330_DBGCMD_DUMP(SZ_DMALDP, "\tDMALDP%c %u\n", + cond == SINGLE ? 'S' : 'B', peri >> 3); + + return SZ_DMALDP; +} + +static inline u32 _emit_LP(unsigned dry_run, u8 buf[], + unsigned loop, u8 cnt) +{ + if (dry_run) + return SZ_DMALP; + + buf[0] = CMD_DMALP; + + if (loop) + buf[0] |= (1 << 1); + + cnt--; /* DMAC increments by 1 internally */ + buf[1] = cnt; + + PL330_DBGCMD_DUMP(SZ_DMALP, "\tDMALP_%c %u\n", loop ? '1' : '0', cnt); + + return SZ_DMALP; +} + +struct _arg_LPEND { + enum pl330_cond cond; + bool forever; + unsigned loop; + u8 bjump; +}; + +static inline u32 _emit_LPEND(unsigned dry_run, u8 buf[], + const struct _arg_LPEND *arg) +{ + enum pl330_cond cond = arg->cond; + bool forever = arg->forever; + unsigned loop = arg->loop; + u8 bjump = arg->bjump; + + if (dry_run) + return SZ_DMALPEND; + + buf[0] = CMD_DMALPEND; + + if (loop) + buf[0] |= (1 << 2); + + if (!forever) + buf[0] |= (1 << 4); + + if (cond == SINGLE) + buf[0] |= (0 << 1) | (1 << 0); + else if (cond == BURST) + buf[0] |= (1 << 1) | (1 << 0); + + buf[1] = bjump; + + PL330_DBGCMD_DUMP(SZ_DMALPEND, "\tDMALP%s%c_%c bjmpto_%x\n", + forever ? "FE" : "END", + cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'A'), + loop ? '1' : '0', + bjump); + + return SZ_DMALPEND; +} + +static inline u32 _emit_KILL(unsigned dry_run, u8 buf[]) +{ + if (dry_run) + return SZ_DMAKILL; + + buf[0] = CMD_DMAKILL; + + return SZ_DMAKILL; +} + +static inline u32 _emit_MOV(unsigned dry_run, u8 buf[], + enum dmamov_dst dst, u32 val) +{ + if (dry_run) + return SZ_DMAMOV; + + buf[0] = CMD_DMAMOV; + buf[1] = dst; + *((u32 *)&buf[2]) = val; + + PL330_DBGCMD_DUMP(SZ_DMAMOV, "\tDMAMOV %s 0x%x\n", + dst == SAR ? "SAR" : (dst == DAR ? "DAR" : "CCR"), val); + + return SZ_DMAMOV; +} + +static inline u32 _emit_NOP(unsigned dry_run, u8 buf[]) +{ + if (dry_run) + return SZ_DMANOP; + + buf[0] = CMD_DMANOP; + + PL330_DBGCMD_DUMP(SZ_DMANOP, "\tDMANOP\n"); + + return SZ_DMANOP; +} + +static inline u32 _emit_RMB(unsigned dry_run, u8 buf[]) +{ + if (dry_run) + return SZ_DMARMB; + + buf[0] = CMD_DMARMB; + + PL330_DBGCMD_DUMP(SZ_DMARMB, "\tDMARMB\n"); + + return SZ_DMARMB; +} + +static inline u32 _emit_SEV(unsigned dry_run, u8 buf[], u8 ev) +{ + if (dry_run) + return SZ_DMASEV; + + buf[0] = CMD_DMASEV; + + ev &= 0x1f; + ev <<= 3; + buf[1] = ev; + + PL330_DBGCMD_DUMP(SZ_DMASEV, "\tDMASEV %u\n", ev >> 3); + + return SZ_DMASEV; +} + +static inline u32 _emit_ST(unsigned dry_run, u8 buf[], enum pl330_cond cond) +{ + if (dry_run) + return SZ_DMAST; + + buf[0] = CMD_DMAST; + + if (cond == SINGLE) + buf[0] |= (0 << 1) | (1 << 0); + else if (cond == BURST) + buf[0] |= (1 << 1) | (1 << 0); + + PL330_DBGCMD_DUMP(SZ_DMAST, "\tDMAST%c\n", + cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'A')); + + return SZ_DMAST; +} + +static inline u32 _emit_STP(unsigned dry_run, u8 buf[], + enum pl330_cond cond, u8 peri) +{ + if (dry_run) + return SZ_DMASTP; + + buf[0] = CMD_DMASTP; + + if (cond == BURST) + buf[0] |= (1 << 1); + + peri &= 0x1f; + peri <<= 3; + buf[1] = peri; + + PL330_DBGCMD_DUMP(SZ_DMASTP, "\tDMASTP%c %u\n", + cond == SINGLE ? 'S' : 'B', peri >> 3); + + return SZ_DMASTP; +} + +static inline u32 _emit_STZ(unsigned dry_run, u8 buf[]) +{ + if (dry_run) + return SZ_DMASTZ; + + buf[0] = CMD_DMASTZ; + + PL330_DBGCMD_DUMP(SZ_DMASTZ, "\tDMASTZ\n"); + + return SZ_DMASTZ; +} + +static inline u32 _emit_WFE(unsigned dry_run, u8 buf[], u8 ev, + unsigned invalidate) +{ + if (dry_run) + return SZ_DMAWFE; + + buf[0] = CMD_DMAWFE; + + ev &= 0x1f; + ev <<= 3; + buf[1] = ev; + + if (invalidate) + buf[1] |= (1 << 1); + + PL330_DBGCMD_DUMP(SZ_DMAWFE, "\tDMAWFE %u%s\n", + ev >> 3, invalidate ? ", I" : ""); + + return SZ_DMAWFE; +} + +static inline u32 _emit_WFP(unsigned dry_run, u8 buf[], + enum pl330_cond cond, u8 peri) +{ + if (dry_run) + return SZ_DMAWFP; + + buf[0] = CMD_DMAWFP; + + if (cond == SINGLE) + buf[0] |= (0 << 1) | (0 << 0); + else if (cond == BURST) + buf[0] |= (1 << 1) | (0 << 0); + else + buf[0] |= (0 << 1) | (1 << 0); + + peri &= 0x1f; + peri <<= 3; + buf[1] = peri; + + PL330_DBGCMD_DUMP(SZ_DMAWFP, "\tDMAWFP%c %u\n", + cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'P'), peri >> 3); + + return SZ_DMAWFP; +} + +static inline u32 _emit_WMB(unsigned dry_run, u8 buf[]) +{ + if (dry_run) + return SZ_DMAWMB; + + buf[0] = CMD_DMAWMB; + + PL330_DBGCMD_DUMP(SZ_DMAWMB, "\tDMAWMB\n"); + + return SZ_DMAWMB; +} + +struct _arg_GO { + u8 chan; + u32 addr; + unsigned ns; +}; + +static inline u32 _emit_GO(unsigned dry_run, u8 buf[], + const struct _arg_GO *arg) +{ + u8 chan = arg->chan; + u32 addr = arg->addr; + unsigned ns = arg->ns; + + if (dry_run) + return SZ_DMAGO; + + buf[0] = CMD_DMAGO; + buf[0] |= (ns << 1); + + buf[1] = chan & 0x7; + + *((u32 *)&buf[2]) = addr; + + return SZ_DMAGO; +} + +#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) + +/* Returns Time-Out */ +static bool _until_dmac_idle(struct pl330_thread *thrd) +{ + void __iomem *regs = thrd->dmac->pinfo->base; + unsigned long loops = msecs_to_loops(5); + + do { + /* Until Manager is Idle */ + if (!(readl(regs + DBGSTATUS) & DBG_BUSY)) + break; + + cpu_relax(); + } while (--loops); + + if (!loops) + return true; + + return false; +} + +static inline void _execute_DBGINSN(struct pl330_thread *thrd, + u8 insn[], bool as_manager) +{ + void __iomem *regs = thrd->dmac->pinfo->base; + u32 val; + + val = (insn[0] << 16) | (insn[1] << 24); + if (!as_manager) { + val |= (1 << 0); + val |= (thrd->id << 8); /* Channel Number */ + } + writel(val, regs + DBGINST0); + + val = *((u32 *)&insn[2]); + writel(val, regs + DBGINST1); + + /* If timed out due to halted state-machine */ + if (_until_dmac_idle(thrd)) { + dev_err(thrd->dmac->pinfo->dev, "DMAC halted!\n"); + return; + } + + /* Get going */ + writel(0, regs + DBGCMD); +} + +static inline u32 _state(struct pl330_thread *thrd) +{ + void __iomem *regs = thrd->dmac->pinfo->base; + u32 val; + + if (is_manager(thrd)) + val = readl(regs + DS) & 0xf; + else + val = readl(regs + CS(thrd->id)) & 0xf; + + switch (val) { + case DS_ST_STOP: + return PL330_STATE_STOPPED; + case DS_ST_EXEC: + return PL330_STATE_EXECUTING; + case DS_ST_CMISS: + return PL330_STATE_CACHEMISS; + case DS_ST_UPDTPC: + return PL330_STATE_UPDTPC; + case DS_ST_WFE: + return PL330_STATE_WFE; + case DS_ST_FAULT: + return PL330_STATE_FAULTING; + case DS_ST_ATBRR: + if (is_manager(thrd)) + return PL330_STATE_INVALID; + else + return PL330_STATE_ATBARRIER; + case DS_ST_QBUSY: + if (is_manager(thrd)) + return PL330_STATE_INVALID; + else + return PL330_STATE_QUEUEBUSY; + case DS_ST_WFP: + if (is_manager(thrd)) + return PL330_STATE_INVALID; + else + return PL330_STATE_WFP; + case DS_ST_KILL: + if (is_manager(thrd)) + return PL330_STATE_INVALID; + else + return PL330_STATE_KILLING; + case DS_ST_CMPLT: + if (is_manager(thrd)) + return PL330_STATE_INVALID; + else + return PL330_STATE_COMPLETING; + case DS_ST_FLTCMP: + if (is_manager(thrd)) + return PL330_STATE_INVALID; + else + return PL330_STATE_FAULT_COMPLETING; + default: + return PL330_STATE_INVALID; + } +} + +/* If the request 'req' of thread 'thrd' is currently active */ +static inline bool _req_active(struct pl330_thread *thrd, + struct _pl330_req *req) +{ + void __iomem *regs = thrd->dmac->pinfo->base; + u32 buf = req->mc_bus, pc = readl(regs + CPC(thrd->id)); + + if (IS_FREE(req)) + return false; + + return (pc >= buf && pc <= buf + req->mc_len) ? true : false; +} + +/* Returns 0 if the thread is inactive, ID of active req + 1 otherwise */ +static inline unsigned _thrd_active(struct pl330_thread *thrd) +{ + if (_req_active(thrd, &thrd->req[0])) + return 1; /* First req active */ + + if (_req_active(thrd, &thrd->req[1])) + return 2; /* Second req active */ + + return 0; +} + +static void _stop(struct pl330_thread *thrd) +{ + void __iomem *regs = thrd->dmac->pinfo->base; + u8 insn[6] = {0, 0, 0, 0, 0, 0}; + + if (_state(thrd) == PL330_STATE_FAULT_COMPLETING) + UNTIL(thrd, PL330_STATE_FAULTING | PL330_STATE_KILLING); + + /* Return if nothing needs to be done */ + if (_state(thrd) == PL330_STATE_COMPLETING + || _state(thrd) == PL330_STATE_KILLING + || _state(thrd) == PL330_STATE_STOPPED) + return; + + _emit_KILL(0, insn); + + /* Stop generating interrupts for SEV */ + writel(readl(regs + INTEN) & ~(1 << thrd->ev), regs + INTEN); + + _execute_DBGINSN(thrd, insn, is_manager(thrd)); +} + +/* Start doing req 'idx' of thread 'thrd' */ +static bool _trigger(struct pl330_thread *thrd) +{ + void __iomem *regs = thrd->dmac->pinfo->base; + struct _pl330_req *req; + struct pl330_req *r; + struct _arg_GO go; + unsigned ns; + u8 insn[6] = {0, 0, 0, 0, 0, 0}; + + /* Return if already ACTIVE */ + if (_state(thrd) != PL330_STATE_STOPPED) + return true; + + if (!IS_FREE(&thrd->req[1 - thrd->lstenq])) + req = &thrd->req[1 - thrd->lstenq]; + else if (!IS_FREE(&thrd->req[thrd->lstenq])) + req = &thrd->req[thrd->lstenq]; + else + req = NULL; + + /* Return if no request */ + if (!req || !req->r) + return true; + + r = req->r; + + if (r->cfg) + ns = r->cfg->nonsecure ? 1 : 0; + else if (readl(regs + CS(thrd->id)) & CS_CNS) + ns = 1; + else + ns = 0; + + /* See 'Abort Sources' point-4 at Page 2-25 */ + if (_manager_ns(thrd) && !ns) + dev_info(thrd->dmac->pinfo->dev, "%s:%d Recipe for ABORT!\n", + __func__, __LINE__); + + go.chan = thrd->id; + go.addr = req->mc_bus; + go.ns = ns; + _emit_GO(0, insn, &go); + + /* Set to generate interrupts for SEV */ + writel(readl(regs + INTEN) | (1 << thrd->ev), regs + INTEN); + + /* Only manager can execute GO */ + _execute_DBGINSN(thrd, insn, true); + + return true; +} + +static bool _start(struct pl330_thread *thrd) +{ + switch (_state(thrd)) { + case PL330_STATE_FAULT_COMPLETING: + UNTIL(thrd, PL330_STATE_FAULTING | PL330_STATE_KILLING); + + if (_state(thrd) == PL330_STATE_KILLING) + UNTIL(thrd, PL330_STATE_STOPPED) + + case PL330_STATE_FAULTING: + _stop(thrd); + + case PL330_STATE_KILLING: + case PL330_STATE_COMPLETING: + UNTIL(thrd, PL330_STATE_STOPPED) + + case PL330_STATE_STOPPED: + return _trigger(thrd); + + case PL330_STATE_WFP: + case PL330_STATE_QUEUEBUSY: + case PL330_STATE_ATBARRIER: + case PL330_STATE_UPDTPC: + case PL330_STATE_CACHEMISS: + case PL330_STATE_EXECUTING: + return true; + + case PL330_STATE_WFE: /* For RESUME, nothing yet */ + default: + return false; + } +} + +static inline int _ldst_memtomem(unsigned dry_run, u8 buf[], + const struct _xfer_spec *pxs, int cyc) +{ + int off = 0; + + while (cyc--) { + off += _emit_LD(dry_run, &buf[off], ALWAYS); + off += _emit_RMB(dry_run, &buf[off]); + off += _emit_ST(dry_run, &buf[off], ALWAYS); + off += _emit_WMB(dry_run, &buf[off]); + } + + return off; +} + +static inline int _ldst_devtomem(unsigned dry_run, u8 buf[], + const struct _xfer_spec *pxs, int cyc) +{ + int off = 0; + + while (cyc--) { + off += _emit_WFP(dry_run, &buf[off], SINGLE, pxs->r->peri); + off += _emit_LDP(dry_run, &buf[off], SINGLE, pxs->r->peri); + off += _emit_ST(dry_run, &buf[off], ALWAYS); + off += _emit_FLUSHP(dry_run, &buf[off], pxs->r->peri); + } + + return off; +} + +static inline int _ldst_memtodev(unsigned dry_run, u8 buf[], + const struct _xfer_spec *pxs, int cyc) +{ + int off = 0; + + while (cyc--) { + off += _emit_WFP(dry_run, &buf[off], SINGLE, pxs->r->peri); + off += _emit_LD(dry_run, &buf[off], ALWAYS); + off += _emit_STP(dry_run, &buf[off], SINGLE, pxs->r->peri); + off += _emit_FLUSHP(dry_run, &buf[off], pxs->r->peri); + } + + return off; +} + +static int _bursts(unsigned dry_run, u8 buf[], + const struct _xfer_spec *pxs, int cyc) +{ + int off = 0; + + switch (pxs->r->rqtype) { + case MEMTODEV: + off += _ldst_memtodev(dry_run, &buf[off], pxs, cyc); + break; + case DEVTOMEM: + off += _ldst_devtomem(dry_run, &buf[off], pxs, cyc); + break; + case MEMTOMEM: + off += _ldst_memtomem(dry_run, &buf[off], pxs, cyc); + break; + default: + off += 0x40000000; /* Scare off the Client */ + break; + } + + return off; +} + +/* Returns bytes consumed and updates bursts */ +static inline int _loop(unsigned dry_run, u8 buf[], + unsigned long *bursts, const struct _xfer_spec *pxs) +{ + int cyc, cycmax, szlp, szlpend, szbrst, off; + unsigned lcnt0, lcnt1, ljmp0, ljmp1; + struct _arg_LPEND lpend; + + /* Max iterations possibile in DMALP is 256 */ + if (*bursts >= 256*256) { + lcnt1 = 256; + lcnt0 = 256; + cyc = *bursts / lcnt1 / lcnt0; + } else if (*bursts > 256) { + lcnt1 = 256; + lcnt0 = *bursts / lcnt1; + cyc = 1; + } else { + lcnt1 = *bursts; + lcnt0 = 0; + cyc = 1; + } + + szlp = _emit_LP(1, buf, 0, 0); + szbrst = _bursts(1, buf, pxs, 1); + + lpend.cond = ALWAYS; + lpend.forever = false; + lpend.loop = 0; + lpend.bjump = 0; + szlpend = _emit_LPEND(1, buf, &lpend); + + if (lcnt0) { + szlp *= 2; + szlpend *= 2; + } + + /* + * Max bursts that we can unroll due to limit on the + * size of backward jump that can be encoded in DMALPEND + * which is 8-bits and hence 255 + */ + cycmax = (255 - (szlp + szlpend)) / szbrst; + + cyc = (cycmax < cyc) ? cycmax : cyc; + + off = 0; + + if (lcnt0) { + off += _emit_LP(dry_run, &buf[off], 0, lcnt0); + ljmp0 = off; + } + + off += _emit_LP(dry_run, &buf[off], 1, lcnt1); + ljmp1 = off; + + off += _bursts(dry_run, &buf[off], pxs, cyc); + + lpend.cond = ALWAYS; + lpend.forever = false; + lpend.loop = 1; + lpend.bjump = off - ljmp1; + off += _emit_LPEND(dry_run, &buf[off], &lpend); + + if (lcnt0) { + lpend.cond = ALWAYS; + lpend.forever = false; + lpend.loop = 0; + lpend.bjump = off - ljmp0; + off += _emit_LPEND(dry_run, &buf[off], &lpend); + } + + *bursts = lcnt1 * cyc; + if (lcnt0) + *bursts *= lcnt0; + + return off; +} + +static inline int _setup_loops(unsigned dry_run, u8 buf[], + const struct _xfer_spec *pxs) +{ + struct pl330_xfer *x = pxs->x; + u32 ccr = pxs->ccr; + unsigned long c, bursts = BYTE_TO_BURST(x->bytes, ccr); + int off = 0; + + while (bursts) { + c = bursts; + off += _loop(dry_run, &buf[off], &c, pxs); + bursts -= c; + } + + return off; +} + +static inline int _setup_xfer(unsigned dry_run, u8 buf[], + const struct _xfer_spec *pxs) +{ + struct pl330_xfer *x = pxs->x; + int off = 0; + + /* DMAMOV SAR, x->src_addr */ + off += _emit_MOV(dry_run, &buf[off], SAR, x->src_addr); + /* DMAMOV DAR, x->dst_addr */ + off += _emit_MOV(dry_run, &buf[off], DAR, x->dst_addr); + + /* Setup Loop(s) */ + off += _setup_loops(dry_run, &buf[off], pxs); + + return off; +} + +/* + * A req is a sequence of one or more xfer units. + * Returns the number of bytes taken to setup the MC for the req. + */ +static int _setup_req(unsigned dry_run, struct pl330_thread *thrd, + unsigned index, struct _xfer_spec *pxs) +{ + struct _pl330_req *req = &thrd->req[index]; + struct pl330_xfer *x; + u8 *buf = req->mc_cpu; + int off = 0; + + PL330_DBGMC_START(req->mc_bus); + + /* DMAMOV CCR, ccr */ + off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); + + x = pxs->r->x; + do { + /* Error if xfer length is not aligned at burst size */ + if (x->bytes % (BRST_SIZE(pxs->ccr) * BRST_LEN(pxs->ccr))) + return -EINVAL; + + pxs->x = x; + off += _setup_xfer(dry_run, &buf[off], pxs); + + x = x->next; + } while (x); + + /* DMASEV peripheral/event */ + off += _emit_SEV(dry_run, &buf[off], thrd->ev); + /* DMAEND */ + off += _emit_END(dry_run, &buf[off]); + + return off; +} + +static inline u32 _prepare_ccr(const struct pl330_reqcfg *rqc) +{ + u32 ccr = 0; + + if (rqc->src_inc) + ccr |= CC_SRCINC; + + if (rqc->dst_inc) + ccr |= CC_DSTINC; + + /* We set same protection levels for Src and DST for now */ + if (rqc->privileged) + ccr |= CC_SRCPRI | CC_DSTPRI; + if (rqc->nonsecure) + ccr |= CC_SRCNS | CC_DSTNS; + if (rqc->insnaccess) + ccr |= CC_SRCIA | CC_DSTIA; + + ccr |= (((rqc->brst_len - 1) & 0xf) << CC_SRCBRSTLEN_SHFT); + ccr |= (((rqc->brst_len - 1) & 0xf) << CC_DSTBRSTLEN_SHFT); + + ccr |= (rqc->brst_size << CC_SRCBRSTSIZE_SHFT); + ccr |= (rqc->brst_size << CC_DSTBRSTSIZE_SHFT); + + ccr |= (rqc->dcctl << CC_SRCCCTRL_SHFT); + ccr |= (rqc->scctl << CC_DSTCCTRL_SHFT); + + ccr |= (rqc->swap << CC_SWAP_SHFT); + + return ccr; +} + +static inline bool _is_valid(u32 ccr) +{ + enum pl330_dstcachectrl dcctl; + enum pl330_srccachectrl scctl; + + dcctl = (ccr >> CC_DSTCCTRL_SHFT) & CC_DRCCCTRL_MASK; + scctl = (ccr >> CC_SRCCCTRL_SHFT) & CC_SRCCCTRL_MASK; + + if (dcctl == DINVALID1 || dcctl == DINVALID2 + || scctl == SINVALID1 || scctl == SINVALID2) + return false; + else + return true; +} + +/* + * Submit a list of xfers after which the client wants notification. + * Client is not notified after each xfer unit, just once after all + * xfer units are done or some error occurs. + */ +int pl330_submit_req(void *ch_id, struct pl330_req *r) +{ + struct pl330_thread *thrd = ch_id; + struct pl330_dmac *pl330; + struct pl330_info *pi; + struct _xfer_spec xs; + unsigned long flags; + void __iomem *regs; + unsigned idx; + u32 ccr; + int ret = 0; + + /* No Req or Unacquired Channel or DMAC */ + if (!r || !thrd || thrd->free) + return -EINVAL; + + pl330 = thrd->dmac; + pi = pl330->pinfo; + regs = pi->base; + + if (pl330->state == DYING + || pl330->dmac_tbd.reset_chan & (1 << thrd->id)) { + dev_info(thrd->dmac->pinfo->dev, "%s:%d\n", + __func__, __LINE__); + return -EAGAIN; + } + + /* If request for non-existing peripheral */ + if (r->rqtype != MEMTOMEM && r->peri >= pi->pcfg.num_peri) { + dev_info(thrd->dmac->pinfo->dev, + "%s:%d Invalid peripheral(%u)!\n", + __func__, __LINE__, r->peri); + return -EINVAL; + } + + spin_lock_irqsave(&pl330->lock, flags); + + if (_queue_full(thrd)) { + ret = -EAGAIN; + goto xfer_exit; + } + + /* Prefer Secure Channel */ + if (!_manager_ns(thrd)) + r->cfg->nonsecure = 0; + else + r->cfg->nonsecure = 1; + + /* Use last settings, if not provided */ + if (r->cfg) + ccr = _prepare_ccr(r->cfg); + else + ccr = readl(regs + CC(thrd->id)); + + /* If this req doesn't have valid xfer settings */ + if (!_is_valid(ccr)) { + ret = -EINVAL; + dev_info(thrd->dmac->pinfo->dev, "%s:%d Invalid CCR(%x)!\n", + __func__, __LINE__, ccr); + goto xfer_exit; + } + + idx = IS_FREE(&thrd->req[0]) ? 0 : 1; + + xs.ccr = ccr; + xs.r = r; + + /* First dry run to check if req is acceptable */ + ret = _setup_req(1, thrd, idx, &xs); + if (ret < 0) + goto xfer_exit; + + if (ret > pi->mcbufsz / 2) { + dev_info(thrd->dmac->pinfo->dev, + "%s:%d Trying increasing mcbufsz\n", + __func__, __LINE__); + ret = -ENOMEM; + goto xfer_exit; + } + + /* Hook the request */ + thrd->lstenq = idx; + thrd->req[idx].mc_len = _setup_req(0, thrd, idx, &xs); + thrd->req[idx].r = r; + + ret = 0; + +xfer_exit: + spin_unlock_irqrestore(&pl330->lock, flags); + + return ret; +} +EXPORT_SYMBOL(pl330_submit_req); + +static void pl330_dotask(unsigned long data) +{ + struct pl330_dmac *pl330 = (struct pl330_dmac *) data; + struct pl330_info *pi = pl330->pinfo; + unsigned long flags; + int i; + + spin_lock_irqsave(&pl330->lock, flags); + + /* The DMAC itself gone nuts */ + if (pl330->dmac_tbd.reset_dmac) { + pl330->state = DYING; + /* Reset the manager too */ + pl330->dmac_tbd.reset_mngr = true; + /* Clear the reset flag */ + pl330->dmac_tbd.reset_dmac = false; + } + + if (pl330->dmac_tbd.reset_mngr) { + _stop(pl330->manager); + /* Reset all channels */ + pl330->dmac_tbd.reset_chan = (1 << pi->pcfg.num_chan) - 1; + /* Clear the reset flag */ + pl330->dmac_tbd.reset_mngr = false; + } + + for (i = 0; i < pi->pcfg.num_chan; i++) { + + if (pl330->dmac_tbd.reset_chan & (1 << i)) { + struct pl330_thread *thrd = &pl330->channels[i]; + void __iomem *regs = pi->base; + enum pl330_op_err err; + + _stop(thrd); + + if (readl(regs + FSC) & (1 << thrd->id)) + err = PL330_ERR_FAIL; + else + err = PL330_ERR_ABORT; + + spin_unlock_irqrestore(&pl330->lock, flags); + + _callback(thrd->req[1 - thrd->lstenq].r, err); + _callback(thrd->req[thrd->lstenq].r, err); + + spin_lock_irqsave(&pl330->lock, flags); + + thrd->req[0].r = NULL; + thrd->req[1].r = NULL; + MARK_FREE(&thrd->req[0]); + MARK_FREE(&thrd->req[1]); + + /* Clear the reset flag */ + pl330->dmac_tbd.reset_chan &= ~(1 << i); + } + } + + spin_unlock_irqrestore(&pl330->lock, flags); + + return; +} + +/* Returns 1 if state was updated, 0 otherwise */ +int pl330_update(const struct pl330_info *pi) +{ + struct _pl330_req *rqdone; + struct pl330_dmac *pl330; + unsigned long flags; + void __iomem *regs; + u32 val; + int id, ev, ret = 0; + + if (!pi || !pi->pl330_data) + return 0; + + regs = pi->base; + pl330 = pi->pl330_data; + + spin_lock_irqsave(&pl330->lock, flags); + + val = readl(regs + FSM) & 0x1; + if (val) + pl330->dmac_tbd.reset_mngr = true; + else + pl330->dmac_tbd.reset_mngr = false; + + val = readl(regs + FSC) & ((1 << pi->pcfg.num_chan) - 1); + pl330->dmac_tbd.reset_chan |= val; + if (val) { + int i = 0; + while (i < pi->pcfg.num_chan) { + if (val & (1 << i)) { + dev_info(pi->dev, + "Reset Channel-%d\t CS-%x FTC-%x\n", + i, readl(regs + CS(i)), + readl(regs + FTC(i))); + _stop(&pl330->channels[i]); + } + i++; + } + } + + /* Check which event happened i.e, thread notified */ + val = readl(regs + ES); + if (pi->pcfg.num_events < 32 + && val & ~((1 << pi->pcfg.num_events) - 1)) { + pl330->dmac_tbd.reset_dmac = true; + dev_err(pi->dev, "%s:%d Unexpected!\n", __func__, __LINE__); + ret = 1; + goto updt_exit; + } + + for (ev = 0; ev < pi->pcfg.num_events; ev++) { + if (val & (1 << ev)) { /* Event occured */ + struct pl330_thread *thrd; + u32 inten = readl(regs + INTEN); + int active; + + /* Clear the event */ + if (inten & (1 << ev)) + writel(1 << ev, regs + INTCLR); + + ret = 1; + + id = pl330->events[ev]; + + thrd = &pl330->channels[id]; + + active = _thrd_active(thrd); + if (!active) /* Aborted */ + continue; + + active -= 1; + + rqdone = &thrd->req[active]; + MARK_FREE(rqdone); + + /* Get going again ASAP */ + _start(thrd); + + /* For now, just make a list of callbacks to be done */ + list_add_tail(&rqdone->rqd, &pl330->req_done); + } + } + + /* Now that we are in no hurry, do the callbacks */ + while (!list_empty(&pl330->req_done)) { + rqdone = container_of(pl330->req_done.next, + struct _pl330_req, rqd); + + list_del_init(&rqdone->rqd); + + spin_unlock_irqrestore(&pl330->lock, flags); + _callback(rqdone->r, PL330_ERR_NONE); + spin_lock_irqsave(&pl330->lock, flags); + } + +updt_exit: + spin_unlock_irqrestore(&pl330->lock, flags); + + if (pl330->dmac_tbd.reset_dmac + || pl330->dmac_tbd.reset_mngr + || pl330->dmac_tbd.reset_chan) { + ret = 1; + tasklet_schedule(&pl330->tasks); + } + + return ret; +} +EXPORT_SYMBOL(pl330_update); + +int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op) +{ + struct pl330_thread *thrd = ch_id; + struct pl330_dmac *pl330; + unsigned long flags; + int ret = 0, active; + + if (!thrd || thrd->free || thrd->dmac->state == DYING) + return -EINVAL; + + pl330 = thrd->dmac; + + spin_lock_irqsave(&pl330->lock, flags); + + switch (op) { + case PL330_OP_FLUSH: + /* Make sure the channel is stopped */ + _stop(thrd); + + thrd->req[0].r = NULL; + thrd->req[1].r = NULL; + MARK_FREE(&thrd->req[0]); + MARK_FREE(&thrd->req[1]); + break; + + case PL330_OP_ABORT: + active = _thrd_active(thrd); + + /* Make sure the channel is stopped */ + _stop(thrd); + + /* ABORT is only for the active req */ + if (!active) + break; + + active--; + + thrd->req[active].r = NULL; + MARK_FREE(&thrd->req[active]); + + /* Start the next */ + case PL330_OP_START: + if (!_start(thrd)) + ret = -EIO; + break; + + default: + ret = -EINVAL; + } + + spin_unlock_irqrestore(&pl330->lock, flags); + return ret; +} +EXPORT_SYMBOL(pl330_chan_ctrl); + +int pl330_chan_status(void *ch_id, struct pl330_chanstatus *pstatus) +{ + struct pl330_thread *thrd = ch_id; + struct pl330_dmac *pl330; + struct pl330_info *pi; + void __iomem *regs; + int active; + u32 val; + + if (!pstatus || !thrd || thrd->free) + return -EINVAL; + + pl330 = thrd->dmac; + pi = pl330->pinfo; + regs = pi->base; + + /* The client should remove the DMAC and add again */ + if (pl330->state == DYING) + pstatus->dmac_halted = true; + else + pstatus->dmac_halted = false; + + val = readl(regs + FSC); + if (val & (1 << thrd->id)) + pstatus->faulting = true; + else + pstatus->faulting = false; + + active = _thrd_active(thrd); + + if (!active) { + /* Indicate that the thread is not running */ + pstatus->top_req = NULL; + pstatus->wait_req = NULL; + } else { + active--; + pstatus->top_req = thrd->req[active].r; + pstatus->wait_req = !IS_FREE(&thrd->req[1 - active]) + ? thrd->req[1 - active].r : NULL; + } + + pstatus->src_addr = readl(regs + SA(thrd->id)); + pstatus->dst_addr = readl(regs + DA(thrd->id)); + + return 0; +} +EXPORT_SYMBOL(pl330_chan_status); + +/* Reserve an event */ +static inline int _alloc_event(struct pl330_thread *thrd) +{ + struct pl330_dmac *pl330 = thrd->dmac; + struct pl330_info *pi = pl330->pinfo; + int ev; + + for (ev = 0; ev < pi->pcfg.num_events; ev++) + if (pl330->events[ev] == -1) { + pl330->events[ev] = thrd->id; + return ev; + } + + return -1; +} + +/* Upon success, returns IdentityToken for the + * allocated channel, NULL otherwise. + */ +void *pl330_request_channel(const struct pl330_info *pi) +{ + struct pl330_thread *thrd = NULL; + struct pl330_dmac *pl330; + unsigned long flags; + int chans, i; + + if (!pi || !pi->pl330_data) + return NULL; + + pl330 = pi->pl330_data; + + if (pl330->state == DYING) + return NULL; + + chans = pi->pcfg.num_chan; + + spin_lock_irqsave(&pl330->lock, flags); + + for (i = 0; i < chans; i++) { + thrd = &pl330->channels[i]; + if (thrd->free) { + thrd->ev = _alloc_event(thrd); + if (thrd->ev >= 0) { + thrd->free = false; + thrd->lstenq = 1; + thrd->req[0].r = NULL; + MARK_FREE(&thrd->req[0]); + thrd->req[1].r = NULL; + MARK_FREE(&thrd->req[1]); + break; + } + } + thrd = NULL; + } + + spin_unlock_irqrestore(&pl330->lock, flags); + + return thrd; +} +EXPORT_SYMBOL(pl330_request_channel); + +/* Release an event */ +static inline void _free_event(struct pl330_thread *thrd, int ev) +{ + struct pl330_dmac *pl330 = thrd->dmac; + struct pl330_info *pi = pl330->pinfo; + + /* If the event is valid and was held by the thread */ + if (ev >= 0 && ev < pi->pcfg.num_events + && pl330->events[ev] == thrd->id) + pl330->events[ev] = -1; +} + +void pl330_release_channel(void *ch_id) +{ + struct pl330_thread *thrd = ch_id; + struct pl330_dmac *pl330; + unsigned long flags; + + if (!thrd || thrd->free) + return; + + _stop(thrd); + + _callback(thrd->req[1 - thrd->lstenq].r, PL330_ERR_ABORT); + _callback(thrd->req[thrd->lstenq].r, PL330_ERR_ABORT); + + pl330 = thrd->dmac; + + spin_lock_irqsave(&pl330->lock, flags); + _free_event(thrd, thrd->ev); + thrd->free = true; + spin_unlock_irqrestore(&pl330->lock, flags); +} +EXPORT_SYMBOL(pl330_release_channel); + +/* Initialize the structure for PL330 configuration, that can be used + * by the client driver the make best use of the DMAC + */ +static void read_dmac_config(struct pl330_info *pi) +{ + void __iomem *regs = pi->base; + u32 val; + + val = readl(regs + CRD) >> CRD_DATA_WIDTH_SHIFT; + val &= CRD_DATA_WIDTH_MASK; + pi->pcfg.data_bus_width = 8 * (1 << val); + + val = readl(regs + CRD) >> CRD_DATA_BUFF_SHIFT; + val &= CRD_DATA_BUFF_MASK; + pi->pcfg.data_buf_dep = val + 1; + + val = readl(regs + CR0) >> CR0_NUM_CHANS_SHIFT; + val &= CR0_NUM_CHANS_MASK; + val += 1; + pi->pcfg.num_chan = val; + + val = readl(regs + CR0); + if (val & CR0_PERIPH_REQ_SET) { + val = (val >> CR0_NUM_PERIPH_SHIFT) & CR0_NUM_PERIPH_MASK; + val += 1; + pi->pcfg.num_peri = val; + pi->pcfg.peri_ns = readl(regs + CR4); + } else { + pi->pcfg.num_peri = 0; + } + + val = readl(regs + CR0); + if (val & CR0_BOOT_MAN_NS) + pi->pcfg.mode |= DMAC_MODE_NS; + else + pi->pcfg.mode &= ~DMAC_MODE_NS; + + val = readl(regs + CR0) >> CR0_NUM_EVENTS_SHIFT; + val &= CR0_NUM_EVENTS_MASK; + val += 1; + pi->pcfg.num_events = val; + + pi->pcfg.irq_ns = readl(regs + CR3); + + pi->pcfg.periph_id = get_id(pi, PERIPH_ID); + pi->pcfg.pcell_id = get_id(pi, PCELL_ID); +} + +static inline void _reset_thread(struct pl330_thread *thrd) +{ + struct pl330_dmac *pl330 = thrd->dmac; + struct pl330_info *pi = pl330->pinfo; + + thrd->req[0].mc_cpu = pl330->mcode_cpu + + (thrd->id * pi->mcbufsz); + thrd->req[0].mc_bus = pl330->mcode_bus + + (thrd->id * pi->mcbufsz); + thrd->req[0].r = NULL; + MARK_FREE(&thrd->req[0]); + + thrd->req[1].mc_cpu = thrd->req[0].mc_cpu + + pi->mcbufsz / 2; + thrd->req[1].mc_bus = thrd->req[0].mc_bus + + pi->mcbufsz / 2; + thrd->req[1].r = NULL; + MARK_FREE(&thrd->req[1]); +} + +static int dmac_alloc_threads(struct pl330_dmac *pl330) +{ + struct pl330_info *pi = pl330->pinfo; + int chans = pi->pcfg.num_chan; + struct pl330_thread *thrd; + int i; + + /* Allocate 1 Manager and 'chans' Channel threads */ + pl330->channels = kzalloc((1 + chans) * sizeof(*thrd), + GFP_KERNEL); + if (!pl330->channels) + return -ENOMEM; + + /* Init Channel threads */ + for (i = 0; i < chans; i++) { + thrd = &pl330->channels[i]; + thrd->id = i; + thrd->dmac = pl330; + _reset_thread(thrd); + thrd->free = true; + } + + /* MANAGER is indexed at the end */ + thrd = &pl330->channels[chans]; + thrd->id = chans; + thrd->dmac = pl330; + thrd->free = false; + pl330->manager = thrd; + + return 0; +} + +static int dmac_alloc_resources(struct pl330_dmac *pl330) +{ + struct pl330_info *pi = pl330->pinfo; + int chans = pi->pcfg.num_chan; + int ret; + + /* + * Alloc MicroCode buffer for 'chans' Channel threads. + * A channel's buffer offset is (Channel_Id * MCODE_BUFF_PERCHAN) + */ + pl330->mcode_cpu = dma_alloc_coherent(pi->dev, + chans * pi->mcbufsz, + &pl330->mcode_bus, GFP_KERNEL); + if (!pl330->mcode_cpu) { + dev_err(pi->dev, "%s:%d Can't allocate memory!\n", + __func__, __LINE__); + return -ENOMEM; + } + + ret = dmac_alloc_threads(pl330); + if (ret) { + dev_err(pi->dev, "%s:%d Can't to create channels for DMAC!\n", + __func__, __LINE__); + dma_free_coherent(pi->dev, + chans * pi->mcbufsz, + pl330->mcode_cpu, pl330->mcode_bus); + return ret; + } + + return 0; +} + +int pl330_add(struct pl330_info *pi) +{ + struct pl330_dmac *pl330; + void __iomem *regs; + int i, ret; + + if (!pi || !pi->dev) + return -EINVAL; + + /* If already added */ + if (pi->pl330_data) + return -EINVAL; + + /* + * If the SoC can perform reset on the DMAC, then do it + * before reading its configuration. + */ + if (pi->dmac_reset) + pi->dmac_reset(pi); + + regs = pi->base; + + /* Check if we can handle this DMAC */ + if (get_id(pi, PERIPH_ID) != PERIPH_ID_VAL + || get_id(pi, PCELL_ID) != PCELL_ID_VAL) { + dev_err(pi->dev, "PERIPH_ID 0x%x, PCELL_ID 0x%x !\n", + readl(regs + PERIPH_ID), readl(regs + PCELL_ID)); + return -EINVAL; + } + + /* Read the configuration of the DMAC */ + read_dmac_config(pi); + + if (pi->pcfg.num_events == 0) { + dev_err(pi->dev, "%s:%d Can't work without events!\n", + __func__, __LINE__); + return -EINVAL; + } + + pl330 = kzalloc(sizeof(*pl330), GFP_KERNEL); + if (!pl330) { + dev_err(pi->dev, "%s:%d Can't allocate memory!\n", + __func__, __LINE__); + return -ENOMEM; + } + + /* Assign the info structure and private data */ + pl330->pinfo = pi; + pi->pl330_data = pl330; + + spin_lock_init(&pl330->lock); + + INIT_LIST_HEAD(&pl330->req_done); + + /* Use default MC buffer size if not provided */ + if (!pi->mcbufsz) + pi->mcbufsz = MCODE_BUFF_PER_REQ * 2; + + /* Mark all events as free */ + for (i = 0; i < pi->pcfg.num_events; i++) + pl330->events[i] = -1; + + /* Allocate resources needed by the DMAC */ + ret = dmac_alloc_resources(pl330); + if (ret) { + dev_err(pi->dev, "Unable to create channels for DMAC\n"); + kfree(pl330); + return ret; + } + + tasklet_init(&pl330->tasks, pl330_dotask, (unsigned long) pl330); + + pl330->state = INIT; + + return 0; +} +EXPORT_SYMBOL(pl330_add); + +static int dmac_free_threads(struct pl330_dmac *pl330) +{ + struct pl330_info *pi = pl330->pinfo; + int chans = pi->pcfg.num_chan; + struct pl330_thread *thrd; + int i; + + /* Release Channel threads */ + for (i = 0; i < chans; i++) { + thrd = &pl330->channels[i]; + pl330_release_channel((void *)thrd); + } + + /* Free memory */ + kfree(pl330->channels); + + return 0; +} + +static void dmac_free_resources(struct pl330_dmac *pl330) +{ + struct pl330_info *pi = pl330->pinfo; + int chans = pi->pcfg.num_chan; + + dmac_free_threads(pl330); + + dma_free_coherent(pi->dev, chans * pi->mcbufsz, + pl330->mcode_cpu, pl330->mcode_bus); +} + +void pl330_del(struct pl330_info *pi) +{ + struct pl330_dmac *pl330; + + if (!pi || !pi->pl330_data) + return; + + pl330 = pi->pl330_data; + + pl330->state = UNINIT; + + tasklet_kill(&pl330->tasks); + + /* Free DMAC resources */ + dmac_free_resources(pl330); + + kfree(pl330); + pi->pl330_data = NULL; +} +EXPORT_SYMBOL(pl330_del); diff --git a/arch/arm/include/asm/hardware/pl330.h b/arch/arm/include/asm/hardware/pl330.h new file mode 100644 index 00000000000..575fa8186ca --- /dev/null +++ b/arch/arm/include/asm/hardware/pl330.h @@ -0,0 +1,217 @@ +/* linux/include/asm/hardware/pl330.h + * + * Copyright (C) 2010 Samsung Electronics Co. Ltd. + * Jaswinder Singh + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __PL330_CORE_H +#define __PL330_CORE_H + +#define PL330_MAX_CHAN 8 +#define PL330_MAX_IRQS 32 +#define PL330_MAX_PERI 32 + +enum pl330_srccachectrl { + SCCTRL0 = 0, /* Noncacheable and nonbufferable */ + SCCTRL1, /* Bufferable only */ + SCCTRL2, /* Cacheable, but do not allocate */ + SCCTRL3, /* Cacheable and bufferable, but do not allocate */ + SINVALID1, + SINVALID2, + SCCTRL6, /* Cacheable write-through, allocate on reads only */ + SCCTRL7, /* Cacheable write-back, allocate on reads only */ +}; + +enum pl330_dstcachectrl { + DCCTRL0 = 0, /* Noncacheable and nonbufferable */ + DCCTRL1, /* Bufferable only */ + DCCTRL2, /* Cacheable, but do not allocate */ + DCCTRL3, /* Cacheable and bufferable, but do not allocate */ + DINVALID1 = 8, + DINVALID2, + DCCTRL6, /* Cacheable write-through, allocate on writes only */ + DCCTRL7, /* Cacheable write-back, allocate on writes only */ +}; + +/* Populated by the PL330 core driver for DMA API driver's info */ +struct pl330_config { + u32 periph_id; + u32 pcell_id; +#define DMAC_MODE_NS (1 << 0) + unsigned int mode; + unsigned int data_bus_width:10; /* In number of bits */ + unsigned int data_buf_dep:10; + unsigned int num_chan:4; + unsigned int num_peri:6; + u32 peri_ns; + unsigned int num_events:6; + u32 irq_ns; +}; + +/* Handle to the DMAC provided to the PL330 core */ +struct pl330_info { + /* Owning device */ + struct device *dev; + /* Size of MicroCode buffers for each channel. */ + unsigned mcbufsz; + /* ioremap'ed address of PL330 registers. */ + void __iomem *base; + /* Client can freely use it. */ + void *client_data; + /* PL330 core data, Client must not touch it. */ + void *pl330_data; + /* Populated by the PL330 core driver during pl330_add */ + struct pl330_config pcfg; + /* + * If the DMAC has some reset mechanism, then the + * client may want to provide pointer to the method. + */ + void (*dmac_reset)(struct pl330_info *pi); +}; + +enum pl330_byteswap { + SWAP_NO = 0, + SWAP_2, + SWAP_4, + SWAP_8, + SWAP_16, +}; + +/** + * Request Configuration. + * The PL330 core does not modify this and uses the last + * working configuration if the request doesn't provide any. + * + * The Client may want to provide this info only for the + * first request and a request with new settings. + */ +struct pl330_reqcfg { + /* Address Incrementing */ + unsigned dst_inc:1; + unsigned src_inc:1; + + /* + * For now, the SRC & DST protection levels + * and burst size/length are assumed same. + */ + bool nonsecure; + bool privileged; + bool insnaccess; + unsigned brst_len:5; + unsigned brst_size:3; /* in power of 2 */ + + enum pl330_dstcachectrl dcctl; + enum pl330_srccachectrl scctl; + enum pl330_byteswap swap; +}; + +/* + * One cycle of DMAC operation. + * There may be more than one xfer in a request. + */ +struct pl330_xfer { + u32 src_addr; + u32 dst_addr; + /* Size to xfer */ + u32 bytes; + /* + * Pointer to next xfer in the list. + * The last xfer in the req must point to NULL. + */ + struct pl330_xfer *next; +}; + +/* The xfer callbacks are made with one of these arguments. */ +enum pl330_op_err { + /* The all xfers in the request were success. */ + PL330_ERR_NONE, + /* If req aborted due to global error. */ + PL330_ERR_ABORT, + /* If req failed due to problem with Channel. */ + PL330_ERR_FAIL, +}; + +enum pl330_reqtype { + MEMTOMEM, + MEMTODEV, + DEVTOMEM, + DEVTODEV, +}; + +/* A request defining Scatter-Gather List ending with NULL xfer. */ +struct pl330_req { + enum pl330_reqtype rqtype; + /* Index of peripheral for the xfer. */ + unsigned peri:5; + /* Unique token for this xfer, set by the client. */ + void *token; + /* Callback to be called after xfer. */ + void (*xfer_cb)(void *token, enum pl330_op_err err); + /* If NULL, req will be done at last set parameters. */ + struct pl330_reqcfg *cfg; + /* Pointer to first xfer in the request. */ + struct pl330_xfer *x; +}; + +/* + * To know the status of the channel and DMAC, the client + * provides a pointer to this structure. The PL330 core + * fills it with current information. + */ +struct pl330_chanstatus { + /* + * If the DMAC engine halted due to some error, + * the client should remove-add DMAC. + */ + bool dmac_halted; + /* + * If channel is halted due to some error, + * the client should ABORT/FLUSH and START the channel. + */ + bool faulting; + /* Location of last load */ + u32 src_addr; + /* Location of last store */ + u32 dst_addr; + /* + * Pointer to the currently active req, NULL if channel is + * inactive, even though the requests may be present. + */ + struct pl330_req *top_req; + /* Pointer to req waiting second in the queue if any. */ + struct pl330_req *wait_req; +}; + +enum pl330_chan_op { + /* Start the channel */ + PL330_OP_START, + /* Abort the active xfer */ + PL330_OP_ABORT, + /* Stop xfer and flush queue */ + PL330_OP_FLUSH, +}; + +extern int pl330_add(struct pl330_info *); +extern void pl330_del(struct pl330_info *pi); +extern int pl330_update(const struct pl330_info *pi); +extern void pl330_release_channel(void *ch_id); +extern void *pl330_request_channel(const struct pl330_info *pi); +extern int pl330_chan_status(void *ch_id, struct pl330_chanstatus *pstatus); +extern int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op); +extern int pl330_submit_req(void *ch_id, struct pl330_req *r); + +#endif /* __PL330_CORE_H */ -- cgit v1.2.3-70-g09d2