2008-08-30 05:01:58 +04:00
/* display7seg.c - Driver implementation for the 7-segment display
* present on Sun Microsystems CP1400 and CP1500
2005-04-17 02:20:36 +04:00
*
* Copyright ( c ) 2000 Eric Brower ( ebrower @ usa . net )
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/fs.h>
# include <linux/errno.h>
# include <linux/major.h>
# include <linux/init.h>
# include <linux/miscdevice.h>
2005-09-13 12:25:15 +04:00
# include <linux/ioport.h> /* request_region */
2005-11-08 01:13:01 +03:00
# include <linux/smp_lock.h>
2008-08-30 05:01:58 +04:00
# include <linux/of.h>
# include <linux/of_device.h>
2005-04-17 02:20:36 +04:00
# include <asm/atomic.h>
# include <asm/uaccess.h> /* put_/get_user */
2007-05-14 09:22:47 +04:00
# include <asm/io.h>
2005-04-17 02:20:36 +04:00
# include <asm/display7seg.h>
# define D7S_MINOR 193
2008-08-30 05:01:58 +04:00
# define DRIVER_NAME "d7s"
# define PFX DRIVER_NAME ": "
2005-04-17 02:20:36 +04:00
static int sol_compat = 0 ; /* Solaris compatibility mode */
/* Solaris compatibility flag -
* The Solaris implementation omits support for several
* documented driver features ( ref Sun doc 806 - 01 80 - 03 ) .
* By default , this module supports the documented driver
* abilities , rather than the Solaris implementation :
*
* 1 ) Device ALWAYS reverts to OBP - specified FLIPPED mode
* upon closure of device or module unload .
* 2 ) Device ioctls D7SIOCRD / D7SIOCWR honor toggling of
* FLIP bit
*
* If you wish the device to operate as under Solaris ,
* omitting above features , set this parameter to non - zero .
*/
2008-08-30 05:01:58 +04:00
module_param ( sol_compat , int , 0 ) ;
MODULE_PARM_DESC ( sol_compat ,
" Disables documented functionality omitted from Solaris driver " ) ;
MODULE_AUTHOR ( " Eric Brower <ebrower@usa.net> " ) ;
MODULE_DESCRIPTION ( " 7-Segment Display driver for Sun Microsystems CP1400/1500 " ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
2008-08-30 05:01:58 +04:00
MODULE_SUPPORTED_DEVICE ( " d7s " ) ;
struct d7s {
void __iomem * regs ;
bool flipped ;
} ;
struct d7s * d7s_device ;
2005-04-17 02:20:36 +04:00
/*
* Register block address - see header for details
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* | DP | ALARM | FLIP | 4 | 3 | 2 | 1 | 0 |
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* DP - Toggles decimal point on / off
* ALARM - Toggles " Alarm " LED green / red
* FLIP - Inverts display for upside - down mounted board
* bits 0 - 4 - 7 - segment display contents
*/
static atomic_t d7s_users = ATOMIC_INIT ( 0 ) ;
static int d7s_open ( struct inode * inode , struct file * f )
{
if ( D7S_MINOR ! = iminor ( inode ) )
return - ENODEV ;
2008-05-20 21:15:47 +04:00
cycle_kernel_lock ( ) ;
2005-04-17 02:20:36 +04:00
atomic_inc ( & d7s_users ) ;
return 0 ;
}
static int d7s_release ( struct inode * inode , struct file * f )
{
/* Reset flipped state to OBP default only if
* no other users have the device open and we
* are not operating in solaris - compat mode
*/
if ( atomic_dec_and_test ( & d7s_users ) & & ! sol_compat ) {
2008-08-30 05:01:58 +04:00
struct d7s * p = d7s_device ;
u8 regval = 0 ;
regval = readb ( p - > regs ) ;
if ( p - > flipped )
regval | = D7S_FLIP ;
else
regval & = ~ D7S_FLIP ;
writeb ( regval , p - > regs ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
2005-11-08 01:13:01 +03:00
static long d7s_ioctl ( struct file * file , unsigned int cmd , unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2008-08-30 05:01:58 +04:00
struct d7s * p = d7s_device ;
u8 regs = readb ( p - > regs ) ;
2005-11-11 08:14:16 +03:00
int error = 0 ;
2008-08-30 05:01:58 +04:00
u8 ireg = 0 ;
2005-04-17 02:20:36 +04:00
2006-12-08 13:37:36 +03:00
if ( D7S_MINOR ! = iminor ( file - > f_path . dentry - > d_inode ) )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2005-11-08 01:13:01 +03:00
lock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
case D7SIOCWR :
2008-08-30 05:01:58 +04:00
/* assign device register values we mask-out D7S_FLIP
* if in sol_compat mode
2005-04-17 02:20:36 +04:00
*/
2005-11-08 01:13:01 +03:00
if ( get_user ( ireg , ( int __user * ) arg ) ) {
error = - EFAULT ;
break ;
}
2008-08-30 05:01:58 +04:00
if ( sol_compat ) {
if ( regs & D7S_FLIP )
ireg | = D7S_FLIP ;
else
ireg & = ~ D7S_FLIP ;
2005-04-17 02:20:36 +04:00
}
2008-08-30 05:01:58 +04:00
writeb ( ireg , p - > regs ) ;
2005-04-17 02:20:36 +04:00
break ;
case D7SIOCRD :
/* retrieve device register values
* NOTE : Solaris implementation returns D7S_FLIP bit
* as toggled by user , even though it does not honor it .
* This driver will not misinform you about the state
* of your hardware while in sol_compat mode
*/
2005-11-08 01:13:01 +03:00
if ( put_user ( regs , ( int __user * ) arg ) ) {
error = - EFAULT ;
break ;
}
2005-04-17 02:20:36 +04:00
break ;
case D7SIOCTM :
/* toggle device mode-- flip display orientation */
2008-08-30 05:01:58 +04:00
if ( regs & D7S_FLIP )
regs & = ~ D7S_FLIP ;
else
regs | = D7S_FLIP ;
writeb ( regs , p - > regs ) ;
2005-04-17 02:20:36 +04:00
break ;
} ;
2005-11-09 23:05:37 +03:00
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
2005-11-08 01:13:01 +03:00
return error ;
2005-04-17 02:20:36 +04:00
}
2007-02-12 11:55:34 +03:00
static const struct file_operations d7s_fops = {
2005-11-08 01:13:01 +03:00
. owner = THIS_MODULE ,
. unlocked_ioctl = d7s_ioctl ,
. compat_ioctl = d7s_ioctl ,
. open = d7s_open ,
. release = d7s_release ,
2005-04-17 02:20:36 +04:00
} ;
2008-08-30 05:01:58 +04:00
static struct miscdevice d7s_miscdev = {
. minor = D7S_MINOR ,
. name = DRIVER_NAME ,
. fops = & d7s_fops
} ;
2005-04-17 02:20:36 +04:00
2008-08-30 05:01:58 +04:00
static int __devinit d7s_probe ( struct of_device * op ,
const struct of_device_id * match )
2005-04-17 02:20:36 +04:00
{
2008-08-30 05:01:58 +04:00
struct device_node * opts ;
int err = - EINVAL ;
struct d7s * p ;
u8 regs ;
if ( d7s_device )
goto out ;
p = kzalloc ( sizeof ( * p ) , GFP_KERNEL ) ;
err = - ENOMEM ;
if ( ! p )
goto out ;
p - > regs = of_ioremap ( & op - > resource [ 0 ] , 0 , sizeof ( u8 ) , " d7s " ) ;
if ( ! p - > regs ) {
printk ( KERN_ERR PFX " Cannot map chip registers \n " ) ;
goto out_free ;
2005-04-17 02:20:36 +04:00
}
2008-08-30 05:01:58 +04:00
err = misc_register ( & d7s_miscdev ) ;
if ( err ) {
printk ( KERN_ERR PFX " Unable to acquire miscdevice minor %i \n " ,
D7S_MINOR ) ;
goto out_iounmap ;
2005-04-17 02:20:36 +04:00
}
2008-08-30 05:01:58 +04:00
/* OBP option "d7s-flipped?" is honored as default for the
* device , and reset default when detached
2005-04-17 02:20:36 +04:00
*/
2008-08-30 05:01:58 +04:00
regs = readb ( p - > regs ) ;
opts = of_find_node_by_path ( " /options " ) ;
if ( opts & &
of_get_property ( opts , " d7s-flipped? " , NULL ) )
p - > flipped = true ;
if ( p - > flipped )
regs | = D7S_FLIP ;
else
regs & = ~ D7S_FLIP ;
writeb ( regs , p - > regs ) ;
2009-01-07 00:20:38 +03:00
printk ( KERN_INFO PFX " 7-Segment Display%s at [%s:0x%llx] %s \n " ,
2008-08-30 05:01:58 +04:00
op - > node - > full_name ,
( regs & D7S_FLIP ) ? " (FLIPPED) " : " " ,
op - > resource [ 0 ] . start ,
sol_compat ? " in sol_compat mode " : " " ) ;
dev_set_drvdata ( & op - > dev , p ) ;
d7s_device = p ;
err = 0 ;
out :
return err ;
out_iounmap :
of_iounmap ( & op - > resource [ 0 ] , p - > regs , sizeof ( u8 ) ) ;
out_free :
kfree ( p ) ;
goto out ;
2005-04-17 02:20:36 +04:00
}
2008-08-30 05:01:58 +04:00
static int __devexit d7s_remove ( struct of_device * op )
2005-04-17 02:20:36 +04:00
{
2008-08-30 05:01:58 +04:00
struct d7s * p = dev_get_drvdata ( & op - > dev ) ;
u8 regs = readb ( p - > regs ) ;
2005-04-17 02:20:36 +04:00
/* Honor OBP d7s-flipped? unless operating in solaris-compat mode */
2008-08-30 05:01:58 +04:00
if ( sol_compat ) {
if ( p - > flipped )
regs | = D7S_FLIP ;
else
regs & = ~ D7S_FLIP ;
writeb ( regs , p - > regs ) ;
2005-04-17 02:20:36 +04:00
}
misc_deregister ( & d7s_miscdev ) ;
2008-08-30 05:01:58 +04:00
of_iounmap ( & op - > resource [ 0 ] , p - > regs , sizeof ( u8 ) ) ;
kfree ( p ) ;
return 0 ;
}
2008-08-31 12:23:17 +04:00
static const struct of_device_id d7s_match [ ] = {
2008-08-30 05:01:58 +04:00
{
. name = " display7seg " ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , d7s_match ) ;
static struct of_platform_driver d7s_driver = {
. name = DRIVER_NAME ,
. match_table = d7s_match ,
. probe = d7s_probe ,
. remove = __devexit_p ( d7s_remove ) ,
} ;
static int __init d7s_init ( void )
{
return of_register_driver ( & d7s_driver , & of_bus_type ) ;
}
static void __exit d7s_exit ( void )
{
of_unregister_driver ( & d7s_driver ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( d7s_init ) ;
2008-08-30 05:01:58 +04:00
module_exit ( d7s_exit ) ;