11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * PC Speaker beeper driver for Linux 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (c) 2002 Vojtech Pavlik 51da177e4SLinus Torvalds * Copyright (c) 1992 Orest Zborowski 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds */ 81da177e4SLinus Torvalds 91da177e4SLinus Torvalds /* 101da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify it 111da177e4SLinus Torvalds * under the terms of the GNU General Public License version 2 as published by 121da177e4SLinus Torvalds * the Free Software Foundation 131da177e4SLinus Torvalds */ 141da177e4SLinus Torvalds 151da177e4SLinus Torvalds #include <linux/kernel.h> 161da177e4SLinus Torvalds #include <linux/module.h> 171da177e4SLinus Torvalds #include <linux/init.h> 181da177e4SLinus Torvalds #include <linux/input.h> 1959317747SDmitry Torokhov #include <linux/platform_device.h> 201da177e4SLinus Torvalds #include <asm/8253pit.h> 211da177e4SLinus Torvalds #include <asm/io.h> 221da177e4SLinus Torvalds 231da177e4SLinus Torvalds MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); 241da177e4SLinus Torvalds MODULE_DESCRIPTION("PC Speaker beeper driver"); 251da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 26*43cc71eeSKay Sievers MODULE_ALIAS("platform:pcspkr"); 271da177e4SLinus Torvalds 2828318dafSThomas Gleixner #ifdef CONFIG_X86 2928318dafSThomas Gleixner /* Use the global PIT lock ! */ 3028318dafSThomas Gleixner #include <asm/i8253.h> 3128318dafSThomas Gleixner #else 3228318dafSThomas Gleixner static DEFINE_SPINLOCK(i8253_lock); 3328318dafSThomas Gleixner #endif 341da177e4SLinus Torvalds 351da177e4SLinus Torvalds static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) 361da177e4SLinus Torvalds { 371da177e4SLinus Torvalds unsigned int count = 0; 381da177e4SLinus Torvalds unsigned long flags; 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds if (type != EV_SND) 411da177e4SLinus Torvalds return -1; 421da177e4SLinus Torvalds 431da177e4SLinus Torvalds switch (code) { 441da177e4SLinus Torvalds case SND_BELL: if (value) value = 1000; 451da177e4SLinus Torvalds case SND_TONE: break; 461da177e4SLinus Torvalds default: return -1; 471da177e4SLinus Torvalds } 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds if (value > 20 && value < 32767) 501da177e4SLinus Torvalds count = PIT_TICK_RATE / value; 511da177e4SLinus Torvalds 5228318dafSThomas Gleixner spin_lock_irqsave(&i8253_lock, flags); 531da177e4SLinus Torvalds 541da177e4SLinus Torvalds if (count) { 551da177e4SLinus Torvalds /* enable counter 2 */ 561da177e4SLinus Torvalds outb_p(inb_p(0x61) | 3, 0x61); 571da177e4SLinus Torvalds /* set command for counter 2, 2 byte write */ 581da177e4SLinus Torvalds outb_p(0xB6, 0x43); 591da177e4SLinus Torvalds /* select desired HZ */ 601da177e4SLinus Torvalds outb_p(count & 0xff, 0x42); 611da177e4SLinus Torvalds outb((count >> 8) & 0xff, 0x42); 621da177e4SLinus Torvalds } else { 631da177e4SLinus Torvalds /* disable counter 2 */ 641da177e4SLinus Torvalds outb(inb_p(0x61) & 0xFC, 0x61); 651da177e4SLinus Torvalds } 661da177e4SLinus Torvalds 6728318dafSThomas Gleixner spin_unlock_irqrestore(&i8253_lock, flags); 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds return 0; 701da177e4SLinus Torvalds } 711da177e4SLinus Torvalds 7259317747SDmitry Torokhov static int __devinit pcspkr_probe(struct platform_device *dev) 731da177e4SLinus Torvalds { 7459317747SDmitry Torokhov struct input_dev *pcspkr_dev; 7559317747SDmitry Torokhov int err; 7659317747SDmitry Torokhov 7776b7cddfSDmitry Torokhov pcspkr_dev = input_allocate_device(); 7876b7cddfSDmitry Torokhov if (!pcspkr_dev) 7976b7cddfSDmitry Torokhov return -ENOMEM; 801da177e4SLinus Torvalds 8176b7cddfSDmitry Torokhov pcspkr_dev->name = "PC Speaker"; 821259f2b3SDmitry Torokhov pcspkr_dev->phys = "isa0061/input0"; 8376b7cddfSDmitry Torokhov pcspkr_dev->id.bustype = BUS_ISA; 8476b7cddfSDmitry Torokhov pcspkr_dev->id.vendor = 0x001f; 8576b7cddfSDmitry Torokhov pcspkr_dev->id.product = 0x0001; 8676b7cddfSDmitry Torokhov pcspkr_dev->id.version = 0x0100; 87293e6392SDmitry Torokhov pcspkr_dev->dev.parent = &dev->dev; 881da177e4SLinus Torvalds 8976b7cddfSDmitry Torokhov pcspkr_dev->evbit[0] = BIT(EV_SND); 9076b7cddfSDmitry Torokhov pcspkr_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE); 9176b7cddfSDmitry Torokhov pcspkr_dev->event = pcspkr_event; 921da177e4SLinus Torvalds 9359317747SDmitry Torokhov err = input_register_device(pcspkr_dev); 9459317747SDmitry Torokhov if (err) { 9559317747SDmitry Torokhov input_free_device(pcspkr_dev); 9659317747SDmitry Torokhov return err; 9759317747SDmitry Torokhov } 9859317747SDmitry Torokhov 9959317747SDmitry Torokhov platform_set_drvdata(dev, pcspkr_dev); 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds return 0; 1021da177e4SLinus Torvalds } 1031da177e4SLinus Torvalds 10459317747SDmitry Torokhov static int __devexit pcspkr_remove(struct platform_device *dev) 1051da177e4SLinus Torvalds { 10659317747SDmitry Torokhov struct input_dev *pcspkr_dev = platform_get_drvdata(dev); 10759317747SDmitry Torokhov 10876b7cddfSDmitry Torokhov input_unregister_device(pcspkr_dev); 10959317747SDmitry Torokhov platform_set_drvdata(dev, NULL); 1101da177e4SLinus Torvalds /* turn off the speaker */ 1111da177e4SLinus Torvalds pcspkr_event(NULL, EV_SND, SND_BELL, 0); 11259317747SDmitry Torokhov 11359317747SDmitry Torokhov return 0; 11459317747SDmitry Torokhov } 11559317747SDmitry Torokhov 11659317747SDmitry Torokhov static int pcspkr_suspend(struct platform_device *dev, pm_message_t state) 11759317747SDmitry Torokhov { 11859317747SDmitry Torokhov pcspkr_event(NULL, EV_SND, SND_BELL, 0); 11959317747SDmitry Torokhov 12059317747SDmitry Torokhov return 0; 12159317747SDmitry Torokhov } 12259317747SDmitry Torokhov 12359317747SDmitry Torokhov static void pcspkr_shutdown(struct platform_device *dev) 12459317747SDmitry Torokhov { 12559317747SDmitry Torokhov /* turn off the speaker */ 12659317747SDmitry Torokhov pcspkr_event(NULL, EV_SND, SND_BELL, 0); 12759317747SDmitry Torokhov } 12859317747SDmitry Torokhov 12959317747SDmitry Torokhov static struct platform_driver pcspkr_platform_driver = { 13059317747SDmitry Torokhov .driver = { 13159317747SDmitry Torokhov .name = "pcspkr", 13259317747SDmitry Torokhov .owner = THIS_MODULE, 13359317747SDmitry Torokhov }, 13459317747SDmitry Torokhov .probe = pcspkr_probe, 13559317747SDmitry Torokhov .remove = __devexit_p(pcspkr_remove), 13659317747SDmitry Torokhov .suspend = pcspkr_suspend, 13759317747SDmitry Torokhov .shutdown = pcspkr_shutdown, 13859317747SDmitry Torokhov }; 13959317747SDmitry Torokhov 14059317747SDmitry Torokhov 14159317747SDmitry Torokhov static int __init pcspkr_init(void) 14259317747SDmitry Torokhov { 143e5c6c8e4SMichael Neuling return platform_driver_register(&pcspkr_platform_driver); 14459317747SDmitry Torokhov } 14559317747SDmitry Torokhov 14659317747SDmitry Torokhov static void __exit pcspkr_exit(void) 14759317747SDmitry Torokhov { 14859317747SDmitry Torokhov platform_driver_unregister(&pcspkr_platform_driver); 1491da177e4SLinus Torvalds } 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds module_init(pcspkr_init); 1521da177e4SLinus Torvalds module_exit(pcspkr_exit); 153