1 /* 2 * extcon_gpio.c - Single-state GPIO extcon driver based on extcon class 3 * 4 * Copyright (C) 2008 Google, Inc. 5 * Author: Mike Lockwood <lockwood@android.com> 6 * 7 * Modified by MyungJoo Ham <myungjoo.ham@samsung.com> to support extcon 8 * (originally switch class is supported) 9 * 10 * This software is licensed under the terms of the GNU General Public 11 * License version 2, as published by the Free Software Foundation, and 12 * may be copied, distributed, and modified under those terms. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 */ 19 20 #include <linux/extcon.h> 21 #include <linux/extcon/extcon-gpio.h> 22 #include <linux/gpio.h> 23 #include <linux/gpio/consumer.h> 24 #include <linux/init.h> 25 #include <linux/interrupt.h> 26 #include <linux/kernel.h> 27 #include <linux/module.h> 28 #include <linux/platform_device.h> 29 #include <linux/slab.h> 30 #include <linux/workqueue.h> 31 32 struct gpio_extcon_data { 33 struct extcon_dev *edev; 34 int irq; 35 struct delayed_work work; 36 unsigned long debounce_jiffies; 37 38 struct gpio_desc *id_gpiod; 39 struct gpio_extcon_pdata *pdata; 40 }; 41 42 static void gpio_extcon_work(struct work_struct *work) 43 { 44 int state; 45 struct gpio_extcon_data *data = 46 container_of(to_delayed_work(work), struct gpio_extcon_data, 47 work); 48 49 state = gpiod_get_value_cansleep(data->id_gpiod); 50 if (data->pdata->gpio_active_low) 51 state = !state; 52 extcon_set_state(data->edev, state); 53 } 54 55 static irqreturn_t gpio_irq_handler(int irq, void *dev_id) 56 { 57 struct gpio_extcon_data *data = dev_id; 58 59 queue_delayed_work(system_power_efficient_wq, &data->work, 60 data->debounce_jiffies); 61 return IRQ_HANDLED; 62 } 63 64 static int gpio_extcon_init(struct device *dev, struct gpio_extcon_data *data) 65 { 66 struct gpio_extcon_pdata *pdata = data->pdata; 67 int ret; 68 69 ret = devm_gpio_request_one(dev, pdata->gpio, GPIOF_DIR_IN, 70 dev_name(dev)); 71 if (ret < 0) 72 return ret; 73 74 data->id_gpiod = gpio_to_desc(pdata->gpio); 75 if (!data->id_gpiod) 76 return -EINVAL; 77 78 if (pdata->debounce) { 79 ret = gpiod_set_debounce(data->id_gpiod, 80 pdata->debounce * 1000); 81 if (ret < 0) 82 data->debounce_jiffies = 83 msecs_to_jiffies(pdata->debounce); 84 } 85 86 data->irq = gpiod_to_irq(data->id_gpiod); 87 if (data->irq < 0) 88 return data->irq; 89 90 return 0; 91 } 92 93 static int gpio_extcon_probe(struct platform_device *pdev) 94 { 95 struct gpio_extcon_pdata *pdata = dev_get_platdata(&pdev->dev); 96 struct gpio_extcon_data *data; 97 int ret; 98 99 if (!pdata) 100 return -EBUSY; 101 if (!pdata->irq_flags || pdata->extcon_id > EXTCON_NONE) 102 return -EINVAL; 103 104 data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_extcon_data), 105 GFP_KERNEL); 106 if (!data) 107 return -ENOMEM; 108 data->pdata = pdata; 109 110 /* Initialize the gpio */ 111 ret = gpio_extcon_init(&pdev->dev, data); 112 if (ret < 0) 113 return ret; 114 115 /* Allocate the memory of extcon devie and register extcon device */ 116 data->edev = devm_extcon_dev_allocate(&pdev->dev, &pdata->extcon_id); 117 if (IS_ERR(data->edev)) { 118 dev_err(&pdev->dev, "failed to allocate extcon device\n"); 119 return -ENOMEM; 120 } 121 122 ret = devm_extcon_dev_register(&pdev->dev, data->edev); 123 if (ret < 0) 124 return ret; 125 126 INIT_DELAYED_WORK(&data->work, gpio_extcon_work); 127 128 /* 129 * Request the interrupt of gpio to detect whether external connector 130 * is attached or detached. 131 */ 132 ret = devm_request_any_context_irq(&pdev->dev, data->irq, 133 gpio_irq_handler, pdata->irq_flags, 134 pdev->name, data); 135 if (ret < 0) 136 return ret; 137 138 platform_set_drvdata(pdev, data); 139 /* Perform initial detection */ 140 gpio_extcon_work(&data->work.work); 141 142 return 0; 143 } 144 145 static int gpio_extcon_remove(struct platform_device *pdev) 146 { 147 struct gpio_extcon_data *data = platform_get_drvdata(pdev); 148 149 cancel_delayed_work_sync(&data->work); 150 151 return 0; 152 } 153 154 #ifdef CONFIG_PM_SLEEP 155 static int gpio_extcon_resume(struct device *dev) 156 { 157 struct gpio_extcon_data *data; 158 159 data = dev_get_drvdata(dev); 160 if (data->pdata->check_on_resume) 161 queue_delayed_work(system_power_efficient_wq, 162 &data->work, data->debounce_jiffies); 163 164 return 0; 165 } 166 #endif 167 168 static SIMPLE_DEV_PM_OPS(gpio_extcon_pm_ops, NULL, gpio_extcon_resume); 169 170 static struct platform_driver gpio_extcon_driver = { 171 .probe = gpio_extcon_probe, 172 .remove = gpio_extcon_remove, 173 .driver = { 174 .name = "extcon-gpio", 175 .pm = &gpio_extcon_pm_ops, 176 }, 177 }; 178 179 module_platform_driver(gpio_extcon_driver); 180 181 MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>"); 182 MODULE_DESCRIPTION("GPIO extcon driver"); 183 MODULE_LICENSE("GPL"); 184