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

axp20x-pek.c (10677B)


      1/*
      2 * axp20x power button driver.
      3 *
      4 * Copyright (C) 2013 Carlo Caione <carlo@caione.org>
      5 *
      6 * This file is subject to the terms and conditions of the GNU General
      7 * Public License. See the file "COPYING" in the main directory of this
      8 * archive for more details.
      9 *
     10 * This program is distributed in the hope that it will be useful,
     11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     13 * GNU General Public License for more details.
     14 */
     15
     16#include <linux/acpi.h>
     17#include <linux/errno.h>
     18#include <linux/irq.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/axp20x.h>
     24#include <linux/module.h>
     25#include <linux/platform_data/x86/soc.h>
     26#include <linux/platform_device.h>
     27#include <linux/regmap.h>
     28#include <linux/slab.h>
     29
     30#define AXP20X_PEK_STARTUP_MASK		(0xc0)
     31#define AXP20X_PEK_SHUTDOWN_MASK	(0x03)
     32
     33struct axp20x_info {
     34	const struct axp20x_time *startup_time;
     35	unsigned int startup_mask;
     36	const struct axp20x_time *shutdown_time;
     37	unsigned int shutdown_mask;
     38};
     39
     40struct axp20x_pek {
     41	struct axp20x_dev *axp20x;
     42	struct input_dev *input;
     43	struct axp20x_info *info;
     44	int irq_dbr;
     45	int irq_dbf;
     46};
     47
     48struct axp20x_time {
     49	unsigned int time;
     50	unsigned int idx;
     51};
     52
     53static const struct axp20x_time startup_time[] = {
     54	{ .time = 128,  .idx = 0 },
     55	{ .time = 1000, .idx = 2 },
     56	{ .time = 3000, .idx = 1 },
     57	{ .time = 2000, .idx = 3 },
     58};
     59
     60static const struct axp20x_time axp221_startup_time[] = {
     61	{ .time = 128,  .idx = 0 },
     62	{ .time = 1000, .idx = 1 },
     63	{ .time = 2000, .idx = 2 },
     64	{ .time = 3000, .idx = 3 },
     65};
     66
     67static const struct axp20x_time shutdown_time[] = {
     68	{ .time = 4000,  .idx = 0 },
     69	{ .time = 6000,  .idx = 1 },
     70	{ .time = 8000,  .idx = 2 },
     71	{ .time = 10000, .idx = 3 },
     72};
     73
     74static const struct axp20x_info axp20x_info = {
     75	.startup_time = startup_time,
     76	.startup_mask = AXP20X_PEK_STARTUP_MASK,
     77	.shutdown_time = shutdown_time,
     78	.shutdown_mask = AXP20X_PEK_SHUTDOWN_MASK,
     79};
     80
     81static const struct axp20x_info axp221_info = {
     82	.startup_time = axp221_startup_time,
     83	.startup_mask = AXP20X_PEK_STARTUP_MASK,
     84	.shutdown_time = shutdown_time,
     85	.shutdown_mask = AXP20X_PEK_SHUTDOWN_MASK,
     86};
     87
     88static ssize_t axp20x_show_attr(struct device *dev,
     89				const struct axp20x_time *time,
     90				unsigned int mask, char *buf)
     91{
     92	struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
     93	unsigned int val;
     94	int ret, i;
     95
     96	ret = regmap_read(axp20x_pek->axp20x->regmap, AXP20X_PEK_KEY, &val);
     97	if (ret != 0)
     98		return ret;
     99
    100	val &= mask;
    101	val >>= ffs(mask) - 1;
    102
    103	for (i = 0; i < 4; i++)
    104		if (val == time[i].idx)
    105			val = time[i].time;
    106
    107	return sprintf(buf, "%u\n", val);
    108}
    109
    110static ssize_t axp20x_show_attr_startup(struct device *dev,
    111					struct device_attribute *attr,
    112					char *buf)
    113{
    114	struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
    115
    116	return axp20x_show_attr(dev, axp20x_pek->info->startup_time,
    117				axp20x_pek->info->startup_mask, buf);
    118}
    119
    120static ssize_t axp20x_show_attr_shutdown(struct device *dev,
    121					 struct device_attribute *attr,
    122					 char *buf)
    123{
    124	struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
    125
    126	return axp20x_show_attr(dev, axp20x_pek->info->shutdown_time,
    127				axp20x_pek->info->shutdown_mask, buf);
    128}
    129
    130static ssize_t axp20x_store_attr(struct device *dev,
    131				 const struct axp20x_time *time,
    132				 unsigned int mask, const char *buf,
    133				 size_t count)
    134{
    135	struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
    136	char val_str[20];
    137	size_t len;
    138	int ret, i;
    139	unsigned int val, idx = 0;
    140	unsigned int best_err = UINT_MAX;
    141
    142	val_str[sizeof(val_str) - 1] = '\0';
    143	strncpy(val_str, buf, sizeof(val_str) - 1);
    144	len = strlen(val_str);
    145
    146	if (len && val_str[len - 1] == '\n')
    147		val_str[len - 1] = '\0';
    148
    149	ret = kstrtouint(val_str, 10, &val);
    150	if (ret)
    151		return ret;
    152
    153	for (i = 3; i >= 0; i--) {
    154		unsigned int err;
    155
    156		err = abs(time[i].time - val);
    157		if (err < best_err) {
    158			best_err = err;
    159			idx = time[i].idx;
    160		}
    161
    162		if (!err)
    163			break;
    164	}
    165
    166	idx <<= ffs(mask) - 1;
    167	ret = regmap_update_bits(axp20x_pek->axp20x->regmap, AXP20X_PEK_KEY,
    168				 mask, idx);
    169	if (ret != 0)
    170		return -EINVAL;
    171
    172	return count;
    173}
    174
    175static ssize_t axp20x_store_attr_startup(struct device *dev,
    176					 struct device_attribute *attr,
    177					 const char *buf, size_t count)
    178{
    179	struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
    180
    181	return axp20x_store_attr(dev, axp20x_pek->info->startup_time,
    182				 axp20x_pek->info->startup_mask, buf, count);
    183}
    184
    185static ssize_t axp20x_store_attr_shutdown(struct device *dev,
    186					  struct device_attribute *attr,
    187					  const char *buf, size_t count)
    188{
    189	struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
    190
    191	return axp20x_store_attr(dev, axp20x_pek->info->shutdown_time,
    192				 axp20x_pek->info->shutdown_mask, buf, count);
    193}
    194
    195static DEVICE_ATTR(startup, 0644, axp20x_show_attr_startup,
    196		   axp20x_store_attr_startup);
    197static DEVICE_ATTR(shutdown, 0644, axp20x_show_attr_shutdown,
    198		   axp20x_store_attr_shutdown);
    199
    200static struct attribute *axp20x_attrs[] = {
    201	&dev_attr_startup.attr,
    202	&dev_attr_shutdown.attr,
    203	NULL,
    204};
    205ATTRIBUTE_GROUPS(axp20x);
    206
    207static irqreturn_t axp20x_pek_irq(int irq, void *pwr)
    208{
    209	struct input_dev *idev = pwr;
    210	struct axp20x_pek *axp20x_pek = input_get_drvdata(idev);
    211
    212	/*
    213	 * The power-button is connected to ground so a falling edge (dbf)
    214	 * means it is pressed.
    215	 */
    216	if (irq == axp20x_pek->irq_dbf)
    217		input_report_key(idev, KEY_POWER, true);
    218	else if (irq == axp20x_pek->irq_dbr)
    219		input_report_key(idev, KEY_POWER, false);
    220
    221	input_sync(idev);
    222
    223	return IRQ_HANDLED;
    224}
    225
    226static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek,
    227					 struct platform_device *pdev)
    228{
    229	struct axp20x_dev *axp20x = axp20x_pek->axp20x;
    230	struct input_dev *idev;
    231	int error;
    232
    233	axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR");
    234	if (axp20x_pek->irq_dbr < 0)
    235		return axp20x_pek->irq_dbr;
    236	axp20x_pek->irq_dbr = regmap_irq_get_virq(axp20x->regmap_irqc,
    237						  axp20x_pek->irq_dbr);
    238
    239	axp20x_pek->irq_dbf = platform_get_irq_byname(pdev, "PEK_DBF");
    240	if (axp20x_pek->irq_dbf < 0)
    241		return axp20x_pek->irq_dbf;
    242	axp20x_pek->irq_dbf = regmap_irq_get_virq(axp20x->regmap_irqc,
    243						  axp20x_pek->irq_dbf);
    244
    245	axp20x_pek->input = devm_input_allocate_device(&pdev->dev);
    246	if (!axp20x_pek->input)
    247		return -ENOMEM;
    248
    249	idev = axp20x_pek->input;
    250
    251	idev->name = "axp20x-pek";
    252	idev->phys = "m1kbd/input2";
    253	idev->dev.parent = &pdev->dev;
    254
    255	input_set_capability(idev, EV_KEY, KEY_POWER);
    256
    257	input_set_drvdata(idev, axp20x_pek);
    258
    259	error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbr,
    260					     axp20x_pek_irq, 0,
    261					     "axp20x-pek-dbr", idev);
    262	if (error < 0) {
    263		dev_err(&pdev->dev, "Failed to request dbr IRQ#%d: %d\n",
    264			axp20x_pek->irq_dbr, error);
    265		return error;
    266	}
    267
    268	error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbf,
    269					  axp20x_pek_irq, 0,
    270					  "axp20x-pek-dbf", idev);
    271	if (error < 0) {
    272		dev_err(&pdev->dev, "Failed to request dbf IRQ#%d: %d\n",
    273			axp20x_pek->irq_dbf, error);
    274		return error;
    275	}
    276
    277	error = input_register_device(idev);
    278	if (error) {
    279		dev_err(&pdev->dev, "Can't register input device: %d\n",
    280			error);
    281		return error;
    282	}
    283
    284	device_init_wakeup(&pdev->dev, true);
    285
    286	return 0;
    287}
    288
    289static bool axp20x_pek_should_register_input(struct axp20x_pek *axp20x_pek)
    290{
    291	if (IS_ENABLED(CONFIG_INPUT_SOC_BUTTON_ARRAY) &&
    292	    axp20x_pek->axp20x->variant == AXP288_ID) {
    293		/*
    294		 * On Cherry Trail platforms (hrv == 3), do not register the
    295		 * input device if there is an "INTCFD9" or "ACPI0011" gpio
    296		 * button ACPI device, as that handles the power button too,
    297		 * and otherwise we end up reporting all presses twice.
    298		 */
    299		if (soc_intel_is_cht() &&
    300				(acpi_dev_present("INTCFD9", NULL, -1) ||
    301				 acpi_dev_present("ACPI0011", NULL, -1)))
    302			return false;
    303	}
    304
    305	return true;
    306}
    307
    308static int axp20x_pek_probe(struct platform_device *pdev)
    309{
    310	struct axp20x_pek *axp20x_pek;
    311	const struct platform_device_id *match = platform_get_device_id(pdev);
    312	int error;
    313
    314	if (!match) {
    315		dev_err(&pdev->dev, "Failed to get platform_device_id\n");
    316		return -EINVAL;
    317	}
    318
    319	axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek),
    320				  GFP_KERNEL);
    321	if (!axp20x_pek)
    322		return -ENOMEM;
    323
    324	axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent);
    325
    326	if (axp20x_pek_should_register_input(axp20x_pek)) {
    327		error = axp20x_pek_probe_input_device(axp20x_pek, pdev);
    328		if (error)
    329			return error;
    330	}
    331
    332	axp20x_pek->info = (struct axp20x_info *)match->driver_data;
    333
    334	platform_set_drvdata(pdev, axp20x_pek);
    335
    336	return 0;
    337}
    338
    339static int __maybe_unused axp20x_pek_suspend(struct device *dev)
    340{
    341	struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
    342
    343	/*
    344	 * As nested threaded IRQs are not automatically disabled during
    345	 * suspend, we must explicitly disable non-wakeup IRQs.
    346	 */
    347	if (device_may_wakeup(dev)) {
    348		enable_irq_wake(axp20x_pek->irq_dbf);
    349		enable_irq_wake(axp20x_pek->irq_dbr);
    350	} else {
    351		disable_irq(axp20x_pek->irq_dbf);
    352		disable_irq(axp20x_pek->irq_dbr);
    353	}
    354
    355	return 0;
    356}
    357
    358static int __maybe_unused axp20x_pek_resume(struct device *dev)
    359{
    360	struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
    361
    362	if (device_may_wakeup(dev)) {
    363		disable_irq_wake(axp20x_pek->irq_dbf);
    364		disable_irq_wake(axp20x_pek->irq_dbr);
    365	} else {
    366		enable_irq(axp20x_pek->irq_dbf);
    367		enable_irq(axp20x_pek->irq_dbr);
    368	}
    369
    370	return 0;
    371}
    372
    373static int __maybe_unused axp20x_pek_resume_noirq(struct device *dev)
    374{
    375	struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
    376
    377	if (axp20x_pek->axp20x->variant != AXP288_ID)
    378		return 0;
    379
    380	/*
    381	 * Clear interrupts from button presses during suspend, to avoid
    382	 * a wakeup power-button press getting reported to userspace.
    383	 */
    384	regmap_write(axp20x_pek->axp20x->regmap,
    385		     AXP20X_IRQ1_STATE + AXP288_IRQ_POKN / 8,
    386		     BIT(AXP288_IRQ_POKN % 8));
    387
    388	return 0;
    389}
    390
    391static const struct dev_pm_ops axp20x_pek_pm_ops = {
    392	SET_SYSTEM_SLEEP_PM_OPS(axp20x_pek_suspend, axp20x_pek_resume)
    393#ifdef CONFIG_PM_SLEEP
    394	.resume_noirq = axp20x_pek_resume_noirq,
    395#endif
    396};
    397
    398static const struct platform_device_id axp_pek_id_match[] = {
    399	{
    400		.name = "axp20x-pek",
    401		.driver_data = (kernel_ulong_t)&axp20x_info,
    402	},
    403	{
    404		.name = "axp221-pek",
    405		.driver_data = (kernel_ulong_t)&axp221_info,
    406	},
    407	{ /* sentinel */ }
    408};
    409MODULE_DEVICE_TABLE(platform, axp_pek_id_match);
    410
    411static struct platform_driver axp20x_pek_driver = {
    412	.probe		= axp20x_pek_probe,
    413	.id_table	= axp_pek_id_match,
    414	.driver		= {
    415		.name		= "axp20x-pek",
    416		.pm		= &axp20x_pek_pm_ops,
    417		.dev_groups	= axp20x_groups,
    418	},
    419};
    420module_platform_driver(axp20x_pek_driver);
    421
    422MODULE_DESCRIPTION("axp20x Power Button");
    423MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
    424MODULE_LICENSE("GPL");