1 // SPDX-License-Identifier: GPL-2.0
3 * simple driver for PWM (Pulse Width Modulator) controller
5 * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com>
8 #include <linux/bitfield.h>
9 #include <linux/bitops.h>
10 #include <linux/clk.h>
11 #include <linux/delay.h>
12 #include <linux/err.h>
14 #include <linux/kernel.h>
15 #include <linux/module.h>
17 #include <linux/platform_device.h>
18 #include <linux/pwm.h>
19 #include <linux/slab.h>
21 #define MX1_PWMC 0x00 /* PWM Control Register */
22 #define MX1_PWMS 0x04 /* PWM Sample Register */
23 #define MX1_PWMP 0x08 /* PWM Period Register */
25 #define MX1_PWMC_EN BIT(4)
27 struct pwm_imx1_chip
{
30 void __iomem
*mmio_base
;
33 static inline struct pwm_imx1_chip
*to_pwm_imx1_chip(struct pwm_chip
*chip
)
35 return pwmchip_get_drvdata(chip
);
38 static int pwm_imx1_clk_prepare_enable(struct pwm_chip
*chip
)
40 struct pwm_imx1_chip
*imx
= to_pwm_imx1_chip(chip
);
43 ret
= clk_prepare_enable(imx
->clk_ipg
);
47 ret
= clk_prepare_enable(imx
->clk_per
);
49 clk_disable_unprepare(imx
->clk_ipg
);
56 static void pwm_imx1_clk_disable_unprepare(struct pwm_chip
*chip
)
58 struct pwm_imx1_chip
*imx
= to_pwm_imx1_chip(chip
);
60 clk_disable_unprepare(imx
->clk_per
);
61 clk_disable_unprepare(imx
->clk_ipg
);
64 static int pwm_imx1_config(struct pwm_chip
*chip
,
65 struct pwm_device
*pwm
, u64 duty_ns
, u64 period_ns
)
67 struct pwm_imx1_chip
*imx
= to_pwm_imx1_chip(chip
);
71 * The PWM subsystem allows for exact frequencies. However,
72 * I cannot connect a scope on my device to the PWM line and
73 * thus cannot provide the program the PWM controller
74 * exactly. Instead, I'm relying on the fact that the
75 * Bootloader (u-boot or WinCE+haret) has programmed the PWM
76 * function group already. So I'll just modify the PWM sample
77 * register to follow the ratio of duty_ns vs. period_ns
80 * This is good enough for programming the brightness of
83 * The real implementation would divide PERCLK[0] first by
84 * both the prescaler (/1 .. /128) and then by CLKSEL
87 max
= readl(imx
->mmio_base
+ MX1_PWMP
);
88 p
= mul_u64_u64_div_u64(max
, duty_ns
, period_ns
);
90 writel(max
- p
, imx
->mmio_base
+ MX1_PWMS
);
95 static int pwm_imx1_enable(struct pwm_chip
*chip
, struct pwm_device
*pwm
)
97 struct pwm_imx1_chip
*imx
= to_pwm_imx1_chip(chip
);
101 ret
= pwm_imx1_clk_prepare_enable(chip
);
105 value
= readl(imx
->mmio_base
+ MX1_PWMC
);
106 value
|= MX1_PWMC_EN
;
107 writel(value
, imx
->mmio_base
+ MX1_PWMC
);
112 static void pwm_imx1_disable(struct pwm_chip
*chip
, struct pwm_device
*pwm
)
114 struct pwm_imx1_chip
*imx
= to_pwm_imx1_chip(chip
);
117 value
= readl(imx
->mmio_base
+ MX1_PWMC
);
118 value
&= ~MX1_PWMC_EN
;
119 writel(value
, imx
->mmio_base
+ MX1_PWMC
);
121 pwm_imx1_clk_disable_unprepare(chip
);
124 static int pwm_imx1_apply(struct pwm_chip
*chip
, struct pwm_device
*pwm
,
125 const struct pwm_state
*state
)
129 if (state
->polarity
!= PWM_POLARITY_NORMAL
)
132 if (!state
->enabled
) {
133 if (pwm
->state
.enabled
)
134 pwm_imx1_disable(chip
, pwm
);
139 err
= pwm_imx1_config(chip
, pwm
, state
->duty_cycle
, state
->period
);
143 if (!pwm
->state
.enabled
)
144 return pwm_imx1_enable(chip
, pwm
);
149 static const struct pwm_ops pwm_imx1_ops
= {
150 .apply
= pwm_imx1_apply
,
153 static const struct of_device_id pwm_imx1_dt_ids
[] = {
154 { .compatible
= "fsl,imx1-pwm", },
157 MODULE_DEVICE_TABLE(of
, pwm_imx1_dt_ids
);
159 static int pwm_imx1_probe(struct platform_device
*pdev
)
161 struct pwm_chip
*chip
;
162 struct pwm_imx1_chip
*imx
;
164 chip
= devm_pwmchip_alloc(&pdev
->dev
, 1, sizeof(*imx
));
166 return PTR_ERR(chip
);
167 imx
= to_pwm_imx1_chip(chip
);
169 imx
->clk_ipg
= devm_clk_get(&pdev
->dev
, "ipg");
170 if (IS_ERR(imx
->clk_ipg
))
171 return dev_err_probe(&pdev
->dev
, PTR_ERR(imx
->clk_ipg
),
172 "getting ipg clock failed\n");
174 imx
->clk_per
= devm_clk_get(&pdev
->dev
, "per");
175 if (IS_ERR(imx
->clk_per
))
176 return dev_err_probe(&pdev
->dev
, PTR_ERR(imx
->clk_per
),
177 "failed to get peripheral clock\n");
179 chip
->ops
= &pwm_imx1_ops
;
181 imx
->mmio_base
= devm_platform_ioremap_resource(pdev
, 0);
182 if (IS_ERR(imx
->mmio_base
))
183 return PTR_ERR(imx
->mmio_base
);
185 return devm_pwmchip_add(&pdev
->dev
, chip
);
188 static struct platform_driver pwm_imx1_driver
= {
191 .of_match_table
= pwm_imx1_dt_ids
,
193 .probe
= pwm_imx1_probe
,
195 module_platform_driver(pwm_imx1_driver
);
197 MODULE_DESCRIPTION("i.MX1 and i.MX21 Pulse Width Modulator driver");
198 MODULE_LICENSE("GPL v2");
199 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");