18c2ecf20Sopenharmony_ci/** 28c2ecf20Sopenharmony_ci * Marvell BT-over-SDIO driver: SDIO interface related functions. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2009, Marvell International Ltd. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This software file (the "File") is distributed by Marvell International 78c2ecf20Sopenharmony_ci * Ltd. under the terms of the GNU General Public License Version 2, June 1991 88c2ecf20Sopenharmony_ci * (the "License"). You may use, redistribute and/or modify this File in 98c2ecf20Sopenharmony_ci * accordance with the terms and conditions of the License, a copy of which 108c2ecf20Sopenharmony_ci * is available by writing to the Free Software Foundation, Inc., 118c2ecf20Sopenharmony_ci * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the 128c2ecf20Sopenharmony_ci * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE 168c2ecf20Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE 178c2ecf20Sopenharmony_ci * ARE EXPRESSLY DISCLAIMED. The License provides additional details about 188c2ecf20Sopenharmony_ci * this warranty disclaimer. 198c2ecf20Sopenharmony_ci **/ 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/firmware.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/suspend.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <linux/mmc/sdio_ids.h> 268c2ecf20Sopenharmony_ci#include <linux/mmc/sdio_func.h> 278c2ecf20Sopenharmony_ci#include <linux/module.h> 288c2ecf20Sopenharmony_ci#include <linux/devcoredump.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <net/bluetooth/bluetooth.h> 318c2ecf20Sopenharmony_ci#include <net/bluetooth/hci_core.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include "btmrvl_drv.h" 348c2ecf20Sopenharmony_ci#include "btmrvl_sdio.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define VERSION "1.0" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic struct memory_type_mapping mem_type_mapping_tbl[] = { 398c2ecf20Sopenharmony_ci {"ITCM", NULL, 0, 0xF0}, 408c2ecf20Sopenharmony_ci {"DTCM", NULL, 0, 0xF1}, 418c2ecf20Sopenharmony_ci {"SQRAM", NULL, 0, 0xF2}, 428c2ecf20Sopenharmony_ci {"APU", NULL, 0, 0xF3}, 438c2ecf20Sopenharmony_ci {"CIU", NULL, 0, 0xF4}, 448c2ecf20Sopenharmony_ci {"ICU", NULL, 0, 0xF5}, 458c2ecf20Sopenharmony_ci {"MAC", NULL, 0, 0xF6}, 468c2ecf20Sopenharmony_ci {"EXT7", NULL, 0, 0xF7}, 478c2ecf20Sopenharmony_ci {"EXT8", NULL, 0, 0xF8}, 488c2ecf20Sopenharmony_ci {"EXT9", NULL, 0, 0xF9}, 498c2ecf20Sopenharmony_ci {"EXT10", NULL, 0, 0xFA}, 508c2ecf20Sopenharmony_ci {"EXT11", NULL, 0, 0xFB}, 518c2ecf20Sopenharmony_ci {"EXT12", NULL, 0, 0xFC}, 528c2ecf20Sopenharmony_ci {"EXT13", NULL, 0, 0xFD}, 538c2ecf20Sopenharmony_ci {"EXTLAST", NULL, 0, 0xFE}, 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic const struct of_device_id btmrvl_sdio_of_match_table[] = { 578c2ecf20Sopenharmony_ci { .compatible = "marvell,sd8897-bt" }, 588c2ecf20Sopenharmony_ci { .compatible = "marvell,sd8997-bt" }, 598c2ecf20Sopenharmony_ci { } 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic irqreturn_t btmrvl_wake_irq_bt(int irq, void *priv) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct btmrvl_sdio_card *card = priv; 658c2ecf20Sopenharmony_ci struct device *dev = &card->func->dev; 668c2ecf20Sopenharmony_ci struct btmrvl_plt_wake_cfg *cfg = card->plt_wake_cfg; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci dev_info(dev, "wake by bt\n"); 698c2ecf20Sopenharmony_ci cfg->wake_by_bt = true; 708c2ecf20Sopenharmony_ci disable_irq_nosync(irq); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci pm_wakeup_event(dev, 0); 738c2ecf20Sopenharmony_ci pm_system_wakeup(); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci return IRQ_HANDLED; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* This function parses device tree node using mmc subnode devicetree API. 798c2ecf20Sopenharmony_ci * The device node is saved in card->plt_of_node. 808c2ecf20Sopenharmony_ci * If the device tree node exists and includes interrupts attributes, this 818c2ecf20Sopenharmony_ci * function will request platform specific wakeup interrupt. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_cistatic int btmrvl_sdio_probe_of(struct device *dev, 848c2ecf20Sopenharmony_ci struct btmrvl_sdio_card *card) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct btmrvl_plt_wake_cfg *cfg; 878c2ecf20Sopenharmony_ci int ret; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (!dev->of_node || 908c2ecf20Sopenharmony_ci !of_match_node(btmrvl_sdio_of_match_table, dev->of_node)) { 918c2ecf20Sopenharmony_ci dev_info(dev, "sdio device tree data not available\n"); 928c2ecf20Sopenharmony_ci return -1; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci card->plt_of_node = dev->of_node; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci card->plt_wake_cfg = devm_kzalloc(dev, sizeof(*card->plt_wake_cfg), 988c2ecf20Sopenharmony_ci GFP_KERNEL); 998c2ecf20Sopenharmony_ci cfg = card->plt_wake_cfg; 1008c2ecf20Sopenharmony_ci if (cfg && card->plt_of_node) { 1018c2ecf20Sopenharmony_ci cfg->irq_bt = irq_of_parse_and_map(card->plt_of_node, 0); 1028c2ecf20Sopenharmony_ci if (!cfg->irq_bt) { 1038c2ecf20Sopenharmony_ci dev_err(dev, "fail to parse irq_bt from device tree\n"); 1048c2ecf20Sopenharmony_ci cfg->irq_bt = -1; 1058c2ecf20Sopenharmony_ci } else { 1068c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, cfg->irq_bt, 1078c2ecf20Sopenharmony_ci btmrvl_wake_irq_bt, 1088c2ecf20Sopenharmony_ci 0, "bt_wake", card); 1098c2ecf20Sopenharmony_ci if (ret) { 1108c2ecf20Sopenharmony_ci dev_err(dev, 1118c2ecf20Sopenharmony_ci "Failed to request irq_bt %d (%d)\n", 1128c2ecf20Sopenharmony_ci cfg->irq_bt, ret); 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* Configure wakeup (enabled by default) */ 1168c2ecf20Sopenharmony_ci device_init_wakeup(dev, true); 1178c2ecf20Sopenharmony_ci disable_irq(cfg->irq_bt); 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* The btmrvl_sdio_remove() callback function is called 1258c2ecf20Sopenharmony_ci * when user removes this module from kernel space or ejects 1268c2ecf20Sopenharmony_ci * the card from the slot. The driver handles these 2 cases 1278c2ecf20Sopenharmony_ci * differently. 1288c2ecf20Sopenharmony_ci * If the user is removing the module, a MODULE_SHUTDOWN_REQ 1298c2ecf20Sopenharmony_ci * command is sent to firmware and interrupt will be disabled. 1308c2ecf20Sopenharmony_ci * If the card is removed, there is no need to send command 1318c2ecf20Sopenharmony_ci * or disable interrupt. 1328c2ecf20Sopenharmony_ci * 1338c2ecf20Sopenharmony_ci * The variable 'user_rmmod' is used to distinguish these two 1348c2ecf20Sopenharmony_ci * scenarios. This flag is initialized as FALSE in case the card 1358c2ecf20Sopenharmony_ci * is removed, and will be set to TRUE for module removal when 1368c2ecf20Sopenharmony_ci * module_exit function is called. 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_cistatic u8 user_rmmod; 1398c2ecf20Sopenharmony_cistatic u8 sdio_ireg; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic const struct btmrvl_sdio_card_reg btmrvl_reg_8688 = { 1428c2ecf20Sopenharmony_ci .cfg = 0x03, 1438c2ecf20Sopenharmony_ci .host_int_mask = 0x04, 1448c2ecf20Sopenharmony_ci .host_intstatus = 0x05, 1458c2ecf20Sopenharmony_ci .card_status = 0x20, 1468c2ecf20Sopenharmony_ci .sq_read_base_addr_a0 = 0x10, 1478c2ecf20Sopenharmony_ci .sq_read_base_addr_a1 = 0x11, 1488c2ecf20Sopenharmony_ci .card_fw_status0 = 0x40, 1498c2ecf20Sopenharmony_ci .card_fw_status1 = 0x41, 1508c2ecf20Sopenharmony_ci .card_rx_len = 0x42, 1518c2ecf20Sopenharmony_ci .card_rx_unit = 0x43, 1528c2ecf20Sopenharmony_ci .io_port_0 = 0x00, 1538c2ecf20Sopenharmony_ci .io_port_1 = 0x01, 1548c2ecf20Sopenharmony_ci .io_port_2 = 0x02, 1558c2ecf20Sopenharmony_ci .int_read_to_clear = false, 1568c2ecf20Sopenharmony_ci}; 1578c2ecf20Sopenharmony_cistatic const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = { 1588c2ecf20Sopenharmony_ci .cfg = 0x00, 1598c2ecf20Sopenharmony_ci .host_int_mask = 0x02, 1608c2ecf20Sopenharmony_ci .host_intstatus = 0x03, 1618c2ecf20Sopenharmony_ci .card_status = 0x30, 1628c2ecf20Sopenharmony_ci .sq_read_base_addr_a0 = 0x40, 1638c2ecf20Sopenharmony_ci .sq_read_base_addr_a1 = 0x41, 1648c2ecf20Sopenharmony_ci .card_revision = 0x5c, 1658c2ecf20Sopenharmony_ci .card_fw_status0 = 0x60, 1668c2ecf20Sopenharmony_ci .card_fw_status1 = 0x61, 1678c2ecf20Sopenharmony_ci .card_rx_len = 0x62, 1688c2ecf20Sopenharmony_ci .card_rx_unit = 0x63, 1698c2ecf20Sopenharmony_ci .io_port_0 = 0x78, 1708c2ecf20Sopenharmony_ci .io_port_1 = 0x79, 1718c2ecf20Sopenharmony_ci .io_port_2 = 0x7a, 1728c2ecf20Sopenharmony_ci .int_read_to_clear = false, 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic const struct btmrvl_sdio_card_reg btmrvl_reg_8887 = { 1768c2ecf20Sopenharmony_ci .cfg = 0x00, 1778c2ecf20Sopenharmony_ci .host_int_mask = 0x08, 1788c2ecf20Sopenharmony_ci .host_intstatus = 0x0C, 1798c2ecf20Sopenharmony_ci .card_status = 0x5C, 1808c2ecf20Sopenharmony_ci .sq_read_base_addr_a0 = 0x6C, 1818c2ecf20Sopenharmony_ci .sq_read_base_addr_a1 = 0x6D, 1828c2ecf20Sopenharmony_ci .card_revision = 0xC8, 1838c2ecf20Sopenharmony_ci .card_fw_status0 = 0x88, 1848c2ecf20Sopenharmony_ci .card_fw_status1 = 0x89, 1858c2ecf20Sopenharmony_ci .card_rx_len = 0x8A, 1868c2ecf20Sopenharmony_ci .card_rx_unit = 0x8B, 1878c2ecf20Sopenharmony_ci .io_port_0 = 0xE4, 1888c2ecf20Sopenharmony_ci .io_port_1 = 0xE5, 1898c2ecf20Sopenharmony_ci .io_port_2 = 0xE6, 1908c2ecf20Sopenharmony_ci .int_read_to_clear = true, 1918c2ecf20Sopenharmony_ci .host_int_rsr = 0x04, 1928c2ecf20Sopenharmony_ci .card_misc_cfg = 0xD8, 1938c2ecf20Sopenharmony_ci}; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic const struct btmrvl_sdio_card_reg btmrvl_reg_8897 = { 1968c2ecf20Sopenharmony_ci .cfg = 0x00, 1978c2ecf20Sopenharmony_ci .host_int_mask = 0x02, 1988c2ecf20Sopenharmony_ci .host_intstatus = 0x03, 1998c2ecf20Sopenharmony_ci .card_status = 0x50, 2008c2ecf20Sopenharmony_ci .sq_read_base_addr_a0 = 0x60, 2018c2ecf20Sopenharmony_ci .sq_read_base_addr_a1 = 0x61, 2028c2ecf20Sopenharmony_ci .card_revision = 0xbc, 2038c2ecf20Sopenharmony_ci .card_fw_status0 = 0xc0, 2048c2ecf20Sopenharmony_ci .card_fw_status1 = 0xc1, 2058c2ecf20Sopenharmony_ci .card_rx_len = 0xc2, 2068c2ecf20Sopenharmony_ci .card_rx_unit = 0xc3, 2078c2ecf20Sopenharmony_ci .io_port_0 = 0xd8, 2088c2ecf20Sopenharmony_ci .io_port_1 = 0xd9, 2098c2ecf20Sopenharmony_ci .io_port_2 = 0xda, 2108c2ecf20Sopenharmony_ci .int_read_to_clear = true, 2118c2ecf20Sopenharmony_ci .host_int_rsr = 0x01, 2128c2ecf20Sopenharmony_ci .card_misc_cfg = 0xcc, 2138c2ecf20Sopenharmony_ci .fw_dump_ctrl = 0xe2, 2148c2ecf20Sopenharmony_ci .fw_dump_start = 0xe3, 2158c2ecf20Sopenharmony_ci .fw_dump_end = 0xea, 2168c2ecf20Sopenharmony_ci}; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic const struct btmrvl_sdio_card_reg btmrvl_reg_89xx = { 2198c2ecf20Sopenharmony_ci .cfg = 0x00, 2208c2ecf20Sopenharmony_ci .host_int_mask = 0x08, 2218c2ecf20Sopenharmony_ci .host_intstatus = 0x0c, 2228c2ecf20Sopenharmony_ci .card_status = 0x5c, 2238c2ecf20Sopenharmony_ci .sq_read_base_addr_a0 = 0xf8, 2248c2ecf20Sopenharmony_ci .sq_read_base_addr_a1 = 0xf9, 2258c2ecf20Sopenharmony_ci .card_revision = 0xc8, 2268c2ecf20Sopenharmony_ci .card_fw_status0 = 0xe8, 2278c2ecf20Sopenharmony_ci .card_fw_status1 = 0xe9, 2288c2ecf20Sopenharmony_ci .card_rx_len = 0xea, 2298c2ecf20Sopenharmony_ci .card_rx_unit = 0xeb, 2308c2ecf20Sopenharmony_ci .io_port_0 = 0xe4, 2318c2ecf20Sopenharmony_ci .io_port_1 = 0xe5, 2328c2ecf20Sopenharmony_ci .io_port_2 = 0xe6, 2338c2ecf20Sopenharmony_ci .int_read_to_clear = true, 2348c2ecf20Sopenharmony_ci .host_int_rsr = 0x04, 2358c2ecf20Sopenharmony_ci .card_misc_cfg = 0xd8, 2368c2ecf20Sopenharmony_ci .fw_dump_ctrl = 0xf0, 2378c2ecf20Sopenharmony_ci .fw_dump_start = 0xf1, 2388c2ecf20Sopenharmony_ci .fw_dump_end = 0xf8, 2398c2ecf20Sopenharmony_ci}; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = { 2428c2ecf20Sopenharmony_ci .helper = "mrvl/sd8688_helper.bin", 2438c2ecf20Sopenharmony_ci .firmware = "mrvl/sd8688.bin", 2448c2ecf20Sopenharmony_ci .reg = &btmrvl_reg_8688, 2458c2ecf20Sopenharmony_ci .support_pscan_win_report = false, 2468c2ecf20Sopenharmony_ci .sd_blksz_fw_dl = 64, 2478c2ecf20Sopenharmony_ci .supports_fw_dump = false, 2488c2ecf20Sopenharmony_ci}; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = { 2518c2ecf20Sopenharmony_ci .helper = NULL, 2528c2ecf20Sopenharmony_ci .firmware = "mrvl/sd8787_uapsta.bin", 2538c2ecf20Sopenharmony_ci .reg = &btmrvl_reg_87xx, 2548c2ecf20Sopenharmony_ci .support_pscan_win_report = false, 2558c2ecf20Sopenharmony_ci .sd_blksz_fw_dl = 256, 2568c2ecf20Sopenharmony_ci .supports_fw_dump = false, 2578c2ecf20Sopenharmony_ci}; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = { 2608c2ecf20Sopenharmony_ci .helper = NULL, 2618c2ecf20Sopenharmony_ci .firmware = "mrvl/sd8797_uapsta.bin", 2628c2ecf20Sopenharmony_ci .reg = &btmrvl_reg_87xx, 2638c2ecf20Sopenharmony_ci .support_pscan_win_report = false, 2648c2ecf20Sopenharmony_ci .sd_blksz_fw_dl = 256, 2658c2ecf20Sopenharmony_ci .supports_fw_dump = false, 2668c2ecf20Sopenharmony_ci}; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic const struct btmrvl_sdio_device btmrvl_sdio_sd8887 = { 2698c2ecf20Sopenharmony_ci .helper = NULL, 2708c2ecf20Sopenharmony_ci .firmware = "mrvl/sd8887_uapsta.bin", 2718c2ecf20Sopenharmony_ci .reg = &btmrvl_reg_8887, 2728c2ecf20Sopenharmony_ci .support_pscan_win_report = true, 2738c2ecf20Sopenharmony_ci .sd_blksz_fw_dl = 256, 2748c2ecf20Sopenharmony_ci .supports_fw_dump = false, 2758c2ecf20Sopenharmony_ci}; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = { 2788c2ecf20Sopenharmony_ci .helper = NULL, 2798c2ecf20Sopenharmony_ci .firmware = "mrvl/sd8897_uapsta.bin", 2808c2ecf20Sopenharmony_ci .reg = &btmrvl_reg_8897, 2818c2ecf20Sopenharmony_ci .support_pscan_win_report = true, 2828c2ecf20Sopenharmony_ci .sd_blksz_fw_dl = 256, 2838c2ecf20Sopenharmony_ci .supports_fw_dump = true, 2848c2ecf20Sopenharmony_ci}; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic const struct btmrvl_sdio_device btmrvl_sdio_sd8977 = { 2878c2ecf20Sopenharmony_ci .helper = NULL, 2888c2ecf20Sopenharmony_ci .firmware = "mrvl/sdsd8977_combo_v2.bin", 2898c2ecf20Sopenharmony_ci .reg = &btmrvl_reg_89xx, 2908c2ecf20Sopenharmony_ci .support_pscan_win_report = true, 2918c2ecf20Sopenharmony_ci .sd_blksz_fw_dl = 256, 2928c2ecf20Sopenharmony_ci .supports_fw_dump = true, 2938c2ecf20Sopenharmony_ci}; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic const struct btmrvl_sdio_device btmrvl_sdio_sd8987 = { 2968c2ecf20Sopenharmony_ci .helper = NULL, 2978c2ecf20Sopenharmony_ci .firmware = "mrvl/sd8987_uapsta.bin", 2988c2ecf20Sopenharmony_ci .reg = &btmrvl_reg_89xx, 2998c2ecf20Sopenharmony_ci .support_pscan_win_report = true, 3008c2ecf20Sopenharmony_ci .sd_blksz_fw_dl = 256, 3018c2ecf20Sopenharmony_ci .supports_fw_dump = true, 3028c2ecf20Sopenharmony_ci}; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic const struct btmrvl_sdio_device btmrvl_sdio_sd8997 = { 3058c2ecf20Sopenharmony_ci .helper = NULL, 3068c2ecf20Sopenharmony_ci .firmware = "mrvl/sdsd8997_combo_v4.bin", 3078c2ecf20Sopenharmony_ci .reg = &btmrvl_reg_89xx, 3088c2ecf20Sopenharmony_ci .support_pscan_win_report = true, 3098c2ecf20Sopenharmony_ci .sd_blksz_fw_dl = 256, 3108c2ecf20Sopenharmony_ci .supports_fw_dump = true, 3118c2ecf20Sopenharmony_ci}; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic const struct sdio_device_id btmrvl_sdio_ids[] = { 3148c2ecf20Sopenharmony_ci /* Marvell SD8688 Bluetooth device */ 3158c2ecf20Sopenharmony_ci { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8688_BT), 3168c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&btmrvl_sdio_sd8688 }, 3178c2ecf20Sopenharmony_ci /* Marvell SD8787 Bluetooth device */ 3188c2ecf20Sopenharmony_ci { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787_BT), 3198c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&btmrvl_sdio_sd8787 }, 3208c2ecf20Sopenharmony_ci /* Marvell SD8787 Bluetooth AMP device */ 3218c2ecf20Sopenharmony_ci { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787_BT_AMP), 3228c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&btmrvl_sdio_sd8787 }, 3238c2ecf20Sopenharmony_ci /* Marvell SD8797 Bluetooth device */ 3248c2ecf20Sopenharmony_ci { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_BT), 3258c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&btmrvl_sdio_sd8797 }, 3268c2ecf20Sopenharmony_ci /* Marvell SD8887 Bluetooth device */ 3278c2ecf20Sopenharmony_ci { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887_BT), 3288c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&btmrvl_sdio_sd8887 }, 3298c2ecf20Sopenharmony_ci /* Marvell SD8897 Bluetooth device */ 3308c2ecf20Sopenharmony_ci { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897_BT), 3318c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&btmrvl_sdio_sd8897 }, 3328c2ecf20Sopenharmony_ci /* Marvell SD8977 Bluetooth device */ 3338c2ecf20Sopenharmony_ci { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8977_BT), 3348c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&btmrvl_sdio_sd8977 }, 3358c2ecf20Sopenharmony_ci /* Marvell SD8987 Bluetooth device */ 3368c2ecf20Sopenharmony_ci { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8987_BT), 3378c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&btmrvl_sdio_sd8987 }, 3388c2ecf20Sopenharmony_ci /* Marvell SD8997 Bluetooth device */ 3398c2ecf20Sopenharmony_ci { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997_BT), 3408c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&btmrvl_sdio_sd8997 }, 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci { } /* Terminating entry */ 3438c2ecf20Sopenharmony_ci}; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(sdio, btmrvl_sdio_ids); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic int btmrvl_sdio_get_rx_unit(struct btmrvl_sdio_card *card) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci u8 reg; 3508c2ecf20Sopenharmony_ci int ret; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci reg = sdio_readb(card->func, card->reg->card_rx_unit, &ret); 3538c2ecf20Sopenharmony_ci if (!ret) 3548c2ecf20Sopenharmony_ci card->rx_unit = reg; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci return ret; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic int btmrvl_sdio_read_fw_status(struct btmrvl_sdio_card *card, u16 *dat) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci u8 fws0, fws1; 3628c2ecf20Sopenharmony_ci int ret; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci *dat = 0; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci fws0 = sdio_readb(card->func, card->reg->card_fw_status0, &ret); 3678c2ecf20Sopenharmony_ci if (ret) 3688c2ecf20Sopenharmony_ci return -EIO; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci fws1 = sdio_readb(card->func, card->reg->card_fw_status1, &ret); 3718c2ecf20Sopenharmony_ci if (ret) 3728c2ecf20Sopenharmony_ci return -EIO; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci *dat = (((u16) fws1) << 8) | fws0; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci return 0; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic int btmrvl_sdio_read_rx_len(struct btmrvl_sdio_card *card, u16 *dat) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci u8 reg; 3828c2ecf20Sopenharmony_ci int ret; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci reg = sdio_readb(card->func, card->reg->card_rx_len, &ret); 3858c2ecf20Sopenharmony_ci if (!ret) 3868c2ecf20Sopenharmony_ci *dat = (u16) reg << card->rx_unit; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci return ret; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic int btmrvl_sdio_enable_host_int_mask(struct btmrvl_sdio_card *card, 3928c2ecf20Sopenharmony_ci u8 mask) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci int ret; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci sdio_writeb(card->func, mask, card->reg->host_int_mask, &ret); 3978c2ecf20Sopenharmony_ci if (ret) { 3988c2ecf20Sopenharmony_ci BT_ERR("Unable to enable the host interrupt!"); 3998c2ecf20Sopenharmony_ci ret = -EIO; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci return ret; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic int btmrvl_sdio_disable_host_int_mask(struct btmrvl_sdio_card *card, 4068c2ecf20Sopenharmony_ci u8 mask) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci u8 host_int_mask; 4098c2ecf20Sopenharmony_ci int ret; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci host_int_mask = sdio_readb(card->func, card->reg->host_int_mask, &ret); 4128c2ecf20Sopenharmony_ci if (ret) 4138c2ecf20Sopenharmony_ci return -EIO; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci host_int_mask &= ~mask; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci sdio_writeb(card->func, host_int_mask, card->reg->host_int_mask, &ret); 4188c2ecf20Sopenharmony_ci if (ret < 0) { 4198c2ecf20Sopenharmony_ci BT_ERR("Unable to disable the host interrupt!"); 4208c2ecf20Sopenharmony_ci return -EIO; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci return 0; 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic int btmrvl_sdio_poll_card_status(struct btmrvl_sdio_card *card, u8 bits) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci unsigned int tries; 4298c2ecf20Sopenharmony_ci u8 status; 4308c2ecf20Sopenharmony_ci int ret; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci for (tries = 0; tries < MAX_POLL_TRIES * 1000; tries++) { 4338c2ecf20Sopenharmony_ci status = sdio_readb(card->func, card->reg->card_status, &ret); 4348c2ecf20Sopenharmony_ci if (ret) 4358c2ecf20Sopenharmony_ci goto failed; 4368c2ecf20Sopenharmony_ci if ((status & bits) == bits) 4378c2ecf20Sopenharmony_ci return ret; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci udelay(1); 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cifailed: 4458c2ecf20Sopenharmony_ci BT_ERR("FAILED! ret=%d", ret); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci return ret; 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic int btmrvl_sdio_verify_fw_download(struct btmrvl_sdio_card *card, 4518c2ecf20Sopenharmony_ci int pollnum) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci u16 firmwarestat; 4548c2ecf20Sopenharmony_ci int tries, ret; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* Wait for firmware to become ready */ 4578c2ecf20Sopenharmony_ci for (tries = 0; tries < pollnum; tries++) { 4588c2ecf20Sopenharmony_ci sdio_claim_host(card->func); 4598c2ecf20Sopenharmony_ci ret = btmrvl_sdio_read_fw_status(card, &firmwarestat); 4608c2ecf20Sopenharmony_ci sdio_release_host(card->func); 4618c2ecf20Sopenharmony_ci if (ret < 0) 4628c2ecf20Sopenharmony_ci continue; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (firmwarestat == FIRMWARE_READY) 4658c2ecf20Sopenharmony_ci return 0; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci msleep(100); 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return -ETIMEDOUT; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic int btmrvl_sdio_download_helper(struct btmrvl_sdio_card *card) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci const struct firmware *fw_helper = NULL; 4768c2ecf20Sopenharmony_ci const u8 *helper = NULL; 4778c2ecf20Sopenharmony_ci int ret; 4788c2ecf20Sopenharmony_ci void *tmphlprbuf = NULL; 4798c2ecf20Sopenharmony_ci int tmphlprbufsz, hlprblknow, helperlen; 4808c2ecf20Sopenharmony_ci u8 *helperbuf; 4818c2ecf20Sopenharmony_ci u32 tx_len; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci ret = request_firmware(&fw_helper, card->helper, 4848c2ecf20Sopenharmony_ci &card->func->dev); 4858c2ecf20Sopenharmony_ci if ((ret < 0) || !fw_helper) { 4868c2ecf20Sopenharmony_ci BT_ERR("request_firmware(helper) failed, error code = %d", 4878c2ecf20Sopenharmony_ci ret); 4888c2ecf20Sopenharmony_ci ret = -ENOENT; 4898c2ecf20Sopenharmony_ci goto done; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci helper = fw_helper->data; 4938c2ecf20Sopenharmony_ci helperlen = fw_helper->size; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci BT_DBG("Downloading helper image (%d bytes), block size %d bytes", 4968c2ecf20Sopenharmony_ci helperlen, SDIO_BLOCK_SIZE); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci tmphlprbufsz = ALIGN_SZ(BTM_UPLD_SIZE, BTSDIO_DMA_ALIGN); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci tmphlprbuf = kzalloc(tmphlprbufsz, GFP_KERNEL); 5018c2ecf20Sopenharmony_ci if (!tmphlprbuf) { 5028c2ecf20Sopenharmony_ci BT_ERR("Unable to allocate buffer for helper." 5038c2ecf20Sopenharmony_ci " Terminating download"); 5048c2ecf20Sopenharmony_ci ret = -ENOMEM; 5058c2ecf20Sopenharmony_ci goto done; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci helperbuf = (u8 *) ALIGN_ADDR(tmphlprbuf, BTSDIO_DMA_ALIGN); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* Perform helper data transfer */ 5118c2ecf20Sopenharmony_ci tx_len = (FIRMWARE_TRANSFER_NBLOCK * SDIO_BLOCK_SIZE) 5128c2ecf20Sopenharmony_ci - SDIO_HEADER_LEN; 5138c2ecf20Sopenharmony_ci hlprblknow = 0; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci do { 5168c2ecf20Sopenharmony_ci ret = btmrvl_sdio_poll_card_status(card, 5178c2ecf20Sopenharmony_ci CARD_IO_READY | DN_LD_CARD_RDY); 5188c2ecf20Sopenharmony_ci if (ret < 0) { 5198c2ecf20Sopenharmony_ci BT_ERR("Helper download poll status timeout @ %d", 5208c2ecf20Sopenharmony_ci hlprblknow); 5218c2ecf20Sopenharmony_ci goto done; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci /* Check if there is more data? */ 5258c2ecf20Sopenharmony_ci if (hlprblknow >= helperlen) 5268c2ecf20Sopenharmony_ci break; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (helperlen - hlprblknow < tx_len) 5298c2ecf20Sopenharmony_ci tx_len = helperlen - hlprblknow; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* Little-endian */ 5328c2ecf20Sopenharmony_ci helperbuf[0] = ((tx_len & 0x000000ff) >> 0); 5338c2ecf20Sopenharmony_ci helperbuf[1] = ((tx_len & 0x0000ff00) >> 8); 5348c2ecf20Sopenharmony_ci helperbuf[2] = ((tx_len & 0x00ff0000) >> 16); 5358c2ecf20Sopenharmony_ci helperbuf[3] = ((tx_len & 0xff000000) >> 24); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci memcpy(&helperbuf[SDIO_HEADER_LEN], &helper[hlprblknow], 5388c2ecf20Sopenharmony_ci tx_len); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci /* Now send the data */ 5418c2ecf20Sopenharmony_ci ret = sdio_writesb(card->func, card->ioport, helperbuf, 5428c2ecf20Sopenharmony_ci FIRMWARE_TRANSFER_NBLOCK * SDIO_BLOCK_SIZE); 5438c2ecf20Sopenharmony_ci if (ret < 0) { 5448c2ecf20Sopenharmony_ci BT_ERR("IO error during helper download @ %d", 5458c2ecf20Sopenharmony_ci hlprblknow); 5468c2ecf20Sopenharmony_ci goto done; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci hlprblknow += tx_len; 5508c2ecf20Sopenharmony_ci } while (true); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci BT_DBG("Transferring helper image EOF block"); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci memset(helperbuf, 0x0, SDIO_BLOCK_SIZE); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci ret = sdio_writesb(card->func, card->ioport, helperbuf, 5578c2ecf20Sopenharmony_ci SDIO_BLOCK_SIZE); 5588c2ecf20Sopenharmony_ci if (ret < 0) { 5598c2ecf20Sopenharmony_ci BT_ERR("IO error in writing helper image EOF block"); 5608c2ecf20Sopenharmony_ci goto done; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci ret = 0; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cidone: 5668c2ecf20Sopenharmony_ci kfree(tmphlprbuf); 5678c2ecf20Sopenharmony_ci release_firmware(fw_helper); 5688c2ecf20Sopenharmony_ci return ret; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic int btmrvl_sdio_download_fw_w_helper(struct btmrvl_sdio_card *card) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci const struct firmware *fw_firmware = NULL; 5748c2ecf20Sopenharmony_ci const u8 *firmware = NULL; 5758c2ecf20Sopenharmony_ci int firmwarelen, tmpfwbufsz, ret; 5768c2ecf20Sopenharmony_ci unsigned int tries, offset; 5778c2ecf20Sopenharmony_ci u8 base0, base1; 5788c2ecf20Sopenharmony_ci void *tmpfwbuf = NULL; 5798c2ecf20Sopenharmony_ci u8 *fwbuf; 5808c2ecf20Sopenharmony_ci u16 len, blksz_dl = card->sd_blksz_fw_dl; 5818c2ecf20Sopenharmony_ci int txlen = 0, tx_blocks = 0, count = 0; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci ret = request_firmware(&fw_firmware, card->firmware, 5848c2ecf20Sopenharmony_ci &card->func->dev); 5858c2ecf20Sopenharmony_ci if ((ret < 0) || !fw_firmware) { 5868c2ecf20Sopenharmony_ci BT_ERR("request_firmware(firmware) failed, error code = %d", 5878c2ecf20Sopenharmony_ci ret); 5888c2ecf20Sopenharmony_ci ret = -ENOENT; 5898c2ecf20Sopenharmony_ci goto done; 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci firmware = fw_firmware->data; 5938c2ecf20Sopenharmony_ci firmwarelen = fw_firmware->size; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci BT_DBG("Downloading FW image (%d bytes)", firmwarelen); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci tmpfwbufsz = ALIGN_SZ(BTM_UPLD_SIZE, BTSDIO_DMA_ALIGN); 5988c2ecf20Sopenharmony_ci tmpfwbuf = kzalloc(tmpfwbufsz, GFP_KERNEL); 5998c2ecf20Sopenharmony_ci if (!tmpfwbuf) { 6008c2ecf20Sopenharmony_ci BT_ERR("Unable to allocate buffer for firmware." 6018c2ecf20Sopenharmony_ci " Terminating download"); 6028c2ecf20Sopenharmony_ci ret = -ENOMEM; 6038c2ecf20Sopenharmony_ci goto done; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci /* Ensure aligned firmware buffer */ 6078c2ecf20Sopenharmony_ci fwbuf = (u8 *) ALIGN_ADDR(tmpfwbuf, BTSDIO_DMA_ALIGN); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci /* Perform firmware data transfer */ 6108c2ecf20Sopenharmony_ci offset = 0; 6118c2ecf20Sopenharmony_ci do { 6128c2ecf20Sopenharmony_ci ret = btmrvl_sdio_poll_card_status(card, 6138c2ecf20Sopenharmony_ci CARD_IO_READY | DN_LD_CARD_RDY); 6148c2ecf20Sopenharmony_ci if (ret < 0) { 6158c2ecf20Sopenharmony_ci BT_ERR("FW download with helper poll status" 6168c2ecf20Sopenharmony_ci " timeout @ %d", offset); 6178c2ecf20Sopenharmony_ci goto done; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci /* Check if there is more data ? */ 6218c2ecf20Sopenharmony_ci if (offset >= firmwarelen) 6228c2ecf20Sopenharmony_ci break; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci for (tries = 0; tries < MAX_POLL_TRIES; tries++) { 6258c2ecf20Sopenharmony_ci base0 = sdio_readb(card->func, 6268c2ecf20Sopenharmony_ci card->reg->sq_read_base_addr_a0, &ret); 6278c2ecf20Sopenharmony_ci if (ret) { 6288c2ecf20Sopenharmony_ci BT_ERR("BASE0 register read failed:" 6298c2ecf20Sopenharmony_ci " base0 = 0x%04X(%d)." 6308c2ecf20Sopenharmony_ci " Terminating download", 6318c2ecf20Sopenharmony_ci base0, base0); 6328c2ecf20Sopenharmony_ci ret = -EIO; 6338c2ecf20Sopenharmony_ci goto done; 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci base1 = sdio_readb(card->func, 6368c2ecf20Sopenharmony_ci card->reg->sq_read_base_addr_a1, &ret); 6378c2ecf20Sopenharmony_ci if (ret) { 6388c2ecf20Sopenharmony_ci BT_ERR("BASE1 register read failed:" 6398c2ecf20Sopenharmony_ci " base1 = 0x%04X(%d)." 6408c2ecf20Sopenharmony_ci " Terminating download", 6418c2ecf20Sopenharmony_ci base1, base1); 6428c2ecf20Sopenharmony_ci ret = -EIO; 6438c2ecf20Sopenharmony_ci goto done; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci len = (((u16) base1) << 8) | base0; 6478c2ecf20Sopenharmony_ci if (len) 6488c2ecf20Sopenharmony_ci break; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci udelay(10); 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci if (!len) 6548c2ecf20Sopenharmony_ci break; 6558c2ecf20Sopenharmony_ci else if (len > BTM_UPLD_SIZE) { 6568c2ecf20Sopenharmony_ci BT_ERR("FW download failure @%d, invalid length %d", 6578c2ecf20Sopenharmony_ci offset, len); 6588c2ecf20Sopenharmony_ci ret = -EINVAL; 6598c2ecf20Sopenharmony_ci goto done; 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci txlen = len; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci if (len & BIT(0)) { 6658c2ecf20Sopenharmony_ci count++; 6668c2ecf20Sopenharmony_ci if (count > MAX_WRITE_IOMEM_RETRY) { 6678c2ecf20Sopenharmony_ci BT_ERR("FW download failure @%d, " 6688c2ecf20Sopenharmony_ci "over max retry count", offset); 6698c2ecf20Sopenharmony_ci ret = -EIO; 6708c2ecf20Sopenharmony_ci goto done; 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci BT_ERR("FW CRC error indicated by the helper: " 6738c2ecf20Sopenharmony_ci "len = 0x%04X, txlen = %d", len, txlen); 6748c2ecf20Sopenharmony_ci len &= ~BIT(0); 6758c2ecf20Sopenharmony_ci /* Set txlen to 0 so as to resend from same offset */ 6768c2ecf20Sopenharmony_ci txlen = 0; 6778c2ecf20Sopenharmony_ci } else { 6788c2ecf20Sopenharmony_ci count = 0; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci /* Last block ? */ 6818c2ecf20Sopenharmony_ci if (firmwarelen - offset < txlen) 6828c2ecf20Sopenharmony_ci txlen = firmwarelen - offset; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci tx_blocks = DIV_ROUND_UP(txlen, blksz_dl); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci memcpy(fwbuf, &firmware[offset], txlen); 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci ret = sdio_writesb(card->func, card->ioport, fwbuf, 6908c2ecf20Sopenharmony_ci tx_blocks * blksz_dl); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci if (ret < 0) { 6938c2ecf20Sopenharmony_ci BT_ERR("FW download, writesb(%d) failed @%d", 6948c2ecf20Sopenharmony_ci count, offset); 6958c2ecf20Sopenharmony_ci sdio_writeb(card->func, HOST_CMD53_FIN, 6968c2ecf20Sopenharmony_ci card->reg->cfg, &ret); 6978c2ecf20Sopenharmony_ci if (ret) 6988c2ecf20Sopenharmony_ci BT_ERR("writeb failed (CFG)"); 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci offset += txlen; 7028c2ecf20Sopenharmony_ci } while (true); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci BT_INFO("FW download over, size %d bytes", offset); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci ret = 0; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_cidone: 7098c2ecf20Sopenharmony_ci kfree(tmpfwbuf); 7108c2ecf20Sopenharmony_ci release_firmware(fw_firmware); 7118c2ecf20Sopenharmony_ci return ret; 7128c2ecf20Sopenharmony_ci} 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_cistatic int btmrvl_sdio_card_to_host(struct btmrvl_private *priv) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci u16 buf_len = 0; 7178c2ecf20Sopenharmony_ci int ret, num_blocks, blksz; 7188c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 7198c2ecf20Sopenharmony_ci u32 type; 7208c2ecf20Sopenharmony_ci u8 *payload; 7218c2ecf20Sopenharmony_ci struct hci_dev *hdev = priv->btmrvl_dev.hcidev; 7228c2ecf20Sopenharmony_ci struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci if (!card || !card->func) { 7258c2ecf20Sopenharmony_ci BT_ERR("card or function is NULL!"); 7268c2ecf20Sopenharmony_ci ret = -EINVAL; 7278c2ecf20Sopenharmony_ci goto exit; 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci /* Read the length of data to be transferred */ 7318c2ecf20Sopenharmony_ci ret = btmrvl_sdio_read_rx_len(card, &buf_len); 7328c2ecf20Sopenharmony_ci if (ret < 0) { 7338c2ecf20Sopenharmony_ci BT_ERR("read rx_len failed"); 7348c2ecf20Sopenharmony_ci ret = -EIO; 7358c2ecf20Sopenharmony_ci goto exit; 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci blksz = SDIO_BLOCK_SIZE; 7398c2ecf20Sopenharmony_ci num_blocks = DIV_ROUND_UP(buf_len, blksz); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci if (buf_len <= SDIO_HEADER_LEN 7428c2ecf20Sopenharmony_ci || (num_blocks * blksz) > ALLOC_BUF_SIZE) { 7438c2ecf20Sopenharmony_ci BT_ERR("invalid packet length: %d", buf_len); 7448c2ecf20Sopenharmony_ci ret = -EINVAL; 7458c2ecf20Sopenharmony_ci goto exit; 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci /* Allocate buffer */ 7498c2ecf20Sopenharmony_ci skb = bt_skb_alloc(num_blocks * blksz + BTSDIO_DMA_ALIGN, GFP_KERNEL); 7508c2ecf20Sopenharmony_ci if (!skb) { 7518c2ecf20Sopenharmony_ci BT_ERR("No free skb"); 7528c2ecf20Sopenharmony_ci ret = -ENOMEM; 7538c2ecf20Sopenharmony_ci goto exit; 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci if ((unsigned long) skb->data & (BTSDIO_DMA_ALIGN - 1)) { 7578c2ecf20Sopenharmony_ci skb_put(skb, (unsigned long) skb->data & 7588c2ecf20Sopenharmony_ci (BTSDIO_DMA_ALIGN - 1)); 7598c2ecf20Sopenharmony_ci skb_pull(skb, (unsigned long) skb->data & 7608c2ecf20Sopenharmony_ci (BTSDIO_DMA_ALIGN - 1)); 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci payload = skb->data; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci ret = sdio_readsb(card->func, payload, card->ioport, 7668c2ecf20Sopenharmony_ci num_blocks * blksz); 7678c2ecf20Sopenharmony_ci if (ret < 0) { 7688c2ecf20Sopenharmony_ci BT_ERR("readsb failed: %d", ret); 7698c2ecf20Sopenharmony_ci ret = -EIO; 7708c2ecf20Sopenharmony_ci goto exit; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci /* This is SDIO specific header length: byte[2][1][0], type: byte[3] 7748c2ecf20Sopenharmony_ci * (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) 7758c2ecf20Sopenharmony_ci */ 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci buf_len = payload[0]; 7788c2ecf20Sopenharmony_ci buf_len |= payload[1] << 8; 7798c2ecf20Sopenharmony_ci buf_len |= payload[2] << 16; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci if (buf_len > blksz * num_blocks) { 7828c2ecf20Sopenharmony_ci BT_ERR("Skip incorrect packet: hdrlen %d buffer %d", 7838c2ecf20Sopenharmony_ci buf_len, blksz * num_blocks); 7848c2ecf20Sopenharmony_ci ret = -EIO; 7858c2ecf20Sopenharmony_ci goto exit; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci type = payload[3]; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci switch (type) { 7918c2ecf20Sopenharmony_ci case HCI_ACLDATA_PKT: 7928c2ecf20Sopenharmony_ci case HCI_SCODATA_PKT: 7938c2ecf20Sopenharmony_ci case HCI_EVENT_PKT: 7948c2ecf20Sopenharmony_ci hci_skb_pkt_type(skb) = type; 7958c2ecf20Sopenharmony_ci skb_put(skb, buf_len); 7968c2ecf20Sopenharmony_ci skb_pull(skb, SDIO_HEADER_LEN); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (type == HCI_EVENT_PKT) { 7998c2ecf20Sopenharmony_ci if (btmrvl_check_evtpkt(priv, skb)) 8008c2ecf20Sopenharmony_ci hci_recv_frame(hdev, skb); 8018c2ecf20Sopenharmony_ci } else { 8028c2ecf20Sopenharmony_ci hci_recv_frame(hdev, skb); 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci hdev->stat.byte_rx += buf_len; 8068c2ecf20Sopenharmony_ci break; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci case MRVL_VENDOR_PKT: 8098c2ecf20Sopenharmony_ci hci_skb_pkt_type(skb) = HCI_VENDOR_PKT; 8108c2ecf20Sopenharmony_ci skb_put(skb, buf_len); 8118c2ecf20Sopenharmony_ci skb_pull(skb, SDIO_HEADER_LEN); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci if (btmrvl_process_event(priv, skb)) 8148c2ecf20Sopenharmony_ci hci_recv_frame(hdev, skb); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci hdev->stat.byte_rx += buf_len; 8178c2ecf20Sopenharmony_ci break; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci default: 8208c2ecf20Sopenharmony_ci BT_ERR("Unknown packet type:%d", type); 8218c2ecf20Sopenharmony_ci BT_ERR("hex: %*ph", blksz * num_blocks, payload); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci kfree_skb(skb); 8248c2ecf20Sopenharmony_ci skb = NULL; 8258c2ecf20Sopenharmony_ci break; 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ciexit: 8298c2ecf20Sopenharmony_ci if (ret) { 8308c2ecf20Sopenharmony_ci hdev->stat.err_rx++; 8318c2ecf20Sopenharmony_ci kfree_skb(skb); 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci return ret; 8358c2ecf20Sopenharmony_ci} 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_cistatic int btmrvl_sdio_process_int_status(struct btmrvl_private *priv) 8388c2ecf20Sopenharmony_ci{ 8398c2ecf20Sopenharmony_ci ulong flags; 8408c2ecf20Sopenharmony_ci u8 ireg; 8418c2ecf20Sopenharmony_ci struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 8448c2ecf20Sopenharmony_ci ireg = sdio_ireg; 8458c2ecf20Sopenharmony_ci sdio_ireg = 0; 8468c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci sdio_claim_host(card->func); 8498c2ecf20Sopenharmony_ci if (ireg & DN_LD_HOST_INT_STATUS) { 8508c2ecf20Sopenharmony_ci if (priv->btmrvl_dev.tx_dnld_rdy) 8518c2ecf20Sopenharmony_ci BT_DBG("tx_done already received: " 8528c2ecf20Sopenharmony_ci " int_status=0x%x", ireg); 8538c2ecf20Sopenharmony_ci else 8548c2ecf20Sopenharmony_ci priv->btmrvl_dev.tx_dnld_rdy = true; 8558c2ecf20Sopenharmony_ci } 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci if (ireg & UP_LD_HOST_INT_STATUS) 8588c2ecf20Sopenharmony_ci btmrvl_sdio_card_to_host(priv); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci sdio_release_host(card->func); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci return 0; 8638c2ecf20Sopenharmony_ci} 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_cistatic int btmrvl_sdio_read_to_clear(struct btmrvl_sdio_card *card, u8 *ireg) 8668c2ecf20Sopenharmony_ci{ 8678c2ecf20Sopenharmony_ci struct btmrvl_adapter *adapter = card->priv->adapter; 8688c2ecf20Sopenharmony_ci int ret; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci ret = sdio_readsb(card->func, adapter->hw_regs, 0, SDIO_BLOCK_SIZE); 8718c2ecf20Sopenharmony_ci if (ret) { 8728c2ecf20Sopenharmony_ci BT_ERR("sdio_readsb: read int hw_regs failed: %d", ret); 8738c2ecf20Sopenharmony_ci return ret; 8748c2ecf20Sopenharmony_ci } 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci *ireg = adapter->hw_regs[card->reg->host_intstatus]; 8778c2ecf20Sopenharmony_ci BT_DBG("hw_regs[%#x]=%#x", card->reg->host_intstatus, *ireg); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci return 0; 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic int btmrvl_sdio_write_to_clear(struct btmrvl_sdio_card *card, u8 *ireg) 8838c2ecf20Sopenharmony_ci{ 8848c2ecf20Sopenharmony_ci int ret; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci *ireg = sdio_readb(card->func, card->reg->host_intstatus, &ret); 8878c2ecf20Sopenharmony_ci if (ret) { 8888c2ecf20Sopenharmony_ci BT_ERR("sdio_readb: read int status failed: %d", ret); 8898c2ecf20Sopenharmony_ci return ret; 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci if (*ireg) { 8938c2ecf20Sopenharmony_ci /* 8948c2ecf20Sopenharmony_ci * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS 8958c2ecf20Sopenharmony_ci * Clear the interrupt status register and re-enable the 8968c2ecf20Sopenharmony_ci * interrupt. 8978c2ecf20Sopenharmony_ci */ 8988c2ecf20Sopenharmony_ci BT_DBG("int_status = 0x%x", *ireg); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci sdio_writeb(card->func, ~(*ireg) & (DN_LD_HOST_INT_STATUS | 9018c2ecf20Sopenharmony_ci UP_LD_HOST_INT_STATUS), 9028c2ecf20Sopenharmony_ci card->reg->host_intstatus, &ret); 9038c2ecf20Sopenharmony_ci if (ret) { 9048c2ecf20Sopenharmony_ci BT_ERR("sdio_writeb: clear int status failed: %d", ret); 9058c2ecf20Sopenharmony_ci return ret; 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci return 0; 9108c2ecf20Sopenharmony_ci} 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_cistatic void btmrvl_sdio_interrupt(struct sdio_func *func) 9138c2ecf20Sopenharmony_ci{ 9148c2ecf20Sopenharmony_ci struct btmrvl_private *priv; 9158c2ecf20Sopenharmony_ci struct btmrvl_sdio_card *card; 9168c2ecf20Sopenharmony_ci ulong flags; 9178c2ecf20Sopenharmony_ci u8 ireg = 0; 9188c2ecf20Sopenharmony_ci int ret; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci card = sdio_get_drvdata(func); 9218c2ecf20Sopenharmony_ci if (!card || !card->priv) { 9228c2ecf20Sopenharmony_ci BT_ERR("sbi_interrupt(%p) card or priv is NULL, card=%p", 9238c2ecf20Sopenharmony_ci func, card); 9248c2ecf20Sopenharmony_ci return; 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci priv = card->priv; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci if (priv->surprise_removed) 9308c2ecf20Sopenharmony_ci return; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci if (card->reg->int_read_to_clear) 9338c2ecf20Sopenharmony_ci ret = btmrvl_sdio_read_to_clear(card, &ireg); 9348c2ecf20Sopenharmony_ci else 9358c2ecf20Sopenharmony_ci ret = btmrvl_sdio_write_to_clear(card, &ireg); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (ret) 9388c2ecf20Sopenharmony_ci return; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 9418c2ecf20Sopenharmony_ci sdio_ireg |= ireg; 9428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci btmrvl_interrupt(priv); 9458c2ecf20Sopenharmony_ci} 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_cistatic int btmrvl_sdio_register_dev(struct btmrvl_sdio_card *card) 9488c2ecf20Sopenharmony_ci{ 9498c2ecf20Sopenharmony_ci struct sdio_func *func; 9508c2ecf20Sopenharmony_ci u8 reg; 9518c2ecf20Sopenharmony_ci int ret; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci if (!card || !card->func) { 9548c2ecf20Sopenharmony_ci BT_ERR("Error: card or function is NULL!"); 9558c2ecf20Sopenharmony_ci ret = -EINVAL; 9568c2ecf20Sopenharmony_ci goto failed; 9578c2ecf20Sopenharmony_ci } 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci func = card->func; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci sdio_claim_host(func); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci ret = sdio_enable_func(func); 9648c2ecf20Sopenharmony_ci if (ret) { 9658c2ecf20Sopenharmony_ci BT_ERR("sdio_enable_func() failed: ret=%d", ret); 9668c2ecf20Sopenharmony_ci ret = -EIO; 9678c2ecf20Sopenharmony_ci goto release_host; 9688c2ecf20Sopenharmony_ci } 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci ret = sdio_claim_irq(func, btmrvl_sdio_interrupt); 9718c2ecf20Sopenharmony_ci if (ret) { 9728c2ecf20Sopenharmony_ci BT_ERR("sdio_claim_irq failed: ret=%d", ret); 9738c2ecf20Sopenharmony_ci ret = -EIO; 9748c2ecf20Sopenharmony_ci goto disable_func; 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci ret = sdio_set_block_size(card->func, SDIO_BLOCK_SIZE); 9788c2ecf20Sopenharmony_ci if (ret) { 9798c2ecf20Sopenharmony_ci BT_ERR("cannot set SDIO block size"); 9808c2ecf20Sopenharmony_ci ret = -EIO; 9818c2ecf20Sopenharmony_ci goto release_irq; 9828c2ecf20Sopenharmony_ci } 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci reg = sdio_readb(func, card->reg->io_port_0, &ret); 9858c2ecf20Sopenharmony_ci if (ret < 0) { 9868c2ecf20Sopenharmony_ci ret = -EIO; 9878c2ecf20Sopenharmony_ci goto release_irq; 9888c2ecf20Sopenharmony_ci } 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci card->ioport = reg; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci reg = sdio_readb(func, card->reg->io_port_1, &ret); 9938c2ecf20Sopenharmony_ci if (ret < 0) { 9948c2ecf20Sopenharmony_ci ret = -EIO; 9958c2ecf20Sopenharmony_ci goto release_irq; 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci card->ioport |= (reg << 8); 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci reg = sdio_readb(func, card->reg->io_port_2, &ret); 10018c2ecf20Sopenharmony_ci if (ret < 0) { 10028c2ecf20Sopenharmony_ci ret = -EIO; 10038c2ecf20Sopenharmony_ci goto release_irq; 10048c2ecf20Sopenharmony_ci } 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci card->ioport |= (reg << 16); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci BT_DBG("SDIO FUNC%d IO port: 0x%x", func->num, card->ioport); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci if (card->reg->int_read_to_clear) { 10118c2ecf20Sopenharmony_ci reg = sdio_readb(func, card->reg->host_int_rsr, &ret); 10128c2ecf20Sopenharmony_ci if (ret < 0) { 10138c2ecf20Sopenharmony_ci ret = -EIO; 10148c2ecf20Sopenharmony_ci goto release_irq; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci sdio_writeb(func, reg | 0x3f, card->reg->host_int_rsr, &ret); 10178c2ecf20Sopenharmony_ci if (ret < 0) { 10188c2ecf20Sopenharmony_ci ret = -EIO; 10198c2ecf20Sopenharmony_ci goto release_irq; 10208c2ecf20Sopenharmony_ci } 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci reg = sdio_readb(func, card->reg->card_misc_cfg, &ret); 10238c2ecf20Sopenharmony_ci if (ret < 0) { 10248c2ecf20Sopenharmony_ci ret = -EIO; 10258c2ecf20Sopenharmony_ci goto release_irq; 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci sdio_writeb(func, reg | 0x10, card->reg->card_misc_cfg, &ret); 10288c2ecf20Sopenharmony_ci if (ret < 0) { 10298c2ecf20Sopenharmony_ci ret = -EIO; 10308c2ecf20Sopenharmony_ci goto release_irq; 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci } 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci sdio_set_drvdata(func, card); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci sdio_release_host(func); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci return 0; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_cirelease_irq: 10418c2ecf20Sopenharmony_ci sdio_release_irq(func); 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_cidisable_func: 10448c2ecf20Sopenharmony_ci sdio_disable_func(func); 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_cirelease_host: 10478c2ecf20Sopenharmony_ci sdio_release_host(func); 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_cifailed: 10508c2ecf20Sopenharmony_ci return ret; 10518c2ecf20Sopenharmony_ci} 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_cistatic int btmrvl_sdio_unregister_dev(struct btmrvl_sdio_card *card) 10548c2ecf20Sopenharmony_ci{ 10558c2ecf20Sopenharmony_ci if (card && card->func) { 10568c2ecf20Sopenharmony_ci sdio_claim_host(card->func); 10578c2ecf20Sopenharmony_ci sdio_release_irq(card->func); 10588c2ecf20Sopenharmony_ci sdio_disable_func(card->func); 10598c2ecf20Sopenharmony_ci sdio_release_host(card->func); 10608c2ecf20Sopenharmony_ci sdio_set_drvdata(card->func, NULL); 10618c2ecf20Sopenharmony_ci } 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci return 0; 10648c2ecf20Sopenharmony_ci} 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_cistatic int btmrvl_sdio_enable_host_int(struct btmrvl_sdio_card *card) 10678c2ecf20Sopenharmony_ci{ 10688c2ecf20Sopenharmony_ci int ret; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci if (!card || !card->func) 10718c2ecf20Sopenharmony_ci return -EINVAL; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci sdio_claim_host(card->func); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci ret = btmrvl_sdio_enable_host_int_mask(card, HIM_ENABLE); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci btmrvl_sdio_get_rx_unit(card); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci sdio_release_host(card->func); 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci return ret; 10828c2ecf20Sopenharmony_ci} 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_cistatic int btmrvl_sdio_disable_host_int(struct btmrvl_sdio_card *card) 10858c2ecf20Sopenharmony_ci{ 10868c2ecf20Sopenharmony_ci int ret; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci if (!card || !card->func) 10898c2ecf20Sopenharmony_ci return -EINVAL; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci sdio_claim_host(card->func); 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci ret = btmrvl_sdio_disable_host_int_mask(card, HIM_DISABLE); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci sdio_release_host(card->func); 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci return ret; 10988c2ecf20Sopenharmony_ci} 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_cistatic int btmrvl_sdio_host_to_card(struct btmrvl_private *priv, 11018c2ecf20Sopenharmony_ci u8 *payload, u16 nb) 11028c2ecf20Sopenharmony_ci{ 11038c2ecf20Sopenharmony_ci struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; 11048c2ecf20Sopenharmony_ci int ret = 0; 11058c2ecf20Sopenharmony_ci int blksz; 11068c2ecf20Sopenharmony_ci int i = 0; 11078c2ecf20Sopenharmony_ci u8 *buf = NULL; 11088c2ecf20Sopenharmony_ci void *tmpbuf = NULL; 11098c2ecf20Sopenharmony_ci int tmpbufsz; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci if (!card || !card->func) { 11128c2ecf20Sopenharmony_ci BT_ERR("card or function is NULL!"); 11138c2ecf20Sopenharmony_ci return -EINVAL; 11148c2ecf20Sopenharmony_ci } 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci blksz = DIV_ROUND_UP(nb, SDIO_BLOCK_SIZE) * SDIO_BLOCK_SIZE; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci buf = payload; 11198c2ecf20Sopenharmony_ci if ((unsigned long) payload & (BTSDIO_DMA_ALIGN - 1) || 11208c2ecf20Sopenharmony_ci nb < blksz) { 11218c2ecf20Sopenharmony_ci tmpbufsz = ALIGN_SZ(blksz, BTSDIO_DMA_ALIGN) + 11228c2ecf20Sopenharmony_ci BTSDIO_DMA_ALIGN; 11238c2ecf20Sopenharmony_ci tmpbuf = kzalloc(tmpbufsz, GFP_KERNEL); 11248c2ecf20Sopenharmony_ci if (!tmpbuf) 11258c2ecf20Sopenharmony_ci return -ENOMEM; 11268c2ecf20Sopenharmony_ci buf = (u8 *) ALIGN_ADDR(tmpbuf, BTSDIO_DMA_ALIGN); 11278c2ecf20Sopenharmony_ci memcpy(buf, payload, nb); 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci sdio_claim_host(card->func); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci do { 11338c2ecf20Sopenharmony_ci /* Transfer data to card */ 11348c2ecf20Sopenharmony_ci ret = sdio_writesb(card->func, card->ioport, buf, 11358c2ecf20Sopenharmony_ci blksz); 11368c2ecf20Sopenharmony_ci if (ret < 0) { 11378c2ecf20Sopenharmony_ci i++; 11388c2ecf20Sopenharmony_ci BT_ERR("i=%d writesb failed: %d", i, ret); 11398c2ecf20Sopenharmony_ci BT_ERR("hex: %*ph", nb, payload); 11408c2ecf20Sopenharmony_ci ret = -EIO; 11418c2ecf20Sopenharmony_ci if (i > MAX_WRITE_IOMEM_RETRY) 11428c2ecf20Sopenharmony_ci goto exit; 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci } while (ret); 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci priv->btmrvl_dev.tx_dnld_rdy = false; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ciexit: 11498c2ecf20Sopenharmony_ci sdio_release_host(card->func); 11508c2ecf20Sopenharmony_ci kfree(tmpbuf); 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci return ret; 11538c2ecf20Sopenharmony_ci} 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_cistatic int btmrvl_sdio_download_fw(struct btmrvl_sdio_card *card) 11568c2ecf20Sopenharmony_ci{ 11578c2ecf20Sopenharmony_ci int ret; 11588c2ecf20Sopenharmony_ci u8 fws0; 11598c2ecf20Sopenharmony_ci int pollnum = MAX_POLL_TRIES; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci if (!card || !card->func) { 11628c2ecf20Sopenharmony_ci BT_ERR("card or function is NULL!"); 11638c2ecf20Sopenharmony_ci return -EINVAL; 11648c2ecf20Sopenharmony_ci } 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci if (!btmrvl_sdio_verify_fw_download(card, 1)) { 11678c2ecf20Sopenharmony_ci BT_DBG("Firmware already downloaded!"); 11688c2ecf20Sopenharmony_ci return 0; 11698c2ecf20Sopenharmony_ci } 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci sdio_claim_host(card->func); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci /* Check if other function driver is downloading the firmware */ 11748c2ecf20Sopenharmony_ci fws0 = sdio_readb(card->func, card->reg->card_fw_status0, &ret); 11758c2ecf20Sopenharmony_ci if (ret) { 11768c2ecf20Sopenharmony_ci BT_ERR("Failed to read FW downloading status!"); 11778c2ecf20Sopenharmony_ci ret = -EIO; 11788c2ecf20Sopenharmony_ci goto done; 11798c2ecf20Sopenharmony_ci } 11808c2ecf20Sopenharmony_ci if (fws0) { 11818c2ecf20Sopenharmony_ci BT_DBG("BT not the winner (%#x). Skip FW downloading", fws0); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci /* Give other function more time to download the firmware */ 11848c2ecf20Sopenharmony_ci pollnum *= 10; 11858c2ecf20Sopenharmony_ci } else { 11868c2ecf20Sopenharmony_ci if (card->helper) { 11878c2ecf20Sopenharmony_ci ret = btmrvl_sdio_download_helper(card); 11888c2ecf20Sopenharmony_ci if (ret) { 11898c2ecf20Sopenharmony_ci BT_ERR("Failed to download helper!"); 11908c2ecf20Sopenharmony_ci ret = -EIO; 11918c2ecf20Sopenharmony_ci goto done; 11928c2ecf20Sopenharmony_ci } 11938c2ecf20Sopenharmony_ci } 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci if (btmrvl_sdio_download_fw_w_helper(card)) { 11968c2ecf20Sopenharmony_ci BT_ERR("Failed to download firmware!"); 11978c2ecf20Sopenharmony_ci ret = -EIO; 11988c2ecf20Sopenharmony_ci goto done; 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci /* 12038c2ecf20Sopenharmony_ci * winner or not, with this test the FW synchronizes when the 12048c2ecf20Sopenharmony_ci * module can continue its initialization 12058c2ecf20Sopenharmony_ci */ 12068c2ecf20Sopenharmony_ci if (btmrvl_sdio_verify_fw_download(card, pollnum)) { 12078c2ecf20Sopenharmony_ci BT_ERR("FW failed to be active in time!"); 12088c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 12098c2ecf20Sopenharmony_ci goto done; 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci sdio_release_host(card->func); 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci return 0; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_cidone: 12178c2ecf20Sopenharmony_ci sdio_release_host(card->func); 12188c2ecf20Sopenharmony_ci return ret; 12198c2ecf20Sopenharmony_ci} 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_cistatic int btmrvl_sdio_wakeup_fw(struct btmrvl_private *priv) 12228c2ecf20Sopenharmony_ci{ 12238c2ecf20Sopenharmony_ci struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; 12248c2ecf20Sopenharmony_ci int ret = 0; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci if (!card || !card->func) { 12278c2ecf20Sopenharmony_ci BT_ERR("card or function is NULL!"); 12288c2ecf20Sopenharmony_ci return -EINVAL; 12298c2ecf20Sopenharmony_ci } 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci sdio_claim_host(card->func); 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci sdio_writeb(card->func, HOST_POWER_UP, card->reg->cfg, &ret); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci sdio_release_host(card->func); 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci BT_DBG("wake up firmware"); 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci return ret; 12408c2ecf20Sopenharmony_ci} 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_cistatic void btmrvl_sdio_dump_regs(struct btmrvl_private *priv) 12438c2ecf20Sopenharmony_ci{ 12448c2ecf20Sopenharmony_ci struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; 12458c2ecf20Sopenharmony_ci int ret = 0; 12468c2ecf20Sopenharmony_ci unsigned int reg, reg_start, reg_end; 12478c2ecf20Sopenharmony_ci char buf[256], *ptr; 12488c2ecf20Sopenharmony_ci u8 loop, func, data; 12498c2ecf20Sopenharmony_ci int MAX_LOOP = 2; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci btmrvl_sdio_wakeup_fw(priv); 12528c2ecf20Sopenharmony_ci sdio_claim_host(card->func); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci for (loop = 0; loop < MAX_LOOP; loop++) { 12558c2ecf20Sopenharmony_ci memset(buf, 0, sizeof(buf)); 12568c2ecf20Sopenharmony_ci ptr = buf; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci if (loop == 0) { 12598c2ecf20Sopenharmony_ci /* Read the registers of SDIO function0 */ 12608c2ecf20Sopenharmony_ci func = loop; 12618c2ecf20Sopenharmony_ci reg_start = 0; 12628c2ecf20Sopenharmony_ci reg_end = 9; 12638c2ecf20Sopenharmony_ci } else { 12648c2ecf20Sopenharmony_ci func = 2; 12658c2ecf20Sopenharmony_ci reg_start = 0; 12668c2ecf20Sopenharmony_ci reg_end = 0x09; 12678c2ecf20Sopenharmony_ci } 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", 12708c2ecf20Sopenharmony_ci func, reg_start, reg_end); 12718c2ecf20Sopenharmony_ci for (reg = reg_start; reg <= reg_end; reg++) { 12728c2ecf20Sopenharmony_ci if (func == 0) 12738c2ecf20Sopenharmony_ci data = sdio_f0_readb(card->func, reg, &ret); 12748c2ecf20Sopenharmony_ci else 12758c2ecf20Sopenharmony_ci data = sdio_readb(card->func, reg, &ret); 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci if (!ret) { 12788c2ecf20Sopenharmony_ci ptr += sprintf(ptr, "%02x ", data); 12798c2ecf20Sopenharmony_ci } else { 12808c2ecf20Sopenharmony_ci ptr += sprintf(ptr, "ERR"); 12818c2ecf20Sopenharmony_ci break; 12828c2ecf20Sopenharmony_ci } 12838c2ecf20Sopenharmony_ci } 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci BT_INFO("%s", buf); 12868c2ecf20Sopenharmony_ci } 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci sdio_release_host(card->func); 12898c2ecf20Sopenharmony_ci} 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci/* This function read/write firmware */ 12928c2ecf20Sopenharmony_cistatic enum 12938c2ecf20Sopenharmony_cirdwr_status btmrvl_sdio_rdwr_firmware(struct btmrvl_private *priv, 12948c2ecf20Sopenharmony_ci u8 doneflag) 12958c2ecf20Sopenharmony_ci{ 12968c2ecf20Sopenharmony_ci struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; 12978c2ecf20Sopenharmony_ci int ret, tries; 12988c2ecf20Sopenharmony_ci u8 ctrl_data = 0; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci sdio_writeb(card->func, FW_DUMP_HOST_READY, card->reg->fw_dump_ctrl, 13018c2ecf20Sopenharmony_ci &ret); 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci if (ret) { 13048c2ecf20Sopenharmony_ci BT_ERR("SDIO write err"); 13058c2ecf20Sopenharmony_ci return RDWR_STATUS_FAILURE; 13068c2ecf20Sopenharmony_ci } 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci for (tries = 0; tries < MAX_POLL_TRIES; tries++) { 13098c2ecf20Sopenharmony_ci ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl, 13108c2ecf20Sopenharmony_ci &ret); 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci if (ret) { 13138c2ecf20Sopenharmony_ci BT_ERR("SDIO read err"); 13148c2ecf20Sopenharmony_ci return RDWR_STATUS_FAILURE; 13158c2ecf20Sopenharmony_ci } 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci if (ctrl_data == FW_DUMP_DONE) 13188c2ecf20Sopenharmony_ci break; 13198c2ecf20Sopenharmony_ci if (doneflag && ctrl_data == doneflag) 13208c2ecf20Sopenharmony_ci return RDWR_STATUS_DONE; 13218c2ecf20Sopenharmony_ci if (ctrl_data != FW_DUMP_HOST_READY) { 13228c2ecf20Sopenharmony_ci BT_INFO("The ctrl reg was changed, re-try again!"); 13238c2ecf20Sopenharmony_ci sdio_writeb(card->func, FW_DUMP_HOST_READY, 13248c2ecf20Sopenharmony_ci card->reg->fw_dump_ctrl, &ret); 13258c2ecf20Sopenharmony_ci if (ret) { 13268c2ecf20Sopenharmony_ci BT_ERR("SDIO write err"); 13278c2ecf20Sopenharmony_ci return RDWR_STATUS_FAILURE; 13288c2ecf20Sopenharmony_ci } 13298c2ecf20Sopenharmony_ci } 13308c2ecf20Sopenharmony_ci usleep_range(100, 200); 13318c2ecf20Sopenharmony_ci } 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci if (ctrl_data == FW_DUMP_HOST_READY) { 13348c2ecf20Sopenharmony_ci BT_ERR("Fail to pull ctrl_data"); 13358c2ecf20Sopenharmony_ci return RDWR_STATUS_FAILURE; 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci return RDWR_STATUS_SUCCESS; 13398c2ecf20Sopenharmony_ci} 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci/* This function dump sdio register and memory data */ 13428c2ecf20Sopenharmony_cistatic void btmrvl_sdio_coredump(struct device *dev) 13438c2ecf20Sopenharmony_ci{ 13448c2ecf20Sopenharmony_ci struct sdio_func *func = dev_to_sdio_func(dev); 13458c2ecf20Sopenharmony_ci struct btmrvl_sdio_card *card; 13468c2ecf20Sopenharmony_ci struct btmrvl_private *priv; 13478c2ecf20Sopenharmony_ci int ret = 0; 13488c2ecf20Sopenharmony_ci unsigned int reg, reg_start, reg_end; 13498c2ecf20Sopenharmony_ci enum rdwr_status stat; 13508c2ecf20Sopenharmony_ci u8 *dbg_ptr, *end_ptr, *fw_dump_data, *fw_dump_ptr; 13518c2ecf20Sopenharmony_ci u8 dump_num = 0, idx, i, read_reg, doneflag = 0; 13528c2ecf20Sopenharmony_ci u32 memory_size, fw_dump_len = 0; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci card = sdio_get_drvdata(func); 13558c2ecf20Sopenharmony_ci priv = card->priv; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci /* dump sdio register first */ 13588c2ecf20Sopenharmony_ci btmrvl_sdio_dump_regs(priv); 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci if (!card->supports_fw_dump) { 13618c2ecf20Sopenharmony_ci BT_ERR("Firmware dump not supported for this card!"); 13628c2ecf20Sopenharmony_ci return; 13638c2ecf20Sopenharmony_ci } 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) { 13668c2ecf20Sopenharmony_ci struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci if (entry->mem_ptr) { 13698c2ecf20Sopenharmony_ci vfree(entry->mem_ptr); 13708c2ecf20Sopenharmony_ci entry->mem_ptr = NULL; 13718c2ecf20Sopenharmony_ci } 13728c2ecf20Sopenharmony_ci entry->mem_size = 0; 13738c2ecf20Sopenharmony_ci } 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci btmrvl_sdio_wakeup_fw(priv); 13768c2ecf20Sopenharmony_ci sdio_claim_host(card->func); 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci BT_INFO("== btmrvl firmware dump start =="); 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci stat = btmrvl_sdio_rdwr_firmware(priv, doneflag); 13818c2ecf20Sopenharmony_ci if (stat == RDWR_STATUS_FAILURE) 13828c2ecf20Sopenharmony_ci goto done; 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci reg = card->reg->fw_dump_start; 13858c2ecf20Sopenharmony_ci /* Read the number of the memories which will dump */ 13868c2ecf20Sopenharmony_ci dump_num = sdio_readb(card->func, reg, &ret); 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci if (ret) { 13898c2ecf20Sopenharmony_ci BT_ERR("SDIO read memory length err"); 13908c2ecf20Sopenharmony_ci goto done; 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci /* Read the length of every memory which will dump */ 13948c2ecf20Sopenharmony_ci for (idx = 0; idx < dump_num; idx++) { 13958c2ecf20Sopenharmony_ci struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci stat = btmrvl_sdio_rdwr_firmware(priv, doneflag); 13988c2ecf20Sopenharmony_ci if (stat == RDWR_STATUS_FAILURE) 13998c2ecf20Sopenharmony_ci goto done; 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci memory_size = 0; 14028c2ecf20Sopenharmony_ci reg = card->reg->fw_dump_start; 14038c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 14048c2ecf20Sopenharmony_ci read_reg = sdio_readb(card->func, reg, &ret); 14058c2ecf20Sopenharmony_ci if (ret) { 14068c2ecf20Sopenharmony_ci BT_ERR("SDIO read err"); 14078c2ecf20Sopenharmony_ci goto done; 14088c2ecf20Sopenharmony_ci } 14098c2ecf20Sopenharmony_ci memory_size |= (read_reg << i*8); 14108c2ecf20Sopenharmony_ci reg++; 14118c2ecf20Sopenharmony_ci } 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci if (memory_size == 0) { 14148c2ecf20Sopenharmony_ci BT_INFO("Firmware dump finished!"); 14158c2ecf20Sopenharmony_ci sdio_writeb(card->func, FW_DUMP_READ_DONE, 14168c2ecf20Sopenharmony_ci card->reg->fw_dump_ctrl, &ret); 14178c2ecf20Sopenharmony_ci if (ret) { 14188c2ecf20Sopenharmony_ci BT_ERR("SDIO Write MEMDUMP_FINISH ERR"); 14198c2ecf20Sopenharmony_ci goto done; 14208c2ecf20Sopenharmony_ci } 14218c2ecf20Sopenharmony_ci break; 14228c2ecf20Sopenharmony_ci } 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci BT_INFO("%s_SIZE=0x%x", entry->mem_name, memory_size); 14258c2ecf20Sopenharmony_ci entry->mem_ptr = vzalloc(memory_size + 1); 14268c2ecf20Sopenharmony_ci entry->mem_size = memory_size; 14278c2ecf20Sopenharmony_ci if (!entry->mem_ptr) { 14288c2ecf20Sopenharmony_ci BT_ERR("Vzalloc %s failed", entry->mem_name); 14298c2ecf20Sopenharmony_ci goto done; 14308c2ecf20Sopenharmony_ci } 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci fw_dump_len += (strlen("========Start dump ") + 14338c2ecf20Sopenharmony_ci strlen(entry->mem_name) + 14348c2ecf20Sopenharmony_ci strlen("========\n") + 14358c2ecf20Sopenharmony_ci (memory_size + 1) + 14368c2ecf20Sopenharmony_ci strlen("\n========End dump========\n")); 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci dbg_ptr = entry->mem_ptr; 14398c2ecf20Sopenharmony_ci end_ptr = dbg_ptr + memory_size; 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci doneflag = entry->done_flag; 14428c2ecf20Sopenharmony_ci BT_INFO("Start %s output, please wait...", 14438c2ecf20Sopenharmony_ci entry->mem_name); 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci do { 14468c2ecf20Sopenharmony_ci stat = btmrvl_sdio_rdwr_firmware(priv, doneflag); 14478c2ecf20Sopenharmony_ci if (stat == RDWR_STATUS_FAILURE) 14488c2ecf20Sopenharmony_ci goto done; 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci reg_start = card->reg->fw_dump_start; 14518c2ecf20Sopenharmony_ci reg_end = card->reg->fw_dump_end; 14528c2ecf20Sopenharmony_ci for (reg = reg_start; reg <= reg_end; reg++) { 14538c2ecf20Sopenharmony_ci *dbg_ptr = sdio_readb(card->func, reg, &ret); 14548c2ecf20Sopenharmony_ci if (ret) { 14558c2ecf20Sopenharmony_ci BT_ERR("SDIO read err"); 14568c2ecf20Sopenharmony_ci goto done; 14578c2ecf20Sopenharmony_ci } 14588c2ecf20Sopenharmony_ci if (dbg_ptr < end_ptr) 14598c2ecf20Sopenharmony_ci dbg_ptr++; 14608c2ecf20Sopenharmony_ci else 14618c2ecf20Sopenharmony_ci BT_ERR("Allocated buffer not enough"); 14628c2ecf20Sopenharmony_ci } 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci if (stat != RDWR_STATUS_DONE) { 14658c2ecf20Sopenharmony_ci continue; 14668c2ecf20Sopenharmony_ci } else { 14678c2ecf20Sopenharmony_ci BT_INFO("%s done: size=0x%tx", 14688c2ecf20Sopenharmony_ci entry->mem_name, 14698c2ecf20Sopenharmony_ci dbg_ptr - entry->mem_ptr); 14708c2ecf20Sopenharmony_ci break; 14718c2ecf20Sopenharmony_ci } 14728c2ecf20Sopenharmony_ci } while (1); 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci BT_INFO("== btmrvl firmware dump end =="); 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_cidone: 14788c2ecf20Sopenharmony_ci sdio_release_host(card->func); 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci if (fw_dump_len == 0) 14818c2ecf20Sopenharmony_ci return; 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci fw_dump_data = vzalloc(fw_dump_len+1); 14848c2ecf20Sopenharmony_ci if (!fw_dump_data) { 14858c2ecf20Sopenharmony_ci BT_ERR("Vzalloc fw_dump_data fail!"); 14868c2ecf20Sopenharmony_ci return; 14878c2ecf20Sopenharmony_ci } 14888c2ecf20Sopenharmony_ci fw_dump_ptr = fw_dump_data; 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci /* Dump all the memory data into single file, a userspace script will 14918c2ecf20Sopenharmony_ci * be used to split all the memory data to multiple files 14928c2ecf20Sopenharmony_ci */ 14938c2ecf20Sopenharmony_ci BT_INFO("== btmrvl firmware dump to /sys/class/devcoredump start"); 14948c2ecf20Sopenharmony_ci for (idx = 0; idx < dump_num; idx++) { 14958c2ecf20Sopenharmony_ci struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci if (entry->mem_ptr) { 14988c2ecf20Sopenharmony_ci strcpy(fw_dump_ptr, "========Start dump "); 14998c2ecf20Sopenharmony_ci fw_dump_ptr += strlen("========Start dump "); 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci strcpy(fw_dump_ptr, entry->mem_name); 15028c2ecf20Sopenharmony_ci fw_dump_ptr += strlen(entry->mem_name); 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci strcpy(fw_dump_ptr, "========\n"); 15058c2ecf20Sopenharmony_ci fw_dump_ptr += strlen("========\n"); 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size); 15088c2ecf20Sopenharmony_ci fw_dump_ptr += entry->mem_size; 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci strcpy(fw_dump_ptr, "\n========End dump========\n"); 15118c2ecf20Sopenharmony_ci fw_dump_ptr += strlen("\n========End dump========\n"); 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci vfree(mem_type_mapping_tbl[idx].mem_ptr); 15148c2ecf20Sopenharmony_ci mem_type_mapping_tbl[idx].mem_ptr = NULL; 15158c2ecf20Sopenharmony_ci } 15168c2ecf20Sopenharmony_ci } 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci /* fw_dump_data will be free in device coredump release function 15198c2ecf20Sopenharmony_ci * after 5 min 15208c2ecf20Sopenharmony_ci */ 15218c2ecf20Sopenharmony_ci dev_coredumpv(&card->func->dev, fw_dump_data, fw_dump_len, GFP_KERNEL); 15228c2ecf20Sopenharmony_ci BT_INFO("== btmrvl firmware dump to /sys/class/devcoredump end"); 15238c2ecf20Sopenharmony_ci} 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_cistatic int btmrvl_sdio_probe(struct sdio_func *func, 15268c2ecf20Sopenharmony_ci const struct sdio_device_id *id) 15278c2ecf20Sopenharmony_ci{ 15288c2ecf20Sopenharmony_ci int ret = 0; 15298c2ecf20Sopenharmony_ci struct btmrvl_private *priv = NULL; 15308c2ecf20Sopenharmony_ci struct btmrvl_sdio_card *card = NULL; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci BT_INFO("vendor=0x%x, device=0x%x, class=%d, fn=%d", 15338c2ecf20Sopenharmony_ci id->vendor, id->device, id->class, func->num); 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci card = devm_kzalloc(&func->dev, sizeof(*card), GFP_KERNEL); 15368c2ecf20Sopenharmony_ci if (!card) 15378c2ecf20Sopenharmony_ci return -ENOMEM; 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci card->func = func; 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci if (id->driver_data) { 15428c2ecf20Sopenharmony_ci struct btmrvl_sdio_device *data = (void *) id->driver_data; 15438c2ecf20Sopenharmony_ci card->helper = data->helper; 15448c2ecf20Sopenharmony_ci card->firmware = data->firmware; 15458c2ecf20Sopenharmony_ci card->reg = data->reg; 15468c2ecf20Sopenharmony_ci card->sd_blksz_fw_dl = data->sd_blksz_fw_dl; 15478c2ecf20Sopenharmony_ci card->support_pscan_win_report = data->support_pscan_win_report; 15488c2ecf20Sopenharmony_ci card->supports_fw_dump = data->supports_fw_dump; 15498c2ecf20Sopenharmony_ci } 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci if (btmrvl_sdio_register_dev(card) < 0) { 15528c2ecf20Sopenharmony_ci BT_ERR("Failed to register BT device!"); 15538c2ecf20Sopenharmony_ci return -ENODEV; 15548c2ecf20Sopenharmony_ci } 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci /* Disable the interrupts on the card */ 15578c2ecf20Sopenharmony_ci btmrvl_sdio_disable_host_int(card); 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci if (btmrvl_sdio_download_fw(card)) { 15608c2ecf20Sopenharmony_ci BT_ERR("Downloading firmware failed!"); 15618c2ecf20Sopenharmony_ci ret = -ENODEV; 15628c2ecf20Sopenharmony_ci goto unreg_dev; 15638c2ecf20Sopenharmony_ci } 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci btmrvl_sdio_enable_host_int(card); 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci /* Device tree node parsing and platform specific configuration*/ 15688c2ecf20Sopenharmony_ci btmrvl_sdio_probe_of(&func->dev, card); 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci priv = btmrvl_add_card(card); 15718c2ecf20Sopenharmony_ci if (!priv) { 15728c2ecf20Sopenharmony_ci BT_ERR("Initializing card failed!"); 15738c2ecf20Sopenharmony_ci ret = -ENODEV; 15748c2ecf20Sopenharmony_ci goto disable_host_int; 15758c2ecf20Sopenharmony_ci } 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci card->priv = priv; 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci /* Initialize the interface specific function pointers */ 15808c2ecf20Sopenharmony_ci priv->hw_host_to_card = btmrvl_sdio_host_to_card; 15818c2ecf20Sopenharmony_ci priv->hw_wakeup_firmware = btmrvl_sdio_wakeup_fw; 15828c2ecf20Sopenharmony_ci priv->hw_process_int_status = btmrvl_sdio_process_int_status; 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci if (btmrvl_register_hdev(priv)) { 15858c2ecf20Sopenharmony_ci BT_ERR("Register hdev failed!"); 15868c2ecf20Sopenharmony_ci ret = -ENODEV; 15878c2ecf20Sopenharmony_ci goto disable_host_int; 15888c2ecf20Sopenharmony_ci } 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci return 0; 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_cidisable_host_int: 15938c2ecf20Sopenharmony_ci btmrvl_sdio_disable_host_int(card); 15948c2ecf20Sopenharmony_ciunreg_dev: 15958c2ecf20Sopenharmony_ci btmrvl_sdio_unregister_dev(card); 15968c2ecf20Sopenharmony_ci return ret; 15978c2ecf20Sopenharmony_ci} 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_cistatic void btmrvl_sdio_remove(struct sdio_func *func) 16008c2ecf20Sopenharmony_ci{ 16018c2ecf20Sopenharmony_ci struct btmrvl_sdio_card *card; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci if (func) { 16048c2ecf20Sopenharmony_ci card = sdio_get_drvdata(func); 16058c2ecf20Sopenharmony_ci if (card) { 16068c2ecf20Sopenharmony_ci /* Send SHUTDOWN command & disable interrupt 16078c2ecf20Sopenharmony_ci * if user removes the module. 16088c2ecf20Sopenharmony_ci */ 16098c2ecf20Sopenharmony_ci if (user_rmmod) { 16108c2ecf20Sopenharmony_ci btmrvl_send_module_cfg_cmd(card->priv, 16118c2ecf20Sopenharmony_ci MODULE_SHUTDOWN_REQ); 16128c2ecf20Sopenharmony_ci btmrvl_sdio_disable_host_int(card); 16138c2ecf20Sopenharmony_ci } 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci BT_DBG("unregister dev"); 16168c2ecf20Sopenharmony_ci card->priv->surprise_removed = true; 16178c2ecf20Sopenharmony_ci btmrvl_sdio_unregister_dev(card); 16188c2ecf20Sopenharmony_ci btmrvl_remove_card(card->priv); 16198c2ecf20Sopenharmony_ci } 16208c2ecf20Sopenharmony_ci } 16218c2ecf20Sopenharmony_ci} 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_cistatic int btmrvl_sdio_suspend(struct device *dev) 16248c2ecf20Sopenharmony_ci{ 16258c2ecf20Sopenharmony_ci struct sdio_func *func = dev_to_sdio_func(dev); 16268c2ecf20Sopenharmony_ci struct btmrvl_sdio_card *card; 16278c2ecf20Sopenharmony_ci struct btmrvl_private *priv; 16288c2ecf20Sopenharmony_ci mmc_pm_flag_t pm_flags; 16298c2ecf20Sopenharmony_ci struct hci_dev *hcidev; 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci if (func) { 16328c2ecf20Sopenharmony_ci pm_flags = sdio_get_host_pm_caps(func); 16338c2ecf20Sopenharmony_ci BT_DBG("%s: suspend: PM flags = 0x%x", sdio_func_id(func), 16348c2ecf20Sopenharmony_ci pm_flags); 16358c2ecf20Sopenharmony_ci if (!(pm_flags & MMC_PM_KEEP_POWER)) { 16368c2ecf20Sopenharmony_ci BT_ERR("%s: cannot remain alive while suspended", 16378c2ecf20Sopenharmony_ci sdio_func_id(func)); 16388c2ecf20Sopenharmony_ci return -ENOSYS; 16398c2ecf20Sopenharmony_ci } 16408c2ecf20Sopenharmony_ci card = sdio_get_drvdata(func); 16418c2ecf20Sopenharmony_ci if (!card || !card->priv) { 16428c2ecf20Sopenharmony_ci BT_ERR("card or priv structure is not valid"); 16438c2ecf20Sopenharmony_ci return 0; 16448c2ecf20Sopenharmony_ci } 16458c2ecf20Sopenharmony_ci } else { 16468c2ecf20Sopenharmony_ci BT_ERR("sdio_func is not specified"); 16478c2ecf20Sopenharmony_ci return 0; 16488c2ecf20Sopenharmony_ci } 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci /* Enable platform specific wakeup interrupt */ 16518c2ecf20Sopenharmony_ci if (card->plt_wake_cfg && card->plt_wake_cfg->irq_bt >= 0 && 16528c2ecf20Sopenharmony_ci device_may_wakeup(dev)) { 16538c2ecf20Sopenharmony_ci card->plt_wake_cfg->wake_by_bt = false; 16548c2ecf20Sopenharmony_ci enable_irq(card->plt_wake_cfg->irq_bt); 16558c2ecf20Sopenharmony_ci enable_irq_wake(card->plt_wake_cfg->irq_bt); 16568c2ecf20Sopenharmony_ci } 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci priv = card->priv; 16598c2ecf20Sopenharmony_ci priv->adapter->is_suspending = true; 16608c2ecf20Sopenharmony_ci hcidev = priv->btmrvl_dev.hcidev; 16618c2ecf20Sopenharmony_ci BT_DBG("%s: SDIO suspend", hcidev->name); 16628c2ecf20Sopenharmony_ci hci_suspend_dev(hcidev); 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci if (priv->adapter->hs_state != HS_ACTIVATED) { 16658c2ecf20Sopenharmony_ci if (btmrvl_enable_hs(priv)) { 16668c2ecf20Sopenharmony_ci BT_ERR("HS not activated, suspend failed!"); 16678c2ecf20Sopenharmony_ci /* Disable platform specific wakeup interrupt */ 16688c2ecf20Sopenharmony_ci if (card->plt_wake_cfg && 16698c2ecf20Sopenharmony_ci card->plt_wake_cfg->irq_bt >= 0 && 16708c2ecf20Sopenharmony_ci device_may_wakeup(dev)) { 16718c2ecf20Sopenharmony_ci disable_irq_wake(card->plt_wake_cfg->irq_bt); 16728c2ecf20Sopenharmony_ci disable_irq(card->plt_wake_cfg->irq_bt); 16738c2ecf20Sopenharmony_ci } 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci priv->adapter->is_suspending = false; 16768c2ecf20Sopenharmony_ci return -EBUSY; 16778c2ecf20Sopenharmony_ci } 16788c2ecf20Sopenharmony_ci } 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci priv->adapter->is_suspending = false; 16818c2ecf20Sopenharmony_ci priv->adapter->is_suspended = true; 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci /* We will keep the power when hs enabled successfully */ 16848c2ecf20Sopenharmony_ci if (priv->adapter->hs_state == HS_ACTIVATED) { 16858c2ecf20Sopenharmony_ci BT_DBG("suspend with MMC_PM_KEEP_POWER"); 16868c2ecf20Sopenharmony_ci return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); 16878c2ecf20Sopenharmony_ci } 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci BT_DBG("suspend without MMC_PM_KEEP_POWER"); 16908c2ecf20Sopenharmony_ci return 0; 16918c2ecf20Sopenharmony_ci} 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_cistatic int btmrvl_sdio_resume(struct device *dev) 16948c2ecf20Sopenharmony_ci{ 16958c2ecf20Sopenharmony_ci struct sdio_func *func = dev_to_sdio_func(dev); 16968c2ecf20Sopenharmony_ci struct btmrvl_sdio_card *card; 16978c2ecf20Sopenharmony_ci struct btmrvl_private *priv; 16988c2ecf20Sopenharmony_ci mmc_pm_flag_t pm_flags; 16998c2ecf20Sopenharmony_ci struct hci_dev *hcidev; 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci if (func) { 17028c2ecf20Sopenharmony_ci pm_flags = sdio_get_host_pm_caps(func); 17038c2ecf20Sopenharmony_ci BT_DBG("%s: resume: PM flags = 0x%x", sdio_func_id(func), 17048c2ecf20Sopenharmony_ci pm_flags); 17058c2ecf20Sopenharmony_ci card = sdio_get_drvdata(func); 17068c2ecf20Sopenharmony_ci if (!card || !card->priv) { 17078c2ecf20Sopenharmony_ci BT_ERR("card or priv structure is not valid"); 17088c2ecf20Sopenharmony_ci return 0; 17098c2ecf20Sopenharmony_ci } 17108c2ecf20Sopenharmony_ci } else { 17118c2ecf20Sopenharmony_ci BT_ERR("sdio_func is not specified"); 17128c2ecf20Sopenharmony_ci return 0; 17138c2ecf20Sopenharmony_ci } 17148c2ecf20Sopenharmony_ci priv = card->priv; 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci if (!priv->adapter->is_suspended) { 17178c2ecf20Sopenharmony_ci BT_DBG("device already resumed"); 17188c2ecf20Sopenharmony_ci return 0; 17198c2ecf20Sopenharmony_ci } 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci priv->hw_wakeup_firmware(priv); 17228c2ecf20Sopenharmony_ci priv->adapter->hs_state = HS_DEACTIVATED; 17238c2ecf20Sopenharmony_ci hcidev = priv->btmrvl_dev.hcidev; 17248c2ecf20Sopenharmony_ci BT_DBG("%s: HS DEACTIVATED in resume!", hcidev->name); 17258c2ecf20Sopenharmony_ci priv->adapter->is_suspended = false; 17268c2ecf20Sopenharmony_ci BT_DBG("%s: SDIO resume", hcidev->name); 17278c2ecf20Sopenharmony_ci hci_resume_dev(hcidev); 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci /* Disable platform specific wakeup interrupt */ 17308c2ecf20Sopenharmony_ci if (card->plt_wake_cfg && card->plt_wake_cfg->irq_bt >= 0 && 17318c2ecf20Sopenharmony_ci device_may_wakeup(dev)) { 17328c2ecf20Sopenharmony_ci disable_irq_wake(card->plt_wake_cfg->irq_bt); 17338c2ecf20Sopenharmony_ci disable_irq(card->plt_wake_cfg->irq_bt); 17348c2ecf20Sopenharmony_ci if (card->plt_wake_cfg->wake_by_bt) 17358c2ecf20Sopenharmony_ci /* Undo our disable, since interrupt handler already 17368c2ecf20Sopenharmony_ci * did this. 17378c2ecf20Sopenharmony_ci */ 17388c2ecf20Sopenharmony_ci enable_irq(card->plt_wake_cfg->irq_bt); 17398c2ecf20Sopenharmony_ci } 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci return 0; 17428c2ecf20Sopenharmony_ci} 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_cistatic const struct dev_pm_ops btmrvl_sdio_pm_ops = { 17458c2ecf20Sopenharmony_ci .suspend = btmrvl_sdio_suspend, 17468c2ecf20Sopenharmony_ci .resume = btmrvl_sdio_resume, 17478c2ecf20Sopenharmony_ci}; 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_cistatic struct sdio_driver bt_mrvl_sdio = { 17508c2ecf20Sopenharmony_ci .name = "btmrvl_sdio", 17518c2ecf20Sopenharmony_ci .id_table = btmrvl_sdio_ids, 17528c2ecf20Sopenharmony_ci .probe = btmrvl_sdio_probe, 17538c2ecf20Sopenharmony_ci .remove = btmrvl_sdio_remove, 17548c2ecf20Sopenharmony_ci .drv = { 17558c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 17568c2ecf20Sopenharmony_ci .coredump = btmrvl_sdio_coredump, 17578c2ecf20Sopenharmony_ci .pm = &btmrvl_sdio_pm_ops, 17588c2ecf20Sopenharmony_ci } 17598c2ecf20Sopenharmony_ci}; 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_cistatic int __init btmrvl_sdio_init_module(void) 17628c2ecf20Sopenharmony_ci{ 17638c2ecf20Sopenharmony_ci if (sdio_register_driver(&bt_mrvl_sdio) != 0) { 17648c2ecf20Sopenharmony_ci BT_ERR("SDIO Driver Registration Failed"); 17658c2ecf20Sopenharmony_ci return -ENODEV; 17668c2ecf20Sopenharmony_ci } 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci /* Clear the flag in case user removes the card. */ 17698c2ecf20Sopenharmony_ci user_rmmod = 0; 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci return 0; 17728c2ecf20Sopenharmony_ci} 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_cistatic void __exit btmrvl_sdio_exit_module(void) 17758c2ecf20Sopenharmony_ci{ 17768c2ecf20Sopenharmony_ci /* Set the flag as user is removing this module. */ 17778c2ecf20Sopenharmony_ci user_rmmod = 1; 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci sdio_unregister_driver(&bt_mrvl_sdio); 17808c2ecf20Sopenharmony_ci} 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_cimodule_init(btmrvl_sdio_init_module); 17838c2ecf20Sopenharmony_cimodule_exit(btmrvl_sdio_exit_module); 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marvell International Ltd."); 17868c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Marvell BT-over-SDIO driver ver " VERSION); 17878c2ecf20Sopenharmony_ciMODULE_VERSION(VERSION); 17888c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 17898c2ecf20Sopenharmony_ciMODULE_FIRMWARE("mrvl/sd8688_helper.bin"); 17908c2ecf20Sopenharmony_ciMODULE_FIRMWARE("mrvl/sd8688.bin"); 17918c2ecf20Sopenharmony_ciMODULE_FIRMWARE("mrvl/sd8787_uapsta.bin"); 17928c2ecf20Sopenharmony_ciMODULE_FIRMWARE("mrvl/sd8797_uapsta.bin"); 17938c2ecf20Sopenharmony_ciMODULE_FIRMWARE("mrvl/sd8887_uapsta.bin"); 17948c2ecf20Sopenharmony_ciMODULE_FIRMWARE("mrvl/sd8897_uapsta.bin"); 17958c2ecf20Sopenharmony_ciMODULE_FIRMWARE("mrvl/sdsd8977_combo_v2.bin"); 17968c2ecf20Sopenharmony_ciMODULE_FIRMWARE("mrvl/sd8987_uapsta.bin"); 17978c2ecf20Sopenharmony_ciMODULE_FIRMWARE("mrvl/sdsd8997_combo_v4.bin"); 1798