1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright 2014 Bart Tanghe <bart.tanghe@thomasmore.be> |
4 | */ |
5 | |
6 | #include <linux/clk.h> |
7 | #include <linux/err.h> |
8 | #include <linux/io.h> |
9 | #include <linux/module.h> |
10 | #include <linux/of.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/pwm.h> |
13 | |
14 | #define PWM_CONTROL 0x000 |
15 | #define PWM_CONTROL_SHIFT(x) ((x) * 8) |
16 | #define PWM_CONTROL_MASK 0xff |
17 | #define PWM_MODE 0x80 /* set timer in PWM mode */ |
18 | #define PWM_ENABLE (1 << 0) |
19 | #define PWM_POLARITY (1 << 4) |
20 | |
21 | #define PERIOD(x) (((x) * 0x10) + 0x10) |
22 | #define DUTY(x) (((x) * 0x10) + 0x14) |
23 | |
24 | #define PERIOD_MIN 0x2 |
25 | |
26 | struct bcm2835_pwm { |
27 | void __iomem *base; |
28 | struct clk *clk; |
29 | unsigned long rate; |
30 | }; |
31 | |
32 | static inline struct bcm2835_pwm *to_bcm2835_pwm(struct pwm_chip *chip) |
33 | { |
34 | return pwmchip_get_drvdata(chip); |
35 | } |
36 | |
37 | static int bcm2835_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) |
38 | { |
39 | struct bcm2835_pwm *pc = to_bcm2835_pwm(chip); |
40 | u32 value; |
41 | |
42 | value = readl(addr: pc->base + PWM_CONTROL); |
43 | value &= ~(PWM_CONTROL_MASK << PWM_CONTROL_SHIFT(pwm->hwpwm)); |
44 | value |= (PWM_MODE << PWM_CONTROL_SHIFT(pwm->hwpwm)); |
45 | writel(val: value, addr: pc->base + PWM_CONTROL); |
46 | |
47 | return 0; |
48 | } |
49 | |
50 | static void bcm2835_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) |
51 | { |
52 | struct bcm2835_pwm *pc = to_bcm2835_pwm(chip); |
53 | u32 value; |
54 | |
55 | value = readl(addr: pc->base + PWM_CONTROL); |
56 | value &= ~(PWM_CONTROL_MASK << PWM_CONTROL_SHIFT(pwm->hwpwm)); |
57 | writel(val: value, addr: pc->base + PWM_CONTROL); |
58 | } |
59 | |
60 | static int bcm2835_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, |
61 | const struct pwm_state *state) |
62 | { |
63 | |
64 | struct bcm2835_pwm *pc = to_bcm2835_pwm(chip); |
65 | unsigned long long period_cycles; |
66 | u64 max_period; |
67 | |
68 | u32 val; |
69 | |
70 | /* |
71 | * period_cycles must be a 32 bit value, so period * rate / NSEC_PER_SEC |
72 | * must be <= U32_MAX. As U32_MAX * NSEC_PER_SEC < U64_MAX the |
73 | * multiplication period * rate doesn't overflow. |
74 | * To calculate the maximal possible period that guarantees the |
75 | * above inequality: |
76 | * |
77 | * round(period * rate / NSEC_PER_SEC) <= U32_MAX |
78 | * <=> period * rate / NSEC_PER_SEC < U32_MAX + 0.5 |
79 | * <=> period * rate < (U32_MAX + 0.5) * NSEC_PER_SEC |
80 | * <=> period < ((U32_MAX + 0.5) * NSEC_PER_SEC) / rate |
81 | * <=> period < ((U32_MAX * NSEC_PER_SEC + NSEC_PER_SEC/2) / rate |
82 | * <=> period <= ceil((U32_MAX * NSEC_PER_SEC + NSEC_PER_SEC/2) / rate) - 1 |
83 | */ |
84 | max_period = DIV_ROUND_UP_ULL((u64)U32_MAX * NSEC_PER_SEC + NSEC_PER_SEC / 2, pc->rate) - 1; |
85 | |
86 | if (state->period > max_period) |
87 | return -EINVAL; |
88 | |
89 | /* set period */ |
90 | period_cycles = DIV_ROUND_CLOSEST_ULL(state->period * pc->rate, NSEC_PER_SEC); |
91 | |
92 | /* don't accept a period that is too small */ |
93 | if (period_cycles < PERIOD_MIN) |
94 | return -EINVAL; |
95 | |
96 | writel(val: period_cycles, addr: pc->base + PERIOD(pwm->hwpwm)); |
97 | |
98 | /* set duty cycle */ |
99 | val = DIV_ROUND_CLOSEST_ULL(state->duty_cycle * pc->rate, NSEC_PER_SEC); |
100 | writel(val, addr: pc->base + DUTY(pwm->hwpwm)); |
101 | |
102 | /* set polarity */ |
103 | val = readl(addr: pc->base + PWM_CONTROL); |
104 | |
105 | if (state->polarity == PWM_POLARITY_NORMAL) |
106 | val &= ~(PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm)); |
107 | else |
108 | val |= PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm); |
109 | |
110 | /* enable/disable */ |
111 | if (state->enabled) |
112 | val |= PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm); |
113 | else |
114 | val &= ~(PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm)); |
115 | |
116 | writel(val, addr: pc->base + PWM_CONTROL); |
117 | |
118 | return 0; |
119 | } |
120 | |
121 | static const struct pwm_ops bcm2835_pwm_ops = { |
122 | .request = bcm2835_pwm_request, |
123 | .free = bcm2835_pwm_free, |
124 | .apply = bcm2835_pwm_apply, |
125 | }; |
126 | |
127 | static void devm_clk_rate_exclusive_put(void *data) |
128 | { |
129 | struct clk *clk = data; |
130 | |
131 | clk_rate_exclusive_put(clk); |
132 | } |
133 | |
134 | static int bcm2835_pwm_probe(struct platform_device *pdev) |
135 | { |
136 | struct pwm_chip *chip; |
137 | struct bcm2835_pwm *pc; |
138 | int ret; |
139 | |
140 | chip = devm_pwmchip_alloc(parent: &pdev->dev, npwm: 2, sizeof_priv: sizeof(*pc)); |
141 | if (IS_ERR(ptr: chip)) |
142 | return PTR_ERR(ptr: chip); |
143 | pc = to_bcm2835_pwm(chip); |
144 | |
145 | pc->base = devm_platform_ioremap_resource(pdev, index: 0); |
146 | if (IS_ERR(ptr: pc->base)) |
147 | return PTR_ERR(ptr: pc->base); |
148 | |
149 | pc->clk = devm_clk_get_enabled(dev: &pdev->dev, NULL); |
150 | if (IS_ERR(ptr: pc->clk)) |
151 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: pc->clk), |
152 | fmt: "clock not found\n" ); |
153 | |
154 | ret = clk_rate_exclusive_get(clk: pc->clk); |
155 | if (ret) |
156 | return dev_err_probe(dev: &pdev->dev, err: ret, |
157 | fmt: "fail to get exclusive rate\n" ); |
158 | |
159 | ret = devm_add_action_or_reset(&pdev->dev, devm_clk_rate_exclusive_put, |
160 | pc->clk); |
161 | if (ret) |
162 | return ret; |
163 | |
164 | pc->rate = clk_get_rate(clk: pc->clk); |
165 | if (!pc->rate) |
166 | return dev_err_probe(dev: &pdev->dev, err: -EINVAL, |
167 | fmt: "failed to get clock rate\n" ); |
168 | |
169 | chip->ops = &bcm2835_pwm_ops; |
170 | chip->atomic = true; |
171 | |
172 | platform_set_drvdata(pdev, data: pc); |
173 | |
174 | ret = devm_pwmchip_add(&pdev->dev, chip); |
175 | if (ret < 0) |
176 | return dev_err_probe(dev: &pdev->dev, err: ret, |
177 | fmt: "failed to add pwmchip\n" ); |
178 | |
179 | return 0; |
180 | } |
181 | |
182 | static int bcm2835_pwm_suspend(struct device *dev) |
183 | { |
184 | struct bcm2835_pwm *pc = dev_get_drvdata(dev); |
185 | |
186 | clk_disable_unprepare(clk: pc->clk); |
187 | |
188 | return 0; |
189 | } |
190 | |
191 | static int bcm2835_pwm_resume(struct device *dev) |
192 | { |
193 | struct bcm2835_pwm *pc = dev_get_drvdata(dev); |
194 | |
195 | return clk_prepare_enable(clk: pc->clk); |
196 | } |
197 | |
198 | static DEFINE_SIMPLE_DEV_PM_OPS(bcm2835_pwm_pm_ops, bcm2835_pwm_suspend, |
199 | bcm2835_pwm_resume); |
200 | |
201 | static const struct of_device_id bcm2835_pwm_of_match[] = { |
202 | { .compatible = "brcm,bcm2835-pwm" , }, |
203 | { /* sentinel */ } |
204 | }; |
205 | MODULE_DEVICE_TABLE(of, bcm2835_pwm_of_match); |
206 | |
207 | static struct platform_driver bcm2835_pwm_driver = { |
208 | .driver = { |
209 | .name = "bcm2835-pwm" , |
210 | .of_match_table = bcm2835_pwm_of_match, |
211 | .pm = pm_ptr(&bcm2835_pwm_pm_ops), |
212 | }, |
213 | .probe = bcm2835_pwm_probe, |
214 | }; |
215 | module_platform_driver(bcm2835_pwm_driver); |
216 | |
217 | MODULE_AUTHOR("Bart Tanghe <bart.tanghe@thomasmore.be>" ); |
218 | MODULE_DESCRIPTION("Broadcom BCM2835 PWM driver" ); |
219 | MODULE_LICENSE("GPL v2" ); |
220 | |