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