Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Tomoya MORINAGA and committed by
Felipe Balbi
dd63180b 20edfbb6

+142 -3
+142 -3
drivers/usb/gadget/pch_udc.c
··· 15 15 #include <linux/interrupt.h> 16 16 #include <linux/usb/ch9.h> 17 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 26 /* Address offset of Registers */ 20 27 #define UDC_EP_REG_SHIFT 0x20 /* Offset to next EP */ ··· 303 296 }; 304 297 305 298 /** 299 + * struct pch_vbus_gpio_data - Structure holding GPIO informaton 300 + * for detecting VBUS 301 + * @port: gpio port number 302 + * @irq_work_fall Structure for WorkQueue 303 + */ 304 + struct pch_vbus_gpio_data { 305 + int port; 306 + struct work_struct irq_work_fall; 307 + }; 308 + 309 + /** 306 310 * struct pch_udc_dev - Structure holding complete information 307 311 * of the PCH USB device 308 312 * @gadget: gadget driver data ··· 341 323 * @base_addr: for mapped device memory 342 324 * @irq: IRQ line for the device 343 325 * @cfg_data: current cfg, intf, and alt in use 326 + * @vbus_gpio: GPIO informaton for detecting VBUS 344 327 */ 345 328 struct pch_udc_dev { 346 329 struct usb_gadget gadget; ··· 368 349 unsigned long phys_addr; 369 350 void __iomem *base_addr; 370 351 unsigned irq; 371 - struct pch_udc_cfg_data cfg_data; 352 + struct pch_udc_cfg_data cfg_data; 353 + struct pch_vbus_gpio_data vbus_gpio; 372 354 }; 373 355 374 356 #define PCH_UDC_PCI_BAR 1 ··· 1244 1224 .start = pch_udc_start, 1245 1225 .stop = pch_udc_stop, 1246 1226 }; 1227 + 1228 + /** 1229 + * pch_vbus_gpio_get_value() - This API gets value of GPIO port as VBUS status. 1230 + * @dev: Reference to the driver structure 1231 + * 1232 + * Return value: 1233 + * 1: VBUS is high 1234 + * 0: VBUS is low 1235 + * -1: It is not enable to detect VBUS using GPIO 1236 + */ 1237 + static int pch_vbus_gpio_get_value(struct pch_udc_dev *dev) 1238 + { 1239 + int vbus = 0; 1240 + 1241 + if (dev->vbus_gpio.port) 1242 + vbus = gpio_get_value(dev->vbus_gpio.port) ? 1 : 0; 1243 + else 1244 + vbus = -1; 1245 + 1246 + return vbus; 1247 + } 1248 + 1249 + /** 1250 + * pch_vbus_gpio_work_fall() - This API keeps watch on VBUS becoming Low. 1251 + * If VBUS is Low, disconnect is processed 1252 + * @irq_work: Structure for WorkQueue 1253 + * 1254 + */ 1255 + static void pch_vbus_gpio_work_fall(struct work_struct *irq_work) 1256 + { 1257 + struct pch_vbus_gpio_data *vbus_gpio = container_of(irq_work, 1258 + struct pch_vbus_gpio_data, irq_work_fall); 1259 + struct pch_udc_dev *dev = 1260 + container_of(vbus_gpio, struct pch_udc_dev, vbus_gpio); 1261 + int vbus_saved = -1; 1262 + int vbus; 1263 + int count; 1264 + 1265 + if (!dev->vbus_gpio.port) 1266 + return; 1267 + 1268 + for (count = 0; count < (PCH_VBUS_PERIOD / PCH_VBUS_INTERVAL); 1269 + count++) { 1270 + vbus = pch_vbus_gpio_get_value(dev); 1271 + 1272 + if ((vbus_saved == vbus) && (vbus == 0)) { 1273 + dev_dbg(&dev->pdev->dev, "VBUS fell"); 1274 + if (dev->driver 1275 + && dev->driver->disconnect) { 1276 + dev->driver->disconnect( 1277 + &dev->gadget); 1278 + } 1279 + pch_udc_reconnect(dev); 1280 + dev_dbg(&dev->pdev->dev, "VBUS fell"); 1281 + return; 1282 + } 1283 + vbus_saved = vbus; 1284 + mdelay(PCH_VBUS_INTERVAL); 1285 + } 1286 + } 1287 + 1288 + /** 1289 + * pch_vbus_gpio_init() - This API initializes GPIO port detecting VBUS. 1290 + * @dev: Reference to the driver structure 1291 + * @vbus_gpio Number of GPIO port to detect gpio 1292 + * 1293 + * Return codes: 1294 + * 0: Success 1295 + * -EINVAL: GPIO port is invalid or can't be initialized. 1296 + */ 1297 + static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port) 1298 + { 1299 + int err; 1300 + 1301 + dev->vbus_gpio.port = 0; 1302 + 1303 + if (vbus_gpio_port <= -1) 1304 + return -EINVAL; 1305 + 1306 + err = gpio_is_valid(vbus_gpio_port); 1307 + if (!err) { 1308 + pr_err("%s: gpio port %d is invalid\n", 1309 + __func__, vbus_gpio_port); 1310 + return -EINVAL; 1311 + } 1312 + 1313 + err = gpio_request(vbus_gpio_port, "pch_vbus"); 1314 + if (err) { 1315 + pr_err("%s: can't request gpio port %d, err: %d\n", 1316 + __func__, vbus_gpio_port, err); 1317 + return -EINVAL; 1318 + } 1319 + 1320 + dev->vbus_gpio.port = vbus_gpio_port; 1321 + gpio_direction_input(vbus_gpio_port); 1322 + INIT_WORK(&dev->vbus_gpio.irq_work_fall, pch_vbus_gpio_work_fall); 1323 + 1324 + return 0; 1325 + } 1326 + 1327 + /** 1328 + * pch_vbus_gpio_free() - This API frees resources of GPIO port 1329 + * @dev: Reference to the driver structure 1330 + */ 1331 + static void pch_vbus_gpio_free(struct pch_udc_dev *dev) 1332 + { 1333 + if (dev->vbus_gpio.port) 1334 + gpio_free(dev->vbus_gpio.port); 1335 + } 1247 1336 1248 1337 /** 1249 1338 * complete_req() - This API is invoked from the driver when processing ··· 2639 2510 */ 2640 2511 static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr) 2641 2512 { 2513 + int vbus; 2514 + 2642 2515 /* USB Reset Interrupt */ 2643 2516 if (dev_intr & UDC_DEVINT_UR) { 2644 2517 pch_udc_svc_ur_interrupt(dev); ··· 2666 2535 spin_lock(&dev->lock); 2667 2536 } 2668 2537 2669 - if (dev->vbus_session == 0) { 2538 + vbus = pch_vbus_gpio_get_value(dev); 2539 + if ((dev->vbus_session == 0) 2540 + && (vbus != 1)) { 2670 2541 if (dev->driver && dev->driver->disconnect) { 2671 2542 spin_unlock(&dev->lock); 2672 2543 dev->driver->disconnect(&dev->gadget); 2673 2544 spin_lock(&dev->lock); 2674 2545 } 2675 2546 pch_udc_reconnect(dev); 2676 - } 2547 + } else if ((dev->vbus_session == 0) 2548 + && (vbus == 1)) 2549 + schedule_work(&dev->vbus_gpio.irq_work_fall); 2550 + 2677 2551 dev_dbg(&dev->pdev->dev, "USB_SUSPEND\n"); 2678 2552 } 2679 2553 /* Clear the SOF interrupt, if enabled */ ··· 2840 2704 { 2841 2705 pch_udc_init(dev); 2842 2706 pch_udc_pcd_reinit(dev); 2707 + pch_vbus_gpio_init(dev, vbus_gpio_port); 2843 2708 return 0; 2844 2709 } 2845 2710 ··· 3018 2881 dma_unmap_single(&dev->pdev->dev, dev->dma_addr, 3019 2882 UDC_EP0OUT_BUFF_SIZE * 4, DMA_FROM_DEVICE); 3020 2883 kfree(dev->ep0out_buf); 2884 + 2885 + pch_vbus_gpio_free(dev); 3021 2886 3022 2887 pch_udc_exit(dev); 3023 2888