162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright 2022 NXP. 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/device.h> 662306a36Sopenharmony_ci#include <linux/err.h> 762306a36Sopenharmony_ci#include <linux/init.h> 862306a36Sopenharmony_ci#include <linux/input.h> 962306a36Sopenharmony_ci#include <linux/interrupt.h> 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/jiffies.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/of_address.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/pm_wakeirq.h> 1962306a36Sopenharmony_ci#include <linux/regmap.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define BBNSM_CTRL 0x8 2262306a36Sopenharmony_ci#define BBNSM_INT_EN 0x10 2362306a36Sopenharmony_ci#define BBNSM_EVENTS 0x14 2462306a36Sopenharmony_ci#define BBNSM_PAD_CTRL 0x24 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define BBNSM_BTN_PRESSED BIT(7) 2762306a36Sopenharmony_ci#define BBNSM_PWR_ON BIT(6) 2862306a36Sopenharmony_ci#define BBNSM_BTN_OFF BIT(5) 2962306a36Sopenharmony_ci#define BBNSM_EMG_OFF BIT(4) 3062306a36Sopenharmony_ci#define BBNSM_PWRKEY_EVENTS (BBNSM_PWR_ON | BBNSM_BTN_OFF | BBNSM_EMG_OFF) 3162306a36Sopenharmony_ci#define BBNSM_DP_EN BIT(24) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define DEBOUNCE_TIME 30 3462306a36Sopenharmony_ci#define REPEAT_INTERVAL 60 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct bbnsm_pwrkey { 3762306a36Sopenharmony_ci struct regmap *regmap; 3862306a36Sopenharmony_ci int irq; 3962306a36Sopenharmony_ci int keycode; 4062306a36Sopenharmony_ci int keystate; /* 1:pressed */ 4162306a36Sopenharmony_ci struct timer_list check_timer; 4262306a36Sopenharmony_ci struct input_dev *input; 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic void bbnsm_pwrkey_check_for_events(struct timer_list *t) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci struct bbnsm_pwrkey *bbnsm = from_timer(bbnsm, t, check_timer); 4862306a36Sopenharmony_ci struct input_dev *input = bbnsm->input; 4962306a36Sopenharmony_ci u32 state; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci regmap_read(bbnsm->regmap, BBNSM_EVENTS, &state); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci state = state & BBNSM_BTN_PRESSED ? 1 : 0; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci /* only report new event if status changed */ 5662306a36Sopenharmony_ci if (state ^ bbnsm->keystate) { 5762306a36Sopenharmony_ci bbnsm->keystate = state; 5862306a36Sopenharmony_ci input_event(input, EV_KEY, bbnsm->keycode, state); 5962306a36Sopenharmony_ci input_sync(input); 6062306a36Sopenharmony_ci pm_relax(bbnsm->input->dev.parent); 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* repeat check if pressed long */ 6462306a36Sopenharmony_ci if (state) 6562306a36Sopenharmony_ci mod_timer(&bbnsm->check_timer, 6662306a36Sopenharmony_ci jiffies + msecs_to_jiffies(REPEAT_INTERVAL)); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic irqreturn_t bbnsm_pwrkey_interrupt(int irq, void *dev_id) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct platform_device *pdev = dev_id; 7262306a36Sopenharmony_ci struct bbnsm_pwrkey *bbnsm = platform_get_drvdata(pdev); 7362306a36Sopenharmony_ci u32 event; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci regmap_read(bbnsm->regmap, BBNSM_EVENTS, &event); 7662306a36Sopenharmony_ci if (!(event & BBNSM_BTN_OFF)) 7762306a36Sopenharmony_ci return IRQ_NONE; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci pm_wakeup_event(bbnsm->input->dev.parent, 0); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci mod_timer(&bbnsm->check_timer, 8262306a36Sopenharmony_ci jiffies + msecs_to_jiffies(DEBOUNCE_TIME)); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* clear PWR OFF */ 8562306a36Sopenharmony_ci regmap_write(bbnsm->regmap, BBNSM_EVENTS, BBNSM_BTN_OFF); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci return IRQ_HANDLED; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic void bbnsm_pwrkey_act(void *pdata) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct bbnsm_pwrkey *bbnsm = pdata; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci timer_shutdown_sync(&bbnsm->check_timer); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic int bbnsm_pwrkey_probe(struct platform_device *pdev) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct bbnsm_pwrkey *bbnsm; 10062306a36Sopenharmony_ci struct input_dev *input; 10162306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 10262306a36Sopenharmony_ci int error; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci bbnsm = devm_kzalloc(&pdev->dev, sizeof(*bbnsm), GFP_KERNEL); 10562306a36Sopenharmony_ci if (!bbnsm) 10662306a36Sopenharmony_ci return -ENOMEM; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci bbnsm->regmap = syscon_node_to_regmap(np->parent); 10962306a36Sopenharmony_ci if (IS_ERR(bbnsm->regmap)) { 11062306a36Sopenharmony_ci dev_err(&pdev->dev, "bbnsm pwerkey get regmap failed\n"); 11162306a36Sopenharmony_ci return PTR_ERR(bbnsm->regmap); 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (device_property_read_u32(&pdev->dev, "linux,code", 11562306a36Sopenharmony_ci &bbnsm->keycode)) { 11662306a36Sopenharmony_ci bbnsm->keycode = KEY_POWER; 11762306a36Sopenharmony_ci dev_warn(&pdev->dev, "key code is not specified, using default KEY_POWER\n"); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci bbnsm->irq = platform_get_irq(pdev, 0); 12162306a36Sopenharmony_ci if (bbnsm->irq < 0) 12262306a36Sopenharmony_ci return -EINVAL; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* config the BBNSM power related register */ 12562306a36Sopenharmony_ci regmap_update_bits(bbnsm->regmap, BBNSM_CTRL, BBNSM_DP_EN, BBNSM_DP_EN); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* clear the unexpected interrupt before driver ready */ 12862306a36Sopenharmony_ci regmap_write_bits(bbnsm->regmap, BBNSM_EVENTS, BBNSM_PWRKEY_EVENTS, 12962306a36Sopenharmony_ci BBNSM_PWRKEY_EVENTS); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci timer_setup(&bbnsm->check_timer, bbnsm_pwrkey_check_for_events, 0); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci input = devm_input_allocate_device(&pdev->dev); 13462306a36Sopenharmony_ci if (!input) { 13562306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to allocate the input device\n"); 13662306a36Sopenharmony_ci return -ENOMEM; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci input->name = pdev->name; 14062306a36Sopenharmony_ci input->phys = "bbnsm-pwrkey/input0"; 14162306a36Sopenharmony_ci input->id.bustype = BUS_HOST; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci input_set_capability(input, EV_KEY, bbnsm->keycode); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* input customer action to cancel release timer */ 14662306a36Sopenharmony_ci error = devm_add_action(&pdev->dev, bbnsm_pwrkey_act, bbnsm); 14762306a36Sopenharmony_ci if (error) { 14862306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register remove action\n"); 14962306a36Sopenharmony_ci return error; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci bbnsm->input = input; 15362306a36Sopenharmony_ci platform_set_drvdata(pdev, bbnsm); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci error = devm_request_irq(&pdev->dev, bbnsm->irq, bbnsm_pwrkey_interrupt, 15662306a36Sopenharmony_ci IRQF_SHARED, pdev->name, pdev); 15762306a36Sopenharmony_ci if (error) { 15862306a36Sopenharmony_ci dev_err(&pdev->dev, "interrupt not available.\n"); 15962306a36Sopenharmony_ci return error; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci error = input_register_device(input); 16362306a36Sopenharmony_ci if (error) { 16462306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register input device\n"); 16562306a36Sopenharmony_ci return error; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci device_init_wakeup(&pdev->dev, true); 16962306a36Sopenharmony_ci error = dev_pm_set_wake_irq(&pdev->dev, bbnsm->irq); 17062306a36Sopenharmony_ci if (error) 17162306a36Sopenharmony_ci dev_warn(&pdev->dev, "irq wake enable failed.\n"); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci return 0; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic const struct of_device_id bbnsm_pwrkey_ids[] = { 17762306a36Sopenharmony_ci { .compatible = "nxp,imx93-bbnsm-pwrkey" }, 17862306a36Sopenharmony_ci { /* sentinel */ } 17962306a36Sopenharmony_ci}; 18062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, bbnsm_pwrkey_ids); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic struct platform_driver bbnsm_pwrkey_driver = { 18362306a36Sopenharmony_ci .driver = { 18462306a36Sopenharmony_ci .name = "bbnsm_pwrkey", 18562306a36Sopenharmony_ci .of_match_table = bbnsm_pwrkey_ids, 18662306a36Sopenharmony_ci }, 18762306a36Sopenharmony_ci .probe = bbnsm_pwrkey_probe, 18862306a36Sopenharmony_ci}; 18962306a36Sopenharmony_cimodule_platform_driver(bbnsm_pwrkey_driver); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ciMODULE_AUTHOR("Jacky Bai <ping.bai@nxp.com>"); 19262306a36Sopenharmony_ciMODULE_DESCRIPTION("NXP bbnsm power key Driver"); 19362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 194