1 /*
2  * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved.
3  *
4  * Permission to use, copy, modify, and/or distribute this software for
5  * any purpose with or without fee is hereby granted, provided that the
6  * above copyright notice and this permission notice appear in all
7  * copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16  * PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include "i_bmi.h"
20 #include "cds_api.h"
21 
22 /* APIs visible to the driver */
23 
24 QDF_STATUS
bmi_read_memory(uint32_t address,uint8_t * buffer,uint32_t length,struct ol_context * ol_ctx)25 bmi_read_memory(uint32_t address,
26 		uint8_t *buffer, uint32_t length, struct ol_context *ol_ctx)
27 {
28 	struct hif_opaque_softc *scn = ol_ctx->scn;
29 	uint32_t cid;
30 	int status;
31 	uint32_t offset;
32 	uint32_t remaining, rxlen;
33 	struct bmi_info *info = GET_BMI_CONTEXT(ol_ctx);
34 	uint8_t *bmi_cmd_buff = info->bmi_cmd_buff;
35 	uint8_t *bmi_rsp_buff = info->bmi_rsp_buff;
36 	uint32_t align;
37 	qdf_dma_addr_t cmd = info->bmi_cmd_da;
38 	qdf_dma_addr_t rsp = info->bmi_rsp_da;
39 
40 	if (info->bmi_done) {
41 		BMI_DBG("command disallowed");
42 		return QDF_STATUS_E_PERM;
43 	}
44 
45 	if (!info->bmi_cmd_buff || !info->bmi_rsp_buff) {
46 		BMI_ERR("BMI Initialization hasn't done");
47 		return QDF_STATUS_NOT_INITIALIZED;
48 	}
49 
50 	bmi_assert(BMI_COMMAND_FITS(BMI_DATASZ_MAX + sizeof(cid) +
51 			sizeof(address) + sizeof(length)));
52 	qdf_mem_zero(bmi_cmd_buff, BMI_DATASZ_MAX + sizeof(cid) +
53 			sizeof(address) + sizeof(length));
54 	qdf_mem_zero(bmi_rsp_buff, BMI_DATASZ_MAX + sizeof(cid) +
55 			sizeof(address) + sizeof(length));
56 
57 	cid = BMI_READ_MEMORY;
58 	align = 0;
59 	remaining = length;
60 
61 	while (remaining) {
62 		rxlen = (remaining < BMI_DATASZ_MAX) ?
63 				remaining : BMI_DATASZ_MAX;
64 		offset = 0;
65 		qdf_mem_copy(&(bmi_cmd_buff[offset]), &cid, sizeof(cid));
66 		offset += sizeof(cid);
67 		qdf_mem_copy(&(bmi_cmd_buff[offset]), &address,
68 						sizeof(address));
69 		offset += sizeof(address);
70 		qdf_mem_copy(&(bmi_cmd_buff[offset]), &rxlen, sizeof(rxlen));
71 		offset += sizeof(length);
72 
73 		/* note we reuse the same buffer to receive on */
74 		status = hif_exchange_bmi_msg(scn, cmd, rsp, bmi_cmd_buff,
75 						offset, bmi_rsp_buff, &rxlen,
76 						BMI_EXCHANGE_TIMEOUT_MS);
77 		if (status) {
78 			BMI_ERR("Unable to read from the device");
79 			return QDF_STATUS_E_FAILURE;
80 		}
81 		if (remaining == rxlen) {
82 			qdf_mem_copy(&buffer[length - remaining + align],
83 					bmi_rsp_buff, rxlen - align);
84 			/* last align bytes are invalid */
85 		} else {
86 			qdf_mem_copy(&buffer[length - remaining + align],
87 				 bmi_rsp_buff, rxlen);
88 		}
89 		remaining -= rxlen;
90 		address += rxlen;
91 	}
92 
93 	return QDF_STATUS_SUCCESS;
94 }
95 
bmi_write_memory(uint32_t address,uint8_t * buffer,uint32_t length,struct ol_context * ol_ctx)96 QDF_STATUS bmi_write_memory(uint32_t address, uint8_t *buffer, uint32_t length,
97 						struct ol_context *ol_ctx)
98 {
99 	struct hif_opaque_softc *scn = ol_ctx->scn;
100 	uint32_t cid;
101 	int status;
102 	uint32_t offset;
103 	uint32_t remaining, txlen;
104 	const uint32_t header = sizeof(cid) + sizeof(address) + sizeof(length);
105 	uint8_t aligned_buffer[BMI_DATASZ_MAX];
106 	uint8_t *src;
107 	struct bmi_info *info = GET_BMI_CONTEXT(ol_ctx);
108 	uint8_t *bmi_cmd_buff = info->bmi_cmd_buff;
109 	qdf_dma_addr_t cmd = info->bmi_cmd_da;
110 	qdf_dma_addr_t rsp = info->bmi_rsp_da;
111 
112 	if (info->bmi_done) {
113 		BMI_ERR("Command disallowed");
114 		return QDF_STATUS_E_PERM;
115 	}
116 
117 	if (!bmi_cmd_buff) {
118 		BMI_ERR("BMI initialization hasn't done");
119 		return QDF_STATUS_E_PERM;
120 	}
121 
122 	bmi_assert(BMI_COMMAND_FITS(BMI_DATASZ_MAX + header));
123 	qdf_mem_zero(bmi_cmd_buff, BMI_DATASZ_MAX + header);
124 
125 	cid = BMI_WRITE_MEMORY;
126 
127 	remaining = length;
128 	while (remaining) {
129 		src = &buffer[length - remaining];
130 		if (remaining < (BMI_DATASZ_MAX - header)) {
131 			if (remaining & 3) {
132 				/* align it with 4 bytes */
133 				remaining = remaining + (4 - (remaining & 3));
134 				memcpy(aligned_buffer, src, remaining);
135 				src = aligned_buffer;
136 			}
137 			txlen = remaining;
138 		} else {
139 			txlen = (BMI_DATASZ_MAX - header);
140 		}
141 		offset = 0;
142 		qdf_mem_copy(&(bmi_cmd_buff[offset]), &cid, sizeof(cid));
143 		offset += sizeof(cid);
144 		qdf_mem_copy(&(bmi_cmd_buff[offset]), &address,
145 						sizeof(address));
146 		offset += sizeof(address);
147 		qdf_mem_copy(&(bmi_cmd_buff[offset]), &txlen, sizeof(txlen));
148 		offset += sizeof(txlen);
149 		qdf_mem_copy(&(bmi_cmd_buff[offset]), src, txlen);
150 		offset += txlen;
151 		status = hif_exchange_bmi_msg(scn, cmd, rsp, bmi_cmd_buff,
152 						offset, NULL, NULL,
153 						BMI_EXCHANGE_TIMEOUT_MS);
154 		if (status) {
155 			BMI_ERR("Unable to write to the device; status:%d",
156 								status);
157 			return QDF_STATUS_E_FAILURE;
158 		}
159 		remaining -= txlen;
160 		address += txlen;
161 	}
162 
163 	return QDF_STATUS_SUCCESS;
164 }
165 
166 QDF_STATUS
bmi_execute(uint32_t address,A_UINT32 * param,struct ol_context * ol_ctx)167 bmi_execute(uint32_t address, A_UINT32 *param, struct ol_context *ol_ctx)
168 {
169 	struct hif_opaque_softc *scn = ol_ctx->scn;
170 	uint32_t cid;
171 	int status;
172 	uint32_t offset;
173 	uint32_t param_len;
174 	struct bmi_info *info = GET_BMI_CONTEXT(ol_ctx);
175 	uint8_t *bmi_cmd_buff = info->bmi_cmd_buff;
176 	uint8_t *bmi_rsp_buff = info->bmi_rsp_buff;
177 	uint32_t size = sizeof(cid) + sizeof(address) + sizeof(param);
178 	qdf_dma_addr_t cmd = info->bmi_cmd_da;
179 	qdf_dma_addr_t rsp = info->bmi_rsp_da;
180 
181 	if (info->bmi_done) {
182 		BMI_ERR("Command disallowed");
183 		return QDF_STATUS_E_PERM;
184 	}
185 
186 	if (!bmi_cmd_buff || !bmi_rsp_buff) {
187 		BMI_ERR("%s:BMI CMD/RSP Buffer is NULL", __func__);
188 		return QDF_STATUS_NOT_INITIALIZED;
189 	}
190 
191 	bmi_assert(BMI_COMMAND_FITS(size));
192 	qdf_mem_zero(bmi_cmd_buff, size);
193 	qdf_mem_zero(bmi_rsp_buff, size);
194 
195 
196 	BMI_DBG("BMI Execute: device: 0x%pK, address: 0x%x, param: %d",
197 						scn, address, *param);
198 
199 	cid = BMI_EXECUTE;
200 
201 	offset = 0;
202 	qdf_mem_copy(&(bmi_cmd_buff[offset]), &cid, sizeof(cid));
203 	offset += sizeof(cid);
204 	qdf_mem_copy(&(bmi_cmd_buff[offset]), &address, sizeof(address));
205 	offset += sizeof(address);
206 	qdf_mem_copy(&(bmi_cmd_buff[offset]), param, sizeof(*param));
207 	offset += sizeof(*param);
208 	param_len = sizeof(*param);
209 	status = hif_exchange_bmi_msg(scn, cmd, rsp, bmi_cmd_buff, offset,
210 					bmi_rsp_buff, &param_len, 0);
211 	if (status) {
212 		BMI_ERR("Unable to read from the device status:%d", status);
213 		return QDF_STATUS_E_FAILURE;
214 	}
215 
216 	qdf_mem_copy(param, bmi_rsp_buff, sizeof(*param));
217 
218 	BMI_DBG("BMI Execute: Exit (param: %d)", *param);
219 	return QDF_STATUS_SUCCESS;
220 }
221 
222 inline QDF_STATUS
bmi_no_command(struct ol_context * ol_ctx)223 bmi_no_command(struct ol_context *ol_ctx)
224 {
225 	return QDF_STATUS_SUCCESS;
226 }
227 
228 QDF_STATUS
bmi_firmware_download(struct ol_context * ol_ctx)229 bmi_firmware_download(struct ol_context *ol_ctx)
230 {
231 	struct hif_opaque_softc *scn = ol_ctx->scn;
232 	QDF_STATUS status;
233 	struct bmi_target_info targ_info;
234 	struct hif_target_info *tgt_info = hif_get_target_info_handle(scn);
235 
236 	qdf_mem_zero(&targ_info, sizeof(targ_info));
237 	/* Initialize BMI */
238 	status = bmi_init(ol_ctx);
239 	if (status != QDF_STATUS_SUCCESS) {
240 		BMI_ERR("BMI Initialization Failed err:%d", status);
241 		return status;
242 	}
243 
244 	/* Get target information */
245 	status = bmi_get_target_info(&targ_info, ol_ctx);
246 	if (status != QDF_STATUS_SUCCESS) {
247 		BMI_ERR("BMI Target Info get failed: status:%d", status);
248 		return status;
249 	}
250 
251 	tgt_info->target_type = targ_info.target_type;
252 	tgt_info->target_version = targ_info.target_ver;
253 	/* Configure target */
254 	status = ol_configure_target(ol_ctx);
255 	if (status != QDF_STATUS_SUCCESS) {
256 		BMI_ERR("BMI Configure Target Failed status:%d", status);
257 		return status;
258 	}
259 	status = ol_download_firmware(ol_ctx);
260 	if (status != QDF_STATUS_SUCCESS)
261 		BMI_ERR("BMI Download Firmware Failed Status:%d", status);
262 
263 	return status;
264 }
265 
bmi_done_local(struct ol_context * ol_ctx)266 QDF_STATUS bmi_done_local(struct ol_context *ol_ctx)
267 {
268 	struct hif_opaque_softc *scn = ol_ctx->scn;
269 	int status;
270 	uint32_t cid;
271 	struct bmi_info *info;
272 	qdf_device_t qdf_dev = ol_ctx->qdf_dev;
273 	qdf_dma_addr_t cmd, rsp;
274 
275 	if (!scn) {
276 		BMI_ERR("Invalid scn context");
277 		bmi_assert(0);
278 		return QDF_STATUS_NOT_INITIALIZED;
279 	}
280 
281 	if (!qdf_dev->dev) {
282 		BMI_ERR("%s: Invalid device pointer", __func__);
283 		return QDF_STATUS_NOT_INITIALIZED;
284 	}
285 
286 	info = GET_BMI_CONTEXT(ol_ctx);
287 	if (info->bmi_done) {
288 		BMI_DBG(FL("skipped"));
289 		return QDF_STATUS_E_PERM;
290 	}
291 
292 	cmd = info->bmi_cmd_da;
293 	rsp = info->bmi_rsp_da;
294 
295 	BMI_DBG("BMI Done: Enter (device: 0x%pK)", scn);
296 
297 	info->bmi_done = true;
298 	cid = BMI_DONE;
299 
300 	if (!info->bmi_cmd_buff) {
301 		BMI_ERR("Invalid scn BMICmdBuff");
302 		bmi_assert(0);
303 		return QDF_STATUS_NOT_INITIALIZED;
304 	}
305 
306 	qdf_mem_copy(info->bmi_cmd_buff, &cid, sizeof(cid));
307 
308 	status = hif_exchange_bmi_msg(scn, cmd, rsp, info->bmi_cmd_buff,
309 				sizeof(cid), NULL, NULL, 0);
310 	if (status) {
311 		BMI_ERR("Failed to write to the device; status:%d", status);
312 		return QDF_STATUS_E_FAILURE;
313 	}
314 
315 	if (info->bmi_cmd_buff) {
316 		qdf_mem_free_consistent(qdf_dev, qdf_dev->dev,
317 					MAX_BMI_CMDBUF_SZ,
318 				    info->bmi_cmd_buff, info->bmi_cmd_da, 0);
319 		info->bmi_cmd_buff = NULL;
320 		info->bmi_cmd_da = 0;
321 	}
322 
323 	if (info->bmi_rsp_buff) {
324 		qdf_mem_free_consistent(qdf_dev, qdf_dev->dev,
325 					MAX_BMI_CMDBUF_SZ,
326 				    info->bmi_rsp_buff, info->bmi_rsp_da, 0);
327 		info->bmi_rsp_buff = NULL;
328 		info->bmi_rsp_da = 0;
329 	}
330 
331 	return QDF_STATUS_SUCCESS;
332 }
333