pzl.c revision 831baa4915de465357b25c471bbb9b36472024df
17e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel/* 27e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * Wireless Host Controller (WHC) periodic schedule management. 37e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 47e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * Copyright (C) 2007 Cambridge Silicon Radio Ltd. 57e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 67e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * This program is free software; you can redistribute it and/or 77e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * modify it under the terms of the GNU General Public License version 87e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 2 as published by the Free Software Foundation. 97e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 107e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * This program is distributed in the hope that it will be useful, 117e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * but WITHOUT ANY WARRANTY; without even the implied warranty of 127e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 137e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * GNU General Public License for more details. 147e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 157e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * You should have received a copy of the GNU General Public License 167e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * along with this program. If not, see <http://www.gnu.org/licenses/>. 177e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 187e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel#include <linux/kernel.h> 197e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel#include <linux/dma-mapping.h> 207e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel#include <linux/uwb/umc.h> 217e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel#include <linux/usb.h> 227e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 237e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel#include "../../wusbcore/wusbhc.h" 247e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 257e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel#include "whcd.h" 267e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 277e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelstatic void update_pzl_pointers(struct whc *whc, int period, u64 addr) 287e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 297e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel switch (period) { 307e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel case 0: 317e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[0], addr); 327e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[2], addr); 337e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[4], addr); 347e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[6], addr); 357e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[8], addr); 367e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[10], addr); 377e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[12], addr); 387e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[14], addr); 397e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel break; 407e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel case 1: 417e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[1], addr); 427e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[5], addr); 437e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[9], addr); 447e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[13], addr); 457e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel break; 467e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel case 2: 477e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[3], addr); 487e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[11], addr); 497e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel break; 507e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel case 3: 517e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[7], addr); 527e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel break; 537e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel case 4: 547e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[15], addr); 557e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel break; 567e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 577e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 587e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 597e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel/* 607e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * Return the 'period' to use for this qset. The minimum interval for 617e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * the endpoint is used so whatever urbs are submitted the device is 627e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * polled often enough. 637e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 647e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelstatic int qset_get_period(struct whc *whc, struct whc_qset *qset) 657e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 667e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel uint8_t bInterval = qset->ep->desc.bInterval; 677e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 687e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (bInterval < 6) 697e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel bInterval = 6; 707e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (bInterval > 10) 717e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel bInterval = 10; 727e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel return bInterval - 6; 737e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 747e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 757e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelstatic void qset_insert_in_sw_list(struct whc *whc, struct whc_qset *qset) 767e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 777e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel int period; 787e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 797e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel period = qset_get_period(whc, qset); 807e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 817e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset_clear(whc, qset); 827e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel list_move(&qset->list_node, &whc->periodic_list[period]); 837e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset->in_sw_list = true; 847e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 857e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 867e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelstatic void pzl_qset_remove(struct whc *whc, struct whc_qset *qset) 877e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 887e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel list_move(&qset->list_node, &whc->periodic_removed_list); 897e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset->in_hw_list = false; 907e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset->in_sw_list = false; 917e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 927e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 937e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel/** 947e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * pzl_process_qset - process any recently inactivated or halted qTDs 957e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * in a qset. 967e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 977e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * After inactive qTDs are removed, new qTDs can be added if the 987e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * urb queue still contains URBs. 997e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 1007e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * Returns the schedule updates required. 1017e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 1027e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelstatic enum whc_update pzl_process_qset(struct whc *whc, struct whc_qset *qset) 1037e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 1047e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel enum whc_update update = 0; 1057e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel uint32_t status = 0; 1067e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1077e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel while (qset->ntds) { 1087e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel struct whc_qtd *td; 1097e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel int t; 1107e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1117e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel t = qset->td_start; 1127e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel td = &qset->qtd[qset->td_start]; 1137e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel status = le32_to_cpu(td->status); 1147e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1157e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel /* 1167e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * Nothing to do with a still active qTD. 1177e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 1187e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (status & QTD_STS_ACTIVE) 1197e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel break; 1207e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1217e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (status & QTD_STS_HALTED) { 1227e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel /* Ug, an error. */ 1237e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel process_halted_qtd(whc, qset, td); 1247e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel goto done; 1257e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 1267e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1277e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel /* Mmm, a completed qTD. */ 1287e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel process_inactive_qtd(whc, qset, td); 1297e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 1307e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1317f0406db5fe4dd3ad3cbd53830239a87d68156fdDavid Vrabel if (!qset->remove) 1327f0406db5fe4dd3ad3cbd53830239a87d68156fdDavid Vrabel update |= qset_add_qtds(whc, qset); 1337e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1347e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabeldone: 1357e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel /* 1367e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * If there are no qTDs in this qset, remove it from the PZL. 1377e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 1387e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (qset->remove && qset->ntds == 0) { 1397e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel pzl_qset_remove(whc, qset); 1407e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel update |= WHC_UPDATE_REMOVED; 1417e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 1427e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1437e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel return update; 1447e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 1457e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1467e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel/** 1477e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * pzl_start - start the periodic schedule 1487e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * @whc: the WHCI host controller 1497e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 1507e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * The PZL must be valid (e.g., all entries in the list should have 1517e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * the T bit set). 1527e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 1537e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelvoid pzl_start(struct whc *whc) 1547e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 1557e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel le_writeq(whc->pz_list_dma, whc->base + WUSBPERIODICLISTBASE); 1567e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1577e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_write_wusbcmd(whc, WUSBCMD_PERIODIC_EN, WUSBCMD_PERIODIC_EN); 1587e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS, 1597e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel WUSBSTS_PERIODIC_SCHED, WUSBSTS_PERIODIC_SCHED, 1607e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1000, "start PZL"); 1617e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 1627e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1637e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel/** 1647e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * pzl_stop - stop the periodic schedule 1657e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * @whc: the WHCI host controller 1667e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 1677e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelvoid pzl_stop(struct whc *whc) 1687e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 1697e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_write_wusbcmd(whc, WUSBCMD_PERIODIC_EN, 0); 1707e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS, 1717e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel WUSBSTS_PERIODIC_SCHED, 0, 1727e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1000, "stop PZL"); 1737e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 1747e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 17556968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel/** 17656968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel * pzl_update - request a PZL update and wait for the hardware to be synced 17756968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel * @whc: the WHCI HC 17856968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel * @wusbcmd: WUSBCMD value to start the update. 17956968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel * 18056968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel * If the WUSB HC is inactive (i.e., the PZL is stopped) then the 18156968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel * update must be skipped as the hardware may not respond to update 18256968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel * requests. 18356968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel */ 1847e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelvoid pzl_update(struct whc *whc, uint32_t wusbcmd) 1857e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 18656968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel struct wusbhc *wusbhc = &whc->wusbhc; 187a5e6ced58d423cb09c4fc0087dcfdb0b5deb5e1cDavid Vrabel long t; 18856968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel 18956968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel mutex_lock(&wusbhc->mutex); 19056968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel if (wusbhc->active) { 19156968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel whc_write_wusbcmd(whc, wusbcmd, wusbcmd); 192a5e6ced58d423cb09c4fc0087dcfdb0b5deb5e1cDavid Vrabel t = wait_event_timeout( 193a5e6ced58d423cb09c4fc0087dcfdb0b5deb5e1cDavid Vrabel whc->periodic_list_wq, 194a5e6ced58d423cb09c4fc0087dcfdb0b5deb5e1cDavid Vrabel (le_readl(whc->base + WUSBCMD) & WUSBCMD_PERIODIC_UPDATED) == 0, 195a5e6ced58d423cb09c4fc0087dcfdb0b5deb5e1cDavid Vrabel msecs_to_jiffies(1000)); 196a5e6ced58d423cb09c4fc0087dcfdb0b5deb5e1cDavid Vrabel if (t == 0) 197a5e6ced58d423cb09c4fc0087dcfdb0b5deb5e1cDavid Vrabel whc_hw_error(whc, "PZL update timeout"); 19856968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel } 19956968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel mutex_unlock(&wusbhc->mutex); 2007e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 2017e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2027e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelstatic void update_pzl_hw_view(struct whc *whc) 2037e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 2047e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel struct whc_qset *qset, *t; 2057e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel int period; 2067e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel u64 tmp_qh = 0; 2077e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2087e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel for (period = 0; period < 5; period++) { 2097e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel list_for_each_entry_safe(qset, t, &whc->periodic_list[period], list_node) { 2107e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&qset->qh.link, tmp_qh); 2117e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel tmp_qh = qset->qset_dma; 2127e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset->in_hw_list = true; 2137e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 2147e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel update_pzl_pointers(whc, period, tmp_qh); 2157e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 2167e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 2177e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2187e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel/** 2197e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * scan_periodic_work - scan the PZL for qsets to process. 2207e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 2217e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * Process each qset in the PZL in turn and then signal the WHC that 2227e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * the PZL has been updated. 2237e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 2247e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * Then start, stop or update the periodic schedule as required. 2257e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 2267e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelvoid scan_periodic_work(struct work_struct *work) 2277e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 2287e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel struct whc *whc = container_of(work, struct whc, periodic_work); 2297e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel struct whc_qset *qset, *t; 2307e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel enum whc_update update = 0; 2317e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel int period; 2327e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2337e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel spin_lock_irq(&whc->lock); 2347e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2357e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel for (period = 4; period >= 0; period--) { 2367e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel list_for_each_entry_safe(qset, t, &whc->periodic_list[period], list_node) { 2377e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (!qset->in_hw_list) 2387e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel update |= WHC_UPDATE_ADDED; 2397e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel update |= pzl_process_qset(whc, qset); 2407e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 2417e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 2427e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2437e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (update & (WHC_UPDATE_ADDED | WHC_UPDATE_REMOVED)) 2447e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel update_pzl_hw_view(whc); 2457e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2467e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel spin_unlock_irq(&whc->lock); 2477e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2487e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (update) { 2497e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel uint32_t wusbcmd = WUSBCMD_PERIODIC_UPDATED | WUSBCMD_PERIODIC_SYNCED_DB; 2507e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (update & WHC_UPDATE_REMOVED) 2517e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel wusbcmd |= WUSBCMD_PERIODIC_QSET_RM; 2527e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel pzl_update(whc, wusbcmd); 2537e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 2547e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2557e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel /* 2567e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * Now that the PZL is updated, complete the removal of any 2577e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * removed qsets. 258831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel * 259831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel * If the qset was to be reset, do so and reinsert it into the 260831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel * PZL if it has pending transfers. 2617e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 262a3c1239eb59c0a907f8be5587d42e950f44543f8David Vrabel spin_lock_irq(&whc->lock); 2637e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2647e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel list_for_each_entry_safe(qset, t, &whc->periodic_removed_list, list_node) { 2657e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset_remove_complete(whc, qset); 266831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel if (qset->reset) { 267831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel qset_reset(whc, qset); 268831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel if (!list_empty(&qset->stds)) { 269831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel qset_insert_in_sw_list(whc, qset); 270831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel queue_work(whc->workqueue, &whc->periodic_work); 271831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel } 272831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel } 2737e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 2747e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 275a3c1239eb59c0a907f8be5587d42e950f44543f8David Vrabel spin_unlock_irq(&whc->lock); 2767e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 2777e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2787e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel/** 2797e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * pzl_urb_enqueue - queue an URB onto the periodic list (PZL) 2807e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * @whc: the WHCI host controller 2817e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * @urb: the URB to enqueue 2827e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * @mem_flags: flags for any memory allocations 2837e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 2847e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * The qset for the endpoint is obtained and the urb queued on to it. 2857e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 2867e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * Work is scheduled to update the hardware's view of the PZL. 2877e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 2887e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelint pzl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags) 2897e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 2907e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel struct whc_qset *qset; 2917e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel int err; 2927e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel unsigned long flags; 2937e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2947e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel spin_lock_irqsave(&whc->lock, flags); 2957e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 296f720af91ec2c67e9a1abbd935570f4b4e1f0dd54David Vrabel err = usb_hcd_link_urb_to_ep(&whc->wusbhc.usb_hcd, urb); 297f720af91ec2c67e9a1abbd935570f4b4e1f0dd54David Vrabel if (err < 0) { 298f720af91ec2c67e9a1abbd935570f4b4e1f0dd54David Vrabel spin_unlock_irqrestore(&whc->lock, flags); 299f720af91ec2c67e9a1abbd935570f4b4e1f0dd54David Vrabel return err; 300f720af91ec2c67e9a1abbd935570f4b4e1f0dd54David Vrabel } 301f720af91ec2c67e9a1abbd935570f4b4e1f0dd54David Vrabel 3027e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset = get_qset(whc, urb, GFP_ATOMIC); 3037e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (qset == NULL) 3047e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel err = -ENOMEM; 3057e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel else 3067e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel err = qset_add_urb(whc, qset, urb, GFP_ATOMIC); 3077e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (!err) { 308831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel if (!qset->in_sw_list && !qset->remove) 3097e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset_insert_in_sw_list(whc, qset); 310f720af91ec2c67e9a1abbd935570f4b4e1f0dd54David Vrabel } else 311f720af91ec2c67e9a1abbd935570f4b4e1f0dd54David Vrabel usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb); 3127e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3137e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel spin_unlock_irqrestore(&whc->lock, flags); 3147e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3157e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (!err) 3167e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel queue_work(whc->workqueue, &whc->periodic_work); 3177e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 318f720af91ec2c67e9a1abbd935570f4b4e1f0dd54David Vrabel return err; 3197e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 3207e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3217e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel/** 3227e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * pzl_urb_dequeue - remove an URB (qset) from the periodic list 3237e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * @whc: the WHCI host controller 3247e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * @urb: the URB to dequeue 3257e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * @status: the current status of the URB 3267e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 3277e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * URBs that do yet have qTDs can simply be removed from the software 3287e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * queue, otherwise the qset must be removed so the qTDs can be safely 3297e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * removed. 3307e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 3317e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelint pzl_urb_dequeue(struct whc *whc, struct urb *urb, int status) 3327e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 3337e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel struct whc_urb *wurb = urb->hcpriv; 3347e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel struct whc_qset *qset = wurb->qset; 3357e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel struct whc_std *std, *t; 3367e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel int ret; 3377e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel unsigned long flags; 3387e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3397e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel spin_lock_irqsave(&whc->lock, flags); 3407e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3417e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel ret = usb_hcd_check_unlink_urb(&whc->wusbhc.usb_hcd, urb, status); 3427e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (ret < 0) 3437e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel goto out; 3447e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3457e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel list_for_each_entry_safe(std, t, &qset->stds, list_node) { 3467e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (std->urb == urb) 3477e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset_free_std(whc, std); 3487e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel else 3497e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel std->qtd = NULL; /* so this std is re-added when the qset is */ 3507e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 3517e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3527e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel pzl_qset_remove(whc, qset); 3537e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel wurb->status = status; 3547e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel wurb->is_async = false; 3557e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel queue_work(whc->workqueue, &wurb->dequeue_work); 3567e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3577e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelout: 3587e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel spin_unlock_irqrestore(&whc->lock, flags); 3597e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3607e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel return ret; 3617e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 3627e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3637e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel/** 3647e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * pzl_qset_delete - delete a qset from the PZL 3657e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 3667e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelvoid pzl_qset_delete(struct whc *whc, struct whc_qset *qset) 3677e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 3687e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset->remove = 1; 3697e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel queue_work(whc->workqueue, &whc->periodic_work); 3707e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset_delete(whc, qset); 3717e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 3727e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3737e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel/** 3747e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * pzl_init - initialize the periodic zone list 3757e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * @whc: the WHCI host controller 3767e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 3777e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelint pzl_init(struct whc *whc) 3787e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 3797e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel int i; 3807e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3817e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc->pz_list = dma_alloc_coherent(&whc->umc->dev, sizeof(u64) * 16, 3827e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel &whc->pz_list_dma, GFP_KERNEL); 3837e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (whc->pz_list == NULL) 3847e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel return -ENOMEM; 3857e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3867e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel /* Set T bit on all elements in PZL. */ 3877e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel for (i = 0; i < 16; i++) 3887e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc->pz_list[i] = cpu_to_le64(QH_LINK_NTDS(8) | QH_LINK_T); 3897e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3907e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel le_writeq(whc->pz_list_dma, whc->base + WUSBPERIODICLISTBASE); 3917e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3927e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel return 0; 3937e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 3947e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3957e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel/** 3967e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * pzl_clean_up - free PZL resources 3977e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * @whc: the WHCI host controller 3987e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 3997e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * The PZL is stopped and empty. 4007e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 4017e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelvoid pzl_clean_up(struct whc *whc) 4027e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 4037e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (whc->pz_list) 4047e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel dma_free_coherent(&whc->umc->dev, sizeof(u64) * 16, whc->pz_list, 4057e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc->pz_list_dma); 4067e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 407