162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC 262306a36Sopenharmony_ci/* Copyright (C) 2021 MediaTek Inc. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/kernel.h> 762306a36Sopenharmony_ci#include <linux/iopoll.h> 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/mmc/host.h> 1162306a36Sopenharmony_ci#include <linux/mmc/sdio_ids.h> 1262306a36Sopenharmony_ci#include <linux/mmc/sdio_func.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "mt7921.h" 1562306a36Sopenharmony_ci#include "../sdio.h" 1662306a36Sopenharmony_ci#include "../mt76_connac2_mac.h" 1762306a36Sopenharmony_ci#include "mcu.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic const struct sdio_device_id mt7921s_table[] = { 2062306a36Sopenharmony_ci { SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7901), 2162306a36Sopenharmony_ci .driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM }, 2262306a36Sopenharmony_ci { } /* Terminating entry */ 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic void mt7921s_txrx_worker(struct mt76_worker *w) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci struct mt76_sdio *sdio = container_of(w, struct mt76_sdio, 2862306a36Sopenharmony_ci txrx_worker); 2962306a36Sopenharmony_ci struct mt76_dev *mdev = container_of(sdio, struct mt76_dev, sdio); 3062306a36Sopenharmony_ci struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci if (!mt76_connac_pm_ref(&dev->mphy, &dev->pm)) { 3362306a36Sopenharmony_ci queue_work(mdev->wq, &dev->pm.wake_work); 3462306a36Sopenharmony_ci return; 3562306a36Sopenharmony_ci } 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci mt76s_txrx_worker(sdio); 3862306a36Sopenharmony_ci mt76_connac_pm_unref(&dev->mphy, &dev->pm); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void mt7921s_unregister_device(struct mt792x_dev *dev) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct mt76_connac_pm *pm = &dev->pm; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci cancel_work_sync(&dev->init_work); 4662306a36Sopenharmony_ci mt76_unregister_device(&dev->mt76); 4762306a36Sopenharmony_ci cancel_delayed_work_sync(&pm->ps_work); 4862306a36Sopenharmony_ci cancel_work_sync(&pm->wake_work); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci mt76s_deinit(&dev->mt76); 5162306a36Sopenharmony_ci mt7921s_wfsys_reset(dev); 5262306a36Sopenharmony_ci skb_queue_purge(&dev->mt76.mcu.res_q); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci mt76_free_device(&dev->mt76); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int mt7921s_parse_intr(struct mt76_dev *dev, struct mt76s_intr *intr) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct mt76_sdio *sdio = &dev->sdio; 6062306a36Sopenharmony_ci struct mt7921_sdio_intr *irq_data = sdio->intr_data; 6162306a36Sopenharmony_ci int i, err; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci sdio_claim_host(sdio->func); 6462306a36Sopenharmony_ci err = sdio_readsb(sdio->func, irq_data, MCR_WHISR, sizeof(*irq_data)); 6562306a36Sopenharmony_ci sdio_release_host(sdio->func); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (err < 0) 6862306a36Sopenharmony_ci return err; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (irq_data->rx.num[0] > 16 || 7162306a36Sopenharmony_ci irq_data->rx.num[1] > 128) 7262306a36Sopenharmony_ci return -EINVAL; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci intr->isr = irq_data->isr; 7562306a36Sopenharmony_ci intr->rec_mb = irq_data->rec_mb; 7662306a36Sopenharmony_ci intr->tx.wtqcr = irq_data->tx.wtqcr; 7762306a36Sopenharmony_ci intr->rx.num = irq_data->rx.num; 7862306a36Sopenharmony_ci for (i = 0; i < 2 ; i++) { 7962306a36Sopenharmony_ci if (!i) 8062306a36Sopenharmony_ci intr->rx.len[0] = irq_data->rx.len0; 8162306a36Sopenharmony_ci else 8262306a36Sopenharmony_ci intr->rx.len[1] = irq_data->rx.len1; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int mt7921s_probe(struct sdio_func *func, 8962306a36Sopenharmony_ci const struct sdio_device_id *id) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci static const struct mt76_driver_ops drv_ops = { 9262306a36Sopenharmony_ci .txwi_size = MT_SDIO_TXD_SIZE, 9362306a36Sopenharmony_ci .drv_flags = MT_DRV_AMSDU_OFFLOAD, 9462306a36Sopenharmony_ci .survey_flags = SURVEY_INFO_TIME_TX | 9562306a36Sopenharmony_ci SURVEY_INFO_TIME_RX | 9662306a36Sopenharmony_ci SURVEY_INFO_TIME_BSS_RX, 9762306a36Sopenharmony_ci .tx_prepare_skb = mt7921_usb_sdio_tx_prepare_skb, 9862306a36Sopenharmony_ci .tx_complete_skb = mt7921_usb_sdio_tx_complete_skb, 9962306a36Sopenharmony_ci .tx_status_data = mt7921_usb_sdio_tx_status_data, 10062306a36Sopenharmony_ci .rx_skb = mt7921_queue_rx_skb, 10162306a36Sopenharmony_ci .rx_check = mt7921_rx_check, 10262306a36Sopenharmony_ci .sta_add = mt7921_mac_sta_add, 10362306a36Sopenharmony_ci .sta_assoc = mt7921_mac_sta_assoc, 10462306a36Sopenharmony_ci .sta_remove = mt7921_mac_sta_remove, 10562306a36Sopenharmony_ci .update_survey = mt792x_update_channel, 10662306a36Sopenharmony_ci }; 10762306a36Sopenharmony_ci static const struct mt76_bus_ops mt7921s_ops = { 10862306a36Sopenharmony_ci .rr = mt76s_rr, 10962306a36Sopenharmony_ci .rmw = mt76s_rmw, 11062306a36Sopenharmony_ci .wr = mt76s_wr, 11162306a36Sopenharmony_ci .write_copy = mt76s_write_copy, 11262306a36Sopenharmony_ci .read_copy = mt76s_read_copy, 11362306a36Sopenharmony_ci .wr_rp = mt76s_wr_rp, 11462306a36Sopenharmony_ci .rd_rp = mt76s_rd_rp, 11562306a36Sopenharmony_ci .type = MT76_BUS_SDIO, 11662306a36Sopenharmony_ci }; 11762306a36Sopenharmony_ci static const struct mt792x_hif_ops mt7921_sdio_ops = { 11862306a36Sopenharmony_ci .init_reset = mt7921s_init_reset, 11962306a36Sopenharmony_ci .reset = mt7921s_mac_reset, 12062306a36Sopenharmony_ci .mcu_init = mt7921s_mcu_init, 12162306a36Sopenharmony_ci .drv_own = mt7921s_mcu_drv_pmctrl, 12262306a36Sopenharmony_ci .fw_own = mt7921s_mcu_fw_pmctrl, 12362306a36Sopenharmony_ci }; 12462306a36Sopenharmony_ci struct ieee80211_ops *ops; 12562306a36Sopenharmony_ci struct mt792x_dev *dev; 12662306a36Sopenharmony_ci struct mt76_dev *mdev; 12762306a36Sopenharmony_ci u8 features; 12862306a36Sopenharmony_ci int ret; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci ops = mt792x_get_mac80211_ops(&func->dev, &mt7921_ops, 13162306a36Sopenharmony_ci (void *)id->driver_data, &features); 13262306a36Sopenharmony_ci if (!ops) 13362306a36Sopenharmony_ci return -ENOMEM; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci mdev = mt76_alloc_device(&func->dev, sizeof(*dev), ops, &drv_ops); 13662306a36Sopenharmony_ci if (!mdev) 13762306a36Sopenharmony_ci return -ENOMEM; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci dev = container_of(mdev, struct mt792x_dev, mt76); 14062306a36Sopenharmony_ci dev->fw_features = features; 14162306a36Sopenharmony_ci dev->hif_ops = &mt7921_sdio_ops; 14262306a36Sopenharmony_ci sdio_set_drvdata(func, dev); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci ret = mt76s_init(mdev, func, &mt7921s_ops); 14562306a36Sopenharmony_ci if (ret < 0) 14662306a36Sopenharmony_ci goto error; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci ret = mt76s_hw_init(mdev, func, MT76_CONNAC2_SDIO); 14962306a36Sopenharmony_ci if (ret) 15062306a36Sopenharmony_ci goto error; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) | 15362306a36Sopenharmony_ci (mt76_rr(dev, MT_HW_REV) & 0xff); 15462306a36Sopenharmony_ci dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci mdev->sdio.parse_irq = mt7921s_parse_intr; 15762306a36Sopenharmony_ci mdev->sdio.intr_data = devm_kmalloc(mdev->dev, 15862306a36Sopenharmony_ci sizeof(struct mt7921_sdio_intr), 15962306a36Sopenharmony_ci GFP_KERNEL); 16062306a36Sopenharmony_ci if (!mdev->sdio.intr_data) { 16162306a36Sopenharmony_ci ret = -ENOMEM; 16262306a36Sopenharmony_ci goto error; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci ret = mt76s_alloc_rx_queue(mdev, MT_RXQ_MAIN); 16662306a36Sopenharmony_ci if (ret) 16762306a36Sopenharmony_ci goto error; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci ret = mt76s_alloc_rx_queue(mdev, MT_RXQ_MCU); 17062306a36Sopenharmony_ci if (ret) 17162306a36Sopenharmony_ci goto error; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci ret = mt76s_alloc_tx(mdev); 17462306a36Sopenharmony_ci if (ret) 17562306a36Sopenharmony_ci goto error; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci ret = mt76_worker_setup(mt76_hw(dev), &mdev->sdio.txrx_worker, 17862306a36Sopenharmony_ci mt7921s_txrx_worker, "sdio-txrx"); 17962306a36Sopenharmony_ci if (ret) 18062306a36Sopenharmony_ci goto error; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci sched_set_fifo_low(mdev->sdio.txrx_worker.task); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci ret = mt7921_register_device(dev); 18562306a36Sopenharmony_ci if (ret) 18662306a36Sopenharmony_ci goto error; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return 0; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cierror: 19162306a36Sopenharmony_ci mt76s_deinit(&dev->mt76); 19262306a36Sopenharmony_ci mt76_free_device(&dev->mt76); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return ret; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void mt7921s_remove(struct sdio_func *func) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct mt792x_dev *dev = sdio_get_drvdata(func); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci mt7921s_unregister_device(dev); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int mt7921s_suspend(struct device *__dev) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct sdio_func *func = dev_to_sdio_func(__dev); 20762306a36Sopenharmony_ci struct mt792x_dev *dev = sdio_get_drvdata(func); 20862306a36Sopenharmony_ci struct mt76_connac_pm *pm = &dev->pm; 20962306a36Sopenharmony_ci struct mt76_dev *mdev = &dev->mt76; 21062306a36Sopenharmony_ci int err; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci pm->suspended = true; 21362306a36Sopenharmony_ci set_bit(MT76_STATE_SUSPEND, &mdev->phy.state); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci flush_work(&dev->reset_work); 21662306a36Sopenharmony_ci cancel_delayed_work_sync(&pm->ps_work); 21762306a36Sopenharmony_ci cancel_work_sync(&pm->wake_work); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci err = mt792x_mcu_drv_pmctrl(dev); 22062306a36Sopenharmony_ci if (err < 0) 22162306a36Sopenharmony_ci goto restore_suspend; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* always enable deep sleep during suspend to reduce 22462306a36Sopenharmony_ci * power consumption 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_ci mt76_connac_mcu_set_deep_sleep(mdev, true); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci mt76_txq_schedule_all(&dev->mphy); 22962306a36Sopenharmony_ci mt76_worker_disable(&mdev->tx_worker); 23062306a36Sopenharmony_ci mt76_worker_disable(&mdev->sdio.status_worker); 23162306a36Sopenharmony_ci mt76_worker_disable(&mdev->sdio.stat_worker); 23262306a36Sopenharmony_ci clear_bit(MT76_READING_STATS, &dev->mphy.state); 23362306a36Sopenharmony_ci mt76_tx_status_check(mdev, true); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci mt76_worker_schedule(&mdev->sdio.txrx_worker); 23662306a36Sopenharmony_ci wait_event_timeout(dev->mt76.sdio.wait, 23762306a36Sopenharmony_ci mt76s_txqs_empty(&dev->mt76), 5 * HZ); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* It is supposed that SDIO bus is idle at the point */ 24062306a36Sopenharmony_ci err = mt76_connac_mcu_set_hif_suspend(mdev, true); 24162306a36Sopenharmony_ci if (err) 24262306a36Sopenharmony_ci goto restore_worker; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci mt76_worker_disable(&mdev->sdio.txrx_worker); 24562306a36Sopenharmony_ci mt76_worker_disable(&mdev->sdio.net_worker); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci err = mt792x_mcu_fw_pmctrl(dev); 24862306a36Sopenharmony_ci if (err) 24962306a36Sopenharmony_ci goto restore_txrx_worker; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return 0; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cirestore_txrx_worker: 25662306a36Sopenharmony_ci mt76_worker_enable(&mdev->sdio.net_worker); 25762306a36Sopenharmony_ci mt76_worker_enable(&mdev->sdio.txrx_worker); 25862306a36Sopenharmony_ci mt76_connac_mcu_set_hif_suspend(mdev, false); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cirestore_worker: 26162306a36Sopenharmony_ci mt76_worker_enable(&mdev->tx_worker); 26262306a36Sopenharmony_ci mt76_worker_enable(&mdev->sdio.status_worker); 26362306a36Sopenharmony_ci mt76_worker_enable(&mdev->sdio.stat_worker); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (!pm->ds_enable) 26662306a36Sopenharmony_ci mt76_connac_mcu_set_deep_sleep(mdev, false); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cirestore_suspend: 26962306a36Sopenharmony_ci clear_bit(MT76_STATE_SUSPEND, &mdev->phy.state); 27062306a36Sopenharmony_ci pm->suspended = false; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (err < 0) 27362306a36Sopenharmony_ci mt792x_reset(&dev->mt76); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return err; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic int mt7921s_resume(struct device *__dev) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct sdio_func *func = dev_to_sdio_func(__dev); 28162306a36Sopenharmony_ci struct mt792x_dev *dev = sdio_get_drvdata(func); 28262306a36Sopenharmony_ci struct mt76_connac_pm *pm = &dev->pm; 28362306a36Sopenharmony_ci struct mt76_dev *mdev = &dev->mt76; 28462306a36Sopenharmony_ci int err; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci clear_bit(MT76_STATE_SUSPEND, &mdev->phy.state); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci err = mt792x_mcu_drv_pmctrl(dev); 28962306a36Sopenharmony_ci if (err < 0) 29062306a36Sopenharmony_ci goto failed; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci mt76_worker_enable(&mdev->tx_worker); 29362306a36Sopenharmony_ci mt76_worker_enable(&mdev->sdio.txrx_worker); 29462306a36Sopenharmony_ci mt76_worker_enable(&mdev->sdio.status_worker); 29562306a36Sopenharmony_ci mt76_worker_enable(&mdev->sdio.net_worker); 29662306a36Sopenharmony_ci mt76_worker_enable(&mdev->sdio.stat_worker); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* restore previous ds setting */ 29962306a36Sopenharmony_ci if (!pm->ds_enable) 30062306a36Sopenharmony_ci mt76_connac_mcu_set_deep_sleep(mdev, false); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci err = mt76_connac_mcu_set_hif_suspend(mdev, false); 30362306a36Sopenharmony_cifailed: 30462306a36Sopenharmony_ci pm->suspended = false; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (err < 0) 30762306a36Sopenharmony_ci mt792x_reset(&dev->mt76); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return err; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(sdio, mt7921s_table); 31362306a36Sopenharmony_ciMODULE_FIRMWARE(MT7921_FIRMWARE_WM); 31462306a36Sopenharmony_ciMODULE_FIRMWARE(MT7921_ROM_PATCH); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(mt7921s_pm_ops, mt7921s_suspend, mt7921s_resume); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic struct sdio_driver mt7921s_driver = { 31962306a36Sopenharmony_ci .name = KBUILD_MODNAME, 32062306a36Sopenharmony_ci .probe = mt7921s_probe, 32162306a36Sopenharmony_ci .remove = mt7921s_remove, 32262306a36Sopenharmony_ci .id_table = mt7921s_table, 32362306a36Sopenharmony_ci .drv.pm = pm_sleep_ptr(&mt7921s_pm_ops), 32462306a36Sopenharmony_ci}; 32562306a36Sopenharmony_cimodule_sdio_driver(mt7921s_driver); 32662306a36Sopenharmony_ciMODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); 32762306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 328