2021-11-21 00:20:20 +00:00

416 lines
11 KiB
C

// ===========================================================================
// Cybook Input Output - cyio.c
// Copyright (C) 2008-2010 Bookeen - All rights reserved
// ===========================================================================
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/pm.h>
#include <linux/proc_fs.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/regs-gpioj.h>
#include <cybook.h>
#include <linux/cyevent.h>
//#define DEBUG_MESSAGES
//#define DBG_IRQ
#include <asm/hardware.h>
#include <asm/delay.h>
#include <asm/uaccess.h>
#include <asm-arm/irq.h>
#include <asm/arch/gpio.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/regs-irq.h>
#include <asm-arm/arch-s3c2410/irqs.h>
#include <asm-arm/arch-s3c2410/gpio.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
#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 <developers@bookeen.com>");
MODULE_DESCRIPTION ("Cybook IO Manager");
MODULE_VERSION ("3.0");
// ===========================================================================