1 // SPDX-License-Identifier: GPL-2.0
3 * Wireless Host Controller (WHC) initialization.
5 * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version
9 * 2 as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include <linux/kernel.h>
20 #include <linux/gfp.h>
21 #include <linux/dma-mapping.h>
22 #include <linux/uwb/umc.h>
24 #include "../../wusbcore/wusbhc.h"
29 * Reset the host controller.
31 static void whc_hw_reset(struct whc *whc)
33 le_writel(WUSBCMD_WHCRESET, whc->base + WUSBCMD);
34 whci_wait_for(&whc->umc->dev, whc->base + WUSBCMD, WUSBCMD_WHCRESET, 0,
38 static void whc_hw_init_di_buf(struct whc *whc)
42 /* Disable all entries in the Device Information buffer. */
43 for (d = 0; d < whc->n_devices; d++)
44 whc->di_buf[d].addr_sec_info = WHC_DI_DISABLE;
46 le_writeq(whc->di_buf_dma, whc->base + WUSBDEVICEINFOADDR);
49 static void whc_hw_init_dn_buf(struct whc *whc)
51 /* Clear the Device Notification buffer to ensure the V (valid)
53 memset(whc->dn_buf, 0, 4096);
55 le_writeq(whc->dn_buf_dma, whc->base + WUSBDNTSBUFADDR);
58 int whc_init(struct whc *whc)
62 resource_size_t start, len;
64 spin_lock_init(&whc->lock);
65 mutex_init(&whc->mutex);
66 init_waitqueue_head(&whc->cmd_wq);
67 init_waitqueue_head(&whc->async_list_wq);
68 init_waitqueue_head(&whc->periodic_list_wq);
69 whc->workqueue = alloc_ordered_workqueue(dev_name(&whc->umc->dev), 0);
70 if (whc->workqueue == NULL) {
74 INIT_WORK(&whc->dn_work, whc_dn_work);
76 INIT_WORK(&whc->async_work, scan_async_work);
77 INIT_LIST_HEAD(&whc->async_list);
78 INIT_LIST_HEAD(&whc->async_removed_list);
80 INIT_WORK(&whc->periodic_work, scan_periodic_work);
81 for (i = 0; i < 5; i++)
82 INIT_LIST_HEAD(&whc->periodic_list[i]);
83 INIT_LIST_HEAD(&whc->periodic_removed_list);
85 /* Map HC registers. */
86 start = whc->umc->resource.start;
87 len = whc->umc->resource.end - start + 1;
88 if (!request_mem_region(start, len, "whci-hc")) {
89 dev_err(&whc->umc->dev, "can't request HC region\n");
93 whc->base_phys = start;
94 whc->base = ioremap(start, len);
96 dev_err(&whc->umc->dev, "ioremap\n");
103 /* Read maximum number of devices, keys and MMC IEs. */
104 whcsparams = le_readl(whc->base + WHCSPARAMS);
105 whc->n_devices = WHCSPARAMS_TO_N_DEVICES(whcsparams);
106 whc->n_keys = WHCSPARAMS_TO_N_KEYS(whcsparams);
107 whc->n_mmc_ies = WHCSPARAMS_TO_N_MMC_IES(whcsparams);
109 dev_dbg(&whc->umc->dev, "N_DEVICES = %d, N_KEYS = %d, N_MMC_IES = %d\n",
110 whc->n_devices, whc->n_keys, whc->n_mmc_ies);
112 whc->qset_pool = dma_pool_create("qset", &whc->umc->dev,
113 sizeof(struct whc_qset), 64, 0);
114 if (whc->qset_pool == NULL) {
126 /* Allocate and initialize a buffer for generic commands, the
127 Device Information buffer, and the Device Notification
130 whc->gen_cmd_buf = dma_alloc_coherent(&whc->umc->dev, WHC_GEN_CMD_DATA_LEN,
131 &whc->gen_cmd_buf_dma, GFP_KERNEL);
132 if (whc->gen_cmd_buf == NULL) {
137 whc->dn_buf = dma_alloc_coherent(&whc->umc->dev,
138 sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES,
139 &whc->dn_buf_dma, GFP_KERNEL);
144 whc_hw_init_dn_buf(whc);
146 whc->di_buf = dma_alloc_coherent(&whc->umc->dev,
147 sizeof(struct di_buf_entry) * whc->n_devices,
148 &whc->di_buf_dma, GFP_KERNEL);
153 whc_hw_init_di_buf(whc);
162 void whc_clean_up(struct whc *whc)
167 dma_free_coherent(&whc->umc->dev, sizeof(struct di_buf_entry) * whc->n_devices,
168 whc->di_buf, whc->di_buf_dma);
170 dma_free_coherent(&whc->umc->dev, sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES,
171 whc->dn_buf, whc->dn_buf_dma);
172 if (whc->gen_cmd_buf)
173 dma_free_coherent(&whc->umc->dev, WHC_GEN_CMD_DATA_LEN,
174 whc->gen_cmd_buf, whc->gen_cmd_buf_dma);
179 dma_pool_destroy(whc->qset_pool);
181 len = resource_size(&whc->umc->resource);
185 release_mem_region(whc->base_phys, len);
188 destroy_workqueue(whc->workqueue);