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

rotary_encoder.c (8565B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * rotary_encoder.c
      4 *
      5 * (c) 2009 Daniel Mack <daniel@caiaq.de>
      6 * Copyright (C) 2011 Johan Hovold <jhovold@gmail.com>
      7 *
      8 * state machine code inspired by code from Tim Ruetz
      9 *
     10 * A generic driver for rotary encoders connected to GPIO lines.
     11 * See file:Documentation/input/devices/rotary-encoder.rst for more information
     12 */
     13
     14#include <linux/kernel.h>
     15#include <linux/module.h>
     16#include <linux/interrupt.h>
     17#include <linux/input.h>
     18#include <linux/device.h>
     19#include <linux/platform_device.h>
     20#include <linux/gpio/consumer.h>
     21#include <linux/slab.h>
     22#include <linux/of.h>
     23#include <linux/pm.h>
     24#include <linux/property.h>
     25
     26#define DRV_NAME "rotary-encoder"
     27
     28enum rotary_encoder_encoding {
     29	ROTENC_GRAY,
     30	ROTENC_BINARY,
     31};
     32
     33struct rotary_encoder {
     34	struct input_dev *input;
     35
     36	struct mutex access_mutex;
     37
     38	u32 steps;
     39	u32 axis;
     40	bool relative_axis;
     41	bool rollover;
     42	enum rotary_encoder_encoding encoding;
     43
     44	unsigned int pos;
     45
     46	struct gpio_descs *gpios;
     47
     48	unsigned int *irq;
     49
     50	bool armed;
     51	signed char dir;	/* 1 - clockwise, -1 - CCW */
     52
     53	unsigned int last_stable;
     54};
     55
     56static unsigned int rotary_encoder_get_state(struct rotary_encoder *encoder)
     57{
     58	int i;
     59	unsigned int ret = 0;
     60
     61	for (i = 0; i < encoder->gpios->ndescs; ++i) {
     62		int val = gpiod_get_value_cansleep(encoder->gpios->desc[i]);
     63
     64		/* convert from gray encoding to normal */
     65		if (encoder->encoding == ROTENC_GRAY && ret & 1)
     66			val = !val;
     67
     68		ret = ret << 1 | val;
     69	}
     70
     71	return ret & 3;
     72}
     73
     74static void rotary_encoder_report_event(struct rotary_encoder *encoder)
     75{
     76	if (encoder->relative_axis) {
     77		input_report_rel(encoder->input,
     78				 encoder->axis, encoder->dir);
     79	} else {
     80		unsigned int pos = encoder->pos;
     81
     82		if (encoder->dir < 0) {
     83			/* turning counter-clockwise */
     84			if (encoder->rollover)
     85				pos += encoder->steps;
     86			if (pos)
     87				pos--;
     88		} else {
     89			/* turning clockwise */
     90			if (encoder->rollover || pos < encoder->steps)
     91				pos++;
     92		}
     93
     94		if (encoder->rollover)
     95			pos %= encoder->steps;
     96
     97		encoder->pos = pos;
     98		input_report_abs(encoder->input, encoder->axis, encoder->pos);
     99	}
    100
    101	input_sync(encoder->input);
    102}
    103
    104static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
    105{
    106	struct rotary_encoder *encoder = dev_id;
    107	unsigned int state;
    108
    109	mutex_lock(&encoder->access_mutex);
    110
    111	state = rotary_encoder_get_state(encoder);
    112
    113	switch (state) {
    114	case 0x0:
    115		if (encoder->armed) {
    116			rotary_encoder_report_event(encoder);
    117			encoder->armed = false;
    118		}
    119		break;
    120
    121	case 0x1:
    122	case 0x3:
    123		if (encoder->armed)
    124			encoder->dir = 2 - state;
    125		break;
    126
    127	case 0x2:
    128		encoder->armed = true;
    129		break;
    130	}
    131
    132	mutex_unlock(&encoder->access_mutex);
    133
    134	return IRQ_HANDLED;
    135}
    136
    137static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id)
    138{
    139	struct rotary_encoder *encoder = dev_id;
    140	unsigned int state;
    141
    142	mutex_lock(&encoder->access_mutex);
    143
    144	state = rotary_encoder_get_state(encoder);
    145
    146	if (state & 1) {
    147		encoder->dir = ((encoder->last_stable - state + 1) % 4) - 1;
    148	} else {
    149		if (state != encoder->last_stable) {
    150			rotary_encoder_report_event(encoder);
    151			encoder->last_stable = state;
    152		}
    153	}
    154
    155	mutex_unlock(&encoder->access_mutex);
    156
    157	return IRQ_HANDLED;
    158}
    159
    160static irqreturn_t rotary_encoder_quarter_period_irq(int irq, void *dev_id)
    161{
    162	struct rotary_encoder *encoder = dev_id;
    163	unsigned int state;
    164
    165	mutex_lock(&encoder->access_mutex);
    166
    167	state = rotary_encoder_get_state(encoder);
    168
    169	if ((encoder->last_stable + 1) % 4 == state)
    170		encoder->dir = 1;
    171	else if (encoder->last_stable == (state + 1) % 4)
    172		encoder->dir = -1;
    173	else
    174		goto out;
    175
    176	rotary_encoder_report_event(encoder);
    177
    178out:
    179	encoder->last_stable = state;
    180	mutex_unlock(&encoder->access_mutex);
    181
    182	return IRQ_HANDLED;
    183}
    184
    185static int rotary_encoder_probe(struct platform_device *pdev)
    186{
    187	struct device *dev = &pdev->dev;
    188	struct rotary_encoder *encoder;
    189	struct input_dev *input;
    190	irq_handler_t handler;
    191	u32 steps_per_period;
    192	unsigned int i;
    193	int err;
    194
    195	encoder = devm_kzalloc(dev, sizeof(struct rotary_encoder), GFP_KERNEL);
    196	if (!encoder)
    197		return -ENOMEM;
    198
    199	mutex_init(&encoder->access_mutex);
    200
    201	device_property_read_u32(dev, "rotary-encoder,steps", &encoder->steps);
    202
    203	err = device_property_read_u32(dev, "rotary-encoder,steps-per-period",
    204				       &steps_per_period);
    205	if (err) {
    206		/*
    207		 * The 'half-period' property has been deprecated, you must
    208		 * use 'steps-per-period' and set an appropriate value, but
    209		 * we still need to parse it to maintain compatibility. If
    210		 * neither property is present we fall back to the one step
    211		 * per period behavior.
    212		 */
    213		steps_per_period = device_property_read_bool(dev,
    214					"rotary-encoder,half-period") ? 2 : 1;
    215	}
    216
    217	encoder->rollover =
    218		device_property_read_bool(dev, "rotary-encoder,rollover");
    219
    220	if (!device_property_present(dev, "rotary-encoder,encoding") ||
    221	    !device_property_match_string(dev, "rotary-encoder,encoding",
    222					  "gray")) {
    223		dev_info(dev, "gray");
    224		encoder->encoding = ROTENC_GRAY;
    225	} else if (!device_property_match_string(dev, "rotary-encoder,encoding",
    226						 "binary")) {
    227		dev_info(dev, "binary");
    228		encoder->encoding = ROTENC_BINARY;
    229	} else {
    230		dev_err(dev, "unknown encoding setting\n");
    231		return -EINVAL;
    232	}
    233
    234	device_property_read_u32(dev, "linux,axis", &encoder->axis);
    235	encoder->relative_axis =
    236		device_property_read_bool(dev, "rotary-encoder,relative-axis");
    237
    238	encoder->gpios = devm_gpiod_get_array(dev, NULL, GPIOD_IN);
    239	if (IS_ERR(encoder->gpios)) {
    240		err = PTR_ERR(encoder->gpios);
    241		if (err != -EPROBE_DEFER)
    242			dev_err(dev, "unable to get gpios: %d\n", err);
    243		return err;
    244	}
    245	if (encoder->gpios->ndescs < 2) {
    246		dev_err(dev, "not enough gpios found\n");
    247		return -EINVAL;
    248	}
    249
    250	input = devm_input_allocate_device(dev);
    251	if (!input)
    252		return -ENOMEM;
    253
    254	encoder->input = input;
    255
    256	input->name = pdev->name;
    257	input->id.bustype = BUS_HOST;
    258	input->dev.parent = dev;
    259
    260	if (encoder->relative_axis)
    261		input_set_capability(input, EV_REL, encoder->axis);
    262	else
    263		input_set_abs_params(input,
    264				     encoder->axis, 0, encoder->steps, 0, 1);
    265
    266	switch (steps_per_period >> (encoder->gpios->ndescs - 2)) {
    267	case 4:
    268		handler = &rotary_encoder_quarter_period_irq;
    269		encoder->last_stable = rotary_encoder_get_state(encoder);
    270		break;
    271	case 2:
    272		handler = &rotary_encoder_half_period_irq;
    273		encoder->last_stable = rotary_encoder_get_state(encoder);
    274		break;
    275	case 1:
    276		handler = &rotary_encoder_irq;
    277		break;
    278	default:
    279		dev_err(dev, "'%d' is not a valid steps-per-period value\n",
    280			steps_per_period);
    281		return -EINVAL;
    282	}
    283
    284	encoder->irq =
    285		devm_kcalloc(dev,
    286			     encoder->gpios->ndescs, sizeof(*encoder->irq),
    287			     GFP_KERNEL);
    288	if (!encoder->irq)
    289		return -ENOMEM;
    290
    291	for (i = 0; i < encoder->gpios->ndescs; ++i) {
    292		encoder->irq[i] = gpiod_to_irq(encoder->gpios->desc[i]);
    293
    294		err = devm_request_threaded_irq(dev, encoder->irq[i],
    295				NULL, handler,
    296				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
    297				IRQF_ONESHOT,
    298				DRV_NAME, encoder);
    299		if (err) {
    300			dev_err(dev, "unable to request IRQ %d (gpio#%d)\n",
    301				encoder->irq[i], i);
    302			return err;
    303		}
    304	}
    305
    306	err = input_register_device(input);
    307	if (err) {
    308		dev_err(dev, "failed to register input device\n");
    309		return err;
    310	}
    311
    312	device_init_wakeup(dev,
    313			   device_property_read_bool(dev, "wakeup-source"));
    314
    315	platform_set_drvdata(pdev, encoder);
    316
    317	return 0;
    318}
    319
    320static int __maybe_unused rotary_encoder_suspend(struct device *dev)
    321{
    322	struct rotary_encoder *encoder = dev_get_drvdata(dev);
    323	unsigned int i;
    324
    325	if (device_may_wakeup(dev)) {
    326		for (i = 0; i < encoder->gpios->ndescs; ++i)
    327			enable_irq_wake(encoder->irq[i]);
    328	}
    329
    330	return 0;
    331}
    332
    333static int __maybe_unused rotary_encoder_resume(struct device *dev)
    334{
    335	struct rotary_encoder *encoder = dev_get_drvdata(dev);
    336	unsigned int i;
    337
    338	if (device_may_wakeup(dev)) {
    339		for (i = 0; i < encoder->gpios->ndescs; ++i)
    340			disable_irq_wake(encoder->irq[i]);
    341	}
    342
    343	return 0;
    344}
    345
    346static SIMPLE_DEV_PM_OPS(rotary_encoder_pm_ops,
    347			 rotary_encoder_suspend, rotary_encoder_resume);
    348
    349#ifdef CONFIG_OF
    350static const struct of_device_id rotary_encoder_of_match[] = {
    351	{ .compatible = "rotary-encoder", },
    352	{ },
    353};
    354MODULE_DEVICE_TABLE(of, rotary_encoder_of_match);
    355#endif
    356
    357static struct platform_driver rotary_encoder_driver = {
    358	.probe		= rotary_encoder_probe,
    359	.driver		= {
    360		.name	= DRV_NAME,
    361		.pm	= &rotary_encoder_pm_ops,
    362		.of_match_table = of_match_ptr(rotary_encoder_of_match),
    363	}
    364};
    365module_platform_driver(rotary_encoder_driver);
    366
    367MODULE_ALIAS("platform:" DRV_NAME);
    368MODULE_DESCRIPTION("GPIO rotary encoder driver");
    369MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>, Johan Hovold");
    370MODULE_LICENSE("GPL v2");