diff options
author | Rob Herring <r.herring@freescale.com> | 2009-10-19 14:43:19 -0500 |
---|---|---|
committer | Rob Herring <r.herring@freescale.com> | 2009-10-26 16:57:04 -0500 |
commit | 460b55880e47a98943f5072bc03ffcfcc8a40a14 (patch) | |
tree | 5690552665f0b7843e6552e4d5fe7b63cbc78f51 /arch/arm/plat-stmp3xxx | |
parent | 40abba66d676c6c7aff57a5fbd1345974c90b2fe (diff) |
ENGR00117389 Port 5.0.0 release to 2.6.31
This is i.MX BSP 5.0.0 release ported to 2.6.31
Signed-off-by: Rob Herring <r.herring@freescale.com>
Signed-off-by: Alan Tull <r80115@freescale.com>
Signed-off-by: Xinyu Chen <xinyu.chen@freescale.com>
Diffstat (limited to 'arch/arm/plat-stmp3xxx')
34 files changed, 2842 insertions, 67 deletions
diff --git a/arch/arm/plat-stmp3xxx/Kconfig b/arch/arm/plat-stmp3xxx/Kconfig index 2cf37c35951b..e211f2604307 100644 --- a/arch/arm/plat-stmp3xxx/Kconfig +++ b/arch/arm/plat-stmp3xxx/Kconfig @@ -32,6 +32,8 @@ config MACH_STMP378X endchoice +source arch/arm/mach-stmp378x/Kconfig + endmenu endif diff --git a/arch/arm/plat-stmp3xxx/Makefile b/arch/arm/plat-stmp3xxx/Makefile index 31dd518f37a5..50114dca7261 100644 --- a/arch/arm/plat-stmp3xxx/Makefile +++ b/arch/arm/plat-stmp3xxx/Makefile @@ -2,4 +2,18 @@ # Makefile for the linux kernel. # # Object file lists. -obj-y += core.o timer.o irq.o dma.o clock.o pinmux.o devices.o +obj-y += core.o timer.o irq.o dma.o clock.o pinmux.o devices.o \ + lradc.o spi.o mmc.o \ + power-test.o + +obj-$(CONFIG_ARCH_STMP378X) += dcp-bootstream.o + +# Power Management +obj-$(CONFIG_CPU_FREQ) += cpufreq.o + +obj-$(CONFIG_STMP3XXX_UNIQUE_ID) += unique-id.o + +# charging/current limitation testing +obj-m += power-test.o + + diff --git a/arch/arm/plat-stmp3xxx/clock.c b/arch/arm/plat-stmp3xxx/clock.c index 5d2f19a09e44..b59ffbb2debe 100644 --- a/arch/arm/plat-stmp3xxx/clock.c +++ b/arch/arm/plat-stmp3xxx/clock.c @@ -421,6 +421,83 @@ static long cpu_round_rate(struct clk *clk, u32 rate) return r; } +static int emi_set_rate(struct clk *clk, u32 rate) +{ + int ret = 0; + + if (rate < 24000) + return -EINVAL; + else { + int i; + struct stmp3xxx_emi_scaling_data sc_data; + int (*scale)(struct stmp3xxx_emi_scaling_data *) = + (void *)STMP3XXX_OCRAM_BASE; + void *saved_ocram; + u32 clkctrl_emi; + u32 clkctrl_frac; + int div = 1; + /* + * We've been setting div to HW_CLKCTRL_CPU_RD() & 0x3f so far. + * TODO: verify 1 is still valid. + */ + + if (!stmp3xxx_ram_funcs_sz) + goto out; + + for (clkctrl_emi = div; clkctrl_emi < 0x3f; + clkctrl_emi += div) { + clkctrl_frac = + (pll_clk.rate * 18 + rate * clkctrl_emi / 2) / + (rate * clkctrl_emi); + if (clkctrl_frac >= 18 && clkctrl_frac <= 35) { + pr_debug("%s: clkctrl_frac found %d for %d\n", + __func__, clkctrl_frac, clkctrl_emi); + if (pll_clk.rate * 18 / + clkctrl_frac / clkctrl_emi / 100 == + rate / 100) + break; + } + } + if (clkctrl_emi >= 0x3f) + return -EINVAL; + pr_debug("%s: clkctrl_emi %d, clkctrl_frac %d\n", + __func__, clkctrl_emi, clkctrl_frac); + + saved_ocram = kmalloc(stmp3xxx_ram_funcs_sz, GFP_KERNEL); + if (!saved_ocram) + return -ENOMEM; + memcpy(saved_ocram, scale, stmp3xxx_ram_funcs_sz); + memcpy(scale, stmp3xxx_ram_freq_scale, stmp3xxx_ram_funcs_sz); + + sc_data.emi_div = clkctrl_emi; + sc_data.frac_div = clkctrl_frac; + sc_data.cur_freq = clk->rate / 1000; + sc_data.new_freq = rate / 1000; + + local_irq_disable(); + local_fiq_disable(); + + scale(&sc_data); + + local_fiq_enable(); + local_irq_enable(); + + for (i = 10000; i; i--) + if (!clk_is_busy(clk)) + break; + memcpy(scale, saved_ocram, stmp3xxx_ram_funcs_sz); + kfree(saved_ocram); + + if (!i) { + printk(KERN_ERR "couldn't set up EMI divisor\n"); + ret = -ETIMEDOUT; + goto out; + } + } +out: + return ret; +} + static long emi_get_rate(struct clk *clk) { long rate = clk->parent->rate * 18; @@ -652,6 +729,8 @@ static struct clk_ops lcdif_ops = { static struct clk_ops emi_ops = { .get_rate = emi_get_rate, + .set_rate = emi_set_rate, + .set_parent = clkseq_set_parent, }; /* List of on-chip clocks */ @@ -860,6 +939,50 @@ static struct clk usb_clk = { .ops = &min_ops, }; +static struct clk vid_clk = { + .parent = &osc_24M, +#ifdef CONFIG_MACH_STMP378X + .enable_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_FRAC1, + .enable_shift = 31, + .enable_negate = 1, +#endif + .flags = RATE_PROPAGATES, + .ops = &min_ops, +}; + +static struct clk clk_tv108M_ng = { + .parent = &vid_clk, +#ifdef CONFIG_MACH_STMP378X + .enable_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_TV, + .enable_shift = 31, + .enable_negate = 1, +#endif + .flags = FIXED_RATE, + .ops = &min_ops, +}; + +static struct clk clk_tv54M = { + .parent = &vid_clk, +#ifdef CONFIG_MACH_STMP378X + .enable_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_TV, + .enable_shift = 30, + .enable_negate = 1, +#endif + .flags = FIXED_RATE, + .ops = &min_ops, +}; + +static struct clk clk_tv27M = { + .parent = &vid_clk, +#ifdef CONFIG_MACH_STMP378X + .enable_reg = REGS_CLKCTRL_BASE + HW_CLKCTRL_TV, + .enable_shift = 30, + .enable_negate = 1, +#endif + .flags = FIXED_RATE, + .ops = &min_ops, +}; + /* list of all the clocks */ static struct clk_lookup onchip_clks[] = { { @@ -922,6 +1045,21 @@ static struct clk_lookup onchip_clks[] = { }, { .con_id = "usb", .clk = &usb_clk, + }, { + .con_id = "ref_vid", + .clk = &vid_clk, + }, { + .con_id = "tv108M_ng", + .clk = &clk_tv108M_ng, + }, { + .con_id = "tv54M", + .clk = &clk_tv54M, + }, { + .con_id = "tv27M", + .clk = &clk_tv27M, + }, { + .con_id = "saif", + .clk = &saif_clk, }, }; diff --git a/arch/arm/plat-stmp3xxx/clock.h b/arch/arm/plat-stmp3xxx/clock.h index a6611e1a3510..1008cb0c8a7a 100644 --- a/arch/arm/plat-stmp3xxx/clock.h +++ b/arch/arm/plat-stmp3xxx/clock.h @@ -48,6 +48,23 @@ struct clk { struct clk_ops *ops; }; +struct stmp3xxx_emi_scaling_data { + u32 emi_div; + u32 frac_div; + u32 cur_freq; + u32 new_freq; +}; + +#ifdef CONFIG_STMP378X_RAM_FREQ_SCALING +extern void stmp3xxx_ram_freq_scale(struct stmp3xxx_emi_scaling_data *); +extern u32 stmp3xxx_ram_funcs_sz; +#else +static inline void stmp3xxx_ram_freq_scale(struct stmp3xxx_emi_scaling_data *p) +{ +} +static u32 stmp3xxx_ram_funcs_sz; +#endif + #endif /* __ASSEMBLER__ */ /* Flags */ diff --git a/arch/arm/plat-stmp3xxx/core.c b/arch/arm/plat-stmp3xxx/core.c index 37b8a09148a4..6ada910d18c0 100644 --- a/arch/arm/plat-stmp3xxx/core.c +++ b/arch/arm/plat-stmp3xxx/core.c @@ -23,6 +23,8 @@ #include <mach/platform.h> #include <mach/dma.h> #include <mach/regs-clkctrl.h> +#include <mach/regs-rtc.h> +#include <mach/system.h> static int __stmp3xxx_reset_block(void __iomem *hwreg, int just_enable) { @@ -114,15 +116,25 @@ int stmp3xxx_reset_block(void __iomem *hwreg, int just_enable) } EXPORT_SYMBOL(stmp3xxx_reset_block); -struct platform_device stmp3xxx_dbguart = { - .name = "stmp3xxx-dbguart", - .id = -1, -}; +static void stmp3xxx_machine_restart(char mode, const char *cmd) +{ + arch_reset(mode, cmd); + printk(KERN_ERR"stmp3xxx_machine_restart failed -- System halted\n"); + for (;;) + continue; +} void __init stmp3xxx_init(void) { /* Turn off auto-slow and other tricks */ stmp3xxx_clearl(0x7f00000, REGS_CLKCTRL_BASE + HW_CLKCTRL_HBUS); + /* Re-route machine restart to our own handler */ + arm_pm_restart = stmp3xxx_machine_restart; + stmp3xxx_dma_init(); + + stmp3xxx_setl(BM_RTC_PERSISTENT0_XTAL32KHZ_PWRUP | + BM_RTC_PERSISTENT0_XTAL24MHZ_PWRUP, + REGS_RTC_BASE + HW_RTC_PERSISTENT0); } diff --git a/arch/arm/plat-stmp3xxx/cpufreq.c b/arch/arm/plat-stmp3xxx/cpufreq.c new file mode 100644 index 000000000000..aa83d33bc64c --- /dev/null +++ b/arch/arm/plat-stmp3xxx/cpufreq.c @@ -0,0 +1,475 @@ +/* + * CPU frequency scaling for Freescale STMP37XX/STMP378X + * + * Author: Vitaly Wool <vital@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +/* #define DEBUG */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/cpufreq.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +//#include <linux/regulator/regulator-drv.h> +#include <linux/notifier.h> + +#include <mach/hardware.h> +#include <linux/io.h> +#include <asm/system.h> +#include <mach/regulator.h> +#include <mach/power.h> +#include <mach/regs-digctl.h> +#include <mach/regs-clkctrl.h> +#include <mach/platform.h> + +#define VERY_HI_RATE 2000000000 +#define CLKCTRL_PLL_PWD_BIT 16 +#define CLKCTRL_PLL_BYPASS 0x1ff + +static struct profile { + int cpu; + int ahb; + int emi; + int ss; + int vddd; + int vddd_bo; + int cur; + int vddio; + int vdda; + int pll_off; +} profiles[] = { + { 24000, 24000, 24000, 3, 1000000, + 925000, 150000, 3075000, 1725000, 1 }, + { 64000, 64000, 48000, 3, 1000000, + 925000, 150000, 3300000, 1750000, 0 }, + { 261820, 130910, 130910, 0, 1225000, + 1125000, 173000, 3300000, 1750000, 0 }, + { 360000, 120000, 120000, 0, 1350000, + 1250000, 200000, 3300000, 1750000, 0 }, + { 392730, 130910, 130910, 0, 1400000, + 1300000, 225000, 3300000, 1750000, 0 }, + { 454740, 151580, 151580, 0, 1550000, + 1450000, 355000, 3300000, 1750000, 0 }, +}; + +static struct stmp3xxx_cpufreq { + struct cpufreq_policy policy; + struct regulator *regulator; + struct notifier_block nb; + struct notifier_block init_nb; + int freq_id; + int next_freq_id; + spinlock_t lock; +} cpufreq_bdata; + +static u32 clkseq_setting; + +static int reg_callback(struct notifier_block *, unsigned long, void *); +static int init_reg_callback(struct notifier_block *, unsigned long, void *); + +static inline void __set_new_policy(struct cpufreq_policy *policy) +{ + spin_lock(&cpufreq_bdata.lock); + + if (policy) + cpufreq_bdata.policy = *policy; + else + memset(&cpufreq_bdata.policy, 0, sizeof(cpufreq_bdata.policy)); + + if (cpufreq_bdata.regulator) + goto out; + + cpufreq_bdata.regulator = regulator_get(NULL, "cpufreq-1"); + if (!cpufreq_bdata.regulator || IS_ERR(cpufreq_bdata.regulator)) + cpufreq_bdata.regulator = NULL; + else { + regulator_set_mode(cpufreq_bdata.regulator, + REGULATOR_MODE_FAST); + if (cpufreq_bdata.regulator) + regulator_register_notifier( + cpufreq_bdata.regulator, + &cpufreq_bdata.nb); + } + +out: + spin_unlock(&cpufreq_bdata.lock); +} + +static int stmp3xxx_verify_speed(struct cpufreq_policy *policy) +{ + struct clk *cpu_clk; + + pr_debug("%s: entered, policy %p\n", __func__, policy); + + __set_new_policy(policy); + + if (policy->cpu) + return -EINVAL; + + cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, + policy->cpuinfo.max_freq); + cpu_clk = clk_get(NULL, "cpu"); + if (IS_ERR(cpu_clk)) + return PTR_ERR(cpu_clk); + + pr_debug("%s: policy->min %d, policy->max %d\n", + __func__, policy->min, policy->max); + policy->min = clk_round_rate(cpu_clk, policy->min); + policy->max = clk_round_rate(cpu_clk, policy->max); + pr_debug("%s: after rounding rate: policy->min %d, policy->max %d\n", + __func__, policy->min, policy->max); + cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, + policy->cpuinfo.max_freq); + clk_put(cpu_clk); + + return 0; +} + +static unsigned int stmp3xxx_getspeed(unsigned int cpu) +{ + struct clk *cpu_clk; + unsigned long rate; + + pr_debug("%s: entered\n", __func__); + if (cpu) + return 0; + + cpu_clk = clk_get(NULL, "cpu"); + if (IS_ERR(cpu_clk)) + return 0; + rate = clk_get_rate(cpu_clk); + pr_debug("%s: got cpu speed %ld\n", __func__, rate); + clk_put(cpu_clk); + + return rate; +} + +static int set_op(unsigned int target_freq) +{ + struct clk *cpu_clk, *ahb_clk, *emi_clk; + struct regulator *vddd, *vdddbo, *cur_limit, *vddio, *vdda; + struct cpufreq_freqs freqs; + int ret = 0, i; + + cur_limit = cpufreq_bdata.regulator; + pr_debug("%s: entered\n", __func__); + cpu_clk = clk_get(NULL, "cpu"); + if (IS_ERR(cpu_clk)) { + ret = PTR_ERR(cpu_clk); + goto out_cpu; + } + ahb_clk = clk_get(NULL, "hclk"); + if (IS_ERR(ahb_clk)) { + ret = PTR_ERR(ahb_clk); + goto out_ahb; + } + emi_clk = clk_get(NULL, "emi"); + if (IS_ERR(emi_clk)) { + ret = PTR_ERR(emi_clk); + goto out_emi; + } + + vddd = regulator_get(NULL, "vddd"); + vdddbo = regulator_get(NULL, "vddd_bo"); + if (IS_ERR(vdddbo)) + vdddbo = NULL; + vddio = regulator_get(NULL, "vddio"); + if (IS_ERR(vddio)) { + vddio = NULL; + pr_warning("unable to get vddio"); + } + vdda = regulator_get(NULL, "vdda"); + if (IS_ERR(vdda)) { + vdda = NULL; + pr_warning("unable to get vddio"); + } + + freqs.old = clk_get_rate(cpu_clk); + freqs.cpu = 0; + for (i = 0; i < ARRAY_SIZE(profiles) - 1; i++) { + if (profiles[i].cpu <= target_freq && + target_freq < profiles[i + 1].cpu) { + freqs.new = profiles[i].cpu; + cpufreq_bdata.next_freq_id = i; + break; + } + if (!vddd && profiles[i].cpu > freqs.old) { + /* can't safely set more than now */ + freqs.new = profiles[i - 1].cpu; + break; + } + } + + pr_debug("target_freq %d, new %d\n", target_freq, freqs.new); + if (i == ARRAY_SIZE(profiles) - 1) { + freqs.new = profiles[i].cpu; + cpufreq_bdata.next_freq_id = i; + } + + if (IS_ERR(vddd)) { + ret = PTR_ERR(vddd); + if (!cpufreq_bdata.init_nb.notifier_call) { + /* we only register once */ + cpufreq_bdata.init_nb.notifier_call = init_reg_callback; + bus_register_notifier(&platform_bus_type, + &cpufreq_bdata.init_nb); + } + goto out_vddd; + } + + pr_debug("i %d: freqs.old %d, freqs.new %d\n", + i, freqs.old, freqs.new); + + spin_lock(&cpufreq_bdata.lock); + + if (freqs.old == 24000 && freqs.new > 24000) { + /* turn pll on */ + stmp3xxx_setl(CLKCTRL_PLL_PWD_BIT, REGS_CLKCTRL_BASE + HW_CLKCTRL_PLLCTRL0); + udelay(10); + } else if (freqs.old > 24000 && freqs.new == 24000) + clkseq_setting = __raw_readl(REGS_CLKCTRL_BASE + HW_CLKCTRL_CLKSEQ); + + if (cur_limit && (freqs.old < freqs.new)) { + ret = regulator_set_current_limit(cur_limit, profiles[i].cur, profiles[i].cur); + if (ret) + goto out_cur; + } + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + if (freqs.old > freqs.new) { + int ss = profiles[i].ss; + clk_set_rate(cpu_clk, profiles[i].cpu); + clk_set_rate(ahb_clk, profiles[i].ahb); + clk_set_rate(emi_clk, profiles[i].emi); + __raw_writel(BF(ss, DIGCTL_ARMCACHE_VALID_SS) | + BF(ss, DIGCTL_ARMCACHE_DRTY_SS) | + BF(ss, DIGCTL_ARMCACHE_CACHE_SS) | + BF(ss, DIGCTL_ARMCACHE_DTAG_SS) | + BF(ss, DIGCTL_ARMCACHE_ITAG_SS), REGS_DIGCTL_BASE + HW_DIGCTL_ARMCACHE); + if (vddd && vdddbo && vddio && vdda) { + ret = regulator_set_voltage(vddd, profiles[i].vddd, profiles[i].vddd); + if (ret) + ret = regulator_set_voltage(vddd, + profiles[i].vddd, + profiles[i].vddd); + regulator_set_voltage(vdddbo, profiles[i].vddd_bo, profiles[i].vddd_bo); + + ret = regulator_set_voltage(vddio, profiles[i].vddio, + profiles[i].vddio); + if (ret) + ret = regulator_set_voltage(vddio, + profiles[i].vddio, + profiles[i].vddio); + ret = regulator_set_voltage(vdda, profiles[i].vdda, + profiles[i].vdda); + if (ret) + ret = regulator_set_voltage(vdda, + profiles[i].vdda, + profiles[i].vdda); + } + } else { + int ss = profiles[i].ss; + if (vddd && vdddbo && vddio && vdda) { + ret = regulator_set_voltage(vddd, profiles[i].vddd, profiles[i].vddd); + if (ret) + ret = regulator_set_voltage(vddd, + profiles[i].vddd, + profiles[i].vddd); + regulator_set_voltage(vdddbo, profiles[i].vddd_bo, profiles[i].vddd_bo); + ret = regulator_set_voltage(vddio, profiles[i].vddio, + profiles[i].vddio); + if (ret) + ret = regulator_set_voltage(vddio, + profiles[i].vddio, + profiles[i].vddio); + ret = regulator_set_voltage(vdda, profiles[i].vdda, + profiles[i].vdda); + if (ret) + ret = regulator_set_voltage(vdda, + profiles[i].vdda, + profiles[i].vdda); + } + __raw_writel(BF(ss, DIGCTL_ARMCACHE_VALID_SS) | + BF(ss, DIGCTL_ARMCACHE_DRTY_SS) | + BF(ss, DIGCTL_ARMCACHE_CACHE_SS) | + BF(ss, DIGCTL_ARMCACHE_DTAG_SS) | + BF(ss, DIGCTL_ARMCACHE_ITAG_SS), REGS_DIGCTL_BASE + HW_DIGCTL_ARMCACHE); + clk_set_rate(cpu_clk, profiles[i].cpu); + clk_set_rate(ahb_clk, profiles[i].ahb); + clk_set_rate(emi_clk, profiles[i].emi); + } + udelay(100); + + if (freqs.old > 24000 && freqs.new == 24000) { + /* turn pll off */ + stmp3xxx_clearl(CLKCTRL_PLL_PWD_BIT, REGS_CLKCTRL_BASE + HW_CLKCTRL_PLLCTRL0); + __raw_writel(CLKCTRL_PLL_BYPASS, REGS_CLKCTRL_BASE + HW_CLKCTRL_CLKSEQ); + } else if (freqs.old == 24000 && freqs.new > 24000) + __raw_writel(clkseq_setting, REGS_CLKCTRL_BASE + HW_CLKCTRL_CLKSEQ); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + if (cur_limit && (freqs.old > freqs.new)) /* will not fail */ + regulator_set_current_limit(cur_limit, profiles[i].cur, profiles[i].cur); + + cpufreq_bdata.freq_id = i; + +out_cur: + spin_unlock(&cpufreq_bdata.lock); + if (vddd) + regulator_put(vddd); + if (vddio) + regulator_put(vddio); + if (vdda) + regulator_put(vdda); +out_vddd: + clk_put(emi_clk); +out_emi: + clk_put(ahb_clk); +out_ahb: + clk_put(cpu_clk); +out_cpu: + + return ret; +} + +static int stmp3xxx_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + return set_op(target_freq); +} + +static int reg_callback(struct notifier_block *self, unsigned long event, + void *data) +{ + struct stmp3xxx_cpufreq *_temp = + container_of(self, struct stmp3xxx_cpufreq, nb); + struct cpufreq_policy *policy = &_temp->policy; + int max_prof = ARRAY_SIZE(profiles) - 1; + int ret = -EINVAL; + + pr_debug("%s: entered, _temp %p, policy %p, cpu %d, freq_id %d\n", + __func__, _temp, policy, policy->cpu, _temp->freq_id); + + if (policy) + policy = cpufreq_cpu_get(policy->cpu); + if (!policy) { + printk(KERN_ERR "%s: couldn't get cpufreq policy\n", __func__); + goto out; + } + + /* FIXME: Need a lock: set policy by user VS async USB event */ + switch (event) { + case STMP3XXX_REG5V_IS_USB: + pr_debug("%s: limiting max_freq to %d\n", __func__, + profiles[max_prof - 1].cpu); + policy->user_policy.min = profiles[0].cpu; + policy->user_policy.max = profiles[max_prof - 1].cpu; + if (_temp->freq_id > max_prof - 1) + set_op(profiles[max_prof - 1].cpu); + break; + + case STMP3XXX_REG5V_NOT_USB: + pr_debug("%s: undo limiting max_freq to %d\n", __func__, + profiles[max_prof - 1].cpu); + policy->user_policy.min = profiles[0].cpu; + policy->user_policy.max = profiles[max_prof].cpu; + break; + + default: + pr_info("%s: unknown event %ld\n", __func__, event); + break; + } + cpufreq_cpu_put(policy); + ret = cpufreq_update_policy(policy->cpu); +out: + return ret; +} + +static int init_reg_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + int ret; + struct stmp3xxx_cpufreq *_temp = + container_of(self, struct stmp3xxx_cpufreq, init_nb); + + ret = set_op(profiles[_temp->next_freq_id].cpu); + if (ret == 0) + bus_unregister_notifier(&platform_bus_type, + &cpufreq_bdata.init_nb); + return ret; +} + +static int __init stmp3xxx_cpu_init(struct cpufreq_policy *policy) +{ + struct clk *cpu_clk = clk_get(NULL, "cpu"); + + pr_debug("%s: entered\n", __func__); + if (IS_ERR(cpu_clk)) + return PTR_ERR(cpu_clk); + + if (policy->cpu != 0) + return -EINVAL; + + policy->cur = policy->min = policy->max = clk_get_rate(cpu_clk); + + pr_debug("got CPU clock rate %d\n", policy->cur); + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; + policy->cpuinfo.min_freq = profiles[0].cpu; + policy->cpuinfo.max_freq = profiles[ARRAY_SIZE(profiles) - 1].cpu; + policy->cpuinfo.transition_latency = 1000000; /* 1 ms, assumed */ + clk_put(cpu_clk); + + return 0; +} + +static struct cpufreq_driver stmp3xxx_driver = { + .flags = CPUFREQ_STICKY, + .verify = stmp3xxx_verify_speed, + .target = stmp3xxx_target, + .get = stmp3xxx_getspeed, + .init = stmp3xxx_cpu_init, + .name = "stmp3xxx", +}; + +static int __init stmp3xxx_cpufreq_init(void) +{ + spin_lock_init(&cpufreq_bdata.lock); + cpufreq_bdata.nb.notifier_call = reg_callback; + return cpufreq_register_driver(&stmp3xxx_driver); +} + +static int __init stmp3xxx_reg_init(void) +{ + pr_debug("%s: enter\n", __func__); + if (!cpufreq_bdata.regulator) + __set_new_policy(&cpufreq_bdata.policy); + + if (cpufreq_bdata.regulator) + regulator_set_current_limit(cpufreq_bdata.regulator, + profiles[cpufreq_bdata.freq_id].cur, + profiles[cpufreq_bdata.freq_id].cur); + return 0 ; +} + +arch_initcall(stmp3xxx_cpufreq_init); +late_initcall(stmp3xxx_reg_init); diff --git a/arch/arm/plat-stmp3xxx/dcp-bootstream.c b/arch/arm/plat-stmp3xxx/dcp-bootstream.c new file mode 100644 index 000000000000..133971d77a42 --- /dev/null +++ b/arch/arm/plat-stmp3xxx/dcp-bootstream.c @@ -0,0 +1,303 @@ +/* + * Freescale STMP378X DCP driver for bootstream update. Only handles the OTP KEY + * case and can only encrypt/decrypt. + * + * Author: Pantelis Antoniou <pantelis@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/sysdev.h> +#include <linux/bitops.h> +#include <linux/platform_device.h> +#include <linux/sysfs.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/uaccess.h> + +#include <mach/hardware.h> +#include <asm/irq.h> +#include <asm/io.h> +#include <mach/stmp3xxx.h> +#include <mach/platform.h> +#include <mach/regs-dcp.h> +#include <mach/dcp_bootstream_ioctl.h> + +/* use this channel (same as the ROM) */ +#define ROM_DCP_CHAN 3 + +/* Defines the channel mask for the rom dcp channel */ +#define ROM_DCP_CHAN_MASK (1 << ROM_DCP_CHAN) + +/* Defines the initialization value for the dcp control register */ +#define DCP_CTRL_INIT \ + (BM_DCP_CTRL_GATHER_RESIDUAL_WRITES | \ + BM_DCP_CTRL_ENABLE_CONTEXT_CACHING) + +/* Defines the initialization value for the dcp channel control register */ +#define DCP_CHANNELCTRL_INIT \ + BF(ROM_DCP_CHAN_MASK, DCP_CHANNELCTRL_ENABLE_CHANNEL) + +/* DCP work packet 1 value used to calculate CBC-MAC over the image header */ +#define DCP_PKT1_ENCRYPT \ + (BM_DCP_PACKET1_DECR_SEMAPHORE | \ + BM_DCP_PACKET1_ENABLE_CIPHER | \ + BM_DCP_PACKET1_CIPHER_ENCRYPT | \ + BM_DCP_PACKET1_CIPHER_INIT | \ + BM_DCP_PACKET1_OTP_KEY) + +/* DCP work packet 1 value used to decrypt DEK in key dictionary */ +#define DCP_PKT1_DECRYPT \ + (BM_DCP_PACKET1_DECR_SEMAPHORE | \ + BM_DCP_PACKET1_ENABLE_CIPHER | \ + BM_DCP_PACKET1_CIPHER_INIT | \ + BM_DCP_PACKET1_OTP_KEY) + +/* DCP (decryption) work packet definition */ +struct hw_dcp_packet { + uint32_t pNext; /* next dcp work packet address */ + uint32_t pkt1; /* dcp work packet 1 (control 0) */ + uint32_t pkt2; /* dcp work packet 2 (control 1) */ + uint32_t pSrc; /* source buffer address */ + uint32_t pDst; /* destination buffer address */ + uint32_t size; /* buffer size in bytes */ + uint32_t pPayload; /* payload buffer address */ + uint32_t stat; /* dcp status (written by dcp) */ +}; + +struct dma_area { + struct hw_dcp_packet hw_packet; + uint16_t block[16]; +}; + +struct stmp3xxx_dcp_bootstream_data { + struct device *dev; + struct dma_area *dma_area; + dma_addr_t dma_area_phys; +}; + +/* Only one instance allowed, so this is OK */ +static struct stmp3xxx_dcp_bootstream_data *global_dbd; + +static int stmp3xxx_dcp_bootstream_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct stmp3xxx_dcp_bootstream_data *dbd = global_dbd; + struct dma_area *da = dbd->dma_area; + void __user *argp = (void __user *)arg; + unsigned long timeout; + + /* be paranoid */ + if (dbd == NULL) + return -EBADF; + + if (cmd != DBS_ENC && cmd != DBS_DEC) + return -EINVAL; + + /* copy to (aligned) block */ + if (copy_from_user(da->block, argp, 16)) + return -EFAULT; + + /* Soft reset and remove the clock gate */ + stmp3xxx_setl(BM_DCP_CTRL_SFTRST, REGS_DCP_BASE + HW_DCP_CTRL); + + /* At 24Mhz, it takes no more than 4 clocks (160 ns) Maximum for + * the part to reset, reading the register twice should + * be sufficient to get 4 clks delay. + */ + __raw_readl(REGS_DCP_BASE + HW_DCP_CTRL); + __raw_readl(REGS_DCP_BASE + HW_DCP_CTRL); + + stmp3xxx_clearl(BM_DCP_CTRL_SFTRST | BM_DCP_CTRL_CLKGATE, + REGS_DCP_BASE + HW_DCP_CTRL); + + /* Initialize control registers */ + __raw_writel(DCP_CTRL_INIT, REGS_DCP_BASE + HW_DCP_CTRL); + __raw_writel(DCP_CHANNELCTRL_INIT, REGS_DCP_BASE + HW_DCP_CHANNELCTRL); + + /* The loader does not enable context switching. Give the context + * buffer pointer an illegal address so if context switching is + * inadvertantly enabled, the dcp will return an error instead of + * trashing good memory. The dcp dma cannot access rom, so any rom + * address will do. + */ + __raw_writel(0xFFFF0000, REGS_DCP_BASE + HW_DCP_CONTEXT); + + stmp3xxx_clearl(-1, REGS_DCP_BASE + HW_DCP_CHnSTAT(ROM_DCP_CHAN)); + stmp3xxx_clearl(-1, REGS_DCP_BASE + HW_DCP_STAT); + + da->hw_packet.pNext = 0; + da->hw_packet.pkt1 = BM_DCP_PACKET1_DECR_SEMAPHORE | + BM_DCP_PACKET1_ENABLE_CIPHER | BM_DCP_PACKET1_OTP_KEY | + BM_DCP_PACKET1_INTERRUPT | + (cmd == DBS_ENC ? BM_DCP_PACKET1_CIPHER_ENCRYPT : 0); + da->hw_packet.pkt2 = BF(0, DCP_PACKET2_CIPHER_CFG) | + BF(0, DCP_PACKET2_KEY_SELECT) | + BF(BV_DCP_PACKET2_CIPHER_MODE__ECB, DCP_PACKET2_CIPHER_MODE) | + BF(BV_DCP_PACKET2_CIPHER_SELECT__AES128, DCP_PACKET2_CIPHER_SELECT); + da->hw_packet.pSrc = dbd->dma_area_phys + + offsetof(struct dma_area, block); + da->hw_packet.pDst = da->hw_packet.pSrc; /* in-place */ + da->hw_packet.size = 16; + da->hw_packet.pPayload = 0; + da->hw_packet.stat = 0; + + /* Load the work packet pointer and bump the channel semaphore */ + __raw_writel(dbd->dma_area_phys + + offsetof(struct dma_area, hw_packet), + REGS_DCP_BASE + HW_DCP_CHnCMDPTR(ROM_DCP_CHAN)); + __raw_writel(BF(1, DCP_CHnSEMA_INCREMENT), + REGS_DCP_BASE + HW_DCP_CHnSEMA(ROM_DCP_CHAN)); + + timeout = jiffies + msecs_to_jiffies(100); + + while (time_before(jiffies, timeout) && + (__raw_readl(REGS_DCP_BASE + HW_DCP_STAT) & + BF(ROM_DCP_CHAN_MASK, DCP_STAT_IRQ)) == 0) + cpu_relax(); + + if (!time_before(jiffies, timeout)) { + dev_err(dbd->dev, "Timeout while waiting STAT\n"); + return -ETIMEDOUT; + } + + if ((__raw_readl(HW_DCP_CHnSTAT(ROM_DCP_CHAN)) & 0xff) != 0) { + dev_err(dbd->dev, "Channel stat error 0x%02x\n", + __raw_readl(REGS_DCP_BASE + + HW_DCP_CHnSTAT(ROM_DCP_CHAN)) & 0xff); + return -EFAULT; + } + + if (copy_to_user(argp, da->block, 16)) + return -EFAULT; + + return 0; +} + +static struct file_operations stmp3xxx_dcp_bootstream_fops = { + .owner = THIS_MODULE, + .ioctl = stmp3xxx_dcp_bootstream_ioctl, +}; + +static struct miscdevice stmp3xxx_dcp_bootstream_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "dcpboot", + .fops = &stmp3xxx_dcp_bootstream_fops, +}; + +static int __devinit stmp3xxx_dcp_bootstream_probe(struct platform_device *pdev) +{ + struct stmp3xxx_dcp_bootstream_data *dbd; + int err; + + /* we only allow a single device */ + if (global_dbd != NULL) + return -ENODEV; + + dbd = kzalloc(sizeof(*dbd), GFP_KERNEL); + if (dbd == NULL) + return -ENOMEM; + memset(dbd, 0, sizeof(*dbd)); + + dbd->dev = &pdev->dev; + platform_set_drvdata(pdev, dbd); + + err = misc_register(&stmp3xxx_dcp_bootstream_misc); + if (err != 0) { + dev_err(&pdev->dev, "Unable to register misc device\n"); + goto err_done; + } + + dbd->dma_area = dma_alloc_coherent(&pdev->dev, sizeof(*dbd->dma_area), + &dbd->dma_area_phys, GFP_KERNEL); + if (dbd->dma_area == NULL) { + dev_err(&pdev->dev, "Unable to allocate DMAable memory\n"); + goto err_dereg; + } + + global_dbd = dbd; + + return 0; + +err_dereg: + misc_deregister(&stmp3xxx_dcp_bootstream_misc); +err_done: + kfree(dbd); + return err; +} + +static int stmp3xxx_dcp_bootstream_remove(struct platform_device *pdev) +{ + struct stmp3xxx_dcp_bootstream_data *dbd; + + dbd = platform_get_drvdata(pdev); + platform_set_drvdata(pdev, NULL); + + dma_free_coherent(&pdev->dev, sizeof(*dbd->dma_area), + dbd->dma_area, dbd->dma_area_phys); + misc_deregister(&stmp3xxx_dcp_bootstream_misc); + + kfree(dbd); + + global_dbd = NULL; + + return 0; +} + +#ifdef CONFIG_PM +static int stmp3xxx_dcp_bootstream_suspend(struct platform_device *pdev, + pm_message_t state) +{ + return 0; +} + +static int stmp3xxx_dcp_bootstream_resume(struct platform_device *pdev) +{ + return 0; +} +#else +#define stmp3xxx_dcp_bootstream_suspend NULL +#define stmp3xxx_dcp_bootstream_resume NULL +#endif + +static struct platform_driver stmp3xxx_dcp_bootstream_driver = { + .probe = stmp3xxx_dcp_bootstream_probe, + .remove = stmp3xxx_dcp_bootstream_remove, + .suspend = stmp3xxx_dcp_bootstream_suspend, + .resume = stmp3xxx_dcp_bootstream_resume, + .driver = { + .name = "stmp3xxx-dcpboot", + .owner = THIS_MODULE, + }, +}; + +static int __init stmp3xxx_dcp_bootstream_init(void) +{ + return platform_driver_register(&stmp3xxx_dcp_bootstream_driver); +} + +static void __exit stmp3xxx_dcp_bootstream_exit(void) +{ + platform_driver_unregister(&stmp3xxx_dcp_bootstream_driver); +} + +MODULE_AUTHOR("Pantelis Antoniou <pantelis@embeddedalley.com>"); +MODULE_DESCRIPTION("DCP bootstream driver"); +MODULE_LICENSE("GPL"); + +module_init(stmp3xxx_dcp_bootstream_init); +module_exit(stmp3xxx_dcp_bootstream_exit); diff --git a/arch/arm/plat-stmp3xxx/devices.c b/arch/arm/plat-stmp3xxx/devices.c index 68fed4b8746a..a7b9ddb034bf 100644 --- a/arch/arm/plat-stmp3xxx/devices.c +++ b/arch/arm/plat-stmp3xxx/devices.c @@ -20,16 +20,26 @@ #include <linux/device.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> +#include <linux/fsl_devices.h> +#include <linux/list.h> +#include <linux/delay.h> #include <mach/dma.h> #include <mach/platform.h> #include <mach/stmp3xxx.h> +#include <mach/lcdif.h> #include <mach/regs-lcdif.h> #include <mach/regs-uartapp.h> #include <mach/regs-gpmi.h> #include <mach/regs-usbctrl.h> +#include <mach/regs-usbphy.h> #include <mach/regs-ssp.h> #include <mach/regs-rtc.h> +#include <mach/regs-digctl.h> +#include <mach/regs-ocotp.h> +#include <mach/lcdif.h> +#include <mach/regs-power.h> +#include <mach/regs-clkctrl.h> static u64 common_dmamask = DMA_BIT_MASK(32); @@ -63,6 +73,7 @@ static struct resource appuart_resources[] = { }, }; + struct platform_device stmp3xxx_appuart = { .name = "stmp3xxx-appuart", .id = 0, @@ -74,6 +85,11 @@ struct platform_device stmp3xxx_appuart = { }, }; +struct platform_device stmp3xxx_dbguart = { + .name = "stmp3xxx-dbguart", + .id = -1, +}; + struct platform_device stmp3xxx_watchdog = { .name = "stmp3xxx_wdt", .id = -1, @@ -98,12 +114,22 @@ struct platform_device stmp3xxx_touchscreen = { .num_resources = ARRAY_SIZE(ts_resource), }; +static struct resource keyboard_resource[] = { + { + .flags = IORESOURCE_IRQ, + .start = IRQ_LRADC_CH0, + .end = IRQ_LRADC_CH0, + }, +}; + /* * Keypad device */ struct platform_device stmp3xxx_keyboard = { .name = "stmp3xxx-keyboard", .id = -1, + .resource = keyboard_resource, + .num_resources = ARRAY_SIZE(keyboard_resource), }; static struct resource gpmi_resources[] = { @@ -155,7 +181,7 @@ static struct resource mmc1_resource[] = { struct platform_device stmp3xxx_mmc = { .name = "stmp3xxx-mmc", - .id = 1, + .id = -1, .dev = { .dma_mask = &common_dmamask, .coherent_dma_mask = DMA_BIT_MASK(32), @@ -176,23 +202,48 @@ static struct resource usb_resources[] = { }, }; +static struct fsl_usb2_platform_data udc_platform_data = { + .operating_mode = FSL_USB2_DR_DEVICE, + .phy_mode = FSL_USB2_PHY_UTMI, + .port_enables = FSL_USB2_DONT_REMAP, + .platform_init = NULL, + .platform_uninit = NULL, +}; + struct platform_device stmp3xxx_udc = { .name = "fsl-usb2-udc", .id = -1, .dev = { .dma_mask = &common_dmamask, .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &udc_platform_data, }, .resource = usb_resources, .num_resources = ARRAY_SIZE(usb_resources), }; +static void usb_host_phy_resume(struct fsl_usb2_platform_data *pdata) +{ + stmp3xxx_clearl(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, + REGS_USBPHY_BASE + HW_USBPHY_CTRL); +} + +static struct fsl_usb2_platform_data ehci_platform_data = { + .operating_mode = FSL_USB2_DR_HOST, + .phy_mode = FSL_USB2_PHY_UTMI, + .port_enables = FSL_USB2_DONT_REMAP, + .platform_init = NULL, + .platform_uninit = NULL, + .platform_resume = usb_host_phy_resume, +}; + struct platform_device stmp3xxx_ehci = { .name = "fsl-ehci", .id = -1, .dev = { .dma_mask = &common_dmamask, .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &ehci_platform_data, }, .resource = usb_resources, .num_resources = ARRAY_SIZE(usb_resources), @@ -291,12 +342,17 @@ static struct resource fb_resource[] = { }, }; +static struct stmp3xxx_platform_fb_data stmp3xxx_framebuffer_pdata = { + .list = LIST_HEAD_INIT(stmp3xxx_framebuffer_pdata.list), +}; + struct platform_device stmp3xxx_framebuffer = { .name = "stmp3xxx-fb", .id = -1, .dev = { .dma_mask = &common_dmamask, .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &stmp3xxx_framebuffer_pdata, }, .num_resources = ARRAY_SIZE(fb_resource), .resource = fb_resource, @@ -337,9 +393,85 @@ struct platform_device stmp3xxx_rotdec = { .id = -1, }; +static const struct stmp3xxx_persistent_bit_config +stmp3xxx_persistent_bit_tab[] = { + { .reg = 0, .start = 0, .width = 1, + .name = "CLOCKSOURCE" }, + { .reg = 0, .start = 1, .width = 1, + .name = "ALARM_WAKE_EN" }, + { .reg = 0, .start = 2, .width = 1, + .name = "ALARM_EN" }, + { .reg = 0, .start = 3, .width = 1, + .name = "CLK_SECS" }, + { .reg = 0, .start = 4, .width = 1, + .name = "XTAL24MHZ_PWRUP" }, + { .reg = 0, .start = 5, .width = 1, + .name = "XTAL32MHZ_PWRUP" }, + { .reg = 0, .start = 6, .width = 1, + .name = "XTAL32_FREQ" }, + { .reg = 0, .start = 7, .width = 1, + .name = "ALARM_WAKE" }, + { .reg = 0, .start = 8, .width = 5, + .name = "MSEC_RES" }, + { .reg = 0, .start = 13, .width = 1, + .name = "DISABLE_XTALOK" }, + { .reg = 0, .start = 14, .width = 2, + .name = "LOWERBIAS" }, + { .reg = 0, .start = 16, .width = 1, + .name = "DISABLE_PSWITCH" }, + { .reg = 0, .start = 17, .width = 1, + .name = "AUTO_RESTART" }, + { .reg = 0, .start = 18, .width = 14, + .name = "SPARE_ANALOG" }, + + { .reg = 1, .start = 0, .width = 1, + .name = "FORCE_RECOVERY" }, + { .reg = 1, .start = 1, .width = 1, + .name = "NAND_SECONDARY_BOOT" }, + { .reg = 1, .start = 2, .width = 1, + .name = "NAND_SDK_BLOCK_REWRITE" }, + { .reg = 1, .start = 3, .width = 1, + .name = "SD_SPEED_ENABLE" }, + { .reg = 1, .start = 4, .width = 1, + .name = "SD_INIT_SEQ_1_DISABLE" }, + { .reg = 1, .start = 5, .width = 1, + .name = "SD_CMD0_DISABLE" }, + { .reg = 1, .start = 6, .width = 1, + .name = "SD_INIT_SEQ_2_ENABLE" }, + { .reg = 1, .start = 7, .width = 1, + .name = "OTG_ATL_ROLE_BIT" }, + { .reg = 1, .start = 8, .width = 1, + .name = "OTG_HNP_BIT" }, + { .reg = 1, .start = 9, .width = 1, + .name = "USB_LOW_POWER_MODE" }, + { .reg = 1, .start = 10, .width = 1, + .name = "SKIP_CHECKDISK" }, + { .reg = 1, .start = 11, .width = 1, + .name = "USB_BOOT_PLAYER_MODE" }, + { .reg = 1, .start = 12, .width = 1, + .name = "ENUMERATE_500MA_TWICE" }, + { .reg = 1, .start = 13, .width = 19, + .name = "SPARE_GENERAL" }, + + { .reg = 2, .start = 0, .width = 32, + .name = "SPARE_2" }, + { .reg = 3, .start = 0, .width = 32, + .name = "SPARE_3" }, + { .reg = 4, .start = 0, .width = 32, + .name = "SPARE_4" }, + { .reg = 5, .start = 0, .width = 32, + .name = "SPARE_5" }, +}; + +static struct stmp3xxx_platform_persistent_data stmp3xxx_persistent_data = { + .bit_config_tab = stmp3xxx_persistent_bit_tab, + .bit_config_cnt = ARRAY_SIZE(stmp3xxx_persistent_bit_tab), +}; + struct platform_device stmp3xxx_persistent = { .name = "stmp3xxx-persistent", .id = -1, + .dev.platform_data = &stmp3xxx_persistent_data, }; struct platform_device stmp3xxx_dcp_bootstream = { @@ -387,3 +519,28 @@ struct platform_device stmp3xxx_battery = { .resource = battery_resource, .num_resources = ARRAY_SIZE(battery_resource), }; + +struct resource viim_resources[] = { + [0] = { + .start = REGS_DIGCTL_PHYS, + .end = REGS_DIGCTL_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = REGS_OCOTP_PHYS, + .end = REGS_OCOTP_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device stmp3xxx_viim = { + .name = "mxs_viim", + .id = 0, + .resource = viim_resources, + .num_resources = ARRAY_SIZE(viim_resources), +}; + +struct platform_device stmp3xxx_spdif = { + .name = "stmp3xxx-spdif", + .id = -1, +}; diff --git a/arch/arm/plat-stmp3xxx/dma.c b/arch/arm/plat-stmp3xxx/dma.c index d2f497764dce..72a5745c94fb 100644 --- a/arch/arm/plat-stmp3xxx/dma.c +++ b/arch/arm/plat-stmp3xxx/dma.c @@ -94,15 +94,15 @@ int stmp3xxx_dma_read_semaphore(int channel) switch (STMP3XXX_DMA_BUS(channel)) { case STMP3XXX_BUS_APBH: - sem = __raw_readl(REGS_APBH_BASE + HW_APBH_CHn_SEMA + - STMP3XXX_DMA_CHANNEL(channel) * 0x70); + sem = __raw_readl(REGS_APBH_BASE + + HW_APBH_CHn_SEMA(STMP3XXX_DMA_CHANNEL(channel))); sem &= BM_APBH_CHn_SEMA_PHORE; sem >>= BP_APBH_CHn_SEMA_PHORE; break; case STMP3XXX_BUS_APBX: - sem = __raw_readl(REGS_APBX_BASE + HW_APBX_CHn_SEMA + - STMP3XXX_DMA_CHANNEL(channel) * 0x70); + sem = __raw_readl(REGS_APBX_BASE + + HW_APBX_CHn_SEMA(STMP3XXX_DMA_CHANNEL(channel))); sem &= BM_APBX_CHn_SEMA_PHORE; sem >>= BP_APBX_CHn_SEMA_PHORE; break; @@ -187,13 +187,13 @@ void stmp3xxx_dma_go(int channel, switch (STMP3XXX_DMA_BUS(channel)) { case STMP3XXX_BUS_APBH: - c = REGS_APBH_BASE + HW_APBH_CHn_NXTCMDAR + 0x70 * ch; - s = REGS_APBH_BASE + HW_APBH_CHn_SEMA + 0x70 * ch; + c = REGS_APBH_BASE + HW_APBH_CHn_NXTCMDAR(ch); + s = REGS_APBH_BASE + HW_APBH_CHn_SEMA(ch); break; case STMP3XXX_BUS_APBX: - c = REGS_APBX_BASE + HW_APBX_CHn_NXTCMDAR + 0x70 * ch; - s = REGS_APBX_BASE + HW_APBX_CHn_SEMA + 0x70 * ch; + c = REGS_APBX_BASE + HW_APBX_CHn_NXTCMDAR(ch); + s = REGS_APBX_BASE + HW_APBX_CHn_SEMA(ch); break; default: @@ -212,13 +212,13 @@ int stmp3xxx_dma_running(int channel) { switch (STMP3XXX_DMA_BUS(channel)) { case STMP3XXX_BUS_APBH: - return (__raw_readl(REGS_APBH_BASE + HW_APBH_CHn_SEMA + - 0x70 * STMP3XXX_DMA_CHANNEL(channel))) & + return (__raw_readl(REGS_APBH_BASE + + HW_APBH_CHn_SEMA(STMP3XXX_DMA_CHANNEL(channel)))) & BM_APBH_CHn_SEMA_PHORE; case STMP3XXX_BUS_APBX: - return (__raw_readl(REGS_APBX_BASE + HW_APBX_CHn_SEMA + - 0x70 * STMP3XXX_DMA_CHANNEL(channel))) & + return (__raw_readl(REGS_APBX_BASE + + HW_APBX_CHn_SEMA(STMP3XXX_DMA_CHANNEL(channel)))) & BM_APBX_CHn_SEMA_PHORE; default: BUG(); @@ -323,7 +323,7 @@ void stmp37xx_circ_advance_active(struct stmp37xx_circ_dma_chain *chain, unsigned count) { void __iomem *c; - u32 mask_clr, mask; + u32 mask_clr, mask, reg; BUG_ON(chain->free_count < count); chain->free_count -= count; @@ -333,12 +333,12 @@ void stmp37xx_circ_advance_active(struct stmp37xx_circ_dma_chain *chain, switch (chain->bus) { case STMP3XXX_BUS_APBH: - c = REGS_APBH_BASE + HW_APBH_CHn_SEMA + 0x70 * chain->channel; + c = REGS_APBH_BASE + HW_APBH_CHn_SEMA(chain->channel); mask_clr = BM_APBH_CHn_SEMA_INCREMENT_SEMA; mask = BF(count, APBH_CHn_SEMA_INCREMENT_SEMA); break; case STMP3XXX_BUS_APBX: - c = REGS_APBX_BASE + HW_APBX_CHn_SEMA + 0x70 * chain->channel; + c = REGS_APBX_BASE + HW_APBX_CHn_SEMA(chain->channel); mask_clr = BM_APBX_CHn_SEMA_INCREMENT_SEMA; mask = BF(count, APBX_CHn_SEMA_INCREMENT_SEMA); break; @@ -349,8 +349,11 @@ void stmp37xx_circ_advance_active(struct stmp37xx_circ_dma_chain *chain, /* Set counting semaphore (kicks off transfer). Assumes peripheral has been set up correctly */ - stmp3xxx_clearl(mask_clr, c); - stmp3xxx_setl(mask, c); + reg = __raw_readl(c); + reg &= ~mask_clr; + __raw_writel(reg, c); + reg |= mask; + __raw_writel(reg, c); } EXPORT_SYMBOL(stmp37xx_circ_advance_active); @@ -373,6 +376,7 @@ EXPORT_SYMBOL(stmp37xx_circ_advance_cooked); void stmp3xxx_dma_set_alt_target(int channel, int function) { + u32 reg; #if defined(CONFIG_ARCH_STMP37XX) unsigned bits = 4; #elif defined(CONFIG_ARCH_STMP378X) @@ -398,8 +402,11 @@ void stmp3xxx_dma_set_alt_target(int channel, int function) default: BUG(); } - stmp3xxx_clearl(mask << shift, c); - stmp3xxx_setl(mask << shift, c); + reg = __raw_readl(c); + reg &= ~(mask << shift); + __raw_writel(reg, c); + reg |= mask; + __raw_writel(reg, c); } EXPORT_SYMBOL(stmp3xxx_dma_set_alt_target); diff --git a/arch/arm/plat-stmp3xxx/gpmi.c b/arch/arm/plat-stmp3xxx/gpmi.c new file mode 100644 index 000000000000..f62c70aa13a8 --- /dev/null +++ b/arch/arm/plat-stmp3xxx/gpmi.c @@ -0,0 +1,40 @@ +/* + * Freescale STMP37XX/STMP378X GPMI module pin multiplexing + * + * Author: dmitry pervushin <dimka@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/module.h> +#include <mach/platform.h> + +int gpmi_pinmux_request(char *title) +{ + int err = 0; + + err = stmp3xxx_request_pin_group(&gpmi_pins, title); + + return err; +} +EXPORT_SYMBOL_GPL(gpmi_pinmux_request); + +void gpmi_pinmux_free(char *title) +{ + stmp3xxx_release_pin_group(&gpmi_pins, title); +} +EXPORT_SYMBOL_GPL(gpmi_pinmux_free); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("dmitry pervushin <dimka@embeddedalley.com>"); diff --git a/arch/arm/plat-stmp3xxx/include/mach/arc_otg.h b/arch/arm/plat-stmp3xxx/include/mach/arc_otg.h new file mode 100644 index 000000000000..e0213b21f4ff --- /dev/null +++ b/arch/arm/plat-stmp3xxx/include/mach/arc_otg.h @@ -0,0 +1,97 @@ +/* + * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#ifndef __ASM_ARCH_MXC_ARC_OTG_H__ +#define __ASM_ARCH_MXC_ARC_OTG_H__ + +#include <mach/hardware.h> + +/* + * register bits + */ + +/* x_PORTSCx */ +#define PORTSC_PTS_MASK (3 << 30)/* parallel xcvr select mask */ +#define PORTSC_PTS_UTMI (0 << 30)/* UTMI/UTMI+ */ +#define PORTSC_PTS_PHILIPS (1 << 30)/* Philips classic */ +#define PORTSC_PTS_ULPI (2 << 30)/* ULPI */ +#define PORTSC_PTS_SERIAL (3 << 30)/* serial */ +#define PORTSC_STS (1 << 29)/* serial xcvr select */ +#define PORTSC_PTW (1 << 28)/* UTMI width */ +#define PORTSC_PORT_POWER (1 << 12)/* port power */ +#define PORTSC_LS_MASK (3 << 10)/* Line State mask */ +#define PORTSC_LS_SE0 (0 << 10)/* SE0 */ +#define PORTSC_LS_K_STATE (1 << 10)/* K-state */ +#define PORTSC_LS_J_STATE (2 << 10)/* J-state */ +#define PORTSC_PORT_RESET (1 << 8)/* Port reset */ +#define PORTSC_PORT_SUSPEND (1 << 7)/* Suspend */ +#define PORTSC_PORT_FORCE_RESUME (1 << 6)/* Force port resume */ +#define PORTSC_OVER_CURRENT_CHG (1 << 5)/* over current change */ +#define PORTSC_OVER_CURRENT_ACT (1 << 4)/* over currrent active */ +#define PORTSC_PORT_EN_DIS_CHANGE (1 << 3)/* port {en,dis}able change */ +#define PORTSC_PORT_ENABLE (1 << 2)/* port enabled */ +#define PORTSC_CONNECT_STATUS_CHANGE (1 << 1)/* connect status change */ +#define PORTSC_CURRENT_CONNECT_STATUS (1 << 0)/* current connect status */ + +#define PORTSC_W1C_BITS (PORTSC_CONNECT_STATUS_CHANGE | \ + PORTSC_PORT_EN_DIS_CHANGE | \ + PORTSC_OVER_CURRENT_CHG) + +/* UOG_OTGSC Register Bits */ +/* control bits: */ +#define OTGSC_CTRL_VBUS_DISCHARGE (1 << 0) +#define OTGSC_CTRL_VBUS_CHARGE (1 << 1) +#define OTGSC_CTRL_OTG_TERM (1 << 3)/* controls DM pulldown */ +#define OTGSC_CTRL_DATA_PULSING (1 << 4) +#define OTGSC_CTRL_USB_ID_PU (1 << 5)/* enable ID pullup */ +/* current status: (R/O) */ +#define OTGSC_STS_USB_ID (1 << 8)/* 0=A-device 1=B-device */ +#define OTGSC_STS_A_VBUS_VALID (1 << 9) +#define OTGSC_STS_A_SESSION_VALID (1 << 10) +#define OTGSC_STS_B_SESSION_VALID (1 << 11) +#define OTGSC_STS_B_SESSION_END (1 << 12) +#define OTGSC_STS_1ms_TIMER (1 << 13) +#define OTGSC_STS_DATA_PULSE (1 << 14) +/* interrupt status: (write to clear) */ +#define OTGSC_IS_MASK (0x7f << 16) +#define OTGSC_IS_USB_ID (1 << 16) +#define OTGSC_IS_A_VBUS_VALID (1 << 17) +#define OTGSC_IS_A_SESSION_VALID (1 << 18) +#define OTGSC_IS_B_SESSION_VALID (1 << 19) +#define OTGSC_IS_B_SESSION_END (1 << 20) +#define OTGSC_IS_1ms_TIMER (1 << 21) +#define OTGSC_IS_DATA_PULSE (1 << 22) +/* interrupt enables: */ +#define OTGSC_IE_MASK (0x7f << 24) +#define OTGSC_IE_USB_ID (1 << 24) +#define OTGSC_IE_A_VBUS_VALID (1 << 25) +#define OTGSC_IE_A_SESSION_VALID (1 << 26) +#define OTGSC_IE_B_SESSION_VALID (1 << 27) +#define OTGSC_IE_B_SESSION_END (1 << 28) +#define OTGSC_IE_1ms_TIMER (1 << 29) +#define OTGSC_IE_DATA_PULSE (1 << 30) + +/* x_USBMODE */ +#define USBMODE_SLOM (1 << 3) /* setup lockout mode */ +#define USBMODE_ES (1 << 2) /* (big) endian select */ +#define USBMODE_CM_MASK (3 << 0) /* controller mode mask */ +#define USBMODE_CM_HOST (3 << 0) /* host */ +#define USBMODE_CM_DEVICE (2 << 0) /* device */ +#define USBMODE_CM_reserved (1 << 0) /* reserved */ + +/* USBCMD */ +#define UCMD_RUN_STOP (1 << 0) /* controller run/stop */ +#define UCMD_RESET (1 << 1) /* controller reset */ +#define UCMD_ITC_NO_THRESHOLD (~(0xff << 16))/* Interrupt Threshold Control */ + +#define HCSPARAMS_PPC (0x1<<4) /* Port Power Control */ +#endif diff --git a/arch/arm/plat-stmp3xxx/include/mach/dcp_bootstream_ioctl.h b/arch/arm/plat-stmp3xxx/include/mach/dcp_bootstream_ioctl.h new file mode 100644 index 000000000000..aec63a5bfef4 --- /dev/null +++ b/arch/arm/plat-stmp3xxx/include/mach/dcp_bootstream_ioctl.h @@ -0,0 +1,32 @@ +/* + * Freescale STMP378X DCP driver for bootstream update. Only handles the OTP KEY + * case and can only encrypt/decrypt. + * + * Author: Pantelis Antoniou <pantelis@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#ifndef DCP_BOOTSTREAM_IOCTL_H +#define DCP_BOOTSTREAM_IOCTL_H + +/* remember to have included the proper _IO definition + * file before hand. + * For user space it's <sys/ioctl.h> + */ + +#define DBS_IOCTL_BASE 'd' + +#define DBS_ENC _IOW(DBS_IOCTL_BASE, 0x00, uint8_t[16]) +#define DBS_DEC _IOW(DBS_IOCTL_BASE, 0x01, uint8_t[16]) + +#endif diff --git a/arch/arm/plat-stmp3xxx/include/mach/fsl_usb.h b/arch/arm/plat-stmp3xxx/include/mach/fsl_usb.h new file mode 100644 index 000000000000..87cc7d8a7abf --- /dev/null +++ b/arch/arm/plat-stmp3xxx/include/mach/fsl_usb.h @@ -0,0 +1,60 @@ +/* + * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* + * USB Host side, platform-specific functionality. + */ + +#include <linux/usb/fsl_xcvr.h> +#include <mach/arc_otg.h> + +/* ehci_arc_hc_driver.flags value */ +#define FSL_PLATFORM_HC_FLAGS (HCD_USB2 | HCD_MEMORY) + +static void fsl_setup_phy(struct ehci_hcd *ehci, + enum fsl_usb2_phy_modes phy_mode, + int port_offset); + +static inline void fsl_platform_usb_setup(struct ehci_hcd *ehci) +{ + struct fsl_usb2_platform_data *pdata; + + pdata = ehci_to_hcd(ehci)->self.controller->platform_data; + fsl_setup_phy(ehci, pdata->phy_mode, 0); +} + +static inline void fsl_platform_set_host_mode(struct usb_hcd *hcd) +{ + unsigned int temp; + struct fsl_usb2_platform_data *pdata; + + pdata = hcd->self.controller->platform_data; + + if (pdata->xcvr_ops && pdata->xcvr_ops->set_host) + pdata->xcvr_ops->set_host(); + + /* set host mode */ + temp = readl(hcd->regs + 0x1a8); + writel(temp | USBMODE_CM_HOST, hcd->regs + 0x1a8); +} + +/* Needed for i2c/serial transceivers */ +static inline void +fsl_platform_set_vbus_power(struct fsl_usb2_platform_data *pdata, int on) +{ +} + +/* Set USB AHB burst length for host */ +static inline void fsl_platform_set_ahb_burst(struct usb_hcd *hcd) +{ +} diff --git a/arch/arm/plat-stmp3xxx/include/mach/fsl_usb_gadget.h b/arch/arm/plat-stmp3xxx/include/mach/fsl_usb_gadget.h new file mode 100644 index 000000000000..638bfa4be52b --- /dev/null +++ b/arch/arm/plat-stmp3xxx/include/mach/fsl_usb_gadget.h @@ -0,0 +1,40 @@ +/* + * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* + * USB Gadget side, platform-specific functionality. + */ + +#include <linux/usb/fsl_xcvr.h> + +/* Needed for i2c/serial transceivers */ +static inline void +fsl_platform_set_device_mode(struct fsl_usb2_platform_data *pdata) +{ +} + +static inline void +fsl_platform_pullup_enable(struct fsl_usb2_platform_data *pdata) +{ +} + +static inline void +fsl_platform_pullup_disable(struct fsl_usb2_platform_data *pdata) +{ +} + +static inline void +fsl_platform_set_test_mode(struct fsl_usb2_platform_data *pdata, + enum usb_test_mode mode) +{ +} diff --git a/arch/arm/plat-stmp3xxx/include/mach/gpmi.h b/arch/arm/plat-stmp3xxx/include/mach/gpmi.h index e166432910ad..b70b6e39754a 100644 --- a/arch/arm/plat-stmp3xxx/include/mach/gpmi.h +++ b/arch/arm/plat-stmp3xxx/include/mach/gpmi.h @@ -1,12 +1,28 @@ #ifndef __MACH_GPMI_H +#include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> #include <mach/regs-gpmi.h> +#define GPMI_PART_CONCAT 0x8000 /* indicates that partitions + should be concatenated */ + struct gpmi_platform_data { - void *pins; - int nr_parts; - struct mtd_partition *parts; - const char *part_types[]; + + u_int32_t uid_offset; + u_int32_t uid_size; + + int items; + int io_uA; + char *concat_name; + char **concat_parts; + int (*pinmux) (int req); + + struct { + const char **part_probe_types; + int nr_partitions; + struct mtd_partition *partitions; + } parts[]; + }; #endif diff --git a/arch/arm/plat-stmp3xxx/include/mach/lradc.h b/arch/arm/plat-stmp3xxx/include/mach/lradc.h new file mode 100644 index 000000000000..56b686896f29 --- /dev/null +++ b/arch/arm/plat-stmp3xxx/include/mach/lradc.h @@ -0,0 +1,60 @@ +/* + * Freescale STMP37XX/STMP378X LRADC helper interface + * + * Embedded Alley Solutions, Inc <source@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#ifndef __ASM_PLAT_LRADC_H +#define __ASM_PLAT_LRADC_H + +int hw_lradc_use_channel(int); +int hw_lradc_unuse_channel(int); +extern u32 hw_lradc_vddio(void); +void hw_lradc_set_delay_trigger_kick(int trigger, int value); +void hw_lradc_configure_channel(int channel, int enable_div2, + int enable_acc, int samples); +int hw_lradc_present(int channel); +int hw_lradc_init_ladder(int channel, int trigger, unsigned sampling); +int hw_lradc_stop_ladder(int channel, int trigger); +void hw_lradc_set_delay_trigger(int trigger, u32 trigger_lradc, + u32 delay_triggers, u32 loops, u32 delays); +void hw_lradc_clear_delay_trigger(int trigger, u32 trigger_lradc, + u32 delay_triggers); + + +#define LRADC_CH0 0 +#define LRADC_CH1 1 +#define LRADC_CH2 2 +#define LRADC_CH3 3 +#define LRADC_CH4 4 +#define LRADC_CH5 5 +#define LRADC_CH6 6 +#define LRADC_CH7 7 +#define LRADC_TOUCH_X_PLUS LRADC_CH2 +#define LRADC_TOUCH_Y_PLUS LRADC_CH3 +#define LRADC_TOUCH_X_MINUS LRADC_CH4 +#define LRADC_TOUCH_Y_MINUS LRADC_CH5 +#define VDDIO_VOLTAGE_CH LRADC_CH6 +#define BATTERY_VOLTAGE_CH LRADC_CH7 + +#define LRADC_CLOCK_6MHZ 0 +#define LRADC_CLOCK_4MHZ 1 +#define LRADC_CLOCK_3MHZ 2 +#define LRADC_CLOCK_2MHZ 3 + +#define LRADC_DELAY_TRIGGER_BUTTON 0 +#define LRADC_DELAY_TRIGGER_BATTERY 1 +#define LRADC_DELAY_TRIGGER_TOUCHSCREEN 2 + +#endif /* __ASM_PLAT_LRADC_H */ diff --git a/arch/arm/plat-stmp3xxx/include/mach/memory.h b/arch/arm/plat-stmp3xxx/include/mach/memory.h index 7b875a07a1a7..b152f0ef30c0 100644 --- a/arch/arm/plat-stmp3xxx/include/mach/memory.h +++ b/arch/arm/plat-stmp3xxx/include/mach/memory.h @@ -14,9 +14,50 @@ #ifndef __ASM_ARCH_MEMORY_H #define __ASM_ARCH_MEMORY_H +#include <asm/page.h> +#include <asm/sizes.h> + /* * Physical DRAM offset. */ #define PHYS_OFFSET UL(0x40000000) +#ifndef __ASSEMBLY__ + +#ifdef CONFIG_DMA_ZONE_SIZE +#define MXC_DMA_ZONE_SIZE ((CONFIG_DMA_ZONE_SIZE * SZ_1M) >> PAGE_SHIFT) +#else +#define MXC_DMA_ZONE_SIZE ((12 * SZ_1M) >> PAGE_SHIFT) +#endif + +static inline void __arch_adjust_zones(int node, unsigned long *zone_size, + unsigned long *zhole_size) +{ + if (node != 0) + return; + /* Create separate zone to reserve memory for DMA */ + zone_size[1] = zone_size[0] - MXC_DMA_ZONE_SIZE; + zone_size[0] = MXC_DMA_ZONE_SIZE; + zhole_size[1] = zhole_size[0]; + zhole_size[0] = 0; +} + +#define arch_adjust_zones(node, size, holes) \ + __arch_adjust_zones(node, size, holes) + +#endif +/* + * Virtual view <-> DMA view memory address translations + * virt_to_bus: Used to translate the virtual address to an + * address suitable to be passed to set_dma_addr + * bus_to_virt: Used to convert an address for DMA operations + * to an address that the kernel can use. + */ +#define __virt_to_bus(x) __virt_to_phys(x) +#define __bus_to_virt(x) __phys_to_virt(x) + +#define ISA_DMA_THRESHOLD (0x0003ffffULL) + +#define CONSISTENT_DMA_SIZE SZ_32M + #endif diff --git a/arch/arm/plat-stmp3xxx/include/mach/mmc.h b/arch/arm/plat-stmp3xxx/include/mach/mmc.h index ba81e1543761..5b68bff809ca 100644 --- a/arch/arm/plat-stmp3xxx/include/mach/mmc.h +++ b/arch/arm/plat-stmp3xxx/include/mach/mmc.h @@ -4,11 +4,20 @@ #include <mach/regs-ssp.h> struct stmp3xxxmmc_platform_data { - int (*get_wp)(void); - unsigned long (*setclock)(void __iomem *base, unsigned long); - void (*cmd_pullup)(int); - int (*hw_init)(void); + int (*hw_init)(void); void (*hw_release)(void); + void (*cmd_pullup)(int enable); + int (*get_wp)(void); + unsigned long (*setclock)(unsigned long hz); + int read_uA; + int write_uA; }; + +extern unsigned long stmp3xxxmmc_setclock_ssp1(unsigned long hz); +extern void stmp3xxxmmc_cmd_pullup_ssp1(int enable); +extern void stmp3xxxmmc_hw_release_ssp1(void); +extern int stmp3xxxmmc_hw_init_ssp1(void); +extern int stmp3xxxmmc_get_wp(void); + #endif diff --git a/arch/arm/plat-stmp3xxx/include/mach/ocram-malloc.h b/arch/arm/plat-stmp3xxx/include/mach/ocram-malloc.h new file mode 100644 index 000000000000..c8bee5352972 --- /dev/null +++ b/arch/arm/plat-stmp3xxx/include/mach/ocram-malloc.h @@ -0,0 +1,26 @@ +/* + * Freescale STMP37XX/STMP378X OCRAM allocator interface + * + * Embedded Alley Solutions, Inc <source@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#ifndef __ASM_PLAT_OCRAM_MALLOC_H +#define __ASM_PLAT_OCRAM_MALLOC_H + +extern int ocram_malloc_init(void); + +extern void *ocram_malloc(size_t size, dma_addr_t *phys); +extern void ocram_free(void *tofree); + +#endif /* __ASM_PLAT_OCRAM_MALLOC_H */ diff --git a/arch/arm/plat-stmp3xxx/include/mach/pinmux.h b/arch/arm/plat-stmp3xxx/include/mach/pinmux.h index cc5af82279ad..763f59572977 100644 --- a/arch/arm/plat-stmp3xxx/include/mach/pinmux.h +++ b/arch/arm/plat-stmp3xxx/include/mach/pinmux.h @@ -24,7 +24,6 @@ #include <asm-generic/gpio.h> /* Pin definitions */ -#include "pins.h" #include <mach/pins.h> /* @@ -94,6 +93,10 @@ int stmp3xxx_request_pin(unsigned id, enum pin_fun fun, const char *label); /* Release pin */ void stmp3xxx_release_pin(unsigned id, const char *label); +int stmp3xxx_request_pin_group(struct pin_group *pin_group, const char *label); + +void stmp3xxx_release_pin_group(struct pin_group *pin_group, const char *label); + void stmp3xxx_set_pin_type(unsigned id, enum pin_fun fun); /* diff --git a/arch/arm/plat-stmp3xxx/include/mach/pins.h b/arch/arm/plat-stmp3xxx/include/mach/pins.h deleted file mode 100644 index c573318e1caa..000000000000 --- a/arch/arm/plat-stmp3xxx/include/mach/pins.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Freescale STMP37XX/STMP378X Pin multiplexing interface definitions - * - * Author: Vladislav Buzov <vbuzov@embeddedalley.com> - * - * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. - */ - -/* - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ -#ifndef __ASM_PLAT_PINS_H -#define __ASM_PLAT_PINS_H - -#define STMP3XXX_PINID(bank, pin) (bank * 32 + pin) -#define STMP3XXX_PINID_TO_BANK(pinid) (pinid / 32) -#define STMP3XXX_PINID_TO_PINNUM(pinid) (pinid % 32) - -/* - * Special invalid pin identificator to show a pin doesn't exist - */ -#define PINID_NO_PIN STMP3XXX_PINID(0xFF, 0xFF) - -#endif /* __ASM_PLAT_PINS_H */ diff --git a/arch/arm/plat-stmp3xxx/include/mach/power.h b/arch/arm/plat-stmp3xxx/include/mach/power.h new file mode 100644 index 000000000000..ac90f8621099 --- /dev/null +++ b/arch/arm/plat-stmp3xxx/include/mach/power.h @@ -0,0 +1,67 @@ +/* + * Freescale STMP37XX/STMP378X voltage regulator structure declarations + * + * Embedded Alley Solutions, Inc <sources@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#ifndef __VOLTAGE_H +#define __VOLTAGE_H +#include <linux/completion.h> +//#include <linux/regulator/consumer.h> +#include <linux/regulator/driver.h> + +struct stmp3xxx_regulator { + struct regulator_desc regulator; + struct stmp3xxx_regulator *parent; + struct stmp3xxx_platform_regulator_data *rdata; + struct completion done; + + spinlock_t lock; + wait_queue_head_t wait_q; + struct notifier_block nb; + + int mode; + int cur_voltage; + int cur_current; + int next_current; +}; + + +struct stmp3xxx_platform_regulator_data { + char name[80]; + char *parent_name; + int (*reg_register)(struct stmp3xxx_regulator *sreg); + int (*set_voltage)(struct stmp3xxx_regulator *sreg, int uv); + int (*get_voltage)(struct stmp3xxx_regulator *sreg); + int (*set_current)(struct stmp3xxx_regulator *sreg, int uA); + int (*get_current)(struct stmp3xxx_regulator *sreg); + int (*enable)(struct stmp3xxx_regulator *sreg); + int (*disable)(struct stmp3xxx_regulator *sreg); + int (*is_enabled)(struct stmp3xxx_regulator *sreg); + int (*set_mode)(struct stmp3xxx_regulator *sreg, int mode); + int (*get_mode)(struct stmp3xxx_regulator *sreg); + int (*get_optimum_mode)(struct stmp3xxx_regulator *sreg, + int input_uV, int output_uV, int load_uA); + u32 control_reg; + int min_voltage; + int max_voltage; + int max_current; + struct regulation_constraints *constraints; +}; + +int stmp3xxx_register_regulator( + struct stmp3xxx_regulator *reg_data, int reg, + struct regulator_init_data *initdata); + +#endif /* __VOLTAGE_H */ diff --git a/arch/arm/plat-stmp3xxx/include/mach/pwm-led.h b/arch/arm/plat-stmp3xxx/include/mach/pwm-led.h new file mode 100644 index 000000000000..03e530727488 --- /dev/null +++ b/arch/arm/plat-stmp3xxx/include/mach/pwm-led.h @@ -0,0 +1,25 @@ +/* + * Freescale STMP37XX/STMP378X PWM LED arch-dependent structure + * and functions declarations + * + * Author: Drew Benedetti <drewb@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#ifndef __ASM_PLAT_PWM_LED_H +#define __ASM_PLAT_PWM_LED_H + +extern int pwm_led_pinmux_request(int, char *); +extern void pwm_led_pinmux_free(int, char *); + +#endif /* __ASM_PLAT_PWM_LED_H */ diff --git a/arch/arm/plat-stmp3xxx/include/mach/regulator.h b/arch/arm/plat-stmp3xxx/include/mach/regulator.h new file mode 100644 index 000000000000..01880bd157a7 --- /dev/null +++ b/arch/arm/plat-stmp3xxx/include/mach/regulator.h @@ -0,0 +1,23 @@ +/* + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#ifndef __PLAT_REGULATOR_H_ +#define __PLAT_REGULATOR_H_ +#define STMP3XXX_REG5V_NOT_USB 0 +#define STMP3XXX_REG5V_IS_USB 1 +#define STMP3XXX_VDDD 0 +#define STMP3XXX_VDDA 1 +#define STMP3XXX_VDDIO 2 +#define STMP3XXX_VDDDBO 3 +#define STMP3XXX_OVERALL_CUR 4 + +#endif diff --git a/arch/arm/plat-stmp3xxx/include/mach/rotdec.h b/arch/arm/plat-stmp3xxx/include/mach/rotdec.h new file mode 100644 index 000000000000..6e81bb32d3b9 --- /dev/null +++ b/arch/arm/plat-stmp3xxx/include/mach/rotdec.h @@ -0,0 +1,25 @@ +/* + * Freescale STMP37XX/STMP378X dev board rotary encoder arch-dependent + * structure and functions declarations + * + * Author: Drew Benedetti <drewb@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#ifndef __ASM_PLAT_ROTDEC_H +#define __ASM_PLAT_ROTDEC_H + +extern int rotdec_pinmux_request(void); +extern void rotdec_pinmux_free(void); + +#endif /* __ASM_PLAT_ROTDEC_H */ diff --git a/arch/arm/plat-stmp3xxx/include/mach/stmp3xxx.h b/arch/arm/plat-stmp3xxx/include/mach/stmp3xxx.h index 2e300feaa4cf..a697124b5ce7 100644 --- a/arch/arm/plat-stmp3xxx/include/mach/stmp3xxx.h +++ b/arch/arm/plat-stmp3xxx/include/mach/stmp3xxx.h @@ -3,7 +3,7 @@ * * Embedded Alley Solutions, Inc <source@embeddedalley.com> * - * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. */ @@ -19,9 +19,43 @@ #define __ASM_PLAT_STMP3XXX_H #include <linux/irq.h> +#include <linux/suspend.h> +#include <linux/platform_device.h> extern struct sys_timer stmp3xxx_timer; +struct stmpkbd_keypair { + int raw; + int kcode; +}; + +struct stmp37xx_spi_platform_data { + unsigned irq_pin; + + int (*hw_init)(void *spi); + int (*hw_release)(void *spi); +}; + +struct stmp3xxx_persistent_bit_config { + int reg; + int start; + int width; + const char *name; +}; + +struct stmp3xxx_platform_persistent_data { + const struct stmp3xxx_persistent_bit_config *bit_config_tab; + int bit_config_cnt; +}; + +#define STMP3XXX_USB_DONT_REMAP 0x00000001 +struct stmp3xxx_usb_platform_data { + unsigned flags; + int (*phy_enable)(struct platform_device *); + void (*hw_init)(void); + void (*hw_release)(void); +}; + void stmp3xxx_init_irq(struct irq_chip *chip); void stmp3xxx_init(void); int stmp3xxx_reset_block(void __iomem *hwreg, int just_enable); @@ -32,18 +66,32 @@ extern struct platform_device stmp3xxx_dbguart, stmp3xxx_keyboard, stmp3xxx_gpmi, stmp3xxx_mmc, + stmp3xxx_mmc2, stmp3xxx_udc, stmp3xxx_ehci, + stmp3xxx_usb, stmp3xxx_rtc, stmp3xxx_spi1, stmp3xxx_spi2, stmp3xxx_backlight, stmp3xxx_rotdec, + stmp3xxx_ssp1, + stmp3xxx_ssp2, stmp3xxx_dcp, stmp3xxx_dcp_bootstream, stmp3xxx_persistent, stmp3xxx_framebuffer, - stmp3xxx_battery; + stmp3xxx_battery, + stmp378x_i2c, + stmp378x_pxp, + stmp378x_audio, + stmp3xxx_viim, + stmp3xxx_spdif; +#ifdef CONFIG_PM +suspend_state_t stmp37xx_pm_get_target(void); +int stmp37xx_pm_sleep_was_deep(void); +#endif + int stmp3xxx_ssp1_device_register(void); int stmp3xxx_ssp2_device_register(void); @@ -51,4 +99,6 @@ struct pin_group; void stmp3xxx_release_pin_group(struct pin_group *pin_group, const char *label); int stmp3xxx_request_pin_group(struct pin_group *pin_group, const char *label); +int get_evk_board_version(void); + #endif /* __ASM_PLAT_STMP3XXX_H */ diff --git a/arch/arm/plat-stmp3xxx/include/mach/unique-id.h b/arch/arm/plat-stmp3xxx/include/mach/unique-id.h new file mode 100644 index 000000000000..ee22ec2502c5 --- /dev/null +++ b/arch/arm/plat-stmp3xxx/include/mach/unique-id.h @@ -0,0 +1,30 @@ +/* + * Unique ID interface for ID storage providers + * + * Embedded Alley Solutions, Inc <source@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#ifndef __UNIQUE_ID_H +#define __UNIQUE_ID_H + +struct uid_ops { + ssize_t (*id_show)(void *context, char *page, int ascii); + ssize_t (*id_store)(void *context, const char *page, + size_t count, int ascii); +}; + +struct kobject *uid_provider_init(const char *name, + struct uid_ops *ops, void *context); +void uid_provider_remove(const char *name); +#endif diff --git a/arch/arm/plat-stmp3xxx/lradc.c b/arch/arm/plat-stmp3xxx/lradc.c new file mode 100644 index 000000000000..ed0b89323add --- /dev/null +++ b/arch/arm/plat-stmp3xxx/lradc.c @@ -0,0 +1,332 @@ +/* + * Freescale STMP37XX/STMP378X LRADC helper routines + * + * Embedded Alley Solutions, Inc <source@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/sysdev.h> +#include <linux/bitops.h> +#include <linux/irq.h> +#include <mach/hardware.h> +#include <linux/delay.h> +#include <mach/platform.h> +#include <mach/stmp3xxx.h> +#include <mach/regs-lradc.h> +#include <mach/lradc.h> + +static int channels[8]; + +int hw_lradc_use_channel(int channel) +{ + if (channel < 0 || channel > 7) + return -EINVAL; + channels[channel]++; + return 0; +} +EXPORT_SYMBOL(hw_lradc_use_channel); + +int hw_lradc_unuse_channel(int channel) +{ + if (channel < 0 || channel > 7) + return -EINVAL; + channels[channel]--; + return 0; +} +EXPORT_SYMBOL(hw_lradc_unuse_channel); + +void hw_lradc_reinit(int enable_ground_ref, unsigned freq) +{ + stmp3xxx_setl(BM_LRADC_CTRL0_SFTRST, REGS_LRADC_BASE + HW_LRADC_CTRL0); + udelay(1); + stmp3xxx_clearl(BM_LRADC_CTRL0_SFTRST, + REGS_LRADC_BASE + HW_LRADC_CTRL0); + + /* Clear the Clock Gate for normal operation */ + stmp3xxx_clearl(BM_LRADC_CTRL0_CLKGATE, + REGS_LRADC_BASE + HW_LRADC_CTRL0); + + if (enable_ground_ref) + stmp3xxx_setl(BM_LRADC_CTRL0_ONCHIP_GROUNDREF, + REGS_LRADC_BASE + HW_LRADC_CTRL0); + else + stmp3xxx_clearl(BM_LRADC_CTRL0_ONCHIP_GROUNDREF, + REGS_LRADC_BASE + HW_LRADC_CTRL0); + + stmp3xxx_clearl(BM_LRADC_CTRL3_CYCLE_TIME, + REGS_LRADC_BASE + HW_LRADC_CTRL3); + stmp3xxx_setl(BF(freq, LRADC_CTRL3_CYCLE_TIME), + REGS_LRADC_BASE + HW_LRADC_CTRL3); + + stmp3xxx_clearl(BM_LRADC_CTRL4_LRADC6SELECT | + BM_LRADC_CTRL4_LRADC7SELECT, + REGS_LRADC_BASE + HW_LRADC_CTRL4); + stmp3xxx_setl(BF(VDDIO_VOLTAGE_CH, LRADC_CTRL4_LRADC6SELECT), + REGS_LRADC_BASE + HW_LRADC_CTRL4); + stmp3xxx_setl(BF(BATTERY_VOLTAGE_CH, LRADC_CTRL4_LRADC7SELECT), + REGS_LRADC_BASE + HW_LRADC_CTRL4); +} + +int hw_lradc_init_ladder(int channel, int trigger, unsigned sampling) +{ + /* + * check if the lradc channel is present in this product + */ + if (!hw_lradc_present(channel)) + return -ENODEV; + + hw_lradc_configure_channel(channel, !0 /* div2 */ , + 0 /* acc */ , + 0 /* num_samples */ ); + + /* Setup the trigger loop forever */ + hw_lradc_set_delay_trigger(trigger, 1 << channel, + 1 << trigger, 0, sampling); + + /* Clear the accumulator & NUM_SAMPLES */ + stmp3xxx_clearl(0xFFFFFFFF, REGS_LRADC_BASE + HW_LRADC_CHn(channel)); + return 0; +} + +EXPORT_SYMBOL(hw_lradc_init_ladder); + +int hw_lradc_stop_ladder(int channel, int trigger) +{ + /* + * check if the lradc channel is present in this product + */ + if (!hw_lradc_present(channel)) + return -ENODEV; + hw_lradc_clear_delay_trigger(trigger, 1 << channel, 1 << trigger); + return 0; +} + +EXPORT_SYMBOL(hw_lradc_stop_ladder); + +int hw_lradc_present(int channel) +{ + if (channel < 0 || channel > 7) + return 0; + return __raw_readl(REGS_LRADC_BASE + HW_LRADC_STATUS) + & (1 << (16 + channel)); +} + +EXPORT_SYMBOL(hw_lradc_present); + +void hw_lradc_configure_channel(int channel, int enable_div2, + int enable_acc, int samples) +{ + if (enable_div2) + stmp3xxx_setl(BF(1 << channel, LRADC_CTRL2_DIVIDE_BY_TWO), + REGS_LRADC_BASE + HW_LRADC_CTRL2); + else + stmp3xxx_clearl(BF(1 << channel, LRADC_CTRL2_DIVIDE_BY_TWO), + REGS_LRADC_BASE + HW_LRADC_CTRL2); + + /* Clear the accumulator & NUM_SAMPLES */ + stmp3xxx_clearl(0xFFFFFFFF, REGS_LRADC_BASE + HW_LRADC_CHn(channel)); + + /* Sets NUM_SAMPLES bitfield of HW_LRADC_CHn register. */ + stmp3xxx_clearl(BM_LRADC_CHn_NUM_SAMPLES, + REGS_LRADC_BASE + HW_LRADC_CHn(channel)); + stmp3xxx_setl(BF(samples, LRADC_CHn_NUM_SAMPLES), + REGS_LRADC_BASE + HW_LRADC_CHn(channel)); + + if (enable_acc) + stmp3xxx_setl(BM_LRADC_CHn_ACCUMULATE, + REGS_LRADC_BASE + HW_LRADC_CHn(channel)); + else + stmp3xxx_clearl(BM_LRADC_CHn_ACCUMULATE, + REGS_LRADC_BASE + HW_LRADC_CHn(channel)); +} + +EXPORT_SYMBOL(hw_lradc_configure_channel); + +void hw_lradc_set_delay_trigger(int trigger, u32 trigger_lradc, + u32 delay_triggers, u32 loops, u32 delays) +{ + /* set TRIGGER_LRADCS in HW_LRADC_DELAYn */ + stmp3xxx_setl(BF(trigger_lradc, LRADC_DELAYn_TRIGGER_LRADCS), + REGS_LRADC_BASE + HW_LRADC_DELAYn(trigger)); + stmp3xxx_setl(BF(delay_triggers, LRADC_DELAYn_TRIGGER_DELAYS), + REGS_LRADC_BASE + HW_LRADC_DELAYn(trigger)); + + stmp3xxx_clearl(BM_LRADC_DELAYn_LOOP_COUNT | BM_LRADC_DELAYn_DELAY, + REGS_LRADC_BASE + HW_LRADC_DELAYn(trigger)); + stmp3xxx_setl(BF(loops, LRADC_DELAYn_LOOP_COUNT), + REGS_LRADC_BASE + HW_LRADC_DELAYn(trigger)); + stmp3xxx_setl(BF(delays, LRADC_DELAYn_DELAY), + REGS_LRADC_BASE + HW_LRADC_DELAYn(trigger)); +} + +EXPORT_SYMBOL(hw_lradc_set_delay_trigger); + +void hw_lradc_clear_delay_trigger(int trigger, u32 trigger_lradc, + u32 delay_triggers) +{ + stmp3xxx_clearl(BF(trigger_lradc, LRADC_DELAYn_TRIGGER_LRADCS), + REGS_LRADC_BASE + HW_LRADC_DELAYn(trigger)); + stmp3xxx_clearl(BF(delay_triggers, LRADC_DELAYn_TRIGGER_DELAYS), + REGS_LRADC_BASE + HW_LRADC_DELAYn(trigger)); +} + +EXPORT_SYMBOL(hw_lradc_clear_delay_trigger); + +void hw_lradc_set_delay_trigger_kick(int trigger, int value) +{ + if (value) + stmp3xxx_setl(BM_LRADC_DELAYn_KICK, + REGS_LRADC_BASE + HW_LRADC_DELAYn(trigger)); + else + stmp3xxx_clearl(BM_LRADC_DELAYn_KICK, + REGS_LRADC_BASE + HW_LRADC_DELAYn(trigger)); +} + +EXPORT_SYMBOL(hw_lradc_set_delay_trigger_kick); + +u32 hw_lradc_vddio(void) +{ + /* Clear the Soft Reset and Clock Gate for normal operation */ + stmp3xxx_clearl(BM_LRADC_CTRL0_SFTRST | BM_LRADC_CTRL0_CLKGATE, + REGS_LRADC_BASE + HW_LRADC_CTRL0); + + /* + * Clear the divide by two for channel 6 since + * it has a HW divide-by-two built in. + */ + stmp3xxx_clearl(BF(1 << VDDIO_VOLTAGE_CH, LRADC_CTRL2_DIVIDE_BY_TWO), + REGS_LRADC_BASE + HW_LRADC_CTRL2); + + /* Clear the accumulator & NUM_SAMPLES */ + stmp3xxx_clearl(0xFFFFFFFF, + REGS_LRADC_BASE + HW_LRADC_CHn(VDDIO_VOLTAGE_CH)); + + /* Clear the interrupt flag */ + stmp3xxx_clearl(BM_LRADC_CTRL1_LRADC6_IRQ, + REGS_LRADC_BASE + HW_LRADC_CTRL1); + + /* + * Get VddIO; this is the max scale value for the button resistor + * ladder. + * schedule ch 6: + */ + stmp3xxx_setl(BF(1 << VDDIO_VOLTAGE_CH, LRADC_CTRL0_SCHEDULE), + REGS_LRADC_BASE + HW_LRADC_CTRL0); + + /* wait for completion */ + while ((__raw_readl(REGS_LRADC_BASE + HW_LRADC_CTRL1) + & BM_LRADC_CTRL1_LRADC6_IRQ) != BM_LRADC_CTRL1_LRADC6_IRQ) + cpu_relax(); + + /* Clear the interrupt flag */ + stmp3xxx_clearl(BM_LRADC_CTRL1_LRADC6_IRQ, + REGS_LRADC_BASE + HW_LRADC_CTRL1); + + /* read ch 6 value. */ + return __raw_readl(REGS_LRADC_BASE + HW_LRADC_CHn(6)) + & BM_LRADC_CHn_VALUE; +} + +EXPORT_SYMBOL(hw_lradc_vddio); + +static u32 lradc_registers[0x16]; +static int do_gate; + +static int hw_lradc_suspend(struct sys_device *dev, pm_message_t state) +{ + int i; + + do_gate = 1; + for (i = 0; i < ARRAY_SIZE(channels); i++) + if (channels[i] > 0) { + do_gate = 0; + break; + } + + for (i = 0; i < ARRAY_SIZE(lradc_registers); i++) + lradc_registers[i] = __raw_readl(REGS_LRADC_BASE + (i << 4)); + + if (do_gate) + stmp3xxx_setl(BM_LRADC_CTRL0_CLKGATE, + REGS_LRADC_BASE + HW_LRADC_CTRL0); + return 0; +} + +static int hw_lradc_resume(struct sys_device *dev) +{ + int i; + + if (do_gate) { + stmp3xxx_setl(BM_LRADC_CTRL0_SFTRST, + REGS_LRADC_BASE + HW_LRADC_CTRL0); + udelay(10); + stmp3xxx_clearl(BM_LRADC_CTRL0_SFTRST | + BM_LRADC_CTRL0_CLKGATE, + REGS_LRADC_BASE + HW_LRADC_CTRL0); + } + for (i = 0; i < ARRAY_SIZE(lradc_registers); i++) + __raw_writel(lradc_registers[i], REGS_LRADC_BASE + (i << 4)); + return 0; +} + +static struct sysdev_class stmp3xxx_lradc_sysclass = { + .name = "stmp3xxx-lradc", +#ifdef CONFIG_PM + .suspend = hw_lradc_suspend, + .resume = hw_lradc_resume, +#endif +}; + +static struct sys_device stmp3xxx_lradc_device = { + .id = -1, + .cls = &stmp3xxx_lradc_sysclass, +}; + +static int __initdata lradc_freq = LRADC_CLOCK_6MHZ; + +static int __init lradc_freq_setup(char *str) +{ + long freq; + + if (strict_strtol(str, 0, &freq) < 0) + return 0; + + if (freq < 0) + return 0; + if (freq >= 6) + lradc_freq = LRADC_CLOCK_6MHZ; + else if (freq >= 4) + lradc_freq = LRADC_CLOCK_4MHZ; + else if (freq >= 3) + lradc_freq = LRADC_CLOCK_3MHZ; + else if (freq >= 2) + lradc_freq = LRADC_CLOCK_2MHZ; + else + return 0; + return 1; +} + +__setup("lradc_freq=", lradc_freq_setup); + +static int __init hw_lradc_init(void) +{ + hw_lradc_reinit(0, lradc_freq); + sysdev_class_register(&stmp3xxx_lradc_sysclass); + sysdev_register(&stmp3xxx_lradc_device); + return 0; +} + +subsys_initcall(hw_lradc_init); diff --git a/arch/arm/plat-stmp3xxx/mmc.c b/arch/arm/plat-stmp3xxx/mmc.c new file mode 100644 index 000000000000..75dc3a9f282a --- /dev/null +++ b/arch/arm/plat-stmp3xxx/mmc.c @@ -0,0 +1,148 @@ +/* + * Freescale STMP37XX/STMP378X MMC pin multiplexing + * + * Embedded Alley Solutions, Inc <source@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/clk.h> +#include <linux/gpio.h> +#include <mach/pinmux.h> +#include <mach/stmp3xxx.h> +#include <mach/platform.h> +#include <mach/mmc.h> +#include <mach/regs-ssp.h> + +#if defined(CONFIG_MACH_STMP378X) +#define MMC_POWER PINID_PWM3 +#define MMC_WP PINID_PWM4 +#elif defined(CONFIG_MACH_STMP37XX) +#define MMC_POWER PINID_PWM3 +#define MMC_WP PINID_PWM4 +#else +#define MMC_POWER PINID_NO_PIN +#define MMC_WP PINID_NO_PIN +#endif + +static int mmc_drive_power; +static int mmc_wp_supported; + +static struct pin_desc mmc_pins_desc[] = { + { PINID_SSP1_DATA0, PIN_FUN1, PIN_8MA, PIN_3_3V, 1 }, + { PINID_SSP1_DATA1, PIN_FUN1, PIN_8MA, PIN_3_3V, 1 }, + { PINID_SSP1_DATA2, PIN_FUN1, PIN_8MA, PIN_3_3V, 1 }, + { PINID_SSP1_DATA3, PIN_FUN1, PIN_8MA, PIN_3_3V, 1 }, + { PINID_SSP1_CMD, PIN_FUN1, PIN_8MA, PIN_3_3V, 1 }, + { PINID_SSP1_SCK, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 }, + { PINID_SSP1_DETECT, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 }, +}; + +static struct pin_group mmc_pins = { + .pins = mmc_pins_desc, + .nr_pins = ARRAY_SIZE(mmc_pins_desc), +}; + +int stmp3xxxmmc_get_wp(void) +{ + if (mmc_wp_supported) + return gpio_get_value(MMC_WP); + + return 0; +} + +int stmp3xxxmmc_hw_init_ssp1(void) +{ + int ret; + + mmc_drive_power = stmp3xxx_valid_pin(MMC_POWER); + mmc_wp_supported = stmp3xxx_valid_pin(MMC_WP); + + ret = stmp3xxx_request_pin_group(&mmc_pins, "mmc"); + if (ret) + goto out; + + if (mmc_wp_supported) { + /* Configure write protect GPIO pin */ + ret = gpio_request(MMC_WP, "mmc wp"); + if (ret) + goto out_wp; + + gpio_set_value(MMC_WP, 0); + gpio_direction_input(MMC_WP); + } + + if (mmc_drive_power) { + /* Configure POWER pin as gpio to drive power to MMC slot */ + ret = gpio_request(MMC_POWER, "mmc power"); + if (ret) + goto out_power; + + gpio_direction_output(MMC_POWER, 0); + mdelay(100); + } + + return 0; + +out_power: + if (mmc_wp_supported) + gpio_free(MMC_WP); +out_wp: + stmp3xxx_release_pin_group(&mmc_pins, "mmc"); +out: + return ret; +} + +void stmp3xxxmmc_hw_release_ssp1(void) +{ + if (mmc_drive_power) + gpio_free(MMC_POWER); + + if (mmc_wp_supported) + gpio_free(MMC_WP); + + stmp3xxx_release_pin_group(&mmc_pins, "mmc"); +} + +void stmp3xxxmmc_cmd_pullup_ssp1(int enable) +{ + stmp3xxx_pin_pullup(PINID_SSP1_CMD, enable, "mmc"); +} + +unsigned long stmp3xxxmmc_setclock_ssp1(unsigned long hz) +{ + struct clk *ssp = clk_get(NULL, "ssp"), *parent; + char *p; + long r; + + /* using SSP1, no timeout, clock rate 1 */ + __raw_writel(BF(2, SSP_TIMING_CLOCK_DIVIDE) | + BF(0xFFFF, SSP_TIMING_TIMEOUT), + REGS_SSP1_BASE + HW_SSP_TIMING); + + if (hz > 1000000) + p = "io"; + else + p = "osc_24M"; + + parent = clk_get(NULL, p); + clk_set_parent(ssp, parent); + r = clk_set_rate(ssp, 2 * hz / 1000); + clk_put(parent); + clk_put(ssp); + + return hz; +} diff --git a/arch/arm/plat-stmp3xxx/pinmux.c b/arch/arm/plat-stmp3xxx/pinmux.c index 6d6b1a468eda..375578d58c27 100644 --- a/arch/arm/plat-stmp3xxx/pinmux.c +++ b/arch/arm/plat-stmp3xxx/pinmux.c @@ -15,7 +15,7 @@ * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ -#define DEBUG +//#define DEBUG #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> diff --git a/arch/arm/plat-stmp3xxx/power-test.c b/arch/arm/plat-stmp3xxx/power-test.c new file mode 100644 index 000000000000..66f15f562798 --- /dev/null +++ b/arch/arm/plat-stmp3xxx/power-test.c @@ -0,0 +1,213 @@ +/* + * Power consumption test module + * + * Author: Dmitrij Frasenyak <sed@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/sched.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> + +static struct regulator *reg; +static struct regulator *freg; + +static struct timer_list pt_timer; +static int timer_delay = 5*60*1000; /* 5min */ +static DEFINE_MUTEX(run_mutex); + + +#define REG_GET() do {\ + if (!reg) {\ + reg = regulator_get(NULL, "power-test-1");\ + if (!reg || IS_ERR(reg)) {\ + reg = NULL ; return -ENODEV;\ + } \ + } \ +} while (0); + +static void timer_func(unsigned long data) +{ + regulator_set_current_limit(reg, 0, 0); + mutex_unlock(&run_mutex); +} + +static ssize_t pt_mode_set(struct device *d, struct device_attribute *attr, + const char *buf, size_t size) +{ + REG_GET(); + if (buf[0] == 'f') + regulator_set_mode(reg, REGULATOR_MODE_FAST); + else + regulator_set_mode(reg, REGULATOR_MODE_NORMAL); + return size; +} + +static ssize_t pt_mode_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + REG_GET(); + if (regulator_get_mode(reg) == REGULATOR_MODE_FAST) + return snprintf(buf, 5, "fast\n"); + else + return snprintf(buf, 7, "normal\n"); +} + +static ssize_t pt_val_fset(struct device *d, struct device_attribute *attr, + const char *buf, size_t size) +{ + int i, ret; + ret = sscanf(buf, "%u", &i); + if (ret != 1) + return -EINVAL; + + if (!freg) { + freg = regulator_get(NULL, "stmp3xxx-bl-1"); + if (!freg || IS_ERR(freg)) { + freg = NULL ; return -ENODEV; + } + } + regulator_set_mode(freg, REGULATOR_MODE_NORMAL); + + if (!regulator_set_current_limit(freg, i, i)) + printk(KERN_ERR "got backlight reg\n"); + else + printk(KERN_ERR "failed to get backlight reg"); + + return size; +} + + +static ssize_t pt_val_set(struct device *d, struct device_attribute *attr, + const char *buf, size_t size) +{ + int i, ret; + REG_GET(); + + ret = sscanf(buf, "%u", &i); + if (ret != 1) + return -EINVAL; + + mutex_lock(&run_mutex); + if (!regulator_set_current_limit(reg, i, i)) { + mod_timer(&pt_timer, + jiffies + msecs_to_jiffies(timer_delay)); + return size; + } else { + mutex_unlock(&run_mutex); + return -EPERM; + } + +} + +static ssize_t pt_val_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + REG_GET(); + return sprintf(buf, "%d\n", regulator_get_current_limit(reg)); +} + +static ssize_t pt_timeout_set(struct device *d, struct device_attribute *attr, + const char *buf, size_t size) +{ + int i, ret; + REG_GET(); + + ret = sscanf(buf, "%u", &i); + if (ret != 1) + return -EINVAL; + + mutex_lock(&run_mutex); + timer_delay = 1000*i ; + mutex_unlock(&run_mutex); + + return size; +} + +static ssize_t pt_timeout_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + REG_GET(); + return sprintf(buf, "%d\n", timer_delay); +} + +static DEVICE_ATTR(mode, 0644, pt_mode_show, pt_mode_set); +static DEVICE_ATTR(val, 0644, pt_val_show, pt_val_set); +static DEVICE_ATTR(fval, 0644, pt_val_show, pt_val_fset); +static DEVICE_ATTR(timeout, 0644, pt_timeout_show, pt_timeout_set); + +static int stmp3xxx_power_test_remove(struct platform_device *pdev) +{ + if (reg) + regulator_put(reg); + + device_remove_file(&pdev->dev, &dev_attr_mode); + device_remove_file(&pdev->dev, &dev_attr_val); + device_remove_file(&pdev->dev, &dev_attr_timeout); + device_remove_file(&pdev->dev, &dev_attr_fval); + return 0; +} + +static int stmp3xxx_power_test_probe(struct platform_device *pdev) +{ + int ret; + init_timer(&pt_timer); + pt_timer.data = 0; + pt_timer.function = timer_func; + + ret = device_create_file(&pdev->dev, &dev_attr_mode); + ret |= device_create_file(&pdev->dev, &dev_attr_val); + ret |= device_create_file(&pdev->dev, &dev_attr_fval); + ret |= device_create_file(&pdev->dev, &dev_attr_timeout); + return ret; +} + +static struct platform_driver stmp3xxx_power_test_driver = { + .probe = stmp3xxx_power_test_probe, + .remove = stmp3xxx_power_test_remove, + .driver = { + .name = "stmp3xxx-power-test", + .owner = THIS_MODULE, + }, +}; + +struct platform_device stmp3xxx_pt = { + .name = "stmp3xxx-power-test", + .id = -1, +}; + +static int __init stmp3xxx_power_test_init(void) +{ + + platform_device_register(&stmp3xxx_pt); + return platform_driver_register(&stmp3xxx_power_test_driver); +} + +static void __exit stmp3xxx_power_test_exit(void) +{ + platform_driver_unregister(&stmp3xxx_power_test_driver); + platform_device_unregister(&stmp3xxx_pt); +} + +MODULE_AUTHOR("<sed@embeddedalley.com>"); +MODULE_DESCRIPTION("Power test driver"); +MODULE_LICENSE("GPL"); + +module_init(stmp3xxx_power_test_init); +module_exit(stmp3xxx_power_test_exit); diff --git a/arch/arm/plat-stmp3xxx/rotdec.c b/arch/arm/plat-stmp3xxx/rotdec.c new file mode 100644 index 000000000000..d390102c1559 --- /dev/null +++ b/arch/arm/plat-stmp3xxx/rotdec.c @@ -0,0 +1,39 @@ +/* + * Freescale STMP378X Rotary Encoder module pin multiplexing + * + * Author: Drew Benedetti <drewb@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/module.h> +#include <mach/pins.h> +#include "pinmux.h" + +#define TITLE "stmp3xxx-rotdec" + +int rotdec_pinmux_request(void) +{ + return stmp3xxx_request_pin_group(&rotdec_pins, TITLE); +} +EXPORT_SYMBOL_GPL(rotdec_pinmux_request); + +void rotdec_pinmux_free(void) +{ + stmp3xxx_release_pin_group(&spdif_pins, TITLE); +} +EXPORT_SYMBOL_GPL(rotdec_pinmux_free); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Drew Benedetti <drewb@embeddedalley.com>"); diff --git a/arch/arm/plat-stmp3xxx/spi.c b/arch/arm/plat-stmp3xxx/spi.c new file mode 100644 index 000000000000..21242dfd5c9a --- /dev/null +++ b/arch/arm/plat-stmp3xxx/spi.c @@ -0,0 +1,106 @@ +/* + * Freescale STMP37XX/STMP378X SPI module pin multiplexing + * + * Author: dmitry pervushin <dimka@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/spi/spi.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <mach/stmp3xxx.h> +#include <mach/pinmux.h> + +/* + These pins are: + SCK (SSPx_SCK) + MOSI (SSPx_CMD) + MISO (SSPx_DATA0) + SSn (SSPx_DATA3) + Please add new pins in the same order, thanks :) +*/ +static struct pin_desc ssp_pins_desc[2][4] = { + [0] = { + { PINID_SSP1_SCK, PIN_FUN1, PIN_8MA, PIN_3_3V, 0, }, + { PINID_SSP1_CMD, PIN_FUN1, PIN_4MA, PIN_3_3V, 0, }, + { PINID_SSP1_DATA0, PIN_FUN1, PIN_4MA, PIN_3_3V, 0, }, + { PINID_SSP1_DATA3, PIN_FUN1, PIN_4MA, PIN_3_3V, 0, }, + }, + [1] = { +#if defined(CONFIG_ARCH_STMP37XX) + { PINID_GPMI_IRQ, PIN_FUN3, PIN_8MA, PIN_3_3V, 0, }, + { PINID_GPMI_RDY2, PIN_FUN3, PIN_4MA, PIN_3_3V, 0, }, + { PINID_EMI_CE2N, PIN_FUN3, PIN_4MA, PIN_3_3V, 0, }, + { PINID_GPMI_D03, PIN_FUN3, PIN_4MA, PIN_3_3V, 0, }, +#elif defined(CONFIG_ARCH_STMP378X) + { PINID_GPMI_WRN, PIN_FUN3, PIN_8MA, PIN_3_3V, 0, }, + { PINID_GPMI_RDY1, PIN_FUN3, PIN_4MA, PIN_3_3V, 0, }, + { PINID_GPMI_D00, PIN_FUN3, PIN_4MA, PIN_3_3V, 0, }, + { PINID_GPMI_D03, PIN_FUN3, PIN_4MA, PIN_3_3V, 0, }, +#endif + }, +}; + +static struct pin_group ssp_pins[2] = { + [0] = { + .pins = ssp_pins_desc[0], + .nr_pins = ARRAY_SIZE(ssp_pins_desc[0]), + }, + [1] = { + .pins = ssp_pins_desc[1], + .nr_pins = ARRAY_SIZE(ssp_pins_desc[1]), + }, +}; + +int stmp37xx_spi_pins_request(char *id, int ssp) +{ + return stmp3xxx_request_pin_group(&ssp_pins[ssp-1], id); +} +EXPORT_SYMBOL_GPL(stmp37xx_spi_pins_request); + +void stmp37xx_spi_pins_release(char *id, int ssp) +{ + stmp3xxx_release_pin_group(&ssp_pins[ssp-1], id); +} +EXPORT_SYMBOL_GPL(stmp37xx_spi_pins_release); + +int stmp37xx_spi_enc_init(void *spi_dev) +{ + struct spi_device *spi = spi_dev; + struct stmp37xx_spi_platform_data *data = spi->dev.platform_data; + + gpio_request(data->irq_pin, dev_name(&spi->dev)); + gpio_direction_input(data->irq_pin); + set_irq_type(gpio_to_irq(data->irq_pin), IRQ_TYPE_EDGE_FALLING); + spi->irq = gpio_to_irq(data->irq_pin); + dev_dbg(&spi->dev, "Assigned IRQ %d(%s)\n", spi->irq, __func__); + return 0; +} + +int stmp37xx_spi_enc_release(void *spi_dev) +{ + struct spi_device *spi = spi_dev; + struct stmp37xx_spi_platform_data *data = spi->dev.platform_data; + + set_irq_type(data->irq_pin, IRQ_TYPE_NONE); + gpio_free(data->irq_pin); + return 0; +} + +MODULE_AUTHOR("dmitry pervushin <dimka@embeddedalley.com>"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/plat-stmp3xxx/unique-id.c b/arch/arm/plat-stmp3xxx/unique-id.c new file mode 100644 index 000000000000..b25cab17555a --- /dev/null +++ b/arch/arm/plat-stmp3xxx/unique-id.c @@ -0,0 +1,198 @@ +/* + * Unique ID manipulation sysfs access generic functions + * + * Author: dmitry pervushin <dimka@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/kobject.h> +#include <linux/string.h> +#include <linux/sysfs.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/err.h> +#include <linux/timer.h> +#include <linux/spinlock.h> + +#include <mach/unique-id.h> + +static int unlock; +static spinlock_t u_lock; +static const unsigned long UID_AUTOLOCK_TIMEOUT = HZ * 60 * 3; +static struct timer_list u_timer; + +static void uid_timer_autolock(unsigned long param) +{ + struct timer_list *tmr = (struct timer_list *)param; + + if (spin_trylock(&u_lock)) { + if (unlock) + pr_debug("%s: locked down.\n", __func__); + unlock = 0; + spin_unlock(&u_lock); + } + mod_timer(tmr, jiffies + UID_AUTOLOCK_TIMEOUT); +} + +static LIST_HEAD(uid_provider_list); + +struct uid_provider { + struct kobject *kobj; + struct list_head list; + struct uid_ops *ops; + void *context; +}; + +static struct uid_provider *uid_provider_find(const char *name); + +#define UID_FWD_SYSFS_FILE(var, file, param) \ + static ssize_t var##_show(struct kobject *kobj, \ + struct kobj_attribute *attr, char *buf) \ + { \ + struct uid_provider *p = \ + uid_provider_find(kobject_name(kobj)); \ + ssize_t r; \ + BUG_ON(p == NULL); \ + r = (p->ops && p->ops->file##_show) ? \ + p->ops->file##_show(p->context, buf, param) : 0;\ + return r; \ + } \ + \ + static ssize_t var##_store(struct kobject *kobj, \ + struct kobj_attribute *attr, const char *buf, \ + size_t count) \ + { \ + struct uid_provider *p = \ + uid_provider_find(kobject_name(kobj)); \ + ssize_t r; \ + int ul; \ + BUG_ON(p == NULL); \ + spin_lock(&u_lock); \ + ul = unlock; \ + spin_unlock(&u_lock); \ + if (ul) \ + r = (p->ops && p->ops->file##_store) ? \ + p->ops->file##_store(p->context, buf, count, param) \ + : count; \ + else \ + r = -EACCES; \ + return r; \ + } + +struct kobject *uid_kobj; + +#define UID_ATTR(_name, _varname) \ + static struct kobj_attribute _varname##_attr = \ + __ATTR(_name, 0644, _varname##_show, _varname##_store) + +UID_FWD_SYSFS_FILE(id, id, 1); +UID_FWD_SYSFS_FILE(id_bin, id, 0); +UID_ATTR(id, id); +UID_ATTR(id.bin, id_bin); + +static struct attribute *uid_attrs[] = { + &id_attr.attr, + &id_bin_attr.attr, + NULL +}; + +static struct attribute_group uid_attr_group = { + .attrs = uid_attrs, +}; + +struct kobject *uid_provider_init(const char *name, + struct uid_ops *ops, void *context) +{ + struct uid_provider *new; + int err; + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) { + err = -ENOMEM; + goto out; + } + + new->kobj = kobject_create_and_add(name, uid_kobj); + if (!new->kobj) { + err = -ENOMEM; + goto out; + } + new->ops = ops; + new->context = context; + + err = sysfs_create_group(new->kobj, &uid_attr_group); + if (err) + goto out2; + + list_add_tail(&new->list, &uid_provider_list); + return new->kobj; +out2: + kobject_del(new->kobj); +out: + kfree(new); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(uid_provider_init); + +static struct uid_provider *uid_provider_find(const char *name) +{ + struct uid_provider *p; + + list_for_each_entry(p, &uid_provider_list, list) { + if (strcmp(kobject_name(p->kobj), name) == 0) + return p; + } + return NULL; +} + +void uid_provider_remove(const char *name) +{ + struct uid_provider *p; + + p = uid_provider_find(name); + if (!p) + return; + kobject_del(p->kobj); + list_del(&p->list); + kfree(p); +} +EXPORT_SYMBOL_GPL(uid_provider_remove); + +static int uid_sysfs_init(void) +{ + int error; + + uid_kobj = kobject_create_and_add("uid", NULL); + if (!uid_kobj) { + error = -ENOMEM; + goto out1; + } + + spin_lock_init(&u_lock); + setup_timer(&u_timer, uid_timer_autolock, (unsigned long)&u_timer); + + /* try to lock each 3 minutes */ + mod_timer(&u_timer, jiffies + UID_AUTOLOCK_TIMEOUT); + return 0; + +out1: + printk(KERN_ERR"%s failed, error %d.", __func__, error); + return error; +} + +module_param(unlock, int, 0600) +core_initcall(uid_sysfs_init); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("dmitry pervushin <dimka@embeddedalley.com>"); +MODULE_DESCRIPTION("Unique ID simple framework"); |