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> 195a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/gfp.h> 207e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel#include <linux/dma-mapping.h> 217e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel#include <linux/uwb/umc.h> 227e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel#include <linux/usb.h> 237e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 247e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel#include "../../wusbcore/wusbhc.h" 257e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 267e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel#include "whcd.h" 277e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 287e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelstatic void update_pzl_pointers(struct whc *whc, int period, u64 addr) 297e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 307e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel switch (period) { 317e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel case 0: 327e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[0], addr); 337e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[2], addr); 347e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[4], addr); 357e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[6], addr); 367e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[8], addr); 377e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[10], addr); 387e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[12], addr); 397e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[14], addr); 407e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel break; 417e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel case 1: 427e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[1], addr); 437e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[5], addr); 447e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[9], addr); 457e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[13], addr); 467e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel break; 477e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel case 2: 487e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[3], addr); 497e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[11], addr); 507e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel break; 517e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel case 3: 527e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[7], addr); 537e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel break; 547e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel case 4: 557e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&whc->pz_list[15], addr); 567e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel break; 577e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 587e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 597e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 607e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel/* 617e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * Return the 'period' to use for this qset. The minimum interval for 627e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * the endpoint is used so whatever urbs are submitted the device is 637e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * polled often enough. 647e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 657e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelstatic int qset_get_period(struct whc *whc, struct whc_qset *qset) 667e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 677e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel uint8_t bInterval = qset->ep->desc.bInterval; 687e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 697e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (bInterval < 6) 707e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel bInterval = 6; 717e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (bInterval > 10) 727e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel bInterval = 10; 737e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel return bInterval - 6; 747e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 757e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 767e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelstatic void qset_insert_in_sw_list(struct whc *whc, struct whc_qset *qset) 777e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 787e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel int period; 797e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 807e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel period = qset_get_period(whc, qset); 817e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 827e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset_clear(whc, qset); 837e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel list_move(&qset->list_node, &whc->periodic_list[period]); 847e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset->in_sw_list = true; 857e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 867e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 877e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelstatic void pzl_qset_remove(struct whc *whc, struct whc_qset *qset) 887e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 897e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel list_move(&qset->list_node, &whc->periodic_removed_list); 907e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset->in_hw_list = false; 917e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset->in_sw_list = false; 927e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 937e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 947e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel/** 957e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * pzl_process_qset - process any recently inactivated or halted qTDs 967e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * in a qset. 977e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 987e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * After inactive qTDs are removed, new qTDs can be added if the 997e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * urb queue still contains URBs. 1007e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 1017e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * Returns the schedule updates required. 1027e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 1037e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelstatic enum whc_update pzl_process_qset(struct whc *whc, struct whc_qset *qset) 1047e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 1057e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel enum whc_update update = 0; 1067e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel uint32_t status = 0; 1077e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1087e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel while (qset->ntds) { 1097e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel struct whc_qtd *td; 1107e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel int t; 1117e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1127e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel t = qset->td_start; 1137e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel td = &qset->qtd[qset->td_start]; 1147e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel status = le32_to_cpu(td->status); 1157e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1167e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel /* 1177e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * Nothing to do with a still active qTD. 1187e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 1197e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (status & QTD_STS_ACTIVE) 1207e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel break; 1217e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1227e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (status & QTD_STS_HALTED) { 1237e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel /* Ug, an error. */ 1247e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel process_halted_qtd(whc, qset, td); 1251f01ca4e0c1d4126eb663f8ea0bab03836099770David Vrabel /* A halted qTD always triggers an update 1261f01ca4e0c1d4126eb663f8ea0bab03836099770David Vrabel because the qset was either removed or 1271f01ca4e0c1d4126eb663f8ea0bab03836099770David Vrabel reactivated. */ 1281f01ca4e0c1d4126eb663f8ea0bab03836099770David Vrabel update |= WHC_UPDATE_UPDATED; 1297e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel goto done; 1307e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 1317e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1327e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel /* Mmm, a completed qTD. */ 1337e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel process_inactive_qtd(whc, qset, td); 1347e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 1357e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1367f0406db5fe4dd3ad3cbd53830239a87d68156fdDavid Vrabel if (!qset->remove) 1377f0406db5fe4dd3ad3cbd53830239a87d68156fdDavid Vrabel update |= qset_add_qtds(whc, qset); 1387e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1397e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabeldone: 1407e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel /* 1417e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * If there are no qTDs in this qset, remove it from the PZL. 1427e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 1437e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (qset->remove && qset->ntds == 0) { 1447e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel pzl_qset_remove(whc, qset); 1457e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel update |= WHC_UPDATE_REMOVED; 1467e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 1477e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1487e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel return update; 1497e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 1507e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1517e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel/** 1527e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * pzl_start - start the periodic schedule 1537e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * @whc: the WHCI host controller 1547e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 1557e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * The PZL must be valid (e.g., all entries in the list should have 1567e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * the T bit set). 1577e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 1587e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelvoid pzl_start(struct whc *whc) 1597e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 1607e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel le_writeq(whc->pz_list_dma, whc->base + WUSBPERIODICLISTBASE); 1617e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1627e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_write_wusbcmd(whc, WUSBCMD_PERIODIC_EN, WUSBCMD_PERIODIC_EN); 1637e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS, 1647e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel WUSBSTS_PERIODIC_SCHED, WUSBSTS_PERIODIC_SCHED, 1657e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1000, "start PZL"); 1667e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 1677e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1687e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel/** 1697e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * pzl_stop - stop the periodic schedule 1707e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * @whc: the WHCI host controller 1717e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 1727e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelvoid pzl_stop(struct whc *whc) 1737e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 1747e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_write_wusbcmd(whc, WUSBCMD_PERIODIC_EN, 0); 1757e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS, 1767e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel WUSBSTS_PERIODIC_SCHED, 0, 1777e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 1000, "stop PZL"); 1787e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 1797e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 18056968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel/** 18156968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel * pzl_update - request a PZL update and wait for the hardware to be synced 18256968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel * @whc: the WHCI HC 18356968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel * @wusbcmd: WUSBCMD value to start the update. 18456968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel * 18556968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel * If the WUSB HC is inactive (i.e., the PZL is stopped) then the 18656968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel * update must be skipped as the hardware may not respond to update 18756968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel * requests. 18856968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel */ 1897e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelvoid pzl_update(struct whc *whc, uint32_t wusbcmd) 1907e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 19156968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel struct wusbhc *wusbhc = &whc->wusbhc; 192a5e6ced58d423cb09c4fc0087dcfdb0b5deb5e1cDavid Vrabel long t; 19356968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel 19456968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel mutex_lock(&wusbhc->mutex); 19556968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel if (wusbhc->active) { 19656968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel whc_write_wusbcmd(whc, wusbcmd, wusbcmd); 197a5e6ced58d423cb09c4fc0087dcfdb0b5deb5e1cDavid Vrabel t = wait_event_timeout( 198a5e6ced58d423cb09c4fc0087dcfdb0b5deb5e1cDavid Vrabel whc->periodic_list_wq, 199a5e6ced58d423cb09c4fc0087dcfdb0b5deb5e1cDavid Vrabel (le_readl(whc->base + WUSBCMD) & WUSBCMD_PERIODIC_UPDATED) == 0, 200a5e6ced58d423cb09c4fc0087dcfdb0b5deb5e1cDavid Vrabel msecs_to_jiffies(1000)); 201a5e6ced58d423cb09c4fc0087dcfdb0b5deb5e1cDavid Vrabel if (t == 0) 202a5e6ced58d423cb09c4fc0087dcfdb0b5deb5e1cDavid Vrabel whc_hw_error(whc, "PZL update timeout"); 20356968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel } 20456968d0c1a920eb165c06318f5c458724e1df0afDavid Vrabel mutex_unlock(&wusbhc->mutex); 2057e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 2067e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2077e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelstatic void update_pzl_hw_view(struct whc *whc) 2087e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 2097e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel struct whc_qset *qset, *t; 2107e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel int period; 2117e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel u64 tmp_qh = 0; 2127e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2137e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel for (period = 0; period < 5; period++) { 2147e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel list_for_each_entry_safe(qset, t, &whc->periodic_list[period], list_node) { 2157e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc_qset_set_link_ptr(&qset->qh.link, tmp_qh); 2167e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel tmp_qh = qset->qset_dma; 2177e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset->in_hw_list = true; 2187e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 2197e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel update_pzl_pointers(whc, period, tmp_qh); 2207e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 2217e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 2227e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2237e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel/** 2247e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * scan_periodic_work - scan the PZL for qsets to process. 2257e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 2267e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * Process each qset in the PZL in turn and then signal the WHC that 2277e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * the PZL has been updated. 2287e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 2297e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * Then start, stop or update the periodic schedule as required. 2307e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 2317e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelvoid scan_periodic_work(struct work_struct *work) 2327e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 2337e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel struct whc *whc = container_of(work, struct whc, periodic_work); 2347e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel struct whc_qset *qset, *t; 2357e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel enum whc_update update = 0; 2367e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel int period; 2377e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2387e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel spin_lock_irq(&whc->lock); 2397e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2407e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel for (period = 4; period >= 0; period--) { 2417e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel list_for_each_entry_safe(qset, t, &whc->periodic_list[period], list_node) { 2427e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (!qset->in_hw_list) 2437e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel update |= WHC_UPDATE_ADDED; 2447e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel update |= pzl_process_qset(whc, qset); 2457e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 2467e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 2477e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2487e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (update & (WHC_UPDATE_ADDED | WHC_UPDATE_REMOVED)) 2497e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel update_pzl_hw_view(whc); 2507e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2517e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel spin_unlock_irq(&whc->lock); 2527e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2537e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (update) { 2547e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel uint32_t wusbcmd = WUSBCMD_PERIODIC_UPDATED | WUSBCMD_PERIODIC_SYNCED_DB; 2557e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (update & WHC_UPDATE_REMOVED) 2567e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel wusbcmd |= WUSBCMD_PERIODIC_QSET_RM; 2577e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel pzl_update(whc, wusbcmd); 2587e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 2597e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2607e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel /* 2617e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * Now that the PZL is updated, complete the removal of any 2627e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * removed qsets. 263831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel * 264831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel * If the qset was to be reset, do so and reinsert it into the 265831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel * PZL if it has pending transfers. 2667e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 267a3c1239eb59c0a907f8be5587d42e950f44543f8David Vrabel spin_lock_irq(&whc->lock); 2687e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2697e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel list_for_each_entry_safe(qset, t, &whc->periodic_removed_list, list_node) { 2707e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset_remove_complete(whc, qset); 271831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel if (qset->reset) { 272831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel qset_reset(whc, qset); 273831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel if (!list_empty(&qset->stds)) { 274831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel qset_insert_in_sw_list(whc, qset); 275831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel queue_work(whc->workqueue, &whc->periodic_work); 276831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel } 277831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel } 2787e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 2797e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 280a3c1239eb59c0a907f8be5587d42e950f44543f8David Vrabel spin_unlock_irq(&whc->lock); 2817e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 2827e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2837e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel/** 2847e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * pzl_urb_enqueue - queue an URB onto the periodic list (PZL) 2857e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * @whc: the WHCI host controller 2867e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * @urb: the URB to enqueue 2877e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * @mem_flags: flags for any memory allocations 2887e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 2897e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * The qset for the endpoint is obtained and the urb queued on to it. 2907e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 2917e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * Work is scheduled to update the hardware's view of the PZL. 2927e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 2937e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelint pzl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags) 2947e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 2957e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel struct whc_qset *qset; 2967e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel int err; 2977e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel unsigned long flags; 2987e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 2997e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel spin_lock_irqsave(&whc->lock, flags); 3007e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 301f720af91ec2c67e9a1abbd935570f4b4e1f0dd54David Vrabel err = usb_hcd_link_urb_to_ep(&whc->wusbhc.usb_hcd, urb); 302f720af91ec2c67e9a1abbd935570f4b4e1f0dd54David Vrabel if (err < 0) { 303f720af91ec2c67e9a1abbd935570f4b4e1f0dd54David Vrabel spin_unlock_irqrestore(&whc->lock, flags); 304f720af91ec2c67e9a1abbd935570f4b4e1f0dd54David Vrabel return err; 305f720af91ec2c67e9a1abbd935570f4b4e1f0dd54David Vrabel } 306f720af91ec2c67e9a1abbd935570f4b4e1f0dd54David Vrabel 3077e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset = get_qset(whc, urb, GFP_ATOMIC); 3087e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (qset == NULL) 3097e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel err = -ENOMEM; 3107e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel else 3117e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel err = qset_add_urb(whc, qset, urb, GFP_ATOMIC); 3127e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (!err) { 313831baa4915de465357b25c471bbb9b36472024dfDavid Vrabel if (!qset->in_sw_list && !qset->remove) 3147e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset_insert_in_sw_list(whc, qset); 315f720af91ec2c67e9a1abbd935570f4b4e1f0dd54David Vrabel } else 316f720af91ec2c67e9a1abbd935570f4b4e1f0dd54David Vrabel usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb); 3177e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3187e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel spin_unlock_irqrestore(&whc->lock, flags); 3197e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3207e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (!err) 3217e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel queue_work(whc->workqueue, &whc->periodic_work); 3227e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 323f720af91ec2c67e9a1abbd935570f4b4e1f0dd54David Vrabel return err; 3247e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 3257e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3267e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel/** 3277e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * pzl_urb_dequeue - remove an URB (qset) from the periodic list 3287e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * @whc: the WHCI host controller 3297e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * @urb: the URB to dequeue 3307e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * @status: the current status of the URB 3317e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 3327e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * URBs that do yet have qTDs can simply be removed from the software 3337e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * queue, otherwise the qset must be removed so the qTDs can be safely 3347e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * removed. 3357e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 3367e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelint pzl_urb_dequeue(struct whc *whc, struct urb *urb, int status) 3377e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 3387e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel struct whc_urb *wurb = urb->hcpriv; 3397e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel struct whc_qset *qset = wurb->qset; 3407e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel struct whc_std *std, *t; 341171b37ee95865c052a88d52a05895c3c584f4871David Vrabel bool has_qtd = false; 3427e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel int ret; 3437e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel unsigned long flags; 3447e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3457e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel spin_lock_irqsave(&whc->lock, flags); 3467e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3477e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel ret = usb_hcd_check_unlink_urb(&whc->wusbhc.usb_hcd, urb, status); 3487e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (ret < 0) 3497e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel goto out; 3507e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3517e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel list_for_each_entry_safe(std, t, &qset->stds, list_node) { 352171b37ee95865c052a88d52a05895c3c584f4871David Vrabel if (std->urb == urb) { 353171b37ee95865c052a88d52a05895c3c584f4871David Vrabel if (std->qtd) 354171b37ee95865c052a88d52a05895c3c584f4871David Vrabel has_qtd = true; 3557e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset_free_std(whc, std); 356171b37ee95865c052a88d52a05895c3c584f4871David Vrabel } else 3577e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel std->qtd = NULL; /* so this std is re-added when the qset is */ 3587e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel } 3597e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 360171b37ee95865c052a88d52a05895c3c584f4871David Vrabel if (has_qtd) { 361171b37ee95865c052a88d52a05895c3c584f4871David Vrabel pzl_qset_remove(whc, qset); 362171b37ee95865c052a88d52a05895c3c584f4871David Vrabel update_pzl_hw_view(whc); 363171b37ee95865c052a88d52a05895c3c584f4871David Vrabel wurb->status = status; 364171b37ee95865c052a88d52a05895c3c584f4871David Vrabel wurb->is_async = false; 365171b37ee95865c052a88d52a05895c3c584f4871David Vrabel queue_work(whc->workqueue, &wurb->dequeue_work); 366171b37ee95865c052a88d52a05895c3c584f4871David Vrabel } else 367171b37ee95865c052a88d52a05895c3c584f4871David Vrabel qset_remove_urb(whc, qset, urb, status); 3687e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelout: 3697e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel spin_unlock_irqrestore(&whc->lock, flags); 3707e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3717e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel return ret; 3727e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 3737e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3747e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel/** 3757e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * pzl_qset_delete - delete a qset from the PZL 3767e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 3777e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelvoid pzl_qset_delete(struct whc *whc, struct whc_qset *qset) 3787e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 3797e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset->remove = 1; 3807e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel queue_work(whc->workqueue, &whc->periodic_work); 3817e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel qset_delete(whc, qset); 3827e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 3837e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3847e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel/** 3857e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * pzl_init - initialize the periodic zone list 3867e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * @whc: the WHCI host controller 3877e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 3887e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelint pzl_init(struct whc *whc) 3897e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 3907e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel int i; 3917e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3927e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc->pz_list = dma_alloc_coherent(&whc->umc->dev, sizeof(u64) * 16, 3937e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel &whc->pz_list_dma, GFP_KERNEL); 3947e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (whc->pz_list == NULL) 3957e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel return -ENOMEM; 3967e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 3977e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel /* Set T bit on all elements in PZL. */ 3987e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel for (i = 0; i < 16; i++) 3997e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc->pz_list[i] = cpu_to_le64(QH_LINK_NTDS(8) | QH_LINK_T); 4007e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 4017e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel le_writeq(whc->pz_list_dma, whc->base + WUSBPERIODICLISTBASE); 4027e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 4037e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel return 0; 4047e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 4057e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel 4067e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel/** 4077e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * pzl_clean_up - free PZL resources 4087e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * @whc: the WHCI host controller 4097e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * 4107e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel * The PZL is stopped and empty. 4117e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel */ 4127e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabelvoid pzl_clean_up(struct whc *whc) 4137e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel{ 4147e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel if (whc->pz_list) 4157e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel dma_free_coherent(&whc->umc->dev, sizeof(u64) * 16, whc->pz_list, 4167e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel whc->pz_list_dma); 4177e6133aa42920ea87ad9791a0fb2b95d1a23b8f9David Vrabel} 418