[OpenWrt-Devel] [RFC 2/3] kernel: owl-loader for delayed Atheros ath9k fixup

Christian Lamparter chunkeey at googlemail.com
Tue Mar 8 18:14:41 EST 2016


Some devices (like the Cisco Meraki Z1 Cloud Managed Teleworker Gateway)
need to be able to initialize the PCIe wifi device. Normally, this is done
during the early stages of booting linux, because the necessary init code
is read from the memory mapped SPI and passed to pci_enable_ath9k_fixup.
However,this isn't possible for devices which have the init code for the
Atheros chip stored on NAND. Hence, this module can be used to initialze
the chip when the user-space is ready to extract the init code.

Signed-off-by: Christian Lamparter <chunkeey at googlemail.com>
---

Note: We tried several methods to get the AR9280 to work. Initially, we
tried just a request_firmware_nowait from the device's init (z1_setup
in mach-z1.c), but this isn't will not work. The issue is that openwrt
has no /sbin/hotplug helper [0] and procd doesn't scan the
/sys/class/firmware directory for already open request once it started.

We played around with different approaches, and also made a helper-script
to fulfill the outstanding requests on boot [1]. however we found no good
place for it in the start-up scripts. So that's why we developed
the owl-loader module, since it won't have to deal with any procd/hotplug
issues.

Note2: Yes, we'll fix the #include "../arch/mips/ath79/pci-ath9k-fixup.h"
(I think by moving the .h to somewhere in arch/mips/include/...). But first
we need to know if we can do this via the owl-loader... or not ;-) .

[0] <https://www.kernel.org/doc/pending/hotplug.txt> section:
"A note about race conditions (or "why bother with netlink?"):"
details on why it is a bad idea to have /sbin/hotplug

[1] <https://github.com/riptidewave93/Openwrt-Z1/commit/9a38c60a1206b4010fbfb626fc7b2ec69bbe232a>

---
 package/kernel/owl-loader/Makefile                 |  55 +++++++++
 package/kernel/owl-loader/src/Makefile             |   1 +
 package/kernel/owl-loader/src/owl-loader.c         | 133 +++++++++++++++++++++
 .../ar71xx/files/arch/mips/ath79/pci-ath9k-fixup.c |   3 +-
 .../ar71xx/files/arch/mips/ath79/pci-ath9k-fixup.h |   2 +-
 5 files changed, 192 insertions(+), 2 deletions(-)
 create mode 100644 package/kernel/owl-loader/Makefile
 create mode 100644 package/kernel/owl-loader/src/Makefile
 create mode 100644 package/kernel/owl-loader/src/owl-loader.c

diff --git a/package/kernel/owl-loader/Makefile b/package/kernel/owl-loader/Makefile
new file mode 100644
index 0000000..7e62fc5
--- /dev/null
+++ b/package/kernel/owl-loader/Makefile
@@ -0,0 +1,55 @@
+#
+# Copyright (C) 2016 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+include $(INCLUDE_DIR)/kernel.mk
+
+PKG_NAME:=owl-loader
+PKG_RELEASE:=1
+
+include $(INCLUDE_DIR)/package.mk
+
+define KernelPackage/owl-loader
+  SUBMENU:=Network Support
+  TITLE:=Owl loader for Atheros PCIe Wifi support
+  DEPENDS:=@PCI_SUPPORT @TARGET_ar71xx +kmod-ath9k
+  FILES:=$(PKG_BUILD_DIR)/owl-loader.ko
+  AUTOLOAD:=$(call AutoProbe,owl-loader)
+  KCONFIG:=
+endef
+
+define KernelPackage/owl-loader/description
+  Kernel module to initialize Owl Emulation Devices.
+  This is necessary for the Cisco Meraki Z1.
+endef
+
+EXTRA_KCONFIG:= \
+	CONFIG_OWL_LOADER=m
+
+EXTRA_CFLAGS:= \
+	$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=m,%,$(filter %=m,$(EXTRA_KCONFIG)))) \
+	$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=y,%,$(filter %=y,$(EXTRA_KCONFIG)))) \
+
+MAKE_OPTS:= \
+	ARCH="$(LINUX_KARCH)" \
+	CROSS_COMPILE="$(TARGET_CROSS)" \
+	SUBDIRS="$(PKG_BUILD_DIR)" \
+	EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \
+	$(EXTRA_KCONFIG)
+
+define Build/Prepare
+	mkdir -p $(PKG_BUILD_DIR)
+	$(CP) ./src/* $(PKG_BUILD_DIR)/
+endef
+
+define Build/Compile
+	$(MAKE) -C "$(LINUX_DIR)" \
+		$(MAKE_OPTS) \
+		modules
+endef
+
+$(eval $(call KernelPackage,owl-loader))
diff --git a/package/kernel/owl-loader/src/Makefile b/package/kernel/owl-loader/src/Makefile
new file mode 100644
index 0000000..6b58276
--- /dev/null
+++ b/package/kernel/owl-loader/src/Makefile
@@ -0,0 +1 @@
+obj-${CONFIG_OWL_LOADER}	+= owl-loader.o
diff --git a/package/kernel/owl-loader/src/owl-loader.c b/package/kernel/owl-loader/src/owl-loader.c
new file mode 100644
index 0000000..36c638e
--- /dev/null
+++ b/package/kernel/owl-loader/src/owl-loader.c
@@ -0,0 +1,133 @@
+/*
+ * Initialize Owl Emulation Devices (PCIID: 168c:ff1c)
+ *
+ * Copyright (C) 2016 Christian Lamparter <chunkeey at googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Some devices (like the Cisco Meraki Z1 Cloud Managed Teleworker Gateway)
+ * need to be able to initialize the PCIe wifi device. Normally, this is done
+ * during the early stages of booting linux, because the necessary init code
+ * is read from the memory mapped SPI and passed to pci_enable_ath9k_fixup.
+ * However,this isn't possible for devices which have the init code for the
+ * Atheros chip stored on NAND. Hence, this module can be used to initialze
+ * the chip when the user-space is ready to extract the init code.
+ */
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/completion.h>
+#include <linux/firmware.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/ath9k_platform.h>
+
+#include "../arch/mips/ath79/pci-ath9k-fixup.h"
+
+struct owl_ctx {
+	struct completion eeprom_load;
+};
+
+static void owl_fw_cb(const struct firmware *fw, void *context)
+{
+	struct pci_dev *pdev = (struct pci_dev *) context;
+	struct owl_ctx *ctx = (struct owl_ctx *) pci_get_drvdata(pdev);
+	struct ath9k_platform_data *pdata = dev_get_platdata(&pdev->dev);
+	struct pci_bus *bus;
+
+	complete(&ctx->eeprom_load);
+
+	if (!fw) {
+		dev_err(&pdev->dev, "no '%s' eeprom file received.",
+		       pdata->eeprom_name);
+		goto release;
+	}
+
+	if (fw->size > sizeof(pdata->eeprom_data)) {
+		dev_err(&pdev->dev, "loaded data is too big.");
+		goto release;
+	}
+
+	pci_lock_rescan_remove();
+	bus = pdev->bus;
+
+	memcpy(pdata->eeprom_data, fw->data, sizeof(pdata->eeprom_data));
+	/* eeprom has been successfully loaded - pass the data to ath9k
+	 * but remove the eeprom_name, so it doesn't try to load it too.
+	 */
+	pdata->eeprom_name = NULL;
+
+	pci_enable_ath9k_fixup(0, pdata->eeprom_data);
+	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();
+
+release:
+	release_firmware(fw);
+}
+
+static int owl_probe(struct pci_dev *pdev,
+		    const struct pci_device_id *id)
+{
+	struct owl_ctx *ctx;
+	struct ath9k_platform_data *pdata;
+	int err = 0;
+
+	if (pcim_enable_device(pdev))
+		return -EIO;
+
+	/* we now have a valid dev->platform_data */
+	pdata = dev_get_platdata(&pdev->dev);
+	if (!pdata || !pdata->eeprom_data) {
+		dev_err(&pdev->dev, "platform data missing or no eeprom file defined.");
+		return -ENODEV;
+	}
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx) {
+		dev_err(&pdev->dev, "failed to alloc device context.");
+		return -ENOMEM;
+	}
+	init_completion(&ctx->eeprom_load);
+
+	pci_set_drvdata(pdev, ctx);
+	err = request_firmware_nowait(THIS_MODULE, true, pdata->eeprom_name,
+				      &pdev->dev, GFP_KERNEL, pdev, owl_fw_cb);
+	if (err) {
+		dev_err(&pdev->dev, "failed to request caldata (%d).", err);
+		kfree(ctx);
+	}
+	return err;
+}
+
+static void owl_remove(struct pci_dev *pdev)
+{
+	struct owl_ctx *ctx = pci_get_drvdata(pdev);
+
+	if (ctx) {
+		wait_for_completion(&ctx->eeprom_load);
+		pci_set_drvdata(pdev, NULL);
+		kfree(ctx);
+	}
+}
+
+static const struct pci_device_id owl_pci_table[] = {
+	/* PCIe Owl Emulation */
+	{ PCI_VDEVICE(ATHEROS, 0xff1c) }, /* * PCI-E */
+	{ },
+};
+MODULE_DEVICE_TABLE(pci, owl_pci_table);
+
+static struct pci_driver owl_driver = {
+	.name		= "owl-loader",
+	.id_table	= owl_pci_table,
+	.probe		= owl_probe,
+	.remove		= owl_remove,
+};
+module_pci_driver(owl_driver);
+
+MODULE_LICENSE("GPL");
diff --git a/target/linux/ar71xx/files/arch/mips/ath79/pci-ath9k-fixup.c b/target/linux/ar71xx/files/arch/mips/ath79/pci-ath9k-fixup.c
index 2202351..10a7be0 100644
--- a/target/linux/ar71xx/files/arch/mips/ath79/pci-ath9k-fixup.c
+++ b/target/linux/ar71xx/files/arch/mips/ath79/pci-ath9k-fixup.c
@@ -115,7 +115,7 @@ static void ath9k_pci_fixup(struct pci_dev *dev)
 }
 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_ATHEROS, PCI_ANY_ID, ath9k_pci_fixup);
 
-void __init pci_enable_ath9k_fixup(unsigned slot, u16 *cal_data)
+void pci_enable_ath9k_fixup(unsigned slot, u16 *cal_data)
 {
 	if (ath9k_num_fixups >= ARRAY_SIZE(ath9k_fixups))
 		return;
@@ -124,3 +124,4 @@ void __init pci_enable_ath9k_fixup(unsigned slot, u16 *cal_data)
 	ath9k_fixups[ath9k_num_fixups].cal_data = cal_data;
 	ath9k_num_fixups++;
 }
+EXPORT_SYMBOL_GPL(pci_enable_ath9k_fixup);
diff --git a/target/linux/ar71xx/files/arch/mips/ath79/pci-ath9k-fixup.h b/target/linux/ar71xx/files/arch/mips/ath79/pci-ath9k-fixup.h
index 5794941..4f3ad85 100644
--- a/target/linux/ar71xx/files/arch/mips/ath79/pci-ath9k-fixup.h
+++ b/target/linux/ar71xx/files/arch/mips/ath79/pci-ath9k-fixup.h
@@ -1,6 +1,6 @@
 #ifndef _PCI_ATH9K_FIXUP
 #define _PCI_ATH9K_FIXUP
 
-void pci_enable_ath9k_fixup(unsigned slot, u16 *cal_data) __init;
+void pci_enable_ath9k_fixup(unsigned slot, u16 *cal_data);
 
 #endif /* _PCI_ATH9K_FIXUP */
-- 
2.7.0
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel


More information about the openwrt-devel mailing list