aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/whci
diff options
context:
space:
mode:
authorDavid Vrabel <david.vrabel@csr.com>2008-09-17 11:34:28 -0400
committerDavid Vrabel <dv02@dv02pc01.europe.root.pri>2008-09-17 11:54:31 -0400
commit7e6133aa42920ea87ad9791a0fb2b95d1a23b8f9 (patch)
treeaa686de823ea3246b6c5cd2e4ef0e54e3797c3e3 /drivers/usb/host/whci
parent870d5395045bfe8e5213525152682c863a10f8d2 (diff)
wusb: WHCI host controller driver
A driver for Wireless USB host controllers that comply with the Wireless Host Controller Interface (HCI) specification as published by Intel. The latest publically available version of the specification (0.95) is supported (except for isochronous transfers). Build fixes by Randy Dunlap <rdunlap@xenotime.net> Signed-off-by: David Vrabel <david.vrabel@csr.com>
Diffstat (limited to 'drivers/usb/host/whci')
-rw-r--r--drivers/usb/host/whci/Kbuild11
-rw-r--r--drivers/usb/host/whci/asl.c367
-rw-r--r--drivers/usb/host/whci/hcd.c339
-rw-r--r--drivers/usb/host/whci/hw.c87
-rw-r--r--drivers/usb/host/whci/init.c188
-rw-r--r--drivers/usb/host/whci/int.c95
-rw-r--r--drivers/usb/host/whci/pzl.c398
-rw-r--r--drivers/usb/host/whci/qset.c567
-rw-r--r--drivers/usb/host/whci/whcd.h197
-rw-r--r--drivers/usb/host/whci/whci-hc.h416
-rw-r--r--drivers/usb/host/whci/wusb.c241
11 files changed, 2906 insertions, 0 deletions
diff --git a/drivers/usb/host/whci/Kbuild b/drivers/usb/host/whci/Kbuild
new file mode 100644
index 000000000000..26a3871ea0f9
--- /dev/null
+++ b/drivers/usb/host/whci/Kbuild
@@ -0,0 +1,11 @@
1obj-$(CONFIG_USB_WHCI_HCD) += whci-hcd.o
2
3whci-hcd-y := \
4 asl.o \
5 hcd.o \
6 hw.o \
7 init.o \
8 int.o \
9 pzl.o \
10 qset.o \
11 wusb.o
diff --git a/drivers/usb/host/whci/asl.c b/drivers/usb/host/whci/asl.c
new file mode 100644
index 000000000000..4d7078e50572
--- /dev/null
+++ b/drivers/usb/host/whci/asl.c
@@ -0,0 +1,367 @@
1/*
2 * Wireless Host Controller (WHC) asynchronous schedule management.
3 *
4 * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version
8 * 2 as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18#include <linux/kernel.h>
19#include <linux/dma-mapping.h>
20#include <linux/uwb/umc.h>
21#include <linux/usb.h>
22#define D_LOCAL 0
23#include <linux/uwb/debug.h>
24
25#include "../../wusbcore/wusbhc.h"
26
27#include "whcd.h"
28
29#if D_LOCAL >= 4
30static void dump_asl(struct whc *whc, const char *tag)
31{
32 struct device *dev = &whc->umc->dev;
33 struct whc_qset *qset;
34
35 d_printf(4, dev, "ASL %s\n", tag);
36
37 list_for_each_entry(qset, &whc->async_list, list_node) {
38 dump_qset(qset, dev);
39 }
40}
41#else
42static inline void dump_asl(struct whc *whc, const char *tag)
43{
44}
45#endif
46
47
48static void qset_get_next_prev(struct whc *whc, struct whc_qset *qset,
49 struct whc_qset **next, struct whc_qset **prev)
50{
51 struct list_head *n, *p;
52
53 BUG_ON(list_empty(&whc->async_list));
54
55 n = qset->list_node.next;
56 if (n == &whc->async_list)
57 n = n->next;
58 p = qset->list_node.prev;
59 if (p == &whc->async_list)
60 p = p->prev;
61
62 *next = container_of(n, struct whc_qset, list_node);
63 *prev = container_of(p, struct whc_qset, list_node);
64
65}
66
67static void asl_qset_insert_begin(struct whc *whc, struct whc_qset *qset)
68{
69 list_move(&qset->list_node, &whc->async_list);
70 qset->in_sw_list = true;
71}
72
73static void asl_qset_insert(struct whc *whc, struct whc_qset *qset)
74{
75 struct whc_qset *next, *prev;
76
77 qset_clear(whc, qset);
78
79 /* Link into ASL. */
80 qset_get_next_prev(whc, qset, &next, &prev);
81 whc_qset_set_link_ptr(&qset->qh.link, next->qset_dma);
82 whc_qset_set_link_ptr(&prev->qh.link, qset->qset_dma);
83 qset->in_hw_list = true;
84}
85
86static void asl_qset_remove(struct whc *whc, struct whc_qset *qset)
87{
88 struct whc_qset *prev, *next;
89
90 qset_get_next_prev(whc, qset, &next, &prev);
91
92 list_move(&qset->list_node, &whc->async_removed_list);
93 qset->in_sw_list = false;
94
95 /*
96 * No more qsets in the ASL? The caller must stop the ASL as
97 * it's no longer valid.
98 */
99 if (list_empty(&whc->async_list))
100 return;
101
102 /* Remove from ASL. */
103 whc_qset_set_link_ptr(&prev->qh.link, next->qset_dma);
104 qset->in_hw_list = false;
105}
106
107/**
108 * process_qset - process any recently inactivated or halted qTDs in a
109 * qset.
110 *
111 * After inactive qTDs are removed, new qTDs can be added if the
112 * urb queue still contains URBs.
113 *
114 * Returns any additional WUSBCMD bits for the ASL sync command (i.e.,
115 * WUSBCMD_ASYNC_QSET_RM if a halted qset was removed).
116 */
117static uint32_t process_qset(struct whc *whc, struct whc_qset *qset)
118{
119 enum whc_update update = 0;
120 uint32_t status = 0;
121
122 while (qset->ntds) {
123 struct whc_qtd *td;
124 int t;
125
126 t = qset->td_start;
127 td = &qset->qtd[qset->td_start];
128 status = le32_to_cpu(td->status);
129
130 /*
131 * Nothing to do with a still active qTD.
132 */
133 if (status & QTD_STS_ACTIVE)
134 break;
135
136 if (status & QTD_STS_HALTED) {
137 /* Ug, an error. */
138 process_halted_qtd(whc, qset, td);
139 goto done;
140 }
141
142 /* Mmm, a completed qTD. */
143 process_inactive_qtd(whc, qset, td);
144 }
145
146 update |= qset_add_qtds(whc, qset);
147
148done:
149 /*
150 * Remove this qset from the ASL if requested, but only if has
151 * no qTDs.
152 */
153 if (qset->remove && qset->ntds == 0) {
154 asl_qset_remove(whc, qset);
155 update |= WHC_UPDATE_REMOVED;
156 }
157 return update;
158}
159
160void asl_start(struct whc *whc)
161{
162 struct whc_qset *qset;
163
164 qset = list_first_entry(&whc->async_list, struct whc_qset, list_node);
165
166 le_writeq(qset->qset_dma | QH_LINK_NTDS(8), whc->base + WUSBASYNCLISTADDR);
167
168 whc_write_wusbcmd(whc, WUSBCMD_ASYNC_EN, WUSBCMD_ASYNC_EN);
169 whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS,
170 WUSBSTS_ASYNC_SCHED, WUSBSTS_ASYNC_SCHED,
171 1000, "start ASL");
172}
173
174void asl_stop(struct whc *whc)
175{
176 whc_write_wusbcmd(whc, WUSBCMD_ASYNC_EN, 0);
177 whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS,
178 WUSBSTS_ASYNC_SCHED, 0,
179 1000, "stop ASL");
180}
181
182void asl_update(struct whc *whc, uint32_t wusbcmd)
183{
184 whc_write_wusbcmd(whc, wusbcmd, wusbcmd);
185 wait_event(whc->async_list_wq,
186 (le_readl(whc->base + WUSBCMD) & WUSBCMD_ASYNC_UPDATED) == 0);
187}
188
189/**
190 * scan_async_work - scan the ASL for qsets to process.
191 *
192 * Process each qset in the ASL in turn and then signal the WHC that
193 * the ASL has been updated.
194 *
195 * Then start, stop or update the asynchronous schedule as required.
196 */
197void scan_async_work(struct work_struct *work)
198{
199 struct whc *whc = container_of(work, struct whc, async_work);
200 struct whc_qset *qset, *t;
201 enum whc_update update = 0;
202
203 spin_lock_irq(&whc->lock);
204
205 dump_asl(whc, "before processing");
206
207 /*
208 * Transerve the software list backwards so new qsets can be
209 * safely inserted into the ASL without making it non-circular.
210 */
211 list_for_each_entry_safe_reverse(qset, t, &whc->async_list, list_node) {
212 if (!qset->in_hw_list) {
213 asl_qset_insert(whc, qset);
214 update |= WHC_UPDATE_ADDED;
215 }
216
217 update |= process_qset(whc, qset);
218 }
219
220 dump_asl(whc, "after processing");
221
222 spin_unlock_irq(&whc->lock);
223
224 if (update) {
225 uint32_t wusbcmd = WUSBCMD_ASYNC_UPDATED | WUSBCMD_ASYNC_SYNCED_DB;
226 if (update & WHC_UPDATE_REMOVED)
227 wusbcmd |= WUSBCMD_ASYNC_QSET_RM;
228 asl_update(whc, wusbcmd);
229 }
230
231 /*
232 * Now that the ASL is updated, complete the removal of any
233 * removed qsets.
234 */
235 spin_lock(&whc->lock);
236
237 list_for_each_entry_safe(qset, t, &whc->async_removed_list, list_node) {
238 qset_remove_complete(whc, qset);
239 }
240
241 spin_unlock(&whc->lock);
242}
243
244/**
245 * asl_urb_enqueue - queue an URB onto the asynchronous list (ASL).
246 * @whc: the WHCI host controller
247 * @urb: the URB to enqueue
248 * @mem_flags: flags for any memory allocations
249 *
250 * The qset for the endpoint is obtained and the urb queued on to it.
251 *
252 * Work is scheduled to update the hardware's view of the ASL.
253 */
254int asl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags)
255{
256 struct whc_qset *qset;
257 int err;
258 unsigned long flags;
259
260 spin_lock_irqsave(&whc->lock, flags);
261
262 qset = get_qset(whc, urb, GFP_ATOMIC);
263 if (qset == NULL)
264 err = -ENOMEM;
265 else
266 err = qset_add_urb(whc, qset, urb, GFP_ATOMIC);
267 if (!err) {
268 usb_hcd_link_urb_to_ep(&whc->wusbhc.usb_hcd, urb);
269 if (!qset->in_sw_list)
270 asl_qset_insert_begin(whc, qset);
271 }
272
273 spin_unlock_irqrestore(&whc->lock, flags);
274
275 if (!err)
276 queue_work(whc->workqueue, &whc->async_work);
277
278 return 0;
279}
280
281/**
282 * asl_urb_dequeue - remove an URB (qset) from the async list.
283 * @whc: the WHCI host controller
284 * @urb: the URB to dequeue
285 * @status: the current status of the URB
286 *
287 * URBs that do yet have qTDs can simply be removed from the software
288 * queue, otherwise the qset must be removed from the ASL so the qTDs
289 * can be removed.
290 */
291int asl_urb_dequeue(struct whc *whc, struct urb *urb, int status)
292{
293 struct whc_urb *wurb = urb->hcpriv;
294 struct whc_qset *qset = wurb->qset;
295 struct whc_std *std, *t;
296 int ret;
297 unsigned long flags;
298
299 spin_lock_irqsave(&whc->lock, flags);
300
301 ret = usb_hcd_check_unlink_urb(&whc->wusbhc.usb_hcd, urb, status);
302 if (ret < 0)
303 goto out;
304
305 list_for_each_entry_safe(std, t, &qset->stds, list_node) {
306 if (std->urb == urb)
307 qset_free_std(whc, std);
308 else
309 std->qtd = NULL; /* so this std is re-added when the qset is */
310 }
311
312 asl_qset_remove(whc, qset);
313 wurb->status = status;
314 wurb->is_async = true;
315 queue_work(whc->workqueue, &wurb->dequeue_work);
316
317out:
318 spin_unlock_irqrestore(&whc->lock, flags);
319
320 return ret;
321}
322
323/**
324 * asl_qset_delete - delete a qset from the ASL
325 */
326void asl_qset_delete(struct whc *whc, struct whc_qset *qset)
327{
328 qset->remove = 1;
329 queue_work(whc->workqueue, &whc->async_work);
330 qset_delete(whc, qset);
331}
332
333/**
334 * asl_init - initialize the asynchronous schedule list
335 *
336 * A dummy qset with no qTDs is added to the ASL to simplify removing
337 * qsets (no need to stop the ASL when the last qset is removed).
338 */
339int asl_init(struct whc *whc)
340{
341 struct whc_qset *qset;
342
343 qset = qset_alloc(whc, GFP_KERNEL);
344 if (qset == NULL)
345 return -ENOMEM;
346
347 asl_qset_insert_begin(whc, qset);
348 asl_qset_insert(whc, qset);
349
350 return 0;
351}
352
353/**
354 * asl_clean_up - free ASL resources
355 *
356 * The ASL is stopped and empty except for the dummy qset.
357 */
358void asl_clean_up(struct whc *whc)
359{
360 struct whc_qset *qset;
361
362 if (!list_empty(&whc->async_list)) {
363 qset = list_first_entry(&whc->async_list, struct whc_qset, list_node);
364 list_del(&qset->list_node);
365 qset_free(whc, qset);
366 }
367}
diff --git a/drivers/usb/host/whci/hcd.c b/drivers/usb/host/whci/hcd.c
new file mode 100644
index 000000000000..ef3ad4dca945
--- /dev/null
+++ b/drivers/usb/host/whci/hcd.c
@@ -0,0 +1,339 @@
1/*
2 * Wireless Host Controller (WHC) driver.
3 *
4 * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version
8 * 2 as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18#include <linux/version.h>
19#include <linux/kernel.h>
20#include <linux/init.h>
21#include <linux/uwb/umc.h>
22
23#include "../../wusbcore/wusbhc.h"
24
25#include "whcd.h"
26
27/*
28 * One time initialization.
29 *
30 * Nothing to do here.
31 */
32static int whc_reset(struct usb_hcd *usb_hcd)
33{
34 return 0;
35}
36
37/*
38 * Start the wireless host controller.
39 *
40 * Start device notification.
41 *
42 * Put hc into run state, set DNTS parameters.
43 */
44static int whc_start(struct usb_hcd *usb_hcd)
45{
46 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
47 struct whc *whc = wusbhc_to_whc(wusbhc);
48 u8 bcid;
49 int ret;
50
51 mutex_lock(&wusbhc->mutex);
52
53 le_writel(WUSBINTR_GEN_CMD_DONE
54 | WUSBINTR_HOST_ERR
55 | WUSBINTR_ASYNC_SCHED_SYNCED
56 | WUSBINTR_DNTS_INT
57 | WUSBINTR_ERR_INT
58 | WUSBINTR_INT,
59 whc->base + WUSBINTR);
60
61 /* set cluster ID */
62 bcid = wusb_cluster_id_get();
63 ret = whc_set_cluster_id(whc, bcid);
64 if (ret < 0)
65 goto out;
66 wusbhc->cluster_id = bcid;
67
68 /* start HC */
69 whc_write_wusbcmd(whc, WUSBCMD_RUN, WUSBCMD_RUN);
70
71 usb_hcd->uses_new_polling = 1;
72 usb_hcd->poll_rh = 1;
73 usb_hcd->state = HC_STATE_RUNNING;
74
75out:
76 mutex_unlock(&wusbhc->mutex);
77 return ret;
78}
79
80
81/*
82 * Stop the wireless host controller.
83 *
84 * Stop device notification.
85 *
86 * Wait for pending transfer to stop? Put hc into stop state?
87 */
88static void whc_stop(struct usb_hcd *usb_hcd)
89{
90 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
91 struct whc *whc = wusbhc_to_whc(wusbhc);
92
93 mutex_lock(&wusbhc->mutex);
94
95 wusbhc_stop(wusbhc);
96
97 /* stop HC */
98 le_writel(0, whc->base + WUSBINTR);
99 whc_write_wusbcmd(whc, WUSBCMD_RUN, 0);
100 whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS,
101 WUSBSTS_HCHALTED, WUSBSTS_HCHALTED,
102 100, "HC to halt");
103
104 wusb_cluster_id_put(wusbhc->cluster_id);
105
106 mutex_unlock(&wusbhc->mutex);
107}
108
109static int whc_get_frame_number(struct usb_hcd *usb_hcd)
110{
111 /* Frame numbers are not applicable to WUSB. */
112 return -ENOSYS;
113}
114
115
116/*
117 * Queue an URB to the ASL or PZL
118 */
119static int whc_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb,
120 gfp_t mem_flags)
121{
122 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
123 struct whc *whc = wusbhc_to_whc(wusbhc);
124 int ret;
125
126 switch (usb_pipetype(urb->pipe)) {
127 case PIPE_INTERRUPT:
128 ret = pzl_urb_enqueue(whc, urb, mem_flags);
129 break;
130 case PIPE_ISOCHRONOUS:
131 dev_err(&whc->umc->dev, "isochronous transfers unsupported\n");
132 ret = -ENOTSUPP;
133 break;
134 case PIPE_CONTROL:
135 case PIPE_BULK:
136 default:
137 ret = asl_urb_enqueue(whc, urb, mem_flags);
138 break;
139 };
140
141 return ret;
142}
143
144/*
145 * Remove a queued URB from the ASL or PZL.
146 */
147static int whc_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb, int status)
148{
149 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
150 struct whc *whc = wusbhc_to_whc(wusbhc);
151 int ret;
152
153 switch (usb_pipetype(urb->pipe)) {
154 case PIPE_INTERRUPT:
155 ret = pzl_urb_dequeue(whc, urb, status);
156 break;
157 case PIPE_ISOCHRONOUS:
158 ret = -ENOTSUPP;
159 break;
160 case PIPE_CONTROL:
161 case PIPE_BULK:
162 default:
163 ret = asl_urb_dequeue(whc, urb, status);
164 break;
165 };
166
167 return ret;
168}
169
170/*
171 * Wait for all URBs to the endpoint to be completed, then delete the
172 * qset.
173 */
174static void whc_endpoint_disable(struct usb_hcd *usb_hcd,
175 struct usb_host_endpoint *ep)
176{
177 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
178 struct whc *whc = wusbhc_to_whc(wusbhc);
179 struct whc_qset *qset;
180
181 qset = ep->hcpriv;
182 if (qset) {
183 ep->hcpriv = NULL;
184 if (usb_endpoint_xfer_bulk(&ep->desc)
185 || usb_endpoint_xfer_control(&ep->desc))
186 asl_qset_delete(whc, qset);
187 else
188 pzl_qset_delete(whc, qset);
189 }
190}
191
192static struct hc_driver whc_hc_driver = {
193 .description = "whci-hcd",
194 .product_desc = "Wireless host controller",
195 .hcd_priv_size = sizeof(struct whc) - sizeof(struct usb_hcd),
196 .irq = whc_int_handler,
197 .flags = HCD_USB2,
198
199 .reset = whc_reset,
200 .start = whc_start,
201 .stop = whc_stop,
202 .get_frame_number = whc_get_frame_number,
203 .urb_enqueue = whc_urb_enqueue,
204 .urb_dequeue = whc_urb_dequeue,
205 .endpoint_disable = whc_endpoint_disable,
206
207 .hub_status_data = wusbhc_rh_status_data,
208 .hub_control = wusbhc_rh_control,
209 .bus_suspend = wusbhc_rh_suspend,
210 .bus_resume = wusbhc_rh_resume,
211 .start_port_reset = wusbhc_rh_start_port_reset,
212};
213
214static int whc_probe(struct umc_dev *umc)
215{
216 int ret = -ENOMEM;
217 struct usb_hcd *usb_hcd;
218 struct wusbhc *wusbhc = NULL;
219 struct whc *whc = NULL;
220 struct device *dev = &umc->dev;
221
222 usb_hcd = usb_create_hcd(&whc_hc_driver, dev, "whci");
223 if (usb_hcd == NULL) {
224 dev_err(dev, "unable to create hcd\n");
225 goto error;
226 }
227
228 usb_hcd->wireless = 1;
229
230 wusbhc = usb_hcd_to_wusbhc(usb_hcd);
231 whc = wusbhc_to_whc(wusbhc);
232 whc->umc = umc;
233
234 ret = whc_init(whc);
235 if (ret)
236 goto error;
237
238 wusbhc->dev = dev;
239 wusbhc->uwb_rc = uwb_rc_get_by_grandpa(umc->dev.parent);
240 if (!wusbhc->uwb_rc) {
241 ret = -ENODEV;
242 dev_err(dev, "cannot get radio controller\n");
243 goto error;
244 }
245
246 if (whc->n_devices > USB_MAXCHILDREN) {
247 dev_warn(dev, "USB_MAXCHILDREN too low for WUSB adapter (%u ports)\n",
248 whc->n_devices);
249 wusbhc->ports_max = USB_MAXCHILDREN;
250 } else
251 wusbhc->ports_max = whc->n_devices;
252 wusbhc->mmcies_max = whc->n_mmc_ies;
253 wusbhc->start = whc_wusbhc_start;
254 wusbhc->stop = whc_wusbhc_stop;
255 wusbhc->mmcie_add = whc_mmcie_add;
256 wusbhc->mmcie_rm = whc_mmcie_rm;
257 wusbhc->dev_info_set = whc_dev_info_set;
258 wusbhc->bwa_set = whc_bwa_set;
259 wusbhc->set_num_dnts = whc_set_num_dnts;
260 wusbhc->set_ptk = whc_set_ptk;
261 wusbhc->set_gtk = whc_set_gtk;
262
263 ret = wusbhc_create(wusbhc);
264 if (ret)
265 goto error_wusbhc_create;
266
267 ret = usb_add_hcd(usb_hcd, whc->umc->irq, IRQF_SHARED);
268 if (ret) {
269 dev_err(dev, "cannot add HCD: %d\n", ret);
270 goto error_usb_add_hcd;
271 }
272
273 ret = wusbhc_b_create(wusbhc);
274 if (ret) {
275 dev_err(dev, "WUSBHC phase B setup failed: %d\n", ret);
276 goto error_wusbhc_b_create;
277 }
278
279 return 0;
280
281error_wusbhc_b_create:
282 usb_remove_hcd(usb_hcd);
283error_usb_add_hcd:
284 wusbhc_destroy(wusbhc);
285error_wusbhc_create:
286 uwb_rc_put(wusbhc->uwb_rc);
287error:
288 whc_clean_up(whc);
289 if (usb_hcd)
290 usb_put_hcd(usb_hcd);
291 return ret;
292}
293
294
295static void whc_remove(struct umc_dev *umc)
296{
297 struct usb_hcd *usb_hcd = dev_get_drvdata(&umc->dev);
298 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
299 struct whc *whc = wusbhc_to_whc(wusbhc);
300
301 if (usb_hcd) {
302 wusbhc_b_destroy(wusbhc);
303 usb_remove_hcd(usb_hcd);
304 wusbhc_destroy(wusbhc);
305 uwb_rc_put(wusbhc->uwb_rc);
306 whc_clean_up(whc);
307 usb_put_hcd(usb_hcd);
308 }
309}
310
311static struct umc_driver whci_hc_driver = {
312 .name = "whci-hcd",
313 .cap_id = UMC_CAP_ID_WHCI_WUSB_HC,
314 .probe = whc_probe,
315 .remove = whc_remove,
316};
317
318static int __init whci_hc_driver_init(void)
319{
320 return umc_driver_register(&whci_hc_driver);
321}
322module_init(whci_hc_driver_init);
323
324static void __exit whci_hc_driver_exit(void)
325{
326 umc_driver_unregister(&whci_hc_driver);
327}
328module_exit(whci_hc_driver_exit);
329
330/* PCI device ID's that we handle (so it gets loaded) */
331static struct pci_device_id whci_hcd_id_table[] = {
332 { PCI_DEVICE_CLASS(PCI_CLASS_WIRELESS_WHCI, ~0) },
333 { /* empty last entry */ }
334};
335MODULE_DEVICE_TABLE(pci, whci_hcd_id_table);
336
337MODULE_DESCRIPTION("WHCI Wireless USB host controller driver");
338MODULE_AUTHOR("Cambridge Silicon Radio Ltd.");
339MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/whci/hw.c b/drivers/usb/host/whci/hw.c
new file mode 100644
index 000000000000..ac86e59c1225
--- /dev/null
+++ b/drivers/usb/host/whci/hw.c
@@ -0,0 +1,87 @@
1/*
2 * Wireless Host Controller (WHC) hardware access helpers.
3 *
4 * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version
8 * 2 as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18#include <linux/kernel.h>
19#include <linux/dma-mapping.h>
20#include <linux/uwb/umc.h>
21
22#include "../../wusbcore/wusbhc.h"
23
24#include "whcd.h"
25
26void whc_write_wusbcmd(struct whc *whc, u32 mask, u32 val)
27{
28 unsigned long flags;
29 u32 cmd;
30
31 spin_lock_irqsave(&whc->lock, flags);
32
33 cmd = le_readl(whc->base + WUSBCMD);
34 cmd = (cmd & ~mask) | val;
35 le_writel(cmd, whc->base + WUSBCMD);
36
37 spin_unlock_irqrestore(&whc->lock, flags);
38}
39
40/**
41 * whc_do_gencmd - start a generic command via the WUSBGENCMDSTS register
42 * @whc: the WHCI HC
43 * @cmd: command to start.
44 * @params: parameters for the command (the WUSBGENCMDPARAMS register value).
45 * @addr: pointer to any data for the command (may be NULL).
46 * @len: length of the data (if any).
47 */
48int whc_do_gencmd(struct whc *whc, u32 cmd, u32 params, void *addr, size_t len)
49{
50 unsigned long flags;
51 dma_addr_t dma_addr;
52 int t;
53
54 mutex_lock(&whc->mutex);
55
56 /* Wait for previous command to complete. */
57 t = wait_event_timeout(whc->cmd_wq,
58 (le_readl(whc->base + WUSBGENCMDSTS) & WUSBGENCMDSTS_ACTIVE) == 0,
59 WHC_GENCMD_TIMEOUT_MS);
60 if (t == 0) {
61 dev_err(&whc->umc->dev, "generic command timeout (%04x/%04x)\n",
62 le_readl(whc->base + WUSBGENCMDSTS),
63 le_readl(whc->base + WUSBGENCMDPARAMS));
64 return -ETIMEDOUT;
65 }
66
67 if (addr) {
68 memcpy(whc->gen_cmd_buf, addr, len);
69 dma_addr = whc->gen_cmd_buf_dma;
70 } else
71 dma_addr = 0;
72
73 /* Poke registers to start cmd. */
74 spin_lock_irqsave(&whc->lock, flags);
75
76 le_writel(params, whc->base + WUSBGENCMDPARAMS);
77 le_writeq(dma_addr, whc->base + WUSBGENADDR);
78
79 le_writel(WUSBGENCMDSTS_ACTIVE | WUSBGENCMDSTS_IOC | cmd,
80 whc->base + WUSBGENCMDSTS);
81
82 spin_unlock_irqrestore(&whc->lock, flags);
83
84 mutex_unlock(&whc->mutex);
85
86 return 0;
87}
diff --git a/drivers/usb/host/whci/init.c b/drivers/usb/host/whci/init.c
new file mode 100644
index 000000000000..34a783cb0133
--- /dev/null
+++ b/drivers/usb/host/whci/init.c
@@ -0,0 +1,188 @@
1/*
2 * Wireless Host Controller (WHC) initialization.
3 *
4 * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version
8 * 2 as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18#include <linux/kernel.h>
19#include <linux/dma-mapping.h>
20#include <linux/uwb/umc.h>
21
22#include "../../wusbcore/wusbhc.h"
23
24#include "whcd.h"
25
26/*
27 * Reset the host controller.
28 */
29static void whc_hw_reset(struct whc *whc)
30{
31 le_writel(WUSBCMD_WHCRESET, whc->base + WUSBCMD);
32 whci_wait_for(&whc->umc->dev, whc->base + WUSBCMD, WUSBCMD_WHCRESET, 0,
33 100, "reset");
34}
35
36static void whc_hw_init_di_buf(struct whc *whc)
37{
38 int d;
39
40 /* Disable all entries in the Device Information buffer. */
41 for (d = 0; d < whc->n_devices; d++)
42 whc->di_buf[d].addr_sec_info = WHC_DI_DISABLE;
43
44 le_writeq(whc->di_buf_dma, whc->base + WUSBDEVICEINFOADDR);
45}
46
47static void whc_hw_init_dn_buf(struct whc *whc)
48{
49 /* Clear the Device Notification buffer to ensure the V (valid)
50 * bits are clear. */
51 memset(whc->dn_buf, 0, 4096);
52
53 le_writeq(whc->dn_buf_dma, whc->base + WUSBDNTSBUFADDR);
54}
55
56int whc_init(struct whc *whc)
57{
58 u32 whcsparams;
59 int ret, i;
60 resource_size_t start, len;
61
62 spin_lock_init(&whc->lock);
63 mutex_init(&whc->mutex);
64 init_waitqueue_head(&whc->cmd_wq);
65 init_waitqueue_head(&whc->async_list_wq);
66 init_waitqueue_head(&whc->periodic_list_wq);
67 whc->workqueue = create_singlethread_workqueue(dev_name(&whc->umc->dev));
68 if (whc->workqueue == NULL) {
69 ret = -ENOMEM;
70 goto error;
71 }
72 INIT_WORK(&whc->dn_work, whc_dn_work);
73
74 INIT_WORK(&whc->async_work, scan_async_work);
75 INIT_LIST_HEAD(&whc->async_list);
76 INIT_LIST_HEAD(&whc->async_removed_list);
77
78 INIT_WORK(&whc->periodic_work, scan_periodic_work);
79 for (i = 0; i < 5; i++)
80 INIT_LIST_HEAD(&whc->periodic_list[i]);
81 INIT_LIST_HEAD(&whc->periodic_removed_list);
82
83 /* Map HC registers. */
84 start = whc->umc->resource.start;
85 len = whc->umc->resource.end - start + 1;
86 if (!request_mem_region(start, len, "whci-hc")) {
87 dev_err(&whc->umc->dev, "can't request HC region\n");
88 ret = -EBUSY;
89 goto error;
90 }
91 whc->base_phys = start;
92 whc->base = ioremap(start, len);
93 if (!whc->base) {
94 dev_err(&whc->umc->dev, "ioremap\n");
95 ret = -ENOMEM;
96 goto error;
97 }
98
99 whc_hw_reset(whc);
100
101 /* Read maximum number of devices, keys and MMC IEs. */
102 whcsparams = le_readl(whc->base + WHCSPARAMS);
103 whc->n_devices = WHCSPARAMS_TO_N_DEVICES(whcsparams);
104 whc->n_keys = WHCSPARAMS_TO_N_KEYS(whcsparams);
105 whc->n_mmc_ies = WHCSPARAMS_TO_N_MMC_IES(whcsparams);
106
107 dev_dbg(&whc->umc->dev, "N_DEVICES = %d, N_KEYS = %d, N_MMC_IES = %d\n",
108 whc->n_devices, whc->n_keys, whc->n_mmc_ies);
109
110 whc->qset_pool = dma_pool_create("qset", &whc->umc->dev,
111 sizeof(struct whc_qset), 64, 0);
112 if (whc->qset_pool == NULL) {
113 ret = -ENOMEM;
114 goto error;
115 }
116
117 ret = asl_init(whc);
118 if (ret < 0)
119 goto error;
120 ret = pzl_init(whc);
121 if (ret < 0)
122 goto error;
123
124 /* Allocate and initialize a buffer for generic commands, the
125 Device Information buffer, and the Device Notification
126 buffer. */
127
128 whc->gen_cmd_buf = dma_alloc_coherent(&whc->umc->dev, WHC_GEN_CMD_DATA_LEN,
129 &whc->gen_cmd_buf_dma, GFP_KERNEL);
130 if (whc->gen_cmd_buf == NULL) {
131 ret = -ENOMEM;
132 goto error;
133 }
134
135 whc->dn_buf = dma_alloc_coherent(&whc->umc->dev,
136 sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES,
137 &whc->dn_buf_dma, GFP_KERNEL);
138 if (!whc->dn_buf) {
139 ret = -ENOMEM;
140 goto error;
141 }
142 whc_hw_init_dn_buf(whc);
143
144 whc->di_buf = dma_alloc_coherent(&whc->umc->dev,
145 sizeof(struct di_buf_entry) * whc->n_devices,
146 &whc->di_buf_dma, GFP_KERNEL);
147 if (!whc->di_buf) {
148 ret = -ENOMEM;
149 goto error;
150 }
151 whc_hw_init_di_buf(whc);
152
153 return 0;
154
155error:
156 whc_clean_up(whc);
157 return ret;
158}
159
160void whc_clean_up(struct whc *whc)
161{
162 resource_size_t len;
163
164 if (whc->di_buf)
165 dma_free_coherent(&whc->umc->dev, sizeof(struct di_buf_entry) * whc->n_devices,
166 whc->di_buf, whc->di_buf_dma);
167 if (whc->dn_buf)
168 dma_free_coherent(&whc->umc->dev, sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES,
169 whc->dn_buf, whc->dn_buf_dma);
170 if (whc->gen_cmd_buf)
171 dma_free_coherent(&whc->umc->dev, WHC_GEN_CMD_DATA_LEN,
172 whc->gen_cmd_buf, whc->gen_cmd_buf_dma);
173
174 pzl_clean_up(whc);
175 asl_clean_up(whc);
176
177 if (whc->qset_pool)
178 dma_pool_destroy(whc->qset_pool);
179
180 len = whc->umc->resource.end - whc->umc->resource.start + 1;
181 if (whc->base)
182 iounmap(whc->base);
183 if (whc->base_phys)
184 release_mem_region(whc->base_phys, len);
185
186 if (whc->workqueue)
187 destroy_workqueue(whc->workqueue);
188}
diff --git a/drivers/usb/host/whci/int.c b/drivers/usb/host/whci/int.c
new file mode 100644
index 000000000000..fce01174aa9b
--- /dev/null
+++ b/drivers/usb/host/whci/int.c
@@ -0,0 +1,95 @@
1/*
2 * Wireless Host Controller (WHC) interrupt handling.
3 *
4 * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version
8 * 2 as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18#include <linux/version.h>
19#include <linux/kernel.h>
20#include <linux/init.h>
21#include <linux/uwb/umc.h>
22
23#include "../../wusbcore/wusbhc.h"
24
25#include "whcd.h"
26
27static void transfer_done(struct whc *whc)
28{
29 queue_work(whc->workqueue, &whc->async_work);
30 queue_work(whc->workqueue, &whc->periodic_work);
31}
32
33irqreturn_t whc_int_handler(struct usb_hcd *hcd)
34{
35 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(hcd);
36 struct whc *whc = wusbhc_to_whc(wusbhc);
37 u32 sts;
38
39 sts = le_readl(whc->base + WUSBSTS);
40 if (!(sts & WUSBSTS_INT_MASK))
41 return IRQ_NONE;
42 le_writel(sts & WUSBSTS_INT_MASK, whc->base + WUSBSTS);
43
44 if (sts & WUSBSTS_GEN_CMD_DONE)
45 wake_up(&whc->cmd_wq);
46
47 if (sts & WUSBSTS_HOST_ERR)
48 dev_err(&whc->umc->dev, "FIXME: host system error\n");
49
50 if (sts & WUSBSTS_ASYNC_SCHED_SYNCED)
51 wake_up(&whc->async_list_wq);
52
53 if (sts & WUSBSTS_PERIODIC_SCHED_SYNCED)
54 wake_up(&whc->periodic_list_wq);
55
56 if (sts & WUSBSTS_DNTS_INT)
57 queue_work(whc->workqueue, &whc->dn_work);
58
59 /*
60 * A transfer completed (see [WHCI] section 4.7.1.2 for when
61 * this occurs).
62 */
63 if (sts & (WUSBSTS_INT | WUSBSTS_ERR_INT))
64 transfer_done(whc);
65
66 return IRQ_HANDLED;
67}
68
69static int process_dn_buf(struct whc *whc)
70{
71 struct wusbhc *wusbhc = &whc->wusbhc;
72 struct dn_buf_entry *dn;
73 int processed = 0;
74
75 for (dn = whc->dn_buf; dn < whc->dn_buf + WHC_N_DN_ENTRIES; dn++) {
76 if (dn->status & WHC_DN_STATUS_VALID) {
77 wusbhc_handle_dn(wusbhc, dn->src_addr,
78 (struct wusb_dn_hdr *)dn->dn_data,
79 dn->msg_size);
80 dn->status &= ~WHC_DN_STATUS_VALID;
81 processed++;
82 }
83 }
84 return processed;
85}
86
87void whc_dn_work(struct work_struct *work)
88{
89 struct whc *whc = container_of(work, struct whc, dn_work);
90 int processed;
91
92 do {
93 processed = process_dn_buf(whc);
94 } while (processed);
95}
diff --git a/drivers/usb/host/whci/pzl.c b/drivers/usb/host/whci/pzl.c
new file mode 100644
index 000000000000..8d62df0c330b
--- /dev/null
+++ b/drivers/usb/host/whci/pzl.c
@@ -0,0 +1,398 @@
1/*
2 * Wireless Host Controller (WHC) periodic schedule management.
3 *
4 * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version
8 * 2 as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18#include <linux/kernel.h>
19#include <linux/dma-mapping.h>
20#include <linux/uwb/umc.h>
21#include <linux/usb.h>
22#define D_LOCAL 0
23#include <linux/uwb/debug.h>
24
25#include "../../wusbcore/wusbhc.h"
26
27#include "whcd.h"
28
29#if D_LOCAL >= 4
30static void dump_pzl(struct whc *whc, const char *tag)
31{
32 struct device *dev = &whc->umc->dev;
33 struct whc_qset *qset;
34 int period = 0;
35
36 d_printf(4, dev, "PZL %s\n", tag);
37
38 for (period = 0; period < 5; period++) {
39 d_printf(4, dev, "Period %d\n", period);
40 list_for_each_entry(qset, &whc->periodic_list[period], list_node) {
41 dump_qset(qset, dev);
42 }
43 }
44}
45#else
46static inline void dump_pzl(struct whc *whc, const char *tag)
47{
48}
49#endif
50
51static void update_pzl_pointers(struct whc *whc, int period, u64 addr)
52{
53 switch (period) {
54 case 0:
55 whc_qset_set_link_ptr(&whc->pz_list[0], addr);
56 whc_qset_set_link_ptr(&whc->pz_list[2], addr);
57 whc_qset_set_link_ptr(&whc->pz_list[4], addr);
58 whc_qset_set_link_ptr(&whc->pz_list[6], addr);
59 whc_qset_set_link_ptr(&whc->pz_list[8], addr);
60 whc_qset_set_link_ptr(&whc->pz_list[10], addr);
61 whc_qset_set_link_ptr(&whc->pz_list[12], addr);
62 whc_qset_set_link_ptr(&whc->pz_list[14], addr);
63 break;
64 case 1:
65 whc_qset_set_link_ptr(&whc->pz_list[1], addr);
66 whc_qset_set_link_ptr(&whc->pz_list[5], addr);
67 whc_qset_set_link_ptr(&whc->pz_list[9], addr);
68 whc_qset_set_link_ptr(&whc->pz_list[13], addr);
69 break;
70 case 2:
71 whc_qset_set_link_ptr(&whc->pz_list[3], addr);
72 whc_qset_set_link_ptr(&whc->pz_list[11], addr);
73 break;
74 case 3:
75 whc_qset_set_link_ptr(&whc->pz_list[7], addr);
76 break;
77 case 4:
78 whc_qset_set_link_ptr(&whc->pz_list[15], addr);
79 break;
80 }
81}
82
83/*
84 * Return the 'period' to use for this qset. The minimum interval for
85 * the endpoint is used so whatever urbs are submitted the device is
86 * polled often enough.
87 */
88static int qset_get_period(struct whc *whc, struct whc_qset *qset)
89{
90 uint8_t bInterval = qset->ep->desc.bInterval;
91
92 if (bInterval < 6)
93 bInterval = 6;
94 if (bInterval > 10)
95 bInterval = 10;
96 return bInterval - 6;
97}
98
99static void qset_insert_in_sw_list(struct whc *whc, struct whc_qset *qset)
100{
101 int period;
102
103 period = qset_get_period(whc, qset);
104
105 qset_clear(whc, qset);
106 list_move(&qset->list_node, &whc->periodic_list[period]);
107 qset->in_sw_list = true;
108}
109
110static void pzl_qset_remove(struct whc *whc, struct whc_qset *qset)
111{
112 list_move(&qset->list_node, &whc->periodic_removed_list);
113 qset->in_hw_list = false;
114 qset->in_sw_list = false;
115}
116
117/**
118 * pzl_process_qset - process any recently inactivated or halted qTDs
119 * in a qset.
120 *
121 * After inactive qTDs are removed, new qTDs can be added if the
122 * urb queue still contains URBs.
123 *
124 * Returns the schedule updates required.
125 */
126static enum whc_update pzl_process_qset(struct whc *whc, struct whc_qset *qset)
127{
128 enum whc_update update = 0;
129 uint32_t status = 0;
130
131 while (qset->ntds) {
132 struct whc_qtd *td;
133 int t;
134
135 t = qset->td_start;
136 td = &qset->qtd[qset->td_start];
137 status = le32_to_cpu(td->status);
138
139 /*
140 * Nothing to do with a still active qTD.
141 */
142 if (status & QTD_STS_ACTIVE)
143 break;
144
145 if (status & QTD_STS_HALTED) {
146 /* Ug, an error. */
147 process_halted_qtd(whc, qset, td);
148 goto done;
149 }
150
151 /* Mmm, a completed qTD. */
152 process_inactive_qtd(whc, qset, td);
153 }
154
155 update |= qset_add_qtds(whc, qset);
156
157done:
158 /*
159 * If there are no qTDs in this qset, remove it from the PZL.
160 */
161 if (qset->remove && qset->ntds == 0) {
162 pzl_qset_remove(whc, qset);
163 update |= WHC_UPDATE_REMOVED;
164 }
165
166 return update;
167}
168
169/**
170 * pzl_start - start the periodic schedule
171 * @whc: the WHCI host controller
172 *
173 * The PZL must be valid (e.g., all entries in the list should have
174 * the T bit set).
175 */
176void pzl_start(struct whc *whc)
177{
178 le_writeq(whc->pz_list_dma, whc->base + WUSBPERIODICLISTBASE);
179
180 whc_write_wusbcmd(whc, WUSBCMD_PERIODIC_EN, WUSBCMD_PERIODIC_EN);
181 whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS,
182 WUSBSTS_PERIODIC_SCHED, WUSBSTS_PERIODIC_SCHED,
183 1000, "start PZL");
184}
185
186/**
187 * pzl_stop - stop the periodic schedule
188 * @whc: the WHCI host controller
189 */
190void pzl_stop(struct whc *whc)
191{
192 whc_write_wusbcmd(whc, WUSBCMD_PERIODIC_EN, 0);
193 whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS,
194 WUSBSTS_PERIODIC_SCHED, 0,
195 1000, "stop PZL");
196}
197
198void pzl_update(struct whc *whc, uint32_t wusbcmd)
199{
200 whc_write_wusbcmd(whc, wusbcmd, wusbcmd);
201 wait_event(whc->periodic_list_wq,
202 (le_readl(whc->base + WUSBCMD) & WUSBCMD_PERIODIC_UPDATED) == 0);
203}
204
205static void update_pzl_hw_view(struct whc *whc)
206{
207 struct whc_qset *qset, *t;
208 int period;
209 u64 tmp_qh = 0;
210
211 for (period = 0; period < 5; period++) {
212 list_for_each_entry_safe(qset, t, &whc->periodic_list[period], list_node) {
213 whc_qset_set_link_ptr(&qset->qh.link, tmp_qh);
214 tmp_qh = qset->qset_dma;
215 qset->in_hw_list = true;
216 }
217 update_pzl_pointers(whc, period, tmp_qh);
218 }
219}
220
221/**
222 * scan_periodic_work - scan the PZL for qsets to process.
223 *
224 * Process each qset in the PZL in turn and then signal the WHC that
225 * the PZL has been updated.
226 *
227 * Then start, stop or update the periodic schedule as required.
228 */
229void scan_periodic_work(struct work_struct *work)
230{
231 struct whc *whc = container_of(work, struct whc, periodic_work);
232 struct whc_qset *qset, *t;
233 enum whc_update update = 0;
234 int period;
235
236 spin_lock_irq(&whc->lock);
237
238 dump_pzl(whc, "before processing");
239
240 for (period = 4; period >= 0; period--) {
241 list_for_each_entry_safe(qset, t, &whc->periodic_list[period], list_node) {
242 if (!qset->in_hw_list)
243 update |= WHC_UPDATE_ADDED;
244 update |= pzl_process_qset(whc, qset);
245 }
246 }
247
248 if (update & (WHC_UPDATE_ADDED | WHC_UPDATE_REMOVED))
249 update_pzl_hw_view(whc);
250
251 dump_pzl(whc, "after processing");
252
253 spin_unlock_irq(&whc->lock);
254
255 if (update) {
256 uint32_t wusbcmd = WUSBCMD_PERIODIC_UPDATED | WUSBCMD_PERIODIC_SYNCED_DB;
257 if (update & WHC_UPDATE_REMOVED)
258 wusbcmd |= WUSBCMD_PERIODIC_QSET_RM;
259 pzl_update(whc, wusbcmd);
260 }
261
262 /*
263 * Now that the PZL is updated, complete the removal of any
264 * removed qsets.
265 */
266 spin_lock(&whc->lock);
267
268 list_for_each_entry_safe(qset, t, &whc->periodic_removed_list, list_node) {
269 qset_remove_complete(whc, qset);
270 }
271
272 spin_unlock(&whc->lock);
273}
274
275/**
276 * pzl_urb_enqueue - queue an URB onto the periodic list (PZL)
277 * @whc: the WHCI host controller
278 * @urb: the URB to enqueue
279 * @mem_flags: flags for any memory allocations
280 *
281 * The qset for the endpoint is obtained and the urb queued on to it.
282 *
283 * Work is scheduled to update the hardware's view of the PZL.
284 */
285int pzl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags)
286{
287 struct whc_qset *qset;
288 int err;
289 unsigned long flags;
290
291 spin_lock_irqsave(&whc->lock, flags);
292
293 qset = get_qset(whc, urb, GFP_ATOMIC);
294 if (qset == NULL)
295 err = -ENOMEM;
296 else
297 err = qset_add_urb(whc, qset, urb, GFP_ATOMIC);
298 if (!err) {
299 usb_hcd_link_urb_to_ep(&whc->wusbhc.usb_hcd, urb);
300 if (!qset->in_sw_list)
301 qset_insert_in_sw_list(whc, qset);
302 }
303
304 spin_unlock_irqrestore(&whc->lock, flags);
305
306 if (!err)
307 queue_work(whc->workqueue, &whc->periodic_work);
308
309 return 0;
310}
311
312/**
313 * pzl_urb_dequeue - remove an URB (qset) from the periodic list
314 * @whc: the WHCI host controller
315 * @urb: the URB to dequeue
316 * @status: the current status of the URB
317 *
318 * URBs that do yet have qTDs can simply be removed from the software
319 * queue, otherwise the qset must be removed so the qTDs can be safely
320 * removed.
321 */
322int pzl_urb_dequeue(struct whc *whc, struct urb *urb, int status)
323{
324 struct whc_urb *wurb = urb->hcpriv;
325 struct whc_qset *qset = wurb->qset;
326 struct whc_std *std, *t;
327 int ret;
328 unsigned long flags;
329
330 spin_lock_irqsave(&whc->lock, flags);
331
332 ret = usb_hcd_check_unlink_urb(&whc->wusbhc.usb_hcd, urb, status);
333 if (ret < 0)
334 goto out;
335
336 list_for_each_entry_safe(std, t, &qset->stds, list_node) {
337 if (std->urb == urb)
338 qset_free_std(whc, std);
339 else
340 std->qtd = NULL; /* so this std is re-added when the qset is */
341 }
342
343 pzl_qset_remove(whc, qset);
344 wurb->status = status;
345 wurb->is_async = false;
346 queue_work(whc->workqueue, &wurb->dequeue_work);
347
348out:
349 spin_unlock_irqrestore(&whc->lock, flags);
350
351 return ret;
352}
353
354/**
355 * pzl_qset_delete - delete a qset from the PZL
356 */
357void pzl_qset_delete(struct whc *whc, struct whc_qset *qset)
358{
359 qset->remove = 1;
360 queue_work(whc->workqueue, &whc->periodic_work);
361 qset_delete(whc, qset);
362}
363
364
365/**
366 * pzl_init - initialize the periodic zone list
367 * @whc: the WHCI host controller
368 */
369int pzl_init(struct whc *whc)
370{
371 int i;
372
373 whc->pz_list = dma_alloc_coherent(&whc->umc->dev, sizeof(u64) * 16,
374 &whc->pz_list_dma, GFP_KERNEL);
375 if (whc->pz_list == NULL)
376 return -ENOMEM;
377
378 /* Set T bit on all elements in PZL. */
379 for (i = 0; i < 16; i++)
380 whc->pz_list[i] = cpu_to_le64(QH_LINK_NTDS(8) | QH_LINK_T);
381
382 le_writeq(whc->pz_list_dma, whc->base + WUSBPERIODICLISTBASE);
383
384 return 0;
385}
386
387/**
388 * pzl_clean_up - free PZL resources
389 * @whc: the WHCI host controller
390 *
391 * The PZL is stopped and empty.
392 */
393void pzl_clean_up(struct whc *whc)
394{
395 if (whc->pz_list)
396 dma_free_coherent(&whc->umc->dev, sizeof(u64) * 16, whc->pz_list,
397 whc->pz_list_dma);
398}
diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c
new file mode 100644
index 000000000000..0420037d2e18
--- /dev/null
+++ b/drivers/usb/host/whci/qset.c
@@ -0,0 +1,567 @@
1/*
2 * Wireless Host Controller (WHC) qset management.
3 *
4 * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version
8 * 2 as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18#include <linux/kernel.h>
19#include <linux/dma-mapping.h>
20#include <linux/uwb/umc.h>
21#include <linux/usb.h>
22
23#include "../../wusbcore/wusbhc.h"
24
25#include "whcd.h"
26
27void dump_qset(struct whc_qset *qset, struct device *dev)
28{
29 struct whc_std *std;
30 struct urb *urb = NULL;
31 int i;
32
33 dev_dbg(dev, "qset %08x\n", (u32)qset->qset_dma);
34 dev_dbg(dev, " -> %08x\n", (u32)qset->qh.link);
35 dev_dbg(dev, " info: %08x %08x %08x\n",
36 qset->qh.info1, qset->qh.info2, qset->qh.info3);
37 dev_dbg(dev, " sts: %04x errs: %d\n", qset->qh.status, qset->qh.err_count);
38 dev_dbg(dev, " TD: sts: %08x opts: %08x\n",
39 qset->qh.overlay.qtd.status, qset->qh.overlay.qtd.options);
40
41 for (i = 0; i < WHCI_QSET_TD_MAX; i++) {
42 dev_dbg(dev, " %c%c TD[%d]: sts: %08x opts: %08x ptr: %08x\n",
43 i == qset->td_start ? 'S' : ' ',
44 i == qset->td_end ? 'E' : ' ',
45 i, qset->qtd[i].status, qset->qtd[i].options,
46 (u32)qset->qtd[i].page_list_ptr);
47 }
48 dev_dbg(dev, " ntds: %d\n", qset->ntds);
49 list_for_each_entry(std, &qset->stds, list_node) {
50 if (urb != std->urb) {
51 urb = std->urb;
52 dev_dbg(dev, " urb %p transferred: %d bytes\n", urb,
53 urb->actual_length);
54 }
55 if (std->qtd)
56 dev_dbg(dev, " sTD[%td]: %zu bytes @ %08x\n",
57 std->qtd - &qset->qtd[0],
58 std->len, std->num_pointers ?
59 (u32)(std->pl_virt[0].buf_ptr) : (u32)std->dma_addr);
60 else
61 dev_dbg(dev, " sTD[-]: %zd bytes @ %08x\n",
62 std->len, std->num_pointers ?
63 (u32)(std->pl_virt[0].buf_ptr) : (u32)std->dma_addr);
64 }
65}
66
67struct whc_qset *qset_alloc(struct whc *whc, gfp_t mem_flags)
68{
69 struct whc_qset *qset;
70 dma_addr_t dma;
71
72 qset = dma_pool_alloc(whc->qset_pool, mem_flags, &dma);
73 if (qset == NULL)
74 return NULL;
75 memset(qset, 0, sizeof(struct whc_qset));
76
77 qset->qset_dma = dma;
78 qset->whc = whc;
79
80 INIT_LIST_HEAD(&qset->list_node);
81 INIT_LIST_HEAD(&qset->stds);
82
83 return qset;
84}
85
86/**
87 * qset_fill_qh - fill the static endpoint state in a qset's QHead
88 * @qset: the qset whose QH needs initializing with static endpoint
89 * state
90 * @urb: an urb for a transfer to this endpoint
91 */
92static void qset_fill_qh(struct whc_qset *qset, struct urb *urb)
93{
94 struct usb_device *usb_dev = urb->dev;
95 struct usb_wireless_ep_comp_descriptor *epcd;
96 bool is_out;
97
98 is_out = usb_pipeout(urb->pipe);
99
100 epcd = (struct usb_wireless_ep_comp_descriptor *)qset->ep->extra;
101
102 if (epcd) {
103 qset->max_seq = epcd->bMaxSequence;
104 qset->max_burst = epcd->bMaxBurst;
105 } else {
106 qset->max_seq = 2;
107 qset->max_burst = 1;
108 }
109
110 qset->qh.info1 = cpu_to_le32(
111 QH_INFO1_EP(usb_pipeendpoint(urb->pipe))
112 | (is_out ? QH_INFO1_DIR_OUT : QH_INFO1_DIR_IN)
113 | usb_pipe_to_qh_type(urb->pipe)
114 | QH_INFO1_DEV_INFO_IDX(wusb_port_no_to_idx(usb_dev->portnum))
115 | QH_INFO1_MAX_PKT_LEN(usb_maxpacket(urb->dev, urb->pipe, is_out))
116 );
117 qset->qh.info2 = cpu_to_le32(
118 QH_INFO2_BURST(qset->max_burst)
119 | QH_INFO2_DBP(0)
120 | QH_INFO2_MAX_COUNT(3)
121 | QH_INFO2_MAX_RETRY(3)
122 | QH_INFO2_MAX_SEQ(qset->max_seq - 1)
123 );
124 /* FIXME: where can we obtain these Tx parameters from? Why
125 * doesn't the chip know what Tx power to use? It knows the Rx
126 * strength and can presumably guess the Tx power required
127 * from that? */
128 qset->qh.info3 = cpu_to_le32(
129 QH_INFO3_TX_RATE_53_3
130 | QH_INFO3_TX_PWR(0) /* 0 == max power */
131 );
132}
133
134/**
135 * qset_clear - clear fields in a qset so it may be reinserted into a
136 * schedule
137 */
138void qset_clear(struct whc *whc, struct whc_qset *qset)
139{
140 qset->td_start = qset->td_end = qset->ntds = 0;
141 qset->remove = 0;
142
143 qset->qh.link = cpu_to_le32(QH_LINK_NTDS(8) | QH_LINK_T);
144 qset->qh.status = cpu_to_le16(QH_STATUS_ICUR(qset->td_start));
145 qset->qh.err_count = 0;
146 qset->qh.cur_window = cpu_to_le32((1 << qset->max_burst) - 1);
147 qset->qh.scratch[0] = 0;
148 qset->qh.scratch[1] = 0;
149 qset->qh.scratch[2] = 0;
150
151 memset(&qset->qh.overlay, 0, sizeof(qset->qh.overlay));
152
153 init_completion(&qset->remove_complete);
154}
155
156/**
157 * get_qset - get the qset for an async endpoint
158 *
159 * A new qset is created if one does not already exist.
160 */
161struct whc_qset *get_qset(struct whc *whc, struct urb *urb,
162 gfp_t mem_flags)
163{
164 struct whc_qset *qset;
165
166 qset = urb->ep->hcpriv;
167 if (qset == NULL) {
168 qset = qset_alloc(whc, mem_flags);
169 if (qset == NULL)
170 return NULL;
171
172 qset->ep = urb->ep;
173 urb->ep->hcpriv = qset;
174 qset_fill_qh(qset, urb);
175 }
176 return qset;
177}
178
179void qset_remove_complete(struct whc *whc, struct whc_qset *qset)
180{
181 list_del_init(&qset->list_node);
182 complete(&qset->remove_complete);
183}
184
185/**
186 * qset_add_qtds - add qTDs for an URB to a qset
187 *
188 * Returns true if the list (ASL/PZL) must be updated because (for a
189 * WHCI 0.95 controller) an activated qTD was pointed to be iCur.
190 */
191enum whc_update qset_add_qtds(struct whc *whc, struct whc_qset *qset)
192{
193 struct whc_std *std;
194 enum whc_update update = 0;
195
196 list_for_each_entry(std, &qset->stds, list_node) {
197 struct whc_qtd *qtd;
198 uint32_t status;
199
200 if (qset->ntds >= WHCI_QSET_TD_MAX
201 || (qset->pause_after_urb && std->urb != qset->pause_after_urb))
202 break;
203
204 if (std->qtd)
205 continue; /* already has a qTD */
206
207 qtd = std->qtd = &qset->qtd[qset->td_end];
208
209 /* Fill in setup bytes for control transfers. */
210 if (usb_pipecontrol(std->urb->pipe))
211 memcpy(qtd->setup, std->urb->setup_packet, 8);
212
213 status = QTD_STS_ACTIVE | QTD_STS_LEN(std->len);
214
215 if (whc_std_last(std) && usb_pipeout(std->urb->pipe))
216 status |= QTD_STS_LAST_PKT;
217
218 /*
219 * For an IN transfer the iAlt field should be set so
220 * the h/w will automatically advance to the next
221 * transfer. However, if there are 8 or more TDs
222 * remaining in this transfer then iAlt cannot be set
223 * as it could point to somewhere in this transfer.
224 */
225 if (std->ntds_remaining < WHCI_QSET_TD_MAX) {
226 int ialt;
227 ialt = (qset->td_end + std->ntds_remaining) % WHCI_QSET_TD_MAX;
228 status |= QTD_STS_IALT(ialt);
229 } else if (usb_pipein(std->urb->pipe))
230 qset->pause_after_urb = std->urb;
231
232 if (std->num_pointers)
233 qtd->options = cpu_to_le32(QTD_OPT_IOC);
234 else
235 qtd->options = cpu_to_le32(QTD_OPT_IOC | QTD_OPT_SMALL);
236 qtd->page_list_ptr = cpu_to_le64(std->dma_addr);
237
238 qtd->status = cpu_to_le32(status);
239
240 if (QH_STATUS_TO_ICUR(qset->qh.status) == qset->td_end)
241 update = WHC_UPDATE_UPDATED;
242
243 if (++qset->td_end >= WHCI_QSET_TD_MAX)
244 qset->td_end = 0;
245 qset->ntds++;
246 }
247
248 return update;
249}
250
251/**
252 * qset_remove_qtd - remove the first qTD from a qset.
253 *
254 * The qTD might be still active (if it's part of a IN URB that
255 * resulted in a short read) so ensure it's deactivated.
256 */
257static void qset_remove_qtd(struct whc *whc, struct whc_qset *qset)
258{
259 qset->qtd[qset->td_start].status = 0;
260
261 if (++qset->td_start >= WHCI_QSET_TD_MAX)
262 qset->td_start = 0;
263 qset->ntds--;
264}
265
266/**
267 * qset_free_std - remove an sTD and free it.
268 * @whc: the WHCI host controller
269 * @std: the sTD to remove and free.
270 */
271void qset_free_std(struct whc *whc, struct whc_std *std)
272{
273 list_del(&std->list_node);
274 if (std->num_pointers) {
275 dma_unmap_single(whc->wusbhc.dev, std->dma_addr,
276 std->num_pointers * sizeof(struct whc_page_list_entry),
277 DMA_TO_DEVICE);
278 kfree(std->pl_virt);
279 }
280
281 kfree(std);
282}
283
284/**
285 * qset_remove_qtds - remove an URB's qTDs (and sTDs).
286 */
287static void qset_remove_qtds(struct whc *whc, struct whc_qset *qset,
288 struct urb *urb)
289{
290 struct whc_std *std, *t;
291
292 list_for_each_entry_safe(std, t, &qset->stds, list_node) {
293 if (std->urb != urb)
294 break;
295 if (std->qtd != NULL)
296 qset_remove_qtd(whc, qset);
297 qset_free_std(whc, std);
298 }
299}
300
301/**
302 * qset_free_stds - free any remaining sTDs for an URB.
303 */
304static void qset_free_stds(struct whc_qset *qset, struct urb *urb)
305{
306 struct whc_std *std, *t;
307
308 list_for_each_entry_safe(std, t, &qset->stds, list_node) {
309 if (std->urb == urb)
310 qset_free_std(qset->whc, std);
311 }
312}
313
314static int qset_fill_page_list(struct whc *whc, struct whc_std *std, gfp_t mem_flags)
315{
316 dma_addr_t dma_addr = std->dma_addr;
317 dma_addr_t sp, ep;
318 size_t std_len = std->len;
319 size_t pl_len;
320 int p;
321
322 sp = ALIGN(dma_addr, WHCI_PAGE_SIZE);
323 ep = dma_addr + std_len;
324 std->num_pointers = DIV_ROUND_UP(ep - sp, WHCI_PAGE_SIZE);
325
326 pl_len = std->num_pointers * sizeof(struct whc_page_list_entry);
327 std->pl_virt = kmalloc(pl_len, mem_flags);
328 if (std->pl_virt == NULL)
329 return -ENOMEM;
330 std->dma_addr = dma_map_single(whc->wusbhc.dev, std->pl_virt, pl_len, DMA_TO_DEVICE);
331
332 for (p = 0; p < std->num_pointers; p++) {
333 std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr);
334 dma_addr = ALIGN(dma_addr + WHCI_PAGE_SIZE, WHCI_PAGE_SIZE);
335 }
336
337 return 0;
338}
339
340/**
341 * urb_dequeue_work - executes asl/pzl update and gives back the urb to the system.
342 */
343static void urb_dequeue_work(struct work_struct *work)
344{
345 struct whc_urb *wurb = container_of(work, struct whc_urb, dequeue_work);
346 struct whc_qset *qset = wurb->qset;
347 struct whc *whc = qset->whc;
348 unsigned long flags;
349
350 if (wurb->is_async == true)
351 asl_update(whc, WUSBCMD_ASYNC_UPDATED
352 | WUSBCMD_ASYNC_SYNCED_DB
353 | WUSBCMD_ASYNC_QSET_RM);
354 else
355 pzl_update(whc, WUSBCMD_PERIODIC_UPDATED
356 | WUSBCMD_PERIODIC_SYNCED_DB
357 | WUSBCMD_PERIODIC_QSET_RM);
358
359 spin_lock_irqsave(&whc->lock, flags);
360 qset_remove_urb(whc, qset, wurb->urb, wurb->status);
361 spin_unlock_irqrestore(&whc->lock, flags);
362}
363
364/**
365 * qset_add_urb - add an urb to the qset's queue.
366 *
367 * The URB is chopped into sTDs, one for each qTD that will required.
368 * At least one qTD (and sTD) is required even if the transfer has no
369 * data (e.g., for some control transfers).
370 */
371int qset_add_urb(struct whc *whc, struct whc_qset *qset, struct urb *urb,
372 gfp_t mem_flags)
373{
374 struct whc_urb *wurb;
375 int remaining = urb->transfer_buffer_length;
376 u64 transfer_dma = urb->transfer_dma;
377 int ntds_remaining;
378
379 ntds_remaining = DIV_ROUND_UP(remaining, QTD_MAX_XFER_SIZE);
380 if (ntds_remaining == 0)
381 ntds_remaining = 1;
382
383 wurb = kzalloc(sizeof(struct whc_urb), mem_flags);
384 if (wurb == NULL)
385 goto err_no_mem;
386 urb->hcpriv = wurb;
387 wurb->qset = qset;
388 wurb->urb = urb;
389 INIT_WORK(&wurb->dequeue_work, urb_dequeue_work);
390
391 while (ntds_remaining) {
392 struct whc_std *std;
393 size_t std_len;
394
395 std = kmalloc(sizeof(struct whc_std), mem_flags);
396 if (std == NULL)
397 goto err_no_mem;
398
399 std_len = remaining;
400 if (std_len > QTD_MAX_XFER_SIZE)
401 std_len = QTD_MAX_XFER_SIZE;
402
403 std->urb = urb;
404 std->dma_addr = transfer_dma;
405 std->len = std_len;
406 std->ntds_remaining = ntds_remaining;
407 std->qtd = NULL;
408
409 INIT_LIST_HEAD(&std->list_node);
410 list_add_tail(&std->list_node, &qset->stds);
411
412 if (std_len > WHCI_PAGE_SIZE) {
413 if (qset_fill_page_list(whc, std, mem_flags) < 0)
414 goto err_no_mem;
415 } else
416 std->num_pointers = 0;
417
418 ntds_remaining--;
419 remaining -= std_len;
420 transfer_dma += std_len;
421 }
422
423 return 0;
424
425err_no_mem:
426 qset_free_stds(qset, urb);
427 return -ENOMEM;
428}
429
430/**
431 * qset_remove_urb - remove an URB from the urb queue.
432 *
433 * The URB is returned to the USB subsystem.
434 */
435void qset_remove_urb(struct whc *whc, struct whc_qset *qset,
436 struct urb *urb, int status)
437{
438 struct wusbhc *wusbhc = &whc->wusbhc;
439 struct whc_urb *wurb = urb->hcpriv;
440
441 usb_hcd_unlink_urb_from_ep(&wusbhc->usb_hcd, urb);
442 /* Drop the lock as urb->complete() may enqueue another urb. */
443 spin_unlock(&whc->lock);
444 wusbhc_giveback_urb(wusbhc, urb, status);
445 spin_lock(&whc->lock);
446
447 kfree(wurb);
448}
449
450/**
451 * get_urb_status_from_qtd - get the completed urb status from qTD status
452 * @urb: completed urb
453 * @status: qTD status
454 */
455static int get_urb_status_from_qtd(struct urb *urb, u32 status)
456{
457 if (status & QTD_STS_HALTED) {
458 if (status & QTD_STS_DBE)
459 return usb_pipein(urb->pipe) ? -ENOSR : -ECOMM;
460 else if (status & QTD_STS_BABBLE)
461 return -EOVERFLOW;
462 else if (status & QTD_STS_RCE)
463 return -ETIME;
464 return -EPIPE;
465 }
466 if (usb_pipein(urb->pipe)
467 && (urb->transfer_flags & URB_SHORT_NOT_OK)
468 && urb->actual_length < urb->transfer_buffer_length)
469 return -EREMOTEIO;
470 return 0;
471}
472
473/**
474 * process_inactive_qtd - process an inactive (but not halted) qTD.
475 *
476 * Update the urb with the transfer bytes from the qTD, if the urb is
477 * completely transfered or (in the case of an IN only) the LPF is
478 * set, then the transfer is complete and the urb should be returned
479 * to the system.
480 */
481void process_inactive_qtd(struct whc *whc, struct whc_qset *qset,
482 struct whc_qtd *qtd)
483{
484 struct whc_std *std = list_first_entry(&qset->stds, struct whc_std, list_node);
485 struct urb *urb = std->urb;
486 uint32_t status;
487 bool complete;
488
489 status = le32_to_cpu(qtd->status);
490
491 urb->actual_length += std->len - QTD_STS_TO_LEN(status);
492
493 if (usb_pipein(urb->pipe) && (status & QTD_STS_LAST_PKT))
494 complete = true;
495 else
496 complete = whc_std_last(std);
497
498 qset_remove_qtd(whc, qset);
499 qset_free_std(whc, std);
500
501 /*
502 * Transfers for this URB are complete? Then return it to the
503 * USB subsystem.
504 */
505 if (complete) {
506 qset_remove_qtds(whc, qset, urb);
507 qset_remove_urb(whc, qset, urb, get_urb_status_from_qtd(urb, status));
508
509 /*
510 * If iAlt isn't valid then the hardware didn't
511 * advance iCur. Adjust the start and end pointers to
512 * match iCur.
513 */
514 if (!(status & QTD_STS_IALT_VALID))
515 qset->td_start = qset->td_end
516 = QH_STATUS_TO_ICUR(le16_to_cpu(qset->qh.status));
517 qset->pause_after_urb = NULL;
518 }
519}
520
521/**
522 * process_halted_qtd - process a qset with a halted qtd
523 *
524 * Remove all the qTDs for the failed URB and return the failed URB to
525 * the USB subsystem. Then remove all other qTDs so the qset can be
526 * removed.
527 *
528 * FIXME: this is the point where rate adaptation can be done. If a
529 * transfer failed because it exceeded the maximum number of retries
530 * then it could be reactivated with a slower rate without having to
531 * remove the qset.
532 */
533void process_halted_qtd(struct whc *whc, struct whc_qset *qset,
534 struct whc_qtd *qtd)
535{
536 struct whc_std *std = list_first_entry(&qset->stds, struct whc_std, list_node);
537 struct urb *urb = std->urb;
538 int urb_status;
539
540 urb_status = get_urb_status_from_qtd(urb, le32_to_cpu(qtd->status));
541
542 qset_remove_qtds(whc, qset, urb);
543 qset_remove_urb(whc, qset, urb, urb_status);
544
545 list_for_each_entry(std, &qset->stds, list_node) {
546 if (qset->ntds == 0)
547 break;
548 qset_remove_qtd(whc, qset);
549 std->qtd = NULL;
550 }
551
552 qset->remove = 1;
553}
554
555void qset_free(struct whc *whc, struct whc_qset *qset)
556{
557 dma_pool_free(whc->qset_pool, qset, qset->qset_dma);
558}
559
560/**
561 * qset_delete - wait for a qset to be unused, then free it.
562 */
563void qset_delete(struct whc *whc, struct whc_qset *qset)
564{
565 wait_for_completion(&qset->remove_complete);
566 qset_free(whc, qset);
567}
diff --git a/drivers/usb/host/whci/whcd.h b/drivers/usb/host/whci/whcd.h
new file mode 100644
index 000000000000..1d2a53bd39fd
--- /dev/null
+++ b/drivers/usb/host/whci/whcd.h
@@ -0,0 +1,197 @@
1/*
2 * Wireless Host Controller (WHC) private header.
3 *
4 * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version
8 * 2 as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301, USA.
19 */
20#ifndef __WHCD_H
21#define __WHCD_H
22
23#include <linux/uwb/whci.h>
24#include <linux/workqueue.h>
25
26#include "whci-hc.h"
27
28/* Generic command timeout. */
29#define WHC_GENCMD_TIMEOUT_MS 100
30
31
32struct whc {
33 struct wusbhc wusbhc;
34 struct umc_dev *umc;
35
36 resource_size_t base_phys;
37 void __iomem *base;
38 int irq;
39
40 u8 n_devices;
41 u8 n_keys;
42 u8 n_mmc_ies;
43
44 u64 *pz_list;
45 struct dn_buf_entry *dn_buf;
46 struct di_buf_entry *di_buf;
47 dma_addr_t pz_list_dma;
48 dma_addr_t dn_buf_dma;
49 dma_addr_t di_buf_dma;
50
51 spinlock_t lock;
52 struct mutex mutex;
53
54 void * gen_cmd_buf;
55 dma_addr_t gen_cmd_buf_dma;
56 wait_queue_head_t cmd_wq;
57
58 struct workqueue_struct *workqueue;
59 struct work_struct dn_work;
60
61 struct dma_pool *qset_pool;
62
63 struct list_head async_list;
64 struct list_head async_removed_list;
65 wait_queue_head_t async_list_wq;
66 struct work_struct async_work;
67
68 struct list_head periodic_list[5];
69 struct list_head periodic_removed_list;
70 wait_queue_head_t periodic_list_wq;
71 struct work_struct periodic_work;
72};
73
74#define wusbhc_to_whc(w) (container_of((w), struct whc, wusbhc))
75
76/**
77 * struct whc_std - a software TD.
78 * @urb: the URB this sTD is for.
79 * @offset: start of the URB's data for this TD.
80 * @len: the length of data in the associated TD.
81 * @ntds_remaining: number of TDs (starting from this one) in this transfer.
82 *
83 * Queued URBs may require more TDs than are available in a qset so we
84 * use a list of these "software TDs" (sTDs) to hold per-TD data.
85 */
86struct whc_std {
87 struct urb *urb;
88 size_t len;
89 int ntds_remaining;
90 struct whc_qtd *qtd;
91
92 struct list_head list_node;
93 int num_pointers;
94 dma_addr_t dma_addr;
95 struct whc_page_list_entry *pl_virt;
96};
97
98/**
99 * struct whc_urb - per URB host controller structure.
100 * @urb: the URB this struct is for.
101 * @qset: the qset associated to the URB.
102 * @dequeue_work: the work to remove the URB when dequeued.
103 * @is_async: the URB belongs to async sheduler or not.
104 * @status: the status to be returned when calling wusbhc_giveback_urb.
105 */
106struct whc_urb {
107 struct urb *urb;
108 struct whc_qset *qset;
109 struct work_struct dequeue_work;
110 bool is_async;
111 int status;
112};
113
114/**
115 * whc_std_last - is this sTD the URB's last?
116 * @std: the sTD to check.
117 */
118static inline bool whc_std_last(struct whc_std *std)
119{
120 return std->ntds_remaining <= 1;
121}
122
123enum whc_update {
124 WHC_UPDATE_ADDED = 0x01,
125 WHC_UPDATE_REMOVED = 0x02,
126 WHC_UPDATE_UPDATED = 0x04,
127};
128
129/* init.c */
130int whc_init(struct whc *whc);
131void whc_clean_up(struct whc *whc);
132
133/* hw.c */
134void whc_write_wusbcmd(struct whc *whc, u32 mask, u32 val);
135int whc_do_gencmd(struct whc *whc, u32 cmd, u32 params, void *addr, size_t len);
136
137/* wusb.c */
138int whc_wusbhc_start(struct wusbhc *wusbhc);
139void whc_wusbhc_stop(struct wusbhc *wusbhc);
140int whc_mmcie_add(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt,
141 u8 handle, struct wuie_hdr *wuie);
142int whc_mmcie_rm(struct wusbhc *wusbhc, u8 handle);
143int whc_bwa_set(struct wusbhc *wusbhc, s8 stream_index, const struct uwb_mas_bm *mas_bm);
144int whc_dev_info_set(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev);
145int whc_set_num_dnts(struct wusbhc *wusbhc, u8 interval, u8 slots);
146int whc_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid,
147 const void *ptk, size_t key_size);
148int whc_set_gtk(struct wusbhc *wusbhc, u32 tkid,
149 const void *gtk, size_t key_size);
150int whc_set_cluster_id(struct whc *whc, u8 bcid);
151
152/* int.c */
153irqreturn_t whc_int_handler(struct usb_hcd *hcd);
154void whc_dn_work(struct work_struct *work);
155
156/* asl.c */
157void asl_start(struct whc *whc);
158void asl_stop(struct whc *whc);
159int asl_init(struct whc *whc);
160void asl_clean_up(struct whc *whc);
161int asl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags);
162int asl_urb_dequeue(struct whc *whc, struct urb *urb, int status);
163void asl_qset_delete(struct whc *whc, struct whc_qset *qset);
164void scan_async_work(struct work_struct *work);
165
166/* pzl.c */
167int pzl_init(struct whc *whc);
168void pzl_clean_up(struct whc *whc);
169void pzl_start(struct whc *whc);
170void pzl_stop(struct whc *whc);
171int pzl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags);
172int pzl_urb_dequeue(struct whc *whc, struct urb *urb, int status);
173void pzl_qset_delete(struct whc *whc, struct whc_qset *qset);
174void scan_periodic_work(struct work_struct *work);
175
176/* qset.c */
177struct whc_qset *qset_alloc(struct whc *whc, gfp_t mem_flags);
178void qset_free(struct whc *whc, struct whc_qset *qset);
179struct whc_qset *get_qset(struct whc *whc, struct urb *urb, gfp_t mem_flags);
180void qset_delete(struct whc *whc, struct whc_qset *qset);
181void qset_clear(struct whc *whc, struct whc_qset *qset);
182int qset_add_urb(struct whc *whc, struct whc_qset *qset, struct urb *urb,
183 gfp_t mem_flags);
184void qset_free_std(struct whc *whc, struct whc_std *std);
185void qset_remove_urb(struct whc *whc, struct whc_qset *qset,
186 struct urb *urb, int status);
187void process_halted_qtd(struct whc *whc, struct whc_qset *qset,
188 struct whc_qtd *qtd);
189void process_inactive_qtd(struct whc *whc, struct whc_qset *qset,
190 struct whc_qtd *qtd);
191enum whc_update qset_add_qtds(struct whc *whc, struct whc_qset *qset);
192void qset_remove_complete(struct whc *whc, struct whc_qset *qset);
193void dump_qset(struct whc_qset *qset, struct device *dev);
194void pzl_update(struct whc *whc, uint32_t wusbcmd);
195void asl_update(struct whc *whc, uint32_t wusbcmd);
196
197#endif /* #ifndef __WHCD_H */
diff --git a/drivers/usb/host/whci/whci-hc.h b/drivers/usb/host/whci/whci-hc.h
new file mode 100644
index 000000000000..bff1eb7a35cf
--- /dev/null
+++ b/drivers/usb/host/whci/whci-hc.h
@@ -0,0 +1,416 @@
1/*
2 * Wireless Host Controller (WHC) data structures.
3 *
4 * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version
8 * 2 as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301, USA.
19 */
20#ifndef _WHCI_WHCI_HC_H
21#define _WHCI_WHCI_HC_H
22
23#include <linux/list.h>
24
25/**
26 * WHCI_PAGE_SIZE - page size use by WHCI
27 *
28 * WHCI assumes that host system uses pages of 4096 octets.
29 */
30#define WHCI_PAGE_SIZE 4096
31
32
33/**
34 * QTD_MAX_TXFER_SIZE - max number of bytes to transfer with a single
35 * qtd.
36 *
37 * This is 2^20 - 1.
38 */
39#define QTD_MAX_XFER_SIZE 1048575
40
41
42/**
43 * struct whc_qtd - Queue Element Transfer Descriptors (qTD)
44 *
45 * This describes the data for a bulk, control or interrupt transfer.
46 *
47 * [WHCI] section 3.2.4
48 */
49struct whc_qtd {
50 __le32 status; /*< remaining transfer len and transfer status */
51 __le32 options;
52 __le64 page_list_ptr; /*< physical pointer to data buffer page list*/
53 __u8 setup[8]; /*< setup data for control transfers */
54} __attribute__((packed));
55
56#define QTD_STS_ACTIVE (1 << 31) /* enable execution of transaction */
57#define QTD_STS_HALTED (1 << 30) /* transfer halted */
58#define QTD_STS_DBE (1 << 29) /* data buffer error */
59#define QTD_STS_BABBLE (1 << 28) /* babble detected */
60#define QTD_STS_RCE (1 << 27) /* retry count exceeded */
61#define QTD_STS_LAST_PKT (1 << 26) /* set Last Packet Flag in WUSB header */
62#define QTD_STS_INACTIVE (1 << 25) /* queue set is marked inactive */
63#define QTD_STS_IALT_VALID (1 << 23) /* iAlt field is valid */
64#define QTD_STS_IALT(i) (QTD_STS_IALT_VALID | ((i) << 20)) /* iAlt field */
65#define QTD_STS_LEN(l) ((l) << 0) /* transfer length */
66#define QTD_STS_TO_LEN(s) ((s) & 0x000fffff)
67
68#define QTD_OPT_IOC (1 << 1) /* page_list_ptr points to buffer directly */
69#define QTD_OPT_SMALL (1 << 0) /* interrupt on complete */
70
71/**
72 * struct whc_itd - Isochronous Queue Element Transfer Descriptors (iTD)
73 *
74 * This describes the data and other parameters for an isochronous
75 * transfer.
76 *
77 * [WHCI] section 3.2.5
78 */
79struct whc_itd {
80 __le16 presentation_time; /*< presentation time for OUT transfers */
81 __u8 num_segments; /*< number of data segments in segment list */
82 __u8 status; /*< command execution status */
83 __le32 options; /*< misc transfer options */
84 __le64 page_list_ptr; /*< physical pointer to data buffer page list */
85 __le64 seg_list_ptr; /*< physical pointer to segment list */
86} __attribute__((packed));
87
88#define ITD_STS_ACTIVE (1 << 7) /* enable execution of transaction */
89#define ITD_STS_DBE (1 << 5) /* data buffer error */
90#define ITD_STS_BABBLE (1 << 4) /* babble detected */
91#define ITD_STS_INACTIVE (1 << 1) /* queue set is marked inactive */
92
93#define ITD_OPT_IOC (1 << 1) /* interrupt on complete */
94#define ITD_OPT_SMALL (1 << 0) /* page_list_ptr points to buffer directly */
95
96/**
97 * Page list entry.
98 *
99 * A TD's page list must contain sufficient page list entries for the
100 * total data length in the TD.
101 *
102 * [WHCI] section 3.2.4.3
103 */
104struct whc_page_list_entry {
105 __le64 buf_ptr; /*< physical pointer to buffer */
106} __attribute__((packed));
107
108/**
109 * struct whc_seg_list_entry - Segment list entry.
110 *
111 * Describes a portion of the data buffer described in the containing
112 * qTD's page list.
113 *
114 * seg_ptr = qtd->page_list_ptr[qtd->seg_list_ptr[seg].idx].buf_ptr
115 * + qtd->seg_list_ptr[seg].offset;
116 *
117 * Segments can't cross page boundries.
118 *
119 * [WHCI] section 3.2.5.5
120 */
121struct whc_seg_list_entry {
122 __le16 len; /*< segment length */
123 __u8 idx; /*< index into page list */
124 __u8 status; /*< segment status */
125 __le16 offset; /*< 12 bit offset into page */
126} __attribute__((packed));
127
128/**
129 * struct whc_qhead - endpoint and status information for a qset.
130 *
131 * [WHCI] section 3.2.6
132 */
133struct whc_qhead {
134 __le64 link; /*< next qset in list */
135 __le32 info1;
136 __le32 info2;
137 __le32 info3;
138 __le16 status;
139 __le16 err_count; /*< transaction error count */
140 __le32 cur_window;
141 __le32 scratch[3]; /*< h/w scratch area */
142 union {
143 struct whc_qtd qtd;
144 struct whc_itd itd;
145 } overlay;
146} __attribute__((packed));
147
148#define QH_LINK_PTR_MASK (~0x03Full)
149#define QH_LINK_PTR(ptr) ((ptr) & QH_LINK_PTR_MASK)
150#define QH_LINK_IQS (1 << 4) /* isochronous queue set */
151#define QH_LINK_NTDS(n) (((n) - 1) << 1) /* number of TDs in queue set */
152#define QH_LINK_T (1 << 0) /* last queue set in periodic schedule list */
153
154#define QH_INFO1_EP(e) ((e) << 0) /* endpoint number */
155#define QH_INFO1_DIR_IN (1 << 4) /* IN transfer */
156#define QH_INFO1_DIR_OUT (0 << 4) /* OUT transfer */
157#define QH_INFO1_TR_TYPE_CTRL (0x0 << 5) /* control transfer */
158#define QH_INFO1_TR_TYPE_ISOC (0x1 << 5) /* isochronous transfer */
159#define QH_INFO1_TR_TYPE_BULK (0x2 << 5) /* bulk transfer */
160#define QH_INFO1_TR_TYPE_INT (0x3 << 5) /* interrupt */
161#define QH_INFO1_TR_TYPE_LP_INT (0x7 << 5) /* low power interrupt */
162#define QH_INFO1_DEV_INFO_IDX(i) ((i) << 8) /* index into device info buffer */
163#define QH_INFO1_SET_INACTIVE (1 << 15) /* set inactive after transfer */
164#define QH_INFO1_MAX_PKT_LEN(l) ((l) << 16) /* maximum packet length */
165
166#define QH_INFO2_BURST(b) ((b) << 0) /* maximum burst length */
167#define QH_INFO2_DBP(p) ((p) << 5) /* data burst policy (see [WUSB] table 5-7) */
168#define QH_INFO2_MAX_COUNT(c) ((c) << 8) /* max isoc/int pkts per zone */
169#define QH_INFO2_RQS (1 << 15) /* reactivate queue set */
170#define QH_INFO2_MAX_RETRY(r) ((r) << 16) /* maximum transaction retries */
171#define QH_INFO2_MAX_SEQ(s) ((s) << 20) /* maximum sequence number */
172#define QH_INFO3_MAX_DELAY(d) ((d) << 0) /* maximum stream delay in 125 us units (isoc only) */
173#define QH_INFO3_INTERVAL(i) ((i) << 16) /* segment interval in 125 us units (isoc only) */
174
175#define QH_INFO3_TX_RATE_53_3 (0 << 24)
176#define QH_INFO3_TX_RATE_80 (1 << 24)
177#define QH_INFO3_TX_RATE_106_7 (2 << 24)
178#define QH_INFO3_TX_RATE_160 (3 << 24)
179#define QH_INFO3_TX_RATE_200 (4 << 24)
180#define QH_INFO3_TX_RATE_320 (5 << 24)
181#define QH_INFO3_TX_RATE_400 (6 << 24)
182#define QH_INFO3_TX_RATE_480 (7 << 24)
183#define QH_INFO3_TX_PWR(p) ((p) << 29) /* transmit power (see [WUSB] section 5.2.1.2) */
184
185#define QH_STATUS_FLOW_CTRL (1 << 15)
186#define QH_STATUS_ICUR(i) ((i) << 5)
187#define QH_STATUS_TO_ICUR(s) (((s) >> 5) & 0x7)
188
189/**
190 * usb_pipe_to_qh_type - USB core pipe type to QH transfer type
191 *
192 * Returns the QH type field for a USB core pipe type.
193 */
194static inline unsigned usb_pipe_to_qh_type(unsigned pipe)
195{
196 static const unsigned type[] = {
197 [PIPE_ISOCHRONOUS] = QH_INFO1_TR_TYPE_ISOC,
198 [PIPE_INTERRUPT] = QH_INFO1_TR_TYPE_INT,
199 [PIPE_CONTROL] = QH_INFO1_TR_TYPE_CTRL,
200 [PIPE_BULK] = QH_INFO1_TR_TYPE_BULK,
201 };
202 return type[usb_pipetype(pipe)];
203}
204
205/**
206 * Maxiumum number of TDs in a qset.
207 */
208#define WHCI_QSET_TD_MAX 8
209
210/**
211 * struct whc_qset - WUSB data transfers to a specific endpoint
212 * @qh: the QHead of this qset
213 * @qtd: up to 8 qTDs (for qsets for control, bulk and interrupt
214 * transfers)
215 * @itd: up to 8 iTDs (for qsets for isochronous transfers)
216 * @qset_dma: DMA address for this qset
217 * @whc: WHCI HC this qset is for
218 * @ep: endpoint
219 * @stds: list of sTDs queued to this qset
220 * @ntds: number of qTDs queued (not necessarily the same as nTDs
221 * field in the QH)
222 * @td_start: index of the first qTD in the list
223 * @td_end: index of next free qTD in the list (provided
224 * ntds < WHCI_QSET_TD_MAX)
225 *
226 * Queue Sets (qsets) are added to the asynchronous schedule list
227 * (ASL) or the periodic zone list (PZL).
228 *
229 * qsets may contain up to 8 TDs (either qTDs or iTDs as appropriate).
230 * Each TD may refer to at most 1 MiB of data. If a single transfer
231 * has > 8MiB of data, TDs can be reused as they are completed since
232 * the TD list is used as a circular buffer. Similarly, several
233 * (smaller) transfers may be queued in a qset.
234 *
235 * WHCI controllers may cache portions of the qsets in the ASL and
236 * PZL, requiring the WHCD to inform the WHC that the lists have been
237 * updated (fields changed or qsets inserted or removed). For safe
238 * insertion and removal of qsets from the lists the schedule must be
239 * stopped to avoid races in updating the QH link pointers.
240 *
241 * Since the HC is free to execute qsets in any order, all transfers
242 * to an endpoint should use the same qset to ensure transfers are
243 * executed in the order they're submitted.
244 *
245 * [WHCI] section 3.2.3
246 */
247struct whc_qset {
248 struct whc_qhead qh;
249 union {
250 struct whc_qtd qtd[WHCI_QSET_TD_MAX];
251 struct whc_itd itd[WHCI_QSET_TD_MAX];
252 };
253
254 /* private data for WHCD */
255 dma_addr_t qset_dma;
256 struct whc *whc;
257 struct usb_host_endpoint *ep;
258 struct list_head stds;
259 int ntds;
260 int td_start;
261 int td_end;
262 struct list_head list_node;
263 unsigned in_sw_list:1;
264 unsigned in_hw_list:1;
265 unsigned remove:1;
266 struct urb *pause_after_urb;
267 struct completion remove_complete;
268 int max_burst;
269 int max_seq;
270};
271
272static inline void whc_qset_set_link_ptr(u64 *ptr, u64 target)
273{
274 if (target)
275 *ptr = (*ptr & ~(QH_LINK_PTR_MASK | QH_LINK_T)) | QH_LINK_PTR(target);
276 else
277 *ptr = QH_LINK_T;
278}
279
280/**
281 * struct di_buf_entry - Device Information (DI) buffer entry.
282 *
283 * There's one of these per connected device.
284 */
285struct di_buf_entry {
286 __le32 availability_info[8]; /*< MAS availability information, one MAS per bit */
287 __le32 addr_sec_info; /*< addressing and security info */
288 __le32 reserved[7];
289} __attribute__((packed));
290
291#define WHC_DI_SECURE (1 << 31)
292#define WHC_DI_DISABLE (1 << 30)
293#define WHC_DI_KEY_IDX(k) ((k) << 8)
294#define WHC_DI_KEY_IDX_MASK 0x0000ff00
295#define WHC_DI_DEV_ADDR(a) ((a) << 0)
296#define WHC_DI_DEV_ADDR_MASK 0x000000ff
297
298/**
299 * struct dn_buf_entry - Device Notification (DN) buffer entry.
300 *
301 * [WHCI] section 3.2.8
302 */
303struct dn_buf_entry {
304 __u8 msg_size; /*< number of octets of valid DN data */
305 __u8 reserved1;
306 __u8 src_addr; /*< source address */
307 __u8 status; /*< buffer entry status */
308 __le32 tkid; /*< TKID for source device, valid if secure bit is set */
309 __u8 dn_data[56]; /*< up to 56 octets of DN data */
310} __attribute__((packed));
311
312#define WHC_DN_STATUS_VALID (1 << 7) /* buffer entry is valid */
313#define WHC_DN_STATUS_SECURE (1 << 6) /* notification received using secure frame */
314
315#define WHC_N_DN_ENTRIES (4096 / sizeof(struct dn_buf_entry))
316
317/* The Add MMC IE WUSB Generic Command may take up to 256 bytes of
318 data. [WHCI] section 2.4.7. */
319#define WHC_GEN_CMD_DATA_LEN 256
320
321/*
322 * HC registers.
323 *
324 * [WHCI] section 2.4
325 */
326
327#define WHCIVERSION 0x00
328
329#define WHCSPARAMS 0x04
330# define WHCSPARAMS_TO_N_MMC_IES(p) (((p) >> 16) & 0xff)
331# define WHCSPARAMS_TO_N_KEYS(p) (((p) >> 8) & 0xff)
332# define WHCSPARAMS_TO_N_DEVICES(p) (((p) >> 0) & 0x7f)
333
334#define WUSBCMD 0x08
335# define WUSBCMD_BCID(b) ((b) << 16)
336# define WUSBCMD_BCID_MASK (0xff << 16)
337# define WUSBCMD_ASYNC_QSET_RM (1 << 12)
338# define WUSBCMD_PERIODIC_QSET_RM (1 << 11)
339# define WUSBCMD_WUSBSI(s) ((s) << 8)
340# define WUSBCMD_WUSBSI_MASK (0x7 << 8)
341# define WUSBCMD_ASYNC_SYNCED_DB (1 << 7)
342# define WUSBCMD_PERIODIC_SYNCED_DB (1 << 6)
343# define WUSBCMD_ASYNC_UPDATED (1 << 5)
344# define WUSBCMD_PERIODIC_UPDATED (1 << 4)
345# define WUSBCMD_ASYNC_EN (1 << 3)
346# define WUSBCMD_PERIODIC_EN (1 << 2)
347# define WUSBCMD_WHCRESET (1 << 1)
348# define WUSBCMD_RUN (1 << 0)
349
350#define WUSBSTS 0x0c
351# define WUSBSTS_ASYNC_SCHED (1 << 15)
352# define WUSBSTS_PERIODIC_SCHED (1 << 14)
353# define WUSBSTS_DNTS_SCHED (1 << 13)
354# define WUSBSTS_HCHALTED (1 << 12)
355# define WUSBSTS_GEN_CMD_DONE (1 << 9)
356# define WUSBSTS_CHAN_TIME_ROLLOVER (1 << 8)
357# define WUSBSTS_DNTS_OVERFLOW (1 << 7)
358# define WUSBSTS_BPST_ADJUSTMENT_CHANGED (1 << 6)
359# define WUSBSTS_HOST_ERR (1 << 5)
360# define WUSBSTS_ASYNC_SCHED_SYNCED (1 << 4)
361# define WUSBSTS_PERIODIC_SCHED_SYNCED (1 << 3)
362# define WUSBSTS_DNTS_INT (1 << 2)
363# define WUSBSTS_ERR_INT (1 << 1)
364# define WUSBSTS_INT (1 << 0)
365# define WUSBSTS_INT_MASK 0x3ff
366
367#define WUSBINTR 0x10
368# define WUSBINTR_GEN_CMD_DONE (1 << 9)
369# define WUSBINTR_CHAN_TIME_ROLLOVER (1 << 8)
370# define WUSBINTR_DNTS_OVERFLOW (1 << 7)
371# define WUSBINTR_BPST_ADJUSTMENT_CHANGED (1 << 6)
372# define WUSBINTR_HOST_ERR (1 << 5)
373# define WUSBINTR_ASYNC_SCHED_SYNCED (1 << 4)
374# define WUSBINTR_PERIODIC_SCHED_SYNCED (1 << 3)
375# define WUSBINTR_DNTS_INT (1 << 2)
376# define WUSBINTR_ERR_INT (1 << 1)
377# define WUSBINTR_INT (1 << 0)
378# define WUSBINTR_ALL 0x3ff
379
380#define WUSBGENCMDSTS 0x14
381# define WUSBGENCMDSTS_ACTIVE (1 << 31)
382# define WUSBGENCMDSTS_ERROR (1 << 24)
383# define WUSBGENCMDSTS_IOC (1 << 23)
384# define WUSBGENCMDSTS_MMCIE_ADD 0x01
385# define WUSBGENCMDSTS_MMCIE_RM 0x02
386# define WUSBGENCMDSTS_SET_MAS 0x03
387# define WUSBGENCMDSTS_CHAN_STOP 0x04
388# define WUSBGENCMDSTS_RWP_EN 0x05
389
390#define WUSBGENCMDPARAMS 0x18
391#define WUSBGENADDR 0x20
392#define WUSBASYNCLISTADDR 0x28
393#define WUSBDNTSBUFADDR 0x30
394#define WUSBDEVICEINFOADDR 0x38
395
396#define WUSBSETSECKEYCMD 0x40
397# define WUSBSETSECKEYCMD_SET (1 << 31)
398# define WUSBSETSECKEYCMD_ERASE (1 << 30)
399# define WUSBSETSECKEYCMD_GTK (1 << 8)
400# define WUSBSETSECKEYCMD_IDX(i) ((i) << 0)
401
402#define WUSBTKID 0x44
403#define WUSBSECKEY 0x48
404#define WUSBPERIODICLISTBASE 0x58
405#define WUSBMASINDEX 0x60
406
407#define WUSBDNTSCTRL 0x64
408# define WUSBDNTSCTRL_ACTIVE (1 << 31)
409# define WUSBDNTSCTRL_INTERVAL(i) ((i) << 8)
410# define WUSBDNTSCTRL_SLOTS(s) ((s) << 0)
411
412#define WUSBTIME 0x68
413#define WUSBBPST 0x6c
414#define WUSBDIBUPDATED 0x70
415
416#endif /* #ifndef _WHCI_WHCI_HC_H */
diff --git a/drivers/usb/host/whci/wusb.c b/drivers/usb/host/whci/wusb.c
new file mode 100644
index 000000000000..66e4ddcd961d
--- /dev/null
+++ b/drivers/usb/host/whci/wusb.c
@@ -0,0 +1,241 @@
1/*
2 * Wireless Host Controller (WHC) WUSB operations.
3 *
4 * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version
8 * 2 as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18#include <linux/version.h>
19#include <linux/kernel.h>
20#include <linux/init.h>
21#include <linux/uwb/umc.h>
22#define D_LOCAL 1
23#include <linux/uwb/debug.h>
24
25#include "../../wusbcore/wusbhc.h"
26
27#include "whcd.h"
28
29#if D_LOCAL >= 1
30static void dump_di(struct whc *whc, int idx)
31{
32 struct di_buf_entry *di = &whc->di_buf[idx];
33 struct device *dev = &whc->umc->dev;
34 char buf[128];
35
36 bitmap_scnprintf(buf, sizeof(buf), (unsigned long *)di->availability_info, UWB_NUM_MAS);
37
38 d_printf(1, dev, "DI[%d]\n", idx);
39 d_printf(1, dev, " availability: %s\n", buf);
40 d_printf(1, dev, " %c%c key idx: %d dev addr: %d\n",
41 (di->addr_sec_info & WHC_DI_SECURE) ? 'S' : ' ',
42 (di->addr_sec_info & WHC_DI_DISABLE) ? 'D' : ' ',
43 (di->addr_sec_info & WHC_DI_KEY_IDX_MASK) >> 8,
44 (di->addr_sec_info & WHC_DI_DEV_ADDR_MASK));
45}
46#else
47static inline void dump_di(struct whc *whc, int idx)
48{
49}
50#endif
51
52static int whc_update_di(struct whc *whc, int idx)
53{
54 int offset = idx / 32;
55 u32 bit = 1 << (idx % 32);
56
57 dump_di(whc, idx);
58
59 le_writel(bit, whc->base + WUSBDIBUPDATED + offset);
60
61 return whci_wait_for(&whc->umc->dev,
62 whc->base + WUSBDIBUPDATED + offset, bit, 0,
63 100, "DI update");
64}
65
66/*
67 * WHCI starts and stops MMCs based on there being a valid GTK so
68 * these need only start/stop the asynchronous and periodic schedules.
69 */
70
71int whc_wusbhc_start(struct wusbhc *wusbhc)
72{
73 struct whc *whc = wusbhc_to_whc(wusbhc);
74
75 asl_start(whc);
76 pzl_start(whc);
77
78 return 0;
79}
80
81void whc_wusbhc_stop(struct wusbhc *wusbhc)
82{
83 struct whc *whc = wusbhc_to_whc(wusbhc);
84
85 pzl_stop(whc);
86 asl_stop(whc);
87}
88
89int whc_mmcie_add(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt,
90 u8 handle, struct wuie_hdr *wuie)
91{
92 struct whc *whc = wusbhc_to_whc(wusbhc);
93 u32 params;
94
95 params = (interval << 24)
96 | (repeat_cnt << 16)
97 | (wuie->bLength << 8)
98 | handle;
99
100 return whc_do_gencmd(whc, WUSBGENCMDSTS_MMCIE_ADD, params, wuie, wuie->bLength);
101}
102
103int whc_mmcie_rm(struct wusbhc *wusbhc, u8 handle)
104{
105 struct whc *whc = wusbhc_to_whc(wusbhc);
106 u32 params;
107
108 params = handle;
109
110 return whc_do_gencmd(whc, WUSBGENCMDSTS_MMCIE_RM, params, NULL, 0);
111}
112
113int whc_bwa_set(struct wusbhc *wusbhc, s8 stream_index, const struct uwb_mas_bm *mas_bm)
114{
115 struct whc *whc = wusbhc_to_whc(wusbhc);
116
117 if (stream_index >= 0)
118 whc_write_wusbcmd(whc, WUSBCMD_WUSBSI_MASK, WUSBCMD_WUSBSI(stream_index));
119
120 return whc_do_gencmd(whc, WUSBGENCMDSTS_SET_MAS, 0, (void *)mas_bm, sizeof(*mas_bm));
121}
122
123int whc_dev_info_set(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
124{
125 struct whc *whc = wusbhc_to_whc(wusbhc);
126 int idx = wusb_dev->port_idx;
127 struct di_buf_entry *di = &whc->di_buf[idx];
128 int ret;
129
130 mutex_lock(&whc->mutex);
131
132 uwb_mas_bm_copy_le(di->availability_info, &wusb_dev->availability);
133 di->addr_sec_info &= ~(WHC_DI_DISABLE | WHC_DI_DEV_ADDR_MASK);
134 di->addr_sec_info |= WHC_DI_DEV_ADDR(wusb_dev->addr);
135
136 ret = whc_update_di(whc, idx);
137
138 mutex_unlock(&whc->mutex);
139
140 return ret;
141}
142
143/*
144 * Set the number of Device Notification Time Slots (DNTS) and enable
145 * device notifications.
146 */
147int whc_set_num_dnts(struct wusbhc *wusbhc, u8 interval, u8 slots)
148{
149 struct whc *whc = wusbhc_to_whc(wusbhc);
150 u32 dntsctrl;
151
152 dntsctrl = WUSBDNTSCTRL_ACTIVE
153 | WUSBDNTSCTRL_INTERVAL(interval)
154 | WUSBDNTSCTRL_SLOTS(slots);
155
156 le_writel(dntsctrl, whc->base + WUSBDNTSCTRL);
157
158 return 0;
159}
160
161static int whc_set_key(struct whc *whc, u8 key_index, uint32_t tkid,
162 const void *key, size_t key_size, bool is_gtk)
163{
164 uint32_t setkeycmd;
165 uint32_t seckey[4];
166 int i;
167 int ret;
168
169 memcpy(seckey, key, key_size);
170 setkeycmd = WUSBSETSECKEYCMD_SET | WUSBSETSECKEYCMD_IDX(key_index);
171 if (is_gtk)
172 setkeycmd |= WUSBSETSECKEYCMD_GTK;
173
174 le_writel(tkid, whc->base + WUSBTKID);
175 for (i = 0; i < 4; i++)
176 le_writel(seckey[i], whc->base + WUSBSECKEY + 4*i);
177 le_writel(setkeycmd, whc->base + WUSBSETSECKEYCMD);
178
179 ret = whci_wait_for(&whc->umc->dev, whc->base + WUSBSETSECKEYCMD,
180 WUSBSETSECKEYCMD_SET, 0, 100, "set key");
181
182 return ret;
183}
184
185/**
186 * whc_set_ptk - set the PTK to use for a device.
187 *
188 * The index into the key table for this PTK is the same as the
189 * device's port index.
190 */
191int whc_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid,
192 const void *ptk, size_t key_size)
193{
194 struct whc *whc = wusbhc_to_whc(wusbhc);
195 struct di_buf_entry *di = &whc->di_buf[port_idx];
196 int ret;
197
198 mutex_lock(&whc->mutex);
199
200 if (ptk) {
201 ret = whc_set_key(whc, port_idx, tkid, ptk, key_size, false);
202 if (ret)
203 goto out;
204
205 di->addr_sec_info &= ~WHC_DI_KEY_IDX_MASK;
206 di->addr_sec_info |= WHC_DI_SECURE | WHC_DI_KEY_IDX(port_idx);
207 } else
208 di->addr_sec_info &= ~WHC_DI_SECURE;
209
210 ret = whc_update_di(whc, port_idx);
211out:
212 mutex_unlock(&whc->mutex);
213 return ret;
214}
215
216/**
217 * whc_set_gtk - set the GTK for subsequent broadcast packets
218 *
219 * The GTK is stored in the last entry in the key table (the previous
220 * N_DEVICES entries are for the per-device PTKs).
221 */
222int whc_set_gtk(struct wusbhc *wusbhc, u32 tkid,
223 const void *gtk, size_t key_size)
224{
225 struct whc *whc = wusbhc_to_whc(wusbhc);
226 int ret;
227
228 mutex_lock(&whc->mutex);
229
230 ret = whc_set_key(whc, whc->n_devices, tkid, gtk, key_size, true);
231
232 mutex_unlock(&whc->mutex);
233
234 return ret;
235}
236
237int whc_set_cluster_id(struct whc *whc, u8 bcid)
238{
239 whc_write_wusbcmd(whc, WUSBCMD_BCID_MASK, WUSBCMD_BCID(bcid));
240 return 0;
241}