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