1f16f700dSLorenzo Bianconi /* 2f16f700dSLorenzo Bianconi * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> 3f16f700dSLorenzo Bianconi * 4f16f700dSLorenzo Bianconi * Permission to use, copy, modify, and/or distribute this software for any 5f16f700dSLorenzo Bianconi * purpose with or without fee is hereby granted, provided that the above 6f16f700dSLorenzo Bianconi * copyright notice and this permission notice appear in all copies. 7f16f700dSLorenzo Bianconi * 8f16f700dSLorenzo Bianconi * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9f16f700dSLorenzo Bianconi * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10f16f700dSLorenzo Bianconi * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11f16f700dSLorenzo Bianconi * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12f16f700dSLorenzo Bianconi * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13f16f700dSLorenzo Bianconi * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14f16f700dSLorenzo Bianconi * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15f16f700dSLorenzo Bianconi */ 16f16f700dSLorenzo Bianconi #include <linux/kernel.h> 17f16f700dSLorenzo Bianconi #include <linux/firmware.h> 18f16f700dSLorenzo Bianconi 19f16f700dSLorenzo Bianconi #include "mt76x0.h" 20f16f700dSLorenzo Bianconi #include "mcu.h" 21f16f700dSLorenzo Bianconi #include "../mt76x02_usb.h" 22f16f700dSLorenzo Bianconi 23f16f700dSLorenzo Bianconi #define MCU_FW_URB_MAX_PAYLOAD 0x38f8 24f16f700dSLorenzo Bianconi #define MCU_FW_URB_SIZE (MCU_FW_URB_MAX_PAYLOAD + 12) 25f16f700dSLorenzo Bianconi #define MT7610U_FIRMWARE "mediatek/mt7610u.bin" 26f16f700dSLorenzo Bianconi 27f16f700dSLorenzo Bianconi static int 28b2d871c0SLorenzo Bianconi mt76x0u_upload_firmware(struct mt76x02_dev *dev, 29f16f700dSLorenzo Bianconi const struct mt76x02_fw_header *hdr) 30f16f700dSLorenzo Bianconi { 31f16f700dSLorenzo Bianconi u8 *fw_payload = (u8 *)(hdr + 1); 32f16f700dSLorenzo Bianconi u32 ilm_len, dlm_len; 33f16f700dSLorenzo Bianconi void *ivb; 34f16f700dSLorenzo Bianconi int err; 35f16f700dSLorenzo Bianconi 36f16f700dSLorenzo Bianconi ivb = kmemdup(fw_payload, MT_MCU_IVB_SIZE, GFP_KERNEL); 37f16f700dSLorenzo Bianconi if (!ivb) 38f16f700dSLorenzo Bianconi return -ENOMEM; 39f16f700dSLorenzo Bianconi 40f16f700dSLorenzo Bianconi ilm_len = le32_to_cpu(hdr->ilm_len) - MT_MCU_IVB_SIZE; 41f16f700dSLorenzo Bianconi dev_dbg(dev->mt76.dev, "loading FW - ILM %u + IVB %u\n", 42f16f700dSLorenzo Bianconi ilm_len, MT_MCU_IVB_SIZE); 4395c3e451SLorenzo Bianconi err = mt76x02u_mcu_fw_send_data(dev, fw_payload + MT_MCU_IVB_SIZE, 44f16f700dSLorenzo Bianconi ilm_len, MCU_FW_URB_MAX_PAYLOAD, 45f16f700dSLorenzo Bianconi MT_MCU_IVB_SIZE); 46f16f700dSLorenzo Bianconi if (err) 47f16f700dSLorenzo Bianconi goto out; 48f16f700dSLorenzo Bianconi 49f16f700dSLorenzo Bianconi dlm_len = le32_to_cpu(hdr->dlm_len); 50f16f700dSLorenzo Bianconi dev_dbg(dev->mt76.dev, "loading FW - DLM %u\n", dlm_len); 5195c3e451SLorenzo Bianconi err = mt76x02u_mcu_fw_send_data(dev, 52f16f700dSLorenzo Bianconi fw_payload + le32_to_cpu(hdr->ilm_len), 53f16f700dSLorenzo Bianconi dlm_len, MCU_FW_URB_MAX_PAYLOAD, 54f16f700dSLorenzo Bianconi MT_MCU_DLM_OFFSET); 55f16f700dSLorenzo Bianconi if (err) 56f16f700dSLorenzo Bianconi goto out; 57f16f700dSLorenzo Bianconi 58f16f700dSLorenzo Bianconi err = mt76u_vendor_request(&dev->mt76, MT_VEND_DEV_MODE, 59f16f700dSLorenzo Bianconi USB_DIR_OUT | USB_TYPE_VENDOR, 60f16f700dSLorenzo Bianconi 0x12, 0, ivb, MT_MCU_IVB_SIZE); 61f16f700dSLorenzo Bianconi if (err < 0) 62f16f700dSLorenzo Bianconi goto out; 63f16f700dSLorenzo Bianconi 64f16f700dSLorenzo Bianconi if (!mt76_poll_msec(dev, MT_MCU_COM_REG0, 1, 1, 1000)) { 65f16f700dSLorenzo Bianconi dev_err(dev->mt76.dev, "Firmware failed to start\n"); 66f16f700dSLorenzo Bianconi err = -ETIMEDOUT; 67f16f700dSLorenzo Bianconi goto out; 68f16f700dSLorenzo Bianconi } 69f16f700dSLorenzo Bianconi 70f16f700dSLorenzo Bianconi dev_dbg(dev->mt76.dev, "Firmware running!\n"); 71f16f700dSLorenzo Bianconi 72f16f700dSLorenzo Bianconi out: 73f16f700dSLorenzo Bianconi kfree(ivb); 74f16f700dSLorenzo Bianconi 75f16f700dSLorenzo Bianconi return err; 76f16f700dSLorenzo Bianconi } 77f16f700dSLorenzo Bianconi 78b2d871c0SLorenzo Bianconi static int mt76x0u_load_firmware(struct mt76x02_dev *dev) 79f16f700dSLorenzo Bianconi { 80f16f700dSLorenzo Bianconi const struct firmware *fw; 81f16f700dSLorenzo Bianconi const struct mt76x02_fw_header *hdr; 82f16f700dSLorenzo Bianconi int len, ret; 83f16f700dSLorenzo Bianconi u32 val; 84f16f700dSLorenzo Bianconi 85f16f700dSLorenzo Bianconi mt76_wr(dev, MT_USB_DMA_CFG, (MT_USB_DMA_CFG_RX_BULK_EN | 86f16f700dSLorenzo Bianconi MT_USB_DMA_CFG_TX_BULK_EN)); 87f16f700dSLorenzo Bianconi 88f16f700dSLorenzo Bianconi if (mt76x0_firmware_running(dev)) 89f16f700dSLorenzo Bianconi return 0; 90f16f700dSLorenzo Bianconi 91f16f700dSLorenzo Bianconi ret = request_firmware(&fw, MT7610U_FIRMWARE, dev->mt76.dev); 92f16f700dSLorenzo Bianconi if (ret) 93f16f700dSLorenzo Bianconi return ret; 94f16f700dSLorenzo Bianconi 95f16f700dSLorenzo Bianconi if (!fw || !fw->data || fw->size < sizeof(*hdr)) 96f16f700dSLorenzo Bianconi goto err_inv_fw; 97f16f700dSLorenzo Bianconi 98f16f700dSLorenzo Bianconi hdr = (const struct mt76x02_fw_header *)fw->data; 99f16f700dSLorenzo Bianconi 100f16f700dSLorenzo Bianconi if (le32_to_cpu(hdr->ilm_len) <= MT_MCU_IVB_SIZE) 101f16f700dSLorenzo Bianconi goto err_inv_fw; 102f16f700dSLorenzo Bianconi 103f16f700dSLorenzo Bianconi len = sizeof(*hdr); 104f16f700dSLorenzo Bianconi len += le32_to_cpu(hdr->ilm_len); 105f16f700dSLorenzo Bianconi len += le32_to_cpu(hdr->dlm_len); 106f16f700dSLorenzo Bianconi 107f16f700dSLorenzo Bianconi if (fw->size != len) 108f16f700dSLorenzo Bianconi goto err_inv_fw; 109f16f700dSLorenzo Bianconi 110f16f700dSLorenzo Bianconi val = le16_to_cpu(hdr->fw_ver); 111f16f700dSLorenzo Bianconi dev_dbg(dev->mt76.dev, 112f16f700dSLorenzo Bianconi "Firmware Version: %d.%d.%02d Build: %x Build time: %.16s\n", 113f16f700dSLorenzo Bianconi (val >> 12) & 0xf, (val >> 8) & 0xf, val & 0xf, 114f16f700dSLorenzo Bianconi le16_to_cpu(hdr->build_ver), hdr->build_time); 115f16f700dSLorenzo Bianconi 116f16f700dSLorenzo Bianconi len = le32_to_cpu(hdr->ilm_len); 117f16f700dSLorenzo Bianconi 118f16f700dSLorenzo Bianconi mt76_wr(dev, 0x1004, 0x2c); 119f16f700dSLorenzo Bianconi 120f16f700dSLorenzo Bianconi mt76_set(dev, MT_USB_DMA_CFG, 121f16f700dSLorenzo Bianconi (MT_USB_DMA_CFG_RX_BULK_EN | MT_USB_DMA_CFG_TX_BULK_EN) | 122f16f700dSLorenzo Bianconi FIELD_PREP(MT_USB_DMA_CFG_RX_BULK_AGG_TOUT, 0x20)); 12395c3e451SLorenzo Bianconi mt76x02u_mcu_fw_reset(dev); 124f16f700dSLorenzo Bianconi usleep_range(5000, 6000); 125f16f700dSLorenzo Bianconi /* 126f16f700dSLorenzo Bianconi mt76x0_rmw(dev, MT_PBF_CFG, 0, (MT_PBF_CFG_TX0Q_EN | 127f16f700dSLorenzo Bianconi MT_PBF_CFG_TX1Q_EN | 128f16f700dSLorenzo Bianconi MT_PBF_CFG_TX2Q_EN | 129f16f700dSLorenzo Bianconi MT_PBF_CFG_TX3Q_EN)); 130f16f700dSLorenzo Bianconi */ 131f16f700dSLorenzo Bianconi 132f16f700dSLorenzo Bianconi mt76_wr(dev, MT_FCE_PSE_CTRL, 1); 133f16f700dSLorenzo Bianconi 134f16f700dSLorenzo Bianconi /* FCE tx_fs_base_ptr */ 135f16f700dSLorenzo Bianconi mt76_wr(dev, MT_TX_CPU_FROM_FCE_BASE_PTR, 0x400230); 136f16f700dSLorenzo Bianconi /* FCE tx_fs_max_cnt */ 137f16f700dSLorenzo Bianconi mt76_wr(dev, MT_TX_CPU_FROM_FCE_MAX_COUNT, 1); 138f16f700dSLorenzo Bianconi /* FCE pdma enable */ 139f16f700dSLorenzo Bianconi mt76_wr(dev, MT_FCE_PDMA_GLOBAL_CONF, 0x44); 140f16f700dSLorenzo Bianconi /* FCE skip_fs_en */ 141f16f700dSLorenzo Bianconi mt76_wr(dev, MT_FCE_SKIP_FS, 3); 142f16f700dSLorenzo Bianconi 143f16f700dSLorenzo Bianconi val = mt76_rr(dev, MT_USB_DMA_CFG); 144f16f700dSLorenzo Bianconi val |= MT_USB_DMA_CFG_UDMA_TX_WL_DROP; 145f16f700dSLorenzo Bianconi mt76_wr(dev, MT_USB_DMA_CFG, val); 146f16f700dSLorenzo Bianconi val &= ~MT_USB_DMA_CFG_UDMA_TX_WL_DROP; 147f16f700dSLorenzo Bianconi mt76_wr(dev, MT_USB_DMA_CFG, val); 148f16f700dSLorenzo Bianconi 149f16f700dSLorenzo Bianconi ret = mt76x0u_upload_firmware(dev, hdr); 150f16f700dSLorenzo Bianconi release_firmware(fw); 151f16f700dSLorenzo Bianconi 152f16f700dSLorenzo Bianconi mt76_wr(dev, MT_FCE_PSE_CTRL, 1); 153f16f700dSLorenzo Bianconi 154f16f700dSLorenzo Bianconi return ret; 155f16f700dSLorenzo Bianconi 156f16f700dSLorenzo Bianconi err_inv_fw: 157f16f700dSLorenzo Bianconi dev_err(dev->mt76.dev, "Invalid firmware image\n"); 158f16f700dSLorenzo Bianconi release_firmware(fw); 159f16f700dSLorenzo Bianconi return -ENOENT; 160f16f700dSLorenzo Bianconi } 161f16f700dSLorenzo Bianconi 162b2d871c0SLorenzo Bianconi int mt76x0u_mcu_init(struct mt76x02_dev *dev) 163f16f700dSLorenzo Bianconi { 164f16f700dSLorenzo Bianconi int ret; 165f16f700dSLorenzo Bianconi 166f16f700dSLorenzo Bianconi ret = mt76x0u_load_firmware(dev); 167f16f700dSLorenzo Bianconi if (ret < 0) 168f16f700dSLorenzo Bianconi return ret; 169f16f700dSLorenzo Bianconi 170f16f700dSLorenzo Bianconi set_bit(MT76_STATE_MCU_RUNNING, &dev->mt76.state); 171f16f700dSLorenzo Bianconi 172f16f700dSLorenzo Bianconi return 0; 173f16f700dSLorenzo Bianconi } 174f16f700dSLorenzo Bianconi 175f16f700dSLorenzo Bianconi MODULE_FIRMWARE(MT7610U_FIRMWARE); 176