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