diff mbox series

[RFC] wifi: mt76: mt7916: Support per-radio configuration 'firmware' file.

Message ID 20230123175500.3375542-1-carson.vandegriffe@candelatech.com (mailing list archive)
State RFC
Delegated to: Felix Fietkau
Headers show
Series [RFC] wifi: mt76: mt7916: Support per-radio configuration 'firmware' file. | expand

Commit Message

carson.vandegriffe@candelatech.com Jan. 23, 2023, 5:55 p.m. UTC
From: Carson Vandegriffe <carson.vandegriffe@candelatech.com>

This lets users specify the upper band that the 7916 radio should use.
Upon reboot, the 7916 will be using that upper band.

Example config file:

myhost@: cat /usr/lib/firmware/mediatek/fwcfg-mmio-0000\:04\:00.0.txt

high_band=6

Signed-off-by: Carson Vandegriffe <carson.vandegriffe@candelatech.com>
---
 This patch is against the 5.19.17+ kernel.
 drivers/net/wireless/mediatek/mt76/mt76.h     |  14 ++
 .../wireless/mediatek/mt76/mt7915/eeprom.c    | 164 ++++++++++++++++++
 .../net/wireless/mediatek/mt76/mt7915/init.c  |   3 +-
 .../wireless/mediatek/mt76/mt7915/mt7915.h    |  10 ++
 4 files changed, 190 insertions(+), 1 deletion(-)

Comments

Kalle Valo Feb. 7, 2023, 3:13 p.m. UTC | #1
carson.vandegriffe@candelatech.com writes:

> From: Carson Vandegriffe <carson.vandegriffe@candelatech.com>
>
> This lets users specify the upper band that the 7916 radio should use.
> Upon reboot, the 7916 will be using that upper band.
>
> Example config file:
>
> myhost@: cat /usr/lib/firmware/mediatek/fwcfg-mmio-0000\:04\:00.0.txt
>
> high_band=6
>
> Signed-off-by: Carson Vandegriffe <carson.vandegriffe@candelatech.com>

So this is basically an .ini file with settings for the driver? It's a
long standing request feature request how wireless drivers should handle
thatbut there's still no resolution. Having an ASCII parser in the
driver does not sound a good idea.
Ben Greear Feb. 7, 2023, 4:49 p.m. UTC | #2
On 2/7/23 07:13, Kalle Valo wrote:
> carson.vandegriffe@candelatech.com writes:
> 
>> From: Carson Vandegriffe <carson.vandegriffe@candelatech.com>
>>
>> This lets users specify the upper band that the 7916 radio should use.
>> Upon reboot, the 7916 will be using that upper band.
>>
>> Example config file:
>>
>> myhost@: cat /usr/lib/firmware/mediatek/fwcfg-mmio-0000\:04\:00.0.txt
>>
>> high_band=6
>>
>> Signed-off-by: Carson Vandegriffe <carson.vandegriffe@candelatech.com>
> 
> So this is basically an .ini file with settings for the driver? It's a
> long standing request feature request how wireless drivers should handle
> thatbut there's still no resolution. Having an ASCII parser in the
> driver does not sound a good idea.

It has been many years, no one has offered a better solution that I'm
aware of, so....how long to wait?

Perhaps the parsing logic could move to some helper methods higher in the stack and create a table of
key/value pairs to be used by drivers so that multiple drivers could more easily re-use the parsing
code?

Thanks,
Ben
diff mbox series

Patch

diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index 812c1eb8866a..2b1b730aeb2d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -961,6 +961,20 @@  struct mt76_sta_stats {
 	u32 rx_ampdu_len[15];
 };
 
+static inline const char *mt76_bus_str(enum mt76_bus_type bus)
+{
+	switch (bus) {
+	case MT76_BUS_MMIO:
+		return "mmio";
+	case MT76_BUS_USB:
+		return "usb";
+	case MT76_BUS_SDIO:
+		return "sdio";
+	}
+
+	return "unknown";
+}
+
 static inline
 void mt76_inc_ampdu_bucket(int ampdu_len, struct mt76_sta_stats *stats)
 {
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
index fdef6a3a6cb3..04edadcf5107 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
@@ -108,6 +108,28 @@  mt7915_eeprom_load_default(struct mt7915_dev *dev)
 	return ret;
 }
 
+static const struct firmware
+*mt7915_eeprom_load_file(struct mt7915_dev *dev, const char *dir, const char *file)
+{
+	char filename[100];
+	const struct firmware *fw = NULL;
+	int ret;
+
+	if (!file)
+		return ERR_PTR(-ENOENT);
+
+	if (!dir)
+		dir = ".";
+
+	snprintf(filename, sizeof(filename), "%s/%s", dir, file);
+	ret = request_firmware(&fw, filename, dev->mt76.dev);
+
+	if (ret)
+		return ERR_PTR(ret);
+
+	return fw;
+}
+
 static int mt7915_eeprom_load(struct mt7915_dev *dev)
 {
 	int ret;
@@ -139,6 +161,122 @@  static int mt7915_eeprom_load(struct mt7915_dev *dev)
 	return mt7915_check_eeprom(dev);
 }
 
+static int mt7915_fetch_fwcfg_file(struct mt7915_dev *dev)
+{
+	char filename[100];
+	const struct firmware *fw;
+	const char *buf;
+	size_t i = 0;
+	char val[100];
+	size_t key_idx;
+	size_t val_idx;
+	char c;
+	long t;
+
+	dev->fwcfg.flags = 0;
+
+	/* fwcfg-<bus>-<id>.txt */
+	scnprintf(filename, sizeof(filename), "fwcfg-%s-%s.txt",
+		  mt76_bus_str(dev->mt76.bus->type), dev_name(dev->mt76.dev));
+
+	fw = mt7915_eeprom_load_file(dev, MT7915_FIRMWARE_BD, filename);
+	if (IS_ERR(fw))
+		return PTR_ERR(fw);
+
+	/* Now, attempt to parse results.
+	 * Format is key=value
+	 */
+	buf = (const char *)(fw->data);
+	while (i < fw->size) {
+start_again:
+		/* First, eat space, or entire line if we have # as first char */
+		c = buf[i];
+		while (isspace(c)) {
+			i++;
+			if (i >= fw->size)
+				goto done;
+			c = buf[i];
+		}
+		/* Eat comment ? */
+		if (c == '#') {
+			i++;
+			while (i < fw->size) {
+				c = buf[i];
+				i++;
+				if (c == '\n')
+					goto start_again;
+			}
+			/* Found no newline, must be done. */
+			goto done;
+		}
+
+		/* If here, we have start of token, store it in 'filename' to save space */
+		key_idx = 0;
+		while (i < fw->size) {
+			c = buf[i];
+			if (c == '=') {
+				i++;
+				c = buf[i];
+				/* Eat any space after the '=' sign. */
+				while (i < fw->size) {
+					if (!isspace(c))
+						break;
+					i++;
+					c = buf[i];
+				}
+				break;
+			}
+			if (isspace(c)) {
+				i++;
+				continue;
+			}
+			filename[key_idx] = c;
+			key_idx++;
+			if (key_idx >= sizeof(filename)) {
+				/* Too long, bail out. */
+				goto done;
+			}
+			i++;
+		}
+		filename[key_idx] = 0; /* null terminate */
+
+		/* We have found the key, now find the value */
+		val_idx = 0;
+		while (i < fw->size) {
+			c = buf[i];
+			if (isspace(c))
+				break;
+			val[val_idx] = c;
+			val_idx++;
+			if (val_idx >= sizeof(val)) {
+				/* Too long, bail out. */
+				goto done;
+			}
+			i++;
+		}
+		val[val_idx] = 0; /* null terminate value */
+
+		/* We have key and value now. */
+		dev_warn(dev->mt76.dev, "fwcfg key: %s  val: %s\n",
+			 filename, val);
+
+		/* Assign key and values as appropriate */
+		if (strcasecmp(filename, "high_band") == 0) {
+			if (kstrtol(val, 0, &t) == 0) {
+				dev->fwcfg.high_band = t;
+				dev->fwcfg.flags |= MT7915_FWCFG_HIGH_BAND;
+			}
+		} else {
+			dev_warn(dev->mt76.dev, "Unknown fwcfg key name -:%s:-, val: %s\n",
+				 filename, val);
+		}
+	}
+
+done:
+	release_firmware(fw);
+	return 0;
+}
+
 static void mt7915_eeprom_parse_band_config(struct mt7915_phy *phy)
 {
 	struct mt7915_dev *dev = phy->dev;
@@ -149,6 +287,29 @@  static void mt7915_eeprom_parse_band_config(struct mt7915_phy *phy)
 	val = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
 
 	if (!is_mt7915(&dev->mt76)) {
+		/* fwcfg intervention to set upper band to 5GHz or 6GHz */
+		if ((dev->fwcfg.flags & MT7915_FWCFG_HIGH_BAND) &&
+		    val == MT_EE_V2_BAND_SEL_5GHZ_6GHZ) {
+			dev_info(dev->mt76.dev, "FWCFG: Overriding 7916 high_band with %luGHz\n",
+				 (unsigned long)dev->fwcfg.high_band);
+
+			if (dev->fwcfg.high_band == 5) {
+				u8p_replace_bits(&eeprom[MT_EE_WIFI_CONF + phy->band_idx],
+						 MT_EE_V2_BAND_SEL_5GHZ,
+						 MT_EE_WIFI_CONF0_BAND_SEL);
+			}
+			if (dev->fwcfg.high_band == 6) {
+				u8p_replace_bits(&eeprom[MT_EE_WIFI_CONF + phy->band_idx],
+						 MT_EE_V2_BAND_SEL_6GHZ,
+						 MT_EE_WIFI_CONF0_BAND_SEL);
+			}
+
+			/* force to buffer mode */
+			dev->flash_mode = true;
+			val = eeprom[MT_EE_WIFI_CONF + phy->band_idx];
+			val = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
+		}
+
 		switch (val) {
 		case MT_EE_V2_BAND_SEL_5GHZ:
 			phy->mt76->cap.has_5ghz = true;
@@ -270,6 +431,9 @@  int mt7915_eeprom_init(struct mt7915_dev *dev)
 {
 	int ret;
 
+	/* First, see if we have a special config file for this firmware */
+	mt7915_fetch_fwcfg_file(dev);
+
 	dev->bin_file_mode = mt76_check_bin_file_mode(&dev->mt76);
 
 	if (dev->bin_file_mode) {
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
index f82e36664994..2a31b973c843 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
@@ -1195,7 +1195,6 @@  int mt7915_register_device(struct mt7915_dev *dev)
 	if (ret)
 		goto unreg_dev;
 
-	ieee80211_queue_work(mt76_hw(dev), &dev->init_work);
 
 	if (phy2) {
 		ret = mt7915_register_ext_phy(dev, phy2);
@@ -1203,6 +1202,8 @@  int mt7915_register_device(struct mt7915_dev *dev)
 			goto unreg_thermal;
 	}
 
+	ieee80211_queue_work(mt76_hw(dev), &dev->init_work);
+
 	mt7915_init_debugfs(&dev->phy);
 
 	dev->ser.hw_init_done = true;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
index dabf9dce7ed6..f8266de129dd 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
@@ -5,6 +5,7 @@ 
 #define __MT7915_H
 
 #include <linux/interrupt.h>
+#include <linux/firmware.h>
 #include <linux/ktime.h>
 #include "../mt76_connac.h"
 #include "regs.h"
@@ -28,6 +29,8 @@ 
 #define MT7915_RX_RING_SIZE		1536
 #define MT7915_RX_MCU_RING_SIZE		512
 
+#define MT7915_FIRMWARE_BD		"mediatek"
+
 #define MT7915_FIRMWARE_WA		"mediatek/mt7915_wa.bin"
 #define MT7915_FIRMWARE_WM		"mediatek/mt7915_wm.bin"
 #define MT7915_ROM_PATCH		"mediatek/mt7915_rom_patch.bin"
@@ -467,6 +470,13 @@  struct mt7915_dev {
 	u8 dpd_chan_num_5g;
 	u8 dpd_chan_num_6g;
 
+	struct {
+#define MT7915_FWCFG_HIGH_BAND	BIT(1)
+
+		u32 flags; /* let us know which fields have been set */
+		u32 high_band;	/* sets upper-band to use ('5' or '6')GHz */
+	} fwcfg;
+
 	struct {
 		u8 debug_wm;
 		u8 debug_wa;