summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTroy Kisky <troy.kisky@boundarydevices.com>2013-02-12 19:46:45 -0700
committerTroy Kisky <troy.kisky@boundarydevices.com>2013-02-13 12:54:55 -0700
commit82c720893fc5459ee948fdbfd33ba9be2fc4894e (patch)
treec899c9fc488dd6b6b630d0ef17290f5029ad6845
parent1764a0e8eba4ee366dfec8590196c8f3d785d867 (diff)
pwm: add high_perf clock option, fix rounding
-rw-r--r--arch/arm/mach-mx6/clock.c43
-rw-r--r--arch/arm/mach-mx6/devices-imx6q.h5
-rw-r--r--arch/arm/plat-mxc/devices/platform-mxc_pwm.c6
-rw-r--r--arch/arm/plat-mxc/include/mach/devices-common.h3
-rw-r--r--arch/arm/plat-mxc/pwm.c78
-rw-r--r--include/linux/fsl_devices.h5
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);
};