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/errno.h>
   7#include <linux/kernel.h>
   8#include <linux/miscdevice.h>
   9#include <linux/fcntl.h>
  10#include <linux/init.h>
  11#include <linux/delay.h>
  12#include <linux/fs.h>
  13
  14#include <asm/uaccess.h>
  15#include <asm/sections.h>
  16#include <asm/prom.h>
  17#include <asm/io.h>
  18
  19#include "ans-lcd.h"
  20
  21#define ANSLCD_ADDR             0xf301c000
  22#define ANSLCD_CTRL_IX 0x00
  23#define ANSLCD_DATA_IX 0x10
  24
  25static unsigned long anslcd_short_delay = 80;
  26static unsigned long anslcd_long_delay = 3280;
  27static volatile unsigned char __iomem *anslcd_ptr;
  28static DEFINE_MUTEX(anslcd_mutex);
  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
  69        mutex_lock(&anslcd_mutex);
  70        for ( i = *ppos; count > 0; ++i, ++p, --count ) 
  71        {
  72                char c;
  73                __get_user(c, p);
  74                anslcd_write_byte_data( c );
  75        }
  76        mutex_unlock(&anslcd_mutex);
  77        *ppos = i;
  78        return p - buf;
  79}
  80
  81static long
  82anslcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  83{
  84        char ch, __user *temp;
  85        long ret = 0;
  86
  87#ifdef DEBUG
  88        printk(KERN_DEBUG "LCD: ioctl(%d,%d)\n",cmd,arg);
  89#endif
  90
  91        mutex_lock(&anslcd_mutex);
  92
  93        switch ( cmd )
  94        {
  95        case ANSLCD_CLEAR:
  96                anslcd_write_byte_ctrl ( 0x38 );
  97                anslcd_write_byte_ctrl ( 0x0f );
  98                anslcd_write_byte_ctrl ( 0x06 );
  99                anslcd_write_byte_ctrl ( 0x01 );
 100                anslcd_write_byte_ctrl ( 0x02 );
 101                break;
 102        case ANSLCD_SENDCTRL:
 103                temp = (char __user *) arg;
 104                __get_user(ch, temp);
 105                for (; ch; temp++) { /* FIXME: This is ugly, but should work, as a \0 byte is not a valid command code */
 106                        anslcd_write_byte_ctrl ( ch );
 107                        __get_user(ch, temp);
 108                }
 109                break;
 110        case ANSLCD_SETSHORTDELAY:
 111                if (!capable(CAP_SYS_ADMIN))
 112                        ret =-EACCES;
 113                else
 114                        anslcd_short_delay=arg;
 115                break;
 116        case ANSLCD_SETLONGDELAY:
 117                if (!capable(CAP_SYS_ADMIN))
 118                        ret = -EACCES;
 119                else
 120                        anslcd_long_delay=arg;
 121                break;
 122        default:
 123                ret = -EINVAL;
 124        }
 125
 126        mutex_unlock(&anslcd_mutex);
 127        return ret;
 128}
 129
 130static int
 131anslcd_open( struct inode * inode, struct file * file )
 132{
 133        return 0;
 134}
 135
 136const struct file_operations anslcd_fops = {
 137        .write          = anslcd_write,
 138        .unlocked_ioctl = anslcd_ioctl,
 139        .open           = anslcd_open,
 140        .llseek         = default_llseek,
 141};
 142
 143static struct miscdevice anslcd_dev = {
 144        ANSLCD_MINOR,
 145        "anslcd",
 146        &anslcd_fops
 147};
 148
 149const char anslcd_logo[] =      "********************"  /* Line #1 */
 150                                "*      LINUX!      *"  /* Line #3 */
 151                                "*    Welcome to    *"  /* Line #2 */
 152                                "********************"; /* Line #4 */
 153
 154static int __init
 155anslcd_init(void)
 156{
 157        int a;
 158        int retval;
 159        struct device_node* node;
 160
 161        node = of_find_node_by_name(NULL, "lcd");
 162        if (!node || !node->parent || strcmp(node->parent->name, "gc")) {
 163                of_node_put(node);
 164                return -ENODEV;
 165        }
 166        of_node_put(node);
 167
 168        anslcd_ptr = ioremap(ANSLCD_ADDR, 0x20);
 169        
 170        retval = misc_register(&anslcd_dev);
 171        if(retval < 0){
 172                printk(KERN_INFO "LCD: misc_register failed\n");
 173                iounmap(anslcd_ptr);
 174                return retval;
 175        }
 176
 177#ifdef DEBUG
 178        printk(KERN_DEBUG "LCD: init\n");
 179#endif
 180
 181        mutex_lock(&anslcd_mutex);
 182        anslcd_write_byte_ctrl ( 0x38 );
 183        anslcd_write_byte_ctrl ( 0x0c );
 184        anslcd_write_byte_ctrl ( 0x06 );
 185        anslcd_write_byte_ctrl ( 0x01 );
 186        anslcd_write_byte_ctrl ( 0x02 );
 187        for(a=0;a<80;a++) {
 188                anslcd_write_byte_data(anslcd_logo[a]);
 189        }
 190        mutex_unlock(&anslcd_mutex);
 191        return 0;
 192}
 193
 194static void __exit
 195anslcd_exit(void)
 196{
 197        misc_deregister(&anslcd_dev);
 198        iounmap(anslcd_ptr);
 199}
 200
 201module_init(anslcd_init);
 202module_exit(anslcd_exit);
 203