148fab5bbSSean Wang // SPDX-License-Identifier: ISC
248fab5bbSSean Wang /* Copyright (C) 2021 MediaTek Inc. */
348fab5bbSSean Wang
448fab5bbSSean Wang #include <linux/kernel.h>
548fab5bbSSean Wang #include <linux/mmc/sdio_func.h>
648fab5bbSSean Wang #include <linux/module.h>
748fab5bbSSean Wang #include <linux/iopoll.h>
848fab5bbSSean Wang
948fab5bbSSean Wang #include "mt7921.h"
1048fab5bbSSean Wang #include "../sdio.h"
11140efef3SLorenzo Bianconi #include "../mt76_connac2_mac.h"
1248fab5bbSSean Wang #include "mcu.h"
1348fab5bbSSean Wang #include "regs.h"
1448fab5bbSSean Wang
1548fab5bbSSean Wang static int
mt7921s_mcu_send_message(struct mt76_dev * mdev,struct sk_buff * skb,int cmd,int * seq)1648fab5bbSSean Wang mt7921s_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
1748fab5bbSSean Wang int cmd, int *seq)
1848fab5bbSSean Wang {
19975e122dSLorenzo Bianconi struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
2048fab5bbSSean Wang enum mt7921_sdio_pkt_type type = MT7921_SDIO_CMD;
2148fab5bbSSean Wang enum mt76_mcuq_id txq = MT_MCUQ_WM;
2248fab5bbSSean Wang int ret, pad;
2348fab5bbSSean Wang
24ca74b9b9SSean Wang /* We just return in case firmware assertion to avoid blocking the
25ca74b9b9SSean Wang * common workqueue to run, for example, the coredump work might be
26*c74df1c0SLorenzo Bianconi * blocked by mt792x_mac_work that is excuting register access via sdio
27ca74b9b9SSean Wang * bus.
28ca74b9b9SSean Wang */
29ca74b9b9SSean Wang if (dev->fw_assert)
30ca74b9b9SSean Wang return -EBUSY;
31ca74b9b9SSean Wang
32d2f5c8edSLorenzo Bianconi ret = mt76_connac2_mcu_fill_message(mdev, skb, cmd, seq);
3348fab5bbSSean Wang if (ret)
3448fab5bbSSean Wang return ret;
3548fab5bbSSean Wang
36d2f5c8edSLorenzo Bianconi mdev->mcu.timeout = 3 * HZ;
37d2f5c8edSLorenzo Bianconi
38ffc2198dSLorenzo Bianconi if (cmd == MCU_CMD(FW_SCATTER))
3948fab5bbSSean Wang type = MT7921_SDIO_FWDL;
4048fab5bbSSean Wang
41b72fd217SLorenzo Bianconi mt7921_skb_add_usb_sdio_hdr(dev, skb, type);
4248fab5bbSSean Wang pad = round_up(skb->len, 4) - skb->len;
4348fab5bbSSean Wang __skb_put_zero(skb, pad);
4448fab5bbSSean Wang
4548fab5bbSSean Wang ret = mt76_tx_queue_skb_raw(dev, mdev->q_mcu[txq], skb, 0);
4648fab5bbSSean Wang if (ret)
4748fab5bbSSean Wang return ret;
4848fab5bbSSean Wang
4948fab5bbSSean Wang mt76_queue_kick(dev, mdev->q_mcu[txq]);
5048fab5bbSSean Wang
5148fab5bbSSean Wang return ret;
5248fab5bbSSean Wang }
5348fab5bbSSean Wang
mt7921s_read_rm3r(struct mt792x_dev * dev)54975e122dSLorenzo Bianconi static u32 mt7921s_read_rm3r(struct mt792x_dev *dev)
55b12deb5eSLeon Yen {
56b12deb5eSLeon Yen struct mt76_sdio *sdio = &dev->mt76.sdio;
57b12deb5eSLeon Yen
58b12deb5eSLeon Yen return sdio_readl(sdio->func, MCR_D2HRM3R, NULL);
59b12deb5eSLeon Yen }
60b12deb5eSLeon Yen
mt7921s_clear_rm3r_drv_own(struct mt792x_dev * dev)61975e122dSLorenzo Bianconi static u32 mt7921s_clear_rm3r_drv_own(struct mt792x_dev *dev)
62b12deb5eSLeon Yen {
63b12deb5eSLeon Yen struct mt76_sdio *sdio = &dev->mt76.sdio;
64b12deb5eSLeon Yen u32 val;
65b12deb5eSLeon Yen
66b12deb5eSLeon Yen val = sdio_readl(sdio->func, MCR_D2HRM3R, NULL);
67b12deb5eSLeon Yen if (val)
68b12deb5eSLeon Yen sdio_writel(sdio->func, H2D_SW_INT_CLEAR_MAILBOX_ACK,
69b12deb5eSLeon Yen MCR_WSICR, NULL);
70b12deb5eSLeon Yen
71b12deb5eSLeon Yen return val;
72b12deb5eSLeon Yen }
73b12deb5eSLeon Yen
mt7921s_mcu_init(struct mt792x_dev * dev)74975e122dSLorenzo Bianconi int mt7921s_mcu_init(struct mt792x_dev *dev)
7548fab5bbSSean Wang {
7648fab5bbSSean Wang static const struct mt76_mcu_ops mt7921s_mcu_ops = {
77fc6ee71aSLorenzo Bianconi .headroom = MT_SDIO_HDR_SIZE +
78fc6ee71aSLorenzo Bianconi sizeof(struct mt76_connac2_mcu_txd),
7948fab5bbSSean Wang .tailroom = MT_SDIO_TAIL_SIZE,
8048fab5bbSSean Wang .mcu_skb_send_msg = mt7921s_mcu_send_message,
8148fab5bbSSean Wang .mcu_parse_response = mt7921_mcu_parse_response,
8248fab5bbSSean Wang .mcu_rr = mt76_connac_mcu_reg_rr,
8348fab5bbSSean Wang .mcu_wr = mt76_connac_mcu_reg_wr,
8448fab5bbSSean Wang };
8548fab5bbSSean Wang int ret;
8648fab5bbSSean Wang
8748fab5bbSSean Wang mt7921s_mcu_drv_pmctrl(dev);
8848fab5bbSSean Wang
8948fab5bbSSean Wang dev->mt76.mcu_ops = &mt7921s_mcu_ops;
9048fab5bbSSean Wang
9148fab5bbSSean Wang ret = mt7921_run_firmware(dev);
9248fab5bbSSean Wang if (ret)
9348fab5bbSSean Wang return ret;
9448fab5bbSSean Wang
9548fab5bbSSean Wang set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
9648fab5bbSSean Wang
9748fab5bbSSean Wang return 0;
9848fab5bbSSean Wang }
9948fab5bbSSean Wang
mt7921s_mcu_drv_pmctrl(struct mt792x_dev * dev)100975e122dSLorenzo Bianconi int mt7921s_mcu_drv_pmctrl(struct mt792x_dev *dev)
10148fab5bbSSean Wang {
10248fab5bbSSean Wang struct sdio_func *func = dev->mt76.sdio.func;
10348fab5bbSSean Wang struct mt76_phy *mphy = &dev->mt76.phy;
10448fab5bbSSean Wang struct mt76_connac_pm *pm = &dev->pm;
10548fab5bbSSean Wang u32 status;
106cfd61109SLorenzo Bianconi int err;
10748fab5bbSSean Wang
10848fab5bbSSean Wang sdio_claim_host(func);
10948fab5bbSSean Wang
11048fab5bbSSean Wang sdio_writel(func, WHLPCR_FW_OWN_REQ_CLR, MCR_WHLPCR, NULL);
11148fab5bbSSean Wang
11248fab5bbSSean Wang err = readx_poll_timeout(mt76s_read_pcr, &dev->mt76, status,
11348fab5bbSSean Wang status & WHLPCR_IS_DRIVER_OWN, 2000, 1000000);
114b12deb5eSLeon Yen
115b12deb5eSLeon Yen if (!err && test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state))
116b12deb5eSLeon Yen err = readx_poll_timeout(mt7921s_read_rm3r, dev, status,
117b12deb5eSLeon Yen status & D2HRM3R_IS_DRIVER_OWN,
118b12deb5eSLeon Yen 2000, 1000000);
119b12deb5eSLeon Yen
12048fab5bbSSean Wang sdio_release_host(func);
12148fab5bbSSean Wang
12248fab5bbSSean Wang if (err < 0) {
12348fab5bbSSean Wang dev_err(dev->mt76.dev, "driver own failed\n");
124cfd61109SLorenzo Bianconi return -EIO;
12548fab5bbSSean Wang }
12648fab5bbSSean Wang
12748fab5bbSSean Wang clear_bit(MT76_STATE_PM, &mphy->state);
12848fab5bbSSean Wang
12948fab5bbSSean Wang pm->stats.last_wake_event = jiffies;
13048fab5bbSSean Wang pm->stats.doze_time += pm->stats.last_wake_event -
13148fab5bbSSean Wang pm->stats.last_doze_event;
132cfd61109SLorenzo Bianconi
133cfd61109SLorenzo Bianconi return 0;
13448fab5bbSSean Wang }
13548fab5bbSSean Wang
mt7921s_mcu_fw_pmctrl(struct mt792x_dev * dev)136975e122dSLorenzo Bianconi int mt7921s_mcu_fw_pmctrl(struct mt792x_dev *dev)
13748fab5bbSSean Wang {
13848fab5bbSSean Wang struct sdio_func *func = dev->mt76.sdio.func;
13948fab5bbSSean Wang struct mt76_phy *mphy = &dev->mt76.phy;
14048fab5bbSSean Wang struct mt76_connac_pm *pm = &dev->pm;
14148fab5bbSSean Wang u32 status;
142364718c9SDeren Wu int err;
14348fab5bbSSean Wang
14448fab5bbSSean Wang sdio_claim_host(func);
14548fab5bbSSean Wang
146b12deb5eSLeon Yen if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state)) {
147b12deb5eSLeon Yen err = readx_poll_timeout(mt7921s_clear_rm3r_drv_own,
148b12deb5eSLeon Yen dev, status,
149b12deb5eSLeon Yen !(status & D2HRM3R_IS_DRIVER_OWN),
150b12deb5eSLeon Yen 2000, 1000000);
151b12deb5eSLeon Yen if (err < 0) {
152b12deb5eSLeon Yen dev_err(dev->mt76.dev, "mailbox ACK not cleared\n");
153364718c9SDeren Wu goto out;
154b12deb5eSLeon Yen }
155b12deb5eSLeon Yen }
156b12deb5eSLeon Yen
15748fab5bbSSean Wang sdio_writel(func, WHLPCR_FW_OWN_REQ_SET, MCR_WHLPCR, NULL);
15848fab5bbSSean Wang
15948fab5bbSSean Wang err = readx_poll_timeout(mt76s_read_pcr, &dev->mt76, status,
16048fab5bbSSean Wang !(status & WHLPCR_IS_DRIVER_OWN), 2000, 1000000);
161364718c9SDeren Wu out:
16248fab5bbSSean Wang sdio_release_host(func);
16348fab5bbSSean Wang
16448fab5bbSSean Wang if (err < 0) {
16548fab5bbSSean Wang dev_err(dev->mt76.dev, "firmware own failed\n");
16648fab5bbSSean Wang clear_bit(MT76_STATE_PM, &mphy->state);
167364718c9SDeren Wu return -EIO;
16848fab5bbSSean Wang }
16948fab5bbSSean Wang
17048fab5bbSSean Wang pm->stats.last_doze_event = jiffies;
17148fab5bbSSean Wang pm->stats.awake_time += pm->stats.last_doze_event -
17248fab5bbSSean Wang pm->stats.last_wake_event;
17348fab5bbSSean Wang
174364718c9SDeren Wu return 0;
17548fab5bbSSean Wang }
176