diff options
Diffstat (limited to 'drivers/input/misc/sparcspkr.c')
| -rw-r--r-- | drivers/input/misc/sparcspkr.c | 344 |
1 files changed, 238 insertions, 106 deletions
diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c index f0fd2c4740f..65fd3150919 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c @@ -2,32 +2,70 @@ * Driver for PC-speaker like devices found on various Sparc systems. * * Copyright (c) 2002 Vojtech Pavlik - * Copyright (c) 2002 David S. Miller (davem@redhat.com) + * Copyright (c) 2002, 2006, 2008 David S. Miller (davem@davemloft.net) */ -#include <linux/config.h> #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 <linux/slab.h> #include <asm/io.h> -#include <asm/ebus.h> -#ifdef CONFIG_SPARC64 -#include <asm/isa.h> -#endif -MODULE_AUTHOR("David S. Miller <davem@redhat.com>"); +MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); MODULE_DESCRIPTION("Sparc Speaker beeper driver"); MODULE_LICENSE("GPL"); -const char *beep_name; -static unsigned long beep_iobase; -static int (*beep_event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); -static DEFINE_SPINLOCK(beep_lock); +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; + 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 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; -static int ebus_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) + 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; @@ -43,24 +81,29 @@ static int ebus_spkr_event(struct input_dev *dev, unsigned int type, unsigned in if (value > 20 && value < 32767) count = 1193182 / value; - spin_lock_irqsave(&beep_lock, flags); + count = bbc_count_to_reg(info, count); - /* EBUS speaker only has on/off state, the frequency does not - * appear to be programmable. - */ - if (beep_iobase & 0x2UL) - outb(!!count, beep_iobase); - else - outl(!!count, beep_iobase); + spin_lock_irqsave(&state->lock, flags); + + 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(&beep_lock, flags); + spin_unlock_irqrestore(&state->lock, flags); return 0; } -#ifdef CONFIG_SPARC64 -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; @@ -76,29 +119,29 @@ static int isa_spkr_event(struct input_dev *dev, unsigned int type, unsigned int if (value > 20 && value < 32767) count = 1193182 / value; - spin_lock_irqsave(&beep_lock, flags); + spin_lock_irqsave(&state->lock, flags); if (count) { /* enable counter 2 */ - outb(inb(beep_iobase + 0x61) | 3, beep_iobase + 0x61); + outb(inb(info->enable_reg) | 3, info->enable_reg); /* set command for counter 2, 2 byte write */ - outb(0xB6, beep_iobase + 0x43); + outb(0xB6, info->freq_regs + 1); /* select desired HZ */ - outb(count & 0xff, beep_iobase + 0x42); - outb((count >> 8) & 0xff, beep_iobase + 0x42); + outb(count & 0xff, info->freq_regs + 0); + outb((count >> 8) & 0xff, info->freq_regs + 0); } else { /* disable counter 2 */ - outb(inb_p(beep_iobase + 0x61) & 0xFC, beep_iobase + 0x61); + outb(inb_p(info->enable_reg) & 0xFC, info->enable_reg); } - spin_unlock_irqrestore(&beep_lock, flags); + spin_unlock_irqrestore(&state->lock, flags); return 0; } -#endif -static int __devinit sparcspkr_probe(struct platform_device *dev) +static int sparcspkr_probe(struct device *dev) { + struct sparcspkr_state *state = dev_get_drvdata(dev); struct input_dev *input_dev; int error; @@ -106,18 +149,18 @@ static int __devinit sparcspkr_probe(struct platform_device *dev) if (!input_dev) return -ENOMEM; - input_dev->name = beep_name; + input_dev->name = state->name; 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; - input_dev->cdev.dev = &dev->dev; + input_dev->dev.parent = dev; - input_dev->evbit[0] = BIT(EV_SND); - input_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE); + input_dev->evbit[0] = BIT_MASK(EV_SND); + input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); - input_dev->event = beep_event; + input_dev->event = state->event; error = input_register_device(input_dev); if (error) { @@ -125,111 +168,200 @@ static int __devinit sparcspkr_probe(struct platform_device *dev) return error; } - platform_set_drvdata(dev, input_dev); + state->input_dev = input_dev; return 0; } -static int __devexit sparcspkr_remove(struct platform_device *dev) +static void sparcspkr_shutdown(struct platform_device *dev) { - struct input_dev *input_dev = platform_get_drvdata(dev); + struct sparcspkr_state *state = platform_get_drvdata(dev); + struct input_dev *input_dev = state->input_dev; - input_unregister_device(input_dev); - platform_set_drvdata(dev, NULL); /* turn off the speaker */ - beep_event(NULL, EV_SND, SND_BELL, 0); + state->event(input_dev, EV_SND, SND_BELL, 0); +} + +static int bbc_beep_probe(struct platform_device *op) +{ + struct sparcspkr_state *state; + struct bbc_beep_info *info; + struct device_node *dp; + int err = -ENOMEM; + + 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; + + platform_set_drvdata(op, state); + + err = sparcspkr_probe(&op->dev); + if (err) + goto out_clear_drvdata; return 0; + +out_clear_drvdata: + of_iounmap(&op->resource[0], info->regs, 6); + +out_free: + kfree(state); +out_err: + return err; } -static void sparcspkr_shutdown(struct platform_device *dev) +static int bbc_remove(struct platform_device *op) { + struct sparcspkr_state *state = platform_get_drvdata(op); + struct input_dev *input_dev = state->input_dev; + struct bbc_beep_info *info = &state->u.bbc; + /* turn off the speaker */ - beep_event(NULL, EV_SND, SND_BELL, 0); + state->event(input_dev, EV_SND, SND_BELL, 0); + + input_unregister_device(input_dev); + + of_iounmap(&op->resource[0], info->regs, 6); + + kfree(state); + + return 0; } -static struct platform_driver sparcspkr_platform_driver = { - .driver = { - .name = "sparcspkr", - .owner = THIS_MODULE, +static const struct of_device_id bbc_beep_match[] = { + { + .name = "beep", + .compatible = "SUNW,bbc-beep", }, - .probe = sparcspkr_probe, - .remove = __devexit_p(sparcspkr_remove), - .shutdown = sparcspkr_shutdown, + {}, }; -static struct platform_device *sparcspkr_platform_device; +static struct platform_driver bbc_beep_driver = { + .driver = { + .name = "bbcbeep", + .owner = THIS_MODULE, + .of_match_table = bbc_beep_match, + }, + .probe = bbc_beep_probe, + .remove = bbc_remove, + .shutdown = sparcspkr_shutdown, +}; -static int __init sparcspkr_drv_init(void) +static int grover_beep_probe(struct platform_device *op) { - int error; + struct sparcspkr_state *state; + struct grover_beep_info *info; + int err = -ENOMEM; - error = platform_driver_register(&sparcspkr_platform_driver); - if (error) - return error; + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + goto out_err; - sparcspkr_platform_device = platform_device_alloc("sparcspkr", -1); - if (!sparcspkr_platform_device) { - error = -ENOMEM; - goto err_unregister_driver; - } + state->name = "Sparc Grover Speaker"; + state->event = grover_spkr_event; + spin_lock_init(&state->lock); + + info = &state->u.grover; + info->freq_regs = of_ioremap(&op->resource[2], 0, 2, "grover beep freq"); + if (!info->freq_regs) + goto out_free; + + info->enable_reg = of_ioremap(&op->resource[3], 0, 1, "grover beep enable"); + if (!info->enable_reg) + goto out_unmap_freq_regs; - error = platform_device_add(sparcspkr_platform_device); - if (error) - goto err_free_device; + platform_set_drvdata(op, state); + + err = sparcspkr_probe(&op->dev); + if (err) + goto out_clear_drvdata; return 0; - err_free_device: - platform_device_put(sparcspkr_platform_device); - err_unregister_driver: - platform_driver_unregister(&sparcspkr_platform_driver); +out_clear_drvdata: + of_iounmap(&op->resource[3], info->enable_reg, 1); - return error; +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 platform_device *op) +{ + struct sparcspkr_state *state = platform_get_drvdata(op); + 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); + + kfree(state); + + return 0; +} + +static const struct of_device_id grover_beep_match[] = { + { + .name = "beep", + .compatible = "SUNW,smbus-beep", + }, + {}, +}; + +static struct platform_driver grover_beep_driver = { + .driver = { + .name = "groverbeep", + .owner = THIS_MODULE, + .of_match_table = grover_beep_match, + }, + .probe = grover_beep_probe, + .remove = grover_remove, + .shutdown = sparcspkr_shutdown, +}; + static int __init sparcspkr_init(void) { - struct linux_ebus *ebus; - struct linux_ebus_device *edev; -#ifdef CONFIG_SPARC64 - struct sparc_isa_bridge *isa_br; - struct sparc_isa_device *isa_dev; -#endif - - for_each_ebus(ebus) { - for_each_ebusdev(edev, ebus) { - if (!strcmp(edev->prom_name, "beep")) { - beep_name = "Sparc EBUS Speaker"; - beep_event = ebus_spkr_event; - beep_iobase = edev->resource[0].start; - return sparcspkr_drv_init(); - } - } - } -#ifdef CONFIG_SPARC64 - for_each_isa(isa_br) { - for_each_isadev(isa_dev, isa_br) { - /* A hack, the beep device's base lives in - * the DMA isa node. - */ - if (!strcmp(isa_dev->prom_name, "dma")) { - beep_name = "Sparc ISA Speaker"; - beep_event = isa_spkr_event, - beep_iobase = isa_dev->resource.start; - return sparcspkr_drv_init(); - } - } + int err = platform_driver_register(&bbc_beep_driver); + + if (!err) { + err = platform_driver_register(&grover_beep_driver); + if (err) + platform_driver_unregister(&bbc_beep_driver); } -#endif - return -ENODEV; + return err; } static void __exit sparcspkr_exit(void) { - platform_device_unregister(sparcspkr_platform_device); - platform_driver_unregister(&sparcspkr_platform_driver); + platform_driver_unregister(&bbc_beep_driver); + platform_driver_unregister(&grover_beep_driver); } module_init(sparcspkr_init); |
