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