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