diff options
Diffstat (limited to 'drivers/input/misc/sparcspkr.c')
-rw-r--r-- | drivers/input/misc/sparcspkr.c | 262 |
1 files changed, 184 insertions, 78 deletions
diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c index fed3c375ccf3..d8765cc93d27 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c | |||
@@ -2,33 +2,69 @@ | |||
2 | * Driver for PC-speaker like devices found on various Sparc systems. | 2 | * Driver for PC-speaker like devices found on various Sparc systems. |
3 | * | 3 | * |
4 | * Copyright (c) 2002 Vojtech Pavlik | 4 | * Copyright (c) 2002 Vojtech Pavlik |
5 | * Copyright (c) 2002, 2006 David S. Miller (davem@davemloft.net) | 5 | * Copyright (c) 2002, 2006, 2008 David S. Miller (davem@davemloft.net) |
6 | */ | 6 | */ |
7 | #include <linux/kernel.h> | 7 | #include <linux/kernel.h> |
8 | #include <linux/module.h> | 8 | #include <linux/module.h> |
9 | #include <linux/init.h> | 9 | #include <linux/init.h> |
10 | #include <linux/input.h> | 10 | #include <linux/input.h> |
11 | #include <linux/platform_device.h> | 11 | #include <linux/of_device.h> |
12 | 12 | ||
13 | #include <asm/io.h> | 13 | #include <asm/io.h> |
14 | #include <asm/ebus.h> | ||
15 | #include <asm/isa.h> | ||
16 | 14 | ||
17 | MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); | 15 | MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); |
18 | MODULE_DESCRIPTION("Sparc Speaker beeper driver"); | 16 | MODULE_DESCRIPTION("Sparc Speaker beeper driver"); |
19 | MODULE_LICENSE("GPL"); | 17 | MODULE_LICENSE("GPL"); |
20 | 18 | ||
19 | struct grover_beep_info { | ||
20 | void __iomem *freq_regs; | ||
21 | void __iomem *enable_reg; | ||
22 | }; | ||
23 | |||
24 | struct bbc_beep_info { | ||
25 | u32 clock_freq; | ||
26 | void __iomem *regs; | ||
27 | }; | ||
28 | |||
21 | struct sparcspkr_state { | 29 | struct sparcspkr_state { |
22 | const char *name; | 30 | const char *name; |
23 | unsigned long iobase; | ||
24 | int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); | 31 | int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); |
25 | spinlock_t lock; | 32 | spinlock_t lock; |
26 | struct input_dev *input_dev; | 33 | struct input_dev *input_dev; |
34 | union { | ||
35 | struct grover_beep_info grover; | ||
36 | struct bbc_beep_info bbc; | ||
37 | } u; | ||
27 | }; | 38 | }; |
28 | 39 | ||
29 | static int ebus_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) | 40 | static u32 bbc_count_to_reg(struct bbc_beep_info *info, unsigned int count) |
41 | { | ||
42 | u32 val, clock_freq = info->clock_freq; | ||
43 | int i; | ||
44 | |||
45 | if (!count) | ||
46 | return 0; | ||
47 | |||
48 | if (count <= clock_freq >> 20) | ||
49 | return 1 << 18; | ||
50 | |||
51 | if (count >= clock_freq >> 12) | ||
52 | return 1 << 10; | ||
53 | |||
54 | val = 1 << 18; | ||
55 | for (i = 19; i >= 11; i--) { | ||
56 | val >>= 1; | ||
57 | if (count <= clock_freq >> i) | ||
58 | break; | ||
59 | } | ||
60 | |||
61 | return val; | ||
62 | } | ||
63 | |||
64 | static int bbc_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) | ||
30 | { | 65 | { |
31 | struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent); | 66 | struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent); |
67 | struct bbc_beep_info *info = &state->u.bbc; | ||
32 | unsigned int count = 0; | 68 | unsigned int count = 0; |
33 | unsigned long flags; | 69 | unsigned long flags; |
34 | 70 | ||
@@ -44,24 +80,29 @@ static int ebus_spkr_event(struct input_dev *dev, unsigned int type, unsigned in | |||
44 | if (value > 20 && value < 32767) | 80 | if (value > 20 && value < 32767) |
45 | count = 1193182 / value; | 81 | count = 1193182 / value; |
46 | 82 | ||
83 | count = bbc_count_to_reg(info, count); | ||
84 | |||
47 | spin_lock_irqsave(&state->lock, flags); | 85 | spin_lock_irqsave(&state->lock, flags); |
48 | 86 | ||
49 | /* EBUS speaker only has on/off state, the frequency does not | 87 | if (count) { |
50 | * appear to be programmable. | 88 | outb(0x01, info->regs + 0); |
51 | */ | 89 | outb(0x00, info->regs + 2); |
52 | if (state->iobase & 0x2UL) | 90 | outb((count >> 16) & 0xff, info->regs + 3); |
53 | outb(!!count, state->iobase); | 91 | outb((count >> 8) & 0xff, info->regs + 4); |
54 | else | 92 | outb(0x00, info->regs + 5); |
55 | outl(!!count, state->iobase); | 93 | } else { |
94 | outb(0x00, info->regs + 0); | ||
95 | } | ||
56 | 96 | ||
57 | spin_unlock_irqrestore(&state->lock, flags); | 97 | spin_unlock_irqrestore(&state->lock, flags); |
58 | 98 | ||
59 | return 0; | 99 | return 0; |
60 | } | 100 | } |
61 | 101 | ||
62 | static int isa_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) | 102 | static int grover_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) |
63 | { | 103 | { |
64 | struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent); | 104 | struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent); |
105 | struct grover_beep_info *info = &state->u.grover; | ||
65 | unsigned int count = 0; | 106 | unsigned int count = 0; |
66 | unsigned long flags; | 107 | unsigned long flags; |
67 | 108 | ||
@@ -81,15 +122,15 @@ static int isa_spkr_event(struct input_dev *dev, unsigned int type, unsigned int | |||
81 | 122 | ||
82 | if (count) { | 123 | if (count) { |
83 | /* enable counter 2 */ | 124 | /* enable counter 2 */ |
84 | outb(inb(state->iobase + 0x61) | 3, state->iobase + 0x61); | 125 | outb(inb(info->enable_reg) | 3, info->enable_reg); |
85 | /* set command for counter 2, 2 byte write */ | 126 | /* set command for counter 2, 2 byte write */ |
86 | outb(0xB6, state->iobase + 0x43); | 127 | outb(0xB6, info->freq_regs + 1); |
87 | /* select desired HZ */ | 128 | /* select desired HZ */ |
88 | outb(count & 0xff, state->iobase + 0x42); | 129 | outb(count & 0xff, info->freq_regs + 0); |
89 | outb((count >> 8) & 0xff, state->iobase + 0x42); | 130 | outb((count >> 8) & 0xff, info->freq_regs + 0); |
90 | } else { | 131 | } else { |
91 | /* disable counter 2 */ | 132 | /* disable counter 2 */ |
92 | outb(inb_p(state->iobase + 0x61) & 0xFC, state->iobase + 0x61); | 133 | outb(inb_p(info->enable_reg) & 0xFC, info->enable_reg); |
93 | } | 134 | } |
94 | 135 | ||
95 | spin_unlock_irqrestore(&state->lock, flags); | 136 | spin_unlock_irqrestore(&state->lock, flags); |
@@ -131,7 +172,7 @@ static int __devinit sparcspkr_probe(struct device *dev) | |||
131 | return 0; | 172 | return 0; |
132 | } | 173 | } |
133 | 174 | ||
134 | static int __devexit sparcspkr_remove(struct of_device *dev) | 175 | static int sparcspkr_shutdown(struct of_device *dev) |
135 | { | 176 | { |
136 | struct sparcspkr_state *state = dev_get_drvdata(&dev->dev); | 177 | struct sparcspkr_state *state = dev_get_drvdata(&dev->dev); |
137 | struct input_dev *input_dev = state->input_dev; | 178 | struct input_dev *input_dev = state->input_dev; |
@@ -139,115 +180,180 @@ static int __devexit sparcspkr_remove(struct of_device *dev) | |||
139 | /* turn off the speaker */ | 180 | /* turn off the speaker */ |
140 | state->event(input_dev, EV_SND, SND_BELL, 0); | 181 | state->event(input_dev, EV_SND, SND_BELL, 0); |
141 | 182 | ||
142 | input_unregister_device(input_dev); | ||
143 | |||
144 | dev_set_drvdata(&dev->dev, NULL); | ||
145 | kfree(state); | ||
146 | |||
147 | return 0; | 183 | return 0; |
148 | } | 184 | } |
149 | 185 | ||
150 | static int sparcspkr_shutdown(struct of_device *dev) | 186 | static int __devinit bbc_beep_probe(struct of_device *op, const struct of_device_id *match) |
151 | { | 187 | { |
152 | struct sparcspkr_state *state = dev_get_drvdata(&dev->dev); | 188 | struct sparcspkr_state *state; |
153 | struct input_dev *input_dev = state->input_dev; | 189 | struct bbc_beep_info *info; |
190 | struct device_node *dp; | ||
191 | int err = -ENOMEM; | ||
154 | 192 | ||
155 | /* turn off the speaker */ | 193 | state = kzalloc(sizeof(*state), GFP_KERNEL); |
156 | state->event(input_dev, EV_SND, SND_BELL, 0); | 194 | if (!state) |
195 | goto out_err; | ||
196 | |||
197 | state->name = "Sparc BBC Speaker"; | ||
198 | state->event = bbc_spkr_event; | ||
199 | spin_lock_init(&state->lock); | ||
200 | |||
201 | dp = of_find_node_by_path("/"); | ||
202 | err = -ENODEV; | ||
203 | if (!dp) | ||
204 | goto out_free; | ||
205 | |||
206 | 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 | |||
215 | dev_set_drvdata(&op->dev, state); | ||
216 | |||
217 | err = sparcspkr_probe(&op->dev); | ||
218 | if (err) | ||
219 | goto out_clear_drvdata; | ||
157 | 220 | ||
158 | return 0; | 221 | return 0; |
222 | |||
223 | out_clear_drvdata: | ||
224 | dev_set_drvdata(&op->dev, NULL); | ||
225 | of_iounmap(&op->resource[0], info->regs, 6); | ||
226 | |||
227 | out_free: | ||
228 | kfree(state); | ||
229 | out_err: | ||
230 | return err; | ||
159 | } | 231 | } |
160 | 232 | ||
161 | static int __devinit ebus_beep_probe(struct of_device *dev, const struct of_device_id *match) | 233 | static int bbc_remove(struct of_device *op) |
162 | { | 234 | { |
163 | struct linux_ebus_device *edev = to_ebus_device(&dev->dev); | 235 | struct sparcspkr_state *state = dev_get_drvdata(&op->dev); |
164 | struct sparcspkr_state *state; | 236 | struct input_dev *input_dev = state->input_dev; |
165 | int err; | 237 | struct bbc_beep_info *info = &state->u.bbc; |
166 | 238 | ||
167 | state = kzalloc(sizeof(*state), GFP_KERNEL); | 239 | /* turn off the speaker */ |
168 | if (!state) | 240 | state->event(input_dev, EV_SND, SND_BELL, 0); |
169 | return -ENOMEM; | ||
170 | 241 | ||
171 | state->name = "Sparc EBUS Speaker"; | 242 | input_unregister_device(input_dev); |
172 | state->iobase = edev->resource[0].start; | ||
173 | state->event = ebus_spkr_event; | ||
174 | spin_lock_init(&state->lock); | ||
175 | 243 | ||
176 | dev_set_drvdata(&dev->dev, state); | 244 | of_iounmap(&op->resource[0], info->regs, 6); |
177 | 245 | ||
178 | err = sparcspkr_probe(&dev->dev); | 246 | dev_set_drvdata(&op->dev, NULL); |
179 | if (err) { | 247 | kfree(state); |
180 | dev_set_drvdata(&dev->dev, NULL); | ||
181 | kfree(state); | ||
182 | } | ||
183 | 248 | ||
184 | return 0; | 249 | return 0; |
185 | } | 250 | } |
186 | 251 | ||
187 | static struct of_device_id ebus_beep_match[] = { | 252 | static struct of_device_id bbc_beep_match[] = { |
188 | { | 253 | { |
189 | .name = "beep", | 254 | .name = "beep", |
255 | .compatible = "SUNW,bbc-beep", | ||
190 | }, | 256 | }, |
191 | {}, | 257 | {}, |
192 | }; | 258 | }; |
193 | 259 | ||
194 | static struct of_platform_driver ebus_beep_driver = { | 260 | static struct of_platform_driver bbc_beep_driver = { |
195 | .name = "beep", | 261 | .name = "bbcbeep", |
196 | .match_table = ebus_beep_match, | 262 | .match_table = bbc_beep_match, |
197 | .probe = ebus_beep_probe, | 263 | .probe = bbc_beep_probe, |
198 | .remove = __devexit_p(sparcspkr_remove), | 264 | .remove = __devexit_p(bbc_remove), |
199 | .shutdown = sparcspkr_shutdown, | 265 | .shutdown = sparcspkr_shutdown, |
200 | }; | 266 | }; |
201 | 267 | ||
202 | static int __devinit isa_beep_probe(struct of_device *dev, const struct of_device_id *match) | 268 | static int __devinit grover_beep_probe(struct of_device *op, const struct of_device_id *match) |
203 | { | 269 | { |
204 | struct sparc_isa_device *idev = to_isa_device(&dev->dev); | ||
205 | struct sparcspkr_state *state; | 270 | struct sparcspkr_state *state; |
206 | int err; | 271 | struct grover_beep_info *info; |
272 | int err = -ENOMEM; | ||
207 | 273 | ||
208 | state = kzalloc(sizeof(*state), GFP_KERNEL); | 274 | state = kzalloc(sizeof(*state), GFP_KERNEL); |
209 | if (!state) | 275 | if (!state) |
210 | return -ENOMEM; | 276 | goto out_err; |
211 | 277 | ||
212 | state->name = "Sparc ISA Speaker"; | 278 | state->name = "Sparc Grover Speaker"; |
213 | state->iobase = idev->resource.start; | 279 | state->event = grover_spkr_event; |
214 | state->event = isa_spkr_event; | ||
215 | spin_lock_init(&state->lock); | 280 | spin_lock_init(&state->lock); |
216 | 281 | ||
217 | dev_set_drvdata(&dev->dev, state); | 282 | info = &state->u.grover; |
283 | info->freq_regs = of_ioremap(&op->resource[2], 0, 2, "grover beep freq"); | ||
284 | if (!info->freq_regs) | ||
285 | goto out_free; | ||
218 | 286 | ||
219 | err = sparcspkr_probe(&dev->dev); | 287 | info->enable_reg = of_ioremap(&op->resource[3], 0, 1, "grover beep enable"); |
220 | if (err) { | 288 | if (!info->enable_reg) |
221 | dev_set_drvdata(&dev->dev, NULL); | 289 | goto out_unmap_freq_regs; |
222 | kfree(state); | 290 | |
223 | } | 291 | dev_set_drvdata(&op->dev, state); |
292 | |||
293 | err = sparcspkr_probe(&op->dev); | ||
294 | if (err) | ||
295 | goto out_clear_drvdata; | ||
296 | |||
297 | return 0; | ||
298 | |||
299 | out_clear_drvdata: | ||
300 | dev_set_drvdata(&op->dev, NULL); | ||
301 | of_iounmap(&op->resource[3], info->enable_reg, 1); | ||
302 | |||
303 | out_unmap_freq_regs: | ||
304 | of_iounmap(&op->resource[2], info->freq_regs, 2); | ||
305 | out_free: | ||
306 | kfree(state); | ||
307 | out_err: | ||
308 | return err; | ||
309 | } | ||
310 | |||
311 | static int grover_remove(struct of_device *op) | ||
312 | { | ||
313 | struct sparcspkr_state *state = dev_get_drvdata(&op->dev); | ||
314 | 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 | |||
325 | dev_set_drvdata(&op->dev, NULL); | ||
326 | kfree(state); | ||
224 | 327 | ||
225 | return 0; | 328 | return 0; |
226 | } | 329 | } |
227 | 330 | ||
228 | static struct of_device_id isa_beep_match[] = { | 331 | static struct of_device_id grover_beep_match[] = { |
229 | { | 332 | { |
230 | .name = "dma", | 333 | .name = "beep", |
334 | .compatible = "SUNW,smbus-beep", | ||
231 | }, | 335 | }, |
232 | {}, | 336 | {}, |
233 | }; | 337 | }; |
234 | 338 | ||
235 | static struct of_platform_driver isa_beep_driver = { | 339 | static struct of_platform_driver grover_beep_driver = { |
236 | .name = "beep", | 340 | .name = "groverbeep", |
237 | .match_table = isa_beep_match, | 341 | .match_table = grover_beep_match, |
238 | .probe = isa_beep_probe, | 342 | .probe = grover_beep_probe, |
239 | .remove = __devexit_p(sparcspkr_remove), | 343 | .remove = __devexit_p(grover_remove), |
240 | .shutdown = sparcspkr_shutdown, | 344 | .shutdown = sparcspkr_shutdown, |
241 | }; | 345 | }; |
242 | 346 | ||
243 | static int __init sparcspkr_init(void) | 347 | static int __init sparcspkr_init(void) |
244 | { | 348 | { |
245 | int err = of_register_driver(&ebus_beep_driver, &ebus_bus_type); | 349 | int err = of_register_driver(&bbc_beep_driver, |
350 | &of_platform_bus_type); | ||
246 | 351 | ||
247 | if (!err) { | 352 | if (!err) { |
248 | err = of_register_driver(&isa_beep_driver, &isa_bus_type); | 353 | err = of_register_driver(&grover_beep_driver, |
354 | &of_platform_bus_type); | ||
249 | if (err) | 355 | if (err) |
250 | of_unregister_driver(&ebus_beep_driver); | 356 | of_unregister_driver(&bbc_beep_driver); |
251 | } | 357 | } |
252 | 358 | ||
253 | return err; | 359 | return err; |
@@ -255,8 +361,8 @@ static int __init sparcspkr_init(void) | |||
255 | 361 | ||
256 | static void __exit sparcspkr_exit(void) | 362 | static void __exit sparcspkr_exit(void) |
257 | { | 363 | { |
258 | of_unregister_driver(&ebus_beep_driver); | 364 | of_unregister_driver(&bbc_beep_driver); |
259 | of_unregister_driver(&isa_beep_driver); | 365 | of_unregister_driver(&grover_beep_driver); |
260 | } | 366 | } |
261 | 367 | ||
262 | module_init(sparcspkr_init); | 368 | module_init(sparcspkr_init); |