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
45 #define to_mxs_pwm_chip(_chip) container_of(_chip, struct mxs_pwm_chip, chip)
47 static int mxs_pwm_apply(struct pwm_chip
*chip
, struct pwm_device
*pwm
,
48 const struct pwm_state
*state
)
50 struct mxs_pwm_chip
*mxs
= to_mxs_pwm_chip(chip
);
52 unsigned int period_cycles
, duty_cycles
;
55 unsigned int pol_bits
;
58 * If the PWM channel is disabled, make sure to turn on the
59 * clock before calling clk_get_rate() and writing to the
60 * registers. Otherwise, just keep it enabled.
62 if (!pwm_is_enabled(pwm
)) {
63 ret
= clk_prepare_enable(mxs
->clk
);
68 if (!state
->enabled
&& pwm_is_enabled(pwm
))
69 writel(1 << pwm
->hwpwm
, mxs
->base
+ PWM_CTRL
+ CLR
);
71 rate
= clk_get_rate(mxs
->clk
);
73 c
= rate
>> cdiv_shift
[div
];
74 c
= c
* state
->period
;
75 do_div(c
, 1000000000);
76 if (c
< PERIOD_PERIOD_MAX
)
79 if (div
>= PERIOD_CDIV_MAX
)
84 c
*= state
->duty_cycle
;
85 do_div(c
, state
->period
);
89 * The data sheet the says registers must be written to in
90 * this order (ACTIVEn, then PERIODn). Also, the new settings
91 * only take effect at the beginning of a new period, avoiding
95 pol_bits
= state
->polarity
== PWM_POLARITY_NORMAL
?
96 PERIOD_POLARITY_NORMAL
: PERIOD_POLARITY_INVERSE
;
97 writel(duty_cycles
<< 16,
98 mxs
->base
+ PWM_ACTIVE0
+ pwm
->hwpwm
* 0x20);
99 writel(PERIOD_PERIOD(period_cycles
) | pol_bits
| PERIOD_CDIV(div
),
100 mxs
->base
+ PWM_PERIOD0
+ pwm
->hwpwm
* 0x20);
102 if (state
->enabled
) {
103 if (!pwm_is_enabled(pwm
)) {
105 * The clock was enabled above. Just enable
106 * the channel in the control register.
108 writel(1 << pwm
->hwpwm
, mxs
->base
+ PWM_CTRL
+ SET
);
111 clk_disable_unprepare(mxs
->clk
);
116 static const struct pwm_ops mxs_pwm_ops
= {
117 .apply
= mxs_pwm_apply
,
118 .owner
= THIS_MODULE
,
121 static int mxs_pwm_probe(struct platform_device
*pdev
)
123 struct device_node
*np
= pdev
->dev
.of_node
;
124 struct mxs_pwm_chip
*mxs
;
127 mxs
= devm_kzalloc(&pdev
->dev
, sizeof(*mxs
), GFP_KERNEL
);
131 mxs
->base
= devm_platform_ioremap_resource(pdev
, 0);
132 if (IS_ERR(mxs
->base
))
133 return PTR_ERR(mxs
->base
);
135 mxs
->clk
= devm_clk_get(&pdev
->dev
, NULL
);
136 if (IS_ERR(mxs
->clk
))
137 return PTR_ERR(mxs
->clk
);
139 mxs
->chip
.dev
= &pdev
->dev
;
140 mxs
->chip
.ops
= &mxs_pwm_ops
;
141 mxs
->chip
.of_xlate
= of_pwm_xlate_with_flags
;
142 mxs
->chip
.of_pwm_n_cells
= 3;
145 ret
= of_property_read_u32(np
, "fsl,pwm-number", &mxs
->chip
.npwm
);
147 dev_err(&pdev
->dev
, "failed to get pwm number: %d\n", ret
);
151 ret
= pwmchip_add(&mxs
->chip
);
153 dev_err(&pdev
->dev
, "failed to add pwm chip %d\n", ret
);
157 platform_set_drvdata(pdev
, mxs
);
159 ret
= stmp_reset_block(mxs
->base
);
166 pwmchip_remove(&mxs
->chip
);
170 static int mxs_pwm_remove(struct platform_device
*pdev
)
172 struct mxs_pwm_chip
*mxs
= platform_get_drvdata(pdev
);
174 return pwmchip_remove(&mxs
->chip
);
177 static const struct of_device_id mxs_pwm_dt_ids
[] = {
178 { .compatible
= "fsl,imx23-pwm", },
181 MODULE_DEVICE_TABLE(of
, mxs_pwm_dt_ids
);
183 static struct platform_driver mxs_pwm_driver
= {
186 .of_match_table
= mxs_pwm_dt_ids
,
188 .probe
= mxs_pwm_probe
,
189 .remove
= mxs_pwm_remove
,
191 module_platform_driver(mxs_pwm_driver
);
193 MODULE_ALIAS("platform:mxs-pwm");
194 MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
195 MODULE_DESCRIPTION("Freescale MXS PWM Driver");
196 MODULE_LICENSE("GPL v2");