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 28f16f700dSLorenzo Bianconi mt76x0u_upload_firmware(struct mt76x0_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); 43f16f700dSLorenzo Bianconi err = mt76x02u_mcu_fw_send_data(&dev->mt76, 44f16f700dSLorenzo Bianconi fw_payload + MT_MCU_IVB_SIZE, 45f16f700dSLorenzo Bianconi ilm_len, MCU_FW_URB_MAX_PAYLOAD, 46f16f700dSLorenzo Bianconi MT_MCU_IVB_SIZE); 47f16f700dSLorenzo Bianconi if (err) 48f16f700dSLorenzo Bianconi goto out; 49f16f700dSLorenzo Bianconi 50f16f700dSLorenzo Bianconi dlm_len = le32_to_cpu(hdr->dlm_len); 51f16f700dSLorenzo Bianconi dev_dbg(dev->mt76.dev, "loading FW - DLM %u\n", dlm_len); 52f16f700dSLorenzo Bianconi err = mt76x02u_mcu_fw_send_data(&dev->mt76, 53f16f700dSLorenzo Bianconi fw_payload + le32_to_cpu(hdr->ilm_len), 54f16f700dSLorenzo Bianconi dlm_len, MCU_FW_URB_MAX_PAYLOAD, 55f16f700dSLorenzo Bianconi MT_MCU_DLM_OFFSET); 56f16f700dSLorenzo Bianconi if (err) 57f16f700dSLorenzo Bianconi goto out; 58f16f700dSLorenzo Bianconi 59f16f700dSLorenzo Bianconi err = mt76u_vendor_request(&dev->mt76, MT_VEND_DEV_MODE, 60f16f700dSLorenzo Bianconi USB_DIR_OUT | USB_TYPE_VENDOR, 61f16f700dSLorenzo Bianconi 0x12, 0, ivb, MT_MCU_IVB_SIZE); 62f16f700dSLorenzo Bianconi if (err < 0) 63f16f700dSLorenzo Bianconi goto out; 64f16f700dSLorenzo Bianconi 65f16f700dSLorenzo Bianconi if (!mt76_poll_msec(dev, MT_MCU_COM_REG0, 1, 1, 1000)) { 66f16f700dSLorenzo Bianconi dev_err(dev->mt76.dev, "Firmware failed to start\n"); 67f16f700dSLorenzo Bianconi err = -ETIMEDOUT; 68f16f700dSLorenzo Bianconi goto out; 69f16f700dSLorenzo Bianconi } 70f16f700dSLorenzo Bianconi 71f16f700dSLorenzo Bianconi dev_dbg(dev->mt76.dev, "Firmware running!\n"); 72f16f700dSLorenzo Bianconi 73f16f700dSLorenzo Bianconi out: 74f16f700dSLorenzo Bianconi kfree(ivb); 75f16f700dSLorenzo Bianconi 76f16f700dSLorenzo Bianconi return err; 77f16f700dSLorenzo Bianconi } 78f16f700dSLorenzo Bianconi 79f16f700dSLorenzo Bianconi static int mt76x0u_load_firmware(struct mt76x0_dev *dev) 80f16f700dSLorenzo Bianconi { 81f16f700dSLorenzo Bianconi const struct firmware *fw; 82f16f700dSLorenzo Bianconi const struct mt76x02_fw_header *hdr; 83f16f700dSLorenzo Bianconi int len, ret; 84f16f700dSLorenzo Bianconi u32 val; 85f16f700dSLorenzo Bianconi 86f16f700dSLorenzo Bianconi mt76_wr(dev, MT_USB_DMA_CFG, (MT_USB_DMA_CFG_RX_BULK_EN | 87f16f700dSLorenzo Bianconi MT_USB_DMA_CFG_TX_BULK_EN)); 88f16f700dSLorenzo Bianconi 89f16f700dSLorenzo Bianconi if (mt76x0_firmware_running(dev)) 90f16f700dSLorenzo Bianconi return 0; 91f16f700dSLorenzo Bianconi 92f16f700dSLorenzo Bianconi ret = request_firmware(&fw, MT7610U_FIRMWARE, dev->mt76.dev); 93f16f700dSLorenzo Bianconi if (ret) 94f16f700dSLorenzo Bianconi return ret; 95f16f700dSLorenzo Bianconi 96f16f700dSLorenzo Bianconi if (!fw || !fw->data || fw->size < sizeof(*hdr)) 97f16f700dSLorenzo Bianconi goto err_inv_fw; 98f16f700dSLorenzo Bianconi 99f16f700dSLorenzo Bianconi hdr = (const struct mt76x02_fw_header *)fw->data; 100f16f700dSLorenzo Bianconi 101f16f700dSLorenzo Bianconi if (le32_to_cpu(hdr->ilm_len) <= MT_MCU_IVB_SIZE) 102f16f700dSLorenzo Bianconi goto err_inv_fw; 103f16f700dSLorenzo Bianconi 104f16f700dSLorenzo Bianconi len = sizeof(*hdr); 105f16f700dSLorenzo Bianconi len += le32_to_cpu(hdr->ilm_len); 106f16f700dSLorenzo Bianconi len += le32_to_cpu(hdr->dlm_len); 107f16f700dSLorenzo Bianconi 108f16f700dSLorenzo Bianconi if (fw->size != len) 109f16f700dSLorenzo Bianconi goto err_inv_fw; 110f16f700dSLorenzo Bianconi 111f16f700dSLorenzo Bianconi val = le16_to_cpu(hdr->fw_ver); 112f16f700dSLorenzo Bianconi dev_dbg(dev->mt76.dev, 113f16f700dSLorenzo Bianconi "Firmware Version: %d.%d.%02d Build: %x Build time: %.16s\n", 114f16f700dSLorenzo Bianconi (val >> 12) & 0xf, (val >> 8) & 0xf, val & 0xf, 115f16f700dSLorenzo Bianconi le16_to_cpu(hdr->build_ver), hdr->build_time); 116f16f700dSLorenzo Bianconi 117f16f700dSLorenzo Bianconi len = le32_to_cpu(hdr->ilm_len); 118f16f700dSLorenzo Bianconi 119f16f700dSLorenzo Bianconi mt76_wr(dev, 0x1004, 0x2c); 120f16f700dSLorenzo Bianconi 121f16f700dSLorenzo Bianconi mt76_set(dev, MT_USB_DMA_CFG, 122f16f700dSLorenzo Bianconi (MT_USB_DMA_CFG_RX_BULK_EN | MT_USB_DMA_CFG_TX_BULK_EN) | 123f16f700dSLorenzo Bianconi FIELD_PREP(MT_USB_DMA_CFG_RX_BULK_AGG_TOUT, 0x20)); 124f16f700dSLorenzo Bianconi mt76x02u_mcu_fw_reset(&dev->mt76); 125f16f700dSLorenzo Bianconi usleep_range(5000, 6000); 126f16f700dSLorenzo Bianconi /* 127f16f700dSLorenzo Bianconi mt76x0_rmw(dev, MT_PBF_CFG, 0, (MT_PBF_CFG_TX0Q_EN | 128f16f700dSLorenzo Bianconi MT_PBF_CFG_TX1Q_EN | 129f16f700dSLorenzo Bianconi MT_PBF_CFG_TX2Q_EN | 130f16f700dSLorenzo Bianconi MT_PBF_CFG_TX3Q_EN)); 131f16f700dSLorenzo Bianconi */ 132f16f700dSLorenzo Bianconi 133f16f700dSLorenzo Bianconi mt76_wr(dev, MT_FCE_PSE_CTRL, 1); 134f16f700dSLorenzo Bianconi 135f16f700dSLorenzo Bianconi /* FCE tx_fs_base_ptr */ 136f16f700dSLorenzo Bianconi mt76_wr(dev, MT_TX_CPU_FROM_FCE_BASE_PTR, 0x400230); 137f16f700dSLorenzo Bianconi /* FCE tx_fs_max_cnt */ 138f16f700dSLorenzo Bianconi mt76_wr(dev, MT_TX_CPU_FROM_FCE_MAX_COUNT, 1); 139f16f700dSLorenzo Bianconi /* FCE pdma enable */ 140f16f700dSLorenzo Bianconi mt76_wr(dev, MT_FCE_PDMA_GLOBAL_CONF, 0x44); 141f16f700dSLorenzo Bianconi /* FCE skip_fs_en */ 142f16f700dSLorenzo Bianconi mt76_wr(dev, MT_FCE_SKIP_FS, 3); 143f16f700dSLorenzo Bianconi 144f16f700dSLorenzo Bianconi val = mt76_rr(dev, MT_USB_DMA_CFG); 145f16f700dSLorenzo Bianconi val |= MT_USB_DMA_CFG_UDMA_TX_WL_DROP; 146f16f700dSLorenzo Bianconi mt76_wr(dev, MT_USB_DMA_CFG, val); 147f16f700dSLorenzo Bianconi val &= ~MT_USB_DMA_CFG_UDMA_TX_WL_DROP; 148f16f700dSLorenzo Bianconi mt76_wr(dev, MT_USB_DMA_CFG, val); 149f16f700dSLorenzo Bianconi 150f16f700dSLorenzo Bianconi ret = mt76x0u_upload_firmware(dev, hdr); 151f16f700dSLorenzo Bianconi release_firmware(fw); 152f16f700dSLorenzo Bianconi 153f16f700dSLorenzo Bianconi mt76_wr(dev, MT_FCE_PSE_CTRL, 1); 154f16f700dSLorenzo Bianconi 155f16f700dSLorenzo Bianconi return ret; 156f16f700dSLorenzo Bianconi 157f16f700dSLorenzo Bianconi err_inv_fw: 158f16f700dSLorenzo Bianconi dev_err(dev->mt76.dev, "Invalid firmware image\n"); 159f16f700dSLorenzo Bianconi release_firmware(fw); 160f16f700dSLorenzo Bianconi return -ENOENT; 161f16f700dSLorenzo Bianconi } 162f16f700dSLorenzo Bianconi 163f16f700dSLorenzo Bianconi int mt76x0u_mcu_init(struct mt76x0_dev *dev) 164f16f700dSLorenzo Bianconi { 165f16f700dSLorenzo Bianconi int ret; 166f16f700dSLorenzo Bianconi 167f16f700dSLorenzo Bianconi ret = mt76x0u_load_firmware(dev); 168f16f700dSLorenzo Bianconi if (ret < 0) 169f16f700dSLorenzo Bianconi return ret; 170f16f700dSLorenzo Bianconi 171f16f700dSLorenzo Bianconi set_bit(MT76_STATE_MCU_RUNNING, &dev->mt76.state); 172f16f700dSLorenzo Bianconi 173f16f700dSLorenzo Bianconi return 0; 174f16f700dSLorenzo Bianconi } 175f16f700dSLorenzo Bianconi 176f16f700dSLorenzo Bianconi MODULE_FIRMWARE(MT7610U_FIRMWARE); 177