18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2004-2011 Atheros Communications Inc.
48c2ecf20Sopenharmony_ci * Copyright (c) 2011-2012,2017 Qualcomm Atheros, Inc.
58c2ecf20Sopenharmony_ci * Copyright (c) 2016-2017 Erik Stromdahl <erik.stromdahl@gmail.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/mmc/card.h>
108c2ecf20Sopenharmony_ci#include <linux/mmc/mmc.h>
118c2ecf20Sopenharmony_ci#include <linux/mmc/host.h>
128c2ecf20Sopenharmony_ci#include <linux/mmc/sdio_func.h>
138c2ecf20Sopenharmony_ci#include <linux/mmc/sdio_ids.h>
148c2ecf20Sopenharmony_ci#include <linux/mmc/sdio.h>
158c2ecf20Sopenharmony_ci#include <linux/mmc/sd.h>
168c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
178c2ecf20Sopenharmony_ci#include "core.h"
188c2ecf20Sopenharmony_ci#include "bmi.h"
198c2ecf20Sopenharmony_ci#include "debug.h"
208c2ecf20Sopenharmony_ci#include "hif.h"
218c2ecf20Sopenharmony_ci#include "htc.h"
228c2ecf20Sopenharmony_ci#include "mac.h"
238c2ecf20Sopenharmony_ci#include "targaddrs.h"
248c2ecf20Sopenharmony_ci#include "trace.h"
258c2ecf20Sopenharmony_ci#include "sdio.h"
268c2ecf20Sopenharmony_ci#include "coredump.h"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_civoid ath10k_sdio_fw_crashed_dump(struct ath10k *ar);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define ATH10K_SDIO_VSG_BUF_SIZE	(64 * 1024)
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/* inlined helper functions */
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic inline int ath10k_sdio_calc_txrx_padded_len(struct ath10k_sdio *ar_sdio,
358c2ecf20Sopenharmony_ci						   size_t len)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	return __ALIGN_MASK((len), ar_sdio->mbox_info.block_mask);
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic inline enum ath10k_htc_ep_id pipe_id_to_eid(u8 pipe_id)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	return (enum ath10k_htc_ep_id)pipe_id;
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic inline void ath10k_sdio_mbox_free_rx_pkt(struct ath10k_sdio_rx_data *pkt)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	dev_kfree_skb(pkt->skb);
488c2ecf20Sopenharmony_ci	pkt->skb = NULL;
498c2ecf20Sopenharmony_ci	pkt->alloc_len = 0;
508c2ecf20Sopenharmony_ci	pkt->act_len = 0;
518c2ecf20Sopenharmony_ci	pkt->trailer_only = false;
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic inline int ath10k_sdio_mbox_alloc_rx_pkt(struct ath10k_sdio_rx_data *pkt,
558c2ecf20Sopenharmony_ci						size_t act_len, size_t full_len,
568c2ecf20Sopenharmony_ci						bool part_of_bundle,
578c2ecf20Sopenharmony_ci						bool last_in_bundle)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	pkt->skb = dev_alloc_skb(full_len);
608c2ecf20Sopenharmony_ci	if (!pkt->skb)
618c2ecf20Sopenharmony_ci		return -ENOMEM;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	pkt->act_len = act_len;
648c2ecf20Sopenharmony_ci	pkt->alloc_len = full_len;
658c2ecf20Sopenharmony_ci	pkt->part_of_bundle = part_of_bundle;
668c2ecf20Sopenharmony_ci	pkt->last_in_bundle = last_in_bundle;
678c2ecf20Sopenharmony_ci	pkt->trailer_only = false;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	return 0;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic inline bool is_trailer_only_msg(struct ath10k_sdio_rx_data *pkt)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	bool trailer_only = false;
758c2ecf20Sopenharmony_ci	struct ath10k_htc_hdr *htc_hdr =
768c2ecf20Sopenharmony_ci		(struct ath10k_htc_hdr *)pkt->skb->data;
778c2ecf20Sopenharmony_ci	u16 len = __le16_to_cpu(htc_hdr->len);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (len == htc_hdr->trailer_len)
808c2ecf20Sopenharmony_ci		trailer_only = true;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	return trailer_only;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/* sdio/mmc functions */
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic inline void ath10k_sdio_set_cmd52_arg(u32 *arg, u8 write, u8 raw,
888c2ecf20Sopenharmony_ci					     unsigned int address,
898c2ecf20Sopenharmony_ci					     unsigned char val)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	*arg = FIELD_PREP(BIT(31), write) |
928c2ecf20Sopenharmony_ci	       FIELD_PREP(BIT(27), raw) |
938c2ecf20Sopenharmony_ci	       FIELD_PREP(BIT(26), 1) |
948c2ecf20Sopenharmony_ci	       FIELD_PREP(GENMASK(25, 9), address) |
958c2ecf20Sopenharmony_ci	       FIELD_PREP(BIT(8), 1) |
968c2ecf20Sopenharmony_ci	       FIELD_PREP(GENMASK(7, 0), val);
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic int ath10k_sdio_func0_cmd52_wr_byte(struct mmc_card *card,
1008c2ecf20Sopenharmony_ci					   unsigned int address,
1018c2ecf20Sopenharmony_ci					   unsigned char byte)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	struct mmc_command io_cmd;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	memset(&io_cmd, 0, sizeof(io_cmd));
1068c2ecf20Sopenharmony_ci	ath10k_sdio_set_cmd52_arg(&io_cmd.arg, 1, 0, address, byte);
1078c2ecf20Sopenharmony_ci	io_cmd.opcode = SD_IO_RW_DIRECT;
1088c2ecf20Sopenharmony_ci	io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	return mmc_wait_for_cmd(card->host, &io_cmd, 0);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic int ath10k_sdio_func0_cmd52_rd_byte(struct mmc_card *card,
1148c2ecf20Sopenharmony_ci					   unsigned int address,
1158c2ecf20Sopenharmony_ci					   unsigned char *byte)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	struct mmc_command io_cmd;
1188c2ecf20Sopenharmony_ci	int ret;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	memset(&io_cmd, 0, sizeof(io_cmd));
1218c2ecf20Sopenharmony_ci	ath10k_sdio_set_cmd52_arg(&io_cmd.arg, 0, 0, address, 0);
1228c2ecf20Sopenharmony_ci	io_cmd.opcode = SD_IO_RW_DIRECT;
1238c2ecf20Sopenharmony_ci	io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	ret = mmc_wait_for_cmd(card->host, &io_cmd, 0);
1268c2ecf20Sopenharmony_ci	if (!ret)
1278c2ecf20Sopenharmony_ci		*byte = io_cmd.resp[0];
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	return ret;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic int ath10k_sdio_config(struct ath10k *ar)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
1358c2ecf20Sopenharmony_ci	struct sdio_func *func = ar_sdio->func;
1368c2ecf20Sopenharmony_ci	unsigned char byte, asyncintdelay = 2;
1378c2ecf20Sopenharmony_ci	int ret;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_BOOT, "sdio configuration\n");
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	sdio_claim_host(func);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	byte = 0;
1448c2ecf20Sopenharmony_ci	ret = ath10k_sdio_func0_cmd52_rd_byte(func->card,
1458c2ecf20Sopenharmony_ci					      SDIO_CCCR_DRIVE_STRENGTH,
1468c2ecf20Sopenharmony_ci					      &byte);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	byte &= ~ATH10K_SDIO_DRIVE_DTSX_MASK;
1498c2ecf20Sopenharmony_ci	byte |= FIELD_PREP(ATH10K_SDIO_DRIVE_DTSX_MASK,
1508c2ecf20Sopenharmony_ci			   ATH10K_SDIO_DRIVE_DTSX_TYPE_D);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	ret = ath10k_sdio_func0_cmd52_wr_byte(func->card,
1538c2ecf20Sopenharmony_ci					      SDIO_CCCR_DRIVE_STRENGTH,
1548c2ecf20Sopenharmony_ci					      byte);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	byte = 0;
1578c2ecf20Sopenharmony_ci	ret = ath10k_sdio_func0_cmd52_rd_byte(
1588c2ecf20Sopenharmony_ci		func->card,
1598c2ecf20Sopenharmony_ci		CCCR_SDIO_DRIVER_STRENGTH_ENABLE_ADDR,
1608c2ecf20Sopenharmony_ci		&byte);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	byte |= (CCCR_SDIO_DRIVER_STRENGTH_ENABLE_A |
1638c2ecf20Sopenharmony_ci		 CCCR_SDIO_DRIVER_STRENGTH_ENABLE_C |
1648c2ecf20Sopenharmony_ci		 CCCR_SDIO_DRIVER_STRENGTH_ENABLE_D);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	ret = ath10k_sdio_func0_cmd52_wr_byte(func->card,
1678c2ecf20Sopenharmony_ci					      CCCR_SDIO_DRIVER_STRENGTH_ENABLE_ADDR,
1688c2ecf20Sopenharmony_ci					      byte);
1698c2ecf20Sopenharmony_ci	if (ret) {
1708c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to enable driver strength: %d\n", ret);
1718c2ecf20Sopenharmony_ci		goto out;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	byte = 0;
1758c2ecf20Sopenharmony_ci	ret = ath10k_sdio_func0_cmd52_rd_byte(func->card,
1768c2ecf20Sopenharmony_ci					      CCCR_SDIO_IRQ_MODE_REG_SDIO3,
1778c2ecf20Sopenharmony_ci					      &byte);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	byte |= SDIO_IRQ_MODE_ASYNC_4BIT_IRQ_SDIO3;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	ret = ath10k_sdio_func0_cmd52_wr_byte(func->card,
1828c2ecf20Sopenharmony_ci					      CCCR_SDIO_IRQ_MODE_REG_SDIO3,
1838c2ecf20Sopenharmony_ci					      byte);
1848c2ecf20Sopenharmony_ci	if (ret) {
1858c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to enable 4-bit async irq mode: %d\n",
1868c2ecf20Sopenharmony_ci			    ret);
1878c2ecf20Sopenharmony_ci		goto out;
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	byte = 0;
1918c2ecf20Sopenharmony_ci	ret = ath10k_sdio_func0_cmd52_rd_byte(func->card,
1928c2ecf20Sopenharmony_ci					      CCCR_SDIO_ASYNC_INT_DELAY_ADDRESS,
1938c2ecf20Sopenharmony_ci					      &byte);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	byte &= ~CCCR_SDIO_ASYNC_INT_DELAY_MASK;
1968c2ecf20Sopenharmony_ci	byte |= FIELD_PREP(CCCR_SDIO_ASYNC_INT_DELAY_MASK, asyncintdelay);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	ret = ath10k_sdio_func0_cmd52_wr_byte(func->card,
1998c2ecf20Sopenharmony_ci					      CCCR_SDIO_ASYNC_INT_DELAY_ADDRESS,
2008c2ecf20Sopenharmony_ci					      byte);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	/* give us some time to enable, in ms */
2038c2ecf20Sopenharmony_ci	func->enable_timeout = 100;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	ret = sdio_set_block_size(func, ar_sdio->mbox_info.block_size);
2068c2ecf20Sopenharmony_ci	if (ret) {
2078c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to set sdio block size to %d: %d\n",
2088c2ecf20Sopenharmony_ci			    ar_sdio->mbox_info.block_size, ret);
2098c2ecf20Sopenharmony_ci		goto out;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ciout:
2138c2ecf20Sopenharmony_ci	sdio_release_host(func);
2148c2ecf20Sopenharmony_ci	return ret;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic int ath10k_sdio_write32(struct ath10k *ar, u32 addr, u32 val)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
2208c2ecf20Sopenharmony_ci	struct sdio_func *func = ar_sdio->func;
2218c2ecf20Sopenharmony_ci	int ret;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	sdio_claim_host(func);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	sdio_writel(func, val, addr, &ret);
2268c2ecf20Sopenharmony_ci	if (ret) {
2278c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to write 0x%x to address 0x%x: %d\n",
2288c2ecf20Sopenharmony_ci			    val, addr, ret);
2298c2ecf20Sopenharmony_ci		goto out;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio write32 addr 0x%x val 0x%x\n",
2338c2ecf20Sopenharmony_ci		   addr, val);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ciout:
2368c2ecf20Sopenharmony_ci	sdio_release_host(func);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	return ret;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic int ath10k_sdio_writesb32(struct ath10k *ar, u32 addr, u32 val)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
2448c2ecf20Sopenharmony_ci	struct sdio_func *func = ar_sdio->func;
2458c2ecf20Sopenharmony_ci	__le32 *buf;
2468c2ecf20Sopenharmony_ci	int ret;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
2498c2ecf20Sopenharmony_ci	if (!buf)
2508c2ecf20Sopenharmony_ci		return -ENOMEM;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	*buf = cpu_to_le32(val);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	sdio_claim_host(func);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	ret = sdio_writesb(func, addr, buf, sizeof(*buf));
2578c2ecf20Sopenharmony_ci	if (ret) {
2588c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to write value 0x%x to fixed sb address 0x%x: %d\n",
2598c2ecf20Sopenharmony_ci			    val, addr, ret);
2608c2ecf20Sopenharmony_ci		goto out;
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio writesb32 addr 0x%x val 0x%x\n",
2648c2ecf20Sopenharmony_ci		   addr, val);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ciout:
2678c2ecf20Sopenharmony_ci	sdio_release_host(func);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	kfree(buf);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	return ret;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic int ath10k_sdio_read32(struct ath10k *ar, u32 addr, u32 *val)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
2778c2ecf20Sopenharmony_ci	struct sdio_func *func = ar_sdio->func;
2788c2ecf20Sopenharmony_ci	int ret;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	sdio_claim_host(func);
2818c2ecf20Sopenharmony_ci	*val = sdio_readl(func, addr, &ret);
2828c2ecf20Sopenharmony_ci	if (ret) {
2838c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to read from address 0x%x: %d\n",
2848c2ecf20Sopenharmony_ci			    addr, ret);
2858c2ecf20Sopenharmony_ci		goto out;
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio read32 addr 0x%x val 0x%x\n",
2898c2ecf20Sopenharmony_ci		   addr, *val);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ciout:
2928c2ecf20Sopenharmony_ci	sdio_release_host(func);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	return ret;
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic int ath10k_sdio_read(struct ath10k *ar, u32 addr, void *buf, size_t len)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
3008c2ecf20Sopenharmony_ci	struct sdio_func *func = ar_sdio->func;
3018c2ecf20Sopenharmony_ci	int ret;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	sdio_claim_host(func);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	ret = sdio_memcpy_fromio(func, buf, addr, len);
3068c2ecf20Sopenharmony_ci	if (ret) {
3078c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to read from address 0x%x: %d\n",
3088c2ecf20Sopenharmony_ci			    addr, ret);
3098c2ecf20Sopenharmony_ci		goto out;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio read addr 0x%x buf 0x%p len %zu\n",
3138c2ecf20Sopenharmony_ci		   addr, buf, len);
3148c2ecf20Sopenharmony_ci	ath10k_dbg_dump(ar, ATH10K_DBG_SDIO_DUMP, NULL, "sdio read ", buf, len);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ciout:
3178c2ecf20Sopenharmony_ci	sdio_release_host(func);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	return ret;
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic int ath10k_sdio_write(struct ath10k *ar, u32 addr, const void *buf, size_t len)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
3258c2ecf20Sopenharmony_ci	struct sdio_func *func = ar_sdio->func;
3268c2ecf20Sopenharmony_ci	int ret;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	sdio_claim_host(func);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	/* For some reason toio() doesn't have const for the buffer, need
3318c2ecf20Sopenharmony_ci	 * an ugly hack to workaround that.
3328c2ecf20Sopenharmony_ci	 */
3338c2ecf20Sopenharmony_ci	ret = sdio_memcpy_toio(func, addr, (void *)buf, len);
3348c2ecf20Sopenharmony_ci	if (ret) {
3358c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to write to address 0x%x: %d\n",
3368c2ecf20Sopenharmony_ci			    addr, ret);
3378c2ecf20Sopenharmony_ci		goto out;
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio write addr 0x%x buf 0x%p len %zu\n",
3418c2ecf20Sopenharmony_ci		   addr, buf, len);
3428c2ecf20Sopenharmony_ci	ath10k_dbg_dump(ar, ATH10K_DBG_SDIO_DUMP, NULL, "sdio write ", buf, len);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ciout:
3458c2ecf20Sopenharmony_ci	sdio_release_host(func);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	return ret;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic int ath10k_sdio_readsb(struct ath10k *ar, u32 addr, void *buf, size_t len)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
3538c2ecf20Sopenharmony_ci	struct sdio_func *func = ar_sdio->func;
3548c2ecf20Sopenharmony_ci	int ret;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	sdio_claim_host(func);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	len = round_down(len, ar_sdio->mbox_info.block_size);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	ret = sdio_readsb(func, buf, addr, len);
3618c2ecf20Sopenharmony_ci	if (ret) {
3628c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to read from fixed (sb) address 0x%x: %d\n",
3638c2ecf20Sopenharmony_ci			    addr, ret);
3648c2ecf20Sopenharmony_ci		goto out;
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio readsb addr 0x%x buf 0x%p len %zu\n",
3688c2ecf20Sopenharmony_ci		   addr, buf, len);
3698c2ecf20Sopenharmony_ci	ath10k_dbg_dump(ar, ATH10K_DBG_SDIO_DUMP, NULL, "sdio readsb ", buf, len);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ciout:
3728c2ecf20Sopenharmony_ci	sdio_release_host(func);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	return ret;
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci/* HIF mbox functions */
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic int ath10k_sdio_mbox_rx_process_packet(struct ath10k *ar,
3808c2ecf20Sopenharmony_ci					      struct ath10k_sdio_rx_data *pkt,
3818c2ecf20Sopenharmony_ci					      u32 *lookaheads,
3828c2ecf20Sopenharmony_ci					      int *n_lookaheads)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	struct ath10k_htc *htc = &ar->htc;
3858c2ecf20Sopenharmony_ci	struct sk_buff *skb = pkt->skb;
3868c2ecf20Sopenharmony_ci	struct ath10k_htc_hdr *htc_hdr = (struct ath10k_htc_hdr *)skb->data;
3878c2ecf20Sopenharmony_ci	bool trailer_present = htc_hdr->flags & ATH10K_HTC_FLAG_TRAILER_PRESENT;
3888c2ecf20Sopenharmony_ci	enum ath10k_htc_ep_id eid;
3898c2ecf20Sopenharmony_ci	u8 *trailer;
3908c2ecf20Sopenharmony_ci	int ret;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	if (trailer_present) {
3938c2ecf20Sopenharmony_ci		trailer = skb->data + skb->len - htc_hdr->trailer_len;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci		eid = pipe_id_to_eid(htc_hdr->eid);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci		ret = ath10k_htc_process_trailer(htc,
3988c2ecf20Sopenharmony_ci						 trailer,
3998c2ecf20Sopenharmony_ci						 htc_hdr->trailer_len,
4008c2ecf20Sopenharmony_ci						 eid,
4018c2ecf20Sopenharmony_ci						 lookaheads,
4028c2ecf20Sopenharmony_ci						 n_lookaheads);
4038c2ecf20Sopenharmony_ci		if (ret)
4048c2ecf20Sopenharmony_ci			return ret;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci		if (is_trailer_only_msg(pkt))
4078c2ecf20Sopenharmony_ci			pkt->trailer_only = true;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci		skb_trim(skb, skb->len - htc_hdr->trailer_len);
4108c2ecf20Sopenharmony_ci	}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	skb_pull(skb, sizeof(*htc_hdr));
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	return 0;
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic int ath10k_sdio_mbox_rx_process_packets(struct ath10k *ar,
4188c2ecf20Sopenharmony_ci					       u32 lookaheads[],
4198c2ecf20Sopenharmony_ci					       int *n_lookahead)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
4228c2ecf20Sopenharmony_ci	struct ath10k_htc *htc = &ar->htc;
4238c2ecf20Sopenharmony_ci	struct ath10k_sdio_rx_data *pkt;
4248c2ecf20Sopenharmony_ci	struct ath10k_htc_ep *ep;
4258c2ecf20Sopenharmony_ci	struct ath10k_skb_rxcb *cb;
4268c2ecf20Sopenharmony_ci	enum ath10k_htc_ep_id id;
4278c2ecf20Sopenharmony_ci	int ret, i, *n_lookahead_local;
4288c2ecf20Sopenharmony_ci	u32 *lookaheads_local;
4298c2ecf20Sopenharmony_ci	int lookahead_idx = 0;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	for (i = 0; i < ar_sdio->n_rx_pkts; i++) {
4328c2ecf20Sopenharmony_ci		lookaheads_local = lookaheads;
4338c2ecf20Sopenharmony_ci		n_lookahead_local = n_lookahead;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci		id = ((struct ath10k_htc_hdr *)
4368c2ecf20Sopenharmony_ci		      &lookaheads[lookahead_idx++])->eid;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci		if (id >= ATH10K_HTC_EP_COUNT) {
4398c2ecf20Sopenharmony_ci			ath10k_warn(ar, "invalid endpoint in look-ahead: %d\n",
4408c2ecf20Sopenharmony_ci				    id);
4418c2ecf20Sopenharmony_ci			ret = -ENOMEM;
4428c2ecf20Sopenharmony_ci			goto out;
4438c2ecf20Sopenharmony_ci		}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci		ep = &htc->endpoint[id];
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci		if (ep->service_id == 0) {
4488c2ecf20Sopenharmony_ci			ath10k_warn(ar, "ep %d is not connected\n", id);
4498c2ecf20Sopenharmony_ci			ret = -ENOMEM;
4508c2ecf20Sopenharmony_ci			goto out;
4518c2ecf20Sopenharmony_ci		}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci		pkt = &ar_sdio->rx_pkts[i];
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci		if (pkt->part_of_bundle && !pkt->last_in_bundle) {
4568c2ecf20Sopenharmony_ci			/* Only read lookahead's from RX trailers
4578c2ecf20Sopenharmony_ci			 * for the last packet in a bundle.
4588c2ecf20Sopenharmony_ci			 */
4598c2ecf20Sopenharmony_ci			lookahead_idx--;
4608c2ecf20Sopenharmony_ci			lookaheads_local = NULL;
4618c2ecf20Sopenharmony_ci			n_lookahead_local = NULL;
4628c2ecf20Sopenharmony_ci		}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci		ret = ath10k_sdio_mbox_rx_process_packet(ar,
4658c2ecf20Sopenharmony_ci							 pkt,
4668c2ecf20Sopenharmony_ci							 lookaheads_local,
4678c2ecf20Sopenharmony_ci							 n_lookahead_local);
4688c2ecf20Sopenharmony_ci		if (ret)
4698c2ecf20Sopenharmony_ci			goto out;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci		if (!pkt->trailer_only) {
4728c2ecf20Sopenharmony_ci			cb = ATH10K_SKB_RXCB(pkt->skb);
4738c2ecf20Sopenharmony_ci			cb->eid = id;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci			skb_queue_tail(&ar_sdio->rx_head, pkt->skb);
4768c2ecf20Sopenharmony_ci			queue_work(ar->workqueue_aux,
4778c2ecf20Sopenharmony_ci				   &ar_sdio->async_work_rx);
4788c2ecf20Sopenharmony_ci		} else {
4798c2ecf20Sopenharmony_ci			kfree_skb(pkt->skb);
4808c2ecf20Sopenharmony_ci		}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci		/* The RX complete handler now owns the skb...*/
4838c2ecf20Sopenharmony_ci		pkt->skb = NULL;
4848c2ecf20Sopenharmony_ci		pkt->alloc_len = 0;
4858c2ecf20Sopenharmony_ci	}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	ret = 0;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ciout:
4908c2ecf20Sopenharmony_ci	/* Free all packets that was not passed on to the RX completion
4918c2ecf20Sopenharmony_ci	 * handler...
4928c2ecf20Sopenharmony_ci	 */
4938c2ecf20Sopenharmony_ci	for (; i < ar_sdio->n_rx_pkts; i++)
4948c2ecf20Sopenharmony_ci		ath10k_sdio_mbox_free_rx_pkt(&ar_sdio->rx_pkts[i]);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	return ret;
4978c2ecf20Sopenharmony_ci}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_cistatic int ath10k_sdio_mbox_alloc_bundle(struct ath10k *ar,
5008c2ecf20Sopenharmony_ci					 struct ath10k_sdio_rx_data *rx_pkts,
5018c2ecf20Sopenharmony_ci					 struct ath10k_htc_hdr *htc_hdr,
5028c2ecf20Sopenharmony_ci					 size_t full_len, size_t act_len,
5038c2ecf20Sopenharmony_ci					 size_t *bndl_cnt)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	int ret, i;
5068c2ecf20Sopenharmony_ci	u8 max_msgs = ar->htc.max_msgs_per_htc_bundle;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	*bndl_cnt = ath10k_htc_get_bundle_count(max_msgs, htc_hdr->flags);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	if (*bndl_cnt > max_msgs) {
5118c2ecf20Sopenharmony_ci		ath10k_warn(ar,
5128c2ecf20Sopenharmony_ci			    "HTC bundle length %u exceeds maximum %u\n",
5138c2ecf20Sopenharmony_ci			    le16_to_cpu(htc_hdr->len),
5148c2ecf20Sopenharmony_ci			    max_msgs);
5158c2ecf20Sopenharmony_ci		return -ENOMEM;
5168c2ecf20Sopenharmony_ci	}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	/* Allocate bndl_cnt extra skb's for the bundle.
5198c2ecf20Sopenharmony_ci	 * The package containing the
5208c2ecf20Sopenharmony_ci	 * ATH10K_HTC_FLAG_BUNDLE_MASK flag is not included
5218c2ecf20Sopenharmony_ci	 * in bndl_cnt. The skb for that packet will be
5228c2ecf20Sopenharmony_ci	 * allocated separately.
5238c2ecf20Sopenharmony_ci	 */
5248c2ecf20Sopenharmony_ci	for (i = 0; i < *bndl_cnt; i++) {
5258c2ecf20Sopenharmony_ci		ret = ath10k_sdio_mbox_alloc_rx_pkt(&rx_pkts[i],
5268c2ecf20Sopenharmony_ci						    act_len,
5278c2ecf20Sopenharmony_ci						    full_len,
5288c2ecf20Sopenharmony_ci						    true,
5298c2ecf20Sopenharmony_ci						    false);
5308c2ecf20Sopenharmony_ci		if (ret)
5318c2ecf20Sopenharmony_ci			return ret;
5328c2ecf20Sopenharmony_ci	}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	return 0;
5358c2ecf20Sopenharmony_ci}
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_cistatic int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar,
5388c2ecf20Sopenharmony_ci				     u32 lookaheads[], int n_lookaheads)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
5418c2ecf20Sopenharmony_ci	struct ath10k_htc_hdr *htc_hdr;
5428c2ecf20Sopenharmony_ci	size_t full_len, act_len;
5438c2ecf20Sopenharmony_ci	bool last_in_bundle;
5448c2ecf20Sopenharmony_ci	int ret, i;
5458c2ecf20Sopenharmony_ci	int pkt_cnt = 0;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	if (n_lookaheads > ATH10K_SDIO_MAX_RX_MSGS) {
5488c2ecf20Sopenharmony_ci		ath10k_warn(ar, "the total number of pkts to be fetched (%u) exceeds maximum %u\n",
5498c2ecf20Sopenharmony_ci			    n_lookaheads, ATH10K_SDIO_MAX_RX_MSGS);
5508c2ecf20Sopenharmony_ci		ret = -ENOMEM;
5518c2ecf20Sopenharmony_ci		goto err;
5528c2ecf20Sopenharmony_ci	}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	for (i = 0; i < n_lookaheads; i++) {
5558c2ecf20Sopenharmony_ci		htc_hdr = (struct ath10k_htc_hdr *)&lookaheads[i];
5568c2ecf20Sopenharmony_ci		last_in_bundle = false;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci		if (le16_to_cpu(htc_hdr->len) > ATH10K_HTC_MBOX_MAX_PAYLOAD_LENGTH) {
5598c2ecf20Sopenharmony_ci			ath10k_warn(ar, "payload length %d exceeds max htc length: %zu\n",
5608c2ecf20Sopenharmony_ci				    le16_to_cpu(htc_hdr->len),
5618c2ecf20Sopenharmony_ci				    ATH10K_HTC_MBOX_MAX_PAYLOAD_LENGTH);
5628c2ecf20Sopenharmony_ci			ret = -ENOMEM;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci			queue_work(ar->workqueue, &ar->restart_work);
5658c2ecf20Sopenharmony_ci			ath10k_warn(ar, "exceeds length, start recovery\n");
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci			goto err;
5688c2ecf20Sopenharmony_ci		}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci		act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr);
5718c2ecf20Sopenharmony_ci		full_len = ath10k_sdio_calc_txrx_padded_len(ar_sdio, act_len);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci		if (full_len > ATH10K_SDIO_MAX_BUFFER_SIZE) {
5748c2ecf20Sopenharmony_ci			ath10k_warn(ar, "rx buffer requested with invalid htc_hdr length (%d, 0x%x): %d\n",
5758c2ecf20Sopenharmony_ci				    htc_hdr->eid, htc_hdr->flags,
5768c2ecf20Sopenharmony_ci				    le16_to_cpu(htc_hdr->len));
5778c2ecf20Sopenharmony_ci			ret = -EINVAL;
5788c2ecf20Sopenharmony_ci			goto err;
5798c2ecf20Sopenharmony_ci		}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci		if (ath10k_htc_get_bundle_count(
5828c2ecf20Sopenharmony_ci			ar->htc.max_msgs_per_htc_bundle, htc_hdr->flags)) {
5838c2ecf20Sopenharmony_ci			/* HTC header indicates that every packet to follow
5848c2ecf20Sopenharmony_ci			 * has the same padded length so that it can be
5858c2ecf20Sopenharmony_ci			 * optimally fetched as a full bundle.
5868c2ecf20Sopenharmony_ci			 */
5878c2ecf20Sopenharmony_ci			size_t bndl_cnt;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci			ret = ath10k_sdio_mbox_alloc_bundle(ar,
5908c2ecf20Sopenharmony_ci							    &ar_sdio->rx_pkts[pkt_cnt],
5918c2ecf20Sopenharmony_ci							    htc_hdr,
5928c2ecf20Sopenharmony_ci							    full_len,
5938c2ecf20Sopenharmony_ci							    act_len,
5948c2ecf20Sopenharmony_ci							    &bndl_cnt);
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci			if (ret) {
5978c2ecf20Sopenharmony_ci				ath10k_warn(ar, "failed to allocate a bundle: %d\n",
5988c2ecf20Sopenharmony_ci					    ret);
5998c2ecf20Sopenharmony_ci				goto err;
6008c2ecf20Sopenharmony_ci			}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci			pkt_cnt += bndl_cnt;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci			/* next buffer will be the last in the bundle */
6058c2ecf20Sopenharmony_ci			last_in_bundle = true;
6068c2ecf20Sopenharmony_ci		}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci		/* Allocate skb for packet. If the packet had the
6098c2ecf20Sopenharmony_ci		 * ATH10K_HTC_FLAG_BUNDLE_MASK flag set, all bundled
6108c2ecf20Sopenharmony_ci		 * packet skb's have been allocated in the previous step.
6118c2ecf20Sopenharmony_ci		 */
6128c2ecf20Sopenharmony_ci		if (htc_hdr->flags & ATH10K_HTC_FLAGS_RECV_1MORE_BLOCK)
6138c2ecf20Sopenharmony_ci			full_len += ATH10K_HIF_MBOX_BLOCK_SIZE;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci		ret = ath10k_sdio_mbox_alloc_rx_pkt(&ar_sdio->rx_pkts[pkt_cnt],
6168c2ecf20Sopenharmony_ci						    act_len,
6178c2ecf20Sopenharmony_ci						    full_len,
6188c2ecf20Sopenharmony_ci						    last_in_bundle,
6198c2ecf20Sopenharmony_ci						    last_in_bundle);
6208c2ecf20Sopenharmony_ci		if (ret) {
6218c2ecf20Sopenharmony_ci			ath10k_warn(ar, "alloc_rx_pkt error %d\n", ret);
6228c2ecf20Sopenharmony_ci			goto err;
6238c2ecf20Sopenharmony_ci		}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci		pkt_cnt++;
6268c2ecf20Sopenharmony_ci	}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	ar_sdio->n_rx_pkts = pkt_cnt;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	return 0;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_cierr:
6338c2ecf20Sopenharmony_ci	for (i = 0; i < ATH10K_SDIO_MAX_RX_MSGS; i++) {
6348c2ecf20Sopenharmony_ci		if (!ar_sdio->rx_pkts[i].alloc_len)
6358c2ecf20Sopenharmony_ci			break;
6368c2ecf20Sopenharmony_ci		ath10k_sdio_mbox_free_rx_pkt(&ar_sdio->rx_pkts[i]);
6378c2ecf20Sopenharmony_ci	}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	return ret;
6408c2ecf20Sopenharmony_ci}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_cistatic int ath10k_sdio_mbox_rx_fetch(struct ath10k *ar)
6438c2ecf20Sopenharmony_ci{
6448c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
6458c2ecf20Sopenharmony_ci	struct ath10k_sdio_rx_data *pkt = &ar_sdio->rx_pkts[0];
6468c2ecf20Sopenharmony_ci	struct sk_buff *skb = pkt->skb;
6478c2ecf20Sopenharmony_ci	struct ath10k_htc_hdr *htc_hdr;
6488c2ecf20Sopenharmony_ci	int ret;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr,
6518c2ecf20Sopenharmony_ci				 skb->data, pkt->alloc_len);
6528c2ecf20Sopenharmony_ci	if (ret)
6538c2ecf20Sopenharmony_ci		goto err;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	htc_hdr = (struct ath10k_htc_hdr *)skb->data;
6568c2ecf20Sopenharmony_ci	pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr);
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	if (pkt->act_len > pkt->alloc_len) {
6598c2ecf20Sopenharmony_ci		ret = -EINVAL;
6608c2ecf20Sopenharmony_ci		goto err;
6618c2ecf20Sopenharmony_ci	}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	skb_put(skb, pkt->act_len);
6648c2ecf20Sopenharmony_ci	return 0;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_cierr:
6678c2ecf20Sopenharmony_ci	ar_sdio->n_rx_pkts = 0;
6688c2ecf20Sopenharmony_ci	ath10k_sdio_mbox_free_rx_pkt(pkt);
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	return ret;
6718c2ecf20Sopenharmony_ci}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_cistatic int ath10k_sdio_mbox_rx_fetch_bundle(struct ath10k *ar)
6748c2ecf20Sopenharmony_ci{
6758c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
6768c2ecf20Sopenharmony_ci	struct ath10k_sdio_rx_data *pkt;
6778c2ecf20Sopenharmony_ci	struct ath10k_htc_hdr *htc_hdr;
6788c2ecf20Sopenharmony_ci	int ret, i;
6798c2ecf20Sopenharmony_ci	u32 pkt_offset, virt_pkt_len;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	virt_pkt_len = 0;
6828c2ecf20Sopenharmony_ci	for (i = 0; i < ar_sdio->n_rx_pkts; i++)
6838c2ecf20Sopenharmony_ci		virt_pkt_len += ar_sdio->rx_pkts[i].alloc_len;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	if (virt_pkt_len > ATH10K_SDIO_VSG_BUF_SIZE) {
6868c2ecf20Sopenharmony_ci		ath10k_warn(ar, "sdio vsg buffer size limit: %d\n", virt_pkt_len);
6878c2ecf20Sopenharmony_ci		ret = -E2BIG;
6888c2ecf20Sopenharmony_ci		goto err;
6898c2ecf20Sopenharmony_ci	}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr,
6928c2ecf20Sopenharmony_ci				 ar_sdio->vsg_buffer, virt_pkt_len);
6938c2ecf20Sopenharmony_ci	if (ret) {
6948c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to read bundle packets: %d", ret);
6958c2ecf20Sopenharmony_ci		goto err;
6968c2ecf20Sopenharmony_ci	}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	pkt_offset = 0;
6998c2ecf20Sopenharmony_ci	for (i = 0; i < ar_sdio->n_rx_pkts; i++) {
7008c2ecf20Sopenharmony_ci		pkt = &ar_sdio->rx_pkts[i];
7018c2ecf20Sopenharmony_ci		htc_hdr = (struct ath10k_htc_hdr *)(ar_sdio->vsg_buffer + pkt_offset);
7028c2ecf20Sopenharmony_ci		pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr);
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci		if (pkt->act_len > pkt->alloc_len) {
7058c2ecf20Sopenharmony_ci			ret = -EINVAL;
7068c2ecf20Sopenharmony_ci			goto err;
7078c2ecf20Sopenharmony_ci		}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci		skb_put_data(pkt->skb, htc_hdr, pkt->act_len);
7108c2ecf20Sopenharmony_ci		pkt_offset += pkt->alloc_len;
7118c2ecf20Sopenharmony_ci	}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	return 0;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_cierr:
7168c2ecf20Sopenharmony_ci	/* Free all packets that was not successfully fetched. */
7178c2ecf20Sopenharmony_ci	for (i = 0; i < ar_sdio->n_rx_pkts; i++)
7188c2ecf20Sopenharmony_ci		ath10k_sdio_mbox_free_rx_pkt(&ar_sdio->rx_pkts[i]);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	ar_sdio->n_rx_pkts = 0;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	return ret;
7238c2ecf20Sopenharmony_ci}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci/* This is the timeout for mailbox processing done in the sdio irq
7268c2ecf20Sopenharmony_ci * handler. The timeout is deliberately set quite high since SDIO dump logs
7278c2ecf20Sopenharmony_ci * over serial port can/will add a substantial overhead to the processing
7288c2ecf20Sopenharmony_ci * (if enabled).
7298c2ecf20Sopenharmony_ci */
7308c2ecf20Sopenharmony_ci#define SDIO_MBOX_PROCESSING_TIMEOUT_HZ (20 * HZ)
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_cistatic int ath10k_sdio_mbox_rxmsg_pending_handler(struct ath10k *ar,
7338c2ecf20Sopenharmony_ci						  u32 msg_lookahead, bool *done)
7348c2ecf20Sopenharmony_ci{
7358c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
7368c2ecf20Sopenharmony_ci	u32 lookaheads[ATH10K_SDIO_MAX_RX_MSGS];
7378c2ecf20Sopenharmony_ci	int n_lookaheads = 1;
7388c2ecf20Sopenharmony_ci	unsigned long timeout;
7398c2ecf20Sopenharmony_ci	int ret;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	*done = true;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	/* Copy the lookahead obtained from the HTC register table into our
7448c2ecf20Sopenharmony_ci	 * temp array as a start value.
7458c2ecf20Sopenharmony_ci	 */
7468c2ecf20Sopenharmony_ci	lookaheads[0] = msg_lookahead;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	timeout = jiffies + SDIO_MBOX_PROCESSING_TIMEOUT_HZ;
7498c2ecf20Sopenharmony_ci	do {
7508c2ecf20Sopenharmony_ci		/* Try to allocate as many HTC RX packets indicated by
7518c2ecf20Sopenharmony_ci		 * n_lookaheads.
7528c2ecf20Sopenharmony_ci		 */
7538c2ecf20Sopenharmony_ci		ret = ath10k_sdio_mbox_rx_alloc(ar, lookaheads,
7548c2ecf20Sopenharmony_ci						n_lookaheads);
7558c2ecf20Sopenharmony_ci		if (ret)
7568c2ecf20Sopenharmony_ci			break;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci		if (ar_sdio->n_rx_pkts >= 2)
7598c2ecf20Sopenharmony_ci			/* A recv bundle was detected, force IRQ status
7608c2ecf20Sopenharmony_ci			 * re-check again.
7618c2ecf20Sopenharmony_ci			 */
7628c2ecf20Sopenharmony_ci			*done = false;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci		if (ar_sdio->n_rx_pkts > 1)
7658c2ecf20Sopenharmony_ci			ret = ath10k_sdio_mbox_rx_fetch_bundle(ar);
7668c2ecf20Sopenharmony_ci		else
7678c2ecf20Sopenharmony_ci			ret = ath10k_sdio_mbox_rx_fetch(ar);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci		/* Process fetched packets. This will potentially update
7708c2ecf20Sopenharmony_ci		 * n_lookaheads depending on if the packets contain lookahead
7718c2ecf20Sopenharmony_ci		 * reports.
7728c2ecf20Sopenharmony_ci		 */
7738c2ecf20Sopenharmony_ci		n_lookaheads = 0;
7748c2ecf20Sopenharmony_ci		ret = ath10k_sdio_mbox_rx_process_packets(ar,
7758c2ecf20Sopenharmony_ci							  lookaheads,
7768c2ecf20Sopenharmony_ci							  &n_lookaheads);
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci		if (!n_lookaheads || ret)
7798c2ecf20Sopenharmony_ci			break;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci		/* For SYNCH processing, if we get here, we are running
7828c2ecf20Sopenharmony_ci		 * through the loop again due to updated lookaheads. Set
7838c2ecf20Sopenharmony_ci		 * flag that we should re-check IRQ status registers again
7848c2ecf20Sopenharmony_ci		 * before leaving IRQ processing, this can net better
7858c2ecf20Sopenharmony_ci		 * performance in high throughput situations.
7868c2ecf20Sopenharmony_ci		 */
7878c2ecf20Sopenharmony_ci		*done = false;
7888c2ecf20Sopenharmony_ci	} while (time_before(jiffies, timeout));
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	if (ret && (ret != -ECANCELED))
7918c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to get pending recv messages: %d\n",
7928c2ecf20Sopenharmony_ci			    ret);
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	return ret;
7958c2ecf20Sopenharmony_ci}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_cistatic int ath10k_sdio_mbox_proc_dbg_intr(struct ath10k *ar)
7988c2ecf20Sopenharmony_ci{
7998c2ecf20Sopenharmony_ci	u32 val;
8008c2ecf20Sopenharmony_ci	int ret;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	/* TODO: Add firmware crash handling */
8038c2ecf20Sopenharmony_ci	ath10k_warn(ar, "firmware crashed\n");
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	/* read counter to clear the interrupt, the debug error interrupt is
8068c2ecf20Sopenharmony_ci	 * counter 0.
8078c2ecf20Sopenharmony_ci	 */
8088c2ecf20Sopenharmony_ci	ret = ath10k_sdio_read32(ar, MBOX_COUNT_DEC_ADDRESS, &val);
8098c2ecf20Sopenharmony_ci	if (ret)
8108c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to clear debug interrupt: %d\n", ret);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	return ret;
8138c2ecf20Sopenharmony_ci}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_cistatic int ath10k_sdio_mbox_proc_counter_intr(struct ath10k *ar)
8168c2ecf20Sopenharmony_ci{
8178c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
8188c2ecf20Sopenharmony_ci	struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
8198c2ecf20Sopenharmony_ci	u8 counter_int_status;
8208c2ecf20Sopenharmony_ci	int ret;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	mutex_lock(&irq_data->mtx);
8238c2ecf20Sopenharmony_ci	counter_int_status = irq_data->irq_proc_reg->counter_int_status &
8248c2ecf20Sopenharmony_ci			     irq_data->irq_en_reg->cntr_int_status_en;
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	/* NOTE: other modules like GMBOX may use the counter interrupt for
8278c2ecf20Sopenharmony_ci	 * credit flow control on other counters, we only need to check for
8288c2ecf20Sopenharmony_ci	 * the debug assertion counter interrupt.
8298c2ecf20Sopenharmony_ci	 */
8308c2ecf20Sopenharmony_ci	if (counter_int_status & ATH10K_SDIO_TARGET_DEBUG_INTR_MASK)
8318c2ecf20Sopenharmony_ci		ret = ath10k_sdio_mbox_proc_dbg_intr(ar);
8328c2ecf20Sopenharmony_ci	else
8338c2ecf20Sopenharmony_ci		ret = 0;
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	mutex_unlock(&irq_data->mtx);
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	return ret;
8388c2ecf20Sopenharmony_ci}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_cistatic int ath10k_sdio_mbox_proc_err_intr(struct ath10k *ar)
8418c2ecf20Sopenharmony_ci{
8428c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
8438c2ecf20Sopenharmony_ci	struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
8448c2ecf20Sopenharmony_ci	u8 error_int_status;
8458c2ecf20Sopenharmony_ci	int ret;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio error interrupt\n");
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	error_int_status = irq_data->irq_proc_reg->error_int_status & 0x0F;
8508c2ecf20Sopenharmony_ci	if (!error_int_status) {
8518c2ecf20Sopenharmony_ci		ath10k_warn(ar, "invalid error interrupt status: 0x%x\n",
8528c2ecf20Sopenharmony_ci			    error_int_status);
8538c2ecf20Sopenharmony_ci		return -EIO;
8548c2ecf20Sopenharmony_ci	}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO,
8578c2ecf20Sopenharmony_ci		   "sdio error_int_status 0x%x\n", error_int_status);
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	if (FIELD_GET(MBOX_ERROR_INT_STATUS_WAKEUP_MASK,
8608c2ecf20Sopenharmony_ci		      error_int_status))
8618c2ecf20Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio interrupt error wakeup\n");
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	if (FIELD_GET(MBOX_ERROR_INT_STATUS_RX_UNDERFLOW_MASK,
8648c2ecf20Sopenharmony_ci		      error_int_status))
8658c2ecf20Sopenharmony_ci		ath10k_warn(ar, "rx underflow interrupt error\n");
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	if (FIELD_GET(MBOX_ERROR_INT_STATUS_TX_OVERFLOW_MASK,
8688c2ecf20Sopenharmony_ci		      error_int_status))
8698c2ecf20Sopenharmony_ci		ath10k_warn(ar, "tx overflow interrupt error\n");
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	/* Clear the interrupt */
8728c2ecf20Sopenharmony_ci	irq_data->irq_proc_reg->error_int_status &= ~error_int_status;
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	/* set W1C value to clear the interrupt, this hits the register first */
8758c2ecf20Sopenharmony_ci	ret = ath10k_sdio_writesb32(ar, MBOX_ERROR_INT_STATUS_ADDRESS,
8768c2ecf20Sopenharmony_ci				    error_int_status);
8778c2ecf20Sopenharmony_ci	if (ret) {
8788c2ecf20Sopenharmony_ci		ath10k_warn(ar, "unable to write to error int status address: %d\n",
8798c2ecf20Sopenharmony_ci			    ret);
8808c2ecf20Sopenharmony_ci		return ret;
8818c2ecf20Sopenharmony_ci	}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	return 0;
8848c2ecf20Sopenharmony_ci}
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_cistatic int ath10k_sdio_mbox_proc_cpu_intr(struct ath10k *ar)
8878c2ecf20Sopenharmony_ci{
8888c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
8898c2ecf20Sopenharmony_ci	struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
8908c2ecf20Sopenharmony_ci	u8 cpu_int_status;
8918c2ecf20Sopenharmony_ci	int ret;
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	mutex_lock(&irq_data->mtx);
8948c2ecf20Sopenharmony_ci	cpu_int_status = irq_data->irq_proc_reg->cpu_int_status &
8958c2ecf20Sopenharmony_ci			 irq_data->irq_en_reg->cpu_int_status_en;
8968c2ecf20Sopenharmony_ci	if (!cpu_int_status) {
8978c2ecf20Sopenharmony_ci		ath10k_warn(ar, "CPU interrupt status is zero\n");
8988c2ecf20Sopenharmony_ci		ret = -EIO;
8998c2ecf20Sopenharmony_ci		goto out;
9008c2ecf20Sopenharmony_ci	}
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	/* Clear the interrupt */
9038c2ecf20Sopenharmony_ci	irq_data->irq_proc_reg->cpu_int_status &= ~cpu_int_status;
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	/* Set up the register transfer buffer to hit the register 4 times,
9068c2ecf20Sopenharmony_ci	 * this is done to make the access 4-byte aligned to mitigate issues
9078c2ecf20Sopenharmony_ci	 * with host bus interconnects that restrict bus transfer lengths to
9088c2ecf20Sopenharmony_ci	 * be a multiple of 4-bytes.
9098c2ecf20Sopenharmony_ci	 *
9108c2ecf20Sopenharmony_ci	 * Set W1C value to clear the interrupt, this hits the register first.
9118c2ecf20Sopenharmony_ci	 */
9128c2ecf20Sopenharmony_ci	ret = ath10k_sdio_writesb32(ar, MBOX_CPU_INT_STATUS_ADDRESS,
9138c2ecf20Sopenharmony_ci				    cpu_int_status);
9148c2ecf20Sopenharmony_ci	if (ret) {
9158c2ecf20Sopenharmony_ci		ath10k_warn(ar, "unable to write to cpu interrupt status address: %d\n",
9168c2ecf20Sopenharmony_ci			    ret);
9178c2ecf20Sopenharmony_ci		goto out;
9188c2ecf20Sopenharmony_ci	}
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ciout:
9218c2ecf20Sopenharmony_ci	mutex_unlock(&irq_data->mtx);
9228c2ecf20Sopenharmony_ci	if (cpu_int_status & MBOX_CPU_STATUS_ENABLE_ASSERT_MASK)
9238c2ecf20Sopenharmony_ci		ath10k_sdio_fw_crashed_dump(ar);
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	return ret;
9268c2ecf20Sopenharmony_ci}
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_cistatic int ath10k_sdio_mbox_read_int_status(struct ath10k *ar,
9298c2ecf20Sopenharmony_ci					    u8 *host_int_status,
9308c2ecf20Sopenharmony_ci					    u32 *lookahead)
9318c2ecf20Sopenharmony_ci{
9328c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
9338c2ecf20Sopenharmony_ci	struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
9348c2ecf20Sopenharmony_ci	struct ath10k_sdio_irq_proc_regs *irq_proc_reg = irq_data->irq_proc_reg;
9358c2ecf20Sopenharmony_ci	struct ath10k_sdio_irq_enable_regs *irq_en_reg = irq_data->irq_en_reg;
9368c2ecf20Sopenharmony_ci	u8 htc_mbox = FIELD_PREP(ATH10K_HTC_MAILBOX_MASK, 1);
9378c2ecf20Sopenharmony_ci	int ret;
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	mutex_lock(&irq_data->mtx);
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	*lookahead = 0;
9428c2ecf20Sopenharmony_ci	*host_int_status = 0;
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	/* int_status_en is supposed to be non zero, otherwise interrupts
9458c2ecf20Sopenharmony_ci	 * shouldn't be enabled. There is however a short time frame during
9468c2ecf20Sopenharmony_ci	 * initialization between the irq register and int_status_en init
9478c2ecf20Sopenharmony_ci	 * where this can happen.
9488c2ecf20Sopenharmony_ci	 * We silently ignore this condition.
9498c2ecf20Sopenharmony_ci	 */
9508c2ecf20Sopenharmony_ci	if (!irq_en_reg->int_status_en) {
9518c2ecf20Sopenharmony_ci		ret = 0;
9528c2ecf20Sopenharmony_ci		goto out;
9538c2ecf20Sopenharmony_ci	}
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	/* Read the first sizeof(struct ath10k_irq_proc_registers)
9568c2ecf20Sopenharmony_ci	 * bytes of the HTC register table. This
9578c2ecf20Sopenharmony_ci	 * will yield us the value of different int status
9588c2ecf20Sopenharmony_ci	 * registers and the lookahead registers.
9598c2ecf20Sopenharmony_ci	 */
9608c2ecf20Sopenharmony_ci	ret = ath10k_sdio_read(ar, MBOX_HOST_INT_STATUS_ADDRESS,
9618c2ecf20Sopenharmony_ci			       irq_proc_reg, sizeof(*irq_proc_reg));
9628c2ecf20Sopenharmony_ci	if (ret) {
9638c2ecf20Sopenharmony_ci		queue_work(ar->workqueue, &ar->restart_work);
9648c2ecf20Sopenharmony_ci		ath10k_warn(ar, "read int status fail, start recovery\n");
9658c2ecf20Sopenharmony_ci		goto out;
9668c2ecf20Sopenharmony_ci	}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	/* Update only those registers that are enabled */
9698c2ecf20Sopenharmony_ci	*host_int_status = irq_proc_reg->host_int_status &
9708c2ecf20Sopenharmony_ci			   irq_en_reg->int_status_en;
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	/* Look at mbox status */
9738c2ecf20Sopenharmony_ci	if (!(*host_int_status & htc_mbox)) {
9748c2ecf20Sopenharmony_ci		*lookahead = 0;
9758c2ecf20Sopenharmony_ci		ret = 0;
9768c2ecf20Sopenharmony_ci		goto out;
9778c2ecf20Sopenharmony_ci	}
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	/* Mask out pending mbox value, we use look ahead as
9808c2ecf20Sopenharmony_ci	 * the real flag for mbox processing.
9818c2ecf20Sopenharmony_ci	 */
9828c2ecf20Sopenharmony_ci	*host_int_status &= ~htc_mbox;
9838c2ecf20Sopenharmony_ci	if (irq_proc_reg->rx_lookahead_valid & htc_mbox) {
9848c2ecf20Sopenharmony_ci		*lookahead = le32_to_cpu(
9858c2ecf20Sopenharmony_ci			irq_proc_reg->rx_lookahead[ATH10K_HTC_MAILBOX]);
9868c2ecf20Sopenharmony_ci		if (!*lookahead)
9878c2ecf20Sopenharmony_ci			ath10k_warn(ar, "sdio mbox lookahead is zero\n");
9888c2ecf20Sopenharmony_ci	}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ciout:
9918c2ecf20Sopenharmony_ci	mutex_unlock(&irq_data->mtx);
9928c2ecf20Sopenharmony_ci	return ret;
9938c2ecf20Sopenharmony_ci}
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_cistatic int ath10k_sdio_mbox_proc_pending_irqs(struct ath10k *ar,
9968c2ecf20Sopenharmony_ci					      bool *done)
9978c2ecf20Sopenharmony_ci{
9988c2ecf20Sopenharmony_ci	u8 host_int_status;
9998c2ecf20Sopenharmony_ci	u32 lookahead;
10008c2ecf20Sopenharmony_ci	int ret;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	/* NOTE: HIF implementation guarantees that the context of this
10038c2ecf20Sopenharmony_ci	 * call allows us to perform SYNCHRONOUS I/O, that is we can block,
10048c2ecf20Sopenharmony_ci	 * sleep or call any API that can block or switch thread/task
10058c2ecf20Sopenharmony_ci	 * contexts. This is a fully schedulable context.
10068c2ecf20Sopenharmony_ci	 */
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	ret = ath10k_sdio_mbox_read_int_status(ar,
10098c2ecf20Sopenharmony_ci					       &host_int_status,
10108c2ecf20Sopenharmony_ci					       &lookahead);
10118c2ecf20Sopenharmony_ci	if (ret) {
10128c2ecf20Sopenharmony_ci		*done = true;
10138c2ecf20Sopenharmony_ci		goto out;
10148c2ecf20Sopenharmony_ci	}
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	if (!host_int_status && !lookahead) {
10178c2ecf20Sopenharmony_ci		ret = 0;
10188c2ecf20Sopenharmony_ci		*done = true;
10198c2ecf20Sopenharmony_ci		goto out;
10208c2ecf20Sopenharmony_ci	}
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	if (lookahead) {
10238c2ecf20Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_SDIO,
10248c2ecf20Sopenharmony_ci			   "sdio pending mailbox msg lookahead 0x%08x\n",
10258c2ecf20Sopenharmony_ci			   lookahead);
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci		ret = ath10k_sdio_mbox_rxmsg_pending_handler(ar,
10288c2ecf20Sopenharmony_ci							     lookahead,
10298c2ecf20Sopenharmony_ci							     done);
10308c2ecf20Sopenharmony_ci		if (ret)
10318c2ecf20Sopenharmony_ci			goto out;
10328c2ecf20Sopenharmony_ci	}
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	/* now, handle the rest of the interrupts */
10358c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO,
10368c2ecf20Sopenharmony_ci		   "sdio host_int_status 0x%x\n", host_int_status);
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	if (FIELD_GET(MBOX_HOST_INT_STATUS_CPU_MASK, host_int_status)) {
10398c2ecf20Sopenharmony_ci		/* CPU Interrupt */
10408c2ecf20Sopenharmony_ci		ret = ath10k_sdio_mbox_proc_cpu_intr(ar);
10418c2ecf20Sopenharmony_ci		if (ret)
10428c2ecf20Sopenharmony_ci			goto out;
10438c2ecf20Sopenharmony_ci	}
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	if (FIELD_GET(MBOX_HOST_INT_STATUS_ERROR_MASK, host_int_status)) {
10468c2ecf20Sopenharmony_ci		/* Error Interrupt */
10478c2ecf20Sopenharmony_ci		ret = ath10k_sdio_mbox_proc_err_intr(ar);
10488c2ecf20Sopenharmony_ci		if (ret)
10498c2ecf20Sopenharmony_ci			goto out;
10508c2ecf20Sopenharmony_ci	}
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	if (FIELD_GET(MBOX_HOST_INT_STATUS_COUNTER_MASK, host_int_status))
10538c2ecf20Sopenharmony_ci		/* Counter Interrupt */
10548c2ecf20Sopenharmony_ci		ret = ath10k_sdio_mbox_proc_counter_intr(ar);
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	ret = 0;
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ciout:
10598c2ecf20Sopenharmony_ci	/* An optimization to bypass reading the IRQ status registers
10608c2ecf20Sopenharmony_ci	 * unecessarily which can re-wake the target, if upper layers
10618c2ecf20Sopenharmony_ci	 * determine that we are in a low-throughput mode, we can rely on
10628c2ecf20Sopenharmony_ci	 * taking another interrupt rather than re-checking the status
10638c2ecf20Sopenharmony_ci	 * registers which can re-wake the target.
10648c2ecf20Sopenharmony_ci	 *
10658c2ecf20Sopenharmony_ci	 * NOTE : for host interfaces that makes use of detecting pending
10668c2ecf20Sopenharmony_ci	 * mbox messages at hif can not use this optimization due to
10678c2ecf20Sopenharmony_ci	 * possible side effects, SPI requires the host to drain all
10688c2ecf20Sopenharmony_ci	 * messages from the mailbox before exiting the ISR routine.
10698c2ecf20Sopenharmony_ci	 */
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO,
10728c2ecf20Sopenharmony_ci		   "sdio pending irqs done %d status %d",
10738c2ecf20Sopenharmony_ci		   *done, ret);
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	return ret;
10768c2ecf20Sopenharmony_ci}
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_cistatic void ath10k_sdio_set_mbox_info(struct ath10k *ar)
10798c2ecf20Sopenharmony_ci{
10808c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
10818c2ecf20Sopenharmony_ci	struct ath10k_mbox_info *mbox_info = &ar_sdio->mbox_info;
10828c2ecf20Sopenharmony_ci	u16 device = ar_sdio->func->device, dev_id_base, dev_id_chiprev;
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	mbox_info->htc_addr = ATH10K_HIF_MBOX_BASE_ADDR;
10858c2ecf20Sopenharmony_ci	mbox_info->block_size = ATH10K_HIF_MBOX_BLOCK_SIZE;
10868c2ecf20Sopenharmony_ci	mbox_info->block_mask = ATH10K_HIF_MBOX_BLOCK_SIZE - 1;
10878c2ecf20Sopenharmony_ci	mbox_info->gmbox_addr = ATH10K_HIF_GMBOX_BASE_ADDR;
10888c2ecf20Sopenharmony_ci	mbox_info->gmbox_sz = ATH10K_HIF_GMBOX_WIDTH;
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	mbox_info->ext_info[0].htc_ext_addr = ATH10K_HIF_MBOX0_EXT_BASE_ADDR;
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	dev_id_base = (device & 0x0F00);
10938c2ecf20Sopenharmony_ci	dev_id_chiprev = (device & 0x00FF);
10948c2ecf20Sopenharmony_ci	switch (dev_id_base) {
10958c2ecf20Sopenharmony_ci	case (SDIO_DEVICE_ID_ATHEROS_AR6005 & 0x0F00):
10968c2ecf20Sopenharmony_ci		if (dev_id_chiprev < 4)
10978c2ecf20Sopenharmony_ci			mbox_info->ext_info[0].htc_ext_sz =
10988c2ecf20Sopenharmony_ci				ATH10K_HIF_MBOX0_EXT_WIDTH;
10998c2ecf20Sopenharmony_ci		else
11008c2ecf20Sopenharmony_ci			/* from QCA6174 2.0(0x504), the width has been extended
11018c2ecf20Sopenharmony_ci			 * to 56K
11028c2ecf20Sopenharmony_ci			 */
11038c2ecf20Sopenharmony_ci			mbox_info->ext_info[0].htc_ext_sz =
11048c2ecf20Sopenharmony_ci				ATH10K_HIF_MBOX0_EXT_WIDTH_ROME_2_0;
11058c2ecf20Sopenharmony_ci		break;
11068c2ecf20Sopenharmony_ci	case (SDIO_DEVICE_ID_ATHEROS_QCA9377 & 0x0F00):
11078c2ecf20Sopenharmony_ci		mbox_info->ext_info[0].htc_ext_sz =
11088c2ecf20Sopenharmony_ci			ATH10K_HIF_MBOX0_EXT_WIDTH_ROME_2_0;
11098c2ecf20Sopenharmony_ci		break;
11108c2ecf20Sopenharmony_ci	default:
11118c2ecf20Sopenharmony_ci		mbox_info->ext_info[0].htc_ext_sz =
11128c2ecf20Sopenharmony_ci				ATH10K_HIF_MBOX0_EXT_WIDTH;
11138c2ecf20Sopenharmony_ci	}
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	mbox_info->ext_info[1].htc_ext_addr =
11168c2ecf20Sopenharmony_ci		mbox_info->ext_info[0].htc_ext_addr +
11178c2ecf20Sopenharmony_ci		mbox_info->ext_info[0].htc_ext_sz +
11188c2ecf20Sopenharmony_ci		ATH10K_HIF_MBOX_DUMMY_SPACE_SIZE;
11198c2ecf20Sopenharmony_ci	mbox_info->ext_info[1].htc_ext_sz = ATH10K_HIF_MBOX1_EXT_WIDTH;
11208c2ecf20Sopenharmony_ci}
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci/* BMI functions */
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_cistatic int ath10k_sdio_bmi_credits(struct ath10k *ar)
11258c2ecf20Sopenharmony_ci{
11268c2ecf20Sopenharmony_ci	u32 addr, cmd_credits;
11278c2ecf20Sopenharmony_ci	unsigned long timeout;
11288c2ecf20Sopenharmony_ci	int ret;
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	/* Read the counter register to get the command credits */
11318c2ecf20Sopenharmony_ci	addr = MBOX_COUNT_DEC_ADDRESS + ATH10K_HIF_MBOX_NUM_MAX * 4;
11328c2ecf20Sopenharmony_ci	timeout = jiffies + BMI_COMMUNICATION_TIMEOUT_HZ;
11338c2ecf20Sopenharmony_ci	cmd_credits = 0;
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	while (time_before(jiffies, timeout) && !cmd_credits) {
11368c2ecf20Sopenharmony_ci		/* Hit the credit counter with a 4-byte access, the first byte
11378c2ecf20Sopenharmony_ci		 * read will hit the counter and cause a decrement, while the
11388c2ecf20Sopenharmony_ci		 * remaining 3 bytes has no effect. The rationale behind this
11398c2ecf20Sopenharmony_ci		 * is to make all HIF accesses 4-byte aligned.
11408c2ecf20Sopenharmony_ci		 */
11418c2ecf20Sopenharmony_ci		ret = ath10k_sdio_read32(ar, addr, &cmd_credits);
11428c2ecf20Sopenharmony_ci		if (ret) {
11438c2ecf20Sopenharmony_ci			ath10k_warn(ar,
11448c2ecf20Sopenharmony_ci				    "unable to decrement the command credit count register: %d\n",
11458c2ecf20Sopenharmony_ci				    ret);
11468c2ecf20Sopenharmony_ci			return ret;
11478c2ecf20Sopenharmony_ci		}
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci		/* The counter is only 8 bits.
11508c2ecf20Sopenharmony_ci		 * Ignore anything in the upper 3 bytes
11518c2ecf20Sopenharmony_ci		 */
11528c2ecf20Sopenharmony_ci		cmd_credits &= 0xFF;
11538c2ecf20Sopenharmony_ci	}
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	if (!cmd_credits) {
11568c2ecf20Sopenharmony_ci		ath10k_warn(ar, "bmi communication timeout\n");
11578c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
11588c2ecf20Sopenharmony_ci	}
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	return 0;
11618c2ecf20Sopenharmony_ci}
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_cistatic int ath10k_sdio_bmi_get_rx_lookahead(struct ath10k *ar)
11648c2ecf20Sopenharmony_ci{
11658c2ecf20Sopenharmony_ci	unsigned long timeout;
11668c2ecf20Sopenharmony_ci	u32 rx_word;
11678c2ecf20Sopenharmony_ci	int ret;
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	timeout = jiffies + BMI_COMMUNICATION_TIMEOUT_HZ;
11708c2ecf20Sopenharmony_ci	rx_word = 0;
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci	while ((time_before(jiffies, timeout)) && !rx_word) {
11738c2ecf20Sopenharmony_ci		ret = ath10k_sdio_read32(ar,
11748c2ecf20Sopenharmony_ci					 MBOX_HOST_INT_STATUS_ADDRESS,
11758c2ecf20Sopenharmony_ci					 &rx_word);
11768c2ecf20Sopenharmony_ci		if (ret) {
11778c2ecf20Sopenharmony_ci			ath10k_warn(ar, "unable to read RX_LOOKAHEAD_VALID: %d\n", ret);
11788c2ecf20Sopenharmony_ci			return ret;
11798c2ecf20Sopenharmony_ci		}
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci		 /* all we really want is one bit */
11828c2ecf20Sopenharmony_ci		rx_word &= 1;
11838c2ecf20Sopenharmony_ci	}
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	if (!rx_word) {
11868c2ecf20Sopenharmony_ci		ath10k_warn(ar, "bmi_recv_buf FIFO empty\n");
11878c2ecf20Sopenharmony_ci		return -EINVAL;
11888c2ecf20Sopenharmony_ci	}
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	return ret;
11918c2ecf20Sopenharmony_ci}
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_cistatic int ath10k_sdio_bmi_exchange_msg(struct ath10k *ar,
11948c2ecf20Sopenharmony_ci					void *req, u32 req_len,
11958c2ecf20Sopenharmony_ci					void *resp, u32 *resp_len)
11968c2ecf20Sopenharmony_ci{
11978c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
11988c2ecf20Sopenharmony_ci	u32 addr;
11998c2ecf20Sopenharmony_ci	int ret;
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	if (req) {
12028c2ecf20Sopenharmony_ci		ret = ath10k_sdio_bmi_credits(ar);
12038c2ecf20Sopenharmony_ci		if (ret)
12048c2ecf20Sopenharmony_ci			return ret;
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci		addr = ar_sdio->mbox_info.htc_addr;
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci		memcpy(ar_sdio->bmi_buf, req, req_len);
12098c2ecf20Sopenharmony_ci		ret = ath10k_sdio_write(ar, addr, ar_sdio->bmi_buf, req_len);
12108c2ecf20Sopenharmony_ci		if (ret) {
12118c2ecf20Sopenharmony_ci			ath10k_warn(ar,
12128c2ecf20Sopenharmony_ci				    "unable to send the bmi data to the device: %d\n",
12138c2ecf20Sopenharmony_ci				    ret);
12148c2ecf20Sopenharmony_ci			return ret;
12158c2ecf20Sopenharmony_ci		}
12168c2ecf20Sopenharmony_ci	}
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	if (!resp || !resp_len)
12198c2ecf20Sopenharmony_ci		/* No response expected */
12208c2ecf20Sopenharmony_ci		return 0;
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	/* During normal bootup, small reads may be required.
12238c2ecf20Sopenharmony_ci	 * Rather than issue an HIF Read and then wait as the Target
12248c2ecf20Sopenharmony_ci	 * adds successive bytes to the FIFO, we wait here until
12258c2ecf20Sopenharmony_ci	 * we know that response data is available.
12268c2ecf20Sopenharmony_ci	 *
12278c2ecf20Sopenharmony_ci	 * This allows us to cleanly timeout on an unexpected
12288c2ecf20Sopenharmony_ci	 * Target failure rather than risk problems at the HIF level.
12298c2ecf20Sopenharmony_ci	 * In particular, this avoids SDIO timeouts and possibly garbage
12308c2ecf20Sopenharmony_ci	 * data on some host controllers.  And on an interconnect
12318c2ecf20Sopenharmony_ci	 * such as Compact Flash (as well as some SDIO masters) which
12328c2ecf20Sopenharmony_ci	 * does not provide any indication on data timeout, it avoids
12338c2ecf20Sopenharmony_ci	 * a potential hang or garbage response.
12348c2ecf20Sopenharmony_ci	 *
12358c2ecf20Sopenharmony_ci	 * Synchronization is more difficult for reads larger than the
12368c2ecf20Sopenharmony_ci	 * size of the MBOX FIFO (128B), because the Target is unable
12378c2ecf20Sopenharmony_ci	 * to push the 129th byte of data until AFTER the Host posts an
12388c2ecf20Sopenharmony_ci	 * HIF Read and removes some FIFO data.  So for large reads the
12398c2ecf20Sopenharmony_ci	 * Host proceeds to post an HIF Read BEFORE all the data is
12408c2ecf20Sopenharmony_ci	 * actually available to read.  Fortunately, large BMI reads do
12418c2ecf20Sopenharmony_ci	 * not occur in practice -- they're supported for debug/development.
12428c2ecf20Sopenharmony_ci	 *
12438c2ecf20Sopenharmony_ci	 * So Host/Target BMI synchronization is divided into these cases:
12448c2ecf20Sopenharmony_ci	 *  CASE 1: length < 4
12458c2ecf20Sopenharmony_ci	 *        Should not happen
12468c2ecf20Sopenharmony_ci	 *
12478c2ecf20Sopenharmony_ci	 *  CASE 2: 4 <= length <= 128
12488c2ecf20Sopenharmony_ci	 *        Wait for first 4 bytes to be in FIFO
12498c2ecf20Sopenharmony_ci	 *        If CONSERVATIVE_BMI_READ is enabled, also wait for
12508c2ecf20Sopenharmony_ci	 *        a BMI command credit, which indicates that the ENTIRE
12518c2ecf20Sopenharmony_ci	 *        response is available in the the FIFO
12528c2ecf20Sopenharmony_ci	 *
12538c2ecf20Sopenharmony_ci	 *  CASE 3: length > 128
12548c2ecf20Sopenharmony_ci	 *        Wait for the first 4 bytes to be in FIFO
12558c2ecf20Sopenharmony_ci	 *
12568c2ecf20Sopenharmony_ci	 * For most uses, a small timeout should be sufficient and we will
12578c2ecf20Sopenharmony_ci	 * usually see a response quickly; but there may be some unusual
12588c2ecf20Sopenharmony_ci	 * (debug) cases of BMI_EXECUTE where we want an larger timeout.
12598c2ecf20Sopenharmony_ci	 * For now, we use an unbounded busy loop while waiting for
12608c2ecf20Sopenharmony_ci	 * BMI_EXECUTE.
12618c2ecf20Sopenharmony_ci	 *
12628c2ecf20Sopenharmony_ci	 * If BMI_EXECUTE ever needs to support longer-latency execution,
12638c2ecf20Sopenharmony_ci	 * especially in production, this code needs to be enhanced to sleep
12648c2ecf20Sopenharmony_ci	 * and yield.  Also note that BMI_COMMUNICATION_TIMEOUT is currently
12658c2ecf20Sopenharmony_ci	 * a function of Host processor speed.
12668c2ecf20Sopenharmony_ci	 */
12678c2ecf20Sopenharmony_ci	ret = ath10k_sdio_bmi_get_rx_lookahead(ar);
12688c2ecf20Sopenharmony_ci	if (ret)
12698c2ecf20Sopenharmony_ci		return ret;
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	/* We always read from the start of the mbox address */
12728c2ecf20Sopenharmony_ci	addr = ar_sdio->mbox_info.htc_addr;
12738c2ecf20Sopenharmony_ci	ret = ath10k_sdio_read(ar, addr, ar_sdio->bmi_buf, *resp_len);
12748c2ecf20Sopenharmony_ci	if (ret) {
12758c2ecf20Sopenharmony_ci		ath10k_warn(ar,
12768c2ecf20Sopenharmony_ci			    "unable to read the bmi data from the device: %d\n",
12778c2ecf20Sopenharmony_ci			    ret);
12788c2ecf20Sopenharmony_ci		return ret;
12798c2ecf20Sopenharmony_ci	}
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ci	memcpy(resp, ar_sdio->bmi_buf, *resp_len);
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	return 0;
12848c2ecf20Sopenharmony_ci}
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci/* sdio async handling functions */
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_cistatic struct ath10k_sdio_bus_request
12898c2ecf20Sopenharmony_ci*ath10k_sdio_alloc_busreq(struct ath10k *ar)
12908c2ecf20Sopenharmony_ci{
12918c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
12928c2ecf20Sopenharmony_ci	struct ath10k_sdio_bus_request *bus_req;
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_ci	spin_lock_bh(&ar_sdio->lock);
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci	if (list_empty(&ar_sdio->bus_req_freeq)) {
12978c2ecf20Sopenharmony_ci		bus_req = NULL;
12988c2ecf20Sopenharmony_ci		goto out;
12998c2ecf20Sopenharmony_ci	}
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	bus_req = list_first_entry(&ar_sdio->bus_req_freeq,
13028c2ecf20Sopenharmony_ci				   struct ath10k_sdio_bus_request, list);
13038c2ecf20Sopenharmony_ci	list_del(&bus_req->list);
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ciout:
13068c2ecf20Sopenharmony_ci	spin_unlock_bh(&ar_sdio->lock);
13078c2ecf20Sopenharmony_ci	return bus_req;
13088c2ecf20Sopenharmony_ci}
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_cistatic void ath10k_sdio_free_bus_req(struct ath10k *ar,
13118c2ecf20Sopenharmony_ci				     struct ath10k_sdio_bus_request *bus_req)
13128c2ecf20Sopenharmony_ci{
13138c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	memset(bus_req, 0, sizeof(*bus_req));
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci	spin_lock_bh(&ar_sdio->lock);
13188c2ecf20Sopenharmony_ci	list_add_tail(&bus_req->list, &ar_sdio->bus_req_freeq);
13198c2ecf20Sopenharmony_ci	spin_unlock_bh(&ar_sdio->lock);
13208c2ecf20Sopenharmony_ci}
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_cistatic void __ath10k_sdio_write_async(struct ath10k *ar,
13238c2ecf20Sopenharmony_ci				      struct ath10k_sdio_bus_request *req)
13248c2ecf20Sopenharmony_ci{
13258c2ecf20Sopenharmony_ci	struct ath10k_htc_ep *ep;
13268c2ecf20Sopenharmony_ci	struct sk_buff *skb;
13278c2ecf20Sopenharmony_ci	int ret;
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci	skb = req->skb;
13308c2ecf20Sopenharmony_ci	ret = ath10k_sdio_write(ar, req->address, skb->data, skb->len);
13318c2ecf20Sopenharmony_ci	if (ret)
13328c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to write skb to 0x%x asynchronously: %d",
13338c2ecf20Sopenharmony_ci			    req->address, ret);
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci	if (req->htc_msg) {
13368c2ecf20Sopenharmony_ci		ep = &ar->htc.endpoint[req->eid];
13378c2ecf20Sopenharmony_ci		ath10k_htc_notify_tx_completion(ep, skb);
13388c2ecf20Sopenharmony_ci	} else if (req->comp) {
13398c2ecf20Sopenharmony_ci		complete(req->comp);
13408c2ecf20Sopenharmony_ci	}
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci	ath10k_sdio_free_bus_req(ar, req);
13438c2ecf20Sopenharmony_ci}
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci/* To improve throughput use workqueue to deliver packets to HTC layer,
13468c2ecf20Sopenharmony_ci * this way SDIO bus is utilised much better.
13478c2ecf20Sopenharmony_ci */
13488c2ecf20Sopenharmony_cistatic void ath10k_rx_indication_async_work(struct work_struct *work)
13498c2ecf20Sopenharmony_ci{
13508c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = container_of(work, struct ath10k_sdio,
13518c2ecf20Sopenharmony_ci						   async_work_rx);
13528c2ecf20Sopenharmony_ci	struct ath10k *ar = ar_sdio->ar;
13538c2ecf20Sopenharmony_ci	struct ath10k_htc_ep *ep;
13548c2ecf20Sopenharmony_ci	struct ath10k_skb_rxcb *cb;
13558c2ecf20Sopenharmony_ci	struct sk_buff *skb;
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci	while (true) {
13588c2ecf20Sopenharmony_ci		skb = skb_dequeue(&ar_sdio->rx_head);
13598c2ecf20Sopenharmony_ci		if (!skb)
13608c2ecf20Sopenharmony_ci			break;
13618c2ecf20Sopenharmony_ci		cb = ATH10K_SKB_RXCB(skb);
13628c2ecf20Sopenharmony_ci		ep = &ar->htc.endpoint[cb->eid];
13638c2ecf20Sopenharmony_ci		ep->ep_ops.ep_rx_complete(ar, skb);
13648c2ecf20Sopenharmony_ci	}
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	if (test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags)) {
13678c2ecf20Sopenharmony_ci		local_bh_disable();
13688c2ecf20Sopenharmony_ci		napi_schedule(&ar->napi);
13698c2ecf20Sopenharmony_ci		local_bh_enable();
13708c2ecf20Sopenharmony_ci	}
13718c2ecf20Sopenharmony_ci}
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_cistatic int ath10k_sdio_read_rtc_state(struct ath10k_sdio *ar_sdio, unsigned char *state)
13748c2ecf20Sopenharmony_ci{
13758c2ecf20Sopenharmony_ci	struct ath10k *ar = ar_sdio->ar;
13768c2ecf20Sopenharmony_ci	unsigned char rtc_state = 0;
13778c2ecf20Sopenharmony_ci	int ret = 0;
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci	rtc_state = sdio_f0_readb(ar_sdio->func, ATH10K_CIS_RTC_STATE_ADDR, &ret);
13808c2ecf20Sopenharmony_ci	if (ret) {
13818c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to read rtc state: %d\n", ret);
13828c2ecf20Sopenharmony_ci		return ret;
13838c2ecf20Sopenharmony_ci	}
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	*state = rtc_state & 0x3;
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_ci	return ret;
13888c2ecf20Sopenharmony_ci}
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_cistatic int ath10k_sdio_set_mbox_sleep(struct ath10k *ar, bool enable_sleep)
13918c2ecf20Sopenharmony_ci{
13928c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
13938c2ecf20Sopenharmony_ci	u32 val;
13948c2ecf20Sopenharmony_ci	int retry = ATH10K_CIS_READ_RETRY, ret = 0;
13958c2ecf20Sopenharmony_ci	unsigned char rtc_state = 0;
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	sdio_claim_host(ar_sdio->func);
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	ret = ath10k_sdio_read32(ar, ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL, &val);
14008c2ecf20Sopenharmony_ci	if (ret) {
14018c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to read fifo/chip control register: %d\n",
14028c2ecf20Sopenharmony_ci			    ret);
14038c2ecf20Sopenharmony_ci		goto release;
14048c2ecf20Sopenharmony_ci	}
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci	if (enable_sleep) {
14078c2ecf20Sopenharmony_ci		val &= ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL_DISABLE_SLEEP_OFF;
14088c2ecf20Sopenharmony_ci		ar_sdio->mbox_state = SDIO_MBOX_SLEEP_STATE;
14098c2ecf20Sopenharmony_ci	} else {
14108c2ecf20Sopenharmony_ci		val |= ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL_DISABLE_SLEEP_ON;
14118c2ecf20Sopenharmony_ci		ar_sdio->mbox_state = SDIO_MBOX_AWAKE_STATE;
14128c2ecf20Sopenharmony_ci	}
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	ret = ath10k_sdio_write32(ar, ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL, val);
14158c2ecf20Sopenharmony_ci	if (ret) {
14168c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to write to FIFO_TIMEOUT_AND_CHIP_CONTROL: %d",
14178c2ecf20Sopenharmony_ci			    ret);
14188c2ecf20Sopenharmony_ci	}
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci	if (!enable_sleep) {
14218c2ecf20Sopenharmony_ci		do {
14228c2ecf20Sopenharmony_ci			udelay(ATH10K_CIS_READ_WAIT_4_RTC_CYCLE_IN_US);
14238c2ecf20Sopenharmony_ci			ret = ath10k_sdio_read_rtc_state(ar_sdio, &rtc_state);
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci			if (ret) {
14268c2ecf20Sopenharmony_ci				ath10k_warn(ar, "failed to disable mbox sleep: %d", ret);
14278c2ecf20Sopenharmony_ci				break;
14288c2ecf20Sopenharmony_ci			}
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci			ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio read rtc state: %d\n",
14318c2ecf20Sopenharmony_ci				   rtc_state);
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci			if (rtc_state == ATH10K_CIS_RTC_STATE_ON)
14348c2ecf20Sopenharmony_ci				break;
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci			udelay(ATH10K_CIS_XTAL_SETTLE_DURATION_IN_US);
14378c2ecf20Sopenharmony_ci			retry--;
14388c2ecf20Sopenharmony_ci		} while (retry > 0);
14398c2ecf20Sopenharmony_ci	}
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_cirelease:
14428c2ecf20Sopenharmony_ci	sdio_release_host(ar_sdio->func);
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci	return ret;
14458c2ecf20Sopenharmony_ci}
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_cistatic void ath10k_sdio_sleep_timer_handler(struct timer_list *t)
14488c2ecf20Sopenharmony_ci{
14498c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = from_timer(ar_sdio, t, sleep_timer);
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci	ar_sdio->mbox_state = SDIO_MBOX_REQUEST_TO_SLEEP_STATE;
14528c2ecf20Sopenharmony_ci	queue_work(ar_sdio->workqueue, &ar_sdio->wr_async_work);
14538c2ecf20Sopenharmony_ci}
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_cistatic void ath10k_sdio_write_async_work(struct work_struct *work)
14568c2ecf20Sopenharmony_ci{
14578c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = container_of(work, struct ath10k_sdio,
14588c2ecf20Sopenharmony_ci						   wr_async_work);
14598c2ecf20Sopenharmony_ci	struct ath10k *ar = ar_sdio->ar;
14608c2ecf20Sopenharmony_ci	struct ath10k_sdio_bus_request *req, *tmp_req;
14618c2ecf20Sopenharmony_ci	struct ath10k_mbox_info *mbox_info = &ar_sdio->mbox_info;
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	spin_lock_bh(&ar_sdio->wr_async_lock);
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	list_for_each_entry_safe(req, tmp_req, &ar_sdio->wr_asyncq, list) {
14668c2ecf20Sopenharmony_ci		list_del(&req->list);
14678c2ecf20Sopenharmony_ci		spin_unlock_bh(&ar_sdio->wr_async_lock);
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci		if (req->address >= mbox_info->htc_addr &&
14708c2ecf20Sopenharmony_ci		    ar_sdio->mbox_state == SDIO_MBOX_SLEEP_STATE) {
14718c2ecf20Sopenharmony_ci			ath10k_sdio_set_mbox_sleep(ar, false);
14728c2ecf20Sopenharmony_ci			mod_timer(&ar_sdio->sleep_timer, jiffies +
14738c2ecf20Sopenharmony_ci				  msecs_to_jiffies(ATH10K_MIN_SLEEP_INACTIVITY_TIME_MS));
14748c2ecf20Sopenharmony_ci		}
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci		__ath10k_sdio_write_async(ar, req);
14778c2ecf20Sopenharmony_ci		spin_lock_bh(&ar_sdio->wr_async_lock);
14788c2ecf20Sopenharmony_ci	}
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci	spin_unlock_bh(&ar_sdio->wr_async_lock);
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci	if (ar_sdio->mbox_state == SDIO_MBOX_REQUEST_TO_SLEEP_STATE)
14838c2ecf20Sopenharmony_ci		ath10k_sdio_set_mbox_sleep(ar, true);
14848c2ecf20Sopenharmony_ci}
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_cistatic int ath10k_sdio_prep_async_req(struct ath10k *ar, u32 addr,
14878c2ecf20Sopenharmony_ci				      struct sk_buff *skb,
14888c2ecf20Sopenharmony_ci				      struct completion *comp,
14898c2ecf20Sopenharmony_ci				      bool htc_msg, enum ath10k_htc_ep_id eid)
14908c2ecf20Sopenharmony_ci{
14918c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
14928c2ecf20Sopenharmony_ci	struct ath10k_sdio_bus_request *bus_req;
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci	/* Allocate a bus request for the message and queue it on the
14958c2ecf20Sopenharmony_ci	 * SDIO workqueue.
14968c2ecf20Sopenharmony_ci	 */
14978c2ecf20Sopenharmony_ci	bus_req = ath10k_sdio_alloc_busreq(ar);
14988c2ecf20Sopenharmony_ci	if (!bus_req) {
14998c2ecf20Sopenharmony_ci		ath10k_warn(ar,
15008c2ecf20Sopenharmony_ci			    "unable to allocate bus request for async request\n");
15018c2ecf20Sopenharmony_ci		return -ENOMEM;
15028c2ecf20Sopenharmony_ci	}
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci	bus_req->skb = skb;
15058c2ecf20Sopenharmony_ci	bus_req->eid = eid;
15068c2ecf20Sopenharmony_ci	bus_req->address = addr;
15078c2ecf20Sopenharmony_ci	bus_req->htc_msg = htc_msg;
15088c2ecf20Sopenharmony_ci	bus_req->comp = comp;
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci	spin_lock_bh(&ar_sdio->wr_async_lock);
15118c2ecf20Sopenharmony_ci	list_add_tail(&bus_req->list, &ar_sdio->wr_asyncq);
15128c2ecf20Sopenharmony_ci	spin_unlock_bh(&ar_sdio->wr_async_lock);
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci	return 0;
15158c2ecf20Sopenharmony_ci}
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci/* IRQ handler */
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_cistatic void ath10k_sdio_irq_handler(struct sdio_func *func)
15208c2ecf20Sopenharmony_ci{
15218c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = sdio_get_drvdata(func);
15228c2ecf20Sopenharmony_ci	struct ath10k *ar = ar_sdio->ar;
15238c2ecf20Sopenharmony_ci	unsigned long timeout;
15248c2ecf20Sopenharmony_ci	bool done = false;
15258c2ecf20Sopenharmony_ci	int ret;
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci	/* Release the host during interrupts so we can pick it back up when
15288c2ecf20Sopenharmony_ci	 * we process commands.
15298c2ecf20Sopenharmony_ci	 */
15308c2ecf20Sopenharmony_ci	sdio_release_host(ar_sdio->func);
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci	timeout = jiffies + ATH10K_SDIO_HIF_COMMUNICATION_TIMEOUT_HZ;
15338c2ecf20Sopenharmony_ci	do {
15348c2ecf20Sopenharmony_ci		ret = ath10k_sdio_mbox_proc_pending_irqs(ar, &done);
15358c2ecf20Sopenharmony_ci		if (ret)
15368c2ecf20Sopenharmony_ci			break;
15378c2ecf20Sopenharmony_ci	} while (time_before(jiffies, timeout) && !done);
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci	ath10k_mac_tx_push_pending(ar);
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_ci	sdio_claim_host(ar_sdio->func);
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci	if (ret && ret != -ECANCELED)
15448c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to process pending SDIO interrupts: %d\n",
15458c2ecf20Sopenharmony_ci			    ret);
15468c2ecf20Sopenharmony_ci}
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci/* sdio HIF functions */
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_cistatic int ath10k_sdio_disable_intrs(struct ath10k *ar)
15518c2ecf20Sopenharmony_ci{
15528c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
15538c2ecf20Sopenharmony_ci	struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
15548c2ecf20Sopenharmony_ci	struct ath10k_sdio_irq_enable_regs *regs = irq_data->irq_en_reg;
15558c2ecf20Sopenharmony_ci	int ret;
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci	mutex_lock(&irq_data->mtx);
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_ci	memset(regs, 0, sizeof(*regs));
15608c2ecf20Sopenharmony_ci	ret = ath10k_sdio_write(ar, MBOX_INT_STATUS_ENABLE_ADDRESS,
15618c2ecf20Sopenharmony_ci				&regs->int_status_en, sizeof(*regs));
15628c2ecf20Sopenharmony_ci	if (ret)
15638c2ecf20Sopenharmony_ci		ath10k_warn(ar, "unable to disable sdio interrupts: %d\n", ret);
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci	mutex_unlock(&irq_data->mtx);
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci	return ret;
15688c2ecf20Sopenharmony_ci}
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_cistatic int ath10k_sdio_hif_power_up(struct ath10k *ar,
15718c2ecf20Sopenharmony_ci				    enum ath10k_firmware_mode fw_mode)
15728c2ecf20Sopenharmony_ci{
15738c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
15748c2ecf20Sopenharmony_ci	struct sdio_func *func = ar_sdio->func;
15758c2ecf20Sopenharmony_ci	int ret;
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci	if (!ar_sdio->is_disabled)
15788c2ecf20Sopenharmony_ci		return 0;
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_BOOT, "sdio power on\n");
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci	ret = ath10k_sdio_config(ar);
15838c2ecf20Sopenharmony_ci	if (ret) {
15848c2ecf20Sopenharmony_ci		ath10k_err(ar, "failed to config sdio: %d\n", ret);
15858c2ecf20Sopenharmony_ci		return ret;
15868c2ecf20Sopenharmony_ci	}
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci	sdio_claim_host(func);
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_ci	ret = sdio_enable_func(func);
15918c2ecf20Sopenharmony_ci	if (ret) {
15928c2ecf20Sopenharmony_ci		ath10k_warn(ar, "unable to enable sdio function: %d)\n", ret);
15938c2ecf20Sopenharmony_ci		sdio_release_host(func);
15948c2ecf20Sopenharmony_ci		return ret;
15958c2ecf20Sopenharmony_ci	}
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci	sdio_release_host(func);
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci	/* Wait for hardware to initialise. It should take a lot less than
16008c2ecf20Sopenharmony_ci	 * 20 ms but let's be conservative here.
16018c2ecf20Sopenharmony_ci	 */
16028c2ecf20Sopenharmony_ci	msleep(20);
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_ci	ar_sdio->is_disabled = false;
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	ret = ath10k_sdio_disable_intrs(ar);
16078c2ecf20Sopenharmony_ci	if (ret)
16088c2ecf20Sopenharmony_ci		return ret;
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_ci	return 0;
16118c2ecf20Sopenharmony_ci}
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_cistatic void ath10k_sdio_hif_power_down(struct ath10k *ar)
16148c2ecf20Sopenharmony_ci{
16158c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
16168c2ecf20Sopenharmony_ci	int ret;
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci	if (ar_sdio->is_disabled)
16198c2ecf20Sopenharmony_ci		return;
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_BOOT, "sdio power off\n");
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci	del_timer_sync(&ar_sdio->sleep_timer);
16248c2ecf20Sopenharmony_ci	ath10k_sdio_set_mbox_sleep(ar, true);
16258c2ecf20Sopenharmony_ci
16268c2ecf20Sopenharmony_ci	/* Disable the card */
16278c2ecf20Sopenharmony_ci	sdio_claim_host(ar_sdio->func);
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	ret = sdio_disable_func(ar_sdio->func);
16308c2ecf20Sopenharmony_ci	if (ret) {
16318c2ecf20Sopenharmony_ci		ath10k_warn(ar, "unable to disable sdio function: %d\n", ret);
16328c2ecf20Sopenharmony_ci		sdio_release_host(ar_sdio->func);
16338c2ecf20Sopenharmony_ci		return;
16348c2ecf20Sopenharmony_ci	}
16358c2ecf20Sopenharmony_ci
16368c2ecf20Sopenharmony_ci	ret = mmc_hw_reset(ar_sdio->func->card->host);
16378c2ecf20Sopenharmony_ci	if (ret)
16388c2ecf20Sopenharmony_ci		ath10k_warn(ar, "unable to reset sdio: %d\n", ret);
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci	sdio_release_host(ar_sdio->func);
16418c2ecf20Sopenharmony_ci
16428c2ecf20Sopenharmony_ci	ar_sdio->is_disabled = true;
16438c2ecf20Sopenharmony_ci}
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_cistatic int ath10k_sdio_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
16468c2ecf20Sopenharmony_ci				 struct ath10k_hif_sg_item *items, int n_items)
16478c2ecf20Sopenharmony_ci{
16488c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
16498c2ecf20Sopenharmony_ci	enum ath10k_htc_ep_id eid;
16508c2ecf20Sopenharmony_ci	struct sk_buff *skb;
16518c2ecf20Sopenharmony_ci	int ret, i;
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_ci	eid = pipe_id_to_eid(pipe_id);
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_ci	for (i = 0; i < n_items; i++) {
16568c2ecf20Sopenharmony_ci		size_t padded_len;
16578c2ecf20Sopenharmony_ci		u32 address;
16588c2ecf20Sopenharmony_ci
16598c2ecf20Sopenharmony_ci		skb = items[i].transfer_context;
16608c2ecf20Sopenharmony_ci		padded_len = ath10k_sdio_calc_txrx_padded_len(ar_sdio,
16618c2ecf20Sopenharmony_ci							      skb->len);
16628c2ecf20Sopenharmony_ci		skb_trim(skb, padded_len);
16638c2ecf20Sopenharmony_ci
16648c2ecf20Sopenharmony_ci		/* Write TX data to the end of the mbox address space */
16658c2ecf20Sopenharmony_ci		address = ar_sdio->mbox_addr[eid] + ar_sdio->mbox_size[eid] -
16668c2ecf20Sopenharmony_ci			  skb->len;
16678c2ecf20Sopenharmony_ci		ret = ath10k_sdio_prep_async_req(ar, address, skb,
16688c2ecf20Sopenharmony_ci						 NULL, true, eid);
16698c2ecf20Sopenharmony_ci		if (ret)
16708c2ecf20Sopenharmony_ci			return ret;
16718c2ecf20Sopenharmony_ci	}
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci	queue_work(ar_sdio->workqueue, &ar_sdio->wr_async_work);
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci	return 0;
16768c2ecf20Sopenharmony_ci}
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_cistatic int ath10k_sdio_enable_intrs(struct ath10k *ar)
16798c2ecf20Sopenharmony_ci{
16808c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
16818c2ecf20Sopenharmony_ci	struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
16828c2ecf20Sopenharmony_ci	struct ath10k_sdio_irq_enable_regs *regs = irq_data->irq_en_reg;
16838c2ecf20Sopenharmony_ci	int ret;
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_ci	mutex_lock(&irq_data->mtx);
16868c2ecf20Sopenharmony_ci
16878c2ecf20Sopenharmony_ci	/* Enable all but CPU interrupts */
16888c2ecf20Sopenharmony_ci	regs->int_status_en = FIELD_PREP(MBOX_INT_STATUS_ENABLE_ERROR_MASK, 1) |
16898c2ecf20Sopenharmony_ci			      FIELD_PREP(MBOX_INT_STATUS_ENABLE_CPU_MASK, 1) |
16908c2ecf20Sopenharmony_ci			      FIELD_PREP(MBOX_INT_STATUS_ENABLE_COUNTER_MASK, 1);
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_ci	/* NOTE: There are some cases where HIF can do detection of
16938c2ecf20Sopenharmony_ci	 * pending mbox messages which is disabled now.
16948c2ecf20Sopenharmony_ci	 */
16958c2ecf20Sopenharmony_ci	regs->int_status_en |=
16968c2ecf20Sopenharmony_ci		FIELD_PREP(MBOX_INT_STATUS_ENABLE_MBOX_DATA_MASK, 1);
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci	/* Set up the CPU Interrupt Status Register, enable CPU sourced interrupt #0
16998c2ecf20Sopenharmony_ci	 * #0 is used for report assertion from target
17008c2ecf20Sopenharmony_ci	 */
17018c2ecf20Sopenharmony_ci	regs->cpu_int_status_en = FIELD_PREP(MBOX_CPU_STATUS_ENABLE_ASSERT_MASK, 1);
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_ci	/* Set up the Error Interrupt status Register */
17048c2ecf20Sopenharmony_ci	regs->err_int_status_en =
17058c2ecf20Sopenharmony_ci		FIELD_PREP(MBOX_ERROR_STATUS_ENABLE_RX_UNDERFLOW_MASK, 1) |
17068c2ecf20Sopenharmony_ci		FIELD_PREP(MBOX_ERROR_STATUS_ENABLE_TX_OVERFLOW_MASK, 1);
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_ci	/* Enable Counter interrupt status register to get fatal errors for
17098c2ecf20Sopenharmony_ci	 * debugging.
17108c2ecf20Sopenharmony_ci	 */
17118c2ecf20Sopenharmony_ci	regs->cntr_int_status_en =
17128c2ecf20Sopenharmony_ci		FIELD_PREP(MBOX_COUNTER_INT_STATUS_ENABLE_BIT_MASK,
17138c2ecf20Sopenharmony_ci			   ATH10K_SDIO_TARGET_DEBUG_INTR_MASK);
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_ci	ret = ath10k_sdio_write(ar, MBOX_INT_STATUS_ENABLE_ADDRESS,
17168c2ecf20Sopenharmony_ci				&regs->int_status_en, sizeof(*regs));
17178c2ecf20Sopenharmony_ci	if (ret)
17188c2ecf20Sopenharmony_ci		ath10k_warn(ar,
17198c2ecf20Sopenharmony_ci			    "failed to update mbox interrupt status register : %d\n",
17208c2ecf20Sopenharmony_ci			    ret);
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci	mutex_unlock(&irq_data->mtx);
17238c2ecf20Sopenharmony_ci	return ret;
17248c2ecf20Sopenharmony_ci}
17258c2ecf20Sopenharmony_ci
17268c2ecf20Sopenharmony_ci/* HIF diagnostics */
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_cistatic int ath10k_sdio_hif_diag_read(struct ath10k *ar, u32 address, void *buf,
17298c2ecf20Sopenharmony_ci				     size_t buf_len)
17308c2ecf20Sopenharmony_ci{
17318c2ecf20Sopenharmony_ci	int ret;
17328c2ecf20Sopenharmony_ci	void *mem;
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci	mem = kzalloc(buf_len, GFP_KERNEL);
17358c2ecf20Sopenharmony_ci	if (!mem)
17368c2ecf20Sopenharmony_ci		return -ENOMEM;
17378c2ecf20Sopenharmony_ci
17388c2ecf20Sopenharmony_ci	/* set window register to start read cycle */
17398c2ecf20Sopenharmony_ci	ret = ath10k_sdio_write32(ar, MBOX_WINDOW_READ_ADDR_ADDRESS, address);
17408c2ecf20Sopenharmony_ci	if (ret) {
17418c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to set mbox window read address: %d", ret);
17428c2ecf20Sopenharmony_ci		goto out;
17438c2ecf20Sopenharmony_ci	}
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	/* read the data */
17468c2ecf20Sopenharmony_ci	ret = ath10k_sdio_read(ar, MBOX_WINDOW_DATA_ADDRESS, mem, buf_len);
17478c2ecf20Sopenharmony_ci	if (ret) {
17488c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to read from mbox window data address: %d\n",
17498c2ecf20Sopenharmony_ci			    ret);
17508c2ecf20Sopenharmony_ci		goto out;
17518c2ecf20Sopenharmony_ci	}
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci	memcpy(buf, mem, buf_len);
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ciout:
17568c2ecf20Sopenharmony_ci	kfree(mem);
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_ci	return ret;
17598c2ecf20Sopenharmony_ci}
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_cistatic int ath10k_sdio_diag_read32(struct ath10k *ar, u32 address,
17628c2ecf20Sopenharmony_ci				   u32 *value)
17638c2ecf20Sopenharmony_ci{
17648c2ecf20Sopenharmony_ci	__le32 *val;
17658c2ecf20Sopenharmony_ci	int ret;
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci	val = kzalloc(sizeof(*val), GFP_KERNEL);
17688c2ecf20Sopenharmony_ci	if (!val)
17698c2ecf20Sopenharmony_ci		return -ENOMEM;
17708c2ecf20Sopenharmony_ci
17718c2ecf20Sopenharmony_ci	ret = ath10k_sdio_hif_diag_read(ar, address, val, sizeof(*val));
17728c2ecf20Sopenharmony_ci	if (ret)
17738c2ecf20Sopenharmony_ci		goto out;
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci	*value = __le32_to_cpu(*val);
17768c2ecf20Sopenharmony_ci
17778c2ecf20Sopenharmony_ciout:
17788c2ecf20Sopenharmony_ci	kfree(val);
17798c2ecf20Sopenharmony_ci
17808c2ecf20Sopenharmony_ci	return ret;
17818c2ecf20Sopenharmony_ci}
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_cistatic int ath10k_sdio_hif_diag_write_mem(struct ath10k *ar, u32 address,
17848c2ecf20Sopenharmony_ci					  const void *data, int nbytes)
17858c2ecf20Sopenharmony_ci{
17868c2ecf20Sopenharmony_ci	int ret;
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci	/* set write data */
17898c2ecf20Sopenharmony_ci	ret = ath10k_sdio_write(ar, MBOX_WINDOW_DATA_ADDRESS, data, nbytes);
17908c2ecf20Sopenharmony_ci	if (ret) {
17918c2ecf20Sopenharmony_ci		ath10k_warn(ar,
17928c2ecf20Sopenharmony_ci			    "failed to write 0x%p to mbox window data address: %d\n",
17938c2ecf20Sopenharmony_ci			    data, ret);
17948c2ecf20Sopenharmony_ci		return ret;
17958c2ecf20Sopenharmony_ci	}
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_ci	/* set window register, which starts the write cycle */
17988c2ecf20Sopenharmony_ci	ret = ath10k_sdio_write32(ar, MBOX_WINDOW_WRITE_ADDR_ADDRESS, address);
17998c2ecf20Sopenharmony_ci	if (ret) {
18008c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to set mbox window write address: %d", ret);
18018c2ecf20Sopenharmony_ci		return ret;
18028c2ecf20Sopenharmony_ci	}
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_ci	return 0;
18058c2ecf20Sopenharmony_ci}
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_cistatic int ath10k_sdio_hif_start_post(struct ath10k *ar)
18088c2ecf20Sopenharmony_ci{
18098c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
18108c2ecf20Sopenharmony_ci	u32 addr, val;
18118c2ecf20Sopenharmony_ci	int ret = 0;
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_ci	addr = host_interest_item_address(HI_ITEM(hi_acs_flags));
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_ci	ret = ath10k_sdio_diag_read32(ar, addr, &val);
18168c2ecf20Sopenharmony_ci	if (ret) {
18178c2ecf20Sopenharmony_ci		ath10k_warn(ar, "unable to read hi_acs_flags : %d\n", ret);
18188c2ecf20Sopenharmony_ci		return ret;
18198c2ecf20Sopenharmony_ci	}
18208c2ecf20Sopenharmony_ci
18218c2ecf20Sopenharmony_ci	if (val & HI_ACS_FLAGS_SDIO_SWAP_MAILBOX_FW_ACK) {
18228c2ecf20Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_SDIO,
18238c2ecf20Sopenharmony_ci			   "sdio mailbox swap service enabled\n");
18248c2ecf20Sopenharmony_ci		ar_sdio->swap_mbox = true;
18258c2ecf20Sopenharmony_ci	} else {
18268c2ecf20Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_SDIO,
18278c2ecf20Sopenharmony_ci			   "sdio mailbox swap service disabled\n");
18288c2ecf20Sopenharmony_ci		ar_sdio->swap_mbox = false;
18298c2ecf20Sopenharmony_ci	}
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ci	ath10k_sdio_set_mbox_sleep(ar, true);
18328c2ecf20Sopenharmony_ci
18338c2ecf20Sopenharmony_ci	return 0;
18348c2ecf20Sopenharmony_ci}
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_cistatic int ath10k_sdio_get_htt_tx_complete(struct ath10k *ar)
18378c2ecf20Sopenharmony_ci{
18388c2ecf20Sopenharmony_ci	u32 addr, val;
18398c2ecf20Sopenharmony_ci	int ret;
18408c2ecf20Sopenharmony_ci
18418c2ecf20Sopenharmony_ci	addr = host_interest_item_address(HI_ITEM(hi_acs_flags));
18428c2ecf20Sopenharmony_ci
18438c2ecf20Sopenharmony_ci	ret = ath10k_sdio_diag_read32(ar, addr, &val);
18448c2ecf20Sopenharmony_ci	if (ret) {
18458c2ecf20Sopenharmony_ci		ath10k_warn(ar,
18468c2ecf20Sopenharmony_ci			    "unable to read hi_acs_flags for htt tx comple : %d\n", ret);
18478c2ecf20Sopenharmony_ci		return ret;
18488c2ecf20Sopenharmony_ci	}
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci	ret = (val & HI_ACS_FLAGS_SDIO_REDUCE_TX_COMPL_FW_ACK);
18518c2ecf20Sopenharmony_ci
18528c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio reduce tx complete fw%sack\n",
18538c2ecf20Sopenharmony_ci		   ret ? " " : " not ");
18548c2ecf20Sopenharmony_ci
18558c2ecf20Sopenharmony_ci	return ret;
18568c2ecf20Sopenharmony_ci}
18578c2ecf20Sopenharmony_ci
18588c2ecf20Sopenharmony_ci/* HIF start/stop */
18598c2ecf20Sopenharmony_ci
18608c2ecf20Sopenharmony_cistatic int ath10k_sdio_hif_start(struct ath10k *ar)
18618c2ecf20Sopenharmony_ci{
18628c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
18638c2ecf20Sopenharmony_ci	int ret;
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_ci	napi_enable(&ar->napi);
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_ci	/* Sleep 20 ms before HIF interrupts are disabled.
18688c2ecf20Sopenharmony_ci	 * This will give target plenty of time to process the BMI done
18698c2ecf20Sopenharmony_ci	 * request before interrupts are disabled.
18708c2ecf20Sopenharmony_ci	 */
18718c2ecf20Sopenharmony_ci	msleep(20);
18728c2ecf20Sopenharmony_ci	ret = ath10k_sdio_disable_intrs(ar);
18738c2ecf20Sopenharmony_ci	if (ret)
18748c2ecf20Sopenharmony_ci		return ret;
18758c2ecf20Sopenharmony_ci
18768c2ecf20Sopenharmony_ci	/* eid 0 always uses the lower part of the extended mailbox address
18778c2ecf20Sopenharmony_ci	 * space (ext_info[0].htc_ext_addr).
18788c2ecf20Sopenharmony_ci	 */
18798c2ecf20Sopenharmony_ci	ar_sdio->mbox_addr[0] = ar_sdio->mbox_info.ext_info[0].htc_ext_addr;
18808c2ecf20Sopenharmony_ci	ar_sdio->mbox_size[0] = ar_sdio->mbox_info.ext_info[0].htc_ext_sz;
18818c2ecf20Sopenharmony_ci
18828c2ecf20Sopenharmony_ci	sdio_claim_host(ar_sdio->func);
18838c2ecf20Sopenharmony_ci
18848c2ecf20Sopenharmony_ci	/* Register the isr */
18858c2ecf20Sopenharmony_ci	ret =  sdio_claim_irq(ar_sdio->func, ath10k_sdio_irq_handler);
18868c2ecf20Sopenharmony_ci	if (ret) {
18878c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to claim sdio interrupt: %d\n", ret);
18888c2ecf20Sopenharmony_ci		sdio_release_host(ar_sdio->func);
18898c2ecf20Sopenharmony_ci		return ret;
18908c2ecf20Sopenharmony_ci	}
18918c2ecf20Sopenharmony_ci
18928c2ecf20Sopenharmony_ci	sdio_release_host(ar_sdio->func);
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_ci	ret = ath10k_sdio_enable_intrs(ar);
18958c2ecf20Sopenharmony_ci	if (ret)
18968c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to enable sdio interrupts: %d\n", ret);
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci	/* Enable sleep and then disable it again */
18998c2ecf20Sopenharmony_ci	ret = ath10k_sdio_set_mbox_sleep(ar, true);
19008c2ecf20Sopenharmony_ci	if (ret)
19018c2ecf20Sopenharmony_ci		return ret;
19028c2ecf20Sopenharmony_ci
19038c2ecf20Sopenharmony_ci	/* Wait for 20ms for the written value to take effect */
19048c2ecf20Sopenharmony_ci	msleep(20);
19058c2ecf20Sopenharmony_ci
19068c2ecf20Sopenharmony_ci	ret = ath10k_sdio_set_mbox_sleep(ar, false);
19078c2ecf20Sopenharmony_ci	if (ret)
19088c2ecf20Sopenharmony_ci		return ret;
19098c2ecf20Sopenharmony_ci
19108c2ecf20Sopenharmony_ci	return 0;
19118c2ecf20Sopenharmony_ci}
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_ci#define SDIO_IRQ_DISABLE_TIMEOUT_HZ (3 * HZ)
19148c2ecf20Sopenharmony_ci
19158c2ecf20Sopenharmony_cistatic void ath10k_sdio_irq_disable(struct ath10k *ar)
19168c2ecf20Sopenharmony_ci{
19178c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
19188c2ecf20Sopenharmony_ci	struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
19198c2ecf20Sopenharmony_ci	struct ath10k_sdio_irq_enable_regs *regs = irq_data->irq_en_reg;
19208c2ecf20Sopenharmony_ci	struct sk_buff *skb;
19218c2ecf20Sopenharmony_ci	struct completion irqs_disabled_comp;
19228c2ecf20Sopenharmony_ci	int ret;
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_ci	skb = dev_alloc_skb(sizeof(*regs));
19258c2ecf20Sopenharmony_ci	if (!skb)
19268c2ecf20Sopenharmony_ci		return;
19278c2ecf20Sopenharmony_ci
19288c2ecf20Sopenharmony_ci	mutex_lock(&irq_data->mtx);
19298c2ecf20Sopenharmony_ci
19308c2ecf20Sopenharmony_ci	memset(regs, 0, sizeof(*regs)); /* disable all interrupts */
19318c2ecf20Sopenharmony_ci	memcpy(skb->data, regs, sizeof(*regs));
19328c2ecf20Sopenharmony_ci	skb_put(skb, sizeof(*regs));
19338c2ecf20Sopenharmony_ci
19348c2ecf20Sopenharmony_ci	mutex_unlock(&irq_data->mtx);
19358c2ecf20Sopenharmony_ci
19368c2ecf20Sopenharmony_ci	init_completion(&irqs_disabled_comp);
19378c2ecf20Sopenharmony_ci	ret = ath10k_sdio_prep_async_req(ar, MBOX_INT_STATUS_ENABLE_ADDRESS,
19388c2ecf20Sopenharmony_ci					 skb, &irqs_disabled_comp, false, 0);
19398c2ecf20Sopenharmony_ci	if (ret)
19408c2ecf20Sopenharmony_ci		goto out;
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_ci	queue_work(ar_sdio->workqueue, &ar_sdio->wr_async_work);
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_ci	/* Wait for the completion of the IRQ disable request.
19458c2ecf20Sopenharmony_ci	 * If there is a timeout we will try to disable irq's anyway.
19468c2ecf20Sopenharmony_ci	 */
19478c2ecf20Sopenharmony_ci	ret = wait_for_completion_timeout(&irqs_disabled_comp,
19488c2ecf20Sopenharmony_ci					  SDIO_IRQ_DISABLE_TIMEOUT_HZ);
19498c2ecf20Sopenharmony_ci	if (!ret)
19508c2ecf20Sopenharmony_ci		ath10k_warn(ar, "sdio irq disable request timed out\n");
19518c2ecf20Sopenharmony_ci
19528c2ecf20Sopenharmony_ci	sdio_claim_host(ar_sdio->func);
19538c2ecf20Sopenharmony_ci
19548c2ecf20Sopenharmony_ci	ret = sdio_release_irq(ar_sdio->func);
19558c2ecf20Sopenharmony_ci	if (ret)
19568c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to release sdio interrupt: %d\n", ret);
19578c2ecf20Sopenharmony_ci
19588c2ecf20Sopenharmony_ci	sdio_release_host(ar_sdio->func);
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ciout:
19618c2ecf20Sopenharmony_ci	kfree_skb(skb);
19628c2ecf20Sopenharmony_ci}
19638c2ecf20Sopenharmony_ci
19648c2ecf20Sopenharmony_cistatic void ath10k_sdio_hif_stop(struct ath10k *ar)
19658c2ecf20Sopenharmony_ci{
19668c2ecf20Sopenharmony_ci	struct ath10k_sdio_bus_request *req, *tmp_req;
19678c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_ci	ath10k_sdio_irq_disable(ar);
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci	cancel_work_sync(&ar_sdio->wr_async_work);
19728c2ecf20Sopenharmony_ci
19738c2ecf20Sopenharmony_ci	spin_lock_bh(&ar_sdio->wr_async_lock);
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_ci	/* Free all bus requests that have not been handled */
19768c2ecf20Sopenharmony_ci	list_for_each_entry_safe(req, tmp_req, &ar_sdio->wr_asyncq, list) {
19778c2ecf20Sopenharmony_ci		struct ath10k_htc_ep *ep;
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci		list_del(&req->list);
19808c2ecf20Sopenharmony_ci
19818c2ecf20Sopenharmony_ci		if (req->htc_msg) {
19828c2ecf20Sopenharmony_ci			ep = &ar->htc.endpoint[req->eid];
19838c2ecf20Sopenharmony_ci			ath10k_htc_notify_tx_completion(ep, req->skb);
19848c2ecf20Sopenharmony_ci		} else if (req->skb) {
19858c2ecf20Sopenharmony_ci			kfree_skb(req->skb);
19868c2ecf20Sopenharmony_ci		}
19878c2ecf20Sopenharmony_ci		ath10k_sdio_free_bus_req(ar, req);
19888c2ecf20Sopenharmony_ci	}
19898c2ecf20Sopenharmony_ci
19908c2ecf20Sopenharmony_ci	spin_unlock_bh(&ar_sdio->wr_async_lock);
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_ci	napi_synchronize(&ar->napi);
19938c2ecf20Sopenharmony_ci	napi_disable(&ar->napi);
19948c2ecf20Sopenharmony_ci}
19958c2ecf20Sopenharmony_ci
19968c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
19978c2ecf20Sopenharmony_ci
19988c2ecf20Sopenharmony_cistatic int ath10k_sdio_hif_suspend(struct ath10k *ar)
19998c2ecf20Sopenharmony_ci{
20008c2ecf20Sopenharmony_ci	return 0;
20018c2ecf20Sopenharmony_ci}
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_cistatic int ath10k_sdio_hif_resume(struct ath10k *ar)
20048c2ecf20Sopenharmony_ci{
20058c2ecf20Sopenharmony_ci	switch (ar->state) {
20068c2ecf20Sopenharmony_ci	case ATH10K_STATE_OFF:
20078c2ecf20Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_SDIO,
20088c2ecf20Sopenharmony_ci			   "sdio resume configuring sdio\n");
20098c2ecf20Sopenharmony_ci
20108c2ecf20Sopenharmony_ci		/* need to set sdio settings after power is cut from sdio */
20118c2ecf20Sopenharmony_ci		ath10k_sdio_config(ar);
20128c2ecf20Sopenharmony_ci		break;
20138c2ecf20Sopenharmony_ci
20148c2ecf20Sopenharmony_ci	case ATH10K_STATE_ON:
20158c2ecf20Sopenharmony_ci	default:
20168c2ecf20Sopenharmony_ci		break;
20178c2ecf20Sopenharmony_ci	}
20188c2ecf20Sopenharmony_ci
20198c2ecf20Sopenharmony_ci	return 0;
20208c2ecf20Sopenharmony_ci}
20218c2ecf20Sopenharmony_ci#endif
20228c2ecf20Sopenharmony_ci
20238c2ecf20Sopenharmony_cistatic int ath10k_sdio_hif_map_service_to_pipe(struct ath10k *ar,
20248c2ecf20Sopenharmony_ci					       u16 service_id,
20258c2ecf20Sopenharmony_ci					       u8 *ul_pipe, u8 *dl_pipe)
20268c2ecf20Sopenharmony_ci{
20278c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
20288c2ecf20Sopenharmony_ci	struct ath10k_htc *htc = &ar->htc;
20298c2ecf20Sopenharmony_ci	u32 htt_addr, wmi_addr, htt_mbox_size, wmi_mbox_size;
20308c2ecf20Sopenharmony_ci	enum ath10k_htc_ep_id eid;
20318c2ecf20Sopenharmony_ci	bool ep_found = false;
20328c2ecf20Sopenharmony_ci	int i;
20338c2ecf20Sopenharmony_ci
20348c2ecf20Sopenharmony_ci	/* For sdio, we are interested in the mapping between eid
20358c2ecf20Sopenharmony_ci	 * and pipeid rather than service_id to pipe_id.
20368c2ecf20Sopenharmony_ci	 * First we find out which eid has been allocated to the
20378c2ecf20Sopenharmony_ci	 * service...
20388c2ecf20Sopenharmony_ci	 */
20398c2ecf20Sopenharmony_ci	for (i = 0; i < ATH10K_HTC_EP_COUNT; i++) {
20408c2ecf20Sopenharmony_ci		if (htc->endpoint[i].service_id == service_id) {
20418c2ecf20Sopenharmony_ci			eid = htc->endpoint[i].eid;
20428c2ecf20Sopenharmony_ci			ep_found = true;
20438c2ecf20Sopenharmony_ci			break;
20448c2ecf20Sopenharmony_ci		}
20458c2ecf20Sopenharmony_ci	}
20468c2ecf20Sopenharmony_ci
20478c2ecf20Sopenharmony_ci	if (!ep_found)
20488c2ecf20Sopenharmony_ci		return -EINVAL;
20498c2ecf20Sopenharmony_ci
20508c2ecf20Sopenharmony_ci	/* Then we create the simplest mapping possible between pipeid
20518c2ecf20Sopenharmony_ci	 * and eid
20528c2ecf20Sopenharmony_ci	 */
20538c2ecf20Sopenharmony_ci	*ul_pipe = *dl_pipe = (u8)eid;
20548c2ecf20Sopenharmony_ci
20558c2ecf20Sopenharmony_ci	/* Normally, HTT will use the upper part of the extended
20568c2ecf20Sopenharmony_ci	 * mailbox address space (ext_info[1].htc_ext_addr) and WMI ctrl
20578c2ecf20Sopenharmony_ci	 * the lower part (ext_info[0].htc_ext_addr).
20588c2ecf20Sopenharmony_ci	 * If fw wants swapping of mailbox addresses, the opposite is true.
20598c2ecf20Sopenharmony_ci	 */
20608c2ecf20Sopenharmony_ci	if (ar_sdio->swap_mbox) {
20618c2ecf20Sopenharmony_ci		htt_addr = ar_sdio->mbox_info.ext_info[0].htc_ext_addr;
20628c2ecf20Sopenharmony_ci		wmi_addr = ar_sdio->mbox_info.ext_info[1].htc_ext_addr;
20638c2ecf20Sopenharmony_ci		htt_mbox_size = ar_sdio->mbox_info.ext_info[0].htc_ext_sz;
20648c2ecf20Sopenharmony_ci		wmi_mbox_size = ar_sdio->mbox_info.ext_info[1].htc_ext_sz;
20658c2ecf20Sopenharmony_ci	} else {
20668c2ecf20Sopenharmony_ci		htt_addr = ar_sdio->mbox_info.ext_info[1].htc_ext_addr;
20678c2ecf20Sopenharmony_ci		wmi_addr = ar_sdio->mbox_info.ext_info[0].htc_ext_addr;
20688c2ecf20Sopenharmony_ci		htt_mbox_size = ar_sdio->mbox_info.ext_info[1].htc_ext_sz;
20698c2ecf20Sopenharmony_ci		wmi_mbox_size = ar_sdio->mbox_info.ext_info[0].htc_ext_sz;
20708c2ecf20Sopenharmony_ci	}
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci	switch (service_id) {
20738c2ecf20Sopenharmony_ci	case ATH10K_HTC_SVC_ID_RSVD_CTRL:
20748c2ecf20Sopenharmony_ci		/* HTC ctrl ep mbox address has already been setup in
20758c2ecf20Sopenharmony_ci		 * ath10k_sdio_hif_start
20768c2ecf20Sopenharmony_ci		 */
20778c2ecf20Sopenharmony_ci		break;
20788c2ecf20Sopenharmony_ci	case ATH10K_HTC_SVC_ID_WMI_CONTROL:
20798c2ecf20Sopenharmony_ci		ar_sdio->mbox_addr[eid] = wmi_addr;
20808c2ecf20Sopenharmony_ci		ar_sdio->mbox_size[eid] = wmi_mbox_size;
20818c2ecf20Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_SDIO,
20828c2ecf20Sopenharmony_ci			   "sdio wmi ctrl mbox_addr 0x%x mbox_size %d\n",
20838c2ecf20Sopenharmony_ci			   ar_sdio->mbox_addr[eid], ar_sdio->mbox_size[eid]);
20848c2ecf20Sopenharmony_ci		break;
20858c2ecf20Sopenharmony_ci	case ATH10K_HTC_SVC_ID_HTT_DATA_MSG:
20868c2ecf20Sopenharmony_ci		ar_sdio->mbox_addr[eid] = htt_addr;
20878c2ecf20Sopenharmony_ci		ar_sdio->mbox_size[eid] = htt_mbox_size;
20888c2ecf20Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_SDIO,
20898c2ecf20Sopenharmony_ci			   "sdio htt data mbox_addr 0x%x mbox_size %d\n",
20908c2ecf20Sopenharmony_ci			   ar_sdio->mbox_addr[eid], ar_sdio->mbox_size[eid]);
20918c2ecf20Sopenharmony_ci		break;
20928c2ecf20Sopenharmony_ci	default:
20938c2ecf20Sopenharmony_ci		ath10k_warn(ar, "unsupported HTC service id: %d\n",
20948c2ecf20Sopenharmony_ci			    service_id);
20958c2ecf20Sopenharmony_ci		return -EINVAL;
20968c2ecf20Sopenharmony_ci	}
20978c2ecf20Sopenharmony_ci
20988c2ecf20Sopenharmony_ci	return 0;
20998c2ecf20Sopenharmony_ci}
21008c2ecf20Sopenharmony_ci
21018c2ecf20Sopenharmony_cistatic void ath10k_sdio_hif_get_default_pipe(struct ath10k *ar,
21028c2ecf20Sopenharmony_ci					     u8 *ul_pipe, u8 *dl_pipe)
21038c2ecf20Sopenharmony_ci{
21048c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio hif get default pipe\n");
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci	/* HTC ctrl ep (SVC id 1) always has eid (and pipe_id in our
21078c2ecf20Sopenharmony_ci	 * case) == 0
21088c2ecf20Sopenharmony_ci	 */
21098c2ecf20Sopenharmony_ci	*ul_pipe = 0;
21108c2ecf20Sopenharmony_ci	*dl_pipe = 0;
21118c2ecf20Sopenharmony_ci}
21128c2ecf20Sopenharmony_ci
21138c2ecf20Sopenharmony_cistatic const struct ath10k_hif_ops ath10k_sdio_hif_ops = {
21148c2ecf20Sopenharmony_ci	.tx_sg			= ath10k_sdio_hif_tx_sg,
21158c2ecf20Sopenharmony_ci	.diag_read		= ath10k_sdio_hif_diag_read,
21168c2ecf20Sopenharmony_ci	.diag_write		= ath10k_sdio_hif_diag_write_mem,
21178c2ecf20Sopenharmony_ci	.exchange_bmi_msg	= ath10k_sdio_bmi_exchange_msg,
21188c2ecf20Sopenharmony_ci	.start			= ath10k_sdio_hif_start,
21198c2ecf20Sopenharmony_ci	.stop			= ath10k_sdio_hif_stop,
21208c2ecf20Sopenharmony_ci	.start_post		= ath10k_sdio_hif_start_post,
21218c2ecf20Sopenharmony_ci	.get_htt_tx_complete	= ath10k_sdio_get_htt_tx_complete,
21228c2ecf20Sopenharmony_ci	.map_service_to_pipe	= ath10k_sdio_hif_map_service_to_pipe,
21238c2ecf20Sopenharmony_ci	.get_default_pipe	= ath10k_sdio_hif_get_default_pipe,
21248c2ecf20Sopenharmony_ci	.power_up		= ath10k_sdio_hif_power_up,
21258c2ecf20Sopenharmony_ci	.power_down		= ath10k_sdio_hif_power_down,
21268c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
21278c2ecf20Sopenharmony_ci	.suspend		= ath10k_sdio_hif_suspend,
21288c2ecf20Sopenharmony_ci	.resume			= ath10k_sdio_hif_resume,
21298c2ecf20Sopenharmony_ci#endif
21308c2ecf20Sopenharmony_ci};
21318c2ecf20Sopenharmony_ci
21328c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
21338c2ecf20Sopenharmony_ci
21348c2ecf20Sopenharmony_ci/* Empty handlers so that mmc subsystem doesn't remove us entirely during
21358c2ecf20Sopenharmony_ci * suspend. We instead follow cfg80211 suspend/resume handlers.
21368c2ecf20Sopenharmony_ci */
21378c2ecf20Sopenharmony_cistatic int ath10k_sdio_pm_suspend(struct device *device)
21388c2ecf20Sopenharmony_ci{
21398c2ecf20Sopenharmony_ci	struct sdio_func *func = dev_to_sdio_func(device);
21408c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = sdio_get_drvdata(func);
21418c2ecf20Sopenharmony_ci	struct ath10k *ar = ar_sdio->ar;
21428c2ecf20Sopenharmony_ci	mmc_pm_flag_t pm_flag, pm_caps;
21438c2ecf20Sopenharmony_ci	int ret;
21448c2ecf20Sopenharmony_ci
21458c2ecf20Sopenharmony_ci	if (!device_may_wakeup(ar->dev))
21468c2ecf20Sopenharmony_ci		return 0;
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_ci	ath10k_sdio_set_mbox_sleep(ar, true);
21498c2ecf20Sopenharmony_ci
21508c2ecf20Sopenharmony_ci	pm_flag = MMC_PM_KEEP_POWER;
21518c2ecf20Sopenharmony_ci
21528c2ecf20Sopenharmony_ci	ret = sdio_set_host_pm_flags(func, pm_flag);
21538c2ecf20Sopenharmony_ci	if (ret) {
21548c2ecf20Sopenharmony_ci		pm_caps = sdio_get_host_pm_caps(func);
21558c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to set sdio host pm flags (0x%x, 0x%x): %d\n",
21568c2ecf20Sopenharmony_ci			    pm_flag, pm_caps, ret);
21578c2ecf20Sopenharmony_ci		return ret;
21588c2ecf20Sopenharmony_ci	}
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_ci	return ret;
21618c2ecf20Sopenharmony_ci}
21628c2ecf20Sopenharmony_ci
21638c2ecf20Sopenharmony_cistatic int ath10k_sdio_pm_resume(struct device *device)
21648c2ecf20Sopenharmony_ci{
21658c2ecf20Sopenharmony_ci	return 0;
21668c2ecf20Sopenharmony_ci}
21678c2ecf20Sopenharmony_ci
21688c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(ath10k_sdio_pm_ops, ath10k_sdio_pm_suspend,
21698c2ecf20Sopenharmony_ci			 ath10k_sdio_pm_resume);
21708c2ecf20Sopenharmony_ci
21718c2ecf20Sopenharmony_ci#define ATH10K_SDIO_PM_OPS (&ath10k_sdio_pm_ops)
21728c2ecf20Sopenharmony_ci
21738c2ecf20Sopenharmony_ci#else
21748c2ecf20Sopenharmony_ci
21758c2ecf20Sopenharmony_ci#define ATH10K_SDIO_PM_OPS NULL
21768c2ecf20Sopenharmony_ci
21778c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
21788c2ecf20Sopenharmony_ci
21798c2ecf20Sopenharmony_cistatic int ath10k_sdio_napi_poll(struct napi_struct *ctx, int budget)
21808c2ecf20Sopenharmony_ci{
21818c2ecf20Sopenharmony_ci	struct ath10k *ar = container_of(ctx, struct ath10k, napi);
21828c2ecf20Sopenharmony_ci	int done;
21838c2ecf20Sopenharmony_ci
21848c2ecf20Sopenharmony_ci	done = ath10k_htt_rx_hl_indication(ar, budget);
21858c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "napi poll: done: %d, budget:%d\n", done, budget);
21868c2ecf20Sopenharmony_ci
21878c2ecf20Sopenharmony_ci	if (done < budget)
21888c2ecf20Sopenharmony_ci		napi_complete_done(ctx, done);
21898c2ecf20Sopenharmony_ci
21908c2ecf20Sopenharmony_ci	return done;
21918c2ecf20Sopenharmony_ci}
21928c2ecf20Sopenharmony_ci
21938c2ecf20Sopenharmony_cistatic int ath10k_sdio_read_host_interest_value(struct ath10k *ar,
21948c2ecf20Sopenharmony_ci						u32 item_offset,
21958c2ecf20Sopenharmony_ci						u32 *val)
21968c2ecf20Sopenharmony_ci{
21978c2ecf20Sopenharmony_ci	u32 addr;
21988c2ecf20Sopenharmony_ci	int ret;
21998c2ecf20Sopenharmony_ci
22008c2ecf20Sopenharmony_ci	addr = host_interest_item_address(item_offset);
22018c2ecf20Sopenharmony_ci
22028c2ecf20Sopenharmony_ci	ret = ath10k_sdio_diag_read32(ar, addr, val);
22038c2ecf20Sopenharmony_ci
22048c2ecf20Sopenharmony_ci	if (ret)
22058c2ecf20Sopenharmony_ci		ath10k_warn(ar, "unable to read host interest offset %d value\n",
22068c2ecf20Sopenharmony_ci			    item_offset);
22078c2ecf20Sopenharmony_ci
22088c2ecf20Sopenharmony_ci	return ret;
22098c2ecf20Sopenharmony_ci}
22108c2ecf20Sopenharmony_ci
22118c2ecf20Sopenharmony_cistatic int ath10k_sdio_read_mem(struct ath10k *ar, u32 address, void *buf,
22128c2ecf20Sopenharmony_ci				u32 buf_len)
22138c2ecf20Sopenharmony_ci{
22148c2ecf20Sopenharmony_ci	u32 val;
22158c2ecf20Sopenharmony_ci	int i, ret;
22168c2ecf20Sopenharmony_ci
22178c2ecf20Sopenharmony_ci	for (i = 0; i < buf_len; i += 4) {
22188c2ecf20Sopenharmony_ci		ret = ath10k_sdio_diag_read32(ar, address + i, &val);
22198c2ecf20Sopenharmony_ci		if (ret) {
22208c2ecf20Sopenharmony_ci			ath10k_warn(ar, "unable to read mem %d value\n", address + i);
22218c2ecf20Sopenharmony_ci			break;
22228c2ecf20Sopenharmony_ci		}
22238c2ecf20Sopenharmony_ci		memcpy(buf + i, &val, 4);
22248c2ecf20Sopenharmony_ci	}
22258c2ecf20Sopenharmony_ci
22268c2ecf20Sopenharmony_ci	return ret;
22278c2ecf20Sopenharmony_ci}
22288c2ecf20Sopenharmony_ci
22298c2ecf20Sopenharmony_cistatic bool ath10k_sdio_is_fast_dump_supported(struct ath10k *ar)
22308c2ecf20Sopenharmony_ci{
22318c2ecf20Sopenharmony_ci	u32 param;
22328c2ecf20Sopenharmony_ci
22338c2ecf20Sopenharmony_ci	ath10k_sdio_read_host_interest_value(ar, HI_ITEM(hi_option_flag2), &param);
22348c2ecf20Sopenharmony_ci
22358c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio hi_option_flag2 %x\n", param);
22368c2ecf20Sopenharmony_ci
22378c2ecf20Sopenharmony_ci	return param & HI_OPTION_SDIO_CRASH_DUMP_ENHANCEMENT_FW;
22388c2ecf20Sopenharmony_ci}
22398c2ecf20Sopenharmony_ci
22408c2ecf20Sopenharmony_cistatic void ath10k_sdio_dump_registers(struct ath10k *ar,
22418c2ecf20Sopenharmony_ci				       struct ath10k_fw_crash_data *crash_data,
22428c2ecf20Sopenharmony_ci				       bool fast_dump)
22438c2ecf20Sopenharmony_ci{
22448c2ecf20Sopenharmony_ci	u32 reg_dump_values[REG_DUMP_COUNT_QCA988X] = {};
22458c2ecf20Sopenharmony_ci	int i, ret;
22468c2ecf20Sopenharmony_ci	u32 reg_dump_area;
22478c2ecf20Sopenharmony_ci
22488c2ecf20Sopenharmony_ci	ret = ath10k_sdio_read_host_interest_value(ar, HI_ITEM(hi_failure_state),
22498c2ecf20Sopenharmony_ci						   &reg_dump_area);
22508c2ecf20Sopenharmony_ci	if (ret) {
22518c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to read firmware dump area: %d\n", ret);
22528c2ecf20Sopenharmony_ci		return;
22538c2ecf20Sopenharmony_ci	}
22548c2ecf20Sopenharmony_ci
22558c2ecf20Sopenharmony_ci	if (fast_dump)
22568c2ecf20Sopenharmony_ci		ret = ath10k_bmi_read_memory(ar, reg_dump_area, reg_dump_values,
22578c2ecf20Sopenharmony_ci					     sizeof(reg_dump_values));
22588c2ecf20Sopenharmony_ci	else
22598c2ecf20Sopenharmony_ci		ret = ath10k_sdio_read_mem(ar, reg_dump_area, reg_dump_values,
22608c2ecf20Sopenharmony_ci					   sizeof(reg_dump_values));
22618c2ecf20Sopenharmony_ci
22628c2ecf20Sopenharmony_ci	if (ret) {
22638c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to read firmware dump value: %d\n", ret);
22648c2ecf20Sopenharmony_ci		return;
22658c2ecf20Sopenharmony_ci	}
22668c2ecf20Sopenharmony_ci
22678c2ecf20Sopenharmony_ci	ath10k_err(ar, "firmware register dump:\n");
22688c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(reg_dump_values); i += 4)
22698c2ecf20Sopenharmony_ci		ath10k_err(ar, "[%02d]: 0x%08X 0x%08X 0x%08X 0x%08X\n",
22708c2ecf20Sopenharmony_ci			   i,
22718c2ecf20Sopenharmony_ci			   reg_dump_values[i],
22728c2ecf20Sopenharmony_ci			   reg_dump_values[i + 1],
22738c2ecf20Sopenharmony_ci			   reg_dump_values[i + 2],
22748c2ecf20Sopenharmony_ci			   reg_dump_values[i + 3]);
22758c2ecf20Sopenharmony_ci
22768c2ecf20Sopenharmony_ci	if (!crash_data)
22778c2ecf20Sopenharmony_ci		return;
22788c2ecf20Sopenharmony_ci
22798c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(reg_dump_values); i++)
22808c2ecf20Sopenharmony_ci		crash_data->registers[i] = __cpu_to_le32(reg_dump_values[i]);
22818c2ecf20Sopenharmony_ci}
22828c2ecf20Sopenharmony_ci
22838c2ecf20Sopenharmony_cistatic int ath10k_sdio_dump_memory_section(struct ath10k *ar,
22848c2ecf20Sopenharmony_ci					   const struct ath10k_mem_region *mem_region,
22858c2ecf20Sopenharmony_ci					   u8 *buf, size_t buf_len)
22868c2ecf20Sopenharmony_ci{
22878c2ecf20Sopenharmony_ci	const struct ath10k_mem_section *cur_section, *next_section;
22888c2ecf20Sopenharmony_ci	unsigned int count, section_size, skip_size;
22898c2ecf20Sopenharmony_ci	int ret, i, j;
22908c2ecf20Sopenharmony_ci
22918c2ecf20Sopenharmony_ci	if (!mem_region || !buf)
22928c2ecf20Sopenharmony_ci		return 0;
22938c2ecf20Sopenharmony_ci
22948c2ecf20Sopenharmony_ci	cur_section = &mem_region->section_table.sections[0];
22958c2ecf20Sopenharmony_ci
22968c2ecf20Sopenharmony_ci	if (mem_region->start > cur_section->start) {
22978c2ecf20Sopenharmony_ci		ath10k_warn(ar, "incorrect memdump region 0x%x with section start address 0x%x.\n",
22988c2ecf20Sopenharmony_ci			    mem_region->start, cur_section->start);
22998c2ecf20Sopenharmony_ci		return 0;
23008c2ecf20Sopenharmony_ci	}
23018c2ecf20Sopenharmony_ci
23028c2ecf20Sopenharmony_ci	skip_size = cur_section->start - mem_region->start;
23038c2ecf20Sopenharmony_ci
23048c2ecf20Sopenharmony_ci	/* fill the gap between the first register section and register
23058c2ecf20Sopenharmony_ci	 * start address
23068c2ecf20Sopenharmony_ci	 */
23078c2ecf20Sopenharmony_ci	for (i = 0; i < skip_size; i++) {
23088c2ecf20Sopenharmony_ci		*buf = ATH10K_MAGIC_NOT_COPIED;
23098c2ecf20Sopenharmony_ci		buf++;
23108c2ecf20Sopenharmony_ci	}
23118c2ecf20Sopenharmony_ci
23128c2ecf20Sopenharmony_ci	count = 0;
23138c2ecf20Sopenharmony_ci
23148c2ecf20Sopenharmony_ci	for (i = 0; cur_section; i++) {
23158c2ecf20Sopenharmony_ci		section_size = cur_section->end - cur_section->start;
23168c2ecf20Sopenharmony_ci
23178c2ecf20Sopenharmony_ci		if (section_size <= 0) {
23188c2ecf20Sopenharmony_ci			ath10k_warn(ar, "incorrect ramdump format with start address 0x%x and stop address 0x%x\n",
23198c2ecf20Sopenharmony_ci				    cur_section->start,
23208c2ecf20Sopenharmony_ci				    cur_section->end);
23218c2ecf20Sopenharmony_ci			break;
23228c2ecf20Sopenharmony_ci		}
23238c2ecf20Sopenharmony_ci
23248c2ecf20Sopenharmony_ci		if ((i + 1) == mem_region->section_table.size) {
23258c2ecf20Sopenharmony_ci			/* last section */
23268c2ecf20Sopenharmony_ci			next_section = NULL;
23278c2ecf20Sopenharmony_ci			skip_size = 0;
23288c2ecf20Sopenharmony_ci		} else {
23298c2ecf20Sopenharmony_ci			next_section = cur_section + 1;
23308c2ecf20Sopenharmony_ci
23318c2ecf20Sopenharmony_ci			if (cur_section->end > next_section->start) {
23328c2ecf20Sopenharmony_ci				ath10k_warn(ar, "next ramdump section 0x%x is smaller than current end address 0x%x\n",
23338c2ecf20Sopenharmony_ci					    next_section->start,
23348c2ecf20Sopenharmony_ci					    cur_section->end);
23358c2ecf20Sopenharmony_ci				break;
23368c2ecf20Sopenharmony_ci			}
23378c2ecf20Sopenharmony_ci
23388c2ecf20Sopenharmony_ci			skip_size = next_section->start - cur_section->end;
23398c2ecf20Sopenharmony_ci		}
23408c2ecf20Sopenharmony_ci
23418c2ecf20Sopenharmony_ci		if (buf_len < (skip_size + section_size)) {
23428c2ecf20Sopenharmony_ci			ath10k_warn(ar, "ramdump buffer is too small: %zu\n", buf_len);
23438c2ecf20Sopenharmony_ci			break;
23448c2ecf20Sopenharmony_ci		}
23458c2ecf20Sopenharmony_ci
23468c2ecf20Sopenharmony_ci		buf_len -= skip_size + section_size;
23478c2ecf20Sopenharmony_ci
23488c2ecf20Sopenharmony_ci		/* read section to dest memory */
23498c2ecf20Sopenharmony_ci		ret = ath10k_sdio_read_mem(ar, cur_section->start,
23508c2ecf20Sopenharmony_ci					   buf, section_size);
23518c2ecf20Sopenharmony_ci		if (ret) {
23528c2ecf20Sopenharmony_ci			ath10k_warn(ar, "failed to read ramdump from section 0x%x: %d\n",
23538c2ecf20Sopenharmony_ci				    cur_section->start, ret);
23548c2ecf20Sopenharmony_ci			break;
23558c2ecf20Sopenharmony_ci		}
23568c2ecf20Sopenharmony_ci
23578c2ecf20Sopenharmony_ci		buf += section_size;
23588c2ecf20Sopenharmony_ci		count += section_size;
23598c2ecf20Sopenharmony_ci
23608c2ecf20Sopenharmony_ci		/* fill in the gap between this section and the next */
23618c2ecf20Sopenharmony_ci		for (j = 0; j < skip_size; j++) {
23628c2ecf20Sopenharmony_ci			*buf = ATH10K_MAGIC_NOT_COPIED;
23638c2ecf20Sopenharmony_ci			buf++;
23648c2ecf20Sopenharmony_ci		}
23658c2ecf20Sopenharmony_ci
23668c2ecf20Sopenharmony_ci		count += skip_size;
23678c2ecf20Sopenharmony_ci
23688c2ecf20Sopenharmony_ci		if (!next_section)
23698c2ecf20Sopenharmony_ci			/* this was the last section */
23708c2ecf20Sopenharmony_ci			break;
23718c2ecf20Sopenharmony_ci
23728c2ecf20Sopenharmony_ci		cur_section = next_section;
23738c2ecf20Sopenharmony_ci	}
23748c2ecf20Sopenharmony_ci
23758c2ecf20Sopenharmony_ci	return count;
23768c2ecf20Sopenharmony_ci}
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci/* if an error happened returns < 0, otherwise the length */
23798c2ecf20Sopenharmony_cistatic int ath10k_sdio_dump_memory_generic(struct ath10k *ar,
23808c2ecf20Sopenharmony_ci					   const struct ath10k_mem_region *current_region,
23818c2ecf20Sopenharmony_ci					   u8 *buf,
23828c2ecf20Sopenharmony_ci					   bool fast_dump)
23838c2ecf20Sopenharmony_ci{
23848c2ecf20Sopenharmony_ci	int ret;
23858c2ecf20Sopenharmony_ci
23868c2ecf20Sopenharmony_ci	if (current_region->section_table.size > 0)
23878c2ecf20Sopenharmony_ci		/* Copy each section individually. */
23888c2ecf20Sopenharmony_ci		return ath10k_sdio_dump_memory_section(ar,
23898c2ecf20Sopenharmony_ci						      current_region,
23908c2ecf20Sopenharmony_ci						      buf,
23918c2ecf20Sopenharmony_ci						      current_region->len);
23928c2ecf20Sopenharmony_ci
23938c2ecf20Sopenharmony_ci	/* No individiual memory sections defined so we can
23948c2ecf20Sopenharmony_ci	 * copy the entire memory region.
23958c2ecf20Sopenharmony_ci	 */
23968c2ecf20Sopenharmony_ci	if (fast_dump)
23978c2ecf20Sopenharmony_ci		ret = ath10k_bmi_read_memory(ar,
23988c2ecf20Sopenharmony_ci					     current_region->start,
23998c2ecf20Sopenharmony_ci					     buf,
24008c2ecf20Sopenharmony_ci					     current_region->len);
24018c2ecf20Sopenharmony_ci	else
24028c2ecf20Sopenharmony_ci		ret = ath10k_sdio_read_mem(ar,
24038c2ecf20Sopenharmony_ci					   current_region->start,
24048c2ecf20Sopenharmony_ci					   buf,
24058c2ecf20Sopenharmony_ci					   current_region->len);
24068c2ecf20Sopenharmony_ci
24078c2ecf20Sopenharmony_ci	if (ret) {
24088c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to copy ramdump region %s: %d\n",
24098c2ecf20Sopenharmony_ci			    current_region->name, ret);
24108c2ecf20Sopenharmony_ci		return ret;
24118c2ecf20Sopenharmony_ci	}
24128c2ecf20Sopenharmony_ci
24138c2ecf20Sopenharmony_ci	return current_region->len;
24148c2ecf20Sopenharmony_ci}
24158c2ecf20Sopenharmony_ci
24168c2ecf20Sopenharmony_cistatic void ath10k_sdio_dump_memory(struct ath10k *ar,
24178c2ecf20Sopenharmony_ci				    struct ath10k_fw_crash_data *crash_data,
24188c2ecf20Sopenharmony_ci				    bool fast_dump)
24198c2ecf20Sopenharmony_ci{
24208c2ecf20Sopenharmony_ci	const struct ath10k_hw_mem_layout *mem_layout;
24218c2ecf20Sopenharmony_ci	const struct ath10k_mem_region *current_region;
24228c2ecf20Sopenharmony_ci	struct ath10k_dump_ram_data_hdr *hdr;
24238c2ecf20Sopenharmony_ci	u32 count;
24248c2ecf20Sopenharmony_ci	size_t buf_len;
24258c2ecf20Sopenharmony_ci	int ret, i;
24268c2ecf20Sopenharmony_ci	u8 *buf;
24278c2ecf20Sopenharmony_ci
24288c2ecf20Sopenharmony_ci	if (!crash_data)
24298c2ecf20Sopenharmony_ci		return;
24308c2ecf20Sopenharmony_ci
24318c2ecf20Sopenharmony_ci	mem_layout = ath10k_coredump_get_mem_layout(ar);
24328c2ecf20Sopenharmony_ci	if (!mem_layout)
24338c2ecf20Sopenharmony_ci		return;
24348c2ecf20Sopenharmony_ci
24358c2ecf20Sopenharmony_ci	current_region = &mem_layout->region_table.regions[0];
24368c2ecf20Sopenharmony_ci
24378c2ecf20Sopenharmony_ci	buf = crash_data->ramdump_buf;
24388c2ecf20Sopenharmony_ci	buf_len = crash_data->ramdump_buf_len;
24398c2ecf20Sopenharmony_ci
24408c2ecf20Sopenharmony_ci	memset(buf, 0, buf_len);
24418c2ecf20Sopenharmony_ci
24428c2ecf20Sopenharmony_ci	for (i = 0; i < mem_layout->region_table.size; i++) {
24438c2ecf20Sopenharmony_ci		count = 0;
24448c2ecf20Sopenharmony_ci
24458c2ecf20Sopenharmony_ci		if (current_region->len > buf_len) {
24468c2ecf20Sopenharmony_ci			ath10k_warn(ar, "memory region %s size %d is larger that remaining ramdump buffer size %zu\n",
24478c2ecf20Sopenharmony_ci				    current_region->name,
24488c2ecf20Sopenharmony_ci				    current_region->len,
24498c2ecf20Sopenharmony_ci				    buf_len);
24508c2ecf20Sopenharmony_ci			break;
24518c2ecf20Sopenharmony_ci		}
24528c2ecf20Sopenharmony_ci
24538c2ecf20Sopenharmony_ci		/* Reserve space for the header. */
24548c2ecf20Sopenharmony_ci		hdr = (void *)buf;
24558c2ecf20Sopenharmony_ci		buf += sizeof(*hdr);
24568c2ecf20Sopenharmony_ci		buf_len -= sizeof(*hdr);
24578c2ecf20Sopenharmony_ci
24588c2ecf20Sopenharmony_ci		ret = ath10k_sdio_dump_memory_generic(ar, current_region, buf,
24598c2ecf20Sopenharmony_ci						      fast_dump);
24608c2ecf20Sopenharmony_ci		if (ret >= 0)
24618c2ecf20Sopenharmony_ci			count = ret;
24628c2ecf20Sopenharmony_ci
24638c2ecf20Sopenharmony_ci		hdr->region_type = cpu_to_le32(current_region->type);
24648c2ecf20Sopenharmony_ci		hdr->start = cpu_to_le32(current_region->start);
24658c2ecf20Sopenharmony_ci		hdr->length = cpu_to_le32(count);
24668c2ecf20Sopenharmony_ci
24678c2ecf20Sopenharmony_ci		if (count == 0)
24688c2ecf20Sopenharmony_ci			/* Note: the header remains, just with zero length. */
24698c2ecf20Sopenharmony_ci			break;
24708c2ecf20Sopenharmony_ci
24718c2ecf20Sopenharmony_ci		buf += count;
24728c2ecf20Sopenharmony_ci		buf_len -= count;
24738c2ecf20Sopenharmony_ci
24748c2ecf20Sopenharmony_ci		current_region++;
24758c2ecf20Sopenharmony_ci	}
24768c2ecf20Sopenharmony_ci}
24778c2ecf20Sopenharmony_ci
24788c2ecf20Sopenharmony_civoid ath10k_sdio_fw_crashed_dump(struct ath10k *ar)
24798c2ecf20Sopenharmony_ci{
24808c2ecf20Sopenharmony_ci	struct ath10k_fw_crash_data *crash_data;
24818c2ecf20Sopenharmony_ci	char guid[UUID_STRING_LEN + 1];
24828c2ecf20Sopenharmony_ci	bool fast_dump;
24838c2ecf20Sopenharmony_ci
24848c2ecf20Sopenharmony_ci	fast_dump = ath10k_sdio_is_fast_dump_supported(ar);
24858c2ecf20Sopenharmony_ci
24868c2ecf20Sopenharmony_ci	if (fast_dump)
24878c2ecf20Sopenharmony_ci		ath10k_bmi_start(ar);
24888c2ecf20Sopenharmony_ci
24898c2ecf20Sopenharmony_ci	ar->stats.fw_crash_counter++;
24908c2ecf20Sopenharmony_ci
24918c2ecf20Sopenharmony_ci	ath10k_sdio_disable_intrs(ar);
24928c2ecf20Sopenharmony_ci
24938c2ecf20Sopenharmony_ci	crash_data = ath10k_coredump_new(ar);
24948c2ecf20Sopenharmony_ci
24958c2ecf20Sopenharmony_ci	if (crash_data)
24968c2ecf20Sopenharmony_ci		scnprintf(guid, sizeof(guid), "%pUl", &crash_data->guid);
24978c2ecf20Sopenharmony_ci	else
24988c2ecf20Sopenharmony_ci		scnprintf(guid, sizeof(guid), "n/a");
24998c2ecf20Sopenharmony_ci
25008c2ecf20Sopenharmony_ci	ath10k_err(ar, "firmware crashed! (guid %s)\n", guid);
25018c2ecf20Sopenharmony_ci	ath10k_print_driver_info(ar);
25028c2ecf20Sopenharmony_ci	ath10k_sdio_dump_registers(ar, crash_data, fast_dump);
25038c2ecf20Sopenharmony_ci	ath10k_sdio_dump_memory(ar, crash_data, fast_dump);
25048c2ecf20Sopenharmony_ci
25058c2ecf20Sopenharmony_ci	ath10k_sdio_enable_intrs(ar);
25068c2ecf20Sopenharmony_ci
25078c2ecf20Sopenharmony_ci	queue_work(ar->workqueue, &ar->restart_work);
25088c2ecf20Sopenharmony_ci}
25098c2ecf20Sopenharmony_ci
25108c2ecf20Sopenharmony_cistatic int ath10k_sdio_probe(struct sdio_func *func,
25118c2ecf20Sopenharmony_ci			     const struct sdio_device_id *id)
25128c2ecf20Sopenharmony_ci{
25138c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio;
25148c2ecf20Sopenharmony_ci	struct ath10k *ar;
25158c2ecf20Sopenharmony_ci	enum ath10k_hw_rev hw_rev;
25168c2ecf20Sopenharmony_ci	u32 dev_id_base;
25178c2ecf20Sopenharmony_ci	struct ath10k_bus_params bus_params = {};
25188c2ecf20Sopenharmony_ci	int ret, i;
25198c2ecf20Sopenharmony_ci
25208c2ecf20Sopenharmony_ci	/* Assumption: All SDIO based chipsets (so far) are QCA6174 based.
25218c2ecf20Sopenharmony_ci	 * If there will be newer chipsets that does not use the hw reg
25228c2ecf20Sopenharmony_ci	 * setup as defined in qca6174_regs and qca6174_values, this
25238c2ecf20Sopenharmony_ci	 * assumption is no longer valid and hw_rev must be setup differently
25248c2ecf20Sopenharmony_ci	 * depending on chipset.
25258c2ecf20Sopenharmony_ci	 */
25268c2ecf20Sopenharmony_ci	hw_rev = ATH10K_HW_QCA6174;
25278c2ecf20Sopenharmony_ci
25288c2ecf20Sopenharmony_ci	ar = ath10k_core_create(sizeof(*ar_sdio), &func->dev, ATH10K_BUS_SDIO,
25298c2ecf20Sopenharmony_ci				hw_rev, &ath10k_sdio_hif_ops);
25308c2ecf20Sopenharmony_ci	if (!ar) {
25318c2ecf20Sopenharmony_ci		dev_err(&func->dev, "failed to allocate core\n");
25328c2ecf20Sopenharmony_ci		return -ENOMEM;
25338c2ecf20Sopenharmony_ci	}
25348c2ecf20Sopenharmony_ci
25358c2ecf20Sopenharmony_ci	netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_sdio_napi_poll,
25368c2ecf20Sopenharmony_ci		       ATH10K_NAPI_BUDGET);
25378c2ecf20Sopenharmony_ci
25388c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_BOOT,
25398c2ecf20Sopenharmony_ci		   "sdio new func %d vendor 0x%x device 0x%x block 0x%x/0x%x\n",
25408c2ecf20Sopenharmony_ci		   func->num, func->vendor, func->device,
25418c2ecf20Sopenharmony_ci		   func->max_blksize, func->cur_blksize);
25428c2ecf20Sopenharmony_ci
25438c2ecf20Sopenharmony_ci	ar_sdio = ath10k_sdio_priv(ar);
25448c2ecf20Sopenharmony_ci
25458c2ecf20Sopenharmony_ci	ar_sdio->irq_data.irq_proc_reg =
25468c2ecf20Sopenharmony_ci		devm_kzalloc(ar->dev, sizeof(struct ath10k_sdio_irq_proc_regs),
25478c2ecf20Sopenharmony_ci			     GFP_KERNEL);
25488c2ecf20Sopenharmony_ci	if (!ar_sdio->irq_data.irq_proc_reg) {
25498c2ecf20Sopenharmony_ci		ret = -ENOMEM;
25508c2ecf20Sopenharmony_ci		goto err_core_destroy;
25518c2ecf20Sopenharmony_ci	}
25528c2ecf20Sopenharmony_ci
25538c2ecf20Sopenharmony_ci	ar_sdio->vsg_buffer = devm_kmalloc(ar->dev, ATH10K_SDIO_VSG_BUF_SIZE, GFP_KERNEL);
25548c2ecf20Sopenharmony_ci	if (!ar_sdio->vsg_buffer) {
25558c2ecf20Sopenharmony_ci		ret = -ENOMEM;
25568c2ecf20Sopenharmony_ci		goto err_core_destroy;
25578c2ecf20Sopenharmony_ci	}
25588c2ecf20Sopenharmony_ci
25598c2ecf20Sopenharmony_ci	ar_sdio->irq_data.irq_en_reg =
25608c2ecf20Sopenharmony_ci		devm_kzalloc(ar->dev, sizeof(struct ath10k_sdio_irq_enable_regs),
25618c2ecf20Sopenharmony_ci			     GFP_KERNEL);
25628c2ecf20Sopenharmony_ci	if (!ar_sdio->irq_data.irq_en_reg) {
25638c2ecf20Sopenharmony_ci		ret = -ENOMEM;
25648c2ecf20Sopenharmony_ci		goto err_core_destroy;
25658c2ecf20Sopenharmony_ci	}
25668c2ecf20Sopenharmony_ci
25678c2ecf20Sopenharmony_ci	ar_sdio->bmi_buf = devm_kzalloc(ar->dev, BMI_MAX_LARGE_CMDBUF_SIZE, GFP_KERNEL);
25688c2ecf20Sopenharmony_ci	if (!ar_sdio->bmi_buf) {
25698c2ecf20Sopenharmony_ci		ret = -ENOMEM;
25708c2ecf20Sopenharmony_ci		goto err_core_destroy;
25718c2ecf20Sopenharmony_ci	}
25728c2ecf20Sopenharmony_ci
25738c2ecf20Sopenharmony_ci	ar_sdio->func = func;
25748c2ecf20Sopenharmony_ci	sdio_set_drvdata(func, ar_sdio);
25758c2ecf20Sopenharmony_ci
25768c2ecf20Sopenharmony_ci	ar_sdio->is_disabled = true;
25778c2ecf20Sopenharmony_ci	ar_sdio->ar = ar;
25788c2ecf20Sopenharmony_ci
25798c2ecf20Sopenharmony_ci	spin_lock_init(&ar_sdio->lock);
25808c2ecf20Sopenharmony_ci	spin_lock_init(&ar_sdio->wr_async_lock);
25818c2ecf20Sopenharmony_ci	mutex_init(&ar_sdio->irq_data.mtx);
25828c2ecf20Sopenharmony_ci
25838c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ar_sdio->bus_req_freeq);
25848c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ar_sdio->wr_asyncq);
25858c2ecf20Sopenharmony_ci
25868c2ecf20Sopenharmony_ci	INIT_WORK(&ar_sdio->wr_async_work, ath10k_sdio_write_async_work);
25878c2ecf20Sopenharmony_ci	ar_sdio->workqueue = create_singlethread_workqueue("ath10k_sdio_wq");
25888c2ecf20Sopenharmony_ci	if (!ar_sdio->workqueue) {
25898c2ecf20Sopenharmony_ci		ret = -ENOMEM;
25908c2ecf20Sopenharmony_ci		goto err_core_destroy;
25918c2ecf20Sopenharmony_ci	}
25928c2ecf20Sopenharmony_ci
25938c2ecf20Sopenharmony_ci	for (i = 0; i < ATH10K_SDIO_BUS_REQUEST_MAX_NUM; i++)
25948c2ecf20Sopenharmony_ci		ath10k_sdio_free_bus_req(ar, &ar_sdio->bus_req[i]);
25958c2ecf20Sopenharmony_ci
25968c2ecf20Sopenharmony_ci	skb_queue_head_init(&ar_sdio->rx_head);
25978c2ecf20Sopenharmony_ci	INIT_WORK(&ar_sdio->async_work_rx, ath10k_rx_indication_async_work);
25988c2ecf20Sopenharmony_ci
25998c2ecf20Sopenharmony_ci	dev_id_base = (id->device & 0x0F00);
26008c2ecf20Sopenharmony_ci	if (dev_id_base != (SDIO_DEVICE_ID_ATHEROS_AR6005 & 0x0F00) &&
26018c2ecf20Sopenharmony_ci	    dev_id_base != (SDIO_DEVICE_ID_ATHEROS_QCA9377 & 0x0F00)) {
26028c2ecf20Sopenharmony_ci		ret = -ENODEV;
26038c2ecf20Sopenharmony_ci		ath10k_err(ar, "unsupported device id %u (0x%x)\n",
26048c2ecf20Sopenharmony_ci			   dev_id_base, id->device);
26058c2ecf20Sopenharmony_ci		goto err_free_wq;
26068c2ecf20Sopenharmony_ci	}
26078c2ecf20Sopenharmony_ci
26088c2ecf20Sopenharmony_ci	ar->dev_id = QCA9377_1_0_DEVICE_ID;
26098c2ecf20Sopenharmony_ci	ar->id.vendor = id->vendor;
26108c2ecf20Sopenharmony_ci	ar->id.device = id->device;
26118c2ecf20Sopenharmony_ci
26128c2ecf20Sopenharmony_ci	ath10k_sdio_set_mbox_info(ar);
26138c2ecf20Sopenharmony_ci
26148c2ecf20Sopenharmony_ci	bus_params.dev_type = ATH10K_DEV_TYPE_HL;
26158c2ecf20Sopenharmony_ci	/* TODO: don't know yet how to get chip_id with SDIO */
26168c2ecf20Sopenharmony_ci	bus_params.chip_id = 0;
26178c2ecf20Sopenharmony_ci	bus_params.hl_msdu_ids = true;
26188c2ecf20Sopenharmony_ci
26198c2ecf20Sopenharmony_ci	ar->hw->max_mtu = ETH_DATA_LEN;
26208c2ecf20Sopenharmony_ci
26218c2ecf20Sopenharmony_ci	ret = ath10k_core_register(ar, &bus_params);
26228c2ecf20Sopenharmony_ci	if (ret) {
26238c2ecf20Sopenharmony_ci		ath10k_err(ar, "failed to register driver core: %d\n", ret);
26248c2ecf20Sopenharmony_ci		goto err_free_wq;
26258c2ecf20Sopenharmony_ci	}
26268c2ecf20Sopenharmony_ci
26278c2ecf20Sopenharmony_ci	timer_setup(&ar_sdio->sleep_timer, ath10k_sdio_sleep_timer_handler, 0);
26288c2ecf20Sopenharmony_ci
26298c2ecf20Sopenharmony_ci	return 0;
26308c2ecf20Sopenharmony_ci
26318c2ecf20Sopenharmony_cierr_free_wq:
26328c2ecf20Sopenharmony_ci	destroy_workqueue(ar_sdio->workqueue);
26338c2ecf20Sopenharmony_cierr_core_destroy:
26348c2ecf20Sopenharmony_ci	ath10k_core_destroy(ar);
26358c2ecf20Sopenharmony_ci
26368c2ecf20Sopenharmony_ci	return ret;
26378c2ecf20Sopenharmony_ci}
26388c2ecf20Sopenharmony_ci
26398c2ecf20Sopenharmony_cistatic void ath10k_sdio_remove(struct sdio_func *func)
26408c2ecf20Sopenharmony_ci{
26418c2ecf20Sopenharmony_ci	struct ath10k_sdio *ar_sdio = sdio_get_drvdata(func);
26428c2ecf20Sopenharmony_ci	struct ath10k *ar = ar_sdio->ar;
26438c2ecf20Sopenharmony_ci
26448c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_BOOT,
26458c2ecf20Sopenharmony_ci		   "sdio removed func %d vendor 0x%x device 0x%x\n",
26468c2ecf20Sopenharmony_ci		   func->num, func->vendor, func->device);
26478c2ecf20Sopenharmony_ci
26488c2ecf20Sopenharmony_ci	ath10k_core_unregister(ar);
26498c2ecf20Sopenharmony_ci
26508c2ecf20Sopenharmony_ci	netif_napi_del(&ar->napi);
26518c2ecf20Sopenharmony_ci
26528c2ecf20Sopenharmony_ci	ath10k_core_destroy(ar);
26538c2ecf20Sopenharmony_ci
26548c2ecf20Sopenharmony_ci	flush_workqueue(ar_sdio->workqueue);
26558c2ecf20Sopenharmony_ci	destroy_workqueue(ar_sdio->workqueue);
26568c2ecf20Sopenharmony_ci}
26578c2ecf20Sopenharmony_ci
26588c2ecf20Sopenharmony_cistatic const struct sdio_device_id ath10k_sdio_devices[] = {
26598c2ecf20Sopenharmony_ci	{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6005)},
26608c2ecf20Sopenharmony_ci	{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_QCA9377)},
26618c2ecf20Sopenharmony_ci	{},
26628c2ecf20Sopenharmony_ci};
26638c2ecf20Sopenharmony_ci
26648c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(sdio, ath10k_sdio_devices);
26658c2ecf20Sopenharmony_ci
26668c2ecf20Sopenharmony_cistatic struct sdio_driver ath10k_sdio_driver = {
26678c2ecf20Sopenharmony_ci	.name = "ath10k_sdio",
26688c2ecf20Sopenharmony_ci	.id_table = ath10k_sdio_devices,
26698c2ecf20Sopenharmony_ci	.probe = ath10k_sdio_probe,
26708c2ecf20Sopenharmony_ci	.remove = ath10k_sdio_remove,
26718c2ecf20Sopenharmony_ci	.drv = {
26728c2ecf20Sopenharmony_ci		.owner = THIS_MODULE,
26738c2ecf20Sopenharmony_ci		.pm = ATH10K_SDIO_PM_OPS,
26748c2ecf20Sopenharmony_ci	},
26758c2ecf20Sopenharmony_ci};
26768c2ecf20Sopenharmony_ci
26778c2ecf20Sopenharmony_cistatic int __init ath10k_sdio_init(void)
26788c2ecf20Sopenharmony_ci{
26798c2ecf20Sopenharmony_ci	int ret;
26808c2ecf20Sopenharmony_ci
26818c2ecf20Sopenharmony_ci	ret = sdio_register_driver(&ath10k_sdio_driver);
26828c2ecf20Sopenharmony_ci	if (ret)
26838c2ecf20Sopenharmony_ci		pr_err("sdio driver registration failed: %d\n", ret);
26848c2ecf20Sopenharmony_ci
26858c2ecf20Sopenharmony_ci	return ret;
26868c2ecf20Sopenharmony_ci}
26878c2ecf20Sopenharmony_ci
26888c2ecf20Sopenharmony_cistatic void __exit ath10k_sdio_exit(void)
26898c2ecf20Sopenharmony_ci{
26908c2ecf20Sopenharmony_ci	sdio_unregister_driver(&ath10k_sdio_driver);
26918c2ecf20Sopenharmony_ci}
26928c2ecf20Sopenharmony_ci
26938c2ecf20Sopenharmony_cimodule_init(ath10k_sdio_init);
26948c2ecf20Sopenharmony_cimodule_exit(ath10k_sdio_exit);
26958c2ecf20Sopenharmony_ci
26968c2ecf20Sopenharmony_ciMODULE_AUTHOR("Qualcomm Atheros");
26978c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver support for Qualcomm Atheros 802.11ac WLAN SDIO devices");
26988c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
2699