• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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  */
whc_hw_reset(struct whc * whc)29 static 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 
whc_hw_init_di_buf(struct whc * whc)36 static 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 
whc_hw_init_dn_buf(struct whc * whc)47 static 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 
whc_init(struct whc * whc)56 int 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 
155 error:
156 	whc_clean_up(whc);
157 	return ret;
158 }
159 
whc_clean_up(struct whc * whc)160 void 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 }
189