summaryrefslogtreecommitdiff
path: root/arch/arm/plat-mxc/pwm.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/plat-mxc/pwm.c')
-rw-r--r--arch/arm/plat-mxc/pwm.c78
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) {