// =========================================================================== // Cybook Input Output - cyio.c // Copyright (C) 2008-2010 Bookeen - All rights reserved // =========================================================================== #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#define DEBUG_MESSAGES //#define DBG_IRQ #include #include #include #include #include #include #include #include #include #include #include #include "asm/io.h" // =========================================================================== #define IO_TIMER_DELAY_0500 ((HZ * 1) / 2) /* 500 ms */ #define IO_TIMER_DELAY_2000 ((HZ * 2) / 1) /* 2 s */ #define IO_TIMER_DELAY_1000 ((HZ * 1) / 1) /* 1 s */ static struct timer_list io_timer; #define TRUE (1==1) #define FALSE (0==1) #define PFX "CyIO:" typedef struct _cyIrq_ { int nIrq; u32 nGpio; u8 bActive; u8 bAllowRepeat; // Allow Repeat? u8 nCodeActive; // All interrupts generate a code when active u8 nCodeInactive; // Some interrupts generate a code when inactive char* sName; } cyIrq; #define GPIO_F0 S3C2410_GPF0 #define GPIO_F1 S3C2410_GPF1 #define GPIO_F2 S3C2410_GPF2 #define GPIO_F3 S3C2410_GPF3 #define GPIO_F4 S3C2410_GPF4 #define GPIO_F5 S3C2410_GPF5 #define GPIO_F6 S3C2410_GPF6 #define GPIO_F7 S3C2410_GPF7 #define GPIO_F8 S3C2410_GPF8 #define GPIO_F9 S3C2410_GPF9 #define GPIO_F10 S3C2410_GPF10 #define GPIO_G0 S3C2410_GPG0 #define GPIO_G1 S3C2410_GPG1 #define GPIO_G2 S3C2410_GPG2 #define GPIO_G3 S3C2410_GPG3 #define GPIO_G4 S3C2410_GPG4 #define GPIO_G5 S3C2410_GPG5 #define GPIO_G6 S3C2410_GPG6 #define GPIO_G7 S3C2410_GPG7 #define GPIO_G8 S3C2410_GPG8 #define GPIO_G9 S3C2410_GPG9 #define GPIO_D10 S3C2410_GPD10 #define GPIO_D11 S3C2410_GPD11 #define GPIO_D14 S3C2410_GPD14 #define GPIO_H4 S3C2410_GPH4 #define GPIO_H5 S3C2410_GPH5 /* USB related */ #define USBDETECT_GPIO GPIO_G1 #define USBDETECT_EINT IRQ_EINT9 static int nCnt; static cyIrq s_nIrq[] ={ /* IRQ GPIO A B Depress Event Release Event Name Number */ { IRQ_EINT0, GPIO_F0, 0, 1, CYEVENT_KEY_OFF, 0, "PowerBtn" } //0 , { IRQ_EINT7, GPIO_F7, 0, 1, CYEVENT_KEY_DOWN, 0, "Down" } //1 , { IRQ_EINT15, GPIO_G7, 0, 1, CYEVENT_KEY_UP, 0, "Up" } //2 , { IRQ_EINT8, GPIO_G0, 0, 1, CYEVENT_KEY_LEFT, 0, "Left" } //3 , { IRQ_EINT10, GPIO_G2, 0, 1, CYEVENT_KEY_RIGHT, 0, "Right" } //4 , { IRQ_EINT14, GPIO_G6, 0, 1, CYEVENT_KEY_ENTER, 0, "Center" } //5 /* A:Reserved (must be 0) - B: Allow Repeat? (1: Yes, 0: No) */ }; static irqreturn_t io_interrupt (int irq, void *dev_id); static irqreturn_t io_usb_interrupt (int irq, void *dev_id); static void io_timer_handler (unsigned long nData); static void io_ev_emptylist(void); static int io_ioctl(unsigned int cmd, unsigned long arg); static void io_ev_read(unsigned char data); static struct cyevent_device cyio_cye_dev = { .deepsleep = NULL, .suspend = NULL, .resume = NULL, .event_read = io_ev_read, .ioctl = io_ioctl, .ioctl_prefix = IOCTL_PFX('C'), .event_listempty = io_ev_emptylist, .event_read_listen = 0, }; #undef MSG #undef DBG #ifdef DEBUG_MESSAGES # define MSG(str) { printk(KERN_ERR str "\n"); } # define DBG(str, ...) { printk(KERN_ERR str "\n", __VA_ARGS__); } #else # define MSG(str) # define DBG(str, ...) #endif // --------------------------------------------------------------------------- void io_initIrq (void) { int i; cyIrq *pIrq, *pIrq0; int ret; pIrq0 = &s_nIrq[0]; DBG(">>%s()\n", __func__); for ( i = 0, pIrq = pIrq0; i < nCnt; ++i, ++pIrq ) { int nIrq = pIrq->nIrq; set_irq_type(nIrq, IRQT_BOTHEDGE); /* Set no Pullup and no Pulldown */ s3c2410_gpio_pullup(pIrq->nGpio, 0); ret = request_irq(nIrq, io_interrupt, SA_INTERRUPT | SA_SHIRQ, pIrq->sName, pIrq); enable_irq_wake(nIrq); if ( ret != 0 ) { printk(KERN_ERR PFX "Error registering IRQ %d [%s]!\n", nIrq, pIrq->sName); } } /* Setup USB irq */ set_irq_type(USBDETECT_EINT, IRQT_BOTHEDGE); s3c2410_gpio_pullup(USBDETECT_GPIO, 0); ret = request_irq(USBDETECT_EINT, io_usb_interrupt, SA_INTERRUPT | SA_SHIRQ, "USB Detect", &nCnt); enable_irq_wake(USBDETECT_EINT); if (ret != 0) { printk(KERN_ERR PFX "Error registering USB Detect IRQ!\n"); } init_timer(&io_timer); io_timer.function = io_timer_handler; io_timer.data = CYEVENT_TIMER_SCREEN; } // ----------------------------------------------------------------------------- static void io_timer_handler (unsigned long nData) { CyEvent_t event = NEW_CYEVENT(CYEVENT_TYPE_TIMER); DBG("Timer [%ld] tick...\n", nData); del_timer(&io_timer); event.data.timer_type = nData; CyEvent_PushNewUniqueEvent(&event); } // ----------------------------------------------------------------------------- void io_deinitIrq (void) { int i; cyIrq *pIrq, *pIrq0; del_timer(&io_timer); pIrq0 = &s_nIrq[0]; for ( i = 0, pIrq = pIrq0; i < nCnt; ++i, ++pIrq ) { disable_irq_wake(pIrq->nIrq); free_irq(pIrq->nIrq, pIrq); } disable_irq_wake(USBDETECT_EINT); free_irq(USBDETECT_EINT, &nCnt); } // ----------------------------------------------------------------------------- static int is_usb_plugged(void) { int i, nUp = 0; for ( i = 0; i < 5000; ++i ) if ( gpio_get_value(USBDETECT_GPIO) ) ++nUp; return nUp > 2500; } // ----------------------------------------------------------------------------- static void io_ev_emptylist(void) { /* If we are powered, don't use timers... */ if (!is_usb_plugged()) { del_timer(&io_timer); if (io_timer.data == CYEVENT_TIMER_SCREEN) { io_timer.expires = IO_TIMER_DELAY_0500; io_timer.expires += jiffies; add_timer(&io_timer); } else //if (io_timer.data == CYEVENT_TIMER_DEVICE) { io_timer.expires = IO_TIMER_DELAY_2000 - IO_TIMER_DELAY_0500; io_timer.expires += jiffies; add_timer(&io_timer); } } } // ----------------------------------------------------------------------------- static void io_ev_read(unsigned char type) { del_timer(&io_timer); if (type != CYEVENT_TYPE_TIMER) io_timer.data = CYEVENT_TIMER_SCREEN; else io_timer.data = (io_timer.data == CYEVENT_TIMER_SCREEN)?CYEVENT_TIMER_DEVICE:CYEVENT_TIMER_SCREEN; } // ----------------------------------------------------------------------------- static int io_ioctl(unsigned int cmd, unsigned long arg) { int ret = -EINVAL; unsigned long value; int i; ret = -EINVAL; switch(cmd) { case CYIO_CTL_LED_CMD: if ((arg & 0x2) && (arg & 0x1)) /* Power LED */ { //GPH12 __raw_writel(__raw_readl(S3C2410_GPHDAT) | (1 << 12), S3C2410_GPHDAT); } else if (arg & 0x2) { __raw_writel(__raw_readl(S3C2410_GPHDAT) & ~(1 << 12), S3C2410_GPHDAT); } if ((arg & 0x20) && (arg & 0x10)) /* Wifi LED */ { //GPK8 __raw_writel(__raw_readl(S3C2416_GPKDAT) | (1 << 8), S3C2416_GPKDAT); } else if (arg & 0x20) { __raw_writel(__raw_readl(S3C2416_GPKDAT) & ~(1 << 8), S3C2416_GPKDAT); } if ((arg & 0x200) && (arg & 0x100)) /* Bluetooth LED */ { //GPK9 __raw_writel(__raw_readl(S3C2416_GPKDAT) | (1 << 9), S3C2416_GPKDAT); } else if (arg & 0x200) { __raw_writel(__raw_readl(S3C2416_GPKDAT) & ~(1 << 9), S3C2416_GPKDAT); } ret = 0; break; case CYIO_CTL_USB_STATUS: value = gpio_get_value(S3C2410_GPG1); put_user(value, (unsigned long __user *)arg); ret = 0; break; case CYIO_CTL_SD_STATUS: value = gpio_get_value(S3C2410_GPF1); put_user(value, (unsigned long __user *)arg); ret = 0; break; } return ret; } // ----------------------------------------------------------------------------- static irqreturn_t io_usb_interrupt (int irq, void *dev_id) { CyEvent_t event = NEW_CYEVENT(CYEVENT_TYPE_SYSTEM); event.flags |= CYEVENT_FLAG_USB_MESSAGE; if (is_usb_plugged()) { event.flags |= CYEVENT_FLAG_SYS_STATUS; } CyEvent_PushNewEvent(&event, false); return IRQ_HANDLED; } // ----------------------------------------------------------------------------- static irqreturn_t io_interrupt (int irq, void *dev_id) { u8 bActive; u8 blCodeActive; unsigned long flags; CyEvent_t event = NEW_CYEVENT(CYEVENT_TYPE_KEY); cyIrq* pIrq = (cyIrq*)dev_id; spinlock_t lock = SPIN_LOCK_UNLOCKED; #ifdef DBG_IRQ static int s_nIrq_dbg = 0; ++s_nIrq_dbg; #endif /* Prevent bouncing... */ spin_lock_irqsave(&lock, flags); { // Avoid bounces int i, nUp = 0; for ( i = 0; i < 10000; ++i ) if ( gpio_get_value(pIrq->nGpio) ) ++nUp; bActive = (nUp < 5000); } spin_unlock_irqrestore(&lock, flags); if ( pIrq->bActive == bActive ) return IRQ_HANDLED; pIrq->bActive = bActive; #ifdef DBG_IRQ DBG(".. io_irq #%d [%s][%c] bActive[%d]", s_nIrq_dbg, pIrq->sName, bActive || !pIrq->nCodeInactive ? (char)pIrq->nCodeActive : (char)pIrq->nCodeInactive, (bActive ? pIrq->nCodeActive : pIrq->nCodeInactive), bActive); #endif blCodeActive = bActive ? pIrq->nCodeActive : pIrq->nCodeInactive; event.data.key = blCodeActive; event.flags |= CYEVENT_FLAG_KEY_CONTROL_CHARS; DBG("blCodeActive: %d", blCodeActive); if ( blCodeActive == 0 ) { CyEvent_InvalidateRepeatableEvent(&event); } else { if ( pIrq->bAllowRepeat ) CyEvent_PushNewEvent(&event, true); else CyEvent_PushNewUniqueEvent(&event); } return IRQ_HANDLED; } // =========================================================================== static int __init cyIo_init (void) { nCnt = sizeof (s_nIrq) / sizeof (s_nIrq[0]); io_initIrq(); CyEvent_RegisterDevice(&cyio_cye_dev); return 0; } // --------------------------------------------------------------------------- static void __exit cyIo_exit (void) { CyEvent_DeregisterDevice(&cyio_cye_dev); io_deinitIrq(); //MSG("<< cyIo_exit"); } // --------------------------------------------------------------------------- module_init (cyIo_init); module_exit (cyIo_exit); // --------------------------------------------------------------------------- MODULE_LICENSE ("BPL (Bookeen Private Licence)"); MODULE_AUTHOR ("Bookeen "); MODULE_DESCRIPTION ("Cybook IO Manager"); MODULE_VERSION ("3.0"); // ===========================================================================