1 // SPDX-License-Identifier: GPL-2.0
5 * Raspberry Pi RP1 PWM.
7 * Copyright © 2023 Raspberry Pi Ltd.
9 * Author: Naushir Patuck (naush@raspberrypi.com)
11 * Based on the pwm-bcm2835 driver by:
12 * Bart Tanghe <bart.tanghe@thomasmore.be>
15 #include <linux/bitops.h>
16 #include <linux/clk.h>
17 #include <linux/err.h>
19 #include <linux/module.h>
21 #include <linux/platform_device.h>
22 #include <linux/pwm.h>
24 #define PWM_GLOBAL_CTRL 0x000
25 #define PWM_CHANNEL_CTRL(x) (0x014 + ((x) * 16))
26 #define PWM_RANGE(x) (0x018 + ((x) * 16))
27 #define PWM_DUTY(x) (0x020 + ((x) * 16))
29 /* 8:FIFO_POP_MASK + 0:Trailing edge M/S modulation */
30 #define PWM_CHANNEL_DEFAULT (BIT(8) + BIT(0))
31 #define PWM_CHANNEL_ENABLE(x) BIT(x)
32 #define PWM_POLARITY BIT(3)
33 #define SET_UPDATE BIT(31)
34 #define PWM_MODE_MASK GENMASK(1, 0)
43 static inline struct rp1_pwm *to_rp1_pwm(struct pwm_chip *chip)
45 return container_of(chip, struct rp1_pwm, chip);
48 static void rp1_pwm_apply_config(struct pwm_chip *chip, struct pwm_device *pwm)
50 struct rp1_pwm *pc = to_rp1_pwm(chip);
53 value = readl(pc->base + PWM_GLOBAL_CTRL);
55 writel(value, pc->base + PWM_GLOBAL_CTRL);
58 static int rp1_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
60 struct rp1_pwm *pc = to_rp1_pwm(chip);
62 writel(PWM_CHANNEL_DEFAULT, pc->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
66 static void rp1_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
68 struct rp1_pwm *pc = to_rp1_pwm(chip);
71 value = readl(pc->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
72 value &= ~PWM_MODE_MASK;
73 writel(value, pc->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
74 rp1_pwm_apply_config(chip, pwm);
77 static int rp1_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
78 const struct pwm_state *state)
80 struct rp1_pwm *pc = to_rp1_pwm(chip);
81 unsigned long clk_rate = clk_get_rate(pc->clk);
82 unsigned long clk_period;
86 dev_err(pc->dev, "failed to get clock rate\n");
91 clk_period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, clk_rate);
93 writel(DIV_ROUND_CLOSEST(state->duty_cycle, clk_period),
94 pc->base + PWM_DUTY(pwm->hwpwm));
97 writel(DIV_ROUND_CLOSEST(state->period, clk_period),
98 pc->base + PWM_RANGE(pwm->hwpwm));
101 value = readl(pc->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
102 if (state->polarity == PWM_POLARITY_NORMAL)
103 value &= ~PWM_POLARITY;
105 value |= PWM_POLARITY;
106 writel(value, pc->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
109 value = readl(pc->base + PWM_GLOBAL_CTRL);
111 value |= PWM_CHANNEL_ENABLE(pwm->hwpwm);
113 value &= ~PWM_CHANNEL_ENABLE(pwm->hwpwm);
114 writel(value, pc->base + PWM_GLOBAL_CTRL);
116 rp1_pwm_apply_config(chip, pwm);
121 static const struct pwm_ops rp1_pwm_ops = {
122 .request = rp1_pwm_request,
123 .free = rp1_pwm_free,
124 .apply = rp1_pwm_apply,
125 .owner = THIS_MODULE,
128 static int rp1_pwm_probe(struct platform_device *pdev)
131 struct resource *res;
134 pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
138 pc->dev = &pdev->dev;
140 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
141 pc->base = devm_ioremap_resource(&pdev->dev, res);
142 if (IS_ERR(pc->base))
143 return PTR_ERR(pc->base);
145 pc->clk = devm_clk_get(&pdev->dev, NULL);
147 return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk),
148 "clock not found\n");
150 ret = clk_prepare_enable(pc->clk);
154 pc->chip.dev = &pdev->dev;
155 pc->chip.ops = &rp1_pwm_ops;
158 pc->chip.of_xlate = of_pwm_xlate_with_flags;
159 pc->chip.of_pwm_n_cells = 3;
161 platform_set_drvdata(pdev, pc);
163 ret = pwmchip_add(&pc->chip);
170 clk_disable_unprepare(pc->clk);
174 static int rp1_pwm_remove(struct platform_device *pdev)
176 struct rp1_pwm *pc = platform_get_drvdata(pdev);
178 clk_disable_unprepare(pc->clk);
180 pwmchip_remove(&pc->chip);
185 static const struct of_device_id rp1_pwm_of_match[] = {
186 { .compatible = "raspberrypi,rp1-pwm" },
189 MODULE_DEVICE_TABLE(of, rp1_pwm_of_match);
191 static struct platform_driver rp1_pwm_driver = {
194 .of_match_table = rp1_pwm_of_match,
196 .probe = rp1_pwm_probe,
197 .remove = rp1_pwm_remove,
199 module_platform_driver(rp1_pwm_driver);
201 MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com");
202 MODULE_DESCRIPTION("RP1 PWM driver");
203 MODULE_LICENSE("GPL");