xref: /freebsd/sys/contrib/dev/rtw88/sdio.c (revision 90aac0d8)
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 
rtw_sdio_is_bus_addr(u32 addr)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 
rtw_sdio_bus_claim_needed(struct rtw_sdio * rtwsdio)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 
rtw_sdio_to_bus_offset(struct rtw_dev * rtwdev,u32 addr)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 
rtw_sdio_use_memcpy_io(struct rtw_dev * rtwdev,u32 addr,u8 alignment)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 
rtw_sdio_writel(struct rtw_dev * rtwdev,u32 val,u32 addr,int * err_ret)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 
rtw_sdio_writew(struct rtw_dev * rtwdev,u16 val,u32 addr,int * err_ret)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 
rtw_sdio_readl(struct rtw_dev * rtwdev,u32 addr,int * err_ret)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 
rtw_sdio_readw(struct rtw_dev * rtwdev,u32 addr,int * err_ret)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 
rtw_sdio_to_io_address(struct rtw_dev * rtwdev,u32 addr,bool direct)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 
rtw_sdio_use_direct_io(struct rtw_dev * rtwdev,u32 addr)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 
rtw_sdio_indirect_reg_cfg(struct rtw_dev * rtwdev,u32 addr,u32 cfg)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 
rtw_sdio_indirect_read8(struct rtw_dev * rtwdev,u32 addr,int * err_ret)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 
rtw_sdio_indirect_read_bytes(struct rtw_dev * rtwdev,u32 addr,u8 * buf,int count)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 
rtw_sdio_indirect_read16(struct rtw_dev * rtwdev,u32 addr,int * err_ret)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 
rtw_sdio_indirect_read32(struct rtw_dev * rtwdev,u32 addr,int * err_ret)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 
rtw_sdio_read8(struct rtw_dev * rtwdev,u32 addr)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 
rtw_sdio_read16(struct rtw_dev * rtwdev,u32 addr)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 
rtw_sdio_read32(struct rtw_dev * rtwdev,u32 addr)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 
rtw_sdio_indirect_write8(struct rtw_dev * rtwdev,u8 val,u32 addr,int * err_ret)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 
rtw_sdio_indirect_write16(struct rtw_dev * rtwdev,u16 val,u32 addr,int * err_ret)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 
rtw_sdio_indirect_write32(struct rtw_dev * rtwdev,u32 val,u32 addr,int * err_ret)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 
rtw_sdio_write8(struct rtw_dev * rtwdev,u32 addr,u8 val)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 
rtw_sdio_write16(struct rtw_dev * rtwdev,u32 addr,u16 val)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 
rtw_sdio_write32(struct rtw_dev * rtwdev,u32 addr,u32 val)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 
rtw_sdio_get_tx_addr(struct rtw_dev * rtwdev,size_t size,enum rtw_tx_queue_type queue)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 
rtw_sdio_read_port(struct rtw_dev * rtwdev,u8 * buf,size_t count)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;
50390aac0d8SBjoern A. Zeeb 	bool bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
50490aac0d8SBjoern A. Zeeb 	u32 rxaddr = rtwsdio->rx_addr++;
50590aac0d8SBjoern A. Zeeb 	int ret;
50690aac0d8SBjoern A. Zeeb 
50790aac0d8SBjoern A. Zeeb 	if (bus_claim)
50890aac0d8SBjoern A. Zeeb 		sdio_claim_host(rtwsdio->sdio_func);
50990aac0d8SBjoern A. Zeeb 
51090aac0d8SBjoern A. Zeeb 	ret = sdio_memcpy_fromio(rtwsdio->sdio_func, buf,
51190aac0d8SBjoern A. Zeeb 				 RTW_SDIO_ADDR_RX_RX0FF_GEN(rxaddr), count);
51290aac0d8SBjoern A. Zeeb 	if (ret)
51390aac0d8SBjoern A. Zeeb 		rtw_warn(rtwdev,
51490aac0d8SBjoern A. Zeeb 			 "Failed to read %zu byte(s) from SDIO port 0x%08x",
51590aac0d8SBjoern A. Zeeb 			 count, rxaddr);
51690aac0d8SBjoern A. Zeeb 
51790aac0d8SBjoern A. Zeeb 	if (bus_claim)
51890aac0d8SBjoern A. Zeeb 		sdio_release_host(rtwsdio->sdio_func);
51990aac0d8SBjoern A. Zeeb 
52090aac0d8SBjoern A. Zeeb 	return ret;
52190aac0d8SBjoern A. Zeeb }
52290aac0d8SBjoern A. Zeeb 
rtw_sdio_check_free_txpg(struct rtw_dev * rtwdev,u8 queue,size_t count)52390aac0d8SBjoern A. Zeeb static int rtw_sdio_check_free_txpg(struct rtw_dev *rtwdev, u8 queue,
52490aac0d8SBjoern A. Zeeb 				    size_t count)
52590aac0d8SBjoern A. Zeeb {
52690aac0d8SBjoern A. Zeeb 	unsigned int pages_free, pages_needed;
52790aac0d8SBjoern A. Zeeb 
52890aac0d8SBjoern A. Zeeb 	if (rtw_chip_wcpu_11n(rtwdev)) {
52990aac0d8SBjoern A. Zeeb 		u32 free_txpg;
53090aac0d8SBjoern A. Zeeb 
53190aac0d8SBjoern A. Zeeb 		free_txpg = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG);
53290aac0d8SBjoern A. Zeeb 
53390aac0d8SBjoern A. Zeeb 		switch (queue) {
53490aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_BCN:
53590aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_H2C:
53690aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_HI0:
53790aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_MGMT:
53890aac0d8SBjoern A. Zeeb 			/* high */
53990aac0d8SBjoern A. Zeeb 			pages_free = free_txpg & 0xff;
54090aac0d8SBjoern A. Zeeb 			break;
54190aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_VI:
54290aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_VO:
54390aac0d8SBjoern A. Zeeb 			/* normal */
54490aac0d8SBjoern A. Zeeb 			pages_free = (free_txpg >> 8) & 0xff;
54590aac0d8SBjoern A. Zeeb 			break;
54690aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_BE:
54790aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_BK:
54890aac0d8SBjoern A. Zeeb 			/* low */
54990aac0d8SBjoern A. Zeeb 			pages_free = (free_txpg >> 16) & 0xff;
55090aac0d8SBjoern A. Zeeb 			break;
55190aac0d8SBjoern A. Zeeb 		default:
55290aac0d8SBjoern A. Zeeb 			rtw_warn(rtwdev, "Unknown mapping for queue %u\n", queue);
55390aac0d8SBjoern A. Zeeb 			return -EINVAL;
55490aac0d8SBjoern A. Zeeb 		}
55590aac0d8SBjoern A. Zeeb 
55690aac0d8SBjoern A. Zeeb 		/* add the pages from the public queue */
55790aac0d8SBjoern A. Zeeb 		pages_free += (free_txpg >> 24) & 0xff;
55890aac0d8SBjoern A. Zeeb 	} else {
55990aac0d8SBjoern A. Zeeb 		u32 free_txpg[3];
56090aac0d8SBjoern A. Zeeb 
56190aac0d8SBjoern A. Zeeb 		free_txpg[0] = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG);
56290aac0d8SBjoern A. Zeeb 		free_txpg[1] = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG + 4);
56390aac0d8SBjoern A. Zeeb 		free_txpg[2] = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG + 8);
56490aac0d8SBjoern A. Zeeb 
56590aac0d8SBjoern A. Zeeb 		switch (queue) {
56690aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_BCN:
56790aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_H2C:
56890aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_HI0:
56990aac0d8SBjoern A. Zeeb 			/* high */
57090aac0d8SBjoern A. Zeeb 			pages_free = free_txpg[0] & 0xfff;
57190aac0d8SBjoern A. Zeeb 			break;
57290aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_VI:
57390aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_VO:
57490aac0d8SBjoern A. Zeeb 			/* normal */
57590aac0d8SBjoern A. Zeeb 			pages_free = (free_txpg[0] >> 16) & 0xfff;
57690aac0d8SBjoern A. Zeeb 			break;
57790aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_BE:
57890aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_BK:
57990aac0d8SBjoern A. Zeeb 			/* low */
58090aac0d8SBjoern A. Zeeb 			pages_free = free_txpg[1] & 0xfff;
58190aac0d8SBjoern A. Zeeb 			break;
58290aac0d8SBjoern A. Zeeb 		case RTW_TX_QUEUE_MGMT:
58390aac0d8SBjoern A. Zeeb 			/* extra */
58490aac0d8SBjoern A. Zeeb 			pages_free = free_txpg[2] & 0xfff;
58590aac0d8SBjoern A. Zeeb 			break;
58690aac0d8SBjoern A. Zeeb 		default:
58790aac0d8SBjoern A. Zeeb 			rtw_warn(rtwdev, "Unknown mapping for queue %u\n", queue);
58890aac0d8SBjoern A. Zeeb 			return -EINVAL;
58990aac0d8SBjoern A. Zeeb 		}
59090aac0d8SBjoern A. Zeeb 
59190aac0d8SBjoern A. Zeeb 		/* add the pages from the public queue */
59290aac0d8SBjoern A. Zeeb 		pages_free += (free_txpg[1] >> 16) & 0xfff;
59390aac0d8SBjoern A. Zeeb 	}
59490aac0d8SBjoern A. Zeeb 
59590aac0d8SBjoern A. Zeeb 	pages_needed = DIV_ROUND_UP(count, rtwdev->chip->page_size);
59690aac0d8SBjoern A. Zeeb 
59790aac0d8SBjoern A. Zeeb 	if (pages_needed > pages_free) {
59890aac0d8SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_SDIO,
59990aac0d8SBjoern A. Zeeb 			"Not enough free pages (%u needed, %u free) in queue %u for %zu bytes\n",
60090aac0d8SBjoern A. Zeeb 			pages_needed, pages_free, queue, count);
60190aac0d8SBjoern A. Zeeb 		return -EBUSY;
60290aac0d8SBjoern A. Zeeb 	}
60390aac0d8SBjoern A. Zeeb 
60490aac0d8SBjoern A. Zeeb 	return 0;
60590aac0d8SBjoern A. Zeeb }
60690aac0d8SBjoern A. Zeeb 
rtw_sdio_write_port(struct rtw_dev * rtwdev,struct sk_buff * skb,enum rtw_tx_queue_type queue)60790aac0d8SBjoern A. Zeeb static int rtw_sdio_write_port(struct rtw_dev *rtwdev, struct sk_buff *skb,
60890aac0d8SBjoern A. Zeeb 			       enum rtw_tx_queue_type queue)
60990aac0d8SBjoern A. Zeeb {
61090aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
61190aac0d8SBjoern A. Zeeb 	bool bus_claim;
61290aac0d8SBjoern A. Zeeb 	size_t txsize;
61390aac0d8SBjoern A. Zeeb 	u32 txaddr;
61490aac0d8SBjoern A. Zeeb 	int ret;
61590aac0d8SBjoern A. Zeeb 
61690aac0d8SBjoern A. Zeeb 	txaddr = rtw_sdio_get_tx_addr(rtwdev, skb->len, queue);
61790aac0d8SBjoern A. Zeeb 	if (!txaddr)
61890aac0d8SBjoern A. Zeeb 		return -EINVAL;
61990aac0d8SBjoern A. Zeeb 
62090aac0d8SBjoern A. Zeeb 	txsize = sdio_align_size(rtwsdio->sdio_func, skb->len);
62190aac0d8SBjoern A. Zeeb 
62290aac0d8SBjoern A. Zeeb 	ret = rtw_sdio_check_free_txpg(rtwdev, queue, txsize);
62390aac0d8SBjoern A. Zeeb 	if (ret)
62490aac0d8SBjoern A. Zeeb 		return ret;
62590aac0d8SBjoern A. Zeeb 
62690aac0d8SBjoern A. Zeeb 	if (!IS_ALIGNED((unsigned long)skb->data, RTW_SDIO_DATA_PTR_ALIGN))
62790aac0d8SBjoern A. Zeeb 		rtw_warn(rtwdev, "Got unaligned SKB in %s() for queue %u\n",
62890aac0d8SBjoern A. Zeeb 			 __func__, queue);
62990aac0d8SBjoern A. Zeeb 
63090aac0d8SBjoern A. Zeeb 	bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
63190aac0d8SBjoern A. Zeeb 
63290aac0d8SBjoern A. Zeeb 	if (bus_claim)
63390aac0d8SBjoern A. Zeeb 		sdio_claim_host(rtwsdio->sdio_func);
63490aac0d8SBjoern A. Zeeb 
63590aac0d8SBjoern A. Zeeb 	ret = sdio_memcpy_toio(rtwsdio->sdio_func, txaddr, skb->data, txsize);
63690aac0d8SBjoern A. Zeeb 
63790aac0d8SBjoern A. Zeeb 	if (bus_claim)
63890aac0d8SBjoern A. Zeeb 		sdio_release_host(rtwsdio->sdio_func);
63990aac0d8SBjoern A. Zeeb 
64090aac0d8SBjoern A. Zeeb 	if (ret)
64190aac0d8SBjoern A. Zeeb 		rtw_warn(rtwdev,
64290aac0d8SBjoern A. Zeeb 			 "Failed to write %zu byte(s) to SDIO port 0x%08x",
64390aac0d8SBjoern A. Zeeb 			 txsize, txaddr);
64490aac0d8SBjoern A. Zeeb 
64590aac0d8SBjoern A. Zeeb 	return ret;
64690aac0d8SBjoern A. Zeeb }
64790aac0d8SBjoern A. Zeeb 
rtw_sdio_init(struct rtw_dev * rtwdev)64890aac0d8SBjoern A. Zeeb static void rtw_sdio_init(struct rtw_dev *rtwdev)
64990aac0d8SBjoern A. Zeeb {
65090aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
65190aac0d8SBjoern A. Zeeb 
65290aac0d8SBjoern A. Zeeb 	rtwsdio->irq_mask = REG_SDIO_HIMR_RX_REQUEST | REG_SDIO_HIMR_CPWM1;
65390aac0d8SBjoern A. Zeeb }
65490aac0d8SBjoern A. Zeeb 
rtw_sdio_enable_rx_aggregation(struct rtw_dev * rtwdev)65590aac0d8SBjoern A. Zeeb static void rtw_sdio_enable_rx_aggregation(struct rtw_dev *rtwdev)
65690aac0d8SBjoern A. Zeeb {
65790aac0d8SBjoern A. Zeeb 	u8 size, timeout;
65890aac0d8SBjoern A. Zeeb 
65990aac0d8SBjoern A. Zeeb 	if (rtw_chip_wcpu_11n(rtwdev)) {
66090aac0d8SBjoern A. Zeeb 		size = 0x6;
66190aac0d8SBjoern A. Zeeb 		timeout = 0x6;
66290aac0d8SBjoern A. Zeeb 	} else {
66390aac0d8SBjoern A. Zeeb 		size = 0xff;
66490aac0d8SBjoern A. Zeeb 		timeout = 0x1;
66590aac0d8SBjoern A. Zeeb 	}
66690aac0d8SBjoern A. Zeeb 
66790aac0d8SBjoern A. Zeeb 	/* Make the firmware honor the size limit configured below */
66890aac0d8SBjoern A. Zeeb 	rtw_write32_set(rtwdev, REG_RXDMA_AGG_PG_TH, BIT_EN_PRE_CALC);
66990aac0d8SBjoern A. Zeeb 
67090aac0d8SBjoern A. Zeeb 	rtw_write8_set(rtwdev, REG_TXDMA_PQ_MAP, BIT_RXDMA_AGG_EN);
67190aac0d8SBjoern A. Zeeb 
67290aac0d8SBjoern A. Zeeb 	rtw_write16(rtwdev, REG_RXDMA_AGG_PG_TH,
67390aac0d8SBjoern A. Zeeb 		    FIELD_PREP(BIT_RXDMA_AGG_PG_TH, size) |
67490aac0d8SBjoern A. Zeeb 		    FIELD_PREP(BIT_DMA_AGG_TO_V1, timeout));
67590aac0d8SBjoern A. Zeeb 
67690aac0d8SBjoern A. Zeeb 	rtw_write8_set(rtwdev, REG_RXDMA_MODE, BIT_DMA_MODE);
67790aac0d8SBjoern A. Zeeb }
67890aac0d8SBjoern A. Zeeb 
rtw_sdio_enable_interrupt(struct rtw_dev * rtwdev)67990aac0d8SBjoern A. Zeeb static void rtw_sdio_enable_interrupt(struct rtw_dev *rtwdev)
68090aac0d8SBjoern A. Zeeb {
68190aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
68290aac0d8SBjoern A. Zeeb 
68390aac0d8SBjoern A. Zeeb 	rtw_write32(rtwdev, REG_SDIO_HIMR, rtwsdio->irq_mask);
68490aac0d8SBjoern A. Zeeb }
68590aac0d8SBjoern A. Zeeb 
rtw_sdio_disable_interrupt(struct rtw_dev * rtwdev)68690aac0d8SBjoern A. Zeeb static void rtw_sdio_disable_interrupt(struct rtw_dev *rtwdev)
68790aac0d8SBjoern A. Zeeb {
68890aac0d8SBjoern A. Zeeb 	rtw_write32(rtwdev, REG_SDIO_HIMR, 0x0);
68990aac0d8SBjoern A. Zeeb }
69090aac0d8SBjoern A. Zeeb 
rtw_sdio_get_tx_qsel(struct rtw_dev * rtwdev,struct sk_buff * skb,u8 queue)69190aac0d8SBjoern A. Zeeb static u8 rtw_sdio_get_tx_qsel(struct rtw_dev *rtwdev, struct sk_buff *skb,
69290aac0d8SBjoern A. Zeeb 			       u8 queue)
69390aac0d8SBjoern A. Zeeb {
69490aac0d8SBjoern A. Zeeb 	switch (queue) {
69590aac0d8SBjoern A. Zeeb 	case RTW_TX_QUEUE_BCN:
69690aac0d8SBjoern A. Zeeb 		return TX_DESC_QSEL_BEACON;
69790aac0d8SBjoern A. Zeeb 	case RTW_TX_QUEUE_H2C:
69890aac0d8SBjoern A. Zeeb 		return TX_DESC_QSEL_H2C;
69990aac0d8SBjoern A. Zeeb 	case RTW_TX_QUEUE_MGMT:
70090aac0d8SBjoern A. Zeeb 		if (rtw_chip_wcpu_11n(rtwdev))
70190aac0d8SBjoern A. Zeeb 			return TX_DESC_QSEL_HIGH;
70290aac0d8SBjoern A. Zeeb 		else
70390aac0d8SBjoern A. Zeeb 			return TX_DESC_QSEL_MGMT;
70490aac0d8SBjoern A. Zeeb 	case RTW_TX_QUEUE_HI0:
70590aac0d8SBjoern A. Zeeb 		return TX_DESC_QSEL_HIGH;
70690aac0d8SBjoern A. Zeeb 	default:
70790aac0d8SBjoern A. Zeeb 		return skb->priority;
70890aac0d8SBjoern A. Zeeb 	}
70990aac0d8SBjoern A. Zeeb }
71090aac0d8SBjoern A. Zeeb 
rtw_sdio_setup(struct rtw_dev * rtwdev)71190aac0d8SBjoern A. Zeeb static int rtw_sdio_setup(struct rtw_dev *rtwdev)
71290aac0d8SBjoern A. Zeeb {
71390aac0d8SBjoern A. Zeeb 	/* nothing to do */
71490aac0d8SBjoern A. Zeeb 	return 0;
71590aac0d8SBjoern A. Zeeb }
71690aac0d8SBjoern A. Zeeb 
rtw_sdio_start(struct rtw_dev * rtwdev)71790aac0d8SBjoern A. Zeeb static int rtw_sdio_start(struct rtw_dev *rtwdev)
71890aac0d8SBjoern A. Zeeb {
71990aac0d8SBjoern A. Zeeb 	rtw_sdio_enable_rx_aggregation(rtwdev);
72090aac0d8SBjoern A. Zeeb 	rtw_sdio_enable_interrupt(rtwdev);
72190aac0d8SBjoern A. Zeeb 
72290aac0d8SBjoern A. Zeeb 	return 0;
72390aac0d8SBjoern A. Zeeb }
72490aac0d8SBjoern A. Zeeb 
rtw_sdio_stop(struct rtw_dev * rtwdev)72590aac0d8SBjoern A. Zeeb static void rtw_sdio_stop(struct rtw_dev *rtwdev)
72690aac0d8SBjoern A. Zeeb {
72790aac0d8SBjoern A. Zeeb 	rtw_sdio_disable_interrupt(rtwdev);
72890aac0d8SBjoern A. Zeeb }
72990aac0d8SBjoern A. Zeeb 
rtw_sdio_deep_ps_enter(struct rtw_dev * rtwdev)73090aac0d8SBjoern A. Zeeb static void rtw_sdio_deep_ps_enter(struct rtw_dev *rtwdev)
73190aac0d8SBjoern A. Zeeb {
73290aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
73390aac0d8SBjoern A. Zeeb 	bool tx_empty = true;
73490aac0d8SBjoern A. Zeeb 	u8 queue;
73590aac0d8SBjoern A. Zeeb 
73690aac0d8SBjoern A. Zeeb 	if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE)) {
73790aac0d8SBjoern A. Zeeb 		/* Deep PS state is not allowed to TX-DMA */
73890aac0d8SBjoern A. Zeeb 		for (queue = 0; queue < RTK_MAX_TX_QUEUE_NUM; queue++) {
73990aac0d8SBjoern A. Zeeb 			/* BCN queue is rsvd page, does not have DMA interrupt
74090aac0d8SBjoern A. Zeeb 			 * H2C queue is managed by firmware
74190aac0d8SBjoern A. Zeeb 			 */
74290aac0d8SBjoern A. Zeeb 			if (queue == RTW_TX_QUEUE_BCN ||
74390aac0d8SBjoern A. Zeeb 			    queue == RTW_TX_QUEUE_H2C)
74490aac0d8SBjoern A. Zeeb 				continue;
74590aac0d8SBjoern A. Zeeb 
74690aac0d8SBjoern A. Zeeb 			/* check if there is any skb DMAing */
74790aac0d8SBjoern A. Zeeb 			if (skb_queue_len(&rtwsdio->tx_queue[queue])) {
74890aac0d8SBjoern A. Zeeb 				tx_empty = false;
74990aac0d8SBjoern A. Zeeb 				break;
75090aac0d8SBjoern A. Zeeb 			}
75190aac0d8SBjoern A. Zeeb 		}
75290aac0d8SBjoern A. Zeeb 	}
75390aac0d8SBjoern A. Zeeb 
75490aac0d8SBjoern A. Zeeb 	if (!tx_empty) {
75590aac0d8SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_PS,
75690aac0d8SBjoern A. Zeeb 			"TX path not empty, cannot enter deep power save state\n");
75790aac0d8SBjoern A. Zeeb 		return;
75890aac0d8SBjoern A. Zeeb 	}
75990aac0d8SBjoern A. Zeeb 
76090aac0d8SBjoern A. Zeeb 	set_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags);
76190aac0d8SBjoern A. Zeeb 	rtw_power_mode_change(rtwdev, true);
76290aac0d8SBjoern A. Zeeb }
76390aac0d8SBjoern A. Zeeb 
rtw_sdio_deep_ps_leave(struct rtw_dev * rtwdev)76490aac0d8SBjoern A. Zeeb static void rtw_sdio_deep_ps_leave(struct rtw_dev *rtwdev)
76590aac0d8SBjoern A. Zeeb {
76690aac0d8SBjoern A. Zeeb 	if (test_and_clear_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags))
76790aac0d8SBjoern A. Zeeb 		rtw_power_mode_change(rtwdev, false);
76890aac0d8SBjoern A. Zeeb }
76990aac0d8SBjoern A. Zeeb 
rtw_sdio_deep_ps(struct rtw_dev * rtwdev,bool enter)77090aac0d8SBjoern A. Zeeb static void rtw_sdio_deep_ps(struct rtw_dev *rtwdev, bool enter)
77190aac0d8SBjoern A. Zeeb {
77290aac0d8SBjoern A. Zeeb 	if (enter && !test_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags))
77390aac0d8SBjoern A. Zeeb 		rtw_sdio_deep_ps_enter(rtwdev);
77490aac0d8SBjoern A. Zeeb 
77590aac0d8SBjoern A. Zeeb 	if (!enter && test_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags))
77690aac0d8SBjoern A. Zeeb 		rtw_sdio_deep_ps_leave(rtwdev);
77790aac0d8SBjoern A. Zeeb }
77890aac0d8SBjoern A. Zeeb 
rtw_sdio_tx_kick_off(struct rtw_dev * rtwdev)77990aac0d8SBjoern A. Zeeb static void rtw_sdio_tx_kick_off(struct rtw_dev *rtwdev)
78090aac0d8SBjoern A. Zeeb {
78190aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
78290aac0d8SBjoern A. Zeeb 
78390aac0d8SBjoern A. Zeeb 	queue_work(rtwsdio->txwq, &rtwsdio->tx_handler_data->work);
78490aac0d8SBjoern A. Zeeb }
78590aac0d8SBjoern A. Zeeb 
rtw_sdio_link_ps(struct rtw_dev * rtwdev,bool enter)78690aac0d8SBjoern A. Zeeb static void rtw_sdio_link_ps(struct rtw_dev *rtwdev, bool enter)
78790aac0d8SBjoern A. Zeeb {
78890aac0d8SBjoern A. Zeeb 	/* nothing to do */
78990aac0d8SBjoern A. Zeeb }
79090aac0d8SBjoern A. Zeeb 
rtw_sdio_interface_cfg(struct rtw_dev * rtwdev)79190aac0d8SBjoern A. Zeeb static void rtw_sdio_interface_cfg(struct rtw_dev *rtwdev)
79290aac0d8SBjoern A. Zeeb {
79390aac0d8SBjoern A. Zeeb 	u32 val;
79490aac0d8SBjoern A. Zeeb 
79590aac0d8SBjoern A. Zeeb 	rtw_read32(rtwdev, REG_SDIO_FREE_TXPG);
79690aac0d8SBjoern A. Zeeb 
79790aac0d8SBjoern A. Zeeb 	val = rtw_read32(rtwdev, REG_SDIO_TX_CTRL);
79890aac0d8SBjoern A. Zeeb 	val &= 0xfff8;
79990aac0d8SBjoern A. Zeeb 	rtw_write32(rtwdev, REG_SDIO_TX_CTRL, val);
80090aac0d8SBjoern A. Zeeb }
80190aac0d8SBjoern A. Zeeb 
rtw_sdio_get_tx_data(struct sk_buff * skb)80290aac0d8SBjoern A. Zeeb static struct rtw_sdio_tx_data *rtw_sdio_get_tx_data(struct sk_buff *skb)
80390aac0d8SBjoern A. Zeeb {
80490aac0d8SBjoern A. Zeeb 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
80590aac0d8SBjoern A. Zeeb 
80690aac0d8SBjoern A. Zeeb 	BUILD_BUG_ON(sizeof(struct rtw_sdio_tx_data) >
80790aac0d8SBjoern A. Zeeb 		     sizeof(info->status.status_driver_data));
80890aac0d8SBjoern A. Zeeb 
80990aac0d8SBjoern A. Zeeb 	return (struct rtw_sdio_tx_data *)info->status.status_driver_data;
81090aac0d8SBjoern A. Zeeb }
81190aac0d8SBjoern A. Zeeb 
rtw_sdio_tx_skb_prepare(struct rtw_dev * rtwdev,struct rtw_tx_pkt_info * pkt_info,struct sk_buff * skb,enum rtw_tx_queue_type queue)81290aac0d8SBjoern A. Zeeb static void rtw_sdio_tx_skb_prepare(struct rtw_dev *rtwdev,
81390aac0d8SBjoern A. Zeeb 				    struct rtw_tx_pkt_info *pkt_info,
81490aac0d8SBjoern A. Zeeb 				    struct sk_buff *skb,
81590aac0d8SBjoern A. Zeeb 				    enum rtw_tx_queue_type queue)
81690aac0d8SBjoern A. Zeeb {
81790aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
81890aac0d8SBjoern A. Zeeb 	unsigned long data_addr, aligned_addr;
81990aac0d8SBjoern A. Zeeb 	size_t offset;
82090aac0d8SBjoern A. Zeeb 	u8 *pkt_desc;
82190aac0d8SBjoern A. Zeeb 
82290aac0d8SBjoern A. Zeeb 	pkt_desc = skb_push(skb, chip->tx_pkt_desc_sz);
82390aac0d8SBjoern A. Zeeb 
82490aac0d8SBjoern A. Zeeb 	data_addr = (unsigned long)pkt_desc;
82590aac0d8SBjoern A. Zeeb 	aligned_addr = ALIGN(data_addr, RTW_SDIO_DATA_PTR_ALIGN);
82690aac0d8SBjoern A. Zeeb 
82790aac0d8SBjoern A. Zeeb 	if (data_addr != aligned_addr) {
82890aac0d8SBjoern A. Zeeb 		/* Ensure that the start of the pkt_desc is always aligned at
82990aac0d8SBjoern A. Zeeb 		 * RTW_SDIO_DATA_PTR_ALIGN.
83090aac0d8SBjoern A. Zeeb 		 */
83190aac0d8SBjoern A. Zeeb 		offset = RTW_SDIO_DATA_PTR_ALIGN - (aligned_addr - data_addr);
83290aac0d8SBjoern A. Zeeb 
83390aac0d8SBjoern A. Zeeb 		pkt_desc = skb_push(skb, offset);
83490aac0d8SBjoern A. Zeeb 
83590aac0d8SBjoern A. Zeeb 		/* By inserting padding to align the start of the pkt_desc we
83690aac0d8SBjoern A. Zeeb 		 * need to inform the firmware that the actual data starts at
83790aac0d8SBjoern A. Zeeb 		 * a different offset than normal.
83890aac0d8SBjoern A. Zeeb 		 */
83990aac0d8SBjoern A. Zeeb 		pkt_info->offset += offset;
84090aac0d8SBjoern A. Zeeb 	}
84190aac0d8SBjoern A. Zeeb 
84290aac0d8SBjoern A. Zeeb 	memset(pkt_desc, 0, chip->tx_pkt_desc_sz);
84390aac0d8SBjoern A. Zeeb 
84490aac0d8SBjoern A. Zeeb 	pkt_info->qsel = rtw_sdio_get_tx_qsel(rtwdev, skb, queue);
84590aac0d8SBjoern A. Zeeb 
84690aac0d8SBjoern A. Zeeb 	rtw_tx_fill_tx_desc(pkt_info, skb);
84790aac0d8SBjoern A. Zeeb 	rtw_tx_fill_txdesc_checksum(rtwdev, pkt_info, pkt_desc);
84890aac0d8SBjoern A. Zeeb }
84990aac0d8SBjoern A. Zeeb 
rtw_sdio_write_data(struct rtw_dev * rtwdev,struct rtw_tx_pkt_info * pkt_info,struct sk_buff * skb,enum rtw_tx_queue_type queue)85090aac0d8SBjoern A. Zeeb static int rtw_sdio_write_data(struct rtw_dev *rtwdev,
85190aac0d8SBjoern A. Zeeb 			       struct rtw_tx_pkt_info *pkt_info,
85290aac0d8SBjoern A. Zeeb 			       struct sk_buff *skb,
85390aac0d8SBjoern A. Zeeb 			       enum rtw_tx_queue_type queue)
85490aac0d8SBjoern A. Zeeb {
85590aac0d8SBjoern A. Zeeb 	int ret;
85690aac0d8SBjoern A. Zeeb 
85790aac0d8SBjoern A. Zeeb 	rtw_sdio_tx_skb_prepare(rtwdev, pkt_info, skb, queue);
85890aac0d8SBjoern A. Zeeb 
85990aac0d8SBjoern A. Zeeb 	ret = rtw_sdio_write_port(rtwdev, skb, queue);
86090aac0d8SBjoern A. Zeeb 	dev_kfree_skb_any(skb);
86190aac0d8SBjoern A. Zeeb 
86290aac0d8SBjoern A. Zeeb 	return ret;
86390aac0d8SBjoern A. Zeeb }
86490aac0d8SBjoern A. Zeeb 
rtw_sdio_write_data_rsvd_page(struct rtw_dev * rtwdev,u8 * buf,u32 size)86590aac0d8SBjoern A. Zeeb static int rtw_sdio_write_data_rsvd_page(struct rtw_dev *rtwdev, u8 *buf,
86690aac0d8SBjoern A. Zeeb 					 u32 size)
86790aac0d8SBjoern A. Zeeb {
86890aac0d8SBjoern A. Zeeb 	struct rtw_tx_pkt_info pkt_info = {};
86990aac0d8SBjoern A. Zeeb 	struct sk_buff *skb;
87090aac0d8SBjoern A. Zeeb 
87190aac0d8SBjoern A. Zeeb 	skb = rtw_tx_write_data_rsvd_page_get(rtwdev, &pkt_info, buf, size);
87290aac0d8SBjoern A. Zeeb 	if (!skb)
87390aac0d8SBjoern A. Zeeb 		return -ENOMEM;
87490aac0d8SBjoern A. Zeeb 
87590aac0d8SBjoern A. Zeeb 	return rtw_sdio_write_data(rtwdev, &pkt_info, skb, RTW_TX_QUEUE_BCN);
87690aac0d8SBjoern A. Zeeb }
87790aac0d8SBjoern A. Zeeb 
rtw_sdio_write_data_h2c(struct rtw_dev * rtwdev,u8 * buf,u32 size)87890aac0d8SBjoern A. Zeeb static int rtw_sdio_write_data_h2c(struct rtw_dev *rtwdev, u8 *buf, u32 size)
87990aac0d8SBjoern A. Zeeb {
88090aac0d8SBjoern A. Zeeb 	struct rtw_tx_pkt_info pkt_info = {};
88190aac0d8SBjoern A. Zeeb 	struct sk_buff *skb;
88290aac0d8SBjoern A. Zeeb 
88390aac0d8SBjoern A. Zeeb 	skb = rtw_tx_write_data_h2c_get(rtwdev, &pkt_info, buf, size);
88490aac0d8SBjoern A. Zeeb 	if (!skb)
88590aac0d8SBjoern A. Zeeb 		return -ENOMEM;
88690aac0d8SBjoern A. Zeeb 
88790aac0d8SBjoern A. Zeeb 	return rtw_sdio_write_data(rtwdev, &pkt_info, skb, RTW_TX_QUEUE_H2C);
88890aac0d8SBjoern A. Zeeb }
88990aac0d8SBjoern A. Zeeb 
rtw_sdio_tx_write(struct rtw_dev * rtwdev,struct rtw_tx_pkt_info * pkt_info,struct sk_buff * skb)89090aac0d8SBjoern A. Zeeb static int rtw_sdio_tx_write(struct rtw_dev *rtwdev,
89190aac0d8SBjoern A. Zeeb 			     struct rtw_tx_pkt_info *pkt_info,
89290aac0d8SBjoern A. Zeeb 			     struct sk_buff *skb)
89390aac0d8SBjoern A. Zeeb {
89490aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
89590aac0d8SBjoern A. Zeeb 	enum rtw_tx_queue_type queue = rtw_tx_queue_mapping(skb);
89690aac0d8SBjoern A. Zeeb 	struct rtw_sdio_tx_data *tx_data;
89790aac0d8SBjoern A. Zeeb 
89890aac0d8SBjoern A. Zeeb 	rtw_sdio_tx_skb_prepare(rtwdev, pkt_info, skb, queue);
89990aac0d8SBjoern A. Zeeb 
90090aac0d8SBjoern A. Zeeb 	tx_data = rtw_sdio_get_tx_data(skb);
90190aac0d8SBjoern A. Zeeb 	tx_data->sn = pkt_info->sn;
90290aac0d8SBjoern A. Zeeb 
90390aac0d8SBjoern A. Zeeb 	skb_queue_tail(&rtwsdio->tx_queue[queue], skb);
90490aac0d8SBjoern A. Zeeb 
90590aac0d8SBjoern A. Zeeb 	return 0;
90690aac0d8SBjoern A. Zeeb }
90790aac0d8SBjoern A. Zeeb 
rtw_sdio_tx_err_isr(struct rtw_dev * rtwdev)90890aac0d8SBjoern A. Zeeb static void rtw_sdio_tx_err_isr(struct rtw_dev *rtwdev)
90990aac0d8SBjoern A. Zeeb {
91090aac0d8SBjoern A. Zeeb 	u32 val = rtw_read32(rtwdev, REG_TXDMA_STATUS);
91190aac0d8SBjoern A. Zeeb 
91290aac0d8SBjoern A. Zeeb 	rtw_write32(rtwdev, REG_TXDMA_STATUS, val);
91390aac0d8SBjoern A. Zeeb }
91490aac0d8SBjoern A. Zeeb 
rtw_sdio_rx_skb(struct rtw_dev * rtwdev,struct sk_buff * skb,u32 pkt_offset,struct rtw_rx_pkt_stat * pkt_stat,struct ieee80211_rx_status * rx_status)91590aac0d8SBjoern A. Zeeb static void rtw_sdio_rx_skb(struct rtw_dev *rtwdev, struct sk_buff *skb,
91690aac0d8SBjoern A. Zeeb 			    u32 pkt_offset, struct rtw_rx_pkt_stat *pkt_stat,
91790aac0d8SBjoern A. Zeeb 			    struct ieee80211_rx_status *rx_status)
91890aac0d8SBjoern A. Zeeb {
91990aac0d8SBjoern A. Zeeb 	*IEEE80211_SKB_RXCB(skb) = *rx_status;
92090aac0d8SBjoern A. Zeeb 
92190aac0d8SBjoern A. Zeeb 	if (pkt_stat->is_c2h) {
92290aac0d8SBjoern A. Zeeb 		skb_put(skb, pkt_stat->pkt_len + pkt_offset);
92390aac0d8SBjoern A. Zeeb 		rtw_fw_c2h_cmd_rx_irqsafe(rtwdev, pkt_offset, skb);
92490aac0d8SBjoern A. Zeeb 		return;
92590aac0d8SBjoern A. Zeeb 	}
92690aac0d8SBjoern A. Zeeb 
92790aac0d8SBjoern A. Zeeb 	skb_put(skb, pkt_stat->pkt_len);
92890aac0d8SBjoern A. Zeeb 	skb_reserve(skb, pkt_offset);
92990aac0d8SBjoern A. Zeeb 
93090aac0d8SBjoern A. Zeeb 	rtw_rx_stats(rtwdev, pkt_stat->vif, skb);
93190aac0d8SBjoern A. Zeeb 
93290aac0d8SBjoern A. Zeeb 	ieee80211_rx_irqsafe(rtwdev->hw, skb);
93390aac0d8SBjoern A. Zeeb }
93490aac0d8SBjoern A. Zeeb 
rtw_sdio_rxfifo_recv(struct rtw_dev * rtwdev,u32 rx_len)93590aac0d8SBjoern A. Zeeb static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len)
93690aac0d8SBjoern A. Zeeb {
93790aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
93890aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
93990aac0d8SBjoern A. Zeeb 	u32 pkt_desc_sz = chip->rx_pkt_desc_sz;
94090aac0d8SBjoern A. Zeeb 	struct ieee80211_rx_status rx_status;
94190aac0d8SBjoern A. Zeeb 	struct rtw_rx_pkt_stat pkt_stat;
94290aac0d8SBjoern A. Zeeb 	struct sk_buff *skb, *split_skb;
94390aac0d8SBjoern A. Zeeb 	u32 pkt_offset, curr_pkt_len;
94490aac0d8SBjoern A. Zeeb 	size_t bufsz;
94590aac0d8SBjoern A. Zeeb 	u8 *rx_desc;
94690aac0d8SBjoern A. Zeeb 	int ret;
94790aac0d8SBjoern A. Zeeb 
94890aac0d8SBjoern A. Zeeb 	bufsz = sdio_align_size(rtwsdio->sdio_func, rx_len);
94990aac0d8SBjoern A. Zeeb 
95090aac0d8SBjoern A. Zeeb 	skb = dev_alloc_skb(bufsz);
95190aac0d8SBjoern A. Zeeb 	if (!skb)
95290aac0d8SBjoern A. Zeeb 		return;
95390aac0d8SBjoern A. Zeeb 
95490aac0d8SBjoern A. Zeeb 	ret = rtw_sdio_read_port(rtwdev, skb->data, bufsz);
95590aac0d8SBjoern A. Zeeb 	if (ret) {
95690aac0d8SBjoern A. Zeeb 		dev_kfree_skb_any(skb);
95790aac0d8SBjoern A. Zeeb 		return;
95890aac0d8SBjoern A. Zeeb 	}
95990aac0d8SBjoern A. Zeeb 
96090aac0d8SBjoern A. Zeeb 	while (true) {
96190aac0d8SBjoern A. Zeeb 		rx_desc = skb->data;
96290aac0d8SBjoern A. Zeeb 		chip->ops->query_rx_desc(rtwdev, rx_desc, &pkt_stat,
96390aac0d8SBjoern A. Zeeb 					 &rx_status);
96490aac0d8SBjoern A. Zeeb 		pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz +
96590aac0d8SBjoern A. Zeeb 			     pkt_stat.shift;
96690aac0d8SBjoern A. Zeeb 
96790aac0d8SBjoern A. Zeeb 		curr_pkt_len = ALIGN(pkt_offset + pkt_stat.pkt_len,
96890aac0d8SBjoern A. Zeeb 				     RTW_SDIO_DATA_PTR_ALIGN);
96990aac0d8SBjoern A. Zeeb 
97090aac0d8SBjoern A. Zeeb 		if ((curr_pkt_len + pkt_desc_sz) >= rx_len) {
97190aac0d8SBjoern A. Zeeb 			/* Use the original skb (with it's adjusted offset)
97290aac0d8SBjoern A. Zeeb 			 * when processing the last (or even the only) entry to
97390aac0d8SBjoern A. Zeeb 			 * have it's memory freed automatically.
97490aac0d8SBjoern A. Zeeb 			 */
97590aac0d8SBjoern A. Zeeb 			rtw_sdio_rx_skb(rtwdev, skb, pkt_offset, &pkt_stat,
97690aac0d8SBjoern A. Zeeb 					&rx_status);
97790aac0d8SBjoern A. Zeeb 			break;
97890aac0d8SBjoern A. Zeeb 		}
97990aac0d8SBjoern A. Zeeb 
98090aac0d8SBjoern A. Zeeb 		split_skb = dev_alloc_skb(curr_pkt_len);
98190aac0d8SBjoern A. Zeeb 		if (!split_skb) {
98290aac0d8SBjoern A. Zeeb 			rtw_sdio_rx_skb(rtwdev, skb, pkt_offset, &pkt_stat,
98390aac0d8SBjoern A. Zeeb 					&rx_status);
98490aac0d8SBjoern A. Zeeb 			break;
98590aac0d8SBjoern A. Zeeb 		}
98690aac0d8SBjoern A. Zeeb 
98790aac0d8SBjoern A. Zeeb 		skb_copy_header(split_skb, skb);
98890aac0d8SBjoern A. Zeeb 		memcpy(split_skb->data, skb->data, curr_pkt_len);
98990aac0d8SBjoern A. Zeeb 
99090aac0d8SBjoern A. Zeeb 		rtw_sdio_rx_skb(rtwdev, split_skb, pkt_offset, &pkt_stat,
99190aac0d8SBjoern A. Zeeb 				&rx_status);
99290aac0d8SBjoern A. Zeeb 
99390aac0d8SBjoern A. Zeeb 		/* Move to the start of the next RX descriptor */
99490aac0d8SBjoern A. Zeeb 		skb_reserve(skb, curr_pkt_len);
99590aac0d8SBjoern A. Zeeb 		rx_len -= curr_pkt_len;
99690aac0d8SBjoern A. Zeeb 	}
99790aac0d8SBjoern A. Zeeb }
99890aac0d8SBjoern A. Zeeb 
rtw_sdio_rx_isr(struct rtw_dev * rtwdev)99990aac0d8SBjoern A. Zeeb static void rtw_sdio_rx_isr(struct rtw_dev *rtwdev)
100090aac0d8SBjoern A. Zeeb {
100190aac0d8SBjoern A. Zeeb 	u32 rx_len, hisr, total_rx_bytes = 0;
100290aac0d8SBjoern A. Zeeb 
100390aac0d8SBjoern A. Zeeb 	do {
100490aac0d8SBjoern A. Zeeb 		if (rtw_chip_wcpu_11n(rtwdev))
100590aac0d8SBjoern A. Zeeb 			rx_len = rtw_read16(rtwdev, REG_SDIO_RX0_REQ_LEN);
100690aac0d8SBjoern A. Zeeb 		else
100790aac0d8SBjoern A. Zeeb 			rx_len = rtw_read32(rtwdev, REG_SDIO_RX0_REQ_LEN);
100890aac0d8SBjoern A. Zeeb 
100990aac0d8SBjoern A. Zeeb 		if (!rx_len)
101090aac0d8SBjoern A. Zeeb 			break;
101190aac0d8SBjoern A. Zeeb 
101290aac0d8SBjoern A. Zeeb 		rtw_sdio_rxfifo_recv(rtwdev, rx_len);
101390aac0d8SBjoern A. Zeeb 
101490aac0d8SBjoern A. Zeeb 		total_rx_bytes += rx_len;
101590aac0d8SBjoern A. Zeeb 
101690aac0d8SBjoern A. Zeeb 		if (rtw_chip_wcpu_11n(rtwdev)) {
101790aac0d8SBjoern A. Zeeb 			/* Stop if no more RX requests are pending, even if
101890aac0d8SBjoern A. Zeeb 			 * rx_len could be greater than zero in the next
101990aac0d8SBjoern A. Zeeb 			 * iteration. This is needed because the RX buffer may
102090aac0d8SBjoern A. Zeeb 			 * already contain data while either HW or FW are not
102190aac0d8SBjoern A. Zeeb 			 * done filling that buffer yet. Still reading the
102290aac0d8SBjoern A. Zeeb 			 * buffer can result in packets where
102390aac0d8SBjoern A. Zeeb 			 * rtw_rx_pkt_stat.pkt_len is zero or points beyond the
102490aac0d8SBjoern A. Zeeb 			 * end of the buffer.
102590aac0d8SBjoern A. Zeeb 			 */
102690aac0d8SBjoern A. Zeeb 			hisr = rtw_read32(rtwdev, REG_SDIO_HISR);
102790aac0d8SBjoern A. Zeeb 		} else {
102890aac0d8SBjoern A. Zeeb 			/* RTW_WCPU_11AC chips have improved hardware or
102990aac0d8SBjoern A. Zeeb 			 * firmware and can use rx_len unconditionally.
103090aac0d8SBjoern A. Zeeb 			 */
103190aac0d8SBjoern A. Zeeb 			hisr = REG_SDIO_HISR_RX_REQUEST;
103290aac0d8SBjoern A. Zeeb 		}
103390aac0d8SBjoern A. Zeeb 	} while (total_rx_bytes < SZ_64K && hisr & REG_SDIO_HISR_RX_REQUEST);
103490aac0d8SBjoern A. Zeeb }
103590aac0d8SBjoern A. Zeeb 
rtw_sdio_handle_interrupt(struct sdio_func * sdio_func)103690aac0d8SBjoern A. Zeeb static void rtw_sdio_handle_interrupt(struct sdio_func *sdio_func)
103790aac0d8SBjoern A. Zeeb {
103890aac0d8SBjoern A. Zeeb 	struct ieee80211_hw *hw = sdio_get_drvdata(sdio_func);
103990aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio;
104090aac0d8SBjoern A. Zeeb 	struct rtw_dev *rtwdev;
104190aac0d8SBjoern A. Zeeb 	u32 hisr;
104290aac0d8SBjoern A. Zeeb 
104390aac0d8SBjoern A. Zeeb 	rtwdev = hw->priv;
104490aac0d8SBjoern A. Zeeb 	rtwsdio = (struct rtw_sdio *)rtwdev->priv;
104590aac0d8SBjoern A. Zeeb 
104690aac0d8SBjoern A. Zeeb 	rtwsdio->irq_thread = current;
104790aac0d8SBjoern A. Zeeb 
104890aac0d8SBjoern A. Zeeb 	hisr = rtw_read32(rtwdev, REG_SDIO_HISR);
104990aac0d8SBjoern A. Zeeb 
105090aac0d8SBjoern A. Zeeb 	if (hisr & REG_SDIO_HISR_TXERR)
105190aac0d8SBjoern A. Zeeb 		rtw_sdio_tx_err_isr(rtwdev);
105290aac0d8SBjoern A. Zeeb 	if (hisr & REG_SDIO_HISR_RX_REQUEST) {
105390aac0d8SBjoern A. Zeeb 		hisr &= ~REG_SDIO_HISR_RX_REQUEST;
105490aac0d8SBjoern A. Zeeb 		rtw_sdio_rx_isr(rtwdev);
105590aac0d8SBjoern A. Zeeb 	}
105690aac0d8SBjoern A. Zeeb 
105790aac0d8SBjoern A. Zeeb 	rtw_write32(rtwdev, REG_SDIO_HISR, hisr);
105890aac0d8SBjoern A. Zeeb 
105990aac0d8SBjoern A. Zeeb 	rtwsdio->irq_thread = NULL;
106090aac0d8SBjoern A. Zeeb }
106190aac0d8SBjoern A. Zeeb 
rtw_sdio_suspend(struct device * dev)106290aac0d8SBjoern A. Zeeb static int __maybe_unused rtw_sdio_suspend(struct device *dev)
106390aac0d8SBjoern A. Zeeb {
106490aac0d8SBjoern A. Zeeb 	struct sdio_func *func = dev_to_sdio_func(dev);
106590aac0d8SBjoern A. Zeeb 	struct ieee80211_hw *hw = dev_get_drvdata(dev);
106690aac0d8SBjoern A. Zeeb 	struct rtw_dev *rtwdev = hw->priv;
106790aac0d8SBjoern A. Zeeb 	int ret;
106890aac0d8SBjoern A. Zeeb 
106990aac0d8SBjoern A. Zeeb 	ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
107090aac0d8SBjoern A. Zeeb 	if (ret)
107190aac0d8SBjoern A. Zeeb 		rtw_err(rtwdev, "Failed to host PM flag MMC_PM_KEEP_POWER");
107290aac0d8SBjoern A. Zeeb 
107390aac0d8SBjoern A. Zeeb 	return ret;
107490aac0d8SBjoern A. Zeeb }
107590aac0d8SBjoern A. Zeeb 
rtw_sdio_resume(struct device * dev)107690aac0d8SBjoern A. Zeeb static int __maybe_unused rtw_sdio_resume(struct device *dev)
107790aac0d8SBjoern A. Zeeb {
107890aac0d8SBjoern A. Zeeb 	return 0;
107990aac0d8SBjoern A. Zeeb }
108090aac0d8SBjoern A. Zeeb 
108190aac0d8SBjoern A. Zeeb SIMPLE_DEV_PM_OPS(rtw_sdio_pm_ops, rtw_sdio_suspend, rtw_sdio_resume);
108290aac0d8SBjoern A. Zeeb EXPORT_SYMBOL(rtw_sdio_pm_ops);
108390aac0d8SBjoern A. Zeeb 
rtw_sdio_claim(struct rtw_dev * rtwdev,struct sdio_func * sdio_func)108490aac0d8SBjoern A. Zeeb static int rtw_sdio_claim(struct rtw_dev *rtwdev, struct sdio_func *sdio_func)
108590aac0d8SBjoern A. Zeeb {
108690aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
108790aac0d8SBjoern A. Zeeb 	int ret;
108890aac0d8SBjoern A. Zeeb 
108990aac0d8SBjoern A. Zeeb 	sdio_claim_host(sdio_func);
109090aac0d8SBjoern A. Zeeb 
109190aac0d8SBjoern A. Zeeb 	ret = sdio_enable_func(sdio_func);
109290aac0d8SBjoern A. Zeeb 	if (ret) {
109390aac0d8SBjoern A. Zeeb 		rtw_err(rtwdev, "Failed to enable SDIO func");
109490aac0d8SBjoern A. Zeeb 		goto err_release_host;
109590aac0d8SBjoern A. Zeeb 	}
109690aac0d8SBjoern A. Zeeb 
109790aac0d8SBjoern A. Zeeb 	ret = sdio_set_block_size(sdio_func, RTW_SDIO_BLOCK_SIZE);
109890aac0d8SBjoern A. Zeeb 	if (ret) {
109990aac0d8SBjoern A. Zeeb 		rtw_err(rtwdev, "Failed to set SDIO block size to 512");
110090aac0d8SBjoern A. Zeeb 		goto err_disable_func;
110190aac0d8SBjoern A. Zeeb 	}
110290aac0d8SBjoern A. Zeeb 
110390aac0d8SBjoern A. Zeeb 	rtwsdio->sdio_func = sdio_func;
110490aac0d8SBjoern A. Zeeb 
110590aac0d8SBjoern A. Zeeb 	rtwsdio->sdio3_bus_mode = mmc_card_uhs(sdio_func->card);
110690aac0d8SBjoern A. Zeeb 
110790aac0d8SBjoern A. Zeeb 	sdio_set_drvdata(sdio_func, rtwdev->hw);
110890aac0d8SBjoern A. Zeeb 	SET_IEEE80211_DEV(rtwdev->hw, &sdio_func->dev);
110990aac0d8SBjoern A. Zeeb 
111090aac0d8SBjoern A. Zeeb 	sdio_release_host(sdio_func);
111190aac0d8SBjoern A. Zeeb 
111290aac0d8SBjoern A. Zeeb 	return 0;
111390aac0d8SBjoern A. Zeeb 
111490aac0d8SBjoern A. Zeeb err_disable_func:
111590aac0d8SBjoern A. Zeeb 	sdio_disable_func(sdio_func);
111690aac0d8SBjoern A. Zeeb err_release_host:
111790aac0d8SBjoern A. Zeeb 	sdio_release_host(sdio_func);
111890aac0d8SBjoern A. Zeeb 	return ret;
111990aac0d8SBjoern A. Zeeb }
112090aac0d8SBjoern A. Zeeb 
rtw_sdio_declaim(struct rtw_dev * rtwdev,struct sdio_func * sdio_func)112190aac0d8SBjoern A. Zeeb static void rtw_sdio_declaim(struct rtw_dev *rtwdev,
112290aac0d8SBjoern A. Zeeb 			     struct sdio_func *sdio_func)
112390aac0d8SBjoern A. Zeeb {
112490aac0d8SBjoern A. Zeeb 	sdio_claim_host(sdio_func);
112590aac0d8SBjoern A. Zeeb 	sdio_disable_func(sdio_func);
112690aac0d8SBjoern A. Zeeb 	sdio_release_host(sdio_func);
112790aac0d8SBjoern A. Zeeb }
112890aac0d8SBjoern A. Zeeb 
112990aac0d8SBjoern A. Zeeb static struct rtw_hci_ops rtw_sdio_ops = {
113090aac0d8SBjoern A. Zeeb 	.tx_write = rtw_sdio_tx_write,
113190aac0d8SBjoern A. Zeeb 	.tx_kick_off = rtw_sdio_tx_kick_off,
113290aac0d8SBjoern A. Zeeb 	.setup = rtw_sdio_setup,
113390aac0d8SBjoern A. Zeeb 	.start = rtw_sdio_start,
113490aac0d8SBjoern A. Zeeb 	.stop = rtw_sdio_stop,
113590aac0d8SBjoern A. Zeeb 	.deep_ps = rtw_sdio_deep_ps,
113690aac0d8SBjoern A. Zeeb 	.link_ps = rtw_sdio_link_ps,
113790aac0d8SBjoern A. Zeeb 	.interface_cfg = rtw_sdio_interface_cfg,
113890aac0d8SBjoern A. Zeeb 
113990aac0d8SBjoern A. Zeeb 	.read8 = rtw_sdio_read8,
114090aac0d8SBjoern A. Zeeb 	.read16 = rtw_sdio_read16,
114190aac0d8SBjoern A. Zeeb 	.read32 = rtw_sdio_read32,
114290aac0d8SBjoern A. Zeeb 	.write8 = rtw_sdio_write8,
114390aac0d8SBjoern A. Zeeb 	.write16 = rtw_sdio_write16,
114490aac0d8SBjoern A. Zeeb 	.write32 = rtw_sdio_write32,
114590aac0d8SBjoern A. Zeeb 	.write_data_rsvd_page = rtw_sdio_write_data_rsvd_page,
114690aac0d8SBjoern A. Zeeb 	.write_data_h2c = rtw_sdio_write_data_h2c,
114790aac0d8SBjoern A. Zeeb };
114890aac0d8SBjoern A. Zeeb 
rtw_sdio_request_irq(struct rtw_dev * rtwdev,struct sdio_func * sdio_func)114990aac0d8SBjoern A. Zeeb static int rtw_sdio_request_irq(struct rtw_dev *rtwdev,
115090aac0d8SBjoern A. Zeeb 				struct sdio_func *sdio_func)
115190aac0d8SBjoern A. Zeeb {
115290aac0d8SBjoern A. Zeeb 	int ret;
115390aac0d8SBjoern A. Zeeb 
115490aac0d8SBjoern A. Zeeb 	sdio_claim_host(sdio_func);
115590aac0d8SBjoern A. Zeeb 	ret = sdio_claim_irq(sdio_func, &rtw_sdio_handle_interrupt);
115690aac0d8SBjoern A. Zeeb 	sdio_release_host(sdio_func);
115790aac0d8SBjoern A. Zeeb 
115890aac0d8SBjoern A. Zeeb 	if (ret) {
115990aac0d8SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to claim SDIO IRQ");
116090aac0d8SBjoern A. Zeeb 		return ret;
116190aac0d8SBjoern A. Zeeb 	}
116290aac0d8SBjoern A. Zeeb 
116390aac0d8SBjoern A. Zeeb 	return 0;
116490aac0d8SBjoern A. Zeeb }
116590aac0d8SBjoern A. Zeeb 
rtw_sdio_indicate_tx_status(struct rtw_dev * rtwdev,struct sk_buff * skb)116690aac0d8SBjoern A. Zeeb static void rtw_sdio_indicate_tx_status(struct rtw_dev *rtwdev,
116790aac0d8SBjoern A. Zeeb 					struct sk_buff *skb)
116890aac0d8SBjoern A. Zeeb {
116990aac0d8SBjoern A. Zeeb 	struct rtw_sdio_tx_data *tx_data = rtw_sdio_get_tx_data(skb);
117090aac0d8SBjoern A. Zeeb 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
117190aac0d8SBjoern A. Zeeb 	struct ieee80211_hw *hw = rtwdev->hw;
117290aac0d8SBjoern A. Zeeb 
117390aac0d8SBjoern A. Zeeb 	/* enqueue to wait for tx report */
117490aac0d8SBjoern A. Zeeb 	if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) {
117590aac0d8SBjoern A. Zeeb 		rtw_tx_report_enqueue(rtwdev, skb, tx_data->sn);
117690aac0d8SBjoern A. Zeeb 		return;
117790aac0d8SBjoern A. Zeeb 	}
117890aac0d8SBjoern A. Zeeb 
117990aac0d8SBjoern A. Zeeb 	/* always ACK for others, then they won't be marked as drop */
118090aac0d8SBjoern A. Zeeb 	ieee80211_tx_info_clear_status(info);
118190aac0d8SBjoern A. Zeeb 	if (info->flags & IEEE80211_TX_CTL_NO_ACK)
118290aac0d8SBjoern A. Zeeb 		info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
118390aac0d8SBjoern A. Zeeb 	else
118490aac0d8SBjoern A. Zeeb 		info->flags |= IEEE80211_TX_STAT_ACK;
118590aac0d8SBjoern A. Zeeb 
118690aac0d8SBjoern A. Zeeb 	ieee80211_tx_status_irqsafe(hw, skb);
118790aac0d8SBjoern A. Zeeb }
118890aac0d8SBjoern A. Zeeb 
rtw_sdio_process_tx_queue(struct rtw_dev * rtwdev,enum rtw_tx_queue_type queue)118990aac0d8SBjoern A. Zeeb static void rtw_sdio_process_tx_queue(struct rtw_dev *rtwdev,
119090aac0d8SBjoern A. Zeeb 				      enum rtw_tx_queue_type queue)
119190aac0d8SBjoern A. Zeeb {
119290aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
119390aac0d8SBjoern A. Zeeb 	struct sk_buff *skb;
119490aac0d8SBjoern A. Zeeb 	int ret;
119590aac0d8SBjoern A. Zeeb 
119690aac0d8SBjoern A. Zeeb 	skb = skb_dequeue(&rtwsdio->tx_queue[queue]);
119790aac0d8SBjoern A. Zeeb 	if (!skb)
119890aac0d8SBjoern A. Zeeb 		return;
119990aac0d8SBjoern A. Zeeb 
120090aac0d8SBjoern A. Zeeb 	ret = rtw_sdio_write_port(rtwdev, skb, queue);
120190aac0d8SBjoern A. Zeeb 	if (ret) {
120290aac0d8SBjoern A. Zeeb 		skb_queue_head(&rtwsdio->tx_queue[queue], skb);
120390aac0d8SBjoern A. Zeeb 		return;
120490aac0d8SBjoern A. Zeeb 	}
120590aac0d8SBjoern A. Zeeb 
120690aac0d8SBjoern A. Zeeb 	if (queue <= RTW_TX_QUEUE_VO)
120790aac0d8SBjoern A. Zeeb 		rtw_sdio_indicate_tx_status(rtwdev, skb);
120890aac0d8SBjoern A. Zeeb 	else
120990aac0d8SBjoern A. Zeeb 		dev_kfree_skb_any(skb);
121090aac0d8SBjoern A. Zeeb }
121190aac0d8SBjoern A. Zeeb 
rtw_sdio_tx_handler(struct work_struct * work)121290aac0d8SBjoern A. Zeeb static void rtw_sdio_tx_handler(struct work_struct *work)
121390aac0d8SBjoern A. Zeeb {
121490aac0d8SBjoern A. Zeeb 	struct rtw_sdio_work_data *work_data =
121590aac0d8SBjoern A. Zeeb 		container_of(work, struct rtw_sdio_work_data, work);
121690aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio;
121790aac0d8SBjoern A. Zeeb 	struct rtw_dev *rtwdev;
121890aac0d8SBjoern A. Zeeb 	int limit, queue;
121990aac0d8SBjoern A. Zeeb 
122090aac0d8SBjoern A. Zeeb 	rtwdev = work_data->rtwdev;
122190aac0d8SBjoern A. Zeeb 	rtwsdio = (struct rtw_sdio *)rtwdev->priv;
122290aac0d8SBjoern A. Zeeb 
122390aac0d8SBjoern A. Zeeb 	if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE))
122490aac0d8SBjoern A. Zeeb 		rtw_sdio_deep_ps_leave(rtwdev);
122590aac0d8SBjoern A. Zeeb 
122690aac0d8SBjoern A. Zeeb 	for (queue = RTK_MAX_TX_QUEUE_NUM - 1; queue >= 0; queue--) {
122790aac0d8SBjoern A. Zeeb 		for (limit = 0; limit < 1000; limit++) {
122890aac0d8SBjoern A. Zeeb 			rtw_sdio_process_tx_queue(rtwdev, queue);
122990aac0d8SBjoern A. Zeeb 
123090aac0d8SBjoern A. Zeeb 			if (skb_queue_empty(&rtwsdio->tx_queue[queue]))
123190aac0d8SBjoern A. Zeeb 				break;
123290aac0d8SBjoern A. Zeeb 		}
123390aac0d8SBjoern A. Zeeb 	}
123490aac0d8SBjoern A. Zeeb }
123590aac0d8SBjoern A. Zeeb 
rtw_sdio_free_irq(struct rtw_dev * rtwdev,struct sdio_func * sdio_func)123690aac0d8SBjoern A. Zeeb static void rtw_sdio_free_irq(struct rtw_dev *rtwdev,
123790aac0d8SBjoern A. Zeeb 			      struct sdio_func *sdio_func)
123890aac0d8SBjoern A. Zeeb {
123990aac0d8SBjoern A. Zeeb 	sdio_claim_host(sdio_func);
124090aac0d8SBjoern A. Zeeb 	sdio_release_irq(sdio_func);
124190aac0d8SBjoern A. Zeeb 	sdio_release_host(sdio_func);
124290aac0d8SBjoern A. Zeeb }
124390aac0d8SBjoern A. Zeeb 
rtw_sdio_init_tx(struct rtw_dev * rtwdev)124490aac0d8SBjoern A. Zeeb static int rtw_sdio_init_tx(struct rtw_dev *rtwdev)
124590aac0d8SBjoern A. Zeeb {
124690aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
124790aac0d8SBjoern A. Zeeb 	int i;
124890aac0d8SBjoern A. Zeeb 
124990aac0d8SBjoern A. Zeeb 	rtwsdio->txwq = create_singlethread_workqueue("rtw88_sdio: tx wq");
125090aac0d8SBjoern A. Zeeb 	if (!rtwsdio->txwq) {
125190aac0d8SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to create TX work queue\n");
125290aac0d8SBjoern A. Zeeb 		return -ENOMEM;
125390aac0d8SBjoern A. Zeeb 	}
125490aac0d8SBjoern A. Zeeb 
125590aac0d8SBjoern A. Zeeb 	for (i = 0; i < RTK_MAX_TX_QUEUE_NUM; i++)
125690aac0d8SBjoern A. Zeeb 		skb_queue_head_init(&rtwsdio->tx_queue[i]);
125790aac0d8SBjoern A. Zeeb 	rtwsdio->tx_handler_data = kmalloc(sizeof(*rtwsdio->tx_handler_data),
125890aac0d8SBjoern A. Zeeb 					   GFP_KERNEL);
125990aac0d8SBjoern A. Zeeb 	if (!rtwsdio->tx_handler_data)
126090aac0d8SBjoern A. Zeeb 		goto err_destroy_wq;
126190aac0d8SBjoern A. Zeeb 
126290aac0d8SBjoern A. Zeeb 	rtwsdio->tx_handler_data->rtwdev = rtwdev;
126390aac0d8SBjoern A. Zeeb 	INIT_WORK(&rtwsdio->tx_handler_data->work, rtw_sdio_tx_handler);
126490aac0d8SBjoern A. Zeeb 
126590aac0d8SBjoern A. Zeeb 	return 0;
126690aac0d8SBjoern A. Zeeb 
126790aac0d8SBjoern A. Zeeb err_destroy_wq:
126890aac0d8SBjoern A. Zeeb 	destroy_workqueue(rtwsdio->txwq);
126990aac0d8SBjoern A. Zeeb 	return -ENOMEM;
127090aac0d8SBjoern A. Zeeb }
127190aac0d8SBjoern A. Zeeb 
rtw_sdio_deinit_tx(struct rtw_dev * rtwdev)127290aac0d8SBjoern A. Zeeb static void rtw_sdio_deinit_tx(struct rtw_dev *rtwdev)
127390aac0d8SBjoern A. Zeeb {
127490aac0d8SBjoern A. Zeeb 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
127590aac0d8SBjoern A. Zeeb 	int i;
127690aac0d8SBjoern A. Zeeb 
127790aac0d8SBjoern A. Zeeb 	for (i = 0; i < RTK_MAX_TX_QUEUE_NUM; i++)
127890aac0d8SBjoern A. Zeeb 		skb_queue_purge(&rtwsdio->tx_queue[i]);
127990aac0d8SBjoern A. Zeeb 
128090aac0d8SBjoern A. Zeeb 	flush_workqueue(rtwsdio->txwq);
128190aac0d8SBjoern A. Zeeb 	destroy_workqueue(rtwsdio->txwq);
128290aac0d8SBjoern A. Zeeb 	kfree(rtwsdio->tx_handler_data);
128390aac0d8SBjoern A. Zeeb }
128490aac0d8SBjoern A. Zeeb 
rtw_sdio_probe(struct sdio_func * sdio_func,const struct sdio_device_id * id)128590aac0d8SBjoern A. Zeeb int rtw_sdio_probe(struct sdio_func *sdio_func,
128690aac0d8SBjoern A. Zeeb 		   const struct sdio_device_id *id)
128790aac0d8SBjoern A. Zeeb {
128890aac0d8SBjoern A. Zeeb 	struct ieee80211_hw *hw;
128990aac0d8SBjoern A. Zeeb 	struct rtw_dev *rtwdev;
129090aac0d8SBjoern A. Zeeb 	int drv_data_size;
129190aac0d8SBjoern A. Zeeb 	int ret;
129290aac0d8SBjoern A. Zeeb 
129390aac0d8SBjoern A. Zeeb 	drv_data_size = sizeof(struct rtw_dev) + sizeof(struct rtw_sdio);
129490aac0d8SBjoern A. Zeeb 	hw = ieee80211_alloc_hw(drv_data_size, &rtw_ops);
129590aac0d8SBjoern A. Zeeb 	if (!hw) {
129690aac0d8SBjoern A. Zeeb 		dev_err(&sdio_func->dev, "failed to allocate hw");
129790aac0d8SBjoern A. Zeeb 		return -ENOMEM;
129890aac0d8SBjoern A. Zeeb 	}
129990aac0d8SBjoern A. Zeeb 
130090aac0d8SBjoern A. Zeeb 	rtwdev = hw->priv;
130190aac0d8SBjoern A. Zeeb 	rtwdev->hw = hw;
130290aac0d8SBjoern A. Zeeb 	rtwdev->dev = &sdio_func->dev;
130390aac0d8SBjoern A. Zeeb 	rtwdev->chip = (struct rtw_chip_info *)id->driver_data;
130490aac0d8SBjoern A. Zeeb 	rtwdev->hci.ops = &rtw_sdio_ops;
130590aac0d8SBjoern A. Zeeb 	rtwdev->hci.type = RTW_HCI_TYPE_SDIO;
130690aac0d8SBjoern A. Zeeb 
130790aac0d8SBjoern A. Zeeb 	ret = rtw_core_init(rtwdev);
130890aac0d8SBjoern A. Zeeb 	if (ret)
130990aac0d8SBjoern A. Zeeb 		goto err_release_hw;
131090aac0d8SBjoern A. Zeeb 
131190aac0d8SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_SDIO,
131290aac0d8SBjoern A. Zeeb 		"rtw88 SDIO probe: vendor=0x%04x device=%04x class=%02x",
131390aac0d8SBjoern A. Zeeb 		id->vendor, id->device, id->class);
131490aac0d8SBjoern A. Zeeb 
131590aac0d8SBjoern A. Zeeb 	ret = rtw_sdio_claim(rtwdev, sdio_func);
131690aac0d8SBjoern A. Zeeb 	if (ret) {
131790aac0d8SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to claim SDIO device");
131890aac0d8SBjoern A. Zeeb 		goto err_deinit_core;
131990aac0d8SBjoern A. Zeeb 	}
132090aac0d8SBjoern A. Zeeb 
132190aac0d8SBjoern A. Zeeb 	rtw_sdio_init(rtwdev);
132290aac0d8SBjoern A. Zeeb 
132390aac0d8SBjoern A. Zeeb 	ret = rtw_sdio_init_tx(rtwdev);
132490aac0d8SBjoern A. Zeeb 	if (ret) {
132590aac0d8SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to init SDIO TX queue\n");
132690aac0d8SBjoern A. Zeeb 		goto err_sdio_declaim;
132790aac0d8SBjoern A. Zeeb 	}
132890aac0d8SBjoern A. Zeeb 
132990aac0d8SBjoern A. Zeeb 	ret = rtw_chip_info_setup(rtwdev);
133090aac0d8SBjoern A. Zeeb 	if (ret) {
133190aac0d8SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to setup chip information");
133290aac0d8SBjoern A. Zeeb 		goto err_destroy_txwq;
133390aac0d8SBjoern A. Zeeb 	}
133490aac0d8SBjoern A. Zeeb 
133590aac0d8SBjoern A. Zeeb 	ret = rtw_sdio_request_irq(rtwdev, sdio_func);
133690aac0d8SBjoern A. Zeeb 	if (ret)
133790aac0d8SBjoern A. Zeeb 		goto err_destroy_txwq;
133890aac0d8SBjoern A. Zeeb 
133990aac0d8SBjoern A. Zeeb 	ret = rtw_register_hw(rtwdev, hw);
134090aac0d8SBjoern A. Zeeb 	if (ret) {
134190aac0d8SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to register hw");
134290aac0d8SBjoern A. Zeeb 		goto err_free_irq;
134390aac0d8SBjoern A. Zeeb 	}
134490aac0d8SBjoern A. Zeeb 
134590aac0d8SBjoern A. Zeeb 	return 0;
134690aac0d8SBjoern A. Zeeb 
134790aac0d8SBjoern A. Zeeb err_free_irq:
134890aac0d8SBjoern A. Zeeb 	rtw_sdio_free_irq(rtwdev, sdio_func);
134990aac0d8SBjoern A. Zeeb err_destroy_txwq:
135090aac0d8SBjoern A. Zeeb 	rtw_sdio_deinit_tx(rtwdev);
135190aac0d8SBjoern A. Zeeb err_sdio_declaim:
135290aac0d8SBjoern A. Zeeb 	rtw_sdio_declaim(rtwdev, sdio_func);
135390aac0d8SBjoern A. Zeeb err_deinit_core:
135490aac0d8SBjoern A. Zeeb 	rtw_core_deinit(rtwdev);
135590aac0d8SBjoern A. Zeeb err_release_hw:
135690aac0d8SBjoern A. Zeeb 	ieee80211_free_hw(hw);
135790aac0d8SBjoern A. Zeeb 
135890aac0d8SBjoern A. Zeeb 	return ret;
135990aac0d8SBjoern A. Zeeb }
136090aac0d8SBjoern A. Zeeb EXPORT_SYMBOL(rtw_sdio_probe);
136190aac0d8SBjoern A. Zeeb 
rtw_sdio_remove(struct sdio_func * sdio_func)136290aac0d8SBjoern A. Zeeb void rtw_sdio_remove(struct sdio_func *sdio_func)
136390aac0d8SBjoern A. Zeeb {
136490aac0d8SBjoern A. Zeeb 	struct ieee80211_hw *hw = sdio_get_drvdata(sdio_func);
136590aac0d8SBjoern A. Zeeb 	struct rtw_dev *rtwdev;
136690aac0d8SBjoern A. Zeeb 
136790aac0d8SBjoern A. Zeeb 	if (!hw)
136890aac0d8SBjoern A. Zeeb 		return;
136990aac0d8SBjoern A. Zeeb 
137090aac0d8SBjoern A. Zeeb 	rtwdev = hw->priv;
137190aac0d8SBjoern A. Zeeb 
137290aac0d8SBjoern A. Zeeb 	rtw_unregister_hw(rtwdev, hw);
137390aac0d8SBjoern A. Zeeb 	rtw_sdio_disable_interrupt(rtwdev);
137490aac0d8SBjoern A. Zeeb 	rtw_sdio_free_irq(rtwdev, sdio_func);
137590aac0d8SBjoern A. Zeeb 	rtw_sdio_declaim(rtwdev, sdio_func);
137690aac0d8SBjoern A. Zeeb 	rtw_sdio_deinit_tx(rtwdev);
137790aac0d8SBjoern A. Zeeb 	rtw_core_deinit(rtwdev);
137890aac0d8SBjoern A. Zeeb 	ieee80211_free_hw(hw);
137990aac0d8SBjoern A. Zeeb }
138090aac0d8SBjoern A. Zeeb EXPORT_SYMBOL(rtw_sdio_remove);
138190aac0d8SBjoern A. Zeeb 
rtw_sdio_shutdown(struct device * dev)138290aac0d8SBjoern A. Zeeb void rtw_sdio_shutdown(struct device *dev)
138390aac0d8SBjoern A. Zeeb {
138490aac0d8SBjoern A. Zeeb 	struct sdio_func *sdio_func = dev_to_sdio_func(dev);
138590aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip;
138690aac0d8SBjoern A. Zeeb 	struct ieee80211_hw *hw;
138790aac0d8SBjoern A. Zeeb 	struct rtw_dev *rtwdev;
138890aac0d8SBjoern A. Zeeb 
138990aac0d8SBjoern A. Zeeb 	hw = sdio_get_drvdata(sdio_func);
139090aac0d8SBjoern A. Zeeb 	if (!hw)
139190aac0d8SBjoern A. Zeeb 		return;
139290aac0d8SBjoern A. Zeeb 
139390aac0d8SBjoern A. Zeeb 	rtwdev = hw->priv;
139490aac0d8SBjoern A. Zeeb 	chip = rtwdev->chip;
139590aac0d8SBjoern A. Zeeb 
139690aac0d8SBjoern A. Zeeb 	if (chip->ops->shutdown)
139790aac0d8SBjoern A. Zeeb 		chip->ops->shutdown(rtwdev);
139890aac0d8SBjoern A. Zeeb }
139990aac0d8SBjoern A. Zeeb EXPORT_SYMBOL(rtw_sdio_shutdown);
140090aac0d8SBjoern A. Zeeb 
140190aac0d8SBjoern A. Zeeb MODULE_AUTHOR("Martin Blumenstingl");
140290aac0d8SBjoern A. Zeeb MODULE_AUTHOR("Jernej Skrabec");
140390aac0d8SBjoern A. Zeeb MODULE_DESCRIPTION("Realtek 802.11ac wireless SDIO driver");
140490aac0d8SBjoern A. Zeeb MODULE_LICENSE("Dual BSD/GPL");
1405