1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright 2012 Freescale Semiconductor, Inc.
9 #include <linux/kernel.h>
10 #include <linux/module.h>
12 #include <linux/platform_device.h>
13 #include <linux/pwm.h>
14 #include <linux/slab.h>
15 #include <linux/stmp_device.h>
22 #define PWM_ACTIVE0 0x10
23 #define PWM_PERIOD0 0x20
24 #define PERIOD_PERIOD(p) ((p) & 0xffff)
25 #define PERIOD_PERIOD_MAX 0x10000
26 #define PERIOD_ACTIVE_HIGH (3 << 16)
27 #define PERIOD_ACTIVE_LOW (2 << 16)
28 #define PERIOD_INACTIVE_HIGH (3 << 18)
29 #define PERIOD_INACTIVE_LOW (2 << 18)
30 #define PERIOD_POLARITY_NORMAL (PERIOD_ACTIVE_HIGH | PERIOD_INACTIVE_LOW)
31 #define PERIOD_POLARITY_INVERSE (PERIOD_ACTIVE_LOW | PERIOD_INACTIVE_HIGH)
32 #define PERIOD_CDIV(div) (((div) & 0x7) << 20)
33 #define PERIOD_CDIV_MAX 8
35 static const u8 cdiv_shift
[PERIOD_CDIV_MAX
] = {
36 0, 1, 2, 3, 4, 6, 8, 10
44 static inline struct mxs_pwm_chip
*to_mxs_pwm_chip(struct pwm_chip
*chip
)
46 return pwmchip_get_drvdata(chip
);
49 static int mxs_pwm_apply(struct pwm_chip
*chip
, struct pwm_device
*pwm
,
50 const struct pwm_state
*state
)
52 struct mxs_pwm_chip
*mxs
= to_mxs_pwm_chip(chip
);
54 unsigned int period_cycles
, duty_cycles
;
57 unsigned int pol_bits
;
60 * If the PWM channel is disabled, make sure to turn on the
61 * clock before calling clk_get_rate() and writing to the
62 * registers. Otherwise, just keep it enabled.
64 if (!pwm_is_enabled(pwm
)) {
65 ret
= clk_prepare_enable(mxs
->clk
);
70 if (!state
->enabled
&& pwm_is_enabled(pwm
))
71 writel(1 << pwm
->hwpwm
, mxs
->base
+ PWM_CTRL
+ CLR
);
73 rate
= clk_get_rate(mxs
->clk
);
75 c
= rate
>> cdiv_shift
[div
];
76 c
= c
* state
->period
;
77 do_div(c
, 1000000000);
78 if (c
< PERIOD_PERIOD_MAX
)
81 if (div
>= PERIOD_CDIV_MAX
)
86 c
*= state
->duty_cycle
;
87 do_div(c
, state
->period
);
91 * The data sheet the says registers must be written to in
92 * this order (ACTIVEn, then PERIODn). Also, the new settings
93 * only take effect at the beginning of a new period, avoiding
97 pol_bits
= state
->polarity
== PWM_POLARITY_NORMAL
?
98 PERIOD_POLARITY_NORMAL
: PERIOD_POLARITY_INVERSE
;
99 writel(duty_cycles
<< 16,
100 mxs
->base
+ PWM_ACTIVE0
+ pwm
->hwpwm
* 0x20);
101 writel(PERIOD_PERIOD(period_cycles
) | pol_bits
| PERIOD_CDIV(div
),
102 mxs
->base
+ PWM_PERIOD0
+ pwm
->hwpwm
* 0x20);
104 if (state
->enabled
) {
105 if (!pwm_is_enabled(pwm
)) {
107 * The clock was enabled above. Just enable
108 * the channel in the control register.
110 writel(1 << pwm
->hwpwm
, mxs
->base
+ PWM_CTRL
+ SET
);
113 clk_disable_unprepare(mxs
->clk
);
118 static const struct pwm_ops mxs_pwm_ops
= {
119 .apply
= mxs_pwm_apply
,
122 static int mxs_pwm_probe(struct platform_device
*pdev
)
124 struct device_node
*np
= pdev
->dev
.of_node
;
125 struct pwm_chip
*chip
;
126 struct mxs_pwm_chip
*mxs
;
130 ret
= of_property_read_u32(np
, "fsl,pwm-number", &npwm
);
132 dev_err(&pdev
->dev
, "failed to get pwm number: %d\n", ret
);
136 chip
= devm_pwmchip_alloc(&pdev
->dev
, npwm
, sizeof(*mxs
));
138 return PTR_ERR(chip
);
139 mxs
= to_mxs_pwm_chip(chip
);
141 mxs
->base
= devm_platform_ioremap_resource(pdev
, 0);
142 if (IS_ERR(mxs
->base
))
143 return PTR_ERR(mxs
->base
);
145 mxs
->clk
= devm_clk_get(&pdev
->dev
, NULL
);
146 if (IS_ERR(mxs
->clk
))
147 return PTR_ERR(mxs
->clk
);
149 chip
->ops
= &mxs_pwm_ops
;
151 /* FIXME: Only do this if the PWM isn't already running */
152 ret
= stmp_reset_block(mxs
->base
);
154 return dev_err_probe(&pdev
->dev
, ret
, "failed to reset PWM\n");
156 ret
= devm_pwmchip_add(&pdev
->dev
, chip
);
158 dev_err(&pdev
->dev
, "failed to add pwm chip %d\n", ret
);
165 static const struct of_device_id mxs_pwm_dt_ids
[] = {
166 { .compatible
= "fsl,imx23-pwm", },
169 MODULE_DEVICE_TABLE(of
, mxs_pwm_dt_ids
);
171 static struct platform_driver mxs_pwm_driver
= {
174 .of_match_table
= mxs_pwm_dt_ids
,
176 .probe
= mxs_pwm_probe
,
178 module_platform_driver(mxs_pwm_driver
);
180 MODULE_ALIAS("platform:mxs-pwm");
181 MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
182 MODULE_DESCRIPTION("Freescale MXS PWM Driver");
183 MODULE_LICENSE("GPL v2");