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

mcs5000_ts.c (7931B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * mcs5000_ts.c - Touchscreen driver for MELFAS MCS-5000 controller
      4 *
      5 * Copyright (C) 2009 Samsung Electronics Co.Ltd
      6 * Author: Joonyoung Shim <jy0922.shim@samsung.com>
      7 *
      8 * Based on wm97xx-core.c
      9 */
     10
     11#include <linux/module.h>
     12#include <linux/i2c.h>
     13#include <linux/interrupt.h>
     14#include <linux/input.h>
     15#include <linux/irq.h>
     16#include <linux/platform_data/mcs.h>
     17#include <linux/slab.h>
     18
     19/* Registers */
     20#define MCS5000_TS_STATUS		0x00
     21#define STATUS_OFFSET			0
     22#define STATUS_NO			(0 << STATUS_OFFSET)
     23#define STATUS_INIT			(1 << STATUS_OFFSET)
     24#define STATUS_SENSING			(2 << STATUS_OFFSET)
     25#define STATUS_COORD			(3 << STATUS_OFFSET)
     26#define STATUS_GESTURE			(4 << STATUS_OFFSET)
     27#define ERROR_OFFSET			4
     28#define ERROR_NO			(0 << ERROR_OFFSET)
     29#define ERROR_POWER_ON_RESET		(1 << ERROR_OFFSET)
     30#define ERROR_INT_RESET			(2 << ERROR_OFFSET)
     31#define ERROR_EXT_RESET			(3 << ERROR_OFFSET)
     32#define ERROR_INVALID_REG_ADDRESS	(8 << ERROR_OFFSET)
     33#define ERROR_INVALID_REG_VALUE		(9 << ERROR_OFFSET)
     34
     35#define MCS5000_TS_OP_MODE		0x01
     36#define RESET_OFFSET			0
     37#define RESET_NO			(0 << RESET_OFFSET)
     38#define RESET_EXT_SOFT			(1 << RESET_OFFSET)
     39#define OP_MODE_OFFSET			1
     40#define OP_MODE_SLEEP			(0 << OP_MODE_OFFSET)
     41#define OP_MODE_ACTIVE			(1 << OP_MODE_OFFSET)
     42#define GESTURE_OFFSET			4
     43#define GESTURE_DISABLE			(0 << GESTURE_OFFSET)
     44#define GESTURE_ENABLE			(1 << GESTURE_OFFSET)
     45#define PROXIMITY_OFFSET		5
     46#define PROXIMITY_DISABLE		(0 << PROXIMITY_OFFSET)
     47#define PROXIMITY_ENABLE		(1 << PROXIMITY_OFFSET)
     48#define SCAN_MODE_OFFSET		6
     49#define SCAN_MODE_INTERRUPT		(0 << SCAN_MODE_OFFSET)
     50#define SCAN_MODE_POLLING		(1 << SCAN_MODE_OFFSET)
     51#define REPORT_RATE_OFFSET		7
     52#define REPORT_RATE_40			(0 << REPORT_RATE_OFFSET)
     53#define REPORT_RATE_80			(1 << REPORT_RATE_OFFSET)
     54
     55#define MCS5000_TS_SENS_CTL		0x02
     56#define MCS5000_TS_FILTER_CTL		0x03
     57#define PRI_FILTER_OFFSET		0
     58#define SEC_FILTER_OFFSET		4
     59
     60#define MCS5000_TS_X_SIZE_UPPER		0x08
     61#define MCS5000_TS_X_SIZE_LOWER		0x09
     62#define MCS5000_TS_Y_SIZE_UPPER		0x0A
     63#define MCS5000_TS_Y_SIZE_LOWER		0x0B
     64
     65#define MCS5000_TS_INPUT_INFO		0x10
     66#define INPUT_TYPE_OFFSET		0
     67#define INPUT_TYPE_NONTOUCH		(0 << INPUT_TYPE_OFFSET)
     68#define INPUT_TYPE_SINGLE		(1 << INPUT_TYPE_OFFSET)
     69#define INPUT_TYPE_DUAL			(2 << INPUT_TYPE_OFFSET)
     70#define INPUT_TYPE_PALM			(3 << INPUT_TYPE_OFFSET)
     71#define INPUT_TYPE_PROXIMITY		(7 << INPUT_TYPE_OFFSET)
     72#define GESTURE_CODE_OFFSET		3
     73#define GESTURE_CODE_NO			(0 << GESTURE_CODE_OFFSET)
     74
     75#define MCS5000_TS_X_POS_UPPER		0x11
     76#define MCS5000_TS_X_POS_LOWER		0x12
     77#define MCS5000_TS_Y_POS_UPPER		0x13
     78#define MCS5000_TS_Y_POS_LOWER		0x14
     79#define MCS5000_TS_Z_POS		0x15
     80#define MCS5000_TS_WIDTH		0x16
     81#define MCS5000_TS_GESTURE_VAL		0x17
     82#define MCS5000_TS_MODULE_REV		0x20
     83#define MCS5000_TS_FIRMWARE_VER		0x21
     84
     85/* Touchscreen absolute values */
     86#define MCS5000_MAX_XC			0x3ff
     87#define MCS5000_MAX_YC			0x3ff
     88
     89enum mcs5000_ts_read_offset {
     90	READ_INPUT_INFO,
     91	READ_X_POS_UPPER,
     92	READ_X_POS_LOWER,
     93	READ_Y_POS_UPPER,
     94	READ_Y_POS_LOWER,
     95	READ_BLOCK_SIZE,
     96};
     97
     98/* Each client has this additional data */
     99struct mcs5000_ts_data {
    100	struct i2c_client *client;
    101	struct input_dev *input_dev;
    102	const struct mcs_platform_data *platform_data;
    103};
    104
    105static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id)
    106{
    107	struct mcs5000_ts_data *data = dev_id;
    108	struct i2c_client *client = data->client;
    109	u8 buffer[READ_BLOCK_SIZE];
    110	int err;
    111	int x;
    112	int y;
    113
    114	err = i2c_smbus_read_i2c_block_data(client, MCS5000_TS_INPUT_INFO,
    115			READ_BLOCK_SIZE, buffer);
    116	if (err < 0) {
    117		dev_err(&client->dev, "%s, err[%d]\n", __func__, err);
    118		goto out;
    119	}
    120
    121	switch (buffer[READ_INPUT_INFO]) {
    122	case INPUT_TYPE_NONTOUCH:
    123		input_report_key(data->input_dev, BTN_TOUCH, 0);
    124		input_sync(data->input_dev);
    125		break;
    126
    127	case INPUT_TYPE_SINGLE:
    128		x = (buffer[READ_X_POS_UPPER] << 8) | buffer[READ_X_POS_LOWER];
    129		y = (buffer[READ_Y_POS_UPPER] << 8) | buffer[READ_Y_POS_LOWER];
    130
    131		input_report_key(data->input_dev, BTN_TOUCH, 1);
    132		input_report_abs(data->input_dev, ABS_X, x);
    133		input_report_abs(data->input_dev, ABS_Y, y);
    134		input_sync(data->input_dev);
    135		break;
    136
    137	case INPUT_TYPE_DUAL:
    138		/* TODO */
    139		break;
    140
    141	case INPUT_TYPE_PALM:
    142		/* TODO */
    143		break;
    144
    145	case INPUT_TYPE_PROXIMITY:
    146		/* TODO */
    147		break;
    148
    149	default:
    150		dev_err(&client->dev, "Unknown ts input type %d\n",
    151				buffer[READ_INPUT_INFO]);
    152		break;
    153	}
    154
    155 out:
    156	return IRQ_HANDLED;
    157}
    158
    159static void mcs5000_ts_phys_init(struct mcs5000_ts_data *data,
    160				 const struct mcs_platform_data *platform_data)
    161{
    162	struct i2c_client *client = data->client;
    163
    164	/* Touch reset & sleep mode */
    165	i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE,
    166			RESET_EXT_SOFT | OP_MODE_SLEEP);
    167
    168	/* Touch size */
    169	i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_UPPER,
    170			platform_data->x_size >> 8);
    171	i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_LOWER,
    172			platform_data->x_size & 0xff);
    173	i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_UPPER,
    174			platform_data->y_size >> 8);
    175	i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_LOWER,
    176			platform_data->y_size & 0xff);
    177
    178	/* Touch active mode & 80 report rate */
    179	i2c_smbus_write_byte_data(data->client, MCS5000_TS_OP_MODE,
    180			OP_MODE_ACTIVE | REPORT_RATE_80);
    181}
    182
    183static int mcs5000_ts_probe(struct i2c_client *client,
    184			    const struct i2c_device_id *id)
    185{
    186	const struct mcs_platform_data *pdata;
    187	struct mcs5000_ts_data *data;
    188	struct input_dev *input_dev;
    189	int error;
    190
    191	pdata = dev_get_platdata(&client->dev);
    192	if (!pdata)
    193		return -EINVAL;
    194
    195	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
    196	if (!data) {
    197		dev_err(&client->dev, "Failed to allocate memory\n");
    198		return -ENOMEM;
    199	}
    200
    201	data->client = client;
    202
    203	input_dev = devm_input_allocate_device(&client->dev);
    204	if (!input_dev) {
    205		dev_err(&client->dev, "Failed to allocate input device\n");
    206		return -ENOMEM;
    207	}
    208
    209	input_dev->name = "MELFAS MCS-5000 Touchscreen";
    210	input_dev->id.bustype = BUS_I2C;
    211	input_dev->dev.parent = &client->dev;
    212
    213	__set_bit(EV_ABS, input_dev->evbit);
    214	__set_bit(EV_KEY, input_dev->evbit);
    215	__set_bit(BTN_TOUCH, input_dev->keybit);
    216	input_set_abs_params(input_dev, ABS_X, 0, MCS5000_MAX_XC, 0, 0);
    217	input_set_abs_params(input_dev, ABS_Y, 0, MCS5000_MAX_YC, 0, 0);
    218
    219	data->input_dev = input_dev;
    220
    221	if (pdata->cfg_pin)
    222		pdata->cfg_pin();
    223
    224	error = devm_request_threaded_irq(&client->dev, client->irq,
    225					  NULL, mcs5000_ts_interrupt,
    226					  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
    227					  "mcs5000_ts", data);
    228	if (error) {
    229		dev_err(&client->dev, "Failed to register interrupt\n");
    230		return error;
    231	}
    232
    233	error = input_register_device(data->input_dev);
    234	if (error) {
    235		dev_err(&client->dev, "Failed to register input device\n");
    236		return error;
    237	}
    238
    239	mcs5000_ts_phys_init(data, pdata);
    240	i2c_set_clientdata(client, data);
    241
    242	return 0;
    243}
    244
    245static int __maybe_unused mcs5000_ts_suspend(struct device *dev)
    246{
    247	struct i2c_client *client = to_i2c_client(dev);
    248
    249	/* Touch sleep mode */
    250	i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, OP_MODE_SLEEP);
    251
    252	return 0;
    253}
    254
    255static int __maybe_unused mcs5000_ts_resume(struct device *dev)
    256{
    257	struct i2c_client *client = to_i2c_client(dev);
    258	struct mcs5000_ts_data *data = i2c_get_clientdata(client);
    259	const struct mcs_platform_data *pdata = dev_get_platdata(dev);
    260
    261	mcs5000_ts_phys_init(data, pdata);
    262
    263	return 0;
    264}
    265
    266static SIMPLE_DEV_PM_OPS(mcs5000_ts_pm, mcs5000_ts_suspend, mcs5000_ts_resume);
    267
    268static const struct i2c_device_id mcs5000_ts_id[] = {
    269	{ "mcs5000_ts", 0 },
    270	{ }
    271};
    272MODULE_DEVICE_TABLE(i2c, mcs5000_ts_id);
    273
    274static struct i2c_driver mcs5000_ts_driver = {
    275	.probe		= mcs5000_ts_probe,
    276	.driver = {
    277		.name = "mcs5000_ts",
    278		.pm   = &mcs5000_ts_pm,
    279	},
    280	.id_table	= mcs5000_ts_id,
    281};
    282
    283module_i2c_driver(mcs5000_ts_driver);
    284
    285/* Module information */
    286MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
    287MODULE_DESCRIPTION("Touchscreen driver for MELFAS MCS-5000 controller");
    288MODULE_LICENSE("GPL");