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