Merge tag 'sunxi-drivers-for-6.9-1' of https://git.kernel.org/pub/scm/linux/kernel...
authorArnd Bergmann <arnd@arndb.de>
Mon, 4 Mar 2024 15:51:58 +0000 (16:51 +0100)
committerArnd Bergmann <arnd@arndb.de>
Mon, 4 Mar 2024 15:51:58 +0000 (16:51 +0100)
- make sunxi_rsb_bus constant

* tag 'sunxi-drivers-for-6.9-1' of https://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux:
  bus: sunxi-rsb: make sunxi_rsb_bus const

Link: https://lore.kernel.org/r/20240223205734.GA9027@jernej-laptop
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
44 files changed:
Documentation/devicetree/bindings/i2c/i2c-exynos5.yaml
Documentation/devicetree/bindings/memory-controllers/nvidia,tegra20-emc.yaml
Documentation/devicetree/bindings/soc/samsung/samsung,exynos-sysreg.yaml
MAINTAINERS
drivers/bus/Kconfig
drivers/firmware/arm_ffa/bus.c
drivers/firmware/arm_scmi/bus.c
drivers/firmware/arm_scmi/clock.c
drivers/firmware/arm_scmi/common.h
drivers/firmware/arm_scmi/driver.c
drivers/firmware/arm_scmi/mailbox.c
drivers/firmware/arm_scmi/notify.c
drivers/firmware/arm_scmi/notify.h
drivers/firmware/arm_scmi/optee.c
drivers/firmware/arm_scmi/perf.c
drivers/firmware/arm_scmi/power.c
drivers/firmware/arm_scmi/powercap.c
drivers/firmware/arm_scmi/protocols.h
drivers/firmware/arm_scmi/raw_mode.c
drivers/firmware/arm_scmi/reset.c
drivers/firmware/arm_scmi/sensors.c
drivers/firmware/arm_scmi/shmem.c
drivers/firmware/arm_scmi/smc.c
drivers/firmware/arm_scmi/system.c
drivers/firmware/tegra/bpmp-debugfs.c
drivers/memory/emif.c
drivers/soc/mediatek/Kconfig
drivers/soc/mediatek/Makefile
drivers/soc/mediatek/mtk-socinfo.c [new file with mode: 0644]
drivers/soc/renesas/Kconfig
drivers/soc/renesas/rcar-rst.c
drivers/soc/renesas/renesas-soc.c
drivers/soc/tegra/Kconfig
drivers/soc/tegra/fuse/fuse-tegra.c
drivers/soc/tegra/fuse/fuse-tegra30.c
drivers/soc/tegra/fuse/fuse.h
drivers/soc/tegra/fuse/tegra-apbmisc.c
drivers/soc/tegra/pmc.c
include/linux/arm_ffa.h
include/linux/scmi_protocol.h
include/linux/string.h
include/soc/tegra/fuse.h
include/soc/tegra/pmc.h
mm/util.c

index df9c57bca2a89cd81ac4d6c8eabf4ec24f51a171..cc8bba5537b94b67b04cc5d791e882160a63530e 100644 (file)
@@ -33,6 +33,7 @@ properties:
           - const: samsung,exynos7-hsi2c
       - items:
           - enum:
+              - google,gs101-hsi2c
               - samsung,exynos850-hsi2c
           - const: samsung,exynosautov9-hsi2c
       - const: samsung,exynos5-hsi2c    # Exynos5250 and Exynos5420
index f54e553e6c0e6a6e4c0d56e485f567f814dcaf12..71896cb10692630a78c21bf9fdb81491680cdd31 100644 (file)
@@ -145,7 +145,7 @@ patternProperties:
   "^emc-table@[0-9]+$":
     $ref: "#/$defs/emc-table"
 
-  "^emc-tables@[a-z0-9-]+$":
+  "^emc-tables@[a-f0-9-]+$":
     type: object
     properties:
       reg:
index 1794e3799f2110e716c44ef8cd1da737eeb16270..c0c6ce8fc7863e1bc942fa3cea9c66906d1cdca1 100644 (file)
@@ -72,6 +72,8 @@ allOf:
         compatible:
           contains:
             enum:
+              - google,gs101-peric0-sysreg
+              - google,gs101-peric1-sysreg
               - samsung,exynos850-cmgp-sysreg
               - samsung,exynos850-peri-sysreg
               - samsung,exynos850-sysreg
index 8d1052fa6a6924d17a4d2681fa7907c544e35186..cd167276a0339e2db992c6cdb961bacb4ba580e7 100644 (file)
@@ -17339,7 +17339,6 @@ F:      Documentation/devicetree/bindings/pinctrl/renesas,*
 F:     drivers/pinctrl/renesas/
 
 PIN CONTROLLER - SAMSUNG
-M:     Tomasz Figa <tomasz.figa@gmail.com>
 M:     Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
 M:     Sylwester Nawrocki <s.nawrocki@samsung.com>
 R:     Alim Akhtar <alim.akhtar@samsung.com>
@@ -19388,7 +19387,6 @@ F:      drivers/media/platform/samsung/exynos4-is/
 SAMSUNG SOC CLOCK DRIVERS
 M:     Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
 M:     Sylwester Nawrocki <s.nawrocki@samsung.com>
-M:     Tomasz Figa <tomasz.figa@gmail.com>
 M:     Chanwoo Choi <cw00.choi@samsung.com>
 R:     Alim Akhtar <alim.akhtar@samsung.com>
 L:     linux-samsung-soc@vger.kernel.org
@@ -21321,6 +21319,7 @@ F:      drivers/clk/clk-sc[mp]i.c
 F:     drivers/cpufreq/sc[mp]i-cpufreq.c
 F:     drivers/firmware/arm_scmi/
 F:     drivers/firmware/arm_scpi.c
+F:     drivers/hwmon/scmi-hwmon.c
 F:     drivers/pmdomain/arm/
 F:     drivers/powercap/arm_scmi_powercap.c
 F:     drivers/regulator/scmi-regulator.c
index e6742998f372c4b2d7959a943938f10f44163bc0..d5e7fa9173a1690656494e09333cdf98099ba7c7 100644 (file)
@@ -186,11 +186,12 @@ config SUNXI_RSB
 
 config TEGRA_ACONNECT
        tristate "Tegra ACONNECT Bus Driver"
-       depends on ARCH_TEGRA_210_SOC
+       depends on ARCH_TEGRA
        depends on OF && PM
        help
          Driver for the Tegra ACONNECT bus which is used to interface with
-         the devices inside the Audio Processing Engine (APE) for Tegra210.
+         the devices inside the Audio Processing Engine (APE) for
+         Tegra210 and later.
 
 config TEGRA_GMI
        tristate "Tegra Generic Memory Interface bus driver"
index 1c7940ba55393f62050fde61d8f4fd71dce73233..2f557e90f2ebe57839f909e3910037f10b84a323 100644 (file)
@@ -105,7 +105,7 @@ static struct attribute *ffa_device_attributes_attrs[] = {
 };
 ATTRIBUTE_GROUPS(ffa_device_attributes);
 
-struct bus_type ffa_bus_type = {
+const struct bus_type ffa_bus_type = {
        .name           = "arm_ffa",
        .match          = ffa_device_match,
        .probe          = ffa_device_probe,
index c15928b8c5cc9976b9f12ca5a7e4154dcbb0e888..77c78be6e79c959fd8a15319b12e21e92e3f8c54 100644 (file)
@@ -141,6 +141,17 @@ out:
        return ret;
 }
 
+static int scmi_protocol_table_register(const struct scmi_device_id *id_table)
+{
+       int ret = 0;
+       const struct scmi_device_id *entry;
+
+       for (entry = id_table; entry->name && ret == 0; entry++)
+               ret = scmi_protocol_device_request(entry);
+
+       return ret;
+}
+
 /**
  * scmi_protocol_device_unrequest  - Helper to unrequest a device
  *
@@ -186,6 +197,15 @@ static void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table
        mutex_unlock(&scmi_requested_devices_mtx);
 }
 
+static void
+scmi_protocol_table_unregister(const struct scmi_device_id *id_table)
+{
+       const struct scmi_device_id *entry;
+
+       for (entry = id_table; entry->name; entry++)
+               scmi_protocol_device_unrequest(entry);
+}
+
 static const struct scmi_device_id *
 scmi_dev_match_id(struct scmi_device *scmi_dev, struct scmi_driver *scmi_drv)
 {
@@ -263,7 +283,7 @@ static void scmi_dev_remove(struct device *dev)
                scmi_drv->remove(scmi_dev);
 }
 
-struct bus_type scmi_bus_type = {
+const struct bus_type scmi_bus_type = {
        .name = "scmi_protocol",
        .match = scmi_dev_match,
        .probe = scmi_dev_probe,
@@ -279,7 +299,7 @@ int scmi_driver_register(struct scmi_driver *driver, struct module *owner,
        if (!driver->probe)
                return -EINVAL;
 
-       retval = scmi_protocol_device_request(driver->id_table);
+       retval = scmi_protocol_table_register(driver->id_table);
        if (retval)
                return retval;
 
@@ -299,7 +319,7 @@ EXPORT_SYMBOL_GPL(scmi_driver_register);
 void scmi_driver_unregister(struct scmi_driver *driver)
 {
        driver_unregister(&driver->driver);
-       scmi_protocol_device_unrequest(driver->id_table);
+       scmi_protocol_table_unregister(driver->id_table);
 }
 EXPORT_SYMBOL_GPL(scmi_driver_unregister);
 
index c0644558042a06dc1f0c087af4f22264abeee52a..134019297d08b1bd9e4b02e69a1cc16a3dc429b2 100644 (file)
@@ -13,7 +13,7 @@
 #include "notify.h"
 
 /* Updated only after ALL the mandatory features for that version are merged */
-#define SCMI_PROTOCOL_SUPPORTED_VERSION                0x20001
+#define SCMI_PROTOCOL_SUPPORTED_VERSION                0x30000
 
 enum scmi_clock_protocol_cmd {
        CLOCK_ATTRIBUTES = 0x3,
@@ -28,8 +28,13 @@ enum scmi_clock_protocol_cmd {
        CLOCK_POSSIBLE_PARENTS_GET = 0xC,
        CLOCK_PARENT_SET = 0xD,
        CLOCK_PARENT_GET = 0xE,
+       CLOCK_GET_PERMISSIONS = 0xF,
 };
 
+#define CLOCK_STATE_CONTROL_ALLOWED    BIT(31)
+#define CLOCK_PARENT_CONTROL_ALLOWED   BIT(30)
+#define CLOCK_RATE_CONTROL_ALLOWED     BIT(29)
+
 enum clk_state {
        CLK_STATE_DISABLE,
        CLK_STATE_ENABLE,
@@ -49,6 +54,8 @@ struct scmi_msg_resp_clock_attributes {
 #define SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(x)        ((x) & BIT(30))
 #define SUPPORTS_EXTENDED_NAMES(x)             ((x) & BIT(29))
 #define SUPPORTS_PARENT_CLOCK(x)               ((x) & BIT(28))
+#define SUPPORTS_EXTENDED_CONFIG(x)            ((x) & BIT(27))
+#define SUPPORTS_GET_PERMISSIONS(x)            ((x) & BIT(1))
        u8 name[SCMI_SHORT_NAME_MAX_SIZE];
        __le32 clock_enable_latency;
 };
@@ -152,14 +159,18 @@ struct clock_info {
        u32 version;
        int num_clocks;
        int max_async_req;
+       bool notify_rate_changed_cmd;
+       bool notify_rate_change_requested_cmd;
        atomic_t cur_async_req;
        struct scmi_clock_info *clk;
        int (*clock_config_set)(const struct scmi_protocol_handle *ph,
                                u32 clk_id, enum clk_state state,
-                               u8 oem_type, u32 oem_val, bool atomic);
+                               enum scmi_clock_oem_config oem_type,
+                               u32 oem_val, bool atomic);
        int (*clock_config_get)(const struct scmi_protocol_handle *ph,
-                               u32 clk_id, u8 oem_type, u32 *attributes,
-                               bool *enabled, u32 *oem_val, bool atomic);
+                               u32 clk_id, enum scmi_clock_oem_config oem_type,
+                               u32 *attributes, bool *enabled, u32 *oem_val,
+                               bool atomic);
 };
 
 static enum scmi_clock_protocol_cmd evt_2_cmd[] = {
@@ -167,6 +178,15 @@ static enum scmi_clock_protocol_cmd evt_2_cmd[] = {
        CLOCK_RATE_CHANGE_REQUESTED_NOTIFY,
 };
 
+static inline struct scmi_clock_info *
+scmi_clock_domain_lookup(struct clock_info *ci, u32 clk_id)
+{
+       if (clk_id >= ci->num_clocks)
+               return ERR_PTR(-EINVAL);
+
+       return ci->clk + clk_id;
+}
+
 static int
 scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph,
                                   struct clock_info *ci)
@@ -189,6 +209,17 @@ scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph,
        }
 
        ph->xops->xfer_put(ph, t);
+
+       if (!ret) {
+               if (!ph->hops->protocol_msg_check(ph, CLOCK_RATE_NOTIFY, NULL))
+                       ci->notify_rate_changed_cmd = true;
+
+               if (!ph->hops->protocol_msg_check(ph,
+                                                 CLOCK_RATE_CHANGE_REQUESTED_NOTIFY,
+                                                 NULL))
+                       ci->notify_rate_change_requested_cmd = true;
+       }
+
        return ret;
 }
 
@@ -284,14 +315,44 @@ static int scmi_clock_possible_parents(const struct scmi_protocol_handle *ph, u3
        return ret;
 }
 
+static int
+scmi_clock_get_permissions(const struct scmi_protocol_handle *ph, u32 clk_id,
+                          struct scmi_clock_info *clk)
+{
+       struct scmi_xfer *t;
+       u32 perm;
+       int ret;
+
+       ret = ph->xops->xfer_get_init(ph, CLOCK_GET_PERMISSIONS,
+                                     sizeof(clk_id), sizeof(perm), &t);
+       if (ret)
+               return ret;
+
+       put_unaligned_le32(clk_id, t->tx.buf);
+
+       ret = ph->xops->do_xfer(ph, t);
+       if (!ret) {
+               perm = get_unaligned_le32(t->rx.buf);
+
+               clk->state_ctrl_forbidden = !(perm & CLOCK_STATE_CONTROL_ALLOWED);
+               clk->rate_ctrl_forbidden = !(perm & CLOCK_RATE_CONTROL_ALLOWED);
+               clk->parent_ctrl_forbidden = !(perm & CLOCK_PARENT_CONTROL_ALLOWED);
+       }
+
+       ph->xops->xfer_put(ph, t);
+
+       return ret;
+}
+
 static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
-                                    u32 clk_id, struct scmi_clock_info *clk,
+                                    u32 clk_id, struct clock_info *cinfo,
                                     u32 version)
 {
        int ret;
        u32 attributes;
        struct scmi_xfer *t;
        struct scmi_msg_resp_clock_attributes *attr;
+       struct scmi_clock_info *clk = cinfo->clk + clk_id;
 
        ret = ph->xops->xfer_get_init(ph, CLOCK_ATTRIBUTES,
                                      sizeof(clk_id), sizeof(*attr), &t);
@@ -324,12 +385,20 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
                                                    NULL, clk->name,
                                                    SCMI_MAX_STR_SIZE);
 
-               if (SUPPORTS_RATE_CHANGED_NOTIF(attributes))
+               if (cinfo->notify_rate_changed_cmd &&
+                   SUPPORTS_RATE_CHANGED_NOTIF(attributes))
                        clk->rate_changed_notifications = true;
-               if (SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(attributes))
+               if (cinfo->notify_rate_change_requested_cmd &&
+                   SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(attributes))
                        clk->rate_change_requested_notifications = true;
-               if (SUPPORTS_PARENT_CLOCK(attributes))
-                       scmi_clock_possible_parents(ph, clk_id, clk);
+               if (PROTOCOL_REV_MAJOR(version) >= 0x3) {
+                       if (SUPPORTS_PARENT_CLOCK(attributes))
+                               scmi_clock_possible_parents(ph, clk_id, clk);
+                       if (SUPPORTS_GET_PERMISSIONS(attributes))
+                               scmi_clock_get_permissions(ph, clk_id, clk);
+                       if (SUPPORTS_EXTENDED_CONFIG(attributes))
+                               clk->extended_config = true;
+               }
        }
 
        return ret;
@@ -502,6 +571,14 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph,
        struct scmi_xfer *t;
        struct scmi_clock_set_rate *cfg;
        struct clock_info *ci = ph->get_priv(ph);
+       struct scmi_clock_info *clk;
+
+       clk = scmi_clock_domain_lookup(ci, clk_id);
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       if (clk->rate_ctrl_forbidden)
+               return -EACCES;
 
        ret = ph->xops->xfer_get_init(ph, CLOCK_RATE_SET, sizeof(*cfg), 0, &t);
        if (ret)
@@ -543,7 +620,8 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph,
 
 static int
 scmi_clock_config_set(const struct scmi_protocol_handle *ph, u32 clk_id,
-                     enum clk_state state, u8 __unused0, u32 __unused1,
+                     enum clk_state state,
+                     enum scmi_clock_oem_config __unused0, u32 __unused1,
                      bool atomic)
 {
        int ret;
@@ -580,14 +658,16 @@ scmi_clock_set_parent(const struct scmi_protocol_handle *ph, u32 clk_id,
        struct clock_info *ci = ph->get_priv(ph);
        struct scmi_clock_info *clk;
 
-       if (clk_id >= ci->num_clocks)
-               return -EINVAL;
-
-       clk = ci->clk + clk_id;
+       clk = scmi_clock_domain_lookup(ci, clk_id);
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
 
        if (parent_id >= clk->num_parents)
                return -EINVAL;
 
+       if (clk->parent_ctrl_forbidden)
+               return -EACCES;
+
        ret = ph->xops->xfer_get_init(ph, CLOCK_PARENT_SET,
                                      sizeof(*cfg), 0, &t);
        if (ret)
@@ -628,10 +708,11 @@ scmi_clock_get_parent(const struct scmi_protocol_handle *ph, u32 clk_id,
        return ret;
 }
 
-/* For SCMI clock v2.1 and onwards */
+/* For SCMI clock v3.0 and onwards */
 static int
 scmi_clock_config_set_v2(const struct scmi_protocol_handle *ph, u32 clk_id,
-                        enum clk_state state, u8 oem_type, u32 oem_val,
+                        enum clk_state state,
+                        enum scmi_clock_oem_config oem_type, u32 oem_val,
                         bool atomic)
 {
        int ret;
@@ -671,6 +752,14 @@ static int scmi_clock_enable(const struct scmi_protocol_handle *ph, u32 clk_id,
                             bool atomic)
 {
        struct clock_info *ci = ph->get_priv(ph);
+       struct scmi_clock_info *clk;
+
+       clk = scmi_clock_domain_lookup(ci, clk_id);
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       if (clk->state_ctrl_forbidden)
+               return -EACCES;
 
        return ci->clock_config_set(ph, clk_id, CLK_STATE_ENABLE,
                                    NULL_OEM_TYPE, 0, atomic);
@@ -680,16 +769,24 @@ static int scmi_clock_disable(const struct scmi_protocol_handle *ph, u32 clk_id,
                              bool atomic)
 {
        struct clock_info *ci = ph->get_priv(ph);
+       struct scmi_clock_info *clk;
+
+       clk = scmi_clock_domain_lookup(ci, clk_id);
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       if (clk->state_ctrl_forbidden)
+               return -EACCES;
 
        return ci->clock_config_set(ph, clk_id, CLK_STATE_DISABLE,
                                    NULL_OEM_TYPE, 0, atomic);
 }
 
-/* For SCMI clock v2.1 and onwards */
+/* For SCMI clock v3.0 and onwards */
 static int
 scmi_clock_config_get_v2(const struct scmi_protocol_handle *ph, u32 clk_id,
-                        u8 oem_type, u32 *attributes, bool *enabled,
-                        u32 *oem_val, bool atomic)
+                        enum scmi_clock_oem_config oem_type, u32 *attributes,
+                        bool *enabled, u32 *oem_val, bool atomic)
 {
        int ret;
        u32 flags;
@@ -730,8 +827,8 @@ scmi_clock_config_get_v2(const struct scmi_protocol_handle *ph, u32 clk_id,
 
 static int
 scmi_clock_config_get(const struct scmi_protocol_handle *ph, u32 clk_id,
-                     u8 oem_type, u32 *attributes, bool *enabled,
-                     u32 *oem_val, bool atomic)
+                     enum scmi_clock_oem_config oem_type, u32 *attributes,
+                     bool *enabled, u32 *oem_val, bool atomic)
 {
        int ret;
        struct scmi_xfer *t;
@@ -768,20 +865,38 @@ static int scmi_clock_state_get(const struct scmi_protocol_handle *ph,
 }
 
 static int scmi_clock_config_oem_set(const struct scmi_protocol_handle *ph,
-                                    u32 clk_id, u8 oem_type, u32 oem_val,
-                                    bool atomic)
+                                    u32 clk_id,
+                                    enum scmi_clock_oem_config oem_type,
+                                    u32 oem_val, bool atomic)
 {
        struct clock_info *ci = ph->get_priv(ph);
+       struct scmi_clock_info *clk;
+
+       clk = scmi_clock_domain_lookup(ci, clk_id);
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       if (!clk->extended_config)
+               return -EOPNOTSUPP;
 
        return ci->clock_config_set(ph, clk_id, CLK_STATE_UNCHANGED,
                                    oem_type, oem_val, atomic);
 }
 
 static int scmi_clock_config_oem_get(const struct scmi_protocol_handle *ph,
-                                    u32 clk_id, u8 oem_type, u32 *oem_val,
-                                    u32 *attributes, bool atomic)
+                                    u32 clk_id,
+                                    enum scmi_clock_oem_config oem_type,
+                                    u32 *oem_val, u32 *attributes, bool atomic)
 {
        struct clock_info *ci = ph->get_priv(ph);
+       struct scmi_clock_info *clk;
+
+       clk = scmi_clock_domain_lookup(ci, clk_id);
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       if (!clk->extended_config)
+               return -EOPNOTSUPP;
 
        return ci->clock_config_get(ph, clk_id, oem_type, attributes,
                                    NULL, oem_val, atomic);
@@ -800,10 +915,10 @@ scmi_clock_info_get(const struct scmi_protocol_handle *ph, u32 clk_id)
        struct scmi_clock_info *clk;
        struct clock_info *ci = ph->get_priv(ph);
 
-       if (clk_id >= ci->num_clocks)
+       clk = scmi_clock_domain_lookup(ci, clk_id);
+       if (IS_ERR(clk))
                return NULL;
 
-       clk = ci->clk + clk_id;
        if (!clk->name[0])
                return NULL;
 
@@ -824,6 +939,28 @@ static const struct scmi_clk_proto_ops clk_proto_ops = {
        .parent_get = scmi_clock_get_parent,
 };
 
+static bool scmi_clk_notify_supported(const struct scmi_protocol_handle *ph,
+                                     u8 evt_id, u32 src_id)
+{
+       bool supported;
+       struct scmi_clock_info *clk;
+       struct clock_info *ci = ph->get_priv(ph);
+
+       if (evt_id >= ARRAY_SIZE(evt_2_cmd))
+               return false;
+
+       clk = scmi_clock_domain_lookup(ci, src_id);
+       if (IS_ERR(clk))
+               return false;
+
+       if (evt_id == SCMI_EVENT_CLOCK_RATE_CHANGED)
+               supported = clk->rate_changed_notifications;
+       else
+               supported = clk->rate_change_requested_notifications;
+
+       return supported;
+}
+
 static int scmi_clk_rate_notify(const struct scmi_protocol_handle *ph,
                                u32 clk_id, int message_id, bool enable)
 {
@@ -908,6 +1045,7 @@ static const struct scmi_event clk_events[] = {
 };
 
 static const struct scmi_event_ops clk_event_ops = {
+       .is_notify_supported = scmi_clk_notify_supported,
        .get_num_sources = scmi_clk_get_num_sources,
        .set_notify_enabled = scmi_clk_set_notify_enabled,
        .fill_custom_report = scmi_clk_fill_custom_report,
@@ -949,13 +1087,12 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
        for (clkid = 0; clkid < cinfo->num_clocks; clkid++) {
                struct scmi_clock_info *clk = cinfo->clk + clkid;
 
-               ret = scmi_clock_attributes_get(ph, clkid, clk, version);
+               ret = scmi_clock_attributes_get(ph, clkid, cinfo, version);
                if (!ret)
                        scmi_clock_describe_rates_get(ph, clkid, clk);
        }
 
-       if (PROTOCOL_REV_MAJOR(version) >= 0x2 &&
-           PROTOCOL_REV_MINOR(version) >= 0x1) {
+       if (PROTOCOL_REV_MAJOR(version) >= 0x3) {
                cinfo->clock_config_set = scmi_clock_config_set_v2;
                cinfo->clock_config_get = scmi_clock_config_get_v2;
        } else {
index c46dc5215af7a7c8a78e0fe26c12fac51c8080b7..6affbfdd1dec1aeedb82dc53adbef3d182e33d02 100644 (file)
@@ -141,7 +141,7 @@ scmi_revision_area_get(const struct scmi_protocol_handle *ph);
 void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph,
                                     u8 *prot_imp);
 
-extern struct bus_type scmi_bus_type;
+extern const struct bus_type scmi_bus_type;
 
 #define SCMI_BUS_NOTIFY_DEVICE_REQUEST         0
 #define SCMI_BUS_NOTIFY_DEVICE_UNREQUEST       1
@@ -314,6 +314,7 @@ void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem,
 void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem);
 bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem,
                     struct scmi_xfer *xfer);
+bool shmem_channel_free(struct scmi_shared_mem __iomem *shmem);
 
 /* declarations for message passing transports */
 struct scmi_msg_payld;
index 3ea64b22cf0dfd4493c8179de90b81b822759342..34d77802c990ac796147359a9b5c7660ee85ee08 100644 (file)
@@ -86,6 +86,12 @@ struct scmi_xfers_info {
  * @users: A refcount to track effective users of this protocol.
  * @priv: Reference for optional protocol private data.
  * @version: Protocol version supported by the platform as detected at runtime.
+ * @negotiated_version: When the platform supports a newer protocol version,
+ *                     the agent will try to negotiate with the platform the
+ *                     usage of the newest version known to it, since
+ *                     backward compatibility is NOT automatically assured.
+ *                     This field is NON-zero when a successful negotiation
+ *                     has completed.
  * @ph: An embedded protocol handle that will be passed down to protocol
  *     initialization code to identify this instance.
  *
@@ -99,6 +105,7 @@ struct scmi_protocol_instance {
        refcount_t                      users;
        void                            *priv;
        unsigned int                    version;
+       unsigned int                    negotiated_version;
        struct scmi_protocol_handle     ph;
 };
 
@@ -1754,10 +1761,44 @@ static void scmi_common_fastchannel_db_ring(struct scmi_fc_db_info *db)
 #endif
 }
 
+/**
+ * scmi_protocol_msg_check  - Check protocol message attributes
+ *
+ * @ph: A reference to the protocol handle.
+ * @message_id: The ID of the message to check.
+ * @attributes: A parameter to optionally return the retrieved message
+ *             attributes, in case of Success.
+ *
+ * An helper to check protocol message attributes for a specific protocol
+ * and message pair.
+ *
+ * Return: 0 on SUCCESS
+ */
+static int scmi_protocol_msg_check(const struct scmi_protocol_handle *ph,
+                                  u32 message_id, u32 *attributes)
+{
+       int ret;
+       struct scmi_xfer *t;
+
+       ret = xfer_get_init(ph, PROTOCOL_MESSAGE_ATTRIBUTES,
+                           sizeof(__le32), 0, &t);
+       if (ret)
+               return ret;
+
+       put_unaligned_le32(message_id, t->tx.buf);
+       ret = do_xfer(ph, t);
+       if (!ret && attributes)
+               *attributes = get_unaligned_le32(t->rx.buf);
+       xfer_put(ph, t);
+
+       return ret;
+}
+
 static const struct scmi_proto_helpers_ops helpers_ops = {
        .extended_name_get = scmi_common_extended_name_get,
        .iter_response_init = scmi_iterator_init,
        .iter_response_run = scmi_iterator_run,
+       .protocol_msg_check = scmi_protocol_msg_check,
        .fastchannel_init = scmi_common_fastchannel_init,
        .fastchannel_db_ring = scmi_common_fastchannel_db_ring,
 };
@@ -1781,6 +1822,44 @@ scmi_revision_area_get(const struct scmi_protocol_handle *ph)
        return pi->handle->version;
 }
 
+/**
+ * scmi_protocol_version_negotiate  - Negotiate protocol version
+ *
+ * @ph: A reference to the protocol handle.
+ *
+ * An helper to negotiate a protocol version different from the latest
+ * advertised as supported from the platform: on Success backward
+ * compatibility is assured by the platform.
+ *
+ * Return: 0 on Success
+ */
+static int scmi_protocol_version_negotiate(struct scmi_protocol_handle *ph)
+{
+       int ret;
+       struct scmi_xfer *t;
+       struct scmi_protocol_instance *pi = ph_to_pi(ph);
+
+       /* At first check if NEGOTIATE_PROTOCOL_VERSION is supported ... */
+       ret = scmi_protocol_msg_check(ph, NEGOTIATE_PROTOCOL_VERSION, NULL);
+       if (ret)
+               return ret;
+
+       /* ... then attempt protocol version negotiation */
+       ret = xfer_get_init(ph, NEGOTIATE_PROTOCOL_VERSION,
+                           sizeof(__le32), 0, &t);
+       if (ret)
+               return ret;
+
+       put_unaligned_le32(pi->proto->supported_version, t->tx.buf);
+       ret = do_xfer(ph, t);
+       if (!ret)
+               pi->negotiated_version = pi->proto->supported_version;
+
+       xfer_put(ph, t);
+
+       return ret;
+}
+
 /**
  * scmi_alloc_init_protocol_instance  - Allocate and initialize a protocol
  * instance descriptor.
@@ -1853,11 +1932,21 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
        devres_close_group(handle->dev, pi->gid);
        dev_dbg(handle->dev, "Initialized protocol: 0x%X\n", pi->proto->id);
 
-       if (pi->version > proto->supported_version)
-               dev_warn(handle->dev,
-                        "Detected UNSUPPORTED higher version 0x%X for protocol 0x%X."
-                        "Backward compatibility is NOT assured.\n",
-                        pi->version, pi->proto->id);
+       if (pi->version > proto->supported_version) {
+               ret = scmi_protocol_version_negotiate(&pi->ph);
+               if (!ret) {
+                       dev_info(handle->dev,
+                                "Protocol 0x%X successfully negotiated version 0x%X\n",
+                                proto->id, pi->negotiated_version);
+               } else {
+                       dev_warn(handle->dev,
+                                "Detected UNSUPPORTED higher version 0x%X for protocol 0x%X.\n",
+                                pi->version, pi->proto->id);
+                       dev_warn(handle->dev,
+                                "Trying version 0x%X. Backward compatibility is NOT assured.\n",
+                                pi->proto->supported_version);
+               }
+       }
 
        return pi;
 
index 19246ed1f01ff7cc3ea7346402c32e02b57b336a..b8d470417e8f99bb6408aba541bc4b89541ddf7c 100644 (file)
@@ -45,6 +45,20 @@ static void rx_callback(struct mbox_client *cl, void *m)
 {
        struct scmi_mailbox *smbox = client_to_scmi_mailbox(cl);
 
+       /*
+        * An A2P IRQ is NOT valid when received while the platform still has
+        * the ownership of the channel, because the platform at first releases
+        * the SMT channel and then sends the completion interrupt.
+        *
+        * This addresses a possible race condition in which a spurious IRQ from
+        * a previous timed-out reply which arrived late could be wrongly
+        * associated with the next pending transaction.
+        */
+       if (cl->knows_txdone && !shmem_channel_free(smbox->shmem)) {
+               dev_warn(smbox->cinfo->dev, "Ignoring spurious A2P IRQ !\n");
+               return;
+       }
+
        scmi_rx_callback(smbox->cinfo, shmem_read_header(smbox->shmem), NULL);
 }
 
index 0efd20cd9d69d8586f04022cc96fe08270c207b4..27c52531194d0797ac0682f51bbad34a7cef479f 100644 (file)
@@ -99,6 +99,7 @@
 #define PROTO_ID_MASK          GENMASK(31, 24)
 #define EVT_ID_MASK            GENMASK(23, 16)
 #define SRC_ID_MASK            GENMASK(15, 0)
+#define NOTIF_UNSUPP           -1
 
 /*
  * Builds an unsigned 32bit key from the given input tuple to be used
@@ -788,6 +789,7 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,
 
        pd->ph = ph;
        for (i = 0; i < ee->num_events; i++, evt++) {
+               int id;
                struct scmi_registered_event *r_evt;
 
                r_evt = devm_kzalloc(ni->handle->dev, sizeof(*r_evt),
@@ -809,6 +811,11 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,
                if (!r_evt->report)
                        return -ENOMEM;
 
+               for (id = 0; id < r_evt->num_sources; id++)
+                       if (ee->ops->is_notify_supported &&
+                           !ee->ops->is_notify_supported(ph, r_evt->evt->id, id))
+                               refcount_set(&r_evt->sources[id], NOTIF_UNSUPP);
+
                pd->registered_events[i] = r_evt;
                /* Ensure events are updated */
                smp_wmb();
@@ -1166,7 +1173,13 @@ static inline int __scmi_enable_evt(struct scmi_registered_event *r_evt,
                        int ret = 0;
 
                        sid = &r_evt->sources[src_id];
-                       if (refcount_read(sid) == 0) {
+                       if (refcount_read(sid) == NOTIF_UNSUPP) {
+                               dev_dbg(r_evt->proto->ph->dev,
+                                       "Notification NOT supported - proto_id:%d  evt_id:%d  src_id:%d",
+                                       r_evt->proto->id, r_evt->evt->id,
+                                       src_id);
+                               ret = -EOPNOTSUPP;
+                       } else if (refcount_read(sid) == 0) {
                                ret = REVT_NOTIFY_ENABLE(r_evt, r_evt->evt->id,
                                                         src_id);
                                if (!ret)
@@ -1179,6 +1192,8 @@ static inline int __scmi_enable_evt(struct scmi_registered_event *r_evt,
        } else {
                for (; num_sources; src_id++, num_sources--) {
                        sid = &r_evt->sources[src_id];
+                       if (refcount_read(sid) == NOTIF_UNSUPP)
+                               continue;
                        if (refcount_dec_and_test(sid))
                                REVT_NOTIFY_DISABLE(r_evt,
                                                    r_evt->evt->id, src_id);
index 4e9b627edfefaa819f958be55b67e188b257c1ed..76758a736cf47ac24a41f2ac1f76d0f34aa56160 100644 (file)
@@ -35,6 +35,8 @@ struct scmi_protocol_handle;
 
 /**
  * struct scmi_event_ops  - Protocol helpers called by the notification core.
+ * @is_notify_supported: Return 0 if the specified notification for the
+ *                      specified resource (src_id) is supported.
  * @get_num_sources: Returns the number of possible events' sources for this
  *                  protocol
  * @set_notify_enabled: Enable/disable the required evt_id/src_id notifications
@@ -50,6 +52,8 @@ struct scmi_protocol_handle;
  *         process context.
  */
 struct scmi_event_ops {
+       bool (*is_notify_supported)(const struct scmi_protocol_handle *ph,
+                                   u8 evt_id, u32 src_id);
        int (*get_num_sources)(const struct scmi_protocol_handle *ph);
        int (*set_notify_enabled)(const struct scmi_protocol_handle *ph,
                                  u8 evt_id, u32 src_id, bool enabled);
index 25bfb465484d089479b4a48b4526a4473bac00f0..4e7944b91e3857a582b8d2f5cd40c29be31a7d37 100644 (file)
@@ -109,8 +109,10 @@ enum scmi_optee_pta_cmd {
  * @rx_len: Response size
  * @mu: Mutex protection on channel access
  * @cinfo: SCMI channel information
- * @shmem: Virtual base address of the shared memory
- * @req: Shared memory protocol handle for SCMI request and synchronous response
+ * @req: union for SCMI interface
+ * @req.shmem: Virtual base address of the shared memory
+ * @req.msg: Shared memory protocol handle for SCMI request and
+ *   synchronous response
  * @tee_shm: TEE shared memory handle @req or NULL if using IOMEM shmem
  * @link: Reference in agent's channel list
  */
index 8ea2a7b3d35d2029f9731ef3031d575609093d6e..981e327e63e38211ae657e8e28f284e9c0ea6f19 100644 (file)
@@ -182,6 +182,8 @@ struct scmi_perf_info {
        enum scmi_power_scale power_scale;
        u64 stats_addr;
        u32 stats_size;
+       bool notify_lvl_cmd;
+       bool notify_lim_cmd;
        struct perf_dom_info *dom_info;
 };
 
@@ -222,6 +224,15 @@ static int scmi_perf_attributes_get(const struct scmi_protocol_handle *ph,
        }
 
        ph->xops->xfer_put(ph, t);
+
+       if (!ret) {
+               if (!ph->hops->protocol_msg_check(ph, PERF_NOTIFY_LEVEL, NULL))
+                       pi->notify_lvl_cmd = true;
+
+               if (!ph->hops->protocol_msg_check(ph, PERF_NOTIFY_LIMITS, NULL))
+                       pi->notify_lim_cmd = true;
+       }
+
        return ret;
 }
 
@@ -239,6 +250,7 @@ static void scmi_perf_xa_destroy(void *data)
 static int
 scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
                                struct perf_dom_info *dom_info,
+                               bool notify_lim_cmd, bool notify_lvl_cmd,
                                u32 version)
 {
        int ret;
@@ -260,8 +272,12 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
 
                dom_info->set_limits = SUPPORTS_SET_LIMITS(flags);
                dom_info->info.set_perf = SUPPORTS_SET_PERF_LVL(flags);
-               dom_info->perf_limit_notify = SUPPORTS_PERF_LIMIT_NOTIFY(flags);
-               dom_info->perf_level_notify = SUPPORTS_PERF_LEVEL_NOTIFY(flags);
+               if (notify_lim_cmd)
+                       dom_info->perf_limit_notify =
+                               SUPPORTS_PERF_LIMIT_NOTIFY(flags);
+               if (notify_lvl_cmd)
+                       dom_info->perf_level_notify =
+                               SUPPORTS_PERF_LEVEL_NOTIFY(flags);
                dom_info->perf_fastchannels = SUPPORTS_PERF_FASTCHANNELS(flags);
                if (PROTOCOL_REV_MAJOR(version) >= 0x4)
                        dom_info->level_indexing_mode =
@@ -270,15 +286,30 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
                                        le32_to_cpu(attr->sustained_freq_khz);
                dom_info->sustained_perf_level =
                                        le32_to_cpu(attr->sustained_perf_level);
+               /*
+                * sustained_freq_khz = mult_factor * sustained_perf_level
+                * mult_factor must be non zero positive integer(not fraction)
+                */
                if (!dom_info->sustained_freq_khz ||
                    !dom_info->sustained_perf_level ||
-                   dom_info->level_indexing_mode)
+                   dom_info->level_indexing_mode) {
                        /* CPUFreq converts to kHz, hence default 1000 */
                        dom_info->mult_factor = 1000;
-               else
+               } else {
                        dom_info->mult_factor =
                                        (dom_info->sustained_freq_khz * 1000UL)
                                        / dom_info->sustained_perf_level;
+                       if ((dom_info->sustained_freq_khz * 1000UL) %
+                           dom_info->sustained_perf_level)
+                               dev_warn(ph->dev,
+                                        "multiplier for domain %d rounded\n",
+                                        dom_info->id);
+               }
+               if (!dom_info->mult_factor)
+                       dev_warn(ph->dev,
+                                "Wrong sustained perf/frequency(domain %d)\n",
+                                dom_info->id);
+
                strscpy(dom_info->info.name, attr->name,
                        SCMI_SHORT_NAME_MAX_SIZE);
        }
@@ -295,9 +326,9 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
                                            dom_info->id, NULL, dom_info->info.name,
                                            SCMI_MAX_STR_SIZE);
 
+       xa_init(&dom_info->opps_by_lvl);
        if (dom_info->level_indexing_mode) {
                xa_init(&dom_info->opps_by_idx);
-               xa_init(&dom_info->opps_by_lvl);
                hash_init(dom_info->opps_by_freq);
        }
 
@@ -340,32 +371,52 @@ static int iter_perf_levels_update_state(struct scmi_iterator_state *st,
 }
 
 static inline void
-process_response_opp(struct scmi_opp *opp, unsigned int loop_idx,
+process_response_opp(struct device *dev, struct perf_dom_info *dom,
+                    struct scmi_opp *opp, unsigned int loop_idx,
                     const struct scmi_msg_resp_perf_describe_levels *r)
 {
+       int ret;
+
        opp->perf = le32_to_cpu(r->opp[loop_idx].perf_val);
        opp->power = le32_to_cpu(r->opp[loop_idx].power);
        opp->trans_latency_us =
                le16_to_cpu(r->opp[loop_idx].transition_latency_us);
+
+       ret = xa_insert(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL);
+       if (ret)
+               dev_warn(dev, "Failed to add opps_by_lvl at %d - ret:%d\n",
+                        opp->perf, ret);
 }
 
 static inline void
-process_response_opp_v4(struct perf_dom_info *dom, struct scmi_opp *opp,
-                       unsigned int loop_idx,
+process_response_opp_v4(struct device *dev, struct perf_dom_info *dom,
+                       struct scmi_opp *opp, unsigned int loop_idx,
                        const struct scmi_msg_resp_perf_describe_levels_v4 *r)
 {
+       int ret;
+
        opp->perf = le32_to_cpu(r->opp[loop_idx].perf_val);
        opp->power = le32_to_cpu(r->opp[loop_idx].power);
        opp->trans_latency_us =
                le16_to_cpu(r->opp[loop_idx].transition_latency_us);
 
+       ret = xa_insert(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL);
+       if (ret)
+               dev_warn(dev, "Failed to add opps_by_lvl at %d - ret:%d\n",
+                        opp->perf, ret);
+
        /* Note that PERF v4 reports always five 32-bit words */
        opp->indicative_freq = le32_to_cpu(r->opp[loop_idx].indicative_freq);
        if (dom->level_indexing_mode) {
                opp->level_index = le32_to_cpu(r->opp[loop_idx].level_index);
 
-               xa_store(&dom->opps_by_idx, opp->level_index, opp, GFP_KERNEL);
-               xa_store(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL);
+               ret = xa_insert(&dom->opps_by_idx, opp->level_index, opp,
+                               GFP_KERNEL);
+               if (ret)
+                       dev_warn(dev,
+                                "Failed to add opps_by_idx at %d - ret:%d\n",
+                                opp->level_index, ret);
+
                hash_add(dom->opps_by_freq, &opp->hash, opp->indicative_freq);
        }
 }
@@ -380,9 +431,10 @@ iter_perf_levels_process_response(const struct scmi_protocol_handle *ph,
 
        opp = &p->perf_dom->opp[st->desc_index + st->loop_idx];
        if (PROTOCOL_REV_MAJOR(p->version) <= 0x3)
-               process_response_opp(opp, st->loop_idx, response);
+               process_response_opp(ph->dev, p->perf_dom, opp, st->loop_idx,
+                                    response);
        else
-               process_response_opp_v4(p->perf_dom, opp, st->loop_idx,
+               process_response_opp_v4(ph->dev, p->perf_dom, opp, st->loop_idx,
                                        response);
        p->perf_dom->opp_count++;
 
@@ -965,6 +1017,27 @@ static const struct scmi_perf_proto_ops perf_proto_ops = {
        .power_scale_get = scmi_power_scale_get,
 };
 
+static bool scmi_perf_notify_supported(const struct scmi_protocol_handle *ph,
+                                      u8 evt_id, u32 src_id)
+{
+       bool supported;
+       struct perf_dom_info *dom;
+
+       if (evt_id >= ARRAY_SIZE(evt_2_cmd))
+               return false;
+
+       dom = scmi_perf_domain_lookup(ph, src_id);
+       if (IS_ERR(dom))
+               return false;
+
+       if (evt_id == SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED)
+               supported = dom->perf_limit_notify;
+       else
+               supported = dom->perf_level_notify;
+
+       return supported;
+}
+
 static int scmi_perf_set_notify_enabled(const struct scmi_protocol_handle *ph,
                                        u8 evt_id, u32 src_id, bool enable)
 {
@@ -982,18 +1055,47 @@ static int scmi_perf_set_notify_enabled(const struct scmi_protocol_handle *ph,
        return ret;
 }
 
+static int
+scmi_perf_xlate_opp_to_freq(struct perf_dom_info *dom,
+                           unsigned int index, unsigned long *freq)
+{
+       struct scmi_opp *opp;
+
+       if (!dom || !freq)
+               return -EINVAL;
+
+       if (!dom->level_indexing_mode) {
+               opp = xa_load(&dom->opps_by_lvl, index);
+               if (!opp)
+                       return -ENODEV;
+
+               *freq = opp->perf * dom->mult_factor;
+       } else {
+               opp = xa_load(&dom->opps_by_idx, index);
+               if (!opp)
+                       return -ENODEV;
+
+               *freq = opp->indicative_freq * dom->mult_factor;
+       }
+
+       return 0;
+}
+
 static void *scmi_perf_fill_custom_report(const struct scmi_protocol_handle *ph,
                                          u8 evt_id, ktime_t timestamp,
                                          const void *payld, size_t payld_sz,
                                          void *report, u32 *src_id)
 {
+       int ret;
        void *rep = NULL;
+       struct perf_dom_info *dom;
 
        switch (evt_id) {
        case SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED:
        {
                const struct scmi_perf_limits_notify_payld *p = payld;
                struct scmi_perf_limits_report *r = report;
+               unsigned long freq_min, freq_max;
 
                if (sizeof(*p) != payld_sz)
                        break;
@@ -1003,14 +1105,36 @@ static void *scmi_perf_fill_custom_report(const struct scmi_protocol_handle *ph,
                r->domain_id = le32_to_cpu(p->domain_id);
                r->range_max = le32_to_cpu(p->range_max);
                r->range_min = le32_to_cpu(p->range_min);
+               /* Check if the reported domain exist at all */
+               dom = scmi_perf_domain_lookup(ph, r->domain_id);
+               if (IS_ERR(dom))
+                       break;
+               /*
+                * Event will be reported from this point on...
+                * ...even if, later, xlated frequencies were not retrieved.
+                */
                *src_id = r->domain_id;
                rep = r;
+
+               ret = scmi_perf_xlate_opp_to_freq(dom, r->range_max, &freq_max);
+               if (ret)
+                       break;
+
+               ret = scmi_perf_xlate_opp_to_freq(dom, r->range_min, &freq_min);
+               if (ret)
+                       break;
+
+               /* Report translated freqs ONLY if both available */
+               r->range_max_freq = freq_max;
+               r->range_min_freq = freq_min;
+
                break;
        }
        case SCMI_EVENT_PERFORMANCE_LEVEL_CHANGED:
        {
                const struct scmi_perf_level_notify_payld *p = payld;
                struct scmi_perf_level_report *r = report;
+               unsigned long freq;
 
                if (sizeof(*p) != payld_sz)
                        break;
@@ -1018,9 +1142,27 @@ static void *scmi_perf_fill_custom_report(const struct scmi_protocol_handle *ph,
                r->timestamp = timestamp;
                r->agent_id = le32_to_cpu(p->agent_id);
                r->domain_id = le32_to_cpu(p->domain_id);
+               /* Report translated freqs ONLY if available */
                r->performance_level = le32_to_cpu(p->performance_level);
+               /* Check if the reported domain exist at all */
+               dom = scmi_perf_domain_lookup(ph, r->domain_id);
+               if (IS_ERR(dom))
+                       break;
+               /*
+                * Event will be reported from this point on...
+                * ...even if, later, xlated frequencies were not retrieved.
+                */
                *src_id = r->domain_id;
                rep = r;
+
+               /* Report translated freqs ONLY if available */
+               ret = scmi_perf_xlate_opp_to_freq(dom, r->performance_level,
+                                                 &freq);
+               if (ret)
+                       break;
+
+               r->performance_level_freq = freq;
+
                break;
        }
        default:
@@ -1054,6 +1196,7 @@ static const struct scmi_event perf_events[] = {
 };
 
 static const struct scmi_event_ops perf_event_ops = {
+       .is_notify_supported = scmi_perf_notify_supported,
        .get_num_sources = scmi_perf_get_num_sources,
        .set_notify_enabled = scmi_perf_set_notify_enabled,
        .fill_custom_report = scmi_perf_fill_custom_report,
@@ -1098,7 +1241,8 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph)
                struct perf_dom_info *dom = pinfo->dom_info + domain;
 
                dom->id = domain;
-               scmi_perf_domain_attributes_get(ph, dom, version);
+               scmi_perf_domain_attributes_get(ph, dom, pinfo->notify_lim_cmd,
+                                               pinfo->notify_lvl_cmd, version);
                scmi_perf_describe_levels_get(ph, dom, version);
 
                if (dom->perf_fastchannels)
index c2e6b9b4d941cd4e9e5ba9032be4b23006fc435a..49666bd1d8ac0c4efcba123aa365416068a3b50c 100644 (file)
@@ -68,6 +68,7 @@ struct power_dom_info {
 
 struct scmi_power_info {
        u32 version;
+       bool notify_state_change_cmd;
        int num_domains;
        u64 stats_addr;
        u32 stats_size;
@@ -97,13 +98,18 @@ static int scmi_power_attributes_get(const struct scmi_protocol_handle *ph,
        }
 
        ph->xops->xfer_put(ph, t);
+
+       if (!ret)
+               if (!ph->hops->protocol_msg_check(ph, POWER_STATE_NOTIFY, NULL))
+                       pi->notify_state_change_cmd = true;
+
        return ret;
 }
 
 static int
 scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
                                 u32 domain, struct power_dom_info *dom_info,
-                                u32 version)
+                                u32 version, bool notify_state_change_cmd)
 {
        int ret;
        u32 flags;
@@ -122,7 +128,9 @@ scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
        if (!ret) {
                flags = le32_to_cpu(attr->flags);
 
-               dom_info->state_set_notify = SUPPORTS_STATE_SET_NOTIFY(flags);
+               if (notify_state_change_cmd)
+                       dom_info->state_set_notify =
+                               SUPPORTS_STATE_SET_NOTIFY(flags);
                dom_info->state_set_async = SUPPORTS_STATE_SET_ASYNC(flags);
                dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags);
                strscpy(dom_info->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE);
@@ -231,6 +239,20 @@ static int scmi_power_request_notify(const struct scmi_protocol_handle *ph,
        return ret;
 }
 
+static bool scmi_power_notify_supported(const struct scmi_protocol_handle *ph,
+                                       u8 evt_id, u32 src_id)
+{
+       struct power_dom_info *dom;
+       struct scmi_power_info *pinfo = ph->get_priv(ph);
+
+       if (evt_id != SCMI_EVENT_POWER_STATE_CHANGED ||
+           src_id >= pinfo->num_domains)
+               return false;
+
+       dom = pinfo->dom_info + src_id;
+       return dom->state_set_notify;
+}
+
 static int scmi_power_set_notify_enabled(const struct scmi_protocol_handle *ph,
                                         u8 evt_id, u32 src_id, bool enable)
 {
@@ -285,6 +307,7 @@ static const struct scmi_event power_events[] = {
 };
 
 static const struct scmi_event_ops power_event_ops = {
+       .is_notify_supported = scmi_power_notify_supported,
        .get_num_sources = scmi_power_get_num_sources,
        .set_notify_enabled = scmi_power_set_notify_enabled,
        .fill_custom_report = scmi_power_fill_custom_report,
@@ -326,7 +349,8 @@ static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph)
        for (domain = 0; domain < pinfo->num_domains; domain++) {
                struct power_dom_info *dom = pinfo->dom_info + domain;
 
-               scmi_power_domain_attributes_get(ph, domain, dom, version);
+               scmi_power_domain_attributes_get(ph, domain, dom, version,
+                                                pinfo->notify_state_change_cmd);
        }
 
        pinfo->version = version;
index a4c6cd4716fe4497681a76e5fc662f7fb670e0c5..2fab92367e42f8eb454f5b38383789673914e4e9 100644 (file)
@@ -124,6 +124,8 @@ struct scmi_powercap_state {
 struct powercap_info {
        u32 version;
        int num_domains;
+       bool notify_cap_cmd;
+       bool notify_measurements_cmd;
        struct scmi_powercap_state *states;
        struct scmi_powercap_info *powercaps;
 };
@@ -157,6 +159,18 @@ scmi_powercap_attributes_get(const struct scmi_protocol_handle *ph,
        }
 
        ph->xops->xfer_put(ph, t);
+
+       if (!ret) {
+               if (!ph->hops->protocol_msg_check(ph,
+                                                 POWERCAP_CAP_NOTIFY, NULL))
+                       pi->notify_cap_cmd = true;
+
+               if (!ph->hops->protocol_msg_check(ph,
+                                                 POWERCAP_MEASUREMENTS_NOTIFY,
+                                                 NULL))
+                       pi->notify_measurements_cmd = true;
+       }
+
        return ret;
 }
 
@@ -200,10 +214,12 @@ scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
                flags = le32_to_cpu(resp->attributes);
 
                dom_info->id = domain;
-               dom_info->notify_powercap_cap_change =
-                       SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);
-               dom_info->notify_powercap_measurement_change =
-                       SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
+               if (pinfo->notify_cap_cmd)
+                       dom_info->notify_powercap_cap_change =
+                               SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);
+               if (pinfo->notify_measurements_cmd)
+                       dom_info->notify_powercap_measurement_change =
+                               SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
                dom_info->async_powercap_cap_set =
                        SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags);
                dom_info->powercap_cap_config =
@@ -788,6 +804,26 @@ static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
        return ret;
 }
 
+static bool
+scmi_powercap_notify_supported(const struct scmi_protocol_handle *ph,
+                              u8 evt_id, u32 src_id)
+{
+       bool supported = false;
+       const struct scmi_powercap_info *dom_info;
+       struct powercap_info *pi = ph->get_priv(ph);
+
+       if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains)
+               return false;
+
+       dom_info = pi->powercaps + src_id;
+       if (evt_id == SCMI_EVENT_POWERCAP_CAP_CHANGED)
+               supported = dom_info->notify_powercap_cap_change;
+       else if (evt_id == SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED)
+               supported = dom_info->notify_powercap_measurement_change;
+
+       return supported;
+}
+
 static int
 scmi_powercap_set_notify_enabled(const struct scmi_protocol_handle *ph,
                                 u8 evt_id, u32 src_id, bool enable)
@@ -904,6 +940,7 @@ static const struct scmi_event powercap_events[] = {
 };
 
 static const struct scmi_event_ops powercap_event_ops = {
+       .is_notify_supported = scmi_powercap_notify_supported,
        .get_num_sources = scmi_powercap_get_num_sources,
        .set_notify_enabled = scmi_powercap_set_notify_enabled,
        .fill_custom_report = scmi_powercap_fill_custom_report,
index e683c26f24eb0cffac8a817c6d8162a61ae1d4c2..693019fff0f67e52cbd120b3e56eb237fa7dd1c1 100644 (file)
@@ -33,6 +33,7 @@ enum scmi_common_cmd {
        PROTOCOL_VERSION = 0x0,
        PROTOCOL_ATTRIBUTES = 0x1,
        PROTOCOL_MESSAGE_ATTRIBUTES = 0x2,
+       NEGOTIATE_PROTOCOL_VERSION = 0x10,
 };
 
 /**
@@ -251,6 +252,8 @@ struct scmi_fc_info {
  *                     provided in @ops.
  * @iter_response_run: A common helper to trigger the run of a previously
  *                    initialized iterator.
+ * @protocol_msg_check: A common helper to check is a specific protocol message
+ *                     is supported.
  * @fastchannel_init: A common helper used to initialize FC descriptors by
  *                   gathering FC descriptions from the SCMI platform server.
  * @fastchannel_db_ring: A common helper to ring a FC doorbell.
@@ -264,6 +267,8 @@ struct scmi_proto_helpers_ops {
                                    unsigned int max_resources, u8 msg_id,
                                    size_t tx_size, void *priv);
        int (*iter_response_run)(void *iter);
+       int (*protocol_msg_check)(const struct scmi_protocol_handle *ph,
+                                 u32 message_id, u32 *attributes);
        void (*fastchannel_init)(const struct scmi_protocol_handle *ph,
                                 u8 describe_id, u32 message_id,
                                 u32 valid_size, u32 domain,
index 0493aa3c12bf5363e02c1ecc9b2520d1bd8b3d67..350573518503355f6abaa4d24cbcac6368e8930c 100644 (file)
@@ -1111,7 +1111,6 @@ static int scmi_raw_mode_setup(struct scmi_raw_mode_info *raw,
                int i;
 
                for (i = 0; i < num_chans; i++) {
-                       void *xret;
                        struct scmi_raw_queue *q;
 
                        q = scmi_raw_queue_init(raw);
@@ -1120,13 +1119,12 @@ static int scmi_raw_mode_setup(struct scmi_raw_mode_info *raw,
                                goto err_xa;
                        }
 
-                       xret = xa_store(&raw->chans_q, channels[i], q,
+                       ret = xa_insert(&raw->chans_q, channels[i], q,
                                        GFP_KERNEL);
-                       if (xa_err(xret)) {
+                       if (ret) {
                                dev_err(dev,
                                        "Fail to allocate Raw queue 0x%02X\n",
                                        channels[i]);
-                               ret = xa_err(xret);
                                goto err_xa;
                        }
                }
@@ -1322,6 +1320,12 @@ void scmi_raw_message_report(void *r, struct scmi_xfer *xfer,
        dev = raw->handle->dev;
        q = scmi_raw_queue_select(raw, idx,
                                  SCMI_XFER_IS_CHAN_SET(xfer) ? chan_id : 0);
+       if (!q) {
+               dev_warn(dev,
+                        "RAW[%d] - NO queue for chan 0x%X. Dropping report.\n",
+                        idx, chan_id);
+               return;
+       }
 
        /*
         * Grab the msg_q_lock upfront to avoid a possible race between
index 19970d9f9e3677f90c17c3a016c040e541112a51..1b318316535ec4d5c5dc8c454a0a0d6d15155694 100644 (file)
@@ -67,6 +67,7 @@ struct reset_dom_info {
 struct scmi_reset_info {
        u32 version;
        int num_domains;
+       bool notify_reset_cmd;
        struct reset_dom_info *dom_info;
 };
 
@@ -89,18 +90,24 @@ static int scmi_reset_attributes_get(const struct scmi_protocol_handle *ph,
        }
 
        ph->xops->xfer_put(ph, t);
+
+       if (!ret)
+               if (!ph->hops->protocol_msg_check(ph, RESET_NOTIFY, NULL))
+                       pi->notify_reset_cmd = true;
+
        return ret;
 }
 
 static int
 scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
-                                u32 domain, struct reset_dom_info *dom_info,
-                                u32 version)
+                                struct scmi_reset_info *pinfo,
+                                u32 domain, u32 version)
 {
        int ret;
        u32 attributes;
        struct scmi_xfer *t;
        struct scmi_msg_resp_reset_domain_attributes *attr;
+       struct reset_dom_info *dom_info = pinfo->dom_info + domain;
 
        ret = ph->xops->xfer_get_init(ph, RESET_DOMAIN_ATTRIBUTES,
                                      sizeof(domain), sizeof(*attr), &t);
@@ -115,7 +122,9 @@ scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
                attributes = le32_to_cpu(attr->attributes);
 
                dom_info->async_reset = SUPPORTS_ASYNC_RESET(attributes);
-               dom_info->reset_notify = SUPPORTS_NOTIFY_RESET(attributes);
+               if (pinfo->notify_reset_cmd)
+                       dom_info->reset_notify =
+                               SUPPORTS_NOTIFY_RESET(attributes);
                dom_info->latency_us = le32_to_cpu(attr->latency);
                if (dom_info->latency_us == U32_MAX)
                        dom_info->latency_us = 0;
@@ -226,6 +235,20 @@ static const struct scmi_reset_proto_ops reset_proto_ops = {
        .deassert = scmi_reset_domain_deassert,
 };
 
+static bool scmi_reset_notify_supported(const struct scmi_protocol_handle *ph,
+                                       u8 evt_id, u32 src_id)
+{
+       struct reset_dom_info *dom;
+       struct scmi_reset_info *pi = ph->get_priv(ph);
+
+       if (evt_id != SCMI_EVENT_RESET_ISSUED || src_id >= pi->num_domains)
+               return false;
+
+       dom = pi->dom_info + src_id;
+
+       return dom->reset_notify;
+}
+
 static int scmi_reset_notify(const struct scmi_protocol_handle *ph,
                             u32 domain_id, bool enable)
 {
@@ -301,6 +324,7 @@ static const struct scmi_event reset_events[] = {
 };
 
 static const struct scmi_event_ops reset_event_ops = {
+       .is_notify_supported = scmi_reset_notify_supported,
        .get_num_sources = scmi_reset_get_num_sources,
        .set_notify_enabled = scmi_reset_set_notify_enabled,
        .fill_custom_report = scmi_reset_fill_custom_report,
@@ -339,11 +363,8 @@ static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)
        if (!pinfo->dom_info)
                return -ENOMEM;
 
-       for (domain = 0; domain < pinfo->num_domains; domain++) {
-               struct reset_dom_info *dom = pinfo->dom_info + domain;
-
-               scmi_reset_domain_attributes_get(ph, domain, dom, version);
-       }
+       for (domain = 0; domain < pinfo->num_domains; domain++)
+               scmi_reset_domain_attributes_get(ph, pinfo, domain, version);
 
        pinfo->version = version;
        return ph->set_priv(ph, pinfo, version);
index 3111499653704695d249a877a0defdf38aef89d4..7fc5535ca34c710f7154ff55c5e53b9e655a828e 100644 (file)
@@ -215,6 +215,8 @@ struct scmi_sensor_update_notify_payld {
 
 struct sensors_info {
        u32 version;
+       bool notify_trip_point_cmd;
+       bool notify_continuos_update_cmd;
        int num_sensors;
        int max_requests;
        u64 reg_addr;
@@ -246,6 +248,18 @@ static int scmi_sensor_attributes_get(const struct scmi_protocol_handle *ph,
        }
 
        ph->xops->xfer_put(ph, t);
+
+       if (!ret) {
+               if (!ph->hops->protocol_msg_check(ph,
+                                                 SENSOR_TRIP_POINT_NOTIFY, NULL))
+                       si->notify_trip_point_cmd = true;
+
+               if (!ph->hops->protocol_msg_check(ph,
+                                                 SENSOR_CONTINUOUS_UPDATE_NOTIFY,
+                                                 NULL))
+                       si->notify_continuos_update_cmd = true;
+       }
+
        return ret;
 }
 
@@ -594,7 +608,8 @@ iter_sens_descr_process_response(const struct scmi_protocol_handle *ph,
         * Such bitfields are assumed to be zeroed on non
         * relevant fw versions...assuming fw not buggy !
         */
-       s->update = SUPPORTS_UPDATE_NOTIFY(attrl);
+       if (si->notify_continuos_update_cmd)
+               s->update = SUPPORTS_UPDATE_NOTIFY(attrl);
        s->timestamped = SUPPORTS_TIMESTAMP(attrl);
        if (s->timestamped)
                s->tstamp_scale = S32_EXT(SENSOR_TSTAMP_EXP(attrl));
@@ -988,6 +1003,25 @@ static const struct scmi_sensor_proto_ops sensor_proto_ops = {
        .config_set = scmi_sensor_config_set,
 };
 
+static bool scmi_sensor_notify_supported(const struct scmi_protocol_handle *ph,
+                                        u8 evt_id, u32 src_id)
+{
+       bool supported = false;
+       const struct scmi_sensor_info *s;
+       struct sensors_info *sinfo = ph->get_priv(ph);
+
+       s = scmi_sensor_info_get(ph, src_id);
+       if (!s)
+               return false;
+
+       if (evt_id == SCMI_EVENT_SENSOR_TRIP_POINT_EVENT)
+               supported = sinfo->notify_trip_point_cmd;
+       else if (evt_id == SCMI_EVENT_SENSOR_UPDATE)
+               supported = s->update;
+
+       return supported;
+}
+
 static int scmi_sensor_set_notify_enabled(const struct scmi_protocol_handle *ph,
                                          u8 evt_id, u32 src_id, bool enable)
 {
@@ -1099,6 +1133,7 @@ static const struct scmi_event sensor_events[] = {
 };
 
 static const struct scmi_event_ops sensor_event_ops = {
+       .is_notify_supported = scmi_sensor_notify_supported,
        .get_num_sources = scmi_sensor_get_num_sources,
        .set_notify_enabled = scmi_sensor_set_notify_enabled,
        .fill_custom_report = scmi_sensor_fill_custom_report,
index 87b4f4d35f06230bc161fc4205c7b199e03c0015..8bf495bcad09b7ba8246c05b4e76086fa1bdaf90 100644 (file)
@@ -10,7 +10,7 @@
 #include <linux/processor.h>
 #include <linux/types.h>
 
-#include <asm-generic/bug.h>
+#include <linux/bug.h>
 
 #include "common.h"
 
@@ -122,3 +122,9 @@ bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem,
                (SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR |
                 SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE);
 }
+
+bool shmem_channel_free(struct scmi_shared_mem __iomem *shmem)
+{
+       return (ioread32(&shmem->channel_status) &
+                       SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE);
+}
index 7611e9665038dc0eeb0d49a36ac84bc52fa03396..39936e1dd30e982c099ba68b0f893df60ed1cd15 100644 (file)
@@ -214,6 +214,13 @@ static int smc_chan_free(int id, void *p, void *data)
        struct scmi_chan_info *cinfo = p;
        struct scmi_smc *scmi_info = cinfo->transport_info;
 
+       /*
+        * Different protocols might share the same chan info, so a previous
+        * smc_chan_free call might have already freed the structure.
+        */
+       if (!scmi_info)
+               return 0;
+
        /* Ignore any possible further reception on the IRQ path */
        if (scmi_info->irq > 0)
                free_irq(scmi_info->irq, scmi_info);
index 1621da97bcbb85d668eb3bd91428d7fc5bec1aa8..b6358c155f7fcac6df05b3287e7dc95e58f5c6e5 100644 (file)
@@ -36,8 +36,20 @@ struct scmi_system_power_state_notifier_payld {
 struct scmi_system_info {
        u32 version;
        bool graceful_timeout_supported;
+       bool power_state_notify_cmd;
 };
 
+static bool scmi_system_notify_supported(const struct scmi_protocol_handle *ph,
+                                        u8 evt_id, u32 src_id)
+{
+       struct scmi_system_info *pinfo = ph->get_priv(ph);
+
+       if (evt_id != SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER)
+               return false;
+
+       return pinfo->power_state_notify_cmd;
+}
+
 static int scmi_system_request_notify(const struct scmi_protocol_handle *ph,
                                      bool enable)
 {
@@ -114,6 +126,7 @@ static const struct scmi_event system_events[] = {
 };
 
 static const struct scmi_event_ops system_event_ops = {
+       .is_notify_supported = scmi_system_notify_supported,
        .set_notify_enabled = scmi_system_set_notify_enabled,
        .fill_custom_report = scmi_system_fill_custom_report,
 };
@@ -147,6 +160,9 @@ static int scmi_system_protocol_init(const struct scmi_protocol_handle *ph)
        if (PROTOCOL_REV_MAJOR(pinfo->version) >= 0x2)
                pinfo->graceful_timeout_supported = true;
 
+       if (!ph->hops->protocol_msg_check(ph, SYSTEM_POWER_STATE_NOTIFY, NULL))
+               pinfo->power_state_notify_cmd = true;
+
        return ph->set_priv(ph, pinfo, version);
 }
 
index bbcdd9fed3fb61625105121e026c1a70cb01e1e4..4221fed70ad48c8d82b29279ecd47db8d968c973 100644 (file)
@@ -77,7 +77,7 @@ static const char *get_filename(struct tegra_bpmp *bpmp,
 
        root_path_buf = kzalloc(root_path_buf_len, GFP_KERNEL);
        if (!root_path_buf)
-               goto out;
+               return NULL;
 
        root_path = dentry_path(bpmp->debugfs_mirror, root_path_buf,
                                root_path_buf_len);
index 434982545be637331e535e13218d57b3e780d314..8c5ad5c025fad1636b94f19dd5a365ccb18cc973 100644 (file)
@@ -72,7 +72,6 @@ static DEFINE_SPINLOCK(emif_lock);
 static unsigned long   irq_state;
 static LIST_HEAD(device_list);
 
-#ifdef CONFIG_DEBUG_FS
 static void do_emif_regdump_show(struct seq_file *s, struct emif_data *emif,
        struct emif_regs *regs)
 {
@@ -140,31 +139,24 @@ static int emif_mr4_show(struct seq_file *s, void *unused)
 
 DEFINE_SHOW_ATTRIBUTE(emif_mr4);
 
-static int __init_or_module emif_debugfs_init(struct emif_data *emif)
+static void emif_debugfs_init(struct emif_data *emif)
 {
-       emif->debugfs_root = debugfs_create_dir(dev_name(emif->dev), NULL);
-       debugfs_create_file("regcache_dump", S_IRUGO, emif->debugfs_root, emif,
-                           &emif_regdump_fops);
-       debugfs_create_file("mr4", S_IRUGO, emif->debugfs_root, emif,
-                           &emif_mr4_fops);
-       return 0;
-}
-
-static void __exit emif_debugfs_exit(struct emif_data *emif)
-{
-       debugfs_remove_recursive(emif->debugfs_root);
-       emif->debugfs_root = NULL;
-}
-#else
-static inline int __init_or_module emif_debugfs_init(struct emif_data *emif)
-{
-       return 0;
+       if (IS_ENABLED(CONFIG_DEBUG_FS)) {
+               emif->debugfs_root = debugfs_create_dir(dev_name(emif->dev), NULL);
+               debugfs_create_file("regcache_dump", S_IRUGO, emif->debugfs_root, emif,
+                                   &emif_regdump_fops);
+               debugfs_create_file("mr4", S_IRUGO, emif->debugfs_root, emif,
+                                   &emif_mr4_fops);
+       }
 }
 
-static inline void __exit emif_debugfs_exit(struct emif_data *emif)
+static void emif_debugfs_exit(struct emif_data *emif)
 {
+       if (IS_ENABLED(CONFIG_DEBUG_FS)) {
+               debugfs_remove_recursive(emif->debugfs_root);
+               emif->debugfs_root = NULL;
+       }
 }
-#endif
 
 /*
  * Get bus width used by EMIF. Note that this may be different from the
@@ -679,7 +671,7 @@ static void disable_and_clear_all_interrupts(struct emif_data *emif)
        clear_all_interrupts(emif);
 }
 
-static int __init_or_module setup_interrupts(struct emif_data *emif, u32 irq)
+static int setup_interrupts(struct emif_data *emif, u32 irq)
 {
        u32             interrupts, type;
        void __iomem    *base = emif->base;
@@ -710,7 +702,7 @@ static int __init_or_module setup_interrupts(struct emif_data *emif, u32 irq)
 
 }
 
-static void __init_or_module emif_onetime_settings(struct emif_data *emif)
+static void emif_onetime_settings(struct emif_data *emif)
 {
        u32                             pwr_mgmt_ctrl, zq, temp_alert_cfg;
        void __iomem                    *base = emif->base;
@@ -834,8 +826,7 @@ static int is_custom_config_valid(struct emif_custom_configs *cust_cfgs,
        return valid;
 }
 
-#if defined(CONFIG_OF)
-static void __init_or_module of_get_custom_configs(struct device_node *np_emif,
+static void of_get_custom_configs(struct device_node *np_emif,
                struct emif_data *emif)
 {
        struct emif_custom_configs      *cust_cfgs = NULL;
@@ -884,7 +875,7 @@ static void __init_or_module of_get_custom_configs(struct device_node *np_emif,
        emif->plat_data->custom_configs = cust_cfgs;
 }
 
-static void __init_or_module of_get_ddr_info(struct device_node *np_emif,
+static void of_get_ddr_info(struct device_node *np_emif,
                struct device_node *np_ddr,
                struct ddr_device_info *dev_info)
 {
@@ -918,7 +909,7 @@ static void __init_or_module of_get_ddr_info(struct device_node *np_emif,
                dev_info->io_width = __fls(io_width) - 1;
 }
 
-static struct emif_data * __init_or_module of_get_memory_device_details(
+static struct emif_data *of_get_memory_device_details(
                struct device_node *np_emif, struct device *dev)
 {
        struct emif_data                *emif = NULL;
@@ -991,16 +982,7 @@ out:
        return emif;
 }
 
-#else
-
-static struct emif_data * __init_or_module of_get_memory_device_details(
-               struct device_node *np_emif, struct device *dev)
-{
-       return NULL;
-}
-#endif
-
-static struct emif_data *__init_or_module get_device_details(
+static struct emif_data *get_device_details(
                struct platform_device *pdev)
 {
        u32                             size;
@@ -1104,7 +1086,7 @@ error:
        return NULL;
 }
 
-static int __init_or_module emif_probe(struct platform_device *pdev)
+static int emif_probe(struct platform_device *pdev)
 {
        struct emif_data        *emif;
        int                     irq, ret;
@@ -1159,7 +1141,7 @@ error:
        return -ENODEV;
 }
 
-static void __exit emif_remove(struct platform_device *pdev)
+static void emif_remove(struct platform_device *pdev)
 {
        struct emif_data *emif = platform_get_drvdata(pdev);
 
@@ -1183,7 +1165,8 @@ MODULE_DEVICE_TABLE(of, emif_of_match);
 #endif
 
 static struct platform_driver emif_driver = {
-       .remove_new     = __exit_p(emif_remove),
+       .probe          = emif_probe,
+       .remove_new     = emif_remove,
        .shutdown       = emif_shutdown,
        .driver = {
                .name = "emif",
@@ -1191,7 +1174,7 @@ static struct platform_driver emif_driver = {
        },
 };
 
-module_platform_driver_probe(emif_driver, emif_probe);
+module_platform_driver(emif_driver);
 
 MODULE_DESCRIPTION("TI EMIF SDRAM Controller Driver");
 MODULE_LICENSE("GPL");
index 0810b5b0c6883f5f88215651d6b8fa2f4461a923..50c664b65f4d44ada7892098029ee25b7a3af876 100644 (file)
@@ -68,4 +68,13 @@ config MTK_SVS
          chip process corner, temperatures and other factors. Then DVFS
          driver could apply SVS bank voltage to PMIC/Buck.
 
+config MTK_SOCINFO
+       tristate "MediaTek SoC Information"
+       default y
+       depends on NVMEM_MTK_EFUSE
+       help
+         The MediaTek SoC Information (mtk-socinfo) driver provides
+         information about the SoC to the userspace including the
+         manufacturer name, marketing name and soc name.
+
 endmenu
index 9d3ce7878c5c7db3d708137d5ddbb0fe114c79fc..6830512848fd0feb2d559894ac1b88397b487e91 100644 (file)
@@ -7,3 +7,4 @@ obj-$(CONFIG_MTK_REGULATOR_COUPLER) += mtk-regulator-coupler.o
 obj-$(CONFIG_MTK_MMSYS) += mtk-mmsys.o
 obj-$(CONFIG_MTK_MMSYS) += mtk-mutex.o
 obj-$(CONFIG_MTK_SVS) += mtk-svs.o
+obj-$(CONFIG_MTK_SOCINFO) += mtk-socinfo.o
diff --git a/drivers/soc/mediatek/mtk-socinfo.c b/drivers/soc/mediatek/mtk-socinfo.c
new file mode 100644 (file)
index 0000000..42572e8
--- /dev/null
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 MediaTek Inc.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/device.h>
+#include <linux/device/bus.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/string.h>
+#include <linux/sys_soc.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+
+#define MTK_SOCINFO_ENTRY(_soc_name, _segment_name, _marketing_name, _cell_data1, _cell_data2) {\
+       .soc_name = _soc_name,                                                                  \
+       .segment_name = _segment_name,                                                          \
+       .marketing_name = _marketing_name,                                                      \
+       .cell_data = {_cell_data1, _cell_data2}                                                 \
+}
+#define CELL_NOT_USED (0xFFFFFFFF)
+#define MAX_CELLS (2)
+
+struct mtk_socinfo {
+       struct device *dev;
+       struct name_data *name_data;
+       struct socinfo_data *socinfo_data;
+       struct soc_device *soc_dev;
+};
+
+struct socinfo_data {
+       char *soc_name;
+       char *segment_name;
+       char *marketing_name;
+       u32 cell_data[MAX_CELLS];
+};
+
+static const char *cell_names[MAX_CELLS] = {"socinfo-data1", "socinfo-data2"};
+
+static struct socinfo_data socinfo_data_table[] = {
+       MTK_SOCINFO_ENTRY("MT8173", "MT8173V/AC", "MT8173", 0x6CA20004, 0x10000000),
+       MTK_SOCINFO_ENTRY("MT8183", "MT8183V/AZA", "Kompanio 500", 0x00010043, 0x00000840),
+       MTK_SOCINFO_ENTRY("MT8183", "MT8183V/AZA", "Kompanio 500", 0x00010043, 0x00000940),
+       MTK_SOCINFO_ENTRY("MT8186", "MT8186GV/AZA", "Kompanio 520", 0x81861001, CELL_NOT_USED),
+       MTK_SOCINFO_ENTRY("MT8186T", "MT8186TV/AZA", "Kompanio 528", 0x81862001, CELL_NOT_USED),
+       MTK_SOCINFO_ENTRY("MT8188", "MT8188GV/AZA", "Kompanio 830", 0x81880000, 0x00000010),
+       MTK_SOCINFO_ENTRY("MT8188", "MT8188GV/HZA", "Kompanio 830", 0x81880000, 0x00000011),
+       MTK_SOCINFO_ENTRY("MT8192", "MT8192V/AZA", "Kompanio 820", 0x00001100, 0x00040080),
+       MTK_SOCINFO_ENTRY("MT8192T", "MT8192V/ATZA", "Kompanio 828", 0x00000100, 0x000400C0),
+       MTK_SOCINFO_ENTRY("MT8195", "MT8195GV/EZA", "Kompanio 1200", 0x81950300, CELL_NOT_USED),
+       MTK_SOCINFO_ENTRY("MT8195", "MT8195GV/EHZA", "Kompanio 1200", 0x81950304, CELL_NOT_USED),
+       MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EZA", "Kompanio 1380", 0x81950400, CELL_NOT_USED),
+       MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EHZA", "Kompanio 1380", 0x81950404, CELL_NOT_USED),
+};
+
+static int mtk_socinfo_create_socinfo_node(struct mtk_socinfo *mtk_socinfop)
+{
+       struct soc_device_attribute *attrs;
+       static char machine[30] = {0};
+       static const char *soc_manufacturer = "MediaTek";
+
+       attrs = devm_kzalloc(mtk_socinfop->dev, sizeof(*attrs), GFP_KERNEL);
+       if (!attrs)
+               return -ENOMEM;
+
+       snprintf(machine, sizeof(machine), "%s (%s)", mtk_socinfop->socinfo_data->marketing_name,
+               mtk_socinfop->socinfo_data->soc_name);
+       attrs->family = soc_manufacturer;
+       attrs->machine = machine;
+
+       mtk_socinfop->soc_dev = soc_device_register(attrs);
+       if (IS_ERR(mtk_socinfop->soc_dev))
+               return PTR_ERR(mtk_socinfop->soc_dev);
+
+       dev_info(mtk_socinfop->dev, "%s %s SoC detected.\n", soc_manufacturer, attrs->machine);
+       return 0;
+}
+
+static u32 mtk_socinfo_read_cell(struct device *dev, const char *name)
+{
+       struct nvmem_device *nvmemp;
+       struct device_node *np, *nvmem_node = dev->parent->of_node;
+       u32 offset;
+       u32 cell_val = CELL_NOT_USED;
+
+       /* should never fail since the nvmem driver registers this child */
+       nvmemp = nvmem_device_find(nvmem_node, device_match_of_node);
+       if (IS_ERR(nvmemp))
+               goto out;
+
+       np = of_get_child_by_name(nvmem_node, name);
+       if (!np)
+               goto put_device;
+
+       if (of_property_read_u32_index(np, "reg", 0, &offset))
+               goto put_node;
+
+       nvmem_device_read(nvmemp, offset, sizeof(cell_val), &cell_val);
+
+put_node:
+       of_node_put(np);
+put_device:
+       nvmem_device_put(nvmemp);
+out:
+       return cell_val;
+}
+
+static int mtk_socinfo_get_socinfo_data(struct mtk_socinfo *mtk_socinfop)
+{
+       unsigned int i, j;
+       unsigned int num_cell_data = 0;
+       u32 cell_data[MAX_CELLS] = {0};
+       bool match_socinfo;
+       int match_socinfo_index = -1;
+
+       for (i = 0; i < MAX_CELLS; i++) {
+               cell_data[i] = mtk_socinfo_read_cell(mtk_socinfop->dev, cell_names[i]);
+               if (cell_data[i] != CELL_NOT_USED)
+                       num_cell_data++;
+               else
+                       break;
+       }
+
+       if (!num_cell_data)
+               return -ENOENT;
+
+       for (i = 0; i < ARRAY_SIZE(socinfo_data_table); i++) {
+               match_socinfo = true;
+               for (j = 0; j < num_cell_data; j++) {
+                       if (cell_data[j] != socinfo_data_table[i].cell_data[j]) {
+                               match_socinfo = false;
+                               break;
+                       }
+               }
+               if (match_socinfo) {
+                       mtk_socinfop->socinfo_data = &(socinfo_data_table[i]);
+                       match_socinfo_index = i;
+                       break;
+               }
+       }
+
+       return match_socinfo_index >= 0 ? match_socinfo_index : -ENOENT;
+}
+
+static int mtk_socinfo_probe(struct platform_device *pdev)
+{
+       struct mtk_socinfo *mtk_socinfop;
+       int ret;
+
+       mtk_socinfop = devm_kzalloc(&pdev->dev, sizeof(*mtk_socinfop), GFP_KERNEL);
+       if (!mtk_socinfop)
+               return -ENOMEM;
+
+       mtk_socinfop->dev = &pdev->dev;
+
+       ret = mtk_socinfo_get_socinfo_data(mtk_socinfop);
+       if (ret < 0)
+               return dev_err_probe(mtk_socinfop->dev, ret, "Failed to get socinfo data\n");
+
+       ret = mtk_socinfo_create_socinfo_node(mtk_socinfop);
+       if (ret)
+               return dev_err_probe(mtk_socinfop->dev, ret, "Cannot create node\n");
+
+       platform_set_drvdata(pdev, mtk_socinfop);
+       return 0;
+}
+
+static void mtk_socinfo_remove(struct platform_device *pdev)
+{
+       struct mtk_socinfo *mtk_socinfop = platform_get_drvdata(pdev);
+
+       soc_device_unregister(mtk_socinfop->soc_dev);
+}
+
+static struct platform_driver mtk_socinfo = {
+       .probe = mtk_socinfo_probe,
+       .remove_new = mtk_socinfo_remove,
+       .driver = {
+               .name = "mtk-socinfo",
+       },
+};
+module_platform_driver(mtk_socinfo);
+
+MODULE_AUTHOR("William-TW LIN <william-tw.lin@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek socinfo driver");
+MODULE_LICENSE("GPL");
index 0986672f63757663e2e4401bc96a81406b6c13f7..5deca747fb7717448107d0b3e7a9102c23760c69 100644 (file)
@@ -34,6 +34,10 @@ config ARCH_RCAR_GEN3
        select SYS_SUPPORTS_SH_CMT
        select SYS_SUPPORTS_SH_TMU
 
+config ARCH_RCAR_GEN4
+       bool
+       select ARCH_RCAR_GEN3
+
 config ARCH_RMOBILE
        bool
        select PM
@@ -240,7 +244,7 @@ config ARCH_R8A77961
 
 config ARCH_R8A779F0
        bool "ARM64 Platform support for R-Car S4-8"
-       select ARCH_RCAR_GEN3
+       select ARCH_RCAR_GEN4
        select SYSC_R8A779F0
        help
          This enables support for the Renesas R-Car S4-8 SoC.
@@ -261,18 +265,25 @@ config ARCH_R8A77970
 
 config ARCH_R8A779A0
        bool "ARM64 Platform support for R-Car V3U"
-       select ARCH_RCAR_GEN3
+       select ARCH_RCAR_GEN4
        select SYSC_R8A779A0
        help
          This enables support for the Renesas R-Car V3U SoC.
 
 config ARCH_R8A779G0
        bool "ARM64 Platform support for R-Car V4H"
-       select ARCH_RCAR_GEN3
+       select ARCH_RCAR_GEN4
        select SYSC_R8A779G0
        help
          This enables support for the Renesas R-Car V4H SoC.
 
+config ARCH_R8A779H0
+       bool "ARM64 Platform support for R-Car V4M"
+       select ARCH_RCAR_GEN4
+       select SYSC_R8A779H0
+       help
+         This enables support for the Renesas R-Car V4M SoC.
+
 config ARCH_R8A774C0
        bool "ARM64 Platform support for RZ/G2E"
        select ARCH_RCAR_GEN3
index 98fd97da6cd4330dcd1673a2f7d775937e80e174..7ba02f3a4a4fbb9fc41f7c08d5a438de7f16569e 100644 (file)
@@ -117,6 +117,7 @@ static const struct of_device_id rcar_rst_matches[] __initconst = {
        { .compatible = "renesas,r8a779a0-rst", .data = &rcar_rst_v3u },
        { .compatible = "renesas,r8a779f0-rst", .data = &rcar_rst_gen4 },
        { .compatible = "renesas,r8a779g0-rst", .data = &rcar_rst_gen4 },
+       { .compatible = "renesas,r8a779h0-rst", .data = &rcar_rst_gen4 },
        { /* sentinel */ }
 };
 
index 27eae1a354ab23296fea4a873d72b038c323b290..8f9b8d3736dcdb47b5f06f03dd8ea4217ae13ed8 100644 (file)
@@ -270,6 +270,11 @@ static const struct renesas_soc soc_rcar_v4h __initconst __maybe_unused = {
        .id     = 0x5c,
 };
 
+static const struct renesas_soc soc_rcar_v4m __initconst __maybe_unused = {
+       .family = &fam_rcar_gen4,
+       .id     = 0x5d,
+};
+
 static const struct renesas_soc soc_shmobile_ag5 __initconst __maybe_unused = {
        .family = &fam_shmobile,
        .id     = 0x37,
@@ -380,6 +385,9 @@ static const struct of_device_id renesas_socs[] __initconst __maybe_unused = {
 #ifdef CONFIG_ARCH_R8A779G0
        { .compatible = "renesas,r8a779g0",     .data = &soc_rcar_v4h },
 #endif
+#ifdef CONFIG_ARCH_R8A779H0
+       { .compatible = "renesas,r8a779h0",     .data = &soc_rcar_v4m },
+#endif
 #ifdef CONFIG_ARCH_R9A07G043
 #ifdef CONFIG_RISCV
        { .compatible = "renesas,r9a07g043",    .data = &soc_rz_five },
index f16beeabaa92bb890a3174227364bb84d2885b15..33512558af9f7f017a2aa286a3eacb142472f7e5 100644 (file)
@@ -133,6 +133,11 @@ config ARCH_TEGRA_234_SOC
        help
          Enable support for the NVIDIA Tegra234 SoC.
 
+config ARCH_TEGRA_241_SOC
+       bool "NVIDIA Tegra241 SoC"
+       help
+         Enable support for the NVIDIA Tegra241 SoC.
+
 endif
 endif
 
index a2c28f493a75e52a0e620dd5fa35c2636d40d4ae..b6bfd6729df3904adbe5affd1208e59f9a5608dc 100644 (file)
@@ -3,11 +3,13 @@
  * Copyright (c) 2013-2023, NVIDIA CORPORATION.  All rights reserved.
  */
 
+#include <linux/acpi.h>
 #include <linux/clk.h>
 #include <linux/device.h>
 #include <linux/kobject.h>
 #include <linux/init.h>
 #include <linux/io.h>
+#include <linux/mod_devicetable.h>
 #include <linux/nvmem-consumer.h>
 #include <linux/nvmem-provider.h>
 #include <linux/of.h>
@@ -113,6 +115,28 @@ static void tegra_fuse_restore(void *base)
        fuse->clk = NULL;
 }
 
+static void tegra_fuse_print_sku_info(struct tegra_sku_info *tegra_sku_info)
+{
+       pr_info("Tegra Revision: %s SKU: %d CPU Process: %d SoC Process: %d\n",
+               tegra_revision_name[tegra_sku_info->revision],
+               tegra_sku_info->sku_id, tegra_sku_info->cpu_process_id,
+               tegra_sku_info->soc_process_id);
+       pr_debug("Tegra CPU Speedo ID %d, SoC Speedo ID %d\n",
+               tegra_sku_info->cpu_speedo_id, tegra_sku_info->soc_speedo_id);
+}
+
+static int tegra_fuse_add_lookups(struct tegra_fuse *fuse)
+{
+       fuse->lookups = kmemdup_array(fuse->soc->lookups, sizeof(*fuse->lookups),
+                                     fuse->soc->num_lookups, GFP_KERNEL);
+       if (!fuse->lookups)
+               return -ENOMEM;
+
+       nvmem_add_cell_lookups(fuse->lookups, fuse->soc->num_lookups);
+
+       return 0;
+}
+
 static int tegra_fuse_probe(struct platform_device *pdev)
 {
        void __iomem *base = fuse->base;
@@ -130,15 +154,46 @@ static int tegra_fuse_probe(struct platform_device *pdev)
                return PTR_ERR(fuse->base);
        fuse->phys = res->start;
 
-       fuse->clk = devm_clk_get(&pdev->dev, "fuse");
-       if (IS_ERR(fuse->clk)) {
-               if (PTR_ERR(fuse->clk) != -EPROBE_DEFER)
-                       dev_err(&pdev->dev, "failed to get FUSE clock: %ld",
-                               PTR_ERR(fuse->clk));
+       /* Initialize the soc data and lookups if using ACPI boot. */
+       if (is_acpi_node(dev_fwnode(&pdev->dev)) && !fuse->soc) {
+               u8 chip;
 
-               return PTR_ERR(fuse->clk);
+               tegra_acpi_init_apbmisc();
+
+               chip = tegra_get_chip_id();
+               switch (chip) {
+#if defined(CONFIG_ARCH_TEGRA_194_SOC)
+               case TEGRA194:
+                       fuse->soc = &tegra194_fuse_soc;
+                       break;
+#endif
+#if defined(CONFIG_ARCH_TEGRA_234_SOC)
+               case TEGRA234:
+                       fuse->soc = &tegra234_fuse_soc;
+                       break;
+#endif
+#if defined(CONFIG_ARCH_TEGRA_241_SOC)
+               case TEGRA241:
+                       fuse->soc = &tegra241_fuse_soc;
+                       break;
+#endif
+               default:
+                       return dev_err_probe(&pdev->dev, -EINVAL, "Unsupported SoC: %02x\n", chip);
+               }
+
+               fuse->soc->init(fuse);
+               tegra_fuse_print_sku_info(&tegra_sku_info);
+               tegra_soc_device_register();
+
+               err = tegra_fuse_add_lookups(fuse);
+               if (err)
+                       return dev_err_probe(&pdev->dev, err, "failed to add FUSE lookups\n");
        }
 
+       fuse->clk = devm_clk_get_optional(&pdev->dev, "fuse");
+       if (IS_ERR(fuse->clk))
+               return dev_err_probe(&pdev->dev, PTR_ERR(fuse->clk), "failed to get FUSE clock\n");
+
        platform_set_drvdata(pdev, fuse);
        fuse->dev = &pdev->dev;
 
@@ -179,12 +234,8 @@ static int tegra_fuse_probe(struct platform_device *pdev)
        }
 
        fuse->rst = devm_reset_control_get_optional(&pdev->dev, "fuse");
-       if (IS_ERR(fuse->rst)) {
-               err = PTR_ERR(fuse->rst);
-               dev_err(&pdev->dev, "failed to get FUSE reset: %pe\n",
-                       fuse->rst);
-               return err;
-       }
+       if (IS_ERR(fuse->rst))
+               return dev_err_probe(&pdev->dev, PTR_ERR(fuse->rst), "failed to get FUSE reset\n");
 
        /*
         * FUSE clock is enabled at a boot time, hence this resume/suspend
@@ -262,10 +313,17 @@ static const struct dev_pm_ops tegra_fuse_pm = {
        SET_SYSTEM_SLEEP_PM_OPS(tegra_fuse_suspend, tegra_fuse_resume)
 };
 
+static const struct acpi_device_id tegra_fuse_acpi_match[] = {
+       { "NVDA200F" },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, tegra_fuse_acpi_match);
+
 static struct platform_driver tegra_fuse_driver = {
        .driver = {
                .name = "tegra-fuse",
                .of_match_table = tegra_fuse_match,
+               .acpi_match_table = tegra_fuse_acpi_match,
                .pm = &tegra_fuse_pm,
                .suppress_bind_attrs = true,
        },
@@ -287,7 +345,16 @@ u32 __init tegra_fuse_read_early(unsigned int offset)
 
 int tegra_fuse_readl(unsigned long offset, u32 *value)
 {
-       if (!fuse->read || !fuse->clk)
+       if (!fuse->dev)
+               return -EPROBE_DEFER;
+
+       /*
+        * Wait for fuse->clk to be initialized if device-tree boot is used.
+        */
+       if (is_of_node(dev_fwnode(fuse->dev)) && !fuse->clk)
+               return -EPROBE_DEFER;
+
+       if (!fuse->read)
                return -EPROBE_DEFER;
 
        if (IS_ERR(fuse->clk))
@@ -343,7 +410,8 @@ const struct attribute_group tegra_soc_attr_group = {
 };
 
 #if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \
-    IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC)
+    IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) || \
+    IS_ENABLED(CONFIG_ARCH_TEGRA_241_SOC)
 static ssize_t platform_show(struct device *dev, struct device_attribute *attr,
                             char *buf)
 {
@@ -370,7 +438,7 @@ const struct attribute_group tegra194_soc_attr_group = {
 };
 #endif
 
-struct device * __init tegra_soc_device_register(void)
+struct device *tegra_soc_device_register(void)
 {
        struct soc_device_attribute *attr;
        struct soc_device *dev;
@@ -407,6 +475,7 @@ static int __init tegra_init_fuse(void)
        const struct of_device_id *match;
        struct device_node *np;
        struct resource regs;
+       int err;
 
        tegra_init_apbmisc();
 
@@ -497,22 +566,13 @@ static int __init tegra_init_fuse(void)
 
        fuse->soc->init(fuse);
 
-       pr_info("Tegra Revision: %s SKU: %d CPU Process: %d SoC Process: %d\n",
-               tegra_revision_name[tegra_sku_info.revision],
-               tegra_sku_info.sku_id, tegra_sku_info.cpu_process_id,
-               tegra_sku_info.soc_process_id);
-       pr_debug("Tegra CPU Speedo ID %d, SoC Speedo ID %d\n",
-                tegra_sku_info.cpu_speedo_id, tegra_sku_info.soc_speedo_id);
+       tegra_fuse_print_sku_info(&tegra_sku_info);
 
-       if (fuse->soc->lookups) {
-               size_t size = sizeof(*fuse->lookups) * fuse->soc->num_lookups;
-
-               fuse->lookups = kmemdup(fuse->soc->lookups, size, GFP_KERNEL);
-               if (fuse->lookups)
-                       nvmem_add_cell_lookups(fuse->lookups, fuse->soc->num_lookups);
-       }
+       err = tegra_fuse_add_lookups(fuse);
+       if (err)
+               pr_err("failed to add FUSE lookups\n");
 
-       return 0;
+       return err;
 }
 early_initcall(tegra_init_fuse);
 
index e94d46372a6396d14ee5dab31f0aa378a18bc9e1..eb14e5ff5a0aa8b5023b742b14e99320b47b7084 100644 (file)
@@ -38,7 +38,8 @@
     defined(CONFIG_ARCH_TEGRA_210_SOC) || \
     defined(CONFIG_ARCH_TEGRA_186_SOC) || \
     defined(CONFIG_ARCH_TEGRA_194_SOC) || \
-    defined(CONFIG_ARCH_TEGRA_234_SOC)
+    defined(CONFIG_ARCH_TEGRA_234_SOC) || \
+    defined(CONFIG_ARCH_TEGRA_241_SOC)
 static u32 tegra30_fuse_read_early(struct tegra_fuse *fuse, unsigned int offset)
 {
        if (WARN_ON(!fuse->base))
@@ -678,3 +679,23 @@ const struct tegra_fuse_soc tegra234_fuse_soc = {
        .clk_suspend_on = false,
 };
 #endif
+
+#if defined(CONFIG_ARCH_TEGRA_241_SOC)
+static const struct tegra_fuse_info tegra241_fuse_info = {
+       .read = tegra30_fuse_read,
+       .size = 0x16008,
+       .spare = 0xcf0,
+};
+
+static const struct nvmem_keepout tegra241_fuse_keepouts[] = {
+       { .start = 0xc, .end = 0x1600c }
+};
+
+const struct tegra_fuse_soc tegra241_fuse_soc = {
+       .init = tegra30_fuse_init,
+       .info = &tegra241_fuse_info,
+       .keepouts = tegra241_fuse_keepouts,
+       .num_keepouts = ARRAY_SIZE(tegra241_fuse_keepouts),
+       .soc_attr_group = &tegra194_soc_attr_group,
+};
+#endif
index 90f23be738947a5b5ebd7e8dc03e01bdd5d6b434..9fee6ad6ad9e98852c9d47079cdf49433e55fdb7 100644 (file)
@@ -69,6 +69,7 @@ struct tegra_fuse {
 
 void tegra_init_revision(void);
 void tegra_init_apbmisc(void);
+void tegra_acpi_init_apbmisc(void);
 
 u32 __init tegra_fuse_read_spare(unsigned int spare);
 u32 __init tegra_fuse_read_early(unsigned int offset);
@@ -123,7 +124,8 @@ extern const struct tegra_fuse_soc tegra186_fuse_soc;
 #endif
 
 #if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \
-    IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC)
+    IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) || \
+    IS_ENABLED(CONFIG_ARCH_TEGRA_241_SOC)
 extern const struct attribute_group tegra194_soc_attr_group;
 #endif
 
@@ -135,4 +137,8 @@ extern const struct tegra_fuse_soc tegra194_fuse_soc;
 extern const struct tegra_fuse_soc tegra234_fuse_soc;
 #endif
 
+#ifdef CONFIG_ARCH_TEGRA_241_SOC
+extern const struct tegra_fuse_soc tegra241_fuse_soc;
+#endif
+
 #endif
index da970f3dbf35620ac64f3d6255301ee4807a8f57..e2ca5d55fd31259452f444e7d846d9cc145f5e8c 100644 (file)
@@ -3,9 +3,11 @@
  * Copyright (c) 2014-2023, NVIDIA CORPORATION.  All rights reserved.
  */
 
+#include <linux/acpi.h>
 #include <linux/export.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
 
@@ -62,6 +64,7 @@ bool tegra_is_silicon(void)
        switch (tegra_get_chip_id()) {
        case TEGRA194:
        case TEGRA234:
+       case TEGRA241:
        case TEGRA264:
                if (tegra_get_platform() == 0)
                        return true;
@@ -160,9 +163,34 @@ void __init tegra_init_revision(void)
        tegra_sku_info.platform = tegra_get_platform();
 }
 
-void __init tegra_init_apbmisc(void)
+static void tegra_init_apbmisc_resources(struct resource *apbmisc,
+                                        struct resource *straps)
 {
        void __iomem *strapping_base;
+
+       apbmisc_base = ioremap(apbmisc->start, resource_size(apbmisc));
+       if (apbmisc_base)
+               chipid = readl_relaxed(apbmisc_base + 4);
+       else
+               pr_err("failed to map APBMISC registers\n");
+
+       strapping_base = ioremap(straps->start, resource_size(straps));
+       if (strapping_base) {
+               strapping = readl_relaxed(strapping_base);
+               iounmap(strapping_base);
+       } else {
+               pr_err("failed to map strapping options registers\n");
+       }
+}
+
+/**
+ * tegra_init_apbmisc - Initializes Tegra APBMISC and Strapping registers.
+ *
+ * This is called during early init as some of the old 32-bit ARM code needs
+ * information from the APBMISC registers very early during boot.
+ */
+void __init tegra_init_apbmisc(void)
+{
        struct resource apbmisc, straps;
        struct device_node *np;
 
@@ -219,23 +247,73 @@ void __init tegra_init_apbmisc(void)
                }
        }
 
-       apbmisc_base = ioremap(apbmisc.start, resource_size(&apbmisc));
-       if (!apbmisc_base) {
-               pr_err("failed to map APBMISC registers\n");
-       } else {
-               chipid = readl_relaxed(apbmisc_base + 4);
+       tegra_init_apbmisc_resources(&apbmisc, &straps);
+       long_ram_code = of_property_read_bool(np, "nvidia,long-ram-code");
+
+put:
+       of_node_put(np);
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id apbmisc_acpi_match[] = {
+       { "NVDA2010" },
+       { /* sentinel */ }
+};
+
+void tegra_acpi_init_apbmisc(void)
+{
+       struct resource *resources[2] = { NULL };
+       struct resource_entry *rentry;
+       struct acpi_device *adev = NULL;
+       struct list_head resource_list;
+       int rcount = 0;
+       int ret;
+
+       adev = acpi_dev_get_first_match_dev(apbmisc_acpi_match[0].id, NULL, -1);
+       if (!adev)
+               return;
+
+       INIT_LIST_HEAD(&resource_list);
+
+       ret = acpi_dev_get_memory_resources(adev, &resource_list);
+       if (ret < 0) {
+               pr_err("failed to get APBMISC memory resources");
+               goto out_put_acpi_dev;
        }
 
-       strapping_base = ioremap(straps.start, resource_size(&straps));
-       if (!strapping_base) {
-               pr_err("failed to map strapping options registers\n");
-       } else {
-               strapping = readl_relaxed(strapping_base);
-               iounmap(strapping_base);
+       /*
+        * Get required memory resources.
+        *
+        * resources[0]: apbmisc.
+        * resources[1]: straps.
+        */
+       resource_list_for_each_entry(rentry, &resource_list) {
+               if (rcount >= ARRAY_SIZE(resources))
+                       break;
+
+               resources[rcount++] = rentry->res;
        }
 
-       long_ram_code = of_property_read_bool(np, "nvidia,long-ram-code");
+       if (!resources[0]) {
+               pr_err("failed to get APBMISC registers\n");
+               goto out_free_resource_list;
+       }
 
-put:
-       of_node_put(np);
+       if (!resources[1]) {
+               pr_err("failed to get strapping options registers\n");
+               goto out_free_resource_list;
+       }
+
+       tegra_init_apbmisc_resources(resources[0], resources[1]);
+
+out_free_resource_list:
+       acpi_dev_free_resource_list(&resource_list);
+
+out_put_acpi_dev:
+       acpi_dev_put(adev);
+}
+#else
+void tegra_acpi_init_apbmisc(void)
+{
 }
+#endif
index f432aa022ace0685e70329cb7d8cca947f666b5a..d6bfcea5ee653f84dc805fa6c4f4041765ad01ec 100644 (file)
@@ -3,7 +3,7 @@
  * drivers/soc/tegra/pmc.c
  *
  * Copyright (c) 2010 Google, Inc
- * Copyright (c) 2018-2023, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved.
  *
  * Author:
  *     Colin Cross <ccross@google.com>
@@ -384,6 +384,7 @@ struct tegra_pmc_soc {
        bool has_blink_output;
        bool has_usb_sleepwalk;
        bool supports_core_domain;
+       bool has_single_mmio_aperture;
 };
 
 /**
@@ -1777,30 +1778,6 @@ static int tegra_io_pad_get_voltage(struct tegra_pmc *pmc, enum tegra_io_pad id)
        return TEGRA_IO_PAD_VOLTAGE_3V3;
 }
 
-/**
- * tegra_io_rail_power_on() - enable power to I/O rail
- * @id: Tegra I/O pad ID for which to enable power
- *
- * See also: tegra_io_pad_power_enable()
- */
-int tegra_io_rail_power_on(unsigned int id)
-{
-       return tegra_io_pad_power_enable(id);
-}
-EXPORT_SYMBOL(tegra_io_rail_power_on);
-
-/**
- * tegra_io_rail_power_off() - disable power to I/O rail
- * @id: Tegra I/O pad ID for which to disable power
- *
- * See also: tegra_io_pad_power_disable()
- */
-int tegra_io_rail_power_off(unsigned int id)
-{
-       return tegra_io_pad_power_disable(id);
-}
-EXPORT_SYMBOL(tegra_io_rail_power_off);
-
 #ifdef CONFIG_PM_SLEEP
 enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void)
 {
@@ -2909,31 +2886,33 @@ static int tegra_pmc_probe(struct platform_device *pdev)
        if (IS_ERR(base))
                return PTR_ERR(base);
 
-       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wake");
-       if (res) {
+       if (pmc->soc->has_single_mmio_aperture) {
+               pmc->wake = base;
+               pmc->aotag = base;
+               pmc->scratch = base;
+       } else {
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                               "wake");
                pmc->wake = devm_ioremap_resource(&pdev->dev, res);
                if (IS_ERR(pmc->wake))
                        return PTR_ERR(pmc->wake);
-       } else {
-               pmc->wake = base;
-       }
 
-       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aotag");
-       if (res) {
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                               "aotag");
                pmc->aotag = devm_ioremap_resource(&pdev->dev, res);
                if (IS_ERR(pmc->aotag))
                        return PTR_ERR(pmc->aotag);
-       } else {
-               pmc->aotag = base;
-       }
 
-       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scratch");
-       if (res) {
-               pmc->scratch = devm_ioremap_resource(&pdev->dev, res);
-               if (IS_ERR(pmc->scratch))
-                       return PTR_ERR(pmc->scratch);
-       } else {
-               pmc->scratch = base;
+               /* "scratch" is an optional aperture */
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                               "scratch");
+               if (res) {
+                       pmc->scratch = devm_ioremap_resource(&pdev->dev, res);
+                       if (IS_ERR(pmc->scratch))
+                               return PTR_ERR(pmc->scratch);
+               } else {
+                       pmc->scratch = NULL;
+               }
        }
 
        pmc->clk = devm_clk_get_optional(&pdev->dev, "pclk");
@@ -2945,12 +2924,15 @@ static int tegra_pmc_probe(struct platform_device *pdev)
         * PMC should be last resort for restarting since it soft-resets
         * CPU without resetting everything else.
         */
-       err = devm_register_reboot_notifier(&pdev->dev,
-                                           &tegra_pmc_reboot_notifier);
-       if (err) {
-               dev_err(&pdev->dev, "unable to register reboot notifier, %d\n",
-                       err);
-               return err;
+       if (pmc->scratch) {
+               err = devm_register_reboot_notifier(&pdev->dev,
+                                                   &tegra_pmc_reboot_notifier);
+               if (err) {
+                       dev_err(&pdev->dev,
+                               "unable to register reboot notifier, %d\n",
+                               err);
+                       return err;
+               }
        }
 
        err = devm_register_sys_off_handler(&pdev->dev,
@@ -3324,6 +3306,7 @@ static const struct tegra_pmc_soc tegra20_pmc_soc = {
        .num_pmc_clks = 0,
        .has_blink_output = true,
        .has_usb_sleepwalk = true,
+       .has_single_mmio_aperture = true,
 };
 
 static const char * const tegra30_powergates[] = {
@@ -3385,6 +3368,7 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = {
        .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
        .has_blink_output = true,
        .has_usb_sleepwalk = true,
+       .has_single_mmio_aperture = true,
 };
 
 static const char * const tegra114_powergates[] = {
@@ -3442,6 +3426,7 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = {
        .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
        .has_blink_output = true,
        .has_usb_sleepwalk = true,
+       .has_single_mmio_aperture = true,
 };
 
 static const char * const tegra124_powergates[] = {
@@ -3586,6 +3571,7 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = {
        .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
        .has_blink_output = true,
        .has_usb_sleepwalk = true,
+       .has_single_mmio_aperture = true,
 };
 
 static const char * const tegra210_powergates[] = {
@@ -3749,6 +3735,7 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = {
        .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
        .has_blink_output = true,
        .has_usb_sleepwalk = true,
+       .has_single_mmio_aperture = true,
 };
 
 static const struct tegra_io_pad_soc tegra186_io_pads[] = {
@@ -3946,6 +3933,7 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = {
        .num_pmc_clks = 0,
        .has_blink_output = false,
        .has_usb_sleepwalk = false,
+       .has_single_mmio_aperture = false,
 };
 
 static const struct tegra_io_pad_soc tegra194_io_pads[] = {
@@ -4131,6 +4119,7 @@ static const struct tegra_pmc_soc tegra194_pmc_soc = {
        .num_pmc_clks = 0,
        .has_blink_output = false,
        .has_usb_sleepwalk = false,
+       .has_single_mmio_aperture = false,
 };
 
 static const struct tegra_io_pad_soc tegra234_io_pads[] = {
@@ -4220,6 +4209,7 @@ static const char * const tegra234_reset_sources[] = {
 };
 
 static const struct tegra_wake_event tegra234_wake_events[] = {
+       TEGRA_WAKE_GPIO("sd-wake", 8, 0, TEGRA234_MAIN_GPIO(G, 7)),
        TEGRA_WAKE_IRQ("pmu", 24, 209),
        TEGRA_WAKE_GPIO("power", 29, 1, TEGRA234_AON_GPIO(EE, 4)),
        TEGRA_WAKE_GPIO("mgbe", 56, 0, TEGRA234_MAIN_GPIO(Y, 3)),
@@ -4259,6 +4249,7 @@ static const struct tegra_pmc_soc tegra234_pmc_soc = {
        .pmc_clks_data = NULL,
        .num_pmc_clks = 0,
        .has_blink_output = false,
+       .has_single_mmio_aperture = false,
 };
 
 static const struct of_device_id tegra_pmc_match[] = {
index 3d0fde57ba90eb7b6bf6a295181f06ab73cfb03b..c906f666ff5d22007d1732bb0706d891ded27551 100644 (file)
@@ -209,7 +209,7 @@ bool ffa_device_is_valid(struct ffa_device *ffa_dev) { return false; }
 #define module_ffa_driver(__ffa_driver)        \
        module_driver(__ffa_driver, ffa_register, ffa_unregister)
 
-extern struct bus_type ffa_bus_type;
+extern const struct bus_type ffa_bus_type;
 
 /* FFA transport related */
 struct ffa_partition_info {
index f2f05fb42d284034a94ba61d99e0e6d4fc523a1b..2ee94ff0320c32233a528e1b328d58237cee8452 100644 (file)
@@ -47,6 +47,10 @@ struct scmi_clock_info {
        bool rate_discrete;
        bool rate_changed_notifications;
        bool rate_change_requested_notifications;
+       bool state_ctrl_forbidden;
+       bool rate_ctrl_forbidden;
+       bool parent_ctrl_forbidden;
+       bool extended_config;
        union {
                struct {
                        int num_rates;
@@ -72,6 +76,13 @@ struct scmi_handle;
 struct scmi_device;
 struct scmi_protocol_handle;
 
+enum scmi_clock_oem_config {
+       SCMI_CLOCK_CFG_DUTY_CYCLE = 0x1,
+       SCMI_CLOCK_CFG_PHASE,
+       SCMI_CLOCK_CFG_OEM_START = 0x80,
+       SCMI_CLOCK_CFG_OEM_END = 0xFF,
+};
+
 /**
  * struct scmi_clk_proto_ops - represents the various operations provided
  *     by SCMI Clock Protocol
@@ -104,10 +115,11 @@ struct scmi_clk_proto_ops {
        int (*state_get)(const struct scmi_protocol_handle *ph, u32 clk_id,
                         bool *enabled, bool atomic);
        int (*config_oem_get)(const struct scmi_protocol_handle *ph, u32 clk_id,
-                             u8 oem_type, u32 *oem_val, u32 *attributes,
-                             bool atomic);
+                             enum scmi_clock_oem_config oem_type,
+                             u32 *oem_val, u32 *attributes, bool atomic);
        int (*config_oem_set)(const struct scmi_protocol_handle *ph, u32 clk_id,
-                             u8 oem_type, u32 oem_val, bool atomic);
+                             enum scmi_clock_oem_config oem_type,
+                             u32 oem_val, bool atomic);
        int (*parent_get)(const struct scmi_protocol_handle *ph, u32 clk_id, u32 *parent_id);
        int (*parent_set)(const struct scmi_protocol_handle *ph, u32 clk_id, u32 parent_id);
 };
@@ -953,6 +965,8 @@ struct scmi_perf_limits_report {
        unsigned int    domain_id;
        unsigned int    range_max;
        unsigned int    range_min;
+       unsigned long   range_max_freq;
+       unsigned long   range_min_freq;
 };
 
 struct scmi_perf_level_report {
@@ -960,6 +974,7 @@ struct scmi_perf_level_report {
        unsigned int    agent_id;
        unsigned int    domain_id;
        unsigned int    performance_level;
+       unsigned long   performance_level_freq;
 };
 
 struct scmi_sensor_trip_point_report {
index ab148d8dbfc146d2aed178b694f33506d06bfd05..4795ee5c50c66957e34072a8e13167fd1d11c5e8 100644 (file)
@@ -217,6 +217,7 @@ extern char *kstrndup(const char *s, size_t len, gfp_t gfp);
 extern void *kmemdup(const void *src, size_t len, gfp_t gfp) __realloc_size(2);
 extern void *kvmemdup(const void *src, size_t len, gfp_t gfp) __realloc_size(2);
 extern char *kmemdup_nul(const char *s, size_t len, gfp_t gfp);
+extern void *kmemdup_array(const void *src, size_t element_size, size_t count, gfp_t gfp);
 
 extern char **argv_split(gfp_t gfp, const char *str, int *argcp);
 extern void argv_free(char **argv);
index 3a513be502437f991cdb29518a83146e72f48a87..8f421b9f7585ca1714197c8c102d7a19e622d1a2 100644 (file)
@@ -17,6 +17,7 @@
 #define TEGRA186       0x18
 #define TEGRA194       0x19
 #define TEGRA234       0x23
+#define TEGRA241       0x24
 #define TEGRA264       0x26
 
 #define TEGRA_FUSE_SKU_CALIB_0 0xf0
index aadb845d281dd7d3c0532111d82cdfae3e742059..c545875d0ff18e6117e1ad0ffc61252240fd16a2 100644 (file)
@@ -148,10 +148,6 @@ enum tegra_io_pad {
        TEGRA_IO_PAD_AO_HV,
 };
 
-/* deprecated, use TEGRA_IO_PAD_{HDMI,LVDS} instead */
-#define TEGRA_IO_RAIL_HDMI     TEGRA_IO_PAD_HDMI
-#define TEGRA_IO_RAIL_LVDS     TEGRA_IO_PAD_LVDS
-
 #ifdef CONFIG_SOC_TEGRA_PMC
 int tegra_powergate_power_on(unsigned int id);
 int tegra_powergate_power_off(unsigned int id);
@@ -164,10 +160,6 @@ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk,
 int tegra_io_pad_power_enable(enum tegra_io_pad id);
 int tegra_io_pad_power_disable(enum tegra_io_pad id);
 
-/* deprecated, use tegra_io_pad_power_{enable,disable}() instead */
-int tegra_io_rail_power_on(unsigned int id);
-int tegra_io_rail_power_off(unsigned int id);
-
 void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode);
 void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode);
 
@@ -211,16 +203,6 @@ static inline int tegra_io_pad_get_voltage(enum tegra_io_pad id)
        return -ENOSYS;
 }
 
-static inline int tegra_io_rail_power_on(unsigned int id)
-{
-       return -ENOSYS;
-}
-
-static inline int tegra_io_rail_power_off(unsigned int id)
-{
-       return -ENOSYS;
-}
-
 static inline void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode)
 {
 }
index 5a6a9802583b18992ea4d81d0a47794b169541ad..5faf3adc6f433ab153465a3f391027ee73aa4a31 100644 (file)
--- a/mm/util.c
+++ b/mm/util.c
@@ -135,6 +135,23 @@ void *kmemdup(const void *src, size_t len, gfp_t gfp)
 }
 EXPORT_SYMBOL(kmemdup);
 
+/**
+ * kmemdup_array - duplicate a given array.
+ *
+ * @src: array to duplicate.
+ * @element_size: size of each element of array.
+ * @count: number of elements to duplicate from array.
+ * @gfp: GFP mask to use.
+ *
+ * Return: duplicated array of @src or %NULL in case of error,
+ * result is physically contiguous. Use kfree() to free.
+ */
+void *kmemdup_array(const void *src, size_t element_size, size_t count, gfp_t gfp)
+{
+       return kmemdup(src, size_mul(element_size, count), gfp);
+}
+EXPORT_SYMBOL(kmemdup_array);
+
 /**
  * kvmemdup - duplicate region of memory
  *