/*-----------------------------------------------------------------------------
 * gpio-dev.c
 *
 * a device driver file for gpio
 *
 * Copyright 2005,2006 Sony Corporation
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 *---------------------------------------------------------------------------*/
#define __GPIO_DEV_C__

/*-----------------------------------------------------------------------------
 * Include file
 *---------------------------------------------------------------------------*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <asm/uaccess.h>

#include <asm/arch/sonymisc.h>
#include <asm/arch/gpio-core.h>
#include <asm/arch/gpio-dev.h>

//#define CHECK_TIME
#ifdef CHECK_TIME
#include <sony/instrumental.h>
#endif

/*-----------------------------------------------------------------------------
 * Change note
 *
 * 01.00.000    : First release
 * 01.00.001	: In struct sonymisc_device gpio_dev, "gd"member add.
 * 01.00.002    : MODULE_DESCRIPTION changed.
 * 01.01.002    : version up for kernel 2.6.11
 * 01.01.003    : changed GPIO_CLOSE ioctl code in gpio_dev_ioctl() function.
 * 01.01.004    : changed from "Proprietary" driver to "GPL" driver.
 * 01.01.005    : changed to use target_init() function of kernel(cxd4105).
 * 01.01.006    : limited target(cxd4105).
 * 01.01.007    : version up for kernel 2.6.22
*/

/*-----------------------------------------------------------------------------
 * Macro declaration
 *---------------------------------------------------------------------------*/
#define MYNAME		"gpio-dev"
#define MYVERSION	"01.01.007"

/*-----------------------------------------------------------------------------
 * Driver infomationin
 *---------------------------------------------------------------------------*/
#define GPIO_MINOR	1
#define GPIO_NAME	MYNAME

/*-----------------------------------------------------------------------------
 * Module infomation
 *---------------------------------------------------------------------------*/
MODULE_AUTHOR("Sony Corporation");
MODULE_DESCRIPTION("gpio dev driver");
MODULE_VERSION(MYVERSION);
MODULE_LICENSE("GPL");

/*-----------------------------------------------------------------------------
 * Compile switch
 *---------------------------------------------------------------------------*/
#define DEBUG_LEVEL 0

/*-[DEBUG_LEVEL 1]-------------------------------------------------
 * print value when function entry and exit                      */
#if(DEBUG_LEVEL == 1)
#define PDEBUG1(arg...)		{printk(arg);}
#define PDEBUG2(arg...)		{}
#define PDEBUG3(arg...)		{}
#define RETURN1(val,arg)	{printk(arg,val);return(val);}
#define RETURN2(val,arg)	{return(val);}
/*-[DEBUG_LEVEL 2]-------------------------------------------------
 * print value when all function(include inline) entry and exit  */
#elif(DEBUG_LEVEL == 2)
#define PDEBUG1(arg...)		{printk(arg);}
#define PDEBUG2(arg...)		{printk(arg);}
#define PDEBUG3(arg...)		{}
#define RETURN1(val,arg)	{printk(arg,val);return(val);}
#define RETURN2(val,arg)	{printk(arg,val);return(val);}
/*-[DEBUG_LEVEL 3]-------------------------------------------------
 * print value of variable                                       */
#elif(DEBUG_LEVEL == 3)
#define PDEBUG1(arg...)		{printk(arg);}
#define PDEBUG2(arg...)		{printk(arg);}
#define PDEBUG3(arg...)		{printk(arg);}
#define RETURN1(val,arg)	{printk(arg,val);return(val);}
#define RETURN2(val,arg)	{printk(arg,val);return(val);}
/*-[NO DEBUG]------------------------------------------------------
 * print nothing                                                 */
#else
#define PDEBUG1(arg...)		{}
#define PDEBUG2(arg...)		{}
#define PDEBUG3(arg...)		{}
#define RETURN1(val,arg)	{return(val);}
#define RETURN2(val,arg)	{return(val);}
#endif

/*-----------------------------------------------------------------------------
 * Function prototype declaration
 *---------------------------------------------------------------------------*/
/*static*/ int __init gpio_dev_init(void);
static void __exit gpio_dev_exit(void);

static int gpio_dev_open(struct inode *inode, struct file *file);
static int gpio_dev_release(struct inode *inode, struct file *file);
static int gpio_dev_ioctl(struct inode *inode, struct file *file, 
    unsigned int cmd, unsigned long arg);

/*-----------------------------------------------------------------------------
 * Variable declaration
 *---------------------------------------------------------------------------*/
static struct file_operations gpio_dev_fops = 
{
    .owner 	= THIS_MODULE,
    .open 	= gpio_dev_open,
    .release 	= gpio_dev_release,
    .ioctl 	= gpio_dev_ioctl,
};

static struct sonymisc_device gpio_dev = 
{
    .class	= DEV_CLASS_CHR,
    .minor	= GPIO_MINOR,
    .name	= GPIO_NAME,
    .fops	= &gpio_dev_fops,
    .gd		= NULL,
};

/*========================================================================
 *
 * Main function body
 *
 *======================================================================*/
/*------------------------------------------------------------------------
 * init_module
 *----------------------------------------------------------------------*/
/*static*/ int __init gpio_dev_init(void)
{
#ifdef CHECK_TIME
    u32 t = tick();
#endif
    int err;
    PDEBUG1(" "MYNAME"::gpio_dev_init() : Entry\n");

    err = sonymisc_register(&gpio_dev);

#ifdef CHECK_TIME
    tprintk(tick() - t, "done\n");
#endif
    RETURN1(err," "MYNAME"::gpio_dev_init() : Return(%d)\n");
}

/*------------------------------------------------------------------------
 * exit_module
 *----------------------------------------------------------------------*/
static void __exit gpio_dev_exit(void)
{
#ifdef CHECK_TIME
    u32 t = tick();
#endif
    PDEBUG1(" "MYNAME"::gpio_dev_exit() : Entry\n");

    sonymisc_deregister(&gpio_dev);

    PDEBUG1(" "MYNAME"::gpio_dev_exit() : Return\n");
#ifdef CHECK_TIME
    tprintk(tick() - t, "done\n");
#endif
    return;
}

/*------------------------------------------------------------------------
 * open
 *----------------------------------------------------------------------*/
static int gpio_dev_open(struct inode *inode, struct file *file)
{
    PDEBUG1(" "MYNAME"::gpio_dev_open() : Entry\n");
    RETURN1(0, " "MYNAME"::gpio_dev_open() : Return(%x)\n");
}

/*------------------------------------------------------------------------
 * release
 *----------------------------------------------------------------------*/
static int gpio_dev_release(struct inode *inode, struct file *file)
{
    PDEBUG1(" "MYNAME"::gpio_dev_release() : Entry\n");
    RETURN1(0, " "MYNAME"::gpio_dev_release() : Return(%x)\n");
}

/*------------------------------------------------------------------------
 * ioctl
 *----------------------------------------------------------------------*/
static int gpio_dev_ioctl(struct inode *inode, struct file *file, 
    unsigned int cmd, unsigned long arg)
{
    TGpio_ioctl_param param;
    PDEBUG1(" "MYNAME"::gpio_dev_ioctl(%d) : Entry\n", cmd);

    if(! arg){
        RETURN1(-EINVAL, " "MYNAME"::gpio_dev_ioctl() : Return(%x)\n");
    }
    if(copy_from_user((void*)&param, (const void __user *)arg, sizeof(TGpio_ioctl_param))){
        RETURN1(-EFAULT, " "MYNAME"::gpio_dev_ioctl() : Return(%x)\n");
    }
    PDEBUG3(" "MYNAME"::gpio_dev_ioctl(%d,%x,%x,%x,%x) : Call api\n", param.nr, param.io_func, param.val, param.callback_sense, param.callback_timing);
    switch(cmd){
    case GPIO_OPEN:
        param.result = gpio_open(param.nr);
        break;
    case GPIO_CLOSE:
        param.result = gpio_close(gpio_get_hndl(param.nr));
        break;
    case GPIO_WRITE:
        param.result = gpio_write(gpio_get_hndl(param.nr), param.val);
        break;
    case GPIO_READ:
        param.val = gpio_read(gpio_get_hndl(param.nr));
        if(param.val < 0){
            param.result = param.val;
        }else{
            param.result = 0;
        }
        break;
// for driver examination or check H/W in factory line
    case GPIO_SET_IO_FUNC:
        param.result = gpio_set_io_func(gpio_get_hndl(param.nr), 
            param.io_func, param.val);
        break;
    case GPIO_GET_IO_FUNC:
        param.result = gpio_get_io_func(gpio_get_hndl(param.nr), 
            &param.io_func);
        break;
    case GPIO_SET_CALLBACK_FUNC:
        param.result = gpio_set_callback_func(gpio_get_hndl(param.nr), 
            param.callback_sense, param.callback_timing);
        break;
    case GPIO_GET_CALLBACK_FUNC:
        param.result = gpio_get_callback_func(gpio_get_hndl(param.nr), 
            &param.callback_sense, &param.callback_timing);
        break;
    case GPIO_ENABLE_CALLBACK:
        param.result = gpio_enable_callback(gpio_get_hndl(param.nr));
        break;
    case GPIO_DISABLE_CALLBACK:
        param.result = gpio_disable_callback(gpio_get_hndl(param.nr));
        break;
    case GPIO_IS_ENABLE_CALLBACK:
        param.status = gpio_is_enable_callback(gpio_get_hndl(param.nr));
        if(param.status < 0){
            param.result = param.status;
        }else{
            param.result = 0;
        }
        break;
    case GPIO_ENABLE_WAKEUP:
        param.result = gpio_enable_wakeup(gpio_get_hndl(param.nr));
        break;
    case GPIO_DISBLE_WAKEUP:
        param.result = gpio_disable_wakeup(gpio_get_hndl(param.nr));
        break;
    case GPIO_IS_ENABLE_WAKEUP:
        param.status = gpio_is_enable_wakeup(gpio_get_hndl(param.nr));
        if(param.status < 0){
            param.result = param.status;
        }else{
            param.result = 0;
        }
        break;
    default:
        RETURN1(-ENOEXEC, " "MYNAME"::gpio_dev_ioctl() : Return(%x)\n");
    }
    PDEBUG3(" "MYNAME"::gpio_dev_ioctl(%d,%x,%x,%x,%x) : Result(%x), Status(%x)\n", param.nr, param.io_func, param.val, param.callback_sense, param.callback_timing, param.result, param.status);
    if(copy_to_user((void __user *)arg, (void*)&param, sizeof(TGpio_ioctl_param))){
        RETURN1(-EFAULT, " "MYNAME"::gpio_dev_ioctl() : Return(%x)\n");
    }
    if(param.result >= 0){
        RETURN1(0, " "MYNAME"::gpio_dev_ioctl() : Return(%x)\n");
    }else{
        RETURN1(param.result, " "MYNAME"::gpio_dev_ioctl() : Return(%x)\n");
    }
}

/*=============================================================================
 * Export symbols
 *===========================================================================*/
//module_init(gpio_dev_init);
module_exit(gpio_dev_exit);
