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

da9052_tsi.c (8256B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * TSI driver for Dialog DA9052
      4 *
      5 * Copyright(c) 2012 Dialog Semiconductor Ltd.
      6 *
      7 * Author: David Dajun Chen <dchen@diasemi.com>
      8 */
      9#include <linux/module.h>
     10#include <linux/input.h>
     11#include <linux/delay.h>
     12#include <linux/platform_device.h>
     13#include <linux/interrupt.h>
     14
     15#include <linux/mfd/da9052/reg.h>
     16#include <linux/mfd/da9052/da9052.h>
     17
     18#define TSI_PEN_DOWN_STATUS 0x40
     19
     20struct da9052_tsi {
     21	struct da9052 *da9052;
     22	struct input_dev *dev;
     23	struct delayed_work ts_pen_work;
     24	bool stopped;
     25	bool adc_on;
     26};
     27
     28static void da9052_ts_adc_toggle(struct da9052_tsi *tsi, bool on)
     29{
     30	da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 0, on);
     31	tsi->adc_on = on;
     32}
     33
     34static irqreturn_t da9052_ts_pendwn_irq(int irq, void *data)
     35{
     36	struct da9052_tsi *tsi = data;
     37
     38	if (!tsi->stopped) {
     39		/* Mask PEN_DOWN event and unmask TSI_READY event */
     40		da9052_disable_irq_nosync(tsi->da9052, DA9052_IRQ_PENDOWN);
     41		da9052_enable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
     42
     43		da9052_ts_adc_toggle(tsi, true);
     44
     45		schedule_delayed_work(&tsi->ts_pen_work, HZ / 50);
     46	}
     47
     48	return IRQ_HANDLED;
     49}
     50
     51static void da9052_ts_read(struct da9052_tsi *tsi)
     52{
     53	struct input_dev *input = tsi->dev;
     54	int ret;
     55	u16 x, y, z;
     56	u8 v;
     57
     58	ret = da9052_reg_read(tsi->da9052, DA9052_TSI_X_MSB_REG);
     59	if (ret < 0)
     60		return;
     61
     62	x = (u16) ret;
     63
     64	ret = da9052_reg_read(tsi->da9052, DA9052_TSI_Y_MSB_REG);
     65	if (ret < 0)
     66		return;
     67
     68	y = (u16) ret;
     69
     70	ret = da9052_reg_read(tsi->da9052, DA9052_TSI_Z_MSB_REG);
     71	if (ret < 0)
     72		return;
     73
     74	z = (u16) ret;
     75
     76	ret = da9052_reg_read(tsi->da9052, DA9052_TSI_LSB_REG);
     77	if (ret < 0)
     78		return;
     79
     80	v = (u8) ret;
     81
     82	x = ((x << 2) & 0x3fc) | (v & 0x3);
     83	y = ((y << 2) & 0x3fc) | ((v & 0xc) >> 2);
     84	z = ((z << 2) & 0x3fc) | ((v & 0x30) >> 4);
     85
     86	input_report_key(input, BTN_TOUCH, 1);
     87	input_report_abs(input, ABS_X, x);
     88	input_report_abs(input, ABS_Y, y);
     89	input_report_abs(input, ABS_PRESSURE, z);
     90	input_sync(input);
     91}
     92
     93static irqreturn_t da9052_ts_datardy_irq(int irq, void *data)
     94{
     95	struct da9052_tsi *tsi = data;
     96
     97	da9052_ts_read(tsi);
     98
     99	return IRQ_HANDLED;
    100}
    101
    102static void da9052_ts_pen_work(struct work_struct *work)
    103{
    104	struct da9052_tsi *tsi = container_of(work, struct da9052_tsi,
    105					      ts_pen_work.work);
    106	if (!tsi->stopped) {
    107		int ret = da9052_reg_read(tsi->da9052, DA9052_TSI_LSB_REG);
    108		if (ret < 0 || (ret & TSI_PEN_DOWN_STATUS)) {
    109			/* Pen is still DOWN (or read error) */
    110			schedule_delayed_work(&tsi->ts_pen_work, HZ / 50);
    111		} else {
    112			struct input_dev *input = tsi->dev;
    113
    114			/* Pen UP */
    115			da9052_ts_adc_toggle(tsi, false);
    116
    117			/* Report Pen UP */
    118			input_report_key(input, BTN_TOUCH, 0);
    119			input_report_abs(input, ABS_PRESSURE, 0);
    120			input_sync(input);
    121
    122			/*
    123			 * FIXME: Fixes the unhandled irq issue when quick
    124			 * pen down and pen up events occurs
    125			 */
    126			ret = da9052_reg_update(tsi->da9052,
    127						DA9052_EVENT_B_REG, 0xC0, 0xC0);
    128			if (ret < 0)
    129				return;
    130
    131			/* Mask TSI_READY event and unmask PEN_DOWN event */
    132			da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
    133			da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
    134		}
    135	}
    136}
    137
    138static int da9052_ts_configure_gpio(struct da9052 *da9052)
    139{
    140	int error;
    141
    142	error = da9052_reg_update(da9052, DA9052_GPIO_2_3_REG, 0x30, 0);
    143	if (error < 0)
    144		return error;
    145
    146	error = da9052_reg_update(da9052, DA9052_GPIO_4_5_REG, 0x33, 0);
    147	if (error < 0)
    148		return error;
    149
    150	error = da9052_reg_update(da9052, DA9052_GPIO_6_7_REG, 0x33, 0);
    151	if (error < 0)
    152		return error;
    153
    154	return 0;
    155}
    156
    157static int da9052_configure_tsi(struct da9052_tsi *tsi)
    158{
    159	int error;
    160
    161	error = da9052_ts_configure_gpio(tsi->da9052);
    162	if (error)
    163		return error;
    164
    165	/* Measure TSI sample every 1ms */
    166	error = da9052_reg_update(tsi->da9052, DA9052_ADC_CONT_REG,
    167				  1 << 6, 1 << 6);
    168	if (error < 0)
    169		return error;
    170
    171	/* TSI_DELAY: 3 slots, TSI_SKIP: 0 slots, TSI_MODE: XYZP */
    172	error = da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 0xFC, 0xC0);
    173	if (error < 0)
    174		return error;
    175
    176	/* Supply TSIRef through LD09 */
    177	error = da9052_reg_write(tsi->da9052, DA9052_LDO9_REG, 0x59);
    178	if (error < 0)
    179		return error;
    180
    181	return 0;
    182}
    183
    184static int da9052_ts_input_open(struct input_dev *input_dev)
    185{
    186	struct da9052_tsi *tsi = input_get_drvdata(input_dev);
    187
    188	tsi->stopped = false;
    189	mb();
    190
    191	/* Unmask PEN_DOWN event */
    192	da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
    193
    194	/* Enable Pen Detect Circuit */
    195	return da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG,
    196				 1 << 1, 1 << 1);
    197}
    198
    199static void da9052_ts_input_close(struct input_dev *input_dev)
    200{
    201	struct da9052_tsi *tsi = input_get_drvdata(input_dev);
    202
    203	tsi->stopped = true;
    204	mb();
    205	da9052_disable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
    206	cancel_delayed_work_sync(&tsi->ts_pen_work);
    207
    208	if (tsi->adc_on) {
    209		da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
    210		da9052_ts_adc_toggle(tsi, false);
    211
    212		/*
    213		 * If ADC was on that means that pendwn IRQ was disabled
    214		 * twice and we need to enable it to keep enable/disable
    215		 * counter balanced. IRQ is still off though.
    216		 */
    217		da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
    218	}
    219
    220	/* Disable Pen Detect Circuit */
    221	da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 1, 0);
    222}
    223
    224static int da9052_ts_probe(struct platform_device *pdev)
    225{
    226	struct da9052 *da9052;
    227	struct da9052_tsi *tsi;
    228	struct input_dev *input_dev;
    229	int error;
    230
    231	da9052 = dev_get_drvdata(pdev->dev.parent);
    232	if (!da9052)
    233		return -EINVAL;
    234
    235	tsi = kzalloc(sizeof(struct da9052_tsi), GFP_KERNEL);
    236	input_dev = input_allocate_device();
    237	if (!tsi || !input_dev) {
    238		error = -ENOMEM;
    239		goto err_free_mem;
    240	}
    241
    242	tsi->da9052 = da9052;
    243	tsi->dev = input_dev;
    244	tsi->stopped = true;
    245	INIT_DELAYED_WORK(&tsi->ts_pen_work, da9052_ts_pen_work);
    246
    247	input_dev->id.version = 0x0101;
    248	input_dev->id.vendor = 0x15B6;
    249	input_dev->id.product = 0x9052;
    250	input_dev->name = "Dialog DA9052 TouchScreen Driver";
    251	input_dev->dev.parent = &pdev->dev;
    252	input_dev->open = da9052_ts_input_open;
    253	input_dev->close = da9052_ts_input_close;
    254
    255	__set_bit(EV_ABS, input_dev->evbit);
    256	__set_bit(EV_KEY, input_dev->evbit);
    257	__set_bit(BTN_TOUCH, input_dev->keybit);
    258
    259	input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0);
    260	input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0);
    261	input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1023, 0, 0);
    262
    263	input_set_drvdata(input_dev, tsi);
    264
    265	/* Disable Pen Detect Circuit */
    266	da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 1, 0);
    267
    268	/* Disable ADC */
    269	da9052_ts_adc_toggle(tsi, false);
    270
    271	error = da9052_request_irq(tsi->da9052, DA9052_IRQ_PENDOWN,
    272				"pendown-irq", da9052_ts_pendwn_irq, tsi);
    273	if (error) {
    274		dev_err(tsi->da9052->dev,
    275			"Failed to register PENDWN IRQ: %d\n", error);
    276		goto err_free_mem;
    277	}
    278
    279	error = da9052_request_irq(tsi->da9052, DA9052_IRQ_TSIREADY,
    280				"tsiready-irq", da9052_ts_datardy_irq, tsi);
    281	if (error) {
    282		dev_err(tsi->da9052->dev,
    283			"Failed to register TSIRDY IRQ :%d\n", error);
    284		goto err_free_pendwn_irq;
    285	}
    286
    287	/* Mask PEN_DOWN and TSI_READY events */
    288	da9052_disable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
    289	da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
    290
    291	error = da9052_configure_tsi(tsi);
    292	if (error)
    293		goto err_free_datardy_irq;
    294
    295	error = input_register_device(tsi->dev);
    296	if (error)
    297		goto err_free_datardy_irq;
    298
    299	platform_set_drvdata(pdev, tsi);
    300
    301	return 0;
    302
    303err_free_datardy_irq:
    304	da9052_free_irq(tsi->da9052, DA9052_IRQ_TSIREADY, tsi);
    305err_free_pendwn_irq:
    306	da9052_free_irq(tsi->da9052, DA9052_IRQ_PENDOWN, tsi);
    307err_free_mem:
    308	kfree(tsi);
    309	input_free_device(input_dev);
    310
    311	return error;
    312}
    313
    314static int  da9052_ts_remove(struct platform_device *pdev)
    315{
    316	struct da9052_tsi *tsi = platform_get_drvdata(pdev);
    317
    318	da9052_reg_write(tsi->da9052, DA9052_LDO9_REG, 0x19);
    319
    320	da9052_free_irq(tsi->da9052, DA9052_IRQ_TSIREADY, tsi);
    321	da9052_free_irq(tsi->da9052, DA9052_IRQ_PENDOWN, tsi);
    322
    323	input_unregister_device(tsi->dev);
    324	kfree(tsi);
    325
    326	return 0;
    327}
    328
    329static struct platform_driver da9052_tsi_driver = {
    330	.probe	= da9052_ts_probe,
    331	.remove	= da9052_ts_remove,
    332	.driver	= {
    333		.name	= "da9052-tsi",
    334	},
    335};
    336
    337module_platform_driver(da9052_tsi_driver);
    338
    339MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9052");
    340MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>");
    341MODULE_LICENSE("GPL");
    342MODULE_ALIAS("platform:da9052-tsi");