162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * NXP Wireless LAN device driver: SDIO specific handling 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2011-2020 NXP 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/firmware.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "decl.h" 1162306a36Sopenharmony_ci#include "ioctl.h" 1262306a36Sopenharmony_ci#include "util.h" 1362306a36Sopenharmony_ci#include "fw.h" 1462306a36Sopenharmony_ci#include "main.h" 1562306a36Sopenharmony_ci#include "wmm.h" 1662306a36Sopenharmony_ci#include "11n.h" 1762306a36Sopenharmony_ci#include "sdio.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define SDIO_VERSION "1.0" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic void mwifiex_sdio_work(struct work_struct *work); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic struct mwifiex_if_ops sdio_ops; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = { 2762306a36Sopenharmony_ci .start_rd_port = 1, 2862306a36Sopenharmony_ci .start_wr_port = 1, 2962306a36Sopenharmony_ci .base_0_reg = 0x0040, 3062306a36Sopenharmony_ci .base_1_reg = 0x0041, 3162306a36Sopenharmony_ci .poll_reg = 0x30, 3262306a36Sopenharmony_ci .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK, 3362306a36Sopenharmony_ci .host_int_rsr_reg = 0x1, 3462306a36Sopenharmony_ci .host_int_mask_reg = 0x02, 3562306a36Sopenharmony_ci .host_int_status_reg = 0x03, 3662306a36Sopenharmony_ci .status_reg_0 = 0x60, 3762306a36Sopenharmony_ci .status_reg_1 = 0x61, 3862306a36Sopenharmony_ci .sdio_int_mask = 0x3f, 3962306a36Sopenharmony_ci .data_port_mask = 0x0000fffe, 4062306a36Sopenharmony_ci .io_port_0_reg = 0x78, 4162306a36Sopenharmony_ci .io_port_1_reg = 0x79, 4262306a36Sopenharmony_ci .io_port_2_reg = 0x7A, 4362306a36Sopenharmony_ci .max_mp_regs = 64, 4462306a36Sopenharmony_ci .rd_bitmap_l = 0x04, 4562306a36Sopenharmony_ci .rd_bitmap_u = 0x05, 4662306a36Sopenharmony_ci .wr_bitmap_l = 0x06, 4762306a36Sopenharmony_ci .wr_bitmap_u = 0x07, 4862306a36Sopenharmony_ci .rd_len_p0_l = 0x08, 4962306a36Sopenharmony_ci .rd_len_p0_u = 0x09, 5062306a36Sopenharmony_ci .card_misc_cfg_reg = 0x6c, 5162306a36Sopenharmony_ci .func1_dump_reg_start = 0x0, 5262306a36Sopenharmony_ci .func1_dump_reg_end = 0x9, 5362306a36Sopenharmony_ci .func1_scratch_reg = 0x60, 5462306a36Sopenharmony_ci .func1_spec_reg_num = 5, 5562306a36Sopenharmony_ci .func1_spec_reg_table = {0x28, 0x30, 0x34, 0x38, 0x3c}, 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = { 5962306a36Sopenharmony_ci .start_rd_port = 0, 6062306a36Sopenharmony_ci .start_wr_port = 0, 6162306a36Sopenharmony_ci .base_0_reg = 0x60, 6262306a36Sopenharmony_ci .base_1_reg = 0x61, 6362306a36Sopenharmony_ci .poll_reg = 0x50, 6462306a36Sopenharmony_ci .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | 6562306a36Sopenharmony_ci CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, 6662306a36Sopenharmony_ci .host_int_rsr_reg = 0x1, 6762306a36Sopenharmony_ci .host_int_status_reg = 0x03, 6862306a36Sopenharmony_ci .host_int_mask_reg = 0x02, 6962306a36Sopenharmony_ci .status_reg_0 = 0xc0, 7062306a36Sopenharmony_ci .status_reg_1 = 0xc1, 7162306a36Sopenharmony_ci .sdio_int_mask = 0xff, 7262306a36Sopenharmony_ci .data_port_mask = 0xffffffff, 7362306a36Sopenharmony_ci .io_port_0_reg = 0xD8, 7462306a36Sopenharmony_ci .io_port_1_reg = 0xD9, 7562306a36Sopenharmony_ci .io_port_2_reg = 0xDA, 7662306a36Sopenharmony_ci .max_mp_regs = 184, 7762306a36Sopenharmony_ci .rd_bitmap_l = 0x04, 7862306a36Sopenharmony_ci .rd_bitmap_u = 0x05, 7962306a36Sopenharmony_ci .rd_bitmap_1l = 0x06, 8062306a36Sopenharmony_ci .rd_bitmap_1u = 0x07, 8162306a36Sopenharmony_ci .wr_bitmap_l = 0x08, 8262306a36Sopenharmony_ci .wr_bitmap_u = 0x09, 8362306a36Sopenharmony_ci .wr_bitmap_1l = 0x0a, 8462306a36Sopenharmony_ci .wr_bitmap_1u = 0x0b, 8562306a36Sopenharmony_ci .rd_len_p0_l = 0x0c, 8662306a36Sopenharmony_ci .rd_len_p0_u = 0x0d, 8762306a36Sopenharmony_ci .card_misc_cfg_reg = 0xcc, 8862306a36Sopenharmony_ci .card_cfg_2_1_reg = 0xcd, 8962306a36Sopenharmony_ci .cmd_rd_len_0 = 0xb4, 9062306a36Sopenharmony_ci .cmd_rd_len_1 = 0xb5, 9162306a36Sopenharmony_ci .cmd_rd_len_2 = 0xb6, 9262306a36Sopenharmony_ci .cmd_rd_len_3 = 0xb7, 9362306a36Sopenharmony_ci .cmd_cfg_0 = 0xb8, 9462306a36Sopenharmony_ci .cmd_cfg_1 = 0xb9, 9562306a36Sopenharmony_ci .cmd_cfg_2 = 0xba, 9662306a36Sopenharmony_ci .cmd_cfg_3 = 0xbb, 9762306a36Sopenharmony_ci .fw_dump_host_ready = 0xee, 9862306a36Sopenharmony_ci .fw_dump_ctrl = 0xe2, 9962306a36Sopenharmony_ci .fw_dump_start = 0xe3, 10062306a36Sopenharmony_ci .fw_dump_end = 0xea, 10162306a36Sopenharmony_ci .func1_dump_reg_start = 0x0, 10262306a36Sopenharmony_ci .func1_dump_reg_end = 0xb, 10362306a36Sopenharmony_ci .func1_scratch_reg = 0xc0, 10462306a36Sopenharmony_ci .func1_spec_reg_num = 8, 10562306a36Sopenharmony_ci .func1_spec_reg_table = {0x4C, 0x50, 0x54, 0x55, 0x58, 10662306a36Sopenharmony_ci 0x59, 0x5c, 0x5d}, 10762306a36Sopenharmony_ci}; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic const struct mwifiex_sdio_card_reg mwifiex_reg_sd8977 = { 11062306a36Sopenharmony_ci .start_rd_port = 0, 11162306a36Sopenharmony_ci .start_wr_port = 0, 11262306a36Sopenharmony_ci .base_0_reg = 0xF8, 11362306a36Sopenharmony_ci .base_1_reg = 0xF9, 11462306a36Sopenharmony_ci .poll_reg = 0x5C, 11562306a36Sopenharmony_ci .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | 11662306a36Sopenharmony_ci CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, 11762306a36Sopenharmony_ci .host_int_rsr_reg = 0x4, 11862306a36Sopenharmony_ci .host_int_status_reg = 0x0C, 11962306a36Sopenharmony_ci .host_int_mask_reg = 0x08, 12062306a36Sopenharmony_ci .status_reg_0 = 0xE8, 12162306a36Sopenharmony_ci .status_reg_1 = 0xE9, 12262306a36Sopenharmony_ci .sdio_int_mask = 0xff, 12362306a36Sopenharmony_ci .data_port_mask = 0xffffffff, 12462306a36Sopenharmony_ci .io_port_0_reg = 0xE4, 12562306a36Sopenharmony_ci .io_port_1_reg = 0xE5, 12662306a36Sopenharmony_ci .io_port_2_reg = 0xE6, 12762306a36Sopenharmony_ci .max_mp_regs = 196, 12862306a36Sopenharmony_ci .rd_bitmap_l = 0x10, 12962306a36Sopenharmony_ci .rd_bitmap_u = 0x11, 13062306a36Sopenharmony_ci .rd_bitmap_1l = 0x12, 13162306a36Sopenharmony_ci .rd_bitmap_1u = 0x13, 13262306a36Sopenharmony_ci .wr_bitmap_l = 0x14, 13362306a36Sopenharmony_ci .wr_bitmap_u = 0x15, 13462306a36Sopenharmony_ci .wr_bitmap_1l = 0x16, 13562306a36Sopenharmony_ci .wr_bitmap_1u = 0x17, 13662306a36Sopenharmony_ci .rd_len_p0_l = 0x18, 13762306a36Sopenharmony_ci .rd_len_p0_u = 0x19, 13862306a36Sopenharmony_ci .card_misc_cfg_reg = 0xd8, 13962306a36Sopenharmony_ci .card_cfg_2_1_reg = 0xd9, 14062306a36Sopenharmony_ci .cmd_rd_len_0 = 0xc0, 14162306a36Sopenharmony_ci .cmd_rd_len_1 = 0xc1, 14262306a36Sopenharmony_ci .cmd_rd_len_2 = 0xc2, 14362306a36Sopenharmony_ci .cmd_rd_len_3 = 0xc3, 14462306a36Sopenharmony_ci .cmd_cfg_0 = 0xc4, 14562306a36Sopenharmony_ci .cmd_cfg_1 = 0xc5, 14662306a36Sopenharmony_ci .cmd_cfg_2 = 0xc6, 14762306a36Sopenharmony_ci .cmd_cfg_3 = 0xc7, 14862306a36Sopenharmony_ci .fw_dump_host_ready = 0xcc, 14962306a36Sopenharmony_ci .fw_dump_ctrl = 0xf0, 15062306a36Sopenharmony_ci .fw_dump_start = 0xf1, 15162306a36Sopenharmony_ci .fw_dump_end = 0xf8, 15262306a36Sopenharmony_ci .func1_dump_reg_start = 0x10, 15362306a36Sopenharmony_ci .func1_dump_reg_end = 0x17, 15462306a36Sopenharmony_ci .func1_scratch_reg = 0xe8, 15562306a36Sopenharmony_ci .func1_spec_reg_num = 13, 15662306a36Sopenharmony_ci .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D, 15762306a36Sopenharmony_ci 0x60, 0x61, 0x62, 0x64, 15862306a36Sopenharmony_ci 0x65, 0x66, 0x68, 0x69, 15962306a36Sopenharmony_ci 0x6a}, 16062306a36Sopenharmony_ci}; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic const struct mwifiex_sdio_card_reg mwifiex_reg_sd8997 = { 16362306a36Sopenharmony_ci .start_rd_port = 0, 16462306a36Sopenharmony_ci .start_wr_port = 0, 16562306a36Sopenharmony_ci .base_0_reg = 0xF8, 16662306a36Sopenharmony_ci .base_1_reg = 0xF9, 16762306a36Sopenharmony_ci .poll_reg = 0x5C, 16862306a36Sopenharmony_ci .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | 16962306a36Sopenharmony_ci CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, 17062306a36Sopenharmony_ci .host_int_rsr_reg = 0x4, 17162306a36Sopenharmony_ci .host_int_status_reg = 0x0C, 17262306a36Sopenharmony_ci .host_int_mask_reg = 0x08, 17362306a36Sopenharmony_ci .host_strap_reg = 0xF4, 17462306a36Sopenharmony_ci .host_strap_mask = 0x01, 17562306a36Sopenharmony_ci .host_strap_value = 0x00, 17662306a36Sopenharmony_ci .status_reg_0 = 0xE8, 17762306a36Sopenharmony_ci .status_reg_1 = 0xE9, 17862306a36Sopenharmony_ci .sdio_int_mask = 0xff, 17962306a36Sopenharmony_ci .data_port_mask = 0xffffffff, 18062306a36Sopenharmony_ci .io_port_0_reg = 0xE4, 18162306a36Sopenharmony_ci .io_port_1_reg = 0xE5, 18262306a36Sopenharmony_ci .io_port_2_reg = 0xE6, 18362306a36Sopenharmony_ci .max_mp_regs = 196, 18462306a36Sopenharmony_ci .rd_bitmap_l = 0x10, 18562306a36Sopenharmony_ci .rd_bitmap_u = 0x11, 18662306a36Sopenharmony_ci .rd_bitmap_1l = 0x12, 18762306a36Sopenharmony_ci .rd_bitmap_1u = 0x13, 18862306a36Sopenharmony_ci .wr_bitmap_l = 0x14, 18962306a36Sopenharmony_ci .wr_bitmap_u = 0x15, 19062306a36Sopenharmony_ci .wr_bitmap_1l = 0x16, 19162306a36Sopenharmony_ci .wr_bitmap_1u = 0x17, 19262306a36Sopenharmony_ci .rd_len_p0_l = 0x18, 19362306a36Sopenharmony_ci .rd_len_p0_u = 0x19, 19462306a36Sopenharmony_ci .card_misc_cfg_reg = 0xd8, 19562306a36Sopenharmony_ci .card_cfg_2_1_reg = 0xd9, 19662306a36Sopenharmony_ci .cmd_rd_len_0 = 0xc0, 19762306a36Sopenharmony_ci .cmd_rd_len_1 = 0xc1, 19862306a36Sopenharmony_ci .cmd_rd_len_2 = 0xc2, 19962306a36Sopenharmony_ci .cmd_rd_len_3 = 0xc3, 20062306a36Sopenharmony_ci .cmd_cfg_0 = 0xc4, 20162306a36Sopenharmony_ci .cmd_cfg_1 = 0xc5, 20262306a36Sopenharmony_ci .cmd_cfg_2 = 0xc6, 20362306a36Sopenharmony_ci .cmd_cfg_3 = 0xc7, 20462306a36Sopenharmony_ci .fw_dump_host_ready = 0xcc, 20562306a36Sopenharmony_ci .fw_dump_ctrl = 0xf0, 20662306a36Sopenharmony_ci .fw_dump_start = 0xf1, 20762306a36Sopenharmony_ci .fw_dump_end = 0xf8, 20862306a36Sopenharmony_ci .func1_dump_reg_start = 0x10, 20962306a36Sopenharmony_ci .func1_dump_reg_end = 0x17, 21062306a36Sopenharmony_ci .func1_scratch_reg = 0xe8, 21162306a36Sopenharmony_ci .func1_spec_reg_num = 13, 21262306a36Sopenharmony_ci .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D, 21362306a36Sopenharmony_ci 0x60, 0x61, 0x62, 0x64, 21462306a36Sopenharmony_ci 0x65, 0x66, 0x68, 0x69, 21562306a36Sopenharmony_ci 0x6a}, 21662306a36Sopenharmony_ci}; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic const struct mwifiex_sdio_card_reg mwifiex_reg_sd8887 = { 21962306a36Sopenharmony_ci .start_rd_port = 0, 22062306a36Sopenharmony_ci .start_wr_port = 0, 22162306a36Sopenharmony_ci .base_0_reg = 0x6C, 22262306a36Sopenharmony_ci .base_1_reg = 0x6D, 22362306a36Sopenharmony_ci .poll_reg = 0x5C, 22462306a36Sopenharmony_ci .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | 22562306a36Sopenharmony_ci CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, 22662306a36Sopenharmony_ci .host_int_rsr_reg = 0x4, 22762306a36Sopenharmony_ci .host_int_status_reg = 0x0C, 22862306a36Sopenharmony_ci .host_int_mask_reg = 0x08, 22962306a36Sopenharmony_ci .status_reg_0 = 0x90, 23062306a36Sopenharmony_ci .status_reg_1 = 0x91, 23162306a36Sopenharmony_ci .sdio_int_mask = 0xff, 23262306a36Sopenharmony_ci .data_port_mask = 0xffffffff, 23362306a36Sopenharmony_ci .io_port_0_reg = 0xE4, 23462306a36Sopenharmony_ci .io_port_1_reg = 0xE5, 23562306a36Sopenharmony_ci .io_port_2_reg = 0xE6, 23662306a36Sopenharmony_ci .max_mp_regs = 196, 23762306a36Sopenharmony_ci .rd_bitmap_l = 0x10, 23862306a36Sopenharmony_ci .rd_bitmap_u = 0x11, 23962306a36Sopenharmony_ci .rd_bitmap_1l = 0x12, 24062306a36Sopenharmony_ci .rd_bitmap_1u = 0x13, 24162306a36Sopenharmony_ci .wr_bitmap_l = 0x14, 24262306a36Sopenharmony_ci .wr_bitmap_u = 0x15, 24362306a36Sopenharmony_ci .wr_bitmap_1l = 0x16, 24462306a36Sopenharmony_ci .wr_bitmap_1u = 0x17, 24562306a36Sopenharmony_ci .rd_len_p0_l = 0x18, 24662306a36Sopenharmony_ci .rd_len_p0_u = 0x19, 24762306a36Sopenharmony_ci .card_misc_cfg_reg = 0xd8, 24862306a36Sopenharmony_ci .card_cfg_2_1_reg = 0xd9, 24962306a36Sopenharmony_ci .cmd_rd_len_0 = 0xc0, 25062306a36Sopenharmony_ci .cmd_rd_len_1 = 0xc1, 25162306a36Sopenharmony_ci .cmd_rd_len_2 = 0xc2, 25262306a36Sopenharmony_ci .cmd_rd_len_3 = 0xc3, 25362306a36Sopenharmony_ci .cmd_cfg_0 = 0xc4, 25462306a36Sopenharmony_ci .cmd_cfg_1 = 0xc5, 25562306a36Sopenharmony_ci .cmd_cfg_2 = 0xc6, 25662306a36Sopenharmony_ci .cmd_cfg_3 = 0xc7, 25762306a36Sopenharmony_ci .func1_dump_reg_start = 0x10, 25862306a36Sopenharmony_ci .func1_dump_reg_end = 0x17, 25962306a36Sopenharmony_ci .func1_scratch_reg = 0x90, 26062306a36Sopenharmony_ci .func1_spec_reg_num = 13, 26162306a36Sopenharmony_ci .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, 26262306a36Sopenharmony_ci 0x61, 0x62, 0x64, 0x65, 0x66, 26362306a36Sopenharmony_ci 0x68, 0x69, 0x6a}, 26462306a36Sopenharmony_ci}; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic const struct mwifiex_sdio_card_reg mwifiex_reg_sd89xx = { 26762306a36Sopenharmony_ci .start_rd_port = 0, 26862306a36Sopenharmony_ci .start_wr_port = 0, 26962306a36Sopenharmony_ci .base_0_reg = 0xF8, 27062306a36Sopenharmony_ci .base_1_reg = 0xF9, 27162306a36Sopenharmony_ci .poll_reg = 0x5C, 27262306a36Sopenharmony_ci .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | 27362306a36Sopenharmony_ci CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, 27462306a36Sopenharmony_ci .host_int_rsr_reg = 0x4, 27562306a36Sopenharmony_ci .host_int_status_reg = 0x0C, 27662306a36Sopenharmony_ci .host_int_mask_reg = 0x08, 27762306a36Sopenharmony_ci .host_strap_reg = 0xF4, 27862306a36Sopenharmony_ci .host_strap_mask = 0x01, 27962306a36Sopenharmony_ci .host_strap_value = 0x00, 28062306a36Sopenharmony_ci .status_reg_0 = 0xE8, 28162306a36Sopenharmony_ci .status_reg_1 = 0xE9, 28262306a36Sopenharmony_ci .sdio_int_mask = 0xff, 28362306a36Sopenharmony_ci .data_port_mask = 0xffffffff, 28462306a36Sopenharmony_ci .io_port_0_reg = 0xE4, 28562306a36Sopenharmony_ci .io_port_1_reg = 0xE5, 28662306a36Sopenharmony_ci .io_port_2_reg = 0xE6, 28762306a36Sopenharmony_ci .max_mp_regs = 196, 28862306a36Sopenharmony_ci .rd_bitmap_l = 0x10, 28962306a36Sopenharmony_ci .rd_bitmap_u = 0x11, 29062306a36Sopenharmony_ci .rd_bitmap_1l = 0x12, 29162306a36Sopenharmony_ci .rd_bitmap_1u = 0x13, 29262306a36Sopenharmony_ci .wr_bitmap_l = 0x14, 29362306a36Sopenharmony_ci .wr_bitmap_u = 0x15, 29462306a36Sopenharmony_ci .wr_bitmap_1l = 0x16, 29562306a36Sopenharmony_ci .wr_bitmap_1u = 0x17, 29662306a36Sopenharmony_ci .rd_len_p0_l = 0x18, 29762306a36Sopenharmony_ci .rd_len_p0_u = 0x19, 29862306a36Sopenharmony_ci .card_misc_cfg_reg = 0xd8, 29962306a36Sopenharmony_ci .card_cfg_2_1_reg = 0xd9, 30062306a36Sopenharmony_ci .cmd_rd_len_0 = 0xc0, 30162306a36Sopenharmony_ci .cmd_rd_len_1 = 0xc1, 30262306a36Sopenharmony_ci .cmd_rd_len_2 = 0xc2, 30362306a36Sopenharmony_ci .cmd_rd_len_3 = 0xc3, 30462306a36Sopenharmony_ci .cmd_cfg_0 = 0xc4, 30562306a36Sopenharmony_ci .cmd_cfg_1 = 0xc5, 30662306a36Sopenharmony_ci .cmd_cfg_2 = 0xc6, 30762306a36Sopenharmony_ci .cmd_cfg_3 = 0xc7, 30862306a36Sopenharmony_ci .fw_dump_host_ready = 0xcc, 30962306a36Sopenharmony_ci .fw_dump_ctrl = 0xf9, 31062306a36Sopenharmony_ci .fw_dump_start = 0xf1, 31162306a36Sopenharmony_ci .fw_dump_end = 0xf8, 31262306a36Sopenharmony_ci .func1_dump_reg_start = 0x10, 31362306a36Sopenharmony_ci .func1_dump_reg_end = 0x17, 31462306a36Sopenharmony_ci .func1_scratch_reg = 0xE8, 31562306a36Sopenharmony_ci .func1_spec_reg_num = 13, 31662306a36Sopenharmony_ci .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, 31762306a36Sopenharmony_ci 0x61, 0x62, 0x64, 0x65, 0x66, 31862306a36Sopenharmony_ci 0x68, 0x69, 0x6a}, 31962306a36Sopenharmony_ci}; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = { 32262306a36Sopenharmony_ci .firmware = SD8786_DEFAULT_FW_NAME, 32362306a36Sopenharmony_ci .reg = &mwifiex_reg_sd87xx, 32462306a36Sopenharmony_ci .max_ports = 16, 32562306a36Sopenharmony_ci .mp_agg_pkt_limit = 8, 32662306a36Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, 32762306a36Sopenharmony_ci .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, 32862306a36Sopenharmony_ci .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, 32962306a36Sopenharmony_ci .supports_sdio_new_mode = false, 33062306a36Sopenharmony_ci .has_control_mask = true, 33162306a36Sopenharmony_ci .can_dump_fw = false, 33262306a36Sopenharmony_ci .can_auto_tdls = false, 33362306a36Sopenharmony_ci .can_ext_scan = false, 33462306a36Sopenharmony_ci .fw_ready_extra_delay = false, 33562306a36Sopenharmony_ci}; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = { 33862306a36Sopenharmony_ci .firmware = SD8787_DEFAULT_FW_NAME, 33962306a36Sopenharmony_ci .reg = &mwifiex_reg_sd87xx, 34062306a36Sopenharmony_ci .max_ports = 16, 34162306a36Sopenharmony_ci .mp_agg_pkt_limit = 8, 34262306a36Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, 34362306a36Sopenharmony_ci .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, 34462306a36Sopenharmony_ci .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, 34562306a36Sopenharmony_ci .supports_sdio_new_mode = false, 34662306a36Sopenharmony_ci .has_control_mask = true, 34762306a36Sopenharmony_ci .can_dump_fw = false, 34862306a36Sopenharmony_ci .can_auto_tdls = false, 34962306a36Sopenharmony_ci .can_ext_scan = true, 35062306a36Sopenharmony_ci .fw_ready_extra_delay = false, 35162306a36Sopenharmony_ci}; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = { 35462306a36Sopenharmony_ci .firmware = SD8797_DEFAULT_FW_NAME, 35562306a36Sopenharmony_ci .reg = &mwifiex_reg_sd87xx, 35662306a36Sopenharmony_ci .max_ports = 16, 35762306a36Sopenharmony_ci .mp_agg_pkt_limit = 8, 35862306a36Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, 35962306a36Sopenharmony_ci .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, 36062306a36Sopenharmony_ci .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, 36162306a36Sopenharmony_ci .supports_sdio_new_mode = false, 36262306a36Sopenharmony_ci .has_control_mask = true, 36362306a36Sopenharmony_ci .can_dump_fw = false, 36462306a36Sopenharmony_ci .can_auto_tdls = false, 36562306a36Sopenharmony_ci .can_ext_scan = true, 36662306a36Sopenharmony_ci .fw_ready_extra_delay = false, 36762306a36Sopenharmony_ci}; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = { 37062306a36Sopenharmony_ci .firmware = SD8897_DEFAULT_FW_NAME, 37162306a36Sopenharmony_ci .reg = &mwifiex_reg_sd8897, 37262306a36Sopenharmony_ci .max_ports = 32, 37362306a36Sopenharmony_ci .mp_agg_pkt_limit = 16, 37462306a36Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, 37562306a36Sopenharmony_ci .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, 37662306a36Sopenharmony_ci .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, 37762306a36Sopenharmony_ci .supports_sdio_new_mode = true, 37862306a36Sopenharmony_ci .has_control_mask = false, 37962306a36Sopenharmony_ci .can_dump_fw = true, 38062306a36Sopenharmony_ci .can_auto_tdls = false, 38162306a36Sopenharmony_ci .can_ext_scan = true, 38262306a36Sopenharmony_ci .fw_ready_extra_delay = false, 38362306a36Sopenharmony_ci}; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic const struct mwifiex_sdio_device mwifiex_sdio_sd8977 = { 38662306a36Sopenharmony_ci .firmware = SD8977_DEFAULT_FW_NAME, 38762306a36Sopenharmony_ci .reg = &mwifiex_reg_sd8977, 38862306a36Sopenharmony_ci .max_ports = 32, 38962306a36Sopenharmony_ci .mp_agg_pkt_limit = 16, 39062306a36Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, 39162306a36Sopenharmony_ci .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, 39262306a36Sopenharmony_ci .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, 39362306a36Sopenharmony_ci .supports_sdio_new_mode = true, 39462306a36Sopenharmony_ci .has_control_mask = false, 39562306a36Sopenharmony_ci .can_dump_fw = true, 39662306a36Sopenharmony_ci .fw_dump_enh = true, 39762306a36Sopenharmony_ci .can_auto_tdls = false, 39862306a36Sopenharmony_ci .can_ext_scan = true, 39962306a36Sopenharmony_ci .fw_ready_extra_delay = false, 40062306a36Sopenharmony_ci}; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic const struct mwifiex_sdio_device mwifiex_sdio_sd8978 = { 40362306a36Sopenharmony_ci .firmware_sdiouart = SD8978_SDIOUART_FW_NAME, 40462306a36Sopenharmony_ci .reg = &mwifiex_reg_sd89xx, 40562306a36Sopenharmony_ci .max_ports = 32, 40662306a36Sopenharmony_ci .mp_agg_pkt_limit = 16, 40762306a36Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, 40862306a36Sopenharmony_ci .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, 40962306a36Sopenharmony_ci .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, 41062306a36Sopenharmony_ci .supports_sdio_new_mode = true, 41162306a36Sopenharmony_ci .has_control_mask = false, 41262306a36Sopenharmony_ci .can_dump_fw = true, 41362306a36Sopenharmony_ci .fw_dump_enh = true, 41462306a36Sopenharmony_ci .can_auto_tdls = false, 41562306a36Sopenharmony_ci .can_ext_scan = true, 41662306a36Sopenharmony_ci .fw_ready_extra_delay = true, 41762306a36Sopenharmony_ci}; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic const struct mwifiex_sdio_device mwifiex_sdio_sd8997 = { 42062306a36Sopenharmony_ci .firmware = SD8997_DEFAULT_FW_NAME, 42162306a36Sopenharmony_ci .firmware_sdiouart = SD8997_SDIOUART_FW_NAME, 42262306a36Sopenharmony_ci .reg = &mwifiex_reg_sd8997, 42362306a36Sopenharmony_ci .max_ports = 32, 42462306a36Sopenharmony_ci .mp_agg_pkt_limit = 16, 42562306a36Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, 42662306a36Sopenharmony_ci .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, 42762306a36Sopenharmony_ci .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, 42862306a36Sopenharmony_ci .supports_sdio_new_mode = true, 42962306a36Sopenharmony_ci .has_control_mask = false, 43062306a36Sopenharmony_ci .can_dump_fw = true, 43162306a36Sopenharmony_ci .fw_dump_enh = true, 43262306a36Sopenharmony_ci .can_auto_tdls = false, 43362306a36Sopenharmony_ci .can_ext_scan = true, 43462306a36Sopenharmony_ci .fw_ready_extra_delay = false, 43562306a36Sopenharmony_ci}; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic const struct mwifiex_sdio_device mwifiex_sdio_sd8887 = { 43862306a36Sopenharmony_ci .firmware = SD8887_DEFAULT_FW_NAME, 43962306a36Sopenharmony_ci .reg = &mwifiex_reg_sd8887, 44062306a36Sopenharmony_ci .max_ports = 32, 44162306a36Sopenharmony_ci .mp_agg_pkt_limit = 16, 44262306a36Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, 44362306a36Sopenharmony_ci .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K, 44462306a36Sopenharmony_ci .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K, 44562306a36Sopenharmony_ci .supports_sdio_new_mode = true, 44662306a36Sopenharmony_ci .has_control_mask = false, 44762306a36Sopenharmony_ci .can_dump_fw = false, 44862306a36Sopenharmony_ci .can_auto_tdls = true, 44962306a36Sopenharmony_ci .can_ext_scan = true, 45062306a36Sopenharmony_ci .fw_ready_extra_delay = false, 45162306a36Sopenharmony_ci}; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic const struct mwifiex_sdio_device mwifiex_sdio_sd8987 = { 45462306a36Sopenharmony_ci .firmware = SD8987_DEFAULT_FW_NAME, 45562306a36Sopenharmony_ci .reg = &mwifiex_reg_sd89xx, 45662306a36Sopenharmony_ci .max_ports = 32, 45762306a36Sopenharmony_ci .mp_agg_pkt_limit = 16, 45862306a36Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, 45962306a36Sopenharmony_ci .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, 46062306a36Sopenharmony_ci .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, 46162306a36Sopenharmony_ci .supports_sdio_new_mode = true, 46262306a36Sopenharmony_ci .has_control_mask = false, 46362306a36Sopenharmony_ci .can_dump_fw = true, 46462306a36Sopenharmony_ci .fw_dump_enh = true, 46562306a36Sopenharmony_ci .can_auto_tdls = true, 46662306a36Sopenharmony_ci .can_ext_scan = true, 46762306a36Sopenharmony_ci .fw_ready_extra_delay = false, 46862306a36Sopenharmony_ci}; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic const struct mwifiex_sdio_device mwifiex_sdio_sd8801 = { 47162306a36Sopenharmony_ci .firmware = SD8801_DEFAULT_FW_NAME, 47262306a36Sopenharmony_ci .reg = &mwifiex_reg_sd87xx, 47362306a36Sopenharmony_ci .max_ports = 16, 47462306a36Sopenharmony_ci .mp_agg_pkt_limit = 8, 47562306a36Sopenharmony_ci .supports_sdio_new_mode = false, 47662306a36Sopenharmony_ci .has_control_mask = true, 47762306a36Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, 47862306a36Sopenharmony_ci .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, 47962306a36Sopenharmony_ci .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, 48062306a36Sopenharmony_ci .can_dump_fw = false, 48162306a36Sopenharmony_ci .can_auto_tdls = false, 48262306a36Sopenharmony_ci .can_ext_scan = true, 48362306a36Sopenharmony_ci .fw_ready_extra_delay = false, 48462306a36Sopenharmony_ci}; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic struct memory_type_mapping generic_mem_type_map[] = { 48762306a36Sopenharmony_ci {"DUMP", NULL, 0, 0xDD}, 48862306a36Sopenharmony_ci}; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic struct memory_type_mapping mem_type_mapping_tbl[] = { 49162306a36Sopenharmony_ci {"ITCM", NULL, 0, 0xF0}, 49262306a36Sopenharmony_ci {"DTCM", NULL, 0, 0xF1}, 49362306a36Sopenharmony_ci {"SQRAM", NULL, 0, 0xF2}, 49462306a36Sopenharmony_ci {"APU", NULL, 0, 0xF3}, 49562306a36Sopenharmony_ci {"CIU", NULL, 0, 0xF4}, 49662306a36Sopenharmony_ci {"ICU", NULL, 0, 0xF5}, 49762306a36Sopenharmony_ci {"MAC", NULL, 0, 0xF6}, 49862306a36Sopenharmony_ci {"EXT7", NULL, 0, 0xF7}, 49962306a36Sopenharmony_ci {"EXT8", NULL, 0, 0xF8}, 50062306a36Sopenharmony_ci {"EXT9", NULL, 0, 0xF9}, 50162306a36Sopenharmony_ci {"EXT10", NULL, 0, 0xFA}, 50262306a36Sopenharmony_ci {"EXT11", NULL, 0, 0xFB}, 50362306a36Sopenharmony_ci {"EXT12", NULL, 0, 0xFC}, 50462306a36Sopenharmony_ci {"EXT13", NULL, 0, 0xFD}, 50562306a36Sopenharmony_ci {"EXTLAST", NULL, 0, 0xFE}, 50662306a36Sopenharmony_ci}; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic const struct of_device_id mwifiex_sdio_of_match_table[] __maybe_unused = { 50962306a36Sopenharmony_ci { .compatible = "marvell,sd8787" }, 51062306a36Sopenharmony_ci { .compatible = "marvell,sd8897" }, 51162306a36Sopenharmony_ci { .compatible = "marvell,sd8978" }, 51262306a36Sopenharmony_ci { .compatible = "marvell,sd8997" }, 51362306a36Sopenharmony_ci { .compatible = "nxp,iw416" }, 51462306a36Sopenharmony_ci { } 51562306a36Sopenharmony_ci}; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci/* This function parse device tree node using mmc subnode devicetree API. 51862306a36Sopenharmony_ci * The device node is saved in card->plt_of_node. 51962306a36Sopenharmony_ci * if the device tree node exist and include interrupts attributes, this 52062306a36Sopenharmony_ci * function will also request platform specific wakeup interrupt. 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_cistatic int mwifiex_sdio_probe_of(struct device *dev) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci if (!of_match_node(mwifiex_sdio_of_match_table, dev->of_node)) { 52562306a36Sopenharmony_ci dev_err(dev, "required compatible string missing\n"); 52662306a36Sopenharmony_ci return -EINVAL; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci return 0; 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci/* 53362306a36Sopenharmony_ci * SDIO probe. 53462306a36Sopenharmony_ci * 53562306a36Sopenharmony_ci * This function probes an mwifiex device and registers it. It allocates 53662306a36Sopenharmony_ci * the card structure, enables SDIO function number and initiates the 53762306a36Sopenharmony_ci * device registration and initialization procedure by adding a logical 53862306a36Sopenharmony_ci * interface. 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_cistatic int 54162306a36Sopenharmony_cimwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci int ret; 54462306a36Sopenharmony_ci struct sdio_mmc_card *card = NULL; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci pr_debug("info: vendor=0x%4.04X device=0x%4.04X class=%d function=%d\n", 54762306a36Sopenharmony_ci func->vendor, func->device, func->class, func->num); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci card = devm_kzalloc(&func->dev, sizeof(*card), GFP_KERNEL); 55062306a36Sopenharmony_ci if (!card) 55162306a36Sopenharmony_ci return -ENOMEM; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci init_completion(&card->fw_done); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci card->func = func; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (id->driver_data) { 56062306a36Sopenharmony_ci struct mwifiex_sdio_device *data = (void *)id->driver_data; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci card->firmware = data->firmware; 56362306a36Sopenharmony_ci card->firmware_sdiouart = data->firmware_sdiouart; 56462306a36Sopenharmony_ci card->reg = data->reg; 56562306a36Sopenharmony_ci card->max_ports = data->max_ports; 56662306a36Sopenharmony_ci card->mp_agg_pkt_limit = data->mp_agg_pkt_limit; 56762306a36Sopenharmony_ci card->supports_sdio_new_mode = data->supports_sdio_new_mode; 56862306a36Sopenharmony_ci card->has_control_mask = data->has_control_mask; 56962306a36Sopenharmony_ci card->tx_buf_size = data->tx_buf_size; 57062306a36Sopenharmony_ci card->mp_tx_agg_buf_size = data->mp_tx_agg_buf_size; 57162306a36Sopenharmony_ci card->mp_rx_agg_buf_size = data->mp_rx_agg_buf_size; 57262306a36Sopenharmony_ci card->can_dump_fw = data->can_dump_fw; 57362306a36Sopenharmony_ci card->fw_dump_enh = data->fw_dump_enh; 57462306a36Sopenharmony_ci card->can_auto_tdls = data->can_auto_tdls; 57562306a36Sopenharmony_ci card->can_ext_scan = data->can_ext_scan; 57662306a36Sopenharmony_ci card->fw_ready_extra_delay = data->fw_ready_extra_delay; 57762306a36Sopenharmony_ci INIT_WORK(&card->work, mwifiex_sdio_work); 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci sdio_claim_host(func); 58162306a36Sopenharmony_ci ret = sdio_enable_func(func); 58262306a36Sopenharmony_ci sdio_release_host(func); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (ret) { 58562306a36Sopenharmony_ci dev_err(&func->dev, "failed to enable function\n"); 58662306a36Sopenharmony_ci return ret; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci /* device tree node parsing and platform specific configuration*/ 59062306a36Sopenharmony_ci if (func->dev.of_node) { 59162306a36Sopenharmony_ci ret = mwifiex_sdio_probe_of(&func->dev); 59262306a36Sopenharmony_ci if (ret) 59362306a36Sopenharmony_ci goto err_disable; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci ret = mwifiex_add_card(card, &card->fw_done, &sdio_ops, 59762306a36Sopenharmony_ci MWIFIEX_SDIO, &func->dev); 59862306a36Sopenharmony_ci if (ret) { 59962306a36Sopenharmony_ci dev_err(&func->dev, "add card failed\n"); 60062306a36Sopenharmony_ci goto err_disable; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci return 0; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cierr_disable: 60662306a36Sopenharmony_ci sdio_claim_host(func); 60762306a36Sopenharmony_ci sdio_disable_func(func); 60862306a36Sopenharmony_ci sdio_release_host(func); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci return ret; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci/* 61462306a36Sopenharmony_ci * SDIO resume. 61562306a36Sopenharmony_ci * 61662306a36Sopenharmony_ci * Kernel needs to suspend all functions separately. Therefore all 61762306a36Sopenharmony_ci * registered functions must have drivers with suspend and resume 61862306a36Sopenharmony_ci * methods. Failing that the kernel simply removes the whole card. 61962306a36Sopenharmony_ci * 62062306a36Sopenharmony_ci * If already not resumed, this function turns on the traffic and 62162306a36Sopenharmony_ci * sends a host sleep cancel request to the firmware. 62262306a36Sopenharmony_ci */ 62362306a36Sopenharmony_cistatic int mwifiex_sdio_resume(struct device *dev) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct sdio_func *func = dev_to_sdio_func(dev); 62662306a36Sopenharmony_ci struct sdio_mmc_card *card; 62762306a36Sopenharmony_ci struct mwifiex_adapter *adapter; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci card = sdio_get_drvdata(func); 63062306a36Sopenharmony_ci if (!card || !card->adapter) { 63162306a36Sopenharmony_ci dev_err(dev, "resume: invalid card or adapter\n"); 63262306a36Sopenharmony_ci return 0; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci adapter = card->adapter; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (!test_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags)) { 63862306a36Sopenharmony_ci mwifiex_dbg(adapter, WARN, 63962306a36Sopenharmony_ci "device already resumed\n"); 64062306a36Sopenharmony_ci return 0; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci clear_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci /* Disable Host Sleep */ 64662306a36Sopenharmony_ci mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), 64762306a36Sopenharmony_ci MWIFIEX_SYNC_CMD); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci mwifiex_disable_wake(adapter); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci return 0; 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci/* Write data into SDIO card register. Caller claims SDIO device. */ 65562306a36Sopenharmony_cistatic int 65662306a36Sopenharmony_cimwifiex_write_reg_locked(struct sdio_func *func, u32 reg, u8 data) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci int ret = -1; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci sdio_writeb(func, data, reg, &ret); 66162306a36Sopenharmony_ci return ret; 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci/* This function writes data into SDIO card register. 66562306a36Sopenharmony_ci */ 66662306a36Sopenharmony_cistatic int 66762306a36Sopenharmony_cimwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u8 data) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 67062306a36Sopenharmony_ci int ret; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci sdio_claim_host(card->func); 67362306a36Sopenharmony_ci ret = mwifiex_write_reg_locked(card->func, reg, data); 67462306a36Sopenharmony_ci sdio_release_host(card->func); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci return ret; 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci/* This function reads data from SDIO card register. 68062306a36Sopenharmony_ci */ 68162306a36Sopenharmony_cistatic int 68262306a36Sopenharmony_cimwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u8 *data) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 68562306a36Sopenharmony_ci int ret = -1; 68662306a36Sopenharmony_ci u8 val; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci sdio_claim_host(card->func); 68962306a36Sopenharmony_ci val = sdio_readb(card->func, reg, &ret); 69062306a36Sopenharmony_ci sdio_release_host(card->func); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci *data = val; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci return ret; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci/* This function writes multiple data into SDIO card memory. 69862306a36Sopenharmony_ci * 69962306a36Sopenharmony_ci * This does not work in suspended mode. 70062306a36Sopenharmony_ci */ 70162306a36Sopenharmony_cistatic int 70262306a36Sopenharmony_cimwifiex_write_data_sync(struct mwifiex_adapter *adapter, 70362306a36Sopenharmony_ci u8 *buffer, u32 pkt_len, u32 port) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 70662306a36Sopenharmony_ci int ret; 70762306a36Sopenharmony_ci u8 blk_mode = 70862306a36Sopenharmony_ci (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE; 70962306a36Sopenharmony_ci u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1; 71062306a36Sopenharmony_ci u32 blk_cnt = 71162306a36Sopenharmony_ci (blk_mode == 71262306a36Sopenharmony_ci BLOCK_MODE) ? (pkt_len / 71362306a36Sopenharmony_ci MWIFIEX_SDIO_BLOCK_SIZE) : pkt_len; 71462306a36Sopenharmony_ci u32 ioport = (port & MWIFIEX_SDIO_IO_PORT_MASK); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci if (test_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags)) { 71762306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 71862306a36Sopenharmony_ci "%s: not allowed while suspended\n", __func__); 71962306a36Sopenharmony_ci return -1; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci sdio_claim_host(card->func); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci ret = sdio_writesb(card->func, ioport, buffer, blk_cnt * blk_size); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci sdio_release_host(card->func); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci return ret; 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci/* This function reads multiple data from SDIO card memory. 73262306a36Sopenharmony_ci */ 73362306a36Sopenharmony_cistatic int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *buffer, 73462306a36Sopenharmony_ci u32 len, u32 port, u8 claim) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 73762306a36Sopenharmony_ci int ret; 73862306a36Sopenharmony_ci u8 blk_mode = (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE 73962306a36Sopenharmony_ci : BLOCK_MODE; 74062306a36Sopenharmony_ci u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1; 74162306a36Sopenharmony_ci u32 blk_cnt = (blk_mode == BLOCK_MODE) ? (len / MWIFIEX_SDIO_BLOCK_SIZE) 74262306a36Sopenharmony_ci : len; 74362306a36Sopenharmony_ci u32 ioport = (port & MWIFIEX_SDIO_IO_PORT_MASK); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci if (claim) 74662306a36Sopenharmony_ci sdio_claim_host(card->func); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci ret = sdio_readsb(card->func, buffer, ioport, blk_cnt * blk_size); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci if (claim) 75162306a36Sopenharmony_ci sdio_release_host(card->func); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci return ret; 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci/* This function reads the firmware status. 75762306a36Sopenharmony_ci */ 75862306a36Sopenharmony_cistatic int 75962306a36Sopenharmony_cimwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 76262306a36Sopenharmony_ci const struct mwifiex_sdio_card_reg *reg = card->reg; 76362306a36Sopenharmony_ci u8 fws0, fws1; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci if (mwifiex_read_reg(adapter, reg->status_reg_0, &fws0)) 76662306a36Sopenharmony_ci return -1; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci if (mwifiex_read_reg(adapter, reg->status_reg_1, &fws1)) 76962306a36Sopenharmony_ci return -1; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci *dat = (u16)((fws1 << 8) | fws0); 77262306a36Sopenharmony_ci return 0; 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci/* This function checks the firmware status in card. 77662306a36Sopenharmony_ci */ 77762306a36Sopenharmony_cistatic int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, 77862306a36Sopenharmony_ci u32 poll_num) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 78162306a36Sopenharmony_ci int ret = 0; 78262306a36Sopenharmony_ci u16 firmware_stat = 0; 78362306a36Sopenharmony_ci u32 tries; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci for (tries = 0; tries < poll_num; tries++) { 78662306a36Sopenharmony_ci ret = mwifiex_sdio_read_fw_status(adapter, &firmware_stat); 78762306a36Sopenharmony_ci if (ret) 78862306a36Sopenharmony_ci continue; 78962306a36Sopenharmony_ci if (firmware_stat == FIRMWARE_READY_SDIO) { 79062306a36Sopenharmony_ci ret = 0; 79162306a36Sopenharmony_ci break; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci msleep(100); 79562306a36Sopenharmony_ci ret = -1; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci if (card->fw_ready_extra_delay && 79962306a36Sopenharmony_ci firmware_stat == FIRMWARE_READY_SDIO) 80062306a36Sopenharmony_ci /* firmware might pretend to be ready, when it's not. 80162306a36Sopenharmony_ci * Wait a little bit more as a workaround. 80262306a36Sopenharmony_ci */ 80362306a36Sopenharmony_ci msleep(100); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci return ret; 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci/* This function checks if WLAN is the winner. 80962306a36Sopenharmony_ci */ 81062306a36Sopenharmony_cistatic int mwifiex_check_winner_status(struct mwifiex_adapter *adapter) 81162306a36Sopenharmony_ci{ 81262306a36Sopenharmony_ci int ret = 0; 81362306a36Sopenharmony_ci u8 winner = 0; 81462306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (mwifiex_read_reg(adapter, card->reg->status_reg_0, &winner)) 81762306a36Sopenharmony_ci return -1; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci if (winner) 82062306a36Sopenharmony_ci adapter->winner = 0; 82162306a36Sopenharmony_ci else 82262306a36Sopenharmony_ci adapter->winner = 1; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci return ret; 82562306a36Sopenharmony_ci} 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci/* 82862306a36Sopenharmony_ci * SDIO remove. 82962306a36Sopenharmony_ci * 83062306a36Sopenharmony_ci * This function removes the interface and frees up the card structure. 83162306a36Sopenharmony_ci */ 83262306a36Sopenharmony_cistatic void 83362306a36Sopenharmony_cimwifiex_sdio_remove(struct sdio_func *func) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci struct sdio_mmc_card *card; 83662306a36Sopenharmony_ci struct mwifiex_adapter *adapter; 83762306a36Sopenharmony_ci struct mwifiex_private *priv; 83862306a36Sopenharmony_ci int ret = 0; 83962306a36Sopenharmony_ci u16 firmware_stat; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci card = sdio_get_drvdata(func); 84262306a36Sopenharmony_ci if (!card) 84362306a36Sopenharmony_ci return; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci wait_for_completion(&card->fw_done); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci adapter = card->adapter; 84862306a36Sopenharmony_ci if (!adapter || !adapter->priv_num) 84962306a36Sopenharmony_ci return; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, "info: SDIO func num=%d\n", func->num); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci ret = mwifiex_sdio_read_fw_status(adapter, &firmware_stat); 85462306a36Sopenharmony_ci if (!ret && firmware_stat == FIRMWARE_READY_SDIO && 85562306a36Sopenharmony_ci !adapter->mfg_mode) { 85662306a36Sopenharmony_ci mwifiex_deauthenticate_all(adapter); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); 85962306a36Sopenharmony_ci mwifiex_disable_auto_ds(priv); 86062306a36Sopenharmony_ci mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci mwifiex_remove_card(adapter); 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci/* 86762306a36Sopenharmony_ci * SDIO suspend. 86862306a36Sopenharmony_ci * 86962306a36Sopenharmony_ci * Kernel needs to suspend all functions separately. Therefore all 87062306a36Sopenharmony_ci * registered functions must have drivers with suspend and resume 87162306a36Sopenharmony_ci * methods. Failing that the kernel simply removes the whole card. 87262306a36Sopenharmony_ci * 87362306a36Sopenharmony_ci * If already not suspended, this function allocates and sends a host 87462306a36Sopenharmony_ci * sleep activate request to the firmware and turns off the traffic. 87562306a36Sopenharmony_ci */ 87662306a36Sopenharmony_cistatic int mwifiex_sdio_suspend(struct device *dev) 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci struct sdio_func *func = dev_to_sdio_func(dev); 87962306a36Sopenharmony_ci struct sdio_mmc_card *card; 88062306a36Sopenharmony_ci struct mwifiex_adapter *adapter; 88162306a36Sopenharmony_ci mmc_pm_flag_t pm_flag = 0; 88262306a36Sopenharmony_ci int ret = 0; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci pm_flag = sdio_get_host_pm_caps(func); 88562306a36Sopenharmony_ci pr_debug("cmd: %s: suspend: PM flag = 0x%x\n", 88662306a36Sopenharmony_ci sdio_func_id(func), pm_flag); 88762306a36Sopenharmony_ci if (!(pm_flag & MMC_PM_KEEP_POWER)) { 88862306a36Sopenharmony_ci dev_err(dev, "%s: cannot remain alive while host is" 88962306a36Sopenharmony_ci " suspended\n", sdio_func_id(func)); 89062306a36Sopenharmony_ci return -ENOSYS; 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci card = sdio_get_drvdata(func); 89462306a36Sopenharmony_ci if (!card) { 89562306a36Sopenharmony_ci dev_err(dev, "suspend: invalid card\n"); 89662306a36Sopenharmony_ci return 0; 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci /* Might still be loading firmware */ 90062306a36Sopenharmony_ci wait_for_completion(&card->fw_done); 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci adapter = card->adapter; 90362306a36Sopenharmony_ci if (!adapter) { 90462306a36Sopenharmony_ci dev_err(dev, "adapter is not valid\n"); 90562306a36Sopenharmony_ci return 0; 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci if (!adapter->is_up) 90962306a36Sopenharmony_ci return -EBUSY; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci mwifiex_enable_wake(adapter); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci /* Enable the Host Sleep */ 91462306a36Sopenharmony_ci if (!mwifiex_enable_hs(adapter)) { 91562306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 91662306a36Sopenharmony_ci "cmd: failed to suspend\n"); 91762306a36Sopenharmony_ci clear_bit(MWIFIEX_IS_HS_ENABLING, &adapter->work_flags); 91862306a36Sopenharmony_ci mwifiex_disable_wake(adapter); 91962306a36Sopenharmony_ci return -EFAULT; 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 92362306a36Sopenharmony_ci "cmd: suspend with MMC_PM_KEEP_POWER\n"); 92462306a36Sopenharmony_ci ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci /* Indicate device suspended */ 92762306a36Sopenharmony_ci set_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags); 92862306a36Sopenharmony_ci clear_bit(MWIFIEX_IS_HS_ENABLING, &adapter->work_flags); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci return ret; 93162306a36Sopenharmony_ci} 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_cistatic void mwifiex_sdio_coredump(struct device *dev) 93462306a36Sopenharmony_ci{ 93562306a36Sopenharmony_ci struct sdio_func *func = dev_to_sdio_func(dev); 93662306a36Sopenharmony_ci struct sdio_mmc_card *card; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci card = sdio_get_drvdata(func); 93962306a36Sopenharmony_ci if (!test_and_set_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, 94062306a36Sopenharmony_ci &card->work_flags)) 94162306a36Sopenharmony_ci schedule_work(&card->work); 94262306a36Sopenharmony_ci} 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci/* WLAN IDs */ 94562306a36Sopenharmony_cistatic const struct sdio_device_id mwifiex_ids[] = { 94662306a36Sopenharmony_ci {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786_WLAN), 94762306a36Sopenharmony_ci .driver_data = (unsigned long) &mwifiex_sdio_sd8786}, 94862306a36Sopenharmony_ci {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787_WLAN), 94962306a36Sopenharmony_ci .driver_data = (unsigned long) &mwifiex_sdio_sd8787}, 95062306a36Sopenharmony_ci {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_WLAN), 95162306a36Sopenharmony_ci .driver_data = (unsigned long) &mwifiex_sdio_sd8797}, 95262306a36Sopenharmony_ci {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897_WLAN), 95362306a36Sopenharmony_ci .driver_data = (unsigned long) &mwifiex_sdio_sd8897}, 95462306a36Sopenharmony_ci {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887_WLAN), 95562306a36Sopenharmony_ci .driver_data = (unsigned long)&mwifiex_sdio_sd8887}, 95662306a36Sopenharmony_ci {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8801_WLAN), 95762306a36Sopenharmony_ci .driver_data = (unsigned long)&mwifiex_sdio_sd8801}, 95862306a36Sopenharmony_ci {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8977_WLAN), 95962306a36Sopenharmony_ci .driver_data = (unsigned long)&mwifiex_sdio_sd8977}, 96062306a36Sopenharmony_ci {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8978_WLAN), 96162306a36Sopenharmony_ci .driver_data = (unsigned long)&mwifiex_sdio_sd8978}, 96262306a36Sopenharmony_ci {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8987_WLAN), 96362306a36Sopenharmony_ci .driver_data = (unsigned long)&mwifiex_sdio_sd8987}, 96462306a36Sopenharmony_ci {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997_WLAN), 96562306a36Sopenharmony_ci .driver_data = (unsigned long)&mwifiex_sdio_sd8997}, 96662306a36Sopenharmony_ci {}, 96762306a36Sopenharmony_ci}; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(sdio, mwifiex_ids); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_cistatic const struct dev_pm_ops mwifiex_sdio_pm_ops = { 97262306a36Sopenharmony_ci .suspend = mwifiex_sdio_suspend, 97362306a36Sopenharmony_ci .resume = mwifiex_sdio_resume, 97462306a36Sopenharmony_ci}; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_cistatic struct sdio_driver mwifiex_sdio = { 97762306a36Sopenharmony_ci .name = "mwifiex_sdio", 97862306a36Sopenharmony_ci .id_table = mwifiex_ids, 97962306a36Sopenharmony_ci .probe = mwifiex_sdio_probe, 98062306a36Sopenharmony_ci .remove = mwifiex_sdio_remove, 98162306a36Sopenharmony_ci .drv = { 98262306a36Sopenharmony_ci .owner = THIS_MODULE, 98362306a36Sopenharmony_ci .coredump = mwifiex_sdio_coredump, 98462306a36Sopenharmony_ci .pm = &mwifiex_sdio_pm_ops, 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci}; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci/* 98962306a36Sopenharmony_ci * This function wakes up the card. 99062306a36Sopenharmony_ci * 99162306a36Sopenharmony_ci * A host power up command is written to the card configuration 99262306a36Sopenharmony_ci * register to wake up the card. 99362306a36Sopenharmony_ci */ 99462306a36Sopenharmony_cistatic int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci mwifiex_dbg(adapter, EVENT, 99762306a36Sopenharmony_ci "event: wakeup device...\n"); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci return mwifiex_write_reg(adapter, CONFIGURATION_REG, HOST_POWER_UP); 100062306a36Sopenharmony_ci} 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci/* 100362306a36Sopenharmony_ci * This function is called after the card has woken up. 100462306a36Sopenharmony_ci * 100562306a36Sopenharmony_ci * The card configuration register is reset. 100662306a36Sopenharmony_ci */ 100762306a36Sopenharmony_cistatic int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) 100862306a36Sopenharmony_ci{ 100962306a36Sopenharmony_ci mwifiex_dbg(adapter, EVENT, 101062306a36Sopenharmony_ci "cmd: wakeup device completed\n"); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci return mwifiex_write_reg(adapter, CONFIGURATION_REG, 0); 101362306a36Sopenharmony_ci} 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_cistatic int mwifiex_sdio_dnld_fw(struct mwifiex_adapter *adapter, 101662306a36Sopenharmony_ci struct mwifiex_fw_image *fw) 101762306a36Sopenharmony_ci{ 101862306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 101962306a36Sopenharmony_ci int ret; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci sdio_claim_host(card->func); 102262306a36Sopenharmony_ci ret = mwifiex_dnld_fw(adapter, fw); 102362306a36Sopenharmony_ci sdio_release_host(card->func); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci return ret; 102662306a36Sopenharmony_ci} 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci/* 102962306a36Sopenharmony_ci * This function is used to initialize IO ports for the 103062306a36Sopenharmony_ci * chipsets supporting SDIO new mode eg SD8897. 103162306a36Sopenharmony_ci */ 103262306a36Sopenharmony_cistatic int mwifiex_init_sdio_new_mode(struct mwifiex_adapter *adapter) 103362306a36Sopenharmony_ci{ 103462306a36Sopenharmony_ci u8 reg; 103562306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci adapter->ioport = MEM_PORT; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci /* enable sdio new mode */ 104062306a36Sopenharmony_ci if (mwifiex_read_reg(adapter, card->reg->card_cfg_2_1_reg, ®)) 104162306a36Sopenharmony_ci return -1; 104262306a36Sopenharmony_ci if (mwifiex_write_reg(adapter, card->reg->card_cfg_2_1_reg, 104362306a36Sopenharmony_ci reg | CMD53_NEW_MODE)) 104462306a36Sopenharmony_ci return -1; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci /* Configure cmd port and enable reading rx length from the register */ 104762306a36Sopenharmony_ci if (mwifiex_read_reg(adapter, card->reg->cmd_cfg_0, ®)) 104862306a36Sopenharmony_ci return -1; 104962306a36Sopenharmony_ci if (mwifiex_write_reg(adapter, card->reg->cmd_cfg_0, 105062306a36Sopenharmony_ci reg | CMD_PORT_RD_LEN_EN)) 105162306a36Sopenharmony_ci return -1; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci /* Enable Dnld/Upld ready auto reset for cmd port after cmd53 is 105462306a36Sopenharmony_ci * completed 105562306a36Sopenharmony_ci */ 105662306a36Sopenharmony_ci if (mwifiex_read_reg(adapter, card->reg->cmd_cfg_1, ®)) 105762306a36Sopenharmony_ci return -1; 105862306a36Sopenharmony_ci if (mwifiex_write_reg(adapter, card->reg->cmd_cfg_1, 105962306a36Sopenharmony_ci reg | CMD_PORT_AUTO_EN)) 106062306a36Sopenharmony_ci return -1; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci return 0; 106362306a36Sopenharmony_ci} 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci/* This function initializes the IO ports. 106662306a36Sopenharmony_ci * 106762306a36Sopenharmony_ci * The following operations are performed - 106862306a36Sopenharmony_ci * - Read the IO ports (0, 1 and 2) 106962306a36Sopenharmony_ci * - Set host interrupt Reset-To-Read to clear 107062306a36Sopenharmony_ci * - Set auto re-enable interrupt 107162306a36Sopenharmony_ci */ 107262306a36Sopenharmony_cistatic int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter) 107362306a36Sopenharmony_ci{ 107462306a36Sopenharmony_ci u8 reg; 107562306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci adapter->ioport = 0; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci if (card->supports_sdio_new_mode) { 108062306a36Sopenharmony_ci if (mwifiex_init_sdio_new_mode(adapter)) 108162306a36Sopenharmony_ci return -1; 108262306a36Sopenharmony_ci goto cont; 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci /* Read the IO port */ 108662306a36Sopenharmony_ci if (!mwifiex_read_reg(adapter, card->reg->io_port_0_reg, ®)) 108762306a36Sopenharmony_ci adapter->ioport |= (reg & 0xff); 108862306a36Sopenharmony_ci else 108962306a36Sopenharmony_ci return -1; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci if (!mwifiex_read_reg(adapter, card->reg->io_port_1_reg, ®)) 109262306a36Sopenharmony_ci adapter->ioport |= ((reg & 0xff) << 8); 109362306a36Sopenharmony_ci else 109462306a36Sopenharmony_ci return -1; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci if (!mwifiex_read_reg(adapter, card->reg->io_port_2_reg, ®)) 109762306a36Sopenharmony_ci adapter->ioport |= ((reg & 0xff) << 16); 109862306a36Sopenharmony_ci else 109962306a36Sopenharmony_ci return -1; 110062306a36Sopenharmony_cicont: 110162306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 110262306a36Sopenharmony_ci "info: SDIO FUNC1 IO port: %#x\n", adapter->ioport); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci /* Set Host interrupt reset to read to clear */ 110562306a36Sopenharmony_ci if (mwifiex_read_reg(adapter, card->reg->host_int_rsr_reg, ®)) 110662306a36Sopenharmony_ci return -1; 110762306a36Sopenharmony_ci if (mwifiex_write_reg(adapter, card->reg->host_int_rsr_reg, 110862306a36Sopenharmony_ci reg | card->reg->sdio_int_mask)) 110962306a36Sopenharmony_ci return -1; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci /* Dnld/Upld ready set to auto reset */ 111262306a36Sopenharmony_ci if (mwifiex_read_reg(adapter, card->reg->card_misc_cfg_reg, ®)) 111362306a36Sopenharmony_ci return -1; 111462306a36Sopenharmony_ci if (mwifiex_write_reg(adapter, card->reg->card_misc_cfg_reg, 111562306a36Sopenharmony_ci reg | AUTO_RE_ENABLE_INT)) 111662306a36Sopenharmony_ci return -1; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci return 0; 111962306a36Sopenharmony_ci} 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci/* 112262306a36Sopenharmony_ci * This function sends data to the card. 112362306a36Sopenharmony_ci */ 112462306a36Sopenharmony_cistatic int mwifiex_write_data_to_card(struct mwifiex_adapter *adapter, 112562306a36Sopenharmony_ci u8 *payload, u32 pkt_len, u32 port) 112662306a36Sopenharmony_ci{ 112762306a36Sopenharmony_ci u32 i = 0; 112862306a36Sopenharmony_ci int ret; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci do { 113162306a36Sopenharmony_ci ret = mwifiex_write_data_sync(adapter, payload, pkt_len, port); 113262306a36Sopenharmony_ci if (ret) { 113362306a36Sopenharmony_ci i++; 113462306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 113562306a36Sopenharmony_ci "host_to_card, write iomem\t" 113662306a36Sopenharmony_ci "(%d) failed: %d\n", i, ret); 113762306a36Sopenharmony_ci if (mwifiex_write_reg(adapter, CONFIGURATION_REG, 0x04)) 113862306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 113962306a36Sopenharmony_ci "write CFG reg failed\n"); 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci ret = -1; 114262306a36Sopenharmony_ci if (i > MAX_WRITE_IOMEM_RETRY) 114362306a36Sopenharmony_ci return ret; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci } while (ret == -1); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci return ret; 114862306a36Sopenharmony_ci} 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci/* 115162306a36Sopenharmony_ci * This function gets the read port. 115262306a36Sopenharmony_ci * 115362306a36Sopenharmony_ci * If control port bit is set in MP read bitmap, the control port 115462306a36Sopenharmony_ci * is returned, otherwise the current read port is returned and 115562306a36Sopenharmony_ci * the value is increased (provided it does not reach the maximum 115662306a36Sopenharmony_ci * limit, in which case it is reset to 1) 115762306a36Sopenharmony_ci */ 115862306a36Sopenharmony_cistatic int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port) 115962306a36Sopenharmony_ci{ 116062306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 116162306a36Sopenharmony_ci const struct mwifiex_sdio_card_reg *reg = card->reg; 116262306a36Sopenharmony_ci u32 rd_bitmap = card->mp_rd_bitmap; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci mwifiex_dbg(adapter, DATA, 116562306a36Sopenharmony_ci "data: mp_rd_bitmap=0x%08x\n", rd_bitmap); 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci if (card->supports_sdio_new_mode) { 116862306a36Sopenharmony_ci if (!(rd_bitmap & reg->data_port_mask)) 116962306a36Sopenharmony_ci return -1; 117062306a36Sopenharmony_ci } else { 117162306a36Sopenharmony_ci if (!(rd_bitmap & (CTRL_PORT_MASK | reg->data_port_mask))) 117262306a36Sopenharmony_ci return -1; 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci if ((card->has_control_mask) && 117662306a36Sopenharmony_ci (card->mp_rd_bitmap & CTRL_PORT_MASK)) { 117762306a36Sopenharmony_ci card->mp_rd_bitmap &= (u32) (~CTRL_PORT_MASK); 117862306a36Sopenharmony_ci *port = CTRL_PORT; 117962306a36Sopenharmony_ci mwifiex_dbg(adapter, DATA, 118062306a36Sopenharmony_ci "data: port=%d mp_rd_bitmap=0x%08x\n", 118162306a36Sopenharmony_ci *port, card->mp_rd_bitmap); 118262306a36Sopenharmony_ci return 0; 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci if (!(card->mp_rd_bitmap & (1 << card->curr_rd_port))) 118662306a36Sopenharmony_ci return -1; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci /* We are now handling the SDIO data ports */ 118962306a36Sopenharmony_ci card->mp_rd_bitmap &= (u32)(~(1 << card->curr_rd_port)); 119062306a36Sopenharmony_ci *port = card->curr_rd_port; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci if (++card->curr_rd_port == card->max_ports) 119362306a36Sopenharmony_ci card->curr_rd_port = reg->start_rd_port; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci mwifiex_dbg(adapter, DATA, 119662306a36Sopenharmony_ci "data: port=%d mp_rd_bitmap=0x%08x -> 0x%08x\n", 119762306a36Sopenharmony_ci *port, rd_bitmap, card->mp_rd_bitmap); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci return 0; 120062306a36Sopenharmony_ci} 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci/* 120362306a36Sopenharmony_ci * This function gets the write port for data. 120462306a36Sopenharmony_ci * 120562306a36Sopenharmony_ci * The current write port is returned if available and the value is 120662306a36Sopenharmony_ci * increased (provided it does not reach the maximum limit, in which 120762306a36Sopenharmony_ci * case it is reset to 1) 120862306a36Sopenharmony_ci */ 120962306a36Sopenharmony_cistatic int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u32 *port) 121062306a36Sopenharmony_ci{ 121162306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 121262306a36Sopenharmony_ci const struct mwifiex_sdio_card_reg *reg = card->reg; 121362306a36Sopenharmony_ci u32 wr_bitmap = card->mp_wr_bitmap; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci mwifiex_dbg(adapter, DATA, 121662306a36Sopenharmony_ci "data: mp_wr_bitmap=0x%08x\n", wr_bitmap); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci if (!(wr_bitmap & card->mp_data_port_mask)) { 121962306a36Sopenharmony_ci adapter->data_sent = true; 122062306a36Sopenharmony_ci return -EBUSY; 122162306a36Sopenharmony_ci } 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) { 122462306a36Sopenharmony_ci card->mp_wr_bitmap &= (u32) (~(1 << card->curr_wr_port)); 122562306a36Sopenharmony_ci *port = card->curr_wr_port; 122662306a36Sopenharmony_ci if (++card->curr_wr_port == card->mp_end_port) 122762306a36Sopenharmony_ci card->curr_wr_port = reg->start_wr_port; 122862306a36Sopenharmony_ci } else { 122962306a36Sopenharmony_ci adapter->data_sent = true; 123062306a36Sopenharmony_ci return -EBUSY; 123162306a36Sopenharmony_ci } 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci if ((card->has_control_mask) && (*port == CTRL_PORT)) { 123462306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 123562306a36Sopenharmony_ci "invalid data port=%d cur port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", 123662306a36Sopenharmony_ci *port, card->curr_wr_port, wr_bitmap, 123762306a36Sopenharmony_ci card->mp_wr_bitmap); 123862306a36Sopenharmony_ci return -1; 123962306a36Sopenharmony_ci } 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci mwifiex_dbg(adapter, DATA, 124262306a36Sopenharmony_ci "data: port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", 124362306a36Sopenharmony_ci *port, wr_bitmap, card->mp_wr_bitmap); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci return 0; 124662306a36Sopenharmony_ci} 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci/* 124962306a36Sopenharmony_ci * This function polls the card status. 125062306a36Sopenharmony_ci */ 125162306a36Sopenharmony_cistatic int 125262306a36Sopenharmony_cimwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits) 125362306a36Sopenharmony_ci{ 125462306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 125562306a36Sopenharmony_ci u32 tries; 125662306a36Sopenharmony_ci u8 cs; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci for (tries = 0; tries < MAX_POLL_TRIES; tries++) { 125962306a36Sopenharmony_ci if (mwifiex_read_reg(adapter, card->reg->poll_reg, &cs)) 126062306a36Sopenharmony_ci break; 126162306a36Sopenharmony_ci else if ((cs & bits) == bits) 126262306a36Sopenharmony_ci return 0; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci usleep_range(10, 20); 126562306a36Sopenharmony_ci } 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 126862306a36Sopenharmony_ci "poll card status failed, tries = %d\n", tries); 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci return -1; 127162306a36Sopenharmony_ci} 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci/* 127462306a36Sopenharmony_ci * This function disables the host interrupt. 127562306a36Sopenharmony_ci * 127662306a36Sopenharmony_ci * The host interrupt mask is read, the disable bit is reset and 127762306a36Sopenharmony_ci * written back to the card host interrupt mask register. 127862306a36Sopenharmony_ci */ 127962306a36Sopenharmony_cistatic void mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter) 128062306a36Sopenharmony_ci{ 128162306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 128262306a36Sopenharmony_ci struct sdio_func *func = card->func; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci sdio_claim_host(func); 128562306a36Sopenharmony_ci mwifiex_write_reg_locked(func, card->reg->host_int_mask_reg, 0); 128662306a36Sopenharmony_ci sdio_release_irq(func); 128762306a36Sopenharmony_ci sdio_release_host(func); 128862306a36Sopenharmony_ci} 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci/* 129162306a36Sopenharmony_ci * This function reads the interrupt status from card. 129262306a36Sopenharmony_ci */ 129362306a36Sopenharmony_cistatic void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) 129462306a36Sopenharmony_ci{ 129562306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 129662306a36Sopenharmony_ci u8 sdio_ireg; 129762306a36Sopenharmony_ci unsigned long flags; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci if (mwifiex_read_data_sync(adapter, card->mp_regs, 130062306a36Sopenharmony_ci card->reg->max_mp_regs, 130162306a36Sopenharmony_ci REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) { 130262306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "read mp_regs failed\n"); 130362306a36Sopenharmony_ci return; 130462306a36Sopenharmony_ci } 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci sdio_ireg = card->mp_regs[card->reg->host_int_status_reg]; 130762306a36Sopenharmony_ci if (sdio_ireg) { 130862306a36Sopenharmony_ci /* 130962306a36Sopenharmony_ci * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS 131062306a36Sopenharmony_ci * For SDIO new mode CMD port interrupts 131162306a36Sopenharmony_ci * DN_LD_CMD_PORT_HOST_INT_STATUS and/or 131262306a36Sopenharmony_ci * UP_LD_CMD_PORT_HOST_INT_STATUS 131362306a36Sopenharmony_ci * Clear the interrupt status register 131462306a36Sopenharmony_ci */ 131562306a36Sopenharmony_ci mwifiex_dbg(adapter, INTR, 131662306a36Sopenharmony_ci "int: sdio_ireg = %#x\n", sdio_ireg); 131762306a36Sopenharmony_ci spin_lock_irqsave(&adapter->int_lock, flags); 131862306a36Sopenharmony_ci adapter->int_status |= sdio_ireg; 131962306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->int_lock, flags); 132062306a36Sopenharmony_ci } 132162306a36Sopenharmony_ci} 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci/* 132462306a36Sopenharmony_ci * SDIO interrupt handler. 132562306a36Sopenharmony_ci * 132662306a36Sopenharmony_ci * This function reads the interrupt status from firmware and handles 132762306a36Sopenharmony_ci * the interrupt in current thread (ksdioirqd) right away. 132862306a36Sopenharmony_ci */ 132962306a36Sopenharmony_cistatic void 133062306a36Sopenharmony_cimwifiex_sdio_interrupt(struct sdio_func *func) 133162306a36Sopenharmony_ci{ 133262306a36Sopenharmony_ci struct mwifiex_adapter *adapter; 133362306a36Sopenharmony_ci struct sdio_mmc_card *card; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci card = sdio_get_drvdata(func); 133662306a36Sopenharmony_ci if (!card || !card->adapter) { 133762306a36Sopenharmony_ci pr_err("int: func=%p card=%p adapter=%p\n", 133862306a36Sopenharmony_ci func, card, card ? card->adapter : NULL); 133962306a36Sopenharmony_ci return; 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci adapter = card->adapter; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP) 134462306a36Sopenharmony_ci adapter->ps_state = PS_STATE_AWAKE; 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci mwifiex_interrupt_status(adapter); 134762306a36Sopenharmony_ci mwifiex_main_process(adapter); 134862306a36Sopenharmony_ci} 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci/* 135162306a36Sopenharmony_ci * This function enables the host interrupt. 135262306a36Sopenharmony_ci * 135362306a36Sopenharmony_ci * The host interrupt enable mask is written to the card 135462306a36Sopenharmony_ci * host interrupt mask register. 135562306a36Sopenharmony_ci */ 135662306a36Sopenharmony_cistatic int mwifiex_sdio_enable_host_int(struct mwifiex_adapter *adapter) 135762306a36Sopenharmony_ci{ 135862306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 135962306a36Sopenharmony_ci struct sdio_func *func = card->func; 136062306a36Sopenharmony_ci int ret; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci sdio_claim_host(func); 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci /* Request the SDIO IRQ */ 136562306a36Sopenharmony_ci ret = sdio_claim_irq(func, mwifiex_sdio_interrupt); 136662306a36Sopenharmony_ci if (ret) { 136762306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 136862306a36Sopenharmony_ci "claim irq failed: ret=%d\n", ret); 136962306a36Sopenharmony_ci goto out; 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci /* Simply write the mask to the register */ 137362306a36Sopenharmony_ci ret = mwifiex_write_reg_locked(func, card->reg->host_int_mask_reg, 137462306a36Sopenharmony_ci card->reg->host_int_enable); 137562306a36Sopenharmony_ci if (ret) { 137662306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 137762306a36Sopenharmony_ci "enable host interrupt failed\n"); 137862306a36Sopenharmony_ci sdio_release_irq(func); 137962306a36Sopenharmony_ci } 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ciout: 138262306a36Sopenharmony_ci sdio_release_host(func); 138362306a36Sopenharmony_ci return ret; 138462306a36Sopenharmony_ci} 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci/* 138762306a36Sopenharmony_ci * This function sends a data buffer to the card. 138862306a36Sopenharmony_ci */ 138962306a36Sopenharmony_cistatic int mwifiex_sdio_card_to_host(struct mwifiex_adapter *adapter, 139062306a36Sopenharmony_ci u32 *type, u8 *buffer, 139162306a36Sopenharmony_ci u32 npayload, u32 ioport) 139262306a36Sopenharmony_ci{ 139362306a36Sopenharmony_ci int ret; 139462306a36Sopenharmony_ci u32 nb; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci if (!buffer) { 139762306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 139862306a36Sopenharmony_ci "%s: buffer is NULL\n", __func__); 139962306a36Sopenharmony_ci return -1; 140062306a36Sopenharmony_ci } 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci ret = mwifiex_read_data_sync(adapter, buffer, npayload, ioport, 1); 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci if (ret) { 140562306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 140662306a36Sopenharmony_ci "%s: read iomem failed: %d\n", __func__, 140762306a36Sopenharmony_ci ret); 140862306a36Sopenharmony_ci return -1; 140962306a36Sopenharmony_ci } 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci nb = get_unaligned_le16((buffer)); 141262306a36Sopenharmony_ci if (nb > npayload) { 141362306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 141462306a36Sopenharmony_ci "%s: invalid packet, nb=%d npayload=%d\n", 141562306a36Sopenharmony_ci __func__, nb, npayload); 141662306a36Sopenharmony_ci return -1; 141762306a36Sopenharmony_ci } 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci *type = get_unaligned_le16((buffer + 2)); 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci return ret; 142262306a36Sopenharmony_ci} 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci/* 142562306a36Sopenharmony_ci * This function downloads the firmware to the card. 142662306a36Sopenharmony_ci * 142762306a36Sopenharmony_ci * Firmware is downloaded to the card in blocks. Every block download 142862306a36Sopenharmony_ci * is tested for CRC errors, and retried a number of times before 142962306a36Sopenharmony_ci * returning failure. 143062306a36Sopenharmony_ci */ 143162306a36Sopenharmony_cistatic int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, 143262306a36Sopenharmony_ci struct mwifiex_fw_image *fw) 143362306a36Sopenharmony_ci{ 143462306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 143562306a36Sopenharmony_ci const struct mwifiex_sdio_card_reg *reg = card->reg; 143662306a36Sopenharmony_ci int ret; 143762306a36Sopenharmony_ci u8 *firmware = fw->fw_buf; 143862306a36Sopenharmony_ci u32 firmware_len = fw->fw_len; 143962306a36Sopenharmony_ci u32 offset = 0; 144062306a36Sopenharmony_ci u8 base0, base1; 144162306a36Sopenharmony_ci u8 *fwbuf; 144262306a36Sopenharmony_ci u16 len = 0; 144362306a36Sopenharmony_ci u32 txlen, tx_blocks = 0, tries; 144462306a36Sopenharmony_ci u32 i = 0; 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci if (!firmware_len) { 144762306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 144862306a36Sopenharmony_ci "firmware image not found! Terminating download\n"); 144962306a36Sopenharmony_ci return -1; 145062306a36Sopenharmony_ci } 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 145362306a36Sopenharmony_ci "info: downloading FW image (%d bytes)\n", 145462306a36Sopenharmony_ci firmware_len); 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci /* Assume that the allocated buffer is 8-byte aligned */ 145762306a36Sopenharmony_ci fwbuf = kzalloc(MWIFIEX_UPLD_SIZE, GFP_KERNEL); 145862306a36Sopenharmony_ci if (!fwbuf) 145962306a36Sopenharmony_ci return -ENOMEM; 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci sdio_claim_host(card->func); 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci /* Perform firmware data transfer */ 146462306a36Sopenharmony_ci do { 146562306a36Sopenharmony_ci /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY 146662306a36Sopenharmony_ci bits */ 146762306a36Sopenharmony_ci ret = mwifiex_sdio_poll_card_status(adapter, CARD_IO_READY | 146862306a36Sopenharmony_ci DN_LD_CARD_RDY); 146962306a36Sopenharmony_ci if (ret) { 147062306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 147162306a36Sopenharmony_ci "FW download with helper:\t" 147262306a36Sopenharmony_ci "poll status timeout @ %d\n", offset); 147362306a36Sopenharmony_ci goto done; 147462306a36Sopenharmony_ci } 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci /* More data? */ 147762306a36Sopenharmony_ci if (offset >= firmware_len) 147862306a36Sopenharmony_ci break; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci for (tries = 0; tries < MAX_POLL_TRIES; tries++) { 148162306a36Sopenharmony_ci ret = mwifiex_read_reg(adapter, reg->base_0_reg, 148262306a36Sopenharmony_ci &base0); 148362306a36Sopenharmony_ci if (ret) { 148462306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 148562306a36Sopenharmony_ci "dev BASE0 register read failed:\t" 148662306a36Sopenharmony_ci "base0=%#04X(%d). Terminating dnld\n", 148762306a36Sopenharmony_ci base0, base0); 148862306a36Sopenharmony_ci goto done; 148962306a36Sopenharmony_ci } 149062306a36Sopenharmony_ci ret = mwifiex_read_reg(adapter, reg->base_1_reg, 149162306a36Sopenharmony_ci &base1); 149262306a36Sopenharmony_ci if (ret) { 149362306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 149462306a36Sopenharmony_ci "dev BASE1 register read failed:\t" 149562306a36Sopenharmony_ci "base1=%#04X(%d). Terminating dnld\n", 149662306a36Sopenharmony_ci base1, base1); 149762306a36Sopenharmony_ci goto done; 149862306a36Sopenharmony_ci } 149962306a36Sopenharmony_ci len = (u16) (((base1 & 0xff) << 8) | (base0 & 0xff)); 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci if (len) 150262306a36Sopenharmony_ci break; 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci usleep_range(10, 20); 150562306a36Sopenharmony_ci } 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci if (!len) { 150862306a36Sopenharmony_ci break; 150962306a36Sopenharmony_ci } else if (len > MWIFIEX_UPLD_SIZE) { 151062306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 151162306a36Sopenharmony_ci "FW dnld failed @ %d, invalid length %d\n", 151262306a36Sopenharmony_ci offset, len); 151362306a36Sopenharmony_ci ret = -1; 151462306a36Sopenharmony_ci goto done; 151562306a36Sopenharmony_ci } 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci txlen = len; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci if (len & BIT(0)) { 152062306a36Sopenharmony_ci i++; 152162306a36Sopenharmony_ci if (i > MAX_WRITE_IOMEM_RETRY) { 152262306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 152362306a36Sopenharmony_ci "FW dnld failed @ %d, over max retry\n", 152462306a36Sopenharmony_ci offset); 152562306a36Sopenharmony_ci ret = -1; 152662306a36Sopenharmony_ci goto done; 152762306a36Sopenharmony_ci } 152862306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 152962306a36Sopenharmony_ci "CRC indicated by the helper:\t" 153062306a36Sopenharmony_ci "len = 0x%04X, txlen = %d\n", len, txlen); 153162306a36Sopenharmony_ci len &= ~BIT(0); 153262306a36Sopenharmony_ci /* Setting this to 0 to resend from same offset */ 153362306a36Sopenharmony_ci txlen = 0; 153462306a36Sopenharmony_ci } else { 153562306a36Sopenharmony_ci i = 0; 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci /* Set blocksize to transfer - checking for last 153862306a36Sopenharmony_ci block */ 153962306a36Sopenharmony_ci if (firmware_len - offset < txlen) 154062306a36Sopenharmony_ci txlen = firmware_len - offset; 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci tx_blocks = (txlen + MWIFIEX_SDIO_BLOCK_SIZE - 1) 154362306a36Sopenharmony_ci / MWIFIEX_SDIO_BLOCK_SIZE; 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci /* Copy payload to buffer */ 154662306a36Sopenharmony_ci memmove(fwbuf, &firmware[offset], txlen); 154762306a36Sopenharmony_ci } 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci ret = mwifiex_write_data_sync(adapter, fwbuf, tx_blocks * 155062306a36Sopenharmony_ci MWIFIEX_SDIO_BLOCK_SIZE, 155162306a36Sopenharmony_ci adapter->ioport); 155262306a36Sopenharmony_ci if (ret) { 155362306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 155462306a36Sopenharmony_ci "FW download, write iomem (%d) failed @ %d\n", 155562306a36Sopenharmony_ci i, offset); 155662306a36Sopenharmony_ci if (mwifiex_write_reg(adapter, CONFIGURATION_REG, 0x04)) 155762306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 155862306a36Sopenharmony_ci "write CFG reg failed\n"); 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci ret = -1; 156162306a36Sopenharmony_ci goto done; 156262306a36Sopenharmony_ci } 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci offset += txlen; 156562306a36Sopenharmony_ci } while (true); 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci mwifiex_dbg(adapter, MSG, 156862306a36Sopenharmony_ci "info: FW download over, size %d bytes\n", offset); 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci ret = 0; 157162306a36Sopenharmony_cidone: 157262306a36Sopenharmony_ci sdio_release_host(card->func); 157362306a36Sopenharmony_ci kfree(fwbuf); 157462306a36Sopenharmony_ci return ret; 157562306a36Sopenharmony_ci} 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci/* 157862306a36Sopenharmony_ci * This function decodes sdio aggregation pkt. 157962306a36Sopenharmony_ci * 158062306a36Sopenharmony_ci * Based on the data block size and pkt_len, 158162306a36Sopenharmony_ci * skb data will be decoded to few packets. 158262306a36Sopenharmony_ci */ 158362306a36Sopenharmony_cistatic void mwifiex_deaggr_sdio_pkt(struct mwifiex_adapter *adapter, 158462306a36Sopenharmony_ci struct sk_buff *skb) 158562306a36Sopenharmony_ci{ 158662306a36Sopenharmony_ci u32 total_pkt_len, pkt_len; 158762306a36Sopenharmony_ci struct sk_buff *skb_deaggr; 158862306a36Sopenharmony_ci u16 blk_size; 158962306a36Sopenharmony_ci u8 blk_num; 159062306a36Sopenharmony_ci u8 *data; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci data = skb->data; 159362306a36Sopenharmony_ci total_pkt_len = skb->len; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci while (total_pkt_len >= (SDIO_HEADER_OFFSET + adapter->intf_hdr_len)) { 159662306a36Sopenharmony_ci if (total_pkt_len < adapter->sdio_rx_block_size) 159762306a36Sopenharmony_ci break; 159862306a36Sopenharmony_ci blk_num = *(data + BLOCK_NUMBER_OFFSET); 159962306a36Sopenharmony_ci blk_size = adapter->sdio_rx_block_size * blk_num; 160062306a36Sopenharmony_ci if (blk_size > total_pkt_len) { 160162306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 160262306a36Sopenharmony_ci "%s: error in blk_size,\t" 160362306a36Sopenharmony_ci "blk_num=%d, blk_size=%d, total_pkt_len=%d\n", 160462306a36Sopenharmony_ci __func__, blk_num, blk_size, total_pkt_len); 160562306a36Sopenharmony_ci break; 160662306a36Sopenharmony_ci } 160762306a36Sopenharmony_ci pkt_len = get_unaligned_le16((data + 160862306a36Sopenharmony_ci SDIO_HEADER_OFFSET)); 160962306a36Sopenharmony_ci if ((pkt_len + SDIO_HEADER_OFFSET) > blk_size) { 161062306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 161162306a36Sopenharmony_ci "%s: error in pkt_len,\t" 161262306a36Sopenharmony_ci "pkt_len=%d, blk_size=%d\n", 161362306a36Sopenharmony_ci __func__, pkt_len, blk_size); 161462306a36Sopenharmony_ci break; 161562306a36Sopenharmony_ci } 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci skb_deaggr = mwifiex_alloc_dma_align_buf(pkt_len, GFP_KERNEL); 161862306a36Sopenharmony_ci if (!skb_deaggr) 161962306a36Sopenharmony_ci break; 162062306a36Sopenharmony_ci skb_put(skb_deaggr, pkt_len); 162162306a36Sopenharmony_ci memcpy(skb_deaggr->data, data + SDIO_HEADER_OFFSET, pkt_len); 162262306a36Sopenharmony_ci skb_pull(skb_deaggr, adapter->intf_hdr_len); 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci mwifiex_handle_rx_packet(adapter, skb_deaggr); 162562306a36Sopenharmony_ci data += blk_size; 162662306a36Sopenharmony_ci total_pkt_len -= blk_size; 162762306a36Sopenharmony_ci } 162862306a36Sopenharmony_ci} 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci/* 163162306a36Sopenharmony_ci * This function decodes a received packet. 163262306a36Sopenharmony_ci * 163362306a36Sopenharmony_ci * Based on the type, the packet is treated as either a data, or 163462306a36Sopenharmony_ci * a command response, or an event, and the correct handler 163562306a36Sopenharmony_ci * function is invoked. 163662306a36Sopenharmony_ci */ 163762306a36Sopenharmony_cistatic int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter, 163862306a36Sopenharmony_ci struct sk_buff *skb, u32 upld_typ) 163962306a36Sopenharmony_ci{ 164062306a36Sopenharmony_ci u8 *cmd_buf; 164162306a36Sopenharmony_ci u16 pkt_len; 164262306a36Sopenharmony_ci struct mwifiex_rxinfo *rx_info; 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci pkt_len = get_unaligned_le16(skb->data); 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci if (upld_typ != MWIFIEX_TYPE_AGGR_DATA) { 164762306a36Sopenharmony_ci skb_trim(skb, pkt_len); 164862306a36Sopenharmony_ci skb_pull(skb, adapter->intf_hdr_len); 164962306a36Sopenharmony_ci } 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci switch (upld_typ) { 165262306a36Sopenharmony_ci case MWIFIEX_TYPE_AGGR_DATA: 165362306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 165462306a36Sopenharmony_ci "info: --- Rx: Aggr Data packet ---\n"); 165562306a36Sopenharmony_ci rx_info = MWIFIEX_SKB_RXCB(skb); 165662306a36Sopenharmony_ci rx_info->buf_type = MWIFIEX_TYPE_AGGR_DATA; 165762306a36Sopenharmony_ci if (adapter->rx_work_enabled) { 165862306a36Sopenharmony_ci skb_queue_tail(&adapter->rx_data_q, skb); 165962306a36Sopenharmony_ci atomic_inc(&adapter->rx_pending); 166062306a36Sopenharmony_ci adapter->data_received = true; 166162306a36Sopenharmony_ci } else { 166262306a36Sopenharmony_ci mwifiex_deaggr_sdio_pkt(adapter, skb); 166362306a36Sopenharmony_ci dev_kfree_skb_any(skb); 166462306a36Sopenharmony_ci } 166562306a36Sopenharmony_ci break; 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci case MWIFIEX_TYPE_DATA: 166862306a36Sopenharmony_ci mwifiex_dbg(adapter, DATA, 166962306a36Sopenharmony_ci "info: --- Rx: Data packet ---\n"); 167062306a36Sopenharmony_ci if (adapter->rx_work_enabled) { 167162306a36Sopenharmony_ci skb_queue_tail(&adapter->rx_data_q, skb); 167262306a36Sopenharmony_ci adapter->data_received = true; 167362306a36Sopenharmony_ci atomic_inc(&adapter->rx_pending); 167462306a36Sopenharmony_ci } else { 167562306a36Sopenharmony_ci mwifiex_handle_rx_packet(adapter, skb); 167662306a36Sopenharmony_ci } 167762306a36Sopenharmony_ci break; 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci case MWIFIEX_TYPE_CMD: 168062306a36Sopenharmony_ci mwifiex_dbg(adapter, CMD, 168162306a36Sopenharmony_ci "info: --- Rx: Cmd Response ---\n"); 168262306a36Sopenharmony_ci /* take care of curr_cmd = NULL case */ 168362306a36Sopenharmony_ci if (!adapter->curr_cmd) { 168462306a36Sopenharmony_ci cmd_buf = adapter->upld_buf; 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci if (adapter->ps_state == PS_STATE_SLEEP_CFM) 168762306a36Sopenharmony_ci mwifiex_process_sleep_confirm_resp(adapter, 168862306a36Sopenharmony_ci skb->data, 168962306a36Sopenharmony_ci skb->len); 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci memcpy(cmd_buf, skb->data, 169262306a36Sopenharmony_ci min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER, 169362306a36Sopenharmony_ci skb->len)); 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci dev_kfree_skb_any(skb); 169662306a36Sopenharmony_ci } else { 169762306a36Sopenharmony_ci adapter->cmd_resp_received = true; 169862306a36Sopenharmony_ci adapter->curr_cmd->resp_skb = skb; 169962306a36Sopenharmony_ci } 170062306a36Sopenharmony_ci break; 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci case MWIFIEX_TYPE_EVENT: 170362306a36Sopenharmony_ci mwifiex_dbg(adapter, EVENT, 170462306a36Sopenharmony_ci "info: --- Rx: Event ---\n"); 170562306a36Sopenharmony_ci adapter->event_cause = get_unaligned_le32(skb->data); 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci if ((skb->len > 0) && (skb->len < MAX_EVENT_SIZE)) 170862306a36Sopenharmony_ci memcpy(adapter->event_body, 170962306a36Sopenharmony_ci skb->data + MWIFIEX_EVENT_HEADER_LEN, 171062306a36Sopenharmony_ci skb->len); 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci /* event cause has been saved to adapter->event_cause */ 171362306a36Sopenharmony_ci adapter->event_received = true; 171462306a36Sopenharmony_ci adapter->event_skb = skb; 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci break; 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci default: 171962306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 172062306a36Sopenharmony_ci "unknown upload type %#x\n", upld_typ); 172162306a36Sopenharmony_ci dev_kfree_skb_any(skb); 172262306a36Sopenharmony_ci break; 172362306a36Sopenharmony_ci } 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci return 0; 172662306a36Sopenharmony_ci} 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci/* 172962306a36Sopenharmony_ci * This function transfers received packets from card to driver, performing 173062306a36Sopenharmony_ci * aggregation if required. 173162306a36Sopenharmony_ci * 173262306a36Sopenharmony_ci * For data received on control port, or if aggregation is disabled, the 173362306a36Sopenharmony_ci * received buffers are uploaded as separate packets. However, if aggregation 173462306a36Sopenharmony_ci * is enabled and required, the buffers are copied onto an aggregation buffer, 173562306a36Sopenharmony_ci * provided there is space left, processed and finally uploaded. 173662306a36Sopenharmony_ci */ 173762306a36Sopenharmony_cistatic int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, 173862306a36Sopenharmony_ci u16 rx_len, u8 port) 173962306a36Sopenharmony_ci{ 174062306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 174162306a36Sopenharmony_ci s32 f_do_rx_aggr = 0; 174262306a36Sopenharmony_ci s32 f_do_rx_cur = 0; 174362306a36Sopenharmony_ci s32 f_aggr_cur = 0; 174462306a36Sopenharmony_ci s32 f_post_aggr_cur = 0; 174562306a36Sopenharmony_ci struct sk_buff *skb_deaggr; 174662306a36Sopenharmony_ci struct sk_buff *skb = NULL; 174762306a36Sopenharmony_ci u32 pkt_len, pkt_type, mport, pind; 174862306a36Sopenharmony_ci u8 *curr_ptr; 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci if ((card->has_control_mask) && (port == CTRL_PORT)) { 175162306a36Sopenharmony_ci /* Read the command Resp without aggr */ 175262306a36Sopenharmony_ci mwifiex_dbg(adapter, CMD, 175362306a36Sopenharmony_ci "info: %s: no aggregation for cmd\t" 175462306a36Sopenharmony_ci "response\n", __func__); 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci f_do_rx_cur = 1; 175762306a36Sopenharmony_ci goto rx_curr_single; 175862306a36Sopenharmony_ci } 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci if (!card->mpa_rx.enabled) { 176162306a36Sopenharmony_ci mwifiex_dbg(adapter, WARN, 176262306a36Sopenharmony_ci "info: %s: rx aggregation disabled\n", 176362306a36Sopenharmony_ci __func__); 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci f_do_rx_cur = 1; 176662306a36Sopenharmony_ci goto rx_curr_single; 176762306a36Sopenharmony_ci } 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci if ((!card->has_control_mask && (card->mp_rd_bitmap & 177062306a36Sopenharmony_ci card->reg->data_port_mask)) || 177162306a36Sopenharmony_ci (card->has_control_mask && (card->mp_rd_bitmap & 177262306a36Sopenharmony_ci (~((u32) CTRL_PORT_MASK))))) { 177362306a36Sopenharmony_ci /* Some more data RX pending */ 177462306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 177562306a36Sopenharmony_ci "info: %s: not last packet\n", __func__); 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci if (MP_RX_AGGR_IN_PROGRESS(card)) { 177862306a36Sopenharmony_ci if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len)) { 177962306a36Sopenharmony_ci f_aggr_cur = 1; 178062306a36Sopenharmony_ci } else { 178162306a36Sopenharmony_ci /* No room in Aggr buf, do rx aggr now */ 178262306a36Sopenharmony_ci f_do_rx_aggr = 1; 178362306a36Sopenharmony_ci f_post_aggr_cur = 1; 178462306a36Sopenharmony_ci } 178562306a36Sopenharmony_ci } else { 178662306a36Sopenharmony_ci /* Rx aggr not in progress */ 178762306a36Sopenharmony_ci f_aggr_cur = 1; 178862306a36Sopenharmony_ci } 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci } else { 179162306a36Sopenharmony_ci /* No more data RX pending */ 179262306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 179362306a36Sopenharmony_ci "info: %s: last packet\n", __func__); 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci if (MP_RX_AGGR_IN_PROGRESS(card)) { 179662306a36Sopenharmony_ci f_do_rx_aggr = 1; 179762306a36Sopenharmony_ci if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len)) 179862306a36Sopenharmony_ci f_aggr_cur = 1; 179962306a36Sopenharmony_ci else 180062306a36Sopenharmony_ci /* No room in Aggr buf, do rx aggr now */ 180162306a36Sopenharmony_ci f_do_rx_cur = 1; 180262306a36Sopenharmony_ci } else { 180362306a36Sopenharmony_ci f_do_rx_cur = 1; 180462306a36Sopenharmony_ci } 180562306a36Sopenharmony_ci } 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci if (f_aggr_cur) { 180862306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 180962306a36Sopenharmony_ci "info: current packet aggregation\n"); 181062306a36Sopenharmony_ci /* Curr pkt can be aggregated */ 181162306a36Sopenharmony_ci mp_rx_aggr_setup(card, rx_len, port); 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) || 181462306a36Sopenharmony_ci mp_rx_aggr_port_limit_reached(card)) { 181562306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 181662306a36Sopenharmony_ci "info: %s: aggregated packet\t" 181762306a36Sopenharmony_ci "limit reached\n", __func__); 181862306a36Sopenharmony_ci /* No more pkts allowed in Aggr buf, rx it */ 181962306a36Sopenharmony_ci f_do_rx_aggr = 1; 182062306a36Sopenharmony_ci } 182162306a36Sopenharmony_ci } 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci if (f_do_rx_aggr) { 182462306a36Sopenharmony_ci /* do aggr RX now */ 182562306a36Sopenharmony_ci mwifiex_dbg(adapter, DATA, 182662306a36Sopenharmony_ci "info: do_rx_aggr: num of packets: %d\n", 182762306a36Sopenharmony_ci card->mpa_rx.pkt_cnt); 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci if (card->supports_sdio_new_mode) { 183062306a36Sopenharmony_ci int i; 183162306a36Sopenharmony_ci u32 port_count; 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci for (i = 0, port_count = 0; i < card->max_ports; i++) 183462306a36Sopenharmony_ci if (card->mpa_rx.ports & BIT(i)) 183562306a36Sopenharmony_ci port_count++; 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci /* Reading data from "start_port + 0" to "start_port + 183862306a36Sopenharmony_ci * port_count -1", so decrease the count by 1 183962306a36Sopenharmony_ci */ 184062306a36Sopenharmony_ci port_count--; 184162306a36Sopenharmony_ci mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | 184262306a36Sopenharmony_ci (port_count << 8)) + card->mpa_rx.start_port; 184362306a36Sopenharmony_ci } else { 184462306a36Sopenharmony_ci mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | 184562306a36Sopenharmony_ci (card->mpa_rx.ports << 4)) + 184662306a36Sopenharmony_ci card->mpa_rx.start_port; 184762306a36Sopenharmony_ci } 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci if (card->mpa_rx.pkt_cnt == 1) 185062306a36Sopenharmony_ci mport = adapter->ioport + card->mpa_rx.start_port; 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf, 185362306a36Sopenharmony_ci card->mpa_rx.buf_len, mport, 1)) 185462306a36Sopenharmony_ci goto error; 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci curr_ptr = card->mpa_rx.buf; 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) { 185962306a36Sopenharmony_ci u32 *len_arr = card->mpa_rx.len_arr; 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci /* get curr PKT len & type */ 186262306a36Sopenharmony_ci pkt_len = get_unaligned_le16(&curr_ptr[0]); 186362306a36Sopenharmony_ci pkt_type = get_unaligned_le16(&curr_ptr[2]); 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci /* copy pkt to deaggr buf */ 186662306a36Sopenharmony_ci skb_deaggr = mwifiex_alloc_dma_align_buf(len_arr[pind], 186762306a36Sopenharmony_ci GFP_KERNEL); 186862306a36Sopenharmony_ci if (!skb_deaggr) { 186962306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "skb allocation failure\t" 187062306a36Sopenharmony_ci "drop pkt len=%d type=%d\n", 187162306a36Sopenharmony_ci pkt_len, pkt_type); 187262306a36Sopenharmony_ci curr_ptr += len_arr[pind]; 187362306a36Sopenharmony_ci continue; 187462306a36Sopenharmony_ci } 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci skb_put(skb_deaggr, len_arr[pind]); 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci if ((pkt_type == MWIFIEX_TYPE_DATA || 187962306a36Sopenharmony_ci (pkt_type == MWIFIEX_TYPE_AGGR_DATA && 188062306a36Sopenharmony_ci adapter->sdio_rx_aggr_enable)) && 188162306a36Sopenharmony_ci (pkt_len <= len_arr[pind])) { 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci memcpy(skb_deaggr->data, curr_ptr, pkt_len); 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci skb_trim(skb_deaggr, pkt_len); 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci /* Process de-aggr packet */ 188862306a36Sopenharmony_ci mwifiex_decode_rx_packet(adapter, skb_deaggr, 188962306a36Sopenharmony_ci pkt_type); 189062306a36Sopenharmony_ci } else { 189162306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 189262306a36Sopenharmony_ci "drop wrong aggr pkt:\t" 189362306a36Sopenharmony_ci "sdio_single_port_rx_aggr=%d\t" 189462306a36Sopenharmony_ci "type=%d len=%d max_len=%d\n", 189562306a36Sopenharmony_ci adapter->sdio_rx_aggr_enable, 189662306a36Sopenharmony_ci pkt_type, pkt_len, len_arr[pind]); 189762306a36Sopenharmony_ci dev_kfree_skb_any(skb_deaggr); 189862306a36Sopenharmony_ci } 189962306a36Sopenharmony_ci curr_ptr += len_arr[pind]; 190062306a36Sopenharmony_ci } 190162306a36Sopenharmony_ci MP_RX_AGGR_BUF_RESET(card); 190262306a36Sopenharmony_ci } 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_cirx_curr_single: 190562306a36Sopenharmony_ci if (f_do_rx_cur) { 190662306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, "info: RX: port: %d, rx_len: %d\n", 190762306a36Sopenharmony_ci port, rx_len); 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL); 191062306a36Sopenharmony_ci if (!skb) { 191162306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 191262306a36Sopenharmony_ci "single skb allocated fail,\t" 191362306a36Sopenharmony_ci "drop pkt port=%d len=%d\n", port, rx_len); 191462306a36Sopenharmony_ci if (mwifiex_sdio_card_to_host(adapter, &pkt_type, 191562306a36Sopenharmony_ci card->mpa_rx.buf, rx_len, 191662306a36Sopenharmony_ci adapter->ioport + port)) 191762306a36Sopenharmony_ci goto error; 191862306a36Sopenharmony_ci return 0; 191962306a36Sopenharmony_ci } 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci skb_put(skb, rx_len); 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci if (mwifiex_sdio_card_to_host(adapter, &pkt_type, 192462306a36Sopenharmony_ci skb->data, skb->len, 192562306a36Sopenharmony_ci adapter->ioport + port)) 192662306a36Sopenharmony_ci goto error; 192762306a36Sopenharmony_ci if (!adapter->sdio_rx_aggr_enable && 192862306a36Sopenharmony_ci pkt_type == MWIFIEX_TYPE_AGGR_DATA) { 192962306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "drop wrong pkt type %d\t" 193062306a36Sopenharmony_ci "current SDIO RX Aggr not enabled\n", 193162306a36Sopenharmony_ci pkt_type); 193262306a36Sopenharmony_ci dev_kfree_skb_any(skb); 193362306a36Sopenharmony_ci return 0; 193462306a36Sopenharmony_ci } 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci mwifiex_decode_rx_packet(adapter, skb, pkt_type); 193762306a36Sopenharmony_ci } 193862306a36Sopenharmony_ci if (f_post_aggr_cur) { 193962306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 194062306a36Sopenharmony_ci "info: current packet aggregation\n"); 194162306a36Sopenharmony_ci /* Curr pkt can be aggregated */ 194262306a36Sopenharmony_ci mp_rx_aggr_setup(card, rx_len, port); 194362306a36Sopenharmony_ci } 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci return 0; 194662306a36Sopenharmony_cierror: 194762306a36Sopenharmony_ci if (MP_RX_AGGR_IN_PROGRESS(card)) 194862306a36Sopenharmony_ci MP_RX_AGGR_BUF_RESET(card); 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci if (f_do_rx_cur && skb) 195162306a36Sopenharmony_ci /* Single transfer pending. Free curr buff also */ 195262306a36Sopenharmony_ci dev_kfree_skb_any(skb); 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci return -1; 195562306a36Sopenharmony_ci} 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci/* 195862306a36Sopenharmony_ci * This function checks the current interrupt status. 195962306a36Sopenharmony_ci * 196062306a36Sopenharmony_ci * The following interrupts are checked and handled by this function - 196162306a36Sopenharmony_ci * - Data sent 196262306a36Sopenharmony_ci * - Command sent 196362306a36Sopenharmony_ci * - Packets received 196462306a36Sopenharmony_ci * 196562306a36Sopenharmony_ci * Since the firmware does not generate download ready interrupt if the 196662306a36Sopenharmony_ci * port updated is command port only, command sent interrupt checking 196762306a36Sopenharmony_ci * should be done manually, and for every SDIO interrupt. 196862306a36Sopenharmony_ci * 196962306a36Sopenharmony_ci * In case of Rx packets received, the packets are uploaded from card to 197062306a36Sopenharmony_ci * host and processed accordingly. 197162306a36Sopenharmony_ci */ 197262306a36Sopenharmony_cistatic int mwifiex_process_int_status(struct mwifiex_adapter *adapter) 197362306a36Sopenharmony_ci{ 197462306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 197562306a36Sopenharmony_ci const struct mwifiex_sdio_card_reg *reg = card->reg; 197662306a36Sopenharmony_ci int ret = 0; 197762306a36Sopenharmony_ci u8 sdio_ireg; 197862306a36Sopenharmony_ci struct sk_buff *skb; 197962306a36Sopenharmony_ci u8 port = CTRL_PORT; 198062306a36Sopenharmony_ci u32 len_reg_l, len_reg_u; 198162306a36Sopenharmony_ci u32 rx_blocks; 198262306a36Sopenharmony_ci u16 rx_len; 198362306a36Sopenharmony_ci unsigned long flags; 198462306a36Sopenharmony_ci u32 bitmap; 198562306a36Sopenharmony_ci u8 cr; 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci spin_lock_irqsave(&adapter->int_lock, flags); 198862306a36Sopenharmony_ci sdio_ireg = adapter->int_status; 198962306a36Sopenharmony_ci adapter->int_status = 0; 199062306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->int_lock, flags); 199162306a36Sopenharmony_ci 199262306a36Sopenharmony_ci if (!sdio_ireg) 199362306a36Sopenharmony_ci return ret; 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ci /* Following interrupt is only for SDIO new mode */ 199662306a36Sopenharmony_ci if (sdio_ireg & DN_LD_CMD_PORT_HOST_INT_STATUS && adapter->cmd_sent) 199762306a36Sopenharmony_ci adapter->cmd_sent = false; 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci /* Following interrupt is only for SDIO new mode */ 200062306a36Sopenharmony_ci if (sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) { 200162306a36Sopenharmony_ci u32 pkt_type; 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_ci /* read the len of control packet */ 200462306a36Sopenharmony_ci rx_len = card->mp_regs[reg->cmd_rd_len_1] << 8; 200562306a36Sopenharmony_ci rx_len |= (u16)card->mp_regs[reg->cmd_rd_len_0]; 200662306a36Sopenharmony_ci rx_blocks = DIV_ROUND_UP(rx_len, MWIFIEX_SDIO_BLOCK_SIZE); 200762306a36Sopenharmony_ci if (rx_len <= adapter->intf_hdr_len || 200862306a36Sopenharmony_ci (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > 200962306a36Sopenharmony_ci MWIFIEX_RX_DATA_BUF_SIZE) 201062306a36Sopenharmony_ci return -1; 201162306a36Sopenharmony_ci rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE); 201262306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, "info: rx_len = %d\n", rx_len); 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL); 201562306a36Sopenharmony_ci if (!skb) 201662306a36Sopenharmony_ci return -1; 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci skb_put(skb, rx_len); 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci if (mwifiex_sdio_card_to_host(adapter, &pkt_type, skb->data, 202162306a36Sopenharmony_ci skb->len, adapter->ioport | 202262306a36Sopenharmony_ci CMD_PORT_SLCT)) { 202362306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 202462306a36Sopenharmony_ci "%s: failed to card_to_host", __func__); 202562306a36Sopenharmony_ci dev_kfree_skb_any(skb); 202662306a36Sopenharmony_ci goto term_cmd; 202762306a36Sopenharmony_ci } 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_ci if ((pkt_type != MWIFIEX_TYPE_CMD) && 203062306a36Sopenharmony_ci (pkt_type != MWIFIEX_TYPE_EVENT)) 203162306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 203262306a36Sopenharmony_ci "%s:Received wrong packet on cmd port", 203362306a36Sopenharmony_ci __func__); 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci mwifiex_decode_rx_packet(adapter, skb, pkt_type); 203662306a36Sopenharmony_ci } 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci if (sdio_ireg & DN_LD_HOST_INT_STATUS) { 203962306a36Sopenharmony_ci bitmap = (u32) card->mp_regs[reg->wr_bitmap_l]; 204062306a36Sopenharmony_ci bitmap |= ((u32) card->mp_regs[reg->wr_bitmap_u]) << 8; 204162306a36Sopenharmony_ci if (card->supports_sdio_new_mode) { 204262306a36Sopenharmony_ci bitmap |= 204362306a36Sopenharmony_ci ((u32) card->mp_regs[reg->wr_bitmap_1l]) << 16; 204462306a36Sopenharmony_ci bitmap |= 204562306a36Sopenharmony_ci ((u32) card->mp_regs[reg->wr_bitmap_1u]) << 24; 204662306a36Sopenharmony_ci } 204762306a36Sopenharmony_ci card->mp_wr_bitmap = bitmap; 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_ci mwifiex_dbg(adapter, INTR, 205062306a36Sopenharmony_ci "int: DNLD: wr_bitmap=0x%x\n", 205162306a36Sopenharmony_ci card->mp_wr_bitmap); 205262306a36Sopenharmony_ci if (adapter->data_sent && 205362306a36Sopenharmony_ci (card->mp_wr_bitmap & card->mp_data_port_mask)) { 205462306a36Sopenharmony_ci mwifiex_dbg(adapter, INTR, 205562306a36Sopenharmony_ci "info: <--- Tx DONE Interrupt --->\n"); 205662306a36Sopenharmony_ci adapter->data_sent = false; 205762306a36Sopenharmony_ci } 205862306a36Sopenharmony_ci } 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci /* As firmware will not generate download ready interrupt if the port 206162306a36Sopenharmony_ci updated is command port only, cmd_sent should be done for any SDIO 206262306a36Sopenharmony_ci interrupt. */ 206362306a36Sopenharmony_ci if (card->has_control_mask && adapter->cmd_sent) { 206462306a36Sopenharmony_ci /* Check if firmware has attach buffer at command port and 206562306a36Sopenharmony_ci update just that in wr_bit_map. */ 206662306a36Sopenharmony_ci card->mp_wr_bitmap |= 206762306a36Sopenharmony_ci (u32) card->mp_regs[reg->wr_bitmap_l] & CTRL_PORT_MASK; 206862306a36Sopenharmony_ci if (card->mp_wr_bitmap & CTRL_PORT_MASK) 206962306a36Sopenharmony_ci adapter->cmd_sent = false; 207062306a36Sopenharmony_ci } 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ci mwifiex_dbg(adapter, INTR, "info: cmd_sent=%d data_sent=%d\n", 207362306a36Sopenharmony_ci adapter->cmd_sent, adapter->data_sent); 207462306a36Sopenharmony_ci if (sdio_ireg & UP_LD_HOST_INT_STATUS) { 207562306a36Sopenharmony_ci bitmap = (u32) card->mp_regs[reg->rd_bitmap_l]; 207662306a36Sopenharmony_ci bitmap |= ((u32) card->mp_regs[reg->rd_bitmap_u]) << 8; 207762306a36Sopenharmony_ci if (card->supports_sdio_new_mode) { 207862306a36Sopenharmony_ci bitmap |= 207962306a36Sopenharmony_ci ((u32) card->mp_regs[reg->rd_bitmap_1l]) << 16; 208062306a36Sopenharmony_ci bitmap |= 208162306a36Sopenharmony_ci ((u32) card->mp_regs[reg->rd_bitmap_1u]) << 24; 208262306a36Sopenharmony_ci } 208362306a36Sopenharmony_ci card->mp_rd_bitmap = bitmap; 208462306a36Sopenharmony_ci mwifiex_dbg(adapter, INTR, 208562306a36Sopenharmony_ci "int: UPLD: rd_bitmap=0x%x\n", 208662306a36Sopenharmony_ci card->mp_rd_bitmap); 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_ci while (true) { 208962306a36Sopenharmony_ci ret = mwifiex_get_rd_port(adapter, &port); 209062306a36Sopenharmony_ci if (ret) { 209162306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 209262306a36Sopenharmony_ci "info: no more rd_port available\n"); 209362306a36Sopenharmony_ci break; 209462306a36Sopenharmony_ci } 209562306a36Sopenharmony_ci len_reg_l = reg->rd_len_p0_l + (port << 1); 209662306a36Sopenharmony_ci len_reg_u = reg->rd_len_p0_u + (port << 1); 209762306a36Sopenharmony_ci rx_len = ((u16) card->mp_regs[len_reg_u]) << 8; 209862306a36Sopenharmony_ci rx_len |= (u16) card->mp_regs[len_reg_l]; 209962306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 210062306a36Sopenharmony_ci "info: RX: port=%d rx_len=%u\n", 210162306a36Sopenharmony_ci port, rx_len); 210262306a36Sopenharmony_ci rx_blocks = 210362306a36Sopenharmony_ci (rx_len + MWIFIEX_SDIO_BLOCK_SIZE - 210462306a36Sopenharmony_ci 1) / MWIFIEX_SDIO_BLOCK_SIZE; 210562306a36Sopenharmony_ci if (rx_len <= adapter->intf_hdr_len || 210662306a36Sopenharmony_ci (card->mpa_rx.enabled && 210762306a36Sopenharmony_ci ((rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > 210862306a36Sopenharmony_ci card->mpa_rx.buf_size))) { 210962306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 211062306a36Sopenharmony_ci "invalid rx_len=%d\n", 211162306a36Sopenharmony_ci rx_len); 211262306a36Sopenharmony_ci return -1; 211362306a36Sopenharmony_ci } 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE); 211662306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, "info: rx_len = %d\n", 211762306a36Sopenharmony_ci rx_len); 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci if (mwifiex_sdio_card_to_host_mp_aggr(adapter, rx_len, 212062306a36Sopenharmony_ci port)) { 212162306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 212262306a36Sopenharmony_ci "card_to_host_mpa failed: int status=%#x\n", 212362306a36Sopenharmony_ci sdio_ireg); 212462306a36Sopenharmony_ci goto term_cmd; 212562306a36Sopenharmony_ci } 212662306a36Sopenharmony_ci } 212762306a36Sopenharmony_ci } 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci return 0; 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_citerm_cmd: 213262306a36Sopenharmony_ci /* terminate cmd */ 213362306a36Sopenharmony_ci if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr)) 213462306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "read CFG reg failed\n"); 213562306a36Sopenharmony_ci else 213662306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 213762306a36Sopenharmony_ci "info: CFG reg val = %d\n", cr); 213862306a36Sopenharmony_ci 213962306a36Sopenharmony_ci if (mwifiex_write_reg(adapter, CONFIGURATION_REG, (cr | 0x04))) 214062306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 214162306a36Sopenharmony_ci "write CFG reg failed\n"); 214262306a36Sopenharmony_ci else 214362306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, "info: write success\n"); 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_ci if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr)) 214662306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 214762306a36Sopenharmony_ci "read CFG reg failed\n"); 214862306a36Sopenharmony_ci else 214962306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 215062306a36Sopenharmony_ci "info: CFG reg val =%x\n", cr); 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci return -1; 215362306a36Sopenharmony_ci} 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_ci/* 215662306a36Sopenharmony_ci * This function aggregates transmission buffers in driver and downloads 215762306a36Sopenharmony_ci * the aggregated packet to card. 215862306a36Sopenharmony_ci * 215962306a36Sopenharmony_ci * The individual packets are aggregated by copying into an aggregation 216062306a36Sopenharmony_ci * buffer and then downloaded to the card. Previous unsent packets in the 216162306a36Sopenharmony_ci * aggregation buffer are pre-copied first before new packets are added. 216262306a36Sopenharmony_ci * Aggregation is done till there is space left in the aggregation buffer, 216362306a36Sopenharmony_ci * or till new packets are available. 216462306a36Sopenharmony_ci * 216562306a36Sopenharmony_ci * The function will only download the packet to the card when aggregation 216662306a36Sopenharmony_ci * stops, otherwise it will just aggregate the packet in aggregation buffer 216762306a36Sopenharmony_ci * and return. 216862306a36Sopenharmony_ci */ 216962306a36Sopenharmony_cistatic int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, 217062306a36Sopenharmony_ci u8 *payload, u32 pkt_len, u32 port, 217162306a36Sopenharmony_ci u32 next_pkt_len) 217262306a36Sopenharmony_ci{ 217362306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 217462306a36Sopenharmony_ci int ret = 0; 217562306a36Sopenharmony_ci s32 f_send_aggr_buf = 0; 217662306a36Sopenharmony_ci s32 f_send_cur_buf = 0; 217762306a36Sopenharmony_ci s32 f_precopy_cur_buf = 0; 217862306a36Sopenharmony_ci s32 f_postcopy_cur_buf = 0; 217962306a36Sopenharmony_ci u32 mport; 218062306a36Sopenharmony_ci int index; 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_ci if (!card->mpa_tx.enabled || 218362306a36Sopenharmony_ci (card->has_control_mask && (port == CTRL_PORT)) || 218462306a36Sopenharmony_ci (card->supports_sdio_new_mode && (port == CMD_PORT_SLCT))) { 218562306a36Sopenharmony_ci mwifiex_dbg(adapter, WARN, 218662306a36Sopenharmony_ci "info: %s: tx aggregation disabled\n", 218762306a36Sopenharmony_ci __func__); 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ci f_send_cur_buf = 1; 219062306a36Sopenharmony_ci goto tx_curr_single; 219162306a36Sopenharmony_ci } 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci if (next_pkt_len) { 219462306a36Sopenharmony_ci /* More pkt in TX queue */ 219562306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 219662306a36Sopenharmony_ci "info: %s: more packets in queue.\n", 219762306a36Sopenharmony_ci __func__); 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci if (MP_TX_AGGR_IN_PROGRESS(card)) { 220062306a36Sopenharmony_ci if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) { 220162306a36Sopenharmony_ci f_precopy_cur_buf = 1; 220262306a36Sopenharmony_ci 220362306a36Sopenharmony_ci if (!(card->mp_wr_bitmap & 220462306a36Sopenharmony_ci (1 << card->curr_wr_port)) || 220562306a36Sopenharmony_ci !MP_TX_AGGR_BUF_HAS_ROOM( 220662306a36Sopenharmony_ci card, pkt_len + next_pkt_len)) 220762306a36Sopenharmony_ci f_send_aggr_buf = 1; 220862306a36Sopenharmony_ci } else { 220962306a36Sopenharmony_ci /* No room in Aggr buf, send it */ 221062306a36Sopenharmony_ci f_send_aggr_buf = 1; 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci if (!(card->mp_wr_bitmap & 221362306a36Sopenharmony_ci (1 << card->curr_wr_port))) 221462306a36Sopenharmony_ci f_send_cur_buf = 1; 221562306a36Sopenharmony_ci else 221662306a36Sopenharmony_ci f_postcopy_cur_buf = 1; 221762306a36Sopenharmony_ci } 221862306a36Sopenharmony_ci } else { 221962306a36Sopenharmony_ci if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len) && 222062306a36Sopenharmony_ci (card->mp_wr_bitmap & (1 << card->curr_wr_port))) 222162306a36Sopenharmony_ci f_precopy_cur_buf = 1; 222262306a36Sopenharmony_ci else 222362306a36Sopenharmony_ci f_send_cur_buf = 1; 222462306a36Sopenharmony_ci } 222562306a36Sopenharmony_ci } else { 222662306a36Sopenharmony_ci /* Last pkt in TX queue */ 222762306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 222862306a36Sopenharmony_ci "info: %s: Last packet in Tx Queue.\n", 222962306a36Sopenharmony_ci __func__); 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci if (MP_TX_AGGR_IN_PROGRESS(card)) { 223262306a36Sopenharmony_ci /* some packs in Aggr buf already */ 223362306a36Sopenharmony_ci f_send_aggr_buf = 1; 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_ci if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) 223662306a36Sopenharmony_ci f_precopy_cur_buf = 1; 223762306a36Sopenharmony_ci else 223862306a36Sopenharmony_ci /* No room in Aggr buf, send it */ 223962306a36Sopenharmony_ci f_send_cur_buf = 1; 224062306a36Sopenharmony_ci } else { 224162306a36Sopenharmony_ci f_send_cur_buf = 1; 224262306a36Sopenharmony_ci } 224362306a36Sopenharmony_ci } 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_ci if (f_precopy_cur_buf) { 224662306a36Sopenharmony_ci mwifiex_dbg(adapter, DATA, 224762306a36Sopenharmony_ci "data: %s: precopy current buffer\n", 224862306a36Sopenharmony_ci __func__); 224962306a36Sopenharmony_ci MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci if (MP_TX_AGGR_PKT_LIMIT_REACHED(card) || 225262306a36Sopenharmony_ci mp_tx_aggr_port_limit_reached(card)) 225362306a36Sopenharmony_ci /* No more pkts allowed in Aggr buf, send it */ 225462306a36Sopenharmony_ci f_send_aggr_buf = 1; 225562306a36Sopenharmony_ci } 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci if (f_send_aggr_buf) { 225862306a36Sopenharmony_ci mwifiex_dbg(adapter, DATA, 225962306a36Sopenharmony_ci "data: %s: send aggr buffer: %d %d\n", 226062306a36Sopenharmony_ci __func__, card->mpa_tx.start_port, 226162306a36Sopenharmony_ci card->mpa_tx.ports); 226262306a36Sopenharmony_ci if (card->supports_sdio_new_mode) { 226362306a36Sopenharmony_ci u32 port_count; 226462306a36Sopenharmony_ci int i; 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_ci for (i = 0, port_count = 0; i < card->max_ports; i++) 226762306a36Sopenharmony_ci if (card->mpa_tx.ports & BIT(i)) 226862306a36Sopenharmony_ci port_count++; 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci /* Writing data from "start_port + 0" to "start_port + 227162306a36Sopenharmony_ci * port_count -1", so decrease the count by 1 227262306a36Sopenharmony_ci */ 227362306a36Sopenharmony_ci port_count--; 227462306a36Sopenharmony_ci mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | 227562306a36Sopenharmony_ci (port_count << 8)) + card->mpa_tx.start_port; 227662306a36Sopenharmony_ci } else { 227762306a36Sopenharmony_ci mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | 227862306a36Sopenharmony_ci (card->mpa_tx.ports << 4)) + 227962306a36Sopenharmony_ci card->mpa_tx.start_port; 228062306a36Sopenharmony_ci } 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci if (card->mpa_tx.pkt_cnt == 1) 228362306a36Sopenharmony_ci mport = adapter->ioport + card->mpa_tx.start_port; 228462306a36Sopenharmony_ci 228562306a36Sopenharmony_ci ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf, 228662306a36Sopenharmony_ci card->mpa_tx.buf_len, mport); 228762306a36Sopenharmony_ci 228862306a36Sopenharmony_ci /* Save the last multi port tx aggregation info to debug log. */ 228962306a36Sopenharmony_ci index = adapter->dbg.last_sdio_mp_index; 229062306a36Sopenharmony_ci index = (index + 1) % MWIFIEX_DBG_SDIO_MP_NUM; 229162306a36Sopenharmony_ci adapter->dbg.last_sdio_mp_index = index; 229262306a36Sopenharmony_ci adapter->dbg.last_mp_wr_ports[index] = mport; 229362306a36Sopenharmony_ci adapter->dbg.last_mp_wr_bitmap[index] = card->mp_wr_bitmap; 229462306a36Sopenharmony_ci adapter->dbg.last_mp_wr_len[index] = card->mpa_tx.buf_len; 229562306a36Sopenharmony_ci adapter->dbg.last_mp_curr_wr_port[index] = card->curr_wr_port; 229662306a36Sopenharmony_ci 229762306a36Sopenharmony_ci MP_TX_AGGR_BUF_RESET(card); 229862306a36Sopenharmony_ci } 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_citx_curr_single: 230162306a36Sopenharmony_ci if (f_send_cur_buf) { 230262306a36Sopenharmony_ci mwifiex_dbg(adapter, DATA, 230362306a36Sopenharmony_ci "data: %s: send current buffer %d\n", 230462306a36Sopenharmony_ci __func__, port); 230562306a36Sopenharmony_ci ret = mwifiex_write_data_to_card(adapter, payload, pkt_len, 230662306a36Sopenharmony_ci adapter->ioport + port); 230762306a36Sopenharmony_ci } 230862306a36Sopenharmony_ci 230962306a36Sopenharmony_ci if (f_postcopy_cur_buf) { 231062306a36Sopenharmony_ci mwifiex_dbg(adapter, DATA, 231162306a36Sopenharmony_ci "data: %s: postcopy current buffer\n", 231262306a36Sopenharmony_ci __func__); 231362306a36Sopenharmony_ci MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); 231462306a36Sopenharmony_ci } 231562306a36Sopenharmony_ci 231662306a36Sopenharmony_ci return ret; 231762306a36Sopenharmony_ci} 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ci/* 232062306a36Sopenharmony_ci * This function downloads data from driver to card. 232162306a36Sopenharmony_ci * 232262306a36Sopenharmony_ci * Both commands and data packets are transferred to the card by this 232362306a36Sopenharmony_ci * function. 232462306a36Sopenharmony_ci * 232562306a36Sopenharmony_ci * This function adds the SDIO specific header to the front of the buffer 232662306a36Sopenharmony_ci * before transferring. The header contains the length of the packet and 232762306a36Sopenharmony_ci * the type. The firmware handles the packets based upon this set type. 232862306a36Sopenharmony_ci */ 232962306a36Sopenharmony_cistatic int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter, 233062306a36Sopenharmony_ci u8 type, struct sk_buff *skb, 233162306a36Sopenharmony_ci struct mwifiex_tx_param *tx_param) 233262306a36Sopenharmony_ci{ 233362306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 233462306a36Sopenharmony_ci int ret; 233562306a36Sopenharmony_ci u32 buf_block_len; 233662306a36Sopenharmony_ci u32 blk_size; 233762306a36Sopenharmony_ci u32 port = CTRL_PORT; 233862306a36Sopenharmony_ci u8 *payload = (u8 *)skb->data; 233962306a36Sopenharmony_ci u32 pkt_len = skb->len; 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ci /* Allocate buffer and copy payload */ 234262306a36Sopenharmony_ci blk_size = MWIFIEX_SDIO_BLOCK_SIZE; 234362306a36Sopenharmony_ci buf_block_len = (pkt_len + blk_size - 1) / blk_size; 234462306a36Sopenharmony_ci put_unaligned_le16((u16)pkt_len, payload + 0); 234562306a36Sopenharmony_ci put_unaligned_le16((u32)type, payload + 2); 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_ci /* 234962306a36Sopenharmony_ci * This is SDIO specific header 235062306a36Sopenharmony_ci * u16 length, 235162306a36Sopenharmony_ci * u16 type (MWIFIEX_TYPE_DATA = 0, MWIFIEX_TYPE_CMD = 1, 235262306a36Sopenharmony_ci * MWIFIEX_TYPE_EVENT = 3) 235362306a36Sopenharmony_ci */ 235462306a36Sopenharmony_ci if (type == MWIFIEX_TYPE_DATA) { 235562306a36Sopenharmony_ci ret = mwifiex_get_wr_port_data(adapter, &port); 235662306a36Sopenharmony_ci if (ret) { 235762306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 235862306a36Sopenharmony_ci "%s: no wr_port available\n", 235962306a36Sopenharmony_ci __func__); 236062306a36Sopenharmony_ci return ret; 236162306a36Sopenharmony_ci } 236262306a36Sopenharmony_ci } else { 236362306a36Sopenharmony_ci adapter->cmd_sent = true; 236462306a36Sopenharmony_ci /* Type must be MWIFIEX_TYPE_CMD */ 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci if (pkt_len <= adapter->intf_hdr_len || 236762306a36Sopenharmony_ci pkt_len > MWIFIEX_UPLD_SIZE) 236862306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 236962306a36Sopenharmony_ci "%s: payload=%p, nb=%d\n", 237062306a36Sopenharmony_ci __func__, payload, pkt_len); 237162306a36Sopenharmony_ci 237262306a36Sopenharmony_ci if (card->supports_sdio_new_mode) 237362306a36Sopenharmony_ci port = CMD_PORT_SLCT; 237462306a36Sopenharmony_ci } 237562306a36Sopenharmony_ci 237662306a36Sopenharmony_ci /* Transfer data to card */ 237762306a36Sopenharmony_ci pkt_len = buf_block_len * blk_size; 237862306a36Sopenharmony_ci 237962306a36Sopenharmony_ci if (tx_param) 238062306a36Sopenharmony_ci ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len, 238162306a36Sopenharmony_ci port, tx_param->next_pkt_len 238262306a36Sopenharmony_ci ); 238362306a36Sopenharmony_ci else 238462306a36Sopenharmony_ci ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len, 238562306a36Sopenharmony_ci port, 0); 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_ci if (ret) { 238862306a36Sopenharmony_ci if (type == MWIFIEX_TYPE_CMD) 238962306a36Sopenharmony_ci adapter->cmd_sent = false; 239062306a36Sopenharmony_ci if (type == MWIFIEX_TYPE_DATA) { 239162306a36Sopenharmony_ci adapter->data_sent = false; 239262306a36Sopenharmony_ci /* restore curr_wr_port in error cases */ 239362306a36Sopenharmony_ci card->curr_wr_port = port; 239462306a36Sopenharmony_ci card->mp_wr_bitmap |= (u32)(1 << card->curr_wr_port); 239562306a36Sopenharmony_ci } 239662306a36Sopenharmony_ci } else { 239762306a36Sopenharmony_ci if (type == MWIFIEX_TYPE_DATA) { 239862306a36Sopenharmony_ci if (!(card->mp_wr_bitmap & (1 << card->curr_wr_port))) 239962306a36Sopenharmony_ci adapter->data_sent = true; 240062306a36Sopenharmony_ci else 240162306a36Sopenharmony_ci adapter->data_sent = false; 240262306a36Sopenharmony_ci } 240362306a36Sopenharmony_ci } 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_ci return ret; 240662306a36Sopenharmony_ci} 240762306a36Sopenharmony_ci 240862306a36Sopenharmony_ci/* 240962306a36Sopenharmony_ci * This function allocates the MPA Tx and Rx buffers. 241062306a36Sopenharmony_ci */ 241162306a36Sopenharmony_cistatic int mwifiex_alloc_sdio_mpa_buffers(struct mwifiex_adapter *adapter, 241262306a36Sopenharmony_ci u32 mpa_tx_buf_size, u32 mpa_rx_buf_size) 241362306a36Sopenharmony_ci{ 241462306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 241562306a36Sopenharmony_ci u32 rx_buf_size; 241662306a36Sopenharmony_ci int ret = 0; 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_ci card->mpa_tx.buf = kzalloc(mpa_tx_buf_size, GFP_KERNEL); 241962306a36Sopenharmony_ci if (!card->mpa_tx.buf) { 242062306a36Sopenharmony_ci ret = -1; 242162306a36Sopenharmony_ci goto error; 242262306a36Sopenharmony_ci } 242362306a36Sopenharmony_ci 242462306a36Sopenharmony_ci card->mpa_tx.buf_size = mpa_tx_buf_size; 242562306a36Sopenharmony_ci 242662306a36Sopenharmony_ci rx_buf_size = max_t(u32, mpa_rx_buf_size, 242762306a36Sopenharmony_ci (u32)SDIO_MAX_AGGR_BUF_SIZE); 242862306a36Sopenharmony_ci card->mpa_rx.buf = kzalloc(rx_buf_size, GFP_KERNEL); 242962306a36Sopenharmony_ci if (!card->mpa_rx.buf) { 243062306a36Sopenharmony_ci ret = -1; 243162306a36Sopenharmony_ci goto error; 243262306a36Sopenharmony_ci } 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci card->mpa_rx.buf_size = rx_buf_size; 243562306a36Sopenharmony_ci 243662306a36Sopenharmony_cierror: 243762306a36Sopenharmony_ci if (ret) { 243862306a36Sopenharmony_ci kfree(card->mpa_tx.buf); 243962306a36Sopenharmony_ci kfree(card->mpa_rx.buf); 244062306a36Sopenharmony_ci card->mpa_tx.buf_size = 0; 244162306a36Sopenharmony_ci card->mpa_rx.buf_size = 0; 244262306a36Sopenharmony_ci card->mpa_tx.buf = NULL; 244362306a36Sopenharmony_ci card->mpa_rx.buf = NULL; 244462306a36Sopenharmony_ci } 244562306a36Sopenharmony_ci 244662306a36Sopenharmony_ci return ret; 244762306a36Sopenharmony_ci} 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_ci/* 245062306a36Sopenharmony_ci * This function unregisters the SDIO device. 245162306a36Sopenharmony_ci * 245262306a36Sopenharmony_ci * The SDIO IRQ is released, the function is disabled and driver 245362306a36Sopenharmony_ci * data is set to null. 245462306a36Sopenharmony_ci */ 245562306a36Sopenharmony_cistatic void 245662306a36Sopenharmony_cimwifiex_unregister_dev(struct mwifiex_adapter *adapter) 245762306a36Sopenharmony_ci{ 245862306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 245962306a36Sopenharmony_ci 246062306a36Sopenharmony_ci if (adapter->card) { 246162306a36Sopenharmony_ci card->adapter = NULL; 246262306a36Sopenharmony_ci sdio_claim_host(card->func); 246362306a36Sopenharmony_ci sdio_disable_func(card->func); 246462306a36Sopenharmony_ci sdio_release_host(card->func); 246562306a36Sopenharmony_ci } 246662306a36Sopenharmony_ci} 246762306a36Sopenharmony_ci 246862306a36Sopenharmony_ci/* 246962306a36Sopenharmony_ci * This function registers the SDIO device. 247062306a36Sopenharmony_ci * 247162306a36Sopenharmony_ci * SDIO IRQ is claimed, block size is set and driver data is initialized. 247262306a36Sopenharmony_ci */ 247362306a36Sopenharmony_cistatic int mwifiex_register_dev(struct mwifiex_adapter *adapter) 247462306a36Sopenharmony_ci{ 247562306a36Sopenharmony_ci int ret; 247662306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 247762306a36Sopenharmony_ci struct sdio_func *func = card->func; 247862306a36Sopenharmony_ci const char *firmware = card->firmware; 247962306a36Sopenharmony_ci 248062306a36Sopenharmony_ci /* save adapter pointer in card */ 248162306a36Sopenharmony_ci card->adapter = adapter; 248262306a36Sopenharmony_ci adapter->tx_buf_size = card->tx_buf_size; 248362306a36Sopenharmony_ci 248462306a36Sopenharmony_ci sdio_claim_host(func); 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci /* Set block size */ 248762306a36Sopenharmony_ci ret = sdio_set_block_size(card->func, MWIFIEX_SDIO_BLOCK_SIZE); 248862306a36Sopenharmony_ci sdio_release_host(func); 248962306a36Sopenharmony_ci if (ret) { 249062306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 249162306a36Sopenharmony_ci "cannot set SDIO block size\n"); 249262306a36Sopenharmony_ci return ret; 249362306a36Sopenharmony_ci } 249462306a36Sopenharmony_ci 249562306a36Sopenharmony_ci /* Select correct firmware (sdsd or sdiouart) firmware based on the strapping 249662306a36Sopenharmony_ci * option 249762306a36Sopenharmony_ci */ 249862306a36Sopenharmony_ci if (card->firmware_sdiouart) { 249962306a36Sopenharmony_ci u8 val; 250062306a36Sopenharmony_ci 250162306a36Sopenharmony_ci mwifiex_read_reg(adapter, card->reg->host_strap_reg, &val); 250262306a36Sopenharmony_ci if ((val & card->reg->host_strap_mask) == card->reg->host_strap_value) 250362306a36Sopenharmony_ci firmware = card->firmware_sdiouart; 250462306a36Sopenharmony_ci } 250562306a36Sopenharmony_ci strcpy(adapter->fw_name, firmware); 250662306a36Sopenharmony_ci 250762306a36Sopenharmony_ci if (card->fw_dump_enh) { 250862306a36Sopenharmony_ci adapter->mem_type_mapping_tbl = generic_mem_type_map; 250962306a36Sopenharmony_ci adapter->num_mem_types = 1; 251062306a36Sopenharmony_ci } else { 251162306a36Sopenharmony_ci adapter->mem_type_mapping_tbl = mem_type_mapping_tbl; 251262306a36Sopenharmony_ci adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl); 251362306a36Sopenharmony_ci } 251462306a36Sopenharmony_ci 251562306a36Sopenharmony_ci return 0; 251662306a36Sopenharmony_ci} 251762306a36Sopenharmony_ci 251862306a36Sopenharmony_ci/* 251962306a36Sopenharmony_ci * This function initializes the SDIO driver. 252062306a36Sopenharmony_ci * 252162306a36Sopenharmony_ci * The following initializations steps are followed - 252262306a36Sopenharmony_ci * - Read the Host interrupt status register to acknowledge 252362306a36Sopenharmony_ci * the first interrupt got from bootloader 252462306a36Sopenharmony_ci * - Disable host interrupt mask register 252562306a36Sopenharmony_ci * - Get SDIO port 252662306a36Sopenharmony_ci * - Initialize SDIO variables in card 252762306a36Sopenharmony_ci * - Allocate MP registers 252862306a36Sopenharmony_ci * - Allocate MPA Tx and Rx buffers 252962306a36Sopenharmony_ci */ 253062306a36Sopenharmony_cistatic int mwifiex_init_sdio(struct mwifiex_adapter *adapter) 253162306a36Sopenharmony_ci{ 253262306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 253362306a36Sopenharmony_ci const struct mwifiex_sdio_card_reg *reg = card->reg; 253462306a36Sopenharmony_ci int ret; 253562306a36Sopenharmony_ci u8 sdio_ireg; 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_ci sdio_set_drvdata(card->func, card); 253862306a36Sopenharmony_ci 253962306a36Sopenharmony_ci /* 254062306a36Sopenharmony_ci * Read the host_int_status_reg for ACK the first interrupt got 254162306a36Sopenharmony_ci * from the bootloader. If we don't do this we get a interrupt 254262306a36Sopenharmony_ci * as soon as we register the irq. 254362306a36Sopenharmony_ci */ 254462306a36Sopenharmony_ci mwifiex_read_reg(adapter, card->reg->host_int_status_reg, &sdio_ireg); 254562306a36Sopenharmony_ci 254662306a36Sopenharmony_ci /* Get SDIO ioport */ 254762306a36Sopenharmony_ci if (mwifiex_init_sdio_ioport(adapter)) 254862306a36Sopenharmony_ci return -EIO; 254962306a36Sopenharmony_ci 255062306a36Sopenharmony_ci /* Initialize SDIO variables in card */ 255162306a36Sopenharmony_ci card->mp_rd_bitmap = 0; 255262306a36Sopenharmony_ci card->mp_wr_bitmap = 0; 255362306a36Sopenharmony_ci card->curr_rd_port = reg->start_rd_port; 255462306a36Sopenharmony_ci card->curr_wr_port = reg->start_wr_port; 255562306a36Sopenharmony_ci 255662306a36Sopenharmony_ci card->mp_data_port_mask = reg->data_port_mask; 255762306a36Sopenharmony_ci 255862306a36Sopenharmony_ci card->mpa_tx.buf_len = 0; 255962306a36Sopenharmony_ci card->mpa_tx.pkt_cnt = 0; 256062306a36Sopenharmony_ci card->mpa_tx.start_port = 0; 256162306a36Sopenharmony_ci 256262306a36Sopenharmony_ci card->mpa_tx.enabled = 1; 256362306a36Sopenharmony_ci card->mpa_tx.pkt_aggr_limit = card->mp_agg_pkt_limit; 256462306a36Sopenharmony_ci 256562306a36Sopenharmony_ci card->mpa_rx.buf_len = 0; 256662306a36Sopenharmony_ci card->mpa_rx.pkt_cnt = 0; 256762306a36Sopenharmony_ci card->mpa_rx.start_port = 0; 256862306a36Sopenharmony_ci 256962306a36Sopenharmony_ci card->mpa_rx.enabled = 1; 257062306a36Sopenharmony_ci card->mpa_rx.pkt_aggr_limit = card->mp_agg_pkt_limit; 257162306a36Sopenharmony_ci 257262306a36Sopenharmony_ci /* Allocate buffers for SDIO MP-A */ 257362306a36Sopenharmony_ci card->mp_regs = kzalloc(reg->max_mp_regs, GFP_KERNEL); 257462306a36Sopenharmony_ci if (!card->mp_regs) 257562306a36Sopenharmony_ci return -ENOMEM; 257662306a36Sopenharmony_ci 257762306a36Sopenharmony_ci /* Allocate skb pointer buffers */ 257862306a36Sopenharmony_ci card->mpa_rx.skb_arr = kcalloc(card->mp_agg_pkt_limit, sizeof(void *), 257962306a36Sopenharmony_ci GFP_KERNEL); 258062306a36Sopenharmony_ci if (!card->mpa_rx.skb_arr) { 258162306a36Sopenharmony_ci kfree(card->mp_regs); 258262306a36Sopenharmony_ci return -ENOMEM; 258362306a36Sopenharmony_ci } 258462306a36Sopenharmony_ci 258562306a36Sopenharmony_ci card->mpa_rx.len_arr = kcalloc(card->mp_agg_pkt_limit, 258662306a36Sopenharmony_ci sizeof(*card->mpa_rx.len_arr), 258762306a36Sopenharmony_ci GFP_KERNEL); 258862306a36Sopenharmony_ci if (!card->mpa_rx.len_arr) { 258962306a36Sopenharmony_ci kfree(card->mp_regs); 259062306a36Sopenharmony_ci kfree(card->mpa_rx.skb_arr); 259162306a36Sopenharmony_ci return -ENOMEM; 259262306a36Sopenharmony_ci } 259362306a36Sopenharmony_ci 259462306a36Sopenharmony_ci ret = mwifiex_alloc_sdio_mpa_buffers(adapter, 259562306a36Sopenharmony_ci card->mp_tx_agg_buf_size, 259662306a36Sopenharmony_ci card->mp_rx_agg_buf_size); 259762306a36Sopenharmony_ci 259862306a36Sopenharmony_ci /* Allocate 32k MPA Tx/Rx buffers if 64k memory allocation fails */ 259962306a36Sopenharmony_ci if (ret && (card->mp_tx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX || 260062306a36Sopenharmony_ci card->mp_rx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX)) { 260162306a36Sopenharmony_ci /* Disable rx single port aggregation */ 260262306a36Sopenharmony_ci adapter->host_disable_sdio_rx_aggr = true; 260362306a36Sopenharmony_ci 260462306a36Sopenharmony_ci ret = mwifiex_alloc_sdio_mpa_buffers 260562306a36Sopenharmony_ci (adapter, MWIFIEX_MP_AGGR_BUF_SIZE_32K, 260662306a36Sopenharmony_ci MWIFIEX_MP_AGGR_BUF_SIZE_32K); 260762306a36Sopenharmony_ci if (ret) { 260862306a36Sopenharmony_ci /* Disable multi port aggregation */ 260962306a36Sopenharmony_ci card->mpa_tx.enabled = 0; 261062306a36Sopenharmony_ci card->mpa_rx.enabled = 0; 261162306a36Sopenharmony_ci } 261262306a36Sopenharmony_ci } 261362306a36Sopenharmony_ci 261462306a36Sopenharmony_ci adapter->auto_tdls = card->can_auto_tdls; 261562306a36Sopenharmony_ci adapter->ext_scan = card->can_ext_scan; 261662306a36Sopenharmony_ci return 0; 261762306a36Sopenharmony_ci} 261862306a36Sopenharmony_ci 261962306a36Sopenharmony_ci/* 262062306a36Sopenharmony_ci * This function resets the MPA Tx and Rx buffers. 262162306a36Sopenharmony_ci */ 262262306a36Sopenharmony_cistatic void mwifiex_cleanup_mpa_buf(struct mwifiex_adapter *adapter) 262362306a36Sopenharmony_ci{ 262462306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 262562306a36Sopenharmony_ci 262662306a36Sopenharmony_ci MP_TX_AGGR_BUF_RESET(card); 262762306a36Sopenharmony_ci MP_RX_AGGR_BUF_RESET(card); 262862306a36Sopenharmony_ci} 262962306a36Sopenharmony_ci 263062306a36Sopenharmony_ci/* 263162306a36Sopenharmony_ci * This function cleans up the allocated card buffers. 263262306a36Sopenharmony_ci * 263362306a36Sopenharmony_ci * The following are freed by this function - 263462306a36Sopenharmony_ci * - MP registers 263562306a36Sopenharmony_ci * - MPA Tx buffer 263662306a36Sopenharmony_ci * - MPA Rx buffer 263762306a36Sopenharmony_ci */ 263862306a36Sopenharmony_cistatic void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter) 263962306a36Sopenharmony_ci{ 264062306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 264162306a36Sopenharmony_ci 264262306a36Sopenharmony_ci cancel_work_sync(&card->work); 264362306a36Sopenharmony_ci 264462306a36Sopenharmony_ci kfree(card->mp_regs); 264562306a36Sopenharmony_ci kfree(card->mpa_rx.skb_arr); 264662306a36Sopenharmony_ci kfree(card->mpa_rx.len_arr); 264762306a36Sopenharmony_ci kfree(card->mpa_tx.buf); 264862306a36Sopenharmony_ci kfree(card->mpa_rx.buf); 264962306a36Sopenharmony_ci} 265062306a36Sopenharmony_ci 265162306a36Sopenharmony_ci/* 265262306a36Sopenharmony_ci * This function updates the MP end port in card. 265362306a36Sopenharmony_ci */ 265462306a36Sopenharmony_cistatic void 265562306a36Sopenharmony_cimwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port) 265662306a36Sopenharmony_ci{ 265762306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 265862306a36Sopenharmony_ci const struct mwifiex_sdio_card_reg *reg = card->reg; 265962306a36Sopenharmony_ci int i; 266062306a36Sopenharmony_ci 266162306a36Sopenharmony_ci card->mp_end_port = port; 266262306a36Sopenharmony_ci 266362306a36Sopenharmony_ci card->mp_data_port_mask = reg->data_port_mask; 266462306a36Sopenharmony_ci 266562306a36Sopenharmony_ci if (reg->start_wr_port) { 266662306a36Sopenharmony_ci for (i = 1; i <= card->max_ports - card->mp_end_port; i++) 266762306a36Sopenharmony_ci card->mp_data_port_mask &= 266862306a36Sopenharmony_ci ~(1 << (card->max_ports - i)); 266962306a36Sopenharmony_ci } 267062306a36Sopenharmony_ci 267162306a36Sopenharmony_ci card->curr_wr_port = reg->start_wr_port; 267262306a36Sopenharmony_ci 267362306a36Sopenharmony_ci mwifiex_dbg(adapter, CMD, 267462306a36Sopenharmony_ci "cmd: mp_end_port %d, data port mask 0x%x\n", 267562306a36Sopenharmony_ci port, card->mp_data_port_mask); 267662306a36Sopenharmony_ci} 267762306a36Sopenharmony_ci 267862306a36Sopenharmony_cistatic void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter) 267962306a36Sopenharmony_ci{ 268062306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 268162306a36Sopenharmony_ci struct sdio_func *func = card->func; 268262306a36Sopenharmony_ci int ret; 268362306a36Sopenharmony_ci 268462306a36Sopenharmony_ci /* Prepare the adapter for the reset. */ 268562306a36Sopenharmony_ci mwifiex_shutdown_sw(adapter); 268662306a36Sopenharmony_ci clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &card->work_flags); 268762306a36Sopenharmony_ci clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &card->work_flags); 268862306a36Sopenharmony_ci 268962306a36Sopenharmony_ci /* Run a HW reset of the SDIO interface. */ 269062306a36Sopenharmony_ci sdio_claim_host(func); 269162306a36Sopenharmony_ci ret = mmc_hw_reset(func->card); 269262306a36Sopenharmony_ci sdio_release_host(func); 269362306a36Sopenharmony_ci 269462306a36Sopenharmony_ci switch (ret) { 269562306a36Sopenharmony_ci case 1: 269662306a36Sopenharmony_ci dev_dbg(&func->dev, "SDIO HW reset asynchronous\n"); 269762306a36Sopenharmony_ci complete_all(adapter->fw_done); 269862306a36Sopenharmony_ci break; 269962306a36Sopenharmony_ci case 0: 270062306a36Sopenharmony_ci ret = mwifiex_reinit_sw(adapter); 270162306a36Sopenharmony_ci if (ret) 270262306a36Sopenharmony_ci dev_err(&func->dev, "reinit failed: %d\n", ret); 270362306a36Sopenharmony_ci break; 270462306a36Sopenharmony_ci default: 270562306a36Sopenharmony_ci dev_err(&func->dev, "SDIO HW reset failed: %d\n", ret); 270662306a36Sopenharmony_ci break; 270762306a36Sopenharmony_ci } 270862306a36Sopenharmony_ci} 270962306a36Sopenharmony_ci 271062306a36Sopenharmony_ci/* This function read/write firmware */ 271162306a36Sopenharmony_cistatic enum 271262306a36Sopenharmony_cirdwr_status mwifiex_sdio_rdwr_firmware(struct mwifiex_adapter *adapter, 271362306a36Sopenharmony_ci u8 doneflag) 271462306a36Sopenharmony_ci{ 271562306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 271662306a36Sopenharmony_ci int ret, tries; 271762306a36Sopenharmony_ci u8 ctrl_data = 0; 271862306a36Sopenharmony_ci 271962306a36Sopenharmony_ci sdio_writeb(card->func, card->reg->fw_dump_host_ready, 272062306a36Sopenharmony_ci card->reg->fw_dump_ctrl, &ret); 272162306a36Sopenharmony_ci if (ret) { 272262306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "SDIO Write ERR\n"); 272362306a36Sopenharmony_ci return RDWR_STATUS_FAILURE; 272462306a36Sopenharmony_ci } 272562306a36Sopenharmony_ci for (tries = 0; tries < MAX_POLL_TRIES; tries++) { 272662306a36Sopenharmony_ci ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl, 272762306a36Sopenharmony_ci &ret); 272862306a36Sopenharmony_ci if (ret) { 272962306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "SDIO read err\n"); 273062306a36Sopenharmony_ci return RDWR_STATUS_FAILURE; 273162306a36Sopenharmony_ci } 273262306a36Sopenharmony_ci if (ctrl_data == FW_DUMP_DONE) 273362306a36Sopenharmony_ci break; 273462306a36Sopenharmony_ci if (doneflag && ctrl_data == doneflag) 273562306a36Sopenharmony_ci return RDWR_STATUS_DONE; 273662306a36Sopenharmony_ci if (ctrl_data != card->reg->fw_dump_host_ready) { 273762306a36Sopenharmony_ci mwifiex_dbg(adapter, WARN, 273862306a36Sopenharmony_ci "The ctrl reg was changed, re-try again\n"); 273962306a36Sopenharmony_ci sdio_writeb(card->func, card->reg->fw_dump_host_ready, 274062306a36Sopenharmony_ci card->reg->fw_dump_ctrl, &ret); 274162306a36Sopenharmony_ci if (ret) { 274262306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "SDIO write err\n"); 274362306a36Sopenharmony_ci return RDWR_STATUS_FAILURE; 274462306a36Sopenharmony_ci } 274562306a36Sopenharmony_ci } 274662306a36Sopenharmony_ci usleep_range(100, 200); 274762306a36Sopenharmony_ci } 274862306a36Sopenharmony_ci if (ctrl_data == card->reg->fw_dump_host_ready) { 274962306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 275062306a36Sopenharmony_ci "Fail to pull ctrl_data\n"); 275162306a36Sopenharmony_ci return RDWR_STATUS_FAILURE; 275262306a36Sopenharmony_ci } 275362306a36Sopenharmony_ci 275462306a36Sopenharmony_ci return RDWR_STATUS_SUCCESS; 275562306a36Sopenharmony_ci} 275662306a36Sopenharmony_ci 275762306a36Sopenharmony_ci/* This function dump firmware memory to file */ 275862306a36Sopenharmony_cistatic void mwifiex_sdio_fw_dump(struct mwifiex_adapter *adapter) 275962306a36Sopenharmony_ci{ 276062306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 276162306a36Sopenharmony_ci int ret = 0; 276262306a36Sopenharmony_ci unsigned int reg, reg_start, reg_end; 276362306a36Sopenharmony_ci u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0; 276462306a36Sopenharmony_ci enum rdwr_status stat; 276562306a36Sopenharmony_ci u32 memory_size; 276662306a36Sopenharmony_ci 276762306a36Sopenharmony_ci if (!card->can_dump_fw) 276862306a36Sopenharmony_ci return; 276962306a36Sopenharmony_ci 277062306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) { 277162306a36Sopenharmony_ci struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_ci if (entry->mem_ptr) { 277462306a36Sopenharmony_ci vfree(entry->mem_ptr); 277562306a36Sopenharmony_ci entry->mem_ptr = NULL; 277662306a36Sopenharmony_ci } 277762306a36Sopenharmony_ci entry->mem_size = 0; 277862306a36Sopenharmony_ci } 277962306a36Sopenharmony_ci 278062306a36Sopenharmony_ci mwifiex_pm_wakeup_card(adapter); 278162306a36Sopenharmony_ci sdio_claim_host(card->func); 278262306a36Sopenharmony_ci 278362306a36Sopenharmony_ci mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump start ==\n"); 278462306a36Sopenharmony_ci 278562306a36Sopenharmony_ci stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); 278662306a36Sopenharmony_ci if (stat == RDWR_STATUS_FAILURE) 278762306a36Sopenharmony_ci goto done; 278862306a36Sopenharmony_ci 278962306a36Sopenharmony_ci reg = card->reg->fw_dump_start; 279062306a36Sopenharmony_ci /* Read the number of the memories which will dump */ 279162306a36Sopenharmony_ci dump_num = sdio_readb(card->func, reg, &ret); 279262306a36Sopenharmony_ci if (ret) { 279362306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "SDIO read memory length err\n"); 279462306a36Sopenharmony_ci goto done; 279562306a36Sopenharmony_ci } 279662306a36Sopenharmony_ci 279762306a36Sopenharmony_ci /* Read the length of every memory which will dump */ 279862306a36Sopenharmony_ci for (idx = 0; idx < dump_num; idx++) { 279962306a36Sopenharmony_ci struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; 280062306a36Sopenharmony_ci 280162306a36Sopenharmony_ci stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); 280262306a36Sopenharmony_ci if (stat == RDWR_STATUS_FAILURE) 280362306a36Sopenharmony_ci goto done; 280462306a36Sopenharmony_ci 280562306a36Sopenharmony_ci memory_size = 0; 280662306a36Sopenharmony_ci reg = card->reg->fw_dump_start; 280762306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 280862306a36Sopenharmony_ci read_reg = sdio_readb(card->func, reg, &ret); 280962306a36Sopenharmony_ci if (ret) { 281062306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "SDIO read err\n"); 281162306a36Sopenharmony_ci goto done; 281262306a36Sopenharmony_ci } 281362306a36Sopenharmony_ci memory_size |= (read_reg << i*8); 281462306a36Sopenharmony_ci reg++; 281562306a36Sopenharmony_ci } 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_ci if (memory_size == 0) { 281862306a36Sopenharmony_ci mwifiex_dbg(adapter, DUMP, "Firmware dump Finished!\n"); 281962306a36Sopenharmony_ci ret = mwifiex_write_reg(adapter, 282062306a36Sopenharmony_ci card->reg->fw_dump_ctrl, 282162306a36Sopenharmony_ci FW_DUMP_READ_DONE); 282262306a36Sopenharmony_ci if (ret) { 282362306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "SDIO write err\n"); 282462306a36Sopenharmony_ci return; 282562306a36Sopenharmony_ci } 282662306a36Sopenharmony_ci break; 282762306a36Sopenharmony_ci } 282862306a36Sopenharmony_ci 282962306a36Sopenharmony_ci mwifiex_dbg(adapter, DUMP, 283062306a36Sopenharmony_ci "%s_SIZE=0x%x\n", entry->mem_name, memory_size); 283162306a36Sopenharmony_ci entry->mem_ptr = vmalloc(memory_size + 1); 283262306a36Sopenharmony_ci entry->mem_size = memory_size; 283362306a36Sopenharmony_ci if (!entry->mem_ptr) { 283462306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "Vmalloc %s failed\n", 283562306a36Sopenharmony_ci entry->mem_name); 283662306a36Sopenharmony_ci goto done; 283762306a36Sopenharmony_ci } 283862306a36Sopenharmony_ci dbg_ptr = entry->mem_ptr; 283962306a36Sopenharmony_ci end_ptr = dbg_ptr + memory_size; 284062306a36Sopenharmony_ci 284162306a36Sopenharmony_ci doneflag = entry->done_flag; 284262306a36Sopenharmony_ci mwifiex_dbg(adapter, DUMP, 284362306a36Sopenharmony_ci "Start %s output, please wait...\n", 284462306a36Sopenharmony_ci entry->mem_name); 284562306a36Sopenharmony_ci 284662306a36Sopenharmony_ci do { 284762306a36Sopenharmony_ci stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); 284862306a36Sopenharmony_ci if (stat == RDWR_STATUS_FAILURE) 284962306a36Sopenharmony_ci goto done; 285062306a36Sopenharmony_ci 285162306a36Sopenharmony_ci reg_start = card->reg->fw_dump_start; 285262306a36Sopenharmony_ci reg_end = card->reg->fw_dump_end; 285362306a36Sopenharmony_ci for (reg = reg_start; reg <= reg_end; reg++) { 285462306a36Sopenharmony_ci *dbg_ptr = sdio_readb(card->func, reg, &ret); 285562306a36Sopenharmony_ci if (ret) { 285662306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 285762306a36Sopenharmony_ci "SDIO read err\n"); 285862306a36Sopenharmony_ci goto done; 285962306a36Sopenharmony_ci } 286062306a36Sopenharmony_ci if (dbg_ptr < end_ptr) 286162306a36Sopenharmony_ci dbg_ptr++; 286262306a36Sopenharmony_ci else 286362306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 286462306a36Sopenharmony_ci "Allocated buf not enough\n"); 286562306a36Sopenharmony_ci } 286662306a36Sopenharmony_ci 286762306a36Sopenharmony_ci if (stat != RDWR_STATUS_DONE) 286862306a36Sopenharmony_ci continue; 286962306a36Sopenharmony_ci 287062306a36Sopenharmony_ci mwifiex_dbg(adapter, DUMP, "%s done: size=0x%tx\n", 287162306a36Sopenharmony_ci entry->mem_name, dbg_ptr - entry->mem_ptr); 287262306a36Sopenharmony_ci break; 287362306a36Sopenharmony_ci } while (1); 287462306a36Sopenharmony_ci } 287562306a36Sopenharmony_ci mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump end ==\n"); 287662306a36Sopenharmony_ci 287762306a36Sopenharmony_cidone: 287862306a36Sopenharmony_ci sdio_release_host(card->func); 287962306a36Sopenharmony_ci} 288062306a36Sopenharmony_ci 288162306a36Sopenharmony_cistatic void mwifiex_sdio_generic_fw_dump(struct mwifiex_adapter *adapter) 288262306a36Sopenharmony_ci{ 288362306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 288462306a36Sopenharmony_ci struct memory_type_mapping *entry = &generic_mem_type_map[0]; 288562306a36Sopenharmony_ci unsigned int reg, reg_start, reg_end; 288662306a36Sopenharmony_ci u8 start_flag = 0, done_flag = 0; 288762306a36Sopenharmony_ci u8 *dbg_ptr, *end_ptr; 288862306a36Sopenharmony_ci enum rdwr_status stat; 288962306a36Sopenharmony_ci int ret = -1, tries; 289062306a36Sopenharmony_ci 289162306a36Sopenharmony_ci if (!card->fw_dump_enh) 289262306a36Sopenharmony_ci return; 289362306a36Sopenharmony_ci 289462306a36Sopenharmony_ci if (entry->mem_ptr) { 289562306a36Sopenharmony_ci vfree(entry->mem_ptr); 289662306a36Sopenharmony_ci entry->mem_ptr = NULL; 289762306a36Sopenharmony_ci } 289862306a36Sopenharmony_ci entry->mem_size = 0; 289962306a36Sopenharmony_ci 290062306a36Sopenharmony_ci mwifiex_pm_wakeup_card(adapter); 290162306a36Sopenharmony_ci sdio_claim_host(card->func); 290262306a36Sopenharmony_ci 290362306a36Sopenharmony_ci mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump start ==\n"); 290462306a36Sopenharmony_ci 290562306a36Sopenharmony_ci stat = mwifiex_sdio_rdwr_firmware(adapter, done_flag); 290662306a36Sopenharmony_ci if (stat == RDWR_STATUS_FAILURE) 290762306a36Sopenharmony_ci goto done; 290862306a36Sopenharmony_ci 290962306a36Sopenharmony_ci reg_start = card->reg->fw_dump_start; 291062306a36Sopenharmony_ci reg_end = card->reg->fw_dump_end; 291162306a36Sopenharmony_ci for (reg = reg_start; reg <= reg_end; reg++) { 291262306a36Sopenharmony_ci for (tries = 0; tries < MAX_POLL_TRIES; tries++) { 291362306a36Sopenharmony_ci start_flag = sdio_readb(card->func, reg, &ret); 291462306a36Sopenharmony_ci if (ret) { 291562306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 291662306a36Sopenharmony_ci "SDIO read err\n"); 291762306a36Sopenharmony_ci goto done; 291862306a36Sopenharmony_ci } 291962306a36Sopenharmony_ci if (start_flag == 0) 292062306a36Sopenharmony_ci break; 292162306a36Sopenharmony_ci if (tries == MAX_POLL_TRIES) { 292262306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 292362306a36Sopenharmony_ci "FW not ready to dump\n"); 292462306a36Sopenharmony_ci ret = -1; 292562306a36Sopenharmony_ci goto done; 292662306a36Sopenharmony_ci } 292762306a36Sopenharmony_ci } 292862306a36Sopenharmony_ci usleep_range(100, 200); 292962306a36Sopenharmony_ci } 293062306a36Sopenharmony_ci 293162306a36Sopenharmony_ci entry->mem_ptr = vmalloc(0xf0000 + 1); 293262306a36Sopenharmony_ci if (!entry->mem_ptr) { 293362306a36Sopenharmony_ci ret = -1; 293462306a36Sopenharmony_ci goto done; 293562306a36Sopenharmony_ci } 293662306a36Sopenharmony_ci dbg_ptr = entry->mem_ptr; 293762306a36Sopenharmony_ci entry->mem_size = 0xf0000; 293862306a36Sopenharmony_ci end_ptr = dbg_ptr + entry->mem_size; 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_ci done_flag = entry->done_flag; 294162306a36Sopenharmony_ci mwifiex_dbg(adapter, DUMP, 294262306a36Sopenharmony_ci "Start %s output, please wait...\n", entry->mem_name); 294362306a36Sopenharmony_ci 294462306a36Sopenharmony_ci while (true) { 294562306a36Sopenharmony_ci stat = mwifiex_sdio_rdwr_firmware(adapter, done_flag); 294662306a36Sopenharmony_ci if (stat == RDWR_STATUS_FAILURE) 294762306a36Sopenharmony_ci goto done; 294862306a36Sopenharmony_ci for (reg = reg_start; reg <= reg_end; reg++) { 294962306a36Sopenharmony_ci *dbg_ptr = sdio_readb(card->func, reg, &ret); 295062306a36Sopenharmony_ci if (ret) { 295162306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 295262306a36Sopenharmony_ci "SDIO read err\n"); 295362306a36Sopenharmony_ci goto done; 295462306a36Sopenharmony_ci } 295562306a36Sopenharmony_ci dbg_ptr++; 295662306a36Sopenharmony_ci if (dbg_ptr >= end_ptr) { 295762306a36Sopenharmony_ci u8 *tmp_ptr; 295862306a36Sopenharmony_ci 295962306a36Sopenharmony_ci tmp_ptr = vmalloc(entry->mem_size + 0x4000 + 1); 296062306a36Sopenharmony_ci if (!tmp_ptr) 296162306a36Sopenharmony_ci goto done; 296262306a36Sopenharmony_ci 296362306a36Sopenharmony_ci memcpy(tmp_ptr, entry->mem_ptr, 296462306a36Sopenharmony_ci entry->mem_size); 296562306a36Sopenharmony_ci vfree(entry->mem_ptr); 296662306a36Sopenharmony_ci entry->mem_ptr = tmp_ptr; 296762306a36Sopenharmony_ci tmp_ptr = NULL; 296862306a36Sopenharmony_ci dbg_ptr = entry->mem_ptr + entry->mem_size; 296962306a36Sopenharmony_ci entry->mem_size += 0x4000; 297062306a36Sopenharmony_ci end_ptr = entry->mem_ptr + entry->mem_size; 297162306a36Sopenharmony_ci } 297262306a36Sopenharmony_ci } 297362306a36Sopenharmony_ci if (stat == RDWR_STATUS_DONE) { 297462306a36Sopenharmony_ci entry->mem_size = dbg_ptr - entry->mem_ptr; 297562306a36Sopenharmony_ci mwifiex_dbg(adapter, DUMP, "dump %s done size=0x%x\n", 297662306a36Sopenharmony_ci entry->mem_name, entry->mem_size); 297762306a36Sopenharmony_ci ret = 0; 297862306a36Sopenharmony_ci break; 297962306a36Sopenharmony_ci } 298062306a36Sopenharmony_ci } 298162306a36Sopenharmony_ci mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump end ==\n"); 298262306a36Sopenharmony_ci 298362306a36Sopenharmony_cidone: 298462306a36Sopenharmony_ci if (ret) { 298562306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "firmware dump failed\n"); 298662306a36Sopenharmony_ci if (entry->mem_ptr) { 298762306a36Sopenharmony_ci vfree(entry->mem_ptr); 298862306a36Sopenharmony_ci entry->mem_ptr = NULL; 298962306a36Sopenharmony_ci } 299062306a36Sopenharmony_ci entry->mem_size = 0; 299162306a36Sopenharmony_ci } 299262306a36Sopenharmony_ci sdio_release_host(card->func); 299362306a36Sopenharmony_ci} 299462306a36Sopenharmony_ci 299562306a36Sopenharmony_cistatic void mwifiex_sdio_device_dump_work(struct mwifiex_adapter *adapter) 299662306a36Sopenharmony_ci{ 299762306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 299862306a36Sopenharmony_ci 299962306a36Sopenharmony_ci adapter->devdump_data = vzalloc(MWIFIEX_FW_DUMP_SIZE); 300062306a36Sopenharmony_ci if (!adapter->devdump_data) { 300162306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 300262306a36Sopenharmony_ci "vzalloc devdump data failure!\n"); 300362306a36Sopenharmony_ci return; 300462306a36Sopenharmony_ci } 300562306a36Sopenharmony_ci 300662306a36Sopenharmony_ci mwifiex_drv_info_dump(adapter); 300762306a36Sopenharmony_ci if (card->fw_dump_enh) 300862306a36Sopenharmony_ci mwifiex_sdio_generic_fw_dump(adapter); 300962306a36Sopenharmony_ci else 301062306a36Sopenharmony_ci mwifiex_sdio_fw_dump(adapter); 301162306a36Sopenharmony_ci mwifiex_prepare_fw_dump_info(adapter); 301262306a36Sopenharmony_ci mwifiex_upload_device_dump(adapter); 301362306a36Sopenharmony_ci} 301462306a36Sopenharmony_ci 301562306a36Sopenharmony_cistatic void mwifiex_sdio_work(struct work_struct *work) 301662306a36Sopenharmony_ci{ 301762306a36Sopenharmony_ci struct sdio_mmc_card *card = 301862306a36Sopenharmony_ci container_of(work, struct sdio_mmc_card, work); 301962306a36Sopenharmony_ci 302062306a36Sopenharmony_ci if (test_and_clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, 302162306a36Sopenharmony_ci &card->work_flags)) 302262306a36Sopenharmony_ci mwifiex_sdio_device_dump_work(card->adapter); 302362306a36Sopenharmony_ci if (test_and_clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, 302462306a36Sopenharmony_ci &card->work_flags)) 302562306a36Sopenharmony_ci mwifiex_sdio_card_reset_work(card->adapter); 302662306a36Sopenharmony_ci} 302762306a36Sopenharmony_ci 302862306a36Sopenharmony_ci/* This function resets the card */ 302962306a36Sopenharmony_cistatic void mwifiex_sdio_card_reset(struct mwifiex_adapter *adapter) 303062306a36Sopenharmony_ci{ 303162306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 303262306a36Sopenharmony_ci 303362306a36Sopenharmony_ci if (!test_and_set_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &card->work_flags)) 303462306a36Sopenharmony_ci schedule_work(&card->work); 303562306a36Sopenharmony_ci} 303662306a36Sopenharmony_ci 303762306a36Sopenharmony_ci/* This function dumps FW information */ 303862306a36Sopenharmony_cistatic void mwifiex_sdio_device_dump(struct mwifiex_adapter *adapter) 303962306a36Sopenharmony_ci{ 304062306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 304162306a36Sopenharmony_ci 304262306a36Sopenharmony_ci if (!test_and_set_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, 304362306a36Sopenharmony_ci &card->work_flags)) 304462306a36Sopenharmony_ci schedule_work(&card->work); 304562306a36Sopenharmony_ci} 304662306a36Sopenharmony_ci 304762306a36Sopenharmony_ci/* Function to dump SDIO function registers and SDIO scratch registers in case 304862306a36Sopenharmony_ci * of FW crash 304962306a36Sopenharmony_ci */ 305062306a36Sopenharmony_cistatic int 305162306a36Sopenharmony_cimwifiex_sdio_reg_dump(struct mwifiex_adapter *adapter, char *drv_buf) 305262306a36Sopenharmony_ci{ 305362306a36Sopenharmony_ci char *p = drv_buf; 305462306a36Sopenharmony_ci struct sdio_mmc_card *cardp = adapter->card; 305562306a36Sopenharmony_ci int ret = 0; 305662306a36Sopenharmony_ci u8 count, func, data, index = 0, size = 0; 305762306a36Sopenharmony_ci u8 reg, reg_start, reg_end; 305862306a36Sopenharmony_ci char buf[256], *ptr; 305962306a36Sopenharmony_ci 306062306a36Sopenharmony_ci if (!p) 306162306a36Sopenharmony_ci return 0; 306262306a36Sopenharmony_ci 306362306a36Sopenharmony_ci mwifiex_dbg(adapter, MSG, "SDIO register dump start\n"); 306462306a36Sopenharmony_ci 306562306a36Sopenharmony_ci mwifiex_pm_wakeup_card(adapter); 306662306a36Sopenharmony_ci 306762306a36Sopenharmony_ci sdio_claim_host(cardp->func); 306862306a36Sopenharmony_ci 306962306a36Sopenharmony_ci for (count = 0; count < 5; count++) { 307062306a36Sopenharmony_ci memset(buf, 0, sizeof(buf)); 307162306a36Sopenharmony_ci ptr = buf; 307262306a36Sopenharmony_ci 307362306a36Sopenharmony_ci switch (count) { 307462306a36Sopenharmony_ci case 0: 307562306a36Sopenharmony_ci /* Read the registers of SDIO function0 */ 307662306a36Sopenharmony_ci func = count; 307762306a36Sopenharmony_ci reg_start = 0; 307862306a36Sopenharmony_ci reg_end = 9; 307962306a36Sopenharmony_ci break; 308062306a36Sopenharmony_ci case 1: 308162306a36Sopenharmony_ci /* Read the registers of SDIO function1 */ 308262306a36Sopenharmony_ci func = count; 308362306a36Sopenharmony_ci reg_start = cardp->reg->func1_dump_reg_start; 308462306a36Sopenharmony_ci reg_end = cardp->reg->func1_dump_reg_end; 308562306a36Sopenharmony_ci break; 308662306a36Sopenharmony_ci case 2: 308762306a36Sopenharmony_ci index = 0; 308862306a36Sopenharmony_ci func = 1; 308962306a36Sopenharmony_ci reg_start = cardp->reg->func1_spec_reg_table[index++]; 309062306a36Sopenharmony_ci size = cardp->reg->func1_spec_reg_num; 309162306a36Sopenharmony_ci reg_end = cardp->reg->func1_spec_reg_table[size-1]; 309262306a36Sopenharmony_ci break; 309362306a36Sopenharmony_ci default: 309462306a36Sopenharmony_ci /* Read the scratch registers of SDIO function1 */ 309562306a36Sopenharmony_ci if (count == 4) 309662306a36Sopenharmony_ci mdelay(100); 309762306a36Sopenharmony_ci func = 1; 309862306a36Sopenharmony_ci reg_start = cardp->reg->func1_scratch_reg; 309962306a36Sopenharmony_ci reg_end = reg_start + MWIFIEX_SDIO_SCRATCH_SIZE; 310062306a36Sopenharmony_ci } 310162306a36Sopenharmony_ci 310262306a36Sopenharmony_ci if (count != 2) 310362306a36Sopenharmony_ci ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", 310462306a36Sopenharmony_ci func, reg_start, reg_end); 310562306a36Sopenharmony_ci else 310662306a36Sopenharmony_ci ptr += sprintf(ptr, "SDIO Func%d: ", func); 310762306a36Sopenharmony_ci 310862306a36Sopenharmony_ci for (reg = reg_start; reg <= reg_end;) { 310962306a36Sopenharmony_ci if (func == 0) 311062306a36Sopenharmony_ci data = sdio_f0_readb(cardp->func, reg, &ret); 311162306a36Sopenharmony_ci else 311262306a36Sopenharmony_ci data = sdio_readb(cardp->func, reg, &ret); 311362306a36Sopenharmony_ci 311462306a36Sopenharmony_ci if (count == 2) 311562306a36Sopenharmony_ci ptr += sprintf(ptr, "(%#x) ", reg); 311662306a36Sopenharmony_ci if (!ret) { 311762306a36Sopenharmony_ci ptr += sprintf(ptr, "%02x ", data); 311862306a36Sopenharmony_ci } else { 311962306a36Sopenharmony_ci ptr += sprintf(ptr, "ERR"); 312062306a36Sopenharmony_ci break; 312162306a36Sopenharmony_ci } 312262306a36Sopenharmony_ci 312362306a36Sopenharmony_ci if (count == 2 && reg < reg_end) 312462306a36Sopenharmony_ci reg = cardp->reg->func1_spec_reg_table[index++]; 312562306a36Sopenharmony_ci else 312662306a36Sopenharmony_ci reg++; 312762306a36Sopenharmony_ci } 312862306a36Sopenharmony_ci 312962306a36Sopenharmony_ci mwifiex_dbg(adapter, MSG, "%s\n", buf); 313062306a36Sopenharmony_ci p += sprintf(p, "%s\n", buf); 313162306a36Sopenharmony_ci } 313262306a36Sopenharmony_ci 313362306a36Sopenharmony_ci sdio_release_host(cardp->func); 313462306a36Sopenharmony_ci 313562306a36Sopenharmony_ci mwifiex_dbg(adapter, MSG, "SDIO register dump end\n"); 313662306a36Sopenharmony_ci 313762306a36Sopenharmony_ci return p - drv_buf; 313862306a36Sopenharmony_ci} 313962306a36Sopenharmony_ci 314062306a36Sopenharmony_ci/* sdio device/function initialization, code is extracted 314162306a36Sopenharmony_ci * from init_if handler and register_dev handler. 314262306a36Sopenharmony_ci */ 314362306a36Sopenharmony_cistatic void mwifiex_sdio_up_dev(struct mwifiex_adapter *adapter) 314462306a36Sopenharmony_ci{ 314562306a36Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 314662306a36Sopenharmony_ci u8 sdio_ireg; 314762306a36Sopenharmony_ci 314862306a36Sopenharmony_ci sdio_claim_host(card->func); 314962306a36Sopenharmony_ci sdio_enable_func(card->func); 315062306a36Sopenharmony_ci sdio_set_block_size(card->func, MWIFIEX_SDIO_BLOCK_SIZE); 315162306a36Sopenharmony_ci sdio_release_host(card->func); 315262306a36Sopenharmony_ci 315362306a36Sopenharmony_ci /* tx_buf_size might be changed to 3584 by firmware during 315462306a36Sopenharmony_ci * data transfer, we will reset to default size. 315562306a36Sopenharmony_ci */ 315662306a36Sopenharmony_ci adapter->tx_buf_size = card->tx_buf_size; 315762306a36Sopenharmony_ci 315862306a36Sopenharmony_ci /* Read the host_int_status_reg for ACK the first interrupt got 315962306a36Sopenharmony_ci * from the bootloader. If we don't do this we get a interrupt 316062306a36Sopenharmony_ci * as soon as we register the irq. 316162306a36Sopenharmony_ci */ 316262306a36Sopenharmony_ci mwifiex_read_reg(adapter, card->reg->host_int_status_reg, &sdio_ireg); 316362306a36Sopenharmony_ci 316462306a36Sopenharmony_ci if (mwifiex_init_sdio_ioport(adapter)) 316562306a36Sopenharmony_ci dev_err(&card->func->dev, "error enabling SDIO port\n"); 316662306a36Sopenharmony_ci} 316762306a36Sopenharmony_ci 316862306a36Sopenharmony_cistatic struct mwifiex_if_ops sdio_ops = { 316962306a36Sopenharmony_ci .init_if = mwifiex_init_sdio, 317062306a36Sopenharmony_ci .cleanup_if = mwifiex_cleanup_sdio, 317162306a36Sopenharmony_ci .check_fw_status = mwifiex_check_fw_status, 317262306a36Sopenharmony_ci .check_winner_status = mwifiex_check_winner_status, 317362306a36Sopenharmony_ci .prog_fw = mwifiex_prog_fw_w_helper, 317462306a36Sopenharmony_ci .register_dev = mwifiex_register_dev, 317562306a36Sopenharmony_ci .unregister_dev = mwifiex_unregister_dev, 317662306a36Sopenharmony_ci .enable_int = mwifiex_sdio_enable_host_int, 317762306a36Sopenharmony_ci .disable_int = mwifiex_sdio_disable_host_int, 317862306a36Sopenharmony_ci .process_int_status = mwifiex_process_int_status, 317962306a36Sopenharmony_ci .host_to_card = mwifiex_sdio_host_to_card, 318062306a36Sopenharmony_ci .wakeup = mwifiex_pm_wakeup_card, 318162306a36Sopenharmony_ci .wakeup_complete = mwifiex_pm_wakeup_card_complete, 318262306a36Sopenharmony_ci 318362306a36Sopenharmony_ci /* SDIO specific */ 318462306a36Sopenharmony_ci .update_mp_end_port = mwifiex_update_mp_end_port, 318562306a36Sopenharmony_ci .cleanup_mpa_buf = mwifiex_cleanup_mpa_buf, 318662306a36Sopenharmony_ci .cmdrsp_complete = mwifiex_sdio_cmdrsp_complete, 318762306a36Sopenharmony_ci .event_complete = mwifiex_sdio_event_complete, 318862306a36Sopenharmony_ci .dnld_fw = mwifiex_sdio_dnld_fw, 318962306a36Sopenharmony_ci .card_reset = mwifiex_sdio_card_reset, 319062306a36Sopenharmony_ci .reg_dump = mwifiex_sdio_reg_dump, 319162306a36Sopenharmony_ci .device_dump = mwifiex_sdio_device_dump, 319262306a36Sopenharmony_ci .deaggr_pkt = mwifiex_deaggr_sdio_pkt, 319362306a36Sopenharmony_ci .up_dev = mwifiex_sdio_up_dev, 319462306a36Sopenharmony_ci}; 319562306a36Sopenharmony_ci 319662306a36Sopenharmony_cimodule_driver(mwifiex_sdio, sdio_register_driver, sdio_unregister_driver); 319762306a36Sopenharmony_ci 319862306a36Sopenharmony_ciMODULE_AUTHOR("Marvell International Ltd."); 319962306a36Sopenharmony_ciMODULE_DESCRIPTION("Marvell WiFi-Ex SDIO Driver version " SDIO_VERSION); 320062306a36Sopenharmony_ciMODULE_VERSION(SDIO_VERSION); 320162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 320262306a36Sopenharmony_ciMODULE_FIRMWARE(SD8786_DEFAULT_FW_NAME); 320362306a36Sopenharmony_ciMODULE_FIRMWARE(SD8787_DEFAULT_FW_NAME); 320462306a36Sopenharmony_ciMODULE_FIRMWARE(SD8797_DEFAULT_FW_NAME); 320562306a36Sopenharmony_ciMODULE_FIRMWARE(SD8897_DEFAULT_FW_NAME); 320662306a36Sopenharmony_ciMODULE_FIRMWARE(SD8887_DEFAULT_FW_NAME); 320762306a36Sopenharmony_ciMODULE_FIRMWARE(SD8977_DEFAULT_FW_NAME); 320862306a36Sopenharmony_ciMODULE_FIRMWARE(SD8978_SDIOUART_FW_NAME); 320962306a36Sopenharmony_ciMODULE_FIRMWARE(SD8987_DEFAULT_FW_NAME); 321062306a36Sopenharmony_ciMODULE_FIRMWARE(SD8997_DEFAULT_FW_NAME); 321162306a36Sopenharmony_ciMODULE_FIRMWARE(SD8997_SDIOUART_FW_NAME); 3212