root/drivers/extcon/extcon-adc-jack.c

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

DEFINITIONS

This source file includes following definitions.
  1. adc_jack_handler
  2. adc_jack_irq_thread
  3. adc_jack_probe
  4. adc_jack_remove
  5. adc_jack_suspend
  6. adc_jack_resume

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * drivers/extcon/extcon-adc-jack.c
   4  *
   5  * Analog Jack extcon driver with ADC-based detection capability.
   6  *
   7  * Copyright (C) 2016 Samsung Electronics
   8  * Chanwoo Choi <cw00.choi@samsung.com>
   9  *
  10  * Copyright (C) 2012 Samsung Electronics
  11  * MyungJoo Ham <myungjoo.ham@samsung.com>
  12  *
  13  * Modified for calling to IIO to get adc by <anish.singh@samsung.com>
  14  */
  15 
  16 #include <linux/module.h>
  17 #include <linux/slab.h>
  18 #include <linux/device.h>
  19 #include <linux/platform_device.h>
  20 #include <linux/err.h>
  21 #include <linux/interrupt.h>
  22 #include <linux/workqueue.h>
  23 #include <linux/iio/consumer.h>
  24 #include <linux/extcon/extcon-adc-jack.h>
  25 #include <linux/extcon-provider.h>
  26 
  27 /**
  28  * struct adc_jack_data - internal data for adc_jack device driver
  29  * @edev:               extcon device.
  30  * @cable_names:        list of supported cables.
  31  * @adc_conditions:     list of adc value conditions.
  32  * @num_conditions:     size of adc_conditions.
  33  * @irq:                irq number of attach/detach event (0 if not exist).
  34  * @handling_delay:     interrupt handler will schedule extcon event
  35  *                      handling at handling_delay jiffies.
  36  * @handler:            extcon event handler called by interrupt handler.
  37  * @chan:               iio channel being queried.
  38  */
  39 struct adc_jack_data {
  40         struct device *dev;
  41         struct extcon_dev *edev;
  42 
  43         const unsigned int **cable_names;
  44         struct adc_jack_cond *adc_conditions;
  45         int num_conditions;
  46 
  47         int irq;
  48         unsigned long handling_delay; /* in jiffies */
  49         struct delayed_work handler;
  50 
  51         struct iio_channel *chan;
  52         bool wakeup_source;
  53 };
  54 
  55 static void adc_jack_handler(struct work_struct *work)
  56 {
  57         struct adc_jack_data *data = container_of(to_delayed_work(work),
  58                         struct adc_jack_data,
  59                         handler);
  60         struct adc_jack_cond *def;
  61         int ret, adc_val;
  62         int i;
  63 
  64         ret = iio_read_channel_raw(data->chan, &adc_val);
  65         if (ret < 0) {
  66                 dev_err(data->dev, "read channel() error: %d\n", ret);
  67                 return;
  68         }
  69 
  70         /* Get state from adc value with adc_conditions */
  71         for (i = 0; i < data->num_conditions; i++) {
  72                 def = &data->adc_conditions[i];
  73                 if (def->min_adc <= adc_val && def->max_adc >= adc_val) {
  74                         extcon_set_state_sync(data->edev, def->id, true);
  75                         return;
  76                 }
  77         }
  78 
  79         /* Set the detached state if adc value is not included in the range */
  80         for (i = 0; i < data->num_conditions; i++) {
  81                 def = &data->adc_conditions[i];
  82                 extcon_set_state_sync(data->edev, def->id, false);
  83         }
  84 }
  85 
  86 static irqreturn_t adc_jack_irq_thread(int irq, void *_data)
  87 {
  88         struct adc_jack_data *data = _data;
  89 
  90         queue_delayed_work(system_power_efficient_wq,
  91                            &data->handler, data->handling_delay);
  92         return IRQ_HANDLED;
  93 }
  94 
  95 static int adc_jack_probe(struct platform_device *pdev)
  96 {
  97         struct adc_jack_data *data;
  98         struct adc_jack_pdata *pdata = dev_get_platdata(&pdev->dev);
  99         int i, err = 0;
 100 
 101         data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 102         if (!data)
 103                 return -ENOMEM;
 104 
 105         if (!pdata->cable_names) {
 106                 dev_err(&pdev->dev, "error: cable_names not defined.\n");
 107                 return -EINVAL;
 108         }
 109 
 110         data->dev = &pdev->dev;
 111         data->edev = devm_extcon_dev_allocate(&pdev->dev, pdata->cable_names);
 112         if (IS_ERR(data->edev)) {
 113                 dev_err(&pdev->dev, "failed to allocate extcon device\n");
 114                 return -ENOMEM;
 115         }
 116 
 117         if (!pdata->adc_conditions) {
 118                 dev_err(&pdev->dev, "error: adc_conditions not defined.\n");
 119                 return -EINVAL;
 120         }
 121         data->adc_conditions = pdata->adc_conditions;
 122 
 123         /* Check the length of array and set num_conditions */
 124         for (i = 0; data->adc_conditions[i].id != EXTCON_NONE; i++);
 125         data->num_conditions = i;
 126 
 127         data->chan = iio_channel_get(&pdev->dev, pdata->consumer_channel);
 128         if (IS_ERR(data->chan))
 129                 return PTR_ERR(data->chan);
 130 
 131         data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms);
 132         data->wakeup_source = pdata->wakeup_source;
 133 
 134         INIT_DEFERRABLE_WORK(&data->handler, adc_jack_handler);
 135 
 136         platform_set_drvdata(pdev, data);
 137 
 138         err = devm_extcon_dev_register(&pdev->dev, data->edev);
 139         if (err)
 140                 return err;
 141 
 142         data->irq = platform_get_irq(pdev, 0);
 143         if (data->irq < 0)
 144                 return -ENODEV;
 145 
 146         err = request_any_context_irq(data->irq, adc_jack_irq_thread,
 147                         pdata->irq_flags, pdata->name, data);
 148 
 149         if (err < 0) {
 150                 dev_err(&pdev->dev, "error: irq %d\n", data->irq);
 151                 return err;
 152         }
 153 
 154         if (data->wakeup_source)
 155                 device_init_wakeup(&pdev->dev, 1);
 156 
 157         adc_jack_handler(&data->handler.work);
 158         return 0;
 159 }
 160 
 161 static int adc_jack_remove(struct platform_device *pdev)
 162 {
 163         struct adc_jack_data *data = platform_get_drvdata(pdev);
 164 
 165         free_irq(data->irq, data);
 166         cancel_work_sync(&data->handler.work);
 167         iio_channel_release(data->chan);
 168 
 169         return 0;
 170 }
 171 
 172 #ifdef CONFIG_PM_SLEEP
 173 static int adc_jack_suspend(struct device *dev)
 174 {
 175         struct adc_jack_data *data = dev_get_drvdata(dev);
 176 
 177         cancel_delayed_work_sync(&data->handler);
 178         if (device_may_wakeup(data->dev))
 179                 enable_irq_wake(data->irq);
 180 
 181         return 0;
 182 }
 183 
 184 static int adc_jack_resume(struct device *dev)
 185 {
 186         struct adc_jack_data *data = dev_get_drvdata(dev);
 187 
 188         if (device_may_wakeup(data->dev))
 189                 disable_irq_wake(data->irq);
 190 
 191         return 0;
 192 }
 193 #endif /* CONFIG_PM_SLEEP */
 194 
 195 static SIMPLE_DEV_PM_OPS(adc_jack_pm_ops,
 196                 adc_jack_suspend, adc_jack_resume);
 197 
 198 static struct platform_driver adc_jack_driver = {
 199         .probe          = adc_jack_probe,
 200         .remove         = adc_jack_remove,
 201         .driver         = {
 202                 .name   = "adc-jack",
 203                 .pm = &adc_jack_pm_ops,
 204         },
 205 };
 206 
 207 module_platform_driver(adc_jack_driver);
 208 
 209 MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
 210 MODULE_DESCRIPTION("ADC Jack extcon driver");
 211 MODULE_LICENSE("GPL v2");

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