diff options
Diffstat (limited to 'target/linux/s3c24xx/patches-2.6.30/011-s3c-pwm.patch')
-rw-r--r-- | target/linux/s3c24xx/patches-2.6.30/011-s3c-pwm.patch | 802 |
1 files changed, 0 insertions, 802 deletions
diff --git a/target/linux/s3c24xx/patches-2.6.30/011-s3c-pwm.patch b/target/linux/s3c24xx/patches-2.6.30/011-s3c-pwm.patch deleted file mode 100644 index 5e097d8bb9..0000000000 --- a/target/linux/s3c24xx/patches-2.6.30/011-s3c-pwm.patch +++ /dev/null @@ -1,802 +0,0 @@ ---- /dev/null -+++ b/arch/arm/plat-s3c/include/plat/pwm.h -@@ -0,0 +1,45 @@ -+#ifndef __S3C2410_PWM_H -+#define __S3C2410_PWM_H -+ -+#include <linux/err.h> -+#include <linux/platform_device.h> -+#include <linux/clk.h> -+ -+#include <mach/io.h> -+#include <mach/hardware.h> -+#include <asm/mach-types.h> -+#include <plat/regs-timer.h> -+ -+enum pwm_timer { -+ PWM0, -+ PWM1, -+ PWM2, -+ PWM3, -+ PWM4 -+}; -+ -+struct s3c2410_pwm { -+ enum pwm_timer timerid; -+ struct clk *pclk; -+ unsigned long pclk_rate; -+ unsigned long prescaler; -+ unsigned long divider; -+ unsigned long counter; -+ unsigned long comparer; -+}; -+ -+struct s3c24xx_pwm_platform_data{ -+ /* callback to attach platform children (to enforce suspend / resume -+ * ordering */ -+ void (*attach_child_devices)(struct device *parent_device); -+}; -+ -+int s3c2410_pwm_init(struct s3c2410_pwm *s3c2410_pwm); -+int s3c2410_pwm_enable(struct s3c2410_pwm *s3c2410_pwm); -+int s3c2410_pwm_disable(struct s3c2410_pwm *s3c2410_pwm); -+int s3c2410_pwm_start(struct s3c2410_pwm *s3c2410_pwm); -+int s3c2410_pwm_stop(struct s3c2410_pwm *s3c2410_pwm); -+int s3c2410_pwm_duty_cycle(int reg_value, struct s3c2410_pwm *s3c2410_pwm); -+int s3c2410_pwm_dumpregs(void); -+ -+#endif /* __S3C2410_PWM_H */ ---- a/arch/arm/plat-s3c/Kconfig -+++ b/arch/arm/plat-s3c/Kconfig -@@ -157,6 +157,11 @@ config S3C_DMA - help - Internal configuration for S3C DMA core - -+config S3C_PWM -+ bool -+ help -+ PWM timer code for the S3C2410, and similar processors -+ - # device definitions to compile in - - config S3C_DEV_HSMMC ---- a/arch/arm/plat-s3c/Makefile -+++ b/arch/arm/plat-s3c/Makefile -@@ -35,5 +35,6 @@ obj-$(CONFIG_S3C_DEV_HSMMC1) += dev-hsmm - obj-y += dev-i2c0.o - obj-$(CONFIG_S3C_DEV_I2C1) += dev-i2c1.o - obj-$(CONFIG_S3C_DEV_FB) += dev-fb.o -+obj-$(CONFIG_S3C_PWM) += pwm.o - obj-$(CONFIG_S3C_DMA) += dma.o - ---- /dev/null -+++ b/arch/arm/plat-s3c/pwm.c -@@ -0,0 +1,288 @@ -+/* -+ * arch/arm/plat-s3c/pwm.c -+ * -+ * Copyright (c) by Javi Roman <javiroman@kernel-labs.org> -+ * for the Openmoko Project. -+ * -+ * S3C2410A SoC PWM support -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ * -+ */ -+ -+#include <linux/kernel.h> -+#include <linux/init.h> -+#include <linux/clk.h> -+#include <linux/device.h> -+#include <mach/hardware.h> -+#include <plat/regs-timer.h> -+#include <plat/pwm.h> -+#include <asm/io.h> -+ -+#ifdef CONFIG_PM -+ static unsigned long standby_reg_tcon; -+ static unsigned long standby_reg_tcfg0; -+ static unsigned long standby_reg_tcfg1; -+#endif -+ -+int s3c2410_pwm_disable(struct s3c2410_pwm *pwm) -+{ -+ unsigned long tcon; -+ -+ /* stop timer */ -+ tcon = __raw_readl(S3C2410_TCON); -+ tcon &= 0xffffff00; -+ __raw_writel(tcon, S3C2410_TCON); -+ -+ clk_disable(pwm->pclk); -+ clk_put(pwm->pclk); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(s3c2410_pwm_disable); -+ -+int s3c2410_pwm_init(struct s3c2410_pwm *pwm) -+{ -+ pwm->pclk = clk_get(NULL, "timers"); -+ if (IS_ERR(pwm->pclk)) -+ return PTR_ERR(pwm->pclk); -+ -+ clk_enable(pwm->pclk); -+ pwm->pclk_rate = clk_get_rate(pwm->pclk); -+ return 0; -+} -+EXPORT_SYMBOL_GPL(s3c2410_pwm_init); -+ -+int s3c2410_pwm_enable(struct s3c2410_pwm *pwm) -+{ -+ unsigned long tcfg0, tcfg1, tcnt, tcmp; -+ -+ /* control registers bits */ -+ tcfg1 = __raw_readl(S3C2410_TCFG1); -+ tcfg0 = __raw_readl(S3C2410_TCFG0); -+ -+ /* divider & scaler slection */ -+ switch (pwm->timerid) { -+ case PWM0: -+ tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK; -+ tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK; -+ break; -+ case PWM1: -+ tcfg1 &= ~S3C2410_TCFG1_MUX1_MASK; -+ tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK; -+ break; -+ case PWM2: -+ tcfg1 &= ~S3C2410_TCFG1_MUX2_MASK; -+ tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; -+ break; -+ case PWM3: -+ tcfg1 &= ~S3C2410_TCFG1_MUX3_MASK; -+ tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; -+ break; -+ case PWM4: -+ /* timer four is not capable of doing PWM */ -+ break; -+ default: -+ clk_disable(pwm->pclk); -+ clk_put(pwm->pclk); -+ return -1; -+ } -+ -+ /* divider & scaler values */ -+ tcfg1 |= pwm->divider; -+ __raw_writel(tcfg1, S3C2410_TCFG1); -+ -+ switch (pwm->timerid) { -+ case PWM0: -+ case PWM1: -+ tcfg0 |= pwm->prescaler; -+ __raw_writel(tcfg0, S3C2410_TCFG0); -+ break; -+ default: -+ if ((tcfg0 | pwm->prescaler) != tcfg0) { -+ printk(KERN_WARNING "not changing prescaler of PWM %u," -+ " since it's shared with timer4 (clock tick)\n", -+ pwm->timerid); -+ } -+ break; -+ } -+ -+ /* timer count and compare buffer initial values */ -+ tcnt = pwm->counter; -+ tcmp = pwm->comparer; -+ -+ __raw_writel(tcnt, S3C2410_TCNTB(pwm->timerid)); -+ __raw_writel(tcmp, S3C2410_TCMPB(pwm->timerid)); -+ -+ /* ensure timer is stopped */ -+ s3c2410_pwm_stop(pwm); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(s3c2410_pwm_enable); -+ -+int s3c2410_pwm_start(struct s3c2410_pwm *pwm) -+{ -+ unsigned long tcon; -+ -+ tcon = __raw_readl(S3C2410_TCON); -+ -+ switch (pwm->timerid) { -+ case PWM0: -+ tcon |= S3C2410_TCON_T0START; -+ tcon &= ~S3C2410_TCON_T0MANUALUPD; -+ break; -+ case PWM1: -+ tcon |= S3C2410_TCON_T1START; -+ tcon &= ~S3C2410_TCON_T1MANUALUPD; -+ break; -+ case PWM2: -+ tcon |= S3C2410_TCON_T2START; -+ tcon &= ~S3C2410_TCON_T2MANUALUPD; -+ break; -+ case PWM3: -+ tcon |= S3C2410_TCON_T3START; -+ tcon &= ~S3C2410_TCON_T3MANUALUPD; -+ break; -+ case PWM4: -+ /* timer four is not capable of doing PWM */ -+ default: -+ return -ENODEV; -+ } -+ -+ __raw_writel(tcon, S3C2410_TCON); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(s3c2410_pwm_start); -+ -+int s3c2410_pwm_stop(struct s3c2410_pwm *pwm) -+{ -+ unsigned long tcon; -+ -+ tcon = __raw_readl(S3C2410_TCON); -+ -+ switch (pwm->timerid) { -+ case PWM0: -+ tcon &= ~0x00000000; -+ tcon |= S3C2410_TCON_T0RELOAD; -+ tcon |= S3C2410_TCON_T0MANUALUPD; -+ break; -+ case PWM1: -+ tcon &= ~0x00000080; -+ tcon |= S3C2410_TCON_T1RELOAD; -+ tcon |= S3C2410_TCON_T1MANUALUPD; -+ break; -+ case PWM2: -+ tcon &= ~0x00000800; -+ tcon |= S3C2410_TCON_T2RELOAD; -+ tcon |= S3C2410_TCON_T2MANUALUPD; -+ break; -+ case PWM3: -+ tcon &= ~0x00008000; -+ tcon |= S3C2410_TCON_T3RELOAD; -+ tcon |= S3C2410_TCON_T3MANUALUPD; -+ break; -+ case PWM4: -+ /* timer four is not capable of doing PWM */ -+ default: -+ return -ENODEV; -+ } -+ -+ __raw_writel(tcon, S3C2410_TCON); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(s3c2410_pwm_stop); -+ -+int s3c2410_pwm_duty_cycle(int reg_value, struct s3c2410_pwm *pwm) -+{ -+ __raw_writel(reg_value, S3C2410_TCMPB(pwm->timerid)); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(s3c2410_pwm_duty_cycle); -+ -+int s3c2410_pwm_dumpregs(void) -+{ -+ printk(KERN_INFO "TCON: %08lx, TCFG0: %08lx, TCFG1: %08lx\n", -+ (unsigned long) __raw_readl(S3C2410_TCON), -+ (unsigned long) __raw_readl(S3C2410_TCFG0), -+ (unsigned long) __raw_readl(S3C2410_TCFG1)); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(s3c2410_pwm_dumpregs); -+ -+static int __init s3c24xx_pwm_probe(struct platform_device *pdev) -+{ -+ struct s3c24xx_pwm_platform_data *pdata = pdev->dev.platform_data; -+ -+ dev_info(&pdev->dev, "s3c24xx_pwm is registered \n"); -+ -+ /* if platform was interested, give him a chance to register -+ * platform devices that switch power with us as the parent -+ * at registration time -- ensures suspend / resume ordering -+ */ -+ if (pdata) -+ if (pdata->attach_child_devices) -+ (pdata->attach_child_devices)(&pdev->dev); -+ -+ return 0; -+} -+ -+#ifdef CONFIG_PM -+static int s3c24xx_pwm_suspend(struct platform_device *pdev, pm_message_t state) -+{ -+ /* PWM config should be kept in suspending */ -+ standby_reg_tcon = __raw_readl(S3C2410_TCON); -+ standby_reg_tcfg0 = __raw_readl(S3C2410_TCFG0); -+ standby_reg_tcfg1 = __raw_readl(S3C2410_TCFG1); -+ -+ return 0; -+} -+ -+static int s3c24xx_pwm_resume(struct platform_device *pdev) -+{ -+ __raw_writel(standby_reg_tcon, S3C2410_TCON); -+ __raw_writel(standby_reg_tcfg0, S3C2410_TCFG0); -+ __raw_writel(standby_reg_tcfg1, S3C2410_TCFG1); -+ -+ return 0; -+} -+#else -+#define s3c24xx_pwm_suspend NULL -+#define s3c24xx_pwm_resume NULL -+#endif -+ -+static struct platform_driver s3c24xx_pwm_driver = { -+ .driver = { -+ .name = "s3c24xx_pwm", -+ .owner = THIS_MODULE, -+ }, -+ .probe = s3c24xx_pwm_probe, -+ .suspend = s3c24xx_pwm_suspend, -+ .resume = s3c24xx_pwm_resume, -+}; -+ -+static int __init s3c24xx_pwm_init(void) -+{ -+ return platform_driver_register(&s3c24xx_pwm_driver); -+} -+ -+static void __exit s3c24xx_pwm_exit(void) -+{ -+} -+ -+MODULE_AUTHOR("Javi Roman <javiroman@kernel-labs.org>"); -+MODULE_LICENSE("GPL"); -+ -+module_init(s3c24xx_pwm_init); -+module_exit(s3c24xx_pwm_exit); ---- /dev/null -+++ b/arch/arm/plat-s3c24xx/pwm-clock.c -@@ -0,0 +1,437 @@ -+/* linux/arch/arm/plat-s3c24xx/pwm-clock.c -+ * -+ * Copyright (c) 2007 Simtec Electronics -+ * Copyright (c) 2007, 2008 Ben Dooks -+ * Ben Dooks <ben-linux@fluff.org> -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License. -+*/ -+ -+#include <linux/init.h> -+#include <linux/module.h> -+#include <linux/kernel.h> -+#include <linux/list.h> -+#include <linux/errno.h> -+#include <linux/clk.h> -+#include <linux/err.h> -+#include <linux/io.h> -+ -+#include <mach/hardware.h> -+#include <asm/irq.h> -+ -+#include <mach/regs-clock.h> -+#include <mach/regs-gpio.h> -+ -+#include <asm/plat-s3c24xx/clock.h> -+#include <asm/plat-s3c24xx/cpu.h> -+ -+#include <asm/plat-s3c/regs-timer.h> -+ -+/* Each of the timers 0 through 5 go through the following -+ * clock tree, with the inputs depending on the timers. -+ * -+ * pclk ---- [ prescaler 0 ] -+---> timer 0 -+ * +---> timer 1 -+ * -+ * pclk ---- [ prescaler 1 ] -+---> timer 2 -+ * +---> timer 3 -+ * \---> timer 4 -+ * -+ * Which are fed into the timers as so: -+ * -+ * prescaled 0 ---- [ div 2,4,8,16 ] ---\ -+ * [mux] -> timer 0 -+ * tclk 0 ------------------------------/ -+ * -+ * prescaled 0 ---- [ div 2,4,8,16 ] ---\ -+ * [mux] -> timer 1 -+ * tclk 0 ------------------------------/ -+ * -+ * -+ * prescaled 1 ---- [ div 2,4,8,16 ] ---\ -+ * [mux] -> timer 2 -+ * tclk 1 ------------------------------/ -+ * -+ * prescaled 1 ---- [ div 2,4,8,16 ] ---\ -+ * [mux] -> timer 3 -+ * tclk 1 ------------------------------/ -+ * -+ * prescaled 1 ---- [ div 2,4,8, 16 ] --\ -+ * [mux] -> timer 4 -+ * tclk 1 ------------------------------/ -+ * -+ * Since the mux and the divider are tied together in the -+ * same register space, it is impossible to set the parent -+ * and the rate at the same time. To avoid this, we add an -+ * intermediate 'prescaled-and-divided' clock to select -+ * as the parent for the timer input clock called tdiv. -+ * -+ * prescaled clk --> pwm-tdiv ---\ -+ * [ mux ] --> timer X -+ * tclk -------------------------/ -+*/ -+ -+static unsigned long clk_pwm_scaler_getrate(struct clk *clk) -+{ -+ unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0); -+ -+ if (clk->id == 1) { -+ tcfg0 &= S3C2410_TCFG_PRESCALER1_MASK; -+ tcfg0 >>= S3C2410_TCFG_PRESCALER1_SHIFT; -+ } else { -+ tcfg0 &= S3C2410_TCFG_PRESCALER0_MASK; -+ } -+ -+ return clk_get_rate(clk->parent) / (tcfg0 + 1); -+} -+ -+/* TODO - add set rate calls. */ -+ -+static struct clk clk_timer_scaler[] = { -+ [0] = { -+ .name = "pwm-scaler0", -+ .id = -1, -+ .get_rate = clk_pwm_scaler_getrate, -+ }, -+ [1] = { -+ .name = "pwm-scaler1", -+ .id = -1, -+ .get_rate = clk_pwm_scaler_getrate, -+ }, -+}; -+ -+static struct clk clk_timer_tclk[] = { -+ [0] = { -+ .name = "pwm-tclk0", -+ .id = -1, -+ }, -+ [1] = { -+ .name = "pwm-tclk1", -+ .id = -1, -+ }, -+}; -+ -+struct pwm_tdiv_clk { -+ struct clk clk; -+ unsigned int divisor; -+}; -+ -+static inline struct pwm_tdiv_clk *to_tdiv(struct clk *clk) -+{ -+ return container_of(clk, struct pwm_tdiv_clk, clk); -+} -+ -+static inline unsigned long tcfg_to_divisor(unsigned long tcfg1) -+{ -+ return 1 << (1 + tcfg1); -+} -+ -+static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk) -+{ -+ unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); -+ unsigned int divisor; -+ -+ tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); -+ tcfg1 &= S3C2410_TCFG1_MUX_MASK; -+ -+ if (tcfg1 == S3C2410_TCFG1_MUX_TCLK) -+ divisor = to_tdiv(clk)->divisor; -+ else -+ divisor = tcfg_to_divisor(tcfg1); -+ -+ return clk_get_rate(clk->parent) / divisor; -+} -+ -+static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk, -+ unsigned long rate) -+{ -+ unsigned long parent_rate; -+ unsigned long divisor; -+ -+ parent_rate = clk_get_rate(clk->parent); -+ divisor = parent_rate / rate; -+ -+ if (divisor <= 2) -+ divisor = 2; -+ else if (divisor <= 4) -+ divisor = 4; -+ else if (divisor <= 8) -+ divisor = 8; -+ else -+ divisor = 16; -+ -+ return parent_rate / divisor; -+} -+ -+static unsigned long clk_pwm_tdiv_bits(struct pwm_tdiv_clk *divclk) -+{ -+ unsigned long bits; -+ -+ switch (divclk->divisor) { -+ case 2: -+ bits = S3C2410_TCFG1_MUX_DIV2; -+ break; -+ case 4: -+ bits = S3C2410_TCFG1_MUX_DIV4; -+ break; -+ case 8: -+ bits = S3C2410_TCFG1_MUX_DIV8; -+ break; -+ case 16: -+ default: -+ bits = S3C2410_TCFG1_MUX_DIV16; -+ break; -+ } -+ -+ return bits; -+} -+ -+static void clk_pwm_tdiv_update(struct pwm_tdiv_clk *divclk) -+{ -+ unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); -+ unsigned long bits = clk_pwm_tdiv_bits(divclk); -+ unsigned long flags; -+ unsigned long shift = S3C2410_TCFG1_SHIFT(divclk->clk.id); -+ -+ local_irq_save(flags); -+ -+ tcfg1 = __raw_readl(S3C2410_TCFG1); -+ tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift); -+ tcfg1 |= bits << shift; -+ __raw_writel(tcfg1, S3C2410_TCFG1); -+ -+ local_irq_restore(flags); -+} -+ -+static int clk_pwm_tdiv_set_rate(struct clk *clk, unsigned long rate) -+{ -+ struct pwm_tdiv_clk *divclk = to_tdiv(clk); -+ unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); -+ unsigned long parent_rate = clk_get_rate(clk->parent); -+ unsigned long divisor; -+ -+ tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); -+ tcfg1 &= S3C2410_TCFG1_MUX_MASK; -+ -+ rate = clk_round_rate(clk, rate); -+ divisor = parent_rate / rate; -+ -+ if (divisor > 16) -+ return -EINVAL; -+ -+ divclk->divisor = divisor; -+ -+ /* Update the current MUX settings if we are currently -+ * selected as the clock source for this clock. */ -+ -+ if (tcfg1 != S3C2410_TCFG1_MUX_TCLK) -+ clk_pwm_tdiv_update(divclk); -+ -+ return 0; -+} -+ -+static struct pwm_tdiv_clk clk_timer_tdiv[] = { -+ [0] = { -+ .clk = { -+ .name = "pwm-tdiv", -+ .parent = &clk_timer_scaler[0], -+ .get_rate = clk_pwm_tdiv_get_rate, -+ .set_rate = clk_pwm_tdiv_set_rate, -+ .round_rate = clk_pwm_tdiv_round_rate, -+ }, -+ }, -+ [1] = { -+ .clk = { -+ .name = "pwm-tdiv", -+ .parent = &clk_timer_scaler[0], -+ .get_rate = clk_pwm_tdiv_get_rate, -+ .set_rate = clk_pwm_tdiv_set_rate, -+ .round_rate = clk_pwm_tdiv_round_rate, -+ } -+ }, -+ [2] = { -+ .clk = { -+ .name = "pwm-tdiv", -+ .parent = &clk_timer_scaler[1], -+ .get_rate = clk_pwm_tdiv_get_rate, -+ .set_rate = clk_pwm_tdiv_set_rate, -+ .round_rate = clk_pwm_tdiv_round_rate, -+ }, -+ }, -+ [3] = { -+ .clk = { -+ .name = "pwm-tdiv", -+ .parent = &clk_timer_scaler[1], -+ .get_rate = clk_pwm_tdiv_get_rate, -+ .set_rate = clk_pwm_tdiv_set_rate, -+ .round_rate = clk_pwm_tdiv_round_rate, -+ }, -+ }, -+ [4] = { -+ .clk = { -+ .name = "pwm-tdiv", -+ .parent = &clk_timer_scaler[1], -+ .get_rate = clk_pwm_tdiv_get_rate, -+ .set_rate = clk_pwm_tdiv_set_rate, -+ .round_rate = clk_pwm_tdiv_round_rate, -+ }, -+ }, -+}; -+ -+static int __init clk_pwm_tdiv_register(unsigned int id) -+{ -+ struct pwm_tdiv_clk *divclk = &clk_timer_tdiv[id]; -+ unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); -+ -+ tcfg1 >>= S3C2410_TCFG1_SHIFT(id); -+ tcfg1 &= S3C2410_TCFG1_MUX_MASK; -+ -+ divclk->clk.id = id; -+ divclk->divisor = tcfg_to_divisor(tcfg1); -+ -+ return s3c24xx_register_clock(&divclk->clk); -+} -+ -+static inline struct clk *s3c24xx_pwmclk_tclk(unsigned int id) -+{ -+ return (id >= 2) ? &clk_timer_tclk[1] : &clk_timer_tclk[0]; -+} -+ -+static inline struct clk *s3c24xx_pwmclk_tdiv(unsigned int id) -+{ -+ return &clk_timer_tdiv[id].clk; -+} -+ -+static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent) -+{ -+ unsigned int id = clk->id; -+ unsigned long tcfg1; -+ unsigned long flags; -+ unsigned long bits; -+ unsigned long shift = S3C2410_TCFG1_SHIFT(id); -+ -+ if (parent == s3c24xx_pwmclk_tclk(id)) -+ bits = S3C2410_TCFG1_MUX_TCLK << shift; -+ else if (parent == s3c24xx_pwmclk_tdiv(id)) -+ bits = clk_pwm_tdiv_bits(to_tdiv(parent)) << shift; -+ else -+ return -EINVAL; -+ -+ clk->parent = parent; -+ -+ local_irq_save(flags); -+ -+ tcfg1 = __raw_readl(S3C2410_TCFG1); -+ tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift); -+ __raw_writel(tcfg1 | bits, S3C2410_TCFG1); -+ -+ local_irq_restore(flags); -+ -+ return 0; -+} -+ -+static struct clk clk_tin[] = { -+ [0] = { -+ .name = "pwm-tin", -+ .id = 0, -+ .set_parent = clk_pwm_tin_set_parent, -+ }, -+ [1] = { -+ .name = "pwm-tin", -+ .id = 1, -+ .set_parent = clk_pwm_tin_set_parent, -+ }, -+ [2] = { -+ .name = "pwm-tin", -+ .id = 2, -+ .set_parent = clk_pwm_tin_set_parent, -+ }, -+ [3] = { -+ .name = "pwm-tin", -+ .id = 3, -+ .set_parent = clk_pwm_tin_set_parent, -+ }, -+ [4] = { -+ .name = "pwm-tin", -+ .id = 4, -+ .set_parent = clk_pwm_tin_set_parent, -+ }, -+}; -+ -+static __init int clk_pwm_tin_register(struct clk *pwm) -+{ -+ unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); -+ unsigned int id = pwm->id; -+ -+ struct clk *parent; -+ int ret; -+ -+ ret = s3c24xx_register_clock(pwm); -+ if (ret < 0) -+ return ret; -+ -+ tcfg1 >>= S3C2410_TCFG1_SHIFT(id); -+ tcfg1 &= S3C2410_TCFG1_MUX_MASK; -+ -+ if (tcfg1 == S3C2410_TCFG1_MUX_TCLK) -+ parent = s3c24xx_pwmclk_tclk(id); -+ else -+ parent = s3c24xx_pwmclk_tdiv(id); -+ -+ return clk_set_parent(pwm, parent); -+} -+ -+static __init int s3c24xx_pwmclk_init(void) -+{ -+ struct clk *clk_timers; -+ unsigned int clk; -+ int ret; -+ -+ clk_timers = clk_get(NULL, "timers"); -+ if (IS_ERR(clk_timers)) { -+ printk(KERN_ERR "%s: no parent clock\n", __func__); -+ return -EINVAL; -+ } -+ -+ for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) { -+ clk_timer_scaler[clk].parent = clk_timers; -+ ret = s3c24xx_register_clock(&clk_timer_scaler[clk]); -+ if (ret < 0) { -+ printk(KERN_ERR "error adding pwm scaler%d clock\n", clk); -+ goto err; -+ } -+ } -+ -+ for (clk = 0; clk < ARRAY_SIZE(clk_timer_tclk); clk++) { -+ ret = s3c24xx_register_clock(&clk_timer_tclk[clk]); -+ if (ret < 0) { -+ printk(KERN_ERR "error adding pww tclk%d\n", clk); -+ goto err; -+ } -+ } -+ -+ for (clk = 0; clk < ARRAY_SIZE(clk_timer_tdiv); clk++) { -+ ret = clk_pwm_tdiv_register(clk); -+ if (ret < 0) { -+ printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk); -+ goto err; -+ } -+ } -+ -+ for (clk = 0; clk < ARRAY_SIZE(clk_tin); clk++) { -+ ret = clk_pwm_tin_register(&clk_tin[clk]); -+ if (ret < 0) { -+ printk(KERN_ERR "error adding pwm%d tin clock\n", clk); -+ goto err; -+ } -+ } -+ -+ return 0; -+ -+ err: -+ return ret; -+} -+ -+arch_initcall(s3c24xx_pwmclk_init); |