Commit | Line | Data |
---|---|---|
654c293e BD |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Renesas RZ/G2L Multi-Function Timer Pulse Unit 3(MTU3a) Core driver | |
4 | * | |
5 | * Copyright (C) 2023 Renesas Electronics Corporation | |
6 | */ | |
7 | ||
8 | #include <linux/bitfield.h> | |
9 | #include <linux/clk.h> | |
10 | #include <linux/interrupt.h> | |
11 | #include <linux/irq.h> | |
12 | #include <linux/mfd/core.h> | |
13 | #include <linux/mfd/rz-mtu3.h> | |
edae0946 | 14 | #include <linux/module.h> |
dc0c386e | 15 | #include <linux/mod_devicetable.h> |
dc0c386e | 16 | #include <linux/platform_device.h> |
654c293e BD |
17 | #include <linux/reset.h> |
18 | #include <linux/spinlock.h> | |
19 | ||
20 | #include "rz-mtu3.h" | |
21 | ||
22 | struct rz_mtu3_priv { | |
23 | void __iomem *mmio; | |
24 | struct reset_control *rstc; | |
d92df6fb | 25 | spinlock_t lock; |
654c293e BD |
26 | }; |
27 | ||
28 | /******* MTU3 registers (original offset is +0x1200) *******/ | |
29 | static const unsigned long rz_mtu3_8bit_ch_reg_offs[][13] = { | |
30 | [RZ_MTU3_CHAN_0] = MTU_8BIT_CH_0(0x104, 0x090, 0x100, 0x128, 0x101, 0x102, 0x103, 0x126), | |
31 | [RZ_MTU3_CHAN_1] = MTU_8BIT_CH_1_2(0x184, 0x091, 0x185, 0x180, 0x194, 0x181, 0x182), | |
32 | [RZ_MTU3_CHAN_2] = MTU_8BIT_CH_1_2(0x204, 0x092, 0x205, 0x200, 0x20c, 0x201, 0x202), | |
33 | [RZ_MTU3_CHAN_3] = MTU_8BIT_CH_3_4_6_7(0x008, 0x093, 0x02c, 0x000, 0x04c, 0x002, 0x004, 0x005, 0x038), | |
34 | [RZ_MTU3_CHAN_4] = MTU_8BIT_CH_3_4_6_7(0x009, 0x094, 0x02d, 0x001, 0x04d, 0x003, 0x006, 0x007, 0x039), | |
35 | [RZ_MTU3_CHAN_5] = MTU_8BIT_CH_5(0xab2, 0x1eb, 0xab4, 0xab6, 0xa84, 0xa85, 0xa86, 0xa94, 0xa95, 0xa96, 0xaa4, 0xaa5, 0xaa6), | |
36 | [RZ_MTU3_CHAN_6] = MTU_8BIT_CH_3_4_6_7(0x808, 0x893, 0x82c, 0x800, 0x84c, 0x802, 0x804, 0x805, 0x838), | |
37 | [RZ_MTU3_CHAN_7] = MTU_8BIT_CH_3_4_6_7(0x809, 0x894, 0x82d, 0x801, 0x84d, 0x803, 0x806, 0x807, 0x839), | |
38 | [RZ_MTU3_CHAN_8] = MTU_8BIT_CH_8(0x404, 0x098, 0x400, 0x406, 0x401, 0x402, 0x403) | |
39 | }; | |
40 | ||
41 | static const unsigned long rz_mtu3_16bit_ch_reg_offs[][12] = { | |
42 | [RZ_MTU3_CHAN_0] = MTU_16BIT_CH_0(0x106, 0x108, 0x10a, 0x10c, 0x10e, 0x120, 0x122), | |
43 | [RZ_MTU3_CHAN_1] = MTU_16BIT_CH_1_2(0x186, 0x188, 0x18a), | |
44 | [RZ_MTU3_CHAN_2] = MTU_16BIT_CH_1_2(0x206, 0x208, 0x20a), | |
45 | [RZ_MTU3_CHAN_3] = MTU_16BIT_CH_3_6(0x010, 0x018, 0x01a, 0x024, 0x026, 0x072), | |
46 | [RZ_MTU3_CHAN_4] = MTU_16BIT_CH_4_7(0x012, 0x01c, 0x01e, 0x028, 0x2a, 0x074, 0x076, 0x040, 0x044, 0x046, 0x048, 0x04a), | |
47 | [RZ_MTU3_CHAN_5] = MTU_16BIT_CH_5(0xa80, 0xa82, 0xa90, 0xa92, 0xaa0, 0xaa2), | |
48 | [RZ_MTU3_CHAN_6] = MTU_16BIT_CH_3_6(0x810, 0x818, 0x81a, 0x824, 0x826, 0x872), | |
49 | [RZ_MTU3_CHAN_7] = MTU_16BIT_CH_4_7(0x812, 0x81c, 0x81e, 0x828, 0x82a, 0x874, 0x876, 0x840, 0x844, 0x846, 0x848, 0x84a) | |
50 | }; | |
51 | ||
52 | static const unsigned long rz_mtu3_32bit_ch_reg_offs[][5] = { | |
53 | [RZ_MTU3_CHAN_1] = MTU_32BIT_CH_1(0x1a0, 0x1a4, 0x1a8), | |
54 | [RZ_MTU3_CHAN_8] = MTU_32BIT_CH_8(0x408, 0x40c, 0x410, 0x414, 0x418) | |
55 | }; | |
56 | ||
57 | static bool rz_mtu3_is_16bit_shared_reg(u16 offset) | |
58 | { | |
59 | return (offset == RZ_MTU3_TDDRA || offset == RZ_MTU3_TDDRB || | |
60 | offset == RZ_MTU3_TCDRA || offset == RZ_MTU3_TCDRB || | |
61 | offset == RZ_MTU3_TCBRA || offset == RZ_MTU3_TCBRB || | |
62 | offset == RZ_MTU3_TCNTSA || offset == RZ_MTU3_TCNTSB); | |
63 | } | |
64 | ||
65 | u16 rz_mtu3_shared_reg_read(struct rz_mtu3_channel *ch, u16 offset) | |
66 | { | |
67 | struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); | |
68 | struct rz_mtu3_priv *priv = mtu->priv_data; | |
69 | ||
70 | if (rz_mtu3_is_16bit_shared_reg(offset)) | |
71 | return readw(priv->mmio + offset); | |
72 | else | |
73 | return readb(priv->mmio + offset); | |
74 | } | |
75 | EXPORT_SYMBOL_GPL(rz_mtu3_shared_reg_read); | |
76 | ||
77 | u8 rz_mtu3_8bit_ch_read(struct rz_mtu3_channel *ch, u16 offset) | |
78 | { | |
79 | struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); | |
80 | struct rz_mtu3_priv *priv = mtu->priv_data; | |
81 | u16 ch_offs; | |
82 | ||
83 | ch_offs = rz_mtu3_8bit_ch_reg_offs[ch->channel_number][offset]; | |
84 | ||
85 | return readb(priv->mmio + ch_offs); | |
86 | } | |
87 | EXPORT_SYMBOL_GPL(rz_mtu3_8bit_ch_read); | |
88 | ||
89 | u16 rz_mtu3_16bit_ch_read(struct rz_mtu3_channel *ch, u16 offset) | |
90 | { | |
91 | struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); | |
92 | struct rz_mtu3_priv *priv = mtu->priv_data; | |
93 | u16 ch_offs; | |
94 | ||
95 | /* MTU8 doesn't have 16-bit registers */ | |
96 | if (ch->channel_number == RZ_MTU3_CHAN_8) | |
97 | return 0; | |
98 | ||
99 | ch_offs = rz_mtu3_16bit_ch_reg_offs[ch->channel_number][offset]; | |
100 | ||
101 | return readw(priv->mmio + ch_offs); | |
102 | } | |
103 | EXPORT_SYMBOL_GPL(rz_mtu3_16bit_ch_read); | |
104 | ||
105 | u32 rz_mtu3_32bit_ch_read(struct rz_mtu3_channel *ch, u16 offset) | |
106 | { | |
107 | struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); | |
108 | struct rz_mtu3_priv *priv = mtu->priv_data; | |
109 | u16 ch_offs; | |
110 | ||
111 | if (ch->channel_number != RZ_MTU3_CHAN_1 && ch->channel_number != RZ_MTU3_CHAN_8) | |
112 | return 0; | |
113 | ||
114 | ch_offs = rz_mtu3_32bit_ch_reg_offs[ch->channel_number][offset]; | |
115 | ||
116 | return readl(priv->mmio + ch_offs); | |
117 | } | |
118 | EXPORT_SYMBOL_GPL(rz_mtu3_32bit_ch_read); | |
119 | ||
120 | void rz_mtu3_8bit_ch_write(struct rz_mtu3_channel *ch, u16 offset, u8 val) | |
121 | { | |
122 | struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); | |
123 | struct rz_mtu3_priv *priv = mtu->priv_data; | |
124 | u16 ch_offs; | |
125 | ||
126 | ch_offs = rz_mtu3_8bit_ch_reg_offs[ch->channel_number][offset]; | |
127 | writeb(val, priv->mmio + ch_offs); | |
128 | } | |
129 | EXPORT_SYMBOL_GPL(rz_mtu3_8bit_ch_write); | |
130 | ||
131 | void rz_mtu3_16bit_ch_write(struct rz_mtu3_channel *ch, u16 offset, u16 val) | |
132 | { | |
133 | struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); | |
134 | struct rz_mtu3_priv *priv = mtu->priv_data; | |
135 | u16 ch_offs; | |
136 | ||
137 | /* MTU8 doesn't have 16-bit registers */ | |
138 | if (ch->channel_number == RZ_MTU3_CHAN_8) | |
139 | return; | |
140 | ||
141 | ch_offs = rz_mtu3_16bit_ch_reg_offs[ch->channel_number][offset]; | |
142 | writew(val, priv->mmio + ch_offs); | |
143 | } | |
144 | EXPORT_SYMBOL_GPL(rz_mtu3_16bit_ch_write); | |
145 | ||
146 | void rz_mtu3_32bit_ch_write(struct rz_mtu3_channel *ch, u16 offset, u32 val) | |
147 | { | |
148 | struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); | |
149 | struct rz_mtu3_priv *priv = mtu->priv_data; | |
150 | u16 ch_offs; | |
151 | ||
152 | if (ch->channel_number != RZ_MTU3_CHAN_1 && ch->channel_number != RZ_MTU3_CHAN_8) | |
153 | return; | |
154 | ||
155 | ch_offs = rz_mtu3_32bit_ch_reg_offs[ch->channel_number][offset]; | |
156 | writel(val, priv->mmio + ch_offs); | |
157 | } | |
158 | EXPORT_SYMBOL_GPL(rz_mtu3_32bit_ch_write); | |
159 | ||
160 | void rz_mtu3_shared_reg_write(struct rz_mtu3_channel *ch, u16 offset, u16 value) | |
161 | { | |
162 | struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); | |
163 | struct rz_mtu3_priv *priv = mtu->priv_data; | |
164 | ||
165 | if (rz_mtu3_is_16bit_shared_reg(offset)) | |
166 | writew(value, priv->mmio + offset); | |
167 | else | |
168 | writeb((u8)value, priv->mmio + offset); | |
169 | } | |
170 | EXPORT_SYMBOL_GPL(rz_mtu3_shared_reg_write); | |
171 | ||
172 | void rz_mtu3_shared_reg_update_bit(struct rz_mtu3_channel *ch, u16 offset, | |
173 | u16 pos, u8 val) | |
174 | { | |
175 | struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); | |
176 | struct rz_mtu3_priv *priv = mtu->priv_data; | |
177 | unsigned long tmdr, flags; | |
178 | ||
d92df6fb | 179 | spin_lock_irqsave(&priv->lock, flags); |
654c293e BD |
180 | tmdr = rz_mtu3_shared_reg_read(ch, offset); |
181 | __assign_bit(pos, &tmdr, !!val); | |
182 | rz_mtu3_shared_reg_write(ch, offset, tmdr); | |
d92df6fb | 183 | spin_unlock_irqrestore(&priv->lock, flags); |
654c293e BD |
184 | } |
185 | EXPORT_SYMBOL_GPL(rz_mtu3_shared_reg_update_bit); | |
186 | ||
187 | static u16 rz_mtu3_get_tstr_offset(struct rz_mtu3_channel *ch) | |
188 | { | |
189 | u16 offset; | |
190 | ||
191 | switch (ch->channel_number) { | |
192 | case RZ_MTU3_CHAN_0: | |
193 | case RZ_MTU3_CHAN_1: | |
194 | case RZ_MTU3_CHAN_2: | |
195 | case RZ_MTU3_CHAN_3: | |
196 | case RZ_MTU3_CHAN_4: | |
197 | case RZ_MTU3_CHAN_8: | |
198 | offset = RZ_MTU3_TSTRA; | |
199 | break; | |
200 | case RZ_MTU3_CHAN_5: | |
201 | offset = RZ_MTU3_TSTR; | |
202 | break; | |
203 | case RZ_MTU3_CHAN_6: | |
204 | case RZ_MTU3_CHAN_7: | |
205 | offset = RZ_MTU3_TSTRB; | |
206 | break; | |
207 | default: | |
208 | offset = 0; | |
209 | break; | |
210 | } | |
211 | ||
212 | return offset; | |
213 | } | |
214 | ||
215 | static u8 rz_mtu3_get_tstr_bit_pos(struct rz_mtu3_channel *ch) | |
216 | { | |
217 | u8 bitpos; | |
218 | ||
219 | switch (ch->channel_number) { | |
220 | case RZ_MTU3_CHAN_0: | |
221 | case RZ_MTU3_CHAN_1: | |
222 | case RZ_MTU3_CHAN_2: | |
223 | case RZ_MTU3_CHAN_6: | |
224 | case RZ_MTU3_CHAN_7: | |
225 | bitpos = ch->channel_number; | |
226 | break; | |
227 | case RZ_MTU3_CHAN_3: | |
228 | bitpos = 6; | |
229 | break; | |
230 | case RZ_MTU3_CHAN_4: | |
231 | bitpos = 7; | |
232 | break; | |
233 | case RZ_MTU3_CHAN_5: | |
234 | bitpos = 2; | |
235 | break; | |
236 | case RZ_MTU3_CHAN_8: | |
237 | bitpos = 3; | |
238 | break; | |
239 | default: | |
240 | bitpos = 0; | |
241 | break; | |
242 | } | |
243 | ||
244 | return bitpos; | |
245 | } | |
246 | ||
247 | static void rz_mtu3_start_stop_ch(struct rz_mtu3_channel *ch, bool start) | |
248 | { | |
249 | struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); | |
250 | struct rz_mtu3_priv *priv = mtu->priv_data; | |
251 | unsigned long flags, tstr; | |
252 | u16 offset; | |
253 | u8 bitpos; | |
254 | ||
a160d128 BD |
255 | offset = rz_mtu3_get_tstr_offset(ch); |
256 | bitpos = rz_mtu3_get_tstr_bit_pos(ch); | |
257 | ||
654c293e | 258 | /* start stop register shared by multiple timer channels */ |
d92df6fb | 259 | spin_lock_irqsave(&priv->lock, flags); |
654c293e | 260 | |
654c293e BD |
261 | tstr = rz_mtu3_shared_reg_read(ch, offset); |
262 | __assign_bit(bitpos, &tstr, start); | |
263 | rz_mtu3_shared_reg_write(ch, offset, tstr); | |
264 | ||
d92df6fb | 265 | spin_unlock_irqrestore(&priv->lock, flags); |
654c293e BD |
266 | } |
267 | ||
268 | bool rz_mtu3_is_enabled(struct rz_mtu3_channel *ch) | |
269 | { | |
270 | struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); | |
271 | struct rz_mtu3_priv *priv = mtu->priv_data; | |
272 | unsigned long flags, tstr; | |
654c293e BD |
273 | u16 offset; |
274 | u8 bitpos; | |
275 | ||
654c293e BD |
276 | offset = rz_mtu3_get_tstr_offset(ch); |
277 | bitpos = rz_mtu3_get_tstr_bit_pos(ch); | |
654c293e | 278 | |
a160d128 | 279 | /* start stop register shared by multiple timer channels */ |
d92df6fb | 280 | spin_lock_irqsave(&priv->lock, flags); |
a160d128 | 281 | tstr = rz_mtu3_shared_reg_read(ch, offset); |
d92df6fb | 282 | spin_unlock_irqrestore(&priv->lock, flags); |
654c293e | 283 | |
a160d128 | 284 | return tstr & BIT(bitpos); |
654c293e BD |
285 | } |
286 | EXPORT_SYMBOL_GPL(rz_mtu3_is_enabled); | |
287 | ||
288 | int rz_mtu3_enable(struct rz_mtu3_channel *ch) | |
289 | { | |
290 | /* enable channel */ | |
291 | rz_mtu3_start_stop_ch(ch, true); | |
292 | ||
293 | return 0; | |
294 | } | |
295 | EXPORT_SYMBOL_GPL(rz_mtu3_enable); | |
296 | ||
297 | void rz_mtu3_disable(struct rz_mtu3_channel *ch) | |
298 | { | |
299 | /* disable channel */ | |
300 | rz_mtu3_start_stop_ch(ch, false); | |
301 | } | |
302 | EXPORT_SYMBOL_GPL(rz_mtu3_disable); | |
303 | ||
304 | static void rz_mtu3_reset_assert(void *data) | |
305 | { | |
306 | struct rz_mtu3 *mtu = dev_get_drvdata(data); | |
307 | struct rz_mtu3_priv *priv = mtu->priv_data; | |
308 | ||
309 | mfd_remove_devices(data); | |
310 | reset_control_assert(priv->rstc); | |
311 | } | |
312 | ||
313 | static const struct mfd_cell rz_mtu3_devs[] = { | |
314 | { | |
315 | .name = "rz-mtu3-counter", | |
316 | }, | |
317 | { | |
318 | .name = "pwm-rz-mtu3", | |
319 | }, | |
320 | }; | |
321 | ||
322 | static int rz_mtu3_probe(struct platform_device *pdev) | |
323 | { | |
324 | struct rz_mtu3_priv *priv; | |
325 | struct rz_mtu3 *ddata; | |
326 | unsigned int i; | |
327 | int ret; | |
328 | ||
329 | ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); | |
330 | if (!ddata) | |
331 | return -ENOMEM; | |
332 | ||
333 | ddata->priv_data = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); | |
334 | if (!ddata->priv_data) | |
335 | return -ENOMEM; | |
336 | ||
337 | priv = ddata->priv_data; | |
338 | ||
339 | priv->mmio = devm_platform_ioremap_resource(pdev, 0); | |
340 | if (IS_ERR(priv->mmio)) | |
341 | return PTR_ERR(priv->mmio); | |
342 | ||
343 | priv->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); | |
344 | if (IS_ERR(priv->rstc)) | |
345 | return PTR_ERR(priv->rstc); | |
346 | ||
347 | ddata->clk = devm_clk_get(&pdev->dev, NULL); | |
348 | if (IS_ERR(ddata->clk)) | |
349 | return PTR_ERR(ddata->clk); | |
350 | ||
351 | reset_control_deassert(priv->rstc); | |
d92df6fb | 352 | spin_lock_init(&priv->lock); |
654c293e BD |
353 | platform_set_drvdata(pdev, ddata); |
354 | ||
355 | for (i = 0; i < RZ_MTU_NUM_CHANNELS; i++) { | |
356 | ddata->channels[i].channel_number = i; | |
357 | ddata->channels[i].is_busy = false; | |
358 | mutex_init(&ddata->channels[i].lock); | |
359 | } | |
360 | ||
361 | ret = mfd_add_devices(&pdev->dev, 0, rz_mtu3_devs, | |
362 | ARRAY_SIZE(rz_mtu3_devs), NULL, 0, NULL); | |
363 | if (ret < 0) | |
364 | goto err_assert; | |
365 | ||
366 | return devm_add_action_or_reset(&pdev->dev, rz_mtu3_reset_assert, | |
367 | &pdev->dev); | |
368 | ||
369 | err_assert: | |
370 | reset_control_assert(priv->rstc); | |
371 | return ret; | |
372 | } | |
373 | ||
374 | static const struct of_device_id rz_mtu3_of_match[] = { | |
375 | { .compatible = "renesas,rz-mtu3", }, | |
376 | { /* sentinel */ } | |
377 | }; | |
378 | MODULE_DEVICE_TABLE(of, rz_mtu3_of_match); | |
379 | ||
380 | static struct platform_driver rz_mtu3_driver = { | |
381 | .probe = rz_mtu3_probe, | |
382 | .driver = { | |
383 | .name = "rz-mtu3", | |
384 | .of_match_table = rz_mtu3_of_match, | |
385 | }, | |
386 | }; | |
387 | module_platform_driver(rz_mtu3_driver); | |
388 | ||
389 | MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>"); | |
390 | MODULE_DESCRIPTION("Renesas RZ/G2L MTU3a Core Driver"); | |
391 | MODULE_LICENSE("GPL"); |