/*
 * A simple LED driver for R2D-PLUS
 * (atomic read-and-write version)
 * Licensed under GPL version 2 only.
 */
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define LEDCTRL_DEV_MAJOR   240
#define LEDCTRL_DEV_NAME    "ledctrl"
#define R2DPLUS_LED_REGS    0xa4000036

static unsigned short status;     /* LED status */
DEFINE_SPINLOCK(update_lck);

static int ledctrl_open(struct inode *inode, struct file *filp) 
{
    return 0;
}

static ssize_t ledctrl_read(struct file *filp, char __user *buf,
			    size_t count, loff_t *ppos)
{

    spin_lock(&update_lck);

    if (copy_to_user(buf, &status, sizeof(status)))
	return -EFAULT;

    return sizeof(status);
}


static ssize_t ledctrl_write(struct file *filp, const char __user *buf,
			     size_t count, loff_t *ppos)
{
    unsigned short val;

    if (copy_from_user(&val, buf, sizeof(val)))
	return -EFAULT;

    val &= 0x00ffU;
    writew(val, R2DPLUS_LED_REGS);
    status = val;

    spin_unlock(&update_lck);

    return sizeof(val);
}

static int ledctrl_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static struct file_operations ledctrl_fops = {
    .open    = ledctrl_open,
    .read    = ledctrl_read,
    .write   = ledctrl_write,
    .release = ledctrl_release,
};

static int __init ledctrl_init(void)
{

    /* Turn off all LEDs */
    writel(0x0000U, R2DPLUS_LED_REGS);
    status = 0U;

    /* Register a device file */
    if (register_chrdev(LEDCTRL_DEV_MAJOR, 
			LEDCTRL_DEV_NAME, &ledctrl_fops)) {
	printk(KERN_ERR "Unable to register device.\n");
	return -EFAULT;
    }

    return 0;
}

static void __exit ledctrl_exit(void)
{
    /* Turn off all LEDs */
    writew(0x0000U, R2DPLUS_LED_REGS);

    unregister_chrdev(LEDCTRL_DEV_MAJOR, LEDCTRL_DEV_NAME);
    printk(KERN_INFO "Device %s unregistered.\n", LEDCTRL_DEV_NAME);
    return;
}

module_init(ledctrl_init);
module_exit(ledctrl_exit);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Katsuya Matsubara");
