119939860Sanish kumar /* 219939860Sanish kumar * drivers/extcon/extcon-adc-jack.c 319939860Sanish kumar * 419939860Sanish kumar * Analog Jack extcon driver with ADC-based detection capability. 519939860Sanish kumar * 6a7da72eeSChanwoo Choi * Copyright (C) 2016 Samsung Electronics 7a7da72eeSChanwoo Choi * Chanwoo Choi <cw00.choi@samsung.com> 8a7da72eeSChanwoo Choi * 919939860Sanish kumar * Copyright (C) 2012 Samsung Electronics 1019939860Sanish kumar * MyungJoo Ham <myungjoo.ham@samsung.com> 1119939860Sanish kumar * 1219939860Sanish kumar * Modified for calling to IIO to get adc by <anish.singh@samsung.com> 1319939860Sanish kumar * 1419939860Sanish kumar * This program is free software; you can redistribute it and/or modify 1519939860Sanish kumar * it under the terms of the GNU General Public License version 2 as 1619939860Sanish kumar * published by the Free Software Foundation. 1719939860Sanish kumar * 1819939860Sanish kumar */ 1919939860Sanish kumar 20d9310e35SAxel Lin #include <linux/module.h> 2119939860Sanish kumar #include <linux/slab.h> 2219939860Sanish kumar #include <linux/device.h> 2319939860Sanish kumar #include <linux/platform_device.h> 2419939860Sanish kumar #include <linux/err.h> 2519939860Sanish kumar #include <linux/interrupt.h> 2619939860Sanish kumar #include <linux/workqueue.h> 2719939860Sanish kumar #include <linux/iio/consumer.h> 2819939860Sanish kumar #include <linux/extcon/extcon-adc-jack.h> 2919939860Sanish kumar #include <linux/extcon.h> 3019939860Sanish kumar 3119939860Sanish kumar /** 3219939860Sanish kumar * struct adc_jack_data - internal data for adc_jack device driver 33a75e1c73SChanwoo Choi * @edev: extcon device. 34a75e1c73SChanwoo Choi * @cable_names: list of supported cables. 35a75e1c73SChanwoo Choi * @adc_conditions: list of adc value conditions. 36a75e1c73SChanwoo Choi * @num_conditions: size of adc_conditions. 37a75e1c73SChanwoo Choi * @irq: irq number of attach/detach event (0 if not exist). 38a75e1c73SChanwoo Choi * @handling_delay: interrupt handler will schedule extcon event 3919939860Sanish kumar * handling at handling_delay jiffies. 40a75e1c73SChanwoo Choi * @handler: extcon event handler called by interrupt handler. 41a75e1c73SChanwoo Choi * @chan: iio channel being queried. 4219939860Sanish kumar */ 4319939860Sanish kumar struct adc_jack_data { 441b6cf310SVenkat Reddy Talla struct device *dev; 451876fd9aSChanwoo Choi struct extcon_dev *edev; 4619939860Sanish kumar 4773b6ecdbSChanwoo Choi const unsigned int **cable_names; 4819939860Sanish kumar struct adc_jack_cond *adc_conditions; 4919939860Sanish kumar int num_conditions; 5019939860Sanish kumar 5119939860Sanish kumar int irq; 5219939860Sanish kumar unsigned long handling_delay; /* in jiffies */ 5319939860Sanish kumar struct delayed_work handler; 5419939860Sanish kumar 5519939860Sanish kumar struct iio_channel *chan; 561b6cf310SVenkat Reddy Talla bool wakeup_source; 5719939860Sanish kumar }; 5819939860Sanish kumar 5919939860Sanish kumar static void adc_jack_handler(struct work_struct *work) 6019939860Sanish kumar { 6119939860Sanish kumar struct adc_jack_data *data = container_of(to_delayed_work(work), 6219939860Sanish kumar struct adc_jack_data, 6319939860Sanish kumar handler); 64a7da72eeSChanwoo Choi struct adc_jack_cond *def; 6519939860Sanish kumar int ret, adc_val; 6619939860Sanish kumar int i; 6719939860Sanish kumar 6819939860Sanish kumar ret = iio_read_channel_raw(data->chan, &adc_val); 6919939860Sanish kumar if (ret < 0) { 70*6e3a7e89SChanwoo Choi dev_err(data->dev, "read channel() error: %d\n", ret); 7119939860Sanish kumar return; 7219939860Sanish kumar } 7319939860Sanish kumar 7419939860Sanish kumar /* Get state from adc value with adc_conditions */ 7519939860Sanish kumar for (i = 0; i < data->num_conditions; i++) { 76a7da72eeSChanwoo Choi def = &data->adc_conditions[i]; 7719939860Sanish kumar if (def->min_adc <= adc_val && def->max_adc >= adc_val) { 788670b459SChanwoo Choi extcon_set_state_sync(data->edev, def->id, true); 79a7da72eeSChanwoo Choi return; 8019939860Sanish kumar } 8119939860Sanish kumar } 8219939860Sanish kumar 83a7da72eeSChanwoo Choi /* Set the detached state if adc value is not included in the range */ 84a7da72eeSChanwoo Choi for (i = 0; i < data->num_conditions; i++) { 85a7da72eeSChanwoo Choi def = &data->adc_conditions[i]; 868670b459SChanwoo Choi extcon_set_state_sync(data->edev, def->id, false); 87a7da72eeSChanwoo Choi } 8819939860Sanish kumar } 8919939860Sanish kumar 9019939860Sanish kumar static irqreturn_t adc_jack_irq_thread(int irq, void *_data) 9119939860Sanish kumar { 9219939860Sanish kumar struct adc_jack_data *data = _data; 9319939860Sanish kumar 941a82e81eSMark Brown queue_delayed_work(system_power_efficient_wq, 951a82e81eSMark Brown &data->handler, data->handling_delay); 9619939860Sanish kumar return IRQ_HANDLED; 9719939860Sanish kumar } 9819939860Sanish kumar 9944f34fd4SBill Pemberton static int adc_jack_probe(struct platform_device *pdev) 10019939860Sanish kumar { 10119939860Sanish kumar struct adc_jack_data *data; 1027c0f6558SJingoo Han struct adc_jack_pdata *pdata = dev_get_platdata(&pdev->dev); 10319939860Sanish kumar int i, err = 0; 10419939860Sanish kumar 10519939860Sanish kumar data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 10619939860Sanish kumar if (!data) 10719939860Sanish kumar return -ENOMEM; 10819939860Sanish kumar 10919939860Sanish kumar if (!pdata->cable_names) { 11019939860Sanish kumar dev_err(&pdev->dev, "error: cable_names not defined.\n"); 1114b5dd738SSangjung Woo return -EINVAL; 11219939860Sanish kumar } 11319939860Sanish kumar 1141b6cf310SVenkat Reddy Talla data->dev = &pdev->dev; 1151876fd9aSChanwoo Choi data->edev = devm_extcon_dev_allocate(&pdev->dev, pdata->cable_names); 1161876fd9aSChanwoo Choi if (IS_ERR(data->edev)) { 1171876fd9aSChanwoo Choi dev_err(&pdev->dev, "failed to allocate extcon device\n"); 1181876fd9aSChanwoo Choi return -ENOMEM; 1191876fd9aSChanwoo Choi } 12019939860Sanish kumar 121a7da72eeSChanwoo Choi if (!pdata->adc_conditions) { 12219939860Sanish kumar dev_err(&pdev->dev, "error: adc_conditions not defined.\n"); 1234b5dd738SSangjung Woo return -EINVAL; 12419939860Sanish kumar } 12519939860Sanish kumar data->adc_conditions = pdata->adc_conditions; 12619939860Sanish kumar 12719939860Sanish kumar /* Check the length of array and set num_conditions */ 128a7da72eeSChanwoo Choi for (i = 0; data->adc_conditions[i].id != EXTCON_NONE; i++); 12919939860Sanish kumar data->num_conditions = i; 13019939860Sanish kumar 1315aa57f0aSGuenter Roeck data->chan = iio_channel_get(&pdev->dev, pdata->consumer_channel); 1324b5dd738SSangjung Woo if (IS_ERR(data->chan)) 1334b5dd738SSangjung Woo return PTR_ERR(data->chan); 13419939860Sanish kumar 13519939860Sanish kumar data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms); 1361b6cf310SVenkat Reddy Talla data->wakeup_source = pdata->wakeup_source; 13719939860Sanish kumar 138033d9959SLinus Torvalds INIT_DEFERRABLE_WORK(&data->handler, adc_jack_handler); 13919939860Sanish kumar 14019939860Sanish kumar platform_set_drvdata(pdev, data); 14119939860Sanish kumar 1421876fd9aSChanwoo Choi err = devm_extcon_dev_register(&pdev->dev, data->edev); 14319939860Sanish kumar if (err) 1444b5dd738SSangjung Woo return err; 14519939860Sanish kumar 14619939860Sanish kumar data->irq = platform_get_irq(pdev, 0); 14719939860Sanish kumar if (!data->irq) { 14819939860Sanish kumar dev_err(&pdev->dev, "platform_get_irq failed\n"); 1494b5dd738SSangjung Woo return -ENODEV; 15019939860Sanish kumar } 15119939860Sanish kumar 15219939860Sanish kumar err = request_any_context_irq(data->irq, adc_jack_irq_thread, 15319939860Sanish kumar pdata->irq_flags, pdata->name, data); 15419939860Sanish kumar 15503019759SAxel Lin if (err < 0) { 15619939860Sanish kumar dev_err(&pdev->dev, "error: irq %d\n", data->irq); 1574b5dd738SSangjung Woo return err; 15819939860Sanish kumar } 15919939860Sanish kumar 1601b6cf310SVenkat Reddy Talla if (data->wakeup_source) 1611b6cf310SVenkat Reddy Talla device_init_wakeup(&pdev->dev, 1); 1621b6cf310SVenkat Reddy Talla 163ba4b2715SVenkat Reddy Talla adc_jack_handler(&data->handler.work); 16403019759SAxel Lin return 0; 16519939860Sanish kumar } 16619939860Sanish kumar 16793ed0327SBill Pemberton static int adc_jack_remove(struct platform_device *pdev) 16819939860Sanish kumar { 16919939860Sanish kumar struct adc_jack_data *data = platform_get_drvdata(pdev); 17019939860Sanish kumar 17119939860Sanish kumar free_irq(data->irq, data); 17219939860Sanish kumar cancel_work_sync(&data->handler.work); 1735a696d97SIvan T. Ivanov iio_channel_release(data->chan); 17419939860Sanish kumar 17519939860Sanish kumar return 0; 17619939860Sanish kumar } 17719939860Sanish kumar 1781b6cf310SVenkat Reddy Talla #ifdef CONFIG_PM_SLEEP 1791b6cf310SVenkat Reddy Talla static int adc_jack_suspend(struct device *dev) 1801b6cf310SVenkat Reddy Talla { 1811b6cf310SVenkat Reddy Talla struct adc_jack_data *data = dev_get_drvdata(dev); 1821b6cf310SVenkat Reddy Talla 1831b6cf310SVenkat Reddy Talla cancel_delayed_work_sync(&data->handler); 1841b6cf310SVenkat Reddy Talla if (device_may_wakeup(data->dev)) 1851b6cf310SVenkat Reddy Talla enable_irq_wake(data->irq); 1861b6cf310SVenkat Reddy Talla 1871b6cf310SVenkat Reddy Talla return 0; 1881b6cf310SVenkat Reddy Talla } 1891b6cf310SVenkat Reddy Talla 1901b6cf310SVenkat Reddy Talla static int adc_jack_resume(struct device *dev) 1911b6cf310SVenkat Reddy Talla { 1921b6cf310SVenkat Reddy Talla struct adc_jack_data *data = dev_get_drvdata(dev); 1931b6cf310SVenkat Reddy Talla 1941b6cf310SVenkat Reddy Talla if (device_may_wakeup(data->dev)) 1951b6cf310SVenkat Reddy Talla disable_irq_wake(data->irq); 1961b6cf310SVenkat Reddy Talla 1971b6cf310SVenkat Reddy Talla return 0; 1981b6cf310SVenkat Reddy Talla } 1991b6cf310SVenkat Reddy Talla #endif /* CONFIG_PM_SLEEP */ 2001b6cf310SVenkat Reddy Talla 2011b6cf310SVenkat Reddy Talla static SIMPLE_DEV_PM_OPS(adc_jack_pm_ops, 2021b6cf310SVenkat Reddy Talla adc_jack_suspend, adc_jack_resume); 2031b6cf310SVenkat Reddy Talla 20419939860Sanish kumar static struct platform_driver adc_jack_driver = { 20519939860Sanish kumar .probe = adc_jack_probe, 2065f7e2228SBill Pemberton .remove = adc_jack_remove, 20719939860Sanish kumar .driver = { 20819939860Sanish kumar .name = "adc-jack", 2091b6cf310SVenkat Reddy Talla .pm = &adc_jack_pm_ops, 21019939860Sanish kumar }, 21119939860Sanish kumar }; 21219939860Sanish kumar 21319939860Sanish kumar module_platform_driver(adc_jack_driver); 214d9310e35SAxel Lin 215d9310e35SAxel Lin MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); 216d9310e35SAxel Lin MODULE_DESCRIPTION("ADC Jack extcon driver"); 217d9310e35SAxel Lin MODULE_LICENSE("GPL v2"); 218