From patchwork Thu Feb 2 07:55:22 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mad Horse X-Patchwork-Id: 13125476 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AED9EC05027 for ; Thu, 2 Feb 2023 07:56:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232051AbjBBH4F (ORCPT ); Thu, 2 Feb 2023 02:56:05 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50920 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230094AbjBBH4E (ORCPT ); Thu, 2 Feb 2023 02:56:04 -0500 Received: from smtp161.vfemail.net (smtp161.vfemail.net [146.59.185.161]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 55C9C79F32 for ; Wed, 1 Feb 2023 23:56:02 -0800 (PST) Received: (qmail 28581 invoked from network); 2 Feb 2023 07:56:00 +0000 Received: from localhost (HELO nl101-3.vfemail.net) () by smtpout.vfemail.net with ESMTPS (ECDHE-RSA-AES256-GCM-SHA384 encrypted); 2 Feb 2023 07:56:00 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=simple; d=openmail.cc; h=from:to:cc :subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; s=2018; bh=tYQoI4Q3AB3B1RMYryIlLgzf6 NRBrrW0cI+Svt2aBRU=; b=Sj5yyYRe+O51ULmEiJ/pqS4Lml8o2Cl5RnVGxhlcS 5urUu20r0mfeMQhlKxqarw4wwRrjV38Pf5D+k8LcRr4yThYrYSQVuyD5uDFvE9OM UBzkuBUITAVwzxFZ93+ghmZy87y8dloPqWSoA7821Lvr3+DN4eJMQBqhyL9YsIJ2 UY= Received: (qmail 13953 invoked from network); 2 Feb 2023 07:56:00 -0000 Received: by simscan 1.4.0 ppid: 13438, pid: 13829, t: 0.9724s scanners:none Received: from unknown (HELO bmwxMDEudmZlbWFpbC5uZXQ=) (ZXF1dUBvcGVubWFpbC5jYw==@MTkyLjE2OC4xLjE5Mg==) by nl101.vfemail.net with ESMTPA; 2 Feb 2023 07:55:59 -0000 From: equu@openmail.cc To: lpieralisi@kernel.org, toke@toke.dk, kvalo@kernel.org Cc: linux-pci@vger.kernel.org, robh@kernel.org, linux-wireless@vger.kernel.org, ath10k@lists.infradead.org, equu@openmail.cc, Bjorn Helgaas Subject: [PATCH v3 1/3] PCI: of: Match pci devices or drivers against OF DT nodes Date: Thu, 2 Feb 2023 15:55:22 +0800 Message-Id: <20230202075524.2911058-2-equu@openmail.cc> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230202075524.2911058-1-equu@openmail.cc> References: <20230202075524.2911058-1-equu@openmail.cc> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Edward Chow Currently, whether a compatibility string within an OF DT node for a PCI device (whose spec is at https://www.devicetree.org/open-firmware/bindings/pci/ ) matches the vendor and device id of either the PCI device installed on the corresponding location or the driver suggested by the compatibility string is not supported. This patch introduces a function to decode a compatibility string into a struct pci_device_id, which could further be matched against PCI devices or drivers, as well as functions to match a compatibility string or OF DT node against PCI devices or drivers. Signed-off-by: Edward Chow Cc: Bjorn Helgaas --- drivers/pci/of.c | 299 +++++++++++++++++++++++++++++++++++++++ drivers/pci/pci-driver.c | 5 - drivers/pci/pci.h | 56 ++++++++ include/linux/of_pci.h | 25 ++++ include/linux/pci.h | 6 + 5 files changed, 386 insertions(+), 5 deletions(-) diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 196834ed44fe..edb61195ea53 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include "pci.h" #ifdef CONFIG_PCI @@ -251,6 +253,303 @@ void of_pci_check_probe_only(void) } EXPORT_SYMBOL_GPL(of_pci_check_probe_only); +/** + * of_pci_compat_to_device_id() - Decode an OF compatibility string into a + * pci_device_id structure. + * @compat: the compatibility string to decode, could be NULL + * @id: pointer to a struct pci_device_id, to store the result + * @rev: pointer to output revision info, PCI_ANY_ID if no revision in @compat + * @req_pcie: pointer to output whether @compat mandates PCIe compatibility + * + * returns 0 when success, -EINVAL when failed. + */ +int of_pci_compat_to_device_id(const char *compat, struct pci_device_id *id, + u32 *rev, u32 *req_pcie) +{ + union { + u8 u8; + u16 u16; + u32 u32; + } res = {0}; + *req_pcie = 0; + *rev = PCI_ANY_ID; + if (!compat || strncasecmp(compat, "pci", 3) != 0) + return -EINVAL; + compat += 3; + + if (strncasecmp(compat, "class,", 6) == 0) { + /* pciclass,CCSSPP */ + compat += 6; + if ((strlen(compat) < 4) + || kstrtouint(compat, 16, &id->class)) + return -EINVAL; + if (id->class < 0x10000) { + id->class <<= 8; + id->class_mask = 0xFFFF00; + } else { + id->class_mask = PCI_ANY_ID; + } + id->vendor = PCI_ANY_ID; + id->device = PCI_ANY_ID; + id->subvendor = PCI_ANY_ID; + id->subdevice = PCI_ANY_ID; + return 0 + } + + if (strncasecmp(compat, "ex", 2) == 0) { + /* pciex... */ + *req_pcie = 1; + compat += 2; + } + if (kstrtou16(compat, 16, &res.u16)) + return -EINVAL; + id->vendor = res.u16; + compat = strchr(compat, ','); + if (!compat) + return -EINVAL; + compat++; + if (kstrtou16(compat, 16, &res.u16)) + return -EINVAL; + id->device = res.u16; + compat = strchr(compat, '.'); + if (compat == NULL) { + /* pciVVVV,DDDD */ + id->subvendor = PCI_ANY_ID; + id->subdevice = PCI_ANY_ID; + return 0 + } + + compat++; + if (strlen(compat) == 2) { + /* pciVVVV,DDDD.RR */ + if (kstrtou8(compat, 16, &res.u8)) + return -EINVAL; + *rev = res.u8; + id->subvendor = PCI_ANY_ID; + id->subdevice = PCI_ANY_ID; + return 0 + } + + if (kstrtou16(compat, 16, &res.u16)) + return -EINVAL; + id->subvendor = res.u16; + compat = strchr(compat, '.'); + if (!compat) + return -EINVAL; + compat++; + if (kstrtou16(compat, 16, &res.u16)) + return -EINVAL; + id->subdevice = res.u16; + compat = strchr(compat, '.'); + if (compat == NULL) + /* pciVVVV,DDDD.SSSS.ssss */ + return 0 + + compat++; + if (strlen(compat) == 2) { + /* pciVVVV,DDDD.SSSS.ssss.RR */ + if (kstrtou8(compat, 16, &res.u8)) + return -EINVAL; + *rev = res.u8; + } + return 0; +} + +/** + * of_pci_compat_match_device() - Tell whether a PCI device structure matches + * a given OF compatibility string + * @compat: single OF compatibility string to match, could be NULL + * @dev the PCI device structure to match against + * + * Returns whether they match. + */ +bool of_pci_compat_match_device(const char *compat, const struct pci_dev *dev) +{ + __u32 rev = PCI_ANY_ID; + __u32 req_pcie = 0; + struct pci_device_id id = {0}; + + if (of_pci_compat_to_device_id(compat, &id, &rev, &req_pcie)) + return false; + return pci_match_one_device(&id, dev) && + (rev == PCI_ANY_ID || rev == dev->revision) && + req_pcie ? dev->pcie_cap : true; +} + +/** + * of_pci_node_match_device() - Tell whether an OF device tree node + * matches the given pci device + * @node: single OF device tree node to match, could be NULL + * @dev: the PCI device structure to match against, could be NULL + * + * Returns whether they match. + */ +bool of_pci_node_match_device(const struct device_node *node, + const struct pci_dev *dev) +{ + struct property *prop; + const char *cp; + + if (!node || !dev) + return false; + prop = of_find_property(node, "compatible", NULL); + for (cp = of_prop_next_string(prop, NULL); cp; + cp = of_prop_next_string(prop, cp)) { + if (of_pci_compat_match_device(cp, dev)) + return true; + } + return false; +} +EXPORT_SYMBOL_GPL(of_pci_node_match_device); + +/** + * of_pci_compat_match_one_id() - Tell whether a PCI device ID structure matches + * a given OF compatibility string, note that there is no revision nor PCIe + * capability info in PCI device ID structures + * + * @compat: single OF compatibility string to match, could be NULL + * @id the PCI device ID structure to match against, could be NULL + * + * Returns the matching pci_device_id structure pointed by ID + * or %NULL if there is no match. + */ +const struct pci_device_id * +of_pci_compat_match_one_id(const char *compat, const struct pci_device_id *id) +{ + __u32 rev = PCI_ANY_ID; + __u32 req_pcie = 0; + struct pci_device_id pr = {0}; + + if (!compat || !id || + of_pci_compat_to_device_id(compat, &pr, &rev, &req_pcie)) + return NULL; + return pci_match_one_id(id, &pr); +} + +/** + * of_pci_compat_match_id_table() - Tell whether a given OF compatibility string + * matches a given pci_id table + * + * @compat: single OF compatibility string to match, could be NULL + * @table the PCI device ID table to match against, could be NULL + * + * Returns the matching pci_device_id structure or %NULL if there is no match. + */ +const struct pci_device_id * +of_pci_compat_match_id_table(const char *compat, const struct pci_device_id *table) +{ + if (compat && table) { + while (table->vendor || table->subvendor || table->class_mask) { + if (of_pci_compat_match_one_id(compat, table)) + return table; + table++; + } + } + return NULL; +} + +/** + * of_pci_node_match_id_table() - Tell whether an OF device tree node + * matches the given pci_id table + * @node: single OF device tree node to match, could be NULL + * @table: the PCI device ID table to match against, could be NULL + * + * Returns the matching pci_device_id structure + * or %NULL if there is no match. + */ +const struct pci_device_id * +of_pci_node_match_id_table(const struct device_node *node, + const struct pci_device_id *table) +{ + struct property *prop; + const char *cp; + const struct pci_device_id *id; + + if (!node || !table) + return NULL; + prop = of_find_property(node, "compatible", NULL); + for (cp = of_prop_next_string(prop, NULL); cp; + cp = of_prop_next_string(prop, cp)) { + id = of_pci_compat_match_id_table(cp, table); + if (id) + return id; + } + return NULL; +} +EXPORT_SYMBOL_GPL(of_pci_node_match_id_table); + +/** + * of_pci_compat_match_driver - See if a given OF compatibility string matches + * a driver's list of IDs + * @compat: single OF compatibility string to match, could be NULL + * @drv: the PCI driver to match against, could be NULL + * + * Used by a driver to check whether an OF compatibility string matches one of + * (dynamically) supported devices, which may have been augmented + * via the sysfs "new_id" file. Returns the matching pci_device_id + * structure or %NULL if there is no match. + */ +const struct pci_device_id * +of_pci_compat_match_driver(const char *compat, struct pci_driver *drv) +{ + struct pci_dynid *dynid; + const struct pci_device_id *found_id = NULL, *ids; + + if (!compat || !drv) + return NULL; + /* Look at the dynamic ids first, before the static ones */ + spin_lock(&drv->dynids.lock); + list_for_each_entry(dynid, &drv->dynids.list, node) { + if (of_pci_compat_match_one_id(compat, &dynid->id)) { + found_id = &dynid->id; + break; + } + } + spin_unlock(&drv->dynids.lock); + + if (found_id) + return found_id; + + for (ids = drv->id_table; (found_id = of_pci_compat_match_one_id(compat, ids)); + ids = found_id + 1) { + /* exclude ids in id_table with override_only */ + if (!found_id->override_only) + return found_id; + } + + return NULL; +} + +/** + * of_pci_node_match_driver() - Tell whether an OF device tree node + * matches the given pci driver + * @node: single OF device tree node to match, could be NULL + * @drv: the PCI driver structure to match against, could be NULL + * + * Returns the matching pci_device_id structure + * or %NULL if there is no match. + */ +const struct pci_device_id * +of_pci_node_match_driver(const struct device_node *node, + struct pci_driver *drv) +{ + struct property *prop; + const char *cp; + const struct pci_device_id *id; + + if (!node || !drv) + return NULL; + prop = of_find_property(node, "compatible", NULL); + for (cp = of_prop_next_string(prop, NULL); cp; + cp = of_prop_next_string(prop, cp)) { + id = of_pci_compat_match_driver(cp, drv); + if (id) + return id; + } + return NULL; +} +EXPORT_SYMBOL_GPL(of_pci_node_match_driver); + /** * devm_of_pci_get_host_bridge_resources() - Resource-managed parsing of PCI * host bridge resources from DT diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index a2ceeacc33eb..aa212d12353f 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -24,11 +24,6 @@ #include "pci.h" #include "pcie/portdrv.h" -struct pci_dynid { - struct list_head node; - struct pci_device_id id; -}; - /** * pci_add_dynid - add a new PCI device ID to this driver and re-probe devices * @drv: target pci driver diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 9ed3b5550043..e30652021a63 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -204,6 +204,29 @@ pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev) return NULL; } +/** + * pci_match_one_id - Tell if a PCI device id structure matches another + * PCI device id structure + * @id: single PCI device id structure to match, usually in a list or array + * @pr: the probing PCI device id structure to match against, usually converted from + * other format + * + * Returns the matching pci_device_id structure pointed by id + * or %NULL if there is no match. + */ +static inline const struct pci_device_id * +pci_match_one_id(const struct pci_device_id *id, const struct pci_device_id *pr) +{ + if ((id->vendor == pr->vendor) && + (id->device == pr->device) && + (id->subvendor == pr->subvendor) && + (id->subdevice == pr->subdevice) && + (id->class == pr->class) && + (id->class_mask == pr->class_mask)) + return id; + return NULL; +} + /* PCI slot sysfs helper code */ #define to_pci_slot(s) container_of(s, struct pci_slot, kobj) @@ -638,6 +661,15 @@ void pci_release_bus_of_node(struct pci_bus *bus); int devm_of_pci_bridge_init(struct device *dev, struct pci_host_bridge *bridge); +int of_pci_compat_to_device_id(const char *compat, struct pci_device_id *id, + __u32 *rev, __u32 *req_pcie); +bool of_pci_compat_match_device(const char *compat, const struct pci_dev *dev); +const struct pci_device_id * +of_pci_compat_match_one_id(const char *compat, const struct pci_device_id *id); +const struct pci_device_id * +of_pci_compat_match_id_table(const char *compat, const struct pci_device_id *table); +const struct pci_device_id * +of_pci_compat_match_driver(const char *compat, struct pci_driver *drv); #else static inline int of_pci_parse_bus_range(struct device_node *node, struct resource *res) @@ -679,6 +711,30 @@ static inline int devm_of_pci_bridge_init(struct device *dev, struct pci_host_br return 0; } +static inline int of_pci_compat_to_device_id(const char *compat, struct pci_device_id *id, + __u32 *rev, __u32 *req_pcie) +{ + return -EINVAL; +} +static inline bool of_pci_compat_match_device(const char *compat, const struct pci_dev *dev) +{ + return false; +} +static inline const struct pci_device_id * +of_pci_compat_match_one_id(const char *compat, const struct pci_device_id *id) +{ + return NULL; +} +static inline const struct pci_device_id * +of_pci_compat_match_id_table(const char *compat, const struct pci_device_id *table) +{ + return NULL; +} +static inline const struct pci_device_id * +of_pci_compat_match_driver(const char *compat, struct pci_driver *drv) +{ + return NULL; +} #endif /* CONFIG_OF */ #ifdef CONFIG_PCIEAER diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h index 29658c0ee71f..eef1eaafc03d 100644 --- a/include/linux/of_pci.h +++ b/include/linux/of_pci.h @@ -13,6 +13,14 @@ struct device_node *of_pci_find_child_device(struct device_node *parent, unsigned int devfn); int of_pci_get_devfn(struct device_node *np); void of_pci_check_probe_only(void); +bool of_pci_node_match_device(const struct device_node *node, + const struct pci_dev *dev); +const struct pci_device_id * +of_pci_node_match_id_table(const struct device_node *node, + const struct pci_device_id *table); +const struct pci_device_id * +of_pci_node_match_driver(const struct device_node *node, + struct pci_driver *drv); #else static inline struct device_node *of_pci_find_child_device(struct device_node *parent, unsigned int devfn) @@ -26,6 +34,23 @@ static inline int of_pci_get_devfn(struct device_node *np) } static inline void of_pci_check_probe_only(void) { } +static inline bool of_pci_node_match_device(const struct device_node *node, + const struct pci_dev *dev) +{ + return false; +} +static inline const struct pci_device_id * +of_pci_node_match_id_table(const struct device_node *node, + const struct pci_device_id *table) +{ + return NULL; +} +static inline const struct pci_device_id * +of_pci_node_match_driver(const struct device_node *node, + struct pci_driver *drv) +{ + return NULL; +} #endif #if IS_ENABLED(CONFIG_OF_IRQ) diff --git a/include/linux/pci.h b/include/linux/pci.h index adffd65e84b4..04c908d84b90 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1513,6 +1513,12 @@ void pci_unregister_driver(struct pci_driver *dev); builtin_driver(__pci_driver, pci_register_driver) struct pci_driver *pci_dev_driver(const struct pci_dev *dev); + +struct pci_dynid { + struct list_head node; + struct pci_device_id id; +}; + int pci_add_dynid(struct pci_driver *drv, unsigned int vendor, unsigned int device, unsigned int subvendor, unsigned int subdevice, From patchwork Thu Feb 2 07:55:23 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mad Horse X-Patchwork-Id: 13125477 X-Patchwork-Delegate: toke@redhat.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id EF85FC61DA4 for ; Thu, 2 Feb 2023 07:56:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232073AbjBBH4M (ORCPT ); Thu, 2 Feb 2023 02:56:12 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50962 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229972AbjBBH4J (ORCPT ); Thu, 2 Feb 2023 02:56:09 -0500 Received: from smtp161.vfemail.net (smtp161.vfemail.net [146.59.185.161]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 63A247CC9A for ; Wed, 1 Feb 2023 23:56:08 -0800 (PST) Received: (qmail 28618 invoked from network); 2 Feb 2023 07:56:06 +0000 Received: from localhost (HELO nl101-3.vfemail.net) () by smtpout.vfemail.net with ESMTPS (ECDHE-RSA-AES256-GCM-SHA384 encrypted); 2 Feb 2023 07:56:06 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=simple; d=openmail.cc; h=from:to:cc :subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; s=2018; bh=sKeY59QN/akZNkbUpqrT33k7Q 8PHn/p9w1ND4ntYpHI=; b=PTW1LnAjihB7I2VyeJ24YZ3xZOsA9FvC19B/rbQ85 im0rmXK3YTijdBSjSMElflLf59rIe4JqVVw52a2z3N79VxNYzxQJeHw3x0El7tng wToLGX7XpCM+3OWYfz1+gnzqaBuiSaEvlx1aBr6y6nSi5K3FykLwWSGC16n4nBGr Rg= Received: (qmail 14252 invoked from network); 2 Feb 2023 07:56:06 -0000 Received: by simscan 1.4.0 ppid: 13438, pid: 14227, t: 0.9892s scanners:none Received: from unknown (HELO bmwxMDEudmZlbWFpbC5uZXQ=) (ZXF1dUBvcGVubWFpbC5jYw==@MTkyLjE2OC4xLjE5Mg==) by nl101.vfemail.net with ESMTPA; 2 Feb 2023 07:56:05 -0000 From: equu@openmail.cc To: lpieralisi@kernel.org, toke@toke.dk, kvalo@kernel.org Cc: linux-pci@vger.kernel.org, robh@kernel.org, linux-wireless@vger.kernel.org, ath10k@lists.infradead.org, equu@openmail.cc Subject: [PATCH v3 2/3] wifi: ath9k: stop loading incompatible DT cal data Date: Thu, 2 Feb 2023 15:55:23 +0800 Message-Id: <20230202075524.2911058-3-equu@openmail.cc> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230202075524.2911058-1-equu@openmail.cc> References: <20230202075524.2911058-1-equu@openmail.cc> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Edward Chow As reported in https://github.com/openwrt/openwrt/pull/11345 , ath9k would load calibration data from a device tree node declared incompatible. Now, ath9k will first check whether the device tree node is compatible with it, using the functionality introduced with the first patch of this series, ("PCI: of: Match pci devices or drivers against OF DT nodes") and only proceed loading calibration data from compatible node. Signed-off-by: Edward Chow --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 + drivers/net/wireless/ath/ath9k/init.c | 26 ++++++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/pci.c | 2 +- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 2cc23605c9fc..4f6f0383a5f8 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -35,6 +35,7 @@ struct ath_node; struct ath_vif; extern struct ieee80211_ops ath9k_ops; +extern struct pci_driver ath_pci_driver; extern int ath9k_modparam_nohwcrypt; extern int ath9k_led_blink; extern bool is_ath9k_unloaded; diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 4f00400c7ffb..6c0296b4d366 100644 --- 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 @@ -577,6 +578,31 @@ static int ath9k_nvmem_request_eeprom(struct ath_softc *sc) size_t len; int err; + /* devm_nvmem_cell_get() will get a cell first from the OF + * DT node representing the given device with nvmem-cell-name + * "calibration", and from the global lookup table as a fallback, + * and an ath9k device could be either a pci one or a platform one. + * + * If the OF DT node is not compatible with the real device, the + * calibration data got from the node should not be applied. + * + * dev_is_pci(sc->dev) && ( no OF node || caldata not from node + * || not compatible ) -> do not use caldata . + * + * !dev_is_pci(sc->dev) -> always use caldata . + */ + if (dev_is_pci(sc->dev) && + (!sc->dev->of_node || + !of_property_match_string(sc->dev->of_node, + "nvmem-cell-names", + "calibration") || + !of_pci_node_match_driver(sc->dev->of_node, + &ath_pci_driver))) + /* follow the "just return 0;" convention as + * noted below. + */ + return 0; + cell = devm_nvmem_cell_get(sc->dev, "calibration"); if (IS_ERR(cell)) { err = PTR_ERR(cell); diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index a074e23013c5..fcb19761e60d 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -1074,7 +1074,7 @@ static SIMPLE_DEV_PM_OPS(ath9k_pm_ops, ath_pci_suspend, ath_pci_resume); MODULE_DEVICE_TABLE(pci, ath_pci_id_table); -static struct pci_driver ath_pci_driver = { +struct pci_driver ath_pci_driver = { .name = "ath9k", .id_table = ath_pci_id_table, .probe = ath_pci_probe, From patchwork Thu Feb 2 07:55:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mad Horse X-Patchwork-Id: 13125478 X-Patchwork-Delegate: kvalo@adurom.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D46CBC05027 for ; Thu, 2 Feb 2023 07:56:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229972AbjBBH4Q (ORCPT ); Thu, 2 Feb 2023 02:56:16 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51032 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232057AbjBBH4P (ORCPT ); Thu, 2 Feb 2023 02:56:15 -0500 Received: from smtp161.vfemail.net (smtp161.vfemail.net [146.59.185.161]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 59FFA7E6DB for ; Wed, 1 Feb 2023 23:56:13 -0800 (PST) Received: (qmail 28652 invoked from network); 2 Feb 2023 07:56:11 +0000 Received: from localhost (HELO nl101-3.vfemail.net) () by smtpout.vfemail.net with ESMTPS (ECDHE-RSA-AES256-GCM-SHA384 encrypted); 2 Feb 2023 07:56:11 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=simple; d=openmail.cc; h=from:to:cc :subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; s=2018; bh=vkUokFEKUNrbGw2Y9QiZUTjdI 5XpPUPWkM46g3tjAUg=; b=jzxun3AS3h401SZB1QhFYTNKTWxZXucf8QCVPEWQv tBgtwIhV5KPUTxlhml4SaAG2j1lFpoDmblZvlDYqJkdSRAX141IwwvLhAk0w9Il4 Te7KyiIpp3kV3EWU3xXrYfi5Gq7XjtN0MHXWrcFUo3t1O7meZFFztwYSVzP0ZtNH pQ= Received: (qmail 14461 invoked from network); 2 Feb 2023 07:56:11 -0000 Received: by simscan 1.4.0 ppid: 13438, pid: 14402, t: 0.9448s scanners:none Received: from unknown (HELO bmwxMDEudmZlbWFpbC5uZXQ=) (ZXF1dUBvcGVubWFpbC5jYw==@MTkyLjE2OC4xLjE5Mg==) by nl101.vfemail.net with ESMTPA; 2 Feb 2023 07:56:10 -0000 From: equu@openmail.cc To: lpieralisi@kernel.org, toke@toke.dk, kvalo@kernel.org Cc: linux-pci@vger.kernel.org, robh@kernel.org, linux-wireless@vger.kernel.org, ath10k@lists.infradead.org, equu@openmail.cc, kernel test robot Subject: [PATCH v3 3/3] wifi: ath10k: only load compatible DT cal data Date: Thu, 2 Feb 2023 15:55:24 +0800 Message-Id: <20230202075524.2911058-4-equu@openmail.cc> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230202075524.2911058-1-equu@openmail.cc> References: <20230202075524.2911058-1-equu@openmail.cc> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Edward Chow ath10k might also be sensitive to the issue reported on https://github.com/openwrt/openwrt/pull/11345 , loading calibration data from a device tree node declared incompatible. ath10k will first check whether the device tree node is compatible with it, using the functionality introduced with the first patch of this series, ("PCI: of: Match pci devices or drivers against OF DT nodes") and only proceed loading calibration data from compatible node. Signed-off-by: Edward Chow Reported-by: kernel test robot --- drivers/net/wireless/ath/ath10k/core.c | 29 ++++++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/pci.c | 2 +- drivers/net/wireless/ath/ath10k/pci.h | 2 ++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 5eb131ab916f..a1cf65679046 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "core.h" @@ -26,6 +27,7 @@ #include "testmode.h" #include "wmi-ops.h" #include "coredump.h" +#include "pci.h" unsigned int ath10k_debug_mask; EXPORT_SYMBOL(ath10k_debug_mask); @@ -1958,6 +1960,33 @@ static int ath10k_download_cal_nvmem(struct ath10k *ar, const char *cell_name) size_t len; int ret; + /* devm_nvmem_cell_get() will get a cell first from the OF + * DT node representing the given device with nvmem-cell-name + * "calibration", and from the global lookup table as a fallback, + * and an ath10k device could be either a pci one or a platform one. + * + * If the OF DT node is not compatible with the real device, the + * calibration data got from the node should not be applied. + * + * dev_is_pci(ar->dev) && ( no OF node || caldata not from node + * || not compatible ) -> do not use caldata . + * + * !dev_is_pci(ar->dev) -> always use caldata . + * + * The judgement for compatibility differs with ath9k for many + * DT using "qcom,ath10k" as compatibility string. + */ + if (dev_is_pci(ar->dev) && + (!ar->dev->of_node || + (of_property_match_string(ar->dev->of_node, + "nvmem-cell-names", + cell_name) < 0) || + !of_device_is_compatible(ar->dev->of_node, + "qcom,ath10k") || + !of_pci_node_match_driver(ar->dev->of_node, + &ath10k_pci_driver))) + return -ENOENT; + cell = devm_nvmem_cell_get(ar->dev, cell_name); if (IS_ERR(cell)) { ret = PTR_ERR(cell); diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 728d607289c3..5d9f6046f8cf 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -3780,7 +3780,7 @@ static SIMPLE_DEV_PM_OPS(ath10k_pci_pm_ops, ath10k_pci_pm_suspend, ath10k_pci_pm_resume); -static struct pci_driver ath10k_pci_driver = { +struct pci_driver ath10k_pci_driver = { .name = "ath10k_pci", .id_table = ath10k_pci_id_table, .probe = ath10k_pci_probe, diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index 480cd97ab739..de676797b736 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -209,6 +209,8 @@ static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar) #define DIAG_ACCESS_CE_TIMEOUT_US 10000 /* 10 ms */ #define DIAG_ACCESS_CE_WAIT_US 50 +extern struct pci_driver ath10k_pci_driver; + void ath10k_pci_write32(struct ath10k *ar, u32 offset, u32 value); void ath10k_pci_soc_write32(struct ath10k *ar, u32 addr, u32 val); void ath10k_pci_reg_write32(struct ath10k *ar, u32 addr, u32 val);