1 /*
2  * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for
6  * any purpose with or without fee is hereby granted, provided that the
7  * above copyright notice and this permission notice appear in all
8  * copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include "athdefs.h"
21 #include "a_types.h"
22 #include "a_osapi.h"
23 #define ATH_MODULE_NAME hif
24 #include "a_debug.h"
25 
26 #include "targaddrs.h"
27 #include "hif.h"
28 #include "if_sdio.h"
29 #include "regtable_sdio.h"
30 #include "hif_sdio_dev.h"
31 #include "qdf_module.h"
32 
33 #define CPU_DBG_SEL_ADDRESS                      0x00000483
34 #define CPU_DBG_ADDRESS                          0x00000484
35 #define WORD_NON_ALIGNMENT_MASK                  0x03
36 
37 /**
38  * hif_ar6000_set_address_window_register() - set the window address register
39  *                                            (using 4-byte register access).
40  * @hif_device: hif context
41  * @register_addr: register address
42  * @addr: addr
43  *
44  * This mitigates host interconnect issues with non-4byte aligned bus requests,
45  * some interconnects use bus adapters that impose strict limitations.
46  * Since diag window access is not intended for performance critical operations,
47  * the 4byte mode should be satisfactory as it generates 4X the bus activity.
48  *
49  * Return: QDF_STATUS_SUCCESS for success.
50  */
51 static
hif_ar6000_set_address_window_register(struct hif_sdio_dev * hif_device,uint32_t register_addr,uint32_t addr)52 QDF_STATUS hif_ar6000_set_address_window_register(
53 			struct hif_sdio_dev *hif_device,
54 			uint32_t register_addr,
55 			uint32_t addr)
56 {
57 	QDF_STATUS status;
58 	static uint32_t address;
59 
60 	address = addr;
61 	/*AR6320,just write the 4-byte address to window register*/
62 	status = hif_read_write(hif_device,
63 				register_addr,
64 				(char *) (&address),
65 				4, HIF_WR_SYNC_BYTE_INC, NULL);
66 
67 	if (status != QDF_STATUS_SUCCESS) {
68 		AR_DEBUG_PRINTF(ATH_LOG_ERR,
69 			("Cannot write 0x%x to window reg: 0x%X\n",
70 			 addr, register_addr));
71 		return status;
72 	}
73 
74 	return QDF_STATUS_SUCCESS;
75 }
76 
77 /**
78  * hif_diag_read_access() - Read from the AR6000 through its diagnostic window.
79  * @hif_ctx: hif context
80  * @address: address
81  * @data: data
82  *
83  * No cooperation from the Target is required for this.
84  *
85  * Return: QDF_STATUS_SUCCESS for success.
86  */
hif_diag_read_access(struct hif_opaque_softc * hif_ctx,uint32_t address,uint32_t * data)87 QDF_STATUS hif_diag_read_access(struct hif_opaque_softc *hif_ctx,
88 				uint32_t address,
89 				uint32_t *data)
90 {
91 	QDF_STATUS status;
92 	static uint32_t readvalue;
93 	struct hif_sdio_softc *scn = HIF_GET_SDIO_SOFTC(hif_ctx);
94 	struct hif_sdio_dev *hif_device = scn->hif_handle;
95 
96 	if (address & WORD_NON_ALIGNMENT_MASK) {
97 		AR_DEBUG_PRINTF(ATH_LOG_ERR,
98 			("[%s]addr is not 4 bytes align.addr[0x%08x]\n",
99 			 __func__, address));
100 		return QDF_STATUS_E_FAILURE;
101 	}
102 
103 	/* set window register to start read cycle */
104 	status = hif_ar6000_set_address_window_register(hif_device,
105 						WINDOW_READ_ADDR_ADDRESS,
106 						address);
107 
108 	if (status != QDF_STATUS_SUCCESS)
109 		return status;
110 
111 	/* read the data */
112 	status = hif_read_write(hif_device,
113 				WINDOW_DATA_ADDRESS,
114 				(char *) &readvalue,
115 				sizeof(uint32_t), HIF_RD_SYNC_BYTE_INC, NULL);
116 	if (status != QDF_STATUS_SUCCESS) {
117 		AR_DEBUG_PRINTF(ATH_LOG_ERR,
118 			("Cannot read from WINDOW_DATA_ADDRESS\n"));
119 		return status;
120 	}
121 
122 	*data = readvalue;
123 	return status;
124 }
125 
126 /**
127  * hif_diag_write_access() - Write to the AR6000 through its diagnostic window.
128  * @hif_ctx: hif context
129  * @address: address
130  * @data: data
131  *
132  * No cooperation from the Target is required for this.
133  *
134  * Return: QDF_STATUS_SUCCESS for success.
135  */
hif_diag_write_access(struct hif_opaque_softc * hif_ctx,uint32_t address,uint32_t data)136 QDF_STATUS hif_diag_write_access(struct hif_opaque_softc *hif_ctx,
137 				 uint32_t address, uint32_t data)
138 {
139 	QDF_STATUS status;
140 	static uint32_t write_value;
141 	struct hif_sdio_softc *scn = HIF_GET_SDIO_SOFTC(hif_ctx);
142 	struct hif_sdio_dev *hif_device = scn->hif_handle;
143 
144 	if (address & WORD_NON_ALIGNMENT_MASK) {
145 		AR_DEBUG_PRINTF(ATH_LOG_ERR,
146 			("[%s]addr is not 4 bytes align.addr[0x%08x]\n",
147 			 __func__, address));
148 		return QDF_STATUS_E_FAILURE;
149 	}
150 
151 	write_value = data;
152 
153 	/* set write data */
154 	status = hif_read_write(hif_device,
155 				WINDOW_DATA_ADDRESS,
156 				(char *) &write_value,
157 				sizeof(uint32_t), HIF_WR_SYNC_BYTE_INC, NULL);
158 	if (status != QDF_STATUS_SUCCESS) {
159 		AR_DEBUG_PRINTF(ATH_LOG_ERR,
160 			("Cannot write 0x%x to WINDOW_DATA_ADDRESS\n",
161 			 data));
162 		return status;
163 	}
164 
165 	/* set window register, which starts the write cycle */
166 	return hif_ar6000_set_address_window_register(hif_device,
167 						  WINDOW_WRITE_ADDR_ADDRESS,
168 						  address);
169 }
170 
171 /**
172  * hif_diag_write_mem() - Write a block data to the AR6000 through its
173  *                        diagnostic window.
174  * @scn: hif context
175  * @address: address
176  * @data: data
177  * @nbytes: nbytes
178  *
179  * This function may take some time.
180  * No cooperation from the Target is required for this.
181  *
182  * Return: QDF_STATUS_SUCCESS for success.
183  */
hif_diag_write_mem(struct hif_opaque_softc * scn,uint32_t address,uint8_t * data,int nbytes)184 QDF_STATUS hif_diag_write_mem(struct hif_opaque_softc *scn, uint32_t address,
185 			      uint8_t *data, int nbytes)
186 {
187 	QDF_STATUS status;
188 	int32_t i;
189 	uint32_t tmp_data;
190 
191 	if ((address & WORD_NON_ALIGNMENT_MASK) ||
192 				(nbytes & WORD_NON_ALIGNMENT_MASK)) {
193 		AR_DEBUG_PRINTF(ATH_LOG_ERR,
194 			("[%s]addr or length is not 4 bytes align.addr[0x%08x] len[0x%08x]\n",
195 			 __func__, address, nbytes));
196 		return QDF_STATUS_E_FAILURE;
197 	}
198 
199 	for (i = 0; i < nbytes; i += 4) {
200 		tmp_data =
201 			data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
202 			(data[i + 3] << 24);
203 		status = hif_diag_write_access(scn, address + i, tmp_data);
204 		if (status != QDF_STATUS_SUCCESS) {
205 			AR_DEBUG_PRINTF(ATH_LOG_ERR,
206 				("Diag Write mem failed.addr[0x%08x] value[0x%08x]\n",
207 				 address + i, tmp_data));
208 			return status;
209 		}
210 	}
211 
212 	return QDF_STATUS_SUCCESS;
213 }
214 
215 /**
216  * hif_diag_read_mem() - Read a block data to the AR6000 through its diagnostic
217  *                       window.
218  * @scn: hif context
219  * @address: target address
220  * @data: data
221  * @nbytes: nbytes
222  *
223  * This function may take some time.
224  * No cooperation from the Target is required for this.
225  *
226  * Return: QDF_STATUS_SUCCESS for success.
227  */
hif_diag_read_mem(struct hif_opaque_softc * scn,uint32_t address,uint8_t * data,int nbytes)228 QDF_STATUS hif_diag_read_mem(struct hif_opaque_softc *scn,
229 			     uint32_t address, uint8_t *data,
230 			     int nbytes)
231 {
232 	QDF_STATUS status;
233 	int32_t i;
234 	uint32_t tmp_data;
235 
236 	if ((address & WORD_NON_ALIGNMENT_MASK) ||
237 					(nbytes & WORD_NON_ALIGNMENT_MASK)) {
238 		AR_DEBUG_PRINTF(ATH_LOG_ERR,
239 			("[%s]addr or length is not 4 bytes align.addr[0x%08x] len[0x%08x]\n",
240 			 __func__, address, nbytes));
241 		return QDF_STATUS_E_FAILURE;
242 	}
243 
244 	for (i = 0; i < nbytes; i += 4) {
245 		status = hif_diag_read_access(scn, address + i, &tmp_data);
246 		if (status != QDF_STATUS_SUCCESS) {
247 			AR_DEBUG_PRINTF(ATH_LOG_ERR,
248 					("Diag Write mem failed.addr[0x%08x] value[0x%08x]\n",
249 					 address + i, tmp_data));
250 			return status;
251 		}
252 		data[i] = tmp_data & 0xff;
253 		data[i + 1] = tmp_data >> 8 & 0xff;
254 		data[i + 2] = tmp_data >> 16 & 0xff;
255 		data[i + 3] = tmp_data >> 24 & 0xff;
256 	}
257 
258 	return QDF_STATUS_SUCCESS;
259 }
260 qdf_export_symbol(hif_diag_read_mem);
261 
262 /**
263  * hif_ar6k_read_target_register() - call to read target register values
264  * @hif_device: hif context
265  * @regsel: register selection
266  * @regval: reg value
267  *
268  * Return: QDF_STATUS_SUCCESS for success.
269  */
hif_ar6k_read_target_register(struct hif_sdio_dev * hif_device,int regsel,uint32_t * regval)270 static QDF_STATUS hif_ar6k_read_target_register(struct hif_sdio_dev *hif_device,
271 					 int regsel, uint32_t *regval)
272 {
273 	QDF_STATUS status;
274 	char vals[4];
275 	char register_selection[4];
276 
277 	register_selection[0] = regsel & 0xff;
278 	register_selection[1] = regsel & 0xff;
279 	register_selection[2] = regsel & 0xff;
280 	register_selection[3] = regsel & 0xff;
281 	status = hif_read_write(hif_device, CPU_DBG_SEL_ADDRESS,
282 				register_selection, 4,
283 				HIF_WR_SYNC_BYTE_FIX, NULL);
284 
285 	if (status != QDF_STATUS_SUCCESS) {
286 		AR_DEBUG_PRINTF(ATH_LOG_ERR,
287 			("Cannot write CPU_DBG_SEL (%d)\n", regsel));
288 		return status;
289 	}
290 
291 	status = hif_read_write(hif_device,
292 				CPU_DBG_ADDRESS,
293 				(char *) vals,
294 				sizeof(vals), HIF_RD_SYNC_BYTE_INC, NULL);
295 	if (status != QDF_STATUS_SUCCESS) {
296 		AR_DEBUG_PRINTF(ATH_LOG_ERR,
297 				("Cannot read from CPU_DBG_ADDRESS\n"));
298 		return status;
299 	}
300 
301 	*regval = vals[0] << 0 | vals[1] << 8 |
302 			vals[2] << 16 | vals[3] << 24;
303 
304 	return status;
305 }
306 
307 /**
308  * hif_ar6k_fetch_target_regs() - call to fetch target reg values
309  * @hif_device: hif context
310  * @targregs: target regs
311  *
312  * Return: None
313  */
hif_ar6k_fetch_target_regs(struct hif_sdio_dev * hif_device,uint32_t * targregs)314 void hif_ar6k_fetch_target_regs(struct hif_sdio_dev *hif_device,
315 				uint32_t *targregs)
316 {
317 	int i;
318 	uint32_t val;
319 
320 	for (i = 0; i < AR6003_FETCH_TARG_REGS_COUNT; i++) {
321 		val = 0xffffffff;
322 		hif_ar6k_read_target_register(hif_device, i, &val);
323 		targregs[i] = val;
324 	}
325 }
326