162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2004-2011 Atheros Communications Inc.
462306a36Sopenharmony_ci * Copyright (c) 2011-2012,2017 Qualcomm Atheros, Inc.
562306a36Sopenharmony_ci * Copyright (c) 2016-2017 Erik Stromdahl <erik.stromdahl@gmail.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/mmc/card.h>
1062306a36Sopenharmony_ci#include <linux/mmc/mmc.h>
1162306a36Sopenharmony_ci#include <linux/mmc/host.h>
1262306a36Sopenharmony_ci#include <linux/mmc/sdio_func.h>
1362306a36Sopenharmony_ci#include <linux/mmc/sdio_ids.h>
1462306a36Sopenharmony_ci#include <linux/mmc/sdio.h>
1562306a36Sopenharmony_ci#include <linux/mmc/sd.h>
1662306a36Sopenharmony_ci#include <linux/bitfield.h>
1762306a36Sopenharmony_ci#include "core.h"
1862306a36Sopenharmony_ci#include "bmi.h"
1962306a36Sopenharmony_ci#include "debug.h"
2062306a36Sopenharmony_ci#include "hif.h"
2162306a36Sopenharmony_ci#include "htc.h"
2262306a36Sopenharmony_ci#include "mac.h"
2362306a36Sopenharmony_ci#include "targaddrs.h"
2462306a36Sopenharmony_ci#include "trace.h"
2562306a36Sopenharmony_ci#include "sdio.h"
2662306a36Sopenharmony_ci#include "coredump.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_civoid ath10k_sdio_fw_crashed_dump(struct ath10k *ar);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define ATH10K_SDIO_VSG_BUF_SIZE	(64 * 1024)
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/* inlined helper functions */
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic inline int ath10k_sdio_calc_txrx_padded_len(struct ath10k_sdio *ar_sdio,
3562306a36Sopenharmony_ci						   size_t len)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	return __ALIGN_MASK((len), ar_sdio->mbox_info.block_mask);
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic inline enum ath10k_htc_ep_id pipe_id_to_eid(u8 pipe_id)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	return (enum ath10k_htc_ep_id)pipe_id;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic inline void ath10k_sdio_mbox_free_rx_pkt(struct ath10k_sdio_rx_data *pkt)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	dev_kfree_skb(pkt->skb);
4862306a36Sopenharmony_ci	pkt->skb = NULL;
4962306a36Sopenharmony_ci	pkt->alloc_len = 0;
5062306a36Sopenharmony_ci	pkt->act_len = 0;
5162306a36Sopenharmony_ci	pkt->trailer_only = false;
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic inline int ath10k_sdio_mbox_alloc_rx_pkt(struct ath10k_sdio_rx_data *pkt,
5562306a36Sopenharmony_ci						size_t act_len, size_t full_len,
5662306a36Sopenharmony_ci						bool part_of_bundle,
5762306a36Sopenharmony_ci						bool last_in_bundle)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	pkt->skb = dev_alloc_skb(full_len);
6062306a36Sopenharmony_ci	if (!pkt->skb)
6162306a36Sopenharmony_ci		return -ENOMEM;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	pkt->act_len = act_len;
6462306a36Sopenharmony_ci	pkt->alloc_len = full_len;
6562306a36Sopenharmony_ci	pkt->part_of_bundle = part_of_bundle;
6662306a36Sopenharmony_ci	pkt->last_in_bundle = last_in_bundle;
6762306a36Sopenharmony_ci	pkt->trailer_only = false;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	return 0;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic inline bool is_trailer_only_msg(struct ath10k_sdio_rx_data *pkt)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	bool trailer_only = false;
7562306a36Sopenharmony_ci	struct ath10k_htc_hdr *htc_hdr =
7662306a36Sopenharmony_ci		(struct ath10k_htc_hdr *)pkt->skb->data;
7762306a36Sopenharmony_ci	u16 len = __le16_to_cpu(htc_hdr->len);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (len == htc_hdr->trailer_len)
8062306a36Sopenharmony_ci		trailer_only = true;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	return trailer_only;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/* sdio/mmc functions */
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic inline void ath10k_sdio_set_cmd52_arg(u32 *arg, u8 write, u8 raw,
8862306a36Sopenharmony_ci					     unsigned int address,
8962306a36Sopenharmony_ci					     unsigned char val)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	*arg = FIELD_PREP(BIT(31), write) |
9262306a36Sopenharmony_ci	       FIELD_PREP(BIT(27), raw) |
9362306a36Sopenharmony_ci	       FIELD_PREP(BIT(26), 1) |
9462306a36Sopenharmony_ci	       FIELD_PREP(GENMASK(25, 9), address) |
9562306a36Sopenharmony_ci	       FIELD_PREP(BIT(8), 1) |
9662306a36Sopenharmony_ci	       FIELD_PREP(GENMASK(7, 0), val);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic int ath10k_sdio_func0_cmd52_wr_byte(struct mmc_card *card,
10062306a36Sopenharmony_ci					   unsigned int address,
10162306a36Sopenharmony_ci					   unsigned char byte)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct mmc_command io_cmd;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	memset(&io_cmd, 0, sizeof(io_cmd));
10662306a36Sopenharmony_ci	ath10k_sdio_set_cmd52_arg(&io_cmd.arg, 1, 0, address, byte);
10762306a36Sopenharmony_ci	io_cmd.opcode = SD_IO_RW_DIRECT;
10862306a36Sopenharmony_ci	io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return mmc_wait_for_cmd(card->host, &io_cmd, 0);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic int ath10k_sdio_func0_cmd52_rd_byte(struct mmc_card *card,
11462306a36Sopenharmony_ci					   unsigned int address,
11562306a36Sopenharmony_ci					   unsigned char *byte)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	struct mmc_command io_cmd;
11862306a36Sopenharmony_ci	int ret;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	memset(&io_cmd, 0, sizeof(io_cmd));
12162306a36Sopenharmony_ci	ath10k_sdio_set_cmd52_arg(&io_cmd.arg, 0, 0, address, 0);
12262306a36Sopenharmony_ci	io_cmd.opcode = SD_IO_RW_DIRECT;
12362306a36Sopenharmony_ci	io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	ret = mmc_wait_for_cmd(card->host, &io_cmd, 0);
12662306a36Sopenharmony_ci	if (!ret)
12762306a36Sopenharmony_ci		*byte = io_cmd.resp[0];
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return ret;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic int ath10k_sdio_config(struct ath10k *ar)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
13562306a36Sopenharmony_ci	struct sdio_func *func = ar_sdio->func;
13662306a36Sopenharmony_ci	unsigned char byte, asyncintdelay = 2;
13762306a36Sopenharmony_ci	int ret;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_BOOT, "sdio configuration\n");
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	sdio_claim_host(func);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	byte = 0;
14462306a36Sopenharmony_ci	ret = ath10k_sdio_func0_cmd52_rd_byte(func->card,
14562306a36Sopenharmony_ci					      SDIO_CCCR_DRIVE_STRENGTH,
14662306a36Sopenharmony_ci					      &byte);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	byte &= ~ATH10K_SDIO_DRIVE_DTSX_MASK;
14962306a36Sopenharmony_ci	byte |= FIELD_PREP(ATH10K_SDIO_DRIVE_DTSX_MASK,
15062306a36Sopenharmony_ci			   ATH10K_SDIO_DRIVE_DTSX_TYPE_D);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	ret = ath10k_sdio_func0_cmd52_wr_byte(func->card,
15362306a36Sopenharmony_ci					      SDIO_CCCR_DRIVE_STRENGTH,
15462306a36Sopenharmony_ci					      byte);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	byte = 0;
15762306a36Sopenharmony_ci	ret = ath10k_sdio_func0_cmd52_rd_byte(
15862306a36Sopenharmony_ci		func->card,
15962306a36Sopenharmony_ci		CCCR_SDIO_DRIVER_STRENGTH_ENABLE_ADDR,
16062306a36Sopenharmony_ci		&byte);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	byte |= (CCCR_SDIO_DRIVER_STRENGTH_ENABLE_A |
16362306a36Sopenharmony_ci		 CCCR_SDIO_DRIVER_STRENGTH_ENABLE_C |
16462306a36Sopenharmony_ci		 CCCR_SDIO_DRIVER_STRENGTH_ENABLE_D);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	ret = ath10k_sdio_func0_cmd52_wr_byte(func->card,
16762306a36Sopenharmony_ci					      CCCR_SDIO_DRIVER_STRENGTH_ENABLE_ADDR,
16862306a36Sopenharmony_ci					      byte);
16962306a36Sopenharmony_ci	if (ret) {
17062306a36Sopenharmony_ci		ath10k_warn(ar, "failed to enable driver strength: %d\n", ret);
17162306a36Sopenharmony_ci		goto out;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	byte = 0;
17562306a36Sopenharmony_ci	ret = ath10k_sdio_func0_cmd52_rd_byte(func->card,
17662306a36Sopenharmony_ci					      CCCR_SDIO_IRQ_MODE_REG_SDIO3,
17762306a36Sopenharmony_ci					      &byte);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	byte |= SDIO_IRQ_MODE_ASYNC_4BIT_IRQ_SDIO3;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	ret = ath10k_sdio_func0_cmd52_wr_byte(func->card,
18262306a36Sopenharmony_ci					      CCCR_SDIO_IRQ_MODE_REG_SDIO3,
18362306a36Sopenharmony_ci					      byte);
18462306a36Sopenharmony_ci	if (ret) {
18562306a36Sopenharmony_ci		ath10k_warn(ar, "failed to enable 4-bit async irq mode: %d\n",
18662306a36Sopenharmony_ci			    ret);
18762306a36Sopenharmony_ci		goto out;
18862306a36Sopenharmony_ci	}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	byte = 0;
19162306a36Sopenharmony_ci	ret = ath10k_sdio_func0_cmd52_rd_byte(func->card,
19262306a36Sopenharmony_ci					      CCCR_SDIO_ASYNC_INT_DELAY_ADDRESS,
19362306a36Sopenharmony_ci					      &byte);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	byte &= ~CCCR_SDIO_ASYNC_INT_DELAY_MASK;
19662306a36Sopenharmony_ci	byte |= FIELD_PREP(CCCR_SDIO_ASYNC_INT_DELAY_MASK, asyncintdelay);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	ret = ath10k_sdio_func0_cmd52_wr_byte(func->card,
19962306a36Sopenharmony_ci					      CCCR_SDIO_ASYNC_INT_DELAY_ADDRESS,
20062306a36Sopenharmony_ci					      byte);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/* give us some time to enable, in ms */
20362306a36Sopenharmony_ci	func->enable_timeout = 100;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	ret = sdio_set_block_size(func, ar_sdio->mbox_info.block_size);
20662306a36Sopenharmony_ci	if (ret) {
20762306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set sdio block size to %d: %d\n",
20862306a36Sopenharmony_ci			    ar_sdio->mbox_info.block_size, ret);
20962306a36Sopenharmony_ci		goto out;
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ciout:
21362306a36Sopenharmony_ci	sdio_release_host(func);
21462306a36Sopenharmony_ci	return ret;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic int ath10k_sdio_write32(struct ath10k *ar, u32 addr, u32 val)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
22062306a36Sopenharmony_ci	struct sdio_func *func = ar_sdio->func;
22162306a36Sopenharmony_ci	int ret;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	sdio_claim_host(func);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	sdio_writel(func, val, addr, &ret);
22662306a36Sopenharmony_ci	if (ret) {
22762306a36Sopenharmony_ci		ath10k_warn(ar, "failed to write 0x%x to address 0x%x: %d\n",
22862306a36Sopenharmony_ci			    val, addr, ret);
22962306a36Sopenharmony_ci		goto out;
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio write32 addr 0x%x val 0x%x\n",
23362306a36Sopenharmony_ci		   addr, val);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ciout:
23662306a36Sopenharmony_ci	sdio_release_host(func);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return ret;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic int ath10k_sdio_writesb32(struct ath10k *ar, u32 addr, u32 val)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
24462306a36Sopenharmony_ci	struct sdio_func *func = ar_sdio->func;
24562306a36Sopenharmony_ci	__le32 *buf;
24662306a36Sopenharmony_ci	int ret;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
24962306a36Sopenharmony_ci	if (!buf)
25062306a36Sopenharmony_ci		return -ENOMEM;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	*buf = cpu_to_le32(val);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	sdio_claim_host(func);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	ret = sdio_writesb(func, addr, buf, sizeof(*buf));
25762306a36Sopenharmony_ci	if (ret) {
25862306a36Sopenharmony_ci		ath10k_warn(ar, "failed to write value 0x%x to fixed sb address 0x%x: %d\n",
25962306a36Sopenharmony_ci			    val, addr, ret);
26062306a36Sopenharmony_ci		goto out;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio writesb32 addr 0x%x val 0x%x\n",
26462306a36Sopenharmony_ci		   addr, val);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ciout:
26762306a36Sopenharmony_ci	sdio_release_host(func);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	kfree(buf);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	return ret;
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic int ath10k_sdio_read32(struct ath10k *ar, u32 addr, u32 *val)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
27762306a36Sopenharmony_ci	struct sdio_func *func = ar_sdio->func;
27862306a36Sopenharmony_ci	int ret;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	sdio_claim_host(func);
28162306a36Sopenharmony_ci	*val = sdio_readl(func, addr, &ret);
28262306a36Sopenharmony_ci	if (ret) {
28362306a36Sopenharmony_ci		ath10k_warn(ar, "failed to read from address 0x%x: %d\n",
28462306a36Sopenharmony_ci			    addr, ret);
28562306a36Sopenharmony_ci		goto out;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio read32 addr 0x%x val 0x%x\n",
28962306a36Sopenharmony_ci		   addr, *val);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ciout:
29262306a36Sopenharmony_ci	sdio_release_host(func);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	return ret;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic int ath10k_sdio_read(struct ath10k *ar, u32 addr, void *buf, size_t len)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
30062306a36Sopenharmony_ci	struct sdio_func *func = ar_sdio->func;
30162306a36Sopenharmony_ci	int ret;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	sdio_claim_host(func);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	ret = sdio_memcpy_fromio(func, buf, addr, len);
30662306a36Sopenharmony_ci	if (ret) {
30762306a36Sopenharmony_ci		ath10k_warn(ar, "failed to read from address 0x%x: %d\n",
30862306a36Sopenharmony_ci			    addr, ret);
30962306a36Sopenharmony_ci		goto out;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio read addr 0x%x buf 0x%p len %zu\n",
31362306a36Sopenharmony_ci		   addr, buf, len);
31462306a36Sopenharmony_ci	ath10k_dbg_dump(ar, ATH10K_DBG_SDIO_DUMP, NULL, "sdio read ", buf, len);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ciout:
31762306a36Sopenharmony_ci	sdio_release_host(func);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	return ret;
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic int ath10k_sdio_write(struct ath10k *ar, u32 addr, const void *buf, size_t len)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
32562306a36Sopenharmony_ci	struct sdio_func *func = ar_sdio->func;
32662306a36Sopenharmony_ci	int ret;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	sdio_claim_host(func);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/* For some reason toio() doesn't have const for the buffer, need
33162306a36Sopenharmony_ci	 * an ugly hack to workaround that.
33262306a36Sopenharmony_ci	 */
33362306a36Sopenharmony_ci	ret = sdio_memcpy_toio(func, addr, (void *)buf, len);
33462306a36Sopenharmony_ci	if (ret) {
33562306a36Sopenharmony_ci		ath10k_warn(ar, "failed to write to address 0x%x: %d\n",
33662306a36Sopenharmony_ci			    addr, ret);
33762306a36Sopenharmony_ci		goto out;
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio write addr 0x%x buf 0x%p len %zu\n",
34162306a36Sopenharmony_ci		   addr, buf, len);
34262306a36Sopenharmony_ci	ath10k_dbg_dump(ar, ATH10K_DBG_SDIO_DUMP, NULL, "sdio write ", buf, len);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ciout:
34562306a36Sopenharmony_ci	sdio_release_host(func);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	return ret;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic int ath10k_sdio_readsb(struct ath10k *ar, u32 addr, void *buf, size_t len)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
35362306a36Sopenharmony_ci	struct sdio_func *func = ar_sdio->func;
35462306a36Sopenharmony_ci	int ret;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	sdio_claim_host(func);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	len = round_down(len, ar_sdio->mbox_info.block_size);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	ret = sdio_readsb(func, buf, addr, len);
36162306a36Sopenharmony_ci	if (ret) {
36262306a36Sopenharmony_ci		ath10k_warn(ar, "failed to read from fixed (sb) address 0x%x: %d\n",
36362306a36Sopenharmony_ci			    addr, ret);
36462306a36Sopenharmony_ci		goto out;
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio readsb addr 0x%x buf 0x%p len %zu\n",
36862306a36Sopenharmony_ci		   addr, buf, len);
36962306a36Sopenharmony_ci	ath10k_dbg_dump(ar, ATH10K_DBG_SDIO_DUMP, NULL, "sdio readsb ", buf, len);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ciout:
37262306a36Sopenharmony_ci	sdio_release_host(func);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	return ret;
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci/* HIF mbox functions */
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic int ath10k_sdio_mbox_rx_process_packet(struct ath10k *ar,
38062306a36Sopenharmony_ci					      struct ath10k_sdio_rx_data *pkt,
38162306a36Sopenharmony_ci					      u32 *lookaheads,
38262306a36Sopenharmony_ci					      int *n_lookaheads)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	struct ath10k_htc *htc = &ar->htc;
38562306a36Sopenharmony_ci	struct sk_buff *skb = pkt->skb;
38662306a36Sopenharmony_ci	struct ath10k_htc_hdr *htc_hdr = (struct ath10k_htc_hdr *)skb->data;
38762306a36Sopenharmony_ci	bool trailer_present = htc_hdr->flags & ATH10K_HTC_FLAG_TRAILER_PRESENT;
38862306a36Sopenharmony_ci	enum ath10k_htc_ep_id eid;
38962306a36Sopenharmony_ci	u8 *trailer;
39062306a36Sopenharmony_ci	int ret;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	if (trailer_present) {
39362306a36Sopenharmony_ci		trailer = skb->data + skb->len - htc_hdr->trailer_len;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci		eid = pipe_id_to_eid(htc_hdr->eid);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci		ret = ath10k_htc_process_trailer(htc,
39862306a36Sopenharmony_ci						 trailer,
39962306a36Sopenharmony_ci						 htc_hdr->trailer_len,
40062306a36Sopenharmony_ci						 eid,
40162306a36Sopenharmony_ci						 lookaheads,
40262306a36Sopenharmony_ci						 n_lookaheads);
40362306a36Sopenharmony_ci		if (ret)
40462306a36Sopenharmony_ci			return ret;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci		if (is_trailer_only_msg(pkt))
40762306a36Sopenharmony_ci			pkt->trailer_only = true;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci		skb_trim(skb, skb->len - htc_hdr->trailer_len);
41062306a36Sopenharmony_ci	}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	skb_pull(skb, sizeof(*htc_hdr));
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	return 0;
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic int ath10k_sdio_mbox_rx_process_packets(struct ath10k *ar,
41862306a36Sopenharmony_ci					       u32 lookaheads[],
41962306a36Sopenharmony_ci					       int *n_lookahead)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
42262306a36Sopenharmony_ci	struct ath10k_htc *htc = &ar->htc;
42362306a36Sopenharmony_ci	struct ath10k_sdio_rx_data *pkt;
42462306a36Sopenharmony_ci	struct ath10k_htc_ep *ep;
42562306a36Sopenharmony_ci	struct ath10k_skb_rxcb *cb;
42662306a36Sopenharmony_ci	enum ath10k_htc_ep_id id;
42762306a36Sopenharmony_ci	int ret, i, *n_lookahead_local;
42862306a36Sopenharmony_ci	u32 *lookaheads_local;
42962306a36Sopenharmony_ci	int lookahead_idx = 0;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	for (i = 0; i < ar_sdio->n_rx_pkts; i++) {
43262306a36Sopenharmony_ci		lookaheads_local = lookaheads;
43362306a36Sopenharmony_ci		n_lookahead_local = n_lookahead;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci		id = ((struct ath10k_htc_hdr *)
43662306a36Sopenharmony_ci		      &lookaheads[lookahead_idx++])->eid;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci		if (id >= ATH10K_HTC_EP_COUNT) {
43962306a36Sopenharmony_ci			ath10k_warn(ar, "invalid endpoint in look-ahead: %d\n",
44062306a36Sopenharmony_ci				    id);
44162306a36Sopenharmony_ci			ret = -ENOMEM;
44262306a36Sopenharmony_ci			goto out;
44362306a36Sopenharmony_ci		}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci		ep = &htc->endpoint[id];
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci		if (ep->service_id == 0) {
44862306a36Sopenharmony_ci			ath10k_warn(ar, "ep %d is not connected\n", id);
44962306a36Sopenharmony_ci			ret = -ENOMEM;
45062306a36Sopenharmony_ci			goto out;
45162306a36Sopenharmony_ci		}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci		pkt = &ar_sdio->rx_pkts[i];
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci		if (pkt->part_of_bundle && !pkt->last_in_bundle) {
45662306a36Sopenharmony_ci			/* Only read lookahead's from RX trailers
45762306a36Sopenharmony_ci			 * for the last packet in a bundle.
45862306a36Sopenharmony_ci			 */
45962306a36Sopenharmony_ci			lookahead_idx--;
46062306a36Sopenharmony_ci			lookaheads_local = NULL;
46162306a36Sopenharmony_ci			n_lookahead_local = NULL;
46262306a36Sopenharmony_ci		}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci		ret = ath10k_sdio_mbox_rx_process_packet(ar,
46562306a36Sopenharmony_ci							 pkt,
46662306a36Sopenharmony_ci							 lookaheads_local,
46762306a36Sopenharmony_ci							 n_lookahead_local);
46862306a36Sopenharmony_ci		if (ret)
46962306a36Sopenharmony_ci			goto out;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci		if (!pkt->trailer_only) {
47262306a36Sopenharmony_ci			cb = ATH10K_SKB_RXCB(pkt->skb);
47362306a36Sopenharmony_ci			cb->eid = id;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci			skb_queue_tail(&ar_sdio->rx_head, pkt->skb);
47662306a36Sopenharmony_ci			queue_work(ar->workqueue_aux,
47762306a36Sopenharmony_ci				   &ar_sdio->async_work_rx);
47862306a36Sopenharmony_ci		} else {
47962306a36Sopenharmony_ci			kfree_skb(pkt->skb);
48062306a36Sopenharmony_ci		}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci		/* The RX complete handler now owns the skb...*/
48362306a36Sopenharmony_ci		pkt->skb = NULL;
48462306a36Sopenharmony_ci		pkt->alloc_len = 0;
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	ret = 0;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ciout:
49062306a36Sopenharmony_ci	/* Free all packets that was not passed on to the RX completion
49162306a36Sopenharmony_ci	 * handler...
49262306a36Sopenharmony_ci	 */
49362306a36Sopenharmony_ci	for (; i < ar_sdio->n_rx_pkts; i++)
49462306a36Sopenharmony_ci		ath10k_sdio_mbox_free_rx_pkt(&ar_sdio->rx_pkts[i]);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	return ret;
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic int ath10k_sdio_mbox_alloc_bundle(struct ath10k *ar,
50062306a36Sopenharmony_ci					 struct ath10k_sdio_rx_data *rx_pkts,
50162306a36Sopenharmony_ci					 struct ath10k_htc_hdr *htc_hdr,
50262306a36Sopenharmony_ci					 size_t full_len, size_t act_len,
50362306a36Sopenharmony_ci					 size_t *bndl_cnt)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	int ret, i;
50662306a36Sopenharmony_ci	u8 max_msgs = ar->htc.max_msgs_per_htc_bundle;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	*bndl_cnt = ath10k_htc_get_bundle_count(max_msgs, htc_hdr->flags);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	if (*bndl_cnt > max_msgs) {
51162306a36Sopenharmony_ci		ath10k_warn(ar,
51262306a36Sopenharmony_ci			    "HTC bundle length %u exceeds maximum %u\n",
51362306a36Sopenharmony_ci			    le16_to_cpu(htc_hdr->len),
51462306a36Sopenharmony_ci			    max_msgs);
51562306a36Sopenharmony_ci		return -ENOMEM;
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	/* Allocate bndl_cnt extra skb's for the bundle.
51962306a36Sopenharmony_ci	 * The package containing the
52062306a36Sopenharmony_ci	 * ATH10K_HTC_FLAG_BUNDLE_MASK flag is not included
52162306a36Sopenharmony_ci	 * in bndl_cnt. The skb for that packet will be
52262306a36Sopenharmony_ci	 * allocated separately.
52362306a36Sopenharmony_ci	 */
52462306a36Sopenharmony_ci	for (i = 0; i < *bndl_cnt; i++) {
52562306a36Sopenharmony_ci		ret = ath10k_sdio_mbox_alloc_rx_pkt(&rx_pkts[i],
52662306a36Sopenharmony_ci						    act_len,
52762306a36Sopenharmony_ci						    full_len,
52862306a36Sopenharmony_ci						    true,
52962306a36Sopenharmony_ci						    false);
53062306a36Sopenharmony_ci		if (ret)
53162306a36Sopenharmony_ci			return ret;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	return 0;
53562306a36Sopenharmony_ci}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_cistatic int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar,
53862306a36Sopenharmony_ci				     u32 lookaheads[], int n_lookaheads)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
54162306a36Sopenharmony_ci	struct ath10k_htc_hdr *htc_hdr;
54262306a36Sopenharmony_ci	size_t full_len, act_len;
54362306a36Sopenharmony_ci	bool last_in_bundle;
54462306a36Sopenharmony_ci	int ret, i;
54562306a36Sopenharmony_ci	int pkt_cnt = 0;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	if (n_lookaheads > ATH10K_SDIO_MAX_RX_MSGS) {
54862306a36Sopenharmony_ci		ath10k_warn(ar, "the total number of pkts to be fetched (%u) exceeds maximum %u\n",
54962306a36Sopenharmony_ci			    n_lookaheads, ATH10K_SDIO_MAX_RX_MSGS);
55062306a36Sopenharmony_ci		ret = -ENOMEM;
55162306a36Sopenharmony_ci		goto err;
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	for (i = 0; i < n_lookaheads; i++) {
55562306a36Sopenharmony_ci		htc_hdr = (struct ath10k_htc_hdr *)&lookaheads[i];
55662306a36Sopenharmony_ci		last_in_bundle = false;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci		if (le16_to_cpu(htc_hdr->len) > ATH10K_HTC_MBOX_MAX_PAYLOAD_LENGTH) {
55962306a36Sopenharmony_ci			ath10k_warn(ar, "payload length %d exceeds max htc length: %zu\n",
56062306a36Sopenharmony_ci				    le16_to_cpu(htc_hdr->len),
56162306a36Sopenharmony_ci				    ATH10K_HTC_MBOX_MAX_PAYLOAD_LENGTH);
56262306a36Sopenharmony_ci			ret = -ENOMEM;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci			ath10k_core_start_recovery(ar);
56562306a36Sopenharmony_ci			ath10k_warn(ar, "exceeds length, start recovery\n");
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci			goto err;
56862306a36Sopenharmony_ci		}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci		act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr);
57162306a36Sopenharmony_ci		full_len = ath10k_sdio_calc_txrx_padded_len(ar_sdio, act_len);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci		if (full_len > ATH10K_SDIO_MAX_BUFFER_SIZE) {
57462306a36Sopenharmony_ci			ath10k_warn(ar, "rx buffer requested with invalid htc_hdr length (%d, 0x%x): %d\n",
57562306a36Sopenharmony_ci				    htc_hdr->eid, htc_hdr->flags,
57662306a36Sopenharmony_ci				    le16_to_cpu(htc_hdr->len));
57762306a36Sopenharmony_ci			ret = -EINVAL;
57862306a36Sopenharmony_ci			goto err;
57962306a36Sopenharmony_ci		}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		if (ath10k_htc_get_bundle_count(
58262306a36Sopenharmony_ci			ar->htc.max_msgs_per_htc_bundle, htc_hdr->flags)) {
58362306a36Sopenharmony_ci			/* HTC header indicates that every packet to follow
58462306a36Sopenharmony_ci			 * has the same padded length so that it can be
58562306a36Sopenharmony_ci			 * optimally fetched as a full bundle.
58662306a36Sopenharmony_ci			 */
58762306a36Sopenharmony_ci			size_t bndl_cnt;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci			ret = ath10k_sdio_mbox_alloc_bundle(ar,
59062306a36Sopenharmony_ci							    &ar_sdio->rx_pkts[pkt_cnt],
59162306a36Sopenharmony_ci							    htc_hdr,
59262306a36Sopenharmony_ci							    full_len,
59362306a36Sopenharmony_ci							    act_len,
59462306a36Sopenharmony_ci							    &bndl_cnt);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci			if (ret) {
59762306a36Sopenharmony_ci				ath10k_warn(ar, "failed to allocate a bundle: %d\n",
59862306a36Sopenharmony_ci					    ret);
59962306a36Sopenharmony_ci				goto err;
60062306a36Sopenharmony_ci			}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci			pkt_cnt += bndl_cnt;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci			/* next buffer will be the last in the bundle */
60562306a36Sopenharmony_ci			last_in_bundle = true;
60662306a36Sopenharmony_ci		}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci		/* Allocate skb for packet. If the packet had the
60962306a36Sopenharmony_ci		 * ATH10K_HTC_FLAG_BUNDLE_MASK flag set, all bundled
61062306a36Sopenharmony_ci		 * packet skb's have been allocated in the previous step.
61162306a36Sopenharmony_ci		 */
61262306a36Sopenharmony_ci		if (htc_hdr->flags & ATH10K_HTC_FLAGS_RECV_1MORE_BLOCK)
61362306a36Sopenharmony_ci			full_len += ATH10K_HIF_MBOX_BLOCK_SIZE;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci		ret = ath10k_sdio_mbox_alloc_rx_pkt(&ar_sdio->rx_pkts[pkt_cnt],
61662306a36Sopenharmony_ci						    act_len,
61762306a36Sopenharmony_ci						    full_len,
61862306a36Sopenharmony_ci						    last_in_bundle,
61962306a36Sopenharmony_ci						    last_in_bundle);
62062306a36Sopenharmony_ci		if (ret) {
62162306a36Sopenharmony_ci			ath10k_warn(ar, "alloc_rx_pkt error %d\n", ret);
62262306a36Sopenharmony_ci			goto err;
62362306a36Sopenharmony_ci		}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci		pkt_cnt++;
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	ar_sdio->n_rx_pkts = pkt_cnt;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	return 0;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cierr:
63362306a36Sopenharmony_ci	for (i = 0; i < ATH10K_SDIO_MAX_RX_MSGS; i++) {
63462306a36Sopenharmony_ci		if (!ar_sdio->rx_pkts[i].alloc_len)
63562306a36Sopenharmony_ci			break;
63662306a36Sopenharmony_ci		ath10k_sdio_mbox_free_rx_pkt(&ar_sdio->rx_pkts[i]);
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	return ret;
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cistatic int ath10k_sdio_mbox_rx_fetch(struct ath10k *ar)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
64562306a36Sopenharmony_ci	struct ath10k_sdio_rx_data *pkt = &ar_sdio->rx_pkts[0];
64662306a36Sopenharmony_ci	struct sk_buff *skb = pkt->skb;
64762306a36Sopenharmony_ci	struct ath10k_htc_hdr *htc_hdr;
64862306a36Sopenharmony_ci	int ret;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr,
65162306a36Sopenharmony_ci				 skb->data, pkt->alloc_len);
65262306a36Sopenharmony_ci	if (ret)
65362306a36Sopenharmony_ci		goto err;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	htc_hdr = (struct ath10k_htc_hdr *)skb->data;
65662306a36Sopenharmony_ci	pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	if (pkt->act_len > pkt->alloc_len) {
65962306a36Sopenharmony_ci		ret = -EINVAL;
66062306a36Sopenharmony_ci		goto err;
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	skb_put(skb, pkt->act_len);
66462306a36Sopenharmony_ci	return 0;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_cierr:
66762306a36Sopenharmony_ci	ar_sdio->n_rx_pkts = 0;
66862306a36Sopenharmony_ci	ath10k_sdio_mbox_free_rx_pkt(pkt);
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	return ret;
67162306a36Sopenharmony_ci}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_cistatic int ath10k_sdio_mbox_rx_fetch_bundle(struct ath10k *ar)
67462306a36Sopenharmony_ci{
67562306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
67662306a36Sopenharmony_ci	struct ath10k_sdio_rx_data *pkt;
67762306a36Sopenharmony_ci	struct ath10k_htc_hdr *htc_hdr;
67862306a36Sopenharmony_ci	int ret, i;
67962306a36Sopenharmony_ci	u32 pkt_offset, virt_pkt_len;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	virt_pkt_len = 0;
68262306a36Sopenharmony_ci	for (i = 0; i < ar_sdio->n_rx_pkts; i++)
68362306a36Sopenharmony_ci		virt_pkt_len += ar_sdio->rx_pkts[i].alloc_len;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	if (virt_pkt_len > ATH10K_SDIO_VSG_BUF_SIZE) {
68662306a36Sopenharmony_ci		ath10k_warn(ar, "sdio vsg buffer size limit: %d\n", virt_pkt_len);
68762306a36Sopenharmony_ci		ret = -E2BIG;
68862306a36Sopenharmony_ci		goto err;
68962306a36Sopenharmony_ci	}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr,
69262306a36Sopenharmony_ci				 ar_sdio->vsg_buffer, virt_pkt_len);
69362306a36Sopenharmony_ci	if (ret) {
69462306a36Sopenharmony_ci		ath10k_warn(ar, "failed to read bundle packets: %d", ret);
69562306a36Sopenharmony_ci		goto err;
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	pkt_offset = 0;
69962306a36Sopenharmony_ci	for (i = 0; i < ar_sdio->n_rx_pkts; i++) {
70062306a36Sopenharmony_ci		pkt = &ar_sdio->rx_pkts[i];
70162306a36Sopenharmony_ci		htc_hdr = (struct ath10k_htc_hdr *)(ar_sdio->vsg_buffer + pkt_offset);
70262306a36Sopenharmony_ci		pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci		if (pkt->act_len > pkt->alloc_len) {
70562306a36Sopenharmony_ci			ret = -EINVAL;
70662306a36Sopenharmony_ci			goto err;
70762306a36Sopenharmony_ci		}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci		skb_put_data(pkt->skb, htc_hdr, pkt->act_len);
71062306a36Sopenharmony_ci		pkt_offset += pkt->alloc_len;
71162306a36Sopenharmony_ci	}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	return 0;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cierr:
71662306a36Sopenharmony_ci	/* Free all packets that was not successfully fetched. */
71762306a36Sopenharmony_ci	for (i = 0; i < ar_sdio->n_rx_pkts; i++)
71862306a36Sopenharmony_ci		ath10k_sdio_mbox_free_rx_pkt(&ar_sdio->rx_pkts[i]);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	ar_sdio->n_rx_pkts = 0;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	return ret;
72362306a36Sopenharmony_ci}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci/* This is the timeout for mailbox processing done in the sdio irq
72662306a36Sopenharmony_ci * handler. The timeout is deliberately set quite high since SDIO dump logs
72762306a36Sopenharmony_ci * over serial port can/will add a substantial overhead to the processing
72862306a36Sopenharmony_ci * (if enabled).
72962306a36Sopenharmony_ci */
73062306a36Sopenharmony_ci#define SDIO_MBOX_PROCESSING_TIMEOUT_HZ (20 * HZ)
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_cistatic int ath10k_sdio_mbox_rxmsg_pending_handler(struct ath10k *ar,
73362306a36Sopenharmony_ci						  u32 msg_lookahead, bool *done)
73462306a36Sopenharmony_ci{
73562306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
73662306a36Sopenharmony_ci	u32 lookaheads[ATH10K_SDIO_MAX_RX_MSGS];
73762306a36Sopenharmony_ci	int n_lookaheads = 1;
73862306a36Sopenharmony_ci	unsigned long timeout;
73962306a36Sopenharmony_ci	int ret;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	*done = true;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	/* Copy the lookahead obtained from the HTC register table into our
74462306a36Sopenharmony_ci	 * temp array as a start value.
74562306a36Sopenharmony_ci	 */
74662306a36Sopenharmony_ci	lookaheads[0] = msg_lookahead;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	timeout = jiffies + SDIO_MBOX_PROCESSING_TIMEOUT_HZ;
74962306a36Sopenharmony_ci	do {
75062306a36Sopenharmony_ci		/* Try to allocate as many HTC RX packets indicated by
75162306a36Sopenharmony_ci		 * n_lookaheads.
75262306a36Sopenharmony_ci		 */
75362306a36Sopenharmony_ci		ret = ath10k_sdio_mbox_rx_alloc(ar, lookaheads,
75462306a36Sopenharmony_ci						n_lookaheads);
75562306a36Sopenharmony_ci		if (ret)
75662306a36Sopenharmony_ci			break;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci		if (ar_sdio->n_rx_pkts >= 2)
75962306a36Sopenharmony_ci			/* A recv bundle was detected, force IRQ status
76062306a36Sopenharmony_ci			 * re-check again.
76162306a36Sopenharmony_ci			 */
76262306a36Sopenharmony_ci			*done = false;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci		if (ar_sdio->n_rx_pkts > 1)
76562306a36Sopenharmony_ci			ret = ath10k_sdio_mbox_rx_fetch_bundle(ar);
76662306a36Sopenharmony_ci		else
76762306a36Sopenharmony_ci			ret = ath10k_sdio_mbox_rx_fetch(ar);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci		/* Process fetched packets. This will potentially update
77062306a36Sopenharmony_ci		 * n_lookaheads depending on if the packets contain lookahead
77162306a36Sopenharmony_ci		 * reports.
77262306a36Sopenharmony_ci		 */
77362306a36Sopenharmony_ci		n_lookaheads = 0;
77462306a36Sopenharmony_ci		ret = ath10k_sdio_mbox_rx_process_packets(ar,
77562306a36Sopenharmony_ci							  lookaheads,
77662306a36Sopenharmony_ci							  &n_lookaheads);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci		if (!n_lookaheads || ret)
77962306a36Sopenharmony_ci			break;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci		/* For SYNCH processing, if we get here, we are running
78262306a36Sopenharmony_ci		 * through the loop again due to updated lookaheads. Set
78362306a36Sopenharmony_ci		 * flag that we should re-check IRQ status registers again
78462306a36Sopenharmony_ci		 * before leaving IRQ processing, this can net better
78562306a36Sopenharmony_ci		 * performance in high throughput situations.
78662306a36Sopenharmony_ci		 */
78762306a36Sopenharmony_ci		*done = false;
78862306a36Sopenharmony_ci	} while (time_before(jiffies, timeout));
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	if (ret && (ret != -ECANCELED))
79162306a36Sopenharmony_ci		ath10k_warn(ar, "failed to get pending recv messages: %d\n",
79262306a36Sopenharmony_ci			    ret);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	return ret;
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_cistatic int ath10k_sdio_mbox_proc_dbg_intr(struct ath10k *ar)
79862306a36Sopenharmony_ci{
79962306a36Sopenharmony_ci	u32 val;
80062306a36Sopenharmony_ci	int ret;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	/* TODO: Add firmware crash handling */
80362306a36Sopenharmony_ci	ath10k_warn(ar, "firmware crashed\n");
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	/* read counter to clear the interrupt, the debug error interrupt is
80662306a36Sopenharmony_ci	 * counter 0.
80762306a36Sopenharmony_ci	 */
80862306a36Sopenharmony_ci	ret = ath10k_sdio_read32(ar, MBOX_COUNT_DEC_ADDRESS, &val);
80962306a36Sopenharmony_ci	if (ret)
81062306a36Sopenharmony_ci		ath10k_warn(ar, "failed to clear debug interrupt: %d\n", ret);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	return ret;
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_cistatic int ath10k_sdio_mbox_proc_counter_intr(struct ath10k *ar)
81662306a36Sopenharmony_ci{
81762306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
81862306a36Sopenharmony_ci	struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
81962306a36Sopenharmony_ci	u8 counter_int_status;
82062306a36Sopenharmony_ci	int ret;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	mutex_lock(&irq_data->mtx);
82362306a36Sopenharmony_ci	counter_int_status = irq_data->irq_proc_reg->counter_int_status &
82462306a36Sopenharmony_ci			     irq_data->irq_en_reg->cntr_int_status_en;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	/* NOTE: other modules like GMBOX may use the counter interrupt for
82762306a36Sopenharmony_ci	 * credit flow control on other counters, we only need to check for
82862306a36Sopenharmony_ci	 * the debug assertion counter interrupt.
82962306a36Sopenharmony_ci	 */
83062306a36Sopenharmony_ci	if (counter_int_status & ATH10K_SDIO_TARGET_DEBUG_INTR_MASK)
83162306a36Sopenharmony_ci		ret = ath10k_sdio_mbox_proc_dbg_intr(ar);
83262306a36Sopenharmony_ci	else
83362306a36Sopenharmony_ci		ret = 0;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	mutex_unlock(&irq_data->mtx);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	return ret;
83862306a36Sopenharmony_ci}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_cistatic int ath10k_sdio_mbox_proc_err_intr(struct ath10k *ar)
84162306a36Sopenharmony_ci{
84262306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
84362306a36Sopenharmony_ci	struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
84462306a36Sopenharmony_ci	u8 error_int_status;
84562306a36Sopenharmony_ci	int ret;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio error interrupt\n");
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	error_int_status = irq_data->irq_proc_reg->error_int_status & 0x0F;
85062306a36Sopenharmony_ci	if (!error_int_status) {
85162306a36Sopenharmony_ci		ath10k_warn(ar, "invalid error interrupt status: 0x%x\n",
85262306a36Sopenharmony_ci			    error_int_status);
85362306a36Sopenharmony_ci		return -EIO;
85462306a36Sopenharmony_ci	}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO,
85762306a36Sopenharmony_ci		   "sdio error_int_status 0x%x\n", error_int_status);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	if (FIELD_GET(MBOX_ERROR_INT_STATUS_WAKEUP_MASK,
86062306a36Sopenharmony_ci		      error_int_status))
86162306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio interrupt error wakeup\n");
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	if (FIELD_GET(MBOX_ERROR_INT_STATUS_RX_UNDERFLOW_MASK,
86462306a36Sopenharmony_ci		      error_int_status))
86562306a36Sopenharmony_ci		ath10k_warn(ar, "rx underflow interrupt error\n");
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	if (FIELD_GET(MBOX_ERROR_INT_STATUS_TX_OVERFLOW_MASK,
86862306a36Sopenharmony_ci		      error_int_status))
86962306a36Sopenharmony_ci		ath10k_warn(ar, "tx overflow interrupt error\n");
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	/* Clear the interrupt */
87262306a36Sopenharmony_ci	irq_data->irq_proc_reg->error_int_status &= ~error_int_status;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	/* set W1C value to clear the interrupt, this hits the register first */
87562306a36Sopenharmony_ci	ret = ath10k_sdio_writesb32(ar, MBOX_ERROR_INT_STATUS_ADDRESS,
87662306a36Sopenharmony_ci				    error_int_status);
87762306a36Sopenharmony_ci	if (ret) {
87862306a36Sopenharmony_ci		ath10k_warn(ar, "unable to write to error int status address: %d\n",
87962306a36Sopenharmony_ci			    ret);
88062306a36Sopenharmony_ci		return ret;
88162306a36Sopenharmony_ci	}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	return 0;
88462306a36Sopenharmony_ci}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_cistatic int ath10k_sdio_mbox_proc_cpu_intr(struct ath10k *ar)
88762306a36Sopenharmony_ci{
88862306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
88962306a36Sopenharmony_ci	struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
89062306a36Sopenharmony_ci	u8 cpu_int_status;
89162306a36Sopenharmony_ci	int ret;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	mutex_lock(&irq_data->mtx);
89462306a36Sopenharmony_ci	cpu_int_status = irq_data->irq_proc_reg->cpu_int_status &
89562306a36Sopenharmony_ci			 irq_data->irq_en_reg->cpu_int_status_en;
89662306a36Sopenharmony_ci	if (!cpu_int_status) {
89762306a36Sopenharmony_ci		ath10k_warn(ar, "CPU interrupt status is zero\n");
89862306a36Sopenharmony_ci		ret = -EIO;
89962306a36Sopenharmony_ci		goto out;
90062306a36Sopenharmony_ci	}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	/* Clear the interrupt */
90362306a36Sopenharmony_ci	irq_data->irq_proc_reg->cpu_int_status &= ~cpu_int_status;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	/* Set up the register transfer buffer to hit the register 4 times,
90662306a36Sopenharmony_ci	 * this is done to make the access 4-byte aligned to mitigate issues
90762306a36Sopenharmony_ci	 * with host bus interconnects that restrict bus transfer lengths to
90862306a36Sopenharmony_ci	 * be a multiple of 4-bytes.
90962306a36Sopenharmony_ci	 *
91062306a36Sopenharmony_ci	 * Set W1C value to clear the interrupt, this hits the register first.
91162306a36Sopenharmony_ci	 */
91262306a36Sopenharmony_ci	ret = ath10k_sdio_writesb32(ar, MBOX_CPU_INT_STATUS_ADDRESS,
91362306a36Sopenharmony_ci				    cpu_int_status);
91462306a36Sopenharmony_ci	if (ret) {
91562306a36Sopenharmony_ci		ath10k_warn(ar, "unable to write to cpu interrupt status address: %d\n",
91662306a36Sopenharmony_ci			    ret);
91762306a36Sopenharmony_ci		goto out;
91862306a36Sopenharmony_ci	}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ciout:
92162306a36Sopenharmony_ci	mutex_unlock(&irq_data->mtx);
92262306a36Sopenharmony_ci	if (cpu_int_status & MBOX_CPU_STATUS_ENABLE_ASSERT_MASK)
92362306a36Sopenharmony_ci		ath10k_sdio_fw_crashed_dump(ar);
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	return ret;
92662306a36Sopenharmony_ci}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_cistatic int ath10k_sdio_mbox_read_int_status(struct ath10k *ar,
92962306a36Sopenharmony_ci					    u8 *host_int_status,
93062306a36Sopenharmony_ci					    u32 *lookahead)
93162306a36Sopenharmony_ci{
93262306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
93362306a36Sopenharmony_ci	struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
93462306a36Sopenharmony_ci	struct ath10k_sdio_irq_proc_regs *irq_proc_reg = irq_data->irq_proc_reg;
93562306a36Sopenharmony_ci	struct ath10k_sdio_irq_enable_regs *irq_en_reg = irq_data->irq_en_reg;
93662306a36Sopenharmony_ci	u8 htc_mbox = FIELD_PREP(ATH10K_HTC_MAILBOX_MASK, 1);
93762306a36Sopenharmony_ci	int ret;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	mutex_lock(&irq_data->mtx);
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	*lookahead = 0;
94262306a36Sopenharmony_ci	*host_int_status = 0;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	/* int_status_en is supposed to be non zero, otherwise interrupts
94562306a36Sopenharmony_ci	 * shouldn't be enabled. There is however a short time frame during
94662306a36Sopenharmony_ci	 * initialization between the irq register and int_status_en init
94762306a36Sopenharmony_ci	 * where this can happen.
94862306a36Sopenharmony_ci	 * We silently ignore this condition.
94962306a36Sopenharmony_ci	 */
95062306a36Sopenharmony_ci	if (!irq_en_reg->int_status_en) {
95162306a36Sopenharmony_ci		ret = 0;
95262306a36Sopenharmony_ci		goto out;
95362306a36Sopenharmony_ci	}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	/* Read the first sizeof(struct ath10k_irq_proc_registers)
95662306a36Sopenharmony_ci	 * bytes of the HTC register table. This
95762306a36Sopenharmony_ci	 * will yield us the value of different int status
95862306a36Sopenharmony_ci	 * registers and the lookahead registers.
95962306a36Sopenharmony_ci	 */
96062306a36Sopenharmony_ci	ret = ath10k_sdio_read(ar, MBOX_HOST_INT_STATUS_ADDRESS,
96162306a36Sopenharmony_ci			       irq_proc_reg, sizeof(*irq_proc_reg));
96262306a36Sopenharmony_ci	if (ret) {
96362306a36Sopenharmony_ci		ath10k_core_start_recovery(ar);
96462306a36Sopenharmony_ci		ath10k_warn(ar, "read int status fail, start recovery\n");
96562306a36Sopenharmony_ci		goto out;
96662306a36Sopenharmony_ci	}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	/* Update only those registers that are enabled */
96962306a36Sopenharmony_ci	*host_int_status = irq_proc_reg->host_int_status &
97062306a36Sopenharmony_ci			   irq_en_reg->int_status_en;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	/* Look at mbox status */
97362306a36Sopenharmony_ci	if (!(*host_int_status & htc_mbox)) {
97462306a36Sopenharmony_ci		*lookahead = 0;
97562306a36Sopenharmony_ci		ret = 0;
97662306a36Sopenharmony_ci		goto out;
97762306a36Sopenharmony_ci	}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	/* Mask out pending mbox value, we use look ahead as
98062306a36Sopenharmony_ci	 * the real flag for mbox processing.
98162306a36Sopenharmony_ci	 */
98262306a36Sopenharmony_ci	*host_int_status &= ~htc_mbox;
98362306a36Sopenharmony_ci	if (irq_proc_reg->rx_lookahead_valid & htc_mbox) {
98462306a36Sopenharmony_ci		*lookahead = le32_to_cpu(
98562306a36Sopenharmony_ci			irq_proc_reg->rx_lookahead[ATH10K_HTC_MAILBOX]);
98662306a36Sopenharmony_ci		if (!*lookahead)
98762306a36Sopenharmony_ci			ath10k_warn(ar, "sdio mbox lookahead is zero\n");
98862306a36Sopenharmony_ci	}
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ciout:
99162306a36Sopenharmony_ci	mutex_unlock(&irq_data->mtx);
99262306a36Sopenharmony_ci	return ret;
99362306a36Sopenharmony_ci}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_cistatic int ath10k_sdio_mbox_proc_pending_irqs(struct ath10k *ar,
99662306a36Sopenharmony_ci					      bool *done)
99762306a36Sopenharmony_ci{
99862306a36Sopenharmony_ci	u8 host_int_status;
99962306a36Sopenharmony_ci	u32 lookahead;
100062306a36Sopenharmony_ci	int ret;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	/* NOTE: HIF implementation guarantees that the context of this
100362306a36Sopenharmony_ci	 * call allows us to perform SYNCHRONOUS I/O, that is we can block,
100462306a36Sopenharmony_ci	 * sleep or call any API that can block or switch thread/task
100562306a36Sopenharmony_ci	 * contexts. This is a fully schedulable context.
100662306a36Sopenharmony_ci	 */
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	ret = ath10k_sdio_mbox_read_int_status(ar,
100962306a36Sopenharmony_ci					       &host_int_status,
101062306a36Sopenharmony_ci					       &lookahead);
101162306a36Sopenharmony_ci	if (ret) {
101262306a36Sopenharmony_ci		*done = true;
101362306a36Sopenharmony_ci		goto out;
101462306a36Sopenharmony_ci	}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	if (!host_int_status && !lookahead) {
101762306a36Sopenharmony_ci		ret = 0;
101862306a36Sopenharmony_ci		*done = true;
101962306a36Sopenharmony_ci		goto out;
102062306a36Sopenharmony_ci	}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	if (lookahead) {
102362306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_SDIO,
102462306a36Sopenharmony_ci			   "sdio pending mailbox msg lookahead 0x%08x\n",
102562306a36Sopenharmony_ci			   lookahead);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci		ret = ath10k_sdio_mbox_rxmsg_pending_handler(ar,
102862306a36Sopenharmony_ci							     lookahead,
102962306a36Sopenharmony_ci							     done);
103062306a36Sopenharmony_ci		if (ret)
103162306a36Sopenharmony_ci			goto out;
103262306a36Sopenharmony_ci	}
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	/* now, handle the rest of the interrupts */
103562306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO,
103662306a36Sopenharmony_ci		   "sdio host_int_status 0x%x\n", host_int_status);
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	if (FIELD_GET(MBOX_HOST_INT_STATUS_CPU_MASK, host_int_status)) {
103962306a36Sopenharmony_ci		/* CPU Interrupt */
104062306a36Sopenharmony_ci		ret = ath10k_sdio_mbox_proc_cpu_intr(ar);
104162306a36Sopenharmony_ci		if (ret)
104262306a36Sopenharmony_ci			goto out;
104362306a36Sopenharmony_ci	}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	if (FIELD_GET(MBOX_HOST_INT_STATUS_ERROR_MASK, host_int_status)) {
104662306a36Sopenharmony_ci		/* Error Interrupt */
104762306a36Sopenharmony_ci		ret = ath10k_sdio_mbox_proc_err_intr(ar);
104862306a36Sopenharmony_ci		if (ret)
104962306a36Sopenharmony_ci			goto out;
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	if (FIELD_GET(MBOX_HOST_INT_STATUS_COUNTER_MASK, host_int_status))
105362306a36Sopenharmony_ci		/* Counter Interrupt */
105462306a36Sopenharmony_ci		ret = ath10k_sdio_mbox_proc_counter_intr(ar);
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	ret = 0;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ciout:
105962306a36Sopenharmony_ci	/* An optimization to bypass reading the IRQ status registers
106062306a36Sopenharmony_ci	 * unnecessarily which can re-wake the target, if upper layers
106162306a36Sopenharmony_ci	 * determine that we are in a low-throughput mode, we can rely on
106262306a36Sopenharmony_ci	 * taking another interrupt rather than re-checking the status
106362306a36Sopenharmony_ci	 * registers which can re-wake the target.
106462306a36Sopenharmony_ci	 *
106562306a36Sopenharmony_ci	 * NOTE : for host interfaces that makes use of detecting pending
106662306a36Sopenharmony_ci	 * mbox messages at hif can not use this optimization due to
106762306a36Sopenharmony_ci	 * possible side effects, SPI requires the host to drain all
106862306a36Sopenharmony_ci	 * messages from the mailbox before exiting the ISR routine.
106962306a36Sopenharmony_ci	 */
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO,
107262306a36Sopenharmony_ci		   "sdio pending irqs done %d status %d",
107362306a36Sopenharmony_ci		   *done, ret);
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	return ret;
107662306a36Sopenharmony_ci}
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_cistatic void ath10k_sdio_set_mbox_info(struct ath10k *ar)
107962306a36Sopenharmony_ci{
108062306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
108162306a36Sopenharmony_ci	struct ath10k_mbox_info *mbox_info = &ar_sdio->mbox_info;
108262306a36Sopenharmony_ci	u16 device = ar_sdio->func->device, dev_id_base, dev_id_chiprev;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	mbox_info->htc_addr = ATH10K_HIF_MBOX_BASE_ADDR;
108562306a36Sopenharmony_ci	mbox_info->block_size = ATH10K_HIF_MBOX_BLOCK_SIZE;
108662306a36Sopenharmony_ci	mbox_info->block_mask = ATH10K_HIF_MBOX_BLOCK_SIZE - 1;
108762306a36Sopenharmony_ci	mbox_info->gmbox_addr = ATH10K_HIF_GMBOX_BASE_ADDR;
108862306a36Sopenharmony_ci	mbox_info->gmbox_sz = ATH10K_HIF_GMBOX_WIDTH;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	mbox_info->ext_info[0].htc_ext_addr = ATH10K_HIF_MBOX0_EXT_BASE_ADDR;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	dev_id_base = (device & 0x0F00);
109362306a36Sopenharmony_ci	dev_id_chiprev = (device & 0x00FF);
109462306a36Sopenharmony_ci	switch (dev_id_base) {
109562306a36Sopenharmony_ci	case (SDIO_DEVICE_ID_ATHEROS_AR6005 & 0x0F00):
109662306a36Sopenharmony_ci		if (dev_id_chiprev < 4)
109762306a36Sopenharmony_ci			mbox_info->ext_info[0].htc_ext_sz =
109862306a36Sopenharmony_ci				ATH10K_HIF_MBOX0_EXT_WIDTH;
109962306a36Sopenharmony_ci		else
110062306a36Sopenharmony_ci			/* from QCA6174 2.0(0x504), the width has been extended
110162306a36Sopenharmony_ci			 * to 56K
110262306a36Sopenharmony_ci			 */
110362306a36Sopenharmony_ci			mbox_info->ext_info[0].htc_ext_sz =
110462306a36Sopenharmony_ci				ATH10K_HIF_MBOX0_EXT_WIDTH_ROME_2_0;
110562306a36Sopenharmony_ci		break;
110662306a36Sopenharmony_ci	case (SDIO_DEVICE_ID_ATHEROS_QCA9377 & 0x0F00):
110762306a36Sopenharmony_ci		mbox_info->ext_info[0].htc_ext_sz =
110862306a36Sopenharmony_ci			ATH10K_HIF_MBOX0_EXT_WIDTH_ROME_2_0;
110962306a36Sopenharmony_ci		break;
111062306a36Sopenharmony_ci	default:
111162306a36Sopenharmony_ci		mbox_info->ext_info[0].htc_ext_sz =
111262306a36Sopenharmony_ci				ATH10K_HIF_MBOX0_EXT_WIDTH;
111362306a36Sopenharmony_ci	}
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	mbox_info->ext_info[1].htc_ext_addr =
111662306a36Sopenharmony_ci		mbox_info->ext_info[0].htc_ext_addr +
111762306a36Sopenharmony_ci		mbox_info->ext_info[0].htc_ext_sz +
111862306a36Sopenharmony_ci		ATH10K_HIF_MBOX_DUMMY_SPACE_SIZE;
111962306a36Sopenharmony_ci	mbox_info->ext_info[1].htc_ext_sz = ATH10K_HIF_MBOX1_EXT_WIDTH;
112062306a36Sopenharmony_ci}
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci/* BMI functions */
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_cistatic int ath10k_sdio_bmi_credits(struct ath10k *ar)
112562306a36Sopenharmony_ci{
112662306a36Sopenharmony_ci	u32 addr, cmd_credits;
112762306a36Sopenharmony_ci	unsigned long timeout;
112862306a36Sopenharmony_ci	int ret;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	/* Read the counter register to get the command credits */
113162306a36Sopenharmony_ci	addr = MBOX_COUNT_DEC_ADDRESS + ATH10K_HIF_MBOX_NUM_MAX * 4;
113262306a36Sopenharmony_ci	timeout = jiffies + BMI_COMMUNICATION_TIMEOUT_HZ;
113362306a36Sopenharmony_ci	cmd_credits = 0;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	while (time_before(jiffies, timeout) && !cmd_credits) {
113662306a36Sopenharmony_ci		/* Hit the credit counter with a 4-byte access, the first byte
113762306a36Sopenharmony_ci		 * read will hit the counter and cause a decrement, while the
113862306a36Sopenharmony_ci		 * remaining 3 bytes has no effect. The rationale behind this
113962306a36Sopenharmony_ci		 * is to make all HIF accesses 4-byte aligned.
114062306a36Sopenharmony_ci		 */
114162306a36Sopenharmony_ci		ret = ath10k_sdio_read32(ar, addr, &cmd_credits);
114262306a36Sopenharmony_ci		if (ret) {
114362306a36Sopenharmony_ci			ath10k_warn(ar,
114462306a36Sopenharmony_ci				    "unable to decrement the command credit count register: %d\n",
114562306a36Sopenharmony_ci				    ret);
114662306a36Sopenharmony_ci			return ret;
114762306a36Sopenharmony_ci		}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci		/* The counter is only 8 bits.
115062306a36Sopenharmony_ci		 * Ignore anything in the upper 3 bytes
115162306a36Sopenharmony_ci		 */
115262306a36Sopenharmony_ci		cmd_credits &= 0xFF;
115362306a36Sopenharmony_ci	}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	if (!cmd_credits) {
115662306a36Sopenharmony_ci		ath10k_warn(ar, "bmi communication timeout\n");
115762306a36Sopenharmony_ci		return -ETIMEDOUT;
115862306a36Sopenharmony_ci	}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	return 0;
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_cistatic int ath10k_sdio_bmi_get_rx_lookahead(struct ath10k *ar)
116462306a36Sopenharmony_ci{
116562306a36Sopenharmony_ci	unsigned long timeout;
116662306a36Sopenharmony_ci	u32 rx_word;
116762306a36Sopenharmony_ci	int ret;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	timeout = jiffies + BMI_COMMUNICATION_TIMEOUT_HZ;
117062306a36Sopenharmony_ci	rx_word = 0;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	while ((time_before(jiffies, timeout)) && !rx_word) {
117362306a36Sopenharmony_ci		ret = ath10k_sdio_read32(ar,
117462306a36Sopenharmony_ci					 MBOX_HOST_INT_STATUS_ADDRESS,
117562306a36Sopenharmony_ci					 &rx_word);
117662306a36Sopenharmony_ci		if (ret) {
117762306a36Sopenharmony_ci			ath10k_warn(ar, "unable to read RX_LOOKAHEAD_VALID: %d\n", ret);
117862306a36Sopenharmony_ci			return ret;
117962306a36Sopenharmony_ci		}
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci		 /* all we really want is one bit */
118262306a36Sopenharmony_ci		rx_word &= 1;
118362306a36Sopenharmony_ci	}
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	if (!rx_word) {
118662306a36Sopenharmony_ci		ath10k_warn(ar, "bmi_recv_buf FIFO empty\n");
118762306a36Sopenharmony_ci		return -EINVAL;
118862306a36Sopenharmony_ci	}
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	return ret;
119162306a36Sopenharmony_ci}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_cistatic int ath10k_sdio_bmi_exchange_msg(struct ath10k *ar,
119462306a36Sopenharmony_ci					void *req, u32 req_len,
119562306a36Sopenharmony_ci					void *resp, u32 *resp_len)
119662306a36Sopenharmony_ci{
119762306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
119862306a36Sopenharmony_ci	u32 addr;
119962306a36Sopenharmony_ci	int ret;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	if (req) {
120262306a36Sopenharmony_ci		ret = ath10k_sdio_bmi_credits(ar);
120362306a36Sopenharmony_ci		if (ret)
120462306a36Sopenharmony_ci			return ret;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci		addr = ar_sdio->mbox_info.htc_addr;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci		memcpy(ar_sdio->bmi_buf, req, req_len);
120962306a36Sopenharmony_ci		ret = ath10k_sdio_write(ar, addr, ar_sdio->bmi_buf, req_len);
121062306a36Sopenharmony_ci		if (ret) {
121162306a36Sopenharmony_ci			ath10k_warn(ar,
121262306a36Sopenharmony_ci				    "unable to send the bmi data to the device: %d\n",
121362306a36Sopenharmony_ci				    ret);
121462306a36Sopenharmony_ci			return ret;
121562306a36Sopenharmony_ci		}
121662306a36Sopenharmony_ci	}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	if (!resp || !resp_len)
121962306a36Sopenharmony_ci		/* No response expected */
122062306a36Sopenharmony_ci		return 0;
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	/* During normal bootup, small reads may be required.
122362306a36Sopenharmony_ci	 * Rather than issue an HIF Read and then wait as the Target
122462306a36Sopenharmony_ci	 * adds successive bytes to the FIFO, we wait here until
122562306a36Sopenharmony_ci	 * we know that response data is available.
122662306a36Sopenharmony_ci	 *
122762306a36Sopenharmony_ci	 * This allows us to cleanly timeout on an unexpected
122862306a36Sopenharmony_ci	 * Target failure rather than risk problems at the HIF level.
122962306a36Sopenharmony_ci	 * In particular, this avoids SDIO timeouts and possibly garbage
123062306a36Sopenharmony_ci	 * data on some host controllers.  And on an interconnect
123162306a36Sopenharmony_ci	 * such as Compact Flash (as well as some SDIO masters) which
123262306a36Sopenharmony_ci	 * does not provide any indication on data timeout, it avoids
123362306a36Sopenharmony_ci	 * a potential hang or garbage response.
123462306a36Sopenharmony_ci	 *
123562306a36Sopenharmony_ci	 * Synchronization is more difficult for reads larger than the
123662306a36Sopenharmony_ci	 * size of the MBOX FIFO (128B), because the Target is unable
123762306a36Sopenharmony_ci	 * to push the 129th byte of data until AFTER the Host posts an
123862306a36Sopenharmony_ci	 * HIF Read and removes some FIFO data.  So for large reads the
123962306a36Sopenharmony_ci	 * Host proceeds to post an HIF Read BEFORE all the data is
124062306a36Sopenharmony_ci	 * actually available to read.  Fortunately, large BMI reads do
124162306a36Sopenharmony_ci	 * not occur in practice -- they're supported for debug/development.
124262306a36Sopenharmony_ci	 *
124362306a36Sopenharmony_ci	 * So Host/Target BMI synchronization is divided into these cases:
124462306a36Sopenharmony_ci	 *  CASE 1: length < 4
124562306a36Sopenharmony_ci	 *        Should not happen
124662306a36Sopenharmony_ci	 *
124762306a36Sopenharmony_ci	 *  CASE 2: 4 <= length <= 128
124862306a36Sopenharmony_ci	 *        Wait for first 4 bytes to be in FIFO
124962306a36Sopenharmony_ci	 *        If CONSERVATIVE_BMI_READ is enabled, also wait for
125062306a36Sopenharmony_ci	 *        a BMI command credit, which indicates that the ENTIRE
125162306a36Sopenharmony_ci	 *        response is available in the FIFO
125262306a36Sopenharmony_ci	 *
125362306a36Sopenharmony_ci	 *  CASE 3: length > 128
125462306a36Sopenharmony_ci	 *        Wait for the first 4 bytes to be in FIFO
125562306a36Sopenharmony_ci	 *
125662306a36Sopenharmony_ci	 * For most uses, a small timeout should be sufficient and we will
125762306a36Sopenharmony_ci	 * usually see a response quickly; but there may be some unusual
125862306a36Sopenharmony_ci	 * (debug) cases of BMI_EXECUTE where we want an larger timeout.
125962306a36Sopenharmony_ci	 * For now, we use an unbounded busy loop while waiting for
126062306a36Sopenharmony_ci	 * BMI_EXECUTE.
126162306a36Sopenharmony_ci	 *
126262306a36Sopenharmony_ci	 * If BMI_EXECUTE ever needs to support longer-latency execution,
126362306a36Sopenharmony_ci	 * especially in production, this code needs to be enhanced to sleep
126462306a36Sopenharmony_ci	 * and yield.  Also note that BMI_COMMUNICATION_TIMEOUT is currently
126562306a36Sopenharmony_ci	 * a function of Host processor speed.
126662306a36Sopenharmony_ci	 */
126762306a36Sopenharmony_ci	ret = ath10k_sdio_bmi_get_rx_lookahead(ar);
126862306a36Sopenharmony_ci	if (ret)
126962306a36Sopenharmony_ci		return ret;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	/* We always read from the start of the mbox address */
127262306a36Sopenharmony_ci	addr = ar_sdio->mbox_info.htc_addr;
127362306a36Sopenharmony_ci	ret = ath10k_sdio_read(ar, addr, ar_sdio->bmi_buf, *resp_len);
127462306a36Sopenharmony_ci	if (ret) {
127562306a36Sopenharmony_ci		ath10k_warn(ar,
127662306a36Sopenharmony_ci			    "unable to read the bmi data from the device: %d\n",
127762306a36Sopenharmony_ci			    ret);
127862306a36Sopenharmony_ci		return ret;
127962306a36Sopenharmony_ci	}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	memcpy(resp, ar_sdio->bmi_buf, *resp_len);
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	return 0;
128462306a36Sopenharmony_ci}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci/* sdio async handling functions */
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_cistatic struct ath10k_sdio_bus_request
128962306a36Sopenharmony_ci*ath10k_sdio_alloc_busreq(struct ath10k *ar)
129062306a36Sopenharmony_ci{
129162306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
129262306a36Sopenharmony_ci	struct ath10k_sdio_bus_request *bus_req;
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	spin_lock_bh(&ar_sdio->lock);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	if (list_empty(&ar_sdio->bus_req_freeq)) {
129762306a36Sopenharmony_ci		bus_req = NULL;
129862306a36Sopenharmony_ci		goto out;
129962306a36Sopenharmony_ci	}
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	bus_req = list_first_entry(&ar_sdio->bus_req_freeq,
130262306a36Sopenharmony_ci				   struct ath10k_sdio_bus_request, list);
130362306a36Sopenharmony_ci	list_del(&bus_req->list);
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ciout:
130662306a36Sopenharmony_ci	spin_unlock_bh(&ar_sdio->lock);
130762306a36Sopenharmony_ci	return bus_req;
130862306a36Sopenharmony_ci}
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_cistatic void ath10k_sdio_free_bus_req(struct ath10k *ar,
131162306a36Sopenharmony_ci				     struct ath10k_sdio_bus_request *bus_req)
131262306a36Sopenharmony_ci{
131362306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	memset(bus_req, 0, sizeof(*bus_req));
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	spin_lock_bh(&ar_sdio->lock);
131862306a36Sopenharmony_ci	list_add_tail(&bus_req->list, &ar_sdio->bus_req_freeq);
131962306a36Sopenharmony_ci	spin_unlock_bh(&ar_sdio->lock);
132062306a36Sopenharmony_ci}
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_cistatic void __ath10k_sdio_write_async(struct ath10k *ar,
132362306a36Sopenharmony_ci				      struct ath10k_sdio_bus_request *req)
132462306a36Sopenharmony_ci{
132562306a36Sopenharmony_ci	struct ath10k_htc_ep *ep;
132662306a36Sopenharmony_ci	struct sk_buff *skb;
132762306a36Sopenharmony_ci	int ret;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	skb = req->skb;
133062306a36Sopenharmony_ci	ret = ath10k_sdio_write(ar, req->address, skb->data, skb->len);
133162306a36Sopenharmony_ci	if (ret)
133262306a36Sopenharmony_ci		ath10k_warn(ar, "failed to write skb to 0x%x asynchronously: %d",
133362306a36Sopenharmony_ci			    req->address, ret);
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	if (req->htc_msg) {
133662306a36Sopenharmony_ci		ep = &ar->htc.endpoint[req->eid];
133762306a36Sopenharmony_ci		ath10k_htc_notify_tx_completion(ep, skb);
133862306a36Sopenharmony_ci	} else if (req->comp) {
133962306a36Sopenharmony_ci		complete(req->comp);
134062306a36Sopenharmony_ci	}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	ath10k_sdio_free_bus_req(ar, req);
134362306a36Sopenharmony_ci}
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci/* To improve throughput use workqueue to deliver packets to HTC layer,
134662306a36Sopenharmony_ci * this way SDIO bus is utilised much better.
134762306a36Sopenharmony_ci */
134862306a36Sopenharmony_cistatic void ath10k_rx_indication_async_work(struct work_struct *work)
134962306a36Sopenharmony_ci{
135062306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = container_of(work, struct ath10k_sdio,
135162306a36Sopenharmony_ci						   async_work_rx);
135262306a36Sopenharmony_ci	struct ath10k *ar = ar_sdio->ar;
135362306a36Sopenharmony_ci	struct ath10k_htc_ep *ep;
135462306a36Sopenharmony_ci	struct ath10k_skb_rxcb *cb;
135562306a36Sopenharmony_ci	struct sk_buff *skb;
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	while (true) {
135862306a36Sopenharmony_ci		skb = skb_dequeue(&ar_sdio->rx_head);
135962306a36Sopenharmony_ci		if (!skb)
136062306a36Sopenharmony_ci			break;
136162306a36Sopenharmony_ci		cb = ATH10K_SKB_RXCB(skb);
136262306a36Sopenharmony_ci		ep = &ar->htc.endpoint[cb->eid];
136362306a36Sopenharmony_ci		ep->ep_ops.ep_rx_complete(ar, skb);
136462306a36Sopenharmony_ci	}
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	if (test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags)) {
136762306a36Sopenharmony_ci		local_bh_disable();
136862306a36Sopenharmony_ci		napi_schedule(&ar->napi);
136962306a36Sopenharmony_ci		local_bh_enable();
137062306a36Sopenharmony_ci	}
137162306a36Sopenharmony_ci}
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_cistatic int ath10k_sdio_read_rtc_state(struct ath10k_sdio *ar_sdio, unsigned char *state)
137462306a36Sopenharmony_ci{
137562306a36Sopenharmony_ci	struct ath10k *ar = ar_sdio->ar;
137662306a36Sopenharmony_ci	unsigned char rtc_state = 0;
137762306a36Sopenharmony_ci	int ret = 0;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	rtc_state = sdio_f0_readb(ar_sdio->func, ATH10K_CIS_RTC_STATE_ADDR, &ret);
138062306a36Sopenharmony_ci	if (ret) {
138162306a36Sopenharmony_ci		ath10k_warn(ar, "failed to read rtc state: %d\n", ret);
138262306a36Sopenharmony_ci		return ret;
138362306a36Sopenharmony_ci	}
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci	*state = rtc_state & 0x3;
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	return ret;
138862306a36Sopenharmony_ci}
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_cistatic int ath10k_sdio_set_mbox_sleep(struct ath10k *ar, bool enable_sleep)
139162306a36Sopenharmony_ci{
139262306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
139362306a36Sopenharmony_ci	u32 val;
139462306a36Sopenharmony_ci	int retry = ATH10K_CIS_READ_RETRY, ret = 0;
139562306a36Sopenharmony_ci	unsigned char rtc_state = 0;
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	sdio_claim_host(ar_sdio->func);
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	ret = ath10k_sdio_read32(ar, ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL, &val);
140062306a36Sopenharmony_ci	if (ret) {
140162306a36Sopenharmony_ci		ath10k_warn(ar, "failed to read fifo/chip control register: %d\n",
140262306a36Sopenharmony_ci			    ret);
140362306a36Sopenharmony_ci		goto release;
140462306a36Sopenharmony_ci	}
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	if (enable_sleep) {
140762306a36Sopenharmony_ci		val &= ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL_DISABLE_SLEEP_OFF;
140862306a36Sopenharmony_ci		ar_sdio->mbox_state = SDIO_MBOX_SLEEP_STATE;
140962306a36Sopenharmony_ci	} else {
141062306a36Sopenharmony_ci		val |= ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL_DISABLE_SLEEP_ON;
141162306a36Sopenharmony_ci		ar_sdio->mbox_state = SDIO_MBOX_AWAKE_STATE;
141262306a36Sopenharmony_ci	}
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	ret = ath10k_sdio_write32(ar, ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL, val);
141562306a36Sopenharmony_ci	if (ret) {
141662306a36Sopenharmony_ci		ath10k_warn(ar, "failed to write to FIFO_TIMEOUT_AND_CHIP_CONTROL: %d",
141762306a36Sopenharmony_ci			    ret);
141862306a36Sopenharmony_ci	}
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	if (!enable_sleep) {
142162306a36Sopenharmony_ci		do {
142262306a36Sopenharmony_ci			udelay(ATH10K_CIS_READ_WAIT_4_RTC_CYCLE_IN_US);
142362306a36Sopenharmony_ci			ret = ath10k_sdio_read_rtc_state(ar_sdio, &rtc_state);
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci			if (ret) {
142662306a36Sopenharmony_ci				ath10k_warn(ar, "failed to disable mbox sleep: %d", ret);
142762306a36Sopenharmony_ci				break;
142862306a36Sopenharmony_ci			}
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci			ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio read rtc state: %d\n",
143162306a36Sopenharmony_ci				   rtc_state);
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci			if (rtc_state == ATH10K_CIS_RTC_STATE_ON)
143462306a36Sopenharmony_ci				break;
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci			udelay(ATH10K_CIS_XTAL_SETTLE_DURATION_IN_US);
143762306a36Sopenharmony_ci			retry--;
143862306a36Sopenharmony_ci		} while (retry > 0);
143962306a36Sopenharmony_ci	}
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_cirelease:
144262306a36Sopenharmony_ci	sdio_release_host(ar_sdio->func);
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	return ret;
144562306a36Sopenharmony_ci}
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_cistatic void ath10k_sdio_sleep_timer_handler(struct timer_list *t)
144862306a36Sopenharmony_ci{
144962306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = from_timer(ar_sdio, t, sleep_timer);
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	ar_sdio->mbox_state = SDIO_MBOX_REQUEST_TO_SLEEP_STATE;
145262306a36Sopenharmony_ci	queue_work(ar_sdio->workqueue, &ar_sdio->wr_async_work);
145362306a36Sopenharmony_ci}
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_cistatic void ath10k_sdio_write_async_work(struct work_struct *work)
145662306a36Sopenharmony_ci{
145762306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = container_of(work, struct ath10k_sdio,
145862306a36Sopenharmony_ci						   wr_async_work);
145962306a36Sopenharmony_ci	struct ath10k *ar = ar_sdio->ar;
146062306a36Sopenharmony_ci	struct ath10k_sdio_bus_request *req, *tmp_req;
146162306a36Sopenharmony_ci	struct ath10k_mbox_info *mbox_info = &ar_sdio->mbox_info;
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	spin_lock_bh(&ar_sdio->wr_async_lock);
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	list_for_each_entry_safe(req, tmp_req, &ar_sdio->wr_asyncq, list) {
146662306a36Sopenharmony_ci		list_del(&req->list);
146762306a36Sopenharmony_ci		spin_unlock_bh(&ar_sdio->wr_async_lock);
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci		if (req->address >= mbox_info->htc_addr &&
147062306a36Sopenharmony_ci		    ar_sdio->mbox_state == SDIO_MBOX_SLEEP_STATE) {
147162306a36Sopenharmony_ci			ath10k_sdio_set_mbox_sleep(ar, false);
147262306a36Sopenharmony_ci			mod_timer(&ar_sdio->sleep_timer, jiffies +
147362306a36Sopenharmony_ci				  msecs_to_jiffies(ATH10K_MIN_SLEEP_INACTIVITY_TIME_MS));
147462306a36Sopenharmony_ci		}
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci		__ath10k_sdio_write_async(ar, req);
147762306a36Sopenharmony_ci		spin_lock_bh(&ar_sdio->wr_async_lock);
147862306a36Sopenharmony_ci	}
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	spin_unlock_bh(&ar_sdio->wr_async_lock);
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	if (ar_sdio->mbox_state == SDIO_MBOX_REQUEST_TO_SLEEP_STATE)
148362306a36Sopenharmony_ci		ath10k_sdio_set_mbox_sleep(ar, true);
148462306a36Sopenharmony_ci}
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_cistatic int ath10k_sdio_prep_async_req(struct ath10k *ar, u32 addr,
148762306a36Sopenharmony_ci				      struct sk_buff *skb,
148862306a36Sopenharmony_ci				      struct completion *comp,
148962306a36Sopenharmony_ci				      bool htc_msg, enum ath10k_htc_ep_id eid)
149062306a36Sopenharmony_ci{
149162306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
149262306a36Sopenharmony_ci	struct ath10k_sdio_bus_request *bus_req;
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	/* Allocate a bus request for the message and queue it on the
149562306a36Sopenharmony_ci	 * SDIO workqueue.
149662306a36Sopenharmony_ci	 */
149762306a36Sopenharmony_ci	bus_req = ath10k_sdio_alloc_busreq(ar);
149862306a36Sopenharmony_ci	if (!bus_req) {
149962306a36Sopenharmony_ci		ath10k_warn(ar,
150062306a36Sopenharmony_ci			    "unable to allocate bus request for async request\n");
150162306a36Sopenharmony_ci		return -ENOMEM;
150262306a36Sopenharmony_ci	}
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	bus_req->skb = skb;
150562306a36Sopenharmony_ci	bus_req->eid = eid;
150662306a36Sopenharmony_ci	bus_req->address = addr;
150762306a36Sopenharmony_ci	bus_req->htc_msg = htc_msg;
150862306a36Sopenharmony_ci	bus_req->comp = comp;
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	spin_lock_bh(&ar_sdio->wr_async_lock);
151162306a36Sopenharmony_ci	list_add_tail(&bus_req->list, &ar_sdio->wr_asyncq);
151262306a36Sopenharmony_ci	spin_unlock_bh(&ar_sdio->wr_async_lock);
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	return 0;
151562306a36Sopenharmony_ci}
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci/* IRQ handler */
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_cistatic void ath10k_sdio_irq_handler(struct sdio_func *func)
152062306a36Sopenharmony_ci{
152162306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = sdio_get_drvdata(func);
152262306a36Sopenharmony_ci	struct ath10k *ar = ar_sdio->ar;
152362306a36Sopenharmony_ci	unsigned long timeout;
152462306a36Sopenharmony_ci	bool done = false;
152562306a36Sopenharmony_ci	int ret;
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	/* Release the host during interrupts so we can pick it back up when
152862306a36Sopenharmony_ci	 * we process commands.
152962306a36Sopenharmony_ci	 */
153062306a36Sopenharmony_ci	sdio_release_host(ar_sdio->func);
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	timeout = jiffies + ATH10K_SDIO_HIF_COMMUNICATION_TIMEOUT_HZ;
153362306a36Sopenharmony_ci	do {
153462306a36Sopenharmony_ci		ret = ath10k_sdio_mbox_proc_pending_irqs(ar, &done);
153562306a36Sopenharmony_ci		if (ret)
153662306a36Sopenharmony_ci			break;
153762306a36Sopenharmony_ci	} while (time_before(jiffies, timeout) && !done);
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	ath10k_mac_tx_push_pending(ar);
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	sdio_claim_host(ar_sdio->func);
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	if (ret && ret != -ECANCELED)
154462306a36Sopenharmony_ci		ath10k_warn(ar, "failed to process pending SDIO interrupts: %d\n",
154562306a36Sopenharmony_ci			    ret);
154662306a36Sopenharmony_ci}
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci/* sdio HIF functions */
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_cistatic int ath10k_sdio_disable_intrs(struct ath10k *ar)
155162306a36Sopenharmony_ci{
155262306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
155362306a36Sopenharmony_ci	struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
155462306a36Sopenharmony_ci	struct ath10k_sdio_irq_enable_regs *regs = irq_data->irq_en_reg;
155562306a36Sopenharmony_ci	int ret;
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci	mutex_lock(&irq_data->mtx);
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	memset(regs, 0, sizeof(*regs));
156062306a36Sopenharmony_ci	ret = ath10k_sdio_write(ar, MBOX_INT_STATUS_ENABLE_ADDRESS,
156162306a36Sopenharmony_ci				&regs->int_status_en, sizeof(*regs));
156262306a36Sopenharmony_ci	if (ret)
156362306a36Sopenharmony_ci		ath10k_warn(ar, "unable to disable sdio interrupts: %d\n", ret);
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	mutex_unlock(&irq_data->mtx);
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	return ret;
156862306a36Sopenharmony_ci}
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_cistatic int ath10k_sdio_hif_power_up(struct ath10k *ar,
157162306a36Sopenharmony_ci				    enum ath10k_firmware_mode fw_mode)
157262306a36Sopenharmony_ci{
157362306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
157462306a36Sopenharmony_ci	struct sdio_func *func = ar_sdio->func;
157562306a36Sopenharmony_ci	int ret;
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	if (!ar_sdio->is_disabled)
157862306a36Sopenharmony_ci		return 0;
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_BOOT, "sdio power on\n");
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	ret = ath10k_sdio_config(ar);
158362306a36Sopenharmony_ci	if (ret) {
158462306a36Sopenharmony_ci		ath10k_err(ar, "failed to config sdio: %d\n", ret);
158562306a36Sopenharmony_ci		return ret;
158662306a36Sopenharmony_ci	}
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	sdio_claim_host(func);
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	ret = sdio_enable_func(func);
159162306a36Sopenharmony_ci	if (ret) {
159262306a36Sopenharmony_ci		ath10k_warn(ar, "unable to enable sdio function: %d)\n", ret);
159362306a36Sopenharmony_ci		sdio_release_host(func);
159462306a36Sopenharmony_ci		return ret;
159562306a36Sopenharmony_ci	}
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	sdio_release_host(func);
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	/* Wait for hardware to initialise. It should take a lot less than
160062306a36Sopenharmony_ci	 * 20 ms but let's be conservative here.
160162306a36Sopenharmony_ci	 */
160262306a36Sopenharmony_ci	msleep(20);
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	ar_sdio->is_disabled = false;
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	ret = ath10k_sdio_disable_intrs(ar);
160762306a36Sopenharmony_ci	if (ret)
160862306a36Sopenharmony_ci		return ret;
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	return 0;
161162306a36Sopenharmony_ci}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_cistatic void ath10k_sdio_hif_power_down(struct ath10k *ar)
161462306a36Sopenharmony_ci{
161562306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
161662306a36Sopenharmony_ci	int ret;
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	if (ar_sdio->is_disabled)
161962306a36Sopenharmony_ci		return;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_BOOT, "sdio power off\n");
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	del_timer_sync(&ar_sdio->sleep_timer);
162462306a36Sopenharmony_ci	ath10k_sdio_set_mbox_sleep(ar, true);
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	/* Disable the card */
162762306a36Sopenharmony_ci	sdio_claim_host(ar_sdio->func);
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci	ret = sdio_disable_func(ar_sdio->func);
163062306a36Sopenharmony_ci	if (ret) {
163162306a36Sopenharmony_ci		ath10k_warn(ar, "unable to disable sdio function: %d\n", ret);
163262306a36Sopenharmony_ci		sdio_release_host(ar_sdio->func);
163362306a36Sopenharmony_ci		return;
163462306a36Sopenharmony_ci	}
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	ret = mmc_hw_reset(ar_sdio->func->card);
163762306a36Sopenharmony_ci	if (ret)
163862306a36Sopenharmony_ci		ath10k_warn(ar, "unable to reset sdio: %d\n", ret);
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	sdio_release_host(ar_sdio->func);
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	ar_sdio->is_disabled = true;
164362306a36Sopenharmony_ci}
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_cistatic int ath10k_sdio_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
164662306a36Sopenharmony_ci				 struct ath10k_hif_sg_item *items, int n_items)
164762306a36Sopenharmony_ci{
164862306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
164962306a36Sopenharmony_ci	enum ath10k_htc_ep_id eid;
165062306a36Sopenharmony_ci	struct sk_buff *skb;
165162306a36Sopenharmony_ci	int ret, i;
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	eid = pipe_id_to_eid(pipe_id);
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	for (i = 0; i < n_items; i++) {
165662306a36Sopenharmony_ci		size_t padded_len;
165762306a36Sopenharmony_ci		u32 address;
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci		skb = items[i].transfer_context;
166062306a36Sopenharmony_ci		padded_len = ath10k_sdio_calc_txrx_padded_len(ar_sdio,
166162306a36Sopenharmony_ci							      skb->len);
166262306a36Sopenharmony_ci		skb_trim(skb, padded_len);
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci		/* Write TX data to the end of the mbox address space */
166562306a36Sopenharmony_ci		address = ar_sdio->mbox_addr[eid] + ar_sdio->mbox_size[eid] -
166662306a36Sopenharmony_ci			  skb->len;
166762306a36Sopenharmony_ci		ret = ath10k_sdio_prep_async_req(ar, address, skb,
166862306a36Sopenharmony_ci						 NULL, true, eid);
166962306a36Sopenharmony_ci		if (ret)
167062306a36Sopenharmony_ci			return ret;
167162306a36Sopenharmony_ci	}
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	queue_work(ar_sdio->workqueue, &ar_sdio->wr_async_work);
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	return 0;
167662306a36Sopenharmony_ci}
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_cistatic int ath10k_sdio_enable_intrs(struct ath10k *ar)
167962306a36Sopenharmony_ci{
168062306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
168162306a36Sopenharmony_ci	struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
168262306a36Sopenharmony_ci	struct ath10k_sdio_irq_enable_regs *regs = irq_data->irq_en_reg;
168362306a36Sopenharmony_ci	int ret;
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	mutex_lock(&irq_data->mtx);
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci	/* Enable all but CPU interrupts */
168862306a36Sopenharmony_ci	regs->int_status_en = FIELD_PREP(MBOX_INT_STATUS_ENABLE_ERROR_MASK, 1) |
168962306a36Sopenharmony_ci			      FIELD_PREP(MBOX_INT_STATUS_ENABLE_CPU_MASK, 1) |
169062306a36Sopenharmony_ci			      FIELD_PREP(MBOX_INT_STATUS_ENABLE_COUNTER_MASK, 1);
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	/* NOTE: There are some cases where HIF can do detection of
169362306a36Sopenharmony_ci	 * pending mbox messages which is disabled now.
169462306a36Sopenharmony_ci	 */
169562306a36Sopenharmony_ci	regs->int_status_en |=
169662306a36Sopenharmony_ci		FIELD_PREP(MBOX_INT_STATUS_ENABLE_MBOX_DATA_MASK, 1);
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci	/* Set up the CPU Interrupt Status Register, enable CPU sourced interrupt #0
169962306a36Sopenharmony_ci	 * #0 is used for report assertion from target
170062306a36Sopenharmony_ci	 */
170162306a36Sopenharmony_ci	regs->cpu_int_status_en = FIELD_PREP(MBOX_CPU_STATUS_ENABLE_ASSERT_MASK, 1);
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci	/* Set up the Error Interrupt status Register */
170462306a36Sopenharmony_ci	regs->err_int_status_en =
170562306a36Sopenharmony_ci		FIELD_PREP(MBOX_ERROR_STATUS_ENABLE_RX_UNDERFLOW_MASK, 1) |
170662306a36Sopenharmony_ci		FIELD_PREP(MBOX_ERROR_STATUS_ENABLE_TX_OVERFLOW_MASK, 1);
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	/* Enable Counter interrupt status register to get fatal errors for
170962306a36Sopenharmony_ci	 * debugging.
171062306a36Sopenharmony_ci	 */
171162306a36Sopenharmony_ci	regs->cntr_int_status_en =
171262306a36Sopenharmony_ci		FIELD_PREP(MBOX_COUNTER_INT_STATUS_ENABLE_BIT_MASK,
171362306a36Sopenharmony_ci			   ATH10K_SDIO_TARGET_DEBUG_INTR_MASK);
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci	ret = ath10k_sdio_write(ar, MBOX_INT_STATUS_ENABLE_ADDRESS,
171662306a36Sopenharmony_ci				&regs->int_status_en, sizeof(*regs));
171762306a36Sopenharmony_ci	if (ret)
171862306a36Sopenharmony_ci		ath10k_warn(ar,
171962306a36Sopenharmony_ci			    "failed to update mbox interrupt status register : %d\n",
172062306a36Sopenharmony_ci			    ret);
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci	mutex_unlock(&irq_data->mtx);
172362306a36Sopenharmony_ci	return ret;
172462306a36Sopenharmony_ci}
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci/* HIF diagnostics */
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_cistatic int ath10k_sdio_hif_diag_read(struct ath10k *ar, u32 address, void *buf,
172962306a36Sopenharmony_ci				     size_t buf_len)
173062306a36Sopenharmony_ci{
173162306a36Sopenharmony_ci	int ret;
173262306a36Sopenharmony_ci	void *mem;
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	mem = kzalloc(buf_len, GFP_KERNEL);
173562306a36Sopenharmony_ci	if (!mem)
173662306a36Sopenharmony_ci		return -ENOMEM;
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci	/* set window register to start read cycle */
173962306a36Sopenharmony_ci	ret = ath10k_sdio_write32(ar, MBOX_WINDOW_READ_ADDR_ADDRESS, address);
174062306a36Sopenharmony_ci	if (ret) {
174162306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set mbox window read address: %d", ret);
174262306a36Sopenharmony_ci		goto out;
174362306a36Sopenharmony_ci	}
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	/* read the data */
174662306a36Sopenharmony_ci	ret = ath10k_sdio_read(ar, MBOX_WINDOW_DATA_ADDRESS, mem, buf_len);
174762306a36Sopenharmony_ci	if (ret) {
174862306a36Sopenharmony_ci		ath10k_warn(ar, "failed to read from mbox window data address: %d\n",
174962306a36Sopenharmony_ci			    ret);
175062306a36Sopenharmony_ci		goto out;
175162306a36Sopenharmony_ci	}
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci	memcpy(buf, mem, buf_len);
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ciout:
175662306a36Sopenharmony_ci	kfree(mem);
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	return ret;
175962306a36Sopenharmony_ci}
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_cistatic int ath10k_sdio_diag_read32(struct ath10k *ar, u32 address,
176262306a36Sopenharmony_ci				   u32 *value)
176362306a36Sopenharmony_ci{
176462306a36Sopenharmony_ci	__le32 *val;
176562306a36Sopenharmony_ci	int ret;
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	val = kzalloc(sizeof(*val), GFP_KERNEL);
176862306a36Sopenharmony_ci	if (!val)
176962306a36Sopenharmony_ci		return -ENOMEM;
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	ret = ath10k_sdio_hif_diag_read(ar, address, val, sizeof(*val));
177262306a36Sopenharmony_ci	if (ret)
177362306a36Sopenharmony_ci		goto out;
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci	*value = __le32_to_cpu(*val);
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ciout:
177862306a36Sopenharmony_ci	kfree(val);
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	return ret;
178162306a36Sopenharmony_ci}
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_cistatic int ath10k_sdio_hif_diag_write_mem(struct ath10k *ar, u32 address,
178462306a36Sopenharmony_ci					  const void *data, int nbytes)
178562306a36Sopenharmony_ci{
178662306a36Sopenharmony_ci	int ret;
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	/* set write data */
178962306a36Sopenharmony_ci	ret = ath10k_sdio_write(ar, MBOX_WINDOW_DATA_ADDRESS, data, nbytes);
179062306a36Sopenharmony_ci	if (ret) {
179162306a36Sopenharmony_ci		ath10k_warn(ar,
179262306a36Sopenharmony_ci			    "failed to write 0x%p to mbox window data address: %d\n",
179362306a36Sopenharmony_ci			    data, ret);
179462306a36Sopenharmony_ci		return ret;
179562306a36Sopenharmony_ci	}
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci	/* set window register, which starts the write cycle */
179862306a36Sopenharmony_ci	ret = ath10k_sdio_write32(ar, MBOX_WINDOW_WRITE_ADDR_ADDRESS, address);
179962306a36Sopenharmony_ci	if (ret) {
180062306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set mbox window write address: %d", ret);
180162306a36Sopenharmony_ci		return ret;
180262306a36Sopenharmony_ci	}
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	return 0;
180562306a36Sopenharmony_ci}
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_cistatic int ath10k_sdio_hif_start_post(struct ath10k *ar)
180862306a36Sopenharmony_ci{
180962306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
181062306a36Sopenharmony_ci	u32 addr, val;
181162306a36Sopenharmony_ci	int ret = 0;
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci	addr = host_interest_item_address(HI_ITEM(hi_acs_flags));
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci	ret = ath10k_sdio_diag_read32(ar, addr, &val);
181662306a36Sopenharmony_ci	if (ret) {
181762306a36Sopenharmony_ci		ath10k_warn(ar, "unable to read hi_acs_flags : %d\n", ret);
181862306a36Sopenharmony_ci		return ret;
181962306a36Sopenharmony_ci	}
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci	if (val & HI_ACS_FLAGS_SDIO_SWAP_MAILBOX_FW_ACK) {
182262306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_SDIO,
182362306a36Sopenharmony_ci			   "sdio mailbox swap service enabled\n");
182462306a36Sopenharmony_ci		ar_sdio->swap_mbox = true;
182562306a36Sopenharmony_ci	} else {
182662306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_SDIO,
182762306a36Sopenharmony_ci			   "sdio mailbox swap service disabled\n");
182862306a36Sopenharmony_ci		ar_sdio->swap_mbox = false;
182962306a36Sopenharmony_ci	}
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	ath10k_sdio_set_mbox_sleep(ar, true);
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	return 0;
183462306a36Sopenharmony_ci}
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_cistatic int ath10k_sdio_get_htt_tx_complete(struct ath10k *ar)
183762306a36Sopenharmony_ci{
183862306a36Sopenharmony_ci	u32 addr, val;
183962306a36Sopenharmony_ci	int ret;
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci	addr = host_interest_item_address(HI_ITEM(hi_acs_flags));
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci	ret = ath10k_sdio_diag_read32(ar, addr, &val);
184462306a36Sopenharmony_ci	if (ret) {
184562306a36Sopenharmony_ci		ath10k_warn(ar,
184662306a36Sopenharmony_ci			    "unable to read hi_acs_flags for htt tx comple : %d\n", ret);
184762306a36Sopenharmony_ci		return ret;
184862306a36Sopenharmony_ci	}
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci	ret = (val & HI_ACS_FLAGS_SDIO_REDUCE_TX_COMPL_FW_ACK);
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio reduce tx complete fw%sack\n",
185362306a36Sopenharmony_ci		   ret ? " " : " not ");
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	return ret;
185662306a36Sopenharmony_ci}
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci/* HIF start/stop */
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_cistatic int ath10k_sdio_hif_start(struct ath10k *ar)
186162306a36Sopenharmony_ci{
186262306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
186362306a36Sopenharmony_ci	int ret;
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	ath10k_core_napi_enable(ar);
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	/* Sleep 20 ms before HIF interrupts are disabled.
186862306a36Sopenharmony_ci	 * This will give target plenty of time to process the BMI done
186962306a36Sopenharmony_ci	 * request before interrupts are disabled.
187062306a36Sopenharmony_ci	 */
187162306a36Sopenharmony_ci	msleep(20);
187262306a36Sopenharmony_ci	ret = ath10k_sdio_disable_intrs(ar);
187362306a36Sopenharmony_ci	if (ret)
187462306a36Sopenharmony_ci		return ret;
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	/* eid 0 always uses the lower part of the extended mailbox address
187762306a36Sopenharmony_ci	 * space (ext_info[0].htc_ext_addr).
187862306a36Sopenharmony_ci	 */
187962306a36Sopenharmony_ci	ar_sdio->mbox_addr[0] = ar_sdio->mbox_info.ext_info[0].htc_ext_addr;
188062306a36Sopenharmony_ci	ar_sdio->mbox_size[0] = ar_sdio->mbox_info.ext_info[0].htc_ext_sz;
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci	sdio_claim_host(ar_sdio->func);
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci	/* Register the isr */
188562306a36Sopenharmony_ci	ret =  sdio_claim_irq(ar_sdio->func, ath10k_sdio_irq_handler);
188662306a36Sopenharmony_ci	if (ret) {
188762306a36Sopenharmony_ci		ath10k_warn(ar, "failed to claim sdio interrupt: %d\n", ret);
188862306a36Sopenharmony_ci		sdio_release_host(ar_sdio->func);
188962306a36Sopenharmony_ci		return ret;
189062306a36Sopenharmony_ci	}
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	sdio_release_host(ar_sdio->func);
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci	ret = ath10k_sdio_enable_intrs(ar);
189562306a36Sopenharmony_ci	if (ret)
189662306a36Sopenharmony_ci		ath10k_warn(ar, "failed to enable sdio interrupts: %d\n", ret);
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci	/* Enable sleep and then disable it again */
189962306a36Sopenharmony_ci	ret = ath10k_sdio_set_mbox_sleep(ar, true);
190062306a36Sopenharmony_ci	if (ret)
190162306a36Sopenharmony_ci		return ret;
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci	/* Wait for 20ms for the written value to take effect */
190462306a36Sopenharmony_ci	msleep(20);
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	ret = ath10k_sdio_set_mbox_sleep(ar, false);
190762306a36Sopenharmony_ci	if (ret)
190862306a36Sopenharmony_ci		return ret;
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	return 0;
191162306a36Sopenharmony_ci}
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci#define SDIO_IRQ_DISABLE_TIMEOUT_HZ (3 * HZ)
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_cistatic void ath10k_sdio_irq_disable(struct ath10k *ar)
191662306a36Sopenharmony_ci{
191762306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
191862306a36Sopenharmony_ci	struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
191962306a36Sopenharmony_ci	struct ath10k_sdio_irq_enable_regs *regs = irq_data->irq_en_reg;
192062306a36Sopenharmony_ci	struct sk_buff *skb;
192162306a36Sopenharmony_ci	struct completion irqs_disabled_comp;
192262306a36Sopenharmony_ci	int ret;
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci	skb = dev_alloc_skb(sizeof(*regs));
192562306a36Sopenharmony_ci	if (!skb)
192662306a36Sopenharmony_ci		return;
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci	mutex_lock(&irq_data->mtx);
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	memset(regs, 0, sizeof(*regs)); /* disable all interrupts */
193162306a36Sopenharmony_ci	memcpy(skb->data, regs, sizeof(*regs));
193262306a36Sopenharmony_ci	skb_put(skb, sizeof(*regs));
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci	mutex_unlock(&irq_data->mtx);
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	init_completion(&irqs_disabled_comp);
193762306a36Sopenharmony_ci	ret = ath10k_sdio_prep_async_req(ar, MBOX_INT_STATUS_ENABLE_ADDRESS,
193862306a36Sopenharmony_ci					 skb, &irqs_disabled_comp, false, 0);
193962306a36Sopenharmony_ci	if (ret)
194062306a36Sopenharmony_ci		goto out;
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_ci	queue_work(ar_sdio->workqueue, &ar_sdio->wr_async_work);
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci	/* Wait for the completion of the IRQ disable request.
194562306a36Sopenharmony_ci	 * If there is a timeout we will try to disable irq's anyway.
194662306a36Sopenharmony_ci	 */
194762306a36Sopenharmony_ci	ret = wait_for_completion_timeout(&irqs_disabled_comp,
194862306a36Sopenharmony_ci					  SDIO_IRQ_DISABLE_TIMEOUT_HZ);
194962306a36Sopenharmony_ci	if (!ret)
195062306a36Sopenharmony_ci		ath10k_warn(ar, "sdio irq disable request timed out\n");
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_ci	sdio_claim_host(ar_sdio->func);
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci	ret = sdio_release_irq(ar_sdio->func);
195562306a36Sopenharmony_ci	if (ret)
195662306a36Sopenharmony_ci		ath10k_warn(ar, "failed to release sdio interrupt: %d\n", ret);
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	sdio_release_host(ar_sdio->func);
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_ciout:
196162306a36Sopenharmony_ci	kfree_skb(skb);
196262306a36Sopenharmony_ci}
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_cistatic void ath10k_sdio_hif_stop(struct ath10k *ar)
196562306a36Sopenharmony_ci{
196662306a36Sopenharmony_ci	struct ath10k_sdio_bus_request *req, *tmp_req;
196762306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
196862306a36Sopenharmony_ci	struct sk_buff *skb;
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci	ath10k_sdio_irq_disable(ar);
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci	cancel_work_sync(&ar_sdio->async_work_rx);
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci	while ((skb = skb_dequeue(&ar_sdio->rx_head)))
197562306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci	cancel_work_sync(&ar_sdio->wr_async_work);
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci	spin_lock_bh(&ar_sdio->wr_async_lock);
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	/* Free all bus requests that have not been handled */
198262306a36Sopenharmony_ci	list_for_each_entry_safe(req, tmp_req, &ar_sdio->wr_asyncq, list) {
198362306a36Sopenharmony_ci		struct ath10k_htc_ep *ep;
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_ci		list_del(&req->list);
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci		if (req->htc_msg) {
198862306a36Sopenharmony_ci			ep = &ar->htc.endpoint[req->eid];
198962306a36Sopenharmony_ci			ath10k_htc_notify_tx_completion(ep, req->skb);
199062306a36Sopenharmony_ci		} else if (req->skb) {
199162306a36Sopenharmony_ci			kfree_skb(req->skb);
199262306a36Sopenharmony_ci		}
199362306a36Sopenharmony_ci		ath10k_sdio_free_bus_req(ar, req);
199462306a36Sopenharmony_ci	}
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	spin_unlock_bh(&ar_sdio->wr_async_lock);
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci	ath10k_core_napi_sync_disable(ar);
199962306a36Sopenharmony_ci}
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_ci#ifdef CONFIG_PM
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_cistatic int ath10k_sdio_hif_suspend(struct ath10k *ar)
200462306a36Sopenharmony_ci{
200562306a36Sopenharmony_ci	return 0;
200662306a36Sopenharmony_ci}
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_cistatic int ath10k_sdio_hif_resume(struct ath10k *ar)
200962306a36Sopenharmony_ci{
201062306a36Sopenharmony_ci	switch (ar->state) {
201162306a36Sopenharmony_ci	case ATH10K_STATE_OFF:
201262306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_SDIO,
201362306a36Sopenharmony_ci			   "sdio resume configuring sdio\n");
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci		/* need to set sdio settings after power is cut from sdio */
201662306a36Sopenharmony_ci		ath10k_sdio_config(ar);
201762306a36Sopenharmony_ci		break;
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci	case ATH10K_STATE_ON:
202062306a36Sopenharmony_ci	default:
202162306a36Sopenharmony_ci		break;
202262306a36Sopenharmony_ci	}
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_ci	return 0;
202562306a36Sopenharmony_ci}
202662306a36Sopenharmony_ci#endif
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_cistatic int ath10k_sdio_hif_map_service_to_pipe(struct ath10k *ar,
202962306a36Sopenharmony_ci					       u16 service_id,
203062306a36Sopenharmony_ci					       u8 *ul_pipe, u8 *dl_pipe)
203162306a36Sopenharmony_ci{
203262306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
203362306a36Sopenharmony_ci	struct ath10k_htc *htc = &ar->htc;
203462306a36Sopenharmony_ci	u32 htt_addr, wmi_addr, htt_mbox_size, wmi_mbox_size;
203562306a36Sopenharmony_ci	enum ath10k_htc_ep_id eid;
203662306a36Sopenharmony_ci	bool ep_found = false;
203762306a36Sopenharmony_ci	int i;
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci	/* For sdio, we are interested in the mapping between eid
204062306a36Sopenharmony_ci	 * and pipeid rather than service_id to pipe_id.
204162306a36Sopenharmony_ci	 * First we find out which eid has been allocated to the
204262306a36Sopenharmony_ci	 * service...
204362306a36Sopenharmony_ci	 */
204462306a36Sopenharmony_ci	for (i = 0; i < ATH10K_HTC_EP_COUNT; i++) {
204562306a36Sopenharmony_ci		if (htc->endpoint[i].service_id == service_id) {
204662306a36Sopenharmony_ci			eid = htc->endpoint[i].eid;
204762306a36Sopenharmony_ci			ep_found = true;
204862306a36Sopenharmony_ci			break;
204962306a36Sopenharmony_ci		}
205062306a36Sopenharmony_ci	}
205162306a36Sopenharmony_ci
205262306a36Sopenharmony_ci	if (!ep_found)
205362306a36Sopenharmony_ci		return -EINVAL;
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	/* Then we create the simplest mapping possible between pipeid
205662306a36Sopenharmony_ci	 * and eid
205762306a36Sopenharmony_ci	 */
205862306a36Sopenharmony_ci	*ul_pipe = *dl_pipe = (u8)eid;
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci	/* Normally, HTT will use the upper part of the extended
206162306a36Sopenharmony_ci	 * mailbox address space (ext_info[1].htc_ext_addr) and WMI ctrl
206262306a36Sopenharmony_ci	 * the lower part (ext_info[0].htc_ext_addr).
206362306a36Sopenharmony_ci	 * If fw wants swapping of mailbox addresses, the opposite is true.
206462306a36Sopenharmony_ci	 */
206562306a36Sopenharmony_ci	if (ar_sdio->swap_mbox) {
206662306a36Sopenharmony_ci		htt_addr = ar_sdio->mbox_info.ext_info[0].htc_ext_addr;
206762306a36Sopenharmony_ci		wmi_addr = ar_sdio->mbox_info.ext_info[1].htc_ext_addr;
206862306a36Sopenharmony_ci		htt_mbox_size = ar_sdio->mbox_info.ext_info[0].htc_ext_sz;
206962306a36Sopenharmony_ci		wmi_mbox_size = ar_sdio->mbox_info.ext_info[1].htc_ext_sz;
207062306a36Sopenharmony_ci	} else {
207162306a36Sopenharmony_ci		htt_addr = ar_sdio->mbox_info.ext_info[1].htc_ext_addr;
207262306a36Sopenharmony_ci		wmi_addr = ar_sdio->mbox_info.ext_info[0].htc_ext_addr;
207362306a36Sopenharmony_ci		htt_mbox_size = ar_sdio->mbox_info.ext_info[1].htc_ext_sz;
207462306a36Sopenharmony_ci		wmi_mbox_size = ar_sdio->mbox_info.ext_info[0].htc_ext_sz;
207562306a36Sopenharmony_ci	}
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci	switch (service_id) {
207862306a36Sopenharmony_ci	case ATH10K_HTC_SVC_ID_RSVD_CTRL:
207962306a36Sopenharmony_ci		/* HTC ctrl ep mbox address has already been setup in
208062306a36Sopenharmony_ci		 * ath10k_sdio_hif_start
208162306a36Sopenharmony_ci		 */
208262306a36Sopenharmony_ci		break;
208362306a36Sopenharmony_ci	case ATH10K_HTC_SVC_ID_WMI_CONTROL:
208462306a36Sopenharmony_ci		ar_sdio->mbox_addr[eid] = wmi_addr;
208562306a36Sopenharmony_ci		ar_sdio->mbox_size[eid] = wmi_mbox_size;
208662306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_SDIO,
208762306a36Sopenharmony_ci			   "sdio wmi ctrl mbox_addr 0x%x mbox_size %d\n",
208862306a36Sopenharmony_ci			   ar_sdio->mbox_addr[eid], ar_sdio->mbox_size[eid]);
208962306a36Sopenharmony_ci		break;
209062306a36Sopenharmony_ci	case ATH10K_HTC_SVC_ID_HTT_DATA_MSG:
209162306a36Sopenharmony_ci		ar_sdio->mbox_addr[eid] = htt_addr;
209262306a36Sopenharmony_ci		ar_sdio->mbox_size[eid] = htt_mbox_size;
209362306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_SDIO,
209462306a36Sopenharmony_ci			   "sdio htt data mbox_addr 0x%x mbox_size %d\n",
209562306a36Sopenharmony_ci			   ar_sdio->mbox_addr[eid], ar_sdio->mbox_size[eid]);
209662306a36Sopenharmony_ci		break;
209762306a36Sopenharmony_ci	default:
209862306a36Sopenharmony_ci		ath10k_warn(ar, "unsupported HTC service id: %d\n",
209962306a36Sopenharmony_ci			    service_id);
210062306a36Sopenharmony_ci		return -EINVAL;
210162306a36Sopenharmony_ci	}
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci	return 0;
210462306a36Sopenharmony_ci}
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_cistatic void ath10k_sdio_hif_get_default_pipe(struct ath10k *ar,
210762306a36Sopenharmony_ci					     u8 *ul_pipe, u8 *dl_pipe)
210862306a36Sopenharmony_ci{
210962306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio hif get default pipe\n");
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_ci	/* HTC ctrl ep (SVC id 1) always has eid (and pipe_id in our
211262306a36Sopenharmony_ci	 * case) == 0
211362306a36Sopenharmony_ci	 */
211462306a36Sopenharmony_ci	*ul_pipe = 0;
211562306a36Sopenharmony_ci	*dl_pipe = 0;
211662306a36Sopenharmony_ci}
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_cistatic const struct ath10k_hif_ops ath10k_sdio_hif_ops = {
211962306a36Sopenharmony_ci	.tx_sg			= ath10k_sdio_hif_tx_sg,
212062306a36Sopenharmony_ci	.diag_read		= ath10k_sdio_hif_diag_read,
212162306a36Sopenharmony_ci	.diag_write		= ath10k_sdio_hif_diag_write_mem,
212262306a36Sopenharmony_ci	.exchange_bmi_msg	= ath10k_sdio_bmi_exchange_msg,
212362306a36Sopenharmony_ci	.start			= ath10k_sdio_hif_start,
212462306a36Sopenharmony_ci	.stop			= ath10k_sdio_hif_stop,
212562306a36Sopenharmony_ci	.start_post		= ath10k_sdio_hif_start_post,
212662306a36Sopenharmony_ci	.get_htt_tx_complete	= ath10k_sdio_get_htt_tx_complete,
212762306a36Sopenharmony_ci	.map_service_to_pipe	= ath10k_sdio_hif_map_service_to_pipe,
212862306a36Sopenharmony_ci	.get_default_pipe	= ath10k_sdio_hif_get_default_pipe,
212962306a36Sopenharmony_ci	.power_up		= ath10k_sdio_hif_power_up,
213062306a36Sopenharmony_ci	.power_down		= ath10k_sdio_hif_power_down,
213162306a36Sopenharmony_ci#ifdef CONFIG_PM
213262306a36Sopenharmony_ci	.suspend		= ath10k_sdio_hif_suspend,
213362306a36Sopenharmony_ci	.resume			= ath10k_sdio_hif_resume,
213462306a36Sopenharmony_ci#endif
213562306a36Sopenharmony_ci};
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci/* Empty handlers so that mmc subsystem doesn't remove us entirely during
214062306a36Sopenharmony_ci * suspend. We instead follow cfg80211 suspend/resume handlers.
214162306a36Sopenharmony_ci */
214262306a36Sopenharmony_cistatic int ath10k_sdio_pm_suspend(struct device *device)
214362306a36Sopenharmony_ci{
214462306a36Sopenharmony_ci	struct sdio_func *func = dev_to_sdio_func(device);
214562306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = sdio_get_drvdata(func);
214662306a36Sopenharmony_ci	struct ath10k *ar = ar_sdio->ar;
214762306a36Sopenharmony_ci	mmc_pm_flag_t pm_flag, pm_caps;
214862306a36Sopenharmony_ci	int ret;
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	if (!device_may_wakeup(ar->dev))
215162306a36Sopenharmony_ci		return 0;
215262306a36Sopenharmony_ci
215362306a36Sopenharmony_ci	ath10k_sdio_set_mbox_sleep(ar, true);
215462306a36Sopenharmony_ci
215562306a36Sopenharmony_ci	pm_flag = MMC_PM_KEEP_POWER;
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_ci	ret = sdio_set_host_pm_flags(func, pm_flag);
215862306a36Sopenharmony_ci	if (ret) {
215962306a36Sopenharmony_ci		pm_caps = sdio_get_host_pm_caps(func);
216062306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set sdio host pm flags (0x%x, 0x%x): %d\n",
216162306a36Sopenharmony_ci			    pm_flag, pm_caps, ret);
216262306a36Sopenharmony_ci		return ret;
216362306a36Sopenharmony_ci	}
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_ci	return ret;
216662306a36Sopenharmony_ci}
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_cistatic int ath10k_sdio_pm_resume(struct device *device)
216962306a36Sopenharmony_ci{
217062306a36Sopenharmony_ci	return 0;
217162306a36Sopenharmony_ci}
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(ath10k_sdio_pm_ops, ath10k_sdio_pm_suspend,
217462306a36Sopenharmony_ci			 ath10k_sdio_pm_resume);
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci#define ATH10K_SDIO_PM_OPS (&ath10k_sdio_pm_ops)
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci#else
217962306a36Sopenharmony_ci
218062306a36Sopenharmony_ci#define ATH10K_SDIO_PM_OPS NULL
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_cistatic int ath10k_sdio_napi_poll(struct napi_struct *ctx, int budget)
218562306a36Sopenharmony_ci{
218662306a36Sopenharmony_ci	struct ath10k *ar = container_of(ctx, struct ath10k, napi);
218762306a36Sopenharmony_ci	int done;
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_ci	done = ath10k_htt_rx_hl_indication(ar, budget);
219062306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "napi poll: done: %d, budget:%d\n", done, budget);
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_ci	if (done < budget)
219362306a36Sopenharmony_ci		napi_complete_done(ctx, done);
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci	return done;
219662306a36Sopenharmony_ci}
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_cistatic int ath10k_sdio_read_host_interest_value(struct ath10k *ar,
219962306a36Sopenharmony_ci						u32 item_offset,
220062306a36Sopenharmony_ci						u32 *val)
220162306a36Sopenharmony_ci{
220262306a36Sopenharmony_ci	u32 addr;
220362306a36Sopenharmony_ci	int ret;
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci	addr = host_interest_item_address(item_offset);
220662306a36Sopenharmony_ci
220762306a36Sopenharmony_ci	ret = ath10k_sdio_diag_read32(ar, addr, val);
220862306a36Sopenharmony_ci
220962306a36Sopenharmony_ci	if (ret)
221062306a36Sopenharmony_ci		ath10k_warn(ar, "unable to read host interest offset %d value\n",
221162306a36Sopenharmony_ci			    item_offset);
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci	return ret;
221462306a36Sopenharmony_ci}
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_cistatic int ath10k_sdio_read_mem(struct ath10k *ar, u32 address, void *buf,
221762306a36Sopenharmony_ci				u32 buf_len)
221862306a36Sopenharmony_ci{
221962306a36Sopenharmony_ci	u32 val;
222062306a36Sopenharmony_ci	int i, ret;
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci	for (i = 0; i < buf_len; i += 4) {
222362306a36Sopenharmony_ci		ret = ath10k_sdio_diag_read32(ar, address + i, &val);
222462306a36Sopenharmony_ci		if (ret) {
222562306a36Sopenharmony_ci			ath10k_warn(ar, "unable to read mem %d value\n", address + i);
222662306a36Sopenharmony_ci			break;
222762306a36Sopenharmony_ci		}
222862306a36Sopenharmony_ci		memcpy(buf + i, &val, 4);
222962306a36Sopenharmony_ci	}
223062306a36Sopenharmony_ci
223162306a36Sopenharmony_ci	return ret;
223262306a36Sopenharmony_ci}
223362306a36Sopenharmony_ci
223462306a36Sopenharmony_cistatic bool ath10k_sdio_is_fast_dump_supported(struct ath10k *ar)
223562306a36Sopenharmony_ci{
223662306a36Sopenharmony_ci	u32 param;
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_ci	ath10k_sdio_read_host_interest_value(ar, HI_ITEM(hi_option_flag2), &param);
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio hi_option_flag2 %x\n", param);
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_ci	return !!(param & HI_OPTION_SDIO_CRASH_DUMP_ENHANCEMENT_FW);
224362306a36Sopenharmony_ci}
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_cistatic void ath10k_sdio_dump_registers(struct ath10k *ar,
224662306a36Sopenharmony_ci				       struct ath10k_fw_crash_data *crash_data,
224762306a36Sopenharmony_ci				       bool fast_dump)
224862306a36Sopenharmony_ci{
224962306a36Sopenharmony_ci	u32 reg_dump_values[REG_DUMP_COUNT_QCA988X] = {};
225062306a36Sopenharmony_ci	int i, ret;
225162306a36Sopenharmony_ci	u32 reg_dump_area;
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_ci	ret = ath10k_sdio_read_host_interest_value(ar, HI_ITEM(hi_failure_state),
225462306a36Sopenharmony_ci						   &reg_dump_area);
225562306a36Sopenharmony_ci	if (ret) {
225662306a36Sopenharmony_ci		ath10k_warn(ar, "failed to read firmware dump area: %d\n", ret);
225762306a36Sopenharmony_ci		return;
225862306a36Sopenharmony_ci	}
225962306a36Sopenharmony_ci
226062306a36Sopenharmony_ci	if (fast_dump)
226162306a36Sopenharmony_ci		ret = ath10k_bmi_read_memory(ar, reg_dump_area, reg_dump_values,
226262306a36Sopenharmony_ci					     sizeof(reg_dump_values));
226362306a36Sopenharmony_ci	else
226462306a36Sopenharmony_ci		ret = ath10k_sdio_read_mem(ar, reg_dump_area, reg_dump_values,
226562306a36Sopenharmony_ci					   sizeof(reg_dump_values));
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_ci	if (ret) {
226862306a36Sopenharmony_ci		ath10k_warn(ar, "failed to read firmware dump value: %d\n", ret);
226962306a36Sopenharmony_ci		return;
227062306a36Sopenharmony_ci	}
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci	ath10k_err(ar, "firmware register dump:\n");
227362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(reg_dump_values); i += 4)
227462306a36Sopenharmony_ci		ath10k_err(ar, "[%02d]: 0x%08X 0x%08X 0x%08X 0x%08X\n",
227562306a36Sopenharmony_ci			   i,
227662306a36Sopenharmony_ci			   reg_dump_values[i],
227762306a36Sopenharmony_ci			   reg_dump_values[i + 1],
227862306a36Sopenharmony_ci			   reg_dump_values[i + 2],
227962306a36Sopenharmony_ci			   reg_dump_values[i + 3]);
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_ci	if (!crash_data)
228262306a36Sopenharmony_ci		return;
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(reg_dump_values); i++)
228562306a36Sopenharmony_ci		crash_data->registers[i] = __cpu_to_le32(reg_dump_values[i]);
228662306a36Sopenharmony_ci}
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_cistatic int ath10k_sdio_dump_memory_section(struct ath10k *ar,
228962306a36Sopenharmony_ci					   const struct ath10k_mem_region *mem_region,
229062306a36Sopenharmony_ci					   u8 *buf, size_t buf_len)
229162306a36Sopenharmony_ci{
229262306a36Sopenharmony_ci	const struct ath10k_mem_section *cur_section, *next_section;
229362306a36Sopenharmony_ci	unsigned int count, section_size, skip_size;
229462306a36Sopenharmony_ci	int ret, i, j;
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci	if (!mem_region || !buf)
229762306a36Sopenharmony_ci		return 0;
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci	cur_section = &mem_region->section_table.sections[0];
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_ci	if (mem_region->start > cur_section->start) {
230262306a36Sopenharmony_ci		ath10k_warn(ar, "incorrect memdump region 0x%x with section start address 0x%x.\n",
230362306a36Sopenharmony_ci			    mem_region->start, cur_section->start);
230462306a36Sopenharmony_ci		return 0;
230562306a36Sopenharmony_ci	}
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_ci	skip_size = cur_section->start - mem_region->start;
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_ci	/* fill the gap between the first register section and register
231062306a36Sopenharmony_ci	 * start address
231162306a36Sopenharmony_ci	 */
231262306a36Sopenharmony_ci	for (i = 0; i < skip_size; i++) {
231362306a36Sopenharmony_ci		*buf = ATH10K_MAGIC_NOT_COPIED;
231462306a36Sopenharmony_ci		buf++;
231562306a36Sopenharmony_ci	}
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_ci	count = 0;
231862306a36Sopenharmony_ci	i = 0;
231962306a36Sopenharmony_ci	for (; cur_section; cur_section = next_section) {
232062306a36Sopenharmony_ci		section_size = cur_section->end - cur_section->start;
232162306a36Sopenharmony_ci
232262306a36Sopenharmony_ci		if (section_size <= 0) {
232362306a36Sopenharmony_ci			ath10k_warn(ar, "incorrect ramdump format with start address 0x%x and stop address 0x%x\n",
232462306a36Sopenharmony_ci				    cur_section->start,
232562306a36Sopenharmony_ci				    cur_section->end);
232662306a36Sopenharmony_ci			break;
232762306a36Sopenharmony_ci		}
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_ci		if (++i == mem_region->section_table.size) {
233062306a36Sopenharmony_ci			/* last section */
233162306a36Sopenharmony_ci			next_section = NULL;
233262306a36Sopenharmony_ci			skip_size = 0;
233362306a36Sopenharmony_ci		} else {
233462306a36Sopenharmony_ci			next_section = cur_section + 1;
233562306a36Sopenharmony_ci
233662306a36Sopenharmony_ci			if (cur_section->end > next_section->start) {
233762306a36Sopenharmony_ci				ath10k_warn(ar, "next ramdump section 0x%x is smaller than current end address 0x%x\n",
233862306a36Sopenharmony_ci					    next_section->start,
233962306a36Sopenharmony_ci					    cur_section->end);
234062306a36Sopenharmony_ci				break;
234162306a36Sopenharmony_ci			}
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci			skip_size = next_section->start - cur_section->end;
234462306a36Sopenharmony_ci		}
234562306a36Sopenharmony_ci
234662306a36Sopenharmony_ci		if (buf_len < (skip_size + section_size)) {
234762306a36Sopenharmony_ci			ath10k_warn(ar, "ramdump buffer is too small: %zu\n", buf_len);
234862306a36Sopenharmony_ci			break;
234962306a36Sopenharmony_ci		}
235062306a36Sopenharmony_ci
235162306a36Sopenharmony_ci		buf_len -= skip_size + section_size;
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci		/* read section to dest memory */
235462306a36Sopenharmony_ci		ret = ath10k_sdio_read_mem(ar, cur_section->start,
235562306a36Sopenharmony_ci					   buf, section_size);
235662306a36Sopenharmony_ci		if (ret) {
235762306a36Sopenharmony_ci			ath10k_warn(ar, "failed to read ramdump from section 0x%x: %d\n",
235862306a36Sopenharmony_ci				    cur_section->start, ret);
235962306a36Sopenharmony_ci			break;
236062306a36Sopenharmony_ci		}
236162306a36Sopenharmony_ci
236262306a36Sopenharmony_ci		buf += section_size;
236362306a36Sopenharmony_ci		count += section_size;
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci		/* fill in the gap between this section and the next */
236662306a36Sopenharmony_ci		for (j = 0; j < skip_size; j++) {
236762306a36Sopenharmony_ci			*buf = ATH10K_MAGIC_NOT_COPIED;
236862306a36Sopenharmony_ci			buf++;
236962306a36Sopenharmony_ci		}
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_ci		count += skip_size;
237262306a36Sopenharmony_ci	}
237362306a36Sopenharmony_ci
237462306a36Sopenharmony_ci	return count;
237562306a36Sopenharmony_ci}
237662306a36Sopenharmony_ci
237762306a36Sopenharmony_ci/* if an error happened returns < 0, otherwise the length */
237862306a36Sopenharmony_cistatic int ath10k_sdio_dump_memory_generic(struct ath10k *ar,
237962306a36Sopenharmony_ci					   const struct ath10k_mem_region *current_region,
238062306a36Sopenharmony_ci					   u8 *buf,
238162306a36Sopenharmony_ci					   bool fast_dump)
238262306a36Sopenharmony_ci{
238362306a36Sopenharmony_ci	int ret;
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_ci	if (current_region->section_table.size > 0)
238662306a36Sopenharmony_ci		/* Copy each section individually. */
238762306a36Sopenharmony_ci		return ath10k_sdio_dump_memory_section(ar,
238862306a36Sopenharmony_ci						      current_region,
238962306a36Sopenharmony_ci						      buf,
239062306a36Sopenharmony_ci						      current_region->len);
239162306a36Sopenharmony_ci
239262306a36Sopenharmony_ci	/* No individual memory sections defined so we can
239362306a36Sopenharmony_ci	 * copy the entire memory region.
239462306a36Sopenharmony_ci	 */
239562306a36Sopenharmony_ci	if (fast_dump)
239662306a36Sopenharmony_ci		ret = ath10k_bmi_read_memory(ar,
239762306a36Sopenharmony_ci					     current_region->start,
239862306a36Sopenharmony_ci					     buf,
239962306a36Sopenharmony_ci					     current_region->len);
240062306a36Sopenharmony_ci	else
240162306a36Sopenharmony_ci		ret = ath10k_sdio_read_mem(ar,
240262306a36Sopenharmony_ci					   current_region->start,
240362306a36Sopenharmony_ci					   buf,
240462306a36Sopenharmony_ci					   current_region->len);
240562306a36Sopenharmony_ci
240662306a36Sopenharmony_ci	if (ret) {
240762306a36Sopenharmony_ci		ath10k_warn(ar, "failed to copy ramdump region %s: %d\n",
240862306a36Sopenharmony_ci			    current_region->name, ret);
240962306a36Sopenharmony_ci		return ret;
241062306a36Sopenharmony_ci	}
241162306a36Sopenharmony_ci
241262306a36Sopenharmony_ci	return current_region->len;
241362306a36Sopenharmony_ci}
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_cistatic void ath10k_sdio_dump_memory(struct ath10k *ar,
241662306a36Sopenharmony_ci				    struct ath10k_fw_crash_data *crash_data,
241762306a36Sopenharmony_ci				    bool fast_dump)
241862306a36Sopenharmony_ci{
241962306a36Sopenharmony_ci	const struct ath10k_hw_mem_layout *mem_layout;
242062306a36Sopenharmony_ci	const struct ath10k_mem_region *current_region;
242162306a36Sopenharmony_ci	struct ath10k_dump_ram_data_hdr *hdr;
242262306a36Sopenharmony_ci	u32 count;
242362306a36Sopenharmony_ci	size_t buf_len;
242462306a36Sopenharmony_ci	int ret, i;
242562306a36Sopenharmony_ci	u8 *buf;
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_ci	if (!crash_data)
242862306a36Sopenharmony_ci		return;
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_ci	mem_layout = ath10k_coredump_get_mem_layout(ar);
243162306a36Sopenharmony_ci	if (!mem_layout)
243262306a36Sopenharmony_ci		return;
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_ci	current_region = &mem_layout->region_table.regions[0];
243562306a36Sopenharmony_ci
243662306a36Sopenharmony_ci	buf = crash_data->ramdump_buf;
243762306a36Sopenharmony_ci	buf_len = crash_data->ramdump_buf_len;
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_ci	memset(buf, 0, buf_len);
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_ci	for (i = 0; i < mem_layout->region_table.size; i++) {
244262306a36Sopenharmony_ci		count = 0;
244362306a36Sopenharmony_ci
244462306a36Sopenharmony_ci		if (current_region->len > buf_len) {
244562306a36Sopenharmony_ci			ath10k_warn(ar, "memory region %s size %d is larger that remaining ramdump buffer size %zu\n",
244662306a36Sopenharmony_ci				    current_region->name,
244762306a36Sopenharmony_ci				    current_region->len,
244862306a36Sopenharmony_ci				    buf_len);
244962306a36Sopenharmony_ci			break;
245062306a36Sopenharmony_ci		}
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci		/* Reserve space for the header. */
245362306a36Sopenharmony_ci		hdr = (void *)buf;
245462306a36Sopenharmony_ci		buf += sizeof(*hdr);
245562306a36Sopenharmony_ci		buf_len -= sizeof(*hdr);
245662306a36Sopenharmony_ci
245762306a36Sopenharmony_ci		ret = ath10k_sdio_dump_memory_generic(ar, current_region, buf,
245862306a36Sopenharmony_ci						      fast_dump);
245962306a36Sopenharmony_ci		if (ret >= 0)
246062306a36Sopenharmony_ci			count = ret;
246162306a36Sopenharmony_ci
246262306a36Sopenharmony_ci		hdr->region_type = cpu_to_le32(current_region->type);
246362306a36Sopenharmony_ci		hdr->start = cpu_to_le32(current_region->start);
246462306a36Sopenharmony_ci		hdr->length = cpu_to_le32(count);
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci		if (count == 0)
246762306a36Sopenharmony_ci			/* Note: the header remains, just with zero length. */
246862306a36Sopenharmony_ci			break;
246962306a36Sopenharmony_ci
247062306a36Sopenharmony_ci		buf += count;
247162306a36Sopenharmony_ci		buf_len -= count;
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_ci		current_region++;
247462306a36Sopenharmony_ci	}
247562306a36Sopenharmony_ci}
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_civoid ath10k_sdio_fw_crashed_dump(struct ath10k *ar)
247862306a36Sopenharmony_ci{
247962306a36Sopenharmony_ci	struct ath10k_fw_crash_data *crash_data;
248062306a36Sopenharmony_ci	char guid[UUID_STRING_LEN + 1];
248162306a36Sopenharmony_ci	bool fast_dump;
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_ci	fast_dump = ath10k_sdio_is_fast_dump_supported(ar);
248462306a36Sopenharmony_ci
248562306a36Sopenharmony_ci	if (fast_dump)
248662306a36Sopenharmony_ci		ath10k_bmi_start(ar);
248762306a36Sopenharmony_ci
248862306a36Sopenharmony_ci	ar->stats.fw_crash_counter++;
248962306a36Sopenharmony_ci
249062306a36Sopenharmony_ci	ath10k_sdio_disable_intrs(ar);
249162306a36Sopenharmony_ci
249262306a36Sopenharmony_ci	crash_data = ath10k_coredump_new(ar);
249362306a36Sopenharmony_ci
249462306a36Sopenharmony_ci	if (crash_data)
249562306a36Sopenharmony_ci		scnprintf(guid, sizeof(guid), "%pUl", &crash_data->guid);
249662306a36Sopenharmony_ci	else
249762306a36Sopenharmony_ci		scnprintf(guid, sizeof(guid), "n/a");
249862306a36Sopenharmony_ci
249962306a36Sopenharmony_ci	ath10k_err(ar, "firmware crashed! (guid %s)\n", guid);
250062306a36Sopenharmony_ci	ath10k_print_driver_info(ar);
250162306a36Sopenharmony_ci	ath10k_sdio_dump_registers(ar, crash_data, fast_dump);
250262306a36Sopenharmony_ci	ath10k_sdio_dump_memory(ar, crash_data, fast_dump);
250362306a36Sopenharmony_ci
250462306a36Sopenharmony_ci	ath10k_sdio_enable_intrs(ar);
250562306a36Sopenharmony_ci
250662306a36Sopenharmony_ci	ath10k_core_start_recovery(ar);
250762306a36Sopenharmony_ci}
250862306a36Sopenharmony_ci
250962306a36Sopenharmony_cistatic int ath10k_sdio_probe(struct sdio_func *func,
251062306a36Sopenharmony_ci			     const struct sdio_device_id *id)
251162306a36Sopenharmony_ci{
251262306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio;
251362306a36Sopenharmony_ci	struct ath10k *ar;
251462306a36Sopenharmony_ci	enum ath10k_hw_rev hw_rev;
251562306a36Sopenharmony_ci	u32 dev_id_base;
251662306a36Sopenharmony_ci	struct ath10k_bus_params bus_params = {};
251762306a36Sopenharmony_ci	int ret, i;
251862306a36Sopenharmony_ci
251962306a36Sopenharmony_ci	/* Assumption: All SDIO based chipsets (so far) are QCA6174 based.
252062306a36Sopenharmony_ci	 * If there will be newer chipsets that does not use the hw reg
252162306a36Sopenharmony_ci	 * setup as defined in qca6174_regs and qca6174_values, this
252262306a36Sopenharmony_ci	 * assumption is no longer valid and hw_rev must be setup differently
252362306a36Sopenharmony_ci	 * depending on chipset.
252462306a36Sopenharmony_ci	 */
252562306a36Sopenharmony_ci	hw_rev = ATH10K_HW_QCA6174;
252662306a36Sopenharmony_ci
252762306a36Sopenharmony_ci	ar = ath10k_core_create(sizeof(*ar_sdio), &func->dev, ATH10K_BUS_SDIO,
252862306a36Sopenharmony_ci				hw_rev, &ath10k_sdio_hif_ops);
252962306a36Sopenharmony_ci	if (!ar) {
253062306a36Sopenharmony_ci		dev_err(&func->dev, "failed to allocate core\n");
253162306a36Sopenharmony_ci		return -ENOMEM;
253262306a36Sopenharmony_ci	}
253362306a36Sopenharmony_ci
253462306a36Sopenharmony_ci	netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_sdio_napi_poll);
253562306a36Sopenharmony_ci
253662306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_BOOT,
253762306a36Sopenharmony_ci		   "sdio new func %d vendor 0x%x device 0x%x block 0x%x/0x%x\n",
253862306a36Sopenharmony_ci		   func->num, func->vendor, func->device,
253962306a36Sopenharmony_ci		   func->max_blksize, func->cur_blksize);
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ci	ar_sdio = ath10k_sdio_priv(ar);
254262306a36Sopenharmony_ci
254362306a36Sopenharmony_ci	ar_sdio->irq_data.irq_proc_reg =
254462306a36Sopenharmony_ci		devm_kzalloc(ar->dev, sizeof(struct ath10k_sdio_irq_proc_regs),
254562306a36Sopenharmony_ci			     GFP_KERNEL);
254662306a36Sopenharmony_ci	if (!ar_sdio->irq_data.irq_proc_reg) {
254762306a36Sopenharmony_ci		ret = -ENOMEM;
254862306a36Sopenharmony_ci		goto err_core_destroy;
254962306a36Sopenharmony_ci	}
255062306a36Sopenharmony_ci
255162306a36Sopenharmony_ci	ar_sdio->vsg_buffer = devm_kmalloc(ar->dev, ATH10K_SDIO_VSG_BUF_SIZE, GFP_KERNEL);
255262306a36Sopenharmony_ci	if (!ar_sdio->vsg_buffer) {
255362306a36Sopenharmony_ci		ret = -ENOMEM;
255462306a36Sopenharmony_ci		goto err_core_destroy;
255562306a36Sopenharmony_ci	}
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_ci	ar_sdio->irq_data.irq_en_reg =
255862306a36Sopenharmony_ci		devm_kzalloc(ar->dev, sizeof(struct ath10k_sdio_irq_enable_regs),
255962306a36Sopenharmony_ci			     GFP_KERNEL);
256062306a36Sopenharmony_ci	if (!ar_sdio->irq_data.irq_en_reg) {
256162306a36Sopenharmony_ci		ret = -ENOMEM;
256262306a36Sopenharmony_ci		goto err_core_destroy;
256362306a36Sopenharmony_ci	}
256462306a36Sopenharmony_ci
256562306a36Sopenharmony_ci	ar_sdio->bmi_buf = devm_kzalloc(ar->dev, BMI_MAX_LARGE_CMDBUF_SIZE, GFP_KERNEL);
256662306a36Sopenharmony_ci	if (!ar_sdio->bmi_buf) {
256762306a36Sopenharmony_ci		ret = -ENOMEM;
256862306a36Sopenharmony_ci		goto err_core_destroy;
256962306a36Sopenharmony_ci	}
257062306a36Sopenharmony_ci
257162306a36Sopenharmony_ci	ar_sdio->func = func;
257262306a36Sopenharmony_ci	sdio_set_drvdata(func, ar_sdio);
257362306a36Sopenharmony_ci
257462306a36Sopenharmony_ci	ar_sdio->is_disabled = true;
257562306a36Sopenharmony_ci	ar_sdio->ar = ar;
257662306a36Sopenharmony_ci
257762306a36Sopenharmony_ci	spin_lock_init(&ar_sdio->lock);
257862306a36Sopenharmony_ci	spin_lock_init(&ar_sdio->wr_async_lock);
257962306a36Sopenharmony_ci	mutex_init(&ar_sdio->irq_data.mtx);
258062306a36Sopenharmony_ci
258162306a36Sopenharmony_ci	INIT_LIST_HEAD(&ar_sdio->bus_req_freeq);
258262306a36Sopenharmony_ci	INIT_LIST_HEAD(&ar_sdio->wr_asyncq);
258362306a36Sopenharmony_ci
258462306a36Sopenharmony_ci	INIT_WORK(&ar_sdio->wr_async_work, ath10k_sdio_write_async_work);
258562306a36Sopenharmony_ci	ar_sdio->workqueue = create_singlethread_workqueue("ath10k_sdio_wq");
258662306a36Sopenharmony_ci	if (!ar_sdio->workqueue) {
258762306a36Sopenharmony_ci		ret = -ENOMEM;
258862306a36Sopenharmony_ci		goto err_core_destroy;
258962306a36Sopenharmony_ci	}
259062306a36Sopenharmony_ci
259162306a36Sopenharmony_ci	for (i = 0; i < ATH10K_SDIO_BUS_REQUEST_MAX_NUM; i++)
259262306a36Sopenharmony_ci		ath10k_sdio_free_bus_req(ar, &ar_sdio->bus_req[i]);
259362306a36Sopenharmony_ci
259462306a36Sopenharmony_ci	skb_queue_head_init(&ar_sdio->rx_head);
259562306a36Sopenharmony_ci	INIT_WORK(&ar_sdio->async_work_rx, ath10k_rx_indication_async_work);
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_ci	dev_id_base = (id->device & 0x0F00);
259862306a36Sopenharmony_ci	if (dev_id_base != (SDIO_DEVICE_ID_ATHEROS_AR6005 & 0x0F00) &&
259962306a36Sopenharmony_ci	    dev_id_base != (SDIO_DEVICE_ID_ATHEROS_QCA9377 & 0x0F00)) {
260062306a36Sopenharmony_ci		ret = -ENODEV;
260162306a36Sopenharmony_ci		ath10k_err(ar, "unsupported device id %u (0x%x)\n",
260262306a36Sopenharmony_ci			   dev_id_base, id->device);
260362306a36Sopenharmony_ci		goto err_free_wq;
260462306a36Sopenharmony_ci	}
260562306a36Sopenharmony_ci
260662306a36Sopenharmony_ci	ar->dev_id = QCA9377_1_0_DEVICE_ID;
260762306a36Sopenharmony_ci	ar->id.vendor = id->vendor;
260862306a36Sopenharmony_ci	ar->id.device = id->device;
260962306a36Sopenharmony_ci
261062306a36Sopenharmony_ci	ath10k_sdio_set_mbox_info(ar);
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_ci	bus_params.dev_type = ATH10K_DEV_TYPE_HL;
261362306a36Sopenharmony_ci	/* TODO: don't know yet how to get chip_id with SDIO */
261462306a36Sopenharmony_ci	bus_params.chip_id = 0;
261562306a36Sopenharmony_ci	bus_params.hl_msdu_ids = true;
261662306a36Sopenharmony_ci
261762306a36Sopenharmony_ci	ar->hw->max_mtu = ETH_DATA_LEN;
261862306a36Sopenharmony_ci
261962306a36Sopenharmony_ci	ret = ath10k_core_register(ar, &bus_params);
262062306a36Sopenharmony_ci	if (ret) {
262162306a36Sopenharmony_ci		ath10k_err(ar, "failed to register driver core: %d\n", ret);
262262306a36Sopenharmony_ci		goto err_free_wq;
262362306a36Sopenharmony_ci	}
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_ci	timer_setup(&ar_sdio->sleep_timer, ath10k_sdio_sleep_timer_handler, 0);
262662306a36Sopenharmony_ci
262762306a36Sopenharmony_ci	return 0;
262862306a36Sopenharmony_ci
262962306a36Sopenharmony_cierr_free_wq:
263062306a36Sopenharmony_ci	destroy_workqueue(ar_sdio->workqueue);
263162306a36Sopenharmony_cierr_core_destroy:
263262306a36Sopenharmony_ci	ath10k_core_destroy(ar);
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	return ret;
263562306a36Sopenharmony_ci}
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_cistatic void ath10k_sdio_remove(struct sdio_func *func)
263862306a36Sopenharmony_ci{
263962306a36Sopenharmony_ci	struct ath10k_sdio *ar_sdio = sdio_get_drvdata(func);
264062306a36Sopenharmony_ci	struct ath10k *ar = ar_sdio->ar;
264162306a36Sopenharmony_ci
264262306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_BOOT,
264362306a36Sopenharmony_ci		   "sdio removed func %d vendor 0x%x device 0x%x\n",
264462306a36Sopenharmony_ci		   func->num, func->vendor, func->device);
264562306a36Sopenharmony_ci
264662306a36Sopenharmony_ci	ath10k_core_unregister(ar);
264762306a36Sopenharmony_ci
264862306a36Sopenharmony_ci	netif_napi_del(&ar->napi);
264962306a36Sopenharmony_ci
265062306a36Sopenharmony_ci	ath10k_core_destroy(ar);
265162306a36Sopenharmony_ci
265262306a36Sopenharmony_ci	destroy_workqueue(ar_sdio->workqueue);
265362306a36Sopenharmony_ci}
265462306a36Sopenharmony_ci
265562306a36Sopenharmony_cistatic const struct sdio_device_id ath10k_sdio_devices[] = {
265662306a36Sopenharmony_ci	{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6005)},
265762306a36Sopenharmony_ci	{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_QCA9377)},
265862306a36Sopenharmony_ci	{},
265962306a36Sopenharmony_ci};
266062306a36Sopenharmony_ci
266162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(sdio, ath10k_sdio_devices);
266262306a36Sopenharmony_ci
266362306a36Sopenharmony_cistatic struct sdio_driver ath10k_sdio_driver = {
266462306a36Sopenharmony_ci	.name = "ath10k_sdio",
266562306a36Sopenharmony_ci	.id_table = ath10k_sdio_devices,
266662306a36Sopenharmony_ci	.probe = ath10k_sdio_probe,
266762306a36Sopenharmony_ci	.remove = ath10k_sdio_remove,
266862306a36Sopenharmony_ci	.drv = {
266962306a36Sopenharmony_ci		.owner = THIS_MODULE,
267062306a36Sopenharmony_ci		.pm = ATH10K_SDIO_PM_OPS,
267162306a36Sopenharmony_ci	},
267262306a36Sopenharmony_ci};
267362306a36Sopenharmony_ci
267462306a36Sopenharmony_cistatic int __init ath10k_sdio_init(void)
267562306a36Sopenharmony_ci{
267662306a36Sopenharmony_ci	int ret;
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_ci	ret = sdio_register_driver(&ath10k_sdio_driver);
267962306a36Sopenharmony_ci	if (ret)
268062306a36Sopenharmony_ci		pr_err("sdio driver registration failed: %d\n", ret);
268162306a36Sopenharmony_ci
268262306a36Sopenharmony_ci	return ret;
268362306a36Sopenharmony_ci}
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_cistatic void __exit ath10k_sdio_exit(void)
268662306a36Sopenharmony_ci{
268762306a36Sopenharmony_ci	sdio_unregister_driver(&ath10k_sdio_driver);
268862306a36Sopenharmony_ci}
268962306a36Sopenharmony_ci
269062306a36Sopenharmony_cimodule_init(ath10k_sdio_init);
269162306a36Sopenharmony_cimodule_exit(ath10k_sdio_exit);
269262306a36Sopenharmony_ci
269362306a36Sopenharmony_ciMODULE_AUTHOR("Qualcomm Atheros");
269462306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver support for Qualcomm Atheros 802.11ac WLAN SDIO devices");
269562306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
2696