2005-04-17 02:20:36 +04:00
/*
* / dev / lcd driver for Apple Network Servers .
*/
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/miscdevice.h>
# include <linux/fcntl.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/fs.h>
# include <asm/uaccess.h>
# include <asm/sections.h>
# include <asm/prom.h>
# include <asm/io.h>
2007-08-20 17:50:28 +04:00
# include "ans-lcd.h"
2005-04-17 02:20:36 +04:00
# define ANSLCD_ADDR 0xf301c000
# define ANSLCD_CTRL_IX 0x00
# define ANSLCD_DATA_IX 0x10
static unsigned long anslcd_short_delay = 80 ;
static unsigned long anslcd_long_delay = 3280 ;
static volatile unsigned char __iomem * anslcd_ptr ;
2009-10-10 15:38:57 +04:00
static DEFINE_MUTEX ( anslcd_mutex ) ;
2005-04-17 02:20:36 +04:00
# undef DEBUG
2005-09-17 19:36:54 +04:00
static void
2005-04-17 02:20:36 +04:00
anslcd_write_byte_ctrl ( unsigned char c )
{
# ifdef DEBUG
printk ( KERN_DEBUG " LCD: CTRL byte: %02x \n " , c ) ;
# endif
out_8 ( anslcd_ptr + ANSLCD_CTRL_IX , c ) ;
switch ( c ) {
case 1 :
case 2 :
case 3 :
udelay ( anslcd_long_delay ) ; break ;
default : udelay ( anslcd_short_delay ) ;
}
}
2005-09-17 19:36:54 +04:00
static void
2005-04-17 02:20:36 +04:00
anslcd_write_byte_data ( unsigned char c )
{
out_8 ( anslcd_ptr + ANSLCD_DATA_IX , c ) ;
udelay ( anslcd_short_delay ) ;
}
2005-09-17 19:36:54 +04:00
static ssize_t
2005-04-17 02:20:36 +04:00
anslcd_write ( struct file * file , const char __user * buf ,
size_t count , loff_t * ppos )
{
const char __user * p = buf ;
int i ;
# ifdef DEBUG
printk ( KERN_DEBUG " LCD: write \n " ) ;
# endif
if ( ! access_ok ( VERIFY_READ , buf , count ) )
return - EFAULT ;
2009-10-10 15:38:57 +04:00
mutex_lock ( & anslcd_mutex ) ;
2005-04-17 02:20:36 +04:00
for ( i = * ppos ; count > 0 ; + + i , + + p , - - count )
{
char c ;
__get_user ( c , p ) ;
anslcd_write_byte_data ( c ) ;
}
2009-10-10 15:38:57 +04:00
mutex_unlock ( & anslcd_mutex ) ;
2005-04-17 02:20:36 +04:00
* ppos = i ;
return p - buf ;
}
2009-10-10 15:38:57 +04:00
static long
anslcd_ioctl ( struct file * file , unsigned int cmd , unsigned long arg )
2005-04-17 02:20:36 +04:00
{
char ch , __user * temp ;
2009-10-10 15:38:57 +04:00
long ret = 0 ;
2005-04-17 02:20:36 +04:00
# ifdef DEBUG
printk ( KERN_DEBUG " LCD: ioctl(%d,%d) \n " , cmd , arg ) ;
# endif
2009-10-10 15:38:57 +04:00
mutex_lock ( & anslcd_mutex ) ;
2005-04-17 02:20:36 +04:00
switch ( cmd )
{
case ANSLCD_CLEAR :
anslcd_write_byte_ctrl ( 0x38 ) ;
anslcd_write_byte_ctrl ( 0x0f ) ;
anslcd_write_byte_ctrl ( 0x06 ) ;
anslcd_write_byte_ctrl ( 0x01 ) ;
anslcd_write_byte_ctrl ( 0x02 ) ;
2009-10-10 15:38:57 +04:00
break ;
2005-04-17 02:20:36 +04:00
case ANSLCD_SENDCTRL :
temp = ( char __user * ) arg ;
__get_user ( ch , temp ) ;
for ( ; ch ; temp + + ) { /* FIXME: This is ugly, but should work, as a \0 byte is not a valid command code */
anslcd_write_byte_ctrl ( ch ) ;
__get_user ( ch , temp ) ;
}
2009-10-10 15:38:57 +04:00
break ;
2005-04-17 02:20:36 +04:00
case ANSLCD_SETSHORTDELAY :
if ( ! capable ( CAP_SYS_ADMIN ) )
2009-10-10 15:38:57 +04:00
ret = - EACCES ;
else
anslcd_short_delay = arg ;
break ;
2005-04-17 02:20:36 +04:00
case ANSLCD_SETLONGDELAY :
if ( ! capable ( CAP_SYS_ADMIN ) )
2009-10-10 15:38:57 +04:00
ret = - EACCES ;
else
anslcd_long_delay = arg ;
break ;
2005-04-17 02:20:36 +04:00
default :
2009-10-10 15:38:57 +04:00
ret = - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2009-10-10 15:38:57 +04:00
mutex_unlock ( & anslcd_mutex ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
2005-09-17 19:36:54 +04:00
static int
2005-04-17 02:20:36 +04:00
anslcd_open ( struct inode * inode , struct file * file )
{
return 0 ;
}
2007-02-12 11:55:33 +03:00
const struct file_operations anslcd_fops = {
2009-10-10 15:38:57 +04:00
. write = anslcd_write ,
. unlocked_ioctl = anslcd_ioctl ,
. open = anslcd_open ,
2005-04-17 02:20:36 +04:00
} ;
static struct miscdevice anslcd_dev = {
ANSLCD_MINOR ,
" anslcd " ,
& anslcd_fops
} ;
const char anslcd_logo [ ] = " ******************** " /* Line #1 */
" * LINUX! * " /* Line #3 */
" * Welcome to * " /* Line #2 */
" ******************** " ; /* Line #4 */
static int __init
anslcd_init ( void )
{
int a ;
int retval ;
struct device_node * node ;
2007-04-24 07:53:04 +04:00
node = of_find_node_by_name ( NULL , " lcd " ) ;
if ( ! node | | ! node - > parent | | strcmp ( node - > parent - > name , " gc " ) ) {
of_node_put ( node ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2007-04-24 07:53:04 +04:00
}
of_node_put ( node ) ;
2005-04-17 02:20:36 +04:00
anslcd_ptr = ioremap ( ANSLCD_ADDR , 0x20 ) ;
retval = misc_register ( & anslcd_dev ) ;
if ( retval < 0 ) {
printk ( KERN_INFO " LCD: misc_register failed \n " ) ;
iounmap ( anslcd_ptr ) ;
return retval ;
}
# ifdef DEBUG
printk ( KERN_DEBUG " LCD: init \n " ) ;
# endif
2009-10-10 15:38:57 +04:00
mutex_lock ( & anslcd_mutex ) ;
2005-04-17 02:20:36 +04:00
anslcd_write_byte_ctrl ( 0x38 ) ;
anslcd_write_byte_ctrl ( 0x0c ) ;
anslcd_write_byte_ctrl ( 0x06 ) ;
anslcd_write_byte_ctrl ( 0x01 ) ;
anslcd_write_byte_ctrl ( 0x02 ) ;
for ( a = 0 ; a < 80 ; a + + ) {
anslcd_write_byte_data ( anslcd_logo [ a ] ) ;
}
2009-10-10 15:38:57 +04:00
mutex_unlock ( & anslcd_mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void __exit
anslcd_exit ( void )
{
misc_deregister ( & anslcd_dev ) ;
iounmap ( anslcd_ptr ) ;
}
module_init ( anslcd_init ) ;
module_exit ( anslcd_exit ) ;