1/* 2 * Wireless Host Controller (WHC) periodic schedule management. 3 * 4 * Copyright (C) 2007 Cambridge Silicon Radio Ltd. 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License version 8 * 2 as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18#include <linux/kernel.h> 19#include <linux/gfp.h> 20#include <linux/dma-mapping.h> 21#include <linux/uwb/umc.h> 22#include <linux/usb.h> 23 24#include "../../wusbcore/wusbhc.h" 25 26#include "whcd.h" 27 28static void update_pzl_pointers(struct whc *whc, int period, u64 addr) 29{ 30 switch (period) { 31 case 0: 32 whc_qset_set_link_ptr(&whc->pz_list[0], addr); 33 whc_qset_set_link_ptr(&whc->pz_list[2], addr); 34 whc_qset_set_link_ptr(&whc->pz_list[4], addr); 35 whc_qset_set_link_ptr(&whc->pz_list[6], addr); 36 whc_qset_set_link_ptr(&whc->pz_list[8], addr); 37 whc_qset_set_link_ptr(&whc->pz_list[10], addr); 38 whc_qset_set_link_ptr(&whc->pz_list[12], addr); 39 whc_qset_set_link_ptr(&whc->pz_list[14], addr); 40 break; 41 case 1: 42 whc_qset_set_link_ptr(&whc->pz_list[1], addr); 43 whc_qset_set_link_ptr(&whc->pz_list[5], addr); 44 whc_qset_set_link_ptr(&whc->pz_list[9], addr); 45 whc_qset_set_link_ptr(&whc->pz_list[13], addr); 46 break; 47 case 2: 48 whc_qset_set_link_ptr(&whc->pz_list[3], addr); 49 whc_qset_set_link_ptr(&whc->pz_list[11], addr); 50 break; 51 case 3: 52 whc_qset_set_link_ptr(&whc->pz_list[7], addr); 53 break; 54 case 4: 55 whc_qset_set_link_ptr(&whc->pz_list[15], addr); 56 break; 57 } 58} 59 60/* 61 * Return the 'period' to use for this qset. The minimum interval for 62 * the endpoint is used so whatever urbs are submitted the device is 63 * polled often enough. 64 */ 65static int qset_get_period(struct whc *whc, struct whc_qset *qset) 66{ 67 uint8_t bInterval = qset->ep->desc.bInterval; 68 69 if (bInterval < 6) 70 bInterval = 6; 71 if (bInterval > 10) 72 bInterval = 10; 73 return bInterval - 6; 74} 75 76static void qset_insert_in_sw_list(struct whc *whc, struct whc_qset *qset) 77{ 78 int period; 79 80 period = qset_get_period(whc, qset); 81 82 qset_clear(whc, qset); 83 list_move(&qset->list_node, &whc->periodic_list[period]); 84 qset->in_sw_list = true; 85} 86 87static void pzl_qset_remove(struct whc *whc, struct whc_qset *qset) 88{ 89 list_move(&qset->list_node, &whc->periodic_removed_list); 90 qset->in_hw_list = false; 91 qset->in_sw_list = false; 92} 93 94/** 95 * pzl_process_qset - process any recently inactivated or halted qTDs 96 * in a qset. 97 * 98 * After inactive qTDs are removed, new qTDs can be added if the 99 * urb queue still contains URBs. 100 * 101 * Returns the schedule updates required. 102 */ 103static enum whc_update pzl_process_qset(struct whc *whc, struct whc_qset *qset) 104{ 105 enum whc_update update = 0; 106 uint32_t status = 0; 107 108 while (qset->ntds) { 109 struct whc_qtd *td; 110 int t; 111 112 t = qset->td_start; 113 td = &qset->qtd[qset->td_start]; 114 status = le32_to_cpu(td->status); 115 116 /* 117 * Nothing to do with a still active qTD. 118 */ 119 if (status & QTD_STS_ACTIVE) 120 break; 121 122 if (status & QTD_STS_HALTED) { 123 /* Ug, an error. */ 124 process_halted_qtd(whc, qset, td); 125 /* A halted qTD always triggers an update 126 because the qset was either removed or 127 reactivated. */ 128 update |= WHC_UPDATE_UPDATED; 129 goto done; 130 } 131 132 /* Mmm, a completed qTD. */ 133 process_inactive_qtd(whc, qset, td); 134 } 135 136 if (!qset->remove) 137 update |= qset_add_qtds(whc, qset); 138 139done: 140 /* 141 * If there are no qTDs in this qset, remove it from the PZL. 142 */ 143 if (qset->remove && qset->ntds == 0) { 144 pzl_qset_remove(whc, qset); 145 update |= WHC_UPDATE_REMOVED; 146 } 147 148 return update; 149} 150 151/** 152 * pzl_start - start the periodic schedule 153 * @whc: the WHCI host controller 154 * 155 * The PZL must be valid (e.g., all entries in the list should have 156 * the T bit set). 157 */ 158void pzl_start(struct whc *whc) 159{ 160 le_writeq(whc->pz_list_dma, whc->base + WUSBPERIODICLISTBASE); 161 162 whc_write_wusbcmd(whc, WUSBCMD_PERIODIC_EN, WUSBCMD_PERIODIC_EN); 163 whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS, 164 WUSBSTS_PERIODIC_SCHED, WUSBSTS_PERIODIC_SCHED, 165 1000, "start PZL"); 166} 167 168/** 169 * pzl_stop - stop the periodic schedule 170 * @whc: the WHCI host controller 171 */ 172void pzl_stop(struct whc *whc) 173{ 174 whc_write_wusbcmd(whc, WUSBCMD_PERIODIC_EN, 0); 175 whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS, 176 WUSBSTS_PERIODIC_SCHED, 0, 177 1000, "stop PZL"); 178} 179 180/** 181 * pzl_update - request a PZL update and wait for the hardware to be synced 182 * @whc: the WHCI HC 183 * @wusbcmd: WUSBCMD value to start the update. 184 * 185 * If the WUSB HC is inactive (i.e., the PZL is stopped) then the 186 * update must be skipped as the hardware may not respond to update 187 * requests. 188 */ 189void pzl_update(struct whc *whc, uint32_t wusbcmd) 190{ 191 struct wusbhc *wusbhc = &whc->wusbhc; 192 long t; 193 194 mutex_lock(&wusbhc->mutex); 195 if (wusbhc->active) { 196 whc_write_wusbcmd(whc, wusbcmd, wusbcmd); 197 t = wait_event_timeout( 198 whc->periodic_list_wq, 199 (le_readl(whc->base + WUSBCMD) & WUSBCMD_PERIODIC_UPDATED) == 0, 200 msecs_to_jiffies(1000)); 201 if (t == 0) 202 whc_hw_error(whc, "PZL update timeout"); 203 } 204 mutex_unlock(&wusbhc->mutex); 205} 206 207static void update_pzl_hw_view(struct whc *whc) 208{ 209 struct whc_qset *qset, *t; 210 int period; 211 u64 tmp_qh = 0; 212 213 for (period = 0; period < 5; period++) { 214 list_for_each_entry_safe(qset, t, &whc->periodic_list[period], list_node) { 215 whc_qset_set_link_ptr(&qset->qh.link, tmp_qh); 216 tmp_qh = qset->qset_dma; 217 qset->in_hw_list = true; 218 } 219 update_pzl_pointers(whc, period, tmp_qh); 220 } 221} 222 223/** 224 * scan_periodic_work - scan the PZL for qsets to process. 225 * 226 * Process each qset in the PZL in turn and then signal the WHC that 227 * the PZL has been updated. 228 * 229 * Then start, stop or update the periodic schedule as required. 230 */ 231void scan_periodic_work(struct work_struct *work) 232{ 233 struct whc *whc = container_of(work, struct whc, periodic_work); 234 struct whc_qset *qset, *t; 235 enum whc_update update = 0; 236 int period; 237 238 spin_lock_irq(&whc->lock); 239 240 for (period = 4; period >= 0; period--) { 241 list_for_each_entry_safe(qset, t, &whc->periodic_list[period], list_node) { 242 if (!qset->in_hw_list) 243 update |= WHC_UPDATE_ADDED; 244 update |= pzl_process_qset(whc, qset); 245 } 246 } 247 248 if (update & (WHC_UPDATE_ADDED | WHC_UPDATE_REMOVED)) 249 update_pzl_hw_view(whc); 250 251 spin_unlock_irq(&whc->lock); 252 253 if (update) { 254 uint32_t wusbcmd = WUSBCMD_PERIODIC_UPDATED | WUSBCMD_PERIODIC_SYNCED_DB; 255 if (update & WHC_UPDATE_REMOVED) 256 wusbcmd |= WUSBCMD_PERIODIC_QSET_RM; 257 pzl_update(whc, wusbcmd); 258 } 259 260 /* 261 * Now that the PZL is updated, complete the removal of any 262 * removed qsets. 263 * 264 * If the qset was to be reset, do so and reinsert it into the 265 * PZL if it has pending transfers. 266 */ 267 spin_lock_irq(&whc->lock); 268 269 list_for_each_entry_safe(qset, t, &whc->periodic_removed_list, list_node) { 270 qset_remove_complete(whc, qset); 271 if (qset->reset) { 272 qset_reset(whc, qset); 273 if (!list_empty(&qset->stds)) { 274 qset_insert_in_sw_list(whc, qset); 275 queue_work(whc->workqueue, &whc->periodic_work); 276 } 277 } 278 } 279 280 spin_unlock_irq(&whc->lock); 281} 282 283/** 284 * pzl_urb_enqueue - queue an URB onto the periodic list (PZL) 285 * @whc: the WHCI host controller 286 * @urb: the URB to enqueue 287 * @mem_flags: flags for any memory allocations 288 * 289 * The qset for the endpoint is obtained and the urb queued on to it. 290 * 291 * Work is scheduled to update the hardware's view of the PZL. 292 */ 293int pzl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags) 294{ 295 struct whc_qset *qset; 296 int err; 297 unsigned long flags; 298 299 spin_lock_irqsave(&whc->lock, flags); 300 301 err = usb_hcd_link_urb_to_ep(&whc->wusbhc.usb_hcd, urb); 302 if (err < 0) { 303 spin_unlock_irqrestore(&whc->lock, flags); 304 return err; 305 } 306 307 qset = get_qset(whc, urb, GFP_ATOMIC); 308 if (qset == NULL) 309 err = -ENOMEM; 310 else 311 err = qset_add_urb(whc, qset, urb, GFP_ATOMIC); 312 if (!err) { 313 if (!qset->in_sw_list && !qset->remove) 314 qset_insert_in_sw_list(whc, qset); 315 } else 316 usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb); 317 318 spin_unlock_irqrestore(&whc->lock, flags); 319 320 if (!err) 321 queue_work(whc->workqueue, &whc->periodic_work); 322 323 return err; 324} 325 326/** 327 * pzl_urb_dequeue - remove an URB (qset) from the periodic list 328 * @whc: the WHCI host controller 329 * @urb: the URB to dequeue 330 * @status: the current status of the URB 331 * 332 * URBs that do yet have qTDs can simply be removed from the software 333 * queue, otherwise the qset must be removed so the qTDs can be safely 334 * removed. 335 */ 336int pzl_urb_dequeue(struct whc *whc, struct urb *urb, int status) 337{ 338 struct whc_urb *wurb = urb->hcpriv; 339 struct whc_qset *qset = wurb->qset; 340 struct whc_std *std, *t; 341 bool has_qtd = false; 342 int ret; 343 unsigned long flags; 344 345 spin_lock_irqsave(&whc->lock, flags); 346 347 ret = usb_hcd_check_unlink_urb(&whc->wusbhc.usb_hcd, urb, status); 348 if (ret < 0) 349 goto out; 350 351 list_for_each_entry_safe(std, t, &qset->stds, list_node) { 352 if (std->urb == urb) { 353 if (std->qtd) 354 has_qtd = true; 355 qset_free_std(whc, std); 356 } else 357 std->qtd = NULL; /* so this std is re-added when the qset is */ 358 } 359 360 if (has_qtd) { 361 pzl_qset_remove(whc, qset); 362 update_pzl_hw_view(whc); 363 wurb->status = status; 364 wurb->is_async = false; 365 queue_work(whc->workqueue, &wurb->dequeue_work); 366 } else 367 qset_remove_urb(whc, qset, urb, status); 368out: 369 spin_unlock_irqrestore(&whc->lock, flags); 370 371 return ret; 372} 373 374/** 375 * pzl_qset_delete - delete a qset from the PZL 376 */ 377void pzl_qset_delete(struct whc *whc, struct whc_qset *qset) 378{ 379 qset->remove = 1; 380 queue_work(whc->workqueue, &whc->periodic_work); 381 qset_delete(whc, qset); 382} 383 384/** 385 * pzl_init - initialize the periodic zone list 386 * @whc: the WHCI host controller 387 */ 388int pzl_init(struct whc *whc) 389{ 390 int i; 391 392 whc->pz_list = dma_alloc_coherent(&whc->umc->dev, sizeof(u64) * 16, 393 &whc->pz_list_dma, GFP_KERNEL); 394 if (whc->pz_list == NULL) 395 return -ENOMEM; 396 397 /* Set T bit on all elements in PZL. */ 398 for (i = 0; i < 16; i++) 399 whc->pz_list[i] = cpu_to_le64(QH_LINK_NTDS(8) | QH_LINK_T); 400 401 le_writeq(whc->pz_list_dma, whc->base + WUSBPERIODICLISTBASE); 402 403 return 0; 404} 405 406/** 407 * pzl_clean_up - free PZL resources 408 * @whc: the WHCI host controller 409 * 410 * The PZL is stopped and empty. 411 */ 412void pzl_clean_up(struct whc *whc) 413{ 414 if (whc->pz_list) 415 dma_free_coherent(&whc->umc->dev, sizeof(u64) * 16, whc->pz_list, 416 whc->pz_list_dma); 417} 418