brintos

brintos / linux-shallow public Read only

0
0
Text · 8.1 KiB · 95f526f Raw
332 lines · c
1// SPDX-License-Identifier: ISC2/* Copyright (C) 2021 MediaTek Inc.3 *4 */5 6#include <linux/kernel.h>7#include <linux/iopoll.h>8#include <linux/module.h>9 10#include <linux/mmc/host.h>11#include <linux/mmc/sdio_ids.h>12#include <linux/mmc/sdio_func.h>13 14#include "mt7921.h"15#include "../sdio.h"16#include "../mt76_connac2_mac.h"17#include "mcu.h"18 19static const struct sdio_device_id mt7921s_table[] = {20	{ SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7901),21		.driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM },22	{ }	/* Terminating entry */23};24 25static void mt7921s_txrx_worker(struct mt76_worker *w)26{27	struct mt76_sdio *sdio = container_of(w, struct mt76_sdio,28					      txrx_worker);29	struct mt76_dev *mdev = container_of(sdio, struct mt76_dev, sdio);30	struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);31 32	if (!mt76_connac_pm_ref(&dev->mphy, &dev->pm)) {33		queue_work(mdev->wq, &dev->pm.wake_work);34		return;35	}36 37	mt76s_txrx_worker(sdio);38	mt76_connac_pm_unref(&dev->mphy, &dev->pm);39}40 41static void mt7921s_unregister_device(struct mt792x_dev *dev)42{43	struct mt76_connac_pm *pm = &dev->pm;44 45	cancel_work_sync(&dev->init_work);46	mt76_unregister_device(&dev->mt76);47	cancel_delayed_work_sync(&pm->ps_work);48	cancel_work_sync(&pm->wake_work);49 50	mt76s_deinit(&dev->mt76);51	mt7921s_wfsys_reset(dev);52	skb_queue_purge(&dev->mt76.mcu.res_q);53 54	mt76_free_device(&dev->mt76);55}56 57static int mt7921s_parse_intr(struct mt76_dev *dev, struct mt76s_intr *intr)58{59	struct mt76_sdio *sdio = &dev->sdio;60	struct mt7921_sdio_intr *irq_data = sdio->intr_data;61	int i, err;62 63	sdio_claim_host(sdio->func);64	err = sdio_readsb(sdio->func, irq_data, MCR_WHISR, sizeof(*irq_data));65	sdio_release_host(sdio->func);66 67	if (err < 0)68		return err;69 70	if (irq_data->rx.num[0] > 16 ||71	    irq_data->rx.num[1] > 128)72		return -EINVAL;73 74	intr->isr = irq_data->isr;75	intr->rec_mb = irq_data->rec_mb;76	intr->tx.wtqcr = irq_data->tx.wtqcr;77	intr->rx.num = irq_data->rx.num;78	for (i = 0; i < 2 ; i++) {79		if (!i)80			intr->rx.len[0] = irq_data->rx.len0;81		else82			intr->rx.len[1] = irq_data->rx.len1;83	}84 85	return 0;86}87 88static int mt7921s_probe(struct sdio_func *func,89			 const struct sdio_device_id *id)90{91	static const struct mt76_driver_ops drv_ops = {92		.txwi_size = MT_SDIO_TXD_SIZE,93		.drv_flags = MT_DRV_AMSDU_OFFLOAD,94		.survey_flags = SURVEY_INFO_TIME_TX |95				SURVEY_INFO_TIME_RX |96				SURVEY_INFO_TIME_BSS_RX,97		.tx_prepare_skb = mt7921_usb_sdio_tx_prepare_skb,98		.tx_complete_skb = mt7921_usb_sdio_tx_complete_skb,99		.tx_status_data = mt7921_usb_sdio_tx_status_data,100		.rx_skb = mt7921_queue_rx_skb,101		.rx_check = mt7921_rx_check,102		.sta_add = mt7921_mac_sta_add,103		.sta_event = mt7921_mac_sta_event,104		.sta_remove = mt7921_mac_sta_remove,105		.update_survey = mt792x_update_channel,106		.set_channel = mt7921_set_channel,107	};108	static const struct mt76_bus_ops mt7921s_ops = {109		.rr = mt76s_rr,110		.rmw = mt76s_rmw,111		.wr = mt76s_wr,112		.write_copy = mt76s_write_copy,113		.read_copy = mt76s_read_copy,114		.wr_rp = mt76s_wr_rp,115		.rd_rp = mt76s_rd_rp,116		.type = MT76_BUS_SDIO,117	};118	static const struct mt792x_hif_ops mt7921_sdio_ops = {119		.init_reset = mt7921s_init_reset,120		.reset = mt7921s_mac_reset,121		.mcu_init = mt7921s_mcu_init,122		.drv_own = mt7921s_mcu_drv_pmctrl,123		.fw_own = mt7921s_mcu_fw_pmctrl,124	};125	struct ieee80211_ops *ops;126	struct mt792x_dev *dev;127	struct mt76_dev *mdev;128	u8 features;129	int ret;130 131	ops = mt792x_get_mac80211_ops(&func->dev, &mt7921_ops,132				      (void *)id->driver_data, &features);133	if (!ops)134		return -ENOMEM;135 136	mdev = mt76_alloc_device(&func->dev, sizeof(*dev), ops, &drv_ops);137	if (!mdev)138		return -ENOMEM;139 140	dev = container_of(mdev, struct mt792x_dev, mt76);141	dev->fw_features = features;142	dev->hif_ops = &mt7921_sdio_ops;143	sdio_set_drvdata(func, dev);144 145	ret = mt76s_init(mdev, func, &mt7921s_ops);146	if (ret < 0)147		goto error;148 149	ret = mt76s_hw_init(mdev, func, MT76_CONNAC2_SDIO);150	if (ret)151		goto error;152 153	mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) |154		    (mt76_rr(dev, MT_HW_REV) & 0xff);155	dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev);156 157	mdev->sdio.parse_irq = mt7921s_parse_intr;158	mdev->sdio.intr_data = devm_kmalloc(mdev->dev,159					    sizeof(struct mt7921_sdio_intr),160					    GFP_KERNEL);161	if (!mdev->sdio.intr_data) {162		ret = -ENOMEM;163		goto error;164	}165 166	ret = mt76s_alloc_rx_queue(mdev, MT_RXQ_MAIN);167	if (ret)168		goto error;169 170	ret = mt76s_alloc_rx_queue(mdev, MT_RXQ_MCU);171	if (ret)172		goto error;173 174	ret = mt76s_alloc_tx(mdev);175	if (ret)176		goto error;177 178	ret = mt76_worker_setup(mt76_hw(dev), &mdev->sdio.txrx_worker,179				mt7921s_txrx_worker, "sdio-txrx");180	if (ret)181		goto error;182 183	sched_set_fifo_low(mdev->sdio.txrx_worker.task);184 185	ret = mt7921_register_device(dev);186	if (ret)187		goto error;188 189	return 0;190 191error:192	mt76s_deinit(&dev->mt76);193	mt76_free_device(&dev->mt76);194 195	return ret;196}197 198static void mt7921s_remove(struct sdio_func *func)199{200	struct mt792x_dev *dev = sdio_get_drvdata(func);201 202	mt7921s_unregister_device(dev);203}204 205static int mt7921s_suspend(struct device *__dev)206{207	struct sdio_func *func = dev_to_sdio_func(__dev);208	struct mt792x_dev *dev = sdio_get_drvdata(func);209	struct mt76_connac_pm *pm = &dev->pm;210	struct mt76_dev *mdev = &dev->mt76;211	int err;212 213	pm->suspended = true;214	set_bit(MT76_STATE_SUSPEND, &mdev->phy.state);215 216	flush_work(&dev->reset_work);217	cancel_delayed_work_sync(&pm->ps_work);218	cancel_work_sync(&pm->wake_work);219 220	mt7921_roc_abort_sync(dev);221 222	err = mt792x_mcu_drv_pmctrl(dev);223	if (err < 0)224		goto restore_suspend;225 226	/* always enable deep sleep during suspend to reduce227	 * power consumption228	 */229	mt76_connac_mcu_set_deep_sleep(mdev, true);230 231	mt76_txq_schedule_all(&dev->mphy);232	mt76_worker_disable(&mdev->tx_worker);233	mt76_worker_disable(&mdev->sdio.status_worker);234	mt76_worker_disable(&mdev->sdio.stat_worker);235	clear_bit(MT76_READING_STATS, &dev->mphy.state);236	mt76_tx_status_check(mdev, true);237 238	mt76_worker_schedule(&mdev->sdio.txrx_worker);239	wait_event_timeout(dev->mt76.sdio.wait,240			   mt76s_txqs_empty(&dev->mt76), 5 * HZ);241 242	/* It is supposed that SDIO bus is idle at the point */243	err = mt76_connac_mcu_set_hif_suspend(mdev, true);244	if (err)245		goto restore_worker;246 247	mt76_worker_disable(&mdev->sdio.txrx_worker);248	mt76_worker_disable(&mdev->sdio.net_worker);249 250	err = mt792x_mcu_fw_pmctrl(dev);251	if (err)252		goto restore_txrx_worker;253 254	sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);255 256	return 0;257 258restore_txrx_worker:259	mt76_worker_enable(&mdev->sdio.net_worker);260	mt76_worker_enable(&mdev->sdio.txrx_worker);261	mt76_connac_mcu_set_hif_suspend(mdev, false);262 263restore_worker:264	mt76_worker_enable(&mdev->tx_worker);265	mt76_worker_enable(&mdev->sdio.status_worker);266	mt76_worker_enable(&mdev->sdio.stat_worker);267 268	if (!pm->ds_enable)269		mt76_connac_mcu_set_deep_sleep(mdev, false);270 271restore_suspend:272	clear_bit(MT76_STATE_SUSPEND, &mdev->phy.state);273	pm->suspended = false;274 275	if (err < 0)276		mt792x_reset(&dev->mt76);277 278	return err;279}280 281static int mt7921s_resume(struct device *__dev)282{283	struct sdio_func *func = dev_to_sdio_func(__dev);284	struct mt792x_dev *dev = sdio_get_drvdata(func);285	struct mt76_connac_pm *pm = &dev->pm;286	struct mt76_dev *mdev = &dev->mt76;287	int err;288 289	clear_bit(MT76_STATE_SUSPEND, &mdev->phy.state);290 291	err = mt792x_mcu_drv_pmctrl(dev);292	if (err < 0)293		goto failed;294 295	mt76_worker_enable(&mdev->tx_worker);296	mt76_worker_enable(&mdev->sdio.txrx_worker);297	mt76_worker_enable(&mdev->sdio.status_worker);298	mt76_worker_enable(&mdev->sdio.net_worker);299	mt76_worker_enable(&mdev->sdio.stat_worker);300 301	/* restore previous ds setting */302	if (!pm->ds_enable)303		mt76_connac_mcu_set_deep_sleep(mdev, false);304 305	err = mt76_connac_mcu_set_hif_suspend(mdev, false);306failed:307	pm->suspended = false;308 309	if (err < 0)310		mt792x_reset(&dev->mt76);311 312	return err;313}314 315MODULE_DEVICE_TABLE(sdio, mt7921s_table);316MODULE_FIRMWARE(MT7921_FIRMWARE_WM);317MODULE_FIRMWARE(MT7921_ROM_PATCH);318 319static DEFINE_SIMPLE_DEV_PM_OPS(mt7921s_pm_ops, mt7921s_suspend, mt7921s_resume);320 321static struct sdio_driver mt7921s_driver = {322	.name		= KBUILD_MODNAME,323	.probe		= mt7921s_probe,324	.remove		= mt7921s_remove,325	.id_table	= mt7921s_table,326	.drv.pm		= pm_sleep_ptr(&mt7921s_pm_ops),327};328module_sdio_driver(mt7921s_driver);329MODULE_DESCRIPTION("MediaTek MT7921S (SDIO) wireless driver");330MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");331MODULE_LICENSE("Dual BSD/GPL");332