File Index
Files and directories
Sign in
FILE

pwm-pxa.c

File preview with syntax highlighting.

linux-6.19.14/drivers/pwm/pwm-pxa.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * drivers/pwm/pwm-pxa.c
  4 *
  5 * simple driver for PWM (Pulse Width Modulator) controller
  6 *
  7 * 2008-02-13	initial version
  8 *		eric miao <eric.miao@marvell.com>
  9 *
 10 * Links to reference manuals for some of the supported PWM chips can be found
 11 * in Documentation/arch/arm/marvell.rst.
 12 *
 13 * Limitations:
 14 * - When PWM is stopped, the current PWM period stops abruptly at the next
 15 *   input clock (PWMCR_SD is set) and the output is driven to inactive.
 16 */
 17
 18#include <linux/mod_devicetable.h>
 19#include <linux/module.h>
 20#include <linux/kernel.h>
 21#include <linux/platform_device.h>
 22#include <linux/slab.h>
 23#include <linux/err.h>
 24#include <linux/clk.h>
 25#include <linux/io.h>
 26#include <linux/pwm.h>
 27#include <linux/of.h>
 28#include <linux/reset.h>
 29
 30#include <asm/div64.h>
 31
 32#define HAS_SECONDARY_PWM	0x10
 33
 34static const struct platform_device_id pwm_id_table[] = {
 35	/*   PWM    has_secondary_pwm? */
 36	{ "pxa25x-pwm", 0 },
 37	{ "pxa27x-pwm", HAS_SECONDARY_PWM },
 38	{ "pxa168-pwm", 0 },
 39	{ "pxa910-pwm", 0 },
 40	{ },
 41};
 42MODULE_DEVICE_TABLE(platform, pwm_id_table);
 43
 44/* PWM registers and bits definitions */
 45#define PWMCR		(0x00)
 46#define PWMDCR		(0x04)
 47#define PWMPCR		(0x08)
 48
 49#define PWMCR_SD	(1 << 6)
 50#define PWMDCR_FD	(1 << 10)
 51
 52struct pxa_pwm_chip {
 53	struct device	*dev;
 54
 55	struct clk	*clk;
 56	void __iomem	*mmio_base;
 57};
 58
 59static inline struct pxa_pwm_chip *to_pxa_pwm_chip(struct pwm_chip *chip)
 60{
 61	return pwmchip_get_drvdata(chip);
 62}
 63
 64/*
 65 * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE
 66 * duty_ns   = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
 67 */
 68static int pxa_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 69			  u64 duty_ns, u64 period_ns)
 70{
 71	struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
 72	unsigned long long c;
 73	unsigned long period_cycles, prescale, pv, dc;
 74	unsigned long offset;
 75
 76	offset = pwm->hwpwm ? 0x10 : 0;
 77
 78	c = clk_get_rate(pc->clk);
 79	c = c * period_ns;
 80	do_div(c, 1000000000);
 81	period_cycles = c;
 82
 83	if (period_cycles < 1)
 84		period_cycles = 1;
 85	prescale = (period_cycles - 1) / 1024;
 86	pv = period_cycles / (prescale + 1) - 1;
 87
 88	if (prescale > 63)
 89		return -EINVAL;
 90
 91	if (duty_ns == period_ns)
 92		dc = PWMDCR_FD;
 93	else
 94		dc = mul_u64_u64_div_u64(pv + 1, duty_ns, period_ns);
 95
 96	writel(prescale | PWMCR_SD, pc->mmio_base + offset + PWMCR);
 97	writel(dc, pc->mmio_base + offset + PWMDCR);
 98	writel(pv, pc->mmio_base + offset + PWMPCR);
 99
100	return 0;
101}
102
103static int pxa_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
104			 const struct pwm_state *state)
105{
106	struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
107	u64 duty_cycle;
108	int err;
109
110	if (state->polarity != PWM_POLARITY_NORMAL)
111		return -EINVAL;
112
113	err = clk_prepare_enable(pc->clk);
114	if (err)
115		return err;
116
117	duty_cycle = state->enabled ? state->duty_cycle : 0;
118
119	err = pxa_pwm_config(chip, pwm, duty_cycle, state->period);
120	if (err) {
121		clk_disable_unprepare(pc->clk);
122		return err;
123	}
124
125	if (state->enabled && !pwm->state.enabled)
126		return 0;
127
128	clk_disable_unprepare(pc->clk);
129
130	if (!state->enabled && pwm->state.enabled)
131		clk_disable_unprepare(pc->clk);
132
133	return 0;
134}
135
136static const struct pwm_ops pxa_pwm_ops = {
137	.apply = pxa_pwm_apply,
138};
139
140#ifdef CONFIG_OF
141/*
142 * Device tree users must create one device instance for each PWM channel.
143 * Hence we dispense with the HAS_SECONDARY_PWM and "tell" the original driver
144 * code that this is a single channel pxa25x-pwm.  Currently all devices are
145 * supported identically.
146 */
147static const struct of_device_id pwm_of_match[] = {
148	{ .compatible = "marvell,pxa250-pwm", .data = &pwm_id_table[0]},
149	{ .compatible = "marvell,pxa270-pwm", .data = &pwm_id_table[0]},
150	{ .compatible = "marvell,pxa168-pwm", .data = &pwm_id_table[0]},
151	{ .compatible = "marvell,pxa910-pwm", .data = &pwm_id_table[0]},
152	{ }
153};
154MODULE_DEVICE_TABLE(of, pwm_of_match);
155#else
156#define pwm_of_match NULL
157#endif
158
159static int pwm_probe(struct platform_device *pdev)
160{
161	const struct platform_device_id *id = platform_get_device_id(pdev);
162	struct pwm_chip *chip;
163	struct pxa_pwm_chip *pc;
164	struct device *dev = &pdev->dev;
165	struct reset_control *rst;
166	int ret = 0;
167
168	if (IS_ENABLED(CONFIG_OF) && id == NULL)
169		id = of_device_get_match_data(dev);
170
171	if (id == NULL)
172		return -EINVAL;
173
174	chip = devm_pwmchip_alloc(dev, (id->driver_data & HAS_SECONDARY_PWM) ? 2 : 1,
175				  sizeof(*pc));
176	if (IS_ERR(chip))
177		return PTR_ERR(chip);
178	pc = to_pxa_pwm_chip(chip);
179
180	pc->clk = devm_clk_get(dev, NULL);
181	if (IS_ERR(pc->clk))
182		return dev_err_probe(dev, PTR_ERR(pc->clk), "Failed to get clock\n");
183
184	rst = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL);
185	if (IS_ERR(rst))
186		return PTR_ERR(rst);
187
188	chip->ops = &pxa_pwm_ops;
189
190	if (IS_ENABLED(CONFIG_OF))
191		chip->of_xlate = of_pwm_single_xlate;
192
193	pc->mmio_base = devm_platform_ioremap_resource(pdev, 0);
194	if (IS_ERR(pc->mmio_base))
195		return PTR_ERR(pc->mmio_base);
196
197	ret = devm_pwmchip_add(dev, chip);
198	if (ret < 0)
199		return dev_err_probe(dev, ret, "pwmchip_add() failed\n");
200
201	return 0;
202}
203
204static struct platform_driver pwm_driver = {
205	.driver		= {
206		.name	= "pxa25x-pwm",
207		.of_match_table = pwm_of_match,
208	},
209	.probe		= pwm_probe,
210	.id_table	= pwm_id_table,
211};
212
213module_platform_driver(pwm_driver);
214
215MODULE_DESCRIPTION("PXA Pulse Width Modulator driver");
216MODULE_LICENSE("GPL v2");
~/ Read-only file preview.