/*-----------------------------------------------------------------------------
 * gpio-core.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_CORE_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 <linux/sched.h>

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

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

/*-----------------------------------------------------------------------------
 * Change note
 *
 * 01.00.000    : First release
 * 01.01.000    : callback input-parameter changed.
 * 01.01.001    : MODULE_DESCRIPTION changed.
 * 01.01.002    : added clear code of CGpioSel pointer in Vclose().
 *                version up for kernel 2.6.11
 * 01.02.002    : added suspend / resume(hot boot) functions.
 * 01.02.003    : changed Vregister_callback() function.
 * 01.02.004    : changed from "Proprietary" driver to "GPL" driver.
 *              : removed static declaration of pGpioCore.
 * 01.02.005    : changed to use target_init() function of kernel(cxd4105).
 * 01.02.006    : added time check codes for suspend / resume.
 *                limited target(cxd4105).
 * 01.02.007    : changed error handling in Vsuspend() / Vresume() functions.
 * 01.02.008    : version up for kernel 2.6.22
*/

/*-----------------------------------------------------------------------------
 * Macro declaration
 *---------------------------------------------------------------------------*/
#define MYNAME		"gpio-core"
#define MYVERSION	"01.02.008"

/*-----------------------------------------------------------------------------
 * Module infomation
 *---------------------------------------------------------------------------*/
MODULE_AUTHOR("Sony Corporation");
MODULE_DESCRIPTION("gpio core 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

/*-----------------------------------------------------------------------------
 * Extern function declaration
 *---------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------
 * Function prototype declaration
 *---------------------------------------------------------------------------*/
/* virtual function */
static int Vopen(IN unsigned int nr);
static int Vclose(IN int hndl);
static int Vset_io_func(IN int hndl, IN unsigned char func, 
    IN unsigned int val);
static int Vget_io_func(IN int hndl, OUT unsigned char *func);
static int Vwrite(IN int hndl, IN unsigned int val);
static int Vread(IN int hndl);
static int Vset_callback_func(IN int hndl, IN unsigned char sense, 
    IN unsigned char timing);
static int Vget_callback_func(IN int hndl, OUT unsigned char *sense, 
    OUT unsigned char *timing);
static int Venable_callback(IN int hndl);
static int Vdisable_callback(IN int hndl);
static int Vis_enable_callback(IN int hndl);
static int Vregister_callback(IN int hndl, IN struct gpio_callback *callback);
static int Vunregister_callback(IN int hndl);
static int Venable_wakeup(IN int hndl);
static int Vdisable_wakeup(IN int hndl);
static int Vis_enable_wakeup(IN int hndl);
static int Vattach_plugin(IN CGpioPlugin *plugin);
static int Vdetach_plugin(IN CGpioPlugin *plugin);
static void Vhandler(IN unsigned int nr);
static int Vget_hndl(IN unsigned int nr);
static int Vprobe(IN struct amba_device *dev, IN void *id);
static int Vsuspend(IN struct amba_device *dev, IN pm_message_t state);
static int Vresume(IN struct amba_device *dev);

static void tasklet(unsigned long nr);
static __inline__ int __is_open(IN unsigned int nr);
static __inline__ int __is_enable_nr(IN unsigned int nr);
static __inline__ int __is_enable_hndl(IN int hndl, IN unsigned int nr);
static __inline__ int __get_hndl(IN unsigned int nr);
static __inline__ int __get_nr(IN int hndl, OUT unsigned int *nr);

/*static*/ int __init gpio_core_init(void);
static void __exit gpio_core_exit(void);

/*-----------------------------------------------------------------------------
 * Variable declaration
 *---------------------------------------------------------------------------*/
static TGpioPublicApi Vapi =
{
    Vopen,
    Vclose,
    Vset_io_func,
    Vget_io_func,
    Vwrite,
    Vread,
    Vset_callback_func,
    Vget_callback_func,
    Venable_callback,
    Vdisable_callback,
    Vis_enable_callback,
    Vregister_callback,
    Vunregister_callback,
    Venable_wakeup,
    Vdisable_wakeup,
    Vis_enable_wakeup,
};

static CGpioCore gpio_core = 
{
    MYNAME,
    MYVERSION,
    &Vapi,
    NULL,	/* plugin */
    NULL,	/* gpioSel */
    Vattach_plugin,
    Vdetach_plugin,
    Vhandler,
    Vget_hndl,
    Vprobe,
    Vsuspend,
    Vresume,
};

static struct amba_id gpio_ids[] = 
{
    {
        .id = 0x00051060,
        .mask = 0x000FFFFF,
    },
    {
        0,
        0,
    },
};

static struct amba_driver gpio_driver = 
{
    .drv = 
    {
        .name = "gpio",
    },
    .id_table = gpio_ids,
    .probe = Vprobe,
    .suspend = Vsuspend,
    .resume = Vresume,
};

static struct amba_device gpio_device = 
{
    .dev = 
    {
        .bus_id = "gpio",
    },
    .res = 
    {
        .start = 0,
        .end = 0,
        .flags = IORESOURCE_MEM,
    },
    .periphid = 0x00051060,
};

static spinlock_t gpio_spinlock = SPIN_LOCK_UNLOCKED;

CGpioCore *pGpioCore = &gpio_core;

#ifdef CHECK_INTERRUPT
struct gpio_callback callback;
static char param = 3;
static void test_callback(IN unsigned int nr, IN void *param);
#endif

/*=============================================================================
 *
 * Main function body
 *
 *===========================================================================*/
/*-----------------------------------------------------------------------------
 * init_module
 *---------------------------------------------------------------------------*/
/*static*/ int __init gpio_core_init(void)
{
    PDEBUG1(" "MYNAME"::gpio_core_init() : Entry\n");
    RETURN1(0, " "MYNAME"::gpio_core_init() : Return(%x)\n");
}

/*-----------------------------------------------------------------------------
 * exit_module
 *---------------------------------------------------------------------------*/
static void __exit gpio_core_exit(void)
{
    PDEBUG1(" "MYNAME"::gpio_core_exit() : Entry\n");
    PDEBUG1(" "MYNAME"::gpio_core_exit() : Return\n");
    return;
}

/*-----------------------------------------------------------------------------
 * open
 *---------------------------------------------------------------------------*/
static int Vopen(IN unsigned int nr)
{
    int err;
    PDEBUG1(" "MYNAME"::Vopen(%d) : Entry\n", nr);

    /* Check nr */
    if(! __is_enable_nr(nr)){
        RETURN1(-ENODEV, " "MYNAME"::Vopen() : Return(%x)\n");
    }
    /* Multiple open => Enable now */
    if(__is_open(nr)){
        pGpioCore->gpioSel[nr]->openCnt++;
        err = __get_hndl(nr);
        RETURN1(err, " "MYNAME"::Vopen() : Return(%x)\n");
    }
    /* Call plugin's open operation */
    if(! ((err= pGpioCore->plugin->ops->open(nr)) == 0)){
        RETURN1(err, " "MYNAME"::Vopen() : Return(%x)\n");
    }
    /* Core's open process. */
#ifdef CHECK_SIZE
    printk("Vopen() : sizeof(CGpioSel) -> %d\n", sizeof(CGpioSel));
#endif
    if(! (pGpioCore->gpioSel[nr] = kmalloc(sizeof(CGpioSel), 
        GFP_KERNEL | GFP_USER))){
        RETURN1(-ENOSPC, " "MYNAME"::Vopen() : Return(%x)\n");
    }
    memset(pGpioCore->gpioSel[nr], 0, sizeof(CGpioSel));
    tasklet_init(&pGpioCore->gpioSel[nr]->tasklet, tasklet, nr);
    pGpioCore->gpioSel[nr]->openCnt++;
    if((err = __get_hndl(nr)) != -ENODEV){
        pGpioCore->gpioSel[nr]->hndl = err;
    }

    RETURN1(err, " "MYNAME"::Vopen() : Return(%x)\n");
}

/*-----------------------------------------------------------------------------
 * close
 *---------------------------------------------------------------------------*/
static int Vclose(IN int hndl)
{
    int err;
    unsigned int nr;
    PDEBUG1(" "MYNAME"::Vclose(%x): Entry\n", hndl);

    /* Check hndl */
    if(__get_nr(hndl, &nr) == -ENODEV){
        RETURN1(-EACCES, " "MYNAME"::Vclose() : Return(%x)\n");
    }
    if(! (--pGpioCore->gpioSel[nr]->openCnt)){
        kfree(pGpioCore->gpioSel[nr]);
        pGpioCore->gpioSel[nr] = 0;
        /* Call plugin's close operation */
        if(pGpioCore->plugin->ops->close){
            err = pGpioCore->plugin->ops->close(nr);
        }else{
            err = -ENOEXEC;
        }
    }else{
        err = 0;
    }

    RETURN1(err, " "MYNAME"::Vclose() : Return(%x)\n");
}

/*-----------------------------------------------------------------------------
 * set_io_func
 *---------------------------------------------------------------------------*/
static int Vset_io_func(IN int hndl, IN unsigned char func, 
    IN unsigned int val)
{
    int err;
    unsigned int nr;
    PDEBUG1(" "MYNAME"::Vset_io_func(%x,%x,%x) : Entry\n", hndl, func, val);

    /* Check hndl */
    if(__get_nr(hndl, &nr) == -ENODEV){
        RETURN1(-EACCES, " "MYNAME"::Vset_io_func() : Return(%x)\n");
    }
    /* Call plugin's operation */
    if(pGpioCore->plugin->ops->set_sel_func){
        err = pGpioCore->plugin->ops->set_sel_func(nr, func, val);
    }else{
        err = -ENOEXEC;
    }

    RETURN1(err, " "MYNAME"::Vset_io_func() : Return(%x)\n");
}

/*-----------------------------------------------------------------------------
 * get_io_func
 *---------------------------------------------------------------------------*/
static int Vget_io_func(IN int hndl, OUT unsigned char *func)
{
    int err;
    unsigned int nr;
    PDEBUG1(" "MYNAME"::Vget_io_func(%x,%x) : Entry\n", hndl, (int)func);

    /* Check hndl */
    if(__get_nr(hndl, &nr) == -ENODEV){
        RETURN1(-EACCES, " "MYNAME"::Vget_io_func() : Return(%x)\n");
    }
    /* Call plugin's operation */
    if(pGpioCore->plugin->ops->get_sel_func){
        err = pGpioCore->plugin->ops->get_sel_func(nr, func);
    }else{
        err = -ENOEXEC;
    }

    RETURN1(err, " "MYNAME"::Vget_io_func() : Return(%x)\n");
}

/*-----------------------------------------------------------------------------
 * write
 *---------------------------------------------------------------------------*/
static int Vwrite(IN int hndl, IN unsigned int val)
{
    int err;
    unsigned int nr;
    PDEBUG1(" "MYNAME"::Vwrite(%x,%x) : Entry\n", hndl, val);

    /* Check hndl */
    if(__get_nr(hndl, &nr) == -ENODEV){
        RETURN1(-EACCES, " "MYNAME"::Vwrite() : Return(%x)\n");
    }
    /* Call plugin's operation */
    if(pGpioCore->plugin->ops->write){
        err = pGpioCore->plugin->ops->write(nr, val);
    }else{
        err = -ENOEXEC;
    }

    RETURN1(err, " "MYNAME"::Vwrite() : Return(%x)\n");
}

/*-----------------------------------------------------------------------------
 * read
 *---------------------------------------------------------------------------*/
static int Vread(IN int hndl)
{
    int val;
    unsigned int nr;
    PDEBUG1(" "MYNAME"::Vread(%x) : Entry\n", hndl);

    /* Check hndl */
    if(__get_nr(hndl, &nr) == -ENODEV){
        RETURN1(-EACCES, " "MYNAME"::Vread() : Return(%x)\n");
    }
    /* Call plugin's operation */
    if(pGpioCore->plugin->ops->read){
        val = pGpioCore->plugin->ops->read(nr);
    }else{
        val = -ENOEXEC;
    }

    RETURN1(val, " "MYNAME"::Vread() : Return(%x)\n");
}

/*-----------------------------------------------------------------------------
 * set_callback_func
 *---------------------------------------------------------------------------*/
static int Vset_callback_func(IN int hndl, IN unsigned char sense, 
    IN unsigned char timing)
{
    int err;
    unsigned int nr;
    PDEBUG1(" "MYNAME"::Vset_callback_func(%x,%x,%x) : Entry\n", hndl, sense, 
        timing);

    /* Check hndl */
    if(__get_nr(hndl, &nr) == -ENODEV){
        RETURN1(-EACCES, " "MYNAME"::Vset_callback_func() : Return(%x)\n");
    }
    /* Call plugin's operation */
    if(! (pGpioCore->plugin->ops->set_irq_sense)){
        RETURN1(-ENOEXEC, " "MYNAME"::Vset_callback_func() : Return(%x)\n");
    }
    if(! (err = pGpioCore->plugin->ops->set_irq_sense(nr, sense))){
          pGpioCore->gpioSel[nr]->callback.sense = sense;
          pGpioCore->gpioSel[nr]->callback.timing = timing;
    }

    RETURN1(err, " "MYNAME"::Vset_callback_func() : Return(%x)\n");
}

/*-----------------------------------------------------------------------------
 * get_callback_func
 *---------------------------------------------------------------------------*/
static int Vget_callback_func(IN int hndl, OUT unsigned char *sense, 
    OUT unsigned char *timing)
{
    int err;
    unsigned int nr;
    PDEBUG1(" "MYNAME"::Vget_callback_func(%x,%x,%x) : Entry\n", hndl, 
        (int)sense, (int)timing);

    /* Check hndl */
    if(__get_nr(hndl, &nr) == -ENODEV){
        RETURN1(-EACCES, " "MYNAME"::Vget_callback_func() : Return(%x)\n");
    }
    /* Call plugin's operation */
    if(! (pGpioCore->plugin->ops->get_irq_sense)){
        RETURN1(-ENOEXEC, " "MYNAME"::Vget_callback_func() : Return(%x)\n");
    }
    if(! timing){
        RETURN1(-EINVAL, " "MYNAME"::Vget_callback_func() : Return(%x)\n");
    }
    if(! (err = pGpioCore->plugin->ops->get_irq_sense(nr, sense))){
        *timing = pGpioCore->gpioSel[nr]->callback.timing;
    }

    RETURN1(err, " "MYNAME"::Vget_callback_func() : Return(%x)\n");
}

/*-----------------------------------------------------------------------------
 * enable_callback
 *---------------------------------------------------------------------------*/
static int Venable_callback(IN int hndl)
{
    int err;
    unsigned int nr;
    PDEBUG1(" "MYNAME"::Venable_callback(%x) : Entry\n", hndl);

    /* Check hndl */
    if(__get_nr(hndl, &nr) == -ENODEV){
        RETURN1(-EACCES, " "MYNAME"::Venable_callback() : Return(%x)\n");
    }
    /* Call plugin's operation */
    if(pGpioCore->plugin->ops->enable_irq){
        err = pGpioCore->plugin->ops->enable_irq(nr);
    }else{
        err = -ENOEXEC;
    }
#ifdef CHECK_INTERRUPT
    callback.function = test_callback;
    callback.param = &param;
    callback.sense = GPIO_INT_DOWN_EDGE;
    callback.timing = GPIO_CALLBACK_TASKLET;
    Vregister_callback(hndl, &callback);
#endif

    RETURN1(err, " "MYNAME"::Venable_callback() : Return(%x)\n");
}

/*-----------------------------------------------------------------------------
 * disable_callback
 *---------------------------------------------------------------------------*/
static int Vdisable_callback(IN int hndl)
{
    int err;
    unsigned int nr;
    PDEBUG1(" "MYNAME"::Vdisable_callback(%x) : Entry\n", hndl);

    /* Check hndl */
    if(__get_nr(hndl, &nr) == -ENODEV){
        RETURN1(-EACCES, " "MYNAME"::Vdisable_callback() : Return(%x)\n");
    }
    /* Call plugin's operation */
    if(pGpioCore->plugin->ops->disable_irq){
        err = pGpioCore->plugin->ops->disable_irq(nr);
    }else{
        err = -ENOEXEC;
    }
#ifdef CHECK_INTERRUPT
    Vunregister_callback(hndl);
#endif

    RETURN1(err, " "MYNAME"::Vdisable_callback() : Return(%x)\n");
}

/*-----------------------------------------------------------------------------
 * is_enable_callback
 *---------------------------------------------------------------------------*/
static int Vis_enable_callback(IN int hndl)
{
    int status;
    unsigned int nr;
    PDEBUG1(" "MYNAME"::Vis_enable_callback(%x) : Entry\n", hndl);

    /* Check hndl */
    if(__get_nr(hndl, &nr) == -ENODEV){
        RETURN1(-EACCES, " "MYNAME"::Vis_enable_callback() : Return(%x)\n");
    }
    /* Call plugin's operation */
    if(pGpioCore->plugin->ops->is_enable_irq){
        status = pGpioCore->plugin->ops->is_enable_irq(nr);
    }else{
        status = -ENOEXEC;
    }

    RETURN1(status, " "MYNAME"::Vis_enable_callback() : Return(%x)\n");
}

/*-----------------------------------------------------------------------------
 * register_callback
 *---------------------------------------------------------------------------*/
static int Vregister_callback(IN int hndl, IN struct gpio_callback *callback)
{
    int err;
    unsigned int nr;
    PDEBUG1(" "MYNAME"::Vregister_callback(%x,%x) : Entry\n", hndl, (int)callback);

    /* Check hndl */
    if(__get_nr(hndl, &nr) == -ENODEV){
        RETURN1(-EACCES, " "MYNAME"::Vregister_callback() : Return(%x)\n");
    }
    if((! callback) || (! callback->function)){
        RETURN1(-EINVAL, " "MYNAME"::Vregister_callback() : Return(%x)\n");
    }
    /* Call plugin's operation */
    if(! (pGpioCore->plugin->ops->register_handler)){
        RETURN1(-ENOEXEC, " "MYNAME"::Vregister_callback() : Return(%x)\n");
    }
    if(! (err = pGpioCore->plugin->ops->register_handler(nr))){
        memcpy(&pGpioCore->gpioSel[nr]->callback, callback, 
            sizeof(struct gpio_callback));
        pGpioCore->gpioSel[nr]->registCb = 1;
    }
    Vset_callback_func(hndl, callback->sense, callback->timing);

    RETURN1(err, " "MYNAME"::Vregister_callback() : Return(%x)\n");
}

/*-----------------------------------------------------------------------------
 * unregister_callback
 *---------------------------------------------------------------------------*/
static int Vunregister_callback(IN int hndl)
{
    int err;
    unsigned int nr;
    PDEBUG1(" "MYNAME"::Vunregister_callback(%x) : Entry\n",hndl);

    /* Check hndl */
    if(__get_nr(hndl, &nr) == -ENODEV){
        RETURN1(-EACCES, " "MYNAME"::Vunregister_callback() : Return(%x)\n");
    }
    /* Call plugin's operation */
    if(! (pGpioCore->plugin->ops->unregister_handler)){
        RETURN1(-ENOEXEC, " "MYNAME"::Vunregister_callback() : Return(%x)\n");
    }
    if(! (err = pGpioCore->plugin->ops->unregister_handler(nr))){
        pGpioCore->gpioSel[nr]->callback.function = 0;
        pGpioCore->gpioSel[nr]->registCb = 0;
    }

    RETURN1(err, " "MYNAME"::Vunregister_callback() : Return(%x)\n");
}

/*-----------------------------------------------------------------------------
 * enable_wakeup
 *---------------------------------------------------------------------------*/
static int Venable_wakeup(IN int hndl)
{
    int err;
    unsigned int nr;
    PDEBUG1(" "MYNAME"::Venable_wakeup(%x) : Entry\n", hndl);

    /* Check hndl */
    if(__get_nr(hndl, &nr) == -ENODEV){
        RETURN1(-EACCES, " "MYNAME"::Venable_wakeup() : Return(%x)\n");
    }
    /* Call plugin's operation */
    if(pGpioCore->plugin->ops->enable_wakeup){
        err = pGpioCore->plugin->ops->enable_wakeup(nr);
    }else{
        err = -ENOEXEC;
    }

    RETURN1(0, " "MYNAME"::Venable_wakeup() : Return(%x)\n");
}

/*-----------------------------------------------------------------------------
 * disable_wakeup
 *---------------------------------------------------------------------------*/
static int Vdisable_wakeup(IN int hndl)
{
    int err;
    unsigned int nr;
    PDEBUG1(" "MYNAME"::Vdisable_wakeup(%x) : Entry\n", hndl);

    /* Check hndl */
    if(__get_nr(hndl, &nr) == -ENODEV){
        RETURN1(-EACCES, " "MYNAME"::Vdisable_wakeup() : Return(%x)\n");
    }
    /* Call plugin's operation */
    if(pGpioCore->plugin->ops->disable_wakeup){
        err = pGpioCore->plugin->ops->disable_wakeup(nr);
    }else{
        err = -ENOEXEC;
    }

    RETURN1(err, " "MYNAME"::Vdisable_wakeup() : Return(%x)\n");
}

/*-----------------------------------------------------------------------------
 * is_enable_wakeup
 *---------------------------------------------------------------------------*/
static int Vis_enable_wakeup(IN int hndl)
{
    int status;
    unsigned int nr;
    PDEBUG1(" "MYNAME"::Vis_enable_wakeup(%x) : Entry\n", hndl);

    /* Check hndl */
    if(__get_nr(hndl, &nr) == -ENODEV){
        RETURN1(-EACCES, " "MYNAME"::Vis_enable_wakeup() : Return(%x)\n");
    }
    /* Call plugin's operation */
    if(pGpioCore->plugin->ops->is_enable_wakeup){
        status = pGpioCore->plugin->ops->is_enable_wakeup(nr);
    }else{
        status = -ENOEXEC;
    }

    RETURN1(status, " "MYNAME"::Vis_enable_wakeup() : Return(%x)\n");
}

/*-----------------------------------------------------------------------------
 * attach_plugin
 *---------------------------------------------------------------------------*/
static int Vattach_plugin(IN CGpioPlugin *plugin)
{
    int err;
    PDEBUG1(" "MYNAME"::Vattach_plugin() : Entry\n");

    /* Override, if sub class's method pointer isn't 0 */
    if(! plugin || ! plugin->portNum){
        RETURN1(-EINVAL, " "MYNAME"::Vattach_plugin() : Return(%x)\n");
    }
    pGpioCore->plugin = plugin;
    /* Keep area of sizeof(CGpioCh pointer * portNum) */
#ifdef CHECK_SIZE
    printk("Vattach_plugin() : sizeof(CGpioSel *) * plugin->portNum -> %d\n", 
        sizeof(CGpioSel *) * plugin->portNum);
#endif
    if(! (pGpioCore->gpioSel = kmalloc(sizeof(CGpioSel *) * plugin->portNum, 
        GFP_KERNEL|GFP_USER))){
        PDEBUG2(" "MYNAME"::Vattach_plugin() : kmalloc() Err\n");
        RETURN1(-ENOSPC, " "MYNAME"::Vattach_plugin() : Return(%x)\n");
    }
    memset(pGpioCore->gpioSel, 0, sizeof(CGpioSel *) * plugin->portNum);
    gpio_device.res.start = plugin->start;
    gpio_device.res.end = plugin->end;
    if((err = amba_device_register(&gpio_device, &iomem_resource)) < 0){
        kfree(pGpioCore->gpioSel);
        RETURN1(err, " "MYNAME"::Vattach_plugin() : Return(%x)\n");
    }
    if((err = amba_driver_register(&gpio_driver)) < 0){
        amba_device_unregister(&gpio_device);
        kfree(pGpioCore->gpioSel);
        RETURN1(err, " "MYNAME"::Vattach_plugin() : Return(%x)\n");
    }

    RETURN1(err, " "MYNAME"::Vattach_plugin() : Return(%d)\n");
}

/*-----------------------------------------------------------------------------
 * detach_plugin
 *---------------------------------------------------------------------------*/
static int Vdetach_plugin(IN CGpioPlugin *plugin)
{
    int err;
    PDEBUG1(" "MYNAME"::detach_plugin() : Entry\n");

    amba_driver_unregister(&gpio_driver);
    amba_device_unregister(&gpio_device);
    if(pGpioCore->plugin == plugin){
        pGpioCore->plugin = 0;
        if(pGpioCore->gpioSel){
            kfree(pGpioCore->gpioSel);
        }
        err = 0;
    }else{
        err = -EINVAL;
    }

    RETURN1(err, " "MYNAME"::detach_plugin() : Return(%d)\n");
}

/*-----------------------------------------------------------------------------
 * handler
 *---------------------------------------------------------------------------*/
static void Vhandler(IN unsigned int nr)
{
    PDEBUG1(" "MYNAME"::Vhandler(%d) : Entry\n", nr);

    /* if need to use tasklet */
    if(pGpioCore->gpioSel[nr]->callback.timing == GPIO_CALLBACK_TASKLET){
        PDEBUG2(" "MYNAME"::Vhandler() : Set tasklet_schedule\n");
        tasklet_schedule(&pGpioCore->gpioSel[nr]->tasklet);
    }else{
        if(pGpioCore->gpioSel[nr]->callback.function){
            PDEBUG2(" "MYNAME"::Vhandler() : Call callback soon\n");
            pGpioCore->gpioSel[nr]->callback.function(nr, 
                pGpioCore->gpioSel[nr]->callback.param);
        }
    }

    PDEBUG1(" "MYNAME"::Vhandler() : Return\n");
    return;
}

/*-----------------------------------------------------------------------------
 * get_hndl
 *---------------------------------------------------------------------------*/
static int Vget_hndl(IN unsigned int nr)
{
    return __get_hndl(nr);
}

/*-----------------------------------------------------------------------------
 * probe
 *---------------------------------------------------------------------------*/
static int Vprobe(IN struct amba_device *dev, IN void *id)
{
    PDEBUG1(" "MYNAME"::Vprobe(%x,%x) : Entry\n", (int)dev, (int)id);

    if(strcmp(dev->dev.bus_id, gpio_device.dev.bus_id))
        RETURN1(1, " "MYNAME"::Vprobe() : Return(%d)\n");

    RETURN1(0, " "MYNAME"::Vprobe() : Return(%d)\n");
}

/*-----------------------------------------------------------------------------
 * suspend
 *---------------------------------------------------------------------------*/
static int Vsuspend(IN struct amba_device *dev, IN pm_message_t state)
{
#ifdef CHECK_TIME
    u32 t = tick();
#endif
    unsigned long flags;
    unsigned int i;
    int err = 0;
    PDEBUG1(" "MYNAME"::Vsuspend(%x) : Entry\n", (int)dev);

    /* spin lock */
    spin_lock_irqsave(&gpio_spinlock, flags);
    for(i = 0; i < pGpioCore->plugin->portNum; i++){
        if(__is_open(i)){
            /* disable tasklet */
            tasklet_disable(&pGpioCore->gpioSel[i]->tasklet);
            /* check register callback */
            if(pGpioCore->gpioSel[i]->registCb){
                /* unregister handler */
                if(pGpioCore->plugin->ops->unregister_handler){
                    if((err = pGpioCore->plugin->ops->unregister_handler(i)) < 0){
                        RETURN1(err, " "MYNAME"::Vsuspend() : Return(%d)\n");
                    }
                }
            }
        }
    }
    /* Call plugin's operation */
    if(pGpioCore->plugin->ops->suspend){
        if((err = pGpioCore->plugin->ops->suspend(dev, state)) < 0){
            RETURN1(err, " "MYNAME"::Vsuspend() : Return(%d)\n");
        }
    }
    /* spin unlock */
    spin_unlock_irqrestore(&gpio_spinlock, flags);

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

/*-----------------------------------------------------------------------------
 * resume
 *---------------------------------------------------------------------------*/
static int Vresume(IN struct amba_device *dev)
{
#ifdef CHECK_TIME
    u32 t = tick();
#endif
    unsigned long flags;
    unsigned int i;
    int err = 0;
    PDEBUG1(" "MYNAME"::Vresume(%x) : Entry\n", (int)dev);

    /* spin lock */
    spin_lock_irqsave(&gpio_spinlock, flags);
    /* Call plugin's operation */
    if(pGpioCore->plugin->ops->resume){
        if((err = pGpioCore->plugin->ops->resume(dev)) < 0){
            RETURN1(err, " "MYNAME"::Vresume() : Return(%d)\n");
        }
    }
    for(i = 0; i < pGpioCore->plugin->portNum; i++){
        if(__is_open(i)){
            /* check register callback */
            if(pGpioCore->gpioSel[i]->registCb){
                /* register handler */
                if(pGpioCore->plugin->ops->register_handler){
                    if((err = pGpioCore->plugin->ops->register_handler(i)) < 0){
                        RETURN1(err, " "MYNAME"::Vresume() : Return(%d)\n");
                    }
                }
            }
            /* enable tasklet */
            tasklet_enable(&pGpioCore->gpioSel[i]->tasklet);
        }
    }
    /* spin unlock */
    spin_unlock_irqrestore(&gpio_spinlock, flags);

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

/*-----------------------------------------------------------------------------
 * tasklet
 *---------------------------------------------------------------------------*/
static void tasklet(IN unsigned long nr)
{
    PDEBUG1(" "MYNAME"::tasklet(%d) : Entry\n", (unsigned int)nr);
    if(pGpioCore->gpioSel[nr] && pGpioCore->gpioSel[nr]->callback.function){
        pGpioCore->gpioSel[nr]->callback.function(nr, 
            pGpioCore->gpioSel[nr]->callback.param);
    }
    PDEBUG1(" "MYNAME"::tasklet() : Return\n");
}

/*-----------------------------------------------------------------------------
 * inline is_enable_nr
 *---------------------------------------------------------------------------*/
static __inline__ int __is_enable_nr(IN unsigned int nr)
{
    PDEBUG2(" "MYNAME"::__is_enable_nr(%d) : Entry\n", nr);

    if(! pGpioCore->plugin || (pGpioCore->plugin->portNum <= nr)){
        RETURN2(0, " "MYNAME"::__is_enable_nr() : Return(%x)\n");
    }

    RETURN2(1, " "MYNAME"::__is_enable_nr() : Return(%x)\n");
}

/*-----------------------------------------------------------------------------
 * inline is_open
 *---------------------------------------------------------------------------*/
static __inline__ int __is_open(IN unsigned int nr)
{
    PDEBUG2(" "MYNAME"::__is_open(%d) : Entry\n", nr);

    if(! pGpioCore->gpioSel || ! pGpioCore->gpioSel[nr] || 
        ! pGpioCore->gpioSel[nr]->openCnt){
        RETURN2(0, " "MYNAME"::_is_open() : Return(%x)\n");
    }

    RETURN2(1, " "MYNAME"::_is_open() : Return(%x)\n");
}

/*-----------------------------------------------------------------------------
 * inline is_enable_hndl
 *---------------------------------------------------------------------------*/
static __inline__ int __is_enable_hndl(IN int hndl, IN unsigned int nr)
{
    PDEBUG2(" "MYNAME"::__is_enable_hndl(%x) : Entry\n", hndl);

    if(! pGpioCore->gpioSel[nr] || (pGpioCore->gpioSel[nr]->hndl != hndl) || 
        ! pGpioCore->gpioSel[nr]->openCnt){
        RETURN2(0, " "MYNAME"::__is_enable_hndl() : Return(%x)\n");
    }

    RETURN2(1, " "MYNAME"::__is_enable_hndl() : Return(%x)\n");
}

/*-----------------------------------------------------------------------------
 * inline get_hndl
 *---------------------------------------------------------------------------*/
static __inline__ int __get_hndl(IN unsigned int nr)
{
    int hndl;
    PDEBUG2(" "MYNAME"::__get_hndl(%d) : Entry\n", nr);

    /* Check nr */
    if(! __is_enable_nr(nr)){
        RETURN2( -ENODEV, " "MYNAME"::_get_hndl() : Return(%x)\n");
    }
    hndl = (int)(0x7FFFFFFF & (~nr));

    RETURN2(hndl, " "MYNAME"::_get_hndl() : Return(%x)\n");
}

/*-----------------------------------------------------------------------------
 * inline get_nr
 *---------------------------------------------------------------------------*/
static __inline__ int __get_nr(IN int hndl, OUT unsigned int *nr)
{
    unsigned int _nr;
    PDEBUG2(" "MYNAME"::__get_nr(%x,%x) : Entry\n", hndl, (int)nr);

    if(hndl < 0){
        RETURN2(-ENODEV, " "MYNAME"::_get_nr() : Return(%x)\n");
    }
    _nr = ~((unsigned int)hndl | 0x80000000);
    /* Check nr */
    if(! __is_enable_nr(_nr)){
        RETURN2(-ENODEV, " "MYNAME"::_get_nr() : Return(%x)\n");
    }
    /* Check hndl */
    if(! __is_enable_hndl(hndl,_nr)){
        RETURN2(-ENODEV, " "MYNAME"::_get_nr() : Return(%x)\n");
    }
    *nr = _nr;
    PDEBUG2(" "MYNAME"::__get_nr() : nr=%d\n", *nr);

    RETURN2(0, " "MYNAME"::_get_nr() : Return(%x)\n");
}

#ifdef CHECK_INTERRUPT
static void test_callback(IN unsigned int nr, IN void *param)
{
    PDEBUG1(" "MYNAME"::test_callback(%d,%x) : Entry\n", nr, *(char*)param);
    printk("test_callback()\n");
    PDEBUG1(" "MYNAME"::test_callback() : Return\n");
}
#endif

/*=============================================================================
 * Export symbols
 *===========================================================================*/
EXPORT_SYMBOL(pGpioCore);

//module_init(gpio_core_init);
module_exit(gpio_core_exit);
