1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Driver for PC-speaker like devices found on various Sparc systems. |
4 | * |
5 | * Copyright (c) 2002 Vojtech Pavlik |
6 | * Copyright (c) 2002, 2006, 2008 David S. Miller (davem@davemloft.net) |
7 | */ |
8 | #include <linux/kernel.h> |
9 | #include <linux/module.h> |
10 | #include <linux/init.h> |
11 | #include <linux/input.h> |
12 | #include <linux/of.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/slab.h> |
15 | |
16 | #include <asm/io.h> |
17 | |
18 | MODULE_AUTHOR("David S. Miller <davem@davemloft.net>" ); |
19 | MODULE_DESCRIPTION("Sparc Speaker beeper driver" ); |
20 | MODULE_LICENSE("GPL" ); |
21 | |
22 | struct grover_beep_info { |
23 | void __iomem *freq_regs; |
24 | void __iomem *enable_reg; |
25 | }; |
26 | |
27 | struct bbc_beep_info { |
28 | u32 clock_freq; |
29 | void __iomem *regs; |
30 | }; |
31 | |
32 | struct sparcspkr_state { |
33 | const char *name; |
34 | int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); |
35 | spinlock_t lock; |
36 | struct input_dev *input_dev; |
37 | union { |
38 | struct grover_beep_info grover; |
39 | struct bbc_beep_info bbc; |
40 | } u; |
41 | }; |
42 | |
43 | static u32 bbc_count_to_reg(struct bbc_beep_info *info, unsigned int count) |
44 | { |
45 | u32 val, clock_freq = info->clock_freq; |
46 | int i; |
47 | |
48 | if (!count) |
49 | return 0; |
50 | |
51 | if (count <= clock_freq >> 20) |
52 | return 1 << 18; |
53 | |
54 | if (count >= clock_freq >> 12) |
55 | return 1 << 10; |
56 | |
57 | val = 1 << 18; |
58 | for (i = 19; i >= 11; i--) { |
59 | val >>= 1; |
60 | if (count <= clock_freq >> i) |
61 | break; |
62 | } |
63 | |
64 | return val; |
65 | } |
66 | |
67 | static int bbc_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) |
68 | { |
69 | struct sparcspkr_state *state = dev_get_drvdata(dev: dev->dev.parent); |
70 | struct bbc_beep_info *info = &state->u.bbc; |
71 | unsigned int count = 0; |
72 | unsigned long flags; |
73 | |
74 | if (type != EV_SND) |
75 | return -1; |
76 | |
77 | switch (code) { |
78 | case SND_BELL: if (value) value = 1000; |
79 | case SND_TONE: break; |
80 | default: return -1; |
81 | } |
82 | |
83 | if (value > 20 && value < 32767) |
84 | count = 1193182 / value; |
85 | |
86 | count = bbc_count_to_reg(info, count); |
87 | |
88 | spin_lock_irqsave(&state->lock, flags); |
89 | |
90 | if (count) { |
91 | sbus_writeb(0x01, info->regs + 0); |
92 | sbus_writeb(0x00, info->regs + 2); |
93 | sbus_writeb((count >> 16) & 0xff, info->regs + 3); |
94 | sbus_writeb((count >> 8) & 0xff, info->regs + 4); |
95 | sbus_writeb(0x00, info->regs + 5); |
96 | } else { |
97 | sbus_writeb(0x00, info->regs + 0); |
98 | } |
99 | |
100 | spin_unlock_irqrestore(lock: &state->lock, flags); |
101 | |
102 | return 0; |
103 | } |
104 | |
105 | static int grover_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) |
106 | { |
107 | struct sparcspkr_state *state = dev_get_drvdata(dev: dev->dev.parent); |
108 | struct grover_beep_info *info = &state->u.grover; |
109 | unsigned int count = 0; |
110 | unsigned long flags; |
111 | |
112 | if (type != EV_SND) |
113 | return -1; |
114 | |
115 | switch (code) { |
116 | case SND_BELL: if (value) value = 1000; |
117 | case SND_TONE: break; |
118 | default: return -1; |
119 | } |
120 | |
121 | if (value > 20 && value < 32767) |
122 | count = 1193182 / value; |
123 | |
124 | spin_lock_irqsave(&state->lock, flags); |
125 | |
126 | if (count) { |
127 | /* enable counter 2 */ |
128 | sbus_writeb(sbus_readb(info->enable_reg) | 3, info->enable_reg); |
129 | /* set command for counter 2, 2 byte write */ |
130 | sbus_writeb(0xB6, info->freq_regs + 1); |
131 | /* select desired HZ */ |
132 | sbus_writeb(count & 0xff, info->freq_regs + 0); |
133 | sbus_writeb((count >> 8) & 0xff, info->freq_regs + 0); |
134 | } else { |
135 | /* disable counter 2 */ |
136 | sbus_writeb(sbus_readb(info->enable_reg) & 0xFC, info->enable_reg); |
137 | } |
138 | |
139 | spin_unlock_irqrestore(lock: &state->lock, flags); |
140 | |
141 | return 0; |
142 | } |
143 | |
144 | static int sparcspkr_probe(struct device *dev) |
145 | { |
146 | struct sparcspkr_state *state = dev_get_drvdata(dev); |
147 | struct input_dev *input_dev; |
148 | int error; |
149 | |
150 | input_dev = input_allocate_device(); |
151 | if (!input_dev) |
152 | return -ENOMEM; |
153 | |
154 | input_dev->name = state->name; |
155 | input_dev->phys = "sparc/input0" ; |
156 | input_dev->id.bustype = BUS_ISA; |
157 | input_dev->id.vendor = 0x001f; |
158 | input_dev->id.product = 0x0001; |
159 | input_dev->id.version = 0x0100; |
160 | input_dev->dev.parent = dev; |
161 | |
162 | input_dev->evbit[0] = BIT_MASK(EV_SND); |
163 | input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); |
164 | |
165 | input_dev->event = state->event; |
166 | |
167 | error = input_register_device(input_dev); |
168 | if (error) { |
169 | input_free_device(dev: input_dev); |
170 | return error; |
171 | } |
172 | |
173 | state->input_dev = input_dev; |
174 | |
175 | return 0; |
176 | } |
177 | |
178 | static void sparcspkr_shutdown(struct platform_device *dev) |
179 | { |
180 | struct sparcspkr_state *state = platform_get_drvdata(pdev: dev); |
181 | struct input_dev *input_dev = state->input_dev; |
182 | |
183 | /* turn off the speaker */ |
184 | state->event(input_dev, EV_SND, SND_BELL, 0); |
185 | } |
186 | |
187 | static int bbc_beep_probe(struct platform_device *op) |
188 | { |
189 | struct sparcspkr_state *state; |
190 | struct bbc_beep_info *info; |
191 | struct device_node *dp; |
192 | int err = -ENOMEM; |
193 | |
194 | state = kzalloc(size: sizeof(*state), GFP_KERNEL); |
195 | if (!state) |
196 | goto out_err; |
197 | |
198 | state->name = "Sparc BBC Speaker" ; |
199 | state->event = bbc_spkr_event; |
200 | spin_lock_init(&state->lock); |
201 | |
202 | dp = of_find_node_by_path(path: "/" ); |
203 | err = -ENODEV; |
204 | if (!dp) |
205 | goto out_free; |
206 | |
207 | info = &state->u.bbc; |
208 | info->clock_freq = of_getintprop_default(dp, "clock-frequency" , 0); |
209 | of_node_put(node: dp); |
210 | if (!info->clock_freq) |
211 | goto out_free; |
212 | |
213 | info->regs = of_ioremap(&op->resource[0], 0, 6, "bbc beep" ); |
214 | if (!info->regs) |
215 | goto out_free; |
216 | |
217 | platform_set_drvdata(pdev: op, data: state); |
218 | |
219 | err = sparcspkr_probe(dev: &op->dev); |
220 | if (err) |
221 | goto out_clear_drvdata; |
222 | |
223 | return 0; |
224 | |
225 | out_clear_drvdata: |
226 | of_iounmap(&op->resource[0], info->regs, 6); |
227 | |
228 | out_free: |
229 | kfree(objp: state); |
230 | out_err: |
231 | return err; |
232 | } |
233 | |
234 | static int bbc_remove(struct platform_device *op) |
235 | { |
236 | struct sparcspkr_state *state = platform_get_drvdata(pdev: op); |
237 | struct input_dev *input_dev = state->input_dev; |
238 | struct bbc_beep_info *info = &state->u.bbc; |
239 | |
240 | /* turn off the speaker */ |
241 | state->event(input_dev, EV_SND, SND_BELL, 0); |
242 | |
243 | input_unregister_device(input_dev); |
244 | |
245 | of_iounmap(&op->resource[0], info->regs, 6); |
246 | |
247 | kfree(objp: state); |
248 | |
249 | return 0; |
250 | } |
251 | |
252 | static const struct of_device_id bbc_beep_match[] = { |
253 | { |
254 | .name = "beep" , |
255 | .compatible = "SUNW,bbc-beep" , |
256 | }, |
257 | {}, |
258 | }; |
259 | MODULE_DEVICE_TABLE(of, bbc_beep_match); |
260 | |
261 | static struct platform_driver bbc_beep_driver = { |
262 | .driver = { |
263 | .name = "bbcbeep" , |
264 | .of_match_table = bbc_beep_match, |
265 | }, |
266 | .probe = bbc_beep_probe, |
267 | .remove = bbc_remove, |
268 | .shutdown = sparcspkr_shutdown, |
269 | }; |
270 | |
271 | static int grover_beep_probe(struct platform_device *op) |
272 | { |
273 | struct sparcspkr_state *state; |
274 | struct grover_beep_info *info; |
275 | int err = -ENOMEM; |
276 | |
277 | state = kzalloc(size: sizeof(*state), GFP_KERNEL); |
278 | if (!state) |
279 | goto out_err; |
280 | |
281 | state->name = "Sparc Grover Speaker" ; |
282 | state->event = grover_spkr_event; |
283 | spin_lock_init(&state->lock); |
284 | |
285 | info = &state->u.grover; |
286 | info->freq_regs = of_ioremap(&op->resource[2], 0, 2, "grover beep freq" ); |
287 | if (!info->freq_regs) |
288 | goto out_free; |
289 | |
290 | info->enable_reg = of_ioremap(&op->resource[3], 0, 1, "grover beep enable" ); |
291 | if (!info->enable_reg) |
292 | goto out_unmap_freq_regs; |
293 | |
294 | platform_set_drvdata(pdev: op, data: state); |
295 | |
296 | err = sparcspkr_probe(dev: &op->dev); |
297 | if (err) |
298 | goto out_clear_drvdata; |
299 | |
300 | return 0; |
301 | |
302 | out_clear_drvdata: |
303 | of_iounmap(&op->resource[3], info->enable_reg, 1); |
304 | |
305 | out_unmap_freq_regs: |
306 | of_iounmap(&op->resource[2], info->freq_regs, 2); |
307 | out_free: |
308 | kfree(objp: state); |
309 | out_err: |
310 | return err; |
311 | } |
312 | |
313 | static int grover_remove(struct platform_device *op) |
314 | { |
315 | struct sparcspkr_state *state = platform_get_drvdata(pdev: op); |
316 | struct grover_beep_info *info = &state->u.grover; |
317 | struct input_dev *input_dev = state->input_dev; |
318 | |
319 | /* turn off the speaker */ |
320 | state->event(input_dev, EV_SND, SND_BELL, 0); |
321 | |
322 | input_unregister_device(input_dev); |
323 | |
324 | of_iounmap(&op->resource[3], info->enable_reg, 1); |
325 | of_iounmap(&op->resource[2], info->freq_regs, 2); |
326 | |
327 | kfree(objp: state); |
328 | |
329 | return 0; |
330 | } |
331 | |
332 | static const struct of_device_id grover_beep_match[] = { |
333 | { |
334 | .name = "beep" , |
335 | .compatible = "SUNW,smbus-beep" , |
336 | }, |
337 | {}, |
338 | }; |
339 | MODULE_DEVICE_TABLE(of, grover_beep_match); |
340 | |
341 | static struct platform_driver grover_beep_driver = { |
342 | .driver = { |
343 | .name = "groverbeep" , |
344 | .of_match_table = grover_beep_match, |
345 | }, |
346 | .probe = grover_beep_probe, |
347 | .remove = grover_remove, |
348 | .shutdown = sparcspkr_shutdown, |
349 | }; |
350 | |
351 | static struct platform_driver * const drivers[] = { |
352 | &bbc_beep_driver, |
353 | &grover_beep_driver, |
354 | }; |
355 | |
356 | static int __init sparcspkr_init(void) |
357 | { |
358 | return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); |
359 | } |
360 | |
361 | static void __exit sparcspkr_exit(void) |
362 | { |
363 | platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); |
364 | } |
365 | |
366 | module_init(sparcspkr_init); |
367 | module_exit(sparcspkr_exit); |
368 | |