linux/drivers/macintosh/ans-lcd.c
<<
>>
Prefs
   1/*
   2 * /dev/lcd driver for Apple Network Servers.
   3 */
   4
   5#include <linux/types.h>
   6#include <linux/smp_lock.h>
   7#include <linux/errno.h>
   8#include <linux/kernel.h>
   9#include <linux/miscdevice.h>
  10#include <linux/fcntl.h>
  11#include <linux/init.h>
  12#include <linux/delay.h>
  13#include <linux/fs.h>
  14
  15#include <asm/uaccess.h>
  16#include <asm/sections.h>
  17#include <asm/prom.h>
  18#include <asm/io.h>
  19
  20#include "ans-lcd.h"
  21
  22#define ANSLCD_ADDR             0xf301c000
  23#define ANSLCD_CTRL_IX 0x00
  24#define ANSLCD_DATA_IX 0x10
  25
  26static unsigned long anslcd_short_delay = 80;
  27static unsigned long anslcd_long_delay = 3280;
  28static volatile unsigned char __iomem *anslcd_ptr;
  29
  30#undef DEBUG
  31
  32static void
  33anslcd_write_byte_ctrl ( unsigned char c )
  34{
  35#ifdef DEBUG
  36        printk(KERN_DEBUG "LCD: CTRL byte: %02x\n",c);
  37#endif
  38        out_8(anslcd_ptr + ANSLCD_CTRL_IX, c);
  39        switch(c) {
  40                case 1:
  41                case 2:
  42                case 3:
  43                        udelay(anslcd_long_delay); break;
  44                default: udelay(anslcd_short_delay);
  45        }
  46}
  47
  48static void
  49anslcd_write_byte_data ( unsigned char c )
  50{
  51        out_8(anslcd_ptr + ANSLCD_DATA_IX, c);
  52        udelay(anslcd_short_delay);
  53}
  54
  55static ssize_t
  56anslcd_write( struct file * file, const char __user * buf, 
  57                                size_t count, loff_t *ppos )
  58{
  59        const char __user *p = buf;
  60        int i;
  61
  62#ifdef DEBUG
  63        printk(KERN_DEBUG "LCD: write\n");
  64#endif
  65
  66        if (!access_ok(VERIFY_READ, buf, count))
  67                return -EFAULT;
  68        for ( i = *ppos; count > 0; ++i, ++p, --count ) 
  69        {
  70                char c;
  71                __get_user(c, p);
  72                anslcd_write_byte_data( c );
  73        }
  74        *ppos = i;
  75        return p - buf;
  76}
  77
  78static int
  79anslcd_ioctl( struct inode * inode, struct file * file,
  80                                unsigned int cmd, unsigned long arg )
  81{
  82        char ch, __user *temp;
  83
  84#ifdef DEBUG
  85        printk(KERN_DEBUG "LCD: ioctl(%d,%d)\n",cmd,arg);
  86#endif
  87
  88        switch ( cmd )
  89        {
  90        case ANSLCD_CLEAR:
  91                anslcd_write_byte_ctrl ( 0x38 );
  92                anslcd_write_byte_ctrl ( 0x0f );
  93                anslcd_write_byte_ctrl ( 0x06 );
  94                anslcd_write_byte_ctrl ( 0x01 );
  95                anslcd_write_byte_ctrl ( 0x02 );
  96                return 0;
  97        case ANSLCD_SENDCTRL:
  98                temp = (char __user *) arg;
  99                __get_user(ch, temp);
 100                for (; ch; temp++) { /* FIXME: This is ugly, but should work, as a \0 byte is not a valid command code */
 101                        anslcd_write_byte_ctrl ( ch );
 102                        __get_user(ch, temp);
 103                }
 104                return 0;
 105        case ANSLCD_SETSHORTDELAY:
 106                if (!capable(CAP_SYS_ADMIN))
 107                        return -EACCES;
 108                anslcd_short_delay=arg;
 109                return 0;
 110        case ANSLCD_SETLONGDELAY:
 111                if (!capable(CAP_SYS_ADMIN))
 112                        return -EACCES;
 113                anslcd_long_delay=arg;
 114                return 0;
 115        default:
 116                return -EINVAL;
 117        }
 118}
 119
 120static int
 121anslcd_open( struct inode * inode, struct file * file )
 122{
 123        cycle_kernel_lock();
 124        return 0;
 125}
 126
 127const struct file_operations anslcd_fops = {
 128        .write  = anslcd_write,
 129        .ioctl  = anslcd_ioctl,
 130        .open   = anslcd_open,
 131};
 132
 133static struct miscdevice anslcd_dev = {
 134        ANSLCD_MINOR,
 135        "anslcd",
 136        &anslcd_fops
 137};
 138
 139const char anslcd_logo[] =      "********************"  /* Line #1 */
 140                                "*      LINUX!      *"  /* Line #3 */
 141                                "*    Welcome to    *"  /* Line #2 */
 142                                "********************"; /* Line #4 */
 143
 144static int __init
 145anslcd_init(void)
 146{
 147        int a;
 148        int retval;
 149        struct device_node* node;
 150
 151        node = of_find_node_by_name(NULL, "lcd");
 152        if (!node || !node->parent || strcmp(node->parent->name, "gc")) {
 153                of_node_put(node);
 154                return -ENODEV;
 155        }
 156        of_node_put(node);
 157
 158        anslcd_ptr = ioremap(ANSLCD_ADDR, 0x20);
 159        
 160        retval = misc_register(&anslcd_dev);
 161        if(retval < 0){
 162                printk(KERN_INFO "LCD: misc_register failed\n");
 163                iounmap(anslcd_ptr);
 164                return retval;
 165        }
 166
 167#ifdef DEBUG
 168        printk(KERN_DEBUG "LCD: init\n");
 169#endif
 170
 171        anslcd_write_byte_ctrl ( 0x38 );
 172        anslcd_write_byte_ctrl ( 0x0c );
 173        anslcd_write_byte_ctrl ( 0x06 );
 174        anslcd_write_byte_ctrl ( 0x01 );
 175        anslcd_write_byte_ctrl ( 0x02 );
 176        for(a=0;a<80;a++) {
 177                anslcd_write_byte_data(anslcd_logo[a]);
 178        }
 179        return 0;
 180}
 181
 182static void __exit
 183anslcd_exit(void)
 184{
 185        misc_deregister(&anslcd_dev);
 186        iounmap(anslcd_ptr);
 187}
 188
 189module_init(anslcd_init);
 190module_exit(anslcd_exit);
 191