/* * GSC3280 SoC ADC(SPI0) Controller Driver * * Copyright (C) 2013 BLX IC Design Corp.,Ltd. * Author: Davied, apple_guet@126.com * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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"); MODULE_DESCRIPTION("GSC3280 touchscreen Driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("gsc3280 touch screen");