2005-04-17 02:20:36 +04:00
/* apc - Driver implementation for power management functions
* of Aurora Personality Chip ( APC ) on SPARCstation - 4 / 5 and
* derivatives .
*
* Copyright ( c ) 2002 Eric Brower ( ebrower @ usa . net )
*/
# include <linux/kernel.h>
# include <linux/fs.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/miscdevice.h>
2008-05-20 21:16:48 +04:00
# include <linux/smp_lock.h>
2005-04-17 02:20:36 +04:00
# include <linux/pm.h>
2008-08-27 13:45:36 +04:00
# include <linux/of.h>
# include <linux/of_device.h>
2005-04-17 02:20:36 +04:00
# include <asm/io.h>
# include <asm/oplib.h>
# include <asm/uaccess.h>
# include <asm/auxio.h>
# include <asm/apc.h>
/* Debugging
*
* # define APC_DEBUG_LED
*/
# define APC_MINOR MISC_DYNAMIC_MINOR
# define APC_OBPNAME "power-management"
# define APC_DEVNAME "apc"
2008-08-27 13:45:36 +04:00
static u8 __iomem * regs ;
2008-10-22 08:55:33 +04:00
static int apc_no_idle __devinitdata = 0 ;
2005-04-17 02:20:36 +04:00
2008-08-27 13:45:36 +04:00
# define apc_readb(offs) (sbus_readb(regs+offs))
2005-04-17 02:20:36 +04:00
# define apc_writeb(val, offs) (sbus_writeb(val, regs+offs))
/* Specify "apc=noidle" on the kernel command line to
* disable APC CPU standby support . Certain prototype
* systems ( SPARCstation - Fox ) do not play well with APC
* CPU idle , so disable this if your system has APC and
* crashes randomly .
*/
static int __init apc_setup ( char * str )
{
if ( ! strncmp ( str , " noidle " , strlen ( " noidle " ) ) ) {
apc_no_idle = 1 ;
return 1 ;
}
return 0 ;
}
__setup ( " apc= " , apc_setup ) ;
/*
* CPU idle callback function
* See . . . / arch / sparc / kernel / process . c
*/
2008-06-05 22:40:58 +04:00
static void apc_swift_idle ( void )
2005-04-17 02:20:36 +04:00
{
# ifdef APC_DEBUG_LED
set_auxio ( 0x00 , AUXIO_LED ) ;
# endif
apc_writeb ( apc_readb ( APC_IDLE_REG ) | APC_IDLE_ON , APC_IDLE_REG ) ;
# ifdef APC_DEBUG_LED
set_auxio ( AUXIO_LED , 0x00 ) ;
# endif
}
2008-08-27 13:45:36 +04:00
static inline void apc_free ( struct of_device * op )
2005-04-17 02:20:36 +04:00
{
2008-08-27 13:45:36 +04:00
of_iounmap ( & op - > resource [ 0 ] , regs , resource_size ( & op - > resource [ 0 ] ) ) ;
2005-04-17 02:20:36 +04:00
}
static int apc_open ( struct inode * inode , struct file * f )
{
2008-05-20 21:16:48 +04:00
cycle_kernel_lock ( ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int apc_release ( struct inode * inode , struct file * f )
{
return 0 ;
}
2008-07-15 09:12:29 +04:00
static long apc_ioctl ( struct file * f , unsigned int cmd , unsigned long __arg )
2005-04-17 02:20:36 +04:00
{
__u8 inarg , __user * arg ;
arg = ( __u8 __user * ) __arg ;
2008-07-15 09:12:29 +04:00
lock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
case APCIOCGFANCTL :
2008-07-15 09:12:29 +04:00
if ( put_user ( apc_readb ( APC_FANCTL_REG ) & APC_REGMASK , arg ) ) {
unlock_kernel ( ) ;
return - EFAULT ;
}
2005-04-17 02:20:36 +04:00
break ;
case APCIOCGCPWR :
2008-07-15 09:12:29 +04:00
if ( put_user ( apc_readb ( APC_CPOWER_REG ) & APC_REGMASK , arg ) ) {
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2008-07-15 09:12:29 +04:00
}
2005-04-17 02:20:36 +04:00
break ;
case APCIOCGBPORT :
2008-07-15 09:12:29 +04:00
if ( put_user ( apc_readb ( APC_BPORT_REG ) & APC_BPMASK , arg ) ) {
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2008-07-15 09:12:29 +04:00
}
2005-04-17 02:20:36 +04:00
break ;
case APCIOCSFANCTL :
2008-07-15 09:12:29 +04:00
if ( get_user ( inarg , arg ) ) {
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2008-07-15 09:12:29 +04:00
}
2005-04-17 02:20:36 +04:00
apc_writeb ( inarg & APC_REGMASK , APC_FANCTL_REG ) ;
break ;
case APCIOCSCPWR :
2008-07-15 09:12:29 +04:00
if ( get_user ( inarg , arg ) ) {
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2008-07-15 09:12:29 +04:00
}
2005-04-17 02:20:36 +04:00
apc_writeb ( inarg & APC_REGMASK , APC_CPOWER_REG ) ;
break ;
case APCIOCSBPORT :
2008-07-15 09:12:29 +04:00
if ( get_user ( inarg , arg ) ) {
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2008-07-15 09:12:29 +04:00
}
2005-04-17 02:20:36 +04:00
apc_writeb ( inarg & APC_BPMASK , APC_BPORT_REG ) ;
break ;
default :
2008-07-15 09:12:29 +04:00
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
} ;
2008-07-15 09:12:29 +04:00
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-02-12 11:55:31 +03:00
static const struct file_operations apc_fops = {
2008-07-15 09:12:29 +04:00
. unlocked_ioctl = apc_ioctl ,
. open = apc_open ,
. release = apc_release ,
2005-04-17 02:20:36 +04:00
} ;
static struct miscdevice apc_miscdev = { APC_MINOR , APC_DEVNAME , & apc_fops } ;
2008-08-27 13:45:36 +04:00
static int __devinit apc_probe ( struct of_device * op ,
const struct of_device_id * match )
2005-04-17 02:20:36 +04:00
{
2008-08-27 13:45:36 +04:00
int err ;
2005-04-17 02:20:36 +04:00
2008-08-27 13:45:36 +04:00
regs = of_ioremap ( & op - > resource [ 0 ] , 0 ,
resource_size ( & op - > resource [ 0 ] ) , APC_OBPNAME ) ;
if ( ! regs ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " %s: unable to map registers \n " , APC_DEVNAME ) ;
return - ENODEV ;
}
2008-08-27 13:45:36 +04:00
err = misc_register ( & apc_miscdev ) ;
if ( err ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " %s: unable to register device \n " , APC_DEVNAME ) ;
2008-08-27 13:45:36 +04:00
apc_free ( op ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
/* Assign power management IDLE handler */
2008-08-27 13:45:36 +04:00
if ( ! apc_no_idle )
2005-04-17 02:20:36 +04:00
pm_idle = apc_swift_idle ;
printk ( KERN_INFO " %s: power management initialized%s \n " ,
2008-08-27 13:45:36 +04:00
APC_DEVNAME , apc_no_idle ? " (CPU idle disabled) " : " " ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-08-31 12:23:17 +04:00
static struct of_device_id __initdata apc_match [ ] = {
2008-08-27 13:45:36 +04:00
{
. name = APC_OBPNAME ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , apc_match ) ;
static struct of_platform_driver apc_driver = {
. name = " apc " ,
. match_table = apc_match ,
. probe = apc_probe ,
} ;
static int __init apc_init ( void )
{
return of_register_driver ( & apc_driver , & of_bus_type ) ;
}
2005-04-17 02:20:36 +04:00
/* This driver is not critical to the boot process
* and is easiest to ioremap when SBus is already
* initialized , so we install ourselves thusly :
*/
2008-08-27 13:45:36 +04:00
__initcall ( apc_init ) ;