diff options
Diffstat (limited to 'arch/arm/plat-mxc/pwm.c')
-rwxr-xr-x | arch/arm/plat-mxc/pwm.c | 212 |
1 files changed, 199 insertions, 13 deletions
diff --git a/arch/arm/plat-mxc/pwm.c b/arch/arm/plat-mxc/pwm.c index 4a67f4de0e4d..048efb788c3d 100755 --- a/arch/arm/plat-mxc/pwm.c +++ b/arch/arm/plat-mxc/pwm.c @@ -6,7 +6,7 @@ * published by the Free Software Foundation. * * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com> - * Copyright 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2009-2012 Freescale Semiconductor, Inc. */ #include <linux/module.h> @@ -45,6 +45,84 @@ #define MX3_PWMCR_CLKSRC_IPG (1 << 16) #define MX3_PWMCR_CLKSRC_IPG_32k (3 << 16) +/* + * Vybrid(CONFIG_ARCH_MVF) FlexTimer PWM registers defination + */ +#define MVF_PWM_FTM_SC 0x00 /* status and controls */ +#define MVF_PWM_FTM_CNT 0x04 /* counter */ +#define MVF_PWM_FTM_MOD 0x08 /* modulo */ + +#define MVF_PWM_FTM_C0SC 0x0C /* channel(0) status and control */ +#define MVF_PWM_FTM_C0V 0x10 /* channel(0) value */ +#define MVF_PWM_FTM_C1SC 0x14 /* channel(1) */ +#define MVF_PWM_FTM_C1V 0x18 /* channel(1) */ +#define MVF_PWM_FTM_C2SC 0x1C /* channel(2) */ +#define MVF_PWM_FTM_C2V 0x20 /* channel(2) */ +#define MVF_PWM_FTM_C3SC 0x24 /* channel(3) */ +#define MVF_PWM_FTM_C3V 0x28 /* channel(3) */ +#define MVF_PWM_FTM_C4SC 0x2C /* channel(4) */ +#define MVF_PWM_FTM_C4V 0x30 /* channel(4) */ +#define MVF_PWM_FTM_C5SC 0x34 /* channel(5) */ +#define MVF_PWM_FTM_C5V 0x38 /* channel(5) */ +#define MVF_PWM_FTM_C6SC 0x3C /* channel(6) */ +#define MVF_PWM_FTM_C6V 0x40 /* channel(6) */ +#define MVF_PWM_FTM_C7SC 0x44 /* channel(7) */ +#define MVF_PWM_FTM_C7V 0x48 /* channel(7) */ + +#define MVF_PWM_FTM_CNTIN 0x4C /* counter initial value */ +#define MVF_PWM_FTM_STATUS 0x50 /* capture and compare status */ +#define MVF_PWM_FTM_MODE 0x54 /* mode select */ +#define MVF_PWM_FTM_SYNC 0x58 /* synchronization */ +#define MVF_PWM_FTM_OUTINIT 0x5C /* initial state for channels output */ +#define MVF_PWM_FTM_OUTMASK 0x60 /* output mask */ +#define MVF_PWM_FTM_COMBINE 0x64 /* function for linked channels */ +#define MVF_PWM_FTM_DEADTIME 0x68 /* deadtime insertion control */ +#define MVF_PWM_FTM_EXTTRIG 0x6C /* external trigger */ +#define MVF_PWM_FTM_POL 0x70 /* channels polarity */ +#define MVF_PWM_FTM_FMS 0x74 /* fault mode status */ +#define MVF_PWM_FTM_FILTER 0x78 /* input capture filter control */ +#define MVF_PWM_FTM_FLTCTRL 0x7C /* fault control */ +#define MVF_PWM_FTM_QDCTRL 0x80 /* quadrature decoder ctrl and status */ +#define MVF_PWM_FTM_CONF 0x84 /* configuration */ +#define MVF_PWM_FTM_FLTPOL 0x88 /* fault input polarity */ +#define MVF_PWM_FTM_SYNCONF 0x8C /* synchronization configuration */ +#define MVF_PWM_FTM_INVCTRL 0x90 /* inverting control */ +#define MVF_PWM_FTM_SWOCTRL 0x94 /* software output control */ +#define MVF_PWM_FTM_PWMLOAD 0x98 /* PWM load */ + +#define PWM_TYPE_EPWM 0x01 /* Edge-aligned pwm */ +#define PWM_TYPE_CPWM 0x02 /* Center-aligned pwm */ + +#define PWM_FTMSC_CPWMS (0x01 << 5) +#define PWM_FTMSC_CLK_MASK 0x3 +#define PWM_FTMSC_CLK_OFFSET 3 +#define PWM_FTMSC_CLKSYS (0x1 << 3) +#define PWM_FTMSC_CLKFIX (0x2 << 3) +#define PWM_FTMSC_CLKEXT (0x3 << 3) +#define PWM_FTMSC_PS_MASK 0x7 +#define PWM_FTMSC_PS_OFFSET 0 +#define PWM_FTMSC_PS1 0x0 +#define PWM_FTMSC_PS2 0x1 +#define PWM_FTMSC_PS4 0x2 +#define PWM_FTMSC_PS8 0x3 +#define PWM_FTMSC_PS16 0x4 +#define PWM_FTMSC_PS32 0x5 +#define PWM_FTMSC_PS64 0x6 +#define PWM_FTMSC_PS128 0x7 + +#define PWM_FTMCnSC_MSB (0x1 << 5) +#define PWM_FTMCnSC_MSA (0x1 << 4) +#define PWM_FTMCnSC_ELSB (0x1 << 3) +#define PWM_FTMCnSC_ELSA (0x1 << 2) + +#define FTM_PWMMODE (PWM_FTMCnSC_MSB) +#define FTM_PWM_HIGH_TRUE (PWM_FTMCnSC_ELSB) +#define FTM_PWM_LOW_TRUE (PWM_FTMCnSC_ELSA) + +#define PWM_FTMMODE_FTMEN 0x01 +#define PWM_FTMMODE_INIT 0x02 +#define PWM_FTMMODE_PWMSYNC (0x01 << 3) + struct pwm_device { struct list_head node; struct platform_device *pdev; @@ -58,6 +136,8 @@ struct pwm_device { unsigned int use_count; unsigned int pwm_id; int pwmo_invert; + unsigned int cpwm; /* CPWM mode */ + unsigned int clk_ps; /* clock prescaler:1/2/4/8/16/32/64/128 */ void (*enable_pwm_pad)(void); void (*disable_pwm_pad)(void); }; @@ -67,7 +147,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) if (pwm == NULL || period_ns == 0 || duty_ns > period_ns) return -EINVAL; - if (!(cpu_is_mx1() || cpu_is_mx21())) { + if (!(cpu_is_mx1() || cpu_is_mx21() || cpu_is_mvf())) { unsigned long long c; unsigned long period_cycles, duty_cycles, prescale; u32 cr; @@ -127,6 +207,91 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) duty_ns = period_ns - duty_ns; p = max * duty_ns / period_ns; writel(max - p, pwm->mmio_base + MX1_PWMS); + } else if (cpu_is_mvf()) { + unsigned long long c; + unsigned long period_cycles, duty_cycles; + /* FTM clock source prescaler */ + u32 ps = (0x1 << pwm->clk_ps) * 1000; + /* IPS bus clock source */ + c = clk_get_rate(pwm->clk) / 1000000UL; + + c = c * period_ns; + do_div(c, ps); + period_cycles = (unsigned long)c; + + c = clk_get_rate(pwm->clk) / 1000000UL; + c = c * duty_ns; + do_div(c, ps); + duty_cycles = (unsigned long)c; + + if (period_cycles > 0xFFFF) { + dev_warn(&pwm->pdev->dev, + "required PWM period cycles(%lu) overflow" + "16-bits counter!\n", period_cycles); + period_cycles = 0xFFFF; + } + if (duty_cycles >= 0xFFFF) { + dev_warn(&pwm->pdev->dev, + "required PWM duty cycles(%lu) overflow" + "16-bits counter!\n", duty_cycles); + duty_cycles = 0xFFFF - 1; + } + if (duty_cycles >= period_cycles) + duty_cycles = (period_cycles/10) * 9; + + /* configure channel to pwm mode */ + /* enable FTMEN */ + if (pwm->cpwm) { + u32 reg; + reg = __raw_readl(pwm->mmio_base + MVF_PWM_FTM_SC); + reg |= 0x01 << 5; + __raw_writel(reg, pwm->mmio_base + MVF_PWM_FTM_SC); + } + __raw_writel(FTM_PWMMODE | FTM_PWM_HIGH_TRUE, + pwm->mmio_base + MVF_PWM_FTM_C0SC); + __raw_writel(FTM_PWMMODE | FTM_PWM_HIGH_TRUE, + pwm->mmio_base + MVF_PWM_FTM_C1SC); + __raw_writel(FTM_PWMMODE | FTM_PWM_HIGH_TRUE, + pwm->mmio_base + MVF_PWM_FTM_C2SC); + __raw_writel(FTM_PWMMODE | FTM_PWM_HIGH_TRUE, + pwm->mmio_base + MVF_PWM_FTM_C3SC); + + __raw_writel(0xF0, pwm->mmio_base + MVF_PWM_FTM_OUTMASK); + __raw_writel(0x0F, pwm->mmio_base + MVF_PWM_FTM_OUTINIT); + __raw_writel(0x0, pwm->mmio_base + MVF_PWM_FTM_CNTIN); + if (pwm->cpwm) { + /* + * Center-aligned PWM: + * period = 2*(MOD - CNTIN) + * duty = 2*(CnV - CNTIN) + */ + __raw_writel(period_cycles / 2, + pwm->mmio_base + MVF_PWM_FTM_MOD); + __raw_writel(duty_cycles / 2, + pwm->mmio_base + MVF_PWM_FTM_C0V); + __raw_writel(duty_cycles / 2, + pwm->mmio_base + MVF_PWM_FTM_C1V); + __raw_writel(duty_cycles / 2, + pwm->mmio_base + MVF_PWM_FTM_C2V); + __raw_writel(duty_cycles / 2, + pwm->mmio_base + MVF_PWM_FTM_C3V); + + } else { + /* Edge-aligend PWM + * period = MOD - CNTIN + 1 + * duty = CnV - CNTIN + */ + __raw_writel(period_cycles - 1, + pwm->mmio_base + MVF_PWM_FTM_MOD); + __raw_writel(duty_cycles, + pwm->mmio_base + MVF_PWM_FTM_C0V); + __raw_writel(duty_cycles, + pwm->mmio_base + MVF_PWM_FTM_C1V); + __raw_writel(duty_cycles, + pwm->mmio_base + MVF_PWM_FTM_C2V); + __raw_writel(duty_cycles, + pwm->mmio_base + MVF_PWM_FTM_C3V); + } } else { BUG(); } @@ -145,25 +310,41 @@ int pwm_enable(struct pwm_device *pwm) if (!rc) pwm->clk_enabled = 1; } + if (cpu_is_mvf()) { + reg = __raw_readl(pwm->mmio_base + MVF_PWM_FTM_SC); + reg &= ~((PWM_FTMSC_CLK_MASK << PWM_FTMSC_CLK_OFFSET) | + (PWM_FTMSC_PS_MASK << PWM_FTMSC_PS_OFFSET)); + /* select IPS bus clock source + * prescale 128 + */ + reg |= (PWM_FTMSC_CLKSYS | pwm->clk_ps); + __raw_writel(reg, pwm->mmio_base + MVF_PWM_FTM_SC); + } else { + reg = readl(pwm->mmio_base + MX3_PWMCR); + reg |= MX3_PWMCR_EN; + writel(reg, pwm->mmio_base + MX3_PWMCR); - reg = readl(pwm->mmio_base + MX3_PWMCR); - reg |= MX3_PWMCR_EN; - writel(reg, pwm->mmio_base + MX3_PWMCR); - - if (pwm->enable_pwm_pad) - pwm->enable_pwm_pad(); - + if (pwm->enable_pwm_pad) + pwm->enable_pwm_pad(); + } return rc; } EXPORT_SYMBOL(pwm_enable); void pwm_disable(struct pwm_device *pwm) { - if (pwm->disable_pwm_pad) - pwm->disable_pwm_pad(); - - writel(0, pwm->mmio_base + MX3_PWMCR); + u32 reg; + if (cpu_is_mvf()) { + __raw_writel(0xFF, pwm->mmio_base + MVF_PWM_FTM_OUTMASK); + reg = __raw_readl(pwm->mmio_base + MVF_PWM_FTM_SC); + reg &= ~(PWM_FTMSC_CLK_MASK << PWM_FTMSC_CLK_OFFSET); + __raw_writel(reg, pwm->mmio_base + MVF_PWM_FTM_SC); + } else { + if (pwm->disable_pwm_pad) + pwm->disable_pwm_pad(); + writel(0, pwm->mmio_base + MX3_PWMCR); + } if (pwm->clk_enabled) { clk_disable(pwm->clk); pwm->clk_enabled = 0; @@ -241,6 +422,11 @@ static int __devinit mxc_pwm_probe(struct platform_device *pdev) pwm->use_count = 0; pwm->pwm_id = pdev->id; pwm->pdev = pdev; + /* default select IPS bus clock, and divided by 128 for MVF platform */ + pwm->clk_ps = PWM_FTMSC_PS128; +#ifdef CONFIG_MXC_PWM_CPWM + pwm->cpwm = 1; +#endif if (plat_data != NULL) { pwm->pwmo_invert = plat_data->pwmo_invert; pwm->enable_pwm_pad = plat_data->enable_pwm_pad; |