1/*
2 * ADM5120 HCD (Host Controller Driver) for USB
3 *
4 * Copyright (C) 2007-2008 Gabor Juhos <juhosg@openwrt.org>
5 *
6 * This file was derived from: drivers/usb/host/ohci-mem.c
7 *   (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
8 *   (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
9 *
10 *  This program is free software; you can redistribute it and/or modify it
11 *  under the terms of the GNU General Public License version 2 as published
12 *  by the Free Software Foundation.
13 *
14 */
15
16/*-------------------------------------------------------------------------*/
17
18/*
19 * OHCI deals with three types of memory:
20 *	- data used only by the HCD ... kmalloc is fine
21 *	- async and periodic schedules, shared by HC and HCD ... these
22 *	  need to use dma_pool or dma_alloc_coherent
23 *	- driver buffers, read/written by HC ... the hcd glue or the
24 *	  device driver provides us with dma addresses
25 *
26 * There's also "register" data, which is memory mapped.
27 * No memory seen by this driver (or any HCD) may be paged out.
28 */
29
30/*-------------------------------------------------------------------------*/
31
32static void admhc_hcd_init(struct admhcd *ahcd)
33{
34	ahcd->next_statechange = jiffies;
35	spin_lock_init(&ahcd->lock);
36	INIT_LIST_HEAD(&ahcd->pending);
37}
38
39/*-------------------------------------------------------------------------*/
40
41static int admhc_mem_init(struct admhcd *ahcd)
42{
43	ahcd->td_cache = dma_pool_create("admhc_td",
44		admhcd_to_hcd(ahcd)->self.controller,
45		sizeof(struct td),
46		TD_ALIGN, /* byte alignment */
47		0 /* no page-crossing issues */
48		);
49	if (!ahcd->td_cache)
50		goto err;
51
52	ahcd->ed_cache = dma_pool_create("admhc_ed",
53		admhcd_to_hcd(ahcd)->self.controller,
54		sizeof(struct ed),
55		ED_ALIGN, /* byte alignment */
56		0 /* no page-crossing issues */
57		);
58	if (!ahcd->ed_cache)
59		goto err_td_cache;
60
61	return 0;
62
63err_td_cache:
64	dma_pool_destroy(ahcd->td_cache);
65	ahcd->td_cache = NULL;
66err:
67	return -ENOMEM;
68}
69
70static void admhc_mem_cleanup(struct admhcd *ahcd)
71{
72	if (ahcd->td_cache) {
73		dma_pool_destroy(ahcd->td_cache);
74		ahcd->td_cache = NULL;
75	}
76
77	if (ahcd->ed_cache) {
78		dma_pool_destroy(ahcd->ed_cache);
79		ahcd->ed_cache = NULL;
80	}
81}
82
83/*-------------------------------------------------------------------------*/
84
85/* ahcd "done list" processing needs this mapping */
86static inline struct td *dma_to_td(struct admhcd *ahcd, dma_addr_t td_dma)
87{
88	struct td *td;
89
90	td_dma &= TD_MASK;
91	td = ahcd->td_hash[TD_HASH_FUNC(td_dma)];
92	while (td && td->td_dma != td_dma)
93		td = td->td_hash;
94
95	return td;
96}
97
98/* TDs ... */
99static struct td *td_alloc(struct admhcd *ahcd, gfp_t mem_flags)
100{
101	dma_addr_t	dma;
102	struct td	*td;
103
104	td = dma_pool_alloc(ahcd->td_cache, mem_flags, &dma);
105	if (!td)
106		return NULL;
107
108	/* in case ahcd fetches it, make it look dead */
109	memset(td, 0, sizeof *td);
110	td->hwNextTD = cpu_to_hc32(ahcd, dma);
111	td->td_dma = dma;
112	/* hashed in td_fill */
113
114	return td;
115}
116
117static void td_free(struct admhcd *ahcd, struct td *td)
118{
119	struct td **prev = &ahcd->td_hash[TD_HASH_FUNC(td->td_dma)];
120
121	while (*prev && *prev != td)
122		prev = &(*prev)->td_hash;
123	if (*prev)
124		*prev = td->td_hash;
125#if 0
126	/* TODO: remove */
127	else if ((td->hwINFO & cpu_to_hc32(ahcd, TD_DONE)) != 0)
128		admhc_dbg(ahcd, "no hash for td %p\n", td);
129#else
130	else if ((td->flags & TD_FLAG_DONE) != 0)
131		admhc_dbg(ahcd, "no hash for td %p\n", td);
132#endif
133	dma_pool_free(ahcd->td_cache, td, td->td_dma);
134}
135
136/*-------------------------------------------------------------------------*/
137
138/* EDs ... */
139static struct ed *ed_alloc(struct admhcd *ahcd, gfp_t mem_flags)
140{
141	dma_addr_t	dma;
142	struct ed	*ed;
143
144	ed = dma_pool_alloc(ahcd->ed_cache, mem_flags, &dma);
145	if (!ed)
146		return NULL;
147
148	memset(ed, 0, sizeof(*ed));
149	ed->dma = dma;
150
151	INIT_LIST_HEAD(&ed->td_list);
152	INIT_LIST_HEAD(&ed->urb_list);
153
154	return ed;
155}
156
157static void ed_free(struct admhcd *ahcd, struct ed *ed)
158{
159	dma_pool_free(ahcd->ed_cache, ed, ed->dma);
160}
161
162/*-------------------------------------------------------------------------*/
163
164/* URB priv ... */
165static void urb_priv_free(struct admhcd *ahcd, struct urb_priv *urb_priv)
166{
167	int i;
168
169	for (i = 0; i < urb_priv->td_cnt; i++)
170		if (urb_priv->td[i])
171			td_free(ahcd, urb_priv->td[i]);
172
173	list_del(&urb_priv->pending);
174	kfree(urb_priv);
175}
176
177static struct urb_priv *urb_priv_alloc(struct admhcd *ahcd, int num_tds,
178		gfp_t mem_flags)
179{
180	struct urb_priv	*priv;
181
182	/* allocate the private part of the URB */
183	priv = kzalloc(sizeof(*priv) + sizeof(struct td) * num_tds, mem_flags);
184	if (!priv)
185		goto err;
186
187	/* allocate the TDs (deferring hash chain updates) */
188	for (priv->td_cnt = 0; priv->td_cnt < num_tds; priv->td_cnt++) {
189		priv->td[priv->td_cnt] = td_alloc(ahcd, mem_flags);
190		if (priv->td[priv->td_cnt] == NULL)
191			goto err_free;
192	}
193
194	INIT_LIST_HEAD(&priv->pending);
195
196	return priv;
197
198err_free:
199	urb_priv_free(ahcd, priv);
200err:
201	return NULL;
202}
203