X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=drivers%2Finput%2Fmisc%2Fsparcspkr.c;h=d8765cc93d27d5daa1f343edea8bee7e75226117;hb=375614422509c98a1f3dbef410206bf81775169b;hp=29d97b12be7a4bc1753e7279e774b7e007fe7b6f;hpb=62d3af1b5f3aa613e0a489e8d798d45b07f373f4;p=linux-2.6-omap-h63xx.git diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c index 29d97b12be7..d8765cc93d2 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c @@ -2,43 +2,69 @@ * 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 #include #include #include #include +#include #include -#include -#ifdef CONFIG_SPARC64 -#include -#endif -MODULE_AUTHOR("David S. Miller "); +MODULE_AUTHOR("David S. Miller "); MODULE_DESCRIPTION("Sparc Speaker beeper driver"); MODULE_LICENSE("GPL"); -static unsigned long beep_iobase; -static struct input_dev *sparcspkr_dev; +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; -DEFINE_SPINLOCK(beep_lock); + if (!count) + return 0; -static void __init init_sparcspkr_struct(void) -{ - sparcspkr_dev->evbit[0] = BIT(EV_SND); - sparcspkr_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE); - - sparcspkr_dev->phys = "sparc/input0"; - sparcspkr_dev->id.bustype = BUS_ISA; - sparcspkr_dev->id.vendor = 0x001f; - sparcspkr_dev->id.product = 0x0001; - sparcspkr_dev->id.version = 0x0100; + 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 ebus_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +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; @@ -54,47 +80,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); + + spin_lock_irqsave(&state->lock, flags); - /* EBUS speaker only has on/off state, the frequency does not - * appear to be programmable. - */ if (count) { - if (beep_iobase & 0x2UL) - outb(1, beep_iobase); - else - outl(1, beep_iobase); + 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 { - if (beep_iobase & 0x2UL) - outb(0, beep_iobase); - else - outl(0, beep_iobase); + outb(0x00, info->regs + 0); } - spin_unlock_irqrestore(&beep_lock, flags); - - return 0; -} - -static int __init init_ebus_beep(struct linux_ebus_device *edev) -{ - beep_iobase = edev->resource[0].start; - - sparcspkr_dev = input_allocate_device(); - if (!sparcspkr_dev) - return -ENOMEM; - - sparcspkr_dev->name = "Sparc EBUS Speaker"; - sparcspkr_dev->event = ebus_spkr_event; - - input_register_device(sparcspkr_dev); + 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; @@ -110,78 +118,251 @@ 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; } -static int __init init_isa_beep(struct sparc_isa_device *isa_dev) +static int __devinit sparcspkr_probe(struct device *dev) { - beep_iobase = isa_dev->resource.start; + struct sparcspkr_state *state = dev_get_drvdata(dev); + struct input_dev *input_dev; + int error; - sparcspkr_dev = input_allocate_device(); - if (!sparcspkr_dev) + input_dev = input_allocate_device(); + if (!input_dev) return -ENOMEM; - init_sparcspkr_struct(); + 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->dev.parent = dev; + + input_dev->evbit[0] = BIT_MASK(EV_SND); + input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); + + input_dev->event = state->event; + + error = input_register_device(input_dev); + if (error) { + input_free_device(input_dev); + return error; + } + + state->input_dev = input_dev; + + return 0; +} + +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; + + /* turn off the speaker */ + state->event(input_dev, EV_SND, SND_BELL, 0); + + return 0; +} + +static int __devinit bbc_beep_probe(struct of_device *op, const struct of_device_id *match) +{ + 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; - sparcspkr_dev->name = "Sparc ISA Speaker"; - sparcspkr_dev->event = isa_spkr_event; + info->regs = of_ioremap(&op->resource[0], 0, 6, "bbc beep"); + if (!info->regs) + goto out_free; - input_register_device(sparcspkr_dev); + 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 bbc_remove(struct of_device *op) +{ + 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; + + /* turn off the speaker */ + state->event(input_dev, EV_SND, SND_BELL, 0); + + input_unregister_device(input_dev); + + of_iounmap(&op->resource[0], info->regs, 6); + + dev_set_drvdata(&op->dev, NULL); + kfree(state); + + return 0; +} + +static struct of_device_id bbc_beep_match[] = { + { + .name = "beep", + .compatible = "SUNW,bbc-beep", + }, + {}, +}; + +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 grover_beep_probe(struct of_device *op, const struct of_device_id *match) +{ + struct sparcspkr_state *state; + struct grover_beep_info *info; + int err = -ENOMEM; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + goto out_err; + + 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; + + 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; } -#endif + +static struct of_device_id grover_beep_match[] = { + { + .name = "beep", + .compatible = "SUNW,smbus-beep", + }, + {}, +}; + +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) { - struct linux_ebus *ebus; - struct linux_ebus_device *edev = NULL; -#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")) - return init_ebus_beep(edev); - } - } -#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")) - return init_isa_beep(isa_dev); - } + int err = of_register_driver(&bbc_beep_driver, + &of_platform_bus_type); + + if (!err) { + err = of_register_driver(&grover_beep_driver, + &of_platform_bus_type); + if (err) + of_unregister_driver(&bbc_beep_driver); } -#endif - return -ENODEV; + return err; } static void __exit sparcspkr_exit(void) { - input_unregister_device(sparcspkr_dev); + of_unregister_driver(&bbc_beep_driver); + of_unregister_driver(&grover_beep_driver); } module_init(sparcspkr_init);