aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget
diff options
context:
space:
mode:
authorTomoya MORINAGA <tomoya.rohm@gmail.com>2012-02-03 02:14:18 -0500
committerFelipe Balbi <balbi@ti.com>2012-02-09 02:58:48 -0500
commit637b78eb31e0b167ed913f1750bb645dfeda38f0 (patch)
tree9496e1f2489bedef09c523bd0f0f0f960e553014 /drivers/usb/gadget
parentdd63180b758d5972fc90621af0741d5bfae1a684 (diff)
usb: gadget: pch_udc: Detecting VBUS through GPIO with interrupt
Problem: pch_udc continues operation even if VBUS becomes Low. pch_udc performs D+ pulling up before VBUS becomes High. USB device should be controlled according to VBUS state. Root cause: The current pch_udc is not always monitoring VBUS. Solution: The change of VBUS is detected using an interrupt of GPIO. If VBUS became Low, pch_udc handles 'disconnect'. After VBUS became High, a pull improves D+, and pch_udc handles 'connect'. [ balbi@ti.com : make it actually compile ] Signed-off-by: Tomoya MORINAGA <tomoya.rohm@gmail.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r--drivers/usb/gadget/pch_udc.c87
1 files changed, 83 insertions, 4 deletions
diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c
index 9c5ae2c10147..a992084d3890 100644
--- a/drivers/usb/gadget/pch_udc.c
+++ b/drivers/usb/gadget/pch_udc.c
@@ -306,11 +306,15 @@ struct pch_udc_ep {
306 * struct pch_vbus_gpio_data - Structure holding GPIO informaton 306 * struct pch_vbus_gpio_data - Structure holding GPIO informaton
307 * for detecting VBUS 307 * for detecting VBUS
308 * @port: gpio port number 308 * @port: gpio port number
309 * @intr: gpio interrupt number
309 * @irq_work_fall Structure for WorkQueue 310 * @irq_work_fall Structure for WorkQueue
311 * @irq_work_rise Structure for WorkQueue
310 */ 312 */
311struct pch_vbus_gpio_data { 313struct pch_vbus_gpio_data {
312 int port; 314 int port;
315 int intr;
313 struct work_struct irq_work_fall; 316 struct work_struct irq_work_fall;
317 struct work_struct irq_work_rise;
314}; 318};
315 319
316/** 320/**
@@ -1296,8 +1300,10 @@ static void pch_vbus_gpio_work_fall(struct work_struct *irq_work)
1296 dev->driver->disconnect( 1300 dev->driver->disconnect(
1297 &dev->gadget); 1301 &dev->gadget);
1298 } 1302 }
1299 pch_udc_reconnect(dev); 1303 if (dev->vbus_gpio.intr)
1300 dev_dbg(&dev->pdev->dev, "VBUS fell"); 1304 pch_udc_init(dev);
1305 else
1306 pch_udc_reconnect(dev);
1301 return; 1307 return;
1302 } 1308 }
1303 vbus_saved = vbus; 1309 vbus_saved = vbus;
@@ -1306,6 +1312,57 @@ static void pch_vbus_gpio_work_fall(struct work_struct *irq_work)
1306} 1312}
1307 1313
1308/** 1314/**
1315 * pch_vbus_gpio_work_rise() - This API checks VBUS is High.
1316 * If VBUS is High, connect is processed
1317 * @irq_work: Structure for WorkQueue
1318 *
1319 */
1320static void pch_vbus_gpio_work_rise(struct work_struct *irq_work)
1321{
1322 struct pch_vbus_gpio_data *vbus_gpio = container_of(irq_work,
1323 struct pch_vbus_gpio_data, irq_work_rise);
1324 struct pch_udc_dev *dev =
1325 container_of(vbus_gpio, struct pch_udc_dev, vbus_gpio);
1326 int vbus;
1327
1328 if (!dev->vbus_gpio.port)
1329 return;
1330
1331 mdelay(PCH_VBUS_INTERVAL);
1332 vbus = pch_vbus_gpio_get_value(dev);
1333
1334 if (vbus == 1) {
1335 dev_dbg(&dev->pdev->dev, "VBUS rose");
1336 pch_udc_reconnect(dev);
1337 return;
1338 }
1339}
1340
1341/**
1342 * pch_vbus_gpio_irq() - IRQ handler for GPIO intrerrupt for changing VBUS
1343 * @irq: Interrupt request number
1344 * @dev: Reference to the device structure
1345 *
1346 * Return codes:
1347 * 0: Success
1348 * -EINVAL: GPIO port is invalid or can't be initialized.
1349 */
1350static irqreturn_t pch_vbus_gpio_irq(int irq, void *data)
1351{
1352 struct pch_udc_dev *dev = (struct pch_udc_dev *)data;
1353
1354 if (!dev->vbus_gpio.port || !dev->vbus_gpio.intr)
1355 return IRQ_NONE;
1356
1357 if (pch_vbus_gpio_get_value(dev))
1358 schedule_work(&dev->vbus_gpio.irq_work_rise);
1359 else
1360 schedule_work(&dev->vbus_gpio.irq_work_fall);
1361
1362 return IRQ_HANDLED;
1363}
1364
1365/**
1309 * pch_vbus_gpio_init() - This API initializes GPIO port detecting VBUS. 1366 * pch_vbus_gpio_init() - This API initializes GPIO port detecting VBUS.
1310 * @dev: Reference to the driver structure 1367 * @dev: Reference to the driver structure
1311 * @vbus_gpio Number of GPIO port to detect gpio 1368 * @vbus_gpio Number of GPIO port to detect gpio
@@ -1317,8 +1374,10 @@ static void pch_vbus_gpio_work_fall(struct work_struct *irq_work)
1317static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port) 1374static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port)
1318{ 1375{
1319 int err; 1376 int err;
1377 int irq_num = 0;
1320 1378
1321 dev->vbus_gpio.port = 0; 1379 dev->vbus_gpio.port = 0;
1380 dev->vbus_gpio.intr = 0;
1322 1381
1323 if (vbus_gpio_port <= -1) 1382 if (vbus_gpio_port <= -1)
1324 return -EINVAL; 1383 return -EINVAL;
@@ -1341,6 +1400,21 @@ static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port)
1341 gpio_direction_input(vbus_gpio_port); 1400 gpio_direction_input(vbus_gpio_port);
1342 INIT_WORK(&dev->vbus_gpio.irq_work_fall, pch_vbus_gpio_work_fall); 1401 INIT_WORK(&dev->vbus_gpio.irq_work_fall, pch_vbus_gpio_work_fall);
1343 1402
1403 irq_num = gpio_to_irq(vbus_gpio_port);
1404 if (irq_num > 0) {
1405 irq_set_irq_type(irq_num, IRQ_TYPE_EDGE_BOTH);
1406 err = request_irq(irq_num, pch_vbus_gpio_irq, 0,
1407 "vbus_detect", dev);
1408 if (!err) {
1409 dev->vbus_gpio.intr = irq_num;
1410 INIT_WORK(&dev->vbus_gpio.irq_work_rise,
1411 pch_vbus_gpio_work_rise);
1412 } else {
1413 pr_err("%s: can't request irq %d, err: %d\n",
1414 __func__, irq_num, err);
1415 }
1416 }
1417
1344 return 0; 1418 return 0;
1345} 1419}
1346 1420
@@ -1350,6 +1424,9 @@ static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port)
1350 */ 1424 */
1351static void pch_vbus_gpio_free(struct pch_udc_dev *dev) 1425static void pch_vbus_gpio_free(struct pch_udc_dev *dev)
1352{ 1426{
1427 if (dev->vbus_gpio.intr)
1428 free_irq(dev->vbus_gpio.intr, dev);
1429
1353 if (dev->vbus_gpio.port) 1430 if (dev->vbus_gpio.port)
1354 gpio_free(dev->vbus_gpio.port); 1431 gpio_free(dev->vbus_gpio.port);
1355} 1432}
@@ -2676,7 +2753,8 @@ static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr)
2676 } 2753 }
2677 pch_udc_reconnect(dev); 2754 pch_udc_reconnect(dev);
2678 } else if ((dev->vbus_session == 0) 2755 } else if ((dev->vbus_session == 0)
2679 && (vbus == 1)) 2756 && (vbus == 1)
2757 && !dev->vbus_gpio.intr)
2680 schedule_work(&dev->vbus_gpio.irq_work_fall); 2758 schedule_work(&dev->vbus_gpio.irq_work_fall);
2681 2759
2682 dev_dbg(&dev->pdev->dev, "USB_SUSPEND\n"); 2760 dev_dbg(&dev->pdev->dev, "USB_SUSPEND\n");
@@ -2941,7 +3019,8 @@ static int pch_udc_start(struct usb_gadget_driver *driver,
2941 pch_udc_setup_ep0(dev); 3019 pch_udc_setup_ep0(dev);
2942 3020
2943 /* clear SD */ 3021 /* clear SD */
2944 pch_udc_clear_disconnect(dev); 3022 if ((pch_vbus_gpio_get_value(dev) != 0) || !dev->vbus_gpio.intr)
3023 pch_udc_clear_disconnect(dev);
2945 3024
2946 dev->connected = 1; 3025 dev->connected = 1;
2947 return 0; 3026 return 0;