root/drivers/input/misc/pf1550-onkey.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Driver for the PF1550 ONKEY
 * Copyright (C) 2016 Freescale Semiconductor, Inc. All Rights Reserved.
 *
 * Portions Copyright (c) 2025 Savoir-faire Linux Inc.
 * Samuel Kayode <samuel.kayode@savoirfairelinux.com>
 */

#include <linux/err.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mfd/pf1550.h>
#include <linux/platform_device.h>

#define PF1550_ONKEY_IRQ_NR     6

struct onkey_drv_data {
        struct device *dev;
        const struct pf1550_ddata *pf1550;
        bool wakeup;
        struct input_dev *input;
};

static irqreturn_t pf1550_onkey_irq_handler(int irq, void *data)
{
        struct onkey_drv_data *onkey = data;
        struct platform_device *pdev = to_platform_device(onkey->dev);
        int i, state, irq_type = -1;

        for (i = 0; i < PF1550_ONKEY_IRQ_NR; i++)
                if (irq == platform_get_irq(pdev, i))
                        irq_type = i;

        switch (irq_type) {
        case PF1550_ONKEY_IRQ_PUSHI:
                state = 0;
                break;
        case PF1550_ONKEY_IRQ_1SI:
        case PF1550_ONKEY_IRQ_2SI:
        case PF1550_ONKEY_IRQ_3SI:
        case PF1550_ONKEY_IRQ_4SI:
        case PF1550_ONKEY_IRQ_8SI:
                state = 1;
                break;
        default:
                dev_err(onkey->dev, "onkey interrupt: irq %d occurred\n",
                        irq_type);
                return IRQ_HANDLED;
        }

        input_event(onkey->input, EV_KEY, KEY_POWER, state);
        input_sync(onkey->input);

        return IRQ_HANDLED;
}

static int pf1550_onkey_probe(struct platform_device *pdev)
{
        struct onkey_drv_data *onkey;
        struct input_dev *input;
        bool key_power = false;
        int i, irq, error;

        onkey = devm_kzalloc(&pdev->dev, sizeof(*onkey), GFP_KERNEL);
        if (!onkey)
                return -ENOMEM;

        onkey->dev = &pdev->dev;

        onkey->pf1550 = dev_get_drvdata(pdev->dev.parent);
        if (!onkey->pf1550->regmap)
                return dev_err_probe(&pdev->dev, -ENODEV,
                                     "failed to get regmap\n");

        onkey->wakeup = device_property_read_bool(pdev->dev.parent,
                                                  "wakeup-source");

        if (device_property_read_bool(pdev->dev.parent,
                                      "nxp,disable-key-power")) {
                error = regmap_clear_bits(onkey->pf1550->regmap,
                                          PF1550_PMIC_REG_PWRCTRL1,
                                          PF1550_ONKEY_RST_EN);
                if (error)
                        return dev_err_probe(&pdev->dev, error,
                                             "failed: disable turn system off");
        } else {
                key_power = true;
        }

        input = devm_input_allocate_device(&pdev->dev);
        if (!input)
                return dev_err_probe(&pdev->dev, -ENOMEM,
                                     "failed to allocate the input device\n");

        input->name = pdev->name;
        input->phys = "pf1550-onkey/input0";
        input->id.bustype = BUS_HOST;

        if (key_power)
                input_set_capability(input, EV_KEY, KEY_POWER);

        onkey->input = input;
        platform_set_drvdata(pdev, onkey);

        for (i = 0; i < PF1550_ONKEY_IRQ_NR; i++) {
                irq = platform_get_irq(pdev, i);
                if (irq < 0)
                        return irq;

                error = devm_request_threaded_irq(&pdev->dev, irq, NULL,
                                                  pf1550_onkey_irq_handler,
                                                  IRQF_NO_SUSPEND,
                                                  "pf1550-onkey", onkey);
                if (error)
                        return dev_err_probe(&pdev->dev, error,
                                             "failed: irq request (IRQ: %d)\n",
                                             i);
        }

        error = input_register_device(input);
        if (error)
                return dev_err_probe(&pdev->dev, error,
                                     "failed to register input device\n");

        device_init_wakeup(&pdev->dev, onkey->wakeup);

        return 0;
}

static int pf1550_onkey_suspend(struct device *dev)
{
        struct platform_device *pdev = to_platform_device(dev);
        struct onkey_drv_data *onkey = platform_get_drvdata(pdev);
        int i, irq;

        if (!device_may_wakeup(&pdev->dev))
                regmap_write(onkey->pf1550->regmap,
                             PF1550_PMIC_REG_ONKEY_INT_MASK0,
                             ONKEY_IRQ_PUSHI | ONKEY_IRQ_1SI | ONKEY_IRQ_2SI |
                             ONKEY_IRQ_3SI | ONKEY_IRQ_4SI | ONKEY_IRQ_8SI);
        else
                for (i = 0; i < PF1550_ONKEY_IRQ_NR; i++) {
                        irq = platform_get_irq(pdev, i);
                        if (irq > 0)
                                enable_irq_wake(irq);
                }

        return 0;
}

static int pf1550_onkey_resume(struct device *dev)
{
        struct platform_device *pdev = to_platform_device(dev);
        struct onkey_drv_data *onkey = platform_get_drvdata(pdev);
        int i, irq;

        if (!device_may_wakeup(&pdev->dev))
                regmap_write(onkey->pf1550->regmap,
                             PF1550_PMIC_REG_ONKEY_INT_MASK0,
                             ~((u8)(ONKEY_IRQ_PUSHI | ONKEY_IRQ_1SI |
                             ONKEY_IRQ_2SI | ONKEY_IRQ_3SI | ONKEY_IRQ_4SI |
                             ONKEY_IRQ_8SI)));
        else
                for (i = 0; i < PF1550_ONKEY_IRQ_NR; i++) {
                        irq = platform_get_irq(pdev, i);
                        if (irq > 0)
                                disable_irq_wake(irq);
                }

        return 0;
}

static DEFINE_SIMPLE_DEV_PM_OPS(pf1550_onkey_pm_ops, pf1550_onkey_suspend,
                         pf1550_onkey_resume);

static const struct platform_device_id pf1550_onkey_id[] = {
        { "pf1550-onkey", },
        { /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, pf1550_onkey_id);

static struct platform_driver pf1550_onkey_driver = {
        .driver = {
                .name = "pf1550-onkey",
                .pm   = pm_sleep_ptr(&pf1550_onkey_pm_ops),
        },
        .probe = pf1550_onkey_probe,
        .id_table = pf1550_onkey_id,
};
module_platform_driver(pf1550_onkey_driver);

MODULE_AUTHOR("Freescale Semiconductor");
MODULE_DESCRIPTION("PF1550 onkey Driver");
MODULE_LICENSE("GPL");