diff options
Diffstat (limited to 'arch/arm/plat-mxc/pwm.c')
-rw-r--r-- | arch/arm/plat-mxc/pwm.c | 78 |
1 files changed, 44 insertions, 34 deletions
diff --git a/arch/arm/plat-mxc/pwm.c b/arch/arm/plat-mxc/pwm.c index b683d2eb6261..c65e7f284478 100644 --- a/arch/arm/plat-mxc/pwm.c +++ b/arch/arm/plat-mxc/pwm.c @@ -34,20 +34,17 @@ #define MX3_PWMSAR 0x0C /* PWM Sample Register */ #define MX3_PWMPR 0x10 /* PWM Period Register */ #define MX3_PWMCR_PRESCALER(x) (((x - 1) & 0xFFF) << 4) -#define MX3_PWMCR_DOZEEN (1 << 24) -#define MX3_PWMCR_WAITEN (1 << 23) -#define MX3_PWMCR_DBGEN (1 << 22) -#define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16) -#define MX3_PWMCR_CLKSRC_IPG (1 << 16) -#define MX3_PWMCR_SWR (1 << 3) -#define MX3_PWMCR_EN (1 << 0) - #define MX3_PWMCR_STOPEN (1 << 25) #define MX3_PWMCR_DOZEEN (1 << 24) #define MX3_PWMCR_WAITEN (1 << 23) #define MX3_PWMCR_DBGEN (1 << 22) +#define MX3_PWMCR_CLKSRC(src) (src << 16) #define MX3_PWMCR_CLKSRC_IPG (1 << 16) +#define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16) #define MX3_PWMCR_CLKSRC_IPG_32k (3 << 16) +#define MX3_PWMCR_SWR (1 << 3) +#define MX3_PWMCR_EN (1 << 0) + struct pwm_device { struct list_head node; @@ -62,6 +59,7 @@ struct pwm_device { unsigned int use_count; unsigned int pwm_id; int pwmo_invert; + int clk_select; void (*enable_pwm_pad)(void); void (*disable_pwm_pad)(void); }; @@ -72,50 +70,48 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) return -EINVAL; if (!(cpu_is_mx1() || cpu_is_mx21())) { + unsigned long clock_rate; unsigned long long c; unsigned long period_cycles, duty_cycles, prescale; u32 cr; + pr_debug("duty_ns=%d, period_ns=%d\n", duty_ns, period_ns); if (pwm->pwmo_invert) duty_ns = period_ns - duty_ns; - c = clk_get_rate(pwm->clk); - c = c * period_ns; + c = clock_rate = clk_get_rate(pwm->clk); + c = c * period_ns + 500000000; do_div(c, 1000000000); period_cycles = c; prescale = period_cycles / 0x10000 + 1; period_cycles /= prescale; - /* the chip document says the counter counts up to - * period_cycles + 1 and then is reset to 0, so the - * actual period of the PWM wave is period_cycles + 2 - */ - c = (unsigned long long)(period_cycles + 2) * duty_ns; + if (period_cycles < 2) + period_cycles = 2; + c = (unsigned long long)(period_cycles) * duty_ns + + (period_ns >> 1); do_div(c, period_ns); duty_cycles = c; + pr_debug("duty_cycles=%ld, period_cycles= %ld duty_ns=%d" + " period_ns=%d\n", + duty_cycles, period_cycles, duty_ns, period_ns); + writel(duty_cycles, pwm->mmio_base + MX3_PWMSAR); /* * according to imx pwm RM, the real period value should be * PERIOD value in PWMPR plus 2. */ - if (period_cycles > 2) - period_cycles -= 2; - else - period_cycles = 0; - - writel(duty_cycles, pwm->mmio_base + MX3_PWMSAR); - writel(period_cycles, pwm->mmio_base + MX3_PWMPR); - + writel(period_cycles - 2, pwm->mmio_base + MX3_PWMPR); + pr_info("%s: pwm freq = %ld, clk_select=%x clock_rate=%ld\n", + __func__, + clock_rate / (prescale * period_cycles), + pwm->clk_select, clock_rate); cr = MX3_PWMCR_PRESCALER(prescale) | MX3_PWMCR_STOPEN | MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | MX3_PWMCR_DBGEN; - if (cpu_is_mx25()) - cr |= MX3_PWMCR_CLKSRC_IPG; - else - cr |= MX3_PWMCR_CLKSRC_IPG_HIGH; - + cr |= MX3_PWMCR_CLKSRC(pwm->clk_select); writel(cr, pwm->mmio_base + MX3_PWMCR); } else if (cpu_is_mx1() || cpu_is_mx21()) { /* The PWM subsystem allows for exact frequencies. However, @@ -231,11 +227,19 @@ void pwm_free(struct pwm_device *pwm) } EXPORT_SYMBOL(pwm_free); +const static char* clk_names[] = { + [PWM_CLK_DEFAULT] = NULL, + [PWM_CLK_HIGHPERF] = "high_perf", + [PWM_CLK_HIGHFREQ] = NULL, + [PWM_CLK_32K] = NULL, +}; + static int __devinit mxc_pwm_probe(struct platform_device *pdev) { struct pwm_device *pwm; struct resource *r; struct mxc_pwm_platform_data *plat_data = pdev->dev.platform_data; + int clk_select = PWM_CLK_DEFAULT; int ret = 0; pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); @@ -244,7 +248,17 @@ static int __devinit mxc_pwm_probe(struct platform_device *pdev) return -ENOMEM; } - pwm->clk = clk_get(&pdev->dev, "pwm"); + if (plat_data) { + pwm->pwmo_invert = plat_data->pwmo_invert; + pwm->enable_pwm_pad = plat_data->enable_pwm_pad; + pwm->disable_pwm_pad = plat_data->disable_pwm_pad; + clk_select = plat_data->clk_select; + } + if (clk_select == PWM_CLK_DEFAULT) + clk_select = (cpu_is_mx25()) ? PWM_CLK_HIGHPERF + : PWM_CLK_HIGHFREQ; + + pwm->clk = clk_get(&pdev->dev, clk_names[clk_select]); if (IS_ERR(pwm->clk)) { ret = PTR_ERR(pwm->clk); @@ -256,11 +270,7 @@ static int __devinit mxc_pwm_probe(struct platform_device *pdev) pwm->use_count = 0; pwm->pwm_id = pdev->id; pwm->pdev = pdev; - if (plat_data != NULL) { - pwm->pwmo_invert = plat_data->pwmo_invert; - pwm->enable_pwm_pad = plat_data->enable_pwm_pad; - pwm->disable_pwm_pad = plat_data->disable_pwm_pad; - } + pwm->clk_select = clk_select; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { |