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

atc260x-onkey.c (8371B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Onkey driver for Actions Semi ATC260x PMICs.
      4 *
      5 * Copyright (c) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
      6 */
      7
      8#include <linux/bitfield.h>
      9#include <linux/input.h>
     10#include <linux/interrupt.h>
     11#include <linux/mfd/atc260x/core.h>
     12#include <linux/module.h>
     13#include <linux/of.h>
     14#include <linux/platform_device.h>
     15#include <linux/regmap.h>
     16
     17/* <2s for short press, >2s for long press */
     18#define KEY_PRESS_TIME_SEC	2
     19
     20/* Driver internals */
     21enum atc260x_onkey_reset_status {
     22	KEY_RESET_HW_DEFAULT,
     23	KEY_RESET_DISABLED,
     24	KEY_RESET_USER_SEL,
     25};
     26
     27struct atc260x_onkey_params {
     28	u32 reg_int_ctl;
     29	u32 kdwn_state_bm;
     30	u32 long_int_pnd_bm;
     31	u32 short_int_pnd_bm;
     32	u32 kdwn_int_pnd_bm;
     33	u32 press_int_en_bm;
     34	u32 kdwn_int_en_bm;
     35	u32 press_time_bm;
     36	u32 reset_en_bm;
     37	u32 reset_time_bm;
     38};
     39
     40struct atc260x_onkey {
     41	struct atc260x *atc260x;
     42	const struct atc260x_onkey_params *params;
     43	struct input_dev *input_dev;
     44	struct delayed_work work;
     45	int irq;
     46};
     47
     48static const struct atc260x_onkey_params atc2603c_onkey_params = {
     49	.reg_int_ctl		= ATC2603C_PMU_SYS_CTL2,
     50	.long_int_pnd_bm	= ATC2603C_PMU_SYS_CTL2_ONOFF_LONG_PRESS,
     51	.short_int_pnd_bm	= ATC2603C_PMU_SYS_CTL2_ONOFF_SHORT_PRESS,
     52	.kdwn_int_pnd_bm	= ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_PD,
     53	.press_int_en_bm	= ATC2603C_PMU_SYS_CTL2_ONOFF_INT_EN,
     54	.kdwn_int_en_bm		= ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_INT_EN,
     55	.kdwn_state_bm		= ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS,
     56	.press_time_bm		= ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_TIME,
     57	.reset_en_bm		= ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_RESET_EN,
     58	.reset_time_bm		= ATC2603C_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL,
     59};
     60
     61static const struct atc260x_onkey_params atc2609a_onkey_params = {
     62	.reg_int_ctl		= ATC2609A_PMU_SYS_CTL2,
     63	.long_int_pnd_bm	= ATC2609A_PMU_SYS_CTL2_ONOFF_LONG_PRESS,
     64	.short_int_pnd_bm	= ATC2609A_PMU_SYS_CTL2_ONOFF_SHORT_PRESS,
     65	.kdwn_int_pnd_bm	= ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_PD,
     66	.press_int_en_bm	= ATC2609A_PMU_SYS_CTL2_ONOFF_LSP_INT_EN,
     67	.kdwn_int_en_bm		= ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_INT_EN,
     68	.kdwn_state_bm		= ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS,
     69	.press_time_bm		= ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_TIME,
     70	.reset_en_bm		= ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_EN,
     71	.reset_time_bm		= ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL,
     72};
     73
     74static int atc2603x_onkey_hw_init(struct atc260x_onkey *onkey,
     75				  enum atc260x_onkey_reset_status reset_status,
     76				  u32 reset_time, u32 press_time)
     77{
     78	u32 reg_bm, reg_val;
     79
     80	reg_bm = onkey->params->long_int_pnd_bm |
     81		 onkey->params->short_int_pnd_bm |
     82		 onkey->params->kdwn_int_pnd_bm |
     83		 onkey->params->press_int_en_bm |
     84		 onkey->params->kdwn_int_en_bm;
     85
     86	reg_val = reg_bm | press_time;
     87	reg_bm |= onkey->params->press_time_bm;
     88
     89	if (reset_status == KEY_RESET_DISABLED) {
     90		reg_bm |= onkey->params->reset_en_bm;
     91	} else if (reset_status == KEY_RESET_USER_SEL) {
     92		reg_bm |= onkey->params->reset_en_bm |
     93			  onkey->params->reset_time_bm;
     94		reg_val |= onkey->params->reset_en_bm | reset_time;
     95	}
     96
     97	return regmap_update_bits(onkey->atc260x->regmap,
     98				  onkey->params->reg_int_ctl, reg_bm, reg_val);
     99}
    100
    101static void atc260x_onkey_query(struct atc260x_onkey *onkey)
    102{
    103	u32 reg_bits;
    104	int ret, key_down;
    105
    106	ret = regmap_read(onkey->atc260x->regmap,
    107			  onkey->params->reg_int_ctl, &key_down);
    108	if (ret) {
    109		key_down = 1;
    110		dev_err(onkey->atc260x->dev,
    111			"Failed to read onkey status: %d\n", ret);
    112	} else {
    113		key_down &= onkey->params->kdwn_state_bm;
    114	}
    115
    116	/*
    117	 * The hardware generates interrupt only when the onkey pin is
    118	 * asserted. Hence, the deassertion of the pin is simulated through
    119	 * work queue.
    120	 */
    121	if (key_down) {
    122		schedule_delayed_work(&onkey->work, msecs_to_jiffies(200));
    123		return;
    124	}
    125
    126	/*
    127	 * The key-down status bit is cleared when the On/Off button
    128	 * is released.
    129	 */
    130	input_report_key(onkey->input_dev, KEY_POWER, 0);
    131	input_sync(onkey->input_dev);
    132
    133	reg_bits = onkey->params->long_int_pnd_bm |
    134		   onkey->params->short_int_pnd_bm |
    135		   onkey->params->kdwn_int_pnd_bm |
    136		   onkey->params->press_int_en_bm |
    137		   onkey->params->kdwn_int_en_bm;
    138
    139	/* Clear key press pending events and enable key press interrupts. */
    140	regmap_update_bits(onkey->atc260x->regmap, onkey->params->reg_int_ctl,
    141			   reg_bits, reg_bits);
    142}
    143
    144static void atc260x_onkey_work(struct work_struct *work)
    145{
    146	struct atc260x_onkey *onkey = container_of(work, struct atc260x_onkey,
    147						   work.work);
    148	atc260x_onkey_query(onkey);
    149}
    150
    151static irqreturn_t atc260x_onkey_irq(int irq, void *data)
    152{
    153	struct atc260x_onkey *onkey = data;
    154	int ret;
    155
    156	/* Disable key press interrupts. */
    157	ret = regmap_update_bits(onkey->atc260x->regmap,
    158				 onkey->params->reg_int_ctl,
    159				 onkey->params->press_int_en_bm |
    160				 onkey->params->kdwn_int_en_bm, 0);
    161	if (ret)
    162		dev_err(onkey->atc260x->dev,
    163			"Failed to disable interrupts: %d\n", ret);
    164
    165	input_report_key(onkey->input_dev, KEY_POWER, 1);
    166	input_sync(onkey->input_dev);
    167
    168	atc260x_onkey_query(onkey);
    169
    170	return IRQ_HANDLED;
    171}
    172
    173static int atc260x_onkey_open(struct input_dev *dev)
    174{
    175	struct atc260x_onkey *onkey = input_get_drvdata(dev);
    176
    177	enable_irq(onkey->irq);
    178
    179	return 0;
    180}
    181
    182static void atc260x_onkey_close(struct input_dev *dev)
    183{
    184	struct atc260x_onkey *onkey = input_get_drvdata(dev);
    185
    186	disable_irq(onkey->irq);
    187	cancel_delayed_work_sync(&onkey->work);
    188}
    189
    190static int atc260x_onkey_probe(struct platform_device *pdev)
    191{
    192	struct atc260x *atc260x = dev_get_drvdata(pdev->dev.parent);
    193	struct atc260x_onkey *onkey;
    194	struct input_dev *input_dev;
    195	enum atc260x_onkey_reset_status reset_status;
    196	u32 press_time = KEY_PRESS_TIME_SEC, reset_time = 0;
    197	int val, error;
    198
    199	onkey = devm_kzalloc(&pdev->dev, sizeof(*onkey), GFP_KERNEL);
    200	if (!onkey)
    201		return -ENOMEM;
    202
    203	error = device_property_read_u32(pdev->dev.parent,
    204					 "reset-time-sec", &val);
    205	if (error) {
    206		reset_status = KEY_RESET_HW_DEFAULT;
    207	} else if (val) {
    208		if (val < 6 || val > 12) {
    209			dev_err(&pdev->dev, "reset-time-sec out of range\n");
    210			return -EINVAL;
    211		}
    212
    213		reset_status = KEY_RESET_USER_SEL;
    214		reset_time = (val - 6) / 2;
    215	} else {
    216		reset_status = KEY_RESET_DISABLED;
    217		dev_dbg(&pdev->dev, "Disabled reset on long-press\n");
    218	}
    219
    220	switch (atc260x->ic_type) {
    221	case ATC2603C:
    222		onkey->params = &atc2603c_onkey_params;
    223		press_time = FIELD_PREP(ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_TIME,
    224					press_time);
    225		reset_time = FIELD_PREP(ATC2603C_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL,
    226					reset_time);
    227		break;
    228	case ATC2609A:
    229		onkey->params = &atc2609a_onkey_params;
    230		press_time = FIELD_PREP(ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_TIME,
    231					press_time);
    232		reset_time = FIELD_PREP(ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL,
    233					reset_time);
    234		break;
    235	default:
    236		dev_err(&pdev->dev,
    237			"OnKey not supported for ATC260x PMIC type: %u\n",
    238			atc260x->ic_type);
    239		return -EINVAL;
    240	}
    241
    242	input_dev = devm_input_allocate_device(&pdev->dev);
    243	if (!input_dev) {
    244		dev_err(&pdev->dev, "Failed to allocate input device\n");
    245		return -ENOMEM;
    246	}
    247
    248	onkey->input_dev = input_dev;
    249	onkey->atc260x = atc260x;
    250
    251	input_dev->name = "atc260x-onkey";
    252	input_dev->phys = "atc260x-onkey/input0";
    253	input_dev->open = atc260x_onkey_open;
    254	input_dev->close = atc260x_onkey_close;
    255
    256	input_set_capability(input_dev, EV_KEY, KEY_POWER);
    257	input_set_drvdata(input_dev, onkey);
    258
    259	INIT_DELAYED_WORK(&onkey->work, atc260x_onkey_work);
    260
    261	onkey->irq = platform_get_irq(pdev, 0);
    262	if (onkey->irq < 0)
    263		return onkey->irq;
    264
    265	error = devm_request_threaded_irq(&pdev->dev, onkey->irq, NULL,
    266					  atc260x_onkey_irq, IRQF_ONESHOT,
    267					  dev_name(&pdev->dev), onkey);
    268	if (error) {
    269		dev_err(&pdev->dev,
    270			"Failed to register IRQ %d: %d\n", onkey->irq, error);
    271		return error;
    272	}
    273
    274	/* Keep IRQ disabled until atc260x_onkey_open() is called. */
    275	disable_irq(onkey->irq);
    276
    277	error = input_register_device(input_dev);
    278	if (error) {
    279		dev_err(&pdev->dev,
    280			"Failed to register input device: %d\n", error);
    281		return error;
    282	}
    283
    284	error = atc2603x_onkey_hw_init(onkey, reset_status,
    285				       reset_time, press_time);
    286	if (error)
    287		return error;
    288
    289	device_init_wakeup(&pdev->dev, true);
    290
    291	return 0;
    292}
    293
    294static struct platform_driver atc260x_onkey_driver = {
    295	.probe	= atc260x_onkey_probe,
    296	.driver	= {
    297		.name = "atc260x-onkey",
    298	},
    299};
    300
    301module_platform_driver(atc260x_onkey_driver);
    302
    303MODULE_DESCRIPTION("Onkey driver for ATC260x PMICs");
    304MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>");
    305MODULE_LICENSE("GPL");