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