aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget
diff options
context:
space:
mode:
authorDarius Augulis <augulis.darius@gmail.com>2009-01-21 08:19:19 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2009-03-24 19:20:29 -0400
commitb633d28e2c5fbe1c8d163892644f57df04aa1421 (patch)
treeae4bb3f6086cc7f0dd2034fcb3be3d377945f602 /drivers/usb/gadget
parentd24921a36df31332c32e1bb539671284d9e36bfa (diff)
USB: imx_udc: Fix IMX UDC gadget general irq handling
Workaround of hw bug in IMX UDC. This bug causes wrong handling of CFG_CHG interrupt. Workaround is documented inline source code. Signed-off-by: Darius Augulis <augulis.darius@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r--drivers/usb/gadget/imx_udc.c158
-rw-r--r--drivers/usb/gadget/imx_udc.h1
2 files changed, 94 insertions, 65 deletions
diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c
index e590464c3a50..8d3c6a960988 100644
--- a/drivers/usb/gadget/imx_udc.c
+++ b/drivers/usb/gadget/imx_udc.c
@@ -28,6 +28,7 @@
28#include <linux/dma-mapping.h> 28#include <linux/dma-mapping.h>
29#include <linux/clk.h> 29#include <linux/clk.h>
30#include <linux/delay.h> 30#include <linux/delay.h>
31#include <linux/timer.h>
31 32
32#include <linux/usb/ch9.h> 33#include <linux/usb/ch9.h>
33#include <linux/usb/gadget.h> 34#include <linux/usb/gadget.h>
@@ -1013,70 +1014,32 @@ static void udc_stop_activity(struct imx_udc_struct *imx_usb,
1013 ******************************************************************************* 1014 *******************************************************************************
1014 */ 1015 */
1015 1016
1016static irqreturn_t imx_udc_irq(int irq, void *dev) 1017/*
1018 * Called when timer expires.
1019 * Timer is started when CFG_CHG is received.
1020 */
1021static void handle_config(unsigned long data)
1017{ 1022{
1018 struct imx_udc_struct *imx_usb = dev; 1023 struct imx_udc_struct *imx_usb = (void *)data;
1019 struct usb_ctrlrequest u; 1024 struct usb_ctrlrequest u;
1020 int temp, cfg, intf, alt; 1025 int temp, cfg, intf, alt;
1021 int intr = __raw_readl(imx_usb->base + USB_INTR);
1022
1023 if (intr & (INTR_WAKEUP | INTR_SUSPEND | INTR_RESUME | INTR_RESET_START
1024 | INTR_RESET_STOP | INTR_CFG_CHG)) {
1025 dump_intr(__func__, intr, imx_usb->dev);
1026 dump_usb_stat(__func__, imx_usb);
1027 }
1028
1029 if (!imx_usb->driver)
1030 goto end_irq;
1031
1032 if (intr & INTR_WAKEUP) {
1033 if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN
1034 && imx_usb->driver && imx_usb->driver->resume)
1035 imx_usb->driver->resume(&imx_usb->gadget);
1036 imx_usb->set_config = 0;
1037 imx_usb->gadget.speed = USB_SPEED_FULL;
1038 }
1039
1040 if (intr & INTR_SUSPEND) {
1041 if (imx_usb->gadget.speed != USB_SPEED_UNKNOWN
1042 && imx_usb->driver && imx_usb->driver->suspend)
1043 imx_usb->driver->suspend(&imx_usb->gadget);
1044 imx_usb->set_config = 0;
1045 imx_usb->gadget.speed = USB_SPEED_UNKNOWN;
1046 }
1047 1026
1048 if (intr & INTR_RESET_START) { 1027 local_irq_disable();
1049 __raw_writel(intr, imx_usb->base + USB_INTR);
1050 udc_stop_activity(imx_usb, imx_usb->driver);
1051 imx_usb->set_config = 0;
1052 imx_usb->gadget.speed = USB_SPEED_UNKNOWN;
1053 }
1054
1055 if (intr & INTR_RESET_STOP)
1056 imx_usb->gadget.speed = USB_SPEED_FULL;
1057 1028
1058 if (intr & INTR_CFG_CHG) { 1029 temp = __raw_readl(imx_usb->base + USB_STAT);
1059 __raw_writel(INTR_CFG_CHG, imx_usb->base + USB_INTR); 1030 cfg = (temp & STAT_CFG) >> 5;
1060 temp = __raw_readl(imx_usb->base + USB_STAT); 1031 intf = (temp & STAT_INTF) >> 3;
1061 cfg = (temp & STAT_CFG) >> 5; 1032 alt = temp & STAT_ALTSET;
1062 intf = (temp & STAT_INTF) >> 3;
1063 alt = temp & STAT_ALTSET;
1064 1033
1065 D_REQ(imx_usb->dev, 1034 D_REQ(imx_usb->dev,
1066 "<%s> orig config C=%d, I=%d, A=%d / " 1035 "<%s> orig config C=%d, I=%d, A=%d / "
1067 "req config C=%d, I=%d, A=%d\n", 1036 "req config C=%d, I=%d, A=%d\n",
1068 __func__, imx_usb->cfg, imx_usb->intf, imx_usb->alt, 1037 __func__, imx_usb->cfg, imx_usb->intf, imx_usb->alt,
1069 cfg, intf, alt); 1038 cfg, intf, alt);
1070 1039
1071 if (cfg != 1 && cfg != 2) 1040 if (cfg == 1 || cfg == 2) {
1072 goto end_irq;
1073 1041
1074 imx_usb->set_config = 0;
1075
1076 /* Config setup */
1077 if (imx_usb->cfg != cfg) { 1042 if (imx_usb->cfg != cfg) {
1078 D_REQ(imx_usb->dev,
1079 "<%s> Change config start\n", __func__);
1080 u.bRequest = USB_REQ_SET_CONFIGURATION; 1043 u.bRequest = USB_REQ_SET_CONFIGURATION;
1081 u.bRequestType = USB_DIR_OUT | 1044 u.bRequestType = USB_DIR_OUT |
1082 USB_TYPE_STANDARD | 1045 USB_TYPE_STANDARD |
@@ -1085,16 +1048,10 @@ static irqreturn_t imx_udc_irq(int irq, void *dev)
1085 u.wIndex = 0; 1048 u.wIndex = 0;
1086 u.wLength = 0; 1049 u.wLength = 0;
1087 imx_usb->cfg = cfg; 1050 imx_usb->cfg = cfg;
1088 imx_usb->set_config = 1;
1089 imx_usb->driver->setup(&imx_usb->gadget, &u); 1051 imx_usb->driver->setup(&imx_usb->gadget, &u);
1090 imx_usb->set_config = 0;
1091 D_REQ(imx_usb->dev,
1092 "<%s> Change config done\n", __func__);
1093 1052
1094 } 1053 }
1095 if (imx_usb->intf != intf || imx_usb->alt != alt) { 1054 if (imx_usb->intf != intf || imx_usb->alt != alt) {
1096 D_REQ(imx_usb->dev,
1097 "<%s> Change interface start\n", __func__);
1098 u.bRequest = USB_REQ_SET_INTERFACE; 1055 u.bRequest = USB_REQ_SET_INTERFACE;
1099 u.bRequestType = USB_DIR_OUT | 1056 u.bRequestType = USB_DIR_OUT |
1100 USB_TYPE_STANDARD | 1057 USB_TYPE_STANDARD |
@@ -1104,14 +1061,30 @@ static irqreturn_t imx_udc_irq(int irq, void *dev)
1104 u.wLength = 0; 1061 u.wLength = 0;
1105 imx_usb->intf = intf; 1062 imx_usb->intf = intf;
1106 imx_usb->alt = alt; 1063 imx_usb->alt = alt;
1107 imx_usb->set_config = 1;
1108 imx_usb->driver->setup(&imx_usb->gadget, &u); 1064 imx_usb->driver->setup(&imx_usb->gadget, &u);
1109 imx_usb->set_config = 0;
1110 D_REQ(imx_usb->dev,
1111 "<%s> Change interface done\n", __func__);
1112 } 1065 }
1113 } 1066 }
1114 1067
1068 imx_usb->set_config = 0;
1069
1070 local_irq_enable();
1071}
1072
1073static irqreturn_t imx_udc_irq(int irq, void *dev)
1074{
1075 struct imx_udc_struct *imx_usb = dev;
1076 int intr = __raw_readl(imx_usb->base + USB_INTR);
1077 int temp;
1078
1079 if (intr & (INTR_WAKEUP | INTR_SUSPEND | INTR_RESUME | INTR_RESET_START
1080 | INTR_RESET_STOP | INTR_CFG_CHG)) {
1081 dump_intr(__func__, intr, imx_usb->dev);
1082 dump_usb_stat(__func__, imx_usb);
1083 }
1084
1085 if (!imx_usb->driver)
1086 goto end_irq;
1087
1115 if (intr & INTR_SOF) { 1088 if (intr & INTR_SOF) {
1116 /* Copy from Freescale BSP. 1089 /* Copy from Freescale BSP.
1117 We must enable SOF intr and set CMDOVER. 1090 We must enable SOF intr and set CMDOVER.
@@ -1125,6 +1098,55 @@ static irqreturn_t imx_udc_irq(int irq, void *dev)
1125 } 1098 }
1126 } 1099 }
1127 1100
1101 if (intr & INTR_CFG_CHG) {
1102 /* A workaround of serious IMX UDC bug.
1103 Handling of CFG_CHG should be delayed for some time, because
1104 IMX does not NACK the host when CFG_CHG interrupt is pending.
1105 There is no time to handle current CFG_CHG
1106 if next CFG_CHG or SETUP packed is send immediately.
1107 We have to clear CFG_CHG, start the timer and
1108 NACK the host by setting CTRL_CMDOVER
1109 if it sends any SETUP packet.
1110 When timer expires, handler is called to handle configuration
1111 changes. While CFG_CHG is not handled (set_config=1),
1112 we must NACK the host to every SETUP packed.
1113 This delay prevents from going out of sync with host.
1114 */
1115 __raw_writel(INTR_CFG_CHG, imx_usb->base + USB_INTR);
1116 imx_usb->set_config = 1;
1117 mod_timer(&imx_usb->timer, jiffies + 5);
1118 goto end_irq;
1119 }
1120
1121 if (intr & INTR_WAKEUP) {
1122 if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN
1123 && imx_usb->driver && imx_usb->driver->resume)
1124 imx_usb->driver->resume(&imx_usb->gadget);
1125 imx_usb->set_config = 0;
1126 del_timer(&imx_usb->timer);
1127 imx_usb->gadget.speed = USB_SPEED_FULL;
1128 }
1129
1130 if (intr & INTR_SUSPEND) {
1131 if (imx_usb->gadget.speed != USB_SPEED_UNKNOWN
1132 && imx_usb->driver && imx_usb->driver->suspend)
1133 imx_usb->driver->suspend(&imx_usb->gadget);
1134 imx_usb->set_config = 0;
1135 del_timer(&imx_usb->timer);
1136 imx_usb->gadget.speed = USB_SPEED_UNKNOWN;
1137 }
1138
1139 if (intr & INTR_RESET_START) {
1140 __raw_writel(intr, imx_usb->base + USB_INTR);
1141 udc_stop_activity(imx_usb, imx_usb->driver);
1142 imx_usb->set_config = 0;
1143 del_timer(&imx_usb->timer);
1144 imx_usb->gadget.speed = USB_SPEED_UNKNOWN;
1145 }
1146
1147 if (intr & INTR_RESET_STOP)
1148 imx_usb->gadget.speed = USB_SPEED_FULL;
1149
1128end_irq: 1150end_irq:
1129 __raw_writel(intr, imx_usb->base + USB_INTR); 1151 __raw_writel(intr, imx_usb->base + USB_INTR);
1130 return IRQ_HANDLED; 1152 return IRQ_HANDLED;
@@ -1342,6 +1364,7 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
1342 1364
1343 udc_stop_activity(imx_usb, driver); 1365 udc_stop_activity(imx_usb, driver);
1344 imx_udc_disable(imx_usb); 1366 imx_udc_disable(imx_usb);
1367 del_timer(&imx_usb->timer);
1345 1368
1346 driver->unbind(&imx_usb->gadget); 1369 driver->unbind(&imx_usb->gadget);
1347 imx_usb->gadget.dev.driver = NULL; 1370 imx_usb->gadget.dev.driver = NULL;
@@ -1459,6 +1482,10 @@ static int __init imx_udc_probe(struct platform_device *pdev)
1459 usb_init_data(imx_usb); 1482 usb_init_data(imx_usb);
1460 imx_udc_init(imx_usb); 1483 imx_udc_init(imx_usb);
1461 1484
1485 init_timer(&imx_usb->timer);
1486 imx_usb->timer.function = handle_config;
1487 imx_usb->timer.data = (unsigned long)imx_usb;
1488
1462 return 0; 1489 return 0;
1463 1490
1464fail3: 1491fail3:
@@ -1481,6 +1508,7 @@ static int __exit imx_udc_remove(struct platform_device *pdev)
1481 int i; 1508 int i;
1482 1509
1483 imx_udc_disable(imx_usb); 1510 imx_udc_disable(imx_usb);
1511 del_timer(&imx_usb->timer);
1484 1512
1485 for (i = 0; i < IMX_USB_NB_EP + 1; i++) 1513 for (i = 0; i < IMX_USB_NB_EP + 1; i++)
1486 free_irq(imx_usb->usbd_int[i], imx_usb); 1514 free_irq(imx_usb->usbd_int[i], imx_usb);
diff --git a/drivers/usb/gadget/imx_udc.h b/drivers/usb/gadget/imx_udc.h
index 6b0b1e3d6fc7..b48ad59603d1 100644
--- a/drivers/usb/gadget/imx_udc.h
+++ b/drivers/usb/gadget/imx_udc.h
@@ -59,6 +59,7 @@ struct imx_udc_struct {
59 struct device *dev; 59 struct device *dev;
60 struct imx_ep_struct imx_ep[IMX_USB_NB_EP]; 60 struct imx_ep_struct imx_ep[IMX_USB_NB_EP];
61 struct clk *clk; 61 struct clk *clk;
62 struct timer_list timer;
62 enum ep0_state ep0state; 63 enum ep0_state ep0state;
63 struct resource *res; 64 struct resource *res;
64 void __iomem *base; 65 void __iomem *base;