diff options
-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 fed3c375ccf..d8765cc93d2 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c @@ -2,33 +2,69 @@ * Driver for PC-speaker like devices found on various Sparc systems. * * Copyright (c) 2002 Vojtech Pavlik - * Copyright (c) 2002, 2006 David S. Miller (davem@davemloft.net) + * Copyright (c) 2002, 2006, 2008 David S. Miller (davem@davemloft.net) */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/input.h> -#include <linux/platform_device.h> +#include <linux/of_device.h> #include <asm/io.h> -#include <asm/ebus.h> -#include <asm/isa.h> MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); MODULE_DESCRIPTION("Sparc Speaker beeper driver"); MODULE_LICENSE("GPL"); +struct grover_beep_info { + void __iomem *freq_regs; + void __iomem *enable_reg; +}; + +struct bbc_beep_info { + u32 clock_freq; + void __iomem *regs; +}; + struct sparcspkr_state { const char *name; - unsigned long iobase; int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); spinlock_t lock; struct input_dev *input_dev; + union { + struct grover_beep_info grover; + struct bbc_beep_info bbc; + } u; }; -static int ebus_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +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) { struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent); + struct bbc_beep_info *info = &state->u.bbc; unsigned int count = 0; unsigned long flags; @@ -44,24 +80,29 @@ static int ebus_spkr_event(struct input_dev *dev, unsigned int type, unsigned in if (value > 20 && value < 32767) count = 1193182 / value; + count = bbc_count_to_reg(info, count); + spin_lock_irqsave(&state->lock, flags); - /* EBUS speaker only has on/off state, the frequency does not - * appear to be programmable. - */ - if (state->iobase & 0x2UL) - outb(!!count, state->iobase); - else - outl(!!count, state->iobase); + 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); + } spin_unlock_irqrestore(&state->lock, flags); return 0; } -static int isa_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +static int grover_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent); + struct grover_beep_info *info = &state->u.grover; unsigned int count = 0; unsigned long flags; @@ -81,15 +122,15 @@ static int isa_spkr_event(struct input_dev *dev, unsigned int type, unsigned int if (count) { /* enable counter 2 */ - outb(inb(state->iobase + 0x61) | 3, state->iobase + 0x61); + outb(inb(info->enable_reg) | 3, info->enable_reg); /* set command for counter 2, 2 byte write */ - outb(0xB6, state->iobase + 0x43); + outb(0xB6, info->freq_regs + 1); /* select desired HZ */ - outb(count & 0xff, state->iobase + 0x42); - outb((count >> 8) & 0xff, state->iobase + 0x42); + outb(count & 0xff, info->freq_regs + 0); + outb((count >> 8) & 0xff, info->freq_regs + 0); } else { /* disable counter 2 */ - outb(inb_p(state->iobase + 0x61) & 0xFC, state->iobase + 0x61); + outb(inb_p(info->enable_reg) & 0xFC, info->enable_reg); } spin_unlock_irqrestore(&state->lock, flags); @@ -131,7 +172,7 @@ static int __devinit sparcspkr_probe(struct device *dev) return 0; } -static int __devexit sparcspkr_remove(struct of_device *dev) +static int sparcspkr_shutdown(struct of_device *dev) { struct sparcspkr_state *state = dev_get_drvdata(&dev->dev); struct input_dev *input_dev = state->input_dev; @@ -139,115 +180,180 @@ static int __devexit sparcspkr_remove(struct of_device *dev) /* turn off the speaker */ state->event(input_dev, EV_SND, SND_BELL, 0); - input_unregister_device(input_dev); - - dev_set_drvdata(&dev->dev, NULL); - kfree(state); - return 0; } -static int sparcspkr_shutdown(struct of_device *dev) +static int __devinit bbc_beep_probe(struct of_device *op, const struct of_device_id *match) { - struct sparcspkr_state *state = dev_get_drvdata(&dev->dev); - struct input_dev *input_dev = state->input_dev; + struct sparcspkr_state *state; + struct bbc_beep_info *info; + struct device_node *dp; + int err = -ENOMEM; - /* turn off the speaker */ - state->event(input_dev, EV_SND, SND_BELL, 0); + 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; return 0; + +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; } -static int __devinit ebus_beep_probe(struct of_device *dev, const struct of_device_id *match) +static int bbc_remove(struct of_device *op) { - struct linux_ebus_device *edev = to_ebus_device(&dev->dev); - struct sparcspkr_state *state; - int err; + 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; - state = kzalloc(sizeof(*state), GFP_KERNEL); - if (!state) - return -ENOMEM; + /* turn off the speaker */ + state->event(input_dev, EV_SND, SND_BELL, 0); - state->name = "Sparc EBUS Speaker"; - state->iobase = edev->resource[0].start; - state->event = ebus_spkr_event; - spin_lock_init(&state->lock); + input_unregister_device(input_dev); - dev_set_drvdata(&dev->dev, state); + of_iounmap(&op->resource[0], info->regs, 6); - err = sparcspkr_probe(&dev->dev); - if (err) { - dev_set_drvdata(&dev->dev, NULL); - kfree(state); - } + dev_set_drvdata(&op->dev, NULL); + kfree(state); return 0; } -static struct of_device_id ebus_beep_match[] = { +static struct of_device_id bbc_beep_match[] = { { .name = "beep", + .compatible = "SUNW,bbc-beep", }, {}, }; -static struct of_platform_driver ebus_beep_driver = { - .name = "beep", - .match_table = ebus_beep_match, - .probe = ebus_beep_probe, - .remove = __devexit_p(sparcspkr_remove), +static struct of_platform_driver bbc_beep_driver = { + .name = "bbcbeep", + .match_table = bbc_beep_match, + .probe = bbc_beep_probe, + .remove = __devexit_p(bbc_remove), .shutdown = sparcspkr_shutdown, }; -static int __devinit isa_beep_probe(struct of_device *dev, const struct of_device_id *match) +static int __devinit grover_beep_probe(struct of_device *op, const struct of_device_id *match) { - struct sparc_isa_device *idev = to_isa_device(&dev->dev); struct sparcspkr_state *state; - int err; + struct grover_beep_info *info; + int err = -ENOMEM; state = kzalloc(sizeof(*state), GFP_KERNEL); if (!state) - return -ENOMEM; + goto out_err; - state->name = "Sparc ISA Speaker"; - state->iobase = idev->resource.start; - state->event = isa_spkr_event; + state->name = "Sparc Grover Speaker"; + state->event = grover_spkr_event; spin_lock_init(&state->lock); - dev_set_drvdata(&dev->dev, state); + info = &state->u.grover; + info->freq_regs = of_ioremap(&op->resource[2], 0, 2, "grover beep freq"); + if (!info->freq_regs) + goto out_free; - err = sparcspkr_probe(&dev->dev); - if (err) { - dev_set_drvdata(&dev->dev, NULL); - kfree(state); - } + 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; +} + +static int grover_remove(struct of_device *op) +{ + 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); return 0; } -static struct of_device_id isa_beep_match[] = { +static struct of_device_id grover_beep_match[] = { { - .name = "dma", + .name = "beep", + .compatible = "SUNW,smbus-beep", }, {}, }; -static struct of_platform_driver isa_beep_driver = { - .name = "beep", - .match_table = isa_beep_match, - .probe = isa_beep_probe, - .remove = __devexit_p(sparcspkr_remove), +static struct of_platform_driver grover_beep_driver = { + .name = "groverbeep", + .match_table = grover_beep_match, + .probe = grover_beep_probe, + .remove = __devexit_p(grover_remove), .shutdown = sparcspkr_shutdown, }; static int __init sparcspkr_init(void) { - int err = of_register_driver(&ebus_beep_driver, &ebus_bus_type); + int err = of_register_driver(&bbc_beep_driver, + &of_platform_bus_type); if (!err) { - err = of_register_driver(&isa_beep_driver, &isa_bus_type); + err = of_register_driver(&grover_beep_driver, + &of_platform_bus_type); if (err) - of_unregister_driver(&ebus_beep_driver); + of_unregister_driver(&bbc_beep_driver); } return err; @@ -255,8 +361,8 @@ static int __init sparcspkr_init(void) static void __exit sparcspkr_exit(void) { - of_unregister_driver(&ebus_beep_driver); - of_unregister_driver(&isa_beep_driver); + of_unregister_driver(&bbc_beep_driver); + of_unregister_driver(&grover_beep_driver); } module_init(sparcspkr_init); |