1 /* 2 * Copyright (c) 2015-2021 The Linux Foundation. All rights reserved. 3 * Copyright (c) 2022 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 /** 21 * DOC: if_snoc.c 22 * 23 * c file for snoc specific implementations. 24 */ 25 26 #include "hif.h" 27 #include "hif_main.h" 28 #include "hif_debug.h" 29 #include "hif_io32.h" 30 #include "ce_main.h" 31 #include "ce_tasklet.h" 32 #include "ce_api.h" 33 #include "ce_internal.h" 34 #include "snoc_api.h" 35 #include "pld_common.h" 36 #include "qdf_util.h" 37 #ifdef IPA_OFFLOAD 38 #include <uapi/linux/msm_ipa.h> 39 #endif 40 #include "target_type.h" 41 42 /** 43 * hif_disable_isr(): disable isr 44 * 45 * This function disables isr and kills tasklets 46 * 47 * @hif_ctx: struct hif_softc 48 * 49 * Return: void 50 */ 51 void hif_snoc_disable_isr(struct hif_softc *scn) 52 { 53 hif_exec_kill(&scn->osc); 54 hif_nointrs(scn); 55 ce_tasklet_kill(scn); 56 qdf_atomic_set(&scn->active_tasklet_cnt, 0); 57 qdf_atomic_set(&scn->active_grp_tasklet_cnt, 0); 58 } 59 60 /** 61 * hif_dump_registers(): dump bus debug registers 62 * @hif_ctx: struct hif_opaque_softc 63 * 64 * This function dumps hif bus debug registers 65 * 66 * Return: 0 for success or error code 67 */ 68 int hif_snoc_dump_registers(struct hif_softc *hif_ctx) 69 { 70 int status; 71 struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx); 72 73 status = hif_dump_ce_registers(scn); 74 if (status) 75 hif_err("Dump CE Registers Failed"); 76 77 return 0; 78 } 79 80 void hif_snoc_display_stats(struct hif_softc *hif_ctx) 81 { 82 if (!hif_ctx) { 83 hif_err("hif_ctx null"); 84 return; 85 } 86 hif_display_ce_stats(hif_ctx); 87 } 88 89 void hif_snoc_clear_stats(struct hif_softc *hif_ctx) 90 { 91 struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(hif_ctx); 92 93 if (!hif_state) { 94 hif_err("hif_ctx null"); 95 return; 96 } 97 hif_clear_ce_stats(hif_state); 98 } 99 100 /** 101 * hif_snoc_close(): hif_bus_close 102 * 103 * Return: n/a 104 */ 105 void hif_snoc_close(struct hif_softc *scn) 106 { 107 hif_ce_close(scn); 108 } 109 110 /** 111 * hif_bus_open(): hif_bus_open 112 * @hif_ctx: hif context 113 * @bus_type: bus type 114 * 115 * Return: n/a 116 */ 117 QDF_STATUS hif_snoc_open(struct hif_softc *hif_ctx, enum qdf_bus_type bus_type) 118 { 119 return hif_ce_open(hif_ctx); 120 } 121 122 /** 123 * hif_snoc_get_soc_info() - populates scn with hw info 124 * 125 * fills in the virtual and physical base address as well as 126 * soc version info. 127 * 128 * return 0 or QDF_STATUS_E_FAILURE 129 */ 130 static QDF_STATUS hif_snoc_get_soc_info(struct hif_softc *scn) 131 { 132 int ret; 133 struct pld_soc_info soc_info; 134 135 qdf_mem_zero(&soc_info, sizeof(soc_info)); 136 137 ret = pld_get_soc_info(scn->qdf_dev->dev, &soc_info); 138 if (ret < 0) { 139 hif_err("pld_get_soc_info error = %d", ret); 140 return QDF_STATUS_E_FAILURE; 141 } 142 143 scn->mem = soc_info.v_addr; 144 scn->mem_pa = soc_info.p_addr; 145 146 scn->target_info.soc_version = soc_info.soc_id; 147 scn->target_info.target_version = soc_info.soc_id; 148 scn->target_info.target_revision = 0; 149 return QDF_STATUS_SUCCESS; 150 } 151 152 /** 153 * hif_bus_configure() - configure the snoc bus 154 * @scn: pointer to the hif context. 155 * 156 * return: 0 for success. nonzero for failure. 157 */ 158 int hif_snoc_bus_configure(struct hif_softc *scn) 159 { 160 int ret; 161 uint8_t wake_ce_id; 162 163 ret = hif_snoc_get_soc_info(scn); 164 if (ret) 165 return ret; 166 167 hif_ce_prepare_config(scn); 168 169 ret = hif_wlan_enable(scn); 170 if (ret) { 171 hif_err("hif_wlan_enable error = %d", ret); 172 return ret; 173 } 174 175 ret = hif_config_ce(scn); 176 if (ret) 177 goto wlan_disable; 178 179 ret = hif_get_wake_ce_id(scn, &wake_ce_id); 180 if (ret) 181 goto unconfig_ce; 182 183 scn->wake_irq = pld_get_irq(scn->qdf_dev->dev, wake_ce_id); 184 scn->wake_irq_type = HIF_PM_CE_WAKE; 185 186 hif_info("expecting wake from ce %d, irq %d", 187 wake_ce_id, scn->wake_irq); 188 189 return 0; 190 191 unconfig_ce: 192 hif_unconfig_ce(scn); 193 194 wlan_disable: 195 hif_wlan_disable(scn); 196 197 return ret; 198 } 199 200 /** 201 * hif_snoc_get_target_type(): Get the target type 202 * 203 * This function is used to query the target type. 204 * 205 * @ol_sc: hif_softc struct pointer 206 * @dev: device pointer 207 * @bdev: bus dev pointer 208 * @bid: bus id pointer 209 * @hif_type: HIF type such as HIF_TYPE_QCA6180 210 * @target_type: target type such as TARGET_TYPE_QCA6180 211 * 212 * Return: 0 for success 213 */ 214 static inline int hif_snoc_get_target_type(struct hif_softc *ol_sc, 215 struct device *dev, void *bdev, const struct hif_bus_id *bid, 216 uint32_t *hif_type, uint32_t *target_type) 217 { 218 /* TODO: need to use HW version. Hard code for now */ 219 #ifdef QCA_WIFI_3_0_ADRASTEA 220 *hif_type = HIF_TYPE_ADRASTEA; 221 *target_type = TARGET_TYPE_ADRASTEA; 222 #else 223 *hif_type = 0; 224 *target_type = 0; 225 #endif 226 return 0; 227 } 228 229 #ifdef IPA_OFFLOAD 230 static int hif_set_dma_coherent_mask(qdf_device_t osdev) 231 { 232 uint8_t addr_bits; 233 234 if (false == hif_get_ipa_present()) 235 return qdf_set_dma_coherent_mask(osdev->dev, 236 DMA_COHERENT_MASK_DEFAULT); 237 238 if (hif_get_ipa_hw_type() < IPA_HW_v3_0) 239 addr_bits = DMA_COHERENT_MASK_BELOW_IPA_VER_3; 240 else 241 addr_bits = DMA_COHERENT_MASK_DEFAULT; 242 243 return qdf_set_dma_coherent_mask(osdev->dev, addr_bits); 244 } 245 #else 246 static int hif_set_dma_coherent_mask(qdf_device_t osdev) 247 { 248 return qdf_set_dma_coherent_mask(osdev->dev, 249 DMA_COHERENT_MASK_DEFAULT); 250 } 251 #endif 252 253 /** 254 * hif_enable_bus(): hif_enable_bus 255 * @dev: dev 256 * @bdev: bus dev 257 * @bid: bus id 258 * @type: bus type 259 * 260 * Return: QDF_STATUS 261 */ 262 QDF_STATUS hif_snoc_enable_bus(struct hif_softc *ol_sc, 263 struct device *dev, void *bdev, 264 const struct hif_bus_id *bid, 265 enum hif_enable_type type) 266 { 267 int ret; 268 int hif_type; 269 int target_type; 270 271 if (!ol_sc) { 272 hif_err("hif_ctx is NULL"); 273 return QDF_STATUS_E_NOMEM; 274 } 275 276 ret = hif_set_dma_coherent_mask(ol_sc->qdf_dev); 277 if (ret) { 278 hif_err("Failed to set dma mask error = %d", ret); 279 return qdf_status_from_os_return(ret); 280 } 281 282 ret = qdf_device_init_wakeup(ol_sc->qdf_dev, true); 283 if (ret == -EEXIST) 284 hif_warn("device_init_wakeup already done"); 285 else if (ret) { 286 hif_err("device_init_wakeup: err= %d", ret); 287 return qdf_status_from_os_return(ret); 288 } 289 290 ret = hif_snoc_get_target_type(ol_sc, dev, bdev, bid, 291 &hif_type, &target_type); 292 if (ret < 0) { 293 hif_err("Invalid device id/revision_id"); 294 return QDF_STATUS_E_FAILURE; 295 } 296 297 ol_sc->target_info.target_type = target_type; 298 299 hif_register_tbl_attach(ol_sc, hif_type); 300 hif_target_register_tbl_attach(ol_sc, target_type); 301 302 /* the bus should remain on during suspend for snoc */ 303 hif_vote_link_up(GET_HIF_OPAQUE_HDL(ol_sc)); 304 305 hif_debug("X - hif_type = 0x%x, target_type = 0x%x", 306 hif_type, target_type); 307 308 return QDF_STATUS_SUCCESS; 309 } 310 311 /** 312 * hif_disable_bus(): hif_disable_bus 313 * 314 * This function disables the bus 315 * 316 * @bdev: bus dev 317 * 318 * Return: none 319 */ 320 void hif_snoc_disable_bus(struct hif_softc *scn) 321 { 322 int ret; 323 324 hif_vote_link_down(GET_HIF_OPAQUE_HDL(scn)); 325 326 ret = qdf_device_init_wakeup(scn->qdf_dev, false); 327 if (ret) 328 hif_err("device_init_wakeup: err %d", ret); 329 } 330 331 /** 332 * hif_nointrs(): disable IRQ 333 * 334 * This function stops interrupt(s) 335 * 336 * @scn: struct hif_softc 337 * 338 * Return: none 339 */ 340 void hif_snoc_nointrs(struct hif_softc *scn) 341 { 342 struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn); 343 344 scn->free_irq_done = true; 345 ce_unregister_irq(hif_state, CE_ALL_BITMAP); 346 } 347 348 /** 349 * ce_irq_enable() - enable copy engine IRQ 350 * @scn: struct hif_softc 351 * @ce_id: ce_id 352 * 353 * Return: N/A 354 */ 355 void hif_snoc_irq_enable(struct hif_softc *scn, 356 int ce_id) 357 { 358 ce_enable_irq_in_individual_register(scn, ce_id); 359 } 360 361 /** 362 * ce_irq_disable() - disable copy engine IRQ 363 * @scn: struct hif_softc 364 * @ce_id: ce_id 365 * 366 * Return: N/A 367 */ 368 void hif_snoc_irq_disable(struct hif_softc *scn, int ce_id) 369 { 370 ce_disable_irq_in_individual_register(scn, ce_id); 371 } 372 373 /* 374 * hif_snoc_setup_wakeup_sources() - enable/disable irq wake on correct irqs 375 * @hif_softc: hif context 376 * 377 * Firmware will send a wakeup request to the HTC_CTRL_RSVD_SVC when waking up 378 * the host driver. Ensure that the copy complete interrupt from this copy 379 * engine can wake up the apps processor. 380 * 381 * Return: 0 for success 382 */ 383 static 384 QDF_STATUS hif_snoc_setup_wakeup_sources(struct hif_softc *scn, bool enable) 385 { 386 int ret; 387 388 if (enable) 389 ret = enable_irq_wake(scn->wake_irq); 390 else 391 ret = disable_irq_wake(scn->wake_irq); 392 393 if (ret) { 394 hif_err("Fail to setup wake IRQ!"); 395 return QDF_STATUS_E_RESOURCES; 396 } 397 398 return QDF_STATUS_SUCCESS; 399 } 400 401 /** 402 * hif_snoc_bus_suspend() - prepare to suspend the bus 403 * @scn: hif context 404 * 405 * Setup wakeup interrupt configuration. 406 * Disable CE interrupts (wakeup interrupt will still wake apps) 407 * Drain tasklets. - make sure that we don't suspend while processing 408 * the wakeup message. 409 * 410 * Return: 0 on success. 411 */ 412 int hif_snoc_bus_suspend(struct hif_softc *scn) 413 { 414 if (hif_snoc_setup_wakeup_sources(scn, true) != QDF_STATUS_SUCCESS) 415 return -EFAULT; 416 return 0; 417 } 418 419 /** 420 * hif_snoc_bus_resume() - snoc bus resume function 421 * @scn: hif context 422 * 423 * Clear wakeup interrupt configuration. 424 * Re-enable ce interrupts 425 * 426 * Return: 0 on success 427 */ 428 int hif_snoc_bus_resume(struct hif_softc *scn) 429 { 430 if (hif_snoc_setup_wakeup_sources(scn, false) != QDF_STATUS_SUCCESS) 431 QDF_BUG(0); 432 433 return 0; 434 } 435 436 /** 437 * hif_snoc_bus_suspend_noirq() - ensure there are no pending transactions 438 * @scn: hif context 439 * 440 * Ensure that if we received the wakeup message before the irq 441 * was disabled that the message is processed before suspending. 442 * 443 * Return: -EBUSY if we fail to flush the tasklets. 444 */ 445 int hif_snoc_bus_suspend_noirq(struct hif_softc *scn) 446 { 447 if (hif_drain_tasklets(scn) != 0) 448 return -EBUSY; 449 return 0; 450 } 451 452 int hif_snoc_map_ce_to_irq(struct hif_softc *scn, int ce_id) 453 { 454 return pld_get_irq(scn->qdf_dev->dev, ce_id); 455 } 456 457 /** 458 * hif_is_target_register_access_allowed(): Check target register access allow 459 * @scn: HIF Context 460 * 461 * This function help to check whether target register access is allowed or not 462 * 463 * Return: true if target access is allowed else false 464 */ 465 bool hif_is_target_register_access_allowed(struct hif_softc *scn) 466 { 467 if (hif_is_recovery_in_progress(scn)) 468 return hif_is_target_ready(scn); 469 else 470 return true; 471 } 472 473 /** 474 * hif_snoc_needs_bmi() - return true if the soc needs bmi through the driver 475 * @scn: hif context 476 * 477 * Return: true if soc needs driver bmi otherwise false 478 */ 479 bool hif_snoc_needs_bmi(struct hif_softc *scn) 480 { 481 return false; 482 } 483