Root/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
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

Archive Download this file



interactive