Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright 2019 NXP.
0004  */
0005 
0006 #include <linux/err.h>
0007 #include <linux/device.h>
0008 #include <linux/firmware/imx/sci.h>
0009 #include <linux/init.h>
0010 #include <linux/input.h>
0011 #include <linux/interrupt.h>
0012 #include <linux/jiffies.h>
0013 #include <linux/kernel.h>
0014 #include <linux/module.h>
0015 #include <linux/of.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/property.h>
0018 
0019 #define DEBOUNCE_TIME               30
0020 #define REPEAT_INTERVAL             60
0021 
0022 #define SC_IRQ_BUTTON               1
0023 #define SC_IRQ_GROUP_WAKE           3
0024 
0025 #define IMX_SC_MISC_FUNC_GET_BUTTON_STATUS  18
0026 
0027 struct imx_key_drv_data {
0028     u32 keycode;
0029     bool keystate;  /* true: pressed, false: released */
0030     struct delayed_work check_work;
0031     struct input_dev *input;
0032     struct imx_sc_ipc *key_ipc_handle;
0033     struct notifier_block key_notifier;
0034 };
0035 
0036 struct imx_sc_msg_key {
0037     struct imx_sc_rpc_msg hdr;
0038     u32 state;
0039 };
0040 
0041 static int imx_sc_key_notify(struct notifier_block *nb,
0042                  unsigned long event, void *group)
0043 {
0044     struct imx_key_drv_data *priv =
0045                  container_of(nb,
0046                           struct imx_key_drv_data,
0047                           key_notifier);
0048 
0049     if ((event & SC_IRQ_BUTTON) && (*(u8 *)group == SC_IRQ_GROUP_WAKE)) {
0050         schedule_delayed_work(&priv->check_work,
0051                       msecs_to_jiffies(DEBOUNCE_TIME));
0052         pm_wakeup_event(priv->input->dev.parent, 0);
0053     }
0054 
0055     return 0;
0056 }
0057 
0058 static void imx_sc_check_for_events(struct work_struct *work)
0059 {
0060     struct imx_key_drv_data *priv =
0061                  container_of(work,
0062                           struct imx_key_drv_data,
0063                           check_work.work);
0064     struct input_dev *input = priv->input;
0065     struct imx_sc_msg_key msg;
0066     struct imx_sc_rpc_msg *hdr = &msg.hdr;
0067     bool state;
0068     int error;
0069 
0070     hdr->ver = IMX_SC_RPC_VERSION;
0071     hdr->svc = IMX_SC_RPC_SVC_MISC;
0072     hdr->func = IMX_SC_MISC_FUNC_GET_BUTTON_STATUS;
0073     hdr->size = 1;
0074 
0075     error = imx_scu_call_rpc(priv->key_ipc_handle, &msg, true);
0076     if (error) {
0077         dev_err(&input->dev, "read imx sc key failed, error %d\n", error);
0078         return;
0079     }
0080 
0081     /*
0082      * The response data from SCU firmware is 4 bytes,
0083      * but ONLY the first byte is the key state, other
0084      * 3 bytes could be some dirty data, so we should
0085      * ONLY take the first byte as key state.
0086      */
0087     state = (bool)(msg.state & 0xff);
0088 
0089     if (state ^ priv->keystate) {
0090         priv->keystate = state;
0091         input_event(input, EV_KEY, priv->keycode, state);
0092         input_sync(input);
0093         if (!priv->keystate)
0094             pm_relax(priv->input->dev.parent);
0095     }
0096 
0097     if (state)
0098         schedule_delayed_work(&priv->check_work,
0099                       msecs_to_jiffies(REPEAT_INTERVAL));
0100 }
0101 
0102 static void imx_sc_key_action(void *data)
0103 {
0104     struct imx_key_drv_data *priv = data;
0105 
0106     imx_scu_irq_group_enable(SC_IRQ_GROUP_WAKE, SC_IRQ_BUTTON, false);
0107     imx_scu_irq_unregister_notifier(&priv->key_notifier);
0108     cancel_delayed_work_sync(&priv->check_work);
0109 }
0110 
0111 static int imx_sc_key_probe(struct platform_device *pdev)
0112 {
0113     struct imx_key_drv_data *priv;
0114     struct input_dev *input;
0115     int error;
0116 
0117     priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
0118     if (!priv)
0119         return -ENOMEM;
0120 
0121     error = imx_scu_get_handle(&priv->key_ipc_handle);
0122     if (error)
0123         return error;
0124 
0125     if (device_property_read_u32(&pdev->dev, "linux,keycodes",
0126                      &priv->keycode)) {
0127         dev_err(&pdev->dev, "missing linux,keycodes property\n");
0128         return -EINVAL;
0129     }
0130 
0131     INIT_DELAYED_WORK(&priv->check_work, imx_sc_check_for_events);
0132 
0133     input = devm_input_allocate_device(&pdev->dev);
0134     if (!input) {
0135         dev_err(&pdev->dev, "failed to allocate the input device\n");
0136         return -ENOMEM;
0137     }
0138 
0139     input->name = pdev->name;
0140     input->phys = "imx-sc-key/input0";
0141     input->id.bustype = BUS_HOST;
0142 
0143     input_set_capability(input, EV_KEY, priv->keycode);
0144 
0145     error = input_register_device(input);
0146     if (error) {
0147         dev_err(&pdev->dev, "failed to register input device\n");
0148         return error;
0149     }
0150 
0151     priv->input = input;
0152     platform_set_drvdata(pdev, priv);
0153 
0154     error = imx_scu_irq_group_enable(SC_IRQ_GROUP_WAKE, SC_IRQ_BUTTON,
0155                      true);
0156     if (error) {
0157         dev_err(&pdev->dev, "failed to enable scu group irq\n");
0158         return error;
0159     }
0160 
0161     error = devm_add_action_or_reset(&pdev->dev, imx_sc_key_action, &priv);
0162     if (error)
0163         return error;
0164 
0165     priv->key_notifier.notifier_call = imx_sc_key_notify;
0166     error = imx_scu_irq_register_notifier(&priv->key_notifier);
0167     if (error)
0168         dev_err(&pdev->dev, "failed to register scu notifier\n");
0169 
0170     return error;
0171 }
0172 
0173 static const struct of_device_id imx_sc_key_ids[] = {
0174     { .compatible = "fsl,imx-sc-key" },
0175     { /* sentinel */ }
0176 };
0177 MODULE_DEVICE_TABLE(of, imx_sc_key_ids);
0178 
0179 static struct platform_driver imx_sc_key_driver = {
0180     .driver = {
0181         .name = "imx-sc-key",
0182         .of_match_table = imx_sc_key_ids,
0183     },
0184     .probe = imx_sc_key_probe,
0185 };
0186 module_platform_driver(imx_sc_key_driver);
0187 
0188 MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
0189 MODULE_DESCRIPTION("i.MX System Controller Key Driver");
0190 MODULE_LICENSE("GPL v2");