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