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"); 261da177e4SLinus Torvalds 27*28318dafSThomas Gleixner #ifdef CONFIG_X86 28*28318dafSThomas Gleixner /* Use the global PIT lock ! */ 29*28318dafSThomas Gleixner #include <asm/i8253.h> 30*28318dafSThomas Gleixner #else 31*28318dafSThomas Gleixner static DEFINE_SPINLOCK(i8253_lock); 32*28318dafSThomas Gleixner #endif 331da177e4SLinus Torvalds 341da177e4SLinus Torvalds static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) 351da177e4SLinus Torvalds { 361da177e4SLinus Torvalds unsigned int count = 0; 371da177e4SLinus Torvalds unsigned long flags; 381da177e4SLinus Torvalds 391da177e4SLinus Torvalds if (type != EV_SND) 401da177e4SLinus Torvalds return -1; 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds switch (code) { 431da177e4SLinus Torvalds case SND_BELL: if (value) value = 1000; 441da177e4SLinus Torvalds case SND_TONE: break; 451da177e4SLinus Torvalds default: return -1; 461da177e4SLinus Torvalds } 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds if (value > 20 && value < 32767) 491da177e4SLinus Torvalds count = PIT_TICK_RATE / value; 501da177e4SLinus Torvalds 51*28318dafSThomas Gleixner spin_lock_irqsave(&i8253_lock, flags); 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds if (count) { 541da177e4SLinus Torvalds /* enable counter 2 */ 551da177e4SLinus Torvalds outb_p(inb_p(0x61) | 3, 0x61); 561da177e4SLinus Torvalds /* set command for counter 2, 2 byte write */ 571da177e4SLinus Torvalds outb_p(0xB6, 0x43); 581da177e4SLinus Torvalds /* select desired HZ */ 591da177e4SLinus Torvalds outb_p(count & 0xff, 0x42); 601da177e4SLinus Torvalds outb((count >> 8) & 0xff, 0x42); 611da177e4SLinus Torvalds } else { 621da177e4SLinus Torvalds /* disable counter 2 */ 631da177e4SLinus Torvalds outb(inb_p(0x61) & 0xFC, 0x61); 641da177e4SLinus Torvalds } 651da177e4SLinus Torvalds 66*28318dafSThomas Gleixner spin_unlock_irqrestore(&i8253_lock, flags); 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds return 0; 691da177e4SLinus Torvalds } 701da177e4SLinus Torvalds 7159317747SDmitry Torokhov static int __devinit pcspkr_probe(struct platform_device *dev) 721da177e4SLinus Torvalds { 7359317747SDmitry Torokhov struct input_dev *pcspkr_dev; 7459317747SDmitry Torokhov int err; 7559317747SDmitry Torokhov 7676b7cddfSDmitry Torokhov pcspkr_dev = input_allocate_device(); 7776b7cddfSDmitry Torokhov if (!pcspkr_dev) 7876b7cddfSDmitry Torokhov return -ENOMEM; 791da177e4SLinus Torvalds 8076b7cddfSDmitry Torokhov pcspkr_dev->name = "PC Speaker"; 811259f2b3SDmitry Torokhov pcspkr_dev->phys = "isa0061/input0"; 8276b7cddfSDmitry Torokhov pcspkr_dev->id.bustype = BUS_ISA; 8376b7cddfSDmitry Torokhov pcspkr_dev->id.vendor = 0x001f; 8476b7cddfSDmitry Torokhov pcspkr_dev->id.product = 0x0001; 8576b7cddfSDmitry Torokhov pcspkr_dev->id.version = 0x0100; 86293e6392SDmitry Torokhov pcspkr_dev->dev.parent = &dev->dev; 871da177e4SLinus Torvalds 8876b7cddfSDmitry Torokhov pcspkr_dev->evbit[0] = BIT(EV_SND); 8976b7cddfSDmitry Torokhov pcspkr_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE); 9076b7cddfSDmitry Torokhov pcspkr_dev->event = pcspkr_event; 911da177e4SLinus Torvalds 9259317747SDmitry Torokhov err = input_register_device(pcspkr_dev); 9359317747SDmitry Torokhov if (err) { 9459317747SDmitry Torokhov input_free_device(pcspkr_dev); 9559317747SDmitry Torokhov return err; 9659317747SDmitry Torokhov } 9759317747SDmitry Torokhov 9859317747SDmitry Torokhov platform_set_drvdata(dev, pcspkr_dev); 991da177e4SLinus Torvalds 1001da177e4SLinus Torvalds return 0; 1011da177e4SLinus Torvalds } 1021da177e4SLinus Torvalds 10359317747SDmitry Torokhov static int __devexit pcspkr_remove(struct platform_device *dev) 1041da177e4SLinus Torvalds { 10559317747SDmitry Torokhov struct input_dev *pcspkr_dev = platform_get_drvdata(dev); 10659317747SDmitry Torokhov 10776b7cddfSDmitry Torokhov input_unregister_device(pcspkr_dev); 10859317747SDmitry Torokhov platform_set_drvdata(dev, NULL); 1091da177e4SLinus Torvalds /* turn off the speaker */ 1101da177e4SLinus Torvalds pcspkr_event(NULL, EV_SND, SND_BELL, 0); 11159317747SDmitry Torokhov 11259317747SDmitry Torokhov return 0; 11359317747SDmitry Torokhov } 11459317747SDmitry Torokhov 11559317747SDmitry Torokhov static int pcspkr_suspend(struct platform_device *dev, pm_message_t state) 11659317747SDmitry Torokhov { 11759317747SDmitry Torokhov pcspkr_event(NULL, EV_SND, SND_BELL, 0); 11859317747SDmitry Torokhov 11959317747SDmitry Torokhov return 0; 12059317747SDmitry Torokhov } 12159317747SDmitry Torokhov 12259317747SDmitry Torokhov static void pcspkr_shutdown(struct platform_device *dev) 12359317747SDmitry Torokhov { 12459317747SDmitry Torokhov /* turn off the speaker */ 12559317747SDmitry Torokhov pcspkr_event(NULL, EV_SND, SND_BELL, 0); 12659317747SDmitry Torokhov } 12759317747SDmitry Torokhov 12859317747SDmitry Torokhov static struct platform_driver pcspkr_platform_driver = { 12959317747SDmitry Torokhov .driver = { 13059317747SDmitry Torokhov .name = "pcspkr", 13159317747SDmitry Torokhov .owner = THIS_MODULE, 13259317747SDmitry Torokhov }, 13359317747SDmitry Torokhov .probe = pcspkr_probe, 13459317747SDmitry Torokhov .remove = __devexit_p(pcspkr_remove), 13559317747SDmitry Torokhov .suspend = pcspkr_suspend, 13659317747SDmitry Torokhov .shutdown = pcspkr_shutdown, 13759317747SDmitry Torokhov }; 13859317747SDmitry Torokhov 13959317747SDmitry Torokhov 14059317747SDmitry Torokhov static int __init pcspkr_init(void) 14159317747SDmitry Torokhov { 142e5c6c8e4SMichael Neuling return platform_driver_register(&pcspkr_platform_driver); 14359317747SDmitry Torokhov } 14459317747SDmitry Torokhov 14559317747SDmitry Torokhov static void __exit pcspkr_exit(void) 14659317747SDmitry Torokhov { 14759317747SDmitry Torokhov platform_driver_unregister(&pcspkr_platform_driver); 1481da177e4SLinus Torvalds } 1491da177e4SLinus Torvalds 1501da177e4SLinus Torvalds module_init(pcspkr_init); 1511da177e4SLinus Torvalds module_exit(pcspkr_exit); 152