* [RFC PATCH v1 2/3] ath9k: fetch calibration data via nvmem subsystem
2021-08-21 1:09 [RFC PATCH v1 1/3] dt-bindings:net:wireless:qca,ath9k: add nvmem-cells for calibration data Christian Lamparter
@ 2021-08-21 1:09 ` Christian Lamparter
2021-08-21 1:09 ` [RFC PATCH v1 3/3] ath9k: owl-loader: fetch pci init values through nvmem Christian Lamparter
` (2 subsequent siblings)
3 siblings, 0 replies; 8+ messages in thread
From: Christian Lamparter @ 2021-08-21 1:09 UTC (permalink / raw)
To: linux-wireless, devicetree, ath9k-devel
On most embedded ath9k devices (like range extenders,
routers, accesspoints, ...) the calibration data is
stored in a MTD partitions named "ART", or "caldata"/
"calibration".
Since commit 4b361cfa8624 ("mtd: core: add OTP nvmem provider support"):
All MTD partitions are all automatically available through
the nvmem subsystem. This feature - together with an nvmem cell definition
in the device-tree - allows drivers like ath9k to get the data necessary
for starting the WIFI, without having to wait around for the filesystem
and userspace to do the extractions.
Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
---
drivers/net/wireless/ath/ath9k/eeprom.c | 12 +++++-
drivers/net/wireless/ath/ath9k/hw.h | 2 +
drivers/net/wireless/ath/ath9k/init.c | 56 +++++++++++++++++++++++++
3 files changed, 69 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c
index c22d457dbc54..e6b3cd49ea18 100644
--- 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(const struct firmware *eeprom_blob,
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);
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index b7b65b1c90e8..096a206f49ed 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -977,6 +977,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;
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index e9a36dd7144f..1568730fc01e 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -22,6 +22,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_net.h>
+#include <linux/nvmem-consumer.h>
#include <linux/relay.h>
#include <linux/dmi.h>
#include <net/ieee80211_radiotap.h>
@@ -568,6 +569,57 @@ static void ath9k_eeprom_release(struct ath_softc *sc)
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 (IS_ERR(ah->nvmem_blob))
+ return PTR_ERR(ah->nvmem_blob);
+
+ 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;
@@ -704,6 +756,10 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
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;
--
2.33.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [RFC PATCH v1 3/3] ath9k: owl-loader: fetch pci init values through nvmem
2021-08-21 1:09 [RFC PATCH v1 1/3] dt-bindings:net:wireless:qca,ath9k: add nvmem-cells for calibration data Christian Lamparter
2021-08-21 1:09 ` [RFC PATCH v1 2/3] ath9k: fetch calibration data via nvmem subsystem Christian Lamparter
@ 2021-08-21 1:09 ` Christian Lamparter
2021-08-21 5:40 ` [RFC PATCH v1 1/3] dt-bindings:net:wireless:qca,ath9k: add nvmem-cells for calibration data Kalle Valo
2021-08-24 16:51 ` Rob Herring
3 siblings, 0 replies; 8+ messages in thread
From: Christian Lamparter @ 2021-08-21 1:09 UTC (permalink / raw)
To: linux-wireless, devicetree, ath9k-devel
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 <chunkeey@gmail.com>
---
.../wireless/ath/ath9k/ath9k_pci_owl_loader.c | 105 +++++++++++++-----
1 file changed, 76 insertions(+), 29 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c
index 56d1a7764b9f..708c8969b503 100644
--- 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 <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/ath9k_platform.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/workqueue.h>
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_dev *pdev, const u16 *cal_data,
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_dev *pdev, const u16 *cal_data,
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(struct pci_dev *pdev)
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 *pdev,
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.33.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [RFC PATCH v1 1/3] dt-bindings:net:wireless:qca,ath9k: add nvmem-cells for calibration data
2021-08-21 1:09 [RFC PATCH v1 1/3] dt-bindings:net:wireless:qca,ath9k: add nvmem-cells for calibration data Christian Lamparter
2021-08-21 1:09 ` [RFC PATCH v1 2/3] ath9k: fetch calibration data via nvmem subsystem Christian Lamparter
2021-08-21 1:09 ` [RFC PATCH v1 3/3] ath9k: owl-loader: fetch pci init values through nvmem Christian Lamparter
@ 2021-08-21 5:40 ` Kalle Valo
2021-08-21 20:02 ` Christian Lamparter
2021-08-24 16:51 ` Rob Herring
3 siblings, 1 reply; 8+ messages in thread
From: Kalle Valo @ 2021-08-21 5:40 UTC (permalink / raw)
To: Christian Lamparter; +Cc: linux-wireless, devicetree, ath9k-devel
Christian Lamparter <chunkeey@gmail.com> writes:
> On most embedded ath9k devices (like range extenders,
> routers, accesspoints, ...) the calibration data for
> the RF/PHY is simply stored in a MTD partition named
> "ART", "caldata"/"calibration", etc.
>
> Any mtd partition is automatically registered in the
> nvmem subsystem. This makes is possible to fetch the
> necessary calibration directly from there at the low
> cost of adding nvmem cell information via the
> device-tree or via similar means.
>
> This speeds up the driver's initialization a lot,
> because the driver doesn't have to wait for userspace
> to provide the data via helpers.
>
> Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
The series looks good to me. But I'm curious, why you marked this as
RFC? Is there something controversial I missed?
--
https://patchwork.kernel.org/project/linux-wireless/list/
https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH v1 1/3] dt-bindings:net:wireless:qca,ath9k: add nvmem-cells for calibration data
2021-08-21 5:40 ` [RFC PATCH v1 1/3] dt-bindings:net:wireless:qca,ath9k: add nvmem-cells for calibration data Kalle Valo
@ 2021-08-21 20:02 ` Christian Lamparter
2021-08-22 5:09 ` Kalle Valo
2021-08-24 16:51 ` Rob Herring
0 siblings, 2 replies; 8+ messages in thread
From: Christian Lamparter @ 2021-08-21 20:02 UTC (permalink / raw)
To: Kalle Valo; +Cc: linux-wireless, devicetree, ath9k-devel, Rob Herring
On 21/08/2021 07:40, Kalle Valo wrote:
> Christian Lamparter <chunkeey@gmail.com> writes:
>
>> On most embedded ath9k devices (like range extenders,
>> routers, accesspoints, ...) the calibration data for
>> the RF/PHY is simply stored in a MTD partition named
>> "ART", "caldata"/"calibration", etc.
>>
>> Any mtd partition is automatically registered in the
>> nvmem subsystem. This makes is possible to fetch the
>> necessary calibration directly from there at the low
>> cost of adding nvmem cell information via the
>> device-tree or via similar means.
>>
>> This speeds up the driver's initialization a lot,
>> because the driver doesn't have to wait for userspace
>> to provide the data via helpers.
>>
>> Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
>
> The series looks good to me. But I'm curious, why you marked this as
> RFC? Is there something controversial I missed?
yeah. Last night (it was already really late) I was tunnel-visioning
at the thought that device-tree binding update was a must there.
... And ath9k's qca,ath9k.txt is still in that .txt and not .yaml
format. So, I'm not sure if that file has to be converted first.
(I couldn't get Rob's tools to work. And without them, I've no idea
what error messages a converted .yaml of it will pop up)
However... since then, I had the change to re-read:
<https://www.kernel.org/doc/Documentation/nvmem/nvmem.txt>
And found that nvmem cells and lookups can be specified in
the old platform data way as well... So that binding patch
1/3 is optional.
So, yeah. If nobody has an comment until next week, I'll post this
series with only patches 2/3 and 3/3.
Cheers,
Christian
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH v1 1/3] dt-bindings:net:wireless:qca,ath9k: add nvmem-cells for calibration data
2021-08-21 20:02 ` Christian Lamparter
@ 2021-08-22 5:09 ` Kalle Valo
2021-08-24 16:51 ` Rob Herring
1 sibling, 0 replies; 8+ messages in thread
From: Kalle Valo @ 2021-08-22 5:09 UTC (permalink / raw)
To: Christian Lamparter; +Cc: linux-wireless, devicetree, ath9k-devel, Rob Herring
Christian Lamparter <chunkeey@gmail.com> writes:
> On 21/08/2021 07:40, Kalle Valo wrote:
>> Christian Lamparter <chunkeey@gmail.com> writes:
>>
>>> On most embedded ath9k devices (like range extenders,
>>> routers, accesspoints, ...) the calibration data for
>>> the RF/PHY is simply stored in a MTD partition named
>>> "ART", "caldata"/"calibration", etc.
>>>
>>> Any mtd partition is automatically registered in the
>>> nvmem subsystem. This makes is possible to fetch the
>>> necessary calibration directly from there at the low
>>> cost of adding nvmem cell information via the
>>> device-tree or via similar means.
>>>
>>> This speeds up the driver's initialization a lot,
>>> because the driver doesn't have to wait for userspace
>>> to provide the data via helpers.
>>>
>>> Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
>>
>> The series looks good to me. But I'm curious, why you marked this as
>> RFC? Is there something controversial I missed?
>
> yeah. Last night (it was already really late) I was tunnel-visioning
> at the thought that device-tree binding update was a must there.
> ... And ath9k's qca,ath9k.txt is still in that .txt and not .yaml
> format. So, I'm not sure if that file has to be converted first.
> (I couldn't get Rob's tools to work. And without them, I've no idea
> what error messages a converted .yaml of it will pop up)
>
> However... since then, I had the change to re-read:
> <https://www.kernel.org/doc/Documentation/nvmem/nvmem.txt>
>
> And found that nvmem cells and lookups can be specified in
> the old platform data way as well... So that binding patch
> 1/3 is optional.
>
> So, yeah. If nobody has an comment until next week, I'll post this
> series with only patches 2/3 and 3/3.
Sounds good, thanks for the background.
--
https://patchwork.kernel.org/project/linux-wireless/list/
https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH v1 1/3] dt-bindings:net:wireless:qca,ath9k: add nvmem-cells for calibration data
2021-08-21 20:02 ` Christian Lamparter
2021-08-22 5:09 ` Kalle Valo
@ 2021-08-24 16:51 ` Rob Herring
1 sibling, 0 replies; 8+ messages in thread
From: Rob Herring @ 2021-08-24 16:51 UTC (permalink / raw)
To: Christian Lamparter; +Cc: Kalle Valo, linux-wireless, devicetree, ath9k-devel
On Sat, Aug 21, 2021 at 10:02:57PM +0200, Christian Lamparter wrote:
> On 21/08/2021 07:40, Kalle Valo wrote:
> > Christian Lamparter <chunkeey@gmail.com> writes:
> >
> > > On most embedded ath9k devices (like range extenders,
> > > routers, accesspoints, ...) the calibration data for
> > > the RF/PHY is simply stored in a MTD partition named
> > > "ART", "caldata"/"calibration", etc.
> > >
> > > Any mtd partition is automatically registered in the
> > > nvmem subsystem. This makes is possible to fetch the
> > > necessary calibration directly from there at the low
> > > cost of adding nvmem cell information via the
> > > device-tree or via similar means.
> > >
> > > This speeds up the driver's initialization a lot,
> > > because the driver doesn't have to wait for userspace
> > > to provide the data via helpers.
> > >
> > > Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
> >
> > The series looks good to me. But I'm curious, why you marked this as
> > RFC? Is there something controversial I missed?
>
> yeah. Last night (it was already really late) I was tunnel-visioning
> at the thought that device-tree binding update was a must there.
> ... And ath9k's qca,ath9k.txt is still in that .txt and not .yaml
> format. So, I'm not sure if that file has to be converted first.
That would be nice, but it still falls under my bar for must convert
first. :)
> (I couldn't get Rob's tools to work. And without them, I've no idea
> what error messages a converted .yaml of it will pop up)
What issue were you having?
Rob
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH v1 1/3] dt-bindings:net:wireless:qca,ath9k: add nvmem-cells for calibration data
2021-08-21 1:09 [RFC PATCH v1 1/3] dt-bindings:net:wireless:qca,ath9k: add nvmem-cells for calibration data Christian Lamparter
` (2 preceding siblings ...)
2021-08-21 5:40 ` [RFC PATCH v1 1/3] dt-bindings:net:wireless:qca,ath9k: add nvmem-cells for calibration data Kalle Valo
@ 2021-08-24 16:51 ` Rob Herring
3 siblings, 0 replies; 8+ messages in thread
From: Rob Herring @ 2021-08-24 16:51 UTC (permalink / raw)
To: Christian Lamparter; +Cc: linux-wireless, ath9k-devel, devicetree
On Sat, 21 Aug 2021 03:09:13 +0200, Christian Lamparter wrote:
> On most embedded ath9k devices (like range extenders,
> routers, accesspoints, ...) the calibration data for
> the RF/PHY is simply stored in a MTD partition named
> "ART", "caldata"/"calibration", etc.
>
> Any mtd partition is automatically registered in the
> nvmem subsystem. This makes is possible to fetch the
> necessary calibration directly from there at the low
> cost of adding nvmem cell information via the
> device-tree or via similar means.
>
> This speeds up the driver's initialization a lot,
> because the driver doesn't have to wait for userspace
> to provide the data via helpers.
>
> Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
> ---
> .../devicetree/bindings/net/wireless/qca,ath9k.txt | 10 ++++++++++
> 1 file changed, 10 insertions(+)
>
Acked-by: Rob Herring <robh@kernel.org>
^ permalink raw reply [flat|nested] 8+ messages in thread