adm5120: add 3.18 support
[openwrt.git] / target / linux / adm5120 / files-3.18 / 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 }