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