398 lines
9.8 KiB
C
Executable File
398 lines
9.8 KiB
C
Executable File
/*
|
|
* GSC3280 SoC ADC(SPI0) Controller Driver
|
|
*
|
|
* Copyright (C) 2013 BLX IC Design Corp.,Ltd.
|
|
* Author: Davied, apple_guet@126.com
|
|
*
|
|
*/
|
|
#include <linux/types.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/device.h>
|
|
#include <linux/spi/spi.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/sysfs.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/input.h>
|
|
#include <linux/irqreturn.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/input.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/pm.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/spi/gsc3280_spi.h>
|
|
#include <gsc3280/gsc3280_int.h>
|
|
|
|
|
|
#ifdef CONFIG_GSC3280_TS_DEBUG
|
|
#define DBG(msg...) do { \
|
|
printk(KERN_INFO msg); \
|
|
} while (0)
|
|
#else
|
|
#define DBG(msg...) do { } while(0)
|
|
#endif
|
|
|
|
#define MAX_TRY_CNT 10000
|
|
#define MAX_VALID_CNT 1
|
|
/* Touch screen coordinates */
|
|
#define GSC_X_MIN 0
|
|
#define GSC_X_MAX 4096
|
|
#define GSC_X_FUZZ 5
|
|
#define GSC_Y_MIN 0
|
|
#define GSC_Y_MAX 4096
|
|
#define GSC_Y_FUZZ 5
|
|
#define GSC_PRESSURE_MIN 0
|
|
#define GSC_PRESSURE_MAX 2000
|
|
#define ICTL_RAW_STATUS_REG 0xbc048018 //irq register
|
|
#define ICTL_SPI0_BITS 1 << 29
|
|
/* gsc3280 ts press state */
|
|
#define TS_PRESS_UP 0x00
|
|
#define TS_PRESS_DOWN 0x01
|
|
#define SPI0_STATUS_REG *((volatile unsigned int *)0xbc101018)
|
|
#define SPI_BUSY_STAT 0x10
|
|
/* receive fifo not empty, then can read data */
|
|
#define SPI_RX_N_EMPTY BIT(2)
|
|
#define SPI_RX_FULL BIT(3) //transmit fifo full
|
|
#define SPI0_DATA_REG *((volatile unsigned int *)0xbc101024)
|
|
|
|
#define GSC3280_TS_NAME_SIZE 20
|
|
#define GSC3280_TS_NAME "gsc-ts"
|
|
/***************** ADC DECLARE ***************/
|
|
#define ADC_CMD_MEASUREX 0x4000
|
|
#define ADC_CMD_MEASUREY 0x5000
|
|
#define ADC_CMD_MEASUREZ 0x6000
|
|
|
|
|
|
/* Touch screen device structure */
|
|
struct gsc3280_ts {
|
|
char phys[32];
|
|
spinlock_t slock;
|
|
struct clk *clk;
|
|
void __iomem *regs;
|
|
struct device *dev;
|
|
struct resource *irq;
|
|
struct input_dev *input;
|
|
};
|
|
|
|
|
|
/*
|
|
* desc: prepare to read data from adc
|
|
* write data to adc with spi interface
|
|
*/
|
|
unsigned short spi0_read(void)
|
|
{
|
|
int cnt= 0;
|
|
//DBG("enter adc_read()\n");
|
|
do {
|
|
while (SPI0_STATUS_REG & 0x10)
|
|
if (cnt++ > 10000)
|
|
break;
|
|
} while(0);
|
|
cnt = 0;
|
|
/*spi0 fifo receive not empty*/
|
|
while (!(SPI0_STATUS_REG & SPI_RX_N_EMPTY))
|
|
if (cnt++>10000000)
|
|
break;
|
|
//DBG("leave adc_read()\n");
|
|
return (unsigned short)(SPI0_DATA_REG);
|
|
}
|
|
|
|
void spi0_write(unsigned short v)
|
|
{
|
|
int cnt=0;
|
|
//DBG("enter adc_write()\n");
|
|
do {
|
|
while (SPI0_STATUS_REG & 0x10)
|
|
if (cnt++ > 10000)
|
|
break;
|
|
} while(0);
|
|
cnt = 0;
|
|
/*spi0 fifo can write, transmit fifo empty */
|
|
while (SPI0_STATUS_REG & SPI_RX_FULL)
|
|
if (cnt++ > 1000000)
|
|
break;
|
|
SPI0_DATA_REG = v;
|
|
//DBG("leave adc_write()\n");
|
|
}
|
|
|
|
unsigned short adc_cmd(unsigned short cmd)
|
|
{
|
|
unsigned short res = 0;
|
|
DBG("enter adc_cmd\n");
|
|
spi0_write( cmd );
|
|
while (1) {
|
|
res = spi0_read();
|
|
if ((res == 0xF000) || (res == (0x8000 | (cmd >> 12)))) {
|
|
spi0_write(0xF000);
|
|
}
|
|
else if ( res < 0x1000 ) { //data
|
|
char buf[20] ;
|
|
sprintf(buf, "adc_cmd %0x result %0x\n", cmd, res);
|
|
//DBG(buf);
|
|
return res;
|
|
}
|
|
else{
|
|
spi0_write(cmd);
|
|
}
|
|
}
|
|
//DBG("leave adc_cmd\n");
|
|
return res;
|
|
}
|
|
|
|
static void gsc3280_report_event(struct gsc3280_ts *ts, u32 x, u32 y, u32 z)
|
|
{
|
|
#ifdef CONFIG_GSC3280_POS_PRINT
|
|
printk(KERN_INFO "x = %d\n", x);
|
|
printk(KERN_INFO "y = %d\n", y);
|
|
printk(KERN_INFO "z = %d\n", z);
|
|
#endif
|
|
|
|
input_report_abs(ts->input, ABS_PRESSURE, z);
|
|
input_report_abs(ts->input, ABS_X, x);
|
|
input_report_abs(ts->input, ABS_Y, y);
|
|
if (z > 0)
|
|
input_report_key(ts->input, BTN_TOUCH, 1);
|
|
else
|
|
input_report_key(ts->input, BTN_TOUCH, 0);
|
|
input_sync(ts->input);
|
|
}
|
|
|
|
static int test_ts_state(void)
|
|
{
|
|
u32 state = *((volatile unsigned int *)(ICTL_RAW_STATUS_REG)) ;
|
|
|
|
if (state & ICTL_SPI0_BITS) {
|
|
return TS_PRESS_DOWN;
|
|
} else
|
|
return TS_PRESS_UP;
|
|
}
|
|
|
|
/* PENDET interrupt handler */
|
|
static irqreturn_t gsc3280_ts_irq(int irq, void *dev_id)
|
|
{
|
|
u8 flg = 0;
|
|
u32 x = 0, y = 0, z = 0;
|
|
struct gsc3280_ts *ts = dev_id;
|
|
|
|
begin:
|
|
x = adc_cmd(ADC_CMD_MEASUREX);
|
|
y = adc_cmd(ADC_CMD_MEASUREY);
|
|
z = adc_cmd(ADC_CMD_MEASUREZ);
|
|
x = ((x -0x83) * 800) / (0xf5d - 0x83);
|
|
y = ((0xeea - y) * 480) / (0xeea - 0xcc);
|
|
|
|
if ((z < 700) && (z > 10) && (x > 0) && (x < 800) && (y > 0) && (y < 480)) {
|
|
if (flg == 0) {
|
|
flg = 1;
|
|
gsc3280_report_event(ts, x, y, 0);
|
|
}
|
|
gsc3280_report_event(ts, x, y, z);
|
|
msleep(10);
|
|
}
|
|
if (test_ts_state() == TS_PRESS_UP)
|
|
goto Up;
|
|
else
|
|
goto begin;
|
|
Up:
|
|
if (flg == 0)
|
|
return IRQ_HANDLED;
|
|
if ((x == 0) || (y == 0))
|
|
return IRQ_HANDLED;
|
|
gsc3280_report_event(ts, x, y, 0);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static void adc_sysctl(void)
|
|
{
|
|
int c = 0;
|
|
unsigned int rsl = 0;
|
|
*(volatile unsigned int *)0xbc04a00c |= 0x10;
|
|
*(volatile unsigned int *)0xbc04a008 |= 0x1000000;
|
|
*(volatile unsigned int *)(0xbc04a0ac) = 1;
|
|
*(volatile unsigned int *)(0xbc04a008) |= 0x8;
|
|
//setup sysctrl module io reuse
|
|
*(volatile unsigned int *)(0xbc04a0b0) |= 1 << 29;
|
|
//devide freq
|
|
*(volatile unsigned int *)(0xbc04a034) = 0x13;
|
|
//*(volatile unsigned int *)(0xbc04a034) = 0x9;
|
|
*(volatile unsigned int *)(0xbc101004) = 0x1;
|
|
*(volatile unsigned int *)(0xbc101000) = 0xf07;
|
|
msleep(300);
|
|
*(volatile unsigned int *)(0xbc101050) = 0x0;
|
|
c = 0;
|
|
while(1){
|
|
*(volatile unsigned int *)(0xbc101024) = 0x0;
|
|
while ( *(volatile unsigned int *)(0xbc101018) & 0x10 ) {
|
|
;
|
|
}
|
|
rsl = *(volatile unsigned int *)(0xbc101024);
|
|
if (rsl == 0x8000)
|
|
break;
|
|
if (c++ > 1000)
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int __devinit gsc3280_ts_probe(struct platform_device *pdev)
|
|
{
|
|
int ret = 0, size = 0;
|
|
struct gsc3280_ts *ts;
|
|
unsigned long rate = 0;
|
|
struct input_dev *input;
|
|
struct resource *mem, *ioarea;
|
|
|
|
adc_sysctl();
|
|
DBG("############\n");
|
|
printk(KERN_INFO "gsc3280 touch screen probe start.\n");
|
|
ts = kzalloc(sizeof(struct gsc3280_ts), GFP_KERNEL);
|
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!mem) {
|
|
DBG("no mem resource.\n");
|
|
ret = -EINVAL;
|
|
goto err_alloc;
|
|
}
|
|
size = resource_size(mem);
|
|
ioarea = request_mem_region(mem->start, size, pdev->name);
|
|
if (!ioarea) {
|
|
DBG("SPI region already claimed.\n");
|
|
ret = -EBUSY;
|
|
goto err_alloc;
|
|
}
|
|
ts->regs = ioremap_nocache(mem->start, resource_size(mem));
|
|
if (!ts->regs) {
|
|
DBG("SPI region already mapped.\n");
|
|
ret = -ENOMEM;
|
|
goto err_mem;
|
|
}
|
|
DBG("probe: mapped spi0 base=%p.\n", ts->regs);
|
|
|
|
ts->clk = clk_get(NULL, "spi0");
|
|
if (IS_ERR(ts->clk)) {
|
|
DBG("failed to find watchdog clock source.\n");
|
|
ret = PTR_ERR(ts->clk);
|
|
goto err_map;
|
|
}
|
|
rate = clk_get_rate(ts->clk);
|
|
DBG("rate is %ld.\n", rate);
|
|
clk_enable(ts->clk);
|
|
input = input_allocate_device();
|
|
if (!input) {
|
|
ret = -ENOMEM;
|
|
goto err_clk;
|
|
}
|
|
ts->irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
|
if (ts->irq == NULL) {
|
|
DBG("!!!!no irq resource specified!\n");
|
|
ret = -ENOENT;
|
|
goto err_free_input_mem;
|
|
}
|
|
ret = request_threaded_irq(ts->irq ->start, NULL, gsc3280_ts_irq, IRQF_ONESHOT, "adcirq", ts);
|
|
if (ret) {
|
|
DBG("!!!!request_threaded_irq error!\n");
|
|
goto err_free_input_mem;
|
|
}
|
|
|
|
ts->dev = &pdev->dev;
|
|
spin_lock_init(&ts->slock);
|
|
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(ts->dev));
|
|
input->name = "gsc3280-ts";
|
|
input->phys = ts->phys;
|
|
input->dev.parent = ts->dev;
|
|
input->id.vendor = 0x00; //tsdev->vendor;
|
|
input->id.version = 0x00; //tsdev->rev;
|
|
input->id.product = 0x03; //tsdev->rev;
|
|
input->id.bustype = BUS_HOST; //should be spi
|
|
|
|
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
|
input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
|
|
|
__set_bit(EV_REP, input->evbit);
|
|
|
|
__set_bit(EV_KEY, input->evbit);
|
|
__set_bit(BTN_TOUCH, input->keybit);
|
|
__set_bit(EV_ABS, input->evbit);
|
|
__set_bit(ABS_X, input->absbit);
|
|
__set_bit(ABS_Y, input->absbit);
|
|
__set_bit(ABS_PRESSURE, input->absbit);
|
|
|
|
ts->input = input;
|
|
input_set_abs_params(ts->input, ABS_X, GSC_X_MIN, GSC_X_MAX, GSC_X_FUZZ, 0);
|
|
input_set_abs_params(ts->input, ABS_Y, GSC_Y_MIN, GSC_Y_MAX, GSC_Y_FUZZ, 0);
|
|
input_set_abs_params(ts->input, ABS_PRESSURE, GSC_PRESSURE_MIN, GSC_PRESSURE_MAX, 0, 0);
|
|
ret = input_register_device(ts->input);
|
|
if (ret) {
|
|
DBG("!!!!input register device error!\n");
|
|
goto err_free_irq;
|
|
}
|
|
platform_set_drvdata(pdev, ts);
|
|
printk(KERN_INFO "gsc3280 touch screen probe success.\n");
|
|
DBG("############\n");
|
|
return 0;
|
|
|
|
err_free_irq:
|
|
free_irq(ts->irq->start, ts);
|
|
err_free_input_mem:
|
|
input_free_device(input);
|
|
err_clk:
|
|
clk_disable(ts->clk);
|
|
clk_put(ts->clk);
|
|
err_map:
|
|
iounmap(ts->regs);
|
|
err_mem:
|
|
release_mem_region(mem->start, size);
|
|
mem = NULL;
|
|
err_alloc:
|
|
kfree(ts);
|
|
printk(KERN_INFO "!!!!gsc3280 touch screen probe error!\n");
|
|
return ret;
|
|
}
|
|
|
|
static int __devexit gsc3280_ts_remove(struct platform_device *pdev)
|
|
{
|
|
struct gsc3280_ts *ts = platform_get_drvdata(pdev);
|
|
free_irq(ts->irq->start, ts);
|
|
input_unregister_device(ts->input);
|
|
iounmap(ts->regs);
|
|
clk_disable(ts->clk);
|
|
clk_put(ts->clk);
|
|
kfree(ts);
|
|
platform_set_drvdata(pdev, NULL);
|
|
printk(KERN_INFO "gsc3280 touch screen remove\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
static struct platform_driver gsc3280_ts_driver = {
|
|
.driver = {
|
|
.name = "gsc3280-ts",
|
|
.owner = THIS_MODULE,
|
|
},
|
|
.probe = gsc3280_ts_probe,
|
|
.remove = __devexit_p(gsc3280_ts_remove),
|
|
};
|
|
|
|
static int __init gsc3280_ts_init(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = platform_driver_register(&gsc3280_ts_driver);
|
|
if (ret)
|
|
printk(KERN_ERR "!!!!!!gsc ts init register error!!!!!!\n");
|
|
return ret;
|
|
}
|
|
static void __exit gsc3280_ts_exit(void)
|
|
{
|
|
platform_driver_unregister(&gsc3280_ts_driver);
|
|
}
|
|
subsys_initcall(gsc3280_ts_init);
|
|
module_exit(gsc3280_ts_exit);
|
|
|
|
MODULE_AUTHOR("Davied<apple_guet@126.com>");
|
|
MODULE_DESCRIPTION("GSC3280 touchscreen Driver");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_ALIAS("gsc3280 touch screen");
|
|
|