18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Power button driver for Intel MID platforms.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2010,2017 Intel Corp
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Hong Liu <hong.liu@intel.com>
88c2ecf20Sopenharmony_ci * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/input.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/mfd/intel_msic.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci#include <linux/pm_wakeirq.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <asm/cpu_device_id.h>
208c2ecf20Sopenharmony_ci#include <asm/intel-family.h>
218c2ecf20Sopenharmony_ci#include <asm/intel_scu_ipc.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define DRIVER_NAME "msic_power_btn"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define MSIC_PB_LEVEL	(1 << 3) /* 1 - release, 0 - press */
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/*
288c2ecf20Sopenharmony_ci * MSIC document ti_datasheet defines the 1st bit reg 0x21 is used to mask
298c2ecf20Sopenharmony_ci * power button interrupt
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_ci#define MSIC_PWRBTNM    (1 << 0)
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/* Intel Tangier */
348c2ecf20Sopenharmony_ci#define BCOVE_PB_LEVEL		(1 << 4)	/* 1 - release, 0 - press */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/* Basin Cove PMIC */
378c2ecf20Sopenharmony_ci#define BCOVE_PBIRQ		0x02
388c2ecf20Sopenharmony_ci#define BCOVE_IRQLVL1MSK	0x0c
398c2ecf20Sopenharmony_ci#define BCOVE_PBIRQMASK		0x0d
408c2ecf20Sopenharmony_ci#define BCOVE_PBSTATUS		0x27
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistruct mid_pb_ddata {
438c2ecf20Sopenharmony_ci	struct device *dev;
448c2ecf20Sopenharmony_ci	int irq;
458c2ecf20Sopenharmony_ci	struct input_dev *input;
468c2ecf20Sopenharmony_ci	unsigned short mirqlvl1_addr;
478c2ecf20Sopenharmony_ci	unsigned short pbstat_addr;
488c2ecf20Sopenharmony_ci	u8 pbstat_mask;
498c2ecf20Sopenharmony_ci	struct intel_scu_ipc_dev *scu;
508c2ecf20Sopenharmony_ci	int (*setup)(struct mid_pb_ddata *ddata);
518c2ecf20Sopenharmony_ci};
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic int mid_pbstat(struct mid_pb_ddata *ddata, int *value)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct input_dev *input = ddata->input;
568c2ecf20Sopenharmony_ci	int ret;
578c2ecf20Sopenharmony_ci	u8 pbstat;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	ret = intel_scu_ipc_dev_ioread8(ddata->scu, ddata->pbstat_addr,
608c2ecf20Sopenharmony_ci					&pbstat);
618c2ecf20Sopenharmony_ci	if (ret)
628c2ecf20Sopenharmony_ci		return ret;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	dev_dbg(input->dev.parent, "PB_INT status= %d\n", pbstat);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	*value = !(pbstat & ddata->pbstat_mask);
678c2ecf20Sopenharmony_ci	return 0;
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic int mid_irq_ack(struct mid_pb_ddata *ddata)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	return intel_scu_ipc_dev_update(ddata->scu, ddata->mirqlvl1_addr, 0,
738c2ecf20Sopenharmony_ci					MSIC_PWRBTNM);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int mrfld_setup(struct mid_pb_ddata *ddata)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	/* Unmask the PBIRQ and MPBIRQ on Tangier */
798c2ecf20Sopenharmony_ci	intel_scu_ipc_dev_update(ddata->scu, BCOVE_PBIRQ, 0, MSIC_PWRBTNM);
808c2ecf20Sopenharmony_ci	intel_scu_ipc_dev_update(ddata->scu, BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	return 0;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic irqreturn_t mid_pb_isr(int irq, void *dev_id)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct mid_pb_ddata *ddata = dev_id;
888c2ecf20Sopenharmony_ci	struct input_dev *input = ddata->input;
898c2ecf20Sopenharmony_ci	int value = 0;
908c2ecf20Sopenharmony_ci	int ret;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	ret = mid_pbstat(ddata, &value);
938c2ecf20Sopenharmony_ci	if (ret < 0) {
948c2ecf20Sopenharmony_ci		dev_err(input->dev.parent,
958c2ecf20Sopenharmony_ci			"Read error %d while reading MSIC_PB_STATUS\n", ret);
968c2ecf20Sopenharmony_ci	} else {
978c2ecf20Sopenharmony_ci		input_event(input, EV_KEY, KEY_POWER, value);
988c2ecf20Sopenharmony_ci		input_sync(input);
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	mid_irq_ack(ddata);
1028c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic const struct mid_pb_ddata mfld_ddata = {
1068c2ecf20Sopenharmony_ci	.mirqlvl1_addr	= INTEL_MSIC_IRQLVL1MSK,
1078c2ecf20Sopenharmony_ci	.pbstat_addr	= INTEL_MSIC_PBSTATUS,
1088c2ecf20Sopenharmony_ci	.pbstat_mask	= MSIC_PB_LEVEL,
1098c2ecf20Sopenharmony_ci};
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic const struct mid_pb_ddata mrfld_ddata = {
1128c2ecf20Sopenharmony_ci	.mirqlvl1_addr	= BCOVE_IRQLVL1MSK,
1138c2ecf20Sopenharmony_ci	.pbstat_addr	= BCOVE_PBSTATUS,
1148c2ecf20Sopenharmony_ci	.pbstat_mask	= BCOVE_PB_LEVEL,
1158c2ecf20Sopenharmony_ci	.setup	= mrfld_setup,
1168c2ecf20Sopenharmony_ci};
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic const struct x86_cpu_id mid_pb_cpu_ids[] = {
1198c2ecf20Sopenharmony_ci	X86_MATCH_INTEL_FAM6_MODEL(ATOM_SALTWELL_MID,	&mfld_ddata),
1208c2ecf20Sopenharmony_ci	X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT_MID,	&mrfld_ddata),
1218c2ecf20Sopenharmony_ci	{}
1228c2ecf20Sopenharmony_ci};
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic int mid_pb_probe(struct platform_device *pdev)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	const struct x86_cpu_id *id;
1278c2ecf20Sopenharmony_ci	struct mid_pb_ddata *ddata;
1288c2ecf20Sopenharmony_ci	struct input_dev *input;
1298c2ecf20Sopenharmony_ci	int irq = platform_get_irq(pdev, 0);
1308c2ecf20Sopenharmony_ci	int error;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	id = x86_match_cpu(mid_pb_cpu_ids);
1338c2ecf20Sopenharmony_ci	if (!id)
1348c2ecf20Sopenharmony_ci		return -ENODEV;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (irq < 0) {
1378c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq);
1388c2ecf20Sopenharmony_ci		return irq;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	input = devm_input_allocate_device(&pdev->dev);
1428c2ecf20Sopenharmony_ci	if (!input)
1438c2ecf20Sopenharmony_ci		return -ENOMEM;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	input->name = pdev->name;
1468c2ecf20Sopenharmony_ci	input->phys = "power-button/input0";
1478c2ecf20Sopenharmony_ci	input->id.bustype = BUS_HOST;
1488c2ecf20Sopenharmony_ci	input->dev.parent = &pdev->dev;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	input_set_capability(input, EV_KEY, KEY_POWER);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	ddata = devm_kmemdup(&pdev->dev, (void *)id->driver_data,
1538c2ecf20Sopenharmony_ci			     sizeof(*ddata), GFP_KERNEL);
1548c2ecf20Sopenharmony_ci	if (!ddata)
1558c2ecf20Sopenharmony_ci		return -ENOMEM;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	ddata->dev = &pdev->dev;
1588c2ecf20Sopenharmony_ci	ddata->irq = irq;
1598c2ecf20Sopenharmony_ci	ddata->input = input;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (ddata->setup) {
1628c2ecf20Sopenharmony_ci		error = ddata->setup(ddata);
1638c2ecf20Sopenharmony_ci		if (error)
1648c2ecf20Sopenharmony_ci			return error;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	ddata->scu = devm_intel_scu_ipc_dev_get(&pdev->dev);
1688c2ecf20Sopenharmony_ci	if (!ddata->scu)
1698c2ecf20Sopenharmony_ci		return -EPROBE_DEFER;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	error = devm_request_threaded_irq(&pdev->dev, irq, NULL, mid_pb_isr,
1728c2ecf20Sopenharmony_ci					  IRQF_ONESHOT, DRIVER_NAME, ddata);
1738c2ecf20Sopenharmony_ci	if (error) {
1748c2ecf20Sopenharmony_ci		dev_err(&pdev->dev,
1758c2ecf20Sopenharmony_ci			"Unable to request irq %d for MID power button\n", irq);
1768c2ecf20Sopenharmony_ci		return error;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	error = input_register_device(input);
1808c2ecf20Sopenharmony_ci	if (error) {
1818c2ecf20Sopenharmony_ci		dev_err(&pdev->dev,
1828c2ecf20Sopenharmony_ci			"Unable to register input dev, error %d\n", error);
1838c2ecf20Sopenharmony_ci		return error;
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, ddata);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/*
1898c2ecf20Sopenharmony_ci	 * SCU firmware might send power button interrupts to IA core before
1908c2ecf20Sopenharmony_ci	 * kernel boots and doesn't get EOI from IA core. The first bit of
1918c2ecf20Sopenharmony_ci	 * MSIC reg 0x21 is kept masked, and SCU firmware doesn't send new
1928c2ecf20Sopenharmony_ci	 * power interrupt to Android kernel. Unmask the bit when probing
1938c2ecf20Sopenharmony_ci	 * power button in kernel.
1948c2ecf20Sopenharmony_ci	 * There is a very narrow race between irq handler and power button
1958c2ecf20Sopenharmony_ci	 * initialization. The race happens rarely. So we needn't worry
1968c2ecf20Sopenharmony_ci	 * about it.
1978c2ecf20Sopenharmony_ci	 */
1988c2ecf20Sopenharmony_ci	error = mid_irq_ack(ddata);
1998c2ecf20Sopenharmony_ci	if (error) {
2008c2ecf20Sopenharmony_ci		dev_err(&pdev->dev,
2018c2ecf20Sopenharmony_ci			"Unable to clear power button interrupt, error: %d\n",
2028c2ecf20Sopenharmony_ci			error);
2038c2ecf20Sopenharmony_ci		return error;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	device_init_wakeup(&pdev->dev, true);
2078c2ecf20Sopenharmony_ci	dev_pm_set_wake_irq(&pdev->dev, irq);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	return 0;
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic int mid_pb_remove(struct platform_device *pdev)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	dev_pm_clear_wake_irq(&pdev->dev);
2158c2ecf20Sopenharmony_ci	device_init_wakeup(&pdev->dev, false);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	return 0;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic struct platform_driver mid_pb_driver = {
2218c2ecf20Sopenharmony_ci	.driver = {
2228c2ecf20Sopenharmony_ci		.name = DRIVER_NAME,
2238c2ecf20Sopenharmony_ci	},
2248c2ecf20Sopenharmony_ci	.probe	= mid_pb_probe,
2258c2ecf20Sopenharmony_ci	.remove	= mid_pb_remove,
2268c2ecf20Sopenharmony_ci};
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cimodule_platform_driver(mid_pb_driver);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hong Liu <hong.liu@intel.com>");
2318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel MID Power Button Driver");
2328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
2338c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" DRIVER_NAME);
234