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

palmas-pwrbutton.c (8458B)


      1/*
      2 * Texas Instruments' Palmas Power Button Input Driver
      3 *
      4 * Copyright (C) 2012-2014 Texas Instruments Incorporated - http://www.ti.com/
      5 *	Girish S Ghongdemath
      6 *	Nishanth Menon
      7 *
      8 * This program is free software; you can redistribute it and/or modify
      9 * it under the terms of the GNU General Public License version 2 as
     10 * published by the Free Software Foundation.
     11 *
     12 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
     13 * kind, whether express or implied; without even the implied warranty
     14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16 */
     17
     18#include <linux/bitfield.h>
     19#include <linux/init.h>
     20#include <linux/input.h>
     21#include <linux/interrupt.h>
     22#include <linux/kernel.h>
     23#include <linux/mfd/palmas.h>
     24#include <linux/module.h>
     25#include <linux/of.h>
     26#include <linux/platform_device.h>
     27#include <linux/slab.h>
     28
     29#define PALMAS_LPK_TIME_MASK		0x0c
     30#define PALMAS_PWRON_DEBOUNCE_MASK	0x03
     31#define PALMAS_PWR_KEY_Q_TIME_MS	20
     32
     33/**
     34 * struct palmas_pwron - Palmas power on data
     35 * @palmas:		pointer to palmas device
     36 * @input_dev:		pointer to input device
     37 * @input_work:		work for detecting release of key
     38 * @irq:		irq that we are hooked on to
     39 */
     40struct palmas_pwron {
     41	struct palmas *palmas;
     42	struct input_dev *input_dev;
     43	struct delayed_work input_work;
     44	int irq;
     45};
     46
     47/**
     48 * struct palmas_pwron_config - configuration of palmas power on
     49 * @long_press_time_val:	value for long press h/w shutdown event
     50 * @pwron_debounce_val:		value for debounce of power button
     51 */
     52struct palmas_pwron_config {
     53	u8 long_press_time_val;
     54	u8 pwron_debounce_val;
     55};
     56
     57/**
     58 * palmas_power_button_work() - Detects the button release event
     59 * @work:	work item to detect button release
     60 */
     61static void palmas_power_button_work(struct work_struct *work)
     62{
     63	struct palmas_pwron *pwron = container_of(work,
     64						  struct palmas_pwron,
     65						  input_work.work);
     66	struct input_dev *input_dev = pwron->input_dev;
     67	unsigned int reg;
     68	int error;
     69
     70	error = palmas_read(pwron->palmas, PALMAS_INTERRUPT_BASE,
     71			    PALMAS_INT1_LINE_STATE, &reg);
     72	if (error) {
     73		dev_err(input_dev->dev.parent,
     74			"Cannot read palmas PWRON status: %d\n", error);
     75	} else if (reg & BIT(1)) {
     76		/* The button is released, report event. */
     77		input_report_key(input_dev, KEY_POWER, 0);
     78		input_sync(input_dev);
     79	} else {
     80		/* The button is still depressed, keep checking. */
     81		schedule_delayed_work(&pwron->input_work,
     82				msecs_to_jiffies(PALMAS_PWR_KEY_Q_TIME_MS));
     83	}
     84}
     85
     86/**
     87 * pwron_irq() - button press isr
     88 * @irq:		irq
     89 * @palmas_pwron:	pwron struct
     90 *
     91 * Return: IRQ_HANDLED
     92 */
     93static irqreturn_t pwron_irq(int irq, void *palmas_pwron)
     94{
     95	struct palmas_pwron *pwron = palmas_pwron;
     96	struct input_dev *input_dev = pwron->input_dev;
     97
     98	input_report_key(input_dev, KEY_POWER, 1);
     99	pm_wakeup_event(input_dev->dev.parent, 0);
    100	input_sync(input_dev);
    101
    102	mod_delayed_work(system_wq, &pwron->input_work,
    103			 msecs_to_jiffies(PALMAS_PWR_KEY_Q_TIME_MS));
    104
    105	return IRQ_HANDLED;
    106}
    107
    108/**
    109 * palmas_pwron_params_ofinit() - device tree parameter parser
    110 * @dev:	palmas button device
    111 * @config:	configuration params that this fills up
    112 */
    113static void palmas_pwron_params_ofinit(struct device *dev,
    114				       struct palmas_pwron_config *config)
    115{
    116	struct device_node *np;
    117	u32 val;
    118	int i, error;
    119	static const u8 lpk_times[] = { 6, 8, 10, 12 };
    120	static const int pwr_on_deb_ms[] = { 15, 100, 500, 1000 };
    121
    122	memset(config, 0, sizeof(*config));
    123
    124	/* Default config parameters */
    125	config->long_press_time_val = ARRAY_SIZE(lpk_times) - 1;
    126
    127	np = dev->of_node;
    128	if (!np)
    129		return;
    130
    131	error = of_property_read_u32(np, "ti,palmas-long-press-seconds", &val);
    132	if (!error) {
    133		for (i = 0; i < ARRAY_SIZE(lpk_times); i++) {
    134			if (val <= lpk_times[i]) {
    135				config->long_press_time_val = i;
    136				break;
    137			}
    138		}
    139	}
    140
    141	error = of_property_read_u32(np,
    142				     "ti,palmas-pwron-debounce-milli-seconds",
    143				     &val);
    144	if (!error) {
    145		for (i = 0; i < ARRAY_SIZE(pwr_on_deb_ms); i++) {
    146			if (val <= pwr_on_deb_ms[i]) {
    147				config->pwron_debounce_val = i;
    148				break;
    149			}
    150		}
    151	}
    152
    153	dev_info(dev, "h/w controlled shutdown duration=%d seconds\n",
    154		 lpk_times[config->long_press_time_val]);
    155}
    156
    157/**
    158 * palmas_pwron_probe() - probe
    159 * @pdev:	platform device for the button
    160 *
    161 * Return: 0 for successful probe else appropriate error
    162 */
    163static int palmas_pwron_probe(struct platform_device *pdev)
    164{
    165	struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
    166	struct device *dev = &pdev->dev;
    167	struct input_dev *input_dev;
    168	struct palmas_pwron *pwron;
    169	struct palmas_pwron_config config;
    170	int val;
    171	int error;
    172
    173	palmas_pwron_params_ofinit(dev, &config);
    174
    175	pwron = kzalloc(sizeof(*pwron), GFP_KERNEL);
    176	if (!pwron)
    177		return -ENOMEM;
    178
    179	input_dev = input_allocate_device();
    180	if (!input_dev) {
    181		dev_err(dev, "Can't allocate power button\n");
    182		error = -ENOMEM;
    183		goto err_free_mem;
    184	}
    185
    186	input_dev->name = "palmas_pwron";
    187	input_dev->phys = "palmas_pwron/input0";
    188	input_dev->dev.parent = dev;
    189
    190	input_set_capability(input_dev, EV_KEY, KEY_POWER);
    191
    192	/*
    193	 * Setup default hardware shutdown option (long key press)
    194	 * and debounce.
    195	 */
    196	val = FIELD_PREP(PALMAS_LPK_TIME_MASK, config.long_press_time_val) |
    197	      FIELD_PREP(PALMAS_PWRON_DEBOUNCE_MASK, config.pwron_debounce_val);
    198	error = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE,
    199				   PALMAS_LONG_PRESS_KEY,
    200				   PALMAS_LPK_TIME_MASK |
    201					PALMAS_PWRON_DEBOUNCE_MASK,
    202				   val);
    203	if (error) {
    204		dev_err(dev, "LONG_PRESS_KEY_UPDATE failed: %d\n", error);
    205		goto err_free_input;
    206	}
    207
    208	pwron->palmas = palmas;
    209	pwron->input_dev = input_dev;
    210
    211	INIT_DELAYED_WORK(&pwron->input_work, palmas_power_button_work);
    212
    213	pwron->irq = platform_get_irq(pdev, 0);
    214	if (pwron->irq < 0) {
    215		error = pwron->irq;
    216		goto err_free_input;
    217	}
    218
    219	error = request_threaded_irq(pwron->irq, NULL, pwron_irq,
    220				     IRQF_TRIGGER_HIGH |
    221					IRQF_TRIGGER_LOW |
    222					IRQF_ONESHOT,
    223				     dev_name(dev), pwron);
    224	if (error) {
    225		dev_err(dev, "Can't get IRQ for pwron: %d\n", error);
    226		goto err_free_input;
    227	}
    228
    229	error = input_register_device(input_dev);
    230	if (error) {
    231		dev_err(dev, "Can't register power button: %d\n", error);
    232		goto err_free_irq;
    233	}
    234
    235	platform_set_drvdata(pdev, pwron);
    236	device_init_wakeup(dev, true);
    237
    238	return 0;
    239
    240err_free_irq:
    241	cancel_delayed_work_sync(&pwron->input_work);
    242	free_irq(pwron->irq, pwron);
    243err_free_input:
    244	input_free_device(input_dev);
    245err_free_mem:
    246	kfree(pwron);
    247	return error;
    248}
    249
    250/**
    251 * palmas_pwron_remove() - Cleanup on removal
    252 * @pdev:	platform device for the button
    253 *
    254 * Return: 0
    255 */
    256static int palmas_pwron_remove(struct platform_device *pdev)
    257{
    258	struct palmas_pwron *pwron = platform_get_drvdata(pdev);
    259
    260	free_irq(pwron->irq, pwron);
    261	cancel_delayed_work_sync(&pwron->input_work);
    262
    263	input_unregister_device(pwron->input_dev);
    264	kfree(pwron);
    265
    266	return 0;
    267}
    268
    269/**
    270 * palmas_pwron_suspend() - suspend handler
    271 * @dev:	power button device
    272 *
    273 * Cancel all pending work items for the power button, setup irq for wakeup
    274 *
    275 * Return: 0
    276 */
    277static int __maybe_unused palmas_pwron_suspend(struct device *dev)
    278{
    279	struct platform_device *pdev = to_platform_device(dev);
    280	struct palmas_pwron *pwron = platform_get_drvdata(pdev);
    281
    282	cancel_delayed_work_sync(&pwron->input_work);
    283
    284	if (device_may_wakeup(dev))
    285		enable_irq_wake(pwron->irq);
    286
    287	return 0;
    288}
    289
    290/**
    291 * palmas_pwron_resume() - resume handler
    292 * @dev:	power button device
    293 *
    294 * Just disable the wakeup capability of irq here.
    295 *
    296 * Return: 0
    297 */
    298static int __maybe_unused palmas_pwron_resume(struct device *dev)
    299{
    300	struct platform_device *pdev = to_platform_device(dev);
    301	struct palmas_pwron *pwron = platform_get_drvdata(pdev);
    302
    303	if (device_may_wakeup(dev))
    304		disable_irq_wake(pwron->irq);
    305
    306	return 0;
    307}
    308
    309static SIMPLE_DEV_PM_OPS(palmas_pwron_pm,
    310			 palmas_pwron_suspend, palmas_pwron_resume);
    311
    312#ifdef CONFIG_OF
    313static const struct of_device_id of_palmas_pwr_match[] = {
    314	{ .compatible = "ti,palmas-pwrbutton" },
    315	{ },
    316};
    317
    318MODULE_DEVICE_TABLE(of, of_palmas_pwr_match);
    319#endif
    320
    321static struct platform_driver palmas_pwron_driver = {
    322	.probe	= palmas_pwron_probe,
    323	.remove	= palmas_pwron_remove,
    324	.driver	= {
    325		.name	= "palmas_pwrbutton",
    326		.of_match_table = of_match_ptr(of_palmas_pwr_match),
    327		.pm	= &palmas_pwron_pm,
    328	},
    329};
    330module_platform_driver(palmas_pwron_driver);
    331
    332MODULE_ALIAS("platform:palmas-pwrbutton");
    333MODULE_DESCRIPTION("Palmas Power Button");
    334MODULE_LICENSE("GPL v2");
    335MODULE_AUTHOR("Texas Instruments Inc.");