blob: fe43e5557ed72b1b8b942b7a3b78d8807a700980 [file] [log] [blame]
Thomas Gleixner09c434b2019-05-19 13:08:20 +01001// SPDX-License-Identifier: GPL-2.0-only
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * Driver for PC-speaker like devices found on various Sparc systems.
4 *
5 * Copyright (c) 2002 Vojtech Pavlik
David S. Miller9c1a5072008-04-26 21:02:21 -07006 * Copyright (c) 2002, 2006, 2008 David S. Miller (davem@davemloft.net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07008#include <linux/kernel.h>
9#include <linux/module.h>
10#include <linux/init.h>
11#include <linux/input.h>
David S. Miller9c1a5072008-04-26 21:02:21 -070012#include <linux/of_device.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090013#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070014
15#include <asm/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016
David S. Millera2bd4fd2006-06-23 01:44:10 -070017MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
Dmitry Torokhov76b7cdd2005-09-15 02:01:51 -050018MODULE_DESCRIPTION("Sparc Speaker beeper driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -070019MODULE_LICENSE("GPL");
20
David S. Miller9c1a5072008-04-26 21:02:21 -070021struct grover_beep_info {
22 void __iomem *freq_regs;
23 void __iomem *enable_reg;
24};
25
26struct bbc_beep_info {
27 u32 clock_freq;
28 void __iomem *regs;
29};
30
David S. Millera2bd4fd2006-06-23 01:44:10 -070031struct sparcspkr_state {
32 const char *name;
David S. Millera2bd4fd2006-06-23 01:44:10 -070033 int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
34 spinlock_t lock;
35 struct input_dev *input_dev;
David S. Miller9c1a5072008-04-26 21:02:21 -070036 union {
37 struct grover_beep_info grover;
38 struct bbc_beep_info bbc;
39 } u;
David S. Millera2bd4fd2006-06-23 01:44:10 -070040};
Linus Torvalds1da177e2005-04-16 15:20:36 -070041
David S. Miller9c1a5072008-04-26 21:02:21 -070042static u32 bbc_count_to_reg(struct bbc_beep_info *info, unsigned int count)
43{
44 u32 val, clock_freq = info->clock_freq;
45 int i;
46
47 if (!count)
48 return 0;
49
50 if (count <= clock_freq >> 20)
51 return 1 << 18;
52
53 if (count >= clock_freq >> 12)
54 return 1 << 10;
55
56 val = 1 << 18;
57 for (i = 19; i >= 11; i--) {
58 val >>= 1;
59 if (count <= clock_freq >> i)
60 break;
61 }
62
63 return val;
64}
65
66static int bbc_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
Linus Torvalds1da177e2005-04-16 15:20:36 -070067{
Dmitry Torokhov293e6392007-04-12 01:35:32 -040068 struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent);
David S. Miller9c1a5072008-04-26 21:02:21 -070069 struct bbc_beep_info *info = &state->u.bbc;
Linus Torvalds1da177e2005-04-16 15:20:36 -070070 unsigned int count = 0;
71 unsigned long flags;
72
73 if (type != EV_SND)
74 return -1;
75
76 switch (code) {
77 case SND_BELL: if (value) value = 1000;
78 case SND_TONE: break;
79 default: return -1;
80 }
81
82 if (value > 20 && value < 32767)
83 count = 1193182 / value;
84
David S. Miller9c1a5072008-04-26 21:02:21 -070085 count = bbc_count_to_reg(info, count);
86
David S. Millera2bd4fd2006-06-23 01:44:10 -070087 spin_lock_irqsave(&state->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -070088
David S. Miller9c1a5072008-04-26 21:02:21 -070089 if (count) {
Sam Ravnborgd6504712014-07-20 13:38:58 +020090 sbus_writeb(0x01, info->regs + 0);
91 sbus_writeb(0x00, info->regs + 2);
92 sbus_writeb((count >> 16) & 0xff, info->regs + 3);
93 sbus_writeb((count >> 8) & 0xff, info->regs + 4);
94 sbus_writeb(0x00, info->regs + 5);
David S. Miller9c1a5072008-04-26 21:02:21 -070095 } else {
Sam Ravnborgd6504712014-07-20 13:38:58 +020096 sbus_writeb(0x00, info->regs + 0);
David S. Miller9c1a5072008-04-26 21:02:21 -070097 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070098
David S. Millera2bd4fd2006-06-23 01:44:10 -070099 spin_unlock_irqrestore(&state->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100
101 return 0;
102}
103
David S. Miller9c1a5072008-04-26 21:02:21 -0700104static int grover_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105{
Dmitry Torokhov293e6392007-04-12 01:35:32 -0400106 struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent);
David S. Miller9c1a5072008-04-26 21:02:21 -0700107 struct grover_beep_info *info = &state->u.grover;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 unsigned int count = 0;
109 unsigned long flags;
110
111 if (type != EV_SND)
112 return -1;
113
114 switch (code) {
115 case SND_BELL: if (value) value = 1000;
116 case SND_TONE: break;
117 default: return -1;
118 }
119
120 if (value > 20 && value < 32767)
121 count = 1193182 / value;
122
David S. Millera2bd4fd2006-06-23 01:44:10 -0700123 spin_lock_irqsave(&state->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124
125 if (count) {
126 /* enable counter 2 */
Sam Ravnborgd6504712014-07-20 13:38:58 +0200127 sbus_writeb(sbus_readb(info->enable_reg) | 3, info->enable_reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 /* set command for counter 2, 2 byte write */
Sam Ravnborgd6504712014-07-20 13:38:58 +0200129 sbus_writeb(0xB6, info->freq_regs + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 /* select desired HZ */
Sam Ravnborgd6504712014-07-20 13:38:58 +0200131 sbus_writeb(count & 0xff, info->freq_regs + 0);
132 sbus_writeb((count >> 8) & 0xff, info->freq_regs + 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 } else {
134 /* disable counter 2 */
Sam Ravnborgd6504712014-07-20 13:38:58 +0200135 sbus_writeb(sbus_readb(info->enable_reg) & 0xFC, info->enable_reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 }
137
David S. Millera2bd4fd2006-06-23 01:44:10 -0700138 spin_unlock_irqrestore(&state->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139
140 return 0;
141}
142
Bill Pemberton5298cc42012-11-23 21:38:25 -0800143static int sparcspkr_probe(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144{
David S. Millera2bd4fd2006-06-23 01:44:10 -0700145 struct sparcspkr_state *state = dev_get_drvdata(dev);
Dmitry Torokhovf5b64072005-12-21 00:52:35 -0500146 struct input_dev *input_dev;
147 int error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148
Dmitry Torokhovf5b64072005-12-21 00:52:35 -0500149 input_dev = input_allocate_device();
150 if (!input_dev)
Dmitry Torokhov76b7cdd2005-09-15 02:01:51 -0500151 return -ENOMEM;
152
David S. Millera2bd4fd2006-06-23 01:44:10 -0700153 input_dev->name = state->name;
Dmitry Torokhovf5b64072005-12-21 00:52:35 -0500154 input_dev->phys = "sparc/input0";
155 input_dev->id.bustype = BUS_ISA;
156 input_dev->id.vendor = 0x001f;
157 input_dev->id.product = 0x0001;
158 input_dev->id.version = 0x0100;
Dmitry Torokhov293e6392007-04-12 01:35:32 -0400159 input_dev->dev.parent = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160
Jiri Slaby7b19ada2007-10-18 23:40:32 -0700161 input_dev->evbit[0] = BIT_MASK(EV_SND);
162 input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163
David S. Millera2bd4fd2006-06-23 01:44:10 -0700164 input_dev->event = state->event;
Dmitry Torokhovf5b64072005-12-21 00:52:35 -0500165
166 error = input_register_device(input_dev);
167 if (error) {
168 input_free_device(input_dev);
169 return error;
170 }
171
David S. Millera2bd4fd2006-06-23 01:44:10 -0700172 state->input_dev = input_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 return 0;
175}
Dmitry Torokhovf5b64072005-12-21 00:52:35 -0500176
Grant Likely4ebb24f2011-02-22 20:01:33 -0700177static void sparcspkr_shutdown(struct platform_device *dev)
Dmitry Torokhovf5b64072005-12-21 00:52:35 -0500178{
Jingoo Han35c4b122013-05-23 09:20:21 -0700179 struct sparcspkr_state *state = platform_get_drvdata(dev);
David S. Millera2bd4fd2006-06-23 01:44:10 -0700180 struct input_dev *input_dev = state->input_dev;
181
Dmitry Torokhovf5b64072005-12-21 00:52:35 -0500182 /* turn off the speaker */
David S. Millera2bd4fd2006-06-23 01:44:10 -0700183 state->event(input_dev, EV_SND, SND_BELL, 0);
Dmitry Torokhovf5b64072005-12-21 00:52:35 -0500184}
185
Bill Pemberton5298cc42012-11-23 21:38:25 -0800186static int bbc_beep_probe(struct platform_device *op)
David S. Millera2bd4fd2006-06-23 01:44:10 -0700187{
David S. Millera2bd4fd2006-06-23 01:44:10 -0700188 struct sparcspkr_state *state;
David S. Miller9c1a5072008-04-26 21:02:21 -0700189 struct bbc_beep_info *info;
190 struct device_node *dp;
191 int err = -ENOMEM;
David S. Millera2bd4fd2006-06-23 01:44:10 -0700192
193 state = kzalloc(sizeof(*state), GFP_KERNEL);
194 if (!state)
David S. Miller9c1a5072008-04-26 21:02:21 -0700195 goto out_err;
David S. Millera2bd4fd2006-06-23 01:44:10 -0700196
David S. Miller9c1a5072008-04-26 21:02:21 -0700197 state->name = "Sparc BBC Speaker";
198 state->event = bbc_spkr_event;
David S. Millera2bd4fd2006-06-23 01:44:10 -0700199 spin_lock_init(&state->lock);
200
David S. Miller9c1a5072008-04-26 21:02:21 -0700201 dp = of_find_node_by_path("/");
202 err = -ENODEV;
203 if (!dp)
204 goto out_free;
David S. Millera2bd4fd2006-06-23 01:44:10 -0700205
David S. Miller9c1a5072008-04-26 21:02:21 -0700206 info = &state->u.bbc;
207 info->clock_freq = of_getintprop_default(dp, "clock-frequency", 0);
208 if (!info->clock_freq)
209 goto out_free;
210
211 info->regs = of_ioremap(&op->resource[0], 0, 6, "bbc beep");
212 if (!info->regs)
213 goto out_free;
214
Jingoo Han35c4b122013-05-23 09:20:21 -0700215 platform_set_drvdata(op, state);
David S. Miller9c1a5072008-04-26 21:02:21 -0700216
217 err = sparcspkr_probe(&op->dev);
218 if (err)
219 goto out_clear_drvdata;
220
221 return 0;
222
223out_clear_drvdata:
David S. Miller9c1a5072008-04-26 21:02:21 -0700224 of_iounmap(&op->resource[0], info->regs, 6);
225
226out_free:
227 kfree(state);
228out_err:
229 return err;
230}
231
Bill Pembertone2619cf2012-11-23 21:50:47 -0800232static int bbc_remove(struct platform_device *op)
David S. Miller9c1a5072008-04-26 21:02:21 -0700233{
Jingoo Han35c4b122013-05-23 09:20:21 -0700234 struct sparcspkr_state *state = platform_get_drvdata(op);
David S. Miller9c1a5072008-04-26 21:02:21 -0700235 struct input_dev *input_dev = state->input_dev;
236 struct bbc_beep_info *info = &state->u.bbc;
237
238 /* turn off the speaker */
239 state->event(input_dev, EV_SND, SND_BELL, 0);
240
241 input_unregister_device(input_dev);
242
243 of_iounmap(&op->resource[0], info->regs, 6);
244
David S. Miller9c1a5072008-04-26 21:02:21 -0700245 kfree(state);
David S. Millera2bd4fd2006-06-23 01:44:10 -0700246
247 return 0;
248}
249
David S. Millerfd098312008-08-31 01:23:17 -0700250static const struct of_device_id bbc_beep_match[] = {
David S. Millera2bd4fd2006-06-23 01:44:10 -0700251 {
252 .name = "beep",
David S. Miller9c1a5072008-04-26 21:02:21 -0700253 .compatible = "SUNW,bbc-beep",
Dmitry Torokhovf5b64072005-12-21 00:52:35 -0500254 },
David S. Millera2bd4fd2006-06-23 01:44:10 -0700255 {},
256};
Luis de Bethencourt26492f12015-09-03 10:50:57 -0700257MODULE_DEVICE_TABLE(of, bbc_beep_match);
David S. Millera2bd4fd2006-06-23 01:44:10 -0700258
Grant Likely4ebb24f2011-02-22 20:01:33 -0700259static struct platform_driver bbc_beep_driver = {
Grant Likely40182942010-04-13 16:13:02 -0700260 .driver = {
261 .name = "bbcbeep",
Grant Likely40182942010-04-13 16:13:02 -0700262 .of_match_table = bbc_beep_match,
263 },
David S. Miller9c1a5072008-04-26 21:02:21 -0700264 .probe = bbc_beep_probe,
Bill Pemberton1cb0aa82012-11-23 21:27:39 -0800265 .remove = bbc_remove,
Dmitry Torokhovf5b64072005-12-21 00:52:35 -0500266 .shutdown = sparcspkr_shutdown,
267};
268
Bill Pemberton5298cc42012-11-23 21:38:25 -0800269static int grover_beep_probe(struct platform_device *op)
Dmitry Torokhovf5b64072005-12-21 00:52:35 -0500270{
David S. Millera2bd4fd2006-06-23 01:44:10 -0700271 struct sparcspkr_state *state;
David S. Miller9c1a5072008-04-26 21:02:21 -0700272 struct grover_beep_info *info;
273 int err = -ENOMEM;
Dmitry Torokhovf5b64072005-12-21 00:52:35 -0500274
David S. Millera2bd4fd2006-06-23 01:44:10 -0700275 state = kzalloc(sizeof(*state), GFP_KERNEL);
276 if (!state)
David S. Miller9c1a5072008-04-26 21:02:21 -0700277 goto out_err;
Dmitry Torokhovf5b64072005-12-21 00:52:35 -0500278
David S. Miller9c1a5072008-04-26 21:02:21 -0700279 state->name = "Sparc Grover Speaker";
280 state->event = grover_spkr_event;
David S. Millera2bd4fd2006-06-23 01:44:10 -0700281 spin_lock_init(&state->lock);
282
David S. Miller9c1a5072008-04-26 21:02:21 -0700283 info = &state->u.grover;
284 info->freq_regs = of_ioremap(&op->resource[2], 0, 2, "grover beep freq");
285 if (!info->freq_regs)
286 goto out_free;
David S. Millera2bd4fd2006-06-23 01:44:10 -0700287
David S. Miller9c1a5072008-04-26 21:02:21 -0700288 info->enable_reg = of_ioremap(&op->resource[3], 0, 1, "grover beep enable");
289 if (!info->enable_reg)
290 goto out_unmap_freq_regs;
291
Jingoo Han35c4b122013-05-23 09:20:21 -0700292 platform_set_drvdata(op, state);
David S. Miller9c1a5072008-04-26 21:02:21 -0700293
294 err = sparcspkr_probe(&op->dev);
295 if (err)
296 goto out_clear_drvdata;
297
298 return 0;
299
300out_clear_drvdata:
David S. Miller9c1a5072008-04-26 21:02:21 -0700301 of_iounmap(&op->resource[3], info->enable_reg, 1);
302
303out_unmap_freq_regs:
304 of_iounmap(&op->resource[2], info->freq_regs, 2);
305out_free:
306 kfree(state);
307out_err:
308 return err;
309}
310
Bill Pembertone2619cf2012-11-23 21:50:47 -0800311static int grover_remove(struct platform_device *op)
David S. Miller9c1a5072008-04-26 21:02:21 -0700312{
Jingoo Han35c4b122013-05-23 09:20:21 -0700313 struct sparcspkr_state *state = platform_get_drvdata(op);
David S. Miller9c1a5072008-04-26 21:02:21 -0700314 struct grover_beep_info *info = &state->u.grover;
315 struct input_dev *input_dev = state->input_dev;
316
317 /* turn off the speaker */
318 state->event(input_dev, EV_SND, SND_BELL, 0);
319
320 input_unregister_device(input_dev);
321
322 of_iounmap(&op->resource[3], info->enable_reg, 1);
323 of_iounmap(&op->resource[2], info->freq_regs, 2);
324
David S. Miller9c1a5072008-04-26 21:02:21 -0700325 kfree(state);
Dmitry Torokhovf5b64072005-12-21 00:52:35 -0500326
Dmitry Torokhovf5b64072005-12-21 00:52:35 -0500327 return 0;
Dmitry Torokhovf5b64072005-12-21 00:52:35 -0500328}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329
David S. Millerfd098312008-08-31 01:23:17 -0700330static const struct of_device_id grover_beep_match[] = {
David S. Millera2bd4fd2006-06-23 01:44:10 -0700331 {
David S. Miller9c1a5072008-04-26 21:02:21 -0700332 .name = "beep",
333 .compatible = "SUNW,smbus-beep",
David S. Millera2bd4fd2006-06-23 01:44:10 -0700334 },
335 {},
336};
Luis de Bethencourt26492f12015-09-03 10:50:57 -0700337MODULE_DEVICE_TABLE(of, grover_beep_match);
David S. Millera2bd4fd2006-06-23 01:44:10 -0700338
Grant Likely4ebb24f2011-02-22 20:01:33 -0700339static struct platform_driver grover_beep_driver = {
Grant Likely40182942010-04-13 16:13:02 -0700340 .driver = {
341 .name = "groverbeep",
Grant Likely40182942010-04-13 16:13:02 -0700342 .of_match_table = grover_beep_match,
343 },
David S. Miller9c1a5072008-04-26 21:02:21 -0700344 .probe = grover_beep_probe,
Bill Pemberton1cb0aa82012-11-23 21:27:39 -0800345 .remove = grover_remove,
David S. Millera2bd4fd2006-06-23 01:44:10 -0700346 .shutdown = sparcspkr_shutdown,
347};
348
Thierry Redingd352c0e2015-12-02 09:28:03 -0800349static struct platform_driver * const drivers[] = {
350 &bbc_beep_driver,
351 &grover_beep_driver,
352};
353
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354static int __init sparcspkr_init(void)
355{
Thierry Redingd352c0e2015-12-02 09:28:03 -0800356 return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357}
358
359static void __exit sparcspkr_exit(void)
360{
Thierry Redingd352c0e2015-12-02 09:28:03 -0800361 platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362}
363
364module_init(sparcspkr_init);
365module_exit(sparcspkr_exit);