xref: /freebsd/sys/contrib/dev/rtw88/sdio.c (revision 11c53278a8a3e86e14377f09bbaa7bad193d3713)
190aac0d8SBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
290aac0d8SBjoern A. Zeeb /* Copyright (C) 2021 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
390aac0d8SBjoern A. Zeeb  * Copyright (C) 2021 Jernej Skrabec <jernej.skrabec@gmail.com>
490aac0d8SBjoern A. Zeeb  *
590aac0d8SBjoern A. Zeeb  * Based on rtw88/pci.c:
690aac0d8SBjoern A. Zeeb  *   Copyright(c) 2018-2019  Realtek Corporation
790aac0d8SBjoern A. Zeeb  */
890aac0d8SBjoern A. Zeeb 
990aac0d8SBjoern A. Zeeb #include <linux/module.h>
1090aac0d8SBjoern A. Zeeb #include <linux/mmc/host.h>
1190aac0d8SBjoern A. Zeeb #include <linux/mmc/sdio_func.h>
1290aac0d8SBjoern A. Zeeb #include "main.h"
1390aac0d8SBjoern A. Zeeb #include "debug.h"
1490aac0d8SBjoern A. Zeeb #include "fw.h"
1590aac0d8SBjoern A. Zeeb #include "ps.h"
1690aac0d8SBjoern A. Zeeb #include "reg.h"
1790aac0d8SBjoern A. Zeeb #include "rx.h"
1890aac0d8SBjoern A. Zeeb #include "sdio.h"
1990aac0d8SBjoern A. Zeeb #include "tx.h"
2090aac0d8SBjoern A. Zeeb 
2190aac0d8SBjoern A. Zeeb #define RTW_SDIO_INDIRECT_RW_RETRIES			50
2290aac0d8SBjoern A. Zeeb 
2390aac0d8SBjoern A. Zeeb static bool rtw_sdio_is_bus_addr(u32 addr)
2490aac0d8SBjoern A. Zeeb {
2590aac0d8SBjoern A. Zeeb 	return !!(addr & RTW_SDIO_BUS_MSK);
2690aac0d8SBjoern A. Zeeb }
2790aac0d8SBjoern A. Zeeb 
2890aac0d8SBjoern A. Zeeb static bool rtw_sdio_bus_claim_needed(struct rtw_sdio *rtwsdio)
2990aac0d8SBjoern A. Zeeb {
3090aac0d8SBjoern A. Zeeb 	return !rtwsdio->irq_thread ||
3190aac0d8SBjoern A. Zeeb 	       rtwsdio->irq_thread != current;
3290aac0d8SBjoern A. Zeeb }
3390aac0d8SBjoern A. Zeeb 
3490aac0d8SBjoern A. Zeeb static u32 rtw_sdio_to_bus_offset(struct rtw_dev *rtwdev, u32 addr)
3590aac0d8SBjoern A. Zeeb {
3690aac0d8SBjoern A. Zeeb 	switch (addr & RTW_SDIO_BUS_MSK) {
3790aac0d8SBjoern A. Zeeb 	case WLAN_IOREG_OFFSET:
3890aac0d8SBjoern A. Zeeb 		addr &= WLAN_IOREG_REG_MSK;
3990aac0d8SBjoern A. Zeeb 		addr |= FIELD_PREP(REG_SDIO_CMD_ADDR_MSK,
4090aac0d8SBjoern A. Zeeb 				   REG_SDIO_CMD_ADDR_MAC_REG);
4190aac0d8SBjoern A. Zeeb 		break;
4290aac0d8SBjoern A. Zeeb 	case SDIO_LOCAL_OFFSET:
4390aac0d8SBjoern A. Zeeb 		addr &= SDIO_LOCAL_REG_MSK;
4490aac0d8SBjoern A. Zeeb 		addr |= FIELD_PREP(REG_SDIO_CMD_ADDR_MSK,
4590aac0d8SBjoern A. Zeeb 				   REG_SDIO_CMD_ADDR_SDIO_REG);
4690aac0d8SBjoern A. Zeeb 		break;
4790aac0d8SBjoern A. Zeeb 	default:
4890aac0d8SBjoern A. Zeeb 		rtw_warn(rtwdev, "Cannot convert addr 0x%08x to bus offset",
4990aac0d8SBjoern A. Zeeb 			 addr);
5090aac0d8SBjoern A. Zeeb 	}
5190aac0d8SBjoern A. Zeeb 
5290aac0d8SBjoern A. Zeeb 	return addr;
5390aac0d8SBjoern A. Zeeb }
5490aac0d8SBjoern A. Zeeb 
5590aac0d8SBjoern A. Zeeb static bool rtw_sdio_use_memcpy_io(struct rtw_dev *rtwdev, u32 addr,
5690aac0d8SBjoern A. Zeeb 				   u8 alignment)
5790aac0d8SBjoern A. Zeeb {
5890aac0d8SBjoern A. Zeeb 	return IS_ALIGNED(addr, alignment) &&
5990aac0d8SBjoern A. Zeeb 	       test_bit(RTW_FLAG_POWERON, rtwdev->flags);
6090aac0d8SBjoern A. Zeeb }
6190aac0d8SBjoern A. Zeeb 
6290aac0d8SBjoern A. Zeeb static void rtw_sdio_writel(struct rtw_dev *rtwdev, u32 val, u32 addr,
6390aac0d8SBjoern A. Zeeb 			    int *err_ret)
6490aac0d8SBjoern A. Zeeb {
6590aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
6690aac0d8SBjoern A. Zeeb 	u8 buf[4];
6790aac0d8SBjoern A. Zeeb 	int i;
6890aac0d8SBjoern A. Zeeb 
6990aac0d8SBjoern A. Zeeb 	if (rtw_sdio_use_memcpy_io(rtwdev, addr, 4)) {
7090aac0d8SBjoern A. Zeeb 		sdio_writel(rtwsdio->sdio_func, val, addr, err_ret);
7190aac0d8SBjoern A. Zeeb 		return;
7290aac0d8SBjoern A. Zeeb 	}
7390aac0d8SBjoern A. Zeeb 
7490aac0d8SBjoern A. Zeeb 	*(__le32 *)buf = cpu_to_le32(val);
7590aac0d8SBjoern A. Zeeb 
7690aac0d8SBjoern A. Zeeb 	for (i = 0; i < 4; i++) {
7790aac0d8SBjoern A. Zeeb 		sdio_writeb(rtwsdio->sdio_func, buf[i], addr + i, err_ret);
7890aac0d8SBjoern A. Zeeb 		if (*err_ret)
7990aac0d8SBjoern A. Zeeb 			return;
8090aac0d8SBjoern A. Zeeb 	}
8190aac0d8SBjoern A. Zeeb }
8290aac0d8SBjoern A. Zeeb 
8390aac0d8SBjoern A. Zeeb static void rtw_sdio_writew(struct rtw_dev *rtwdev, u16 val, u32 addr,
8490aac0d8SBjoern A. Zeeb 			    int *err_ret)
8590aac0d8SBjoern A. Zeeb {
8690aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
8790aac0d8SBjoern A. Zeeb 	u8 buf[2];
8890aac0d8SBjoern A. Zeeb 	int i;
8990aac0d8SBjoern A. Zeeb 
9090aac0d8SBjoern A. Zeeb 	*(__le16 *)buf = cpu_to_le16(val);
9190aac0d8SBjoern A. Zeeb 
9290aac0d8SBjoern A. Zeeb 	for (i = 0; i < 2; i++) {
9390aac0d8SBjoern A. Zeeb 		sdio_writeb(rtwsdio->sdio_func, buf[i], addr + i, err_ret);
9490aac0d8SBjoern A. Zeeb 		if (*err_ret)
9590aac0d8SBjoern A. Zeeb 			return;
9690aac0d8SBjoern A. Zeeb 	}
9790aac0d8SBjoern A. Zeeb }
9890aac0d8SBjoern A. Zeeb 
9990aac0d8SBjoern A. Zeeb static u32 rtw_sdio_readl(struct rtw_dev *rtwdev, u32 addr, int *err_ret)
10090aac0d8SBjoern A. Zeeb {
10190aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
10290aac0d8SBjoern A. Zeeb 	u8 buf[4];
10390aac0d8SBjoern A. Zeeb 	int i;
10490aac0d8SBjoern A. Zeeb 
10590aac0d8SBjoern A. Zeeb 	if (rtw_sdio_use_memcpy_io(rtwdev, addr, 4))
10690aac0d8SBjoern A. Zeeb 		return sdio_readl(rtwsdio->sdio_func, addr, err_ret);
10790aac0d8SBjoern A. Zeeb 
10890aac0d8SBjoern A. Zeeb 	for (i = 0; i < 4; i++) {
10990aac0d8SBjoern A. Zeeb 		buf[i] = sdio_readb(rtwsdio->sdio_func, addr + i, err_ret);
11090aac0d8SBjoern A. Zeeb 		if (*err_ret)
11190aac0d8SBjoern A. Zeeb 			return 0;
11290aac0d8SBjoern A. Zeeb 	}
11390aac0d8SBjoern A. Zeeb 
11490aac0d8SBjoern A. Zeeb 	return le32_to_cpu(*(__le32 *)buf);
11590aac0d8SBjoern A. Zeeb }
11690aac0d8SBjoern A. Zeeb 
11790aac0d8SBjoern A. Zeeb static u16 rtw_sdio_readw(struct rtw_dev *rtwdev, u32 addr, int *err_ret)
11890aac0d8SBjoern A. Zeeb {
11990aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
12090aac0d8SBjoern A. Zeeb 	u8 buf[2];
12190aac0d8SBjoern A. Zeeb 	int i;
12290aac0d8SBjoern A. Zeeb 
12390aac0d8SBjoern A. Zeeb 	for (i = 0; i < 2; i++) {
12490aac0d8SBjoern A. Zeeb 		buf[i] = sdio_readb(rtwsdio->sdio_func, addr + i, err_ret);
12590aac0d8SBjoern A. Zeeb 		if (*err_ret)
12690aac0d8SBjoern A. Zeeb 			return 0;
12790aac0d8SBjoern A. Zeeb 	}
12890aac0d8SBjoern A. Zeeb 
12990aac0d8SBjoern A. Zeeb 	return le16_to_cpu(*(__le16 *)buf);
13090aac0d8SBjoern A. Zeeb }
13190aac0d8SBjoern A. Zeeb 
13290aac0d8SBjoern A. Zeeb static u32 rtw_sdio_to_io_address(struct rtw_dev *rtwdev, u32 addr,
13390aac0d8SBjoern A. Zeeb 				  bool direct)
13490aac0d8SBjoern A. Zeeb {
13590aac0d8SBjoern A. Zeeb 	if (!direct)
13690aac0d8SBjoern A. Zeeb 		return addr;
13790aac0d8SBjoern A. Zeeb 
13890aac0d8SBjoern A. Zeeb 	if (!rtw_sdio_is_bus_addr(addr))
13990aac0d8SBjoern A. Zeeb 		addr |= WLAN_IOREG_OFFSET;
14090aac0d8SBjoern A. Zeeb 
14190aac0d8SBjoern A. Zeeb 	return rtw_sdio_to_bus_offset(rtwdev, addr);
14290aac0d8SBjoern A. Zeeb }
14390aac0d8SBjoern A. Zeeb 
14490aac0d8SBjoern A. Zeeb static bool rtw_sdio_use_direct_io(struct rtw_dev *rtwdev, u32 addr)
14590aac0d8SBjoern A. Zeeb {
14690aac0d8SBjoern A. Zeeb 	return !rtw_sdio_is_sdio30_supported(rtwdev) ||
14790aac0d8SBjoern A. Zeeb 		rtw_sdio_is_bus_addr(addr);
14890aac0d8SBjoern A. Zeeb }
14990aac0d8SBjoern A. Zeeb 
15090aac0d8SBjoern A. Zeeb static int rtw_sdio_indirect_reg_cfg(struct rtw_dev *rtwdev, u32 addr, u32 cfg)
15190aac0d8SBjoern A. Zeeb {
15290aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
15390aac0d8SBjoern A. Zeeb 	unsigned int retry;
15490aac0d8SBjoern A. Zeeb 	u32 reg_cfg;
15590aac0d8SBjoern A. Zeeb 	int ret;
15690aac0d8SBjoern A. Zeeb 	u8 tmp;
15790aac0d8SBjoern A. Zeeb 
15890aac0d8SBjoern A. Zeeb 	reg_cfg = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_CFG);
15990aac0d8SBjoern A. Zeeb 
16090aac0d8SBjoern A. Zeeb 	rtw_sdio_writel(rtwdev, addr | cfg | BIT_SDIO_INDIRECT_REG_CFG_UNK20,
16190aac0d8SBjoern A. Zeeb 			reg_cfg, &ret);
16290aac0d8SBjoern A. Zeeb 	if (ret)
16390aac0d8SBjoern A. Zeeb 		return ret;
16490aac0d8SBjoern A. Zeeb 
16590aac0d8SBjoern A. Zeeb 	for (retry = 0; retry < RTW_SDIO_INDIRECT_RW_RETRIES; retry++) {
16690aac0d8SBjoern A. Zeeb 		tmp = sdio_readb(rtwsdio->sdio_func, reg_cfg + 2, &ret);
16790aac0d8SBjoern A. Zeeb 		if (!ret && (tmp & BIT(4)))
16890aac0d8SBjoern A. Zeeb 			return 0;
16990aac0d8SBjoern A. Zeeb 	}
17090aac0d8SBjoern A. Zeeb 
17190aac0d8SBjoern A. Zeeb 	return -ETIMEDOUT;
17290aac0d8SBjoern A. Zeeb }
17390aac0d8SBjoern A. Zeeb 
17490aac0d8SBjoern A. Zeeb static u8 rtw_sdio_indirect_read8(struct rtw_dev *rtwdev, u32 addr,
17590aac0d8SBjoern A. Zeeb 				  int *err_ret)
17690aac0d8SBjoern A. Zeeb {
17790aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
17890aac0d8SBjoern A. Zeeb 	u32 reg_data;
17990aac0d8SBjoern A. Zeeb 
18090aac0d8SBjoern A. Zeeb 	*err_ret = rtw_sdio_indirect_reg_cfg(rtwdev, addr,
18190aac0d8SBjoern A. Zeeb 					     BIT_SDIO_INDIRECT_REG_CFG_READ);
18290aac0d8SBjoern A. Zeeb 	if (*err_ret)
18390aac0d8SBjoern A. Zeeb 		return 0;
18490aac0d8SBjoern A. Zeeb 
18590aac0d8SBjoern A. Zeeb 	reg_data = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_DATA);
18690aac0d8SBjoern A. Zeeb 	return sdio_readb(rtwsdio->sdio_func, reg_data, err_ret);
18790aac0d8SBjoern A. Zeeb }
18890aac0d8SBjoern A. Zeeb 
18990aac0d8SBjoern A. Zeeb static int rtw_sdio_indirect_read_bytes(struct rtw_dev *rtwdev, u32 addr,
19090aac0d8SBjoern A. Zeeb 					u8 *buf, int count)
19190aac0d8SBjoern A. Zeeb {
19290aac0d8SBjoern A. Zeeb 	int i, ret = 0;
19390aac0d8SBjoern A. Zeeb 
19490aac0d8SBjoern A. Zeeb 	for (i = 0; i < count; i++) {
19590aac0d8SBjoern A. Zeeb 		buf[i] = rtw_sdio_indirect_read8(rtwdev, addr + i, &ret);
19690aac0d8SBjoern A. Zeeb 		if (ret)
19790aac0d8SBjoern A. Zeeb 			break;
19890aac0d8SBjoern A. Zeeb 	}
19990aac0d8SBjoern A. Zeeb 
20090aac0d8SBjoern A. Zeeb 	return ret;
20190aac0d8SBjoern A. Zeeb }
20290aac0d8SBjoern A. Zeeb 
20390aac0d8SBjoern A. Zeeb static u16 rtw_sdio_indirect_read16(struct rtw_dev *rtwdev, u32 addr,
20490aac0d8SBjoern A. Zeeb 				    int *err_ret)
20590aac0d8SBjoern A. Zeeb {
20690aac0d8SBjoern A. Zeeb 	u32 reg_data;
20790aac0d8SBjoern A. Zeeb 	u8 buf[2];
20890aac0d8SBjoern A. Zeeb 
20990aac0d8SBjoern A. Zeeb 	if (!IS_ALIGNED(addr, 2)) {
21090aac0d8SBjoern A. Zeeb 		*err_ret = rtw_sdio_indirect_read_bytes(rtwdev, addr, buf, 2);
21190aac0d8SBjoern A. Zeeb 		if (*err_ret)
21290aac0d8SBjoern A. Zeeb 			return 0;
21390aac0d8SBjoern A. Zeeb 
21490aac0d8SBjoern A. Zeeb 		return le16_to_cpu(*(__le16 *)buf);
21590aac0d8SBjoern A. Zeeb 	}
21690aac0d8SBjoern A. Zeeb 
21790aac0d8SBjoern A. Zeeb 	*err_ret = rtw_sdio_indirect_reg_cfg(rtwdev, addr,
21890aac0d8SBjoern A. Zeeb 					     BIT_SDIO_INDIRECT_REG_CFG_READ);
21990aac0d8SBjoern A. Zeeb 	if (*err_ret)
22090aac0d8SBjoern A. Zeeb 		return 0;
22190aac0d8SBjoern A. Zeeb 
22290aac0d8SBjoern A. Zeeb 	reg_data = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_DATA);
22390aac0d8SBjoern A. Zeeb 	return rtw_sdio_readw(rtwdev, reg_data, err_ret);
22490aac0d8SBjoern A. Zeeb }
22590aac0d8SBjoern A. Zeeb 
22690aac0d8SBjoern A. Zeeb static u32 rtw_sdio_indirect_read32(struct rtw_dev *rtwdev, u32 addr,
22790aac0d8SBjoern A. Zeeb 				    int *err_ret)
22890aac0d8SBjoern A. Zeeb {
22990aac0d8SBjoern A. Zeeb 	u32 reg_data;
23090aac0d8SBjoern A. Zeeb 	u8 buf[4];
23190aac0d8SBjoern A. Zeeb 
23290aac0d8SBjoern A. Zeeb 	if (!IS_ALIGNED(addr, 4)) {
23390aac0d8SBjoern A. Zeeb 		*err_ret = rtw_sdio_indirect_read_bytes(rtwdev, addr, buf, 4);
23490aac0d8SBjoern A. Zeeb 		if (*err_ret)
23590aac0d8SBjoern A. Zeeb 			return 0;
23690aac0d8SBjoern A. Zeeb 
23790aac0d8SBjoern A. Zeeb 		return le32_to_cpu(*(__le32 *)buf);
23890aac0d8SBjoern A. Zeeb 	}
23990aac0d8SBjoern A. Zeeb 
24090aac0d8SBjoern A. Zeeb 	*err_ret = rtw_sdio_indirect_reg_cfg(rtwdev, addr,
24190aac0d8SBjoern A. Zeeb 					     BIT_SDIO_INDIRECT_REG_CFG_READ);
24290aac0d8SBjoern A. Zeeb 	if (*err_ret)
24390aac0d8SBjoern A. Zeeb 		return 0;
24490aac0d8SBjoern A. Zeeb 
24590aac0d8SBjoern A. Zeeb 	reg_data = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_DATA);
24690aac0d8SBjoern A. Zeeb 	return rtw_sdio_readl(rtwdev, reg_data, err_ret);
24790aac0d8SBjoern A. Zeeb }
24890aac0d8SBjoern A. Zeeb 
24990aac0d8SBjoern A. Zeeb static u8 rtw_sdio_read8(struct rtw_dev *rtwdev, u32 addr)
25090aac0d8SBjoern A. Zeeb {
25190aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
25290aac0d8SBjoern A. Zeeb 	bool direct, bus_claim;
25390aac0d8SBjoern A. Zeeb 	int ret;
25490aac0d8SBjoern A. Zeeb 	u8 val;
25590aac0d8SBjoern A. Zeeb 
25690aac0d8SBjoern A. Zeeb 	direct = rtw_sdio_use_direct_io(rtwdev, addr);
25790aac0d8SBjoern A. Zeeb 	addr = rtw_sdio_to_io_address(rtwdev, addr, direct);
25890aac0d8SBjoern A. Zeeb 	bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
25990aac0d8SBjoern A. Zeeb 
26090aac0d8SBjoern A. Zeeb 	if (bus_claim)
26190aac0d8SBjoern A. Zeeb 		sdio_claim_host(rtwsdio->sdio_func);
26290aac0d8SBjoern A. Zeeb 
26390aac0d8SBjoern A. Zeeb 	if (direct)
26490aac0d8SBjoern A. Zeeb 		val = sdio_readb(rtwsdio->sdio_func, addr, &ret);
26590aac0d8SBjoern A. Zeeb 	else
26690aac0d8SBjoern A. Zeeb 		val = rtw_sdio_indirect_read8(rtwdev, addr, &ret);
26790aac0d8SBjoern A. Zeeb 
26890aac0d8SBjoern A. Zeeb 	if (bus_claim)
26990aac0d8SBjoern A. Zeeb 		sdio_release_host(rtwsdio->sdio_func);
27090aac0d8SBjoern A. Zeeb 
27190aac0d8SBjoern A. Zeeb 	if (ret)
27290aac0d8SBjoern A. Zeeb 		rtw_warn(rtwdev, "sdio read8 failed (0x%x): %d", addr, ret);
27390aac0d8SBjoern A. Zeeb 
27490aac0d8SBjoern A. Zeeb 	return val;
27590aac0d8SBjoern A. Zeeb }
27690aac0d8SBjoern A. Zeeb 
27790aac0d8SBjoern A. Zeeb static u16 rtw_sdio_read16(struct rtw_dev *rtwdev, u32 addr)
27890aac0d8SBjoern A. Zeeb {
27990aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
28090aac0d8SBjoern A. Zeeb 	bool direct, bus_claim;
28190aac0d8SBjoern A. Zeeb 	int ret;
28290aac0d8SBjoern A. Zeeb 	u16 val;
28390aac0d8SBjoern A. Zeeb 
28490aac0d8SBjoern A. Zeeb 	direct = rtw_sdio_use_direct_io(rtwdev, addr);
28590aac0d8SBjoern A. Zeeb 	addr = rtw_sdio_to_io_address(rtwdev, addr, direct);
28690aac0d8SBjoern A. Zeeb 	bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
28790aac0d8SBjoern A. Zeeb 
28890aac0d8SBjoern A. Zeeb 	if (bus_claim)
28990aac0d8SBjoern A. Zeeb 		sdio_claim_host(rtwsdio->sdio_func);
29090aac0d8SBjoern A. Zeeb 
29190aac0d8SBjoern A. Zeeb 	if (direct)
29290aac0d8SBjoern A. Zeeb 		val = rtw_sdio_readw(rtwdev, addr, &ret);
29390aac0d8SBjoern A. Zeeb 	else
29490aac0d8SBjoern A. Zeeb 		val = rtw_sdio_indirect_read16(rtwdev, addr, &ret);
29590aac0d8SBjoern A. Zeeb 
29690aac0d8SBjoern A. Zeeb 	if (bus_claim)
29790aac0d8SBjoern A. Zeeb 		sdio_release_host(rtwsdio->sdio_func);
29890aac0d8SBjoern A. Zeeb 
29990aac0d8SBjoern A. Zeeb 	if (ret)
30090aac0d8SBjoern A. Zeeb 		rtw_warn(rtwdev, "sdio read16 failed (0x%x): %d", addr, ret);
30190aac0d8SBjoern A. Zeeb 
30290aac0d8SBjoern A. Zeeb 	return val;
30390aac0d8SBjoern A. Zeeb }
30490aac0d8SBjoern A. Zeeb 
30590aac0d8SBjoern A. Zeeb static u32 rtw_sdio_read32(struct rtw_dev *rtwdev, u32 addr)
30690aac0d8SBjoern A. Zeeb {
30790aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
30890aac0d8SBjoern A. Zeeb 	bool direct, bus_claim;
30990aac0d8SBjoern A. Zeeb 	u32 val;
31090aac0d8SBjoern A. Zeeb 	int ret;
31190aac0d8SBjoern A. Zeeb 
31290aac0d8SBjoern A. Zeeb 	direct = rtw_sdio_use_direct_io(rtwdev, addr);
31390aac0d8SBjoern A. Zeeb 	addr = rtw_sdio_to_io_address(rtwdev, addr, direct);
31490aac0d8SBjoern A. Zeeb 	bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
31590aac0d8SBjoern A. Zeeb 
31690aac0d8SBjoern A. Zeeb 	if (bus_claim)
31790aac0d8SBjoern A. Zeeb 		sdio_claim_host(rtwsdio->sdio_func);
31890aac0d8SBjoern A. Zeeb 
31990aac0d8SBjoern A. Zeeb 	if (direct)
32090aac0d8SBjoern A. Zeeb 		val = rtw_sdio_readl(rtwdev, addr, &ret);
32190aac0d8SBjoern A. Zeeb 	else
32290aac0d8SBjoern A. Zeeb 		val = rtw_sdio_indirect_read32(rtwdev, addr, &ret);
32390aac0d8SBjoern A. Zeeb 
32490aac0d8SBjoern A. Zeeb 	if (bus_claim)
32590aac0d8SBjoern A. Zeeb 		sdio_release_host(rtwsdio->sdio_func);
32690aac0d8SBjoern A. Zeeb 
32790aac0d8SBjoern A. Zeeb 	if (ret)
32890aac0d8SBjoern A. Zeeb 		rtw_warn(rtwdev, "sdio read32 failed (0x%x): %d", addr, ret);
32990aac0d8SBjoern A. Zeeb 
33090aac0d8SBjoern A. Zeeb 	return val;
33190aac0d8SBjoern A. Zeeb }
33290aac0d8SBjoern A. Zeeb 
33390aac0d8SBjoern A. Zeeb static void rtw_sdio_indirect_write8(struct rtw_dev *rtwdev, u8 val, u32 addr,
33490aac0d8SBjoern A. Zeeb 				     int *err_ret)
33590aac0d8SBjoern A. Zeeb {
33690aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
33790aac0d8SBjoern A. Zeeb 	u32 reg_data;
33890aac0d8SBjoern A. Zeeb 
33990aac0d8SBjoern A. Zeeb 	reg_data = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_DATA);
34090aac0d8SBjoern A. Zeeb 	sdio_writeb(rtwsdio->sdio_func, val, reg_data, err_ret);
34190aac0d8SBjoern A. Zeeb 	if (*err_ret)
34290aac0d8SBjoern A. Zeeb 		return;
34390aac0d8SBjoern A. Zeeb 
34490aac0d8SBjoern A. Zeeb 	*err_ret = rtw_sdio_indirect_reg_cfg(rtwdev, addr,
34590aac0d8SBjoern A. Zeeb 					     BIT_SDIO_INDIRECT_REG_CFG_WRITE);
34690aac0d8SBjoern A. Zeeb }
34790aac0d8SBjoern A. Zeeb 
34890aac0d8SBjoern A. Zeeb static void rtw_sdio_indirect_write16(struct rtw_dev *rtwdev, u16 val, u32 addr,
34990aac0d8SBjoern A. Zeeb 				      int *err_ret)
35090aac0d8SBjoern A. Zeeb {
35190aac0d8SBjoern A. Zeeb 	u32 reg_data;
35290aac0d8SBjoern A. Zeeb 
35390aac0d8SBjoern A. Zeeb 	if (!IS_ALIGNED(addr, 2)) {
35490aac0d8SBjoern A. Zeeb 		addr = rtw_sdio_to_io_address(rtwdev, addr, true);
35590aac0d8SBjoern A. Zeeb 		rtw_sdio_writew(rtwdev, val, addr, err_ret);
35690aac0d8SBjoern A. Zeeb 		return;
35790aac0d8SBjoern A. Zeeb 	}
35890aac0d8SBjoern A. Zeeb 
35990aac0d8SBjoern A. Zeeb 	reg_data = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_DATA);
36090aac0d8SBjoern A. Zeeb 	rtw_sdio_writew(rtwdev, val, reg_data, err_ret);
36190aac0d8SBjoern A. Zeeb 	if (*err_ret)
36290aac0d8SBjoern A. Zeeb 		return;
36390aac0d8SBjoern A. Zeeb 
36490aac0d8SBjoern A. Zeeb 	*err_ret = rtw_sdio_indirect_reg_cfg(rtwdev, addr,
36590aac0d8SBjoern A. Zeeb 					     BIT_SDIO_INDIRECT_REG_CFG_WRITE |
36690aac0d8SBjoern A. Zeeb 					     BIT_SDIO_INDIRECT_REG_CFG_WORD);
36790aac0d8SBjoern A. Zeeb }
36890aac0d8SBjoern A. Zeeb 
36990aac0d8SBjoern A. Zeeb static void rtw_sdio_indirect_write32(struct rtw_dev *rtwdev, u32 val,
37090aac0d8SBjoern A. Zeeb 				      u32 addr, int *err_ret)
37190aac0d8SBjoern A. Zeeb {
37290aac0d8SBjoern A. Zeeb 	u32 reg_data;
37390aac0d8SBjoern A. Zeeb 
37490aac0d8SBjoern A. Zeeb 	if (!IS_ALIGNED(addr, 4)) {
37590aac0d8SBjoern A. Zeeb 		addr = rtw_sdio_to_io_address(rtwdev, addr, true);
37690aac0d8SBjoern A. Zeeb 		rtw_sdio_writel(rtwdev, val, addr, err_ret);
37790aac0d8SBjoern A. Zeeb 		return;
37890aac0d8SBjoern A. Zeeb 	}
37990aac0d8SBjoern A. Zeeb 
38090aac0d8SBjoern A. Zeeb 	reg_data = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_DATA);
38190aac0d8SBjoern A. Zeeb 	rtw_sdio_writel(rtwdev, val, reg_data, err_ret);
38290aac0d8SBjoern A. Zeeb 
38390aac0d8SBjoern A. Zeeb 	*err_ret = rtw_sdio_indirect_reg_cfg(rtwdev, addr,
38490aac0d8SBjoern A. Zeeb 					     BIT_SDIO_INDIRECT_REG_CFG_WRITE |
38590aac0d8SBjoern A. Zeeb 					     BIT_SDIO_INDIRECT_REG_CFG_DWORD);
38690aac0d8SBjoern A. Zeeb }
38790aac0d8SBjoern A. Zeeb 
38890aac0d8SBjoern A. Zeeb static void rtw_sdio_write8(struct rtw_dev *rtwdev, u32 addr, u8 val)
38990aac0d8SBjoern A. Zeeb {
39090aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
39190aac0d8SBjoern A. Zeeb 	bool direct, bus_claim;
39290aac0d8SBjoern A. Zeeb 	int ret;
39390aac0d8SBjoern A. Zeeb 
39490aac0d8SBjoern A. Zeeb 	direct = rtw_sdio_use_direct_io(rtwdev, addr);
39590aac0d8SBjoern A. Zeeb 	addr = rtw_sdio_to_io_address(rtwdev, addr, direct);
39690aac0d8SBjoern A. Zeeb 	bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
39790aac0d8SBjoern A. Zeeb 
39890aac0d8SBjoern A. Zeeb 	if (bus_claim)
39990aac0d8SBjoern A. Zeeb 		sdio_claim_host(rtwsdio->sdio_func);
40090aac0d8SBjoern A. Zeeb 
40190aac0d8SBjoern A. Zeeb 	if (direct)
40290aac0d8SBjoern A. Zeeb 		sdio_writeb(rtwsdio->sdio_func, val, addr, &ret);
40390aac0d8SBjoern A. Zeeb 	else
40490aac0d8SBjoern A. Zeeb 		rtw_sdio_indirect_write8(rtwdev, val, addr, &ret);
40590aac0d8SBjoern A. Zeeb 
40690aac0d8SBjoern A. Zeeb 	if (bus_claim)
40790aac0d8SBjoern A. Zeeb 		sdio_release_host(rtwsdio->sdio_func);
40890aac0d8SBjoern A. Zeeb 
40990aac0d8SBjoern A. Zeeb 	if (ret)
41090aac0d8SBjoern A. Zeeb 		rtw_warn(rtwdev, "sdio write8 failed (0x%x): %d", addr, ret);
41190aac0d8SBjoern A. Zeeb }
41290aac0d8SBjoern A. Zeeb 
41390aac0d8SBjoern A. Zeeb static void rtw_sdio_write16(struct rtw_dev *rtwdev, u32 addr, u16 val)
41490aac0d8SBjoern A. Zeeb {
41590aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
41690aac0d8SBjoern A. Zeeb 	bool direct, bus_claim;
41790aac0d8SBjoern A. Zeeb 	int ret;
41890aac0d8SBjoern A. Zeeb 
41990aac0d8SBjoern A. Zeeb 	direct = rtw_sdio_use_direct_io(rtwdev, addr);
42090aac0d8SBjoern A. Zeeb 	addr = rtw_sdio_to_io_address(rtwdev, addr, direct);
42190aac0d8SBjoern A. Zeeb 	bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
42290aac0d8SBjoern A. Zeeb 
42390aac0d8SBjoern A. Zeeb 	if (bus_claim)
42490aac0d8SBjoern A. Zeeb 		sdio_claim_host(rtwsdio->sdio_func);
42590aac0d8SBjoern A. Zeeb 
42690aac0d8SBjoern A. Zeeb 	if (direct)
42790aac0d8SBjoern A. Zeeb 		rtw_sdio_writew(rtwdev, val, addr, &ret);
42890aac0d8SBjoern A. Zeeb 	else
42990aac0d8SBjoern A. Zeeb 		rtw_sdio_indirect_write16(rtwdev, val, addr, &ret);
43090aac0d8SBjoern A. Zeeb 
43190aac0d8SBjoern A. Zeeb 	if (bus_claim)
43290aac0d8SBjoern A. Zeeb 		sdio_release_host(rtwsdio->sdio_func);
43390aac0d8SBjoern A. Zeeb 
43490aac0d8SBjoern A. Zeeb 	if (ret)
43590aac0d8SBjoern A. Zeeb 		rtw_warn(rtwdev, "sdio write16 failed (0x%x): %d", addr, ret);
43690aac0d8SBjoern A. Zeeb }
43790aac0d8SBjoern A. Zeeb 
43890aac0d8SBjoern A. Zeeb static void rtw_sdio_write32(struct rtw_dev *rtwdev, u32 addr, u32 val)
43990aac0d8SBjoern A. Zeeb {
44090aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
44190aac0d8SBjoern A. Zeeb 	bool direct, bus_claim;
44290aac0d8SBjoern A. Zeeb 	int ret;
44390aac0d8SBjoern A. Zeeb 
44490aac0d8SBjoern A. Zeeb 	direct = rtw_sdio_use_direct_io(rtwdev, addr);
44590aac0d8SBjoern A. Zeeb 	addr = rtw_sdio_to_io_address(rtwdev, addr, direct);
44690aac0d8SBjoern A. Zeeb 	bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
44790aac0d8SBjoern A. Zeeb 
44890aac0d8SBjoern A. Zeeb 	if (bus_claim)
44990aac0d8SBjoern A. Zeeb 		sdio_claim_host(rtwsdio->sdio_func);
45090aac0d8SBjoern A. Zeeb 
45190aac0d8SBjoern A. Zeeb 	if (direct)
45290aac0d8SBjoern A. Zeeb 		rtw_sdio_writel(rtwdev, val, addr, &ret);
45390aac0d8SBjoern A. Zeeb 	else
45490aac0d8SBjoern A. Zeeb 		rtw_sdio_indirect_write32(rtwdev, val, addr, &ret);
45590aac0d8SBjoern A. Zeeb 
45690aac0d8SBjoern A. Zeeb 	if (bus_claim)
45790aac0d8SBjoern A. Zeeb 		sdio_release_host(rtwsdio->sdio_func);
45890aac0d8SBjoern A. Zeeb 
45990aac0d8SBjoern A. Zeeb 	if (ret)
46090aac0d8SBjoern A. Zeeb 		rtw_warn(rtwdev, "sdio write32 failed (0x%x): %d", addr, ret);
46190aac0d8SBjoern A. Zeeb }
46290aac0d8SBjoern A. Zeeb 
46390aac0d8SBjoern A. Zeeb static u32 rtw_sdio_get_tx_addr(struct rtw_dev *rtwdev, size_t size,
46490aac0d8SBjoern A. Zeeb 				enum rtw_tx_queue_type queue)
46590aac0d8SBjoern A. Zeeb {
46690aac0d8SBjoern A. Zeeb 	u32 txaddr;
46790aac0d8SBjoern A. Zeeb 
46890aac0d8SBjoern A. Zeeb 	switch (queue) {
46990aac0d8SBjoern A. Zeeb 	case RTW_TX_QUEUE_BCN:
47090aac0d8SBjoern A. Zeeb 	case RTW_TX_QUEUE_H2C:
47190aac0d8SBjoern A. Zeeb 	case RTW_TX_QUEUE_HI0:
47290aac0d8SBjoern A. Zeeb 		txaddr = FIELD_PREP(REG_SDIO_CMD_ADDR_MSK,
47390aac0d8SBjoern A. Zeeb 				    REG_SDIO_CMD_ADDR_TXFF_HIGH);
47490aac0d8SBjoern A. Zeeb 		break;
47590aac0d8SBjoern A. Zeeb 	case RTW_TX_QUEUE_VI:
47690aac0d8SBjoern A. Zeeb 	case RTW_TX_QUEUE_VO:
47790aac0d8SBjoern A. Zeeb 		txaddr = FIELD_PREP(REG_SDIO_CMD_ADDR_MSK,
47890aac0d8SBjoern A. Zeeb 				    REG_SDIO_CMD_ADDR_TXFF_NORMAL);
47990aac0d8SBjoern A. Zeeb 		break;
48090aac0d8SBjoern A. Zeeb 	case RTW_TX_QUEUE_BE:
48190aac0d8SBjoern A. Zeeb 	case RTW_TX_QUEUE_BK:
48290aac0d8SBjoern A. Zeeb 		txaddr = FIELD_PREP(REG_SDIO_CMD_ADDR_MSK,
48390aac0d8SBjoern A. Zeeb 				    REG_SDIO_CMD_ADDR_TXFF_LOW);
48490aac0d8SBjoern A. Zeeb 		break;
48590aac0d8SBjoern A. Zeeb 	case RTW_TX_QUEUE_MGMT:
48690aac0d8SBjoern A. Zeeb 		txaddr = FIELD_PREP(REG_SDIO_CMD_ADDR_MSK,
48790aac0d8SBjoern A. Zeeb 				    REG_SDIO_CMD_ADDR_TXFF_EXTRA);
48890aac0d8SBjoern A. Zeeb 		break;
48990aac0d8SBjoern A. Zeeb 	default:
49090aac0d8SBjoern A. Zeeb 		rtw_warn(rtwdev, "Unsupported queue for TX addr: 0x%02x\n",
49190aac0d8SBjoern A. Zeeb 			 queue);
49290aac0d8SBjoern A. Zeeb 		return 0;
49390aac0d8SBjoern A. Zeeb 	}
49490aac0d8SBjoern A. Zeeb 
49590aac0d8SBjoern A. Zeeb 	txaddr += DIV_ROUND_UP(size, 4);
49690aac0d8SBjoern A. Zeeb 
49790aac0d8SBjoern A. Zeeb 	return txaddr;
49890aac0d8SBjoern A. Zeeb };
49990aac0d8SBjoern A. Zeeb 
50090aac0d8SBjoern A. Zeeb static int rtw_sdio_read_port(struct rtw_dev *rtwdev, u8 *buf, size_t count)
50190aac0d8SBjoern A. Zeeb {
50290aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
503*11c53278SBjoern A. Zeeb 	struct mmc_host *host = rtwsdio->sdio_func->card->host;
50490aac0d8SBjoern A. Zeeb 	bool bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
50590aac0d8SBjoern A. Zeeb 	u32 rxaddr = rtwsdio->rx_addr++;
506*11c53278SBjoern A. Zeeb 	int ret = 0, err;
507*11c53278SBjoern A. Zeeb 	size_t bytes;
50890aac0d8SBjoern A. Zeeb 
50990aac0d8SBjoern A. Zeeb 	if (bus_claim)
51090aac0d8SBjoern A. Zeeb 		sdio_claim_host(rtwsdio->sdio_func);
51190aac0d8SBjoern A. Zeeb 
512*11c53278SBjoern A. Zeeb 	while (count > 0) {
513*11c53278SBjoern A. Zeeb 		bytes = min_t(size_t, host->max_req_size, count);
514*11c53278SBjoern A. Zeeb 
515*11c53278SBjoern A. Zeeb 		err = sdio_memcpy_fromio(rtwsdio->sdio_func, buf,
516*11c53278SBjoern A. Zeeb 					 RTW_SDIO_ADDR_RX_RX0FF_GEN(rxaddr),
517*11c53278SBjoern A. Zeeb 					 bytes);
518*11c53278SBjoern A. Zeeb 		if (err) {
51990aac0d8SBjoern A. Zeeb 			rtw_warn(rtwdev,
520*11c53278SBjoern A. Zeeb 				 "Failed to read %zu byte(s) from SDIO port 0x%08x: %d",
521*11c53278SBjoern A. Zeeb 				 bytes, rxaddr, err);
522*11c53278SBjoern A. Zeeb 
523*11c53278SBjoern A. Zeeb 			 /* Signal to the caller that reading did not work and
524*11c53278SBjoern A. Zeeb 			  * that the data in the buffer is short/corrupted.
525*11c53278SBjoern A. Zeeb 			  */
526*11c53278SBjoern A. Zeeb 			ret = err;
527*11c53278SBjoern A. Zeeb 
528*11c53278SBjoern A. Zeeb 			/* Don't stop here - instead drain the remaining data
529*11c53278SBjoern A. Zeeb 			 * from the card's buffer, else the card will return
530*11c53278SBjoern A. Zeeb 			 * corrupt data for the next rtw_sdio_read_port() call.
531*11c53278SBjoern A. Zeeb 			 */
532*11c53278SBjoern A. Zeeb 		}
533*11c53278SBjoern A. Zeeb 
534*11c53278SBjoern A. Zeeb 		count -= bytes;
535*11c53278SBjoern A. Zeeb 		buf += bytes;
536*11c53278SBjoern A. Zeeb 	}
53790aac0d8SBjoern A. Zeeb 
53890aac0d8SBjoern A. Zeeb 	if (bus_claim)
53990aac0d8SBjoern A. Zeeb 		sdio_release_host(rtwsdio->sdio_func);
54090aac0d8SBjoern A. Zeeb 
54190aac0d8SBjoern A. Zeeb 	return ret;
54290aac0d8SBjoern A. Zeeb }
54390aac0d8SBjoern A. Zeeb 
54490aac0d8SBjoern A. Zeeb static int rtw_sdio_check_free_txpg(struct rtw_dev *rtwdev, u8 queue,
54590aac0d8SBjoern A. Zeeb 				    size_t count)
54690aac0d8SBjoern A. Zeeb {
54790aac0d8SBjoern A. Zeeb 	unsigned int pages_free, pages_needed;
54890aac0d8SBjoern A. Zeeb 
54990aac0d8SBjoern A. Zeeb 	if (rtw_chip_wcpu_11n(rtwdev)) {
55090aac0d8SBjoern A. Zeeb 		u32 free_txpg;
55190aac0d8SBjoern A. Zeeb 
55290aac0d8SBjoern A. Zeeb 		free_txpg = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG);
55390aac0d8SBjoern A. Zeeb 
55490aac0d8SBjoern A. Zeeb 		switch (queue) {
55590aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_BCN:
55690aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_H2C:
55790aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_HI0:
55890aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_MGMT:
55990aac0d8SBjoern A. Zeeb 			/* high */
56090aac0d8SBjoern A. Zeeb 			pages_free = free_txpg & 0xff;
56190aac0d8SBjoern A. Zeeb 			break;
56290aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_VI:
56390aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_VO:
56490aac0d8SBjoern A. Zeeb 			/* normal */
56590aac0d8SBjoern A. Zeeb 			pages_free = (free_txpg >> 8) & 0xff;
56690aac0d8SBjoern A. Zeeb 			break;
56790aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_BE:
56890aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_BK:
56990aac0d8SBjoern A. Zeeb 			/* low */
57090aac0d8SBjoern A. Zeeb 			pages_free = (free_txpg >> 16) & 0xff;
57190aac0d8SBjoern A. Zeeb 			break;
57290aac0d8SBjoern A. Zeeb 		default:
57390aac0d8SBjoern A. Zeeb 			rtw_warn(rtwdev, "Unknown mapping for queue %u\n", queue);
57490aac0d8SBjoern A. Zeeb 			return -EINVAL;
57590aac0d8SBjoern A. Zeeb 		}
57690aac0d8SBjoern A. Zeeb 
57790aac0d8SBjoern A. Zeeb 		/* add the pages from the public queue */
57890aac0d8SBjoern A. Zeeb 		pages_free += (free_txpg >> 24) & 0xff;
57990aac0d8SBjoern A. Zeeb 	} else {
58090aac0d8SBjoern A. Zeeb 		u32 free_txpg[3];
58190aac0d8SBjoern A. Zeeb 
58290aac0d8SBjoern A. Zeeb 		free_txpg[0] = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG);
58390aac0d8SBjoern A. Zeeb 		free_txpg[1] = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG + 4);
58490aac0d8SBjoern A. Zeeb 		free_txpg[2] = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG + 8);
58590aac0d8SBjoern A. Zeeb 
58690aac0d8SBjoern A. Zeeb 		switch (queue) {
58790aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_BCN:
58890aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_H2C:
58990aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_HI0:
59090aac0d8SBjoern A. Zeeb 			/* high */
59190aac0d8SBjoern A. Zeeb 			pages_free = free_txpg[0] & 0xfff;
59290aac0d8SBjoern A. Zeeb 			break;
59390aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_VI:
59490aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_VO:
59590aac0d8SBjoern A. Zeeb 			/* normal */
59690aac0d8SBjoern A. Zeeb 			pages_free = (free_txpg[0] >> 16) & 0xfff;
59790aac0d8SBjoern A. Zeeb 			break;
59890aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_BE:
59990aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_BK:
60090aac0d8SBjoern A. Zeeb 			/* low */
60190aac0d8SBjoern A. Zeeb 			pages_free = free_txpg[1] & 0xfff;
60290aac0d8SBjoern A. Zeeb 			break;
60390aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_MGMT:
60490aac0d8SBjoern A. Zeeb 			/* extra */
60590aac0d8SBjoern A. Zeeb 			pages_free = free_txpg[2] & 0xfff;
60690aac0d8SBjoern A. Zeeb 			break;
60790aac0d8SBjoern A. Zeeb 		default:
60890aac0d8SBjoern A. Zeeb 			rtw_warn(rtwdev, "Unknown mapping for queue %u\n", queue);
60990aac0d8SBjoern A. Zeeb 			return -EINVAL;
61090aac0d8SBjoern A. Zeeb 		}
61190aac0d8SBjoern A. Zeeb 
61290aac0d8SBjoern A. Zeeb 		/* add the pages from the public queue */
61390aac0d8SBjoern A. Zeeb 		pages_free += (free_txpg[1] >> 16) & 0xfff;
61490aac0d8SBjoern A. Zeeb 	}
61590aac0d8SBjoern A. Zeeb 
61690aac0d8SBjoern A. Zeeb 	pages_needed = DIV_ROUND_UP(count, rtwdev->chip->page_size);
61790aac0d8SBjoern A. Zeeb 
61890aac0d8SBjoern A. Zeeb 	if (pages_needed > pages_free) {
61990aac0d8SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_SDIO,
62090aac0d8SBjoern A. Zeeb 			"Not enough free pages (%u needed, %u free) in queue %u for %zu bytes\n",
62190aac0d8SBjoern A. Zeeb 			pages_needed, pages_free, queue, count);
62290aac0d8SBjoern A. Zeeb 		return -EBUSY;
62390aac0d8SBjoern A. Zeeb 	}
62490aac0d8SBjoern A. Zeeb 
62590aac0d8SBjoern A. Zeeb 	return 0;
62690aac0d8SBjoern A. Zeeb }
62790aac0d8SBjoern A. Zeeb 
62890aac0d8SBjoern A. Zeeb static int rtw_sdio_write_port(struct rtw_dev *rtwdev, struct sk_buff *skb,
62990aac0d8SBjoern A. Zeeb 			       enum rtw_tx_queue_type queue)
63090aac0d8SBjoern A. Zeeb {
63190aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
63290aac0d8SBjoern A. Zeeb 	bool bus_claim;
63390aac0d8SBjoern A. Zeeb 	size_t txsize;
63490aac0d8SBjoern A. Zeeb 	u32 txaddr;
63590aac0d8SBjoern A. Zeeb 	int ret;
63690aac0d8SBjoern A. Zeeb 
63790aac0d8SBjoern A. Zeeb 	txaddr = rtw_sdio_get_tx_addr(rtwdev, skb->len, queue);
63890aac0d8SBjoern A. Zeeb 	if (!txaddr)
63990aac0d8SBjoern A. Zeeb 		return -EINVAL;
64090aac0d8SBjoern A. Zeeb 
64190aac0d8SBjoern A. Zeeb 	txsize = sdio_align_size(rtwsdio->sdio_func, skb->len);
64290aac0d8SBjoern A. Zeeb 
64390aac0d8SBjoern A. Zeeb 	ret = rtw_sdio_check_free_txpg(rtwdev, queue, txsize);
64490aac0d8SBjoern A. Zeeb 	if (ret)
64590aac0d8SBjoern A. Zeeb 		return ret;
64690aac0d8SBjoern A. Zeeb 
64790aac0d8SBjoern A. Zeeb 	if (!IS_ALIGNED((unsigned long)skb->data, RTW_SDIO_DATA_PTR_ALIGN))
64890aac0d8SBjoern A. Zeeb 		rtw_warn(rtwdev, "Got unaligned SKB in %s() for queue %u\n",
64990aac0d8SBjoern A. Zeeb 			 __func__, queue);
65090aac0d8SBjoern A. Zeeb 
65190aac0d8SBjoern A. Zeeb 	bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
65290aac0d8SBjoern A. Zeeb 
65390aac0d8SBjoern A. Zeeb 	if (bus_claim)
65490aac0d8SBjoern A. Zeeb 		sdio_claim_host(rtwsdio->sdio_func);
65590aac0d8SBjoern A. Zeeb 
65690aac0d8SBjoern A. Zeeb 	ret = sdio_memcpy_toio(rtwsdio->sdio_func, txaddr, skb->data, txsize);
65790aac0d8SBjoern A. Zeeb 
65890aac0d8SBjoern A. Zeeb 	if (bus_claim)
65990aac0d8SBjoern A. Zeeb 		sdio_release_host(rtwsdio->sdio_func);
66090aac0d8SBjoern A. Zeeb 
66190aac0d8SBjoern A. Zeeb 	if (ret)
66290aac0d8SBjoern A. Zeeb 		rtw_warn(rtwdev,
66390aac0d8SBjoern A. Zeeb 			 "Failed to write %zu byte(s) to SDIO port 0x%08x",
66490aac0d8SBjoern A. Zeeb 			 txsize, txaddr);
66590aac0d8SBjoern A. Zeeb 
66690aac0d8SBjoern A. Zeeb 	return ret;
66790aac0d8SBjoern A. Zeeb }
66890aac0d8SBjoern A. Zeeb 
66990aac0d8SBjoern A. Zeeb static void rtw_sdio_init(struct rtw_dev *rtwdev)
67090aac0d8SBjoern A. Zeeb {
67190aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
67290aac0d8SBjoern A. Zeeb 
67390aac0d8SBjoern A. Zeeb 	rtwsdio->irq_mask = REG_SDIO_HIMR_RX_REQUEST | REG_SDIO_HIMR_CPWM1;
67490aac0d8SBjoern A. Zeeb }
67590aac0d8SBjoern A. Zeeb 
67690aac0d8SBjoern A. Zeeb static void rtw_sdio_enable_rx_aggregation(struct rtw_dev *rtwdev)
67790aac0d8SBjoern A. Zeeb {
67890aac0d8SBjoern A. Zeeb 	u8 size, timeout;
67990aac0d8SBjoern A. Zeeb 
68090aac0d8SBjoern A. Zeeb 	if (rtw_chip_wcpu_11n(rtwdev)) {
68190aac0d8SBjoern A. Zeeb 		size = 0x6;
68290aac0d8SBjoern A. Zeeb 		timeout = 0x6;
68390aac0d8SBjoern A. Zeeb 	} else {
68490aac0d8SBjoern A. Zeeb 		size = 0xff;
68590aac0d8SBjoern A. Zeeb 		timeout = 0x1;
68690aac0d8SBjoern A. Zeeb 	}
68790aac0d8SBjoern A. Zeeb 
68890aac0d8SBjoern A. Zeeb 	/* Make the firmware honor the size limit configured below */
68990aac0d8SBjoern A. Zeeb 	rtw_write32_set(rtwdev, REG_RXDMA_AGG_PG_TH, BIT_EN_PRE_CALC);
69090aac0d8SBjoern A. Zeeb 
69190aac0d8SBjoern A. Zeeb 	rtw_write8_set(rtwdev, REG_TXDMA_PQ_MAP, BIT_RXDMA_AGG_EN);
69290aac0d8SBjoern A. Zeeb 
69390aac0d8SBjoern A. Zeeb 	rtw_write16(rtwdev, REG_RXDMA_AGG_PG_TH,
69490aac0d8SBjoern A. Zeeb 		    FIELD_PREP(BIT_RXDMA_AGG_PG_TH, size) |
69590aac0d8SBjoern A. Zeeb 		    FIELD_PREP(BIT_DMA_AGG_TO_V1, timeout));
69690aac0d8SBjoern A. Zeeb 
69790aac0d8SBjoern A. Zeeb 	rtw_write8_set(rtwdev, REG_RXDMA_MODE, BIT_DMA_MODE);
69890aac0d8SBjoern A. Zeeb }
69990aac0d8SBjoern A. Zeeb 
70090aac0d8SBjoern A. Zeeb static void rtw_sdio_enable_interrupt(struct rtw_dev *rtwdev)
70190aac0d8SBjoern A. Zeeb {
70290aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
70390aac0d8SBjoern A. Zeeb 
70490aac0d8SBjoern A. Zeeb 	rtw_write32(rtwdev, REG_SDIO_HIMR, rtwsdio->irq_mask);
70590aac0d8SBjoern A. Zeeb }
70690aac0d8SBjoern A. Zeeb 
70790aac0d8SBjoern A. Zeeb static void rtw_sdio_disable_interrupt(struct rtw_dev *rtwdev)
70890aac0d8SBjoern A. Zeeb {
70990aac0d8SBjoern A. Zeeb 	rtw_write32(rtwdev, REG_SDIO_HIMR, 0x0);
71090aac0d8SBjoern A. Zeeb }
71190aac0d8SBjoern A. Zeeb 
71290aac0d8SBjoern A. Zeeb static u8 rtw_sdio_get_tx_qsel(struct rtw_dev *rtwdev, struct sk_buff *skb,
71390aac0d8SBjoern A. Zeeb 			       u8 queue)
71490aac0d8SBjoern A. Zeeb {
71590aac0d8SBjoern A. Zeeb 	switch (queue) {
71690aac0d8SBjoern A. Zeeb 	case RTW_TX_QUEUE_BCN:
71790aac0d8SBjoern A. Zeeb 		return TX_DESC_QSEL_BEACON;
71890aac0d8SBjoern A. Zeeb 	case RTW_TX_QUEUE_H2C:
71990aac0d8SBjoern A. Zeeb 		return TX_DESC_QSEL_H2C;
72090aac0d8SBjoern A. Zeeb 	case RTW_TX_QUEUE_MGMT:
72190aac0d8SBjoern A. Zeeb 		if (rtw_chip_wcpu_11n(rtwdev))
72290aac0d8SBjoern A. Zeeb 			return TX_DESC_QSEL_HIGH;
72390aac0d8SBjoern A. Zeeb 		else
72490aac0d8SBjoern A. Zeeb 			return TX_DESC_QSEL_MGMT;
72590aac0d8SBjoern A. Zeeb 	case RTW_TX_QUEUE_HI0:
72690aac0d8SBjoern A. Zeeb 		return TX_DESC_QSEL_HIGH;
72790aac0d8SBjoern A. Zeeb 	default:
72890aac0d8SBjoern A. Zeeb 		return skb->priority;
72990aac0d8SBjoern A. Zeeb 	}
73090aac0d8SBjoern A. Zeeb }
73190aac0d8SBjoern A. Zeeb 
73290aac0d8SBjoern A. Zeeb static int rtw_sdio_setup(struct rtw_dev *rtwdev)
73390aac0d8SBjoern A. Zeeb {
73490aac0d8SBjoern A. Zeeb 	/* nothing to do */
73590aac0d8SBjoern A. Zeeb 	return 0;
73690aac0d8SBjoern A. Zeeb }
73790aac0d8SBjoern A. Zeeb 
73890aac0d8SBjoern A. Zeeb static int rtw_sdio_start(struct rtw_dev *rtwdev)
73990aac0d8SBjoern A. Zeeb {
74090aac0d8SBjoern A. Zeeb 	rtw_sdio_enable_rx_aggregation(rtwdev);
74190aac0d8SBjoern A. Zeeb 	rtw_sdio_enable_interrupt(rtwdev);
74290aac0d8SBjoern A. Zeeb 
74390aac0d8SBjoern A. Zeeb 	return 0;
74490aac0d8SBjoern A. Zeeb }
74590aac0d8SBjoern A. Zeeb 
74690aac0d8SBjoern A. Zeeb static void rtw_sdio_stop(struct rtw_dev *rtwdev)
74790aac0d8SBjoern A. Zeeb {
74890aac0d8SBjoern A. Zeeb 	rtw_sdio_disable_interrupt(rtwdev);
74990aac0d8SBjoern A. Zeeb }
75090aac0d8SBjoern A. Zeeb 
75190aac0d8SBjoern A. Zeeb static void rtw_sdio_deep_ps_enter(struct rtw_dev *rtwdev)
75290aac0d8SBjoern A. Zeeb {
75390aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
75490aac0d8SBjoern A. Zeeb 	bool tx_empty = true;
75590aac0d8SBjoern A. Zeeb 	u8 queue;
75690aac0d8SBjoern A. Zeeb 
75790aac0d8SBjoern A. Zeeb 	if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE)) {
75890aac0d8SBjoern A. Zeeb 		/* Deep PS state is not allowed to TX-DMA */
75990aac0d8SBjoern A. Zeeb 		for (queue = 0; queue < RTK_MAX_TX_QUEUE_NUM; queue++) {
76090aac0d8SBjoern A. Zeeb 			/* BCN queue is rsvd page, does not have DMA interrupt
76190aac0d8SBjoern A. Zeeb 			 * H2C queue is managed by firmware
76290aac0d8SBjoern A. Zeeb 			 */
76390aac0d8SBjoern A. Zeeb 			if (queue == RTW_TX_QUEUE_BCN ||
76490aac0d8SBjoern A. Zeeb 			    queue == RTW_TX_QUEUE_H2C)
76590aac0d8SBjoern A. Zeeb 				continue;
76690aac0d8SBjoern A. Zeeb 
76790aac0d8SBjoern A. Zeeb 			/* check if there is any skb DMAing */
76890aac0d8SBjoern A. Zeeb 			if (skb_queue_len(&rtwsdio->tx_queue[queue])) {
76990aac0d8SBjoern A. Zeeb 				tx_empty = false;
77090aac0d8SBjoern A. Zeeb 				break;
77190aac0d8SBjoern A. Zeeb 			}
77290aac0d8SBjoern A. Zeeb 		}
77390aac0d8SBjoern A. Zeeb 	}
77490aac0d8SBjoern A. Zeeb 
77590aac0d8SBjoern A. Zeeb 	if (!tx_empty) {
77690aac0d8SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_PS,
77790aac0d8SBjoern A. Zeeb 			"TX path not empty, cannot enter deep power save state\n");
77890aac0d8SBjoern A. Zeeb 		return;
77990aac0d8SBjoern A. Zeeb 	}
78090aac0d8SBjoern A. Zeeb 
78190aac0d8SBjoern A. Zeeb 	set_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags);
78290aac0d8SBjoern A. Zeeb 	rtw_power_mode_change(rtwdev, true);
78390aac0d8SBjoern A. Zeeb }
78490aac0d8SBjoern A. Zeeb 
78590aac0d8SBjoern A. Zeeb static void rtw_sdio_deep_ps_leave(struct rtw_dev *rtwdev)
78690aac0d8SBjoern A. Zeeb {
78790aac0d8SBjoern A. Zeeb 	if (test_and_clear_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags))
78890aac0d8SBjoern A. Zeeb 		rtw_power_mode_change(rtwdev, false);
78990aac0d8SBjoern A. Zeeb }
79090aac0d8SBjoern A. Zeeb 
79190aac0d8SBjoern A. Zeeb static void rtw_sdio_deep_ps(struct rtw_dev *rtwdev, bool enter)
79290aac0d8SBjoern A. Zeeb {
79390aac0d8SBjoern A. Zeeb 	if (enter && !test_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags))
79490aac0d8SBjoern A. Zeeb 		rtw_sdio_deep_ps_enter(rtwdev);
79590aac0d8SBjoern A. Zeeb 
79690aac0d8SBjoern A. Zeeb 	if (!enter && test_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags))
79790aac0d8SBjoern A. Zeeb 		rtw_sdio_deep_ps_leave(rtwdev);
79890aac0d8SBjoern A. Zeeb }
79990aac0d8SBjoern A. Zeeb 
80090aac0d8SBjoern A. Zeeb static void rtw_sdio_tx_kick_off(struct rtw_dev *rtwdev)
80190aac0d8SBjoern A. Zeeb {
80290aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
80390aac0d8SBjoern A. Zeeb 
80490aac0d8SBjoern A. Zeeb 	queue_work(rtwsdio->txwq, &rtwsdio->tx_handler_data->work);
80590aac0d8SBjoern A. Zeeb }
80690aac0d8SBjoern A. Zeeb 
80790aac0d8SBjoern A. Zeeb static void rtw_sdio_link_ps(struct rtw_dev *rtwdev, bool enter)
80890aac0d8SBjoern A. Zeeb {
80990aac0d8SBjoern A. Zeeb 	/* nothing to do */
81090aac0d8SBjoern A. Zeeb }
81190aac0d8SBjoern A. Zeeb 
81290aac0d8SBjoern A. Zeeb static void rtw_sdio_interface_cfg(struct rtw_dev *rtwdev)
81390aac0d8SBjoern A. Zeeb {
81490aac0d8SBjoern A. Zeeb 	u32 val;
81590aac0d8SBjoern A. Zeeb 
81690aac0d8SBjoern A. Zeeb 	rtw_read32(rtwdev, REG_SDIO_FREE_TXPG);
81790aac0d8SBjoern A. Zeeb 
81890aac0d8SBjoern A. Zeeb 	val = rtw_read32(rtwdev, REG_SDIO_TX_CTRL);
81990aac0d8SBjoern A. Zeeb 	val &= 0xfff8;
82090aac0d8SBjoern A. Zeeb 	rtw_write32(rtwdev, REG_SDIO_TX_CTRL, val);
82190aac0d8SBjoern A. Zeeb }
82290aac0d8SBjoern A. Zeeb 
82390aac0d8SBjoern A. Zeeb static struct rtw_sdio_tx_data *rtw_sdio_get_tx_data(struct sk_buff *skb)
82490aac0d8SBjoern A. Zeeb {
82590aac0d8SBjoern A. Zeeb 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
82690aac0d8SBjoern A. Zeeb 
82790aac0d8SBjoern A. Zeeb 	BUILD_BUG_ON(sizeof(struct rtw_sdio_tx_data) >
82890aac0d8SBjoern A. Zeeb 		     sizeof(info->status.status_driver_data));
82990aac0d8SBjoern A. Zeeb 
83090aac0d8SBjoern A. Zeeb 	return (struct rtw_sdio_tx_data *)info->status.status_driver_data;
83190aac0d8SBjoern A. Zeeb }
83290aac0d8SBjoern A. Zeeb 
83390aac0d8SBjoern A. Zeeb static void rtw_sdio_tx_skb_prepare(struct rtw_dev *rtwdev,
83490aac0d8SBjoern A. Zeeb 				    struct rtw_tx_pkt_info *pkt_info,
83590aac0d8SBjoern A. Zeeb 				    struct sk_buff *skb,
83690aac0d8SBjoern A. Zeeb 				    enum rtw_tx_queue_type queue)
83790aac0d8SBjoern A. Zeeb {
83890aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
83990aac0d8SBjoern A. Zeeb 	unsigned long data_addr, aligned_addr;
84090aac0d8SBjoern A. Zeeb 	size_t offset;
84190aac0d8SBjoern A. Zeeb 	u8 *pkt_desc;
84290aac0d8SBjoern A. Zeeb 
84390aac0d8SBjoern A. Zeeb 	pkt_desc = skb_push(skb, chip->tx_pkt_desc_sz);
84490aac0d8SBjoern A. Zeeb 
84590aac0d8SBjoern A. Zeeb 	data_addr = (unsigned long)pkt_desc;
84690aac0d8SBjoern A. Zeeb 	aligned_addr = ALIGN(data_addr, RTW_SDIO_DATA_PTR_ALIGN);
84790aac0d8SBjoern A. Zeeb 
84890aac0d8SBjoern A. Zeeb 	if (data_addr != aligned_addr) {
84990aac0d8SBjoern A. Zeeb 		/* Ensure that the start of the pkt_desc is always aligned at
85090aac0d8SBjoern A. Zeeb 		 * RTW_SDIO_DATA_PTR_ALIGN.
85190aac0d8SBjoern A. Zeeb 		 */
85290aac0d8SBjoern A. Zeeb 		offset = RTW_SDIO_DATA_PTR_ALIGN - (aligned_addr - data_addr);
85390aac0d8SBjoern A. Zeeb 
85490aac0d8SBjoern A. Zeeb 		pkt_desc = skb_push(skb, offset);
85590aac0d8SBjoern A. Zeeb 
85690aac0d8SBjoern A. Zeeb 		/* By inserting padding to align the start of the pkt_desc we
85790aac0d8SBjoern A. Zeeb 		 * need to inform the firmware that the actual data starts at
85890aac0d8SBjoern A. Zeeb 		 * a different offset than normal.
85990aac0d8SBjoern A. Zeeb 		 */
86090aac0d8SBjoern A. Zeeb 		pkt_info->offset += offset;
86190aac0d8SBjoern A. Zeeb 	}
86290aac0d8SBjoern A. Zeeb 
86390aac0d8SBjoern A. Zeeb 	memset(pkt_desc, 0, chip->tx_pkt_desc_sz);
86490aac0d8SBjoern A. Zeeb 
86590aac0d8SBjoern A. Zeeb 	pkt_info->qsel = rtw_sdio_get_tx_qsel(rtwdev, skb, queue);
86690aac0d8SBjoern A. Zeeb 
86790aac0d8SBjoern A. Zeeb 	rtw_tx_fill_tx_desc(pkt_info, skb);
86890aac0d8SBjoern A. Zeeb 	rtw_tx_fill_txdesc_checksum(rtwdev, pkt_info, pkt_desc);
86990aac0d8SBjoern A. Zeeb }
87090aac0d8SBjoern A. Zeeb 
87190aac0d8SBjoern A. Zeeb static int rtw_sdio_write_data(struct rtw_dev *rtwdev,
87290aac0d8SBjoern A. Zeeb 			       struct rtw_tx_pkt_info *pkt_info,
87390aac0d8SBjoern A. Zeeb 			       struct sk_buff *skb,
87490aac0d8SBjoern A. Zeeb 			       enum rtw_tx_queue_type queue)
87590aac0d8SBjoern A. Zeeb {
87690aac0d8SBjoern A. Zeeb 	int ret;
87790aac0d8SBjoern A. Zeeb 
87890aac0d8SBjoern A. Zeeb 	rtw_sdio_tx_skb_prepare(rtwdev, pkt_info, skb, queue);
87990aac0d8SBjoern A. Zeeb 
88090aac0d8SBjoern A. Zeeb 	ret = rtw_sdio_write_port(rtwdev, skb, queue);
88190aac0d8SBjoern A. Zeeb 	dev_kfree_skb_any(skb);
88290aac0d8SBjoern A. Zeeb 
88390aac0d8SBjoern A. Zeeb 	return ret;
88490aac0d8SBjoern A. Zeeb }
88590aac0d8SBjoern A. Zeeb 
88690aac0d8SBjoern A. Zeeb static int rtw_sdio_write_data_rsvd_page(struct rtw_dev *rtwdev, u8 *buf,
88790aac0d8SBjoern A. Zeeb 					 u32 size)
88890aac0d8SBjoern A. Zeeb {
88990aac0d8SBjoern A. Zeeb 	struct rtw_tx_pkt_info pkt_info = {};
89090aac0d8SBjoern A. Zeeb 	struct sk_buff *skb;
89190aac0d8SBjoern A. Zeeb 
89290aac0d8SBjoern A. Zeeb 	skb = rtw_tx_write_data_rsvd_page_get(rtwdev, &pkt_info, buf, size);
89390aac0d8SBjoern A. Zeeb 	if (!skb)
89490aac0d8SBjoern A. Zeeb 		return -ENOMEM;
89590aac0d8SBjoern A. Zeeb 
89690aac0d8SBjoern A. Zeeb 	return rtw_sdio_write_data(rtwdev, &pkt_info, skb, RTW_TX_QUEUE_BCN);
89790aac0d8SBjoern A. Zeeb }
89890aac0d8SBjoern A. Zeeb 
89990aac0d8SBjoern A. Zeeb static int rtw_sdio_write_data_h2c(struct rtw_dev *rtwdev, u8 *buf, u32 size)
90090aac0d8SBjoern A. Zeeb {
90190aac0d8SBjoern A. Zeeb 	struct rtw_tx_pkt_info pkt_info = {};
90290aac0d8SBjoern A. Zeeb 	struct sk_buff *skb;
90390aac0d8SBjoern A. Zeeb 
90490aac0d8SBjoern A. Zeeb 	skb = rtw_tx_write_data_h2c_get(rtwdev, &pkt_info, buf, size);
90590aac0d8SBjoern A. Zeeb 	if (!skb)
90690aac0d8SBjoern A. Zeeb 		return -ENOMEM;
90790aac0d8SBjoern A. Zeeb 
90890aac0d8SBjoern A. Zeeb 	return rtw_sdio_write_data(rtwdev, &pkt_info, skb, RTW_TX_QUEUE_H2C);
90990aac0d8SBjoern A. Zeeb }
91090aac0d8SBjoern A. Zeeb 
91190aac0d8SBjoern A. Zeeb static int rtw_sdio_tx_write(struct rtw_dev *rtwdev,
91290aac0d8SBjoern A. Zeeb 			     struct rtw_tx_pkt_info *pkt_info,
91390aac0d8SBjoern A. Zeeb 			     struct sk_buff *skb)
91490aac0d8SBjoern A. Zeeb {
91590aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
91690aac0d8SBjoern A. Zeeb 	enum rtw_tx_queue_type queue = rtw_tx_queue_mapping(skb);
91790aac0d8SBjoern A. Zeeb 	struct rtw_sdio_tx_data *tx_data;
91890aac0d8SBjoern A. Zeeb 
91990aac0d8SBjoern A. Zeeb 	rtw_sdio_tx_skb_prepare(rtwdev, pkt_info, skb, queue);
92090aac0d8SBjoern A. Zeeb 
92190aac0d8SBjoern A. Zeeb 	tx_data = rtw_sdio_get_tx_data(skb);
92290aac0d8SBjoern A. Zeeb 	tx_data->sn = pkt_info->sn;
92390aac0d8SBjoern A. Zeeb 
92490aac0d8SBjoern A. Zeeb 	skb_queue_tail(&rtwsdio->tx_queue[queue], skb);
92590aac0d8SBjoern A. Zeeb 
92690aac0d8SBjoern A. Zeeb 	return 0;
92790aac0d8SBjoern A. Zeeb }
92890aac0d8SBjoern A. Zeeb 
92990aac0d8SBjoern A. Zeeb static void rtw_sdio_tx_err_isr(struct rtw_dev *rtwdev)
93090aac0d8SBjoern A. Zeeb {
93190aac0d8SBjoern A. Zeeb 	u32 val = rtw_read32(rtwdev, REG_TXDMA_STATUS);
93290aac0d8SBjoern A. Zeeb 
93390aac0d8SBjoern A. Zeeb 	rtw_write32(rtwdev, REG_TXDMA_STATUS, val);
93490aac0d8SBjoern A. Zeeb }
93590aac0d8SBjoern A. Zeeb 
93690aac0d8SBjoern A. Zeeb static void rtw_sdio_rx_skb(struct rtw_dev *rtwdev, struct sk_buff *skb,
93790aac0d8SBjoern A. Zeeb 			    u32 pkt_offset, struct rtw_rx_pkt_stat *pkt_stat,
93890aac0d8SBjoern A. Zeeb 			    struct ieee80211_rx_status *rx_status)
93990aac0d8SBjoern A. Zeeb {
94090aac0d8SBjoern A. Zeeb 	*IEEE80211_SKB_RXCB(skb) = *rx_status;
94190aac0d8SBjoern A. Zeeb 
94290aac0d8SBjoern A. Zeeb 	if (pkt_stat->is_c2h) {
94390aac0d8SBjoern A. Zeeb 		skb_put(skb, pkt_stat->pkt_len + pkt_offset);
94490aac0d8SBjoern A. Zeeb 		rtw_fw_c2h_cmd_rx_irqsafe(rtwdev, pkt_offset, skb);
94590aac0d8SBjoern A. Zeeb 		return;
94690aac0d8SBjoern A. Zeeb 	}
94790aac0d8SBjoern A. Zeeb 
94890aac0d8SBjoern A. Zeeb 	skb_put(skb, pkt_stat->pkt_len);
94990aac0d8SBjoern A. Zeeb 	skb_reserve(skb, pkt_offset);
95090aac0d8SBjoern A. Zeeb 
95190aac0d8SBjoern A. Zeeb 	rtw_rx_stats(rtwdev, pkt_stat->vif, skb);
95290aac0d8SBjoern A. Zeeb 
95390aac0d8SBjoern A. Zeeb 	ieee80211_rx_irqsafe(rtwdev->hw, skb);
95490aac0d8SBjoern A. Zeeb }
95590aac0d8SBjoern A. Zeeb 
95690aac0d8SBjoern A. Zeeb static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len)
95790aac0d8SBjoern A. Zeeb {
95890aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
95990aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
96090aac0d8SBjoern A. Zeeb 	u32 pkt_desc_sz = chip->rx_pkt_desc_sz;
96190aac0d8SBjoern A. Zeeb 	struct ieee80211_rx_status rx_status;
96290aac0d8SBjoern A. Zeeb 	struct rtw_rx_pkt_stat pkt_stat;
96390aac0d8SBjoern A. Zeeb 	struct sk_buff *skb, *split_skb;
96490aac0d8SBjoern A. Zeeb 	u32 pkt_offset, curr_pkt_len;
96590aac0d8SBjoern A. Zeeb 	size_t bufsz;
96690aac0d8SBjoern A. Zeeb 	u8 *rx_desc;
96790aac0d8SBjoern A. Zeeb 	int ret;
96890aac0d8SBjoern A. Zeeb 
96990aac0d8SBjoern A. Zeeb 	bufsz = sdio_align_size(rtwsdio->sdio_func, rx_len);
97090aac0d8SBjoern A. Zeeb 
97190aac0d8SBjoern A. Zeeb 	skb = dev_alloc_skb(bufsz);
97290aac0d8SBjoern A. Zeeb 	if (!skb)
97390aac0d8SBjoern A. Zeeb 		return;
97490aac0d8SBjoern A. Zeeb 
97590aac0d8SBjoern A. Zeeb 	ret = rtw_sdio_read_port(rtwdev, skb->data, bufsz);
97690aac0d8SBjoern A. Zeeb 	if (ret) {
97790aac0d8SBjoern A. Zeeb 		dev_kfree_skb_any(skb);
97890aac0d8SBjoern A. Zeeb 		return;
97990aac0d8SBjoern A. Zeeb 	}
98090aac0d8SBjoern A. Zeeb 
98190aac0d8SBjoern A. Zeeb 	while (true) {
98290aac0d8SBjoern A. Zeeb 		rx_desc = skb->data;
98390aac0d8SBjoern A. Zeeb 		chip->ops->query_rx_desc(rtwdev, rx_desc, &pkt_stat,
98490aac0d8SBjoern A. Zeeb 					 &rx_status);
98590aac0d8SBjoern A. Zeeb 		pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz +
98690aac0d8SBjoern A. Zeeb 			     pkt_stat.shift;
98790aac0d8SBjoern A. Zeeb 
98890aac0d8SBjoern A. Zeeb 		curr_pkt_len = ALIGN(pkt_offset + pkt_stat.pkt_len,
98990aac0d8SBjoern A. Zeeb 				     RTW_SDIO_DATA_PTR_ALIGN);
99090aac0d8SBjoern A. Zeeb 
99190aac0d8SBjoern A. Zeeb 		if ((curr_pkt_len + pkt_desc_sz) >= rx_len) {
99290aac0d8SBjoern A. Zeeb 			/* Use the original skb (with it's adjusted offset)
99390aac0d8SBjoern A. Zeeb 			 * when processing the last (or even the only) entry to
99490aac0d8SBjoern A. Zeeb 			 * have it's memory freed automatically.
99590aac0d8SBjoern A. Zeeb 			 */
99690aac0d8SBjoern A. Zeeb 			rtw_sdio_rx_skb(rtwdev, skb, pkt_offset, &pkt_stat,
99790aac0d8SBjoern A. Zeeb 					&rx_status);
99890aac0d8SBjoern A. Zeeb 			break;
99990aac0d8SBjoern A. Zeeb 		}
100090aac0d8SBjoern A. Zeeb 
100190aac0d8SBjoern A. Zeeb 		split_skb = dev_alloc_skb(curr_pkt_len);
100290aac0d8SBjoern A. Zeeb 		if (!split_skb) {
100390aac0d8SBjoern A. Zeeb 			rtw_sdio_rx_skb(rtwdev, skb, pkt_offset, &pkt_stat,
100490aac0d8SBjoern A. Zeeb 					&rx_status);
100590aac0d8SBjoern A. Zeeb 			break;
100690aac0d8SBjoern A. Zeeb 		}
100790aac0d8SBjoern A. Zeeb 
100890aac0d8SBjoern A. Zeeb 		skb_copy_header(split_skb, skb);
100990aac0d8SBjoern A. Zeeb 		memcpy(split_skb->data, skb->data, curr_pkt_len);
101090aac0d8SBjoern A. Zeeb 
101190aac0d8SBjoern A. Zeeb 		rtw_sdio_rx_skb(rtwdev, split_skb, pkt_offset, &pkt_stat,
101290aac0d8SBjoern A. Zeeb 				&rx_status);
101390aac0d8SBjoern A. Zeeb 
101490aac0d8SBjoern A. Zeeb 		/* Move to the start of the next RX descriptor */
101590aac0d8SBjoern A. Zeeb 		skb_reserve(skb, curr_pkt_len);
101690aac0d8SBjoern A. Zeeb 		rx_len -= curr_pkt_len;
101790aac0d8SBjoern A. Zeeb 	}
101890aac0d8SBjoern A. Zeeb }
101990aac0d8SBjoern A. Zeeb 
102090aac0d8SBjoern A. Zeeb static void rtw_sdio_rx_isr(struct rtw_dev *rtwdev)
102190aac0d8SBjoern A. Zeeb {
102290aac0d8SBjoern A. Zeeb 	u32 rx_len, hisr, total_rx_bytes = 0;
102390aac0d8SBjoern A. Zeeb 
102490aac0d8SBjoern A. Zeeb 	do {
102590aac0d8SBjoern A. Zeeb 		if (rtw_chip_wcpu_11n(rtwdev))
102690aac0d8SBjoern A. Zeeb 			rx_len = rtw_read16(rtwdev, REG_SDIO_RX0_REQ_LEN);
102790aac0d8SBjoern A. Zeeb 		else
102890aac0d8SBjoern A. Zeeb 			rx_len = rtw_read32(rtwdev, REG_SDIO_RX0_REQ_LEN);
102990aac0d8SBjoern A. Zeeb 
103090aac0d8SBjoern A. Zeeb 		if (!rx_len)
103190aac0d8SBjoern A. Zeeb 			break;
103290aac0d8SBjoern A. Zeeb 
103390aac0d8SBjoern A. Zeeb 		rtw_sdio_rxfifo_recv(rtwdev, rx_len);
103490aac0d8SBjoern A. Zeeb 
103590aac0d8SBjoern A. Zeeb 		total_rx_bytes += rx_len;
103690aac0d8SBjoern A. Zeeb 
103790aac0d8SBjoern A. Zeeb 		if (rtw_chip_wcpu_11n(rtwdev)) {
103890aac0d8SBjoern A. Zeeb 			/* Stop if no more RX requests are pending, even if
103990aac0d8SBjoern A. Zeeb 			 * rx_len could be greater than zero in the next
104090aac0d8SBjoern A. Zeeb 			 * iteration. This is needed because the RX buffer may
104190aac0d8SBjoern A. Zeeb 			 * already contain data while either HW or FW are not
104290aac0d8SBjoern A. Zeeb 			 * done filling that buffer yet. Still reading the
104390aac0d8SBjoern A. Zeeb 			 * buffer can result in packets where
104490aac0d8SBjoern A. Zeeb 			 * rtw_rx_pkt_stat.pkt_len is zero or points beyond the
104590aac0d8SBjoern A. Zeeb 			 * end of the buffer.
104690aac0d8SBjoern A. Zeeb 			 */
104790aac0d8SBjoern A. Zeeb 			hisr = rtw_read32(rtwdev, REG_SDIO_HISR);
104890aac0d8SBjoern A. Zeeb 		} else {
104990aac0d8SBjoern A. Zeeb 			/* RTW_WCPU_11AC chips have improved hardware or
105090aac0d8SBjoern A. Zeeb 			 * firmware and can use rx_len unconditionally.
105190aac0d8SBjoern A. Zeeb 			 */
105290aac0d8SBjoern A. Zeeb 			hisr = REG_SDIO_HISR_RX_REQUEST;
105390aac0d8SBjoern A. Zeeb 		}
105490aac0d8SBjoern A. Zeeb 	} while (total_rx_bytes < SZ_64K && hisr & REG_SDIO_HISR_RX_REQUEST);
105590aac0d8SBjoern A. Zeeb }
105690aac0d8SBjoern A. Zeeb 
105790aac0d8SBjoern A. Zeeb static void rtw_sdio_handle_interrupt(struct sdio_func *sdio_func)
105890aac0d8SBjoern A. Zeeb {
105990aac0d8SBjoern A. Zeeb 	struct ieee80211_hw *hw = sdio_get_drvdata(sdio_func);
106090aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio;
106190aac0d8SBjoern A. Zeeb 	struct rtw_dev *rtwdev;
106290aac0d8SBjoern A. Zeeb 	u32 hisr;
106390aac0d8SBjoern A. Zeeb 
106490aac0d8SBjoern A. Zeeb 	rtwdev = hw->priv;
106590aac0d8SBjoern A. Zeeb 	rtwsdio = (struct rtw_sdio *)rtwdev->priv;
106690aac0d8SBjoern A. Zeeb 
106790aac0d8SBjoern A. Zeeb 	rtwsdio->irq_thread = current;
106890aac0d8SBjoern A. Zeeb 
106990aac0d8SBjoern A. Zeeb 	hisr = rtw_read32(rtwdev, REG_SDIO_HISR);
107090aac0d8SBjoern A. Zeeb 
107190aac0d8SBjoern A. Zeeb 	if (hisr & REG_SDIO_HISR_TXERR)
107290aac0d8SBjoern A. Zeeb 		rtw_sdio_tx_err_isr(rtwdev);
107390aac0d8SBjoern A. Zeeb 	if (hisr & REG_SDIO_HISR_RX_REQUEST) {
107490aac0d8SBjoern A. Zeeb 		hisr &= ~REG_SDIO_HISR_RX_REQUEST;
107590aac0d8SBjoern A. Zeeb 		rtw_sdio_rx_isr(rtwdev);
107690aac0d8SBjoern A. Zeeb 	}
107790aac0d8SBjoern A. Zeeb 
107890aac0d8SBjoern A. Zeeb 	rtw_write32(rtwdev, REG_SDIO_HISR, hisr);
107990aac0d8SBjoern A. Zeeb 
108090aac0d8SBjoern A. Zeeb 	rtwsdio->irq_thread = NULL;
108190aac0d8SBjoern A. Zeeb }
108290aac0d8SBjoern A. Zeeb 
108390aac0d8SBjoern A. Zeeb static int __maybe_unused rtw_sdio_suspend(struct device *dev)
108490aac0d8SBjoern A. Zeeb {
108590aac0d8SBjoern A. Zeeb 	struct sdio_func *func = dev_to_sdio_func(dev);
108690aac0d8SBjoern A. Zeeb 	struct ieee80211_hw *hw = dev_get_drvdata(dev);
108790aac0d8SBjoern A. Zeeb 	struct rtw_dev *rtwdev = hw->priv;
108890aac0d8SBjoern A. Zeeb 	int ret;
108990aac0d8SBjoern A. Zeeb 
109090aac0d8SBjoern A. Zeeb 	ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
109190aac0d8SBjoern A. Zeeb 	if (ret)
109290aac0d8SBjoern A. Zeeb 		rtw_err(rtwdev, "Failed to host PM flag MMC_PM_KEEP_POWER");
109390aac0d8SBjoern A. Zeeb 
109490aac0d8SBjoern A. Zeeb 	return ret;
109590aac0d8SBjoern A. Zeeb }
109690aac0d8SBjoern A. Zeeb 
109790aac0d8SBjoern A. Zeeb static int __maybe_unused rtw_sdio_resume(struct device *dev)
109890aac0d8SBjoern A. Zeeb {
109990aac0d8SBjoern A. Zeeb 	return 0;
110090aac0d8SBjoern A. Zeeb }
110190aac0d8SBjoern A. Zeeb 
110290aac0d8SBjoern A. Zeeb SIMPLE_DEV_PM_OPS(rtw_sdio_pm_ops, rtw_sdio_suspend, rtw_sdio_resume);
110390aac0d8SBjoern A. Zeeb EXPORT_SYMBOL(rtw_sdio_pm_ops);
110490aac0d8SBjoern A. Zeeb 
110590aac0d8SBjoern A. Zeeb static int rtw_sdio_claim(struct rtw_dev *rtwdev, struct sdio_func *sdio_func)
110690aac0d8SBjoern A. Zeeb {
110790aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
110890aac0d8SBjoern A. Zeeb 	int ret;
110990aac0d8SBjoern A. Zeeb 
111090aac0d8SBjoern A. Zeeb 	sdio_claim_host(sdio_func);
111190aac0d8SBjoern A. Zeeb 
111290aac0d8SBjoern A. Zeeb 	ret = sdio_enable_func(sdio_func);
111390aac0d8SBjoern A. Zeeb 	if (ret) {
111490aac0d8SBjoern A. Zeeb 		rtw_err(rtwdev, "Failed to enable SDIO func");
111590aac0d8SBjoern A. Zeeb 		goto err_release_host;
111690aac0d8SBjoern A. Zeeb 	}
111790aac0d8SBjoern A. Zeeb 
111890aac0d8SBjoern A. Zeeb 	ret = sdio_set_block_size(sdio_func, RTW_SDIO_BLOCK_SIZE);
111990aac0d8SBjoern A. Zeeb 	if (ret) {
112090aac0d8SBjoern A. Zeeb 		rtw_err(rtwdev, "Failed to set SDIO block size to 512");
112190aac0d8SBjoern A. Zeeb 		goto err_disable_func;
112290aac0d8SBjoern A. Zeeb 	}
112390aac0d8SBjoern A. Zeeb 
112490aac0d8SBjoern A. Zeeb 	rtwsdio->sdio_func = sdio_func;
112590aac0d8SBjoern A. Zeeb 
112690aac0d8SBjoern A. Zeeb 	rtwsdio->sdio3_bus_mode = mmc_card_uhs(sdio_func->card);
112790aac0d8SBjoern A. Zeeb 
112890aac0d8SBjoern A. Zeeb 	sdio_set_drvdata(sdio_func, rtwdev->hw);
112990aac0d8SBjoern A. Zeeb 	SET_IEEE80211_DEV(rtwdev->hw, &sdio_func->dev);
113090aac0d8SBjoern A. Zeeb 
113190aac0d8SBjoern A. Zeeb 	sdio_release_host(sdio_func);
113290aac0d8SBjoern A. Zeeb 
113390aac0d8SBjoern A. Zeeb 	return 0;
113490aac0d8SBjoern A. Zeeb 
113590aac0d8SBjoern A. Zeeb err_disable_func:
113690aac0d8SBjoern A. Zeeb 	sdio_disable_func(sdio_func);
113790aac0d8SBjoern A. Zeeb err_release_host:
113890aac0d8SBjoern A. Zeeb 	sdio_release_host(sdio_func);
113990aac0d8SBjoern A. Zeeb 	return ret;
114090aac0d8SBjoern A. Zeeb }
114190aac0d8SBjoern A. Zeeb 
114290aac0d8SBjoern A. Zeeb static void rtw_sdio_declaim(struct rtw_dev *rtwdev,
114390aac0d8SBjoern A. Zeeb 			     struct sdio_func *sdio_func)
114490aac0d8SBjoern A. Zeeb {
114590aac0d8SBjoern A. Zeeb 	sdio_claim_host(sdio_func);
114690aac0d8SBjoern A. Zeeb 	sdio_disable_func(sdio_func);
114790aac0d8SBjoern A. Zeeb 	sdio_release_host(sdio_func);
114890aac0d8SBjoern A. Zeeb }
114990aac0d8SBjoern A. Zeeb 
115090aac0d8SBjoern A. Zeeb static struct rtw_hci_ops rtw_sdio_ops = {
115190aac0d8SBjoern A. Zeeb 	.tx_write = rtw_sdio_tx_write,
115290aac0d8SBjoern A. Zeeb 	.tx_kick_off = rtw_sdio_tx_kick_off,
115390aac0d8SBjoern A. Zeeb 	.setup = rtw_sdio_setup,
115490aac0d8SBjoern A. Zeeb 	.start = rtw_sdio_start,
115590aac0d8SBjoern A. Zeeb 	.stop = rtw_sdio_stop,
115690aac0d8SBjoern A. Zeeb 	.deep_ps = rtw_sdio_deep_ps,
115790aac0d8SBjoern A. Zeeb 	.link_ps = rtw_sdio_link_ps,
115890aac0d8SBjoern A. Zeeb 	.interface_cfg = rtw_sdio_interface_cfg,
115990aac0d8SBjoern A. Zeeb 
116090aac0d8SBjoern A. Zeeb 	.read8 = rtw_sdio_read8,
116190aac0d8SBjoern A. Zeeb 	.read16 = rtw_sdio_read16,
116290aac0d8SBjoern A. Zeeb 	.read32 = rtw_sdio_read32,
116390aac0d8SBjoern A. Zeeb 	.write8 = rtw_sdio_write8,
116490aac0d8SBjoern A. Zeeb 	.write16 = rtw_sdio_write16,
116590aac0d8SBjoern A. Zeeb 	.write32 = rtw_sdio_write32,
116690aac0d8SBjoern A. Zeeb 	.write_data_rsvd_page = rtw_sdio_write_data_rsvd_page,
116790aac0d8SBjoern A. Zeeb 	.write_data_h2c = rtw_sdio_write_data_h2c,
116890aac0d8SBjoern A. Zeeb };
116990aac0d8SBjoern A. Zeeb 
117090aac0d8SBjoern A. Zeeb static int rtw_sdio_request_irq(struct rtw_dev *rtwdev,
117190aac0d8SBjoern A. Zeeb 				struct sdio_func *sdio_func)
117290aac0d8SBjoern A. Zeeb {
117390aac0d8SBjoern A. Zeeb 	int ret;
117490aac0d8SBjoern A. Zeeb 
117590aac0d8SBjoern A. Zeeb 	sdio_claim_host(sdio_func);
117690aac0d8SBjoern A. Zeeb 	ret = sdio_claim_irq(sdio_func, &rtw_sdio_handle_interrupt);
117790aac0d8SBjoern A. Zeeb 	sdio_release_host(sdio_func);
117890aac0d8SBjoern A. Zeeb 
117990aac0d8SBjoern A. Zeeb 	if (ret) {
118090aac0d8SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to claim SDIO IRQ");
118190aac0d8SBjoern A. Zeeb 		return ret;
118290aac0d8SBjoern A. Zeeb 	}
118390aac0d8SBjoern A. Zeeb 
118490aac0d8SBjoern A. Zeeb 	return 0;
118590aac0d8SBjoern A. Zeeb }
118690aac0d8SBjoern A. Zeeb 
118790aac0d8SBjoern A. Zeeb static void rtw_sdio_indicate_tx_status(struct rtw_dev *rtwdev,
118890aac0d8SBjoern A. Zeeb 					struct sk_buff *skb)
118990aac0d8SBjoern A. Zeeb {
119090aac0d8SBjoern A. Zeeb 	struct rtw_sdio_tx_data *tx_data = rtw_sdio_get_tx_data(skb);
119190aac0d8SBjoern A. Zeeb 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
119290aac0d8SBjoern A. Zeeb 	struct ieee80211_hw *hw = rtwdev->hw;
119390aac0d8SBjoern A. Zeeb 
119490aac0d8SBjoern A. Zeeb 	/* enqueue to wait for tx report */
119590aac0d8SBjoern A. Zeeb 	if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) {
119690aac0d8SBjoern A. Zeeb 		rtw_tx_report_enqueue(rtwdev, skb, tx_data->sn);
119790aac0d8SBjoern A. Zeeb 		return;
119890aac0d8SBjoern A. Zeeb 	}
119990aac0d8SBjoern A. Zeeb 
120090aac0d8SBjoern A. Zeeb 	/* always ACK for others, then they won't be marked as drop */
120190aac0d8SBjoern A. Zeeb 	ieee80211_tx_info_clear_status(info);
120290aac0d8SBjoern A. Zeeb 	if (info->flags & IEEE80211_TX_CTL_NO_ACK)
120390aac0d8SBjoern A. Zeeb 		info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
120490aac0d8SBjoern A. Zeeb 	else
120590aac0d8SBjoern A. Zeeb 		info->flags |= IEEE80211_TX_STAT_ACK;
120690aac0d8SBjoern A. Zeeb 
120790aac0d8SBjoern A. Zeeb 	ieee80211_tx_status_irqsafe(hw, skb);
120890aac0d8SBjoern A. Zeeb }
120990aac0d8SBjoern A. Zeeb 
121090aac0d8SBjoern A. Zeeb static void rtw_sdio_process_tx_queue(struct rtw_dev *rtwdev,
121190aac0d8SBjoern A. Zeeb 				      enum rtw_tx_queue_type queue)
121290aac0d8SBjoern A. Zeeb {
121390aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
121490aac0d8SBjoern A. Zeeb 	struct sk_buff *skb;
121590aac0d8SBjoern A. Zeeb 	int ret;
121690aac0d8SBjoern A. Zeeb 
121790aac0d8SBjoern A. Zeeb 	skb = skb_dequeue(&rtwsdio->tx_queue[queue]);
121890aac0d8SBjoern A. Zeeb 	if (!skb)
121990aac0d8SBjoern A. Zeeb 		return;
122090aac0d8SBjoern A. Zeeb 
122190aac0d8SBjoern A. Zeeb 	ret = rtw_sdio_write_port(rtwdev, skb, queue);
122290aac0d8SBjoern A. Zeeb 	if (ret) {
122390aac0d8SBjoern A. Zeeb 		skb_queue_head(&rtwsdio->tx_queue[queue], skb);
122490aac0d8SBjoern A. Zeeb 		return;
122590aac0d8SBjoern A. Zeeb 	}
122690aac0d8SBjoern A. Zeeb 
122790aac0d8SBjoern A. Zeeb 	if (queue <= RTW_TX_QUEUE_VO)
122890aac0d8SBjoern A. Zeeb 		rtw_sdio_indicate_tx_status(rtwdev, skb);
122990aac0d8SBjoern A. Zeeb 	else
123090aac0d8SBjoern A. Zeeb 		dev_kfree_skb_any(skb);
123190aac0d8SBjoern A. Zeeb }
123290aac0d8SBjoern A. Zeeb 
123390aac0d8SBjoern A. Zeeb static void rtw_sdio_tx_handler(struct work_struct *work)
123490aac0d8SBjoern A. Zeeb {
123590aac0d8SBjoern A. Zeeb 	struct rtw_sdio_work_data *work_data =
123690aac0d8SBjoern A. Zeeb 		container_of(work, struct rtw_sdio_work_data, work);
123790aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio;
123890aac0d8SBjoern A. Zeeb 	struct rtw_dev *rtwdev;
123990aac0d8SBjoern A. Zeeb 	int limit, queue;
124090aac0d8SBjoern A. Zeeb 
124190aac0d8SBjoern A. Zeeb 	rtwdev = work_data->rtwdev;
124290aac0d8SBjoern A. Zeeb 	rtwsdio = (struct rtw_sdio *)rtwdev->priv;
124390aac0d8SBjoern A. Zeeb 
124490aac0d8SBjoern A. Zeeb 	if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE))
124590aac0d8SBjoern A. Zeeb 		rtw_sdio_deep_ps_leave(rtwdev);
124690aac0d8SBjoern A. Zeeb 
124790aac0d8SBjoern A. Zeeb 	for (queue = RTK_MAX_TX_QUEUE_NUM - 1; queue >= 0; queue--) {
124890aac0d8SBjoern A. Zeeb 		for (limit = 0; limit < 1000; limit++) {
124990aac0d8SBjoern A. Zeeb 			rtw_sdio_process_tx_queue(rtwdev, queue);
125090aac0d8SBjoern A. Zeeb 
125190aac0d8SBjoern A. Zeeb 			if (skb_queue_empty(&rtwsdio->tx_queue[queue]))
125290aac0d8SBjoern A. Zeeb 				break;
125390aac0d8SBjoern A. Zeeb 		}
125490aac0d8SBjoern A. Zeeb 	}
125590aac0d8SBjoern A. Zeeb }
125690aac0d8SBjoern A. Zeeb 
125790aac0d8SBjoern A. Zeeb static void rtw_sdio_free_irq(struct rtw_dev *rtwdev,
125890aac0d8SBjoern A. Zeeb 			      struct sdio_func *sdio_func)
125990aac0d8SBjoern A. Zeeb {
126090aac0d8SBjoern A. Zeeb 	sdio_claim_host(sdio_func);
126190aac0d8SBjoern A. Zeeb 	sdio_release_irq(sdio_func);
126290aac0d8SBjoern A. Zeeb 	sdio_release_host(sdio_func);
126390aac0d8SBjoern A. Zeeb }
126490aac0d8SBjoern A. Zeeb 
126590aac0d8SBjoern A. Zeeb static int rtw_sdio_init_tx(struct rtw_dev *rtwdev)
126690aac0d8SBjoern A. Zeeb {
126790aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
126890aac0d8SBjoern A. Zeeb 	int i;
126990aac0d8SBjoern A. Zeeb 
127090aac0d8SBjoern A. Zeeb 	rtwsdio->txwq = create_singlethread_workqueue("rtw88_sdio: tx wq");
127190aac0d8SBjoern A. Zeeb 	if (!rtwsdio->txwq) {
127290aac0d8SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to create TX work queue\n");
127390aac0d8SBjoern A. Zeeb 		return -ENOMEM;
127490aac0d8SBjoern A. Zeeb 	}
127590aac0d8SBjoern A. Zeeb 
127690aac0d8SBjoern A. Zeeb 	for (i = 0; i < RTK_MAX_TX_QUEUE_NUM; i++)
127790aac0d8SBjoern A. Zeeb 		skb_queue_head_init(&rtwsdio->tx_queue[i]);
127890aac0d8SBjoern A. Zeeb 	rtwsdio->tx_handler_data = kmalloc(sizeof(*rtwsdio->tx_handler_data),
127990aac0d8SBjoern A. Zeeb 					   GFP_KERNEL);
128090aac0d8SBjoern A. Zeeb 	if (!rtwsdio->tx_handler_data)
128190aac0d8SBjoern A. Zeeb 		goto err_destroy_wq;
128290aac0d8SBjoern A. Zeeb 
128390aac0d8SBjoern A. Zeeb 	rtwsdio->tx_handler_data->rtwdev = rtwdev;
128490aac0d8SBjoern A. Zeeb 	INIT_WORK(&rtwsdio->tx_handler_data->work, rtw_sdio_tx_handler);
128590aac0d8SBjoern A. Zeeb 
128690aac0d8SBjoern A. Zeeb 	return 0;
128790aac0d8SBjoern A. Zeeb 
128890aac0d8SBjoern A. Zeeb err_destroy_wq:
128990aac0d8SBjoern A. Zeeb 	destroy_workqueue(rtwsdio->txwq);
129090aac0d8SBjoern A. Zeeb 	return -ENOMEM;
129190aac0d8SBjoern A. Zeeb }
129290aac0d8SBjoern A. Zeeb 
129390aac0d8SBjoern A. Zeeb static void rtw_sdio_deinit_tx(struct rtw_dev *rtwdev)
129490aac0d8SBjoern A. Zeeb {
129590aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
129690aac0d8SBjoern A. Zeeb 	int i;
129790aac0d8SBjoern A. Zeeb 
129890aac0d8SBjoern A. Zeeb 	for (i = 0; i < RTK_MAX_TX_QUEUE_NUM; i++)
129990aac0d8SBjoern A. Zeeb 		skb_queue_purge(&rtwsdio->tx_queue[i]);
130090aac0d8SBjoern A. Zeeb 
130190aac0d8SBjoern A. Zeeb 	flush_workqueue(rtwsdio->txwq);
130290aac0d8SBjoern A. Zeeb 	destroy_workqueue(rtwsdio->txwq);
130390aac0d8SBjoern A. Zeeb 	kfree(rtwsdio->tx_handler_data);
130490aac0d8SBjoern A. Zeeb }
130590aac0d8SBjoern A. Zeeb 
130690aac0d8SBjoern A. Zeeb int rtw_sdio_probe(struct sdio_func *sdio_func,
130790aac0d8SBjoern A. Zeeb 		   const struct sdio_device_id *id)
130890aac0d8SBjoern A. Zeeb {
130990aac0d8SBjoern A. Zeeb 	struct ieee80211_hw *hw;
131090aac0d8SBjoern A. Zeeb 	struct rtw_dev *rtwdev;
131190aac0d8SBjoern A. Zeeb 	int drv_data_size;
131290aac0d8SBjoern A. Zeeb 	int ret;
131390aac0d8SBjoern A. Zeeb 
131490aac0d8SBjoern A. Zeeb 	drv_data_size = sizeof(struct rtw_dev) + sizeof(struct rtw_sdio);
131590aac0d8SBjoern A. Zeeb 	hw = ieee80211_alloc_hw(drv_data_size, &rtw_ops);
131690aac0d8SBjoern A. Zeeb 	if (!hw) {
131790aac0d8SBjoern A. Zeeb 		dev_err(&sdio_func->dev, "failed to allocate hw");
131890aac0d8SBjoern A. Zeeb 		return -ENOMEM;
131990aac0d8SBjoern A. Zeeb 	}
132090aac0d8SBjoern A. Zeeb 
132190aac0d8SBjoern A. Zeeb 	rtwdev = hw->priv;
132290aac0d8SBjoern A. Zeeb 	rtwdev->hw = hw;
132390aac0d8SBjoern A. Zeeb 	rtwdev->dev = &sdio_func->dev;
132490aac0d8SBjoern A. Zeeb 	rtwdev->chip = (struct rtw_chip_info *)id->driver_data;
132590aac0d8SBjoern A. Zeeb 	rtwdev->hci.ops = &rtw_sdio_ops;
132690aac0d8SBjoern A. Zeeb 	rtwdev->hci.type = RTW_HCI_TYPE_SDIO;
132790aac0d8SBjoern A. Zeeb 
132890aac0d8SBjoern A. Zeeb 	ret = rtw_core_init(rtwdev);
132990aac0d8SBjoern A. Zeeb 	if (ret)
133090aac0d8SBjoern A. Zeeb 		goto err_release_hw;
133190aac0d8SBjoern A. Zeeb 
133290aac0d8SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_SDIO,
133390aac0d8SBjoern A. Zeeb 		"rtw88 SDIO probe: vendor=0x%04x device=%04x class=%02x",
133490aac0d8SBjoern A. Zeeb 		id->vendor, id->device, id->class);
133590aac0d8SBjoern A. Zeeb 
133690aac0d8SBjoern A. Zeeb 	ret = rtw_sdio_claim(rtwdev, sdio_func);
133790aac0d8SBjoern A. Zeeb 	if (ret) {
133890aac0d8SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to claim SDIO device");
133990aac0d8SBjoern A. Zeeb 		goto err_deinit_core;
134090aac0d8SBjoern A. Zeeb 	}
134190aac0d8SBjoern A. Zeeb 
134290aac0d8SBjoern A. Zeeb 	rtw_sdio_init(rtwdev);
134390aac0d8SBjoern A. Zeeb 
134490aac0d8SBjoern A. Zeeb 	ret = rtw_sdio_init_tx(rtwdev);
134590aac0d8SBjoern A. Zeeb 	if (ret) {
134690aac0d8SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to init SDIO TX queue\n");
134790aac0d8SBjoern A. Zeeb 		goto err_sdio_declaim;
134890aac0d8SBjoern A. Zeeb 	}
134990aac0d8SBjoern A. Zeeb 
135090aac0d8SBjoern A. Zeeb 	ret = rtw_chip_info_setup(rtwdev);
135190aac0d8SBjoern A. Zeeb 	if (ret) {
135290aac0d8SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to setup chip information");
135390aac0d8SBjoern A. Zeeb 		goto err_destroy_txwq;
135490aac0d8SBjoern A. Zeeb 	}
135590aac0d8SBjoern A. Zeeb 
135690aac0d8SBjoern A. Zeeb 	ret = rtw_sdio_request_irq(rtwdev, sdio_func);
135790aac0d8SBjoern A. Zeeb 	if (ret)
135890aac0d8SBjoern A. Zeeb 		goto err_destroy_txwq;
135990aac0d8SBjoern A. Zeeb 
136090aac0d8SBjoern A. Zeeb 	ret = rtw_register_hw(rtwdev, hw);
136190aac0d8SBjoern A. Zeeb 	if (ret) {
136290aac0d8SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to register hw");
136390aac0d8SBjoern A. Zeeb 		goto err_free_irq;
136490aac0d8SBjoern A. Zeeb 	}
136590aac0d8SBjoern A. Zeeb 
136690aac0d8SBjoern A. Zeeb 	return 0;
136790aac0d8SBjoern A. Zeeb 
136890aac0d8SBjoern A. Zeeb err_free_irq:
136990aac0d8SBjoern A. Zeeb 	rtw_sdio_free_irq(rtwdev, sdio_func);
137090aac0d8SBjoern A. Zeeb err_destroy_txwq:
137190aac0d8SBjoern A. Zeeb 	rtw_sdio_deinit_tx(rtwdev);
137290aac0d8SBjoern A. Zeeb err_sdio_declaim:
137390aac0d8SBjoern A. Zeeb 	rtw_sdio_declaim(rtwdev, sdio_func);
137490aac0d8SBjoern A. Zeeb err_deinit_core:
137590aac0d8SBjoern A. Zeeb 	rtw_core_deinit(rtwdev);
137690aac0d8SBjoern A. Zeeb err_release_hw:
137790aac0d8SBjoern A. Zeeb 	ieee80211_free_hw(hw);
137890aac0d8SBjoern A. Zeeb 
137990aac0d8SBjoern A. Zeeb 	return ret;
138090aac0d8SBjoern A. Zeeb }
138190aac0d8SBjoern A. Zeeb EXPORT_SYMBOL(rtw_sdio_probe);
138290aac0d8SBjoern A. Zeeb 
138390aac0d8SBjoern A. Zeeb void rtw_sdio_remove(struct sdio_func *sdio_func)
138490aac0d8SBjoern A. Zeeb {
138590aac0d8SBjoern A. Zeeb 	struct ieee80211_hw *hw = sdio_get_drvdata(sdio_func);
138690aac0d8SBjoern A. Zeeb 	struct rtw_dev *rtwdev;
138790aac0d8SBjoern A. Zeeb 
138890aac0d8SBjoern A. Zeeb 	if (!hw)
138990aac0d8SBjoern A. Zeeb 		return;
139090aac0d8SBjoern A. Zeeb 
139190aac0d8SBjoern A. Zeeb 	rtwdev = hw->priv;
139290aac0d8SBjoern A. Zeeb 
139390aac0d8SBjoern A. Zeeb 	rtw_unregister_hw(rtwdev, hw);
139490aac0d8SBjoern A. Zeeb 	rtw_sdio_disable_interrupt(rtwdev);
139590aac0d8SBjoern A. Zeeb 	rtw_sdio_free_irq(rtwdev, sdio_func);
139690aac0d8SBjoern A. Zeeb 	rtw_sdio_declaim(rtwdev, sdio_func);
139790aac0d8SBjoern A. Zeeb 	rtw_sdio_deinit_tx(rtwdev);
139890aac0d8SBjoern A. Zeeb 	rtw_core_deinit(rtwdev);
139990aac0d8SBjoern A. Zeeb 	ieee80211_free_hw(hw);
140090aac0d8SBjoern A. Zeeb }
140190aac0d8SBjoern A. Zeeb EXPORT_SYMBOL(rtw_sdio_remove);
140290aac0d8SBjoern A. Zeeb 
140390aac0d8SBjoern A. Zeeb void rtw_sdio_shutdown(struct device *dev)
140490aac0d8SBjoern A. Zeeb {
140590aac0d8SBjoern A. Zeeb 	struct sdio_func *sdio_func = dev_to_sdio_func(dev);
140690aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip;
140790aac0d8SBjoern A. Zeeb 	struct ieee80211_hw *hw;
140890aac0d8SBjoern A. Zeeb 	struct rtw_dev *rtwdev;
140990aac0d8SBjoern A. Zeeb 
141090aac0d8SBjoern A. Zeeb 	hw = sdio_get_drvdata(sdio_func);
141190aac0d8SBjoern A. Zeeb 	if (!hw)
141290aac0d8SBjoern A. Zeeb 		return;
141390aac0d8SBjoern A. Zeeb 
141490aac0d8SBjoern A. Zeeb 	rtwdev = hw->priv;
141590aac0d8SBjoern A. Zeeb 	chip = rtwdev->chip;
141690aac0d8SBjoern A. Zeeb 
141790aac0d8SBjoern A. Zeeb 	if (chip->ops->shutdown)
141890aac0d8SBjoern A. Zeeb 		chip->ops->shutdown(rtwdev);
141990aac0d8SBjoern A. Zeeb }
142090aac0d8SBjoern A. Zeeb EXPORT_SYMBOL(rtw_sdio_shutdown);
142190aac0d8SBjoern A. Zeeb 
142290aac0d8SBjoern A. Zeeb MODULE_AUTHOR("Martin Blumenstingl");
142390aac0d8SBjoern A. Zeeb MODULE_AUTHOR("Jernej Skrabec");
142490aac0d8SBjoern A. Zeeb MODULE_DESCRIPTION("Realtek 802.11ac wireless SDIO driver");
142590aac0d8SBjoern A. Zeeb MODULE_LICENSE("Dual BSD/GPL");
1426