From d91318662d5c290ce97bfb18c84e4cddc514f33a Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 21 Aug 2021 00:32:31 +0200 Subject: [PATCH] ath9k: nvmem for ath9k caldata With "getting WIFI MAC from NVMEM" working on ath79 on 5.10, the next logical step I think is to utilize nvmem subsystem to also get the calibration data from there. This will tremendously speed up the wifi bring-up, since we no longer need the userspace helper for the simple devices that can just load them from there. included with this patch is a package/mac80211/refresh. Tested on: WNDR3700v2, TP-Link Archer C7v2 Signed-off-by: Christian Lamparter --- ...-ieee80211-freq-limit-property-to-li.patch | 3 +- .../patches/ath9k/501-ath9k_ahb_init.patch | 2 +- .../ath9k/543-ath9k_entropy_from_adc.patch | 6 +- .../patches/ath9k/552-ath9k-ahb_of.patch | 8 +- .../ath9k/553-ath9k_of_gpio_mask.patch | 4 +- ...calibration-data-via-nvmem-subsystem.patch | 154 +++++++++++++++ ...-fetch-pci-init-values-through-nvmem.patch | 181 ++++++++++++++++++ 7 files changed, 346 insertions(+), 12 deletions(-) create mode 100644 package/kernel/mac80211/patches/ath9k/600-v5.16-ath9k-fetch-calibration-data-via-nvmem-subsystem.patch create mode 100644 package/kernel/mac80211/patches/ath9k/601-v5.16-ath9k-owl-loader-fetch-pci-init-values-through-nvmem.patch diff --git a/package/kernel/mac80211/patches/ath9k/040-ath9k-support-DT-ieee80211-freq-limit-property-to-li.patch b/package/kernel/mac80211/patches/ath9k/040-ath9k-support-DT-ieee80211-freq-limit-property-to-li.patch index 4142cb6ffd..7d44681760 100644 --- a/package/kernel/mac80211/patches/ath9k/040-ath9k-support-DT-ieee80211-freq-limit-property-to-li.patch +++ b/package/kernel/mac80211/patches/ath9k/040-ath9k-support-DT-ieee80211-freq-limit-property-to-li.patch @@ -17,7 +17,7 @@ Link: https://lore.kernel.org/r/20211009212847.1781986-1-chunkeey@gmail.com --- --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c -@@ -1094,6 +1094,8 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, +@@ -1038,6 +1038,8 @@ int ath9k_init_device(u16 devid, struct ARRAY_SIZE(ath9k_tpt_blink)); #endif @@ -26,4 +26,3 @@ Link: https://lore.kernel.org/r/20211009212847.1781986-1-chunkeey@gmail.com /* Register with mac80211 */ error = ieee80211_register_hw(hw); if (error) - diff --git a/package/kernel/mac80211/patches/ath9k/501-ath9k_ahb_init.patch b/package/kernel/mac80211/patches/ath9k/501-ath9k_ahb_init.patch index ed78d1f9b4..6ab7972b55 100644 --- a/package/kernel/mac80211/patches/ath9k/501-ath9k_ahb_init.patch +++ b/package/kernel/mac80211/patches/ath9k/501-ath9k_ahb_init.patch @@ -1,6 +1,6 @@ --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c -@@ -1120,25 +1120,25 @@ static int __init ath9k_init(void) +@@ -1122,25 +1122,25 @@ static int __init ath9k_init(void) { int error; diff --git a/package/kernel/mac80211/patches/ath9k/543-ath9k_entropy_from_adc.patch b/package/kernel/mac80211/patches/ath9k/543-ath9k_entropy_from_adc.patch index 806e136159..ef4e659870 100644 --- a/package/kernel/mac80211/patches/ath9k/543-ath9k_entropy_from_adc.patch +++ b/package/kernel/mac80211/patches/ath9k/543-ath9k_entropy_from_adc.patch @@ -84,9 +84,9 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, const struct ath_bus_ops *bus_ops) { -@@ -1037,6 +1050,8 @@ int ath9k_init_device(u16 devid, struct - ARRAY_SIZE(ath9k_tpt_blink)); - #endif +@@ -1039,6 +1052,8 @@ int ath9k_init_device(u16 devid, struct + + wiphy_read_of_freq_limits(hw->wiphy); + ath_get_initial_entropy(sc); + diff --git a/package/kernel/mac80211/patches/ath9k/552-ath9k-ahb_of.patch b/package/kernel/mac80211/patches/ath9k/552-ath9k-ahb_of.patch index fce6db2167..7ce8bc5b82 100644 --- a/package/kernel/mac80211/patches/ath9k/552-ath9k-ahb_of.patch +++ b/package/kernel/mac80211/patches/ath9k/552-ath9k-ahb_of.patch @@ -253,7 +253,7 @@ static int ath_ahb_probe(struct platform_device *pdev) { void __iomem *mem; -@@ -80,6 +324,17 @@ static int ath_ahb_probe(struct platform +@@ -80,6 +318,17 @@ static int ath_ahb_probe(struct platform int ret = 0; struct ath_hw *ah; char hw_name[64]; @@ -271,7 +271,7 @@ if (!dev_get_platdata(&pdev->dev)) { dev_err(&pdev->dev, "no platform data specified\n"); -@@ -122,13 +377,16 @@ static int ath_ahb_probe(struct platform +@@ -122,13 +371,16 @@ static int ath_ahb_probe(struct platform sc->mem = mem; sc->irq = irq; @@ -289,7 +289,7 @@ if (ret) { dev_err(&pdev->dev, "failed to initialize device\n"); goto err_irq; -@@ -159,6 +417,9 @@ static int ath_ahb_remove(struct platfor +@@ -159,6 +411,9 @@ static int ath_ahb_remove(struct platfor free_irq(sc->irq, sc); ieee80211_free_hw(sc->hw); } @@ -299,7 +299,7 @@ return 0; } -@@ -168,6 +429,9 @@ static struct platform_driver ath_ahb_dr +@@ -168,6 +423,9 @@ static struct platform_driver ath_ahb_dr .remove = ath_ahb_remove, .driver = { .name = "ath9k", diff --git a/package/kernel/mac80211/patches/ath9k/553-ath9k_of_gpio_mask.patch b/package/kernel/mac80211/patches/ath9k/553-ath9k_of_gpio_mask.patch index f11742d66e..80e0dc4c5e 100644 --- a/package/kernel/mac80211/patches/ath9k/553-ath9k_of_gpio_mask.patch +++ b/package/kernel/mac80211/patches/ath9k/553-ath9k_of_gpio_mask.patch @@ -1,6 +1,6 @@ --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c -@@ -650,6 +650,12 @@ static int ath9k_of_init(struct ath_soft +@@ -644,6 +644,12 @@ static int ath9k_of_init(struct ath_soft return 0; } @@ -13,7 +13,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, const struct ath_bus_ops *bus_ops) { -@@ -753,6 +759,9 @@ static int ath9k_init_softc(u16 devid, s +@@ -747,6 +753,9 @@ static int ath9k_init_softc(u16 devid, s if (ret) goto err_hw; diff --git a/package/kernel/mac80211/patches/ath9k/600-v5.16-ath9k-fetch-calibration-data-via-nvmem-subsystem.patch b/package/kernel/mac80211/patches/ath9k/600-v5.16-ath9k-fetch-calibration-data-via-nvmem-subsystem.patch new file mode 100644 index 0000000000..a250d2318e --- /dev/null +++ b/package/kernel/mac80211/patches/ath9k/600-v5.16-ath9k-fetch-calibration-data-via-nvmem-subsystem.patch @@ -0,0 +1,154 @@ +From dab16ef495dbb3cabb355b6c80f0771a4a25e35d Mon Sep 17 00:00:00 2001 +From: Christian Lamparter +Date: Fri, 20 Aug 2021 22:44:52 +0200 +Subject: [PATCH] ath9k: fetch calibration data via nvmem subsystem + +On most embedded ath9k devices (like range extenders, +routers, accesspoints, ...) the calibration data is +stored in a MTD partitions named "ART", or "caldata"/ +"calibration". + +Ever since commit +4b361cfa8624 ("mtd: core: add OTP nvmem provider support") +all MTD partitions are all automatically available through +the nvmem subsystem. This allows drivers like ath9k to read +the necessary data without needing any userspace helpers +that would do this extraction. + +Signed-off-by: Christian Lamparter +--- + +includes: + +From 57671351379b2051cfb07fc14e0bead9916a0880 Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Mon, 11 Oct 2021 18:18:01 +0300 +Subject: ath9k: fix an IS_ERR() vs NULL check + +The devm_kmemdup() function doesn't return error pointers, it returns +NULL on error. + +Fixes: eb3a97a69be8 ("ath9k: fetch calibration data via nvmem subsystem") +Signed-off-by: Dan Carpenter +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211011123533.GA15188@kili + +--- + +--- a/drivers/net/wireless/ath/ath9k/eeprom.c ++++ b/drivers/net/wireless/ath/ath9k/eeprom.c +@@ -135,13 +135,23 @@ static bool ath9k_hw_nvram_read_firmware + offset, data); + } + ++static bool ath9k_hw_nvram_read_nvmem(struct ath_hw *ah, off_t offset, ++ u16 *data) ++{ ++ return ath9k_hw_nvram_read_array(ah->nvmem_blob, ++ ah->nvmem_blob_len / sizeof(u16), ++ offset, data); ++} ++ + bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data) + { + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_platform_data *pdata = ah->dev->platform_data; + bool ret; + +- if (ah->eeprom_blob) ++ if (ah->nvmem_blob) ++ ret = ath9k_hw_nvram_read_nvmem(ah, off, data); ++ else if (ah->eeprom_blob) + ret = ath9k_hw_nvram_read_firmware(ah->eeprom_blob, off, data); + else if (pdata && !pdata->use_eeprom) + ret = ath9k_hw_nvram_read_pdata(pdata, off, data); +--- a/drivers/net/wireless/ath/ath9k/hw.h ++++ b/drivers/net/wireless/ath/ath9k/hw.h +@@ -988,6 +988,8 @@ struct ath_hw { + bool disable_5ghz; + + const struct firmware *eeprom_blob; ++ u16 *nvmem_blob; /* devres managed */ ++ size_t nvmem_blob_len; + + struct ath_dynack dynack; + +--- a/drivers/net/wireless/ath/ath9k/init.c ++++ b/drivers/net/wireless/ath/ath9k/init.c +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -568,6 +569,57 @@ static void ath9k_eeprom_release(struct + release_firmware(sc->sc_ah->eeprom_blob); + } + ++static int ath9k_nvmem_request_eeprom(struct ath_softc *sc) ++{ ++ struct ath_hw *ah = sc->sc_ah; ++ struct nvmem_cell *cell; ++ void *buf; ++ size_t len; ++ int err; ++ ++ cell = devm_nvmem_cell_get(sc->dev, "calibration"); ++ if (IS_ERR(cell)) { ++ err = PTR_ERR(cell); ++ ++ /* nvmem cell might not be defined, or the nvmem ++ * subsystem isn't included. In this case, follow ++ * the established "just return 0;" convention of ++ * ath9k_init_platform to say: ++ * "All good. Nothing to see here. Please go on." ++ */ ++ if (err == -ENOENT || err == -EOPNOTSUPP) ++ return 0; ++ ++ return err; ++ } ++ ++ buf = nvmem_cell_read(cell, &len); ++ if (IS_ERR(buf)) ++ return PTR_ERR(buf); ++ ++ /* run basic sanity checks on the returned nvram cell length. ++ * That length has to be a multiple of a "u16" (i.e.: & 1). ++ * Furthermore, it has to be more than "let's say" 512 bytes ++ * but less than the maximum of AR9300_EEPROM_SIZE (16kb). ++ */ ++ if (((len & 1) == 1) || (len < 512) || (len >= AR9300_EEPROM_SIZE)) { ++ kfree(buf); ++ return -EINVAL; ++ } ++ ++ /* devres manages the calibration values release on shutdown */ ++ ah->nvmem_blob = (u16 *)devm_kmemdup(sc->dev, buf, len, GFP_KERNEL); ++ kfree(buf); ++ if (!ah->nvmem_blob) ++ return -ENOMEM; ++ ++ ah->nvmem_blob_len = len; ++ ah->ah_flags &= ~AH_USE_EEPROM; ++ ah->ah_flags |= AH_NO_EEP_SWAP; ++ ++ return 0; ++} ++ + static int ath9k_init_platform(struct ath_softc *sc) + { + struct ath9k_platform_data *pdata = sc->dev->platform_data; +@@ -710,6 +762,10 @@ static int ath9k_init_softc(u16 devid, s + if (ret) + return ret; + ++ ret = ath9k_nvmem_request_eeprom(sc); ++ if (ret) ++ return ret; ++ + if (ath9k_led_active_high != -1) + ah->config.led_active_high = ath9k_led_active_high == 1; + diff --git a/package/kernel/mac80211/patches/ath9k/601-v5.16-ath9k-owl-loader-fetch-pci-init-values-through-nvmem.patch b/package/kernel/mac80211/patches/ath9k/601-v5.16-ath9k-owl-loader-fetch-pci-init-values-through-nvmem.patch new file mode 100644 index 0000000000..62c561d619 --- /dev/null +++ b/package/kernel/mac80211/patches/ath9k/601-v5.16-ath9k-owl-loader-fetch-pci-init-values-through-nvmem.patch @@ -0,0 +1,181 @@ +From 9bf31835f11aa3c4fe5a9c1f7462c199c5d8e7ca Mon Sep 17 00:00:00 2001 +From: Christian Lamparter +Date: Sat, 21 Aug 2021 00:22:39 +0200 +Subject: [PATCH] ath9k: owl-loader: fetch pci init values through nvmem + +extends the owl loader to fetch important pci initialization +values - which are stored together with the calibration data - +through the nvmem subsystem. + +This allows for much faster WIFI/ath9k initializations on devices +that do not require to perform any post-processing (like XOR'ing/ +reversal or unpacking) since no userspace helper is required. + +Signed-off-by: Christian Lamparter +--- + .../wireless/ath/ath9k/ath9k_pci_owl_loader.c | 105 +++++++++++++----- + 1 file changed, 76 insertions(+), 29 deletions(-) + +--- a/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c ++++ b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c +@@ -19,9 +19,14 @@ + #include + #include + #include ++#include ++#include + + struct owl_ctx { ++ struct pci_dev *pdev; + struct completion eeprom_load; ++ struct work_struct work; ++ struct nvmem_cell *cell; + }; + + #define EEPROM_FILENAME_LEN 100 +@@ -42,6 +47,12 @@ static int ath9k_pci_fixup(struct pci_de + u32 bar0; + bool swap_needed = false; + ++ /* also note that we are doing *u16 operations on the file */ ++ if (cal_len > 4096 || cal_len < 0x200 || (cal_len & 1) == 1) { ++ dev_err(&pdev->dev, "eeprom has an invalid size.\n"); ++ return -EINVAL; ++ } ++ + if (*cal_data != AR5416_EEPROM_MAGIC) { + if (*cal_data != swab16(AR5416_EEPROM_MAGIC)) { + dev_err(&pdev->dev, "invalid calibration data\n"); +@@ -99,38 +110,31 @@ static int ath9k_pci_fixup(struct pci_de + return 0; + } + +-static void owl_fw_cb(const struct firmware *fw, void *context) ++static void owl_rescan(struct pci_dev *pdev) + { +- struct pci_dev *pdev = (struct pci_dev *)context; +- struct owl_ctx *ctx = (struct owl_ctx *)pci_get_drvdata(pdev); +- struct pci_bus *bus; +- +- complete(&ctx->eeprom_load); +- +- if (!fw) { +- dev_err(&pdev->dev, "no eeprom data received.\n"); +- goto release; +- } +- +- /* also note that we are doing *u16 operations on the file */ +- if (fw->size > 4096 || fw->size < 0x200 || (fw->size & 1) == 1) { +- dev_err(&pdev->dev, "eeprom file has an invalid size.\n"); +- goto release; +- } +- +- if (ath9k_pci_fixup(pdev, (const u16 *)fw->data, fw->size)) +- goto release; ++ struct pci_bus *bus = pdev->bus; + + pci_lock_rescan_remove(); +- bus = pdev->bus; + pci_stop_and_remove_bus_device(pdev); + /* the device should come back with the proper + * ProductId. But we have to initiate a rescan. + */ + pci_rescan_bus(bus); + pci_unlock_rescan_remove(); ++} ++ ++static void owl_fw_cb(const struct firmware *fw, void *context) ++{ ++ struct owl_ctx *ctx = (struct owl_ctx *)context; ++ ++ complete(&ctx->eeprom_load); + +-release: ++ if (fw) { ++ ath9k_pci_fixup(ctx->pdev, (const u16 *)fw->data, fw->size); ++ owl_rescan(ctx->pdev); ++ } else { ++ dev_err(&ctx->pdev->dev, "no eeprom data received.\n"); ++ } + release_firmware(fw); + } + +@@ -152,6 +156,43 @@ static const char *owl_get_eeprom_name(s + return eeprom_name; + } + ++static void owl_nvmem_work(struct work_struct *work) ++{ ++ struct owl_ctx *ctx = container_of(work, struct owl_ctx, work); ++ void *buf; ++ size_t len; ++ ++ complete(&ctx->eeprom_load); ++ ++ buf = nvmem_cell_read(ctx->cell, &len); ++ if (!IS_ERR(buf)) { ++ ath9k_pci_fixup(ctx->pdev, buf, len); ++ kfree(buf); ++ owl_rescan(ctx->pdev); ++ } else { ++ dev_err(&ctx->pdev->dev, "no nvmem data received.\n"); ++ } ++} ++ ++static int owl_nvmem_probe(struct owl_ctx *ctx) ++{ ++ int err; ++ ++ ctx->cell = devm_nvmem_cell_get(&ctx->pdev->dev, "calibration"); ++ if (IS_ERR(ctx->cell)) { ++ err = PTR_ERR(ctx->cell); ++ if (err == -ENOENT || err == -EOPNOTSUPP) ++ return 1; /* not present, try firmware_request */ ++ ++ return err; ++ } ++ ++ INIT_WORK(&ctx->work, owl_nvmem_work); ++ schedule_work(&ctx->work); ++ ++ return 0; ++} ++ + static int owl_probe(struct pci_dev *pdev, + const struct pci_device_id *id) + { +@@ -164,21 +205,27 @@ static int owl_probe(struct pci_dev *pde + + pcim_pin_device(pdev); + +- eeprom_name = owl_get_eeprom_name(pdev); +- if (!eeprom_name) { +- dev_err(&pdev->dev, "no eeprom filename found.\n"); +- return -ENODEV; +- } +- + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + init_completion(&ctx->eeprom_load); ++ ctx->pdev = pdev; + + pci_set_drvdata(pdev, ctx); ++ ++ err = owl_nvmem_probe(ctx); ++ if (err <= 0) ++ return err; ++ ++ eeprom_name = owl_get_eeprom_name(pdev); ++ if (!eeprom_name) { ++ dev_err(&pdev->dev, "no eeprom filename found.\n"); ++ return -ENODEV; ++ } ++ + err = request_firmware_nowait(THIS_MODULE, true, eeprom_name, +- &pdev->dev, GFP_KERNEL, pdev, owl_fw_cb); ++ &pdev->dev, GFP_KERNEL, ctx, owl_fw_cb); + if (err) + dev_err(&pdev->dev, "failed to request caldata (%d).\n", err); + -- 2.30.2