Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
init.c
Go to the documentation of this file.
1 /*
2  * Wireless Host Controller (WHC) initialization.
3  *
4  * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License version
8  * 2 as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 #include <linux/kernel.h>
19 #include <linux/gfp.h>
20 #include <linux/dma-mapping.h>
21 #include <linux/uwb/umc.h>
22 
23 #include "../../wusbcore/wusbhc.h"
24 
25 #include "whcd.h"
26 
27 /*
28  * Reset the host controller.
29  */
30 static void whc_hw_reset(struct whc *whc)
31 {
32  le_writel(WUSBCMD_WHCRESET, whc->base + WUSBCMD);
33  whci_wait_for(&whc->umc->dev, whc->base + WUSBCMD, WUSBCMD_WHCRESET, 0,
34  100, "reset");
35 }
36 
37 static void whc_hw_init_di_buf(struct whc *whc)
38 {
39  int d;
40 
41  /* Disable all entries in the Device Information buffer. */
42  for (d = 0; d < whc->n_devices; d++)
43  whc->di_buf[d].addr_sec_info = WHC_DI_DISABLE;
44 
45  le_writeq(whc->di_buf_dma, whc->base + WUSBDEVICEINFOADDR);
46 }
47 
48 static void whc_hw_init_dn_buf(struct whc *whc)
49 {
50  /* Clear the Device Notification buffer to ensure the V (valid)
51  * bits are clear. */
52  memset(whc->dn_buf, 0, 4096);
53 
54  le_writeq(whc->dn_buf_dma, whc->base + WUSBDNTSBUFADDR);
55 }
56 
57 int whc_init(struct whc *whc)
58 {
59  u32 whcsparams;
60  int ret, i;
62 
63  spin_lock_init(&whc->lock);
64  mutex_init(&whc->mutex);
68  whc->workqueue = create_singlethread_workqueue(dev_name(&whc->umc->dev));
69  if (whc->workqueue == NULL) {
70  ret = -ENOMEM;
71  goto error;
72  }
74 
76  INIT_LIST_HEAD(&whc->async_list);
77  INIT_LIST_HEAD(&whc->async_removed_list);
78 
80  for (i = 0; i < 5; i++)
81  INIT_LIST_HEAD(&whc->periodic_list[i]);
82  INIT_LIST_HEAD(&whc->periodic_removed_list);
83 
84  /* Map HC registers. */
85  start = whc->umc->resource.start;
86  len = whc->umc->resource.end - start + 1;
87  if (!request_mem_region(start, len, "whci-hc")) {
88  dev_err(&whc->umc->dev, "can't request HC region\n");
89  ret = -EBUSY;
90  goto error;
91  }
92  whc->base_phys = start;
93  whc->base = ioremap(start, len);
94  if (!whc->base) {
95  dev_err(&whc->umc->dev, "ioremap\n");
96  ret = -ENOMEM;
97  goto error;
98  }
99 
100  whc_hw_reset(whc);
101 
102  /* Read maximum number of devices, keys and MMC IEs. */
103  whcsparams = le_readl(whc->base + WHCSPARAMS);
104  whc->n_devices = WHCSPARAMS_TO_N_DEVICES(whcsparams);
105  whc->n_keys = WHCSPARAMS_TO_N_KEYS(whcsparams);
106  whc->n_mmc_ies = WHCSPARAMS_TO_N_MMC_IES(whcsparams);
107 
108  dev_dbg(&whc->umc->dev, "N_DEVICES = %d, N_KEYS = %d, N_MMC_IES = %d\n",
109  whc->n_devices, whc->n_keys, whc->n_mmc_ies);
110 
111  whc->qset_pool = dma_pool_create("qset", &whc->umc->dev,
112  sizeof(struct whc_qset), 64, 0);
113  if (whc->qset_pool == NULL) {
114  ret = -ENOMEM;
115  goto error;
116  }
117 
118  ret = asl_init(whc);
119  if (ret < 0)
120  goto error;
121  ret = pzl_init(whc);
122  if (ret < 0)
123  goto error;
124 
125  /* Allocate and initialize a buffer for generic commands, the
126  Device Information buffer, and the Device Notification
127  buffer. */
128 
130  &whc->gen_cmd_buf_dma, GFP_KERNEL);
131  if (whc->gen_cmd_buf == NULL) {
132  ret = -ENOMEM;
133  goto error;
134  }
135 
136  whc->dn_buf = dma_alloc_coherent(&whc->umc->dev,
137  sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES,
138  &whc->dn_buf_dma, GFP_KERNEL);
139  if (!whc->dn_buf) {
140  ret = -ENOMEM;
141  goto error;
142  }
143  whc_hw_init_dn_buf(whc);
144 
145  whc->di_buf = dma_alloc_coherent(&whc->umc->dev,
146  sizeof(struct di_buf_entry) * whc->n_devices,
147  &whc->di_buf_dma, GFP_KERNEL);
148  if (!whc->di_buf) {
149  ret = -ENOMEM;
150  goto error;
151  }
152  whc_hw_init_di_buf(whc);
153 
154  return 0;
155 
156 error:
157  whc_clean_up(whc);
158  return ret;
159 }
160 
161 void whc_clean_up(struct whc *whc)
162 {
164 
165  if (whc->di_buf)
166  dma_free_coherent(&whc->umc->dev, sizeof(struct di_buf_entry) * whc->n_devices,
167  whc->di_buf, whc->di_buf_dma);
168  if (whc->dn_buf)
169  dma_free_coherent(&whc->umc->dev, sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES,
170  whc->dn_buf, whc->dn_buf_dma);
171  if (whc->gen_cmd_buf)
173  whc->gen_cmd_buf, whc->gen_cmd_buf_dma);
174 
175  pzl_clean_up(whc);
176  asl_clean_up(whc);
177 
178  if (whc->qset_pool)
180 
181  len = resource_size(&whc->umc->resource);
182  if (whc->base)
183  iounmap(whc->base);
184  if (whc->base_phys)
185  release_mem_region(whc->base_phys, len);
186 
187  if (whc->workqueue)
189 }