adm5120: split bootext partition on the ZyXEL P-33x boards
[openwrt.git] / target / linux / adm5120 / files / drivers / usb / host / adm5120-mem.c
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
32 static 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
41 static 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
63 err_td_cache:
64 dma_pool_destroy(ahcd->td_cache);
65 ahcd->td_cache = NULL;
66 err:
67 return -ENOMEM;
68 }
69
70 static 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 */
86 static 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 ... */
99 static 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
117 static 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 ... */
139 static 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
157 static 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 ... */
165 static 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
177 static 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
198 err_free:
199 urb_priv_free(ahcd, priv);
200 err:
201 return NULL;
202 }
This page took 0.061638 seconds and 5 git commands to generate.