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