18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Power-button driver for Basin Cove PMIC 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2019, Intel Corporation. 68c2ecf20Sopenharmony_ci * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/input.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/device.h> 128c2ecf20Sopenharmony_ci#include <linux/mfd/intel_soc_pmic.h> 138c2ecf20Sopenharmony_ci#include <linux/mfd/intel_soc_pmic_mrfld.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#define BCOVE_PBSTATUS 0x27 208c2ecf20Sopenharmony_ci#define BCOVE_PBSTATUS_PBLVL BIT(4) /* 1 - release, 0 - press */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic irqreturn_t mrfld_pwrbtn_interrupt(int irq, void *dev_id) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci struct input_dev *input = dev_id; 258c2ecf20Sopenharmony_ci struct device *dev = input->dev.parent; 268c2ecf20Sopenharmony_ci struct regmap *regmap = dev_get_drvdata(dev); 278c2ecf20Sopenharmony_ci unsigned int state; 288c2ecf20Sopenharmony_ci int ret; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci ret = regmap_read(regmap, BCOVE_PBSTATUS, &state); 318c2ecf20Sopenharmony_ci if (ret) 328c2ecf20Sopenharmony_ci return IRQ_NONE; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci dev_dbg(dev, "PBSTATUS=0x%x\n", state); 358c2ecf20Sopenharmony_ci input_report_key(input, KEY_POWER, !(state & BCOVE_PBSTATUS_PBLVL)); 368c2ecf20Sopenharmony_ci input_sync(input); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci regmap_update_bits(regmap, BCOVE_MIRQLVL1, BCOVE_LVL1_PWRBTN, 0); 398c2ecf20Sopenharmony_ci return IRQ_HANDLED; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int mrfld_pwrbtn_probe(struct platform_device *pdev) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 458c2ecf20Sopenharmony_ci struct intel_soc_pmic *pmic = dev_get_drvdata(dev->parent); 468c2ecf20Sopenharmony_ci struct input_dev *input; 478c2ecf20Sopenharmony_ci int irq, ret; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 508c2ecf20Sopenharmony_ci if (irq < 0) 518c2ecf20Sopenharmony_ci return irq; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci input = devm_input_allocate_device(dev); 548c2ecf20Sopenharmony_ci if (!input) 558c2ecf20Sopenharmony_ci return -ENOMEM; 568c2ecf20Sopenharmony_ci input->name = pdev->name; 578c2ecf20Sopenharmony_ci input->phys = "power-button/input0"; 588c2ecf20Sopenharmony_ci input->id.bustype = BUS_HOST; 598c2ecf20Sopenharmony_ci input->dev.parent = dev; 608c2ecf20Sopenharmony_ci input_set_capability(input, EV_KEY, KEY_POWER); 618c2ecf20Sopenharmony_ci ret = input_register_device(input); 628c2ecf20Sopenharmony_ci if (ret) 638c2ecf20Sopenharmony_ci return ret; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci dev_set_drvdata(dev, pmic->regmap); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(dev, irq, NULL, mrfld_pwrbtn_interrupt, 688c2ecf20Sopenharmony_ci IRQF_ONESHOT | IRQF_SHARED, pdev->name, 698c2ecf20Sopenharmony_ci input); 708c2ecf20Sopenharmony_ci if (ret) 718c2ecf20Sopenharmony_ci return ret; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci regmap_update_bits(pmic->regmap, BCOVE_MIRQLVL1, BCOVE_LVL1_PWRBTN, 0); 748c2ecf20Sopenharmony_ci regmap_update_bits(pmic->regmap, BCOVE_MPBIRQ, BCOVE_PBIRQ_PBTN, 0); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci device_init_wakeup(dev, true); 778c2ecf20Sopenharmony_ci dev_pm_set_wake_irq(dev, irq); 788c2ecf20Sopenharmony_ci return 0; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic int mrfld_pwrbtn_remove(struct platform_device *pdev) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci dev_pm_clear_wake_irq(dev); 868c2ecf20Sopenharmony_ci device_init_wakeup(dev, false); 878c2ecf20Sopenharmony_ci return 0; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic const struct platform_device_id mrfld_pwrbtn_id_table[] = { 918c2ecf20Sopenharmony_ci { .name = "mrfld_bcove_pwrbtn" }, 928c2ecf20Sopenharmony_ci {} 938c2ecf20Sopenharmony_ci}; 948c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, mrfld_pwrbtn_id_table); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic struct platform_driver mrfld_pwrbtn_driver = { 978c2ecf20Sopenharmony_ci .driver = { 988c2ecf20Sopenharmony_ci .name = "mrfld_bcove_pwrbtn", 998c2ecf20Sopenharmony_ci }, 1008c2ecf20Sopenharmony_ci .probe = mrfld_pwrbtn_probe, 1018c2ecf20Sopenharmony_ci .remove = mrfld_pwrbtn_remove, 1028c2ecf20Sopenharmony_ci .id_table = mrfld_pwrbtn_id_table, 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_cimodule_platform_driver(mrfld_pwrbtn_driver); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Power-button driver for Basin Cove PMIC"); 1078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 108