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