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

cypress-sf.c (5912B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Cypress StreetFighter Touchkey Driver
      4 *
      5 * Copyright (c) 2021 Yassine Oudjana <y.oudjana@protonmail.com>
      6 */
      7
      8#include <linux/bitmap.h>
      9#include <linux/bitops.h>
     10#include <linux/device.h>
     11#include <linux/i2c.h>
     12#include <linux/input.h>
     13#include <linux/interrupt.h>
     14#include <linux/module.h>
     15#include <linux/pm.h>
     16#include <linux/regulator/consumer.h>
     17
     18#define CYPRESS_SF_DEV_NAME "cypress-sf"
     19
     20#define CYPRESS_SF_REG_BUTTON_STATUS	0x4a
     21
     22struct cypress_sf_data {
     23	struct i2c_client *client;
     24	struct input_dev *input_dev;
     25	struct regulator_bulk_data regulators[2];
     26	u32 *keycodes;
     27	unsigned long keystates;
     28	int num_keys;
     29};
     30
     31static irqreturn_t cypress_sf_irq_handler(int irq, void *devid)
     32{
     33	struct cypress_sf_data *touchkey = devid;
     34	unsigned long keystates, changed;
     35	bool new_state;
     36	int val, key;
     37
     38	val = i2c_smbus_read_byte_data(touchkey->client,
     39				       CYPRESS_SF_REG_BUTTON_STATUS);
     40	if (val < 0) {
     41		dev_err(&touchkey->client->dev,
     42			"Failed to read button status: %d", val);
     43		return IRQ_NONE;
     44	}
     45	keystates = val;
     46
     47	bitmap_xor(&changed, &keystates, &touchkey->keystates,
     48		   touchkey->num_keys);
     49
     50	for_each_set_bit(key, &changed, touchkey->num_keys) {
     51		new_state = keystates & BIT(key);
     52		dev_dbg(&touchkey->client->dev,
     53			"Key %d changed to %d", key, new_state);
     54		input_report_key(touchkey->input_dev,
     55				 touchkey->keycodes[key], new_state);
     56	}
     57
     58	input_sync(touchkey->input_dev);
     59	touchkey->keystates = keystates;
     60
     61	return IRQ_HANDLED;
     62}
     63
     64static void cypress_sf_disable_regulators(void *arg)
     65{
     66	struct cypress_sf_data *touchkey = arg;
     67
     68	regulator_bulk_disable(ARRAY_SIZE(touchkey->regulators),
     69			       touchkey->regulators);
     70}
     71
     72static int cypress_sf_probe(struct i2c_client *client)
     73{
     74	struct cypress_sf_data *touchkey;
     75	int key, error;
     76
     77	touchkey = devm_kzalloc(&client->dev, sizeof(*touchkey), GFP_KERNEL);
     78	if (!touchkey)
     79		return -ENOMEM;
     80
     81	touchkey->client = client;
     82	i2c_set_clientdata(client, touchkey);
     83
     84	touchkey->regulators[0].supply = "vdd";
     85	touchkey->regulators[1].supply = "avdd";
     86
     87	error = devm_regulator_bulk_get(&client->dev,
     88					ARRAY_SIZE(touchkey->regulators),
     89					touchkey->regulators);
     90	if (error) {
     91		dev_err(&client->dev, "Failed to get regulators: %d\n", error);
     92		return error;
     93	}
     94
     95	touchkey->num_keys = device_property_read_u32_array(&client->dev,
     96							    "linux,keycodes",
     97							    NULL, 0);
     98	if (touchkey->num_keys < 0) {
     99		/* Default key count */
    100		touchkey->num_keys = 2;
    101	}
    102
    103	touchkey->keycodes = devm_kcalloc(&client->dev,
    104					  touchkey->num_keys,
    105					  sizeof(*touchkey->keycodes),
    106					  GFP_KERNEL);
    107	if (!touchkey->keycodes)
    108		return -ENOMEM;
    109
    110	error = device_property_read_u32_array(&client->dev, "linux,keycodes",
    111					       touchkey->keycodes,
    112					       touchkey->num_keys);
    113
    114	if (error) {
    115		dev_warn(&client->dev,
    116			 "Failed to read keycodes: %d, using defaults\n",
    117			 error);
    118
    119		/* Default keycodes */
    120		touchkey->keycodes[0] = KEY_BACK;
    121		touchkey->keycodes[1] = KEY_MENU;
    122	}
    123
    124	error = regulator_bulk_enable(ARRAY_SIZE(touchkey->regulators),
    125				      touchkey->regulators);
    126	if (error) {
    127		dev_err(&client->dev,
    128			"Failed to enable regulators: %d\n", error);
    129		return error;
    130	}
    131
    132	error = devm_add_action_or_reset(&client->dev,
    133					 cypress_sf_disable_regulators,
    134					 touchkey);
    135	if (error)
    136		return error;
    137
    138	touchkey->input_dev = devm_input_allocate_device(&client->dev);
    139	if (!touchkey->input_dev) {
    140		dev_err(&client->dev, "Failed to allocate input device\n");
    141		return -ENOMEM;
    142	}
    143
    144	touchkey->input_dev->name = CYPRESS_SF_DEV_NAME;
    145	touchkey->input_dev->id.bustype = BUS_I2C;
    146
    147	for (key = 0; key < touchkey->num_keys; ++key)
    148		input_set_capability(touchkey->input_dev,
    149				     EV_KEY, touchkey->keycodes[key]);
    150
    151	error = input_register_device(touchkey->input_dev);
    152	if (error) {
    153		dev_err(&client->dev,
    154			"Failed to register input device: %d\n", error);
    155		return error;
    156	}
    157
    158	error = devm_request_threaded_irq(&client->dev, client->irq,
    159					  NULL, cypress_sf_irq_handler,
    160					  IRQF_ONESHOT,
    161					  CYPRESS_SF_DEV_NAME, touchkey);
    162	if (error) {
    163		dev_err(&client->dev,
    164			"Failed to register threaded irq: %d", error);
    165		return error;
    166	}
    167
    168	return 0;
    169};
    170
    171static int __maybe_unused cypress_sf_suspend(struct device *dev)
    172{
    173	struct i2c_client *client = to_i2c_client(dev);
    174	struct cypress_sf_data *touchkey = i2c_get_clientdata(client);
    175	int error;
    176
    177	disable_irq(client->irq);
    178
    179	error = regulator_bulk_disable(ARRAY_SIZE(touchkey->regulators),
    180				       touchkey->regulators);
    181	if (error) {
    182		dev_err(dev, "Failed to disable regulators: %d", error);
    183		enable_irq(client->irq);
    184		return error;
    185	}
    186
    187	return 0;
    188}
    189
    190static int __maybe_unused cypress_sf_resume(struct device *dev)
    191{
    192	struct i2c_client *client = to_i2c_client(dev);
    193	struct cypress_sf_data *touchkey = i2c_get_clientdata(client);
    194	int error;
    195
    196	error = regulator_bulk_enable(ARRAY_SIZE(touchkey->regulators),
    197				      touchkey->regulators);
    198	if (error) {
    199		dev_err(dev, "Failed to enable regulators: %d", error);
    200		return error;
    201	}
    202
    203	enable_irq(client->irq);
    204
    205	return 0;
    206}
    207
    208static SIMPLE_DEV_PM_OPS(cypress_sf_pm_ops,
    209			 cypress_sf_suspend, cypress_sf_resume);
    210
    211static struct i2c_device_id cypress_sf_id_table[] = {
    212	{ CYPRESS_SF_DEV_NAME, 0 },
    213	{ }
    214};
    215MODULE_DEVICE_TABLE(i2c, cypress_sf_id_table);
    216
    217#ifdef CONFIG_OF
    218static const struct of_device_id cypress_sf_of_match[] = {
    219	{ .compatible = "cypress,sf3155", },
    220	{ },
    221};
    222MODULE_DEVICE_TABLE(of, cypress_sf_of_match);
    223#endif
    224
    225static struct i2c_driver cypress_sf_driver = {
    226	.driver = {
    227		.name = CYPRESS_SF_DEV_NAME,
    228		.pm = &cypress_sf_pm_ops,
    229		.of_match_table = of_match_ptr(cypress_sf_of_match),
    230	},
    231	.id_table = cypress_sf_id_table,
    232	.probe_new = cypress_sf_probe,
    233};
    234module_i2c_driver(cypress_sf_driver);
    235
    236MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
    237MODULE_DESCRIPTION("Cypress StreetFighter Touchkey Driver");
    238MODULE_LICENSE("GPL v2");