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 */
whc_hw_reset(struct whc * whc)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
whc_hw_init_di_buf(struct whc * whc)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
whc_hw_init_dn_buf(struct whc * whc)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
whc_init(struct whc * whc)57 int whc_init(struct whc *whc)
58 {
59 u32 whcsparams;
60 int ret, i;
61 resource_size_t start, len;
62
63 spin_lock_init(&whc->lock);
64 mutex_init(&whc->mutex);
65 init_waitqueue_head(&whc->cmd_wq);
66 init_waitqueue_head(&whc->async_list_wq);
67 init_waitqueue_head(&whc->periodic_list_wq);
68 whc->workqueue = create_singlethread_workqueue(dev_name(&whc->umc->dev));
69 if (whc->workqueue == NULL) {
70 ret = -ENOMEM;
71 goto error;
72 }
73 INIT_WORK(&whc->dn_work, whc_dn_work);
74
75 INIT_WORK(&whc->async_work, scan_async_work);
76 INIT_LIST_HEAD(&whc->async_list);
77 INIT_LIST_HEAD(&whc->async_removed_list);
78
79 INIT_WORK(&whc->periodic_work, scan_periodic_work);
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
129 whc->gen_cmd_buf = dma_alloc_coherent(&whc->umc->dev, WHC_GEN_CMD_DATA_LEN,
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
whc_clean_up(struct whc * whc)161 void whc_clean_up(struct whc *whc)
162 {
163 resource_size_t len;
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)
172 dma_free_coherent(&whc->umc->dev, WHC_GEN_CMD_DATA_LEN,
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)
179 dma_pool_destroy(whc->qset_pool);
180
181 len = whc->umc->resource.end - whc->umc->resource.start + 1;
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)
188 destroy_workqueue(whc->workqueue);
189 }
190