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