xref: /openbmc/linux/drivers/net/wireless/mediatek/mt76/mt7615/sdio.c (revision 1188f7f111c61394ec56beb8e30322305a8220b6)
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