sdio_mcu.c (4133B)
1// SPDX-License-Identifier: ISC 2/* Copyright (C) 2021 MediaTek Inc. */ 3 4#include <linux/kernel.h> 5#include <linux/mmc/sdio_func.h> 6#include <linux/module.h> 7#include <linux/iopoll.h> 8 9#include "mt7921.h" 10#include "../sdio.h" 11#include "mac.h" 12#include "mcu.h" 13#include "regs.h" 14 15static int 16mt7921s_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, 17 int cmd, int *seq) 18{ 19 struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76); 20 enum mt7921_sdio_pkt_type type = MT7921_SDIO_CMD; 21 enum mt76_mcuq_id txq = MT_MCUQ_WM; 22 int ret, pad; 23 24 /* We just return in case firmware assertion to avoid blocking the 25 * common workqueue to run, for example, the coredump work might be 26 * blocked by mt7921_mac_work that is excuting register access via sdio 27 * bus. 28 */ 29 if (dev->fw_assert) 30 return -EBUSY; 31 32 ret = mt7921_mcu_fill_message(mdev, skb, cmd, seq); 33 if (ret) 34 return ret; 35 36 if (cmd == MCU_CMD(FW_SCATTER)) 37 type = MT7921_SDIO_FWDL; 38 39 mt7921_skb_add_usb_sdio_hdr(dev, skb, type); 40 pad = round_up(skb->len, 4) - skb->len; 41 __skb_put_zero(skb, pad); 42 43 ret = mt76_tx_queue_skb_raw(dev, mdev->q_mcu[txq], skb, 0); 44 if (ret) 45 return ret; 46 47 mt76_queue_kick(dev, mdev->q_mcu[txq]); 48 49 return ret; 50} 51 52static u32 mt7921s_read_rm3r(struct mt7921_dev *dev) 53{ 54 struct mt76_sdio *sdio = &dev->mt76.sdio; 55 56 return sdio_readl(sdio->func, MCR_D2HRM3R, NULL); 57} 58 59static u32 mt7921s_clear_rm3r_drv_own(struct mt7921_dev *dev) 60{ 61 struct mt76_sdio *sdio = &dev->mt76.sdio; 62 u32 val; 63 64 val = sdio_readl(sdio->func, MCR_D2HRM3R, NULL); 65 if (val) 66 sdio_writel(sdio->func, H2D_SW_INT_CLEAR_MAILBOX_ACK, 67 MCR_WSICR, NULL); 68 69 return val; 70} 71 72int mt7921s_mcu_init(struct mt7921_dev *dev) 73{ 74 static const struct mt76_mcu_ops mt7921s_mcu_ops = { 75 .headroom = MT_SDIO_HDR_SIZE + sizeof(struct mt7921_mcu_txd), 76 .tailroom = MT_SDIO_TAIL_SIZE, 77 .mcu_skb_send_msg = mt7921s_mcu_send_message, 78 .mcu_parse_response = mt7921_mcu_parse_response, 79 .mcu_rr = mt76_connac_mcu_reg_rr, 80 .mcu_wr = mt76_connac_mcu_reg_wr, 81 }; 82 int ret; 83 84 mt7921s_mcu_drv_pmctrl(dev); 85 86 dev->mt76.mcu_ops = &mt7921s_mcu_ops; 87 88 ret = mt7921_run_firmware(dev); 89 if (ret) 90 return ret; 91 92 set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); 93 94 return 0; 95} 96 97int mt7921s_mcu_drv_pmctrl(struct mt7921_dev *dev) 98{ 99 struct sdio_func *func = dev->mt76.sdio.func; 100 struct mt76_phy *mphy = &dev->mt76.phy; 101 struct mt76_connac_pm *pm = &dev->pm; 102 int err = 0; 103 u32 status; 104 105 sdio_claim_host(func); 106 107 sdio_writel(func, WHLPCR_FW_OWN_REQ_CLR, MCR_WHLPCR, NULL); 108 109 err = readx_poll_timeout(mt76s_read_pcr, &dev->mt76, status, 110 status & WHLPCR_IS_DRIVER_OWN, 2000, 1000000); 111 112 if (!err && test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state)) 113 err = readx_poll_timeout(mt7921s_read_rm3r, dev, status, 114 status & D2HRM3R_IS_DRIVER_OWN, 115 2000, 1000000); 116 117 sdio_release_host(func); 118 119 if (err < 0) { 120 dev_err(dev->mt76.dev, "driver own failed\n"); 121 err = -EIO; 122 goto out; 123 } 124 125 clear_bit(MT76_STATE_PM, &mphy->state); 126 127 pm->stats.last_wake_event = jiffies; 128 pm->stats.doze_time += pm->stats.last_wake_event - 129 pm->stats.last_doze_event; 130out: 131 return err; 132} 133 134int mt7921s_mcu_fw_pmctrl(struct mt7921_dev *dev) 135{ 136 struct sdio_func *func = dev->mt76.sdio.func; 137 struct mt76_phy *mphy = &dev->mt76.phy; 138 struct mt76_connac_pm *pm = &dev->pm; 139 int err = 0; 140 u32 status; 141 142 sdio_claim_host(func); 143 144 if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state)) { 145 err = readx_poll_timeout(mt7921s_clear_rm3r_drv_own, 146 dev, status, 147 !(status & D2HRM3R_IS_DRIVER_OWN), 148 2000, 1000000); 149 if (err < 0) { 150 dev_err(dev->mt76.dev, "mailbox ACK not cleared\n"); 151 goto err; 152 } 153 } 154 155 sdio_writel(func, WHLPCR_FW_OWN_REQ_SET, MCR_WHLPCR, NULL); 156 157 err = readx_poll_timeout(mt76s_read_pcr, &dev->mt76, status, 158 !(status & WHLPCR_IS_DRIVER_OWN), 2000, 1000000); 159 sdio_release_host(func); 160 161err: 162 if (err < 0) { 163 dev_err(dev->mt76.dev, "firmware own failed\n"); 164 clear_bit(MT76_STATE_PM, &mphy->state); 165 err = -EIO; 166 } 167 168 pm->stats.last_doze_event = jiffies; 169 pm->stats.awake_time += pm->stats.last_doze_event - 170 pm->stats.last_wake_event; 171 172 return err; 173}