root/drivers/input/misc/sirfsoc-onkey.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sirfsoc_pwrc_is_on_key_down
  2. sirfsoc_pwrc_report_event
  3. sirfsoc_pwrc_isr
  4. sirfsoc_pwrc_toggle_interrupts
  5. sirfsoc_pwrc_open
  6. sirfsoc_pwrc_close
  7. sirfsoc_pwrc_probe
  8. sirfsoc_pwrc_resume

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Power key driver for SiRF PrimaII
   4  *
   5  * Copyright (c) 2013 - 2014 Cambridge Silicon Radio Limited, a CSR plc group
   6  * company.
   7  */
   8 
   9 #include <linux/module.h>
  10 #include <linux/interrupt.h>
  11 #include <linux/delay.h>
  12 #include <linux/platform_device.h>
  13 #include <linux/input.h>
  14 #include <linux/rtc/sirfsoc_rtciobrg.h>
  15 #include <linux/of.h>
  16 #include <linux/workqueue.h>
  17 
  18 struct sirfsoc_pwrc_drvdata {
  19         u32                     pwrc_base;
  20         struct input_dev        *input;
  21         struct delayed_work     work;
  22 };
  23 
  24 #define PWRC_ON_KEY_BIT                 (1 << 0)
  25 
  26 #define PWRC_INT_STATUS                 0xc
  27 #define PWRC_INT_MASK                   0x10
  28 #define PWRC_PIN_STATUS                 0x14
  29 #define PWRC_KEY_DETECT_UP_TIME         20      /* ms*/
  30 
  31 static int sirfsoc_pwrc_is_on_key_down(struct sirfsoc_pwrc_drvdata *pwrcdrv)
  32 {
  33         u32 state = sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base +
  34                                                         PWRC_PIN_STATUS);
  35         return !(state & PWRC_ON_KEY_BIT); /* ON_KEY is active low */
  36 }
  37 
  38 static void sirfsoc_pwrc_report_event(struct work_struct *work)
  39 {
  40         struct sirfsoc_pwrc_drvdata *pwrcdrv =
  41                 container_of(work, struct sirfsoc_pwrc_drvdata, work.work);
  42 
  43         if (sirfsoc_pwrc_is_on_key_down(pwrcdrv)) {
  44                 schedule_delayed_work(&pwrcdrv->work,
  45                         msecs_to_jiffies(PWRC_KEY_DETECT_UP_TIME));
  46         } else {
  47                 input_event(pwrcdrv->input, EV_KEY, KEY_POWER, 0);
  48                 input_sync(pwrcdrv->input);
  49         }
  50 }
  51 
  52 static irqreturn_t sirfsoc_pwrc_isr(int irq, void *dev_id)
  53 {
  54         struct sirfsoc_pwrc_drvdata *pwrcdrv = dev_id;
  55         u32 int_status;
  56 
  57         int_status = sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base +
  58                                                         PWRC_INT_STATUS);
  59         sirfsoc_rtc_iobrg_writel(int_status & ~PWRC_ON_KEY_BIT,
  60                                  pwrcdrv->pwrc_base + PWRC_INT_STATUS);
  61 
  62         input_event(pwrcdrv->input, EV_KEY, KEY_POWER, 1);
  63         input_sync(pwrcdrv->input);
  64         schedule_delayed_work(&pwrcdrv->work,
  65                               msecs_to_jiffies(PWRC_KEY_DETECT_UP_TIME));
  66 
  67         return IRQ_HANDLED;
  68 }
  69 
  70 static void sirfsoc_pwrc_toggle_interrupts(struct sirfsoc_pwrc_drvdata *pwrcdrv,
  71                                            bool enable)
  72 {
  73         u32 int_mask;
  74 
  75         int_mask = sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base + PWRC_INT_MASK);
  76         if (enable)
  77                 int_mask |= PWRC_ON_KEY_BIT;
  78         else
  79                 int_mask &= ~PWRC_ON_KEY_BIT;
  80         sirfsoc_rtc_iobrg_writel(int_mask, pwrcdrv->pwrc_base + PWRC_INT_MASK);
  81 }
  82 
  83 static int sirfsoc_pwrc_open(struct input_dev *input)
  84 {
  85         struct sirfsoc_pwrc_drvdata *pwrcdrv = input_get_drvdata(input);
  86 
  87         sirfsoc_pwrc_toggle_interrupts(pwrcdrv, true);
  88 
  89         return 0;
  90 }
  91 
  92 static void sirfsoc_pwrc_close(struct input_dev *input)
  93 {
  94         struct sirfsoc_pwrc_drvdata *pwrcdrv = input_get_drvdata(input);
  95 
  96         sirfsoc_pwrc_toggle_interrupts(pwrcdrv, false);
  97         cancel_delayed_work_sync(&pwrcdrv->work);
  98 }
  99 
 100 static const struct of_device_id sirfsoc_pwrc_of_match[] = {
 101         { .compatible = "sirf,prima2-pwrc" },
 102         {},
 103 };
 104 MODULE_DEVICE_TABLE(of, sirfsoc_pwrc_of_match);
 105 
 106 static int sirfsoc_pwrc_probe(struct platform_device *pdev)
 107 {
 108         struct device_node *np = pdev->dev.of_node;
 109         struct sirfsoc_pwrc_drvdata *pwrcdrv;
 110         int irq;
 111         int error;
 112 
 113         pwrcdrv = devm_kzalloc(&pdev->dev, sizeof(struct sirfsoc_pwrc_drvdata),
 114                                GFP_KERNEL);
 115         if (!pwrcdrv) {
 116                 dev_info(&pdev->dev, "Not enough memory for the device data\n");
 117                 return -ENOMEM;
 118         }
 119 
 120         /*
 121          * We can't use of_iomap because pwrc is not mapped in memory,
 122          * the so-called base address is only offset in rtciobrg
 123          */
 124         error = of_property_read_u32(np, "reg", &pwrcdrv->pwrc_base);
 125         if (error) {
 126                 dev_err(&pdev->dev,
 127                         "unable to find base address of pwrc node in dtb\n");
 128                 return error;
 129         }
 130 
 131         pwrcdrv->input = devm_input_allocate_device(&pdev->dev);
 132         if (!pwrcdrv->input)
 133                 return -ENOMEM;
 134 
 135         pwrcdrv->input->name = "sirfsoc pwrckey";
 136         pwrcdrv->input->phys = "pwrc/input0";
 137         pwrcdrv->input->evbit[0] = BIT_MASK(EV_KEY);
 138         input_set_capability(pwrcdrv->input, EV_KEY, KEY_POWER);
 139 
 140         INIT_DELAYED_WORK(&pwrcdrv->work, sirfsoc_pwrc_report_event);
 141 
 142         pwrcdrv->input->open = sirfsoc_pwrc_open;
 143         pwrcdrv->input->close = sirfsoc_pwrc_close;
 144 
 145         input_set_drvdata(pwrcdrv->input, pwrcdrv);
 146 
 147         /* Make sure the device is quiesced */
 148         sirfsoc_pwrc_toggle_interrupts(pwrcdrv, false);
 149 
 150         irq = platform_get_irq(pdev, 0);
 151         error = devm_request_irq(&pdev->dev, irq,
 152                                  sirfsoc_pwrc_isr, 0,
 153                                  "sirfsoc_pwrc_int", pwrcdrv);
 154         if (error) {
 155                 dev_err(&pdev->dev, "unable to claim irq %d, error: %d\n",
 156                         irq, error);
 157                 return error;
 158         }
 159 
 160         error = input_register_device(pwrcdrv->input);
 161         if (error) {
 162                 dev_err(&pdev->dev,
 163                         "unable to register input device, error: %d\n",
 164                         error);
 165                 return error;
 166         }
 167 
 168         dev_set_drvdata(&pdev->dev, pwrcdrv);
 169         device_init_wakeup(&pdev->dev, 1);
 170 
 171         return 0;
 172 }
 173 
 174 static int __maybe_unused sirfsoc_pwrc_resume(struct device *dev)
 175 {
 176         struct sirfsoc_pwrc_drvdata *pwrcdrv = dev_get_drvdata(dev);
 177         struct input_dev *input = pwrcdrv->input;
 178 
 179         /*
 180          * Do not mask pwrc interrupt as we want pwrc work as a wakeup source
 181          * if users touch X_ONKEY_B, see arch/arm/mach-prima2/pm.c
 182          */
 183         mutex_lock(&input->mutex);
 184         if (input->users)
 185                 sirfsoc_pwrc_toggle_interrupts(pwrcdrv, true);
 186         mutex_unlock(&input->mutex);
 187 
 188         return 0;
 189 }
 190 
 191 static SIMPLE_DEV_PM_OPS(sirfsoc_pwrc_pm_ops, NULL, sirfsoc_pwrc_resume);
 192 
 193 static struct platform_driver sirfsoc_pwrc_driver = {
 194         .probe          = sirfsoc_pwrc_probe,
 195         .driver         = {
 196                 .name   = "sirfsoc-pwrc",
 197                 .pm     = &sirfsoc_pwrc_pm_ops,
 198                 .of_match_table = sirfsoc_pwrc_of_match,
 199         }
 200 };
 201 
 202 module_platform_driver(sirfsoc_pwrc_driver);
 203 
 204 MODULE_LICENSE("GPL v2");
 205 MODULE_AUTHOR("Binghua Duan <Binghua.Duan@csr.com>, Xianglong Du <Xianglong.Du@csr.com>");
 206 MODULE_DESCRIPTION("CSR Prima2 PWRC Driver");
 207 MODULE_ALIAS("platform:sirfsoc-pwrc");

/* [<][>][^][v][top][bottom][index][help] */