xref: /openbmc/linux/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mcu.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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