/** 
 * osddiInt.c
 *
 * Copyright (C) 2011-2018 Fuji Xerox Co.,Ltd. All rights reserved.
 *
 * This 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., 59
 * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The full GNU General Public License is included in this distribution in the
 * file called COPYING.
 */
#include <linux/module.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/semaphore.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/suspend.h>
#include <linux/types.h>
#include <linux/time.h>
#include <linux/irq.h>
#include <linux/irqdesc.h>
#include <linux/jiffies.h>
#include <linux/fxprivate/kernelOsddi.h>
#include <linux/fxprivate/osddi_ioctl.h>
#include "osddiInt.h"
#include "osddiGio.h"
#include "osddiData.h"
#include "osddiGlobalFlag.h"
#define FX_OSDDI_CPLD "osddi_cpld"
#define FX_OSDDI_TIMER "fx_rtc"
#define FX_OSDDI_GPIO "osddi_gpio"
#define OS_SUSPEND_PROHIBITION_TIME 5000
typedef struct {
 wait_queue_head_t wq[FX_MAX_INT_FACTOR];
 atomic_t int_count[FX_MAX_INT_FACTOR];
 unsigned int suspend_state;
} fx_int_tbl;
fx_int_tbl *fxIntTbl = NULL;
#ifdef DEBUG
struct timespec start;
#endif
unsigned long resume_time;
static int osddiWaitInt(fx_int_tbl *devcon,unsigned long index);
static void osddiIntCpldEvent(void);
void osddiIntTimerHandler(void);
static void osddiIntGpioEvent(void);
extern void *osddiIntTimerEvent;
extern void lm2_pm_subsw_pushed();
extern unsigned int resume_is_swoff;
const static INTERRUPT_INDEX gioCpldIntTbl[] = {
 { FX_CPLD_SUBSW_PUSH, FX_INDEX_SUBSW_PUSH},
 { FX_CPLD_OPT_WAKE, FX_INDEX_OPT_WAKE},
 { FX_CPLD_USB_OVRCUR1, FX_INDEX_USB_OVRCUR1},
 { FX_CPLD_USB_FAXWKUP, FX_INDEX_FAXWKUP},
 { FX_CPLD_INTER_LOCK, FX_INDEX_INTER_LOCK},
 { FX_CPLD_FAN_FAIL, FX_INDEX_FAN_FAIL},
 { FX_CPLD_DADF_WAKEUP, FX_INDEX_DADF_WAKEUP},
 { FX_CPLD_PLATEN_WAKEUP, FX_INDEX_PLATEN_WAKEUP},
 { FX_CPLD_WIFI_HOWK, FX_INDEX_WIFI_HOWK},
 { FX_CPLD_SUBSW_RLS, FX_INDEX_SUBSW_RLS},
 { FX_CPLD_USB_VBUS_DETECT,FX_INDEX_USB_VBUS_DETECT},
};
const int osddiGioCpldTblSize = ARRAY_SIZE(gioCpldIntTbl);
const static OSDDI_INDEX_TBL osddiGioIndexTbl[] = {
 { FX_INDEX_HUMAN_SNS, -1},
 { FX_INDEX_DADF_WAKEUP, OS_GIO_DADF_WAKEUP},
 { FX_INDEX_PLATEN_WAKEUP, OS_GIO_PLATEN_WAKEUP},
 { FX_INDEX_INTER_LOCK, OS_GIO_INTER_LOCK},
 { FX_INDEX_SUBSW_PUSH, OS_GIO_SUB_SW_PUSH},
 { FX_INDEX_SUBSW_RLS, OS_GIO_SUB_SW_RLS},
 { FX_INDEX_FAN_FAIL, OS_GIO_FAN_FAIL},
 { FX_INDEX_FAXWKUP, OS_GIO_FAXWKUP},
 { FX_INDEX_WAKEUP_TIMER, -1},
 { FX_INDEX_NETPHY, OS_GIO_PHYINT},
 { FX_INDEX_CPLD, OS_GIO_CPLD_INT},
 { FX_INDEX_USB_VBUS_DETECT, OS_GIO_USB_VBUS_DETECT}
};
const int osddiGioIndexTblSize = ARRAY_SIZE(osddiGioIndexTbl);
const static OSDDI_INDEX_IRQ_TBL osddiIndexIrqTbl[] = {
 { FX_INDEX_CPLD, FX_IRQ_CPLD },
 { FX_INDEX_NETPHY, FX_IRQ_GPIO },
};
const int osddiIndexIrqTblSize = ARRAY_SIZE(osddiIndexIrqTbl);
extern STATUS osddiIntEnable(int index){
 int i;
 if(index == FX_INDEX_RTC)
  return OK;
 for(i = 0;i < osddiGioIndexTblSize;++i){
  if (osddiGioIndexTbl[i].index == index) break;
 }
 if(i == osddiGioIndexTblSize ){
  printk(KERN_ERR "[osddiInt.c]osddiIntEnable: unsupported index 0x%x\n" , index);
  return ERROR;
 }
 return osddiGioIntrMaskSet(osddiGioIndexTbl[i].port, OS_GIO_INT_MASK_OFF);
}
extern STATUS osddiIntDisable(int index){
 int i;
 for(i = 0;i < osddiGioIndexTblSize;++i){
  if (osddiGioIndexTbl[i].index == index) break;
 }
 if(i == osddiGioIndexTblSize ){
  printk(KERN_ERR "[osddiInt.c]osddiIntDisable: unsupported index 0x%x\n" , index);
  return ERROR;
 }
 return osddiGioIntrMaskSet(osddiGioIndexTbl[i].port, OS_GIO_INT_MASK_ON);
}
int kernelIntEnable(unsigned int id, unsigned int number){
 unsigned long intrnum;
 int i,j;
 struct irq_desc *fx_irq_desc;
 intrnum = osddiGetIntIndex(id, number);
 if(intrnum == 0){
  printk(KERN_ERR "[kernelIntEnable]osddiGetIntIndex failed.id:%d,number:%d\n", id, number);
  return -EINVAL;
 }
 for(j=0;j < osddiIndexIrqTblSize;++j){
  if(intrnum == osddiIndexIrqTbl[j].index){
   fx_irq_desc = irq_to_desc(osddiIndexIrqTbl[j].irqnum);
   if(fx_irq_desc->depth == 0){
    printk(KERN_ERR "[kernelIntEnable]depth 0!\n");
    return 0;
   }
   enable_irq(osddiIndexIrqTbl[j].irqnum);
   return OK;
  }
 }
 for(i = 0;i < osddiGioIndexTblSize;++i){
  if(osddiGioIndexTbl[i].index == intrnum){
   return osddiGioIntrMaskSet(osddiGioIndexTbl[i].port, OS_GIO_INT_MASK_OFF);
  }
 }
 return 0;
}
EXPORT_SYMBOL(kernelIntEnable);
int kernelIntDisable(unsigned int id, unsigned int number){
 unsigned long intrnum;
 int i, j;
 struct irq_desc *fx_irq_desc;
 intrnum = osddiGetIntIndex(id, number);
 if(intrnum == 0){
  printk(KERN_ERR "[kernelIntDisable]osddiGetIntIndex failed.id:%d,number:%d\n", id, number);
  return -EINVAL;
 }
 for(j=0;j < osddiIndexIrqTblSize;++j){
  if(intrnum == osddiIndexIrqTbl[j].index){
   fx_irq_desc = irq_to_desc(osddiIndexIrqTbl[j].irqnum);
   if(fx_irq_desc->depth == 1){
    printk(KERN_ERR "[kernelIntDisable]depth 1!\n");
    return 0;
   }
   disable_irq_nosync(osddiIndexIrqTbl[j].irqnum);
   return OK;
  }
 }
 for(i = 0;i < osddiGioIndexTblSize;++i){
  if(osddiGioIndexTbl[i].index == intrnum){
   return osddiGioIntrMaskSet(osddiGioIndexTbl[i].port, OS_GIO_INT_MASK_ON);
  }
 }
 return 0;
}
EXPORT_SYMBOL(kernelIntDisable);
static int osddiWaitInt(fx_int_tbl *devcon, unsigned long index)
{
 int status;
#ifdef DEBUG
 unsigned long intTime;
 struct timespec end;
 int pid;
 pid = task_pid_nr(current);
 printk(KERN_DEBUG "[osddi]osIntConnect called by pid[%d]. index[%x]\n");
#endif
 status = wait_event_interruptible(devcon->wq[index], (atomic_read(&devcon->int_count[index]) > 0));
#ifdef DEBUG
 end = current_kernel_time();
 intTime = end.tv_nsec - start.tv_nsec;
 printk(KERN_DEBUG "[osddi]Wait Time:0x%lx\n", intTime);
#endif
 if(status != 0){
  switch(index){
  case FX_INDEX_HUMAN_SNS:
   printk(KERN_ERR "[osddiWaitInt]wait_event_interruptible failed at HUMAN_SNS\n");
   break;
  case FX_INDEX_FAXWKUP:
   printk(KERN_ERR "[osddiWaitInt]wait_event_interruptible failed at FAX\n");
   break;
  case FX_INDEX_DADF_WAKEUP:
   printk(KERN_ERR "[osddiWaitInt]wait_event_interruptible failed at DADF\n");
   break;
  case FX_INDEX_PLATEN_WAKEUP:
   printk(KERN_ERR "[osddiWaitInt]wait_event_interruptible failed at PLATEN\n");
   break;
  case FX_INDEX_INTER_LOCK:
   printk(KERN_ERR "[osddiWaitInt]wait_event_interruptible failed at INTERLOCK\n");
   break;
  case FX_INDEX_SUBSW_PUSH:
   printk(KERN_ERR "[osddiWaitInt]wait_event_interruptible failed at SUBSW_PUSH\n");
   break;
  case FX_INDEX_SUBSW_RLS:
   printk(KERN_ERR "[osddiWaitInt]wait_event_interruptible failed at SUBSW_RELEASE\n");
   break;
  case FX_INDEX_FAN_FAIL:
   printk(KERN_ERR "[osddiWaitInt]wait_event_interruptible failed at FAN_FAIL\n");
   break;
  case FX_INDEX_RTC:
   printk(KERN_ERR "[osddiWaitInt]wait_event_interruptible failed at RTC\n");
   break;
  case FX_INDEX_NETPHY:
   printk(KERN_ERR "[osddiWaitInt]wait_event_interruptible failed at NETPHY\n");
   break;
  default:
   printk(KERN_ERR "[osddiWaitInt]wait_event_interruptible failed at UNKNOWN DEVICE\n");
   break;
  }
  return status;
 } else {
 }
 atomic_dec(&devcon->int_count[index]);
 return status;
}
extern int osddiIntConnect(unsigned long arg){
 fx_int_tbl *devcon;
 int status;
 devcon = fxIntTbl;
 switch(arg){
  case FX_INDEX_HUMAN_SNS:
  case FX_INDEX_DADF_WAKEUP:
  case FX_INDEX_PLATEN_WAKEUP:
  case FX_INDEX_INTER_LOCK:
  case FX_INDEX_SUBSW_PUSH:
  case FX_INDEX_SUBSW_RLS:
  case FX_INDEX_FAN_FAIL:
  case FX_INDEX_FAXWKUP:
  case FX_INDEX_RTC:
  case FX_INDEX_NETPHY:
   break;
 default:
  printk(KERN_ERR "[osddiIntConnect]unknown device\n");
  return -EINVAL;
  break;
 }
 status = osddiWaitInt(devcon, arg);
#ifdef DEBUG
 printk(KERN_DEBUG "[osddiIntConnect]status:%d\n", status);
#endif
 return status;
}
static void osddiSetVbusIntrSettings(void)
{
 int status;
 osddiGioIntrMaskSet(OS_GIO_USB_VBUS_DETECT, OS_GIO_INT_MASK_ON);
 kernelGioIntClear(OS_GIO_USB_VBUS_DETECT);
 status = kernelGioStatus(OS_GIO_USB_VBUS_DETECT);
 if(status == OS_STATUS_HIGH){
  osddiGioIntTrigSet(OS_GIO_USB_VBUS_DETECT, OS_LEVEL_LOW);
 }else if(status == OS_STATUS_LOW){
  osddiGioIntTrigSet(OS_GIO_USB_VBUS_DETECT, OS_LEVEL_HIGH);
 }else{
  printk(KERN_ERR "[osddiSetVbusIntrSettings]Invalid status.(%d)\n", status);
  return;
 }
 osddiGioIntrMaskSet(OS_GIO_USB_VBUS_DETECT, OS_GIO_INT_MASK_OFF);
 return;
}
static void osddiIntCpldEvent(void)
{
 fx_int_tbl *devcon;
 int status,res;
 int i;
 status = kernelGioIntrStatusGet(OS_GIO_CPLD_INT);
 if(status == 0){
#ifdef DEBUG
  printk(KERN_ERR "[osddiIntCpldEvent]CPLD INT is none.\n");
#endif
  return;
 }
 kernelGioNegate(OS_GIO_CPLD_INT_MASK);
 devcon = fxIntTbl;
 res = osddiGioIntrRegStatusGet();
 if(res == 0 ){
#ifdef DEBUG
  printk(KERN_ERR "[osddiIntCpldEvent]CPLD INT is none.\n");
#endif
  kernelGioIntClear(OS_GIO_CPLD_INT);
  kernelGioAssert(OS_GIO_CPLD_INT_MASK);
  return;
 }
 for(i=0; i < osddiGioCpldTblSize; i++){
  if(res & gioCpldIntTbl[i].factor){
                        if(gioCpldIntTbl[i].index == FX_INDEX_SUBSW_PUSH){
#ifdef DEBUG
                                printk(KERN_DEBUG "[osddiIntCpldEvent] SUB_SW Interrupt Mask status is on\n");
#endif
    kernelGioIntClear(OS_GIO_OFF_COUNT);
    osddiGioIntrMaskSet(OS_GIO_SUB_SW_PUSH, OS_GIO_INT_MASK_ON);
    lm2_pm_subsw_pushed();
   }
   atomic_inc(&devcon->int_count[gioCpldIntTbl[i].index]);
   wake_up_interruptible(&devcon->wq[gioCpldIntTbl[i].index]);
   if(gioCpldIntTbl[i].index == FX_INDEX_USB_VBUS_DETECT){
    osddiSetVbusIntrSettings();
    atomic_dec(&devcon->int_count[gioCpldIntTbl[i].index]);
   }
  }
 }
 osddiGioIntAllClear(res);
 kernelGioIntClear(OS_GIO_CPLD_INT);
 kernelGioAssert(OS_GIO_CPLD_INT_MASK);
 return;
}
static void osddiIntGpioEvent(void)
{
 fx_int_tbl *devcon;
 int res;
 devcon = fxIntTbl;
 res = kernelGioIntrStatusGet(OS_GIO_PHYINT);
 if(res == 0){
  printk(KERN_ERR "[osddiIntGpioEvent]GPIO INT is none.\n");
  return;
 }
 kernelGioIntClear(OS_GIO_PHYINT);
 osddiGioIntrMaskSet(OS_GIO_PHYINT, OS_GIO_INT_MASK_ON);
 atomic_inc(&devcon->int_count[FX_INDEX_NETPHY]);
 wake_up_interruptible(&devcon->wq[FX_INDEX_NETPHY]);
 osddiGioIntrMaskSet(OS_GIO_PHYINT, OS_GIO_INT_MASK_OFF);
 return;
}
void osddiIntTimerHandler(void)
{
 fx_int_tbl *devcon;
 devcon = fxIntTbl;
 atomic_inc(&devcon->int_count[FX_INDEX_RTC]);
 wake_up_interruptible(&devcon->wq[FX_INDEX_RTC]);
 return;
}
extern irqreturn_t osddiCpldInterrupt(int irq, void *dev_id)
{
 fx_int_tbl *devcon;
 devcon = (fx_int_tbl *)dev_id;
 if(!devcon){
  printk(KERN_ERR "[osddiCpldInterrupt()]: no device.\n");
  return IRQ_NONE;
 }
#ifdef DBUG
        start = current_kernel_time();
#endif
 osddiIntCpldEvent();
 return IRQ_HANDLED;
}
extern irqreturn_t osddiGpioInterrupt(int irq, void *dev_id)
{
 fx_int_tbl *devcon;
 devcon = (fx_int_tbl *)dev_id;
 if(!devcon){
  printk(KERN_ERR "[osddiGpioInterrupt()]: no device.\n");
  return IRQ_NONE;
 }
#ifdef DBUG
 start = current_kernel_time();
#endif
 osddiIntGpioEvent();
 return IRQ_HANDLED;
}
void osddiWakeUpTimerInterrupt(void){
 return;
}
void osddiIntCountClear(int port){
 fx_int_tbl *devcon;
 int i;
 devcon = fxIntTbl;
 for(i=0;i<osddiGioIndexTblSize;i++){
  if(osddiGioIndexTbl[i].port == port){
   atomic_set(&devcon->int_count[osddiGioIndexTbl[i].index], 0);
   break;
  }
 }
 return;
}
static int osddiSuspend(struct platform_device *pdev, pm_message_t msg){
 fx_int_tbl *devcon;
 devcon = fxIntTbl;
 printk(KERN_DEBUG "[osddiSuspend] suspend called\n");
 return 0;
}
static int osddiResume(struct platform_device *pdev){
 fx_int_tbl *devcon;
 devcon = fxIntTbl;
 printk(KERN_DEBUG "[osddiResume] resume called\n");
 resume_time = jiffies;
 osddiGioIntrMaskSet(OS_GIO_USB_VBUS_DETECT, OS_GIO_INT_MASK_OFF);
 return 0;
}
int kernelConnectPhyInt(void){
 fx_int_tbl *devcon;
 devcon = fxIntTbl;
 if(request_irq(FX_IRQ_GPIO, osddiGpioInterrupt, IRQF_NO_SUSPEND, FX_OSDDI_GPIO, devcon) != 0){
  printk(KERN_ERR "[osddiInitInterrupt]request_irq failed:FX_IRQ_GPIO\n");
  return -EBUSY;
 }
 return 0;
}
EXPORT_SYMBOL(kernelConnectPhyInt);
static void osddiInitUsbVbusIntSettings(void)
{
 int status;
 status = kernelGioStatus(OS_GIO_USB_VBUS_DETECT);
 if(status == OS_STATUS_LOW){
  kernelGioIntClear(OS_GIO_USB_VBUS_DETECT);
  osddiGioIntTrigSet(OS_GIO_USB_VBUS_DETECT, OS_LEVEL_HIGH);
 }else if(status == OS_STATUS_HIGH){
  kernelGioIntClear(OS_GIO_USB_VBUS_DETECT);
  osddiGioIntTrigSet(OS_GIO_USB_VBUS_DETECT, OS_LEVEL_LOW);
 }else{
  printk("[osddiInitUsbVbusIntSettings]Invalid status.(%d)\n", status);
  return;
 }
 osddiGioIntrMaskSet(OS_GIO_USB_VBUS_DETECT, OS_GIO_INT_MASK_OFF);
 return;
}
int kernelConnectCpldInt(void){
 fx_int_tbl *devcon;
 devcon = fxIntTbl;
 if(request_irq(FX_IRQ_CPLD, osddiCpldInterrupt, IRQF_SHARED | IRQF_NO_SUSPEND , FX_OSDDI_CPLD, devcon) != 0){
  printk(KERN_ERR "[osddiInitInterrupt]request_irq failed:FX_IRQ_CPLD\n");
  return -EBUSY;
 }
 osddiGioIntrMaskSet(OS_GIO_SUB_SW_PUSH, OS_GIO_INT_MASK_OFF);
 osddiInitUsbVbusIntSettings();
 return 0;
}
EXPORT_SYMBOL(kernelConnectCpldInt);
static int osddiPrepareForSuspend(struct device *dev)
{
 int val;
 unsigned long now_time;
 unsigned long elapsed_time_jiffies;
 unsigned int elapsed_time_msec;
 if(resume_is_swoff == 1){
  osddiGioIntrMaskSet(OS_GIO_USB_VBUS_DETECT, OS_GIO_INT_MASK_ON);
  printk(KERN_ERR "[kernelOsddi]osddiPrepareForSuspend: SW_OFF mode enter\n");
  return 0;
 }
 now_time = jiffies;
 if(resume_time > now_time){
  printk(KERN_ERR "[kernelOsddi]osddiPrepareForSuspend: jiffies over flow\n");
  resume_time = jiffies;
  return -EBUSY;
 }
 elapsed_time_jiffies = now_time - resume_time;
 elapsed_time_msec = jiffies_to_msecs(elapsed_time_jiffies);
 if(elapsed_time_msec < OS_SUSPEND_PROHIBITION_TIME){
  printk(KERN_ERR "[kernelOsddi]osddiPrepareForSuspend: elapsed_time is not yet,%d\n", elapsed_time_msec);
  return -EBUSY;
 }
 val = osddiGetGlobalFlag(FX_DRVID_PWRMGR1,2);
 if(val == 0){
  printk(KERN_ERR "[kernelOsddi]osddiPrepareForSuspend: Can not prepare for suspend.DRVID:FX_DRVID_PWRMGR1,index:2\n");
  return -EBUSY;
 }
 val = osddiGetGlobalFlag(FX_DRVID_PWRMGR1,6);
 if(val == 0){
  printk(KERN_ERR "[kernelOsddi]osddiPrepareForSuspend: Can not prepare for suspend.DRVID:FX_DRVID_PWRMGR1,index:6\n");
  return -EBUSY;
 }
 val = kernelGioStatus(OS_GIO_SUB_SW_PUSH);
 if(val == OS_STATUS_HIGH){
  printk(KERN_ERR "[kernelOsddi]osddiPrepareForSuspend: sub switch is pushing.\n");
  return -EBUSY;
 }
 printk(KERN_DEBUG "[kernelOsddi]osddiPrepareForSuspend: Ready for suspend.\n");
 return 0;
}
static struct dev_pm_ops osddi_pm = {
        .suspend = osddiSuspend,
        .resume = osddiResume,
        .prepare = osddiPrepareForSuspend,
};
static struct platform_driver kernel_osddi ={
 .driver = {
  .name = "osddi",
  .pm = &osddi_pm},
};
static struct platform_device *osddi_device = NULL;
extern int osddiInitInterrupt(void){
 int i,status;
 fx_int_tbl *devcon;
 osddiIntTimerEvent = &osddiIntTimerHandler;
 devcon = (fx_int_tbl *)kmalloc(sizeof(fx_int_tbl), GFP_KERNEL);
 if (devcon == NULL) {
  printk(KERN_ERR "ERROR:[osddiInitInterrupt]: failed kmalloc()\n");
  return -ENOMEM;
 }
 status = platform_driver_register(&kernel_osddi);
 if(!status){
  printk(KERN_DEBUG "[osddiInitInterrupt]platform_device_alloc called\n");
  osddi_device = platform_device_alloc("osddi", 0);
  if(osddi_device){
   printk(KERN_DEBUG "[osddiInitInterrupt]platform_device_add called\n");
   status = platform_device_add(osddi_device);
  } else {
   printk(KERN_DEBUG "[osddiInitInterrupt]platform_device_alloc failed\n");
   status = -ENOMEM;
  }
  if(status){
   printk(KERN_DEBUG "[osddiInitInterrupt]platform_device_put & unregister call\n");
   platform_device_put(osddi_device);
   platform_driver_unregister(&kernel_osddi);
   kfree(devcon);
   return status;
  }
 }
 for (i=0; i < FX_MAX_INT_FACTOR;i++)
  init_waitqueue_head(&devcon->wq[i]);
 for (i=0; i < FX_MAX_INT_FACTOR;i++)
  atomic_set(&devcon->int_count[i], 0);
 devcon->suspend_state = 0;
 fxIntTbl = devcon;
 return 0;
}
extern void osddiCleanupInterrupt(void){
 fx_int_tbl *devcon;
 int i;
 devcon = fxIntTbl;
 free_irq(FX_IRQ_CPLD, devcon);
 free_irq(FX_IRQ_GPIO, devcon);
 platform_driver_unregister(&kernel_osddi);
 platform_device_unregister(osddi_device);
 for (i=0; i < FX_MAX_INT_FACTOR;i++)
  atomic_set(&devcon->int_count[i], 0);
 kfree(devcon);
 return;
}
