cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

tps6507x-ts.c (7015B)


      1/*
      2 * Touchscreen driver for the tps6507x chip.
      3 *
      4 * Copyright (c) 2009 RidgeRun (todd.fischer@ridgerun.com)
      5 *
      6 * Credits:
      7 *
      8 *    Using code from tsc2007, MtekVision Co., Ltd.
      9 *
     10 * For licencing details see kernel-base/COPYING
     11 *
     12 * TPS65070, TPS65073, TPS650731, and TPS650732 support
     13 * 10 bit touch screen interface.
     14 */
     15
     16#include <linux/module.h>
     17#include <linux/workqueue.h>
     18#include <linux/slab.h>
     19#include <linux/input.h>
     20#include <linux/platform_device.h>
     21#include <linux/mfd/tps6507x.h>
     22#include <linux/input/tps6507x-ts.h>
     23#include <linux/delay.h>
     24
     25#define TSC_DEFAULT_POLL_PERIOD 30 /* ms */
     26#define TPS_DEFAULT_MIN_PRESSURE 0x30
     27#define MAX_10BIT ((1 << 10) - 1)
     28
     29#define	TPS6507X_ADCONFIG_CONVERT_TS (TPS6507X_ADCONFIG_AD_ENABLE | \
     30					 TPS6507X_ADCONFIG_START_CONVERSION | \
     31					 TPS6507X_ADCONFIG_INPUT_REAL_TSC)
     32#define	TPS6507X_ADCONFIG_POWER_DOWN_TS (TPS6507X_ADCONFIG_INPUT_REAL_TSC)
     33
     34struct ts_event {
     35	u16	x;
     36	u16	y;
     37	u16	pressure;
     38};
     39
     40struct tps6507x_ts {
     41	struct device		*dev;
     42	struct input_dev	*input;
     43	struct tps6507x_dev	*mfd;
     44	char			phys[32];
     45	struct ts_event		tc;
     46	u16			min_pressure;
     47	bool			pendown;
     48};
     49
     50static int tps6507x_read_u8(struct tps6507x_ts *tsc, u8 reg, u8 *data)
     51{
     52	return tsc->mfd->read_dev(tsc->mfd, reg, 1, data);
     53}
     54
     55static int tps6507x_write_u8(struct tps6507x_ts *tsc, u8 reg, u8 data)
     56{
     57	return tsc->mfd->write_dev(tsc->mfd, reg, 1, &data);
     58}
     59
     60static s32 tps6507x_adc_conversion(struct tps6507x_ts *tsc,
     61				   u8 tsc_mode, u16 *value)
     62{
     63	s32 ret;
     64	u8 adc_status;
     65	u8 result;
     66
     67	/* Route input signal to A/D converter */
     68
     69	ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE, tsc_mode);
     70	if (ret) {
     71		dev_err(tsc->dev, "TSC mode read failed\n");
     72		goto err;
     73	}
     74
     75	/* Start A/D conversion */
     76
     77	ret = tps6507x_write_u8(tsc, TPS6507X_REG_ADCONFIG,
     78				TPS6507X_ADCONFIG_CONVERT_TS);
     79	if (ret) {
     80		dev_err(tsc->dev, "ADC config write failed\n");
     81		return ret;
     82	}
     83
     84	do {
     85		ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADCONFIG,
     86				       &adc_status);
     87		if (ret) {
     88			dev_err(tsc->dev, "ADC config read failed\n");
     89			goto err;
     90		}
     91	} while (adc_status & TPS6507X_ADCONFIG_START_CONVERSION);
     92
     93	ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_2, &result);
     94	if (ret) {
     95		dev_err(tsc->dev, "ADC result 2 read failed\n");
     96		goto err;
     97	}
     98
     99	*value = (result & TPS6507X_REG_ADRESULT_2_MASK) << 8;
    100
    101	ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_1, &result);
    102	if (ret) {
    103		dev_err(tsc->dev, "ADC result 1 read failed\n");
    104		goto err;
    105	}
    106
    107	*value |= result;
    108
    109	dev_dbg(tsc->dev, "TSC channel %d = 0x%X\n", tsc_mode, *value);
    110
    111err:
    112	return ret;
    113}
    114
    115/* Need to call tps6507x_adc_standby() after using A/D converter for the
    116 * touch screen interrupt to work properly.
    117 */
    118
    119static s32 tps6507x_adc_standby(struct tps6507x_ts *tsc)
    120{
    121	s32 ret;
    122	s32 loops = 0;
    123	u8 val;
    124
    125	ret = tps6507x_write_u8(tsc,  TPS6507X_REG_ADCONFIG,
    126				TPS6507X_ADCONFIG_INPUT_TSC);
    127	if (ret)
    128		return ret;
    129
    130	ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE,
    131				TPS6507X_TSCMODE_STANDBY);
    132	if (ret)
    133		return ret;
    134
    135	ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val);
    136	if (ret)
    137		return ret;
    138
    139	while (val & TPS6507X_REG_TSC_INT) {
    140		mdelay(10);
    141		ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val);
    142		if (ret)
    143			return ret;
    144		loops++;
    145	}
    146
    147	return ret;
    148}
    149
    150static void tps6507x_ts_poll(struct input_dev *input_dev)
    151{
    152	struct tps6507x_ts *tsc = input_get_drvdata(input_dev);
    153	bool pendown;
    154	s32 ret;
    155
    156	ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_PRESSURE,
    157				      &tsc->tc.pressure);
    158	if (ret)
    159		goto done;
    160
    161	pendown = tsc->tc.pressure > tsc->min_pressure;
    162
    163	if (unlikely(!pendown && tsc->pendown)) {
    164		dev_dbg(tsc->dev, "UP\n");
    165		input_report_key(input_dev, BTN_TOUCH, 0);
    166		input_report_abs(input_dev, ABS_PRESSURE, 0);
    167		input_sync(input_dev);
    168		tsc->pendown = false;
    169	}
    170
    171	if (pendown) {
    172
    173		if (!tsc->pendown) {
    174			dev_dbg(tsc->dev, "DOWN\n");
    175			input_report_key(input_dev, BTN_TOUCH, 1);
    176		} else
    177			dev_dbg(tsc->dev, "still down\n");
    178
    179		ret =  tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_X_POSITION,
    180					       &tsc->tc.x);
    181		if (ret)
    182			goto done;
    183
    184		ret =  tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_Y_POSITION,
    185					       &tsc->tc.y);
    186		if (ret)
    187			goto done;
    188
    189		input_report_abs(input_dev, ABS_X, tsc->tc.x);
    190		input_report_abs(input_dev, ABS_Y, tsc->tc.y);
    191		input_report_abs(input_dev, ABS_PRESSURE, tsc->tc.pressure);
    192		input_sync(input_dev);
    193		tsc->pendown = true;
    194	}
    195
    196done:
    197	tps6507x_adc_standby(tsc);
    198}
    199
    200static int tps6507x_ts_probe(struct platform_device *pdev)
    201{
    202	struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent);
    203	const struct tps6507x_board *tps_board;
    204	const struct touchscreen_init_data *init_data;
    205	struct tps6507x_ts *tsc;
    206	struct input_dev *input_dev;
    207	int error;
    208
    209	/*
    210	 * tps_board points to pmic related constants
    211	 * coming from the board-evm file.
    212	 */
    213	tps_board = dev_get_platdata(tps6507x_dev->dev);
    214	if (!tps_board) {
    215		dev_err(tps6507x_dev->dev,
    216			"Could not find tps6507x platform data\n");
    217		return -ENODEV;
    218	}
    219
    220	/*
    221	 * init_data points to array of regulator_init structures
    222	 * coming from the board-evm file.
    223	 */
    224	init_data = tps_board->tps6507x_ts_init_data;
    225
    226	tsc = devm_kzalloc(&pdev->dev, sizeof(struct tps6507x_ts), GFP_KERNEL);
    227	if (!tsc) {
    228		dev_err(tps6507x_dev->dev, "failed to allocate driver data\n");
    229		return -ENOMEM;
    230	}
    231
    232	tsc->mfd = tps6507x_dev;
    233	tsc->dev = tps6507x_dev->dev;
    234	tsc->min_pressure = init_data ?
    235			init_data->min_pressure : TPS_DEFAULT_MIN_PRESSURE;
    236
    237	snprintf(tsc->phys, sizeof(tsc->phys),
    238		 "%s/input0", dev_name(tsc->dev));
    239
    240	input_dev = devm_input_allocate_device(&pdev->dev);
    241	if (!input_dev) {
    242		dev_err(tsc->dev, "Failed to allocate polled input device.\n");
    243		return -ENOMEM;
    244	}
    245
    246	tsc->input = input_dev;
    247	input_set_drvdata(input_dev, tsc);
    248
    249	input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
    250	input_set_abs_params(input_dev, ABS_X, 0, MAX_10BIT, 0, 0);
    251	input_set_abs_params(input_dev, ABS_Y, 0, MAX_10BIT, 0, 0);
    252	input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_10BIT, 0, 0);
    253
    254	input_dev->name = "TPS6507x Touchscreen";
    255	input_dev->phys = tsc->phys;
    256	input_dev->dev.parent = tsc->dev;
    257	input_dev->id.bustype = BUS_I2C;
    258	if (init_data) {
    259		input_dev->id.vendor = init_data->vendor;
    260		input_dev->id.product = init_data->product;
    261		input_dev->id.version = init_data->version;
    262	}
    263
    264	error = tps6507x_adc_standby(tsc);
    265	if (error)
    266		return error;
    267
    268	error = input_setup_polling(input_dev, tps6507x_ts_poll);
    269	if (error)
    270		return error;
    271
    272	input_set_poll_interval(input_dev,
    273				init_data ? init_data->poll_period :
    274					    TSC_DEFAULT_POLL_PERIOD);
    275
    276	error = input_register_device(input_dev);
    277	if (error)
    278		return error;
    279
    280	return 0;
    281}
    282
    283static struct platform_driver tps6507x_ts_driver = {
    284	.driver = {
    285		.name = "tps6507x-ts",
    286	},
    287	.probe = tps6507x_ts_probe,
    288};
    289module_platform_driver(tps6507x_ts_driver);
    290
    291MODULE_AUTHOR("Todd Fischer <todd.fischer@ridgerun.com>");
    292MODULE_DESCRIPTION("TPS6507x - TouchScreen driver");
    293MODULE_LICENSE("GPL v2");
    294MODULE_ALIAS("platform:tps6507x-ts");