1 /* 2 * Copyright (c) 2016-2018, 2020 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 /* this file dispatches functions to bus specific definitions */ 20 #include "hif_debug.h" 21 #include "hif.h" 22 #include "hif_main.h" 23 #include "hif_io32.h" 24 #include "multibus.h" 25 #include "dummy.h" 26 #if defined(HIF_PCI) || defined(HIF_SNOC) || defined(HIF_AHB) || \ 27 defined(HIF_IPCI) 28 #include "ce_main.h" 29 #include "ce_api.h" 30 #include "ce_internal.h" 31 #endif 32 #include "htc_services.h" 33 #include "a_types.h" 34 #include "dummy.h" 35 #include "qdf_module.h" 36 37 /** 38 * hif_initialize_default_ops() - initializes default operations values 39 * 40 * bus specific features should assign their dummy implementations here. 41 */ 42 static void hif_initialize_default_ops(struct hif_softc *hif_sc) 43 { 44 struct hif_bus_ops *bus_ops = &hif_sc->bus_ops; 45 46 /* must be filled in by hif_bus_open */ 47 bus_ops->hif_bus_close = NULL; 48 /* dummy implementations */ 49 bus_ops->hif_display_stats = 50 &hif_dummy_display_stats; 51 bus_ops->hif_clear_stats = 52 &hif_dummy_clear_stats; 53 bus_ops->hif_set_bundle_mode = &hif_dummy_set_bundle_mode; 54 bus_ops->hif_bus_reset_resume = &hif_dummy_bus_reset_resume; 55 bus_ops->hif_bus_suspend_noirq = &hif_dummy_bus_suspend_noirq; 56 bus_ops->hif_bus_resume_noirq = &hif_dummy_bus_resume_noirq; 57 bus_ops->hif_bus_early_suspend = &hif_dummy_bus_suspend; 58 bus_ops->hif_bus_late_resume = &hif_dummy_bus_resume; 59 bus_ops->hif_map_ce_to_irq = &hif_dummy_map_ce_to_irq; 60 bus_ops->hif_grp_irq_configure = &hif_dummy_grp_irq_configure; 61 } 62 63 #define NUM_OPS (sizeof(struct hif_bus_ops) / sizeof(void *)) 64 65 /** 66 * hif_verify_basic_ops() - ensure required bus apis are defined 67 * 68 * all bus operations must be defined to avoid crashes 69 * itterate over the structure and ensure all function pointers 70 * are non null. 71 * 72 * Return: QDF_STATUS_SUCCESS if all the operations are defined 73 */ 74 static QDF_STATUS hif_verify_basic_ops(struct hif_softc *hif_sc) 75 { 76 struct hif_bus_ops *bus_ops = &hif_sc->bus_ops; 77 void **ops_array = (void *)bus_ops; 78 QDF_STATUS status = QDF_STATUS_SUCCESS; 79 int i; 80 81 for (i = 0; i < NUM_OPS; i++) { 82 if (!ops_array[i]) { 83 HIF_ERROR("%s: function %d is null", __func__, i); 84 status = QDF_STATUS_E_NOSUPPORT; 85 } 86 } 87 return status; 88 } 89 90 /** 91 * hif_bus_get_context_size - API to return size of the bus specific structure 92 * 93 * Return: sizeof of hif_pci_softc 94 */ 95 int hif_bus_get_context_size(enum qdf_bus_type bus_type) 96 { 97 switch (bus_type) { 98 case QDF_BUS_TYPE_PCI: 99 return hif_pci_get_context_size(); 100 case QDF_BUS_TYPE_IPCI: 101 return hif_ipci_get_context_size(); 102 case QDF_BUS_TYPE_AHB: 103 return hif_ahb_get_context_size(); 104 case QDF_BUS_TYPE_SNOC: 105 return hif_snoc_get_context_size(); 106 case QDF_BUS_TYPE_SDIO: 107 return hif_sdio_get_context_size(); 108 case QDF_BUS_TYPE_USB: 109 return hif_usb_get_context_size(); 110 default: 111 return 0; 112 } 113 } 114 115 /** 116 * hif_bus_open() - initialize the bus_ops and call the bus specific open 117 * hif_sc: hif_context 118 * bus_type: type of bus being enumerated 119 * 120 * Return: QDF_STATUS_SUCCESS or error 121 */ 122 QDF_STATUS hif_bus_open(struct hif_softc *hif_sc, 123 enum qdf_bus_type bus_type) 124 { 125 QDF_STATUS status = QDF_STATUS_E_INVAL; 126 127 hif_initialize_default_ops(hif_sc); 128 129 switch (bus_type) { 130 case QDF_BUS_TYPE_PCI: 131 status = hif_initialize_pci_ops(hif_sc); 132 break; 133 case QDF_BUS_TYPE_IPCI: 134 status = hif_initialize_ipci_ops(hif_sc); 135 break; 136 case QDF_BUS_TYPE_SNOC: 137 status = hif_initialize_snoc_ops(&hif_sc->bus_ops); 138 break; 139 case QDF_BUS_TYPE_AHB: 140 status = hif_initialize_ahb_ops(&hif_sc->bus_ops); 141 break; 142 case QDF_BUS_TYPE_SDIO: 143 status = hif_initialize_sdio_ops(hif_sc); 144 break; 145 case QDF_BUS_TYPE_USB: 146 status = hif_initialize_usb_ops(&hif_sc->bus_ops); 147 break; 148 default: 149 status = QDF_STATUS_E_NOSUPPORT; 150 break; 151 } 152 153 if (status != QDF_STATUS_SUCCESS) { 154 HIF_ERROR("%s: %d not supported", __func__, bus_type); 155 return status; 156 } 157 158 status = hif_verify_basic_ops(hif_sc); 159 if (status != QDF_STATUS_SUCCESS) 160 return status; 161 162 return hif_sc->bus_ops.hif_bus_open(hif_sc, bus_type); 163 } 164 165 /** 166 * hif_bus_close() - close the bus 167 * @hif_sc: hif_context 168 */ 169 void hif_bus_close(struct hif_softc *hif_sc) 170 { 171 hif_sc->bus_ops.hif_bus_close(hif_sc); 172 } 173 174 /** 175 * hif_bus_prevent_linkdown() - prevent linkdown 176 * @hif_ctx: hif context 177 * @flag: true = keep bus alive false = let bus go to sleep 178 * 179 * Keeps the bus awake durring suspend. 180 */ 181 void hif_bus_prevent_linkdown(struct hif_softc *hif_sc, bool flag) 182 { 183 hif_sc->bus_ops.hif_bus_prevent_linkdown(hif_sc, flag); 184 } 185 186 187 void hif_reset_soc(struct hif_opaque_softc *hif_ctx) 188 { 189 struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_ctx); 190 191 hif_sc->bus_ops.hif_reset_soc(hif_sc); 192 } 193 194 int hif_bus_early_suspend(struct hif_opaque_softc *hif_ctx) 195 { 196 struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_ctx); 197 198 return hif_sc->bus_ops.hif_bus_early_suspend(hif_sc); 199 } 200 201 int hif_bus_late_resume(struct hif_opaque_softc *hif_ctx) 202 { 203 struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_ctx); 204 205 return hif_sc->bus_ops.hif_bus_late_resume(hif_sc); 206 } 207 208 int hif_bus_suspend(struct hif_opaque_softc *hif_ctx) 209 { 210 struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_ctx); 211 212 return hif_sc->bus_ops.hif_bus_suspend(hif_sc); 213 } 214 215 int hif_bus_resume(struct hif_opaque_softc *hif_ctx) 216 { 217 struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_ctx); 218 219 return hif_sc->bus_ops.hif_bus_resume(hif_sc); 220 } 221 222 int hif_bus_suspend_noirq(struct hif_opaque_softc *hif_ctx) 223 { 224 struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_ctx); 225 226 return hif_sc->bus_ops.hif_bus_suspend_noirq(hif_sc); 227 } 228 229 int hif_bus_resume_noirq(struct hif_opaque_softc *hif_ctx) 230 { 231 struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_ctx); 232 233 return hif_sc->bus_ops.hif_bus_resume_noirq(hif_sc); 234 } 235 236 int hif_target_sleep_state_adjust(struct hif_softc *hif_sc, 237 bool sleep_ok, bool wait_for_it) 238 { 239 return hif_sc->bus_ops.hif_target_sleep_state_adjust(hif_sc, 240 sleep_ok, wait_for_it); 241 } 242 qdf_export_symbol(hif_target_sleep_state_adjust); 243 244 void hif_disable_isr(struct hif_opaque_softc *hif_hdl) 245 { 246 struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_hdl); 247 248 hif_sc->bus_ops.hif_disable_isr(hif_sc); 249 } 250 251 void hif_nointrs(struct hif_softc *hif_sc) 252 { 253 hif_sc->bus_ops.hif_nointrs(hif_sc); 254 } 255 256 QDF_STATUS hif_enable_bus(struct hif_softc *hif_sc, struct device *dev, 257 void *bdev, const struct hif_bus_id *bid, 258 enum hif_enable_type type) 259 { 260 return hif_sc->bus_ops.hif_enable_bus(hif_sc, dev, bdev, bid, type); 261 } 262 263 void hif_disable_bus(struct hif_softc *hif_sc) 264 { 265 hif_sc->bus_ops.hif_disable_bus(hif_sc); 266 } 267 268 int hif_bus_configure(struct hif_softc *hif_sc) 269 { 270 return hif_sc->bus_ops.hif_bus_configure(hif_sc); 271 } 272 273 QDF_STATUS hif_get_config_item(struct hif_opaque_softc *hif_ctx, 274 int opcode, void *config, uint32_t config_len) 275 { 276 struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_ctx); 277 278 return hif_sc->bus_ops.hif_get_config_item(hif_sc, opcode, config, 279 config_len); 280 } 281 282 void hif_set_mailbox_swap(struct hif_opaque_softc *hif_ctx) 283 { 284 struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_ctx); 285 286 hif_sc->bus_ops.hif_set_mailbox_swap(hif_sc); 287 } 288 289 void hif_claim_device(struct hif_opaque_softc *hif_ctx) 290 { 291 struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_ctx); 292 293 hif_sc->bus_ops.hif_claim_device(hif_sc); 294 } 295 296 void hif_shutdown_device(struct hif_opaque_softc *hif_ctx) 297 { 298 struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_ctx); 299 300 hif_sc->bus_ops.hif_shutdown_device(hif_sc); 301 } 302 303 void hif_stop(struct hif_opaque_softc *hif_ctx) 304 { 305 struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_ctx); 306 307 hif_sc->bus_ops.hif_stop(hif_sc); 308 } 309 310 void hif_cancel_deferred_target_sleep(struct hif_softc *hif_sc) 311 { 312 return hif_sc->bus_ops.hif_cancel_deferred_target_sleep(hif_sc); 313 } 314 315 void hif_irq_enable(struct hif_softc *hif_sc, int irq_id) 316 { 317 hif_sc->bus_ops.hif_irq_enable(hif_sc, irq_id); 318 } 319 qdf_export_symbol(hif_irq_enable); 320 321 void hif_irq_disable(struct hif_softc *hif_sc, int irq_id) 322 { 323 hif_sc->bus_ops.hif_irq_disable(hif_sc, irq_id); 324 } 325 326 int hif_grp_irq_configure(struct hif_softc *hif_sc, 327 struct hif_exec_context *hif_exec) 328 { 329 return hif_sc->bus_ops.hif_grp_irq_configure(hif_sc, hif_exec); 330 } 331 332 int hif_dump_registers(struct hif_opaque_softc *hif_hdl) 333 { 334 struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_hdl); 335 336 return hif_sc->bus_ops.hif_dump_registers(hif_sc); 337 } 338 339 void hif_dump_target_memory(struct hif_opaque_softc *hif_hdl, 340 void *ramdump_base, 341 uint32_t address, uint32_t size) 342 { 343 struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_hdl); 344 345 hif_sc->bus_ops.hif_dump_target_memory(hif_sc, ramdump_base, 346 address, size); 347 } 348 349 void hif_ipa_get_ce_resource(struct hif_opaque_softc *hif_hdl, 350 qdf_shared_mem_t **ce_sr, 351 uint32_t *ce_sr_ring_size, 352 qdf_dma_addr_t *ce_reg_paddr) 353 { 354 struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_hdl); 355 356 hif_sc->bus_ops.hif_ipa_get_ce_resource(hif_sc, ce_sr, 357 ce_sr_ring_size, ce_reg_paddr); 358 } 359 360 void hif_mask_interrupt_call(struct hif_opaque_softc *hif_hdl) 361 { 362 struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_hdl); 363 364 hif_sc->bus_ops.hif_mask_interrupt_call(hif_sc); 365 } 366 367 void hif_display_bus_stats(struct hif_opaque_softc *scn) 368 { 369 struct hif_softc *hif_sc = HIF_GET_SOFTC(scn); 370 371 hif_sc->bus_ops.hif_display_stats(hif_sc); 372 } 373 374 void hif_clear_bus_stats(struct hif_opaque_softc *scn) 375 { 376 struct hif_softc *hif_sc = HIF_GET_SOFTC(scn); 377 378 hif_sc->bus_ops.hif_clear_stats(hif_sc); 379 } 380 381 /** 382 * hif_enable_power_management() - enable power management after driver load 383 * @hif_hdl: opaque pointer to the hif context 384 * is_packet_log_enabled: true if packet log is enabled 385 * 386 * Driver load and firmware download are done in a high performance mode. 387 * Enable power management after the driver is loaded. 388 * packet log can require fewer power management features to be enabled. 389 */ 390 void hif_enable_power_management(struct hif_opaque_softc *hif_hdl, 391 bool is_packet_log_enabled) 392 { 393 struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_hdl); 394 395 hif_sc->bus_ops.hif_enable_power_management(hif_sc, 396 is_packet_log_enabled); 397 } 398 399 /** 400 * hif_disable_power_management() - reset the bus power management 401 * @hif_hdl: opaque pointer to the hif context 402 * 403 * return the power management of the bus to its default state. 404 * This isn't necessarily a complete reversal of its counterpart. 405 * This should be called when unloading the driver. 406 */ 407 void hif_disable_power_management(struct hif_opaque_softc *hif_hdl) 408 { 409 struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_hdl); 410 411 hif_sc->bus_ops.hif_disable_power_management(hif_sc); 412 } 413 414 /** 415 * hif_set_bundle_mode() - enable bundling and set default rx bundle cnt 416 * @scn: pointer to hif_opaque_softc structure 417 * @enabled: flag to enable/disable bundling 418 * @rx_bundle_cnt: bundle count to be used for RX 419 * 420 * Return: none 421 */ 422 void hif_set_bundle_mode(struct hif_opaque_softc *scn, bool enabled, 423 int rx_bundle_cnt) 424 { 425 struct hif_softc *hif_sc = HIF_GET_SOFTC(scn); 426 427 hif_sc->bus_ops.hif_set_bundle_mode(hif_sc, enabled, rx_bundle_cnt); 428 } 429 430 /** 431 * hif_bus_reset_resume() - resume the bus after reset 432 * @scn: struct hif_opaque_softc 433 * 434 * This function is called to tell the driver that USB device has been resumed 435 * and it has also been reset. The driver should redo any necessary 436 * initialization. This function resets WLAN SOC. 437 * 438 * Return: int 0 for success, non zero for failure 439 */ 440 int hif_bus_reset_resume(struct hif_opaque_softc *scn) 441 { 442 struct hif_softc *hif_sc = HIF_GET_SOFTC(scn); 443 444 return hif_sc->bus_ops.hif_bus_reset_resume(hif_sc); 445 } 446 447 int hif_apps_irqs_disable(struct hif_opaque_softc *hif_ctx) 448 { 449 struct hif_softc *scn; 450 int i; 451 452 QDF_BUG(hif_ctx); 453 scn = HIF_GET_SOFTC(hif_ctx); 454 if (!scn) 455 return -EINVAL; 456 457 /* if the wake_irq is shared, don't disable it twice */ 458 disable_irq(scn->wake_irq); 459 for (i = 0; i < scn->ce_count; ++i) { 460 int irq = scn->bus_ops.hif_map_ce_to_irq(scn, i); 461 462 if (irq != scn->wake_irq) 463 disable_irq(irq); 464 } 465 466 return 0; 467 } 468 469 int hif_apps_irqs_enable(struct hif_opaque_softc *hif_ctx) 470 { 471 struct hif_softc *scn; 472 int i; 473 474 QDF_BUG(hif_ctx); 475 scn = HIF_GET_SOFTC(hif_ctx); 476 if (!scn) 477 return -EINVAL; 478 479 /* if the wake_irq is shared, don't enable it twice */ 480 enable_irq(scn->wake_irq); 481 for (i = 0; i < scn->ce_count; ++i) { 482 int irq = scn->bus_ops.hif_map_ce_to_irq(scn, i); 483 484 if (irq != scn->wake_irq) 485 enable_irq(irq); 486 } 487 488 return 0; 489 } 490 491 int hif_apps_wake_irq_disable(struct hif_opaque_softc *hif_ctx) 492 { 493 struct hif_softc *scn; 494 495 QDF_BUG(hif_ctx); 496 scn = HIF_GET_SOFTC(hif_ctx); 497 if (!scn) 498 return -EINVAL; 499 500 disable_irq(scn->wake_irq); 501 502 return 0; 503 } 504 505 int hif_apps_wake_irq_enable(struct hif_opaque_softc *hif_ctx) 506 { 507 struct hif_softc *scn; 508 509 QDF_BUG(hif_ctx); 510 scn = HIF_GET_SOFTC(hif_ctx); 511 if (!scn) 512 return -EINVAL; 513 514 enable_irq(scn->wake_irq); 515 516 return 0; 517 } 518 519 #ifdef WLAN_FEATURE_BMI 520 bool hif_needs_bmi(struct hif_opaque_softc *scn) 521 { 522 struct hif_softc *hif_sc = HIF_GET_SOFTC(scn); 523 524 return hif_sc->bus_ops.hif_needs_bmi(hif_sc); 525 } 526 qdf_export_symbol(hif_needs_bmi); 527 #endif /* WLAN_FEATURE_BMI */ 528