18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC
28c2ecf20Sopenharmony_ci/* Copyright (C) 2020 MediaTek Inc.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Author: Felix Fietkau <nbd@nbd.name>
58c2ecf20Sopenharmony_ci *	   Lorenzo Bianconi <lorenzo@kernel.org>
68c2ecf20Sopenharmony_ci *	   Sean Wang <sean.wang@mediatek.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/mmc/host.h>
148c2ecf20Sopenharmony_ci#include <linux/mmc/sdio_ids.h>
158c2ecf20Sopenharmony_ci#include <linux/mmc/sdio_func.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "mt7615.h"
188c2ecf20Sopenharmony_ci#include "sdio.h"
198c2ecf20Sopenharmony_ci#include "mac.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic const struct sdio_device_id mt7663s_table[] = {
228c2ecf20Sopenharmony_ci	{ SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7603) },
238c2ecf20Sopenharmony_ci	{ }	/* Terminating entry */
248c2ecf20Sopenharmony_ci};
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic u32 mt7663s_read_whisr(struct mt76_dev *dev)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	return sdio_readl(dev->sdio.func, MCR_WHISR, NULL);
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ciu32 mt7663s_read_pcr(struct mt7615_dev *dev)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	struct mt76_sdio *sdio = &dev->mt76.sdio;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	return sdio_readl(sdio->func, MCR_WHLPCR, NULL);
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic u32 mt7663s_read_mailbox(struct mt76_dev *dev, u32 offset)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct sdio_func *func = dev->sdio.func;
418c2ecf20Sopenharmony_ci	u32 val = ~0, status;
428c2ecf20Sopenharmony_ci	int err;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	sdio_claim_host(func);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	sdio_writel(func, offset, MCR_H2DSM0R, &err);
478c2ecf20Sopenharmony_ci	if (err < 0) {
488c2ecf20Sopenharmony_ci		dev_err(dev->dev, "failed setting address [err=%d]\n", err);
498c2ecf20Sopenharmony_ci		goto out;
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	sdio_writel(func, H2D_SW_INT_READ, MCR_WSICR, &err);
538c2ecf20Sopenharmony_ci	if (err < 0) {
548c2ecf20Sopenharmony_ci		dev_err(dev->dev, "failed setting read mode [err=%d]\n", err);
558c2ecf20Sopenharmony_ci		goto out;
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	err = readx_poll_timeout(mt7663s_read_whisr, dev, status,
598c2ecf20Sopenharmony_ci				 status & H2D_SW_INT_READ, 0, 1000000);
608c2ecf20Sopenharmony_ci	if (err < 0) {
618c2ecf20Sopenharmony_ci		dev_err(dev->dev, "query whisr timeout\n");
628c2ecf20Sopenharmony_ci		goto out;
638c2ecf20Sopenharmony_ci	}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	sdio_writel(func, H2D_SW_INT_READ, MCR_WHISR, &err);
668c2ecf20Sopenharmony_ci	if (err < 0) {
678c2ecf20Sopenharmony_ci		dev_err(dev->dev, "failed setting read mode [err=%d]\n", err);
688c2ecf20Sopenharmony_ci		goto out;
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	val = sdio_readl(func, MCR_H2DSM0R, &err);
728c2ecf20Sopenharmony_ci	if (err < 0) {
738c2ecf20Sopenharmony_ci		dev_err(dev->dev, "failed reading h2dsm0r [err=%d]\n", err);
748c2ecf20Sopenharmony_ci		goto out;
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (val != offset) {
788c2ecf20Sopenharmony_ci		dev_err(dev->dev, "register mismatch\n");
798c2ecf20Sopenharmony_ci		val = ~0;
808c2ecf20Sopenharmony_ci		goto out;
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	val = sdio_readl(func, MCR_D2HRM1R, &err);
848c2ecf20Sopenharmony_ci	if (err < 0)
858c2ecf20Sopenharmony_ci		dev_err(dev->dev, "failed reading d2hrm1r [err=%d]\n", err);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ciout:
888c2ecf20Sopenharmony_ci	sdio_release_host(func);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	return val;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic void mt7663s_write_mailbox(struct mt76_dev *dev, u32 offset, u32 val)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	struct sdio_func *func = dev->sdio.func;
968c2ecf20Sopenharmony_ci	u32 status;
978c2ecf20Sopenharmony_ci	int err;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	sdio_claim_host(func);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	sdio_writel(func, offset, MCR_H2DSM0R, &err);
1028c2ecf20Sopenharmony_ci	if (err < 0) {
1038c2ecf20Sopenharmony_ci		dev_err(dev->dev, "failed setting address [err=%d]\n", err);
1048c2ecf20Sopenharmony_ci		goto out;
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	sdio_writel(func, val, MCR_H2DSM1R, &err);
1088c2ecf20Sopenharmony_ci	if (err < 0) {
1098c2ecf20Sopenharmony_ci		dev_err(dev->dev,
1108c2ecf20Sopenharmony_ci			"failed setting write value [err=%d]\n", err);
1118c2ecf20Sopenharmony_ci		goto out;
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	sdio_writel(func, H2D_SW_INT_WRITE, MCR_WSICR, &err);
1158c2ecf20Sopenharmony_ci	if (err < 0) {
1168c2ecf20Sopenharmony_ci		dev_err(dev->dev, "failed setting write mode [err=%d]\n", err);
1178c2ecf20Sopenharmony_ci		goto out;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	err = readx_poll_timeout(mt7663s_read_whisr, dev, status,
1218c2ecf20Sopenharmony_ci				 status & H2D_SW_INT_WRITE, 0, 1000000);
1228c2ecf20Sopenharmony_ci	if (err < 0) {
1238c2ecf20Sopenharmony_ci		dev_err(dev->dev, "query whisr timeout\n");
1248c2ecf20Sopenharmony_ci		goto out;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	sdio_writel(func, H2D_SW_INT_WRITE, MCR_WHISR, &err);
1288c2ecf20Sopenharmony_ci	if (err < 0) {
1298c2ecf20Sopenharmony_ci		dev_err(dev->dev, "failed setting write mode [err=%d]\n", err);
1308c2ecf20Sopenharmony_ci		goto out;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	val = sdio_readl(func, MCR_H2DSM0R, &err);
1348c2ecf20Sopenharmony_ci	if (err < 0) {
1358c2ecf20Sopenharmony_ci		dev_err(dev->dev, "failed reading h2dsm0r [err=%d]\n", err);
1368c2ecf20Sopenharmony_ci		goto out;
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if (val != offset)
1408c2ecf20Sopenharmony_ci		dev_err(dev->dev, "register mismatch\n");
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ciout:
1438c2ecf20Sopenharmony_ci	sdio_release_host(func);
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic u32 mt7663s_rr(struct mt76_dev *dev, u32 offset)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	if (test_bit(MT76_STATE_MCU_RUNNING, &dev->phy.state))
1498c2ecf20Sopenharmony_ci		return dev->mcu_ops->mcu_rr(dev, offset);
1508c2ecf20Sopenharmony_ci	else
1518c2ecf20Sopenharmony_ci		return mt7663s_read_mailbox(dev, offset);
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic void mt7663s_wr(struct mt76_dev *dev, u32 offset, u32 val)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	if (test_bit(MT76_STATE_MCU_RUNNING, &dev->phy.state))
1578c2ecf20Sopenharmony_ci		dev->mcu_ops->mcu_wr(dev, offset, val);
1588c2ecf20Sopenharmony_ci	else
1598c2ecf20Sopenharmony_ci		mt7663s_write_mailbox(dev, offset, val);
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic u32 mt7663s_rmw(struct mt76_dev *dev, u32 offset, u32 mask, u32 val)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	val |= mt7663s_rr(dev, offset) & ~mask;
1658c2ecf20Sopenharmony_ci	mt7663s_wr(dev, offset, val);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	return val;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic void mt7663s_write_copy(struct mt76_dev *dev, u32 offset,
1718c2ecf20Sopenharmony_ci			       const void *data, int len)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	const u32 *val = data;
1748c2ecf20Sopenharmony_ci	int i;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	for (i = 0; i < len / sizeof(u32); i++) {
1778c2ecf20Sopenharmony_ci		mt7663s_wr(dev, offset, val[i]);
1788c2ecf20Sopenharmony_ci		offset += sizeof(u32);
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic void mt7663s_read_copy(struct mt76_dev *dev, u32 offset,
1838c2ecf20Sopenharmony_ci			      void *data, int len)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	u32 *val = data;
1868c2ecf20Sopenharmony_ci	int i;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	for (i = 0; i < len / sizeof(u32); i++) {
1898c2ecf20Sopenharmony_ci		val[i] = mt7663s_rr(dev, offset);
1908c2ecf20Sopenharmony_ci		offset += sizeof(u32);
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic int mt7663s_wr_rp(struct mt76_dev *dev, u32 base,
1958c2ecf20Sopenharmony_ci			 const struct mt76_reg_pair *data,
1968c2ecf20Sopenharmony_ci			 int len)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	int i;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++) {
2018c2ecf20Sopenharmony_ci		mt7663s_wr(dev, data->reg, data->value);
2028c2ecf20Sopenharmony_ci		data++;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return 0;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic int mt7663s_rd_rp(struct mt76_dev *dev, u32 base,
2098c2ecf20Sopenharmony_ci			 struct mt76_reg_pair *data,
2108c2ecf20Sopenharmony_ci			 int len)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	int i;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++) {
2158c2ecf20Sopenharmony_ci		data->value = mt7663s_rr(dev, data->reg);
2168c2ecf20Sopenharmony_ci		data++;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	return 0;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic void mt7663s_init_work(struct work_struct *work)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	struct mt7615_dev *dev;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	dev = container_of(work, struct mt7615_dev, mcu_work);
2278c2ecf20Sopenharmony_ci	if (mt7663s_mcu_init(dev))
2288c2ecf20Sopenharmony_ci		return;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	mt7615_mcu_set_eeprom(dev);
2318c2ecf20Sopenharmony_ci	mt7615_mac_init(dev);
2328c2ecf20Sopenharmony_ci	mt7615_phy_init(dev);
2338c2ecf20Sopenharmony_ci	mt7615_mcu_del_wtbl_all(dev);
2348c2ecf20Sopenharmony_ci	mt7615_check_offload_capability(dev);
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic int mt7663s_hw_init(struct mt7615_dev *dev, struct sdio_func *func)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	u32 status, ctrl;
2408c2ecf20Sopenharmony_ci	int ret;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	sdio_claim_host(func);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	ret = sdio_enable_func(func);
2458c2ecf20Sopenharmony_ci	if (ret < 0)
2468c2ecf20Sopenharmony_ci		goto release;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	/* Get ownership from the device */
2498c2ecf20Sopenharmony_ci	sdio_writel(func, WHLPCR_INT_EN_CLR | WHLPCR_FW_OWN_REQ_CLR,
2508c2ecf20Sopenharmony_ci		    MCR_WHLPCR, &ret);
2518c2ecf20Sopenharmony_ci	if (ret < 0)
2528c2ecf20Sopenharmony_ci		goto disable_func;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	ret = readx_poll_timeout(mt7663s_read_pcr, dev, status,
2558c2ecf20Sopenharmony_ci				 status & WHLPCR_IS_DRIVER_OWN, 2000, 1000000);
2568c2ecf20Sopenharmony_ci	if (ret < 0) {
2578c2ecf20Sopenharmony_ci		dev_err(dev->mt76.dev, "Cannot get ownership from device");
2588c2ecf20Sopenharmony_ci		goto disable_func;
2598c2ecf20Sopenharmony_ci	}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	ret = sdio_set_block_size(func, 512);
2628c2ecf20Sopenharmony_ci	if (ret < 0)
2638c2ecf20Sopenharmony_ci		goto disable_func;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	/* Enable interrupt */
2668c2ecf20Sopenharmony_ci	sdio_writel(func, WHLPCR_INT_EN_SET, MCR_WHLPCR, &ret);
2678c2ecf20Sopenharmony_ci	if (ret < 0)
2688c2ecf20Sopenharmony_ci		goto disable_func;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	ctrl = WHIER_RX0_DONE_INT_EN | WHIER_TX_DONE_INT_EN;
2718c2ecf20Sopenharmony_ci	sdio_writel(func, ctrl, MCR_WHIER, &ret);
2728c2ecf20Sopenharmony_ci	if (ret < 0)
2738c2ecf20Sopenharmony_ci		goto disable_func;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	/* set WHISR as read clear and Rx aggregation number as 16 */
2768c2ecf20Sopenharmony_ci	ctrl = FIELD_PREP(MAX_HIF_RX_LEN_NUM, 16);
2778c2ecf20Sopenharmony_ci	sdio_writel(func, ctrl, MCR_WHCR, &ret);
2788c2ecf20Sopenharmony_ci	if (ret < 0)
2798c2ecf20Sopenharmony_ci		goto disable_func;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	ret = sdio_claim_irq(func, mt7663s_sdio_irq);
2828c2ecf20Sopenharmony_ci	if (ret < 0)
2838c2ecf20Sopenharmony_ci		goto disable_func;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	sdio_release_host(func);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	return 0;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cidisable_func:
2908c2ecf20Sopenharmony_ci	sdio_disable_func(func);
2918c2ecf20Sopenharmony_cirelease:
2928c2ecf20Sopenharmony_ci	sdio_release_host(func);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	return ret;
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic int mt7663s_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
2988c2ecf20Sopenharmony_ci			   struct ieee80211_sta *sta)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76);
3018c2ecf20Sopenharmony_ci	struct mt76_sdio *sdio = &mdev->sdio;
3028c2ecf20Sopenharmony_ci	u32 pse, ple;
3038c2ecf20Sopenharmony_ci	int err;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	err = mt7615_mac_sta_add(mdev, vif, sta);
3068c2ecf20Sopenharmony_ci	if (err < 0)
3078c2ecf20Sopenharmony_ci		return err;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/* init sched data quota */
3108c2ecf20Sopenharmony_ci	pse = mt76_get_field(dev, MT_PSE_PG_HIF0_GROUP, MT_HIF0_MIN_QUOTA);
3118c2ecf20Sopenharmony_ci	ple = mt76_get_field(dev, MT_PLE_PG_HIF0_GROUP, MT_HIF0_MIN_QUOTA);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	mutex_lock(&sdio->sched.lock);
3148c2ecf20Sopenharmony_ci	sdio->sched.pse_data_quota = pse;
3158c2ecf20Sopenharmony_ci	sdio->sched.ple_data_quota = ple;
3168c2ecf20Sopenharmony_ci	mutex_unlock(&sdio->sched.lock);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return 0;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic int mt7663s_probe(struct sdio_func *func,
3228c2ecf20Sopenharmony_ci			 const struct sdio_device_id *id)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	static const struct mt76_driver_ops drv_ops = {
3258c2ecf20Sopenharmony_ci		.txwi_size = MT_USB_TXD_SIZE,
3268c2ecf20Sopenharmony_ci		.drv_flags = MT_DRV_RX_DMA_HDR,
3278c2ecf20Sopenharmony_ci		.tx_prepare_skb = mt7663_usb_sdio_tx_prepare_skb,
3288c2ecf20Sopenharmony_ci		.tx_complete_skb = mt7663_usb_sdio_tx_complete_skb,
3298c2ecf20Sopenharmony_ci		.tx_status_data = mt7663_usb_sdio_tx_status_data,
3308c2ecf20Sopenharmony_ci		.rx_skb = mt7615_queue_rx_skb,
3318c2ecf20Sopenharmony_ci		.sta_ps = mt7615_sta_ps,
3328c2ecf20Sopenharmony_ci		.sta_add = mt7663s_sta_add,
3338c2ecf20Sopenharmony_ci		.sta_remove = mt7615_mac_sta_remove,
3348c2ecf20Sopenharmony_ci		.update_survey = mt7615_update_channel,
3358c2ecf20Sopenharmony_ci	};
3368c2ecf20Sopenharmony_ci	static const struct mt76_bus_ops mt7663s_ops = {
3378c2ecf20Sopenharmony_ci		.rr = mt7663s_rr,
3388c2ecf20Sopenharmony_ci		.rmw = mt7663s_rmw,
3398c2ecf20Sopenharmony_ci		.wr = mt7663s_wr,
3408c2ecf20Sopenharmony_ci		.write_copy = mt7663s_write_copy,
3418c2ecf20Sopenharmony_ci		.read_copy = mt7663s_read_copy,
3428c2ecf20Sopenharmony_ci		.wr_rp = mt7663s_wr_rp,
3438c2ecf20Sopenharmony_ci		.rd_rp = mt7663s_rd_rp,
3448c2ecf20Sopenharmony_ci		.type = MT76_BUS_SDIO,
3458c2ecf20Sopenharmony_ci	};
3468c2ecf20Sopenharmony_ci	struct ieee80211_ops *ops;
3478c2ecf20Sopenharmony_ci	struct mt7615_dev *dev;
3488c2ecf20Sopenharmony_ci	struct mt76_dev *mdev;
3498c2ecf20Sopenharmony_ci	int i, ret;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	ops = devm_kmemdup(&func->dev, &mt7615_ops, sizeof(mt7615_ops),
3528c2ecf20Sopenharmony_ci			   GFP_KERNEL);
3538c2ecf20Sopenharmony_ci	if (!ops)
3548c2ecf20Sopenharmony_ci		return -ENOMEM;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	mdev = mt76_alloc_device(&func->dev, sizeof(*dev), ops, &drv_ops);
3578c2ecf20Sopenharmony_ci	if (!mdev)
3588c2ecf20Sopenharmony_ci		return -ENOMEM;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	dev = container_of(mdev, struct mt7615_dev, mt76);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	INIT_WORK(&dev->mcu_work, mt7663s_init_work);
3638c2ecf20Sopenharmony_ci	dev->reg_map = mt7663_usb_sdio_reg_map;
3648c2ecf20Sopenharmony_ci	dev->ops = ops;
3658c2ecf20Sopenharmony_ci	sdio_set_drvdata(func, dev);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	ret = mt76s_init(mdev, func, &mt7663s_ops);
3688c2ecf20Sopenharmony_ci	if (ret < 0)
3698c2ecf20Sopenharmony_ci		goto err_free;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	INIT_WORK(&mdev->sdio.tx.xmit_work, mt7663s_tx_work);
3728c2ecf20Sopenharmony_ci	INIT_WORK(&mdev->sdio.rx.recv_work, mt7663s_rx_work);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	ret = mt7663s_hw_init(dev, func);
3758c2ecf20Sopenharmony_ci	if (ret)
3768c2ecf20Sopenharmony_ci		goto err_deinit;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) |
3798c2ecf20Sopenharmony_ci		    (mt76_rr(dev, MT_HW_REV) & 0xff);
3808c2ecf20Sopenharmony_ci	dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	mdev->sdio.intr_data = devm_kmalloc(mdev->dev,
3838c2ecf20Sopenharmony_ci					    sizeof(struct mt76s_intr),
3848c2ecf20Sopenharmony_ci					    GFP_KERNEL);
3858c2ecf20Sopenharmony_ci	if (!mdev->sdio.intr_data) {
3868c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3878c2ecf20Sopenharmony_ci		goto err_deinit;
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mdev->sdio.xmit_buf); i++) {
3918c2ecf20Sopenharmony_ci		mdev->sdio.xmit_buf[i] = devm_kmalloc(mdev->dev,
3928c2ecf20Sopenharmony_ci						      MT76S_XMIT_BUF_SZ,
3938c2ecf20Sopenharmony_ci						      GFP_KERNEL);
3948c2ecf20Sopenharmony_ci		if (!mdev->sdio.xmit_buf[i]) {
3958c2ecf20Sopenharmony_ci			ret = -ENOMEM;
3968c2ecf20Sopenharmony_ci			goto err_deinit;
3978c2ecf20Sopenharmony_ci		}
3988c2ecf20Sopenharmony_ci	}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	ret = mt76s_alloc_queues(&dev->mt76);
4018c2ecf20Sopenharmony_ci	if (ret)
4028c2ecf20Sopenharmony_ci		goto err_deinit;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	ret = mt7663_usb_sdio_register_device(dev);
4058c2ecf20Sopenharmony_ci	if (ret)
4068c2ecf20Sopenharmony_ci		goto err_deinit;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	return 0;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cierr_deinit:
4118c2ecf20Sopenharmony_ci	mt76s_deinit(&dev->mt76);
4128c2ecf20Sopenharmony_cierr_free:
4138c2ecf20Sopenharmony_ci	mt76_free_device(&dev->mt76);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	return ret;
4168c2ecf20Sopenharmony_ci}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_cistatic void mt7663s_remove(struct sdio_func *func)
4198c2ecf20Sopenharmony_ci{
4208c2ecf20Sopenharmony_ci	struct mt7615_dev *dev = sdio_get_drvdata(func);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	if (!test_and_clear_bit(MT76_STATE_INITIALIZED, &dev->mphy.state))
4238c2ecf20Sopenharmony_ci		return;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	ieee80211_unregister_hw(dev->mt76.hw);
4268c2ecf20Sopenharmony_ci	mt76s_deinit(&dev->mt76);
4278c2ecf20Sopenharmony_ci	mt76_free_device(&dev->mt76);
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
4318c2ecf20Sopenharmony_cistatic int mt7663s_suspend(struct device *dev)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	struct sdio_func *func = dev_to_sdio_func(dev);
4348c2ecf20Sopenharmony_ci	struct mt7615_dev *mdev = sdio_get_drvdata(func);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	if (!test_bit(MT76_STATE_SUSPEND, &mdev->mphy.state) &&
4378c2ecf20Sopenharmony_ci	    mt7615_firmware_offload(mdev)) {
4388c2ecf20Sopenharmony_ci		int err;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci		err = mt7615_mcu_set_hif_suspend(mdev, true);
4418c2ecf20Sopenharmony_ci		if (err < 0)
4428c2ecf20Sopenharmony_ci			return err;
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	mt76s_stop_txrx(&mdev->mt76);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	return mt7615_mcu_set_fw_ctrl(mdev);
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cistatic int mt7663s_resume(struct device *dev)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	struct sdio_func *func = dev_to_sdio_func(dev);
4558c2ecf20Sopenharmony_ci	struct mt7615_dev *mdev = sdio_get_drvdata(func);
4568c2ecf20Sopenharmony_ci	int err;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	err = mt7615_mcu_set_drv_ctrl(mdev);
4598c2ecf20Sopenharmony_ci	if (err)
4608c2ecf20Sopenharmony_ci		return err;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	if (!test_bit(MT76_STATE_SUSPEND, &mdev->mphy.state) &&
4638c2ecf20Sopenharmony_ci	    mt7615_firmware_offload(mdev))
4648c2ecf20Sopenharmony_ci		err = mt7615_mcu_set_hif_suspend(mdev, false);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	return err;
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_cistatic const struct dev_pm_ops mt7663s_pm_ops = {
4708c2ecf20Sopenharmony_ci	.suspend = mt7663s_suspend,
4718c2ecf20Sopenharmony_ci	.resume = mt7663s_resume,
4728c2ecf20Sopenharmony_ci};
4738c2ecf20Sopenharmony_ci#endif
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(sdio, mt7663s_table);
4768c2ecf20Sopenharmony_ciMODULE_FIRMWARE(MT7663_OFFLOAD_FIRMWARE_N9);
4778c2ecf20Sopenharmony_ciMODULE_FIRMWARE(MT7663_OFFLOAD_ROM_PATCH);
4788c2ecf20Sopenharmony_ciMODULE_FIRMWARE(MT7663_FIRMWARE_N9);
4798c2ecf20Sopenharmony_ciMODULE_FIRMWARE(MT7663_ROM_PATCH);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_cistatic struct sdio_driver mt7663s_driver = {
4828c2ecf20Sopenharmony_ci	.name		= KBUILD_MODNAME,
4838c2ecf20Sopenharmony_ci	.probe		= mt7663s_probe,
4848c2ecf20Sopenharmony_ci	.remove		= mt7663s_remove,
4858c2ecf20Sopenharmony_ci	.id_table	= mt7663s_table,
4868c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
4878c2ecf20Sopenharmony_ci	.drv = {
4888c2ecf20Sopenharmony_ci		.pm = &mt7663s_pm_ops,
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci#endif
4918c2ecf20Sopenharmony_ci};
4928c2ecf20Sopenharmony_cimodule_sdio_driver(mt7663s_driver);
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
4958c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>");
4968c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
497