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

ts4800-ts.c (5176B)


      1/*
      2 * Touchscreen driver for the TS-4800 board
      3 *
      4 * Copyright (c) 2015 - Savoir-faire Linux
      5 *
      6 * This file is licensed under the terms of the GNU General Public
      7 * License version 2. This program is licensed "as is" without any
      8 * warranty of any kind, whether express or implied.
      9 */
     10
     11#include <linux/bitops.h>
     12#include <linux/input.h>
     13#include <linux/io.h>
     14#include <linux/kernel.h>
     15#include <linux/mfd/syscon.h>
     16#include <linux/module.h>
     17#include <linux/of.h>
     18#include <linux/platform_device.h>
     19#include <linux/regmap.h>
     20
     21/* polling interval in ms */
     22#define POLL_INTERVAL		3
     23
     24#define DEBOUNCE_COUNT		1
     25
     26/* sensor values are 12-bit wide */
     27#define MAX_12BIT		((1 << 12) - 1)
     28
     29#define PENDOWN_MASK		0x1
     30
     31#define X_OFFSET		0x0
     32#define Y_OFFSET		0x2
     33
     34struct ts4800_ts {
     35	struct input_dev        *input;
     36	struct device           *dev;
     37	char                    phys[32];
     38
     39	void __iomem            *base;
     40	struct regmap           *regmap;
     41	unsigned int            reg;
     42	unsigned int            bit;
     43
     44	bool                    pendown;
     45	int                     debounce;
     46};
     47
     48static int ts4800_ts_open(struct input_dev *input_dev)
     49{
     50	struct ts4800_ts *ts = input_get_drvdata(input_dev);
     51	int error;
     52
     53	ts->pendown = false;
     54	ts->debounce = DEBOUNCE_COUNT;
     55
     56	error = regmap_update_bits(ts->regmap, ts->reg, ts->bit, ts->bit);
     57	if (error) {
     58		dev_warn(ts->dev, "Failed to enable touchscreen: %d\n", error);
     59		return error;
     60	}
     61
     62	return 0;
     63}
     64
     65static void ts4800_ts_close(struct input_dev *input_dev)
     66{
     67	struct ts4800_ts *ts = input_get_drvdata(input_dev);
     68	int ret;
     69
     70	ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, 0);
     71	if (ret)
     72		dev_warn(ts->dev, "Failed to disable touchscreen\n");
     73
     74}
     75
     76static void ts4800_ts_poll(struct input_dev *input_dev)
     77{
     78	struct ts4800_ts *ts = input_get_drvdata(input_dev);
     79	u16 last_x = readw(ts->base + X_OFFSET);
     80	u16 last_y = readw(ts->base + Y_OFFSET);
     81	bool pendown = last_x & PENDOWN_MASK;
     82
     83	if (pendown) {
     84		if (ts->debounce) {
     85			ts->debounce--;
     86			return;
     87		}
     88
     89		if (!ts->pendown) {
     90			input_report_key(input_dev, BTN_TOUCH, 1);
     91			ts->pendown = true;
     92		}
     93
     94		last_x = ((~last_x) >> 4) & MAX_12BIT;
     95		last_y = ((~last_y) >> 4) & MAX_12BIT;
     96
     97		input_report_abs(input_dev, ABS_X, last_x);
     98		input_report_abs(input_dev, ABS_Y, last_y);
     99		input_sync(input_dev);
    100	} else if (ts->pendown) {
    101		ts->pendown = false;
    102		ts->debounce = DEBOUNCE_COUNT;
    103		input_report_key(input_dev, BTN_TOUCH, 0);
    104		input_sync(input_dev);
    105	}
    106}
    107
    108static int ts4800_parse_dt(struct platform_device *pdev,
    109			   struct ts4800_ts *ts)
    110{
    111	struct device *dev = &pdev->dev;
    112	struct device_node *np = dev->of_node;
    113	struct device_node *syscon_np;
    114	u32 reg, bit;
    115	int error;
    116
    117	syscon_np = of_parse_phandle(np, "syscon", 0);
    118	if (!syscon_np) {
    119		dev_err(dev, "no syscon property\n");
    120		return -ENODEV;
    121	}
    122
    123	ts->regmap = syscon_node_to_regmap(syscon_np);
    124	of_node_put(syscon_np);
    125	if (IS_ERR(ts->regmap)) {
    126		dev_err(dev, "cannot get parent's regmap\n");
    127		return PTR_ERR(ts->regmap);
    128	}
    129
    130	error = of_property_read_u32_index(np, "syscon", 1, &reg);
    131	if (error < 0) {
    132		dev_err(dev, "no offset in syscon\n");
    133		return error;
    134	}
    135
    136	ts->reg = reg;
    137
    138	error = of_property_read_u32_index(np, "syscon", 2, &bit);
    139	if (error < 0) {
    140		dev_err(dev, "no bit in syscon\n");
    141		return error;
    142	}
    143
    144	ts->bit = BIT(bit);
    145
    146	return 0;
    147}
    148
    149static int ts4800_ts_probe(struct platform_device *pdev)
    150{
    151	struct input_dev *input_dev;
    152	struct ts4800_ts *ts;
    153	int error;
    154
    155	ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL);
    156	if (!ts)
    157		return -ENOMEM;
    158
    159	error = ts4800_parse_dt(pdev, ts);
    160	if (error)
    161		return error;
    162
    163	ts->base = devm_platform_ioremap_resource(pdev, 0);
    164	if (IS_ERR(ts->base))
    165		return PTR_ERR(ts->base);
    166
    167	input_dev = devm_input_allocate_device(&pdev->dev);
    168	if (!input_dev)
    169		return -ENOMEM;
    170
    171	snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&pdev->dev));
    172	ts->input = input_dev;
    173	ts->dev = &pdev->dev;
    174
    175	input_set_drvdata(input_dev, ts);
    176
    177	input_dev->name = "TS-4800 Touchscreen";
    178	input_dev->phys = ts->phys;
    179
    180	input_dev->open = ts4800_ts_open;
    181	input_dev->close = ts4800_ts_close;
    182
    183	input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
    184	input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
    185	input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
    186
    187	error = input_setup_polling(input_dev, ts4800_ts_poll);
    188	if (error) {
    189		dev_err(&pdev->dev, "Unable to set up polling: %d\n", error);
    190		return error;
    191	}
    192
    193	input_set_poll_interval(input_dev, POLL_INTERVAL);
    194
    195	error = input_register_device(input_dev);
    196	if (error) {
    197		dev_err(&pdev->dev,
    198			"Unable to register input device: %d\n", error);
    199		return error;
    200	}
    201
    202	return 0;
    203}
    204
    205static const struct of_device_id ts4800_ts_of_match[] = {
    206	{ .compatible = "technologic,ts4800-ts", },
    207	{ },
    208};
    209MODULE_DEVICE_TABLE(of, ts4800_ts_of_match);
    210
    211static struct platform_driver ts4800_ts_driver = {
    212	.driver = {
    213		.name = "ts4800-ts",
    214		.of_match_table = ts4800_ts_of_match,
    215	},
    216	.probe = ts4800_ts_probe,
    217};
    218module_platform_driver(ts4800_ts_driver);
    219
    220MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>");
    221MODULE_DESCRIPTION("TS-4800 Touchscreen Driver");
    222MODULE_LICENSE("GPL v2");
    223MODULE_ALIAS("platform:ts4800_ts");