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 * 619939860Sanish kumar * Copyright (C) 2012 Samsung Electronics 719939860Sanish kumar * MyungJoo Ham <myungjoo.ham@samsung.com> 819939860Sanish kumar * 919939860Sanish kumar * Modified for calling to IIO to get adc by <anish.singh@samsung.com> 1019939860Sanish kumar * 1119939860Sanish kumar * This program is free software; you can redistribute it and/or modify 1219939860Sanish kumar * it under the terms of the GNU General Public License version 2 as 1319939860Sanish kumar * published by the Free Software Foundation. 1419939860Sanish kumar * 1519939860Sanish kumar */ 1619939860Sanish kumar 17d9310e35SAxel Lin #include <linux/module.h> 1819939860Sanish kumar #include <linux/slab.h> 1919939860Sanish kumar #include <linux/device.h> 2019939860Sanish kumar #include <linux/platform_device.h> 2119939860Sanish kumar #include <linux/err.h> 2219939860Sanish kumar #include <linux/interrupt.h> 2319939860Sanish kumar #include <linux/workqueue.h> 2419939860Sanish kumar #include <linux/iio/consumer.h> 2519939860Sanish kumar #include <linux/extcon/extcon-adc-jack.h> 2619939860Sanish kumar #include <linux/extcon.h> 2719939860Sanish kumar 2819939860Sanish kumar /** 2919939860Sanish kumar * struct adc_jack_data - internal data for adc_jack device driver 30a75e1c73SChanwoo Choi * @edev: extcon device. 31a75e1c73SChanwoo Choi * @cable_names: list of supported cables. 32a75e1c73SChanwoo Choi * @adc_conditions: list of adc value conditions. 33a75e1c73SChanwoo Choi * @num_conditions: size of adc_conditions. 34a75e1c73SChanwoo Choi * @irq: irq number of attach/detach event (0 if not exist). 35a75e1c73SChanwoo Choi * @handling_delay: interrupt handler will schedule extcon event 3619939860Sanish kumar * handling at handling_delay jiffies. 37a75e1c73SChanwoo Choi * @handler: extcon event handler called by interrupt handler. 38a75e1c73SChanwoo Choi * @chan: iio channel being queried. 3919939860Sanish kumar */ 4019939860Sanish kumar struct adc_jack_data { 411b6cf310SVenkat Reddy Talla struct device *dev; 421876fd9aSChanwoo Choi struct extcon_dev *edev; 4319939860Sanish kumar 4473b6ecdbSChanwoo Choi const unsigned int **cable_names; 4519939860Sanish kumar struct adc_jack_cond *adc_conditions; 4619939860Sanish kumar int num_conditions; 4719939860Sanish kumar 4819939860Sanish kumar int irq; 4919939860Sanish kumar unsigned long handling_delay; /* in jiffies */ 5019939860Sanish kumar struct delayed_work handler; 5119939860Sanish kumar 5219939860Sanish kumar struct iio_channel *chan; 531b6cf310SVenkat Reddy Talla bool wakeup_source; 5419939860Sanish kumar }; 5519939860Sanish kumar 5619939860Sanish kumar static void adc_jack_handler(struct work_struct *work) 5719939860Sanish kumar { 5819939860Sanish kumar struct adc_jack_data *data = container_of(to_delayed_work(work), 5919939860Sanish kumar struct adc_jack_data, 6019939860Sanish kumar handler); 6119939860Sanish kumar u32 state = 0; 6219939860Sanish kumar int ret, adc_val; 6319939860Sanish kumar int i; 6419939860Sanish kumar 6519939860Sanish kumar ret = iio_read_channel_raw(data->chan, &adc_val); 6619939860Sanish kumar if (ret < 0) { 671876fd9aSChanwoo Choi dev_err(&data->edev->dev, "read channel() error: %d\n", ret); 6819939860Sanish kumar return; 6919939860Sanish kumar } 7019939860Sanish kumar 7119939860Sanish kumar /* Get state from adc value with adc_conditions */ 7219939860Sanish kumar for (i = 0; i < data->num_conditions; i++) { 7319939860Sanish kumar struct adc_jack_cond *def = &data->adc_conditions[i]; 7419939860Sanish kumar if (!def->state) 7519939860Sanish kumar break; 7619939860Sanish kumar if (def->min_adc <= adc_val && def->max_adc >= adc_val) { 7719939860Sanish kumar state = def->state; 7819939860Sanish kumar break; 7919939860Sanish kumar } 8019939860Sanish kumar } 8119939860Sanish kumar /* if no def has met, it means state = 0 (no cables attached) */ 8219939860Sanish kumar 831876fd9aSChanwoo Choi extcon_set_state(data->edev, state); 8419939860Sanish kumar } 8519939860Sanish kumar 8619939860Sanish kumar static irqreturn_t adc_jack_irq_thread(int irq, void *_data) 8719939860Sanish kumar { 8819939860Sanish kumar struct adc_jack_data *data = _data; 8919939860Sanish kumar 901a82e81eSMark Brown queue_delayed_work(system_power_efficient_wq, 911a82e81eSMark Brown &data->handler, data->handling_delay); 9219939860Sanish kumar return IRQ_HANDLED; 9319939860Sanish kumar } 9419939860Sanish kumar 9544f34fd4SBill Pemberton static int adc_jack_probe(struct platform_device *pdev) 9619939860Sanish kumar { 9719939860Sanish kumar struct adc_jack_data *data; 987c0f6558SJingoo Han struct adc_jack_pdata *pdata = dev_get_platdata(&pdev->dev); 9919939860Sanish kumar int i, err = 0; 10019939860Sanish kumar 10119939860Sanish kumar data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 10219939860Sanish kumar if (!data) 10319939860Sanish kumar return -ENOMEM; 10419939860Sanish kumar 10519939860Sanish kumar if (!pdata->cable_names) { 10619939860Sanish kumar dev_err(&pdev->dev, "error: cable_names not defined.\n"); 1074b5dd738SSangjung Woo return -EINVAL; 10819939860Sanish kumar } 10919939860Sanish kumar 1101b6cf310SVenkat Reddy Talla data->dev = &pdev->dev; 1111876fd9aSChanwoo Choi data->edev = devm_extcon_dev_allocate(&pdev->dev, pdata->cable_names); 1121876fd9aSChanwoo Choi if (IS_ERR(data->edev)) { 1131876fd9aSChanwoo Choi dev_err(&pdev->dev, "failed to allocate extcon device\n"); 1141876fd9aSChanwoo Choi return -ENOMEM; 1151876fd9aSChanwoo Choi } 11619939860Sanish kumar 11719939860Sanish kumar if (!pdata->adc_conditions || 11819939860Sanish kumar !pdata->adc_conditions[0].state) { 11919939860Sanish kumar dev_err(&pdev->dev, "error: adc_conditions not defined.\n"); 1204b5dd738SSangjung Woo return -EINVAL; 12119939860Sanish kumar } 12219939860Sanish kumar data->adc_conditions = pdata->adc_conditions; 12319939860Sanish kumar 12419939860Sanish kumar /* Check the length of array and set num_conditions */ 12519939860Sanish kumar for (i = 0; data->adc_conditions[i].state; i++) 12619939860Sanish kumar ; 12719939860Sanish kumar data->num_conditions = i; 12819939860Sanish kumar 1295aa57f0aSGuenter Roeck data->chan = iio_channel_get(&pdev->dev, pdata->consumer_channel); 1304b5dd738SSangjung Woo if (IS_ERR(data->chan)) 1314b5dd738SSangjung Woo return PTR_ERR(data->chan); 13219939860Sanish kumar 13319939860Sanish kumar data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms); 1341b6cf310SVenkat Reddy Talla data->wakeup_source = pdata->wakeup_source; 13519939860Sanish kumar 136033d9959SLinus Torvalds INIT_DEFERRABLE_WORK(&data->handler, adc_jack_handler); 13719939860Sanish kumar 13819939860Sanish kumar platform_set_drvdata(pdev, data); 13919939860Sanish kumar 1401876fd9aSChanwoo Choi err = devm_extcon_dev_register(&pdev->dev, data->edev); 14119939860Sanish kumar if (err) 1424b5dd738SSangjung Woo return err; 14319939860Sanish kumar 14419939860Sanish kumar data->irq = platform_get_irq(pdev, 0); 14519939860Sanish kumar if (!data->irq) { 14619939860Sanish kumar dev_err(&pdev->dev, "platform_get_irq failed\n"); 1474b5dd738SSangjung Woo return -ENODEV; 14819939860Sanish kumar } 14919939860Sanish kumar 15019939860Sanish kumar err = request_any_context_irq(data->irq, adc_jack_irq_thread, 15119939860Sanish kumar pdata->irq_flags, pdata->name, data); 15219939860Sanish kumar 15303019759SAxel Lin if (err < 0) { 15419939860Sanish kumar dev_err(&pdev->dev, "error: irq %d\n", data->irq); 1554b5dd738SSangjung Woo return err; 15619939860Sanish kumar } 15719939860Sanish kumar 1581b6cf310SVenkat Reddy Talla if (data->wakeup_source) 1591b6cf310SVenkat Reddy Talla device_init_wakeup(&pdev->dev, 1); 1601b6cf310SVenkat Reddy Talla 161ba4b2715SVenkat Reddy Talla adc_jack_handler(&data->handler.work); 16203019759SAxel Lin return 0; 16319939860Sanish kumar } 16419939860Sanish kumar 16593ed0327SBill Pemberton static int adc_jack_remove(struct platform_device *pdev) 16619939860Sanish kumar { 16719939860Sanish kumar struct adc_jack_data *data = platform_get_drvdata(pdev); 16819939860Sanish kumar 16919939860Sanish kumar free_irq(data->irq, data); 17019939860Sanish kumar cancel_work_sync(&data->handler.work); 1715a696d97SIvan T. Ivanov iio_channel_release(data->chan); 17219939860Sanish kumar 17319939860Sanish kumar return 0; 17419939860Sanish kumar } 17519939860Sanish kumar 1761b6cf310SVenkat Reddy Talla #ifdef CONFIG_PM_SLEEP 1771b6cf310SVenkat Reddy Talla static int adc_jack_suspend(struct device *dev) 1781b6cf310SVenkat Reddy Talla { 1791b6cf310SVenkat Reddy Talla struct adc_jack_data *data = dev_get_drvdata(dev); 1801b6cf310SVenkat Reddy Talla 1811b6cf310SVenkat Reddy Talla cancel_delayed_work_sync(&data->handler); 1821b6cf310SVenkat Reddy Talla if (device_may_wakeup(data->dev)) 1831b6cf310SVenkat Reddy Talla enable_irq_wake(data->irq); 1841b6cf310SVenkat Reddy Talla 1851b6cf310SVenkat Reddy Talla return 0; 1861b6cf310SVenkat Reddy Talla } 1871b6cf310SVenkat Reddy Talla 1881b6cf310SVenkat Reddy Talla static int adc_jack_resume(struct device *dev) 1891b6cf310SVenkat Reddy Talla { 1901b6cf310SVenkat Reddy Talla struct adc_jack_data *data = dev_get_drvdata(dev); 1911b6cf310SVenkat Reddy Talla 1921b6cf310SVenkat Reddy Talla if (device_may_wakeup(data->dev)) 1931b6cf310SVenkat Reddy Talla disable_irq_wake(data->irq); 1941b6cf310SVenkat Reddy Talla 1951b6cf310SVenkat Reddy Talla return 0; 1961b6cf310SVenkat Reddy Talla } 1971b6cf310SVenkat Reddy Talla #endif /* CONFIG_PM_SLEEP */ 1981b6cf310SVenkat Reddy Talla 1991b6cf310SVenkat Reddy Talla static SIMPLE_DEV_PM_OPS(adc_jack_pm_ops, 2001b6cf310SVenkat Reddy Talla adc_jack_suspend, adc_jack_resume); 2011b6cf310SVenkat Reddy Talla 20219939860Sanish kumar static struct platform_driver adc_jack_driver = { 20319939860Sanish kumar .probe = adc_jack_probe, 2045f7e2228SBill Pemberton .remove = adc_jack_remove, 20519939860Sanish kumar .driver = { 20619939860Sanish kumar .name = "adc-jack", 2071b6cf310SVenkat Reddy Talla .pm = &adc_jack_pm_ops, 20819939860Sanish kumar }, 20919939860Sanish kumar }; 21019939860Sanish kumar 21119939860Sanish kumar module_platform_driver(adc_jack_driver); 212d9310e35SAxel Lin 213d9310e35SAxel Lin MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); 214d9310e35SAxel Lin MODULE_DESCRIPTION("ADC Jack extcon driver"); 215d9310e35SAxel Lin MODULE_LICENSE("GPL v2"); 216