soc: qcom: ice: introduce devm_of_qcom_ice_get
authorTudor Ambarus <tudor.ambarus@linaro.org>
Fri, 17 Jan 2025 14:18:50 +0000 (14:18 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 2 May 2025 05:58:52 +0000 (07:58 +0200)
[ Upstream commit 1c13d6060d612601a61423f2e8fbf9e48126acca ]

Callers of of_qcom_ice_get() leak the device reference taken by
of_find_device_by_node(). Introduce devm variant for of_qcom_ice_get().
Existing consumers need the ICE instance for the entire life of their
device, thus exporting qcom_ice_put() is not required.

Signed-off-by: Tudor Ambarus <tudor.ambarus@linaro.org>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Reviewed-by: Abel Vesa <abel.vesa@linaro.org>
Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Link: https://lore.kernel.org/r/20250117-qcom-ice-fix-dev-leak-v2-1-1ffa5b6884cb@linaro.org
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
Stable-dep-of: cbef7442fba5 ("mmc: sdhci-msm: fix dev reference leaked through of_qcom_ice_get")
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/soc/qcom/ice.c
include/soc/qcom/ice.h

index 50be7a9274a14e847d8a63cc0ce4017cd2072292..9d89bfc50e8b8679691a9fe37218095be6844a13 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/cleanup.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
+#include <linux/device.h>
 #include <linux/iopoll.h>
 #include <linux/of.h>
 #include <linux/of_platform.h>
@@ -324,6 +325,53 @@ struct qcom_ice *of_qcom_ice_get(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(of_qcom_ice_get);
 
+static void qcom_ice_put(const struct qcom_ice *ice)
+{
+       struct platform_device *pdev = to_platform_device(ice->dev);
+
+       if (!platform_get_resource_byname(pdev, IORESOURCE_MEM, "ice"))
+               platform_device_put(pdev);
+}
+
+static void devm_of_qcom_ice_put(struct device *dev, void *res)
+{
+       qcom_ice_put(*(struct qcom_ice **)res);
+}
+
+/**
+ * devm_of_qcom_ice_get() - Devres managed helper to get an ICE instance from
+ * a DT node.
+ * @dev: device pointer for the consumer device.
+ *
+ * This function will provide an ICE instance either by creating one for the
+ * consumer device if its DT node provides the 'ice' reg range and the 'ice'
+ * clock (for legacy DT style). On the other hand, if consumer provides a
+ * phandle via 'qcom,ice' property to an ICE DT, the ICE instance will already
+ * be created and so this function will return that instead.
+ *
+ * Return: ICE pointer on success, NULL if there is no ICE data provided by the
+ * consumer or ERR_PTR() on error.
+ */
+struct qcom_ice *devm_of_qcom_ice_get(struct device *dev)
+{
+       struct qcom_ice *ice, **dr;
+
+       dr = devres_alloc(devm_of_qcom_ice_put, sizeof(*dr), GFP_KERNEL);
+       if (!dr)
+               return ERR_PTR(-ENOMEM);
+
+       ice = of_qcom_ice_get(dev);
+       if (!IS_ERR_OR_NULL(ice)) {
+               *dr = ice;
+               devres_add(dev, dr);
+       } else {
+               devres_free(dr);
+       }
+
+       return ice;
+}
+EXPORT_SYMBOL_GPL(devm_of_qcom_ice_get);
+
 static int qcom_ice_probe(struct platform_device *pdev)
 {
        struct qcom_ice *engine;
index 5870a94599a258b06b199dd2e7b7aa69fa86d94c..d5f6a228df6594a886003950a1e94c67aabe1db4 100644 (file)
@@ -34,4 +34,6 @@ int qcom_ice_program_key(struct qcom_ice *ice,
                         int slot);
 int qcom_ice_evict_key(struct qcom_ice *ice, int slot);
 struct qcom_ice *of_qcom_ice_get(struct device *dev);
+struct qcom_ice *devm_of_qcom_ice_get(struct device *dev);
+
 #endif /* __QCOM_ICE_H__ */