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

tps65218-pwrbutton.c (4274B)


      1/*
      2 * Texas Instruments' TPS65217 and TPS65218 Power Button Input Driver
      3 *
      4 * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
      5 * Author: Felipe Balbi <balbi@ti.com>
      6 * Author: Marcin Niestroj <m.niestroj@grinn-global.com>
      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/init.h>
     19#include <linux/input.h>
     20#include <linux/interrupt.h>
     21#include <linux/kernel.h>
     22#include <linux/mfd/tps65217.h>
     23#include <linux/mfd/tps65218.h>
     24#include <linux/module.h>
     25#include <linux/of.h>
     26#include <linux/platform_device.h>
     27#include <linux/regmap.h>
     28#include <linux/slab.h>
     29
     30struct tps6521x_data {
     31	unsigned int reg_status;
     32	unsigned int pb_mask;
     33	const char *name;
     34};
     35
     36static const struct tps6521x_data tps65217_data = {
     37	.reg_status = TPS65217_REG_STATUS,
     38	.pb_mask = TPS65217_STATUS_PB,
     39	.name = "tps65217_pwrbutton",
     40};
     41
     42static const struct tps6521x_data tps65218_data = {
     43	.reg_status = TPS65218_REG_STATUS,
     44	.pb_mask = TPS65218_STATUS_PB_STATE,
     45	.name = "tps65218_pwrbutton",
     46};
     47
     48struct tps6521x_pwrbutton {
     49	struct device *dev;
     50	struct regmap *regmap;
     51	struct input_dev *idev;
     52	const struct tps6521x_data *data;
     53	char phys[32];
     54};
     55
     56static const struct of_device_id of_tps6521x_pb_match[] = {
     57	{ .compatible = "ti,tps65217-pwrbutton", .data = &tps65217_data },
     58	{ .compatible = "ti,tps65218-pwrbutton", .data = &tps65218_data },
     59	{ },
     60};
     61MODULE_DEVICE_TABLE(of, of_tps6521x_pb_match);
     62
     63static irqreturn_t tps6521x_pb_irq(int irq, void *_pwr)
     64{
     65	struct tps6521x_pwrbutton *pwr = _pwr;
     66	const struct tps6521x_data *tps_data = pwr->data;
     67	unsigned int reg;
     68	int error;
     69
     70	error = regmap_read(pwr->regmap, tps_data->reg_status, &reg);
     71	if (error) {
     72		dev_err(pwr->dev, "can't read register: %d\n", error);
     73		goto out;
     74	}
     75
     76	if (reg & tps_data->pb_mask) {
     77		input_report_key(pwr->idev, KEY_POWER, 1);
     78		pm_wakeup_event(pwr->dev, 0);
     79	} else {
     80		input_report_key(pwr->idev, KEY_POWER, 0);
     81	}
     82
     83	input_sync(pwr->idev);
     84
     85out:
     86	return IRQ_HANDLED;
     87}
     88
     89static int tps6521x_pb_probe(struct platform_device *pdev)
     90{
     91	struct device *dev = &pdev->dev;
     92	struct tps6521x_pwrbutton *pwr;
     93	struct input_dev *idev;
     94	const struct of_device_id *match;
     95	int error;
     96	int irq;
     97
     98	match = of_match_node(of_tps6521x_pb_match, dev->of_node);
     99	if (!match)
    100		return -ENXIO;
    101
    102	pwr = devm_kzalloc(dev, sizeof(*pwr), GFP_KERNEL);
    103	if (!pwr)
    104		return -ENOMEM;
    105
    106	pwr->data = match->data;
    107
    108	idev = devm_input_allocate_device(dev);
    109	if (!idev)
    110		return -ENOMEM;
    111
    112	idev->name = pwr->data->name;
    113	snprintf(pwr->phys, sizeof(pwr->phys), "%s/input0",
    114		pwr->data->name);
    115	idev->phys = pwr->phys;
    116	idev->dev.parent = dev;
    117	idev->id.bustype = BUS_I2C;
    118
    119	input_set_capability(idev, EV_KEY, KEY_POWER);
    120
    121	pwr->regmap = dev_get_regmap(dev->parent, NULL);
    122	pwr->dev = dev;
    123	pwr->idev = idev;
    124	device_init_wakeup(dev, true);
    125
    126	irq = platform_get_irq(pdev, 0);
    127	if (irq < 0)
    128		return -EINVAL;
    129
    130	error = devm_request_threaded_irq(dev, irq, NULL, tps6521x_pb_irq,
    131					  IRQF_TRIGGER_RISING |
    132						IRQF_TRIGGER_FALLING |
    133						IRQF_ONESHOT,
    134					  pwr->data->name, pwr);
    135	if (error) {
    136		dev_err(dev, "failed to request IRQ #%d: %d\n", irq, error);
    137		return error;
    138	}
    139
    140	error= input_register_device(idev);
    141	if (error) {
    142		dev_err(dev, "Can't register power button: %d\n", error);
    143		return error;
    144	}
    145
    146	return 0;
    147}
    148
    149static const struct platform_device_id tps6521x_pwrbtn_id_table[] = {
    150	{ "tps65218-pwrbutton", },
    151	{ "tps65217-pwrbutton", },
    152	{ /* sentinel */ }
    153};
    154MODULE_DEVICE_TABLE(platform, tps6521x_pwrbtn_id_table);
    155
    156static struct platform_driver tps6521x_pb_driver = {
    157	.probe	= tps6521x_pb_probe,
    158	.driver	= {
    159		.name	= "tps6521x_pwrbutton",
    160		.of_match_table = of_tps6521x_pb_match,
    161	},
    162	.id_table = tps6521x_pwrbtn_id_table,
    163};
    164module_platform_driver(tps6521x_pb_driver);
    165
    166MODULE_DESCRIPTION("TPS6521X Power Button");
    167MODULE_LICENSE("GPL v2");
    168MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");