2005-04-17 02:20:36 +04:00
/*
* Driver for PC - speaker like devices found on various Sparc systems .
*
* Copyright ( c ) 2002 Vojtech Pavlik
2008-04-27 08:02:21 +04:00
* Copyright ( c ) 2002 , 2006 , 2008 David S . Miller ( davem @ davemloft . net )
2005-04-17 02:20:36 +04:00
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/input.h>
2008-04-27 08:02:21 +04:00
# include <linux/of_device.h>
2005-04-17 02:20:36 +04:00
# include <asm/io.h>
2006-06-23 12:44:10 +04:00
MODULE_AUTHOR ( " David S. Miller <davem@davemloft.net> " ) ;
2005-09-15 11:01:51 +04:00
MODULE_DESCRIPTION ( " Sparc Speaker beeper driver " ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
2008-04-27 08:02:21 +04:00
struct grover_beep_info {
void __iomem * freq_regs ;
void __iomem * enable_reg ;
} ;
struct bbc_beep_info {
u32 clock_freq ;
void __iomem * regs ;
} ;
2006-06-23 12:44:10 +04:00
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 ;
2008-04-27 08:02:21 +04:00
union {
struct grover_beep_info grover ;
struct bbc_beep_info bbc ;
} u ;
2006-06-23 12:44:10 +04:00
} ;
2005-04-17 02:20:36 +04:00
2008-04-27 08:02:21 +04:00
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 )
2005-04-17 02:20:36 +04:00
{
2007-04-12 09:35:32 +04:00
struct sparcspkr_state * state = dev_get_drvdata ( dev - > dev . parent ) ;
2008-04-27 08:02:21 +04:00
struct bbc_beep_info * info = & state - > u . bbc ;
2005-04-17 02:20:36 +04:00
unsigned int count = 0 ;
unsigned long flags ;
if ( type ! = EV_SND )
return - 1 ;
switch ( code ) {
case SND_BELL : if ( value ) value = 1000 ;
case SND_TONE : break ;
default : return - 1 ;
}
if ( value > 20 & & value < 32767 )
count = 1193182 / value ;
2008-04-27 08:02:21 +04:00
count = bbc_count_to_reg ( info , count ) ;
2006-06-23 12:44:10 +04:00
spin_lock_irqsave ( & state - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
2008-04-27 08:02:21 +04:00
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 ) ;
}
2005-04-17 02:20:36 +04:00
2006-06-23 12:44:10 +04:00
spin_unlock_irqrestore ( & state - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-04-27 08:02:21 +04:00
static int grover_spkr_event ( struct input_dev * dev , unsigned int type , unsigned int code , int value )
2005-04-17 02:20:36 +04:00
{
2007-04-12 09:35:32 +04:00
struct sparcspkr_state * state = dev_get_drvdata ( dev - > dev . parent ) ;
2008-04-27 08:02:21 +04:00
struct grover_beep_info * info = & state - > u . grover ;
2005-04-17 02:20:36 +04:00
unsigned int count = 0 ;
unsigned long flags ;
if ( type ! = EV_SND )
return - 1 ;
switch ( code ) {
case SND_BELL : if ( value ) value = 1000 ;
case SND_TONE : break ;
default : return - 1 ;
}
if ( value > 20 & & value < 32767 )
count = 1193182 / value ;
2006-06-23 12:44:10 +04:00
spin_lock_irqsave ( & state - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
if ( count ) {
/* enable counter 2 */
2008-04-27 08:02:21 +04:00
outb ( inb ( info - > enable_reg ) | 3 , info - > enable_reg ) ;
2005-04-17 02:20:36 +04:00
/* set command for counter 2, 2 byte write */
2008-04-27 08:02:21 +04:00
outb ( 0xB6 , info - > freq_regs + 1 ) ;
2005-04-17 02:20:36 +04:00
/* select desired HZ */
2008-04-27 08:02:21 +04:00
outb ( count & 0xff , info - > freq_regs + 0 ) ;
outb ( ( count > > 8 ) & 0xff , info - > freq_regs + 0 ) ;
2005-04-17 02:20:36 +04:00
} else {
/* disable counter 2 */
2008-04-27 08:02:21 +04:00
outb ( inb_p ( info - > enable_reg ) & 0xFC , info - > enable_reg ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-23 12:44:10 +04:00
spin_unlock_irqrestore ( & state - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-06-23 12:44:10 +04:00
static int __devinit sparcspkr_probe ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2006-06-23 12:44:10 +04:00
struct sparcspkr_state * state = dev_get_drvdata ( dev ) ;
2005-12-21 08:52:35 +03:00
struct input_dev * input_dev ;
int error ;
2005-04-17 02:20:36 +04:00
2005-12-21 08:52:35 +03:00
input_dev = input_allocate_device ( ) ;
if ( ! input_dev )
2005-09-15 11:01:51 +04:00
return - ENOMEM ;
2006-06-23 12:44:10 +04:00
input_dev - > name = state - > name ;
2005-12-21 08:52:35 +03:00
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 ;
2007-04-12 09:35:32 +04:00
input_dev - > dev . parent = dev ;
2005-04-17 02:20:36 +04:00
2007-10-19 10:40:32 +04:00
input_dev - > evbit [ 0 ] = BIT_MASK ( EV_SND ) ;
input_dev - > sndbit [ 0 ] = BIT_MASK ( SND_BELL ) | BIT_MASK ( SND_TONE ) ;
2005-04-17 02:20:36 +04:00
2006-06-23 12:44:10 +04:00
input_dev - > event = state - > event ;
2005-12-21 08:52:35 +03:00
error = input_register_device ( input_dev ) ;
if ( error ) {
input_free_device ( input_dev ) ;
return error ;
}
2006-06-23 12:44:10 +04:00
state - > input_dev = input_dev ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-12-21 08:52:35 +03:00
2008-04-27 08:02:21 +04:00
static int sparcspkr_shutdown ( struct of_device * dev )
2005-12-21 08:52:35 +03:00
{
2006-06-23 12:44:10 +04:00
struct sparcspkr_state * state = dev_get_drvdata ( & dev - > dev ) ;
struct input_dev * input_dev = state - > input_dev ;
2005-12-21 08:52:35 +03:00
/* turn off the speaker */
2006-06-23 12:44:10 +04:00
state - > event ( input_dev , EV_SND , SND_BELL , 0 ) ;
2005-12-21 08:52:35 +03:00
return 0 ;
}
2008-04-27 08:02:21 +04:00
static int __devinit bbc_beep_probe ( struct of_device * op , const struct of_device_id * match )
2005-12-21 08:52:35 +03:00
{
2008-04-27 08:02:21 +04:00
struct sparcspkr_state * state ;
struct bbc_beep_info * info ;
struct device_node * dp ;
int err = - ENOMEM ;
2006-06-23 12:44:10 +04:00
2008-04-27 08:02:21 +04:00
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 ;
2006-06-23 12:44:10 +04:00
return 0 ;
2008-04-27 08:02:21 +04:00
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 ;
2006-06-23 12:44:10 +04:00
}
2008-04-27 08:02:21 +04:00
static int bbc_remove ( struct of_device * op )
2006-06-23 12:44:10 +04:00
{
2008-04-27 08:02:21 +04:00
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 ;
2006-06-23 12:44:10 +04:00
2008-04-27 08:02:21 +04:00
/* turn off the speaker */
state - > event ( input_dev , EV_SND , SND_BELL , 0 ) ;
2006-06-23 12:44:10 +04:00
2008-04-27 08:02:21 +04:00
input_unregister_device ( input_dev ) ;
2006-06-23 12:44:10 +04:00
2008-04-27 08:02:21 +04:00
of_iounmap ( & op - > resource [ 0 ] , info - > regs , 6 ) ;
2006-06-23 12:44:10 +04:00
2008-04-27 08:02:21 +04:00
dev_set_drvdata ( & op - > dev , NULL ) ;
kfree ( state ) ;
2006-06-23 12:44:10 +04:00
return 0 ;
2005-12-21 08:52:35 +03:00
}
2008-08-31 12:23:17 +04:00
static const struct of_device_id bbc_beep_match [ ] = {
2006-06-23 12:44:10 +04:00
{
. name = " beep " ,
2008-04-27 08:02:21 +04:00
. compatible = " SUNW,bbc-beep " ,
2005-12-21 08:52:35 +03:00
} ,
2006-06-23 12:44:10 +04:00
{ } ,
2005-12-21 08:52:35 +03:00
} ;
2008-04-27 08:02:21 +04:00
static struct of_platform_driver bbc_beep_driver = {
. name = " bbcbeep " ,
. match_table = bbc_beep_match ,
. probe = bbc_beep_probe ,
. remove = __devexit_p ( bbc_remove ) ,
2006-06-23 12:44:10 +04:00
. shutdown = sparcspkr_shutdown ,
} ;
2005-12-21 08:52:35 +03:00
2008-04-27 08:02:21 +04:00
static int __devinit grover_beep_probe ( struct of_device * op , const struct of_device_id * match )
2005-12-21 08:52:35 +03:00
{
2006-06-23 12:44:10 +04:00
struct sparcspkr_state * state ;
2008-04-27 08:02:21 +04:00
struct grover_beep_info * info ;
int err = - ENOMEM ;
2005-12-21 08:52:35 +03:00
2006-06-23 12:44:10 +04:00
state = kzalloc ( sizeof ( * state ) , GFP_KERNEL ) ;
if ( ! state )
2008-04-27 08:02:21 +04:00
goto out_err ;
2005-12-21 08:52:35 +03:00
2008-04-27 08:02:21 +04:00
state - > name = " Sparc Grover Speaker " ;
state - > event = grover_spkr_event ;
2006-06-23 12:44:10 +04:00
spin_lock_init ( & state - > lock ) ;
2008-04-27 08:02:21 +04:00
info = & state - > u . grover ;
info - > freq_regs = of_ioremap ( & op - > resource [ 2 ] , 0 , 2 , " grover beep freq " ) ;
if ( ! info - > freq_regs )
goto out_free ;
2005-12-21 08:52:35 +03:00
2008-04-27 08:02:21 +04:00
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 ) ;
2005-12-21 08:52:35 +03:00
return 0 ;
2006-06-23 12:44:10 +04:00
}
2005-12-21 08:52:35 +03:00
2008-08-31 12:23:17 +04:00
static const struct of_device_id grover_beep_match [ ] = {
2006-06-23 12:44:10 +04:00
{
2008-04-27 08:02:21 +04:00
. name = " beep " ,
. compatible = " SUNW,smbus-beep " ,
2006-06-23 12:44:10 +04:00
} ,
{ } ,
} ;
2005-12-21 08:52:35 +03:00
2008-04-27 08:02:21 +04:00
static struct of_platform_driver grover_beep_driver = {
. name = " groverbeep " ,
. match_table = grover_beep_match ,
. probe = grover_beep_probe ,
. remove = __devexit_p ( grover_remove ) ,
2006-06-23 12:44:10 +04:00
. shutdown = sparcspkr_shutdown ,
} ;
2005-04-17 02:20:36 +04:00
static int __init sparcspkr_init ( void )
{
2008-04-27 08:02:21 +04:00
int err = of_register_driver ( & bbc_beep_driver ,
& of_platform_bus_type ) ;
2006-06-23 12:44:10 +04:00
if ( ! err ) {
2008-04-27 08:02:21 +04:00
err = of_register_driver ( & grover_beep_driver ,
& of_platform_bus_type ) ;
2006-06-23 12:44:10 +04:00
if ( err )
2008-04-27 08:02:21 +04:00
of_unregister_driver ( & bbc_beep_driver ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-23 12:44:10 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
static void __exit sparcspkr_exit ( void )
{
2008-04-27 08:02:21 +04:00
of_unregister_driver ( & bbc_beep_driver ) ;
of_unregister_driver ( & grover_beep_driver ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( sparcspkr_init ) ;
module_exit ( sparcspkr_exit ) ;