xref: /openbmc/linux/drivers/net/wireless/mediatek/mt76/mt7615/sdio_mcu.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
1a66cbdd6SSean Wang // SPDX-License-Identifier: GPL-2.0
2a66cbdd6SSean Wang /* Copyright (C) 2020 MediaTek Inc.
3a66cbdd6SSean Wang  *
4a66cbdd6SSean Wang  * Author: Felix Fietkau <nbd@nbd.name>
5a66cbdd6SSean Wang  *	   Lorenzo Bianconi <lorenzo@kernel.org>
6a66cbdd6SSean Wang  *	   Sean Wang <sean.wang@mediatek.com>
7a66cbdd6SSean Wang  */
8a66cbdd6SSean Wang #include <linux/kernel.h>
9a66cbdd6SSean Wang #include <linux/mmc/sdio_func.h>
10a66cbdd6SSean Wang #include <linux/module.h>
11a66cbdd6SSean Wang #include <linux/iopoll.h>
12a66cbdd6SSean Wang 
13764dee47SLorenzo Bianconi #include "../sdio.h"
14a66cbdd6SSean Wang #include "mt7615.h"
15a66cbdd6SSean Wang #include "mac.h"
16a66cbdd6SSean Wang #include "mcu.h"
17a66cbdd6SSean Wang #include "regs.h"
18a66cbdd6SSean Wang 
mt7663s_mcu_init_sched(struct mt7615_dev * dev)19a66cbdd6SSean Wang static int mt7663s_mcu_init_sched(struct mt7615_dev *dev)
20a66cbdd6SSean Wang {
21a66cbdd6SSean Wang 	struct mt76_sdio *sdio = &dev->mt76.sdio;
2230578752SLorenzo Bianconi 	u32 txdwcnt;
23a66cbdd6SSean Wang 
2430578752SLorenzo Bianconi 	sdio->sched.pse_data_quota = mt76_get_field(dev, MT_PSE_PG_HIF0_GROUP,
2530578752SLorenzo Bianconi 						    MT_HIF0_MIN_QUOTA);
2630578752SLorenzo Bianconi 	sdio->sched.pse_mcu_quota = mt76_get_field(dev, MT_PSE_PG_HIF1_GROUP,
2730578752SLorenzo Bianconi 						   MT_HIF1_MIN_QUOTA);
2830578752SLorenzo Bianconi 	sdio->sched.ple_data_quota = mt76_get_field(dev, MT_PLE_PG_HIF0_GROUP,
2930578752SLorenzo Bianconi 						    MT_HIF0_MIN_QUOTA);
30*16d98b54SSean Wang 	sdio->sched.pse_page_size = MT_PSE_PAGE_SZ;
31a66cbdd6SSean Wang 	txdwcnt = mt76_get_field(dev, MT_PP_TXDWCNT,
32a66cbdd6SSean Wang 				 MT_PP_TXDWCNT_TX1_ADD_DW_CNT);
33a66cbdd6SSean Wang 	sdio->sched.deficit = txdwcnt << 2;
34a66cbdd6SSean Wang 
35a66cbdd6SSean Wang 	return 0;
36a66cbdd6SSean Wang }
37a66cbdd6SSean Wang 
38a66cbdd6SSean Wang static int
mt7663s_mcu_send_message(struct mt76_dev * mdev,struct sk_buff * skb,int cmd,int * seq)39a66cbdd6SSean Wang mt7663s_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
40e452c6ebSFelix Fietkau 			 int cmd, int *seq)
41a66cbdd6SSean Wang {
42a66cbdd6SSean Wang 	struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76);
43e452c6ebSFelix Fietkau 	int ret;
44a66cbdd6SSean Wang 
45e452c6ebSFelix Fietkau 	mt7615_mcu_fill_msg(dev, skb, cmd, seq);
46e637763bSLorenzo Bianconi 	ret = mt76_tx_queue_skb_raw(dev, mdev->q_mcu[MT_MCUQ_WM], skb, 0);
47a66cbdd6SSean Wang 	if (ret)
48e452c6ebSFelix Fietkau 		return ret;
49a66cbdd6SSean Wang 
50e637763bSLorenzo Bianconi 	mt76_queue_kick(dev, mdev->q_mcu[MT_MCUQ_WM]);
51a66cbdd6SSean Wang 
52a66cbdd6SSean Wang 	return ret;
53a66cbdd6SSean Wang }
54a66cbdd6SSean Wang 
__mt7663s_mcu_drv_pmctrl(struct mt7615_dev * dev)5502de318aSLorenzo Bianconi static int __mt7663s_mcu_drv_pmctrl(struct mt7615_dev *dev)
56a66cbdd6SSean Wang {
57a66cbdd6SSean Wang 	struct sdio_func *func = dev->mt76.sdio.func;
58a66cbdd6SSean Wang 	struct mt76_phy *mphy = &dev->mt76.phy;
5950a97efeSLorenzo Bianconi 	struct mt76_connac_pm *pm = &dev->pm;
60a66cbdd6SSean Wang 	u32 status;
61a66cbdd6SSean Wang 	int ret;
62a66cbdd6SSean Wang 
63a66cbdd6SSean Wang 	sdio_claim_host(func);
64a66cbdd6SSean Wang 
65673d7764SLorenzo Bianconi 	sdio_writel(func, WHLPCR_FW_OWN_REQ_CLR, MCR_WHLPCR, NULL);
66a66cbdd6SSean Wang 
67764dee47SLorenzo Bianconi 	ret = readx_poll_timeout(mt76s_read_pcr, &dev->mt76, status,
68a66cbdd6SSean Wang 				 status & WHLPCR_IS_DRIVER_OWN, 2000, 1000000);
698aff2d91SLorenzo Bianconi 	if (ret < 0) {
70a66cbdd6SSean Wang 		dev_err(dev->mt76.dev, "Cannot get ownership from device");
718aff2d91SLorenzo Bianconi 	} else {
7250a97efeSLorenzo Bianconi 		clear_bit(MT76_STATE_PM, &mphy->state);
7350a97efeSLorenzo Bianconi 
748aff2d91SLorenzo Bianconi 		pm->stats.last_wake_event = jiffies;
758aff2d91SLorenzo Bianconi 		pm->stats.doze_time += pm->stats.last_wake_event -
768aff2d91SLorenzo Bianconi 				       pm->stats.last_doze_event;
778aff2d91SLorenzo Bianconi 	}
78a66cbdd6SSean Wang 	sdio_release_host(func);
79a66cbdd6SSean Wang 
80a66cbdd6SSean Wang 	return ret;
81a66cbdd6SSean Wang }
82a66cbdd6SSean Wang 
mt7663s_mcu_drv_pmctrl(struct mt7615_dev * dev)8302de318aSLorenzo Bianconi static int mt7663s_mcu_drv_pmctrl(struct mt7615_dev *dev)
8402de318aSLorenzo Bianconi {
8502de318aSLorenzo Bianconi 	struct mt76_phy *mphy = &dev->mt76.phy;
8650a97efeSLorenzo Bianconi 	int ret = 0;
8702de318aSLorenzo Bianconi 
8850a97efeSLorenzo Bianconi 	mutex_lock(&dev->pm.mutex);
8902de318aSLorenzo Bianconi 
9050a97efeSLorenzo Bianconi 	if (test_bit(MT76_STATE_PM, &mphy->state))
9150a97efeSLorenzo Bianconi 		ret = __mt7663s_mcu_drv_pmctrl(dev);
9250a97efeSLorenzo Bianconi 
9350a97efeSLorenzo Bianconi 	mutex_unlock(&dev->pm.mutex);
9450a97efeSLorenzo Bianconi 
9550a97efeSLorenzo Bianconi 	return ret;
9602de318aSLorenzo Bianconi }
9702de318aSLorenzo Bianconi 
mt7663s_mcu_fw_pmctrl(struct mt7615_dev * dev)98d6e08f2bSLorenzo Bianconi static int mt7663s_mcu_fw_pmctrl(struct mt7615_dev *dev)
99a66cbdd6SSean Wang {
100a66cbdd6SSean Wang 	struct sdio_func *func = dev->mt76.sdio.func;
101a66cbdd6SSean Wang 	struct mt76_phy *mphy = &dev->mt76.phy;
10250a97efeSLorenzo Bianconi 	struct mt76_connac_pm *pm = &dev->pm;
10350a97efeSLorenzo Bianconi 	int ret = 0;
104a66cbdd6SSean Wang 	u32 status;
105a66cbdd6SSean Wang 
10650a97efeSLorenzo Bianconi 	mutex_lock(&pm->mutex);
10750a97efeSLorenzo Bianconi 
10850a97efeSLorenzo Bianconi 	if (mt76_connac_skip_fw_pmctrl(mphy, pm))
10950a97efeSLorenzo Bianconi 		goto out;
110a66cbdd6SSean Wang 
111a66cbdd6SSean Wang 	sdio_claim_host(func);
112a66cbdd6SSean Wang 
113673d7764SLorenzo Bianconi 	sdio_writel(func, WHLPCR_FW_OWN_REQ_SET, MCR_WHLPCR, NULL);
114a66cbdd6SSean Wang 
115764dee47SLorenzo Bianconi 	ret = readx_poll_timeout(mt76s_read_pcr, &dev->mt76, status,
116a66cbdd6SSean Wang 				 !(status & WHLPCR_IS_DRIVER_OWN), 2000, 1000000);
117a66cbdd6SSean Wang 	if (ret < 0) {
118a66cbdd6SSean Wang 		dev_err(dev->mt76.dev, "Cannot set ownership to device");
119a66cbdd6SSean Wang 		clear_bit(MT76_STATE_PM, &mphy->state);
1208aff2d91SLorenzo Bianconi 	} else {
1218aff2d91SLorenzo Bianconi 		pm->stats.last_doze_event = jiffies;
1228aff2d91SLorenzo Bianconi 		pm->stats.awake_time += pm->stats.last_doze_event -
1238aff2d91SLorenzo Bianconi 					pm->stats.last_wake_event;
124a66cbdd6SSean Wang 	}
125a66cbdd6SSean Wang 
126a66cbdd6SSean Wang 	sdio_release_host(func);
12750a97efeSLorenzo Bianconi out:
12850a97efeSLorenzo Bianconi 	mutex_unlock(&pm->mutex);
129a66cbdd6SSean Wang 
130a66cbdd6SSean Wang 	return ret;
131a66cbdd6SSean Wang }
132a66cbdd6SSean Wang 
mt7663s_mcu_init(struct mt7615_dev * dev)133a66cbdd6SSean Wang int mt7663s_mcu_init(struct mt7615_dev *dev)
134a66cbdd6SSean Wang {
135a66cbdd6SSean Wang 	static const struct mt76_mcu_ops mt7663s_mcu_ops = {
136a66cbdd6SSean Wang 		.headroom = sizeof(struct mt7615_mcu_txd),
137a66cbdd6SSean Wang 		.tailroom = MT_USB_TAIL_SIZE,
138a66cbdd6SSean Wang 		.mcu_skb_send_msg = mt7663s_mcu_send_message,
139f320d812SFelix Fietkau 		.mcu_parse_response = mt7615_mcu_parse_response,
14002fbf819SSean Wang 		.mcu_rr = mt76_connac_mcu_reg_rr,
14102fbf819SSean Wang 		.mcu_wr = mt76_connac_mcu_reg_wr,
142a66cbdd6SSean Wang 	};
143d6e08f2bSLorenzo Bianconi 	struct mt7615_mcu_ops *mcu_ops;
144a66cbdd6SSean Wang 	int ret;
145a66cbdd6SSean Wang 
14602de318aSLorenzo Bianconi 	ret = __mt7663s_mcu_drv_pmctrl(dev);
147a66cbdd6SSean Wang 	if (ret)
148a66cbdd6SSean Wang 		return ret;
149a66cbdd6SSean Wang 
150a66cbdd6SSean Wang 	dev->mt76.mcu_ops = &mt7663s_mcu_ops,
151a66cbdd6SSean Wang 
152a66cbdd6SSean Wang 	ret = mt76_get_field(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY);
153a66cbdd6SSean Wang 	if (ret) {
154a66cbdd6SSean Wang 		mt7615_mcu_restart(&dev->mt76);
155a66cbdd6SSean Wang 		if (!mt76_poll_msec(dev, MT_CONN_ON_MISC,
156a66cbdd6SSean Wang 				    MT_TOP_MISC2_FW_N9_RDY, 0, 500))
157a66cbdd6SSean Wang 			return -EIO;
158a66cbdd6SSean Wang 	}
159a66cbdd6SSean Wang 
160a66cbdd6SSean Wang 	ret = __mt7663_load_firmware(dev);
161a66cbdd6SSean Wang 	if (ret)
162a66cbdd6SSean Wang 		return ret;
163a66cbdd6SSean Wang 
164d6e08f2bSLorenzo Bianconi 	mcu_ops = devm_kmemdup(dev->mt76.dev, dev->mcu_ops, sizeof(*mcu_ops),
165d6e08f2bSLorenzo Bianconi 			       GFP_KERNEL);
166d6e08f2bSLorenzo Bianconi 	if (!mcu_ops)
167d6e08f2bSLorenzo Bianconi 		return -ENOMEM;
168d6e08f2bSLorenzo Bianconi 
169d6e08f2bSLorenzo Bianconi 	mcu_ops->set_drv_ctrl = mt7663s_mcu_drv_pmctrl;
170d6e08f2bSLorenzo Bianconi 	mcu_ops->set_fw_ctrl = mt7663s_mcu_fw_pmctrl;
171d6e08f2bSLorenzo Bianconi 	dev->mcu_ops = mcu_ops;
172d6e08f2bSLorenzo Bianconi 
173a66cbdd6SSean Wang 	ret = mt7663s_mcu_init_sched(dev);
174a66cbdd6SSean Wang 	if (ret)
175a66cbdd6SSean Wang 		return ret;
176a66cbdd6SSean Wang 
177a66cbdd6SSean Wang 	set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
178a66cbdd6SSean Wang 
179a66cbdd6SSean Wang 	return 0;
180a66cbdd6SSean Wang }
181