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