diff options
author | Troy Kisky <troy.kisky@boundarydevices.com> | 2013-02-12 19:46:45 -0700 |
---|---|---|
committer | Troy Kisky <troy.kisky@boundarydevices.com> | 2013-02-13 12:54:55 -0700 |
commit | 82c720893fc5459ee948fdbfd33ba9be2fc4894e (patch) | |
tree | c899c9fc488dd6b6b630d0ef17290f5029ad6845 | |
parent | 1764a0e8eba4ee366dfec8590196c8f3d785d867 (diff) |
pwm: add high_perf clock option, fix rounding
-rw-r--r-- | arch/arm/mach-mx6/clock.c | 43 | ||||
-rw-r--r-- | arch/arm/mach-mx6/devices-imx6q.h | 5 | ||||
-rw-r--r-- | arch/arm/plat-mxc/devices/platform-mxc_pwm.c | 6 | ||||
-rw-r--r-- | arch/arm/plat-mxc/include/mach/devices-common.h | 3 | ||||
-rw-r--r-- | arch/arm/plat-mxc/pwm.c | 78 | ||||
-rw-r--r-- | include/linux/fsl_devices.h | 5 |
6 files changed, 101 insertions, 39 deletions
diff --git a/arch/arm/mach-mx6/clock.c b/arch/arm/mach-mx6/clock.c index 0a8c8803ba6a..7053302ea3f5 100644 --- a/arch/arm/mach-mx6/clock.c +++ b/arch/arm/mach-mx6/clock.c @@ -4719,6 +4719,45 @@ static struct clk pwm_clk[] = { }, }; +static struct clk pwm_high_perf_clk[] = { + { + __INIT_CLK_DEBUG(pwm_clk_0) + .parent = &ipg_clk, + .id = 0, + .enable_reg = MXC_CCM_CCGR4, + .enable_shift = MXC_CCM_CCGRx_CG8_OFFSET, + .enable = _clk_enable, + .disable = _clk_disable, + }, + { + __INIT_CLK_DEBUG(pwm_clk_1) + .parent = &ipg_clk, + .id = 1, + .enable_reg = MXC_CCM_CCGR4, + .enable_shift = MXC_CCM_CCGRx_CG9_OFFSET, + .enable = _clk_enable, + .disable = _clk_disable, + }, + { + __INIT_CLK_DEBUG(pwm_clk_2) + .parent = &ipg_clk, + .id = 2, + .enable_reg = MXC_CCM_CCGR4, + .enable_shift = MXC_CCM_CCGRx_CG10_OFFSET, + .enable = _clk_enable, + .disable = _clk_disable, + }, + { + __INIT_CLK_DEBUG(pwm_clk_3) + .parent = &ipg_clk, + .id = 3, + .enable_reg = MXC_CCM_CCGR4, + .enable_shift = MXC_CCM_CCGRx_CG11_OFFSET, + .enable = _clk_enable, + .disable = _clk_disable, + }, +}; + static int _clk_sata_enable(struct clk *clk) { unsigned int reg; @@ -5277,6 +5316,10 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK("mxc_pwm.1", NULL, pwm_clk[1]), _REGISTER_CLOCK("mxc_pwm.2", NULL, pwm_clk[2]), _REGISTER_CLOCK("mxc_pwm.3", NULL, pwm_clk[3]), + _REGISTER_CLOCK("mxc_pwm.0", "high_perf", pwm_high_perf_clk[0]), + _REGISTER_CLOCK("mxc_pwm.1", "high_perf", pwm_high_perf_clk[1]), + _REGISTER_CLOCK("mxc_pwm.2", "high_perf", pwm_high_perf_clk[2]), + _REGISTER_CLOCK("mxc_pwm.3", "high_perf", pwm_high_perf_clk[3]), _REGISTER_CLOCK(NULL, "pcie_clk", pcie_clk[0]), _REGISTER_CLOCK("enet.0", NULL, enet_clk[0]), _REGISTER_CLOCK(NULL, "imx_sata_clk", sata_clk[0]), diff --git a/arch/arm/mach-mx6/devices-imx6q.h b/arch/arm/mach-mx6/devices-imx6q.h index f7ad317a4fe3..ff49ce1b1419 100644 --- a/arch/arm/mach-mx6/devices-imx6q.h +++ b/arch/arm/mach-mx6/devices-imx6q.h @@ -172,7 +172,10 @@ extern const struct imx_viv_gpu_data imx6_gc355_data __initconst; extern const struct imx_mxc_pwm_data imx6q_mxc_pwm_data[] __initconst; #define imx6q_add_mxc_pwm(id) \ - imx_add_mxc_pwm(&imx6q_mxc_pwm_data[id]) + imx_add_mxc_pwm(&imx6q_mxc_pwm_data[id], NULL) + +#define imx6q_add_mxc_pwm_pdata(id, pdata) \ + imx_add_mxc_pwm(&imx6q_mxc_pwm_data[id], pdata) #define imx6q_add_mxc_pwm_backlight(id, pdata) \ platform_device_register_resndata(NULL, "pwm-backlight",\ diff --git a/arch/arm/plat-mxc/devices/platform-mxc_pwm.c b/arch/arm/plat-mxc/devices/platform-mxc_pwm.c index a8521b978676..fa032f12fc08 100644 --- a/arch/arm/plat-mxc/devices/platform-mxc_pwm.c +++ b/arch/arm/plat-mxc/devices/platform-mxc_pwm.c @@ -70,7 +70,7 @@ const struct imx_mxc_pwm_data imx6q_mxc_pwm_data[] __initconst = { #endif /* ifdef CONFIG_SOC_IMX6Q */ struct platform_device *__init imx_add_mxc_pwm( - const struct imx_mxc_pwm_data *data) + const struct imx_mxc_pwm_data *data, const struct mxc_pwm_platform_data *pdata) { struct resource res[] = { { @@ -83,7 +83,7 @@ struct platform_device *__init imx_add_mxc_pwm( .flags = IORESOURCE_IRQ, }, }; - + pr_info("%s:pdata=%p\n", __func__, pdata); return imx_add_platform_device("mxc_pwm", data->id, - res, ARRAY_SIZE(res), NULL, 0); + res, ARRAY_SIZE(res), pdata, sizeof(*pdata)); } diff --git a/arch/arm/plat-mxc/include/mach/devices-common.h b/arch/arm/plat-mxc/include/mach/devices-common.h index f6d616d0b98a..dc4b4ecdf83a 100644 --- a/arch/arm/plat-mxc/include/mach/devices-common.h +++ b/arch/arm/plat-mxc/include/mach/devices-common.h @@ -312,7 +312,8 @@ struct imx_mxc_pwm_data { resource_size_t irq; }; struct platform_device *__init imx_add_mxc_pwm( - const struct imx_mxc_pwm_data *data); + const struct imx_mxc_pwm_data *data, + const struct mxc_pwm_platform_data *pdata); /* mxc_rtc */ struct imx_mxc_rtc_data { 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) { diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index 78a50acf22a0..f8c1a469bee4 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -400,6 +400,11 @@ struct mxc_audio_codec_platform_data { struct mxc_pwm_platform_data { int pwmo_invert; +#define PWM_CLK_DEFAULT 0 +#define PWM_CLK_HIGHPERF 1 +#define PWM_CLK_HIGHFREQ 2 +#define PWM_CLK_32K 3 + int clk_select; void (*enable_pwm_pad) (void); void (*disable_pwm_pad) (void); }; |