1a66cbdd6SSean Wang // SPDX-License-Identifier: ISC
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
9a66cbdd6SSean Wang #include <linux/kernel.h>
10a66cbdd6SSean Wang #include <linux/iopoll.h>
11a66cbdd6SSean Wang #include <linux/module.h>
12a66cbdd6SSean Wang
13a66cbdd6SSean Wang #include <linux/mmc/host.h>
14a66cbdd6SSean Wang #include <linux/mmc/sdio_ids.h>
15a66cbdd6SSean Wang #include <linux/mmc/sdio_func.h>
16a66cbdd6SSean Wang
17764dee47SLorenzo Bianconi #include "../sdio.h"
18a66cbdd6SSean Wang #include "mt7615.h"
19a66cbdd6SSean Wang #include "mac.h"
20d0e274afSLorenzo Bianconi #include "mcu.h"
21a66cbdd6SSean Wang
22a66cbdd6SSean Wang static const struct sdio_device_id mt7663s_table[] = {
23a66cbdd6SSean Wang { SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7603) },
24a66cbdd6SSean Wang { } /* Terminating entry */
25a66cbdd6SSean Wang };
26a66cbdd6SSean Wang
mt7663s_txrx_worker(struct mt76_worker * w)27764dee47SLorenzo Bianconi static void mt7663s_txrx_worker(struct mt76_worker *w)
28a66cbdd6SSean Wang {
29764dee47SLorenzo Bianconi struct mt76_sdio *sdio = container_of(w, struct mt76_sdio,
30764dee47SLorenzo Bianconi txrx_worker);
31764dee47SLorenzo Bianconi struct mt76_dev *mdev = container_of(sdio, struct mt76_dev, sdio);
32764dee47SLorenzo Bianconi struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76);
33764dee47SLorenzo Bianconi
34764dee47SLorenzo Bianconi if (!mt76_connac_pm_ref(&dev->mphy, &dev->pm)) {
35764dee47SLorenzo Bianconi queue_work(mdev->wq, &dev->pm.wake_work);
36764dee47SLorenzo Bianconi return;
37a66cbdd6SSean Wang }
38764dee47SLorenzo Bianconi mt76s_txrx_worker(sdio);
39764dee47SLorenzo Bianconi mt76_connac_pm_unref(&dev->mphy, &dev->pm);
40a66cbdd6SSean Wang }
41a66cbdd6SSean Wang
mt7663s_init_work(struct work_struct * work)42a66cbdd6SSean Wang static void mt7663s_init_work(struct work_struct *work)
43a66cbdd6SSean Wang {
44a66cbdd6SSean Wang struct mt7615_dev *dev;
45a66cbdd6SSean Wang
46a66cbdd6SSean Wang dev = container_of(work, struct mt7615_dev, mcu_work);
47a66cbdd6SSean Wang if (mt7663s_mcu_init(dev))
48a66cbdd6SSean Wang return;
49a66cbdd6SSean Wang
5095f381c5SFelix Fietkau mt7615_init_work(dev);
51a66cbdd6SSean Wang }
52a66cbdd6SSean Wang
mt7663s_parse_intr(struct mt76_dev * dev,struct mt76s_intr * intr)533ad08509SLorenzo Bianconi static int mt7663s_parse_intr(struct mt76_dev *dev, struct mt76s_intr *intr)
543ad08509SLorenzo Bianconi {
553ad08509SLorenzo Bianconi struct mt76_sdio *sdio = &dev->sdio;
563ad08509SLorenzo Bianconi struct mt7663s_intr *irq_data = sdio->intr_data;
573ad08509SLorenzo Bianconi int i, err;
583ad08509SLorenzo Bianconi
59491e3731SSean Wang sdio_claim_host(sdio->func);
603ad08509SLorenzo Bianconi err = sdio_readsb(sdio->func, irq_data, MCR_WHISR, sizeof(*irq_data));
61491e3731SSean Wang sdio_release_host(sdio->func);
62491e3731SSean Wang
633ad08509SLorenzo Bianconi if (err)
643ad08509SLorenzo Bianconi return err;
653ad08509SLorenzo Bianconi
663ad08509SLorenzo Bianconi intr->isr = irq_data->isr;
673ad08509SLorenzo Bianconi intr->rec_mb = irq_data->rec_mb;
683ad08509SLorenzo Bianconi intr->tx.wtqcr = irq_data->tx.wtqcr;
693ad08509SLorenzo Bianconi intr->rx.num = irq_data->rx.num;
703ad08509SLorenzo Bianconi for (i = 0; i < 2 ; i++)
713ad08509SLorenzo Bianconi intr->rx.len[i] = irq_data->rx.len[i];
723ad08509SLorenzo Bianconi
733ad08509SLorenzo Bianconi return 0;
743ad08509SLorenzo Bianconi }
753ad08509SLorenzo Bianconi
mt7663s_probe(struct sdio_func * func,const struct sdio_device_id * id)76a66cbdd6SSean Wang static int mt7663s_probe(struct sdio_func *func,
77a66cbdd6SSean Wang const struct sdio_device_id *id)
78a66cbdd6SSean Wang {
79a66cbdd6SSean Wang static const struct mt76_driver_ops drv_ops = {
80a66cbdd6SSean Wang .txwi_size = MT_USB_TXD_SIZE,
818bf71ab6SLorenzo Bianconi .drv_flags = MT_DRV_RX_DMA_HDR,
82a66cbdd6SSean Wang .tx_prepare_skb = mt7663_usb_sdio_tx_prepare_skb,
83a66cbdd6SSean Wang .tx_complete_skb = mt7663_usb_sdio_tx_complete_skb,
84a66cbdd6SSean Wang .tx_status_data = mt7663_usb_sdio_tx_status_data,
85a66cbdd6SSean Wang .rx_skb = mt7615_queue_rx_skb,
86deb0f90dSLorenzo Bianconi .rx_check = mt7615_rx_check,
8780dda1edSLorenzo Bianconi .sta_add = mt7615_mac_sta_add,
88a66cbdd6SSean Wang .sta_remove = mt7615_mac_sta_remove,
89a66cbdd6SSean Wang .update_survey = mt7615_update_channel,
90a66cbdd6SSean Wang };
91a66cbdd6SSean Wang static const struct mt76_bus_ops mt7663s_ops = {
92764dee47SLorenzo Bianconi .rr = mt76s_rr,
93764dee47SLorenzo Bianconi .rmw = mt76s_rmw,
94764dee47SLorenzo Bianconi .wr = mt76s_wr,
95764dee47SLorenzo Bianconi .write_copy = mt76s_write_copy,
96764dee47SLorenzo Bianconi .read_copy = mt76s_read_copy,
97764dee47SLorenzo Bianconi .wr_rp = mt76s_wr_rp,
98764dee47SLorenzo Bianconi .rd_rp = mt76s_rd_rp,
99a66cbdd6SSean Wang .type = MT76_BUS_SDIO,
100a66cbdd6SSean Wang };
101a66cbdd6SSean Wang struct ieee80211_ops *ops;
102a66cbdd6SSean Wang struct mt7615_dev *dev;
103a66cbdd6SSean Wang struct mt76_dev *mdev;
104bf08d585SSean Wang int ret;
105a66cbdd6SSean Wang
106a66cbdd6SSean Wang ops = devm_kmemdup(&func->dev, &mt7615_ops, sizeof(mt7615_ops),
107a66cbdd6SSean Wang GFP_KERNEL);
108a66cbdd6SSean Wang if (!ops)
109a66cbdd6SSean Wang return -ENOMEM;
110a66cbdd6SSean Wang
111a66cbdd6SSean Wang mdev = mt76_alloc_device(&func->dev, sizeof(*dev), ops, &drv_ops);
112a66cbdd6SSean Wang if (!mdev)
113a66cbdd6SSean Wang return -ENOMEM;
114a66cbdd6SSean Wang
115a66cbdd6SSean Wang dev = container_of(mdev, struct mt7615_dev, mt76);
116a66cbdd6SSean Wang
117a66cbdd6SSean Wang INIT_WORK(&dev->mcu_work, mt7663s_init_work);
118a66cbdd6SSean Wang dev->reg_map = mt7663_usb_sdio_reg_map;
119a66cbdd6SSean Wang dev->ops = ops;
120a66cbdd6SSean Wang sdio_set_drvdata(func, dev);
121a66cbdd6SSean Wang
122a66cbdd6SSean Wang ret = mt76s_init(mdev, func, &mt7663s_ops);
123a66cbdd6SSean Wang if (ret < 0)
1246a618acbSLorenzo Bianconi goto error;
125a66cbdd6SSean Wang
126dacf0acfSSean Wang ret = mt76s_hw_init(mdev, func, MT76_CONNAC_SDIO);
127a66cbdd6SSean Wang if (ret)
1286a618acbSLorenzo Bianconi goto error;
129a66cbdd6SSean Wang
130a66cbdd6SSean Wang mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) |
131a66cbdd6SSean Wang (mt76_rr(dev, MT_HW_REV) & 0xff);
132a66cbdd6SSean Wang dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev);
133a66cbdd6SSean Wang
1343ad08509SLorenzo Bianconi mdev->sdio.parse_irq = mt7663s_parse_intr;
135b4964908SSean Wang mdev->sdio.intr_data = devm_kmalloc(mdev->dev,
1363ad08509SLorenzo Bianconi sizeof(struct mt7663s_intr),
137b4964908SSean Wang GFP_KERNEL);
138b4964908SSean Wang if (!mdev->sdio.intr_data) {
139b4964908SSean Wang ret = -ENOMEM;
1406a618acbSLorenzo Bianconi goto error;
141b4964908SSean Wang }
142b4964908SSean Wang
143d512b008SLorenzo Bianconi ret = mt76s_alloc_rx_queue(mdev, MT_RXQ_MAIN);
144d512b008SLorenzo Bianconi if (ret)
145d512b008SLorenzo Bianconi goto error;
146d512b008SLorenzo Bianconi
147d512b008SLorenzo Bianconi ret = mt76s_alloc_tx(mdev);
148a66cbdd6SSean Wang if (ret)
1496a618acbSLorenzo Bianconi goto error;
150a66cbdd6SSean Wang
151fefb584dSLorenzo Bianconi ret = mt76_worker_setup(mt76_hw(dev), &mdev->sdio.txrx_worker,
152fefb584dSLorenzo Bianconi mt7663s_txrx_worker, "sdio-txrx");
153fefb584dSLorenzo Bianconi if (ret)
1546a618acbSLorenzo Bianconi goto error;
155fefb584dSLorenzo Bianconi
156fefb584dSLorenzo Bianconi sched_set_fifo_low(mdev->sdio.txrx_worker.task);
157fefb584dSLorenzo Bianconi
158a66cbdd6SSean Wang ret = mt7663_usb_sdio_register_device(dev);
159a66cbdd6SSean Wang if (ret)
1606a618acbSLorenzo Bianconi goto error;
161a66cbdd6SSean Wang
162a66cbdd6SSean Wang return 0;
163a66cbdd6SSean Wang
1646a618acbSLorenzo Bianconi error:
165a66cbdd6SSean Wang mt76s_deinit(&dev->mt76);
166a66cbdd6SSean Wang mt76_free_device(&dev->mt76);
167a66cbdd6SSean Wang
168a66cbdd6SSean Wang return ret;
169a66cbdd6SSean Wang }
170a66cbdd6SSean Wang
mt7663s_remove(struct sdio_func * func)171a66cbdd6SSean Wang static void mt7663s_remove(struct sdio_func *func)
172a66cbdd6SSean Wang {
173a66cbdd6SSean Wang struct mt7615_dev *dev = sdio_get_drvdata(func);
174a66cbdd6SSean Wang
175a66cbdd6SSean Wang if (!test_and_clear_bit(MT76_STATE_INITIALIZED, &dev->mphy.state))
176a66cbdd6SSean Wang return;
177a66cbdd6SSean Wang
178a66cbdd6SSean Wang ieee80211_unregister_hw(dev->mt76.hw);
179a66cbdd6SSean Wang mt76s_deinit(&dev->mt76);
180a66cbdd6SSean Wang mt76_free_device(&dev->mt76);
181a66cbdd6SSean Wang }
182a66cbdd6SSean Wang
mt7663s_suspend(struct device * dev)183a66cbdd6SSean Wang static int mt7663s_suspend(struct device *dev)
184a66cbdd6SSean Wang {
185a66cbdd6SSean Wang struct sdio_func *func = dev_to_sdio_func(dev);
186a66cbdd6SSean Wang struct mt7615_dev *mdev = sdio_get_drvdata(func);
187fefb584dSLorenzo Bianconi int err;
188a66cbdd6SSean Wang
189a66cbdd6SSean Wang if (!test_bit(MT76_STATE_SUSPEND, &mdev->mphy.state) &&
190a66cbdd6SSean Wang mt7615_firmware_offload(mdev)) {
191a66cbdd6SSean Wang int err;
192a66cbdd6SSean Wang
19355d4c19cSLorenzo Bianconi err = mt76_connac_mcu_set_hif_suspend(&mdev->mt76, true);
194a66cbdd6SSean Wang if (err < 0)
195a66cbdd6SSean Wang return err;
196a66cbdd6SSean Wang }
197a66cbdd6SSean Wang
1988b7c6e1cSSean Wang sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
1998b7c6e1cSSean Wang
200fefb584dSLorenzo Bianconi err = mt7615_mcu_set_fw_ctrl(mdev);
201fefb584dSLorenzo Bianconi if (err)
202fefb584dSLorenzo Bianconi return err;
203fefb584dSLorenzo Bianconi
204fefb584dSLorenzo Bianconi mt76_worker_disable(&mdev->mt76.sdio.txrx_worker);
2056a618acbSLorenzo Bianconi mt76_worker_disable(&mdev->mt76.sdio.status_worker);
2066a618acbSLorenzo Bianconi mt76_worker_disable(&mdev->mt76.sdio.net_worker);
207*e89d025dSWang Zhao mt76_worker_disable(&mdev->mt76.sdio.stat_worker);
2086a618acbSLorenzo Bianconi
2096a618acbSLorenzo Bianconi clear_bit(MT76_READING_STATS, &mdev->mphy.state);
2106a618acbSLorenzo Bianconi
211c02f86eeSLorenzo Bianconi mt76_tx_status_check(&mdev->mt76, true);
212a66cbdd6SSean Wang
213fefb584dSLorenzo Bianconi return 0;
214a66cbdd6SSean Wang }
215a66cbdd6SSean Wang
mt7663s_resume(struct device * dev)216a66cbdd6SSean Wang static int mt7663s_resume(struct device *dev)
217a66cbdd6SSean Wang {
218a66cbdd6SSean Wang struct sdio_func *func = dev_to_sdio_func(dev);
219a66cbdd6SSean Wang struct mt7615_dev *mdev = sdio_get_drvdata(func);
220a66cbdd6SSean Wang int err;
221a66cbdd6SSean Wang
222fefb584dSLorenzo Bianconi mt76_worker_enable(&mdev->mt76.sdio.txrx_worker);
2236a618acbSLorenzo Bianconi mt76_worker_enable(&mdev->mt76.sdio.status_worker);
2246a618acbSLorenzo Bianconi mt76_worker_enable(&mdev->mt76.sdio.net_worker);
225fefb584dSLorenzo Bianconi
226d6e08f2bSLorenzo Bianconi err = mt7615_mcu_set_drv_ctrl(mdev);
227a66cbdd6SSean Wang if (err)
228a66cbdd6SSean Wang return err;
229a66cbdd6SSean Wang
230a66cbdd6SSean Wang if (!test_bit(MT76_STATE_SUSPEND, &mdev->mphy.state) &&
231a66cbdd6SSean Wang mt7615_firmware_offload(mdev))
23255d4c19cSLorenzo Bianconi err = mt76_connac_mcu_set_hif_suspend(&mdev->mt76, false);
233a66cbdd6SSean Wang
234a66cbdd6SSean Wang return err;
235a66cbdd6SSean Wang }
236a66cbdd6SSean Wang
237a66cbdd6SSean Wang MODULE_DEVICE_TABLE(sdio, mt7663s_table);
238a66cbdd6SSean Wang MODULE_FIRMWARE(MT7663_OFFLOAD_FIRMWARE_N9);
239a66cbdd6SSean Wang MODULE_FIRMWARE(MT7663_OFFLOAD_ROM_PATCH);
240a66cbdd6SSean Wang MODULE_FIRMWARE(MT7663_FIRMWARE_N9);
241a66cbdd6SSean Wang MODULE_FIRMWARE(MT7663_ROM_PATCH);
242a66cbdd6SSean Wang
243fb47c154SDeren Wu static DEFINE_SIMPLE_DEV_PM_OPS(mt7663s_pm_ops, mt7663s_suspend, mt7663s_resume);
244fb47c154SDeren Wu
245a66cbdd6SSean Wang static struct sdio_driver mt7663s_driver = {
246a66cbdd6SSean Wang .name = KBUILD_MODNAME,
247a66cbdd6SSean Wang .probe = mt7663s_probe,
248a66cbdd6SSean Wang .remove = mt7663s_remove,
249a66cbdd6SSean Wang .id_table = mt7663s_table,
250fb47c154SDeren Wu .drv.pm = pm_sleep_ptr(&mt7663s_pm_ops),
251a66cbdd6SSean Wang };
252a66cbdd6SSean Wang module_sdio_driver(mt7663s_driver);
253a66cbdd6SSean Wang
254a66cbdd6SSean Wang MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
255a66cbdd6SSean Wang MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>");
256a66cbdd6SSean Wang MODULE_LICENSE("Dual BSD/GPL");
257