diff options
-rw-r--r-- | drivers/usb/host/isp1760-hcd.c | 138 | ||||
-rw-r--r-- | drivers/usb/host/isp1760-hcd.h | 3 |
2 files changed, 96 insertions, 45 deletions
diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index e399e235f656..14c9238a5017 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/uaccess.h> | 21 | #include <linux/uaccess.h> |
22 | #include <linux/io.h> | 22 | #include <linux/io.h> |
23 | #include <linux/mm.h> | 23 | #include <linux/mm.h> |
24 | #include <linux/timer.h> | ||
24 | #include <asm/unaligned.h> | 25 | #include <asm/unaligned.h> |
25 | #include <asm/cacheflush.h> | 26 | #include <asm/cacheflush.h> |
26 | 27 | ||
@@ -39,7 +40,6 @@ struct isp1760_hcd { | |||
39 | int int_done_map; | 40 | int int_done_map; |
40 | struct memory_chunk memory_pool[BLOCKS]; | 41 | struct memory_chunk memory_pool[BLOCKS]; |
41 | struct list_head controlqhs, bulkqhs, interruptqhs; | 42 | struct list_head controlqhs, bulkqhs, interruptqhs; |
42 | int active_ptds; | ||
43 | 43 | ||
44 | /* periodic schedule support */ | 44 | /* periodic schedule support */ |
45 | #define DEFAULT_I_TDPS 1024 | 45 | #define DEFAULT_I_TDPS 1024 |
@@ -489,10 +489,6 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) | |||
489 | 16 : 32, (priv->devflags & ISP1760_FLAG_ANALOG_OC) ? | 489 | 16 : 32, (priv->devflags & ISP1760_FLAG_ANALOG_OC) ? |
490 | "analog" : "digital"); | 490 | "analog" : "digital"); |
491 | 491 | ||
492 | /* This is weird: at the first plug-in of a device there seems to be | ||
493 | one packet queued that never gets returned? */ | ||
494 | priv->active_ptds = -1; | ||
495 | |||
496 | /* ATL reset */ | 492 | /* ATL reset */ |
497 | reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode | ALL_ATX_RESET); | 493 | reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode | ALL_ATX_RESET); |
498 | mdelay(10); | 494 | mdelay(10); |
@@ -741,8 +737,8 @@ static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot, | |||
741 | qh->slot = slot; | 737 | qh->slot = slot; |
742 | qtd->status = QTD_XFER_STARTED; /* Set this before writing ptd, since | 738 | qtd->status = QTD_XFER_STARTED; /* Set this before writing ptd, since |
743 | interrupt routine may preempt and expects this value. */ | 739 | interrupt routine may preempt and expects this value. */ |
740 | slots[slot].timestamp = jiffies; | ||
744 | ptd_write(hcd->regs, ptd_offset, slot, ptd); | 741 | ptd_write(hcd->regs, ptd_offset, slot, ptd); |
745 | priv->active_ptds++; | ||
746 | 742 | ||
747 | /* Make sure done map has not triggered from some unlinked transfer */ | 743 | /* Make sure done map has not triggered from some unlinked transfer */ |
748 | if (ptd_offset == ATL_PTD_OFFSET) { | 744 | if (ptd_offset == ATL_PTD_OFFSET) { |
@@ -1091,11 +1087,9 @@ static int check_atl_transfer(struct usb_hcd *hcd, struct ptd *ptd, | |||
1091 | return PTD_STATE_QTD_DONE; | 1087 | return PTD_STATE_QTD_DONE; |
1092 | } | 1088 | } |
1093 | 1089 | ||
1094 | static irqreturn_t isp1760_irq(struct usb_hcd *hcd) | 1090 | static void handle_done_ptds(struct usb_hcd *hcd) |
1095 | { | 1091 | { |
1096 | struct isp1760_hcd *priv = hcd_to_priv(hcd); | 1092 | struct isp1760_hcd *priv = hcd_to_priv(hcd); |
1097 | u32 imask; | ||
1098 | irqreturn_t irqret = IRQ_NONE; | ||
1099 | struct ptd ptd; | 1093 | struct ptd ptd; |
1100 | struct isp1760_qh *qh; | 1094 | struct isp1760_qh *qh; |
1101 | int slot; | 1095 | int slot; |
@@ -1104,27 +1098,14 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd) | |||
1104 | u32 ptd_offset; | 1098 | u32 ptd_offset; |
1105 | struct isp1760_qtd *qtd; | 1099 | struct isp1760_qtd *qtd; |
1106 | int modified; | 1100 | int modified; |
1107 | static int last_active_ptds; | 1101 | int skip_map; |
1108 | int int_skip_map, atl_skip_map; | ||
1109 | |||
1110 | spin_lock(&priv->lock); | ||
1111 | |||
1112 | if (!(hcd->state & HC_STATE_RUNNING)) | ||
1113 | goto leave; | ||
1114 | |||
1115 | imask = reg_read32(hcd->regs, HC_INTERRUPT_REG); | ||
1116 | if (unlikely(!imask)) | ||
1117 | goto leave; | ||
1118 | reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); /* Clear */ | ||
1119 | 1102 | ||
1120 | int_skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); | 1103 | skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); |
1121 | atl_skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); | 1104 | priv->int_done_map &= ~skip_map; |
1122 | priv->int_done_map |= reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG); | 1105 | skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); |
1123 | priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG); | 1106 | priv->atl_done_map &= ~skip_map; |
1124 | priv->int_done_map &= ~int_skip_map; | ||
1125 | priv->atl_done_map &= ~atl_skip_map; | ||
1126 | 1107 | ||
1127 | modified = priv->int_done_map | priv->atl_done_map; | 1108 | modified = priv->int_done_map || priv->atl_done_map; |
1128 | 1109 | ||
1129 | while (priv->int_done_map || priv->atl_done_map) { | 1110 | while (priv->int_done_map || priv->atl_done_map) { |
1130 | if (priv->int_done_map) { | 1111 | if (priv->int_done_map) { |
@@ -1163,7 +1144,6 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd) | |||
1163 | slots[slot].qtd = NULL; | 1144 | slots[slot].qtd = NULL; |
1164 | qh = slots[slot].qh; | 1145 | qh = slots[slot].qh; |
1165 | slots[slot].qh = NULL; | 1146 | slots[slot].qh = NULL; |
1166 | priv->active_ptds--; | ||
1167 | qh->slot = -1; | 1147 | qh->slot = -1; |
1168 | 1148 | ||
1169 | WARN_ON(qtd->status != QTD_XFER_STARTED); | 1149 | WARN_ON(qtd->status != QTD_XFER_STARTED); |
@@ -1234,22 +1214,28 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd) | |||
1234 | 1214 | ||
1235 | if (modified) | 1215 | if (modified) |
1236 | schedule_ptds(hcd); | 1216 | schedule_ptds(hcd); |
1217 | } | ||
1237 | 1218 | ||
1238 | /* ISP1760 Errata 2 explains that interrupts may be missed (or not | 1219 | static irqreturn_t isp1760_irq(struct usb_hcd *hcd) |
1239 | happen?) if two USB devices are running simultaneously. Perhaps | 1220 | { |
1240 | this happens when a PTD is finished during interrupt handling; | 1221 | struct isp1760_hcd *priv = hcd_to_priv(hcd); |
1241 | enable SOF interrupts if PTDs are still scheduled when exiting this | 1222 | u32 imask; |
1242 | interrupt handler, just to be safe. */ | 1223 | irqreturn_t irqret = IRQ_NONE; |
1243 | 1224 | ||
1244 | if (priv->active_ptds != last_active_ptds) { | 1225 | spin_lock(&priv->lock); |
1245 | if (priv->active_ptds > 0) | 1226 | |
1246 | reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, | 1227 | if (!(hcd->state & HC_STATE_RUNNING)) |
1247 | INTERRUPT_ENABLE_SOT_MASK); | 1228 | goto leave; |
1248 | else | 1229 | |
1249 | reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, | 1230 | imask = reg_read32(hcd->regs, HC_INTERRUPT_REG); |
1250 | INTERRUPT_ENABLE_MASK); | 1231 | if (unlikely(!imask)) |
1251 | last_active_ptds = priv->active_ptds; | 1232 | goto leave; |
1252 | } | 1233 | reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); /* Clear */ |
1234 | |||
1235 | priv->int_done_map |= reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG); | ||
1236 | priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG); | ||
1237 | |||
1238 | handle_done_ptds(hcd); | ||
1253 | 1239 | ||
1254 | irqret = IRQ_HANDLED; | 1240 | irqret = IRQ_HANDLED; |
1255 | leave: | 1241 | leave: |
@@ -1258,6 +1244,63 @@ leave: | |||
1258 | return irqret; | 1244 | return irqret; |
1259 | } | 1245 | } |
1260 | 1246 | ||
1247 | /* | ||
1248 | * Workaround for problem described in chip errata 2: | ||
1249 | * | ||
1250 | * Sometimes interrupts are not generated when ATL (not INT?) completion occurs. | ||
1251 | * One solution suggested in the errata is to use SOF interrupts _instead_of_ | ||
1252 | * ATL done interrupts (the "instead of" might be important since it seems | ||
1253 | * enabling ATL interrupts also causes the chip to sometimes - rarely - "forget" | ||
1254 | * to set the PTD's done bit in addition to not generating an interrupt!). | ||
1255 | * | ||
1256 | * So if we use SOF + ATL interrupts, we sometimes get stale PTDs since their | ||
1257 | * done bit is not being set. This is bad - it blocks the endpoint until reboot. | ||
1258 | * | ||
1259 | * If we use SOF interrupts only, we get latency between ptd completion and the | ||
1260 | * actual handling. This is very noticeable in testusb runs which takes several | ||
1261 | * minutes longer without ATL interrupts. | ||
1262 | * | ||
1263 | * A better solution is to run the code below every SLOT_CHECK_PERIOD ms. If it | ||
1264 | * finds active ATL slots which are older than SLOT_TIMEOUT ms, it checks the | ||
1265 | * slot's ACTIVE and VALID bits. If these are not set, the ptd is considered | ||
1266 | * completed and its done map bit is set. | ||
1267 | * | ||
1268 | * The values of SLOT_TIMEOUT and SLOT_CHECK_PERIOD have been arbitrarily chosen | ||
1269 | * not to cause too much lag when this HW bug occurs, while still hopefully | ||
1270 | * ensuring that the check does not falsely trigger. | ||
1271 | */ | ||
1272 | #define SLOT_TIMEOUT 180 | ||
1273 | #define SLOT_CHECK_PERIOD 200 | ||
1274 | static struct timer_list errata2_timer; | ||
1275 | |||
1276 | void errata2_function(unsigned long data) | ||
1277 | { | ||
1278 | struct usb_hcd *hcd = (struct usb_hcd *) data; | ||
1279 | struct isp1760_hcd *priv = hcd_to_priv(hcd); | ||
1280 | int slot; | ||
1281 | struct ptd ptd; | ||
1282 | unsigned long spinflags; | ||
1283 | |||
1284 | spin_lock_irqsave(&priv->lock, spinflags); | ||
1285 | |||
1286 | for (slot = 0; slot < 32; slot++) | ||
1287 | if ((priv->atl_slots[slot].qh || priv->atl_slots[slot].qtd) && | ||
1288 | time_after(jiffies + SLOT_TIMEOUT * HZ / 1000, | ||
1289 | priv->atl_slots[slot].timestamp)) { | ||
1290 | ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd); | ||
1291 | if (!FROM_DW0_VALID(ptd.dw0) && | ||
1292 | !FROM_DW3_ACTIVE(ptd.dw3)) | ||
1293 | priv->atl_done_map |= 1 << slot; | ||
1294 | } | ||
1295 | |||
1296 | handle_done_ptds(hcd); | ||
1297 | |||
1298 | spin_unlock_irqrestore(&priv->lock, spinflags); | ||
1299 | |||
1300 | errata2_timer.expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000; | ||
1301 | add_timer(&errata2_timer); | ||
1302 | } | ||
1303 | |||
1261 | static int isp1760_run(struct usb_hcd *hcd) | 1304 | static int isp1760_run(struct usb_hcd *hcd) |
1262 | { | 1305 | { |
1263 | int retval; | 1306 | int retval; |
@@ -1303,6 +1346,12 @@ static int isp1760_run(struct usb_hcd *hcd) | |||
1303 | if (retval) | 1346 | if (retval) |
1304 | return retval; | 1347 | return retval; |
1305 | 1348 | ||
1349 | init_timer(&errata2_timer); | ||
1350 | errata2_timer.function = errata2_function; | ||
1351 | errata2_timer.data = (unsigned long) hcd; | ||
1352 | errata2_timer.expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000; | ||
1353 | add_timer(&errata2_timer); | ||
1354 | |||
1306 | chipid = reg_read32(hcd->regs, HC_CHIP_ID_REG); | 1355 | chipid = reg_read32(hcd->regs, HC_CHIP_ID_REG); |
1307 | dev_info(hcd->self.controller, "USB ISP %04x HW rev. %d started\n", | 1356 | dev_info(hcd->self.controller, "USB ISP %04x HW rev. %d started\n", |
1308 | chipid & 0xffff, chipid >> 16); | 1357 | chipid & 0xffff, chipid >> 16); |
@@ -1561,7 +1610,6 @@ static void kill_transfer(struct usb_hcd *hcd, struct urb *urb, | |||
1561 | } | 1610 | } |
1562 | 1611 | ||
1563 | qh->slot = -1; | 1612 | qh->slot = -1; |
1564 | priv->active_ptds--; | ||
1565 | } | 1613 | } |
1566 | 1614 | ||
1567 | static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, | 1615 | static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, |
@@ -2012,6 +2060,8 @@ static void isp1760_stop(struct usb_hcd *hcd) | |||
2012 | struct isp1760_hcd *priv = hcd_to_priv(hcd); | 2060 | struct isp1760_hcd *priv = hcd_to_priv(hcd); |
2013 | u32 temp; | 2061 | u32 temp; |
2014 | 2062 | ||
2063 | del_timer(&errata2_timer); | ||
2064 | |||
2015 | isp1760_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER, 1, | 2065 | isp1760_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER, 1, |
2016 | NULL, 0); | 2066 | NULL, 0); |
2017 | mdelay(20); | 2067 | mdelay(20); |
diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index 014a7dfadf91..fda0f2d54e3d 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h | |||
@@ -73,7 +73,6 @@ void deinit_kmem_cache(void); | |||
73 | #define HC_EOT_INT (1 << 3) | 73 | #define HC_EOT_INT (1 << 3) |
74 | #define HC_SOT_INT (1 << 1) | 74 | #define HC_SOT_INT (1 << 1) |
75 | #define INTERRUPT_ENABLE_MASK (HC_INTL_INT | HC_ATL_INT) | 75 | #define INTERRUPT_ENABLE_MASK (HC_INTL_INT | HC_ATL_INT) |
76 | #define INTERRUPT_ENABLE_SOT_MASK (HC_SOT_INT) | ||
77 | 76 | ||
78 | #define HC_ISO_IRQ_MASK_OR_REG 0x318 | 77 | #define HC_ISO_IRQ_MASK_OR_REG 0x318 |
79 | #define HC_INT_IRQ_MASK_OR_REG 0x31C | 78 | #define HC_INT_IRQ_MASK_OR_REG 0x31C |
@@ -107,6 +106,7 @@ struct ptd { | |||
107 | struct slotinfo { | 106 | struct slotinfo { |
108 | struct isp1760_qh *qh; | 107 | struct isp1760_qh *qh; |
109 | struct isp1760_qtd *qtd; | 108 | struct isp1760_qtd *qtd; |
109 | unsigned long timestamp; | ||
110 | }; | 110 | }; |
111 | 111 | ||
112 | 112 | ||
@@ -188,6 +188,7 @@ struct memory_chunk { | |||
188 | #define DW3_BABBLE_BIT (1 << 29) | 188 | #define DW3_BABBLE_BIT (1 << 29) |
189 | #define DW3_HALT_BIT (1 << 30) | 189 | #define DW3_HALT_BIT (1 << 30) |
190 | #define DW3_ACTIVE_BIT (1 << 31) | 190 | #define DW3_ACTIVE_BIT (1 << 31) |
191 | #define FROM_DW3_ACTIVE(x) (((x) >> 31) & 0x01) | ||
191 | 192 | ||
192 | #define INT_UNDERRUN (1 << 2) | 193 | #define INT_UNDERRUN (1 << 2) |
193 | #define INT_BABBLE (1 << 1) | 194 | #define INT_BABBLE (1 << 1) |