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 */ 30static 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 37static 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 48static 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 57int 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 156error: 157 whc_clean_up(whc); 158 return ret; 159} 160 161void 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 = 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) 188 destroy_workqueue(whc->workqueue); 189} 190