blob: 34a783cb0133914b6806c9bcd322852f362a0888 [file] [log] [blame]
David Vrabel7e6133a2008-09-17 16:34:28 +01001/*
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/dma-mapping.h>
20#include <linux/uwb/umc.h>
21
22#include "../../wusbcore/wusbhc.h"
23
24#include "whcd.h"
25
26/*
27 * Reset the host controller.
28 */
29static void whc_hw_reset(struct whc *whc)
30{
31 le_writel(WUSBCMD_WHCRESET, whc->base + WUSBCMD);
32 whci_wait_for(&whc->umc->dev, whc->base + WUSBCMD, WUSBCMD_WHCRESET, 0,
33 100, "reset");
34}
35
36static void whc_hw_init_di_buf(struct whc *whc)
37{
38 int d;
39
40 /* Disable all entries in the Device Information buffer. */
41 for (d = 0; d < whc->n_devices; d++)
42 whc->di_buf[d].addr_sec_info = WHC_DI_DISABLE;
43
44 le_writeq(whc->di_buf_dma, whc->base + WUSBDEVICEINFOADDR);
45}
46
47static void whc_hw_init_dn_buf(struct whc *whc)
48{
49 /* Clear the Device Notification buffer to ensure the V (valid)
50 * bits are clear. */
51 memset(whc->dn_buf, 0, 4096);
52
53 le_writeq(whc->dn_buf_dma, whc->base + WUSBDNTSBUFADDR);
54}
55
56int whc_init(struct whc *whc)
57{
58 u32 whcsparams;
59 int ret, i;
60 resource_size_t start, len;
61
62 spin_lock_init(&whc->lock);
63 mutex_init(&whc->mutex);
64 init_waitqueue_head(&whc->cmd_wq);
65 init_waitqueue_head(&whc->async_list_wq);
66 init_waitqueue_head(&whc->periodic_list_wq);
67 whc->workqueue = create_singlethread_workqueue(dev_name(&whc->umc->dev));
68 if (whc->workqueue == NULL) {
69 ret = -ENOMEM;
70 goto error;
71 }
72 INIT_WORK(&whc->dn_work, whc_dn_work);
73
74 INIT_WORK(&whc->async_work, scan_async_work);
75 INIT_LIST_HEAD(&whc->async_list);
76 INIT_LIST_HEAD(&whc->async_removed_list);
77
78 INIT_WORK(&whc->periodic_work, scan_periodic_work);
79 for (i = 0; i < 5; i++)
80 INIT_LIST_HEAD(&whc->periodic_list[i]);
81 INIT_LIST_HEAD(&whc->periodic_removed_list);
82
83 /* Map HC registers. */
84 start = whc->umc->resource.start;
85 len = whc->umc->resource.end - start + 1;
86 if (!request_mem_region(start, len, "whci-hc")) {
87 dev_err(&whc->umc->dev, "can't request HC region\n");
88 ret = -EBUSY;
89 goto error;
90 }
91 whc->base_phys = start;
92 whc->base = ioremap(start, len);
93 if (!whc->base) {
94 dev_err(&whc->umc->dev, "ioremap\n");
95 ret = -ENOMEM;
96 goto error;
97 }
98
99 whc_hw_reset(whc);
100
101 /* Read maximum number of devices, keys and MMC IEs. */
102 whcsparams = le_readl(whc->base + WHCSPARAMS);
103 whc->n_devices = WHCSPARAMS_TO_N_DEVICES(whcsparams);
104 whc->n_keys = WHCSPARAMS_TO_N_KEYS(whcsparams);
105 whc->n_mmc_ies = WHCSPARAMS_TO_N_MMC_IES(whcsparams);
106
107 dev_dbg(&whc->umc->dev, "N_DEVICES = %d, N_KEYS = %d, N_MMC_IES = %d\n",
108 whc->n_devices, whc->n_keys, whc->n_mmc_ies);
109
110 whc->qset_pool = dma_pool_create("qset", &whc->umc->dev,
111 sizeof(struct whc_qset), 64, 0);
112 if (whc->qset_pool == NULL) {
113 ret = -ENOMEM;
114 goto error;
115 }
116
117 ret = asl_init(whc);
118 if (ret < 0)
119 goto error;
120 ret = pzl_init(whc);
121 if (ret < 0)
122 goto error;
123
124 /* Allocate and initialize a buffer for generic commands, the
125 Device Information buffer, and the Device Notification
126 buffer. */
127
128 whc->gen_cmd_buf = dma_alloc_coherent(&whc->umc->dev, WHC_GEN_CMD_DATA_LEN,
129 &whc->gen_cmd_buf_dma, GFP_KERNEL);
130 if (whc->gen_cmd_buf == NULL) {
131 ret = -ENOMEM;
132 goto error;
133 }
134
135 whc->dn_buf = dma_alloc_coherent(&whc->umc->dev,
136 sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES,
137 &whc->dn_buf_dma, GFP_KERNEL);
138 if (!whc->dn_buf) {
139 ret = -ENOMEM;
140 goto error;
141 }
142 whc_hw_init_dn_buf(whc);
143
144 whc->di_buf = dma_alloc_coherent(&whc->umc->dev,
145 sizeof(struct di_buf_entry) * whc->n_devices,
146 &whc->di_buf_dma, GFP_KERNEL);
147 if (!whc->di_buf) {
148 ret = -ENOMEM;
149 goto error;
150 }
151 whc_hw_init_di_buf(whc);
152
153 return 0;
154
155error:
156 whc_clean_up(whc);
157 return ret;
158}
159
160void whc_clean_up(struct whc *whc)
161{
162 resource_size_t len;
163
164 if (whc->di_buf)
165 dma_free_coherent(&whc->umc->dev, sizeof(struct di_buf_entry) * whc->n_devices,
166 whc->di_buf, whc->di_buf_dma);
167 if (whc->dn_buf)
168 dma_free_coherent(&whc->umc->dev, sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES,
169 whc->dn_buf, whc->dn_buf_dma);
170 if (whc->gen_cmd_buf)
171 dma_free_coherent(&whc->umc->dev, WHC_GEN_CMD_DATA_LEN,
172 whc->gen_cmd_buf, whc->gen_cmd_buf_dma);
173
174 pzl_clean_up(whc);
175 asl_clean_up(whc);
176
177 if (whc->qset_pool)
178 dma_pool_destroy(whc->qset_pool);
179
180 len = whc->umc->resource.end - whc->umc->resource.start + 1;
181 if (whc->base)
182 iounmap(whc->base);
183 if (whc->base_phys)
184 release_mem_region(whc->base_phys, len);
185
186 if (whc->workqueue)
187 destroy_workqueue(whc->workqueue);
188}