/*++
linux/drivers/input/keyboard/wmt_kpad.c
Some descriptions of such software. Copyright (c) 2008 WonderMedia Technologies, Inc.
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, see .
WonderMedia Technologies, Inc.
10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C.
--*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* #define COUNTTIMER */
#ifdef COUNTTIMER
unsigned int start_time;
#endif
/* Debug macros */
#if 0
#define DPRINTK(fmt, args...) printk(KERN_ALERT "[%s]: " fmt, __func__ , ## args)
#else
#define DPRINTK(fmt, args...)
#endif
/* the shortest response time is 20 ms, original timeout (HZ/100)*100 */
#define wmt_saradc_timeout ((HZ/100)*2)
#define WMT_SARADC_FUNCTION_NUM 6
#define SAMETIMES 2
unsigned int HW_Hz;
unsigned int SW_timeout;
unsigned int INT_timeout;
enum adc_func {
FUNC_KPAD,
FUNC_BAT,
FUNC_NONE,
};
unsigned int func = FUNC_NONE;
/* SARADC battery */
unsigned int BatteryCODE;
static unsigned int bat_interval = 3;
static struct delayed_work bat_work;
static unsigned int wmt_saradc_codes[WMT_SARADC_FUNCTION_NUM] = {
[0] = KEY_VOLUMEUP,
[1] = KEY_VOLUMEDOWN,
[2] = KEY_BACK,
[3] = KEY_MENU,
[4] = KEY_HOME,
[5] = KEY_RESERVED,
};
/*high resolution timer */
static struct hrtimer wmt_saradc_hrtimer;
static struct input_dev *saradc_dev;
static struct wmt_saradc_s saradc = {
.ref = 0,
.res = NULL,
.regs = NULL,
.irq = 0,
};
int count_sample_rate(unsigned APB_clock, int Hz)
{
int temp_slot;
/* the Hz that we want */
temp_slot = APB_clock/(4096 * Hz); /* (APB clock/32)/ (128 * HZ)*/
return temp_slot;
}
static int saradc_sample_rate(unsigned ADC_clock, int Hz)
{
int temp_slot;
/* the Hz that we want */
temp_slot = ADC_clock/(128 * Hz); /* ADC clock/ (128 * HZ)*/
return temp_slot;
}
int saradc_event_table(unsigned int eventcode)
{
DPRINTK("eventcode = %d\n", eventcode);
if (eventcode >= 39 && eventcode <= 42)
return 0;
else if (eventcode >= 63 && eventcode <= 64)
return 1;
else if (eventcode >= 84 && eventcode <= 85)
return 2;
else if (eventcode >= 18 && eventcode <= 20)
return 3;
else if (eventcode == 0)
return 4;
else if (eventcode >= 29 && eventcode <= 31)
return 5;
else if (eventcode == 127)
return 5;
else
return 5;
}
static void wmt_saradc_hw_init(void)
{
unsigned int auto_temp_slot;
//unsigned int APB_clk;
unsigned int ADC_clk;
DPRINTK("Start\n");
/*
* Turn on saradc clocks.
*/
auto_pll_divisor(DEV_ADC, CLK_ENABLE, 0, 0);
/* Set the ADC clock to 4.8 MHz */
auto_pll_divisor(DEV_ADC, SET_DIV, 1, 4800);
/* Turn on SARADC controll power */
saradc.regs->Ctr0 &= ~PD;
/* Enable SARADC digital clock */
saradc.regs->Ctr0 |= DigClkEn;
/* Simply clean all previous saradc status. */
saradc.regs->Ctr0 |= (ClrIntADC | ClrIntTOut);
saradc.regs->Ctr1 |= ClrIntValDet;
if (((saradc.regs->Ctr2 & EndcIntStatus) == EndcIntStatus) ||
((saradc.regs->Ctr2 & TOutStatus) == TOutStatus) ||
((saradc.regs->Ctr2 & ValDetIntStatus) == ValDetIntStatus))
printk(KERN_ERR "[saradc] clear status failed! status = %x\n", saradc.regs->Ctr2);
/*Set Timeout Value*/
saradc.regs->Ctr0 &= 0xffff0000;
saradc.regs->Ctr0 |= TOutDly(0xffff);
/* get APB clock & count sample rate*/
ADC_clk = auto_pll_divisor(DEV_ADC, GET_FREQ, 0, 0);
DPRINTK("[%s] ADC_clk = %d\n", __func__, ADC_clk);
/* sample rate: 500 Hz , 1 ms/sample */
auto_temp_slot = saradc_sample_rate(ADC_clk, 500);
#if 0
/* get APB clock & count sample rate*/
APB_clk = auto_pll_divisor(DEV_APB, GET_FREQ, 0, 0);
/* sample rate: 1000 Hz , 1 ms/sample */
auto_temp_slot = count_sample_rate(APB_clk, HW_Hz);
DPRINTK("[%s] APB_clk = %d\n", __func__, APB_clk);
#endif
/*Set Sample Rate*/
saradc.regs->Ctr1 &= 0x0000ffff;
saradc.regs->Ctr1 |= (auto_temp_slot << 16);
DPRINTK("[%s] auto_temp_slot = %x ctr1: %x\n", __func__, auto_temp_slot, saradc.regs->Ctr1);
/* Set saradc as auto mode */
saradc.regs->Ctr0 |= AutoMode;
msleep(200);
/* Enable value changing interrupt and Buffer data valid */
saradc.regs->Ctr1 |= (ValDetIntEn | BufRd);
/* saradc.regs->Ctr0 |= TOutEn; */
DPRINTK("End\n");
}
enum hrtimer_restart wmt_saradc_timeout_hrtimer(struct hrtimer *timer)
{
unsigned int SARCODE = 0xffff;
static unsigned int OLDCODE = 0xffff;
static int time, same;
static bool saradc_flag = 1; /* 0: report event state, 1: get SARCODE value state */
int new_event = -1;
static int pre_event = -1, button_press;
ktime_t ktime;
/* count timeout value */
#ifdef COUNTTIMER
unsigned int end_time;
end_time = wmt_read_oscr();
printk(KERN_ERR "time = %d\n", (end_time - start_time)/3);
#endif
ktime = ktime_set(0, SW_timeout * 1000);
DPRINTK("[%s] Start\n", __func__);
while ((saradc.regs->Ctr2 & EndcIntStatus) == 0)
;
SARCODE = SARCode(saradc.regs->Ctr1);
if (saradc_flag && time < 10) {
if ((SARCODE/4 - OLDCODE/4) <= 1 || (SARCODE/4 - OLDCODE/4) >= -1) {
same++;
DPRINTK("time:%d SARCODE=%u SARCODE/4=%u, OLDCODE=%u, OLDCODE/4=%u, same=%d\n",
time, SARCODE, SARCODE/4, OLDCODE, OLDCODE/4, same);
if (same == SAMETIMES)
saradc_flag = 0; /* get the new event */
} else
same = 0;
DPRINTK("time:%d SARCODE=%u SARCODE/4=%u, OLDCODE=%u, OLDCODE/4=%u, same=%d\n",
time, SARCODE, SARCODE/4, OLDCODE, OLDCODE/4, same);
OLDCODE = SARCODE;
time++;
/* don't call timer when 10th get SARCODE or enough same time */
if (time < 10 && same != SAMETIMES) {
hrtimer_start(&wmt_saradc_hrtimer, ktime, HRTIMER_MODE_REL);
/* count timer from callback function to callback function */
#ifdef COUNTTIMER
start_time = wmt_read_oscr();
#endif
}
/* if not get stable SARCODE value in 10 times, report SARACODE is NONE event */
if (time == 10 && same != SAMETIMES) {
SARCODE = 508;
DPRINTK("time %d SARCODE %u", time, SARCODE);
}
}
if (time == 10 || saradc_flag == 0)
time = 0;
/* disable BufRd */
saradc.regs->Ctr1 &= ~BufRd;
new_event = saradc_event_table(SARCODE/4);
if (SARCODE == 0xffff) {
printk(KERN_ERR "Auto mode witn INT test fail\n");
/*Disable interrupt*/
saradc.regs->Ctr1 &= ~ValDetIntEn;
/* Clean all previous saradc status. */
saradc.regs->Ctr0 |= (ClrIntTOut | ClrIntADC);
saradc.regs->Ctr1 |= ClrIntValDet;
} else {
/*DPRINTK("Buf_rdata = %u Buf_rdata/4 = %u\n", data, data/4);*/
DPRINTK("SARCODE = %u SARCODE/4 = %u\n", SARCODE, SARCODE/4);
/*Disable interrupt*/
saradc.regs->Ctr1 &= ~ValDetIntEn;
/* Clean all previous saradc status. */
saradc.regs->Ctr0 |= (ClrIntTOut | ClrIntADC);
saradc.regs->Ctr1 |= ClrIntValDet;
}
if (saradc_flag == 0) {
/* switch other button means release button*/
if ((pre_event != new_event) && (SARCODE/4 != 127)) {
button_press = 0;
DPRINTK("Different event, pre_event = %d, new_event = %d\n", pre_event, new_event);
DPRINTK("WMT_ROW1_KEY_NUM release key = %d, event=%d\n", SARCODE/4, pre_event);
input_report_key(saradc_dev, wmt_saradc_codes[pre_event], 0); /* key is release*/
input_sync(saradc_dev);
}
if (SARCODE/4 == 127 || SARCODE/4 == 126) { /*Active Low*/
DPRINTK("WMT_ROW1_KEY_NUM release key = %d, event=%d\n", SARCODE/4, pre_event);
input_report_key(saradc_dev, wmt_saradc_codes[pre_event], 0); /* key is release*/
input_sync(saradc_dev);
button_press = 0;
} else {
if (button_press == 0) {
DPRINTK("new event = %d\n", new_event);
input_report_key(saradc_dev, wmt_saradc_codes[new_event], 1);/* key is press*/
input_sync(saradc_dev);
DPRINTK("saradc code = %d\n", wmt_saradc_codes[new_event]);
button_press = 1;
}
DPRINTK("WMT_ROW1_KEY_NUM keep press key = %d, event=%d\n", SARCODE/4, new_event);
}
pre_event = new_event;
saradc_flag = 1; /* report new event to Android, get new SARCODE */
same = 0;
}
saradc.regs->Ctr1 |= ValDetIntEn;
DPRINTK("[%s] End\n", __func__);
return HRTIMER_NORESTART; /* avoid to timer restart */
}
static irqreturn_t
saradc_interrupt(int irq, void *dev_id)
{
ktime_t ktime;
ktime = ktime_set(0, INT_timeout * 1000); /* ms */
DPRINTK("[%s] Start\n", __func__);
DPRINTK("status = %x\n", saradc.regs->Ctr2);
/* Disable interrupt */
/* disable_irq_nosync(saradc.irq);*/
saradc.regs->Ctr1 &= ~ValDetIntEn;
saradc.regs->Ctr1 |= BufRd;
/*
* Get saradc interrupt status and clean interrput source.
*/
/* if (((saradc.regs->Ctr1 & ValDetIntEn) == ValDetIntEn) && */
if ((saradc.regs->Ctr2 & ValDetIntStatus) == ValDetIntStatus) {
/* clear value chaning interrupt */
saradc.regs->Ctr1 |= ClrIntValDet;
/* start hrtimer */
hrtimer_start(&wmt_saradc_hrtimer, ktime, HRTIMER_MODE_REL);
/* count timer from interrupt to callback function */
#ifdef COUNTTIMER
start_time = wmt_read_oscr();
#endif
}
if ((saradc.regs->Ctr2 & ValDetIntStatus) == ValDetIntStatus)
printk(KERN_ERR "[saradc] status clear failed!\n");
/* Enable interrupt */
/* saradc.regs->Ctr1 |= ValDetIntEn; // enable INT in wmt_saradc_timeout_timer*/
DPRINTK("[%s] End\n", __func__);
return IRQ_HANDLED;
}
static unsigned int saradc_read(void)
{
int i;
int min=0xfff,max=0;
int total=0,val;
for(i=0; i < 7; i++){
while ((saradc.regs->Ctr2 & EndcIntStatus) == 0);
val = SARCode(saradc.regs->Ctr1);
//printk("%d--",val);
if(max < val) max = val;
if(min > val) min = val;
total +=val;
}
//printk("value %d\n",(total-max-min)/5);
return (total-max-min)/5;
}
/* Read SARADC BATTRTY CODE */
unsigned int ReadBattery(void)
{
return BatteryCODE;
}
EXPORT_SYMBOL_GPL(ReadBattery);
/* Update SARCODE BATTERY CODE */
static void WriteBattery(unsigned int value)
{
BatteryCODE = value;
}
static void saradc_bat_handler(struct work_struct *work)
{
unsigned int sarcode = 0xffff;
DPRINTK("Start\n");
/* disable value change interrupt */
saradc.regs->Ctr1 &= ~ValDetIntEn;
/* Switch to BATTERY channel and clear INT status */
saradc.regs->Ctr0 |= (AdcChSel | ClrIntADC | ClrIntTOut);
saradc.regs->Ctr1 |= (ClrIntValDet);
msleep(20);
//printk("bat:\n");
sarcode = saradc_read();
WriteBattery(sarcode/4);
DPRINTK("sarcode = %d\n", sarcode);
/* Switch to ADC channel */
saradc.regs->Ctr0 &= ~AdcChSel;
/* too early to clear status will cause interrupts */
msleep(5);
/* Switch to BATTERY channel and clear INT status */
saradc.regs->Ctr0 |= (ClrIntADC | ClrIntTOut);
saradc.regs->Ctr1 |= (ClrIntValDet);
/* enable value change interrupt */
saradc.regs->Ctr1 |= ValDetIntEn;
schedule_delayed_work(&bat_work, bat_interval*HZ);
DPRINTK("End\n\n\n");
return ;
}
static int saradc_open(struct input_dev *dev)
{
int ret = 0;
unsigned int i;
DPRINTK("Start saradc.ref = %d\n", saradc.ref);
if (saradc.ref++) {
/* Return success, but not initialize again. */
DPRINTK("End 1 saradc.ref=%d\n", saradc.ref);
return 0;
}
if (func != FUNC_KPAD)
goto bat_init;
ret = request_irq(saradc.irq, saradc_interrupt, IRQF_DISABLED, "saradc", dev);
if (ret) {
printk(KERN_ERR "%s: Can't allocate irq %d\n", __func__, IRQ_TSC);
saradc.ref--;
free_irq(saradc.irq, dev);
goto saradc_open_out;
}
/* Init hr timer */
hrtimer_init(&wmt_saradc_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
wmt_saradc_hrtimer.function = &wmt_saradc_timeout_hrtimer;
/* Register an input event device. */
dev->name = "saradc",
dev->phys = "saradc",
/*
* Let kpad to implement key repeat.
*/
set_bit(EV_KEY, dev->evbit);
for (i = 0; i < WMT_SARADC_FUNCTION_NUM; i++)
set_bit(wmt_saradc_codes[i], dev->keybit);
dev->keycode = wmt_saradc_codes;
dev->keycodesize = sizeof(unsigned int);
dev->keycodemax = WMT_SARADC_FUNCTION_NUM;
/*
* For better view of /proc/bus/input/devices
*/
dev->id.bustype = 0;
dev->id.vendor = 0;
dev->id.product = 0;
dev->id.version = 0;
input_register_device(dev);
bat_init:
if (func == FUNC_BAT) {
INIT_DELAYED_WORK(&bat_work,saradc_bat_handler);
schedule_delayed_work(&bat_work, HZ);
}
wmt_saradc_hw_init();
DPRINTK("End2\n");
saradc_open_out:
DPRINTK("End3\n");
return ret;
}
static void saradc_close(struct input_dev *dev)
{
DPRINTK("Start\n");
if (--saradc.ref) {
DPRINTK("End1\n");
return;
}
/* Free interrupt resource */
free_irq(saradc.irq, dev);
/*Disable clock*/
auto_pll_divisor(DEV_ADC, CLK_DISABLE, 0, 0);
/* Unregister input device driver */
input_unregister_device(dev);
DPRINTK("End2\n");
}
static int wmt_saradc_probe(struct platform_device *pdev)
{
unsigned long base;
int ret = 0;
DPRINTK("Start\n");
saradc_dev = input_allocate_device();
if (saradc_dev == NULL) {
DPRINTK("End 1\n");
return -1;
}
/*
* Simply check resources parameters.
*/
if (pdev->num_resources < 2 || pdev->num_resources > 3) {
ret = -ENODEV;
goto saradc_probe_out;
}
base = pdev->resource[0].start;
saradc.irq = pdev->resource[1].start;
saradc.regs = (struct saradc_regs_s *)ADC_BASE_ADDR;
if (!saradc.regs) {
ret = -ENOMEM;
goto saradc_probe_out;
}
saradc_dev->open = saradc_open,
saradc_dev->close = saradc_close,
saradc_open(saradc_dev);
DPRINTK("End2\n");
saradc_probe_out:
#ifndef CONFIG_SKIP_DRIVER_MSG
printk(KERN_INFO "WMT saradc driver initialized: %s\n",
(ret == 0) ? "ok" : "failed");
#endif
DPRINTK("End3\n");
return ret;
}
static int wmt_saradc_remove(struct platform_device *pdev)
{
DPRINTK("Start\n");
saradc_close(saradc_dev);
/*
* Free allocated resource
*/
/*kfree(kpad.res);
kpad.res = NULL;
if (kpad.regs) {
iounmap(kpad.regs);
kpad.regs = NULL;
}*/
saradc.ref = 0;
saradc.irq = 0;
DPRINTK("End\n");
return 0;
}
static int wmt_saradc_suspend(struct platform_device *pdev, pm_message_t state)
{
DPRINTK("Start\n");
switch (state.event) {
case PM_EVENT_SUSPEND:
/*Disable clock*/
auto_pll_divisor(DEV_ADC, CLK_DISABLE, 0, 0);
break;
case PM_EVENT_FREEZE:
case PM_EVENT_PRETHAW:
default:
break;
}
DPRINTK("End2\n");
return 0;
}
static int wmt_saradc_resume(struct platform_device *pdev)
{
DPRINTK("Start\n");
wmt_saradc_hw_init();
DPRINTK("End\n");
return 0;
}
static struct platform_driver wmt_saradc_driver = {
.driver.name = "wmt-saradc",
.probe = &wmt_saradc_probe,
.remove = &wmt_saradc_remove,
.suspend = &wmt_saradc_suspend,
.resume = &wmt_saradc_resume
};
static struct resource wmt_saradc_resources[] = {
[0] = {
.start = ADC_BASE_ADDR,
.end = (ADC_BASE_ADDR + 0xFFFF),
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_TSC,
.end = IRQ_TSC,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device wmt_saradc_device = {
.name = "wmt-saradc",
.id = 0,
.num_resources = ARRAY_SIZE(wmt_saradc_resources),
.resource = wmt_saradc_resources,
};
static int __init saradc_init(void)
{
int ret;
int retval;
unsigned char buf[80];
int varlen = 80;
char *varname1 = "wmt.keypad.param";
char *varname2 = "wmt.battery.param";
char *p;
int temp = 0, enable_saradc = 0, function_sel = 0;
DPRINTK(KERN_ALERT "Start\n");
/*read keypad enable*/
retval = wmt_getsyspara(varname1, buf, &varlen);
if (retval == 0) {
sscanf(buf, "%X:%d:%d:%d", &temp, &HW_Hz, &INT_timeout, &SW_timeout);
enable_saradc = temp & 0xf;
function_sel = (temp >> 4) & 0xf;
printk(KERN_ALERT "wmt.keypad.param = %x:%d:%d:%d, enable = %x, function = %x\n",
temp, HW_Hz, INT_timeout, SW_timeout, enable_saradc, function_sel);
if (enable_saradc != 1 || function_sel != 1) {
printk(KERN_ALERT "Disable SARADC as keypad function!!\n");
goto bat;
} else if (enable_saradc == 1 && function_sel == 1)
printk(KERN_ALERT "HW_HZ = %d, INT_time = %d, SW_timeout = %d\n",
HW_Hz, INT_timeout, SW_timeout);
if ((HW_Hz == 0) || (INT_timeout == 0) || (SW_timeout == 0)) {
HW_Hz = 1000; /* 1000 Hz */
INT_timeout = 20000; /* 20 ms */
SW_timeout = 1000; /* 1 ms */
printk(KERN_ALERT "wmt.keypad.param isn't correct. Set the default value\n");
printk(KERN_ALERT "Default HW_HZ = %d, INT_time = %d, SW_timeout = %d\n",
HW_Hz, INT_timeout, SW_timeout);
}
func = FUNC_KPAD;
} else {
printk(KERN_ALERT "##Warning: \"wmt.keypad.param\" not find\n");
printk(KERN_ALERT "Default wmt.keypad.param = %x\n", temp);
//return -ENODEV;
}
bat:
if (func != FUNC_KPAD) {
memset(buf, 0x00,sizeof(buf));
/* read battery enable, dev name and */
retval = wmt_getsyspara(varname2, buf, &varlen);
if (retval == 0) {
p = buf;
if(!strncmp(p,"saradc", 6)){
p = strchr(p,':');
if(p){
p++;
sscanf(p,"%d",&bat_interval);
}
printk("Bat ADC sample period = %ds\n", bat_interval);
func = FUNC_BAT;
}
}
}
if (func == FUNC_NONE) {
printk("SARADC not enable\n");
return -ENODEV;
}
/* check saradc can switch freq.
#ifdef CONFIG_CPU_FREQ
ret = cpufreq_register_notifier(&kpad_clock_nblock, \
CPUFREQ_TRANSITION_NOTIFIER);
if (ret) {
printk(KERN_ERR "Unable to register CPU frequency " \
"change notifier (%d)\n", ret);
}
#endif
*/
ret = platform_device_register(&wmt_saradc_device);
if (ret != 0) {
DPRINTK("End1 ret = %x\n", ret);
return -ENODEV;
}
ret = platform_driver_register(&wmt_saradc_driver);
DPRINTK("End2 ret = %x\n", ret);
return ret;
}
static void __exit saradc_exit(void)
{
DPRINTK("Start\n");
platform_driver_unregister(&wmt_saradc_driver);
platform_device_unregister(&wmt_saradc_device);
DPRINTK("End\n");
}
module_init(saradc_init);
module_exit(saradc_exit);
MODULE_AUTHOR("WonderMedia Technologies, Inc.");
MODULE_DESCRIPTION("WMT [generic saradc] driver");
MODULE_LICENSE("GPL");