diff options
author | Tomoya MORINAGA <tomoya.rohm@gmail.com> | 2012-02-03 02:35:26 -0500 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2012-02-09 02:56:53 -0500 |
commit | dd63180b758d5972fc90621af0741d5bfae1a684 (patch) | |
tree | 0340c73c18cfac563b8b445cc79c74c2ab7d3ba7 /drivers/usb/gadget/pch_udc.c | |
parent | 20edfbb6a17f3007c1905e9849d8d306e318883b (diff) |
usb: gadget: pch_udc: Detecting VBUS through GPIO
Problem:
In USB Suspend, pch_udc handles 'disconnect'.
Root cause:
The current pch_udc is not monitoring VBUS.
When USB cable is disconnected, USB Device Controller generates
an interrupt of USB Suspend.
pch_udc cannot distinguish it is USB Suspend or disconnect.
Therefore, pch_udc handles 'disconnect' after an interrupt of
USB Suspend happend.
Solution:
VBUS is detected through GPIO.
After an interrupt produced USB Suspend, if VBUS is Low,
pch_udc handles 'disconnect'.
If VBUS is High, pch_udc handles 'suspend'.
Signed-off-by: Tomoya MORINAGA <tomoya.rohm@gmail.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/gadget/pch_udc.c')
-rw-r--r-- | drivers/usb/gadget/pch_udc.c | 145 |
1 files changed, 142 insertions, 3 deletions
diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index 82416aae816f..9c5ae2c10147 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c | |||
@@ -15,6 +15,13 @@ | |||
15 | #include <linux/interrupt.h> | 15 | #include <linux/interrupt.h> |
16 | #include <linux/usb/ch9.h> | 16 | #include <linux/usb/ch9.h> |
17 | #include <linux/usb/gadget.h> | 17 | #include <linux/usb/gadget.h> |
18 | #include <linux/gpio.h> | ||
19 | |||
20 | /* GPIO port for VBUS detecting */ | ||
21 | static int vbus_gpio_port = -1; /* GPIO port number (-1:Not used) */ | ||
22 | |||
23 | #define PCH_VBUS_PERIOD 3000 /* VBUS polling period (msec) */ | ||
24 | #define PCH_VBUS_INTERVAL 10 /* VBUS polling interval (msec) */ | ||
18 | 25 | ||
19 | /* Address offset of Registers */ | 26 | /* Address offset of Registers */ |
20 | #define UDC_EP_REG_SHIFT 0x20 /* Offset to next EP */ | 27 | #define UDC_EP_REG_SHIFT 0x20 /* Offset to next EP */ |
@@ -296,6 +303,17 @@ struct pch_udc_ep { | |||
296 | }; | 303 | }; |
297 | 304 | ||
298 | /** | 305 | /** |
306 | * struct pch_vbus_gpio_data - Structure holding GPIO informaton | ||
307 | * for detecting VBUS | ||
308 | * @port: gpio port number | ||
309 | * @irq_work_fall Structure for WorkQueue | ||
310 | */ | ||
311 | struct pch_vbus_gpio_data { | ||
312 | int port; | ||
313 | struct work_struct irq_work_fall; | ||
314 | }; | ||
315 | |||
316 | /** | ||
299 | * struct pch_udc_dev - Structure holding complete information | 317 | * struct pch_udc_dev - Structure holding complete information |
300 | * of the PCH USB device | 318 | * of the PCH USB device |
301 | * @gadget: gadget driver data | 319 | * @gadget: gadget driver data |
@@ -323,6 +341,7 @@ struct pch_udc_ep { | |||
323 | * @base_addr: for mapped device memory | 341 | * @base_addr: for mapped device memory |
324 | * @irq: IRQ line for the device | 342 | * @irq: IRQ line for the device |
325 | * @cfg_data: current cfg, intf, and alt in use | 343 | * @cfg_data: current cfg, intf, and alt in use |
344 | * @vbus_gpio: GPIO informaton for detecting VBUS | ||
326 | */ | 345 | */ |
327 | struct pch_udc_dev { | 346 | struct pch_udc_dev { |
328 | struct usb_gadget gadget; | 347 | struct usb_gadget gadget; |
@@ -349,7 +368,8 @@ struct pch_udc_dev { | |||
349 | unsigned long phys_addr; | 368 | unsigned long phys_addr; |
350 | void __iomem *base_addr; | 369 | void __iomem *base_addr; |
351 | unsigned irq; | 370 | unsigned irq; |
352 | struct pch_udc_cfg_data cfg_data; | 371 | struct pch_udc_cfg_data cfg_data; |
372 | struct pch_vbus_gpio_data vbus_gpio; | ||
353 | }; | 373 | }; |
354 | 374 | ||
355 | #define PCH_UDC_PCI_BAR 1 | 375 | #define PCH_UDC_PCI_BAR 1 |
@@ -1226,6 +1246,115 @@ static const struct usb_gadget_ops pch_udc_ops = { | |||
1226 | }; | 1246 | }; |
1227 | 1247 | ||
1228 | /** | 1248 | /** |
1249 | * pch_vbus_gpio_get_value() - This API gets value of GPIO port as VBUS status. | ||
1250 | * @dev: Reference to the driver structure | ||
1251 | * | ||
1252 | * Return value: | ||
1253 | * 1: VBUS is high | ||
1254 | * 0: VBUS is low | ||
1255 | * -1: It is not enable to detect VBUS using GPIO | ||
1256 | */ | ||
1257 | static int pch_vbus_gpio_get_value(struct pch_udc_dev *dev) | ||
1258 | { | ||
1259 | int vbus = 0; | ||
1260 | |||
1261 | if (dev->vbus_gpio.port) | ||
1262 | vbus = gpio_get_value(dev->vbus_gpio.port) ? 1 : 0; | ||
1263 | else | ||
1264 | vbus = -1; | ||
1265 | |||
1266 | return vbus; | ||
1267 | } | ||
1268 | |||
1269 | /** | ||
1270 | * pch_vbus_gpio_work_fall() - This API keeps watch on VBUS becoming Low. | ||
1271 | * If VBUS is Low, disconnect is processed | ||
1272 | * @irq_work: Structure for WorkQueue | ||
1273 | * | ||
1274 | */ | ||
1275 | static void pch_vbus_gpio_work_fall(struct work_struct *irq_work) | ||
1276 | { | ||
1277 | struct pch_vbus_gpio_data *vbus_gpio = container_of(irq_work, | ||
1278 | struct pch_vbus_gpio_data, irq_work_fall); | ||
1279 | struct pch_udc_dev *dev = | ||
1280 | container_of(vbus_gpio, struct pch_udc_dev, vbus_gpio); | ||
1281 | int vbus_saved = -1; | ||
1282 | int vbus; | ||
1283 | int count; | ||
1284 | |||
1285 | if (!dev->vbus_gpio.port) | ||
1286 | return; | ||
1287 | |||
1288 | for (count = 0; count < (PCH_VBUS_PERIOD / PCH_VBUS_INTERVAL); | ||
1289 | count++) { | ||
1290 | vbus = pch_vbus_gpio_get_value(dev); | ||
1291 | |||
1292 | if ((vbus_saved == vbus) && (vbus == 0)) { | ||
1293 | dev_dbg(&dev->pdev->dev, "VBUS fell"); | ||
1294 | if (dev->driver | ||
1295 | && dev->driver->disconnect) { | ||
1296 | dev->driver->disconnect( | ||
1297 | &dev->gadget); | ||
1298 | } | ||
1299 | pch_udc_reconnect(dev); | ||
1300 | dev_dbg(&dev->pdev->dev, "VBUS fell"); | ||
1301 | return; | ||
1302 | } | ||
1303 | vbus_saved = vbus; | ||
1304 | mdelay(PCH_VBUS_INTERVAL); | ||
1305 | } | ||
1306 | } | ||
1307 | |||
1308 | /** | ||
1309 | * pch_vbus_gpio_init() - This API initializes GPIO port detecting VBUS. | ||
1310 | * @dev: Reference to the driver structure | ||
1311 | * @vbus_gpio Number of GPIO port to detect gpio | ||
1312 | * | ||
1313 | * Return codes: | ||
1314 | * 0: Success | ||
1315 | * -EINVAL: GPIO port is invalid or can't be initialized. | ||
1316 | */ | ||
1317 | static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port) | ||
1318 | { | ||
1319 | int err; | ||
1320 | |||
1321 | dev->vbus_gpio.port = 0; | ||
1322 | |||
1323 | if (vbus_gpio_port <= -1) | ||
1324 | return -EINVAL; | ||
1325 | |||
1326 | err = gpio_is_valid(vbus_gpio_port); | ||
1327 | if (!err) { | ||
1328 | pr_err("%s: gpio port %d is invalid\n", | ||
1329 | __func__, vbus_gpio_port); | ||
1330 | return -EINVAL; | ||
1331 | } | ||
1332 | |||
1333 | err = gpio_request(vbus_gpio_port, "pch_vbus"); | ||
1334 | if (err) { | ||
1335 | pr_err("%s: can't request gpio port %d, err: %d\n", | ||
1336 | __func__, vbus_gpio_port, err); | ||
1337 | return -EINVAL; | ||
1338 | } | ||
1339 | |||
1340 | dev->vbus_gpio.port = vbus_gpio_port; | ||
1341 | gpio_direction_input(vbus_gpio_port); | ||
1342 | INIT_WORK(&dev->vbus_gpio.irq_work_fall, pch_vbus_gpio_work_fall); | ||
1343 | |||
1344 | return 0; | ||
1345 | } | ||
1346 | |||
1347 | /** | ||
1348 | * pch_vbus_gpio_free() - This API frees resources of GPIO port | ||
1349 | * @dev: Reference to the driver structure | ||
1350 | */ | ||
1351 | static void pch_vbus_gpio_free(struct pch_udc_dev *dev) | ||
1352 | { | ||
1353 | if (dev->vbus_gpio.port) | ||
1354 | gpio_free(dev->vbus_gpio.port); | ||
1355 | } | ||
1356 | |||
1357 | /** | ||
1229 | * complete_req() - This API is invoked from the driver when processing | 1358 | * complete_req() - This API is invoked from the driver when processing |
1230 | * of a request is complete | 1359 | * of a request is complete |
1231 | * @ep: Reference to the endpoint structure | 1360 | * @ep: Reference to the endpoint structure |
@@ -2510,6 +2639,8 @@ static void pch_udc_svc_cfg_interrupt(struct pch_udc_dev *dev) | |||
2510 | */ | 2639 | */ |
2511 | static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr) | 2640 | static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr) |
2512 | { | 2641 | { |
2642 | int vbus; | ||
2643 | |||
2513 | /* USB Reset Interrupt */ | 2644 | /* USB Reset Interrupt */ |
2514 | if (dev_intr & UDC_DEVINT_UR) { | 2645 | if (dev_intr & UDC_DEVINT_UR) { |
2515 | pch_udc_svc_ur_interrupt(dev); | 2646 | pch_udc_svc_ur_interrupt(dev); |
@@ -2535,14 +2666,19 @@ static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr) | |||
2535 | spin_lock(&dev->lock); | 2666 | spin_lock(&dev->lock); |
2536 | } | 2667 | } |
2537 | 2668 | ||
2538 | if (dev->vbus_session == 0) { | 2669 | vbus = pch_vbus_gpio_get_value(dev); |
2670 | if ((dev->vbus_session == 0) | ||
2671 | && (vbus != 1)) { | ||
2539 | if (dev->driver && dev->driver->disconnect) { | 2672 | if (dev->driver && dev->driver->disconnect) { |
2540 | spin_unlock(&dev->lock); | 2673 | spin_unlock(&dev->lock); |
2541 | dev->driver->disconnect(&dev->gadget); | 2674 | dev->driver->disconnect(&dev->gadget); |
2542 | spin_lock(&dev->lock); | 2675 | spin_lock(&dev->lock); |
2543 | } | 2676 | } |
2544 | pch_udc_reconnect(dev); | 2677 | pch_udc_reconnect(dev); |
2545 | } | 2678 | } else if ((dev->vbus_session == 0) |
2679 | && (vbus == 1)) | ||
2680 | schedule_work(&dev->vbus_gpio.irq_work_fall); | ||
2681 | |||
2546 | dev_dbg(&dev->pdev->dev, "USB_SUSPEND\n"); | 2682 | dev_dbg(&dev->pdev->dev, "USB_SUSPEND\n"); |
2547 | } | 2683 | } |
2548 | /* Clear the SOF interrupt, if enabled */ | 2684 | /* Clear the SOF interrupt, if enabled */ |
@@ -2704,6 +2840,7 @@ static int pch_udc_pcd_init(struct pch_udc_dev *dev) | |||
2704 | { | 2840 | { |
2705 | pch_udc_init(dev); | 2841 | pch_udc_init(dev); |
2706 | pch_udc_pcd_reinit(dev); | 2842 | pch_udc_pcd_reinit(dev); |
2843 | pch_vbus_gpio_init(dev, vbus_gpio_port); | ||
2707 | return 0; | 2844 | return 0; |
2708 | } | 2845 | } |
2709 | 2846 | ||
@@ -2882,6 +3019,8 @@ static void pch_udc_remove(struct pci_dev *pdev) | |||
2882 | UDC_EP0OUT_BUFF_SIZE * 4, DMA_FROM_DEVICE); | 3019 | UDC_EP0OUT_BUFF_SIZE * 4, DMA_FROM_DEVICE); |
2883 | kfree(dev->ep0out_buf); | 3020 | kfree(dev->ep0out_buf); |
2884 | 3021 | ||
3022 | pch_vbus_gpio_free(dev); | ||
3023 | |||
2885 | pch_udc_exit(dev); | 3024 | pch_udc_exit(dev); |
2886 | 3025 | ||
2887 | if (dev->irq_registered) | 3026 | if (dev->irq_registered) |