aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-11-28 02:46:47 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-11-28 02:46:47 -0500
commit75e9ebecfedb88a22eee0d2b3b63aeaeab1bfdee (patch)
tree1644b2981f64465828d0be9df7f478236c3cf09a
parentcdefb95bfc0de9a60b91b748f15e65eddb4f116e (diff)
parent541332a13b1ded42097ba96c52c7bc70931e528c (diff)
Merge tag 'extcon-next-for-4.10' of git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/extcon into usb-nextx
Chanwoo writes: Update extcon for 4.10 Detailed description for this pull request: - The extcon-usb-gpio driver supports the VBUS detection with USB ID and VBUS pin.
-rw-r--r--Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt3
-rw-r--r--drivers/extcon/extcon-usb-gpio.c169
2 files changed, 132 insertions, 40 deletions
diff --git a/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt b/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt
index af0b903de293..dfc14f71e81f 100644
--- a/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt
+++ b/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt
@@ -5,7 +5,10 @@ connected to a GPIO pin.
5 5
6Required properties: 6Required properties:
7- compatible: Should be "linux,extcon-usb-gpio" 7- compatible: Should be "linux,extcon-usb-gpio"
8
9Either one of id-gpio or vbus-gpio must be present. Both can be present as well.
8- id-gpio: gpio for USB ID pin. See gpio binding. 10- id-gpio: gpio for USB ID pin. See gpio binding.
11- vbus-gpio: gpio for USB VBUS pin.
9 12
10Example: Examples of extcon-usb-gpio node in dra7-evm.dts as listed below: 13Example: Examples of extcon-usb-gpio node in dra7-evm.dts as listed below:
11 extcon_usb1 { 14 extcon_usb1 {
diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c
index a27d350f69e3..d589c5feff3d 100644
--- a/drivers/extcon/extcon-usb-gpio.c
+++ b/drivers/extcon/extcon-usb-gpio.c
@@ -24,7 +24,6 @@
24#include <linux/module.h> 24#include <linux/module.h>
25#include <linux/of_gpio.h> 25#include <linux/of_gpio.h>
26#include <linux/platform_device.h> 26#include <linux/platform_device.h>
27#include <linux/pm_wakeirq.h>
28#include <linux/slab.h> 27#include <linux/slab.h>
29#include <linux/workqueue.h> 28#include <linux/workqueue.h>
30#include <linux/acpi.h> 29#include <linux/acpi.h>
@@ -36,7 +35,9 @@ struct usb_extcon_info {
36 struct extcon_dev *edev; 35 struct extcon_dev *edev;
37 36
38 struct gpio_desc *id_gpiod; 37 struct gpio_desc *id_gpiod;
38 struct gpio_desc *vbus_gpiod;
39 int id_irq; 39 int id_irq;
40 int vbus_irq;
40 41
41 unsigned long debounce_jiffies; 42 unsigned long debounce_jiffies;
42 struct delayed_work wq_detcable; 43 struct delayed_work wq_detcable;
@@ -48,31 +49,47 @@ static const unsigned int usb_extcon_cable[] = {
48 EXTCON_NONE, 49 EXTCON_NONE,
49}; 50};
50 51
52/*
53 * "USB" = VBUS and "USB-HOST" = !ID, so we have:
54 * Both "USB" and "USB-HOST" can't be set as active at the
55 * same time so if "USB-HOST" is active (i.e. ID is 0) we keep "USB" inactive
56 * even if VBUS is on.
57 *
58 * State | ID | VBUS
59 * ----------------------------------------
60 * [1] USB | H | H
61 * [2] none | H | L
62 * [3] USB-HOST | L | H
63 * [4] USB-HOST | L | L
64 *
65 * In case we have only one of these signals:
66 * - VBUS only - we want to distinguish between [1] and [2], so ID is always 1.
67 * - ID only - we want to distinguish between [1] and [4], so VBUS = ID.
68*/
51static void usb_extcon_detect_cable(struct work_struct *work) 69static void usb_extcon_detect_cable(struct work_struct *work)
52{ 70{
53 int id; 71 int id, vbus;
54 struct usb_extcon_info *info = container_of(to_delayed_work(work), 72 struct usb_extcon_info *info = container_of(to_delayed_work(work),
55 struct usb_extcon_info, 73 struct usb_extcon_info,
56 wq_detcable); 74 wq_detcable);
57 75
58 /* check ID and update cable state */ 76 /* check ID and VBUS and update cable state */
59 id = gpiod_get_value_cansleep(info->id_gpiod); 77 id = info->id_gpiod ?
60 if (id) { 78 gpiod_get_value_cansleep(info->id_gpiod) : 1;
61 /* 79 vbus = info->vbus_gpiod ?
62 * ID = 1 means USB HOST cable detached. 80 gpiod_get_value_cansleep(info->vbus_gpiod) : id;
63 * As we don't have event for USB peripheral cable attached, 81
64 * we simulate USB peripheral attach here. 82 /* at first we clean states which are no longer active */
65 */ 83 if (id)
66 extcon_set_state_sync(info->edev, EXTCON_USB_HOST, false); 84 extcon_set_state_sync(info->edev, EXTCON_USB_HOST, false);
67 extcon_set_state_sync(info->edev, EXTCON_USB, true); 85 if (!vbus)
68 } else {
69 /*
70 * ID = 0 means USB HOST cable attached.
71 * As we don't have event for USB peripheral cable detached,
72 * we simulate USB peripheral detach here.
73 */
74 extcon_set_state_sync(info->edev, EXTCON_USB, false); 86 extcon_set_state_sync(info->edev, EXTCON_USB, false);
87
88 if (!id) {
75 extcon_set_state_sync(info->edev, EXTCON_USB_HOST, true); 89 extcon_set_state_sync(info->edev, EXTCON_USB_HOST, true);
90 } else {
91 if (vbus)
92 extcon_set_state_sync(info->edev, EXTCON_USB, true);
76 } 93 }
77} 94}
78 95
@@ -101,12 +118,21 @@ static int usb_extcon_probe(struct platform_device *pdev)
101 return -ENOMEM; 118 return -ENOMEM;
102 119
103 info->dev = dev; 120 info->dev = dev;
104 info->id_gpiod = devm_gpiod_get(&pdev->dev, "id", GPIOD_IN); 121 info->id_gpiod = devm_gpiod_get_optional(&pdev->dev, "id", GPIOD_IN);
105 if (IS_ERR(info->id_gpiod)) { 122 info->vbus_gpiod = devm_gpiod_get_optional(&pdev->dev, "vbus",
106 dev_err(dev, "failed to get ID GPIO\n"); 123 GPIOD_IN);
107 return PTR_ERR(info->id_gpiod); 124
125 if (!info->id_gpiod && !info->vbus_gpiod) {
126 dev_err(dev, "failed to get gpios\n");
127 return -ENODEV;
108 } 128 }
109 129
130 if (IS_ERR(info->id_gpiod))
131 return PTR_ERR(info->id_gpiod);
132
133 if (IS_ERR(info->vbus_gpiod))
134 return PTR_ERR(info->vbus_gpiod);
135
110 info->edev = devm_extcon_dev_allocate(dev, usb_extcon_cable); 136 info->edev = devm_extcon_dev_allocate(dev, usb_extcon_cable);
111 if (IS_ERR(info->edev)) { 137 if (IS_ERR(info->edev)) {
112 dev_err(dev, "failed to allocate extcon device\n"); 138 dev_err(dev, "failed to allocate extcon device\n");
@@ -119,32 +145,56 @@ static int usb_extcon_probe(struct platform_device *pdev)
119 return ret; 145 return ret;
120 } 146 }
121 147
122 ret = gpiod_set_debounce(info->id_gpiod, 148 if (info->id_gpiod)
123 USB_GPIO_DEBOUNCE_MS * 1000); 149 ret = gpiod_set_debounce(info->id_gpiod,
150 USB_GPIO_DEBOUNCE_MS * 1000);
151 if (!ret && info->vbus_gpiod)
152 ret = gpiod_set_debounce(info->vbus_gpiod,
153 USB_GPIO_DEBOUNCE_MS * 1000);
154
124 if (ret < 0) 155 if (ret < 0)
125 info->debounce_jiffies = msecs_to_jiffies(USB_GPIO_DEBOUNCE_MS); 156 info->debounce_jiffies = msecs_to_jiffies(USB_GPIO_DEBOUNCE_MS);
126 157
127 INIT_DELAYED_WORK(&info->wq_detcable, usb_extcon_detect_cable); 158 INIT_DELAYED_WORK(&info->wq_detcable, usb_extcon_detect_cable);
128 159
129 info->id_irq = gpiod_to_irq(info->id_gpiod); 160 if (info->id_gpiod) {
130 if (info->id_irq < 0) { 161 info->id_irq = gpiod_to_irq(info->id_gpiod);
131 dev_err(dev, "failed to get ID IRQ\n"); 162 if (info->id_irq < 0) {
132 return info->id_irq; 163 dev_err(dev, "failed to get ID IRQ\n");
164 return info->id_irq;
165 }
166
167 ret = devm_request_threaded_irq(dev, info->id_irq, NULL,
168 usb_irq_handler,
169 IRQF_TRIGGER_RISING |
170 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
171 pdev->name, info);
172 if (ret < 0) {
173 dev_err(dev, "failed to request handler for ID IRQ\n");
174 return ret;
175 }
133 } 176 }
134 177
135 ret = devm_request_threaded_irq(dev, info->id_irq, NULL, 178 if (info->vbus_gpiod) {
136 usb_irq_handler, 179 info->vbus_irq = gpiod_to_irq(info->vbus_gpiod);
137 IRQF_TRIGGER_RISING | 180 if (info->vbus_irq < 0) {
138 IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 181 dev_err(dev, "failed to get VBUS IRQ\n");
139 pdev->name, info); 182 return info->vbus_irq;
140 if (ret < 0) { 183 }
141 dev_err(dev, "failed to request handler for ID IRQ\n"); 184
142 return ret; 185 ret = devm_request_threaded_irq(dev, info->vbus_irq, NULL,
186 usb_irq_handler,
187 IRQF_TRIGGER_RISING |
188 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
189 pdev->name, info);
190 if (ret < 0) {
191 dev_err(dev, "failed to request handler for VBUS IRQ\n");
192 return ret;
193 }
143 } 194 }
144 195
145 platform_set_drvdata(pdev, info); 196 platform_set_drvdata(pdev, info);
146 device_init_wakeup(dev, true); 197 device_init_wakeup(dev, true);
147 dev_pm_set_wake_irq(dev, info->id_irq);
148 198
149 /* Perform initial detection */ 199 /* Perform initial detection */
150 usb_extcon_detect_cable(&info->wq_detcable.work); 200 usb_extcon_detect_cable(&info->wq_detcable.work);
@@ -157,8 +207,6 @@ static int usb_extcon_remove(struct platform_device *pdev)
157 struct usb_extcon_info *info = platform_get_drvdata(pdev); 207 struct usb_extcon_info *info = platform_get_drvdata(pdev);
158 208
159 cancel_delayed_work_sync(&info->wq_detcable); 209 cancel_delayed_work_sync(&info->wq_detcable);
160
161 dev_pm_clear_wake_irq(&pdev->dev);
162 device_init_wakeup(&pdev->dev, false); 210 device_init_wakeup(&pdev->dev, false);
163 211
164 return 0; 212 return 0;
@@ -170,12 +218,32 @@ static int usb_extcon_suspend(struct device *dev)
170 struct usb_extcon_info *info = dev_get_drvdata(dev); 218 struct usb_extcon_info *info = dev_get_drvdata(dev);
171 int ret = 0; 219 int ret = 0;
172 220
221 if (device_may_wakeup(dev)) {
222 if (info->id_gpiod) {
223 ret = enable_irq_wake(info->id_irq);
224 if (ret)
225 return ret;
226 }
227 if (info->vbus_gpiod) {
228 ret = enable_irq_wake(info->vbus_irq);
229 if (ret) {
230 if (info->id_gpiod)
231 disable_irq_wake(info->id_irq);
232
233 return ret;
234 }
235 }
236 }
237
173 /* 238 /*
174 * We don't want to process any IRQs after this point 239 * We don't want to process any IRQs after this point
175 * as GPIOs used behind I2C subsystem might not be 240 * as GPIOs used behind I2C subsystem might not be
176 * accessible until resume completes. So disable IRQ. 241 * accessible until resume completes. So disable IRQ.
177 */ 242 */
178 disable_irq(info->id_irq); 243 if (info->id_gpiod)
244 disable_irq(info->id_irq);
245 if (info->vbus_gpiod)
246 disable_irq(info->vbus_irq);
179 247
180 return ret; 248 return ret;
181} 249}
@@ -185,7 +253,28 @@ static int usb_extcon_resume(struct device *dev)
185 struct usb_extcon_info *info = dev_get_drvdata(dev); 253 struct usb_extcon_info *info = dev_get_drvdata(dev);
186 int ret = 0; 254 int ret = 0;
187 255
188 enable_irq(info->id_irq); 256 if (device_may_wakeup(dev)) {
257 if (info->id_gpiod) {
258 ret = disable_irq_wake(info->id_irq);
259 if (ret)
260 return ret;
261 }
262 if (info->vbus_gpiod) {
263 ret = disable_irq_wake(info->vbus_irq);
264 if (ret) {
265 if (info->id_gpiod)
266 enable_irq_wake(info->id_irq);
267
268 return ret;
269 }
270 }
271 }
272
273 if (info->id_gpiod)
274 enable_irq(info->id_irq);
275 if (info->vbus_gpiod)
276 enable_irq(info->vbus_irq);
277
189 if (!device_may_wakeup(dev)) 278 if (!device_may_wakeup(dev))
190 queue_delayed_work(system_power_efficient_wq, 279 queue_delayed_work(system_power_efficient_wq,
191 &info->wq_detcable, 0); 280 &info->wq_detcable, 0);