Path: blob/main/sys/contrib/dev/mediatek/mt76/mt76x02_usb_mcu.c
48378 views
// SPDX-License-Identifier: ISC1/*2* Copyright (C) 2018 Lorenzo Bianconi <[email protected]>3*/45#include <linux/module.h>6#include <linux/firmware.h>78#include "mt76x02.h"9#include "mt76x02_mcu.h"10#include "mt76x02_usb.h"1112#define MT_CMD_HDR_LEN 41314#define MT_FCE_DMA_ADDR 0x023015#define MT_FCE_DMA_LEN 0x02341617#define MT_TX_CPU_FROM_FCE_CPU_DESC_IDX 0x09a81819static void20mt76x02u_multiple_mcu_reads(struct mt76_dev *dev, u8 *data, int len)21{22struct mt76_usb *usb = &dev->usb;23int i;2425WARN_ON_ONCE(len / 8 != usb->mcu.rp_len);2627for (i = 0; i < usb->mcu.rp_len; i++) {28u32 reg = get_unaligned_le32(data + 8 * i) - usb->mcu.base;29u32 val = get_unaligned_le32(data + 8 * i + 4);3031WARN_ON_ONCE(usb->mcu.rp[i].reg != reg);32usb->mcu.rp[i].value = val;33}34}3536static int mt76x02u_mcu_wait_resp(struct mt76_dev *dev, u8 seq)37{38struct mt76_usb *usb = &dev->usb;39u8 *data = usb->mcu.data;40int i, len, ret;41u32 rxfce;4243for (i = 0; i < 5; i++) {44ret = mt76u_bulk_msg(dev, data, MCU_RESP_URB_SIZE, &len,45300, MT_EP_IN_CMD_RESP);46if (ret == -ETIMEDOUT)47continue;48if (ret)49goto out;5051if (usb->mcu.rp)52mt76x02u_multiple_mcu_reads(dev, data + 4, len - 8);5354rxfce = get_unaligned_le32(data);55if (seq == FIELD_GET(MT_RX_FCE_INFO_CMD_SEQ, rxfce) &&56FIELD_GET(MT_RX_FCE_INFO_EVT_TYPE, rxfce) == EVT_CMD_DONE)57return 0;5859dev_err(dev->dev, "error: MCU resp evt:%lx seq:%hhx-%lx\n",60FIELD_GET(MT_RX_FCE_INFO_EVT_TYPE, rxfce),61seq, FIELD_GET(MT_RX_FCE_INFO_CMD_SEQ, rxfce));62}63out:64dev_err(dev->dev, "error: %s failed with %d\n", __func__, ret);65return ret;66}6768static int69__mt76x02u_mcu_send_msg(struct mt76_dev *dev, struct sk_buff *skb,70int cmd, bool wait_resp)71{72u8 seq = 0;73u32 info;74int ret;7576if (test_bit(MT76_REMOVED, &dev->phy.state)) {77ret = 0;78goto out;79}8081if (wait_resp) {82seq = ++dev->mcu.msg_seq & 0xf;83if (!seq)84seq = ++dev->mcu.msg_seq & 0xf;85}8687info = FIELD_PREP(MT_MCU_MSG_CMD_SEQ, seq) |88FIELD_PREP(MT_MCU_MSG_CMD_TYPE, cmd) |89MT_MCU_MSG_TYPE_CMD;90ret = mt76x02u_skb_dma_info(skb, CPU_TX_PORT, info);91if (ret)92return ret;9394ret = mt76u_bulk_msg(dev, skb->data, skb->len, NULL, 500,95MT_EP_OUT_INBAND_CMD);96if (ret)97goto out;9899if (wait_resp)100ret = mt76x02u_mcu_wait_resp(dev, seq);101102out:103consume_skb(skb);104105return ret;106}107108static int109mt76x02u_mcu_send_msg(struct mt76_dev *dev, int cmd, const void *data,110int len, bool wait_resp)111{112struct sk_buff *skb;113int err;114115skb = mt76_mcu_msg_alloc(dev, data, len);116if (!skb)117return -ENOMEM;118119mutex_lock(&dev->mcu.mutex);120err = __mt76x02u_mcu_send_msg(dev, skb, cmd, wait_resp);121mutex_unlock(&dev->mcu.mutex);122123return err;124}125126static inline void skb_put_le32(struct sk_buff *skb, u32 val)127{128put_unaligned_le32(val, skb_put(skb, 4));129}130131static int132mt76x02u_mcu_wr_rp(struct mt76_dev *dev, u32 base,133const struct mt76_reg_pair *data, int n)134{135const int max_vals_per_cmd = MT_INBAND_PACKET_MAX_LEN / 8;136const int CMD_RANDOM_WRITE = 12;137struct sk_buff *skb;138int cnt, i, ret;139140if (!n)141return 0;142143cnt = min(max_vals_per_cmd, n);144145skb = alloc_skb(cnt * 8 + MT_DMA_HDR_LEN + 4, GFP_KERNEL);146if (!skb)147return -ENOMEM;148skb_reserve(skb, MT_DMA_HDR_LEN);149150for (i = 0; i < cnt; i++) {151skb_put_le32(skb, base + data[i].reg);152skb_put_le32(skb, data[i].value);153}154155mutex_lock(&dev->mcu.mutex);156ret = __mt76x02u_mcu_send_msg(dev, skb, CMD_RANDOM_WRITE, cnt == n);157mutex_unlock(&dev->mcu.mutex);158if (ret)159return ret;160161return mt76x02u_mcu_wr_rp(dev, base, data + cnt, n - cnt);162}163164static int165mt76x02u_mcu_rd_rp(struct mt76_dev *dev, u32 base,166struct mt76_reg_pair *data, int n)167{168const int CMD_RANDOM_READ = 10;169const int max_vals_per_cmd = MT_INBAND_PACKET_MAX_LEN / 8;170struct mt76_usb *usb = &dev->usb;171struct sk_buff *skb;172int cnt, i, ret;173174if (!n)175return 0;176177cnt = min(max_vals_per_cmd, n);178if (cnt != n)179return -EINVAL;180181skb = alloc_skb(cnt * 8 + MT_DMA_HDR_LEN + 4, GFP_KERNEL);182if (!skb)183return -ENOMEM;184skb_reserve(skb, MT_DMA_HDR_LEN);185186for (i = 0; i < cnt; i++) {187skb_put_le32(skb, base + data[i].reg);188skb_put_le32(skb, data[i].value);189}190191mutex_lock(&dev->mcu.mutex);192193usb->mcu.rp = data;194usb->mcu.rp_len = n;195usb->mcu.base = base;196197ret = __mt76x02u_mcu_send_msg(dev, skb, CMD_RANDOM_READ, true);198199usb->mcu.rp = NULL;200201mutex_unlock(&dev->mcu.mutex);202203return ret;204}205206void mt76x02u_mcu_fw_reset(struct mt76x02_dev *dev)207{208mt76u_vendor_request(&dev->mt76, MT_VEND_DEV_MODE,209USB_DIR_OUT | USB_TYPE_VENDOR,2100x1, 0, NULL, 0);211}212EXPORT_SYMBOL_GPL(mt76x02u_mcu_fw_reset);213214static int215__mt76x02u_mcu_fw_send_data(struct mt76x02_dev *dev, u8 *data,216const void *fw_data, int len, u32 dst_addr)217{218__le32 info;219u32 val;220int err, data_len;221222info = cpu_to_le32(FIELD_PREP(MT_MCU_MSG_PORT, CPU_TX_PORT) |223FIELD_PREP(MT_MCU_MSG_LEN, len) |224MT_MCU_MSG_TYPE_CMD);225226memcpy(data, &info, sizeof(info));227memcpy(data + sizeof(info), fw_data, len);228memset(data + sizeof(info) + len, 0, 4);229230mt76u_single_wr(&dev->mt76, MT_VEND_WRITE_FCE,231MT_FCE_DMA_ADDR, dst_addr);232len = roundup(len, 4);233mt76u_single_wr(&dev->mt76, MT_VEND_WRITE_FCE,234MT_FCE_DMA_LEN, len << 16);235236data_len = MT_CMD_HDR_LEN + len + sizeof(info);237238err = mt76u_bulk_msg(&dev->mt76, data, data_len, NULL, 1000,239MT_EP_OUT_INBAND_CMD);240if (err) {241dev_err(dev->mt76.dev, "firmware upload failed: %d\n", err);242return err;243}244245val = mt76_rr(dev, MT_TX_CPU_FROM_FCE_CPU_DESC_IDX);246val++;247mt76_wr(dev, MT_TX_CPU_FROM_FCE_CPU_DESC_IDX, val);248249return 0;250}251252int mt76x02u_mcu_fw_send_data(struct mt76x02_dev *dev, const void *data,253int data_len, u32 max_payload, u32 offset)254{255int len, err = 0, pos = 0, max_len = max_payload - 8;256u8 *buf;257258buf = kmalloc(max_payload, GFP_KERNEL);259if (!buf)260return -ENOMEM;261262while (data_len > 0) {263len = min_t(int, data_len, max_len);264err = __mt76x02u_mcu_fw_send_data(dev, buf, data + pos,265len, offset + pos);266if (err < 0)267break;268269data_len -= len;270pos += len;271usleep_range(5000, 10000);272}273kfree(buf);274275return err;276}277EXPORT_SYMBOL_GPL(mt76x02u_mcu_fw_send_data);278279void mt76x02u_init_mcu(struct mt76_dev *dev)280{281static const struct mt76_mcu_ops mt76x02u_mcu_ops = {282.headroom = MT_CMD_HDR_LEN,283.tailroom = 8,284.mcu_send_msg = mt76x02u_mcu_send_msg,285.mcu_parse_response = mt76x02_mcu_parse_response,286.mcu_wr_rp = mt76x02u_mcu_wr_rp,287.mcu_rd_rp = mt76x02u_mcu_rd_rp,288};289290dev->mcu_ops = &mt76x02u_mcu_ops;291}292EXPORT_SYMBOL_GPL(mt76x02u_init_mcu);293294MODULE_AUTHOR("Lorenzo Bianconi <[email protected]>");295MODULE_DESCRIPTION("MediaTek MT76x02 MCU helpers");296MODULE_LICENSE("Dual BSD/GPL");297298299