This RFC patch series is intended to introduce the potential generic driver for
pin controls over SCMI protocol, provided in the latest beta version of DEN0056 [0].
On ARM-based systems, a separate Cortex-M based System Control Processor (SCP)
provides control on pins, as well as with power, clocks, reset controllers. In this case,
kernel should use one of the possible transports, described in [0] to access SCP and
control clocks/power-domains etc. This driver is using SMC transport to communicate with SCP via
SCMI protocol and access to the Pin Control Subsystem.
The provided driver consists of 2 parts:
- firmware/arm_scmi/pinctrl.c - the SCMI pinctrl protocol inmplementation
responsible for the communication with SCP firmware.
- drivers/pinctrl/pinctrl-scmi.c - pinctrl driver, which is using pinctrl
protocol implementation to access all necessary data.
Configuration:
The scmi-pinctrl driver can be configured using DT bindings.
For example:
/ {
cpu_scp_shm: scp-shmem@0x53FF0000 {
compatible = "arm,scmi-shmem";
reg = <0x0 0x53FF0000 0x0 0x1000>;
};
firmware {
scmi {
compatible = "arm,scmi-smc";
arm,smc-id = <0x82000002>;
shmem = <&cpu_scp_shm>;
#address-cells = <1>;
#size-cells = <0>;
scmi_pinctrl: protocol@19 {
reg = <0x18>;
#pinctrl-cells = <0>;
i2c2_pins: i2c2 {
groups = "i2c2_a";
function = "i2c2";
};
};
};
};
};
&pfc {
/delete-node/i2c2;
};
So basically, it's enough to move pfc subnode, which configures pin group that should work through
SCMI protocol to scmi_pinctrl node. The current driver implementation is using generic pinctrl dt_node
format.
I've tested this driver on the Renesas H3ULCB Kingfisher board with pinctrl driver ported to the
Arm-trusted-firmware. Unfortunately, not all hardware was possible to test because the Renesas
pinctrl driver has gaps in pins and groups numeration, when Spec [0] requires pins, groups and
functions numerations to be 0..n without gaps.
This implementation still reqires some features, such as gpio support, but I've posted it as RFC to
start the discussion regarding the driver format.
[0] https://developer.arm.com/documentation/den0056/latest
Oleksii Moisieiev (2):
scmi: Introduce pinctrl SCMI protocol driver
pinctrl: Implementation of the generic scmi-pinctrl driver
MAINTAINERS | 7 +
drivers/firmware/arm_scmi/Makefile | 2 +-
drivers/firmware/arm_scmi/common.h | 1 +
drivers/firmware/arm_scmi/driver.c | 3 +
drivers/firmware/arm_scmi/pinctrl.c | 905 ++++++++++++++++++++++++++++
drivers/pinctrl/Kconfig | 9 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-scmi.c | 555 +++++++++++++++++
include/linux/scmi_protocol.h | 58 +-
9 files changed, 1539 insertions(+), 2 deletions(-)
create mode 100644 drivers/firmware/arm_scmi/pinctrl.c
create mode 100644 drivers/pinctrl/pinctrl-scmi.c
--
2.25.1
scmi-pinctrl driver implements pinctrl driver interface and using
SCMI protocol to redirect messages from pinctrl subsystem SDK to
SCP firmware, which does the changes in HW.
This setup expects SCP firmware (or similar system, such as ATF)
to be installed on the platform, which implements pinctrl driver
for the specific platform.
SCMI-Pinctrl driver should be configured from the device-tree and uses
generic device-tree mappings for the configuration.
Signed-off-by: Oleksii Moisieiev <[email protected]>
---
MAINTAINERS | 1 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-scmi.c | 555 +++++++++++++++++++++++++++++++++
3 files changed, 557 insertions(+)
create mode 100644 drivers/pinctrl/pinctrl-scmi.c
diff --git a/MAINTAINERS b/MAINTAINERS
index abc543fd7544..f7559b3b5967 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16966,6 +16966,7 @@ M: Oleksii Moisieiev <[email protected]>
L: [email protected]
S: Maintained
F: drivers/firmware/arm_scmi/pinctrl.c
+F: drivers/pinctrl/pinctrl-scmi.c
SYSTEM RESET/SHUTDOWN DRIVERS
M: Sebastian Reichel <[email protected]>
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index f53933b2ff02..163fe8a06878 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_PINCTRL_INGENIC) += pinctrl-ingenic.o
obj-$(CONFIG_PINCTRL_RK805) += pinctrl-rk805.o
obj-$(CONFIG_PINCTRL_OCELOT) += pinctrl-ocelot.o
obj-$(CONFIG_PINCTRL_EQUILIBRIUM) += pinctrl-equilibrium.o
+obj-$(CONFIG_PINCTRL_SCMI) += pinctrl-scmi.o
obj-y += actions/
obj-$(CONFIG_ARCH_ASPEED) += aspeed/
diff --git a/drivers/pinctrl/pinctrl-scmi.c b/drivers/pinctrl/pinctrl-scmi.c
new file mode 100644
index 000000000000..9b0e884a2829
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-scmi.c
@@ -0,0 +1,555 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Power Interface (SCMI) Protocol based clock driver
+ *
+ * Copyright (C) 2021 EPAM.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/scmi_protocol.h>
+#include <linux/slab.h>
+
+#include "pinctrl-utils.h"
+#include "core.h"
+#include "pinconf.h"
+
+#define DRV_NAME "scmi-pinctrl"
+
+struct scmi_pinctrl_funcs {
+ unsigned int num_groups;
+ const char **groups;
+};
+
+struct scmi_pinctrl {
+ struct device *dev;
+ struct scmi_handle *handle;
+ struct pinctrl_dev *pctldev;
+ struct pinctrl_desc pctl_desc;
+ struct scmi_pinctrl_funcs *functions;
+ unsigned int nr_functions;
+ char **groups;
+ unsigned int nr_groups;
+ struct pinctrl_pin_desc *pins;
+ unsigned int nr_pins;
+};
+
+static int pinctrl_scmi_get_groups_count(struct pinctrl_dev *pctldev)
+{
+ const struct scmi_handle *handle;
+ struct scmi_pinctrl *pmx;
+
+ if (!pctldev)
+ return -EINVAL;
+
+ pmx = pinctrl_dev_get_drvdata(pctldev);
+
+ if (!pmx || !pmx->handle)
+ return -EINVAL;
+
+ handle = pmx->handle;
+
+ return handle->pinctrl_ops->get_groups_count(handle);
+}
+
+static const char *pinctrl_scmi_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned int selector)
+{
+ int ret;
+ const char *name;
+ const struct scmi_handle *handle;
+ struct scmi_pinctrl *pmx;
+
+ if (!pctldev)
+ return NULL;
+
+ pmx = pinctrl_dev_get_drvdata(pctldev);
+
+ if (!pmx || !pmx->handle)
+ return NULL;
+
+ handle = pmx->handle;
+
+ ret = handle->pinctrl_ops->get_group_name(handle, selector, &name);
+ if (ret) {
+ dev_err(pmx->dev, "get name failed with err %d", ret);
+ return NULL;
+ }
+
+ return name;
+}
+
+static int pinctrl_scmi_get_group_pins(struct pinctrl_dev *pctldev,
+ unsigned int selector,
+ const unsigned int **pins,
+ unsigned int *num_pins)
+{
+ const struct scmi_handle *handle;
+ struct scmi_pinctrl *pmx;
+
+ if (!pctldev)
+ return -EINVAL;
+
+ pmx = pinctrl_dev_get_drvdata(pctldev);
+
+ if (!pmx || !pmx->handle)
+ return -EINVAL;
+
+ handle = pmx->handle;
+
+ return handle->pinctrl_ops->get_group_pins(handle, selector,
+ pins, num_pins);
+}
+
+static void pinctrl_scmi_pin_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s,
+ unsigned int offset)
+{
+ seq_puts(s, DRV_NAME);
+}
+
+#ifdef CONFIG_OF
+static int pinctrl_scmi_dt_node_to_map(struct pinctrl_dev *pctldev,
+ struct device_node *np_config,
+ struct pinctrl_map **map,
+ u32 *num_maps)
+{
+ return pinconf_generic_dt_node_to_map(pctldev, np_config, map,
+ num_maps, PIN_MAP_TYPE_INVALID);
+}
+
+static void pinctrl_scmi_dt_free_map(struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, u32 num_maps)
+{
+ kfree(map);
+}
+
+#endif /* CONFIG_OF */
+
+static const struct pinctrl_ops pinctrl_scmi_pinctrl_ops = {
+ .get_groups_count = pinctrl_scmi_get_groups_count,
+ .get_group_name = pinctrl_scmi_get_group_name,
+ .get_group_pins = pinctrl_scmi_get_group_pins,
+ .pin_dbg_show = pinctrl_scmi_pin_dbg_show,
+#ifdef CONFIG_OF
+ .dt_node_to_map = pinctrl_scmi_dt_node_to_map,
+ .dt_free_map = pinctrl_scmi_dt_free_map,
+#endif
+};
+
+static int pinctrl_scmi_get_functions_count(struct pinctrl_dev *pctldev)
+{
+ const struct scmi_handle *handle;
+ struct scmi_pinctrl *pmx;
+
+ if (!pctldev)
+ return -EINVAL;
+
+ pmx = pinctrl_dev_get_drvdata(pctldev);
+
+ if (!pmx || !pmx->handle)
+ return -EINVAL;
+
+ handle = pmx->handle;
+
+ return handle->pinctrl_ops->get_functions_count(handle);
+}
+
+static const char *pinctrl_scmi_get_function_name(struct pinctrl_dev *pctldev,
+ unsigned int selector)
+{
+ int ret;
+ const char *name;
+ const struct scmi_handle *handle;
+ struct scmi_pinctrl *pmx;
+
+ if (!pctldev)
+ return NULL;
+
+ pmx = pinctrl_dev_get_drvdata(pctldev);
+
+ if (!pmx || !pmx->handle)
+ return NULL;
+
+ handle = pmx->handle;
+
+ ret = handle->pinctrl_ops->get_function_name(handle, selector, &name);
+ if (ret) {
+ dev_err(pmx->dev, "get name failed with err %d", ret);
+ return NULL;
+ }
+
+ return name;
+}
+
+static int pinctrl_scmi_get_function_groups(struct pinctrl_dev *pctldev,
+ unsigned int selector,
+ const char * const **groups,
+ unsigned int * const num_groups)
+{
+ const unsigned int *group_ids;
+ int ret, i;
+ const struct scmi_handle *handle;
+ struct scmi_pinctrl *pmx;
+
+ if (!pctldev)
+ return -EINVAL;
+
+ pmx = pinctrl_dev_get_drvdata(pctldev);
+
+ if (!pmx || !pmx->handle || !groups || !num_groups)
+ return -EINVAL;
+
+ handle = pmx->handle;
+
+ if ((selector < pmx->nr_functions)
+ && (pmx->functions[selector].num_groups)) {
+ *groups = (const char * const *)pmx->functions[selector].groups;
+ *num_groups = pmx->functions[selector].num_groups;
+ return 0;
+ }
+
+ ret = handle->pinctrl_ops->get_function_groups(handle, selector,
+ &pmx->functions[selector].num_groups,
+ &group_ids);
+ if (ret) {
+ dev_err(pmx->dev, "Unable to get function groups, err %d", ret);
+ return ret;
+ }
+
+ *num_groups = pmx->functions[selector].num_groups;
+ if (!*num_groups)
+ return -EINVAL;
+
+ pmx->functions[selector].groups =
+ devm_kmalloc_array(pmx->dev, *num_groups,
+ sizeof(*pmx->functions[selector].groups),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!pmx->functions[selector].groups)
+ return -ENOMEM;
+
+ for (i = 0; i < *num_groups; i++) {
+ pmx->functions[selector].groups[i]
+ = pinctrl_scmi_get_group_name(pmx->pctldev,
+ group_ids[i]);
+ if (!pmx->functions[selector].groups[i]) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ }
+
+ *groups = (const char * const *)pmx->functions[selector].groups;
+
+ return 0;
+
+error:
+ kfree(pmx->functions[selector].groups);
+
+ return ret;
+}
+
+static int pinctrl_scmi_func_set_mux(struct pinctrl_dev *pctldev,
+ unsigned int selector, unsigned int group)
+{
+ const struct scmi_handle *handle;
+ struct scmi_pinctrl *pmx;
+
+ if (!pctldev)
+ return -EINVAL;
+
+ pmx = pinctrl_dev_get_drvdata(pctldev);
+
+ if (!pmx || !pmx->handle)
+ return -EINVAL;
+
+ handle = pmx->handle;
+
+ return handle->pinctrl_ops->set_mux(handle, selector, group);
+}
+
+static int pinctrl_scmi_request(struct pinctrl_dev *pctldev,
+ unsigned int offset)
+{
+ const struct scmi_handle *handle;
+ struct scmi_pinctrl *pmx;
+
+ if (!pctldev)
+ return -EINVAL;
+
+ pmx = pinctrl_dev_get_drvdata(pctldev);
+
+ if (!pmx || !pmx->handle)
+ return -EINVAL;
+
+ handle = pmx->handle;
+
+ return handle->pinctrl_ops->request_pin(handle, offset);
+}
+
+static int pinctrl_scmi_free(struct pinctrl_dev *pctldev, unsigned int offset)
+{
+ const struct scmi_handle *handle;
+ struct scmi_pinctrl *pmx;
+
+ if (!pctldev)
+ return -EINVAL;
+
+ pmx = pinctrl_dev_get_drvdata(pctldev);
+
+ if (!pmx || !pmx->handle)
+ return -EINVAL;
+
+ handle = pmx->handle;
+
+ return handle->pinctrl_ops->free_pin(handle, offset);
+}
+
+static const struct pinmux_ops pinctrl_scmi_pinmux_ops = {
+ .request = pinctrl_scmi_request,
+ .free = pinctrl_scmi_free,
+ .get_functions_count = pinctrl_scmi_get_functions_count,
+ .get_function_name = pinctrl_scmi_get_function_name,
+ .get_function_groups = pinctrl_scmi_get_function_groups,
+ .set_mux = pinctrl_scmi_func_set_mux,
+};
+
+static int pinctrl_scmi_pinconf_get(struct pinctrl_dev *pctldev,
+ unsigned int _pin,
+ unsigned long *config)
+{
+ const struct scmi_handle *handle;
+ struct scmi_pinctrl *pmx;
+
+ if (!pctldev)
+ return -EINVAL;
+
+ pmx = pinctrl_dev_get_drvdata(pctldev);
+
+ if (!pmx || !pmx->handle || !config)
+ return -EINVAL;
+
+ handle = pmx->handle;
+
+ return handle->pinctrl_ops->get_config(handle, _pin, (u32 *)config);
+}
+
+static int pinctrl_scmi_pinconf_set(struct pinctrl_dev *pctldev,
+ unsigned int _pin,
+ unsigned long *configs,
+ unsigned int num_configs)
+{
+ const struct scmi_handle *handle;
+ int i, ret;
+ struct scmi_pinctrl *pmx;
+
+ if (!pctldev)
+ return -EINVAL;
+
+ pmx = pinctrl_dev_get_drvdata(pctldev);
+
+ if (!pmx || !pmx->handle || !configs || num_configs == 0)
+ return -EINVAL;
+
+ handle = pmx->handle;
+
+ for (i = 0; i < num_configs; i++) {
+ ret = handle->pinctrl_ops->set_config(handle, _pin, configs[i]);
+ if (ret) {
+ dev_err(pmx->dev, "Error parsing config %ld\n",
+ configs[i]);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int pinctrl_scmi_pinconf_group_set(struct pinctrl_dev *pctldev,
+ unsigned int group,
+ unsigned long *configs,
+ unsigned int num_configs)
+{
+ const struct scmi_handle *handle;
+ int i, ret;
+ struct scmi_pinctrl *pmx;
+
+ if (!pctldev)
+ return -EINVAL;
+
+ pmx = pinctrl_dev_get_drvdata(pctldev);
+
+ if (!pmx || !pmx->handle || !configs || num_configs == 0)
+ return -EINVAL;
+
+ handle = pmx->handle;
+
+ for (i = 0; i < num_configs; i++) {
+ ret = handle->pinctrl_ops->set_config_group(handle, group,
+ configs[i]);
+ if (ret) {
+ dev_err(pmx->dev, "Error parsing config = %ld",
+ configs[i]);
+ break;
+ }
+ }
+
+ return ret;
+};
+
+static const struct pinconf_ops pinctrl_scmi_pinconf_ops = {
+ .is_generic = true,
+ .pin_config_get = pinctrl_scmi_pinconf_get,
+ .pin_config_set = pinctrl_scmi_pinconf_set,
+ .pin_config_group_set = pinctrl_scmi_pinconf_group_set,
+ .pin_config_config_dbg_show = pinconf_generic_dump_config,
+};
+
+static int pinctrl_scmi_get_pins(struct scmi_pinctrl *pmx,
+ unsigned int *nr_pins,
+ const struct pinctrl_pin_desc **pins)
+{
+ int ret, i;
+ struct scmi_handle *handle;
+
+ if (!pmx)
+ return -EINVAL;
+
+ handle = pmx->handle;
+
+ if (!handle || !pins || !nr_pins)
+ return -EINVAL;
+
+ if (pmx->nr_pins) {
+ *pins = pmx->pins;
+ *nr_pins = pmx->nr_pins;
+ return 0;
+ }
+
+ *nr_pins = handle->pinctrl_ops->get_pins_count(handle);
+
+ pmx->nr_pins = *nr_pins;
+ pmx->pins = devm_kmalloc_array(pmx->dev, *nr_pins, sizeof(*pmx->pins),
+ GFP_KERNEL);
+ if (!pmx->pins)
+ return -ENOMEM;
+
+ for (i = 0; i < *nr_pins; i++) {
+ pmx->pins[i].number = i;
+ ret = handle->pinctrl_ops->get_pin_name(handle, i,
+ &pmx->pins[i].name);
+ if (ret) {
+ dev_err(pmx->dev, "Can't get name for pin %d: rc %d",
+ i, ret);
+ goto err;
+ }
+ }
+
+ *pins = pmx->pins;
+ dev_dbg(pmx->dev, "got pins %d", *nr_pins);
+
+ return 0;
+ err:
+ kfree(pmx->pins);
+ pmx->nr_pins = 0;
+
+ return ret;
+}
+
+static const struct scmi_device_id scmi_id_table[] = {
+ { SCMI_PROTOCOL_PINCTRL, "pinctrl" },
+ { },
+};
+MODULE_DEVICE_TABLE(scmi, scmi_id_table);
+
+static int scmi_pinctrl_probe(struct scmi_device *sdev)
+{
+ int ret;
+ struct scmi_pinctrl *pmx;
+
+ if (!sdev || !sdev->handle)
+ return -EINVAL;
+
+ pmx = devm_kzalloc(&sdev->dev, sizeof(*pmx), GFP_KERNEL);
+ if (!pmx)
+ return -ENOMEM;
+
+ pmx->handle = sdev->handle;
+ if (!pmx->handle) {
+ ret = -ENOMEM;
+ goto clean;
+ }
+
+ pmx->dev = &sdev->dev;
+ pmx->pctl_desc.name = DRV_NAME;
+ pmx->pctl_desc.owner = THIS_MODULE;
+ pmx->pctl_desc.pctlops = &pinctrl_scmi_pinctrl_ops;
+ pmx->pctl_desc.pmxops = &pinctrl_scmi_pinmux_ops;
+ pmx->pctl_desc.confops = &pinctrl_scmi_pinconf_ops;
+
+ ret = pinctrl_scmi_get_pins(pmx, &pmx->pctl_desc.npins,
+ &pmx->pctl_desc.pins);
+ if (ret)
+ goto clean;
+
+ ret = devm_pinctrl_register_and_init(&sdev->dev, &pmx->pctl_desc, pmx,
+ &pmx->pctldev);
+ if (ret) {
+ dev_err(&sdev->dev, "could not register: %i\n", ret);
+ goto clean;
+ }
+
+ pmx->nr_functions = pinctrl_scmi_get_functions_count(pmx->pctldev);
+ pmx->nr_groups = pinctrl_scmi_get_groups_count(pmx->pctldev);
+
+ if (pmx->nr_functions) {
+ pmx->functions =
+ devm_kmalloc_array(&sdev->dev, pmx->nr_functions,
+ sizeof(*pmx->functions),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!pmx->functions) {
+ ret = -ENOMEM;
+ goto clean;
+ }
+ }
+
+ if (pmx->nr_groups) {
+ pmx->groups =
+ devm_kmalloc_array(&sdev->dev, pmx->nr_groups,
+ sizeof(*pmx->groups),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!pmx->groups) {
+ ret = -ENOMEM;
+ goto clean;
+ }
+ }
+
+ return pinctrl_enable(pmx->pctldev);
+
+clean:
+ if (pmx) {
+ kfree(pmx->functions);
+ kfree(pmx->groups);
+ }
+
+ kfree(pmx);
+
+ return ret;
+}
+
+static struct scmi_driver scmi_pinctrl_driver = {
+ .name = DRV_NAME,
+ .probe = scmi_pinctrl_probe,
+ .id_table = scmi_id_table,
+};
+module_scmi_driver(scmi_pinctrl_driver);
+
+MODULE_AUTHOR("Oleksii Moisieiev <[email protected]>");
+MODULE_DESCRIPTION("ARM SCMI pin controller driver");
+MODULE_LICENSE("GPL v2");
--
2.25.1
Implementation of the SCMI client driver, which implements
PINCTRL_PROTOCOL. This protocol has ID 19 and is described
in the latest DEN0056 document.
This protocol is part of the feature that was designed to
separate the pinctrl subsystem from the SCP firmware.
The idea is to separate communication of the pin control
subsystem with the hardware to SCP firmware
(or a similar system, such as ATF), which provides an interface
to give the OS ability to control the hardware through SCMI protocol.
This is a generic driver that implements SCMI protocol,
independent of the platform type.
DEN0056 document:
https://developer.arm.com/documentation/den0056/latest
Signed-off-by: Oleksii Moisieiev <[email protected]>
---
MAINTAINERS | 6 +
drivers/firmware/arm_scmi/Makefile | 2 +-
drivers/firmware/arm_scmi/common.h | 1 +
drivers/firmware/arm_scmi/driver.c | 3 +
drivers/firmware/arm_scmi/pinctrl.c | 905 ++++++++++++++++++++++++++++
drivers/pinctrl/Kconfig | 9 +
include/linux/scmi_protocol.h | 58 +-
7 files changed, 982 insertions(+), 2 deletions(-)
create mode 100644 drivers/firmware/arm_scmi/pinctrl.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 281de213ef47..abc543fd7544 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16961,6 +16961,12 @@ F: drivers/reset/reset-scmi.c
F: include/linux/sc[mp]i_protocol.h
F: include/trace/events/scmi.h
+PINCTRL DRIVER FOR SYSTEM CONTROL & POWER/MANAGEMENT INTERFACE (SCPI/SCMI)
+M: Oleksii Moisieiev <[email protected]>
+L: [email protected]
+S: Maintained
+F: drivers/firmware/arm_scmi/pinctrl.c
+
SYSTEM RESET/SHUTDOWN DRIVERS
M: Sebastian Reichel <[email protected]>
L: [email protected]
diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index bc0d54f8e861..5cec357fbfa8 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -4,7 +4,7 @@ scmi-driver-y = driver.o notify.o
scmi-transport-y = shmem.o
scmi-transport-$(CONFIG_MAILBOX) += mailbox.o
scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o
-scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o
+scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o pinctrl.o
scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \
$(scmi-transport-y)
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index 65063fa948d4..8bbb404abe8d 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -170,6 +170,7 @@ DECLARE_SCMI_REGISTER_UNREGISTER(power);
DECLARE_SCMI_REGISTER_UNREGISTER(reset);
DECLARE_SCMI_REGISTER_UNREGISTER(sensors);
DECLARE_SCMI_REGISTER_UNREGISTER(system);
+DECLARE_SCMI_REGISTER_UNREGISTER(pinctrl);
#define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(id, name) \
int __init scmi_##name##_register(void) \
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 3dfd8b6a0ebf..fb9525fb3c24 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -743,6 +743,7 @@ static struct scmi_prot_devnames devnames[] = {
{ SCMI_PROTOCOL_CLOCK, { "clocks" },},
{ SCMI_PROTOCOL_SENSOR, { "hwmon" },},
{ SCMI_PROTOCOL_RESET, { "reset" },},
+ { SCMI_PROTOCOL_PINCTRL, { "pinctrl" },},
};
static inline void
@@ -947,6 +948,7 @@ static int __init scmi_driver_init(void)
scmi_reset_register();
scmi_sensors_register();
scmi_system_register();
+ scmi_pinctrl_register();
return platform_driver_register(&scmi_driver);
}
@@ -962,6 +964,7 @@ static void __exit scmi_driver_exit(void)
scmi_reset_unregister();
scmi_sensors_unregister();
scmi_system_unregister();
+ scmi_pinctrl_unregister();
platform_driver_unregister(&scmi_driver);
}
diff --git a/drivers/firmware/arm_scmi/pinctrl.c b/drivers/firmware/arm_scmi/pinctrl.c
new file mode 100644
index 000000000000..037270d7f39b
--- /dev/null
+++ b/drivers/firmware/arm_scmi/pinctrl.c
@@ -0,0 +1,905 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Pinctrl Protocol
+ *
+ * Copyright (C) 2021 EPAM.
+ */
+
+#define pr_fmt(fmt) "SCMI Notifications PINCTRL - " fmt
+
+#include <linux/scmi_protocol.h>
+#include <linux/slab.h>
+
+#include "common.h"
+#include "notify.h"
+
+#define SET_TYPE(x) ((x) & 0x3)
+
+enum scmi_pinctrl_protocol_cmd {
+ PINCTRL_ATTRIBUTES = 0x3,
+ PINCTRL_LIST_ASSOCIATIONS = 0x4,
+ PINCTRL_CONFIG_GET = 0x5,
+ PINCTRL_CONFIG_SET = 0x6,
+ PINCTRL_FUNCTION_SELECT = 0x7,
+ PINCTRL_REQUEST = 0x8,
+ PINCTRL_RELEASE = 0x9,
+ PINCTRL_NAME_GET = 0xa,
+ PINCTRL_SET_PERMISSIONS = 0xb
+};
+
+enum scmi_pinctrl_selector_type {
+ PIN_TYPE = 0,
+ GROUP_TYPE,
+ FUNCTION_TYPE
+};
+
+struct scmi_group_info {
+ bool present;
+ char *name;
+ unsigned int *group_pins;
+ unsigned int nr_pins;
+};
+
+struct scmi_function_info {
+ bool present;
+ char *name;
+ unsigned int *groups;
+ unsigned int nr_groups;
+};
+
+struct scmi_pin_info {
+ bool present;
+ char *name;
+};
+
+struct scmi_pinctrl_info {
+ u32 version;
+ u16 nr_groups;
+ u16 nr_functions;
+ u16 nr_pins;
+ struct scmi_group_info *groups;
+ struct scmi_function_info *functions;
+ struct scmi_pin_info *pins;
+};
+
+struct scmi_conf_tx {
+ __le32 identifier;
+#define SET_TYPE_BITS(attr, x) (((attr) & 0xFFFFFCFF) | (x & 0x3) << 8)
+#define SET_CONFIG(attr, x) (((attr) & 0xFF) | (x & 0xFF))
+ __le32 attributes;
+};
+
+static int scmi_pinctrl_attributes_get(const struct scmi_handle *handle,
+ struct scmi_pinctrl_info *pi)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_pinctrl_protocol_attributes {
+#define GROUPS_NR(x) ((x) >> 16)
+#define PINS_NR(x) ((x) & 0xffff)
+ __le32 attributes_low;
+#define FUNCTIONS_NR(x) ((x) & 0xffff)
+ __le32 attributes_high;
+ } *attr;
+
+ if (!pi)
+ return -EINVAL;
+
+ ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
+ SCMI_PROTOCOL_PINCTRL, 0, sizeof(*attr), &t);
+ if (ret)
+ return ret;
+
+ attr = t->rx.buf;
+
+ ret = scmi_do_xfer(handle, t);
+ if (!ret) {
+ pi->nr_functions =
+ le16_to_cpu(FUNCTIONS_NR(attr->attributes_high));
+ pi->nr_groups = le16_to_cpu(GROUPS_NR(attr->attributes_low));
+ pi->nr_pins = le16_to_cpu(PINS_NR(attr->attributes_low));
+ }
+
+ scmi_xfer_put(handle, t);
+ return ret;
+}
+
+static int scmi_pinctrl_get_groups_count(const struct scmi_handle *handle)
+{
+ struct scmi_pinctrl_info *pi;
+
+ if (!handle || !handle->pinctrl_priv)
+ return -ENODEV;
+
+ pi = handle->pinctrl_priv;
+
+ return pi->nr_groups;
+}
+
+static int scmi_pinctrl_get_pins_count(const struct scmi_handle *handle)
+{
+ struct scmi_pinctrl_info *pi;
+
+ if (!handle || !handle->pinctrl_priv)
+ return -ENODEV;
+
+ pi = handle->pinctrl_priv;
+
+ return pi->nr_pins;
+}
+
+static int scmi_pinctrl_get_functions_count(const struct scmi_handle *handle)
+{
+ struct scmi_pinctrl_info *pi;
+
+ if (!handle || !handle->pinctrl_priv)
+ return -ENODEV;
+
+ pi = handle->pinctrl_priv;
+
+ return pi->nr_functions;
+}
+
+static int scmi_pinctrl_validate_id(const struct scmi_handle *handle,
+ u32 identifier,
+ enum scmi_pinctrl_selector_type type)
+{
+ struct scmi_pinctrl_info *pi;
+
+ if (!handle || !handle->pinctrl_priv)
+ return -ENODEV;
+
+ switch (type) {
+ case PIN_TYPE:
+ pi = handle->pinctrl_priv;
+
+ return (identifier < pi->nr_pins) ? 0 : -EINVAL;
+ case GROUP_TYPE:
+ return (identifier <
+ scmi_pinctrl_get_groups_count(handle)) ?
+ 0 : -EINVAL;
+ case FUNCTION_TYPE:
+ return (identifier <
+ scmi_pinctrl_get_functions_count(handle)) ?
+ 0 : -EINVAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int scmi_pinctrl_get_name(const struct scmi_handle *handle,
+ u32 identifier,
+ enum scmi_pinctrl_selector_type type,
+ char **name)
+{
+ struct scmi_xfer *t;
+ int ret = 0;
+ struct scmi_name_tx {
+ __le32 identifier;
+ __le32 flags;
+ } *tx;
+ struct scmi_name_rx {
+ __le32 flags;
+ u8 name[64];
+ } *rx;
+
+ if (!handle || !name)
+ return -EINVAL;
+
+ ret = scmi_pinctrl_validate_id(handle, identifier, type);
+ if (ret)
+ return ret;
+
+ ret = scmi_xfer_get_init(handle, PINCTRL_NAME_GET,
+ SCMI_PROTOCOL_PINCTRL,
+ sizeof(*tx), sizeof(*rx), &t);
+
+ tx = t->tx.buf;
+ rx = t->rx.buf;
+ tx->identifier = identifier;
+ tx->flags = SET_TYPE(cpu_to_le32(type));
+
+ ret = scmi_do_xfer(handle, t);
+ if (ret)
+ goto out;
+
+ if (rx->flags) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ *name = kasprintf(GFP_KERNEL, "%s", rx->name);
+ if (!*name)
+ ret = -ENOMEM;
+ out:
+ scmi_xfer_put(handle, t);
+
+ return ret;
+}
+
+static int scmi_pinctrl_attributes(const struct scmi_handle *handle,
+ enum scmi_pinctrl_selector_type type,
+ u32 selector, char **name,
+ unsigned int *n_elems)
+{
+ int ret = 0;
+ struct scmi_xfer *t;
+ struct scmi_pinctrl_attributes_tx {
+ __le32 identifier;
+ __le32 flags;
+ } *tx;
+ struct scmi_pinctrl_attributes_rx {
+#define EXT_NAME_FLAG(x) ((x) & BIT(31))
+#define NUM_ELEMS(x) ((x) & 0xffff)
+ __le32 attributes;
+ u8 name[16];
+ } *rx;
+
+ if (!handle || !name)
+ return -EINVAL;
+
+ ret = scmi_pinctrl_validate_id(handle, selector, type);
+ if (ret)
+ return ret;
+
+ ret = scmi_xfer_get_init(handle, PINCTRL_ATTRIBUTES,
+ SCMI_PROTOCOL_PINCTRL, sizeof(*tx), sizeof(*rx), &t);
+ if (ret)
+ return ret;
+
+ tx = t->tx.buf;
+ rx = t->rx.buf;
+ tx->identifier = selector;
+ tx->flags = SET_TYPE(cpu_to_le32(type));
+
+ ret = scmi_do_xfer(handle, t);
+ if (ret)
+ goto out;
+
+ *n_elems = NUM_ELEMS(rx->attributes);
+
+ if (!EXT_NAME_FLAG(rx->attributes)) {
+ *name = kasprintf(GFP_KERNEL, "%s", rx->name);
+ if (!*name)
+ ret = -ENOMEM;
+ } else
+ ret = scmi_pinctrl_get_name(handle, selector, type, name);
+ out:
+ scmi_xfer_put(handle, t);
+ return ret;
+}
+
+static int scmi_pinctrl_list_associations(const struct scmi_handle *handle,
+ u32 selector,
+ enum scmi_pinctrl_selector_type type,
+ uint16_t size, unsigned int *array)
+{
+ struct scmi_xfer *t;
+ struct scmi_pinctrl_list_assoc_tx {
+ __le32 identifier;
+ __le32 flags;
+ __le32 index;
+ } *tx;
+ struct scmi_pinctrl_list_assoc_rx {
+#define RETURNED(x) ((x) & 0xFFF)
+#define REMAINING(x) ((x) >> 16)
+ __le32 flags;
+ __le16 array[];
+ } *rx;
+ u16 tot_num_ret = 0, loop_num_ret;
+ u16 remaining_num_ret;
+ int ret, loop;
+
+ if (!handle || !array || !size)
+ return -EINVAL;
+
+ if (type == PIN_TYPE)
+ return -EINVAL;
+
+ ret = scmi_pinctrl_validate_id(handle, selector, type);
+ if (ret)
+ return ret;
+
+ ret = scmi_xfer_get_init(handle, PINCTRL_LIST_ASSOCIATIONS,
+ SCMI_PROTOCOL_PINCTRL, sizeof(*tx),
+ 0, &t);
+ if (ret)
+ return ret;
+
+ tx = t->tx.buf;
+ rx = t->rx.buf;
+
+ do {
+ tx->identifier = cpu_to_le32(selector);
+ tx->flags = SET_TYPE(cpu_to_le32(type));
+ tx->index = cpu_to_le32(tot_num_ret);
+
+ ret = scmi_do_xfer(handle, t);
+ if (ret)
+ break;
+
+ loop_num_ret = le32_to_cpu(RETURNED(rx->flags));
+ remaining_num_ret = le32_to_cpu(REMAINING(rx->flags));
+
+ for (loop = 0; loop < loop_num_ret; loop++) {
+ if (tot_num_ret + loop >= size) {
+ ret = -EMSGSIZE;
+ goto out;
+ }
+
+ array[tot_num_ret + loop] =
+ le16_to_cpu(rx->array[loop]);
+ }
+
+ tot_num_ret += loop_num_ret;
+
+ scmi_reset_rx_to_maxsz(handle, t);
+ } while (remaining_num_ret > 0);
+out:
+ scmi_xfer_put(handle, t);
+ return ret;
+}
+
+static int scmi_pinctrl_request_config(const struct scmi_handle *handle,
+ u32 selector,
+ enum scmi_pinctrl_selector_type type,
+ u32 *config)
+{
+ struct scmi_xfer *t;
+ struct scmi_conf_tx *tx;
+ __le32 *packed_config;
+ u32 attributes = 0;
+ int ret;
+
+ if (!handle || !config || type == FUNCTION_TYPE)
+ return -EINVAL;
+
+ ret = scmi_pinctrl_validate_id(handle, selector, type);
+ if (ret)
+ return ret;
+
+ ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_GET,
+ SCMI_PROTOCOL_PINCTRL,
+ sizeof(*tx), sizeof(*packed_config), &t);
+ if (ret)
+ return ret;
+
+ tx = t->tx.buf;
+ packed_config = t->rx.buf;
+ tx->identifier = cpu_to_le32(selector);
+ attributes = SET_TYPE_BITS(attributes, type);
+ attributes = SET_CONFIG(attributes, *config);
+
+ tx->attributes = cpu_to_le32(attributes);
+
+ ret = scmi_do_xfer(handle, t);
+
+ if (!ret)
+ *config = le32_to_cpu(*packed_config);
+
+ scmi_xfer_put(handle, t);
+ return ret;
+}
+
+static int scmi_pinctrl_get_config(const struct scmi_handle *handle, u32 pin,
+ u32 *config)
+{
+ return scmi_pinctrl_request_config(handle, pin, PIN_TYPE, config);
+}
+
+static int scmi_pinctrl_apply_config(const struct scmi_handle *handle,
+ u32 selector,
+ enum scmi_pinctrl_selector_type type,
+ u32 config)
+{
+ struct scmi_xfer *t;
+ struct scmi_conf_tx *tx;
+ u32 attributes = 0;
+ int ret;
+
+ if (!handle || type == FUNCTION_TYPE)
+ return -EINVAL;
+
+ ret = scmi_pinctrl_validate_id(handle, selector, type);
+ if (ret)
+ return ret;
+
+ ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_SET,
+ SCMI_PROTOCOL_PINCTRL,
+ sizeof(*tx), 0, &t);
+ if (ret)
+ return ret;
+
+ tx = t->tx.buf;
+ tx->identifier = cpu_to_le32(selector);
+ attributes = SET_TYPE_BITS(attributes, type);
+ attributes = SET_CONFIG(attributes, config);
+ tx->attributes = cpu_to_le32(attributes);
+
+ ret = scmi_do_xfer(handle, t);
+
+ scmi_xfer_put(handle, t);
+ return ret;
+}
+
+static int scmi_pinctrl_set_config(const struct scmi_handle *handle, u32 pin,
+ u32 config)
+{
+ return scmi_pinctrl_apply_config(handle, pin, PIN_TYPE, config);
+}
+
+static int scmi_pinctrl_get_config_group(const struct scmi_handle *handle,
+ u32 group, u32 *config)
+{
+ return scmi_pinctrl_request_config(handle, group, GROUP_TYPE, config);
+}
+
+static int scmi_pinctrl_set_config_group(const struct scmi_handle *handle,
+ u32 group, u32 config)
+{
+ return scmi_pinctrl_apply_config(handle, group, GROUP_TYPE, config);
+}
+
+static int scmi_pinctrl_function_select(const struct scmi_handle *handle,
+ u32 identifier,
+ enum scmi_pinctrl_selector_type type,
+ u32 function_id)
+{
+ struct scmi_xfer *t;
+ struct scmi_func_set_tx {
+ __le32 identifier;
+ __le32 function_id;
+ __le32 flags;
+ } *tx;
+ int ret;
+
+ if (!handle || type == FUNCTION_TYPE)
+ return -EINVAL;
+
+ ret = scmi_pinctrl_validate_id(handle, identifier, type);
+ if (ret)
+ return ret;
+
+ ret = scmi_xfer_get_init(handle, PINCTRL_FUNCTION_SELECT,
+ SCMI_PROTOCOL_PINCTRL,
+ sizeof(*tx), 0, &t);
+ if (ret)
+ return ret;
+
+ tx = t->tx.buf;
+ tx->identifier = cpu_to_le32(identifier);
+ tx->function_id = cpu_to_le32(function_id);
+ tx->flags = SET_TYPE(cpu_to_le32(type));
+
+ ret = scmi_do_xfer(handle, t);
+ scmi_xfer_put(handle, t);
+
+ return ret;
+}
+
+static int scmi_pinctrl_request(const struct scmi_handle *handle,
+ u32 identifier,
+ enum scmi_pinctrl_selector_type type)
+{
+ struct scmi_xfer *t;
+ int ret;
+ struct scmi_request_tx {
+ __le32 identifier;
+ __le32 flags;
+ } *tx;
+
+ if (!handle || type == FUNCTION_TYPE)
+ return -EINVAL;
+
+ ret = scmi_pinctrl_validate_id(handle, identifier, type);
+ if (ret)
+ return ret;
+
+ ret = scmi_xfer_get_init(handle, PINCTRL_REQUEST, SCMI_PROTOCOL_PINCTRL,
+ sizeof(*tx), 0, &t);
+
+ tx = t->tx.buf;
+ tx->identifier = identifier;
+ tx->flags = SET_TYPE(cpu_to_le32(type));
+
+ ret = scmi_do_xfer(handle, t);
+ scmi_xfer_put(handle, t);
+
+ return ret;
+}
+
+static int scmi_pinctrl_request_pin(const struct scmi_handle *handle, u32 pin)
+{
+ return scmi_pinctrl_request(handle, pin, PIN_TYPE);
+}
+
+static int scmi_pinctrl_free(const struct scmi_handle *handle, u32 identifier,
+ enum scmi_pinctrl_selector_type type)
+{
+ struct scmi_xfer *t;
+ int ret;
+ struct scmi_request_tx {
+ __le32 identifier;
+ __le32 flags;
+ } *tx;
+
+ if (!handle || type == FUNCTION_TYPE)
+ return -EINVAL;
+
+ ret = scmi_pinctrl_validate_id(handle, identifier, type);
+ if (ret)
+ return ret;
+
+ ret = scmi_xfer_get_init(handle, PINCTRL_RELEASE, SCMI_PROTOCOL_PINCTRL,
+ sizeof(*tx), 0, &t);
+
+ tx = t->tx.buf;
+ tx->identifier = identifier;
+ tx->flags = SET_TYPE(cpu_to_le32(type));
+
+ ret = scmi_do_xfer(handle, t);
+ scmi_xfer_put(handle, t);
+
+ return ret;
+}
+
+static int scmi_pinctrl_free_pin(const struct scmi_handle *handle, u32 pin)
+{
+ return scmi_pinctrl_free(handle, pin, PIN_TYPE);
+}
+
+
+static int scmi_pinctrl_get_group_info(const struct scmi_handle *handle,
+ u32 selector,
+ struct scmi_group_info *group)
+{
+ int ret = 0;
+ struct scmi_pinctrl_info *pi;
+
+ if (!handle || !handle->pinctrl_priv || !group)
+ return -EINVAL;
+
+ pi = handle->pinctrl_priv;
+
+ ret = scmi_pinctrl_attributes(handle, GROUP_TYPE, selector,
+ &group->name,
+ &group->nr_pins);
+ if (ret)
+ return ret;
+
+ if (!group->nr_pins) {
+ dev_err(handle->dev, "Group %d has 0 elements", selector);
+ return -ENODATA;
+ }
+
+ group->group_pins = devm_kmalloc_array(handle->dev, group->nr_pins,
+ sizeof(*group->group_pins),
+ GFP_KERNEL);
+ if (!group->group_pins) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = scmi_pinctrl_list_associations(handle, selector, GROUP_TYPE,
+ group->nr_pins, group->group_pins);
+ if (ret)
+ goto err_groups;
+
+ group->present = true;
+ return 0;
+
+ err_groups:
+ kfree(group->group_pins);
+ err:
+ kfree(group->name);
+ return ret;
+}
+
+static int scmi_pinctrl_get_group_name(const struct scmi_handle *handle,
+ u32 selector, const char **name)
+{
+ int ret;
+ struct scmi_pinctrl_info *pi;
+
+ if (!handle || !handle->pinctrl_priv || !name)
+ return -EINVAL;
+
+ pi = handle->pinctrl_priv;
+
+ if (selector > pi->nr_groups)
+ return -EINVAL;
+
+ if (!pi->groups[selector].present) {
+ ret = scmi_pinctrl_get_group_info(handle, selector,
+ &pi->groups[selector]);
+ if (ret)
+ return ret;
+ }
+
+ *name = pi->groups[selector].name;
+
+ return 0;
+}
+
+static int scmi_pinctrl_get_group_pins(const struct scmi_handle *handle,
+ u32 selector, const unsigned int **pins,
+ unsigned int *nr_pins)
+{
+ int ret;
+ struct scmi_pinctrl_info *pi;
+
+ if (!handle || !handle->pinctrl_priv || !pins || !nr_pins)
+ return -EINVAL;
+
+ pi = handle->pinctrl_priv;
+
+ if (selector > pi->nr_groups)
+ return -EINVAL;
+
+ if (!pi->groups[selector].present) {
+ ret = scmi_pinctrl_get_group_info(handle, selector,
+ &pi->groups[selector]);
+ if (ret)
+ return ret;
+ }
+
+ *pins = pi->groups[selector].group_pins;
+ *nr_pins = pi->groups[selector].nr_pins;
+
+ return ret;
+}
+
+static int scmi_pinctrl_get_function_info(const struct scmi_handle *handle,
+ u32 selector,
+ struct scmi_function_info *func)
+{
+ int ret = 0;
+ struct scmi_pinctrl_info *pi;
+
+ if (!handle || !handle->pinctrl_priv || !func)
+ return -EINVAL;
+
+ pi = handle->pinctrl_priv;
+
+ ret = scmi_pinctrl_attributes(handle, FUNCTION_TYPE, selector,
+ &func->name,
+ &func->nr_groups);
+ if (ret)
+ return ret;
+
+ if (!func->nr_groups) {
+ dev_err(handle->dev, "Function %d has 0 elements", selector);
+ return -ENODATA;
+ }
+
+ func->groups = devm_kmalloc_array(handle->dev, func->nr_groups,
+ sizeof(*func->groups),
+ GFP_KERNEL);
+ if (!func->groups) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = scmi_pinctrl_list_associations(handle, selector, FUNCTION_TYPE,
+ func->nr_groups, func->groups);
+ if (ret)
+ goto err_funcs;
+
+ func->present = true;
+ return 0;
+
+ err_funcs:
+ kfree(func->groups);
+ err:
+ kfree(func->name);
+ return ret;
+}
+
+static int scmi_pinctrl_get_function_name(const struct scmi_handle *handle,
+ u32 selector, const char **name)
+{
+ int ret;
+ struct scmi_pinctrl_info *pi;
+
+ if (!handle || !handle->pinctrl_priv || !name)
+ return -EINVAL;
+
+ pi = handle->pinctrl_priv;
+
+ if (selector > pi->nr_functions)
+ return -EINVAL;
+
+ if (!pi->functions[selector].present) {
+ ret = scmi_pinctrl_get_function_info(handle, selector,
+ &pi->functions[selector]);
+ if (ret)
+ return ret;
+ }
+
+ *name = pi->functions[selector].name;
+ return 0;
+}
+
+static int scmi_pinctrl_get_function_groups(const struct scmi_handle *handle,
+ u32 selector,
+ unsigned int *nr_groups,
+ const unsigned int **groups)
+{
+ int ret;
+ struct scmi_pinctrl_info *pi;
+
+ if (!handle || !handle->pinctrl_priv || !groups || !nr_groups)
+ return -EINVAL;
+
+ pi = handle->pinctrl_priv;
+
+ if (selector > pi->nr_functions)
+ return -EINVAL;
+
+ if (!pi->functions[selector].present) {
+ ret = scmi_pinctrl_get_function_info(handle, selector,
+ &pi->functions[selector]);
+ if (ret)
+ return ret;
+ }
+
+ *groups = pi->functions[selector].groups;
+ *nr_groups = pi->functions[selector].nr_groups;
+
+ return ret;
+}
+
+static int scmi_pinctrl_set_mux(const struct scmi_handle *handle, u32 selector,
+ u32 group)
+{
+ return scmi_pinctrl_function_select(handle, group, GROUP_TYPE,
+ selector);
+}
+
+static int scmi_pinctrl_get_pin_info(const struct scmi_handle *handle,
+ u32 selector, struct scmi_pin_info *pin)
+{
+ int ret = 0;
+ struct scmi_pinctrl_info *pi;
+ unsigned int n_elems;
+
+ if (!handle || !handle->pinctrl_priv || !pin)
+ return -EINVAL;
+
+ pi = handle->pinctrl_priv;
+
+ ret = scmi_pinctrl_attributes(handle, PIN_TYPE, selector,
+ &pin->name,
+ &n_elems);
+ if (ret)
+ return ret;
+
+ if (n_elems != pi->nr_pins) {
+ dev_err(handle->dev, "Wrong pin count expected %d has %d",
+ pi->nr_pins, n_elems);
+ return -ENODATA;
+ }
+
+ if (*(pin->name) == 0) {
+ dev_err(handle->dev, "Pin name is empty");
+ goto err;
+ }
+
+ pin->present = true;
+ return 0;
+
+ err:
+ kfree(pin->name);
+ return ret;
+}
+
+static int scmi_pinctrl_get_pin_name(const struct scmi_handle *handle, u32 selector,
+ const char **name)
+{
+
+ int ret;
+ struct scmi_pinctrl_info *pi;
+
+ if (!handle || !handle->pinctrl_priv || !name)
+ return -EINVAL;
+
+ pi = handle->pinctrl_priv;
+
+ if (selector > pi->nr_pins)
+ return -EINVAL;
+
+ if (!pi->pins[selector].present) {
+ ret = scmi_pinctrl_get_pin_info(handle, selector,
+ &pi->pins[selector]);
+ if (ret)
+ return ret;
+ }
+
+ *name = pi->pins[selector].name;
+
+ return 0;
+}
+
+
+static const struct scmi_pinctrl_ops pinctrl_ops = {
+ .get_groups_count = scmi_pinctrl_get_groups_count,
+ .get_group_name = scmi_pinctrl_get_group_name,
+ .get_group_pins = scmi_pinctrl_get_group_pins,
+ .get_functions_count = scmi_pinctrl_get_functions_count,
+ .get_function_name = scmi_pinctrl_get_function_name,
+ .get_function_groups = scmi_pinctrl_get_function_groups,
+ .set_mux = scmi_pinctrl_set_mux,
+ .get_pin_name = scmi_pinctrl_get_pin_name,
+ .get_pins_count = scmi_pinctrl_get_pins_count,
+ .get_config = scmi_pinctrl_get_config,
+ .set_config = scmi_pinctrl_set_config,
+ .get_config_group = scmi_pinctrl_get_config_group,
+ .set_config_group = scmi_pinctrl_set_config_group,
+ .request_pin = scmi_pinctrl_request_pin,
+ .free_pin = scmi_pinctrl_free_pin
+};
+
+static int scmi_pinctrl_protocol_init(struct scmi_handle *handle)
+{
+ u32 version;
+ struct scmi_pinctrl_info *pinfo;
+ int ret;
+
+ if (!handle)
+ return -EINVAL;
+
+ scmi_version_get(handle, SCMI_PROTOCOL_PINCTRL, &version);
+
+ dev_dbg(handle->dev, "Pinctrl Version %d.%d\n",
+ PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+ pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL);
+ if (!pinfo)
+ return -ENOMEM;
+
+ ret = scmi_pinctrl_attributes_get(handle, pinfo);
+ if (ret)
+ goto free;
+
+ pinfo->pins = devm_kmalloc_array(handle->dev, pinfo->nr_pins,
+ sizeof(*pinfo->pins),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!pinfo->pins) {
+ ret = -ENOMEM;
+ goto free;
+ }
+
+ pinfo->groups = devm_kmalloc_array(handle->dev, pinfo->nr_groups,
+ sizeof(*pinfo->groups),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!pinfo->groups) {
+ ret = -ENOMEM;
+ goto free;
+ }
+
+ pinfo->functions = devm_kmalloc_array(handle->dev, pinfo->nr_functions,
+ sizeof(*pinfo->functions),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!pinfo->functions) {
+ ret = -ENOMEM;
+ goto free;
+ }
+
+ pinfo->version = version;
+ handle->pinctrl_ops = &pinctrl_ops;
+ handle->pinctrl_priv = pinfo;
+
+ return 0;
+free:
+ if (pinfo) {
+ devm_kfree(handle->dev, pinfo->pins);
+ devm_kfree(handle->dev, pinfo->functions);
+ devm_kfree(handle->dev, pinfo->groups);
+ }
+
+ devm_kfree(handle->dev, pinfo);
+
+ return ret;
+}
+
+DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_PINCTRL, pinctrl)
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 815095326e2d..68add4d06e8c 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -431,4 +431,13 @@ config PINCTRL_EQUILIBRIUM
pin functions, configure GPIO attributes for LGM SoC pins. Pinmux and
pinconf settings are retrieved from device tree.
+config PINCTRL_SCMI
+ bool "Pinctrl driver controlled via SCMI interface"
+ depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
+ help
+ This driver provides support for pinctrl which is controlled
+ by firmware that implements the SCMI interface.
+ It uses SCMI Message Protocol to interact with the
+ firmware providing all the pinctrl controls.
+
endif
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 9cd312a1ff92..6a909ef3bf51 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -12,7 +12,8 @@
#include <linux/notifier.h>
#include <linux/types.h>
-#define SCMI_MAX_STR_SIZE 16
+#define SCMI_MAX_STR_SIZE 16
+#define SCMI_MAX_STR_EXT_SIZE 64
#define SCMI_MAX_NUM_RATES 16
/**
@@ -252,6 +253,55 @@ struct scmi_notify_ops {
struct notifier_block *nb);
};
+/**
+ * struct scmi_pinctrl_ops - represents the various operations provided
+ * by SCMI Pinctrl Protocol
+ *
+ * @get_groups_count: returns count of the registered groups
+ * @get_group_name: returns group name by index
+ * @get_group_pins: returns the set of pins, assigned to the specified group
+ * @get_functions_count: returns count of the registered fucntions
+ * @get_function_name: returns function name by indes
+ * @get_function_groups: returns the set of groups, assigned to the specified
+ * function
+ * @set_mux: set muxing function for groups of pins
+ * @get_pins: returns the set of pins, registered in driver
+ * @get_config: returns configuration parameter for pin
+ * @set_config: sets the configuration parameter for pin
+ * @get_config_group: returns the configuration parameter for a group of pins
+ * @set_config_group: sets the configuration parameter for a groups of pins
+ * @request_pin: aquire pin before selecting mux setting
+ * @free_pin: frees pin, acquired by request_pin call
+ */
+struct scmi_pinctrl_ops {
+ int (*get_groups_count)(const struct scmi_handle *handle);
+ int (*get_group_name)(const struct scmi_handle *handles, u32 selector,
+ const char **name);
+ int (*get_group_pins)(const struct scmi_handle *handle, u32 selector,
+ const unsigned int **pins, unsigned int *nr_pins);
+ int (*get_functions_count)(const struct scmi_handle *handle);
+ int (*get_function_name)(const struct scmi_handle *handle, u32 selector,
+ const char **name);
+ int (*get_function_groups)(const struct scmi_handle *handle,
+ u32 selector, unsigned int *nr_groups,
+ const unsigned int **groups);
+ int (*set_mux)(const struct scmi_handle *handle, u32 selector,
+ u32 group);
+ int (*get_pin_name)(const struct scmi_handle *handle, u32 selector,
+ const char **name);
+ int (*get_pins_count)(const struct scmi_handle *handle);
+ int (*get_config)(const struct scmi_handle *handle, u32 pin,
+ u32 *config);
+ int (*set_config)(const struct scmi_handle *handle, u32 pin,
+ u32 config);
+ int (*get_config_group)(const struct scmi_handle *handle, u32 pin,
+ u32 *config);
+ int (*set_config_group)(const struct scmi_handle *handle, u32 pin,
+ u32 config);
+ int (*request_pin)(const struct scmi_handle *handle, u32 pin);
+ int (*free_pin)(const struct scmi_handle *handle, u32 pin);
+};
+
/**
* struct scmi_handle - Handle returned to ARM SCMI clients for usage.
*
@@ -263,6 +313,7 @@ struct scmi_notify_ops {
* @sensor_ops: pointer to set of sensor protocol operations
* @reset_ops: pointer to set of reset protocol operations
* @notify_ops: pointer to set of notifications related operations
+ * @pinctrl_ops: pointer to set of pinctrl protocol operations
* @perf_priv: pointer to private data structure specific to performance
* protocol(for internal use only)
* @clk_priv: pointer to private data structure specific to clock
@@ -275,6 +326,8 @@ struct scmi_notify_ops {
* protocol(for internal use only)
* @notify_priv: pointer to private data structure specific to notifications
* (for internal use only)
+ * @pinctrl_priv: pointer to private data structure specific to pinctrl
+ * (for internal use only)
*/
struct scmi_handle {
struct device *dev;
@@ -285,6 +338,7 @@ struct scmi_handle {
const struct scmi_sensor_ops *sensor_ops;
const struct scmi_reset_ops *reset_ops;
const struct scmi_notify_ops *notify_ops;
+ const struct scmi_pinctrl_ops *pinctrl_ops;
/* for protocol internal use */
void *perf_priv;
void *clk_priv;
@@ -293,6 +347,7 @@ struct scmi_handle {
void *reset_priv;
void *notify_priv;
void *system_priv;
+ void *pinctrl_priv;
};
enum scmi_std_protocol {
@@ -303,6 +358,7 @@ enum scmi_std_protocol {
SCMI_PROTOCOL_CLOCK = 0x14,
SCMI_PROTOCOL_SENSOR = 0x15,
SCMI_PROTOCOL_RESET = 0x16,
+ SCMI_PROTOCOL_PINCTRL = 0x19,
};
enum scmi_system_events {
--
2.25.1
Hi Oleksii,
thanks for your patches!
On Fri, Apr 7, 2023 at 12:18 PM Oleksii Moisieiev
<[email protected]> wrote:
> This RFC patch series is intended to introduce the potential generic driver for
> pin controls over SCMI protocol, provided in the latest beta version of DEN0056 [0].
>
> On ARM-based systems, a separate Cortex-M based System Control Processor (SCP)
> provides control on pins, as well as with power, clocks, reset controllers. In this case,
> kernel should use one of the possible transports, described in [0] to access SCP and
> control clocks/power-domains etc. This driver is using SMC transport to communicate with SCP via
> SCMI protocol and access to the Pin Control Subsystem.
>
> The provided driver consists of 2 parts:
> - firmware/arm_scmi/pinctrl.c - the SCMI pinctrl protocol inmplementation
> responsible for the communication with SCP firmware.
>
> - drivers/pinctrl/pinctrl-scmi.c - pinctrl driver, which is using pinctrl
> protocol implementation to access all necessary data.
TBH this looks so good that I am happy to merge it once you send a non-RFC
version.
My main concern would have been the protocol itself, but that was very
carefully tailored to match what the pin control subsystem needs and
I am quite happy with it the way it came out: using strings for groups and
functions.
The scmi code in patch 1 adds an extra layer of abstraction and a vtable
that would not have been necessary if all of the code was confined in
one file in drivers/pinctrl but it is not up to me how the SCMI people
want to abstract their stuff and there seems to be precedents to do things
this way.
I heard that someone wanted to also implement GPIO over SCMI, but
it is not part of this driver so I guess that will be a future addition.
It's a good starting point to add GPIO later.
Yours,
Linus Walleij
On Tue, Apr 11, 2023 at 02:27:53PM +0200, Linus Walleij wrote:
> Hi Oleksii,
>
> thanks for your patches!
>
> On Fri, Apr 7, 2023 at 12:18 PM Oleksii Moisieiev
> <[email protected]> wrote:
>
> > This RFC patch series is intended to introduce the potential generic driver for
> > pin controls over SCMI protocol, provided in the latest beta version of DEN0056 [0].
> >
> > On ARM-based systems, a separate Cortex-M based System Control Processor (SCP)
> > provides control on pins, as well as with power, clocks, reset controllers. In this case,
> > kernel should use one of the possible transports, described in [0] to access SCP and
> > control clocks/power-domains etc. This driver is using SMC transport to communicate with SCP via
> > SCMI protocol and access to the Pin Control Subsystem.
> >
> > The provided driver consists of 2 parts:
> > - firmware/arm_scmi/pinctrl.c - the SCMI pinctrl protocol inmplementation
> > responsible for the communication with SCP firmware.
> >
> > - drivers/pinctrl/pinctrl-scmi.c - pinctrl driver, which is using pinctrl
> > protocol implementation to access all necessary data.
>
> TBH this looks so good that I am happy to merge it once you send a non-RFC
> version.
>
> My main concern would have been the protocol itself, but that was very
> carefully tailored to match what the pin control subsystem needs and
> I am quite happy with it the way it came out: using strings for groups and
> functions.
>
> The scmi code in patch 1 adds an extra layer of abstraction and a vtable
> that would not have been necessary if all of the code was confined in
> one file in drivers/pinctrl but it is not up to me how the SCMI people
> want to abstract their stuff and there seems to be precedents to do things
> this way.
>
I haven't looked at the code to comment on the details, but in general the
SCMI abstraction was created and used in other kernel subsystems for couple
of reasons:
1. Leave all the protocol specific details like the version and other in
the abstraction so that the driver remains simple and doesn't have to deal
with those details.
2. Similar to the version and other details(generic or vendor specific), since
there is a firmware involved, there might be need for quirks and again
dealing with those in these SCMI layer is better as we will not have any
specific compatible say just for pinctrl or dvfs ..etc.
3. Other reason was to allow testing of features that are in the spec and
firmware but not used by any framework. But I think the way we introduced
raw scmi interface nullifies it as it allows to bypass any framework and do
raw SCMI transfers now. But originally the abstraction considered that
possibility as well.
> I heard that someone wanted to also implement GPIO over SCMI, but
> it is not part of this driver so I guess that will be a future addition.
> It's a good starting point to add GPIO later.
>
Yes, Xilinx people want GPIO for moving away from their custom but similar to
SCMI like interface. There are yet to start exploring details.
--
Regards,
Sudeep
On Fri, Apr 07, 2023 at 10:18:26AM +0000, Oleksii Moisieiev wrote:
> This RFC patch series is intended to introduce the potential generic driver for
> pin controls over SCMI protocol, provided in the latest beta version of DEN0056 [0].
>
Hi Oleksii,
a few general remarks here down below and more specific comments will
come inline in the remaining patches of the series.
CC'ing also a few more people possibly interested in your series.
> On ARM-based systems, a separate Cortex-M based System Control Processor (SCP)
> provides control on pins, as well as with power, clocks, reset controllers. In this case,
> kernel should use one of the possible transports, described in [0] to access SCP and
> control clocks/power-domains etc. This driver is using SMC transport to communicate with SCP via
> SCMI protocol and access to the Pin Control Subsystem.
>
> The provided driver consists of 2 parts:
> - firmware/arm_scmi/pinctrl.c - the SCMI pinctrl protocol inmplementation
> responsible for the communication with SCP firmware.
>
> - drivers/pinctrl/pinctrl-scmi.c - pinctrl driver, which is using pinctrl
> protocol implementation to access all necessary data.
>
As discussed offline, the patches you posted to add support for the new
SCMI pinctrl protocol and the related SCMI pinctrl driver are using the
old SCMI API (include/linux/scmi_protocol.h) that changed significantly
since v5.13, so at first they need to be ported to current mainline API.
You can look on the latest v6.3-rc at:
drivers/firmware/arm_scmi/power.c
drivers/firmware/arm_scmi/scmi_pm_domain.c
for a simple example of a core protocol and related driver using the new API.
On the protocol side you can ignore really the part related to "scmi_protocol_events"
that you find there since it is related to notifications and there are no notifs in
SCMI Pinctrl as of now.
In a nutshell the protocol layer now receives a protocol handle (instead
of the instance handle) during protocol_init and uses that to build/send
messages; it has to be used also to store any protocol private data
with ph->set/get_priv() (no more direct access to handle->privs)
That same protocol handle is then used by the SCMI driver users during
tehir probes, so, in your pinctrl SCMI driver probe, you should do something like
this early on:
pinctrl_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_PINCTRL, &ph);
store the 'ph' somewhere and and then use it all over:
pinctrl_ops->set_mux(ph, selector, group);
Beside the API a few helpers has been added too (ph->hops) that can be
re-used by protocol code to implement straight away some common SCMI machinery
like the extended_name_get and the handling of multi-part replies
(like in PINCTRL_LIST_ASSOCIATIONS)...I think you can ignore these ph->hops and
just keep your original code, I'll take care to port these functionalities to
the common helpers later on top of your series if it is fine for you...
(also because I think at least a small modification in the core helpers will be
needed to support PINCTRL usage since it deviates a bit from existent protos...:P)
> Configuration:
> The scmi-pinctrl driver can be configured using DT bindings.
> For example:
> / {
> cpu_scp_shm: scp-shmem@0x53FF0000 {
> compatible = "arm,scmi-shmem";
> reg = <0x0 0x53FF0000 0x0 0x1000>;
> };
>
> firmware {
> scmi {
> compatible = "arm,scmi-smc";
> arm,smc-id = <0x82000002>;
> shmem = <&cpu_scp_shm>;
> #address-cells = <1>;
> #size-cells = <0>;
>
> scmi_pinctrl: protocol@19 {
> reg = <0x18>;
> #pinctrl-cells = <0>;
>
> i2c2_pins: i2c2 {
> groups = "i2c2_a";
> function = "i2c2";
> };
> };
> };
> };
> };
>
These will need a proper formal explanation in DT bindings at:
Documentation/devicetree/bindings/firmware/arm,scmi.yaml
to highlight the usage of "#pinctrl-cells" and whatever else is needed
to be documented with related links to existing reused bindings is any.
(and CC that patch to the proper DT maintainers and MLs... IOW look at
what get_maintanel.pl says)
> &pfc {
> /delete-node/i2c2;
> };
>
> So basically, it's enough to move pfc subnode, which configures pin group that should work through
> SCMI protocol to scmi_pinctrl node. The current driver implementation is using generic pinctrl dt_node
> format.
>
> I've tested this driver on the Renesas H3ULCB Kingfisher board with pinctrl driver ported to the
> Arm-trusted-firmware. Unfortunately, not all hardware was possible to test because the Renesas
> pinctrl driver has gaps in pins and groups numeration, when Spec [0] requires pins, groups and
> functions numerations to be 0..n without gaps.
>
> This implementation still reqires some features, such as gpio support, but I've posted it as RFC to
> start the discussion regarding the driver format.
>
> [0] https://developer.arm.com/documentation/den0056/latest
>
Thanks for this,
Cristian
On Fri, Apr 07, 2023 at 10:18:27AM +0000, Oleksii Moisieiev wrote:
> Implementation of the SCMI client driver, which implements
> PINCTRL_PROTOCOL. This protocol has ID 19 and is described
> in the latest DEN0056 document.
Hi,
> This protocol is part of the feature that was designed to
> separate the pinctrl subsystem from the SCP firmware.
> The idea is to separate communication of the pin control
> subsystem with the hardware to SCP firmware
> (or a similar system, such as ATF), which provides an interface
> to give the OS ability to control the hardware through SCMI protocol.
> This is a generic driver that implements SCMI protocol,
> independent of the platform type.
>
> DEN0056 document:
> https://developer.arm.com/documentation/den0056/latest
>
No need to specify all of this in the commit message, just a note that
you are adding a new SCMIv3.2 Pincontrol protocol, highlighting anything
that has been left out in this patch (if any) will be enough.
You can look at the very first commit logs of existing protos as an
example like: drivers/firmware/arm_scmi/powercap.c
Some more comments down below, I'll mostly skip anything related to the
SCMI API change I mentioned before...
I'll also wont comment on more trivial stuff related to style, BUT there
are lots of them: you should run
./scripts/checkpatch.pl --strict <your-git-format-patch-file>
for each patch in the series. (and fix accordingly..spacing, brackets...etc)
> Signed-off-by: Oleksii Moisieiev <[email protected]>
> ---
> MAINTAINERS | 6 +
> drivers/firmware/arm_scmi/Makefile | 2 +-
> drivers/firmware/arm_scmi/common.h | 1 +
> drivers/firmware/arm_scmi/driver.c | 3 +
> drivers/firmware/arm_scmi/pinctrl.c | 905 ++++++++++++++++++++++++++++
> drivers/pinctrl/Kconfig | 9 +
> include/linux/scmi_protocol.h | 58 +-
> 7 files changed, 982 insertions(+), 2 deletions(-)
> create mode 100644 drivers/firmware/arm_scmi/pinctrl.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 281de213ef47..abc543fd7544 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -16961,6 +16961,12 @@ F: drivers/reset/reset-scmi.c
> F: include/linux/sc[mp]i_protocol.h
> F: include/trace/events/scmi.h
>
> +PINCTRL DRIVER FOR SYSTEM CONTROL & POWER/MANAGEMENT INTERFACE (SCPI/SCMI)
> +M: Oleksii Moisieiev <[email protected]>
> +L: [email protected]
> +S: Maintained
> +F: drivers/firmware/arm_scmi/pinctrl.c
> +
> SYSTEM RESET/SHUTDOWN DRIVERS
> M: Sebastian Reichel <[email protected]>
> L: [email protected]
> diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
> index bc0d54f8e861..5cec357fbfa8 100644
> --- a/drivers/firmware/arm_scmi/Makefile
> +++ b/drivers/firmware/arm_scmi/Makefile
> @@ -4,7 +4,7 @@ scmi-driver-y = driver.o notify.o
> scmi-transport-y = shmem.o
> scmi-transport-$(CONFIG_MAILBOX) += mailbox.o
> scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o
> -scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o
> +scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o pinctrl.o
> scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \
> $(scmi-transport-y)
> obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
> diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
> index 65063fa948d4..8bbb404abe8d 100644
> --- a/drivers/firmware/arm_scmi/common.h
> +++ b/drivers/firmware/arm_scmi/common.h
> @@ -170,6 +170,7 @@ DECLARE_SCMI_REGISTER_UNREGISTER(power);
> DECLARE_SCMI_REGISTER_UNREGISTER(reset);
> DECLARE_SCMI_REGISTER_UNREGISTER(sensors);
> DECLARE_SCMI_REGISTER_UNREGISTER(system);
> +DECLARE_SCMI_REGISTER_UNREGISTER(pinctrl);
>
> #define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(id, name) \
> int __init scmi_##name##_register(void) \
> diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> index 3dfd8b6a0ebf..fb9525fb3c24 100644
> --- a/drivers/firmware/arm_scmi/driver.c
> +++ b/drivers/firmware/arm_scmi/driver.c
> @@ -743,6 +743,7 @@ static struct scmi_prot_devnames devnames[] = {
> { SCMI_PROTOCOL_CLOCK, { "clocks" },},
> { SCMI_PROTOCOL_SENSOR, { "hwmon" },},
> { SCMI_PROTOCOL_RESET, { "reset" },},
> + { SCMI_PROTOCOL_PINCTRL, { "pinctrl" },},
> };
>
> static inline void
> @@ -947,6 +948,7 @@ static int __init scmi_driver_init(void)
> scmi_reset_register();
> scmi_sensors_register();
> scmi_system_register();
> + scmi_pinctrl_register();
>
> return platform_driver_register(&scmi_driver);
> }
> @@ -962,6 +964,7 @@ static void __exit scmi_driver_exit(void)
> scmi_reset_unregister();
> scmi_sensors_unregister();
> scmi_system_unregister();
> + scmi_pinctrl_unregister();
>
> platform_driver_unregister(&scmi_driver);
> }
> diff --git a/drivers/firmware/arm_scmi/pinctrl.c b/drivers/firmware/arm_scmi/pinctrl.c
> new file mode 100644
> index 000000000000..037270d7f39b
> --- /dev/null
> +++ b/drivers/firmware/arm_scmi/pinctrl.c
> @@ -0,0 +1,905 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * System Control and Management Interface (SCMI) Pinctrl Protocol
> + *
> + * Copyright (C) 2021 EPAM.
nitpick: update (C) years
> + */
> +
> +#define pr_fmt(fmt) "SCMI Notifications PINCTRL - " fmt
> +
This is not needed, no notifs in this proto.
> +#include <linux/scmi_protocol.h>
> +#include <linux/slab.h>
> +
> +#include "common.h"
> +#include "notify.h"
Notifs not needed, and in the new API world you'll just need a:
#include "protocols.h"
> +
> +#define SET_TYPE(x) ((x) & 0x3)
> +
Even if trivial better to use std bitfield.h macros like
FIELD_GET() BIT() ... etc
> +enum scmi_pinctrl_protocol_cmd {
> + PINCTRL_ATTRIBUTES = 0x3,
> + PINCTRL_LIST_ASSOCIATIONS = 0x4,
> + PINCTRL_CONFIG_GET = 0x5,
> + PINCTRL_CONFIG_SET = 0x6,
> + PINCTRL_FUNCTION_SELECT = 0x7,
> + PINCTRL_REQUEST = 0x8,
> + PINCTRL_RELEASE = 0x9,
> + PINCTRL_NAME_GET = 0xa,
> + PINCTRL_SET_PERMISSIONS = 0xb
> +};
> +
> +enum scmi_pinctrl_selector_type {
> + PIN_TYPE = 0,
> + GROUP_TYPE,
> + FUNCTION_TYPE
> +};
> +
> +struct scmi_group_info {
> + bool present;
> + char *name;
> + unsigned int *group_pins;
> + unsigned int nr_pins;
> +};
> +
> +struct scmi_function_info {
> + bool present;
> + char *name;
> + unsigned int *groups;
> + unsigned int nr_groups;
> +};
> +
> +struct scmi_pin_info {
> + bool present;
> + char *name;
> +};
> +
> +struct scmi_pinctrl_info {
> + u32 version;
> + u16 nr_groups;
> + u16 nr_functions;
> + u16 nr_pins;
Since these vars are not related to stricly spaced message fields (even though
derived from such messages) do not use sized types, you can just stick with
unsigned int. (it is also better not to mix sized and unsized types in the same
struct). This also could come handy if these will be exposed to the user
in scmi_protocol.h in the future (more on this down below)
> + struct scmi_group_info *groups;
> + struct scmi_function_info *functions;
> + struct scmi_pin_info *pins;
> +};
> +
> +struct scmi_conf_tx {
> + __le32 identifier;
> +#define SET_TYPE_BITS(attr, x) (((attr) & 0xFFFFFCFF) | (x & 0x3) << 8)
> +#define SET_CONFIG(attr, x) (((attr) & 0xFF) | (x & 0xFF))
Use bitfield.h like FIELD_SET / GENMASK etc
> + __le32 attributes;
> +};
> +
> +static int scmi_pinctrl_attributes_get(const struct scmi_handle *handle,
> + struct scmi_pinctrl_info *pi)
> +{
> + int ret;
> + struct scmi_xfer *t;
> + struct scmi_msg_pinctrl_protocol_attributes {
> +#define GROUPS_NR(x) ((x) >> 16)
> +#define PINS_NR(x) ((x) & 0xffff)
> + __le32 attributes_low;
> +#define FUNCTIONS_NR(x) ((x) & 0xffff)
> + __le32 attributes_high;
> + } *attr;
For consistency with the rest of the stack (mostly :D), please move this struct
definition and related macros outside in the global scope right after command
enum. (and use bitfield macros ...)
> +
> + if (!pi)
> + return -EINVAL;
> +
> + ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
> + SCMI_PROTOCOL_PINCTRL, 0, sizeof(*attr), &t);
> + if (ret)
> + return ret;
> +
> + attr = t->rx.buf;
> +
> + ret = scmi_do_xfer(handle, t);
> + if (!ret) {
> + pi->nr_functions =
> + le16_to_cpu(FUNCTIONS_NR(attr->attributes_high));
> + pi->nr_groups = le16_to_cpu(GROUPS_NR(attr->attributes_low));
> + pi->nr_pins = le16_to_cpu(PINS_NR(attr->attributes_low));
> + }
> +
> + scmi_xfer_put(handle, t);
> + return ret;
> +}
> +
> +static int scmi_pinctrl_get_groups_count(const struct scmi_handle *handle)
> +{
> + struct scmi_pinctrl_info *pi;
> +
> + if (!handle || !handle->pinctrl_priv)
> + return -ENODEV;
> +
> + pi = handle->pinctrl_priv;
> +
> + return pi->nr_groups;
> +}
> +
> +static int scmi_pinctrl_get_pins_count(const struct scmi_handle *handle)
> +{
> + struct scmi_pinctrl_info *pi;
> +
> + if (!handle || !handle->pinctrl_priv)
> + return -ENODEV;
> +
> + pi = handle->pinctrl_priv;
> +
> + return pi->nr_pins;
> +}
> +
> +static int scmi_pinctrl_get_functions_count(const struct scmi_handle *handle)
> +{
> + struct scmi_pinctrl_info *pi;
> +
> + if (!handle || !handle->pinctrl_priv)
> + return -ENODEV;
> +
> + pi = handle->pinctrl_priv;
> +
> + return pi->nr_functions;
> +}
> +
> +static int scmi_pinctrl_validate_id(const struct scmi_handle *handle,
> + u32 identifier,
> + enum scmi_pinctrl_selector_type type)
> +{
> + struct scmi_pinctrl_info *pi;
> +
> + if (!handle || !handle->pinctrl_priv)
> + return -ENODEV;
> +
> + switch (type) {
> + case PIN_TYPE:
> + pi = handle->pinctrl_priv;
> +
> + return (identifier < pi->nr_pins) ? 0 : -EINVAL;
> + case GROUP_TYPE:
> + return (identifier <
> + scmi_pinctrl_get_groups_count(handle)) ?
> + 0 : -EINVAL;
> + case FUNCTION_TYPE:
> + return (identifier <
> + scmi_pinctrl_get_functions_count(handle)) ?
> + 0 : -EINVAL;
> + default:
> + return -EINVAL;
> + }
Here I would just pick the right value to compare, break and then
compare and exit, something aroundf the lines of:
case PIN_TYPE:
...
val = pi->nr_pins;
break;
...
case GROUP_TYPE:
val = scmi_pinctrl_get_groups_count());
break;
....
....
default:
return -EINVAL;
}
if (identifier >= val)
return -EINVAL;
return 0;
}
... it's easier to read. What do you think ?
> +}
> +
> +static int scmi_pinctrl_get_name(const struct scmi_handle *handle,
> + u32 identifier,
> + enum scmi_pinctrl_selector_type type,
> + char **name)
> +{
As said, there is common helper for this, but it will need some small
adaptation in the SCMI core to work here so keep it as it is, and I'll take
care of this later, if it sounds fine for you.
> + struct scmi_xfer *t;
> + int ret = 0;
> + struct scmi_name_tx {
> + __le32 identifier;
> + __le32 flags;
> + } *tx;
> + struct scmi_name_rx {
> + __le32 flags;
> + u8 name[64];
> + } *rx;
> +
> + if (!handle || !name)
> + return -EINVAL;
> +
> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
> + if (ret)
> + return ret;
> +
> + ret = scmi_xfer_get_init(handle, PINCTRL_NAME_GET,
> + SCMI_PROTOCOL_PINCTRL,
> + sizeof(*tx), sizeof(*rx), &t);
> +
> + tx = t->tx.buf;
> + rx = t->rx.buf;
> + tx->identifier = identifier;
> + tx->flags = SET_TYPE(cpu_to_le32(type));
> +
> + ret = scmi_do_xfer(handle, t);
> + if (ret)
> + goto out;
> +
> + if (rx->flags) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + *name = kasprintf(GFP_KERNEL, "%s", rx->name);
> + if (!*name)
> + ret = -ENOMEM;
> + out:
> + scmi_xfer_put(handle, t);
> +
> + return ret;
> +}
> +
> +static int scmi_pinctrl_attributes(const struct scmi_handle *handle,
> + enum scmi_pinctrl_selector_type type,
> + u32 selector, char **name,
> + unsigned int *n_elems)
> +{
> + int ret = 0;
> + struct scmi_xfer *t;
> + struct scmi_pinctrl_attributes_tx {
> + __le32 identifier;
> + __le32 flags;
> + } *tx;
> + struct scmi_pinctrl_attributes_rx {
> +#define EXT_NAME_FLAG(x) ((x) & BIT(31))
> +#define NUM_ELEMS(x) ((x) & 0xffff)
> + __le32 attributes;
> + u8 name[16];
> + } *rx;
Ditto. Move these defs outside, bitfield.h for macros and try to use the
same naming style for message structs as in other protos, i.e.
for commands: struct scmi_msg_pinctrl_attributes
for replies: struct scmi_resp_pinctrl_attributes
(or some variations around that...
scmi_msg_cmd_* scmi_msg_resp_*
we have not been fully consistent really, so I dont want to be
pedantic here, but we never used tx/rx in message context since it is
already (mis)-used in SCMI channel context...)
> +
> + if (!handle || !name)
> + return -EINVAL;
> +
> + ret = scmi_pinctrl_validate_id(handle, selector, type);
> + if (ret)
> + return ret;
> +
> + ret = scmi_xfer_get_init(handle, PINCTRL_ATTRIBUTES,
> + SCMI_PROTOCOL_PINCTRL, sizeof(*tx), sizeof(*rx), &t);
> + if (ret)
> + return ret;
> +
> + tx = t->tx.buf;
> + rx = t->rx.buf;
> + tx->identifier = selector;
> + tx->flags = SET_TYPE(cpu_to_le32(type));
> +
> + ret = scmi_do_xfer(handle, t);
> + if (ret)
> + goto out;
> +
> + *n_elems = NUM_ELEMS(rx->attributes);
> +
> + if (!EXT_NAME_FLAG(rx->attributes)) {
> + *name = kasprintf(GFP_KERNEL, "%s", rx->name);
> + if (!*name)
> + ret = -ENOMEM;
> + } else
> + ret = scmi_pinctrl_get_name(handle, selector, type, name);
> + out:
> + scmi_xfer_put(handle, t);
> + return ret;
> +}
> +
> +static int scmi_pinctrl_list_associations(const struct scmi_handle *handle,
> + u32 selector,
> + enum scmi_pinctrl_selector_type type,
> + uint16_t size, unsigned int *array)
> +{
This is the other functionalities you could implement straight away using
ph->hops helpers (iterators) but just leave it this way, and I'll port it later
(once we retested all of this as working with the new API but without any
ph->hops usage..I think it is safer to change one bit at time... :P)
> + struct scmi_xfer *t;
> + struct scmi_pinctrl_list_assoc_tx {
> + __le32 identifier;
> + __le32 flags;
> + __le32 index;
> + } *tx;
> + struct scmi_pinctrl_list_assoc_rx {
> +#define RETURNED(x) ((x) & 0xFFF)
> +#define REMAINING(x) ((x) >> 16)
> + __le32 flags;
> + __le16 array[];
> + } *rx;
Ditto, about struct naming and macros.
> + u16 tot_num_ret = 0, loop_num_ret;
> + u16 remaining_num_ret;
> + int ret, loop;
> +
> + if (!handle || !array || !size)
> + return -EINVAL;
> +
> + if (type == PIN_TYPE)
> + return -EINVAL;
> +
> + ret = scmi_pinctrl_validate_id(handle, selector, type);
> + if (ret)
> + return ret;
> +
> + ret = scmi_xfer_get_init(handle, PINCTRL_LIST_ASSOCIATIONS,
> + SCMI_PROTOCOL_PINCTRL, sizeof(*tx),
> + 0, &t);
> + if (ret)
> + return ret;
> +
> + tx = t->tx.buf;
> + rx = t->rx.buf;
> +
> + do {
> + tx->identifier = cpu_to_le32(selector);
> + tx->flags = SET_TYPE(cpu_to_le32(type));
> + tx->index = cpu_to_le32(tot_num_ret);
> +
> + ret = scmi_do_xfer(handle, t);
> + if (ret)
> + break;
> +
> + loop_num_ret = le32_to_cpu(RETURNED(rx->flags));
> + remaining_num_ret = le32_to_cpu(REMAINING(rx->flags));
> +
> + for (loop = 0; loop < loop_num_ret; loop++) {
> + if (tot_num_ret + loop >= size) {
> + ret = -EMSGSIZE;
> + goto out;
> + }
> +
> + array[tot_num_ret + loop] =
> + le16_to_cpu(rx->array[loop]);
> + }
> +
> + tot_num_ret += loop_num_ret;
> +
> + scmi_reset_rx_to_maxsz(handle, t);
> + } while (remaining_num_ret > 0);
> +out:
> + scmi_xfer_put(handle, t);
> + return ret;
> +}
> +
> +static int scmi_pinctrl_request_config(const struct scmi_handle *handle,
> + u32 selector,
> + enum scmi_pinctrl_selector_type type,
> + u32 *config)
> +{
> + struct scmi_xfer *t;
> + struct scmi_conf_tx *tx;
> + __le32 *packed_config;
> + u32 attributes = 0;
> + int ret;
> +
> + if (!handle || !config || type == FUNCTION_TYPE)
> + return -EINVAL;
> +
> + ret = scmi_pinctrl_validate_id(handle, selector, type);
> + if (ret)
> + return ret;
> +
> + ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_GET,
> + SCMI_PROTOCOL_PINCTRL,
> + sizeof(*tx), sizeof(*packed_config), &t);
> + if (ret)
> + return ret;
> +
> + tx = t->tx.buf;
> + packed_config = t->rx.buf;
> + tx->identifier = cpu_to_le32(selector);
> + attributes = SET_TYPE_BITS(attributes, type);
> + attributes = SET_CONFIG(attributes, *config);
> +
> + tx->attributes = cpu_to_le32(attributes);
> +
> + ret = scmi_do_xfer(handle, t);
> +
> + if (!ret)
> + *config = le32_to_cpu(*packed_config);
> +
> + scmi_xfer_put(handle, t);
> + return ret;
> +}
> +
> +static int scmi_pinctrl_get_config(const struct scmi_handle *handle, u32 pin,
> + u32 *config)
> +{
> + return scmi_pinctrl_request_config(handle, pin, PIN_TYPE, config);
> +}
> +
> +static int scmi_pinctrl_apply_config(const struct scmi_handle *handle,
> + u32 selector,
> + enum scmi_pinctrl_selector_type type,
> + u32 config)
> +{
> + struct scmi_xfer *t;
> + struct scmi_conf_tx *tx;
> + u32 attributes = 0;
> + int ret;
> +
> + if (!handle || type == FUNCTION_TYPE)
> + return -EINVAL;
> +
> + ret = scmi_pinctrl_validate_id(handle, selector, type);
> + if (ret)
> + return ret;
> +
> + ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_SET,
> + SCMI_PROTOCOL_PINCTRL,
> + sizeof(*tx), 0, &t);
> + if (ret)
> + return ret;
> +
> + tx = t->tx.buf;
> + tx->identifier = cpu_to_le32(selector);
> + attributes = SET_TYPE_BITS(attributes, type);
> + attributes = SET_CONFIG(attributes, config);
> + tx->attributes = cpu_to_le32(attributes);
> +
> + ret = scmi_do_xfer(handle, t);
> +
> + scmi_xfer_put(handle, t);
> + return ret;
> +}
> +
> +static int scmi_pinctrl_set_config(const struct scmi_handle *handle, u32 pin,
> + u32 config)
> +{
> + return scmi_pinctrl_apply_config(handle, pin, PIN_TYPE, config);
> +}
> +
> +static int scmi_pinctrl_get_config_group(const struct scmi_handle *handle,
> + u32 group, u32 *config)
> +{
> + return scmi_pinctrl_request_config(handle, group, GROUP_TYPE, config);
> +}
> +
> +static int scmi_pinctrl_set_config_group(const struct scmi_handle *handle,
> + u32 group, u32 config)
> +{
> + return scmi_pinctrl_apply_config(handle, group, GROUP_TYPE, config);
> +}
> +
> +static int scmi_pinctrl_function_select(const struct scmi_handle *handle,
> + u32 identifier,
> + enum scmi_pinctrl_selector_type type,
> + u32 function_id)
> +{
> + struct scmi_xfer *t;
> + struct scmi_func_set_tx {
> + __le32 identifier;
> + __le32 function_id;
> + __le32 flags;
> + } *tx;
Ditto.
> + int ret;
> +
> + if (!handle || type == FUNCTION_TYPE)
> + return -EINVAL;
> +
> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
> + if (ret)
> + return ret;
> +
> + ret = scmi_xfer_get_init(handle, PINCTRL_FUNCTION_SELECT,
> + SCMI_PROTOCOL_PINCTRL,
> + sizeof(*tx), 0, &t);
> + if (ret)
> + return ret;
> +
> + tx = t->tx.buf;
> + tx->identifier = cpu_to_le32(identifier);
> + tx->function_id = cpu_to_le32(function_id);
> + tx->flags = SET_TYPE(cpu_to_le32(type));
> +
> + ret = scmi_do_xfer(handle, t);
> + scmi_xfer_put(handle, t);
> +
> + return ret;
> +}
> +
> +static int scmi_pinctrl_request(const struct scmi_handle *handle,
> + u32 identifier,
> + enum scmi_pinctrl_selector_type type)
> +{
> + struct scmi_xfer *t;
> + int ret;
> + struct scmi_request_tx {
> + __le32 identifier;
> + __le32 flags;
> + } *tx;
> +
Ditto.
> + if (!handle || type == FUNCTION_TYPE)
> + return -EINVAL;
> +
> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
> + if (ret)
> + return ret;
> +
> + ret = scmi_xfer_get_init(handle, PINCTRL_REQUEST, SCMI_PROTOCOL_PINCTRL,
> + sizeof(*tx), 0, &t);
> +
> + tx = t->tx.buf;
> + tx->identifier = identifier;
> + tx->flags = SET_TYPE(cpu_to_le32(type));
> +
> + ret = scmi_do_xfer(handle, t);
> + scmi_xfer_put(handle, t);
> +
> + return ret;
> +}
> +
> +static int scmi_pinctrl_request_pin(const struct scmi_handle *handle, u32 pin)
> +{
> + return scmi_pinctrl_request(handle, pin, PIN_TYPE);
> +}
> +
> +static int scmi_pinctrl_free(const struct scmi_handle *handle, u32 identifier,
> + enum scmi_pinctrl_selector_type type)
> +{
> + struct scmi_xfer *t;
> + int ret;
> + struct scmi_request_tx {
> + __le32 identifier;
> + __le32 flags;
> + } *tx;
> +
Ditto.
> + if (!handle || type == FUNCTION_TYPE)
> + return -EINVAL;
> +
> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
> + if (ret)
> + return ret;
> +
> + ret = scmi_xfer_get_init(handle, PINCTRL_RELEASE, SCMI_PROTOCOL_PINCTRL,
> + sizeof(*tx), 0, &t);
> +
> + tx = t->tx.buf;
> + tx->identifier = identifier;
> + tx->flags = SET_TYPE(cpu_to_le32(type));
> +
> + ret = scmi_do_xfer(handle, t);
> + scmi_xfer_put(handle, t);
> +
> + return ret;
> +}
> +
> +static int scmi_pinctrl_free_pin(const struct scmi_handle *handle, u32 pin)
> +{
> + return scmi_pinctrl_free(handle, pin, PIN_TYPE);
> +}
> +
> +
> +static int scmi_pinctrl_get_group_info(const struct scmi_handle *handle,
> + u32 selector,
> + struct scmi_group_info *group)
> +{
> + int ret = 0;
> + struct scmi_pinctrl_info *pi;
> +
> + if (!handle || !handle->pinctrl_priv || !group)
> + return -EINVAL;
> +
> + pi = handle->pinctrl_priv;
> +
> + ret = scmi_pinctrl_attributes(handle, GROUP_TYPE, selector,
> + &group->name,
> + &group->nr_pins);
> + if (ret)
> + return ret;
> +
> + if (!group->nr_pins) {
> + dev_err(handle->dev, "Group %d has 0 elements", selector);
> + return -ENODATA;
> + }
> +
> + group->group_pins = devm_kmalloc_array(handle->dev, group->nr_pins,
> + sizeof(*group->group_pins),
> + GFP_KERNEL);
I think you can just use for the array allocation
devm_kcalloc(dev, n, size, flags)
and it will add also __GFP_ZERO internally to clear it.
(indeed it calls in turn devm_kmalloc_array)
...BUT I think there is a further tricky issue here related to memory allocation...
You call this and others function of this kind from some scmi_pinctrl_ops,
like in scmi_pinctrl_get_group_pins (scmi_pinctrl_ops->get_group_pins),
and then this is in turn called by the SCMI Pinctrl driver via
pinctrl_ops->get_group_pins AND you set a present flag so that you issue a
PINCTRL_LIST_ASSOCIATIONS and allocate here a new group_pins array just
the first time: but these are never released anywhere, since, even though
lazily dynamically allocated when asked for, these are static data that
you pass to the caller/user of this protocol and so you cannot release
them anytime soon, indeed.
The core SCMI stack usually takes care to track and release all the devm_
resources allocated by the protocol ONLY if they were allocated with devres
while inside scmi_pinctrl_protocol_init() function.
(see drivers/firmware/arm-scmi/driver.c:scmi_alloc_init_protocol_instance()
and scmi_protocol_release)
BUT you do not allocate these arrays inside the protocol-init function,
you allocate them the first time these ops are called at runtime.
If you unbind/unload all the drivers using this protocol and then reload
them, all the devm_ allocations in protocol_init will be freed and
reallocated BUT these arrays will never be freed (they are boudn to handle->dev)
and instead they will be reallocated multiple times (present flag will be cleared
on unload), remained unused and freed finally only when the whole SCMI stack is
unbind/unloaded.
You use a present flag to avoid reissuing the same query and
reallocating all the arrays each time a driver calls these
protocol_ops one, but really all these data is available early on at
protocol init time and they are not supposed to change at runtime, dont they ?
Even in a virtualized environment, you boot an agent and the SCMI
platform server provides to the agent the list of associations when
queried but then this does not change until the next reboot right ?
(indeed you do not query more than once...)
The agent can only change the PIN status with CONFIG_SET or
FUNCTION_SELECT or REQUEST the exclusive use of a pin/group, but it is
not that the platform can change the pin/groups associaion for the same
agent at run time, this are static data for the whole life of the agent.
Am I right ?
IOW I think there is some potential memory leak on unbind/bind and it would
be better to query and allocate all of these resources at init time and keep
them ready to be retrieved by subsequent operations, since the lifetime
of these resources is pretty long and they are basically representing
static data that does not change after the init/probe phases.
Indeed, all the other protocols usually allocate all the needed
resources and query all the available SCMI resources once for all during
the protocol_init, storing all the retrieved info in some struct *_info
exposed in scmi_protocol.h and then provide some related protocol_ops to
get the number of resources and to retrieve specific domain info descriptors.
(voltage.c is an example and more on this down below...)
This way, any dynamic allocation is done during protocol_init, so
it can be automatically freed by the SCMI core once there are no more
users of that protocol, and all of this static info data is queried
and retrieved once for all at protocol initialization time, avoiding
unneeded message exchanges to retrieve always the same data.
(which you avoid anyway with the present flag)
If you have a good reason to instead perform this sort of lazy
allocation/query performed only at the last minute when someone ask for
that specific resource, you will have to provide also a .instance_deinit
function to clean anything you allocated out of the .instance_init
routine; but this would seem strange to me since any resource that is
discovered at init will be eventually immediately queried by a driver
which uses this protocol...am I missing something ?
> + if (!group->group_pins) {
> + ret = -ENOMEM;
> + goto err;
> + }
> +
> + ret = scmi_pinctrl_list_associations(handle, selector, GROUP_TYPE,
> + group->nr_pins, group->group_pins);
> + if (ret)
> + goto err_groups;
> +
> + group->present = true;
> + return 0;
> +
> + err_groups:
> + kfree(group->group_pins);
> + err:
> + kfree(group->name);
> + return ret;
> +}
> +
> +static int scmi_pinctrl_get_group_name(const struct scmi_handle *handle,
> + u32 selector, const char **name)
> +{
> + int ret;
> + struct scmi_pinctrl_info *pi;
> +
> + if (!handle || !handle->pinctrl_priv || !name)
> + return -EINVAL;
> +
> + pi = handle->pinctrl_priv;
> +
> + if (selector > pi->nr_groups)
> + return -EINVAL;
> +
> + if (!pi->groups[selector].present) {
> + ret = scmi_pinctrl_get_group_info(handle, selector,
> + &pi->groups[selector]);
> + if (ret)
> + return ret;
> + }
> +
> + *name = pi->groups[selector].name;
> +
> + return 0;
> +}
> +
> +static int scmi_pinctrl_get_group_pins(const struct scmi_handle *handle,
> + u32 selector, const unsigned int **pins,
> + unsigned int *nr_pins)
> +{
> + int ret;
> + struct scmi_pinctrl_info *pi;
> +
> + if (!handle || !handle->pinctrl_priv || !pins || !nr_pins)
> + return -EINVAL;
> +
> + pi = handle->pinctrl_priv;
> +
> + if (selector > pi->nr_groups)
> + return -EINVAL;
> +
> + if (!pi->groups[selector].present) {
> + ret = scmi_pinctrl_get_group_info(handle, selector,
> + &pi->groups[selector]);
> + if (ret)
> + return ret;
> + }
> +
> + *pins = pi->groups[selector].group_pins;
> + *nr_pins = pi->groups[selector].nr_pins;
> +
> + return ret;
> +}
> +
> +static int scmi_pinctrl_get_function_info(const struct scmi_handle *handle,
> + u32 selector,
> + struct scmi_function_info *func)
> +{
> + int ret = 0;
> + struct scmi_pinctrl_info *pi;
> +
> + if (!handle || !handle->pinctrl_priv || !func)
> + return -EINVAL;
> +
> + pi = handle->pinctrl_priv;
> +
> + ret = scmi_pinctrl_attributes(handle, FUNCTION_TYPE, selector,
> + &func->name,
> + &func->nr_groups);
> + if (ret)
> + return ret;
> +
> + if (!func->nr_groups) {
> + dev_err(handle->dev, "Function %d has 0 elements", selector);
> + return -ENODATA;
> + }
> +
> + func->groups = devm_kmalloc_array(handle->dev, func->nr_groups,
> + sizeof(*func->groups),
> + GFP_KERNEL);
> + if (!func->groups) {
> + ret = -ENOMEM;
> + goto err;
> + }
> +
> + ret = scmi_pinctrl_list_associations(handle, selector, FUNCTION_TYPE,
> + func->nr_groups, func->groups);
> + if (ret)
> + goto err_funcs;
> +
> + func->present = true;
> + return 0;
> +
> + err_funcs:
> + kfree(func->groups);
> + err:
> + kfree(func->name);
> + return ret;
> +}
> +
> +static int scmi_pinctrl_get_function_name(const struct scmi_handle *handle,
> + u32 selector, const char **name)
> +{
> + int ret;
> + struct scmi_pinctrl_info *pi;
> +
> + if (!handle || !handle->pinctrl_priv || !name)
> + return -EINVAL;
> +
> + pi = handle->pinctrl_priv;
> +
> + if (selector > pi->nr_functions)
> + return -EINVAL;
> +
> + if (!pi->functions[selector].present) {
> + ret = scmi_pinctrl_get_function_info(handle, selector,
> + &pi->functions[selector]);
> + if (ret)
> + return ret;
> + }
> +
> + *name = pi->functions[selector].name;
> + return 0;
> +}
> +
> +static int scmi_pinctrl_get_function_groups(const struct scmi_handle *handle,
> + u32 selector,
> + unsigned int *nr_groups,
> + const unsigned int **groups)
> +{
> + int ret;
> + struct scmi_pinctrl_info *pi;
> +
> + if (!handle || !handle->pinctrl_priv || !groups || !nr_groups)
> + return -EINVAL;
> +
> + pi = handle->pinctrl_priv;
> +
> + if (selector > pi->nr_functions)
> + return -EINVAL;
> +
> + if (!pi->functions[selector].present) {
> + ret = scmi_pinctrl_get_function_info(handle, selector,
> + &pi->functions[selector]);
> + if (ret)
> + return ret;
> + }
> +
> + *groups = pi->functions[selector].groups;
> + *nr_groups = pi->functions[selector].nr_groups;
> +
> + return ret;
> +}
> +
> +static int scmi_pinctrl_set_mux(const struct scmi_handle *handle, u32 selector,
> + u32 group)
> +{
> + return scmi_pinctrl_function_select(handle, group, GROUP_TYPE,
> + selector);
> +}
> +
> +static int scmi_pinctrl_get_pin_info(const struct scmi_handle *handle,
> + u32 selector, struct scmi_pin_info *pin)
> +{
> + int ret = 0;
> + struct scmi_pinctrl_info *pi;
> + unsigned int n_elems;
> +
> + if (!handle || !handle->pinctrl_priv || !pin)
> + return -EINVAL;
> +
> + pi = handle->pinctrl_priv;
> +
> + ret = scmi_pinctrl_attributes(handle, PIN_TYPE, selector,
> + &pin->name,
> + &n_elems);
> + if (ret)
> + return ret;
> +
> + if (n_elems != pi->nr_pins) {
> + dev_err(handle->dev, "Wrong pin count expected %d has %d",
> + pi->nr_pins, n_elems);
> + return -ENODATA;
> + }
> +
> + if (*(pin->name) == 0) {
> + dev_err(handle->dev, "Pin name is empty");
> + goto err;
> + }
> +
> + pin->present = true;
> + return 0;
> +
> + err:
> + kfree(pin->name);
> + return ret;
> +}
> +
> +static int scmi_pinctrl_get_pin_name(const struct scmi_handle *handle, u32 selector,
> + const char **name)
> +{
> +
> + int ret;
> + struct scmi_pinctrl_info *pi;
> +
> + if (!handle || !handle->pinctrl_priv || !name)
> + return -EINVAL;
> +
> + pi = handle->pinctrl_priv;
> +
> + if (selector > pi->nr_pins)
> + return -EINVAL;
> +
> + if (!pi->pins[selector].present) {
> + ret = scmi_pinctrl_get_pin_info(handle, selector,
> + &pi->pins[selector]);
> + if (ret)
> + return ret;
> + }
> +
> + *name = pi->pins[selector].name;
> +
> + return 0;
> +}
> +
> +
> +static const struct scmi_pinctrl_ops pinctrl_ops = {
> + .get_groups_count = scmi_pinctrl_get_groups_count,
> + .get_group_name = scmi_pinctrl_get_group_name,
> + .get_group_pins = scmi_pinctrl_get_group_pins,
> + .get_functions_count = scmi_pinctrl_get_functions_count,
> + .get_function_name = scmi_pinctrl_get_function_name,
> + .get_function_groups = scmi_pinctrl_get_function_groups,
> + .set_mux = scmi_pinctrl_set_mux,
> + .get_pin_name = scmi_pinctrl_get_pin_name,
> + .get_pins_count = scmi_pinctrl_get_pins_count,
> + .get_config = scmi_pinctrl_get_config,
> + .set_config = scmi_pinctrl_set_config,
> + .get_config_group = scmi_pinctrl_get_config_group,
> + .set_config_group = scmi_pinctrl_set_config_group,
> + .request_pin = scmi_pinctrl_request_pin,
> + .free_pin = scmi_pinctrl_free_pin
> +};
> +
> +static int scmi_pinctrl_protocol_init(struct scmi_handle *handle)
> +{
> + u32 version;
> + struct scmi_pinctrl_info *pinfo;
> + int ret;
> +
> + if (!handle)
> + return -EINVAL;
> +
> + scmi_version_get(handle, SCMI_PROTOCOL_PINCTRL, &version);
> +
> + dev_dbg(handle->dev, "Pinctrl Version %d.%d\n",
> + PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
> +
> + pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL);
> + if (!pinfo)
> + return -ENOMEM;
> +
> + ret = scmi_pinctrl_attributes_get(handle, pinfo);
> + if (ret)
> + goto free;
> +
> + pinfo->pins = devm_kmalloc_array(handle->dev, pinfo->nr_pins,
> + sizeof(*pinfo->pins),
> + GFP_KERNEL | __GFP_ZERO);
devm_kcalloc() zeroes on its own
> + if (!pinfo->pins) {
> + ret = -ENOMEM;
> + goto free;
> + }
> +
> + pinfo->groups = devm_kmalloc_array(handle->dev, pinfo->nr_groups,
> + sizeof(*pinfo->groups),
> + GFP_KERNEL | __GFP_ZERO);
Ditto.
> + if (!pinfo->groups) {
> + ret = -ENOMEM;
> + goto free;
> + }
> +
> + pinfo->functions = devm_kmalloc_array(handle->dev, pinfo->nr_functions,
> + sizeof(*pinfo->functions),
> + GFP_KERNEL | __GFP_ZERO);
> + if (!pinfo->functions) {
> + ret = -ENOMEM;
> + goto free;
> + }
> +
> + pinfo->version = version;
> + handle->pinctrl_ops = &pinctrl_ops;
> + handle->pinctrl_priv = pinfo;
> +
> + return 0;
> +free:
> + if (pinfo) {
> + devm_kfree(handle->dev, pinfo->pins);
> + devm_kfree(handle->dev, pinfo->functions);
> + devm_kfree(handle->dev, pinfo->groups);
> + }
These frees are really not needed...if this function return failure any
devres allocated in it is freed by the SCMI core. (as said above...in a
recent kernel with the new API of course)
> +
> + devm_kfree(handle->dev, pinfo);
> +
> + return ret;
> +}
> +
> +DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_PINCTRL, pinctrl)
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index 815095326e2d..68add4d06e8c 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -431,4 +431,13 @@ config PINCTRL_EQUILIBRIUM
> pin functions, configure GPIO attributes for LGM SoC pins. Pinmux and
> pinconf settings are retrieved from device tree.
>
> +config PINCTRL_SCMI
> + bool "Pinctrl driver controlled via SCMI interface"
> + depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
> + help
> + This driver provides support for pinctrl which is controlled
> + by firmware that implements the SCMI interface.
> + It uses SCMI Message Protocol to interact with the
> + firmware providing all the pinctrl controls.
> +
This does NOT belong to this patch, but to the next right ?
> endif
> diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
> index 9cd312a1ff92..6a909ef3bf51 100644
> --- a/include/linux/scmi_protocol.h
> +++ b/include/linux/scmi_protocol.h
> @@ -12,7 +12,8 @@
> #include <linux/notifier.h>
> #include <linux/types.h>
>
> -#define SCMI_MAX_STR_SIZE 16
> +#define SCMI_MAX_STR_SIZE 16
> +#define SCMI_MAX_STR_EXT_SIZE 64
This is handled as part of how the extended names are handled with ph->hops
in a common way, as I was saying, so please move this if you need it in
the protocol code, then I'll port to the ph->hops interface and clean
up.
> #define SCMI_MAX_NUM_RATES 16
>
> /**
> @@ -252,6 +253,55 @@ struct scmi_notify_ops {
> struct notifier_block *nb);
> };
>
> +/**
> + * struct scmi_pinctrl_ops - represents the various operations provided
> + * by SCMI Pinctrl Protocol
> + *
> + * @get_groups_count: returns count of the registered groups
> + * @get_group_name: returns group name by index
> + * @get_group_pins: returns the set of pins, assigned to the specified group
> + * @get_functions_count: returns count of the registered fucntions
> + * @get_function_name: returns function name by indes
> + * @get_function_groups: returns the set of groups, assigned to the specified
> + * function
> + * @set_mux: set muxing function for groups of pins
> + * @get_pins: returns the set of pins, registered in driver
> + * @get_config: returns configuration parameter for pin
> + * @set_config: sets the configuration parameter for pin
> + * @get_config_group: returns the configuration parameter for a group of pins
> + * @set_config_group: sets the configuration parameter for a groups of pins
> + * @request_pin: aquire pin before selecting mux setting
> + * @free_pin: frees pin, acquired by request_pin call
> + */
> +struct scmi_pinctrl_ops {
> + int (*get_groups_count)(const struct scmi_handle *handle);
> + int (*get_group_name)(const struct scmi_handle *handles, u32 selector,
> + const char **name);
> + int (*get_group_pins)(const struct scmi_handle *handle, u32 selector,
> + const unsigned int **pins, unsigned int *nr_pins);
> + int (*get_functions_count)(const struct scmi_handle *handle);
> + int (*get_function_name)(const struct scmi_handle *handle, u32 selector,
> + const char **name);
> + int (*get_function_groups)(const struct scmi_handle *handle,
> + u32 selector, unsigned int *nr_groups,
> + const unsigned int **groups);
> + int (*set_mux)(const struct scmi_handle *handle, u32 selector,
> + u32 group);
> + int (*get_pin_name)(const struct scmi_handle *handle, u32 selector,
> + const char **name);
> + int (*get_pins_count)(const struct scmi_handle *handle);
> + int (*get_config)(const struct scmi_handle *handle, u32 pin,
> + u32 *config);
> + int (*set_config)(const struct scmi_handle *handle, u32 pin,
> + u32 config);
> + int (*get_config_group)(const struct scmi_handle *handle, u32 pin,
> + u32 *config);
> + int (*set_config_group)(const struct scmi_handle *handle, u32 pin,
> + u32 config);
> + int (*request_pin)(const struct scmi_handle *handle, u32 pin);
> + int (*free_pin)(const struct scmi_handle *handle, u32 pin);
> +};
> +
As mentioned above, here you could drop a lot of this get_X_count/name/pins
and instead expose a few of the internal proocol struct scmi__X_info and then
provide just a mean to query how many resource are there and then get the info
descriptor you want for the specific domain_id, i.e.:
int (*num_domains_get)(ph, type)
void *(*info_get)(ph, type, domain_id);
Thanks,
Cristian
On 4/13/2023 6:04 AM, Cristian Marussi wrote:
> On Fri, Apr 07, 2023 at 10:18:27AM +0000, Oleksii Moisieiev wrote:
>> Implementation of the SCMI client driver, which implements
>> PINCTRL_PROTOCOL. This protocol has ID 19 and is described
>> in the latest DEN0056 document.
>
> Hi,
>
>> This protocol is part of the feature that was designed to
>> separate the pinctrl subsystem from the SCP firmware.
>> The idea is to separate communication of the pin control
>> subsystem with the hardware to SCP firmware
>> (or a similar system, such as ATF), which provides an interface
>> to give the OS ability to control the hardware through SCMI protocol.
>> This is a generic driver that implements SCMI protocol,
>> independent of the platform type.
>>
>> DEN0056 document:
>> https://developer.arm.com/documentation/den0056/latest
>>
>
> No need to specify all of this in the commit message, just a note that
> you are adding a new SCMIv3.2 Pincontrol protocol, highlighting anything
> that has been left out in this patch (if any) will be enough.
Is it possible to extend the spec to support multilple uint32_t for PIN
CONFIG SET?
With only one uint32_t could not satisfy i.MX requirement.
Thanks,
Peng.
> You can look at the very first commit logs of existing protos as an
> example like: drivers/firmware/arm_scmi/powercap.c
>
> Some more comments down below, I'll mostly skip anything related to the
> SCMI API change I mentioned before...
>
> I'll also wont comment on more trivial stuff related to style, BUT there
> are lots of them: you should run
>
> ./scripts/checkpatch.pl --strict <your-git-format-patch-file>
>
> for each patch in the series. (and fix accordingly..spacing, brackets...etc)
>
>> Signed-off-by: Oleksii Moisieiev <[email protected]>
>> ---
>> MAINTAINERS | 6 +
>> drivers/firmware/arm_scmi/Makefile | 2 +-
>> drivers/firmware/arm_scmi/common.h | 1 +
>> drivers/firmware/arm_scmi/driver.c | 3 +
>> drivers/firmware/arm_scmi/pinctrl.c | 905 ++++++++++++++++++++++++++++
>> drivers/pinctrl/Kconfig | 9 +
>> include/linux/scmi_protocol.h | 58 +-
>> 7 files changed, 982 insertions(+), 2 deletions(-)
>> create mode 100644 drivers/firmware/arm_scmi/pinctrl.c
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 281de213ef47..abc543fd7544 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -16961,6 +16961,12 @@ F: drivers/reset/reset-scmi.c
>> F: include/linux/sc[mp]i_protocol.h
>> F: include/trace/events/scmi.h
>>
>> +PINCTRL DRIVER FOR SYSTEM CONTROL & POWER/MANAGEMENT INTERFACE (SCPI/SCMI)
>> +M: Oleksii Moisieiev <[email protected]>
>> +L: [email protected]
>> +S: Maintained
>> +F: drivers/firmware/arm_scmi/pinctrl.c
>> +
>> SYSTEM RESET/SHUTDOWN DRIVERS
>> M: Sebastian Reichel <[email protected]>
>> L: [email protected]
>> diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
>> index bc0d54f8e861..5cec357fbfa8 100644
>> --- a/drivers/firmware/arm_scmi/Makefile
>> +++ b/drivers/firmware/arm_scmi/Makefile
>> @@ -4,7 +4,7 @@ scmi-driver-y = driver.o notify.o
>> scmi-transport-y = shmem.o
>> scmi-transport-$(CONFIG_MAILBOX) += mailbox.o
>> scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o
>> -scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o
>> +scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o pinctrl.o
>> scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \
>> $(scmi-transport-y)
>> obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
>> diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
>> index 65063fa948d4..8bbb404abe8d 100644
>> --- a/drivers/firmware/arm_scmi/common.h
>> +++ b/drivers/firmware/arm_scmi/common.h
>> @@ -170,6 +170,7 @@ DECLARE_SCMI_REGISTER_UNREGISTER(power);
>> DECLARE_SCMI_REGISTER_UNREGISTER(reset);
>> DECLARE_SCMI_REGISTER_UNREGISTER(sensors);
>> DECLARE_SCMI_REGISTER_UNREGISTER(system);
>> +DECLARE_SCMI_REGISTER_UNREGISTER(pinctrl);
>>
>> #define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(id, name) \
>> int __init scmi_##name##_register(void) \
>> diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
>> index 3dfd8b6a0ebf..fb9525fb3c24 100644
>> --- a/drivers/firmware/arm_scmi/driver.c
>> +++ b/drivers/firmware/arm_scmi/driver.c
>> @@ -743,6 +743,7 @@ static struct scmi_prot_devnames devnames[] = {
>> { SCMI_PROTOCOL_CLOCK, { "clocks" },},
>> { SCMI_PROTOCOL_SENSOR, { "hwmon" },},
>> { SCMI_PROTOCOL_RESET, { "reset" },},
>> + { SCMI_PROTOCOL_PINCTRL, { "pinctrl" },},
>> };
>>
>> static inline void
>> @@ -947,6 +948,7 @@ static int __init scmi_driver_init(void)
>> scmi_reset_register();
>> scmi_sensors_register();
>> scmi_system_register();
>> + scmi_pinctrl_register();
>>
>> return platform_driver_register(&scmi_driver);
>> }
>> @@ -962,6 +964,7 @@ static void __exit scmi_driver_exit(void)
>> scmi_reset_unregister();
>> scmi_sensors_unregister();
>> scmi_system_unregister();
>> + scmi_pinctrl_unregister();
>>
>> platform_driver_unregister(&scmi_driver);
>> }
>> diff --git a/drivers/firmware/arm_scmi/pinctrl.c b/drivers/firmware/arm_scmi/pinctrl.c
>> new file mode 100644
>> index 000000000000..037270d7f39b
>> --- /dev/null
>> +++ b/drivers/firmware/arm_scmi/pinctrl.c
>> @@ -0,0 +1,905 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * System Control and Management Interface (SCMI) Pinctrl Protocol
>> + *
>> + * Copyright (C) 2021 EPAM.
>
> nitpick: update (C) years
>
>> + */
>> +
>> +#define pr_fmt(fmt) "SCMI Notifications PINCTRL - " fmt
>> +
>
> This is not needed, no notifs in this proto.
>
>> +#include <linux/scmi_protocol.h>
>> +#include <linux/slab.h>
>> +
>> +#include "common.h"
>> +#include "notify.h"
>
> Notifs not needed, and in the new API world you'll just need a:
>
> #include "protocols.h"
>
>> +
>> +#define SET_TYPE(x) ((x) & 0x3)
>> +
>
> Even if trivial better to use std bitfield.h macros like
> FIELD_GET() BIT() ... etc
>
>> +enum scmi_pinctrl_protocol_cmd {
>> + PINCTRL_ATTRIBUTES = 0x3,
>> + PINCTRL_LIST_ASSOCIATIONS = 0x4,
>> + PINCTRL_CONFIG_GET = 0x5,
>> + PINCTRL_CONFIG_SET = 0x6,
>> + PINCTRL_FUNCTION_SELECT = 0x7,
>> + PINCTRL_REQUEST = 0x8,
>> + PINCTRL_RELEASE = 0x9,
>> + PINCTRL_NAME_GET = 0xa,
>> + PINCTRL_SET_PERMISSIONS = 0xb
>> +};
>> +
>> +enum scmi_pinctrl_selector_type {
>> + PIN_TYPE = 0,
>> + GROUP_TYPE,
>> + FUNCTION_TYPE
>> +};
>> +
>> +struct scmi_group_info {
>> + bool present;
>> + char *name;
>> + unsigned int *group_pins;
>> + unsigned int nr_pins;
>> +};
>> +
>> +struct scmi_function_info {
>> + bool present;
>> + char *name;
>> + unsigned int *groups;
>> + unsigned int nr_groups;
>> +};
>> +
>> +struct scmi_pin_info {
>> + bool present;
>> + char *name;
>> +};
>> +
>> +struct scmi_pinctrl_info {
>> + u32 version;
>> + u16 nr_groups;
>> + u16 nr_functions;
>> + u16 nr_pins;
>
> Since these vars are not related to stricly spaced message fields (even though
> derived from such messages) do not use sized types, you can just stick with
> unsigned int. (it is also better not to mix sized and unsized types in the same
> struct). This also could come handy if these will be exposed to the user
> in scmi_protocol.h in the future (more on this down below)
>
>> + struct scmi_group_info *groups;
>> + struct scmi_function_info *functions;
>> + struct scmi_pin_info *pins;
>> +};
>> +
>> +struct scmi_conf_tx {
>> + __le32 identifier;
>> +#define SET_TYPE_BITS(attr, x) (((attr) & 0xFFFFFCFF) | (x & 0x3) << 8)
>> +#define SET_CONFIG(attr, x) (((attr) & 0xFF) | (x & 0xFF))
>
> Use bitfield.h like FIELD_SET / GENMASK etc
>
>> + __le32 attributes;
>> +};
>> +
>> +static int scmi_pinctrl_attributes_get(const struct scmi_handle *handle,
>> + struct scmi_pinctrl_info *pi)
>> +{
>> + int ret;
>> + struct scmi_xfer *t;
>> + struct scmi_msg_pinctrl_protocol_attributes {
>> +#define GROUPS_NR(x) ((x) >> 16)
>> +#define PINS_NR(x) ((x) & 0xffff)
>> + __le32 attributes_low;
>> +#define FUNCTIONS_NR(x) ((x) & 0xffff)
>> + __le32 attributes_high;
>> + } *attr;
>
> For consistency with the rest of the stack (mostly :D), please move this struct
> definition and related macros outside in the global scope right after command
> enum. (and use bitfield macros ...)
>
>> +
>> + if (!pi)
>> + return -EINVAL;
>> +
>> + ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
>> + SCMI_PROTOCOL_PINCTRL, 0, sizeof(*attr), &t);
>> + if (ret)
>> + return ret;
>> +
>> + attr = t->rx.buf;
>> +
>> + ret = scmi_do_xfer(handle, t);
>> + if (!ret) {
>> + pi->nr_functions =
>> + le16_to_cpu(FUNCTIONS_NR(attr->attributes_high));
>> + pi->nr_groups = le16_to_cpu(GROUPS_NR(attr->attributes_low));
>> + pi->nr_pins = le16_to_cpu(PINS_NR(attr->attributes_low));
>> + }
>> +
>> + scmi_xfer_put(handle, t);
>> + return ret;
>> +}
>> +
>> +static int scmi_pinctrl_get_groups_count(const struct scmi_handle *handle)
>> +{
>> + struct scmi_pinctrl_info *pi;
>> +
>> + if (!handle || !handle->pinctrl_priv)
>> + return -ENODEV;
>> +
>> + pi = handle->pinctrl_priv;
>> +
>> + return pi->nr_groups;
>> +}
>> +
>> +static int scmi_pinctrl_get_pins_count(const struct scmi_handle *handle)
>> +{
>> + struct scmi_pinctrl_info *pi;
>> +
>> + if (!handle || !handle->pinctrl_priv)
>> + return -ENODEV;
>> +
>> + pi = handle->pinctrl_priv;
>> +
>> + return pi->nr_pins;
>> +}
>> +
>> +static int scmi_pinctrl_get_functions_count(const struct scmi_handle *handle)
>> +{
>> + struct scmi_pinctrl_info *pi;
>> +
>> + if (!handle || !handle->pinctrl_priv)
>> + return -ENODEV;
>> +
>> + pi = handle->pinctrl_priv;
>> +
>> + return pi->nr_functions;
>> +}
>> +
>> +static int scmi_pinctrl_validate_id(const struct scmi_handle *handle,
>> + u32 identifier,
>> + enum scmi_pinctrl_selector_type type)
>> +{
>> + struct scmi_pinctrl_info *pi;
>> +
>> + if (!handle || !handle->pinctrl_priv)
>> + return -ENODEV;
>> +
>> + switch (type) {
>> + case PIN_TYPE:
>> + pi = handle->pinctrl_priv;
>> +
>> + return (identifier < pi->nr_pins) ? 0 : -EINVAL;
>> + case GROUP_TYPE:
>> + return (identifier <
>> + scmi_pinctrl_get_groups_count(handle)) ?
>> + 0 : -EINVAL;
>> + case FUNCTION_TYPE:
>> + return (identifier <
>> + scmi_pinctrl_get_functions_count(handle)) ?
>> + 0 : -EINVAL;
>> + default:
>> + return -EINVAL;
>> + }
>
> Here I would just pick the right value to compare, break and then
> compare and exit, something aroundf the lines of:
>
> case PIN_TYPE:
> ...
> val = pi->nr_pins;
> break;
> ...
> case GROUP_TYPE:
> val = scmi_pinctrl_get_groups_count());
> break;
>
> ....
> ....
> default:
> return -EINVAL;
> }
>
> if (identifier >= val)
> return -EINVAL;
>
> return 0;
> }
>
> ... it's easier to read. What do you think ?
>
>
>> +}
>> +
>> +static int scmi_pinctrl_get_name(const struct scmi_handle *handle,
>> + u32 identifier,
>> + enum scmi_pinctrl_selector_type type,
>> + char **name)
>> +{
>
> As said, there is common helper for this, but it will need some small
> adaptation in the SCMI core to work here so keep it as it is, and I'll take
> care of this later, if it sounds fine for you.
>
>> + struct scmi_xfer *t;
>> + int ret = 0;
>> + struct scmi_name_tx {
>> + __le32 identifier;
>> + __le32 flags;
>> + } *tx;
>> + struct scmi_name_rx {
>> + __le32 flags;
>> + u8 name[64];
>> + } *rx;
>> +
>> + if (!handle || !name)
>> + return -EINVAL;
>> +
>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>> + if (ret)
>> + return ret;
>> +
>> + ret = scmi_xfer_get_init(handle, PINCTRL_NAME_GET,
>> + SCMI_PROTOCOL_PINCTRL,
>> + sizeof(*tx), sizeof(*rx), &t);
>> +
>> + tx = t->tx.buf;
>> + rx = t->rx.buf;
>> + tx->identifier = identifier;
>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>> +
>> + ret = scmi_do_xfer(handle, t);
>> + if (ret)
>> + goto out;
>> +
>> + if (rx->flags) {
>> + ret = -EINVAL;
>> + goto out;
>> + }
>> +
>> + *name = kasprintf(GFP_KERNEL, "%s", rx->name);
>> + if (!*name)
>> + ret = -ENOMEM;
>> + out:
>> + scmi_xfer_put(handle, t);
>> +
>> + return ret;
>> +}
>> +
>> +static int scmi_pinctrl_attributes(const struct scmi_handle *handle,
>> + enum scmi_pinctrl_selector_type type,
>> + u32 selector, char **name,
>> + unsigned int *n_elems)
>> +{
>> + int ret = 0;
>> + struct scmi_xfer *t;
>> + struct scmi_pinctrl_attributes_tx {
>> + __le32 identifier;
>> + __le32 flags;
>> + } *tx;
>> + struct scmi_pinctrl_attributes_rx {
>> +#define EXT_NAME_FLAG(x) ((x) & BIT(31))
>> +#define NUM_ELEMS(x) ((x) & 0xffff)
>> + __le32 attributes;
>> + u8 name[16];
>> + } *rx;
>
> Ditto. Move these defs outside, bitfield.h for macros and try to use the
> same naming style for message structs as in other protos, i.e.
>
> for commands: struct scmi_msg_pinctrl_attributes
> for replies: struct scmi_resp_pinctrl_attributes
>
> (or some variations around that...
> scmi_msg_cmd_* scmi_msg_resp_*
>
> we have not been fully consistent really, so I dont want to be
> pedantic here, but we never used tx/rx in message context since it is
> already (mis)-used in SCMI channel context...)
>
>> +
>> + if (!handle || !name)
>> + return -EINVAL;
>> +
>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>> + if (ret)
>> + return ret;
>> +
>> + ret = scmi_xfer_get_init(handle, PINCTRL_ATTRIBUTES,
>> + SCMI_PROTOCOL_PINCTRL, sizeof(*tx), sizeof(*rx), &t);
>> + if (ret)
>> + return ret;
>> +
>> + tx = t->tx.buf;
>> + rx = t->rx.buf;
>> + tx->identifier = selector;
>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>> +
>> + ret = scmi_do_xfer(handle, t);
>> + if (ret)
>> + goto out;
>> +
>> + *n_elems = NUM_ELEMS(rx->attributes);
>> +
>> + if (!EXT_NAME_FLAG(rx->attributes)) {
>> + *name = kasprintf(GFP_KERNEL, "%s", rx->name);
>> + if (!*name)
>> + ret = -ENOMEM;
>> + } else
>> + ret = scmi_pinctrl_get_name(handle, selector, type, name);
>> + out:
>> + scmi_xfer_put(handle, t);
>> + return ret;
>> +}
>> +
>> +static int scmi_pinctrl_list_associations(const struct scmi_handle *handle,
>> + u32 selector,
>> + enum scmi_pinctrl_selector_type type,
>> + uint16_t size, unsigned int *array)
>> +{
>
> This is the other functionalities you could implement straight away using
> ph->hops helpers (iterators) but just leave it this way, and I'll port it later
> (once we retested all of this as working with the new API but without any
> ph->hops usage..I think it is safer to change one bit at time... :P)
>
>> + struct scmi_xfer *t;
>> + struct scmi_pinctrl_list_assoc_tx {
>> + __le32 identifier;
>> + __le32 flags;
>> + __le32 index;
>> + } *tx;
>> + struct scmi_pinctrl_list_assoc_rx {
>> +#define RETURNED(x) ((x) & 0xFFF)
>> +#define REMAINING(x) ((x) >> 16)
>> + __le32 flags;
>> + __le16 array[];
>> + } *rx;
>
> Ditto, about struct naming and macros.
>
>> + u16 tot_num_ret = 0, loop_num_ret;
>> + u16 remaining_num_ret;
>> + int ret, loop;
>> +
>> + if (!handle || !array || !size)
>> + return -EINVAL;
>> +
>> + if (type == PIN_TYPE)
>> + return -EINVAL;
>> +
>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>> + if (ret)
>> + return ret;
>> +
>> + ret = scmi_xfer_get_init(handle, PINCTRL_LIST_ASSOCIATIONS,
>> + SCMI_PROTOCOL_PINCTRL, sizeof(*tx),
>> + 0, &t);
>> + if (ret)
>> + return ret;
>> +
>> + tx = t->tx.buf;
>> + rx = t->rx.buf;
>> +
>> + do {
>> + tx->identifier = cpu_to_le32(selector);
>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>> + tx->index = cpu_to_le32(tot_num_ret);
>> +
>> + ret = scmi_do_xfer(handle, t);
>> + if (ret)
>> + break;
>> +
>> + loop_num_ret = le32_to_cpu(RETURNED(rx->flags));
>> + remaining_num_ret = le32_to_cpu(REMAINING(rx->flags));
>> +
>> + for (loop = 0; loop < loop_num_ret; loop++) {
>> + if (tot_num_ret + loop >= size) {
>> + ret = -EMSGSIZE;
>> + goto out;
>> + }
>> +
>> + array[tot_num_ret + loop] =
>> + le16_to_cpu(rx->array[loop]);
>> + }
>> +
>> + tot_num_ret += loop_num_ret;
>> +
>> + scmi_reset_rx_to_maxsz(handle, t);
>> + } while (remaining_num_ret > 0);
>> +out:
>> + scmi_xfer_put(handle, t);
>> + return ret;
>> +}
>> +
>> +static int scmi_pinctrl_request_config(const struct scmi_handle *handle,
>> + u32 selector,
>> + enum scmi_pinctrl_selector_type type,
>> + u32 *config)
>> +{
>> + struct scmi_xfer *t;
>> + struct scmi_conf_tx *tx;
>> + __le32 *packed_config;
>> + u32 attributes = 0;
>> + int ret;
>> +
>> + if (!handle || !config || type == FUNCTION_TYPE)
>> + return -EINVAL;
>> +
>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>> + if (ret)
>> + return ret;
>> +
>> + ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_GET,
>> + SCMI_PROTOCOL_PINCTRL,
>> + sizeof(*tx), sizeof(*packed_config), &t);
>> + if (ret)
>> + return ret;
>> +
>> + tx = t->tx.buf;
>> + packed_config = t->rx.buf;
>> + tx->identifier = cpu_to_le32(selector);
>> + attributes = SET_TYPE_BITS(attributes, type);
>> + attributes = SET_CONFIG(attributes, *config);
>> +
>> + tx->attributes = cpu_to_le32(attributes);
>> +
>> + ret = scmi_do_xfer(handle, t);
>> +
>> + if (!ret)
>> + *config = le32_to_cpu(*packed_config);
>> +
>> + scmi_xfer_put(handle, t);
>> + return ret;
>> +}
>> +
>> +static int scmi_pinctrl_get_config(const struct scmi_handle *handle, u32 pin,
>> + u32 *config)
>> +{
>> + return scmi_pinctrl_request_config(handle, pin, PIN_TYPE, config);
>> +}
>> +
>> +static int scmi_pinctrl_apply_config(const struct scmi_handle *handle,
>> + u32 selector,
>> + enum scmi_pinctrl_selector_type type,
>> + u32 config)
>> +{
>> + struct scmi_xfer *t;
>> + struct scmi_conf_tx *tx;
>> + u32 attributes = 0;
>> + int ret;
>> +
>> + if (!handle || type == FUNCTION_TYPE)
>> + return -EINVAL;
>> +
>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>> + if (ret)
>> + return ret;
>> +
>> + ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_SET,
>> + SCMI_PROTOCOL_PINCTRL,
>> + sizeof(*tx), 0, &t);
>> + if (ret)
>> + return ret;
>> +
>> + tx = t->tx.buf;
>> + tx->identifier = cpu_to_le32(selector);
>> + attributes = SET_TYPE_BITS(attributes, type);
>> + attributes = SET_CONFIG(attributes, config);
>> + tx->attributes = cpu_to_le32(attributes);
>> +
>> + ret = scmi_do_xfer(handle, t);
>> +
>> + scmi_xfer_put(handle, t);
>> + return ret;
>> +}
>> +
>> +static int scmi_pinctrl_set_config(const struct scmi_handle *handle, u32 pin,
>> + u32 config)
>> +{
>> + return scmi_pinctrl_apply_config(handle, pin, PIN_TYPE, config);
>> +}
>> +
>> +static int scmi_pinctrl_get_config_group(const struct scmi_handle *handle,
>> + u32 group, u32 *config)
>> +{
>> + return scmi_pinctrl_request_config(handle, group, GROUP_TYPE, config);
>> +}
>> +
>> +static int scmi_pinctrl_set_config_group(const struct scmi_handle *handle,
>> + u32 group, u32 config)
>> +{
>> + return scmi_pinctrl_apply_config(handle, group, GROUP_TYPE, config);
>> +}
>> +
>> +static int scmi_pinctrl_function_select(const struct scmi_handle *handle,
>> + u32 identifier,
>> + enum scmi_pinctrl_selector_type type,
>> + u32 function_id)
>> +{
>> + struct scmi_xfer *t;
>> + struct scmi_func_set_tx {
>> + __le32 identifier;
>> + __le32 function_id;
>> + __le32 flags;
>> + } *tx;
>
> Ditto.
>
>> + int ret;
>> +
>> + if (!handle || type == FUNCTION_TYPE)
>> + return -EINVAL;
>> +
>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>> + if (ret)
>> + return ret;
>> +
>> + ret = scmi_xfer_get_init(handle, PINCTRL_FUNCTION_SELECT,
>> + SCMI_PROTOCOL_PINCTRL,
>> + sizeof(*tx), 0, &t);
>> + if (ret)
>> + return ret;
>> +
>> + tx = t->tx.buf;
>> + tx->identifier = cpu_to_le32(identifier);
>> + tx->function_id = cpu_to_le32(function_id);
>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>> +
>> + ret = scmi_do_xfer(handle, t);
>> + scmi_xfer_put(handle, t);
>> +
>> + return ret;
>> +}
>> +
>> +static int scmi_pinctrl_request(const struct scmi_handle *handle,
>> + u32 identifier,
>> + enum scmi_pinctrl_selector_type type)
>> +{
>> + struct scmi_xfer *t;
>> + int ret;
>> + struct scmi_request_tx {
>> + __le32 identifier;
>> + __le32 flags;
>> + } *tx;
>> +
>
> Ditto.
>
>> + if (!handle || type == FUNCTION_TYPE)
>> + return -EINVAL;
>> +
>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>> + if (ret)
>> + return ret;
>> +
>> + ret = scmi_xfer_get_init(handle, PINCTRL_REQUEST, SCMI_PROTOCOL_PINCTRL,
>> + sizeof(*tx), 0, &t);
>> +
>> + tx = t->tx.buf;
>> + tx->identifier = identifier;
>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>> +
>> + ret = scmi_do_xfer(handle, t);
>> + scmi_xfer_put(handle, t);
>> +
>> + return ret;
>> +}
>> +
>> +static int scmi_pinctrl_request_pin(const struct scmi_handle *handle, u32 pin)
>> +{
>> + return scmi_pinctrl_request(handle, pin, PIN_TYPE);
>> +}
>> +
>> +static int scmi_pinctrl_free(const struct scmi_handle *handle, u32 identifier,
>> + enum scmi_pinctrl_selector_type type)
>> +{
>> + struct scmi_xfer *t;
>> + int ret;
>> + struct scmi_request_tx {
>> + __le32 identifier;
>> + __le32 flags;
>> + } *tx;
>> +
> Ditto.
>
>> + if (!handle || type == FUNCTION_TYPE)
>> + return -EINVAL;
>> +
>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>> + if (ret)
>> + return ret;
>> +
>> + ret = scmi_xfer_get_init(handle, PINCTRL_RELEASE, SCMI_PROTOCOL_PINCTRL,
>> + sizeof(*tx), 0, &t);
>> +
>> + tx = t->tx.buf;
>> + tx->identifier = identifier;
>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>> +
>> + ret = scmi_do_xfer(handle, t);
>> + scmi_xfer_put(handle, t);
>> +
>> + return ret;
>> +}
>> +
>> +static int scmi_pinctrl_free_pin(const struct scmi_handle *handle, u32 pin)
>> +{
>> + return scmi_pinctrl_free(handle, pin, PIN_TYPE);
>> +}
>> +
>> +
>> +static int scmi_pinctrl_get_group_info(const struct scmi_handle *handle,
>> + u32 selector,
>> + struct scmi_group_info *group)
>> +{
>> + int ret = 0;
>> + struct scmi_pinctrl_info *pi;
>> +
>> + if (!handle || !handle->pinctrl_priv || !group)
>> + return -EINVAL;
>> +
>> + pi = handle->pinctrl_priv;
>> +
>> + ret = scmi_pinctrl_attributes(handle, GROUP_TYPE, selector,
>> + &group->name,
>> + &group->nr_pins);
>> + if (ret)
>> + return ret;
>> +
>> + if (!group->nr_pins) {
>> + dev_err(handle->dev, "Group %d has 0 elements", selector);
>> + return -ENODATA;
>> + }
>> +
>> + group->group_pins = devm_kmalloc_array(handle->dev, group->nr_pins,
>> + sizeof(*group->group_pins),
>> + GFP_KERNEL);
>
> I think you can just use for the array allocation
>
> devm_kcalloc(dev, n, size, flags)
>
> and it will add also __GFP_ZERO internally to clear it.
> (indeed it calls in turn devm_kmalloc_array)
>
> ...BUT I think there is a further tricky issue here related to memory allocation...
>
> You call this and others function of this kind from some scmi_pinctrl_ops,
> like in scmi_pinctrl_get_group_pins (scmi_pinctrl_ops->get_group_pins),
> and then this is in turn called by the SCMI Pinctrl driver via
> pinctrl_ops->get_group_pins AND you set a present flag so that you issue a
> PINCTRL_LIST_ASSOCIATIONS and allocate here a new group_pins array just
> the first time: but these are never released anywhere, since, even though
> lazily dynamically allocated when asked for, these are static data that
> you pass to the caller/user of this protocol and so you cannot release
> them anytime soon, indeed.
>
> The core SCMI stack usually takes care to track and release all the devm_
> resources allocated by the protocol ONLY if they were allocated with devres
> while inside scmi_pinctrl_protocol_init() function.
> (see drivers/firmware/arm-scmi/driver.c:scmi_alloc_init_protocol_instance()
> and scmi_protocol_release)
>
> BUT you do not allocate these arrays inside the protocol-init function,
> you allocate them the first time these ops are called at runtime.
>
> If you unbind/unload all the drivers using this protocol and then reload
> them, all the devm_ allocations in protocol_init will be freed and
> reallocated BUT these arrays will never be freed (they are boudn to handle->dev)
> and instead they will be reallocated multiple times (present flag will be cleared
> on unload), remained unused and freed finally only when the whole SCMI stack is
> unbind/unloaded.
>
> You use a present flag to avoid reissuing the same query and
> reallocating all the arrays each time a driver calls these
> protocol_ops one, but really all these data is available early on at
> protocol init time and they are not supposed to change at runtime, dont they ?
>
> Even in a virtualized environment, you boot an agent and the SCMI
> platform server provides to the agent the list of associations when
> queried but then this does not change until the next reboot right ?
> (indeed you do not query more than once...)
>
> The agent can only change the PIN status with CONFIG_SET or
> FUNCTION_SELECT or REQUEST the exclusive use of a pin/group, but it is
> not that the platform can change the pin/groups associaion for the same
> agent at run time, this are static data for the whole life of the agent.
>
> Am I right ?
>
> IOW I think there is some potential memory leak on unbind/bind and it would
> be better to query and allocate all of these resources at init time and keep
> them ready to be retrieved by subsequent operations, since the lifetime
> of these resources is pretty long and they are basically representing
> static data that does not change after the init/probe phases.
>
> Indeed, all the other protocols usually allocate all the needed
> resources and query all the available SCMI resources once for all during
> the protocol_init, storing all the retrieved info in some struct *_info
> exposed in scmi_protocol.h and then provide some related protocol_ops to
> get the number of resources and to retrieve specific domain info descriptors.
> (voltage.c is an example and more on this down below...)
>
> This way, any dynamic allocation is done during protocol_init, so
> it can be automatically freed by the SCMI core once there are no more
> users of that protocol, and all of this static info data is queried
> and retrieved once for all at protocol initialization time, avoiding
> unneeded message exchanges to retrieve always the same data.
> (which you avoid anyway with the present flag)
>
> If you have a good reason to instead perform this sort of lazy
> allocation/query performed only at the last minute when someone ask for
> that specific resource, you will have to provide also a .instance_deinit
> function to clean anything you allocated out of the .instance_init
> routine; but this would seem strange to me since any resource that is
> discovered at init will be eventually immediately queried by a driver
> which uses this protocol...am I missing something ?
>
>
>> + if (!group->group_pins) {
>> + ret = -ENOMEM;
>> + goto err;
>> + }
>> +
>> + ret = scmi_pinctrl_list_associations(handle, selector, GROUP_TYPE,
>> + group->nr_pins, group->group_pins);
>> + if (ret)
>> + goto err_groups;
>> +
>> + group->present = true;
>> + return 0;
>> +
>> + err_groups:
>> + kfree(group->group_pins);
>> + err:
>> + kfree(group->name);
>> + return ret;
>> +}
>> +
>> +static int scmi_pinctrl_get_group_name(const struct scmi_handle *handle,
>> + u32 selector, const char **name)
>> +{
>> + int ret;
>> + struct scmi_pinctrl_info *pi;
>> +
>> + if (!handle || !handle->pinctrl_priv || !name)
>> + return -EINVAL;
>> +
>> + pi = handle->pinctrl_priv;
>> +
>> + if (selector > pi->nr_groups)
>> + return -EINVAL;
>> +
>> + if (!pi->groups[selector].present) {
>> + ret = scmi_pinctrl_get_group_info(handle, selector,
>> + &pi->groups[selector]);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + *name = pi->groups[selector].name;
>> +
>> + return 0;
>> +}
>> +
>> +static int scmi_pinctrl_get_group_pins(const struct scmi_handle *handle,
>> + u32 selector, const unsigned int **pins,
>> + unsigned int *nr_pins)
>> +{
>> + int ret;
>> + struct scmi_pinctrl_info *pi;
>> +
>> + if (!handle || !handle->pinctrl_priv || !pins || !nr_pins)
>> + return -EINVAL;
>> +
>> + pi = handle->pinctrl_priv;
>> +
>> + if (selector > pi->nr_groups)
>> + return -EINVAL;
>> +
>> + if (!pi->groups[selector].present) {
>> + ret = scmi_pinctrl_get_group_info(handle, selector,
>> + &pi->groups[selector]);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + *pins = pi->groups[selector].group_pins;
>> + *nr_pins = pi->groups[selector].nr_pins;
>> +
>> + return ret;
>> +}
>> +
>> +static int scmi_pinctrl_get_function_info(const struct scmi_handle *handle,
>> + u32 selector,
>> + struct scmi_function_info *func)
>> +{
>> + int ret = 0;
>> + struct scmi_pinctrl_info *pi;
>> +
>> + if (!handle || !handle->pinctrl_priv || !func)
>> + return -EINVAL;
>> +
>> + pi = handle->pinctrl_priv;
>> +
>> + ret = scmi_pinctrl_attributes(handle, FUNCTION_TYPE, selector,
>> + &func->name,
>> + &func->nr_groups);
>> + if (ret)
>> + return ret;
>> +
>> + if (!func->nr_groups) {
>> + dev_err(handle->dev, "Function %d has 0 elements", selector);
>> + return -ENODATA;
>> + }
>> +
>> + func->groups = devm_kmalloc_array(handle->dev, func->nr_groups,
>> + sizeof(*func->groups),
>> + GFP_KERNEL);
>> + if (!func->groups) {
>> + ret = -ENOMEM;
>> + goto err;
>> + }
>> +
>> + ret = scmi_pinctrl_list_associations(handle, selector, FUNCTION_TYPE,
>> + func->nr_groups, func->groups);
>> + if (ret)
>> + goto err_funcs;
>> +
>> + func->present = true;
>> + return 0;
>> +
>> + err_funcs:
>> + kfree(func->groups);
>> + err:
>> + kfree(func->name);
>> + return ret;
>> +}
>> +
>> +static int scmi_pinctrl_get_function_name(const struct scmi_handle *handle,
>> + u32 selector, const char **name)
>> +{
>> + int ret;
>> + struct scmi_pinctrl_info *pi;
>> +
>> + if (!handle || !handle->pinctrl_priv || !name)
>> + return -EINVAL;
>> +
>> + pi = handle->pinctrl_priv;
>> +
>> + if (selector > pi->nr_functions)
>> + return -EINVAL;
>> +
>> + if (!pi->functions[selector].present) {
>> + ret = scmi_pinctrl_get_function_info(handle, selector,
>> + &pi->functions[selector]);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + *name = pi->functions[selector].name;
>> + return 0;
>> +}
>> +
>> +static int scmi_pinctrl_get_function_groups(const struct scmi_handle *handle,
>> + u32 selector,
>> + unsigned int *nr_groups,
>> + const unsigned int **groups)
>> +{
>> + int ret;
>> + struct scmi_pinctrl_info *pi;
>> +
>> + if (!handle || !handle->pinctrl_priv || !groups || !nr_groups)
>> + return -EINVAL;
>> +
>> + pi = handle->pinctrl_priv;
>> +
>> + if (selector > pi->nr_functions)
>> + return -EINVAL;
>> +
>> + if (!pi->functions[selector].present) {
>> + ret = scmi_pinctrl_get_function_info(handle, selector,
>> + &pi->functions[selector]);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + *groups = pi->functions[selector].groups;
>> + *nr_groups = pi->functions[selector].nr_groups;
>> +
>> + return ret;
>> +}
>> +
>> +static int scmi_pinctrl_set_mux(const struct scmi_handle *handle, u32 selector,
>> + u32 group)
>> +{
>> + return scmi_pinctrl_function_select(handle, group, GROUP_TYPE,
>> + selector);
>> +}
>> +
>> +static int scmi_pinctrl_get_pin_info(const struct scmi_handle *handle,
>> + u32 selector, struct scmi_pin_info *pin)
>> +{
>> + int ret = 0;
>> + struct scmi_pinctrl_info *pi;
>> + unsigned int n_elems;
>> +
>> + if (!handle || !handle->pinctrl_priv || !pin)
>> + return -EINVAL;
>> +
>> + pi = handle->pinctrl_priv;
>> +
>> + ret = scmi_pinctrl_attributes(handle, PIN_TYPE, selector,
>> + &pin->name,
>> + &n_elems);
>> + if (ret)
>> + return ret;
>> +
>> + if (n_elems != pi->nr_pins) {
>> + dev_err(handle->dev, "Wrong pin count expected %d has %d",
>> + pi->nr_pins, n_elems);
>> + return -ENODATA;
>> + }
>> +
>> + if (*(pin->name) == 0) {
>> + dev_err(handle->dev, "Pin name is empty");
>> + goto err;
>> + }
>> +
>> + pin->present = true;
>> + return 0;
>> +
>> + err:
>> + kfree(pin->name);
>> + return ret;
>> +}
>> +
>> +static int scmi_pinctrl_get_pin_name(const struct scmi_handle *handle, u32 selector,
>> + const char **name)
>> +{
>> +
>> + int ret;
>> + struct scmi_pinctrl_info *pi;
>> +
>> + if (!handle || !handle->pinctrl_priv || !name)
>> + return -EINVAL;
>> +
>> + pi = handle->pinctrl_priv;
>> +
>> + if (selector > pi->nr_pins)
>> + return -EINVAL;
>> +
>> + if (!pi->pins[selector].present) {
>> + ret = scmi_pinctrl_get_pin_info(handle, selector,
>> + &pi->pins[selector]);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + *name = pi->pins[selector].name;
>> +
>> + return 0;
>> +}
>> +
>> +
>> +static const struct scmi_pinctrl_ops pinctrl_ops = {
>> + .get_groups_count = scmi_pinctrl_get_groups_count,
>> + .get_group_name = scmi_pinctrl_get_group_name,
>> + .get_group_pins = scmi_pinctrl_get_group_pins,
>> + .get_functions_count = scmi_pinctrl_get_functions_count,
>> + .get_function_name = scmi_pinctrl_get_function_name,
>> + .get_function_groups = scmi_pinctrl_get_function_groups,
>> + .set_mux = scmi_pinctrl_set_mux,
>> + .get_pin_name = scmi_pinctrl_get_pin_name,
>> + .get_pins_count = scmi_pinctrl_get_pins_count,
>> + .get_config = scmi_pinctrl_get_config,
>> + .set_config = scmi_pinctrl_set_config,
>> + .get_config_group = scmi_pinctrl_get_config_group,
>> + .set_config_group = scmi_pinctrl_set_config_group,
>> + .request_pin = scmi_pinctrl_request_pin,
>> + .free_pin = scmi_pinctrl_free_pin
>> +};
>> +
>> +static int scmi_pinctrl_protocol_init(struct scmi_handle *handle)
>> +{
>> + u32 version;
>> + struct scmi_pinctrl_info *pinfo;
>> + int ret;
>> +
>> + if (!handle)
>> + return -EINVAL;
>> +
>> + scmi_version_get(handle, SCMI_PROTOCOL_PINCTRL, &version);
>> +
>> + dev_dbg(handle->dev, "Pinctrl Version %d.%d\n",
>> + PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
>> +
>> + pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL);
>> + if (!pinfo)
>> + return -ENOMEM;
>> +
>> + ret = scmi_pinctrl_attributes_get(handle, pinfo);
>> + if (ret)
>> + goto free;
>> +
>> + pinfo->pins = devm_kmalloc_array(handle->dev, pinfo->nr_pins,
>> + sizeof(*pinfo->pins),
>> + GFP_KERNEL | __GFP_ZERO);
>
> devm_kcalloc() zeroes on its own
>
>> + if (!pinfo->pins) {
>> + ret = -ENOMEM;
>> + goto free;
>> + }
>> +
>> + pinfo->groups = devm_kmalloc_array(handle->dev, pinfo->nr_groups,
>> + sizeof(*pinfo->groups),
>> + GFP_KERNEL | __GFP_ZERO);
>
> Ditto.
>> + if (!pinfo->groups) {
>> + ret = -ENOMEM;
>> + goto free;
>> + }
>> +
>> + pinfo->functions = devm_kmalloc_array(handle->dev, pinfo->nr_functions,
>> + sizeof(*pinfo->functions),
>> + GFP_KERNEL | __GFP_ZERO);
>> + if (!pinfo->functions) {
>> + ret = -ENOMEM;
>> + goto free;
>> + }
>> +
>> + pinfo->version = version;
>> + handle->pinctrl_ops = &pinctrl_ops;
>> + handle->pinctrl_priv = pinfo;
>> +
>> + return 0;
>> +free:
>> + if (pinfo) {
>> + devm_kfree(handle->dev, pinfo->pins);
>> + devm_kfree(handle->dev, pinfo->functions);
>> + devm_kfree(handle->dev, pinfo->groups);
>> + }
>
> These frees are really not needed...if this function return failure any
> devres allocated in it is freed by the SCMI core. (as said above...in a
> recent kernel with the new API of course)
>
>> +
>> + devm_kfree(handle->dev, pinfo);
>> +
>> + return ret;
>> +}
>> +
>> +DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_PINCTRL, pinctrl)
>> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
>> index 815095326e2d..68add4d06e8c 100644
>> --- a/drivers/pinctrl/Kconfig
>> +++ b/drivers/pinctrl/Kconfig
>> @@ -431,4 +431,13 @@ config PINCTRL_EQUILIBRIUM
>> pin functions, configure GPIO attributes for LGM SoC pins. Pinmux and
>> pinconf settings are retrieved from device tree.
>>
>> +config PINCTRL_SCMI
>> + bool "Pinctrl driver controlled via SCMI interface"
>> + depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
>> + help
>> + This driver provides support for pinctrl which is controlled
>> + by firmware that implements the SCMI interface.
>> + It uses SCMI Message Protocol to interact with the
>> + firmware providing all the pinctrl controls.
>> +
>
> This does NOT belong to this patch, but to the next right ?
>
>> endif
>> diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
>> index 9cd312a1ff92..6a909ef3bf51 100644
>> --- a/include/linux/scmi_protocol.h
>> +++ b/include/linux/scmi_protocol.h
>> @@ -12,7 +12,8 @@
>> #include <linux/notifier.h>
>> #include <linux/types.h>
>>
>> -#define SCMI_MAX_STR_SIZE 16
>> +#define SCMI_MAX_STR_SIZE 16
>> +#define SCMI_MAX_STR_EXT_SIZE 64
>
> This is handled as part of how the extended names are handled with ph->hops
> in a common way, as I was saying, so please move this if you need it in
> the protocol code, then I'll port to the ph->hops interface and clean
> up.
>
>> #define SCMI_MAX_NUM_RATES 16
>>
>> /**
>> @@ -252,6 +253,55 @@ struct scmi_notify_ops {
>> struct notifier_block *nb);
>> };
>>
>> +/**
>> + * struct scmi_pinctrl_ops - represents the various operations provided
>> + * by SCMI Pinctrl Protocol
>> + *
>> + * @get_groups_count: returns count of the registered groups
>> + * @get_group_name: returns group name by index
>> + * @get_group_pins: returns the set of pins, assigned to the specified group
>> + * @get_functions_count: returns count of the registered fucntions
>> + * @get_function_name: returns function name by indes
>> + * @get_function_groups: returns the set of groups, assigned to the specified
>> + * function
>> + * @set_mux: set muxing function for groups of pins
>> + * @get_pins: returns the set of pins, registered in driver
>> + * @get_config: returns configuration parameter for pin
>> + * @set_config: sets the configuration parameter for pin
>> + * @get_config_group: returns the configuration parameter for a group of pins
>> + * @set_config_group: sets the configuration parameter for a groups of pins
>> + * @request_pin: aquire pin before selecting mux setting
>> + * @free_pin: frees pin, acquired by request_pin call
>> + */
>> +struct scmi_pinctrl_ops {
>> + int (*get_groups_count)(const struct scmi_handle *handle);
>> + int (*get_group_name)(const struct scmi_handle *handles, u32 selector,
>> + const char **name);
>> + int (*get_group_pins)(const struct scmi_handle *handle, u32 selector,
>> + const unsigned int **pins, unsigned int *nr_pins);
>> + int (*get_functions_count)(const struct scmi_handle *handle);
>> + int (*get_function_name)(const struct scmi_handle *handle, u32 selector,
>> + const char **name);
>> + int (*get_function_groups)(const struct scmi_handle *handle,
>> + u32 selector, unsigned int *nr_groups,
>> + const unsigned int **groups);
>> + int (*set_mux)(const struct scmi_handle *handle, u32 selector,
>> + u32 group);
>> + int (*get_pin_name)(const struct scmi_handle *handle, u32 selector,
>> + const char **name);
>> + int (*get_pins_count)(const struct scmi_handle *handle);
>> + int (*get_config)(const struct scmi_handle *handle, u32 pin,
>> + u32 *config);
>> + int (*set_config)(const struct scmi_handle *handle, u32 pin,
>> + u32 config);
>> + int (*get_config_group)(const struct scmi_handle *handle, u32 pin,
>> + u32 *config);
>> + int (*set_config_group)(const struct scmi_handle *handle, u32 pin,
>> + u32 config);
>> + int (*request_pin)(const struct scmi_handle *handle, u32 pin);
>> + int (*free_pin)(const struct scmi_handle *handle, u32 pin);
>> +};
>> +
>
> As mentioned above, here you could drop a lot of this get_X_count/name/pins
> and instead expose a few of the internal proocol struct scmi__X_info and then
> provide just a mean to query how many resource are there and then get the info
> descriptor you want for the specific domain_id, i.e.:
>
> int (*num_domains_get)(ph, type)
> void *(*info_get)(ph, type, domain_id);
>
> Thanks,
> Cristian
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On Wed, Apr 12, 2023 at 11:04:05PM +0100, Cristian Marussi wrote:
> On Fri, Apr 07, 2023 at 10:18:27AM +0000, Oleksii Moisieiev wrote:
> > Implementation of the SCMI client driver, which implements
> > PINCTRL_PROTOCOL. This protocol has ID 19 and is described
> > in the latest DEN0056 document.
>
> Hi,
>
Hi Oleksii,
one more thing that I missed in my previous review down below.
> > This protocol is part of the feature that was designed to
> > separate the pinctrl subsystem from the SCP firmware.
> > The idea is to separate communication of the pin control
> > subsystem with the hardware to SCP firmware
> > (or a similar system, such as ATF), which provides an interface
> > to give the OS ability to control the hardware through SCMI protocol.
> > This is a generic driver that implements SCMI protocol,
> > independent of the platform type.
> >
> > DEN0056 document:
> > https://developer.arm.com/documentation/den0056/latest
> >
>
[snip]
> > +static int scmi_pinctrl_request_config(const struct scmi_handle *handle,
> > + u32 selector,
> > + enum scmi_pinctrl_selector_type type,
> > + u32 *config)
> > +{
> > + struct scmi_xfer *t;
> > + struct scmi_conf_tx *tx;
> > + __le32 *packed_config;
> > + u32 attributes = 0;
> > + int ret;
> > +
> > + if (!handle || !config || type == FUNCTION_TYPE)
> > + return -EINVAL;
> > +
> > + ret = scmi_pinctrl_validate_id(handle, selector, type);
> > + if (ret)
> > + return ret;
> > +
> > + ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_GET,
> > + SCMI_PROTOCOL_PINCTRL,
> > + sizeof(*tx), sizeof(*packed_config), &t);
> > + if (ret)
> > + return ret;
> > +
> > + tx = t->tx.buf;
> > + packed_config = t->rx.buf;
> > + tx->identifier = cpu_to_le32(selector);
> > + attributes = SET_TYPE_BITS(attributes, type);
> > + attributes = SET_CONFIG(attributes, *config);
> > +
Looking at scmi_conf_tx and these pinctrl get/set functions, you do not
seem to consider the ConfigType field in the SCMI related messages, so
basically using always the Default 0 Type, and as a consequence you dont
either expose any way to choose a Different type in the related SCMI
protocol ops; I imagine this is because the pinctrl driver currently using
this protocol, at the end, does not need any of the other available types
(as in Table 23 of the spec).
This is fine for pinctrl driver as it is now, BUT the SCMI pinctrl
protocol implementation in the core SCMI stack and its related
protocol_operations as exposed in scmi_protocol.h should be generic
enough to serve any possible user of the SCMI pinctrl protocol (and there
is already a request to extend/amend the spec somehow to send multiple pin
setup of different types in one go as you may have seen), so I'd say it's
better if you add also a ConfigType param to the get/set_config scmi_pinctrl_ops
and expose the whole ConfigType enums (Table23) in scmi_protocol.h (like we do for
sensor classes on scmi_protocol.h) to address this; the pinctrl driver
can then anyway call such new protocol_ops with a Default type, but at
least the SCMI protocol_ops interface will remain generic and could be
reused iin other scenarios.
This is equally true for all the other protocol messages (should I have
miss something else for now...I'll review again you next V2 anyway).
Thanks,
Cristian
Hi Cristian,
On 20.04.23 20:05, Cristian Marussi wrote:
> On Wed, Apr 12, 2023 at 11:04:05PM +0100, Cristian Marussi wrote:
>> On Fri, Apr 07, 2023 at 10:18:27AM +0000, Oleksii Moisieiev wrote:
>>> Implementation of the SCMI client driver, which implements
>>> PINCTRL_PROTOCOL. This protocol has ID 19 and is described
>>> in the latest DEN0056 document.
>> Hi,
>>
> Hi Oleksii,
>
> one more thing that I missed in my previous review down below.
>
>>> This protocol is part of the feature that was designed to
>>> separate the pinctrl subsystem from the SCP firmware.
>>> The idea is to separate communication of the pin control
>>> subsystem with the hardware to SCP firmware
>>> (or a similar system, such as ATF), which provides an interface
>>> to give the OS ability to control the hardware through SCMI protocol.
>>> This is a generic driver that implements SCMI protocol,
>>> independent of the platform type.
>>>
>>> DEN0056 document:
>>> https://urldefense.com/v3/__https://developer.arm.com/documentation/den0056/latest__;!!GF_29dbcQIUBPA!0kMLQ5c3tKsMfWCqTKHp6eolY3sTZlyKmAD7B7pbiSESABUUoBzmhgrYdDgWGC_g0vgLE4zwrS4ppeTOD8KizP9fIeJkpg$ [developer[.]arm[.]com]
>>>
> [snip]
>
>>> +static int scmi_pinctrl_request_config(const struct scmi_handle *handle,
>>> + u32 selector,
>>> + enum scmi_pinctrl_selector_type type,
>>> + u32 *config)
>>> +{
>>> + struct scmi_xfer *t;
>>> + struct scmi_conf_tx *tx;
>>> + __le32 *packed_config;
>>> + u32 attributes = 0;
>>> + int ret;
>>> +
>>> + if (!handle || !config || type == FUNCTION_TYPE)
>>> + return -EINVAL;
>>> +
>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_GET,
>>> + SCMI_PROTOCOL_PINCTRL,
>>> + sizeof(*tx), sizeof(*packed_config), &t);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + tx = t->tx.buf;
>>> + packed_config = t->rx.buf;
>>> + tx->identifier = cpu_to_le32(selector);
>>> + attributes = SET_TYPE_BITS(attributes, type);
>>> + attributes = SET_CONFIG(attributes, *config);
>>> +
> Looking at scmi_conf_tx and these pinctrl get/set functions, you do not
> seem to consider the ConfigType field in the SCMI related messages, so
> basically using always the Default 0 Type, and as a consequence you dont
> either expose any way to choose a Different type in the related SCMI
> protocol ops; I imagine this is because the pinctrl driver currently using
> this protocol, at the end, does not need any of the other available types
> (as in Table 23 of the spec).
>
I'm not sure I've understood your point. Pinctrl subsystem pass config
in so-called Packed format. So this means that config is both input and
output parameter. Packed format means that u32 config has both config id
and config value packed inside.
So I receive packed config with both id and value on config_set call and
then just send it over SCMI, expecting error from server if config is
invalid. On config_get call I receive config parameter with only id
packed inside, them pass it to the server and receive packed_config with
both id and value, which is ready to be returned to the subsystem.
> This is fine for pinctrl driver as it is now, BUT the SCMI pinctrl
> protocol implementation in the core SCMI stack and its related
> protocol_operations as exposed in scmi_protocol.h should be generic
> enough to serve any possible user of the SCMI pinctrl protocol (and there
> is already a request to extend/amend the spec somehow to send multiple pin
> setup of different types in one go as you may have seen), so I'd say it's
> better if you add also a ConfigType param to the get/set_config scmi_pinctrl_ops
> and expose the whole ConfigType enums (Table23) in scmi_protocol.h (like we do for
> sensor classes on scmi_protocol.h) to address this; the pinctrl driver
> can then anyway call such new protocol_ops with a Default type, but at
> least the SCMI protocol_ops interface will remain generic and could be
> reused iin other scenarios.
>
> This is equally true for all the other protocol messages (should I have
> miss something else for now...I'll review again you next V2 anyway).
>
> Thanks,
> Cristian
>
On Thu, Apr 20, 2023 at 05:23:05PM +0000, Oleksii Moisieiev wrote:
> Hi Cristian,
>
Hi,
> On 20.04.23 20:05, Cristian Marussi wrote:
> > On Wed, Apr 12, 2023 at 11:04:05PM +0100, Cristian Marussi wrote:
> >> On Fri, Apr 07, 2023 at 10:18:27AM +0000, Oleksii Moisieiev wrote:
> >>> Implementation of the SCMI client driver, which implements
> >>> PINCTRL_PROTOCOL. This protocol has ID 19 and is described
> >>> in the latest DEN0056 document.
> >> Hi,
> >>
> > Hi Oleksii,
> >
> > one more thing that I missed in my previous review down below.
> >
> >>> This protocol is part of the feature that was designed to
> >>> separate the pinctrl subsystem from the SCP firmware.
> >>> The idea is to separate communication of the pin control
> >>> subsystem with the hardware to SCP firmware
> >>> (or a similar system, such as ATF), which provides an interface
> >>> to give the OS ability to control the hardware through SCMI protocol.
> >>> This is a generic driver that implements SCMI protocol,
> >>> independent of the platform type.
> >>>
> >>> DEN0056 document:
> >>> https://urldefense.com/v3/__https://developer.arm.com/documentation/den0056/latest__;!!GF_29dbcQIUBPA!0kMLQ5c3tKsMfWCqTKHp6eolY3sTZlyKmAD7B7pbiSESABUUoBzmhgrYdDgWGC_g0vgLE4zwrS4ppeTOD8KizP9fIeJkpg$ [developer[.]arm[.]com]
> >>>
> > [snip]
> >
> >>> +static int scmi_pinctrl_request_config(const struct scmi_handle *handle,
> >>> + u32 selector,
> >>> + enum scmi_pinctrl_selector_type type,
> >>> + u32 *config)
> >>> +{
> >>> + struct scmi_xfer *t;
> >>> + struct scmi_conf_tx *tx;
> >>> + __le32 *packed_config;
> >>> + u32 attributes = 0;
> >>> + int ret;
> >>> +
> >>> + if (!handle || !config || type == FUNCTION_TYPE)
> >>> + return -EINVAL;
> >>> +
> >>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
> >>> + if (ret)
> >>> + return ret;
> >>> +
> >>> + ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_GET,
> >>> + SCMI_PROTOCOL_PINCTRL,
> >>> + sizeof(*tx), sizeof(*packed_config), &t);
> >>> + if (ret)
> >>> + return ret;
> >>> +
> >>> + tx = t->tx.buf;
> >>> + packed_config = t->rx.buf;
> >>> + tx->identifier = cpu_to_le32(selector);
> >>> + attributes = SET_TYPE_BITS(attributes, type);
> >>> + attributes = SET_CONFIG(attributes, *config);
> >>> +
> > Looking at scmi_conf_tx and these pinctrl get/set functions, you do not
> > seem to consider the ConfigType field in the SCMI related messages, so
> > basically using always the Default 0 Type, and as a consequence you dont
> > either expose any way to choose a Different type in the related SCMI
> > protocol ops; I imagine this is because the pinctrl driver currently using
> > this protocol, at the end, does not need any of the other available types
> > (as in Table 23 of the spec).
> >
> I'm not sure I've understood your point. Pinctrl subsystem pass config
> in so-called Packed format. So this means that config is both input and
> output parameter. Packed format means that u32 config has both config id
> and config value packed inside.
>
Sorry I was meant to make the above comment on the PINCTRL_SET path but
I messed up and commented on the GET path....my bad.
Anyway even considering the packed format and looking at the PINCTRL_SET
function scmi_pinctrl_apply_config I dont understand how this works.
> So I receive packed config with both id and value on config_set call and
> then just send it over SCMI, expecting error from server if config is
This is where I dont understand: you receive a packed 32bit config from
pinctrl subsystem via ops->set_config together with the pin_id and in turn
this calls down into this apply_config where you build the packet:
> + ret = scmi_pinctrl_validate_id(handle, selector, type);
> + if (ret)
> + return ret;
> +
> + ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_SET,
> + SCMI_PROTOCOL_PINCTRL,
> + sizeof(*tx), 0, &t);
> + if (ret)
> + return ret;
> +
> + tx = t->tx.buf;
> + tx->identifier = cpu_to_le32(selector);
you set the pin number
> + attributes = SET_TYPE_BITS(attributes, type);
then set the Selector type as PIN_TYPE in the attributes bits 9:8
> + attributes = SET_CONFIG(attributes, config);
And here you set the PackedFormat received from pinctrl into the
bits 7:0 of attributes...(...BUT you then assign back to attributes
which is 32 bit so I think this is another bug because this way you
clear any bit just set above with SET_TYPE_BITS...but this is not the
point now...lets ignore this... ad you should anyway use bitfield.h in V2)
...so this attributes now, as you explained me, include both the selector
type (PIN vs GROUP) in bits 9:8 (bugs apart) and then, as a packed format
the ConfigType and the Value to set, both packed into the bits 7:0
> + tx->attributes = cpu_to_le32(attributes);
> +
You convert to proper endianity and
> + ret = scmi_do_xfer(handle, t);
> +
send straight to the SCMI server as a payload for PINCTRL_SET....so you
send basically the pin identifier in this case (u32) AND the u32 with
the attributes which includes the Selector PIN vs GROUP and the
ConfigType ..this last as a packed thing including also the value...
(so this could contain....looking at Table23 as an example:
Bias-pull-up (4) + a value in Ohms)
....BUT from the spec v3.2-BETA regarding PINCTRL_SET (4.11.2.7), you are
supposed to send one more u32 field "config_value" as the payload of
PINCTRL_SET (page 172) wth such field beaing the effectively carried
value to set....field which is instead now NOT considered at all and
so just sent as zero all of the time ... am I missing something ?
...so my understanding is that, being the expected format by the spec as
in 4.11.2.7, you should instead pick the PackedFormat your received as a 32bit
config, UNPACK it and split it into the attributes and config_value field....
from the v3.2 BETA spec I have I cannot see where the 32 bit attributes
payload is supposed to carry straight away the packedformat value...
...as it is implemented now, it is out of spec (at least the latest
v3.2-BETA public that I can see)... and if it works for you, it just means
your backend server is equally out-of-spec....which is probably a way of being
compliant BUT not the way we need to here :P
I cannot see any other way I can interpret this to make it
work...apologies if I am missing something, in such a case please explain...
Thanks,
Cristian
Hi Cristian,
On 20.04.23 21:47, Cristian Marussi wrote:
> On Thu, Apr 20, 2023 at 05:23:05PM +0000, Oleksii Moisieiev wrote:
>> Hi Cristian,
>>
> Hi,
>
>> On 20.04.23 20:05, Cristian Marussi wrote:
>>> On Wed, Apr 12, 2023 at 11:04:05PM +0100, Cristian Marussi wrote:
>>>> On Fri, Apr 07, 2023 at 10:18:27AM +0000, Oleksii Moisieiev wrote:
>>>>> Implementation of the SCMI client driver, which implements
>>>>> PINCTRL_PROTOCOL. This protocol has ID 19 and is described
>>>>> in the latest DEN0056 document.
>>>> Hi,
>>>>
>>> Hi Oleksii,
>>>
>>> one more thing that I missed in my previous review down below.
>>>
>>>>> This protocol is part of the feature that was designed to
>>>>> separate the pinctrl subsystem from the SCP firmware.
>>>>> The idea is to separate communication of the pin control
>>>>> subsystem with the hardware to SCP firmware
>>>>> (or a similar system, such as ATF), which provides an interface
>>>>> to give the OS ability to control the hardware through SCMI protocol.
>>>>> This is a generic driver that implements SCMI protocol,
>>>>> independent of the platform type.
>>>>>
>>>>> DEN0056 document:
>>>>> https://urldefense.com/v3/__https://developer.arm.com/documentation/den0056/latest__;!!GF_29dbcQIUBPA!0kMLQ5c3tKsMfWCqTKHp6eolY3sTZlyKmAD7B7pbiSESABUUoBzmhgrYdDgWGC_g0vgLE4zwrS4ppeTOD8KizP9fIeJkpg$ [developer[.]arm[.]com]
>>>>>
>>> [snip]
>>>
>>>>> +static int scmi_pinctrl_request_config(const struct scmi_handle *handle,
>>>>> + u32 selector,
>>>>> + enum scmi_pinctrl_selector_type type,
>>>>> + u32 *config)
>>>>> +{
>>>>> + struct scmi_xfer *t;
>>>>> + struct scmi_conf_tx *tx;
>>>>> + __le32 *packed_config;
>>>>> + u32 attributes = 0;
>>>>> + int ret;
>>>>> +
>>>>> + if (!handle || !config || type == FUNCTION_TYPE)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_GET,
>>>>> + SCMI_PROTOCOL_PINCTRL,
>>>>> + sizeof(*tx), sizeof(*packed_config), &t);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + tx = t->tx.buf;
>>>>> + packed_config = t->rx.buf;
>>>>> + tx->identifier = cpu_to_le32(selector);
>>>>> + attributes = SET_TYPE_BITS(attributes, type);
>>>>> + attributes = SET_CONFIG(attributes, *config);
>>>>> +
>>> Looking at scmi_conf_tx and these pinctrl get/set functions, you do not
>>> seem to consider the ConfigType field in the SCMI related messages, so
>>> basically using always the Default 0 Type, and as a consequence you dont
>>> either expose any way to choose a Different type in the related SCMI
>>> protocol ops; I imagine this is because the pinctrl driver currently using
>>> this protocol, at the end, does not need any of the other available types
>>> (as in Table 23 of the spec).
>>>
>> I'm not sure I've understood your point. Pinctrl subsystem pass config
>> in so-called Packed format. So this means that config is both input and
>> output parameter. Packed format means that u32 config has both config id
>> and config value packed inside.
>>
> Sorry I was meant to make the above comment on the PINCTRL_SET path but
> I messed up and commented on the GET path....my bad.
>
> Anyway even considering the packed format and looking at the PINCTRL_SET
> function scmi_pinctrl_apply_config I dont understand how this works.
>
>> So I receive packed config with both id and value on config_set call and
>> then just send it over SCMI, expecting error from server if config is
> This is where I dont understand: you receive a packed 32bit config from
> pinctrl subsystem via ops->set_config together with the pin_id and in turn
> this calls down into this apply_config where you build the packet:
>
>
>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>> + if (ret)
>> + return ret;
>> +
>> + ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_SET,
>> + SCMI_PROTOCOL_PINCTRL,
>> + sizeof(*tx), 0, &t);
>> + if (ret)
>> + return ret;
>> +
>> + tx = t->tx.buf;
>> + tx->identifier = cpu_to_le32(selector);
> you set the pin number
>
>> + attributes = SET_TYPE_BITS(attributes, type);
> then set the Selector type as PIN_TYPE in the attributes bits 9:8
>
>
>> + attributes = SET_CONFIG(attributes, config);
> And here you set the PackedFormat received from pinctrl into the
> bits 7:0 of attributes...(...BUT you then assign back to attributes
> which is 32 bit so I think this is another bug because this way you
> clear any bit just set above with SET_TYPE_BITS...but this is not the
> point now...lets ignore this... ad you should anyway use bitfield.h in V2)
>
> ...so this attributes now, as you explained me, include both the selector
> type (PIN vs GROUP) in bits 9:8 (bugs apart) and then, as a packed format
> the ConfigType and the Value to set, both packed into the bits 7:0
>
>> + tx->attributes = cpu_to_le32(attributes);
>> +
> You convert to proper endianity and
>
>> + ret = scmi_do_xfer(handle, t);
>> +
> send straight to the SCMI server as a payload for PINCTRL_SET....so you
> send basically the pin identifier in this case (u32) AND the u32 with
> the attributes which includes the Selector PIN vs GROUP and the
> ConfigType ..this last as a packed thing including also the value...
> (so this could contain....looking at Table23 as an example:
> Bias-pull-up (4) + a value in Ohms)
>
> ....BUT from the spec v3.2-BETA regarding PINCTRL_SET (4.11.2.7), you are
> supposed to send one more u32 field "config_value" as the payload of
> PINCTRL_SET (page 172) wth such field beaing the effectively carried
> value to set....field which is instead now NOT considered at all and
> so just sent as zero all of the time ... am I missing something ?
>
> ...so my understanding is that, being the expected format by the spec as
> in 4.11.2.7, you should instead pick the PackedFormat your received as a 32bit
> config, UNPACK it and split it into the attributes and config_value field....
>
> from the v3.2 BETA spec I have I cannot see where the 32 bit attributes
> payload is supposed to carry straight away the packedformat value...
>
> ...as it is implemented now, it is out of spec (at least the latest
> v3.2-BETA public that I can see)... and if it works for you, it just means
> your backend server is equally out-of-spec....which is probably a way of being
> compliant BUT not the way we need to here :P
>
> I cannot see any other way I can interpret this to make it
> work...apologies if I am missing something, in such a case please explain...
>
> Thanks,
> Cristian
Thank you very much for the tip. This is definitely an issue that should
be addressed. I'll fix that and provide with v2 which I'm currently prepare.
Hi Peng Fan,
On 17.04.23 05:55, Peng Fan wrote:
>
>
> On 4/13/2023 6:04 AM, Cristian Marussi wrote:
>> On Fri, Apr 07, 2023 at 10:18:27AM +0000, Oleksii Moisieiev wrote:
>>> Implementation of the SCMI client driver, which implements
>>> PINCTRL_PROTOCOL. This protocol has ID 19 and is described
>>> in the latest DEN0056 document.
>>
>> Hi,
>>
>>> This protocol is part of the feature that was designed to
>>> separate the pinctrl subsystem from the SCP firmware.
>>> The idea is to separate communication of the pin control
>>> subsystem with the hardware to SCP firmware
>>> (or a similar system, such as ATF), which provides an interface
>>> to give the OS ability to control the hardware through SCMI protocol.
>>> This is a generic driver that implements SCMI protocol,
>>> independent of the platform type.
>>>
>>> DEN0056 document:
>>> https://urldefense.com/v3/__https://developer.arm.com/documentation/den0056/latest__;!!GF_29dbcQIUBPA!y2hR3PEGGxiPjVeXBcgGyV03DPDhzgUKR0uHvsTpiafKgBar8Egc6oOOs-IkFIquhSf-qBzltqEMyzRZHq8eC4g$
>>> [developer[.]arm[.]com]
>>>
>>
>> No need to specify all of this in the commit message, just a note that
>> you are adding a new SCMIv3.2 Pincontrol protocol, highlighting anything
>> that has been left out in this patch (if any) will be enough.
>
> Is it possible to extend the spec to support multilple uint32_t for PIN
> CONFIG SET?
>
> With only one uint32_t could not satisfy i.MX requirement.
>
> Thanks,
> Peng.
>
IIUC you are expecting to have an ability to set some kind of array of
uint32_t config values to some specific ConfigType?
I'm not sure if it's supported by pintctrl subsystem right now. I was
unable to find an example in the existing device-tree pinctrl bindings.
This makes me think that this kind of binding is OEM specific.
Maybe it can be implemented by adding new IDs to OEM specific range
(192-255) which is reserved for OEM specific units (See Table 23 of
DEN0056E).
Best regards,
Oleksii
>> You can look at the very first commit logs of existing protos as an
>> example like: drivers/firmware/arm_scmi/powercap.c
>>
>> Some more comments down below, I'll mostly skip anything related to the
>> SCMI API change I mentioned before...
>>
>> I'll also wont comment on more trivial stuff related to style, BUT there
>> are lots of them: you should run
>>
>> ./scripts/checkpatch.pl --strict <your-git-format-patch-file>
>>
>> for each patch in the series. (and fix accordingly..spacing,
>> brackets...etc)
>>
>>> Signed-off-by: Oleksii Moisieiev <[email protected]>
>>> ---
>>> MAINTAINERS | 6 +
>>> drivers/firmware/arm_scmi/Makefile | 2 +-
>>> drivers/firmware/arm_scmi/common.h | 1 +
>>> drivers/firmware/arm_scmi/driver.c | 3 +
>>> drivers/firmware/arm_scmi/pinctrl.c | 905
>>> ++++++++++++++++++++++++++++
>>> drivers/pinctrl/Kconfig | 9 +
>>> include/linux/scmi_protocol.h | 58 +-
>>> 7 files changed, 982 insertions(+), 2 deletions(-)
>>> create mode 100644 drivers/firmware/arm_scmi/pinctrl.c
>>>
>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>> index 281de213ef47..abc543fd7544 100644
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -16961,6 +16961,12 @@ F: drivers/reset/reset-scmi.c
>>> F: include/linux/sc[mp]i_protocol.h
>>> F: include/trace/events/scmi.h
>>> +PINCTRL DRIVER FOR SYSTEM CONTROL & POWER/MANAGEMENT INTERFACE
>>> (SCPI/SCMI)
>>> +M: Oleksii Moisieiev <[email protected]>
>>> +L: [email protected]
>>> +S: Maintained
>>> +F: drivers/firmware/arm_scmi/pinctrl.c
>>> +
>>> SYSTEM RESET/SHUTDOWN DRIVERS
>>> M: Sebastian Reichel <[email protected]>
>>> L: [email protected]
>>> diff --git a/drivers/firmware/arm_scmi/Makefile
>>> b/drivers/firmware/arm_scmi/Makefile
>>> index bc0d54f8e861..5cec357fbfa8 100644
>>> --- a/drivers/firmware/arm_scmi/Makefile
>>> +++ b/drivers/firmware/arm_scmi/Makefile
>>> @@ -4,7 +4,7 @@ scmi-driver-y = driver.o notify.o
>>> scmi-transport-y = shmem.o
>>> scmi-transport-$(CONFIG_MAILBOX) += mailbox.o
>>> scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o
>>> -scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o
>>> system.o
>>> +scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o
>>> system.o pinctrl.o
>>> scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y)
>>> $(scmi-protocols-y) \
>>> $(scmi-transport-y)
>>> obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
>>> diff --git a/drivers/firmware/arm_scmi/common.h
>>> b/drivers/firmware/arm_scmi/common.h
>>> index 65063fa948d4..8bbb404abe8d 100644
>>> --- a/drivers/firmware/arm_scmi/common.h
>>> +++ b/drivers/firmware/arm_scmi/common.h
>>> @@ -170,6 +170,7 @@ DECLARE_SCMI_REGISTER_UNREGISTER(power);
>>> DECLARE_SCMI_REGISTER_UNREGISTER(reset);
>>> DECLARE_SCMI_REGISTER_UNREGISTER(sensors);
>>> DECLARE_SCMI_REGISTER_UNREGISTER(system);
>>> +DECLARE_SCMI_REGISTER_UNREGISTER(pinctrl);
>>> #define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(id, name) \
>>> int __init scmi_##name##_register(void) \
>>> diff --git a/drivers/firmware/arm_scmi/driver.c
>>> b/drivers/firmware/arm_scmi/driver.c
>>> index 3dfd8b6a0ebf..fb9525fb3c24 100644
>>> --- a/drivers/firmware/arm_scmi/driver.c
>>> +++ b/drivers/firmware/arm_scmi/driver.c
>>> @@ -743,6 +743,7 @@ static struct scmi_prot_devnames devnames[] = {
>>> { SCMI_PROTOCOL_CLOCK, { "clocks" },},
>>> { SCMI_PROTOCOL_SENSOR, { "hwmon" },},
>>> { SCMI_PROTOCOL_RESET, { "reset" },},
>>> + { SCMI_PROTOCOL_PINCTRL, { "pinctrl" },},
>>> };
>>> static inline void
>>> @@ -947,6 +948,7 @@ static int __init scmi_driver_init(void)
>>> scmi_reset_register();
>>> scmi_sensors_register();
>>> scmi_system_register();
>>> + scmi_pinctrl_register();
>>> return platform_driver_register(&scmi_driver);
>>> }
>>> @@ -962,6 +964,7 @@ static void __exit scmi_driver_exit(void)
>>> scmi_reset_unregister();
>>> scmi_sensors_unregister();
>>> scmi_system_unregister();
>>> + scmi_pinctrl_unregister();
>>> platform_driver_unregister(&scmi_driver);
>>> }
>>> diff --git a/drivers/firmware/arm_scmi/pinctrl.c
>>> b/drivers/firmware/arm_scmi/pinctrl.c
>>> new file mode 100644
>>> index 000000000000..037270d7f39b
>>> --- /dev/null
>>> +++ b/drivers/firmware/arm_scmi/pinctrl.c
>>> @@ -0,0 +1,905 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +/*
>>> + * System Control and Management Interface (SCMI) Pinctrl Protocol
>>> + *
>>> + * Copyright (C) 2021 EPAM.
>>
>> nitpick: update (C) years
>>
>>> + */
>>> +
>>> +#define pr_fmt(fmt) "SCMI Notifications PINCTRL - " fmt
>>> +
>>
>> This is not needed, no notifs in this proto.
>>
>>> +#include <linux/scmi_protocol.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#include "common.h"
>>> +#include "notify.h"
>>
>> Notifs not needed, and in the new API world you'll just need a:
>>
>> #include "protocols.h"
>>
>>> +
>>> +#define SET_TYPE(x) ((x) & 0x3)
>>> +
>>
>> Even if trivial better to use std bitfield.h macros like
>> FIELD_GET() BIT() ... etc
>>
>>> +enum scmi_pinctrl_protocol_cmd {
>>> + PINCTRL_ATTRIBUTES = 0x3,
>>> + PINCTRL_LIST_ASSOCIATIONS = 0x4,
>>> + PINCTRL_CONFIG_GET = 0x5,
>>> + PINCTRL_CONFIG_SET = 0x6,
>>> + PINCTRL_FUNCTION_SELECT = 0x7,
>>> + PINCTRL_REQUEST = 0x8,
>>> + PINCTRL_RELEASE = 0x9,
>>> + PINCTRL_NAME_GET = 0xa,
>>> + PINCTRL_SET_PERMISSIONS = 0xb
>>> +};
>>> +
>>> +enum scmi_pinctrl_selector_type {
>>> + PIN_TYPE = 0,
>>> + GROUP_TYPE,
>>> + FUNCTION_TYPE
>>> +};
>>> +
>>> +struct scmi_group_info {
>>> + bool present;
>>> + char *name;
>>> + unsigned int *group_pins;
>>> + unsigned int nr_pins;
>>> +};
>>> +
>>> +struct scmi_function_info {
>>> + bool present;
>>> + char *name;
>>> + unsigned int *groups;
>>> + unsigned int nr_groups;
>>> +};
>>> +
>>> +struct scmi_pin_info {
>>> + bool present;
>>> + char *name;
>>> +};
>>> +
>>> +struct scmi_pinctrl_info {
>>> + u32 version;
>>> + u16 nr_groups;
>>> + u16 nr_functions;
>>> + u16 nr_pins;
>>
>> Since these vars are not related to stricly spaced message fields
>> (even though
>> derived from such messages) do not use sized types, you can just
>> stick with
>> unsigned int. (it is also better not to mix sized and unsized types
>> in the same
>> struct). This also could come handy if these will be exposed to the user
>> in scmi_protocol.h in the future (more on this down below)
>>
>>> + struct scmi_group_info *groups;
>>> + struct scmi_function_info *functions;
>>> + struct scmi_pin_info *pins;
>>> +};
>>> +
>>> +struct scmi_conf_tx {
>>> + __le32 identifier;
>>> +#define SET_TYPE_BITS(attr, x) (((attr) & 0xFFFFFCFF) | (x & 0x3)
>>> << 8)
>>> +#define SET_CONFIG(attr, x) (((attr) & 0xFF) | (x & 0xFF))
>>
>> Use bitfield.h like FIELD_SET / GENMASK etc
>>
>>> + __le32 attributes;
>>> +};
>>> +
>>> +static int scmi_pinctrl_attributes_get(const struct scmi_handle
>>> *handle,
>>> + struct scmi_pinctrl_info *pi)
>>> +{
>>> + int ret;
>>> + struct scmi_xfer *t;
>>> + struct scmi_msg_pinctrl_protocol_attributes {
>>> +#define GROUPS_NR(x) ((x) >> 16)
>>> +#define PINS_NR(x) ((x) & 0xffff)
>>> + __le32 attributes_low;
>>> +#define FUNCTIONS_NR(x) ((x) & 0xffff)
>>> + __le32 attributes_high;
>>> + } *attr;
>>
>> For consistency with the rest of the stack (mostly :D), please move
>> this struct
>> definition and related macros outside in the global scope right after
>> command
>> enum. (and use bitfield macros ...)
>>
>>> +
>>> + if (!pi)
>>> + return -EINVAL;
>>> +
>>> + ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
>>> + SCMI_PROTOCOL_PINCTRL, 0, sizeof(*attr), &t);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + attr = t->rx.buf;
>>> +
>>> + ret = scmi_do_xfer(handle, t);
>>> + if (!ret) {
>>> + pi->nr_functions =
>>> + le16_to_cpu(FUNCTIONS_NR(attr->attributes_high));
>>> + pi->nr_groups = le16_to_cpu(GROUPS_NR(attr->attributes_low));
>>> + pi->nr_pins = le16_to_cpu(PINS_NR(attr->attributes_low));
>>> + }
>>> +
>>> + scmi_xfer_put(handle, t);
>>> + return ret;
>>> +}
>>> +
>>> +static int scmi_pinctrl_get_groups_count(const struct scmi_handle
>>> *handle)
>>> +{
>>> + struct scmi_pinctrl_info *pi;
>>> +
>>> + if (!handle || !handle->pinctrl_priv)
>>> + return -ENODEV;
>>> +
>>> + pi = handle->pinctrl_priv;
>>> +
>>> + return pi->nr_groups;
>>> +}
>>> +
>>> +static int scmi_pinctrl_get_pins_count(const struct scmi_handle
>>> *handle)
>>> +{
>>> + struct scmi_pinctrl_info *pi;
>>> +
>>> + if (!handle || !handle->pinctrl_priv)
>>> + return -ENODEV;
>>> +
>>> + pi = handle->pinctrl_priv;
>>> +
>>> + return pi->nr_pins;
>>> +}
>>> +
>>> +static int scmi_pinctrl_get_functions_count(const struct
>>> scmi_handle *handle)
>>> +{
>>> + struct scmi_pinctrl_info *pi;
>>> +
>>> + if (!handle || !handle->pinctrl_priv)
>>> + return -ENODEV;
>>> +
>>> + pi = handle->pinctrl_priv;
>>> +
>>> + return pi->nr_functions;
>>> +}
>>> +
>>> +static int scmi_pinctrl_validate_id(const struct scmi_handle *handle,
>>> + u32 identifier,
>>> + enum scmi_pinctrl_selector_type type)
>>> +{
>>> + struct scmi_pinctrl_info *pi;
>>> +
>>> + if (!handle || !handle->pinctrl_priv)
>>> + return -ENODEV;
>>> +
>>> + switch (type) {
>>> + case PIN_TYPE:
>>> + pi = handle->pinctrl_priv;
>>> +
>>> + return (identifier < pi->nr_pins) ? 0 : -EINVAL;
>>> + case GROUP_TYPE:
>>> + return (identifier <
>>> + scmi_pinctrl_get_groups_count(handle)) ?
>>> + 0 : -EINVAL;
>>> + case FUNCTION_TYPE:
>>> + return (identifier <
>>> + scmi_pinctrl_get_functions_count(handle)) ?
>>> + 0 : -EINVAL;
>>> + default:
>>> + return -EINVAL;
>>> + }
>>
>> Here I would just pick the right value to compare, break and then
>> compare and exit, something aroundf the lines of:
>>
>> case PIN_TYPE:
>> ...
>> val = pi->nr_pins;
>> break;
>> ...
>> case GROUP_TYPE:
>> val = scmi_pinctrl_get_groups_count());
>> break;
>>
>> ....
>> ....
>> default:
>> return -EINVAL;
>> }
>>
>> if (identifier >= val)
>> return -EINVAL;
>>
>> return 0;
>> }
>>
>> ... it's easier to read. What do you think ?
>>
>>
>>> +}
>>> +
>>> +static int scmi_pinctrl_get_name(const struct scmi_handle *handle,
>>> + u32 identifier,
>>> + enum scmi_pinctrl_selector_type type,
>>> + char **name)
>>> +{
>>
>> As said, there is common helper for this, but it will need some small
>> adaptation in the SCMI core to work here so keep it as it is, and
>> I'll take
>> care of this later, if it sounds fine for you.
>>
>>> + struct scmi_xfer *t;
>>> + int ret = 0;
>>> + struct scmi_name_tx {
>>> + __le32 identifier;
>>> + __le32 flags;
>>> + } *tx;
>>> + struct scmi_name_rx {
>>> + __le32 flags;
>>> + u8 name[64];
>>> + } *rx;
>>> +
>>> + if (!handle || !name)
>>> + return -EINVAL;
>>> +
>>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + ret = scmi_xfer_get_init(handle, PINCTRL_NAME_GET,
>>> + SCMI_PROTOCOL_PINCTRL,
>>> + sizeof(*tx), sizeof(*rx), &t);
>>> +
>>> + tx = t->tx.buf;
>>> + rx = t->rx.buf;
>>> + tx->identifier = identifier;
>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>> +
>>> + ret = scmi_do_xfer(handle, t);
>>> + if (ret)
>>> + goto out;
>>> +
>>> + if (rx->flags) {
>>> + ret = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + *name = kasprintf(GFP_KERNEL, "%s", rx->name);
>>> + if (!*name)
>>> + ret = -ENOMEM;
>>> + out:
>>> + scmi_xfer_put(handle, t);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static int scmi_pinctrl_attributes(const struct scmi_handle *handle,
>>> + enum scmi_pinctrl_selector_type type,
>>> + u32 selector, char **name,
>>> + unsigned int *n_elems)
>>> +{
>>> + int ret = 0;
>>> + struct scmi_xfer *t;
>>> + struct scmi_pinctrl_attributes_tx {
>>> + __le32 identifier;
>>> + __le32 flags;
>>> + } *tx;
>>> + struct scmi_pinctrl_attributes_rx {
>>> +#define EXT_NAME_FLAG(x) ((x) & BIT(31))
>>> +#define NUM_ELEMS(x) ((x) & 0xffff)
>>> + __le32 attributes;
>>> + u8 name[16];
>>> + } *rx;
>>
>> Ditto. Move these defs outside, bitfield.h for macros and try to use the
>> same naming style for message structs as in other protos, i.e.
>>
>> for commands: struct scmi_msg_pinctrl_attributes
>> for replies: struct scmi_resp_pinctrl_attributes
>>
>> (or some variations around that...
>> scmi_msg_cmd_* scmi_msg_resp_*
>>
>> we have not been fully consistent really, so I dont want to be
>> pedantic here, but we never used tx/rx in message context since it is
>> already (mis)-used in SCMI channel context...)
>>
>>> +
>>> + if (!handle || !name)
>>> + return -EINVAL;
>>> +
>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + ret = scmi_xfer_get_init(handle, PINCTRL_ATTRIBUTES,
>>> + SCMI_PROTOCOL_PINCTRL, sizeof(*tx), sizeof(*rx), &t);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + tx = t->tx.buf;
>>> + rx = t->rx.buf;
>>> + tx->identifier = selector;
>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>> +
>>> + ret = scmi_do_xfer(handle, t);
>>> + if (ret)
>>> + goto out;
>>> +
>>> + *n_elems = NUM_ELEMS(rx->attributes);
>>> +
>>> + if (!EXT_NAME_FLAG(rx->attributes)) {
>>> + *name = kasprintf(GFP_KERNEL, "%s", rx->name);
>>> + if (!*name)
>>> + ret = -ENOMEM;
>>> + } else
>>> + ret = scmi_pinctrl_get_name(handle, selector, type, name);
>>> + out:
>>> + scmi_xfer_put(handle, t);
>>> + return ret;
>>> +}
>>> +
>>> +static int scmi_pinctrl_list_associations(const struct scmi_handle
>>> *handle,
>>> + u32 selector,
>>> + enum scmi_pinctrl_selector_type type,
>>> + uint16_t size, unsigned int *array)
>>> +{
>>
>> This is the other functionalities you could implement straight away
>> using
>> ph->hops helpers (iterators) but just leave it this way, and I'll
>> port it later
>> (once we retested all of this as working with the new API but without
>> any
>> ph->hops usage..I think it is safer to change one bit at time... :P)
>>
>>> + struct scmi_xfer *t;
>>> + struct scmi_pinctrl_list_assoc_tx {
>>> + __le32 identifier;
>>> + __le32 flags;
>>> + __le32 index;
>>> + } *tx;
>>> + struct scmi_pinctrl_list_assoc_rx {
>>> +#define RETURNED(x) ((x) & 0xFFF)
>>> +#define REMAINING(x) ((x) >> 16)
>>> + __le32 flags;
>>> + __le16 array[];
>>> + } *rx;
>>
>> Ditto, about struct naming and macros.
>>
>>> + u16 tot_num_ret = 0, loop_num_ret;
>>> + u16 remaining_num_ret;
>>> + int ret, loop;
>>> +
>>> + if (!handle || !array || !size)
>>> + return -EINVAL;
>>> +
>>> + if (type == PIN_TYPE)
>>> + return -EINVAL;
>>> +
>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + ret = scmi_xfer_get_init(handle, PINCTRL_LIST_ASSOCIATIONS,
>>> + SCMI_PROTOCOL_PINCTRL, sizeof(*tx),
>>> + 0, &t);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + tx = t->tx.buf;
>>> + rx = t->rx.buf;
>>> +
>>> + do {
>>> + tx->identifier = cpu_to_le32(selector);
>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>> + tx->index = cpu_to_le32(tot_num_ret);
>>> +
>>> + ret = scmi_do_xfer(handle, t);
>>> + if (ret)
>>> + break;
>>> +
>>> + loop_num_ret = le32_to_cpu(RETURNED(rx->flags));
>>> + remaining_num_ret = le32_to_cpu(REMAINING(rx->flags));
>>> +
>>> + for (loop = 0; loop < loop_num_ret; loop++) {
>>> + if (tot_num_ret + loop >= size) {
>>> + ret = -EMSGSIZE;
>>> + goto out;
>>> + }
>>> +
>>> + array[tot_num_ret + loop] =
>>> + le16_to_cpu(rx->array[loop]);
>>> + }
>>> +
>>> + tot_num_ret += loop_num_ret;
>>> +
>>> + scmi_reset_rx_to_maxsz(handle, t);
>>> + } while (remaining_num_ret > 0);
>>> +out:
>>> + scmi_xfer_put(handle, t);
>>> + return ret;
>>> +}
>>> +
>>> +static int scmi_pinctrl_request_config(const struct scmi_handle
>>> *handle,
>>> + u32 selector,
>>> + enum scmi_pinctrl_selector_type type,
>>> + u32 *config)
>>> +{
>>> + struct scmi_xfer *t;
>>> + struct scmi_conf_tx *tx;
>>> + __le32 *packed_config;
>>> + u32 attributes = 0;
>>> + int ret;
>>> +
>>> + if (!handle || !config || type == FUNCTION_TYPE)
>>> + return -EINVAL;
>>> +
>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_GET,
>>> + SCMI_PROTOCOL_PINCTRL,
>>> + sizeof(*tx), sizeof(*packed_config), &t);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + tx = t->tx.buf;
>>> + packed_config = t->rx.buf;
>>> + tx->identifier = cpu_to_le32(selector);
>>> + attributes = SET_TYPE_BITS(attributes, type);
>>> + attributes = SET_CONFIG(attributes, *config);
>>> +
>>> + tx->attributes = cpu_to_le32(attributes);
>>> +
>>> + ret = scmi_do_xfer(handle, t);
>>> +
>>> + if (!ret)
>>> + *config = le32_to_cpu(*packed_config);
>>> +
>>> + scmi_xfer_put(handle, t);
>>> + return ret;
>>> +}
>>> +
>>> +static int scmi_pinctrl_get_config(const struct scmi_handle
>>> *handle, u32 pin,
>>> + u32 *config)
>>> +{
>>> + return scmi_pinctrl_request_config(handle, pin, PIN_TYPE, config);
>>> +}
>>> +
>>> +static int scmi_pinctrl_apply_config(const struct scmi_handle *handle,
>>> + u32 selector,
>>> + enum scmi_pinctrl_selector_type type,
>>> + u32 config)
>>> +{
>>> + struct scmi_xfer *t;
>>> + struct scmi_conf_tx *tx;
>>> + u32 attributes = 0;
>>> + int ret;
>>> +
>>> + if (!handle || type == FUNCTION_TYPE)
>>> + return -EINVAL;
>>> +
>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_SET,
>>> + SCMI_PROTOCOL_PINCTRL,
>>> + sizeof(*tx), 0, &t);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + tx = t->tx.buf;
>>> + tx->identifier = cpu_to_le32(selector);
>>> + attributes = SET_TYPE_BITS(attributes, type);
>>> + attributes = SET_CONFIG(attributes, config);
>>> + tx->attributes = cpu_to_le32(attributes);
>>> +
>>> + ret = scmi_do_xfer(handle, t);
>>> +
>>> + scmi_xfer_put(handle, t);
>>> + return ret;
>>> +}
>>> +
>>> +static int scmi_pinctrl_set_config(const struct scmi_handle
>>> *handle, u32 pin,
>>> + u32 config)
>>> +{
>>> + return scmi_pinctrl_apply_config(handle, pin, PIN_TYPE, config);
>>> +}
>>> +
>>> +static int scmi_pinctrl_get_config_group(const struct scmi_handle
>>> *handle,
>>> + u32 group, u32 *config)
>>> +{
>>> + return scmi_pinctrl_request_config(handle, group, GROUP_TYPE,
>>> config);
>>> +}
>>> +
>>> +static int scmi_pinctrl_set_config_group(const struct scmi_handle
>>> *handle,
>>> + u32 group, u32 config)
>>> +{
>>> + return scmi_pinctrl_apply_config(handle, group, GROUP_TYPE,
>>> config);
>>> +}
>>> +
>>> +static int scmi_pinctrl_function_select(const struct scmi_handle
>>> *handle,
>>> + u32 identifier,
>>> + enum scmi_pinctrl_selector_type type,
>>> + u32 function_id)
>>> +{
>>> + struct scmi_xfer *t;
>>> + struct scmi_func_set_tx {
>>> + __le32 identifier;
>>> + __le32 function_id;
>>> + __le32 flags;
>>> + } *tx;
>>
>> Ditto.
>>
>>> + int ret;
>>> +
>>> + if (!handle || type == FUNCTION_TYPE)
>>> + return -EINVAL;
>>> +
>>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + ret = scmi_xfer_get_init(handle, PINCTRL_FUNCTION_SELECT,
>>> + SCMI_PROTOCOL_PINCTRL,
>>> + sizeof(*tx), 0, &t);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + tx = t->tx.buf;
>>> + tx->identifier = cpu_to_le32(identifier);
>>> + tx->function_id = cpu_to_le32(function_id);
>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>> +
>>> + ret = scmi_do_xfer(handle, t);
>>> + scmi_xfer_put(handle, t);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static int scmi_pinctrl_request(const struct scmi_handle *handle,
>>> + u32 identifier,
>>> + enum scmi_pinctrl_selector_type type)
>>> +{
>>> + struct scmi_xfer *t;
>>> + int ret;
>>> + struct scmi_request_tx {
>>> + __le32 identifier;
>>> + __le32 flags;
>>> + } *tx;
>>> +
>>
>> Ditto.
>>
>>> + if (!handle || type == FUNCTION_TYPE)
>>> + return -EINVAL;
>>> +
>>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + ret = scmi_xfer_get_init(handle, PINCTRL_REQUEST,
>>> SCMI_PROTOCOL_PINCTRL,
>>> + sizeof(*tx), 0, &t);
>>> +
>>> + tx = t->tx.buf;
>>> + tx->identifier = identifier;
>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>> +
>>> + ret = scmi_do_xfer(handle, t);
>>> + scmi_xfer_put(handle, t);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static int scmi_pinctrl_request_pin(const struct scmi_handle
>>> *handle, u32 pin)
>>> +{
>>> + return scmi_pinctrl_request(handle, pin, PIN_TYPE);
>>> +}
>>> +
>>> +static int scmi_pinctrl_free(const struct scmi_handle *handle, u32
>>> identifier,
>>> + enum scmi_pinctrl_selector_type type)
>>> +{
>>> + struct scmi_xfer *t;
>>> + int ret;
>>> + struct scmi_request_tx {
>>> + __le32 identifier;
>>> + __le32 flags;
>>> + } *tx;
>>> +
>> Ditto.
>>
>>> + if (!handle || type == FUNCTION_TYPE)
>>> + return -EINVAL;
>>> +
>>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + ret = scmi_xfer_get_init(handle, PINCTRL_RELEASE,
>>> SCMI_PROTOCOL_PINCTRL,
>>> + sizeof(*tx), 0, &t);
>>> +
>>> + tx = t->tx.buf;
>>> + tx->identifier = identifier;
>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>> +
>>> + ret = scmi_do_xfer(handle, t);
>>> + scmi_xfer_put(handle, t);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static int scmi_pinctrl_free_pin(const struct scmi_handle *handle,
>>> u32 pin)
>>> +{
>>> + return scmi_pinctrl_free(handle, pin, PIN_TYPE);
>>> +}
>>> +
>>> +
>>> +static int scmi_pinctrl_get_group_info(const struct scmi_handle
>>> *handle,
>>> + u32 selector,
>>> + struct scmi_group_info *group)
>>> +{
>>> + int ret = 0;
>>> + struct scmi_pinctrl_info *pi;
>>> +
>>> + if (!handle || !handle->pinctrl_priv || !group)
>>> + return -EINVAL;
>>> +
>>> + pi = handle->pinctrl_priv;
>>> +
>>> + ret = scmi_pinctrl_attributes(handle, GROUP_TYPE, selector,
>>> + &group->name,
>>> + &group->nr_pins);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + if (!group->nr_pins) {
>>> + dev_err(handle->dev, "Group %d has 0 elements", selector);
>>> + return -ENODATA;
>>> + }
>>> +
>>> + group->group_pins = devm_kmalloc_array(handle->dev,
>>> group->nr_pins,
>>> + sizeof(*group->group_pins),
>>> + GFP_KERNEL);
>>
>> I think you can just use for the array allocation
>>
>> devm_kcalloc(dev, n, size, flags)
>>
>> and it will add also __GFP_ZERO internally to clear it.
>> (indeed it calls in turn devm_kmalloc_array)
>>
>> ...BUT I think there is a further tricky issue here related to memory
>> allocation...
>>
>> You call this and others function of this kind from some
>> scmi_pinctrl_ops,
>> like in scmi_pinctrl_get_group_pins (scmi_pinctrl_ops->get_group_pins),
>> and then this is in turn called by the SCMI Pinctrl driver via
>> pinctrl_ops->get_group_pins AND you set a present flag so that you
>> issue a
>> PINCTRL_LIST_ASSOCIATIONS and allocate here a new group_pins array just
>> the first time: but these are never released anywhere, since, even
>> though
>> lazily dynamically allocated when asked for, these are static data that
>> you pass to the caller/user of this protocol and so you cannot release
>> them anytime soon, indeed.
>>
>> The core SCMI stack usually takes care to track and release all the
>> devm_
>> resources allocated by the protocol ONLY if they were allocated with
>> devres
>> while inside scmi_pinctrl_protocol_init() function.
>> (see
>> drivers/firmware/arm-scmi/driver.c:scmi_alloc_init_protocol_instance()
>> and scmi_protocol_release)
>>
>> BUT you do not allocate these arrays inside the protocol-init function,
>> you allocate them the first time these ops are called at runtime.
>>
>> If you unbind/unload all the drivers using this protocol and then reload
>> them, all the devm_ allocations in protocol_init will be freed and
>> reallocated BUT these arrays will never be freed (they are boudn to
>> handle->dev)
>> and instead they will be reallocated multiple times (present flag
>> will be cleared
>> on unload), remained unused and freed finally only when the whole
>> SCMI stack is
>> unbind/unloaded.
>>
>> You use a present flag to avoid reissuing the same query and
>> reallocating all the arrays each time a driver calls these
>> protocol_ops one, but really all these data is available early on at
>> protocol init time and they are not supposed to change at runtime,
>> dont they ?
>>
>> Even in a virtualized environment, you boot an agent and the SCMI
>> platform server provides to the agent the list of associations when
>> queried but then this does not change until the next reboot right ?
>> (indeed you do not query more than once...)
>>
>> The agent can only change the PIN status with CONFIG_SET or
>> FUNCTION_SELECT or REQUEST the exclusive use of a pin/group, but it is
>> not that the platform can change the pin/groups associaion for the same
>> agent at run time, this are static data for the whole life of the agent.
>>
>> Am I right ?
>>
>> IOW I think there is some potential memory leak on unbind/bind and it
>> would
>> be better to query and allocate all of these resources at init time
>> and keep
>> them ready to be retrieved by subsequent operations, since the lifetime
>> of these resources is pretty long and they are basically representing
>> static data that does not change after the init/probe phases.
>>
>> Indeed, all the other protocols usually allocate all the needed
>> resources and query all the available SCMI resources once for all during
>> the protocol_init, storing all the retrieved info in some struct *_info
>> exposed in scmi_protocol.h and then provide some related protocol_ops to
>> get the number of resources and to retrieve specific domain info
>> descriptors.
>> (voltage.c is an example and more on this down below...)
>>
>> This way, any dynamic allocation is done during protocol_init, so
>> it can be automatically freed by the SCMI core once there are no more
>> users of that protocol, and all of this static info data is queried
>> and retrieved once for all at protocol initialization time, avoiding
>> unneeded message exchanges to retrieve always the same data.
>> (which you avoid anyway with the present flag)
>>
>> If you have a good reason to instead perform this sort of lazy
>> allocation/query performed only at the last minute when someone ask for
>> that specific resource, you will have to provide also a
>> .instance_deinit
>> function to clean anything you allocated out of the .instance_init
>> routine; but this would seem strange to me since any resource that is
>> discovered at init will be eventually immediately queried by a driver
>> which uses this protocol...am I missing something ?
>>
>>> + if (!group->group_pins) {
>>> + ret = -ENOMEM;
>>> + goto err;
>>> + }
>>> +
>>> + ret = scmi_pinctrl_list_associations(handle, selector, GROUP_TYPE,
>>> + group->nr_pins, group->group_pins);
>>> + if (ret)
>>> + goto err_groups;
>>> +
>>> + group->present = true;
>>> + return 0;
>>> +
>>> + err_groups:
>>> + kfree(group->group_pins);
>>> + err:
>>> + kfree(group->name);
>>> + return ret;
>>> +}
>>> +
>>> +static int scmi_pinctrl_get_group_name(const struct scmi_handle
>>> *handle,
>>> + u32 selector, const char **name)
>>> +{
>>> + int ret;
>>> + struct scmi_pinctrl_info *pi;
>>> +
>>> + if (!handle || !handle->pinctrl_priv || !name)
>>> + return -EINVAL;
>>> +
>>> + pi = handle->pinctrl_priv;
>>> +
>>> + if (selector > pi->nr_groups)
>>> + return -EINVAL;
>>> +
>>> + if (!pi->groups[selector].present) {
>>> + ret = scmi_pinctrl_get_group_info(handle, selector,
>>> + &pi->groups[selector]);
>>> + if (ret)
>>> + return ret;
>>> + }
>>> +
>>> + *name = pi->groups[selector].name;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int scmi_pinctrl_get_group_pins(const struct scmi_handle
>>> *handle,
>>> + u32 selector, const unsigned int **pins,
>>> + unsigned int *nr_pins)
>>> +{
>>> + int ret;
>>> + struct scmi_pinctrl_info *pi;
>>> +
>>> + if (!handle || !handle->pinctrl_priv || !pins || !nr_pins)
>>> + return -EINVAL;
>>> +
>>> + pi = handle->pinctrl_priv;
>>> +
>>> + if (selector > pi->nr_groups)
>>> + return -EINVAL;
>>> +
>>> + if (!pi->groups[selector].present) {
>>> + ret = scmi_pinctrl_get_group_info(handle, selector,
>>> + &pi->groups[selector]);
>>> + if (ret)
>>> + return ret;
>>> + }
>>> +
>>> + *pins = pi->groups[selector].group_pins;
>>> + *nr_pins = pi->groups[selector].nr_pins;
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static int scmi_pinctrl_get_function_info(const struct scmi_handle
>>> *handle,
>>> + u32 selector,
>>> + struct scmi_function_info *func)
>>> +{
>>> + int ret = 0;
>>> + struct scmi_pinctrl_info *pi;
>>> +
>>> + if (!handle || !handle->pinctrl_priv || !func)
>>> + return -EINVAL;
>>> +
>>> + pi = handle->pinctrl_priv;
>>> +
>>> + ret = scmi_pinctrl_attributes(handle, FUNCTION_TYPE, selector,
>>> + &func->name,
>>> + &func->nr_groups);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + if (!func->nr_groups) {
>>> + dev_err(handle->dev, "Function %d has 0 elements", selector);
>>> + return -ENODATA;
>>> + }
>>> +
>>> + func->groups = devm_kmalloc_array(handle->dev, func->nr_groups,
>>> + sizeof(*func->groups),
>>> + GFP_KERNEL);
>>> + if (!func->groups) {
>>> + ret = -ENOMEM;
>>> + goto err;
>>> + }
>>> +
>>> + ret = scmi_pinctrl_list_associations(handle, selector,
>>> FUNCTION_TYPE,
>>> + func->nr_groups, func->groups);
>>> + if (ret)
>>> + goto err_funcs;
>>> +
>>> + func->present = true;
>>> + return 0;
>>> +
>>> + err_funcs:
>>> + kfree(func->groups);
>>> + err:
>>> + kfree(func->name);
>>> + return ret;
>>> +}
>>> +
>>> +static int scmi_pinctrl_get_function_name(const struct scmi_handle
>>> *handle,
>>> + u32 selector, const char **name)
>>> +{
>>> + int ret;
>>> + struct scmi_pinctrl_info *pi;
>>> +
>>> + if (!handle || !handle->pinctrl_priv || !name)
>>> + return -EINVAL;
>>> +
>>> + pi = handle->pinctrl_priv;
>>> +
>>> + if (selector > pi->nr_functions)
>>> + return -EINVAL;
>>> +
>>> + if (!pi->functions[selector].present) {
>>> + ret = scmi_pinctrl_get_function_info(handle, selector,
>>> + &pi->functions[selector]);
>>> + if (ret)
>>> + return ret;
>>> + }
>>> +
>>> + *name = pi->functions[selector].name;
>>> + return 0;
>>> +}
>>> +
>>> +static int scmi_pinctrl_get_function_groups(const struct
>>> scmi_handle *handle,
>>> + u32 selector,
>>> + unsigned int *nr_groups,
>>> + const unsigned int **groups)
>>> +{
>>> + int ret;
>>> + struct scmi_pinctrl_info *pi;
>>> +
>>> + if (!handle || !handle->pinctrl_priv || !groups || !nr_groups)
>>> + return -EINVAL;
>>> +
>>> + pi = handle->pinctrl_priv;
>>> +
>>> + if (selector > pi->nr_functions)
>>> + return -EINVAL;
>>> +
>>> + if (!pi->functions[selector].present) {
>>> + ret = scmi_pinctrl_get_function_info(handle, selector,
>>> + &pi->functions[selector]);
>>> + if (ret)
>>> + return ret;
>>> + }
>>> +
>>> + *groups = pi->functions[selector].groups;
>>> + *nr_groups = pi->functions[selector].nr_groups;
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static int scmi_pinctrl_set_mux(const struct scmi_handle *handle,
>>> u32 selector,
>>> + u32 group)
>>> +{
>>> + return scmi_pinctrl_function_select(handle, group, GROUP_TYPE,
>>> + selector);
>>> +}
>>> +
>>> +static int scmi_pinctrl_get_pin_info(const struct scmi_handle *handle,
>>> + u32 selector, struct scmi_pin_info *pin)
>>> +{
>>> + int ret = 0;
>>> + struct scmi_pinctrl_info *pi;
>>> + unsigned int n_elems;
>>> +
>>> + if (!handle || !handle->pinctrl_priv || !pin)
>>> + return -EINVAL;
>>> +
>>> + pi = handle->pinctrl_priv;
>>> +
>>> + ret = scmi_pinctrl_attributes(handle, PIN_TYPE, selector,
>>> + &pin->name,
>>> + &n_elems);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + if (n_elems != pi->nr_pins) {
>>> + dev_err(handle->dev, "Wrong pin count expected %d has %d",
>>> + pi->nr_pins, n_elems);
>>> + return -ENODATA;
>>> + }
>>> +
>>> + if (*(pin->name) == 0) {
>>> + dev_err(handle->dev, "Pin name is empty");
>>> + goto err;
>>> + }
>>> +
>>> + pin->present = true;
>>> + return 0;
>>> +
>>> + err:
>>> + kfree(pin->name);
>>> + return ret;
>>> +}
>>> +
>>> +static int scmi_pinctrl_get_pin_name(const struct scmi_handle
>>> *handle, u32 selector,
>>> + const char **name)
>>> +{
>>> +
>>> + int ret;
>>> + struct scmi_pinctrl_info *pi;
>>> +
>>> + if (!handle || !handle->pinctrl_priv || !name)
>>> + return -EINVAL;
>>> +
>>> + pi = handle->pinctrl_priv;
>>> +
>>> + if (selector > pi->nr_pins)
>>> + return -EINVAL;
>>> +
>>> + if (!pi->pins[selector].present) {
>>> + ret = scmi_pinctrl_get_pin_info(handle, selector,
>>> + &pi->pins[selector]);
>>> + if (ret)
>>> + return ret;
>>> + }
>>> +
>>> + *name = pi->pins[selector].name;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +
>>> +static const struct scmi_pinctrl_ops pinctrl_ops = {
>>> + .get_groups_count = scmi_pinctrl_get_groups_count,
>>> + .get_group_name = scmi_pinctrl_get_group_name,
>>> + .get_group_pins = scmi_pinctrl_get_group_pins,
>>> + .get_functions_count = scmi_pinctrl_get_functions_count,
>>> + .get_function_name = scmi_pinctrl_get_function_name,
>>> + .get_function_groups = scmi_pinctrl_get_function_groups,
>>> + .set_mux = scmi_pinctrl_set_mux,
>>> + .get_pin_name = scmi_pinctrl_get_pin_name,
>>> + .get_pins_count = scmi_pinctrl_get_pins_count,
>>> + .get_config = scmi_pinctrl_get_config,
>>> + .set_config = scmi_pinctrl_set_config,
>>> + .get_config_group = scmi_pinctrl_get_config_group,
>>> + .set_config_group = scmi_pinctrl_set_config_group,
>>> + .request_pin = scmi_pinctrl_request_pin,
>>> + .free_pin = scmi_pinctrl_free_pin
>>> +};
>>> +
>>> +static int scmi_pinctrl_protocol_init(struct scmi_handle *handle)
>>> +{
>>> + u32 version;
>>> + struct scmi_pinctrl_info *pinfo;
>>> + int ret;
>>> +
>>> + if (!handle)
>>> + return -EINVAL;
>>> +
>>> + scmi_version_get(handle, SCMI_PROTOCOL_PINCTRL, &version);
>>> +
>>> + dev_dbg(handle->dev, "Pinctrl Version %d.%d\n",
>>> + PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
>>> +
>>> + pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL);
>>> + if (!pinfo)
>>> + return -ENOMEM;
>>> +
>>> + ret = scmi_pinctrl_attributes_get(handle, pinfo);
>>> + if (ret)
>>> + goto free;
>>> +
>>> + pinfo->pins = devm_kmalloc_array(handle->dev, pinfo->nr_pins,
>>> + sizeof(*pinfo->pins),
>>> + GFP_KERNEL | __GFP_ZERO);
>>
>> devm_kcalloc() zeroes on its own
>>
>>> + if (!pinfo->pins) {
>>> + ret = -ENOMEM;
>>> + goto free;
>>> + }
>>> +
>>> + pinfo->groups = devm_kmalloc_array(handle->dev, pinfo->nr_groups,
>>> + sizeof(*pinfo->groups),
>>> + GFP_KERNEL | __GFP_ZERO);
>>
>> Ditto.
>>> + if (!pinfo->groups) {
>>> + ret = -ENOMEM;
>>> + goto free;
>>> + }
>>> +
>>> + pinfo->functions = devm_kmalloc_array(handle->dev,
>>> pinfo->nr_functions,
>>> + sizeof(*pinfo->functions),
>>> + GFP_KERNEL | __GFP_ZERO);
>>> + if (!pinfo->functions) {
>>> + ret = -ENOMEM;
>>> + goto free;
>>> + }
>>> +
>>> + pinfo->version = version;
>>> + handle->pinctrl_ops = &pinctrl_ops;
>>> + handle->pinctrl_priv = pinfo;
>>> +
>>> + return 0;
>>> +free:
>>> + if (pinfo) {
>>> + devm_kfree(handle->dev, pinfo->pins);
>>> + devm_kfree(handle->dev, pinfo->functions);
>>> + devm_kfree(handle->dev, pinfo->groups);
>>> + }
>>
>> These frees are really not needed...if this function return failure any
>> devres allocated in it is freed by the SCMI core. (as said above...in a
>> recent kernel with the new API of course)
>>
>>> +
>>> + devm_kfree(handle->dev, pinfo);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_PINCTRL,
>>> pinctrl)
>>> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
>>> index 815095326e2d..68add4d06e8c 100644
>>> --- a/drivers/pinctrl/Kconfig
>>> +++ b/drivers/pinctrl/Kconfig
>>> @@ -431,4 +431,13 @@ config PINCTRL_EQUILIBRIUM
>>> pin functions, configure GPIO attributes for LGM SoC pins.
>>> Pinmux and
>>> pinconf settings are retrieved from device tree.
>>> +config PINCTRL_SCMI
>>> + bool "Pinctrl driver controlled via SCMI interface"
>>> + depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
>>> + help
>>> + This driver provides support for pinctrl which is controlled
>>> + by firmware that implements the SCMI interface.
>>> + It uses SCMI Message Protocol to interact with the
>>> + firmware providing all the pinctrl controls.
>>> +
>>
>> This does NOT belong to this patch, but to the next right ?
>>
>>> endif
>>> diff --git a/include/linux/scmi_protocol.h
>>> b/include/linux/scmi_protocol.h
>>> index 9cd312a1ff92..6a909ef3bf51 100644
>>> --- a/include/linux/scmi_protocol.h
>>> +++ b/include/linux/scmi_protocol.h
>>> @@ -12,7 +12,8 @@
>>> #include <linux/notifier.h>
>>> #include <linux/types.h>
>>> -#define SCMI_MAX_STR_SIZE 16
>>> +#define SCMI_MAX_STR_SIZE 16
>>> +#define SCMI_MAX_STR_EXT_SIZE 64
>>
>> This is handled as part of how the extended names are handled with
>> ph->hops
>> in a common way, as I was saying, so please move this if you need it in
>> the protocol code, then I'll port to the ph->hops interface and clean
>> up.
>>
>>> #define SCMI_MAX_NUM_RATES 16
>>> /**
>>> @@ -252,6 +253,55 @@ struct scmi_notify_ops {
>>> struct notifier_block *nb);
>>> };
>>> +/**
>>> + * struct scmi_pinctrl_ops - represents the various operations
>>> provided
>>> + * by SCMI Pinctrl Protocol
>>> + *
>>> + * @get_groups_count: returns count of the registered groups
>>> + * @get_group_name: returns group name by index
>>> + * @get_group_pins: returns the set of pins, assigned to the
>>> specified group
>>> + * @get_functions_count: returns count of the registered fucntions
>>> + * @get_function_name: returns function name by indes
>>> + * @get_function_groups: returns the set of groups, assigned to the
>>> specified
>>> + * function
>>> + * @set_mux: set muxing function for groups of pins
>>> + * @get_pins: returns the set of pins, registered in driver
>>> + * @get_config: returns configuration parameter for pin
>>> + * @set_config: sets the configuration parameter for pin
>>> + * @get_config_group: returns the configuration parameter for a
>>> group of pins
>>> + * @set_config_group: sets the configuration parameter for a groups
>>> of pins
>>> + * @request_pin: aquire pin before selecting mux setting
>>> + * @free_pin: frees pin, acquired by request_pin call
>>> + */
>>> +struct scmi_pinctrl_ops {
>>> + int (*get_groups_count)(const struct scmi_handle *handle);
>>> + int (*get_group_name)(const struct scmi_handle *handles, u32
>>> selector,
>>> + const char **name);
>>> + int (*get_group_pins)(const struct scmi_handle *handle, u32
>>> selector,
>>> + const unsigned int **pins, unsigned int *nr_pins);
>>> + int (*get_functions_count)(const struct scmi_handle *handle);
>>> + int (*get_function_name)(const struct scmi_handle *handle, u32
>>> selector,
>>> + const char **name);
>>> + int (*get_function_groups)(const struct scmi_handle *handle,
>>> + u32 selector, unsigned int *nr_groups,
>>> + const unsigned int **groups);
>>> + int (*set_mux)(const struct scmi_handle *handle, u32 selector,
>>> + u32 group);
>>> + int (*get_pin_name)(const struct scmi_handle *handle, u32
>>> selector,
>>> + const char **name);
>>> + int (*get_pins_count)(const struct scmi_handle *handle);
>>> + int (*get_config)(const struct scmi_handle *handle, u32 pin,
>>> + u32 *config);
>>> + int (*set_config)(const struct scmi_handle *handle, u32 pin,
>>> + u32 config);
>>> + int (*get_config_group)(const struct scmi_handle *handle, u32 pin,
>>> + u32 *config);
>>> + int (*set_config_group)(const struct scmi_handle *handle, u32 pin,
>>> + u32 config);
>>> + int (*request_pin)(const struct scmi_handle *handle, u32 pin);
>>> + int (*free_pin)(const struct scmi_handle *handle, u32 pin);
>>> +};
>>> +
>>
>> As mentioned above, here you could drop a lot of this
>> get_X_count/name/pins
>> and instead expose a few of the internal proocol struct scmi__X_info
>> and then
>> provide just a mean to query how many resource are there and then get
>> the info
>> descriptor you want for the specific domain_id, i.e.:
>>
>> int (*num_domains_get)(ph, type)
>> void *(*info_get)(ph, type, domain_id);
>>
>> Thanks,
>> Cristian
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> [email protected]
>> https://urldefense.com/v3/__http://lists.infradead.org/mailman/listinfo/linux-arm-kernel__;!!GF_29dbcQIUBPA!y2hR3PEGGxiPjVeXBcgGyV03DPDhzgUKR0uHvsTpiafKgBar8Egc6oOOs-IkFIquhSf-qBzltqEMyzRZrZi7qlk$
>> [lists[.]infradead[.]org]
On Fri, Apr 21, 2023 at 10:40 AM Oleksii Moisieiev
<[email protected]> wrote:
> On 17.04.23 05:55, Peng Fan wrote:
> > On 4/13/2023 6:04 AM, Cristian Marussi wrote:
> > Is it possible to extend the spec to support multilple uint32_t for PIN
> > CONFIG SET?
> >
> > With only one uint32_t could not satisfy i.MX requirement.
> >
> > Thanks,
> > Peng.
> >
> IIUC you are expecting to have an ability to set some kind of array of
> uint32_t config values to some specific ConfigType?
>
> I'm not sure if it's supported by pintctrl subsystem right now. I was
> unable to find an example in the existing device-tree pinctrl bindings.
> This makes me think that this kind of binding is OEM specific.
>
> Maybe it can be implemented by adding new IDs to OEM specific range
> (192-255) which is reserved for OEM specific units (See Table 23 of
> DEN0056E).
From a pinctrl point of view I do not understand this requirement.
The pinctrl subsystem in the Linux kernel certainly does not support
an array of u32 for the pin config, we only support passing a single
u32 value along with the enumerator (config type), or well it is
actually 24 bits in Linux, the uppermost 8 bits is for the config type:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/pinctrl/pinconf-generic.h
/*
* Helpful configuration macro to be used in tables etc.
*/
#define PIN_CONF_PACKED(p, a) ((a << 8) | ((unsigned long) p & 0xffUL))
p = parameter (PIN_CONFIG_DRIVE_STRENGTH etc)
a = argument (value such as in mA)
Yours,
Linus Walleij
On Fri, Apr 21, 2023 at 08:40:47AM +0000, Oleksii Moisieiev wrote:
> Hi Peng Fan,
>
> On 17.04.23 05:55, Peng Fan wrote:
> >
> >
> > On 4/13/2023 6:04 AM, Cristian Marussi wrote:
> >> On Fri, Apr 07, 2023 at 10:18:27AM +0000, Oleksii Moisieiev wrote:
> >>> Implementation of the SCMI client driver, which implements
> >>> PINCTRL_PROTOCOL. This protocol has ID 19 and is described
> >>> in the latest DEN0056 document.
> >>
> >> Hi,
> >>
> >>> This protocol is part of the feature that was designed to
> >>> separate the pinctrl subsystem from the SCP firmware.
> >>> The idea is to separate communication of the pin control
> >>> subsystem with the hardware to SCP firmware
> >>> (or a similar system, such as ATF), which provides an interface
> >>> to give the OS ability to control the hardware through SCMI protocol.
> >>> This is a generic driver that implements SCMI protocol,
> >>> independent of the platform type.
> >>>
> >>> DEN0056 document:
> >>> https://urldefense.com/v3/__https://developer.arm.com/documentation/den0056/latest__;!!GF_29dbcQIUBPA!y2hR3PEGGxiPjVeXBcgGyV03DPDhzgUKR0uHvsTpiafKgBar8Egc6oOOs-IkFIquhSf-qBzltqEMyzRZHq8eC4g$
> >>> [developer[.]arm[.]com]
> >>>
> >>
> >> No need to specify all of this in the commit message, just a note that
> >> you are adding a new SCMIv3.2 Pincontrol protocol, highlighting anything
> >> that has been left out in this patch (if any) will be enough.
> >
> > Is it possible to extend the spec to support multilple uint32_t for PIN
> > CONFIG SET?
> >
> > With only one uint32_t could not satisfy i.MX requirement.
> >
> > Thanks,
> > Peng.
> >
> IIUC you are expecting to have an ability to set some kind of array of
> uint32_t config values to some specific ConfigType?
>
> I'm not sure if it's supported by pintctrl subsystem right now. I was
> unable to find an example in the existing device-tree pinctrl bindings.
> This makes me think that this kind of binding is OEM specific.
>
> Maybe it can be implemented by adding new IDs to OEM specific range
> (192-255) which is reserved for OEM specific units (See Table 23 of
> DEN0056E).
>
If I understood correctly the aim of Peng multi-valued request, I think
that even if Linux does not support using this kind of multiple valued
requests (as of now), if it is useful or required by some of the possibly
supported hardware, it should be described and allowed by the specification
and supported by the core SCMI protocol support at least, while the pinctrl
SCMI driver can ignore this and keep using a one-sized array protocol_ops
call internally (since it cannot do any different anyway as of now)
IOW I dont think we should model too strictly the SCMI spec against only
what the Linux pinctrl subsystem support today, since Linux it is just
really only one of the possible SCMI agents and Linux implementation itself
can possibly change: it is better to model the spec on the HW requirements
or the possible usage patterns across all the possibly participating agents.
As an example, for similar reasons, when the SCMI Voltage protocol was added
to the spec, at the very last minute, a change was made to the spec to allow
for negative voltages, even though the Linux regulator subsystem was not
and still is not supporting at all negative voltages as of now; so basically
the SCMI voltage protocol API now exposes a per-domain flag (negative_volts_allowed),
that allows any kind of voltage domain to be enumerated and handled at the SCMI
spec and core layer but that also allows any SCMI driver user, like the SCMI
Regulator driver, to decide on his own if negative voltages domains can be
supported: indeed the scmi-regulator driver just skips the initialization of
any voltage domain that is found to be describing negative voltages.
Here is a bit different, it is more of an optimization in the call path
than an HW difference, but I would follow the same approach: with the
SCMI spec and the core SCMI stack (the protocol) that supports a multi-uint32
call as a general case, if useful for some scenarios, and instead the SCMI
pinctrl driver that just ignores this possibility and keep using a single-value
array anyway....then, it will be up to the guys leveraging this multi-valued
call to come up with a way to use it on their systems, possibly maybe contributing
back to upstream any needed modification if general enough
(not sure about the details of how this multi-vals operation should be...we'll have
to discuss that about the spec all together I think.)
In any case, I would definitely NOT relegate such possibility to vendor space,
since it is something generic and, especially being just (as it seems to me) an
optimization on the call path at the end, it will just lead to uneeded duplication
of functionalities in the vendor implementation of stuff that it is already
very slightly differently supported by the standard.
...just my opinion anyway, I'll happily let other guys in this thread discuss and
decide about this :P
Thanks,
Cristian
On Fri, Apr 21, 2023 at 11:28:38AM +0200, Linus Walleij wrote:
> On Fri, Apr 21, 2023 at 10:40 AM Oleksii Moisieiev
> <[email protected]> wrote:
> > On 17.04.23 05:55, Peng Fan wrote:
> > > On 4/13/2023 6:04 AM, Cristian Marussi wrote:
>
> > > Is it possible to extend the spec to support multilple uint32_t for PIN
> > > CONFIG SET?
> > >
> > > With only one uint32_t could not satisfy i.MX requirement.
> > >
> > > Thanks,
> > > Peng.
> > >
> > IIUC you are expecting to have an ability to set some kind of array of
> > uint32_t config values to some specific ConfigType?
> >
> > I'm not sure if it's supported by pintctrl subsystem right now. I was
> > unable to find an example in the existing device-tree pinctrl bindings.
> > This makes me think that this kind of binding is OEM specific.
> >
> > Maybe it can be implemented by adding new IDs to OEM specific range
> > (192-255) which is reserved for OEM specific units (See Table 23 of
> > DEN0056E).
>
Hi Linus,
> From a pinctrl point of view I do not understand this requirement.
>
> The pinctrl subsystem in the Linux kernel certainly does not support
> an array of u32 for the pin config, we only support passing a single
> u32 value along with the enumerator (config type), or well it is
> actually 24 bits in Linux, the uppermost 8 bits is for the config type:
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/pinctrl/pinconf-generic.h
>
> /*
> * Helpful configuration macro to be used in tables etc.
> */
> #define PIN_CONF_PACKED(p, a) ((a << 8) | ((unsigned long) p & 0xffUL))
>
> p = parameter (PIN_CONFIG_DRIVE_STRENGTH etc)
> a = argument (value such as in mA)
>
My (possibly wrong) reasoning on the other reply, is based on the
(possibly equally wrong :D) understanding that what Peng wants is just
the possibility at the spec and the SCMI protocol layer (exposed in
protocol operations) to issue PINCTRL_SET requests containing optionally
an array of multiple ConfigType/Value pairs (which is anyway not supported
by PinCtrl as I understand) instead of a single pair.
... but I can divine (:D)....that soon a new SCMI spec review/comment/amend
cycle will be coming for people reading this...
Thanks,
Cristian
Hi Cristian,
On 21.04.23 12:30, Cristian Marussi wrote:
> On Fri, Apr 21, 2023 at 08:40:47AM +0000, Oleksii Moisieiev wrote:
>> Hi Peng Fan,
>>
>> On 17.04.23 05:55, Peng Fan wrote:
>>>
>>> On 4/13/2023 6:04 AM, Cristian Marussi wrote:
>>>> On Fri, Apr 07, 2023 at 10:18:27AM +0000, Oleksii Moisieiev wrote:
>>>>> Implementation of the SCMI client driver, which implements
>>>>> PINCTRL_PROTOCOL. This protocol has ID 19 and is described
>>>>> in the latest DEN0056 document.
>>>> Hi,
>>>>
>>>>> This protocol is part of the feature that was designed to
>>>>> separate the pinctrl subsystem from the SCP firmware.
>>>>> The idea is to separate communication of the pin control
>>>>> subsystem with the hardware to SCP firmware
>>>>> (or a similar system, such as ATF), which provides an interface
>>>>> to give the OS ability to control the hardware through SCMI protocol.
>>>>> This is a generic driver that implements SCMI protocol,
>>>>> independent of the platform type.
>>>>>
>>>>> DEN0056 document:
>>>>> https://urldefense.com/v3/__https://developer.arm.com/documentation/den0056/latest__;!!GF_29dbcQIUBPA!y2hR3PEGGxiPjVeXBcgGyV03DPDhzgUKR0uHvsTpiafKgBar8Egc6oOOs-IkFIquhSf-qBzltqEMyzRZHq8eC4g$
>>>>> [developer[.]arm[.]com]
>>>>>
>>>> No need to specify all of this in the commit message, just a note that
>>>> you are adding a new SCMIv3.2 Pincontrol protocol, highlighting anything
>>>> that has been left out in this patch (if any) will be enough.
>>> Is it possible to extend the spec to support multilple uint32_t for PIN
>>> CONFIG SET?
>>>
>>> With only one uint32_t could not satisfy i.MX requirement.
>>>
>>> Thanks,
>>> Peng.
>>>
>> IIUC you are expecting to have an ability to set some kind of array of
>> uint32_t config values to some specific ConfigType?
>>
>> I'm not sure if it's supported by pintctrl subsystem right now. I was
>> unable to find an example in the existing device-tree pinctrl bindings.
>> This makes me think that this kind of binding is OEM specific.
>>
>> Maybe it can be implemented by adding new IDs to OEM specific range
>> (192-255) which is reserved for OEM specific units (See Table 23 of
>> DEN0056E).
>>
> If I understood correctly the aim of Peng multi-valued request, I think
> that even if Linux does not support using this kind of multiple valued
> requests (as of now), if it is useful or required by some of the possibly
> supported hardware, it should be described and allowed by the specification
> and supported by the core SCMI protocol support at least, while the pinctrl
> SCMI driver can ignore this and keep using a one-sized array protocol_ops
> call internally (since it cannot do any different anyway as of now)
>
> IOW I dont think we should model too strictly the SCMI spec against only
> what the Linux pinctrl subsystem support today, since Linux it is just
> really only one of the possible SCMI agents and Linux implementation itself
> can possibly change: it is better to model the spec on the HW requirements
> or the possible usage patterns across all the possibly participating agents.
>
> As an example, for similar reasons, when the SCMI Voltage protocol was added
> to the spec, at the very last minute, a change was made to the spec to allow
> for negative voltages, even though the Linux regulator subsystem was not
> and still is not supporting at all negative voltages as of now; so basically
> the SCMI voltage protocol API now exposes a per-domain flag (negative_volts_allowed),
> that allows any kind of voltage domain to be enumerated and handled at the SCMI
> spec and core layer but that also allows any SCMI driver user, like the SCMI
> Regulator driver, to decide on his own if negative voltages domains can be
> supported: indeed the scmi-regulator driver just skips the initialization of
> any voltage domain that is found to be describing negative voltages.
>
> Here is a bit different, it is more of an optimization in the call path
> than an HW difference, but I would follow the same approach: with the
> SCMI spec and the core SCMI stack (the protocol) that supports a multi-uint32
> call as a general case, if useful for some scenarios, and instead the SCMI
> pinctrl driver that just ignores this possibility and keep using a single-value
> array anyway....then, it will be up to the guys leveraging this multi-valued
> call to come up with a way to use it on their systems, possibly maybe contributing
> back to upstream any needed modification if general enough
> (not sure about the details of how this multi-vals operation should be...we'll have
> to discuss that about the spec all together I think.)
>
> In any case, I would definitely NOT relegate such possibility to vendor space,
> since it is something generic and, especially being just (as it seems to me) an
> optimization on the call path at the end, it will just lead to uneeded duplication
> of functionalities in the vendor implementation of stuff that it is already
> very slightly differently supported by the standard.
>
> ...just my opinion anyway, I'll happily let other guys in this thread discuss and
> decide about this :P
>
> Thanks,
> Cristian
That sounds reasonable for me, although I can't imagine the use case of
multi-valued config values (most likely this is the problem of my
imagination). So I'd appreciate if Peng Fan could provide us with some
examples.
From my standpoint - ConfigTypes are meant to be simple value because
they are mostly related to the electronic properties. But I agree that
protocol should be platform-agnostic.
It will be great if Peng Fan could provide some examples, so we can
think about the best solution.
Best regards,
Oleksii
Hi Cristian,
On 21.04.23 12:47, Cristian Marussi wrote:
> On Fri, Apr 21, 2023 at 11:28:38AM +0200, Linus Walleij wrote:
>> On Fri, Apr 21, 2023 at 10:40 AM Oleksii Moisieiev
>> <[email protected]> wrote:
>>> On 17.04.23 05:55, Peng Fan wrote:
>>>> On 4/13/2023 6:04 AM, Cristian Marussi wrote:
>>>> Is it possible to extend the spec to support multilple uint32_t for PIN
>>>> CONFIG SET?
>>>>
>>>> With only one uint32_t could not satisfy i.MX requirement.
>>>>
>>>> Thanks,
>>>> Peng.
>>>>
>>> IIUC you are expecting to have an ability to set some kind of array of
>>> uint32_t config values to some specific ConfigType?
>>>
>>> I'm not sure if it's supported by pintctrl subsystem right now. I was
>>> unable to find an example in the existing device-tree pinctrl bindings.
>>> This makes me think that this kind of binding is OEM specific.
>>>
>>> Maybe it can be implemented by adding new IDs to OEM specific range
>>> (192-255) which is reserved for OEM specific units (See Table 23 of
>>> DEN0056E).
> Hi Linus,
>
>> From a pinctrl point of view I do not understand this requirement.
>>
>> The pinctrl subsystem in the Linux kernel certainly does not support
>> an array of u32 for the pin config, we only support passing a single
>> u32 value along with the enumerator (config type), or well it is
>> actually 24 bits in Linux, the uppermost 8 bits is for the config type:
>> https://urldefense.com/v3/__https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/pinctrl/pinconf-generic.h__;!!GF_29dbcQIUBPA!0kv3XDV9f6l7rFEw9QFccCFZcl8t0vXfh_76DD80uq_dI_znPtdt4CVU6mLCZA5bHpliIRnzkIady9IAUEeIP7AB7fwWXg$ [git[.]kernel[.]org]
>>
>> /*
>> * Helpful configuration macro to be used in tables etc.
>> */
>> #define PIN_CONF_PACKED(p, a) ((a << 8) | ((unsigned long) p & 0xffUL))
>>
>> p = parameter (PIN_CONFIG_DRIVE_STRENGTH etc)
>> a = argument (value such as in mA)
>>
> My (possibly wrong) reasoning on the other reply, is based on the
> (possibly equally wrong :D) understanding that what Peng wants is just
> the possibility at the spec and the SCMI protocol layer (exposed in
> protocol operations) to issue PINCTRL_SET requests containing optionally
> an array of multiple ConfigType/Value pairs (which is anyway not supported
> by PinCtrl as I understand) instead of a single pair.
>
> ... but I can divine (:D)....that soon a new SCMI spec review/comment/amend
> cycle will be coming for people reading this...
>
> Thanks,
> Cristian
Pinctrl subsystem does support pinctrl_set call with an array of
ConfigType/Value pairs. I'm sending those comfigs one-by-one from
pinctrl_scmi_pinconf_set function. I think that can be added to the
protocol to avoid extra scmi calls.
My understanding on Peng Fan words was that he want's to have multiple
values for one ConfigType.
Let's wait for more information from Peng Fang.
Oleksii
On Fri, Apr 21, 2023 at 09:48:33AM +0000, Oleksii Moisieiev wrote:
> Hi Cristian,
>
Hi,
> On 21.04.23 12:30, Cristian Marussi wrote:
> > On Fri, Apr 21, 2023 at 08:40:47AM +0000, Oleksii Moisieiev wrote:
> >> Hi Peng Fan,
> >>
> >> On 17.04.23 05:55, Peng Fan wrote:
> >>>
> >>> On 4/13/2023 6:04 AM, Cristian Marussi wrote:
> >>>> On Fri, Apr 07, 2023 at 10:18:27AM +0000, Oleksii Moisieiev wrote:
> >>>>> Implementation of the SCMI client driver, which implements
> >>>>> PINCTRL_PROTOCOL. This protocol has ID 19 and is described
> >>>>> in the latest DEN0056 document.
> >>>> Hi,
> >>>>
> >>>>> This protocol is part of the feature that was designed to
> >>>>> separate the pinctrl subsystem from the SCP firmware.
> >>>>> The idea is to separate communication of the pin control
> >>>>> subsystem with the hardware to SCP firmware
> >>>>> (or a similar system, such as ATF), which provides an interface
> >>>>> to give the OS ability to control the hardware through SCMI protocol.
> >>>>> This is a generic driver that implements SCMI protocol,
> >>>>> independent of the platform type.
> >>>>>
> >>>>> DEN0056 document:
> >>>>> https://urldefense.com/v3/__https://developer.arm.com/documentation/den0056/latest__;!!GF_29dbcQIUBPA!y2hR3PEGGxiPjVeXBcgGyV03DPDhzgUKR0uHvsTpiafKgBar8Egc6oOOs-IkFIquhSf-qBzltqEMyzRZHq8eC4g$
> >>>>> [developer[.]arm[.]com]
> >>>>>
> >>>> No need to specify all of this in the commit message, just a note that
> >>>> you are adding a new SCMIv3.2 Pincontrol protocol, highlighting anything
> >>>> that has been left out in this patch (if any) will be enough.
> >>> Is it possible to extend the spec to support multilple uint32_t for PIN
> >>> CONFIG SET?
> >>>
> >>> With only one uint32_t could not satisfy i.MX requirement.
> >>>
> >>> Thanks,
> >>> Peng.
> >>>
> >> IIUC you are expecting to have an ability to set some kind of array of
> >> uint32_t config values to some specific ConfigType?
> >>
> >> I'm not sure if it's supported by pintctrl subsystem right now. I was
> >> unable to find an example in the existing device-tree pinctrl bindings.
> >> This makes me think that this kind of binding is OEM specific.
> >>
> >> Maybe it can be implemented by adding new IDs to OEM specific range
> >> (192-255) which is reserved for OEM specific units (See Table 23 of
> >> DEN0056E).
> >>
> > If I understood correctly the aim of Peng multi-valued request, I think
> > that even if Linux does not support using this kind of multiple valued
> > requests (as of now), if it is useful or required by some of the possibly
> > supported hardware, it should be described and allowed by the specification
> > and supported by the core SCMI protocol support at least, while the pinctrl
> > SCMI driver can ignore this and keep using a one-sized array protocol_ops
> > call internally (since it cannot do any different anyway as of now)
> >
> > IOW I dont think we should model too strictly the SCMI spec against only
> > what the Linux pinctrl subsystem support today, since Linux it is just
> > really only one of the possible SCMI agents and Linux implementation itself
> > can possibly change: it is better to model the spec on the HW requirements
> > or the possible usage patterns across all the possibly participating agents.
> >
> > As an example, for similar reasons, when the SCMI Voltage protocol was added
> > to the spec, at the very last minute, a change was made to the spec to allow
> > for negative voltages, even though the Linux regulator subsystem was not
> > and still is not supporting at all negative voltages as of now; so basically
> > the SCMI voltage protocol API now exposes a per-domain flag (negative_volts_allowed),
> > that allows any kind of voltage domain to be enumerated and handled at the SCMI
> > spec and core layer but that also allows any SCMI driver user, like the SCMI
> > Regulator driver, to decide on his own if negative voltages domains can be
> > supported: indeed the scmi-regulator driver just skips the initialization of
> > any voltage domain that is found to be describing negative voltages.
> >
> > Here is a bit different, it is more of an optimization in the call path
> > than an HW difference, but I would follow the same approach: with the
> > SCMI spec and the core SCMI stack (the protocol) that supports a multi-uint32
> > call as a general case, if useful for some scenarios, and instead the SCMI
> > pinctrl driver that just ignores this possibility and keep using a single-value
> > array anyway....then, it will be up to the guys leveraging this multi-valued
> > call to come up with a way to use it on their systems, possibly maybe contributing
> > back to upstream any needed modification if general enough
> > (not sure about the details of how this multi-vals operation should be...we'll have
> > to discuss that about the spec all together I think.)
> >
> > In any case, I would definitely NOT relegate such possibility to vendor space,
> > since it is something generic and, especially being just (as it seems to me) an
> > optimization on the call path at the end, it will just lead to uneeded duplication
> > of functionalities in the vendor implementation of stuff that it is already
> > very slightly differently supported by the standard.
> >
> > ...just my opinion anyway, I'll happily let other guys in this thread discuss and
> > decide about this :P
> >
> > Thanks,
> > Cristian
>
> That sounds reasonable for me, although I can't imagine the use case of
> multi-valued config values (most likely this is the problem of my
> imagination). So I'd appreciate if Peng Fan could provide us with some
> examples.
>
> From my standpoint - ConfigTypes are meant to be simple value because
> they are mostly related to the electronic properties. But I agree that
> protocol should be platform-agnostic.
>
> It will be great if Peng Fan could provide some examples, so we can
> think about the best solution.
>
All of the above blabbing of mine (:P) is based on the assumption that
Peng wants just to simply fit multiple type/value pairs requests, possibly
about the same pin, at once in one SCMI command payload, not that he wants to
push a type/val1/val2/val3.. like command....but I maybe wrong and I agree that
more use-case examples from Peng could be useful.
Thanks,
Cristian
On 4/21/2023 4:40 PM, Oleksii Moisieiev wrote:
> Hi Peng Fan,
>
> On 17.04.23 05:55, Peng Fan wrote:
>>
>>
>> On 4/13/2023 6:04 AM, Cristian Marussi wrote:
>>> On Fri, Apr 07, 2023 at 10:18:27AM +0000, Oleksii Moisieiev wrote:
>>>> Implementation of the SCMI client driver, which implements
>>>> PINCTRL_PROTOCOL. This protocol has ID 19 and is described
>>>> in the latest DEN0056 document.
>>>
>>> Hi,
>>>
>>>> This protocol is part of the feature that was designed to
>>>> separate the pinctrl subsystem from the SCP firmware.
>>>> The idea is to separate communication of the pin control
>>>> subsystem with the hardware to SCP firmware
>>>> (or a similar system, such as ATF), which provides an interface
>>>> to give the OS ability to control the hardware through SCMI protocol.
>>>> This is a generic driver that implements SCMI protocol,
>>>> independent of the platform type.
>>>>
>>>> DEN0056 document:
>>>> https://urldefense.com/v3/__https://developer.arm.com/documentation/den0056/latest__;!!GF_29dbcQIUBPA!y2hR3PEGGxiPjVeXBcgGyV03DPDhzgUKR0uHvsTpiafKgBar8Egc6oOOs-IkFIquhSf-qBzltqEMyzRZHq8eC4g$
>>>> [developer[.]arm[.]com]
>>>>
>>>
>>> No need to specify all of this in the commit message, just a note that
>>> you are adding a new SCMIv3.2 Pincontrol protocol, highlighting anything
>>> that has been left out in this patch (if any) will be enough.
>>
>> Is it possible to extend the spec to support multilple uint32_t for PIN
>> CONFIG SET?
>>
>> With only one uint32_t could not satisfy i.MX requirement.
>>
>> Thanks,
>> Peng.
>>
> IIUC you are expecting to have an ability to set some kind of array of
> uint32_t config values to some specific ConfigType?
>
> I'm not sure if it's supported by pintctrl subsystem right now. I was
> unable to find an example in the existing device-tree pinctrl bindings.
> This makes me think that this kind of binding is OEM specific.
Look at arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts
Take lpuart for example:
MX93_PAD_UART1_RXD__LPUART1_RX 0x31e
The macro MX93_PAD_UART1_RXD__LPUART1_RX is an array.
i.MX pinctrl driver use
mux reg, mux value, select reg, select value, pad reg, pad value
pinctrl driver will be handled by SCMI firmware for new i.MX SoC.
With current spec, only one uint32 is low speed. So if the spec
could extended to support more uint32 with pin config set,
we could use one SCMI call to support i.MX pinctrl.
Thanks
Peng.
>
> Maybe it can be implemented by adding new IDs to OEM specific range
> (192-255) which is reserved for OEM specific units (See Table 23 of
> DEN0056E).
>
> Best regards,
>
> Oleksii
>
>
>>> You can look at the very first commit logs of existing protos as an
>>> example like: drivers/firmware/arm_scmi/powercap.c
>>>
>>> Some more comments down below, I'll mostly skip anything related to the
>>> SCMI API change I mentioned before...
>>>
>>> I'll also wont comment on more trivial stuff related to style, BUT there
>>> are lots of them: you should run
>>>
>>> ./scripts/checkpatch.pl --strict <your-git-format-patch-file>
>>>
>>> for each patch in the series. (and fix accordingly..spacing,
>>> brackets...etc)
>>>
>>>> Signed-off-by: Oleksii Moisieiev <[email protected]>
>>>> ---
>>>> MAINTAINERS | 6 +
>>>> drivers/firmware/arm_scmi/Makefile | 2 +-
>>>> drivers/firmware/arm_scmi/common.h | 1 +
>>>> drivers/firmware/arm_scmi/driver.c | 3 +
>>>> drivers/firmware/arm_scmi/pinctrl.c | 905
>>>> ++++++++++++++++++++++++++++
>>>> drivers/pinctrl/Kconfig | 9 +
>>>> include/linux/scmi_protocol.h | 58 +-
>>>> 7 files changed, 982 insertions(+), 2 deletions(-)
>>>> create mode 100644 drivers/firmware/arm_scmi/pinctrl.c
>>>>
>>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>>> index 281de213ef47..abc543fd7544 100644
>>>> --- a/MAINTAINERS
>>>> +++ b/MAINTAINERS
>>>> @@ -16961,6 +16961,12 @@ F: drivers/reset/reset-scmi.c
>>>> F: include/linux/sc[mp]i_protocol.h
>>>> F: include/trace/events/scmi.h
>>>> +PINCTRL DRIVER FOR SYSTEM CONTROL & POWER/MANAGEMENT INTERFACE
>>>> (SCPI/SCMI)
>>>> +M: Oleksii Moisieiev <[email protected]>
>>>> +L: [email protected]
>>>> +S: Maintained
>>>> +F: drivers/firmware/arm_scmi/pinctrl.c
>>>> +
>>>> SYSTEM RESET/SHUTDOWN DRIVERS
>>>> M: Sebastian Reichel <[email protected]>
>>>> L: [email protected]
>>>> diff --git a/drivers/firmware/arm_scmi/Makefile
>>>> b/drivers/firmware/arm_scmi/Makefile
>>>> index bc0d54f8e861..5cec357fbfa8 100644
>>>> --- a/drivers/firmware/arm_scmi/Makefile
>>>> +++ b/drivers/firmware/arm_scmi/Makefile
>>>> @@ -4,7 +4,7 @@ scmi-driver-y = driver.o notify.o
>>>> scmi-transport-y = shmem.o
>>>> scmi-transport-$(CONFIG_MAILBOX) += mailbox.o
>>>> scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o
>>>> -scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o
>>>> system.o
>>>> +scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o
>>>> system.o pinctrl.o
>>>> scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y)
>>>> $(scmi-protocols-y) \
>>>> $(scmi-transport-y)
>>>> obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
>>>> diff --git a/drivers/firmware/arm_scmi/common.h
>>>> b/drivers/firmware/arm_scmi/common.h
>>>> index 65063fa948d4..8bbb404abe8d 100644
>>>> --- a/drivers/firmware/arm_scmi/common.h
>>>> +++ b/drivers/firmware/arm_scmi/common.h
>>>> @@ -170,6 +170,7 @@ DECLARE_SCMI_REGISTER_UNREGISTER(power);
>>>> DECLARE_SCMI_REGISTER_UNREGISTER(reset);
>>>> DECLARE_SCMI_REGISTER_UNREGISTER(sensors);
>>>> DECLARE_SCMI_REGISTER_UNREGISTER(system);
>>>> +DECLARE_SCMI_REGISTER_UNREGISTER(pinctrl);
>>>> #define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(id, name) \
>>>> int __init scmi_##name##_register(void) \
>>>> diff --git a/drivers/firmware/arm_scmi/driver.c
>>>> b/drivers/firmware/arm_scmi/driver.c
>>>> index 3dfd8b6a0ebf..fb9525fb3c24 100644
>>>> --- a/drivers/firmware/arm_scmi/driver.c
>>>> +++ b/drivers/firmware/arm_scmi/driver.c
>>>> @@ -743,6 +743,7 @@ static struct scmi_prot_devnames devnames[] = {
>>>> { SCMI_PROTOCOL_CLOCK, { "clocks" },},
>>>> { SCMI_PROTOCOL_SENSOR, { "hwmon" },},
>>>> { SCMI_PROTOCOL_RESET, { "reset" },},
>>>> + { SCMI_PROTOCOL_PINCTRL, { "pinctrl" },},
>>>> };
>>>> static inline void
>>>> @@ -947,6 +948,7 @@ static int __init scmi_driver_init(void)
>>>> scmi_reset_register();
>>>> scmi_sensors_register();
>>>> scmi_system_register();
>>>> + scmi_pinctrl_register();
>>>> return platform_driver_register(&scmi_driver);
>>>> }
>>>> @@ -962,6 +964,7 @@ static void __exit scmi_driver_exit(void)
>>>> scmi_reset_unregister();
>>>> scmi_sensors_unregister();
>>>> scmi_system_unregister();
>>>> + scmi_pinctrl_unregister();
>>>> platform_driver_unregister(&scmi_driver);
>>>> }
>>>> diff --git a/drivers/firmware/arm_scmi/pinctrl.c
>>>> b/drivers/firmware/arm_scmi/pinctrl.c
>>>> new file mode 100644
>>>> index 000000000000..037270d7f39b
>>>> --- /dev/null
>>>> +++ b/drivers/firmware/arm_scmi/pinctrl.c
>>>> @@ -0,0 +1,905 @@
>>>> +// SPDX-License-Identifier: GPL-2.0
>>>> +/*
>>>> + * System Control and Management Interface (SCMI) Pinctrl Protocol
>>>> + *
>>>> + * Copyright (C) 2021 EPAM.
>>>
>>> nitpick: update (C) years
>>>
>>>> + */
>>>> +
>>>> +#define pr_fmt(fmt) "SCMI Notifications PINCTRL - " fmt
>>>> +
>>>
>>> This is not needed, no notifs in this proto.
>>>
>>>> +#include <linux/scmi_protocol.h>
>>>> +#include <linux/slab.h>
>>>> +
>>>> +#include "common.h"
>>>> +#include "notify.h"
>>>
>>> Notifs not needed, and in the new API world you'll just need a:
>>>
>>> #include "protocols.h"
>>>
>>>> +
>>>> +#define SET_TYPE(x) ((x) & 0x3)
>>>> +
>>>
>>> Even if trivial better to use std bitfield.h macros like
>>> FIELD_GET() BIT() ... etc
>>>
>>>> +enum scmi_pinctrl_protocol_cmd {
>>>> + PINCTRL_ATTRIBUTES = 0x3,
>>>> + PINCTRL_LIST_ASSOCIATIONS = 0x4,
>>>> + PINCTRL_CONFIG_GET = 0x5,
>>>> + PINCTRL_CONFIG_SET = 0x6,
>>>> + PINCTRL_FUNCTION_SELECT = 0x7,
>>>> + PINCTRL_REQUEST = 0x8,
>>>> + PINCTRL_RELEASE = 0x9,
>>>> + PINCTRL_NAME_GET = 0xa,
>>>> + PINCTRL_SET_PERMISSIONS = 0xb
>>>> +};
>>>> +
>>>> +enum scmi_pinctrl_selector_type {
>>>> + PIN_TYPE = 0,
>>>> + GROUP_TYPE,
>>>> + FUNCTION_TYPE
>>>> +};
>>>> +
>>>> +struct scmi_group_info {
>>>> + bool present;
>>>> + char *name;
>>>> + unsigned int *group_pins;
>>>> + unsigned int nr_pins;
>>>> +};
>>>> +
>>>> +struct scmi_function_info {
>>>> + bool present;
>>>> + char *name;
>>>> + unsigned int *groups;
>>>> + unsigned int nr_groups;
>>>> +};
>>>> +
>>>> +struct scmi_pin_info {
>>>> + bool present;
>>>> + char *name;
>>>> +};
>>>> +
>>>> +struct scmi_pinctrl_info {
>>>> + u32 version;
>>>> + u16 nr_groups;
>>>> + u16 nr_functions;
>>>> + u16 nr_pins;
>>>
>>> Since these vars are not related to stricly spaced message fields
>>> (even though
>>> derived from such messages) do not use sized types, you can just
>>> stick with
>>> unsigned int. (it is also better not to mix sized and unsized types
>>> in the same
>>> struct). This also could come handy if these will be exposed to the user
>>> in scmi_protocol.h in the future (more on this down below)
>>>
>>>> + struct scmi_group_info *groups;
>>>> + struct scmi_function_info *functions;
>>>> + struct scmi_pin_info *pins;
>>>> +};
>>>> +
>>>> +struct scmi_conf_tx {
>>>> + __le32 identifier;
>>>> +#define SET_TYPE_BITS(attr, x) (((attr) & 0xFFFFFCFF) | (x & 0x3)
>>>> << 8)
>>>> +#define SET_CONFIG(attr, x) (((attr) & 0xFF) | (x & 0xFF))
>>>
>>> Use bitfield.h like FIELD_SET / GENMASK etc
>>>
>>>> + __le32 attributes;
>>>> +};
>>>> +
>>>> +static int scmi_pinctrl_attributes_get(const struct scmi_handle
>>>> *handle,
>>>> + struct scmi_pinctrl_info *pi)
>>>> +{
>>>> + int ret;
>>>> + struct scmi_xfer *t;
>>>> + struct scmi_msg_pinctrl_protocol_attributes {
>>>> +#define GROUPS_NR(x) ((x) >> 16)
>>>> +#define PINS_NR(x) ((x) & 0xffff)
>>>> + __le32 attributes_low;
>>>> +#define FUNCTIONS_NR(x) ((x) & 0xffff)
>>>> + __le32 attributes_high;
>>>> + } *attr;
>>>
>>> For consistency with the rest of the stack (mostly :D), please move
>>> this struct
>>> definition and related macros outside in the global scope right after
>>> command
>>> enum. (and use bitfield macros ...)
>>>
>>>> +
>>>> + if (!pi)
>>>> + return -EINVAL;
>>>> +
>>>> + ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
>>>> + SCMI_PROTOCOL_PINCTRL, 0, sizeof(*attr), &t);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + attr = t->rx.buf;
>>>> +
>>>> + ret = scmi_do_xfer(handle, t);
>>>> + if (!ret) {
>>>> + pi->nr_functions =
>>>> + le16_to_cpu(FUNCTIONS_NR(attr->attributes_high));
>>>> + pi->nr_groups = le16_to_cpu(GROUPS_NR(attr->attributes_low));
>>>> + pi->nr_pins = le16_to_cpu(PINS_NR(attr->attributes_low));
>>>> + }
>>>> +
>>>> + scmi_xfer_put(handle, t);
>>>> + return ret;
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_get_groups_count(const struct scmi_handle
>>>> *handle)
>>>> +{
>>>> + struct scmi_pinctrl_info *pi;
>>>> +
>>>> + if (!handle || !handle->pinctrl_priv)
>>>> + return -ENODEV;
>>>> +
>>>> + pi = handle->pinctrl_priv;
>>>> +
>>>> + return pi->nr_groups;
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_get_pins_count(const struct scmi_handle
>>>> *handle)
>>>> +{
>>>> + struct scmi_pinctrl_info *pi;
>>>> +
>>>> + if (!handle || !handle->pinctrl_priv)
>>>> + return -ENODEV;
>>>> +
>>>> + pi = handle->pinctrl_priv;
>>>> +
>>>> + return pi->nr_pins;
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_get_functions_count(const struct
>>>> scmi_handle *handle)
>>>> +{
>>>> + struct scmi_pinctrl_info *pi;
>>>> +
>>>> + if (!handle || !handle->pinctrl_priv)
>>>> + return -ENODEV;
>>>> +
>>>> + pi = handle->pinctrl_priv;
>>>> +
>>>> + return pi->nr_functions;
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_validate_id(const struct scmi_handle *handle,
>>>> + u32 identifier,
>>>> + enum scmi_pinctrl_selector_type type)
>>>> +{
>>>> + struct scmi_pinctrl_info *pi;
>>>> +
>>>> + if (!handle || !handle->pinctrl_priv)
>>>> + return -ENODEV;
>>>> +
>>>> + switch (type) {
>>>> + case PIN_TYPE:
>>>> + pi = handle->pinctrl_priv;
>>>> +
>>>> + return (identifier < pi->nr_pins) ? 0 : -EINVAL;
>>>> + case GROUP_TYPE:
>>>> + return (identifier <
>>>> + scmi_pinctrl_get_groups_count(handle)) ?
>>>> + 0 : -EINVAL;
>>>> + case FUNCTION_TYPE:
>>>> + return (identifier <
>>>> + scmi_pinctrl_get_functions_count(handle)) ?
>>>> + 0 : -EINVAL;
>>>> + default:
>>>> + return -EINVAL;
>>>> + }
>>>
>>> Here I would just pick the right value to compare, break and then
>>> compare and exit, something aroundf the lines of:
>>>
>>> case PIN_TYPE:
>>> ...
>>> val = pi->nr_pins;
>>> break;
>>> ...
>>> case GROUP_TYPE:
>>> val = scmi_pinctrl_get_groups_count());
>>> break;
>>>
>>> ....
>>> ....
>>> default:
>>> return -EINVAL;
>>> }
>>>
>>> if (identifier >= val)
>>> return -EINVAL;
>>>
>>> return 0;
>>> }
>>>
>>> ... it's easier to read. What do you think ?
>>>
>>>
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_get_name(const struct scmi_handle *handle,
>>>> + u32 identifier,
>>>> + enum scmi_pinctrl_selector_type type,
>>>> + char **name)
>>>> +{
>>>
>>> As said, there is common helper for this, but it will need some small
>>> adaptation in the SCMI core to work here so keep it as it is, and
>>> I'll take
>>> care of this later, if it sounds fine for you.
>>>
>>>> + struct scmi_xfer *t;
>>>> + int ret = 0;
>>>> + struct scmi_name_tx {
>>>> + __le32 identifier;
>>>> + __le32 flags;
>>>> + } *tx;
>>>> + struct scmi_name_rx {
>>>> + __le32 flags;
>>>> + u8 name[64];
>>>> + } *rx;
>>>> +
>>>> + if (!handle || !name)
>>>> + return -EINVAL;
>>>> +
>>>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_NAME_GET,
>>>> + SCMI_PROTOCOL_PINCTRL,
>>>> + sizeof(*tx), sizeof(*rx), &t);
>>>> +
>>>> + tx = t->tx.buf;
>>>> + rx = t->rx.buf;
>>>> + tx->identifier = identifier;
>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>> +
>>>> + ret = scmi_do_xfer(handle, t);
>>>> + if (ret)
>>>> + goto out;
>>>> +
>>>> + if (rx->flags) {
>>>> + ret = -EINVAL;
>>>> + goto out;
>>>> + }
>>>> +
>>>> + *name = kasprintf(GFP_KERNEL, "%s", rx->name);
>>>> + if (!*name)
>>>> + ret = -ENOMEM;
>>>> + out:
>>>> + scmi_xfer_put(handle, t);
>>>> +
>>>> + return ret;
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_attributes(const struct scmi_handle *handle,
>>>> + enum scmi_pinctrl_selector_type type,
>>>> + u32 selector, char **name,
>>>> + unsigned int *n_elems)
>>>> +{
>>>> + int ret = 0;
>>>> + struct scmi_xfer *t;
>>>> + struct scmi_pinctrl_attributes_tx {
>>>> + __le32 identifier;
>>>> + __le32 flags;
>>>> + } *tx;
>>>> + struct scmi_pinctrl_attributes_rx {
>>>> +#define EXT_NAME_FLAG(x) ((x) & BIT(31))
>>>> +#define NUM_ELEMS(x) ((x) & 0xffff)
>>>> + __le32 attributes;
>>>> + u8 name[16];
>>>> + } *rx;
>>>
>>> Ditto. Move these defs outside, bitfield.h for macros and try to use the
>>> same naming style for message structs as in other protos, i.e.
>>>
>>> for commands: struct scmi_msg_pinctrl_attributes
>>> for replies: struct scmi_resp_pinctrl_attributes
>>>
>>> (or some variations around that...
>>> scmi_msg_cmd_* scmi_msg_resp_*
>>>
>>> we have not been fully consistent really, so I dont want to be
>>> pedantic here, but we never used tx/rx in message context since it is
>>> already (mis)-used in SCMI channel context...)
>>>
>>>> +
>>>> + if (!handle || !name)
>>>> + return -EINVAL;
>>>> +
>>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_ATTRIBUTES,
>>>> + SCMI_PROTOCOL_PINCTRL, sizeof(*tx), sizeof(*rx), &t);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + tx = t->tx.buf;
>>>> + rx = t->rx.buf;
>>>> + tx->identifier = selector;
>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>> +
>>>> + ret = scmi_do_xfer(handle, t);
>>>> + if (ret)
>>>> + goto out;
>>>> +
>>>> + *n_elems = NUM_ELEMS(rx->attributes);
>>>> +
>>>> + if (!EXT_NAME_FLAG(rx->attributes)) {
>>>> + *name = kasprintf(GFP_KERNEL, "%s", rx->name);
>>>> + if (!*name)
>>>> + ret = -ENOMEM;
>>>> + } else
>>>> + ret = scmi_pinctrl_get_name(handle, selector, type, name);
>>>> + out:
>>>> + scmi_xfer_put(handle, t);
>>>> + return ret;
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_list_associations(const struct scmi_handle
>>>> *handle,
>>>> + u32 selector,
>>>> + enum scmi_pinctrl_selector_type type,
>>>> + uint16_t size, unsigned int *array)
>>>> +{
>>>
>>> This is the other functionalities you could implement straight away
>>> using
>>> ph->hops helpers (iterators) but just leave it this way, and I'll
>>> port it later
>>> (once we retested all of this as working with the new API but without
>>> any
>>> ph->hops usage..I think it is safer to change one bit at time... :P)
>>>
>>>> + struct scmi_xfer *t;
>>>> + struct scmi_pinctrl_list_assoc_tx {
>>>> + __le32 identifier;
>>>> + __le32 flags;
>>>> + __le32 index;
>>>> + } *tx;
>>>> + struct scmi_pinctrl_list_assoc_rx {
>>>> +#define RETURNED(x) ((x) & 0xFFF)
>>>> +#define REMAINING(x) ((x) >> 16)
>>>> + __le32 flags;
>>>> + __le16 array[];
>>>> + } *rx;
>>>
>>> Ditto, about struct naming and macros.
>>>
>>>> + u16 tot_num_ret = 0, loop_num_ret;
>>>> + u16 remaining_num_ret;
>>>> + int ret, loop;
>>>> +
>>>> + if (!handle || !array || !size)
>>>> + return -EINVAL;
>>>> +
>>>> + if (type == PIN_TYPE)
>>>> + return -EINVAL;
>>>> +
>>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_LIST_ASSOCIATIONS,
>>>> + SCMI_PROTOCOL_PINCTRL, sizeof(*tx),
>>>> + 0, &t);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + tx = t->tx.buf;
>>>> + rx = t->rx.buf;
>>>> +
>>>> + do {
>>>> + tx->identifier = cpu_to_le32(selector);
>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>> + tx->index = cpu_to_le32(tot_num_ret);
>>>> +
>>>> + ret = scmi_do_xfer(handle, t);
>>>> + if (ret)
>>>> + break;
>>>> +
>>>> + loop_num_ret = le32_to_cpu(RETURNED(rx->flags));
>>>> + remaining_num_ret = le32_to_cpu(REMAINING(rx->flags));
>>>> +
>>>> + for (loop = 0; loop < loop_num_ret; loop++) {
>>>> + if (tot_num_ret + loop >= size) {
>>>> + ret = -EMSGSIZE;
>>>> + goto out;
>>>> + }
>>>> +
>>>> + array[tot_num_ret + loop] =
>>>> + le16_to_cpu(rx->array[loop]);
>>>> + }
>>>> +
>>>> + tot_num_ret += loop_num_ret;
>>>> +
>>>> + scmi_reset_rx_to_maxsz(handle, t);
>>>> + } while (remaining_num_ret > 0);
>>>> +out:
>>>> + scmi_xfer_put(handle, t);
>>>> + return ret;
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_request_config(const struct scmi_handle
>>>> *handle,
>>>> + u32 selector,
>>>> + enum scmi_pinctrl_selector_type type,
>>>> + u32 *config)
>>>> +{
>>>> + struct scmi_xfer *t;
>>>> + struct scmi_conf_tx *tx;
>>>> + __le32 *packed_config;
>>>> + u32 attributes = 0;
>>>> + int ret;
>>>> +
>>>> + if (!handle || !config || type == FUNCTION_TYPE)
>>>> + return -EINVAL;
>>>> +
>>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_GET,
>>>> + SCMI_PROTOCOL_PINCTRL,
>>>> + sizeof(*tx), sizeof(*packed_config), &t);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + tx = t->tx.buf;
>>>> + packed_config = t->rx.buf;
>>>> + tx->identifier = cpu_to_le32(selector);
>>>> + attributes = SET_TYPE_BITS(attributes, type);
>>>> + attributes = SET_CONFIG(attributes, *config);
>>>> +
>>>> + tx->attributes = cpu_to_le32(attributes);
>>>> +
>>>> + ret = scmi_do_xfer(handle, t);
>>>> +
>>>> + if (!ret)
>>>> + *config = le32_to_cpu(*packed_config);
>>>> +
>>>> + scmi_xfer_put(handle, t);
>>>> + return ret;
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_get_config(const struct scmi_handle
>>>> *handle, u32 pin,
>>>> + u32 *config)
>>>> +{
>>>> + return scmi_pinctrl_request_config(handle, pin, PIN_TYPE, config);
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_apply_config(const struct scmi_handle *handle,
>>>> + u32 selector,
>>>> + enum scmi_pinctrl_selector_type type,
>>>> + u32 config)
>>>> +{
>>>> + struct scmi_xfer *t;
>>>> + struct scmi_conf_tx *tx;
>>>> + u32 attributes = 0;
>>>> + int ret;
>>>> +
>>>> + if (!handle || type == FUNCTION_TYPE)
>>>> + return -EINVAL;
>>>> +
>>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_SET,
>>>> + SCMI_PROTOCOL_PINCTRL,
>>>> + sizeof(*tx), 0, &t);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + tx = t->tx.buf;
>>>> + tx->identifier = cpu_to_le32(selector);
>>>> + attributes = SET_TYPE_BITS(attributes, type);
>>>> + attributes = SET_CONFIG(attributes, config);
>>>> + tx->attributes = cpu_to_le32(attributes);
>>>> +
>>>> + ret = scmi_do_xfer(handle, t);
>>>> +
>>>> + scmi_xfer_put(handle, t);
>>>> + return ret;
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_set_config(const struct scmi_handle
>>>> *handle, u32 pin,
>>>> + u32 config)
>>>> +{
>>>> + return scmi_pinctrl_apply_config(handle, pin, PIN_TYPE, config);
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_get_config_group(const struct scmi_handle
>>>> *handle,
>>>> + u32 group, u32 *config)
>>>> +{
>>>> + return scmi_pinctrl_request_config(handle, group, GROUP_TYPE,
>>>> config);
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_set_config_group(const struct scmi_handle
>>>> *handle,
>>>> + u32 group, u32 config)
>>>> +{
>>>> + return scmi_pinctrl_apply_config(handle, group, GROUP_TYPE,
>>>> config);
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_function_select(const struct scmi_handle
>>>> *handle,
>>>> + u32 identifier,
>>>> + enum scmi_pinctrl_selector_type type,
>>>> + u32 function_id)
>>>> +{
>>>> + struct scmi_xfer *t;
>>>> + struct scmi_func_set_tx {
>>>> + __le32 identifier;
>>>> + __le32 function_id;
>>>> + __le32 flags;
>>>> + } *tx;
>>>
>>> Ditto.
>>>
>>>> + int ret;
>>>> +
>>>> + if (!handle || type == FUNCTION_TYPE)
>>>> + return -EINVAL;
>>>> +
>>>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_FUNCTION_SELECT,
>>>> + SCMI_PROTOCOL_PINCTRL,
>>>> + sizeof(*tx), 0, &t);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + tx = t->tx.buf;
>>>> + tx->identifier = cpu_to_le32(identifier);
>>>> + tx->function_id = cpu_to_le32(function_id);
>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>> +
>>>> + ret = scmi_do_xfer(handle, t);
>>>> + scmi_xfer_put(handle, t);
>>>> +
>>>> + return ret;
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_request(const struct scmi_handle *handle,
>>>> + u32 identifier,
>>>> + enum scmi_pinctrl_selector_type type)
>>>> +{
>>>> + struct scmi_xfer *t;
>>>> + int ret;
>>>> + struct scmi_request_tx {
>>>> + __le32 identifier;
>>>> + __le32 flags;
>>>> + } *tx;
>>>> +
>>>
>>> Ditto.
>>>
>>>> + if (!handle || type == FUNCTION_TYPE)
>>>> + return -EINVAL;
>>>> +
>>>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_REQUEST,
>>>> SCMI_PROTOCOL_PINCTRL,
>>>> + sizeof(*tx), 0, &t);
>>>> +
>>>> + tx = t->tx.buf;
>>>> + tx->identifier = identifier;
>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>> +
>>>> + ret = scmi_do_xfer(handle, t);
>>>> + scmi_xfer_put(handle, t);
>>>> +
>>>> + return ret;
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_request_pin(const struct scmi_handle
>>>> *handle, u32 pin)
>>>> +{
>>>> + return scmi_pinctrl_request(handle, pin, PIN_TYPE);
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_free(const struct scmi_handle *handle, u32
>>>> identifier,
>>>> + enum scmi_pinctrl_selector_type type)
>>>> +{
>>>> + struct scmi_xfer *t;
>>>> + int ret;
>>>> + struct scmi_request_tx {
>>>> + __le32 identifier;
>>>> + __le32 flags;
>>>> + } *tx;
>>>> +
>>> Ditto.
>>>
>>>> + if (!handle || type == FUNCTION_TYPE)
>>>> + return -EINVAL;
>>>> +
>>>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_RELEASE,
>>>> SCMI_PROTOCOL_PINCTRL,
>>>> + sizeof(*tx), 0, &t);
>>>> +
>>>> + tx = t->tx.buf;
>>>> + tx->identifier = identifier;
>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>> +
>>>> + ret = scmi_do_xfer(handle, t);
>>>> + scmi_xfer_put(handle, t);
>>>> +
>>>> + return ret;
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_free_pin(const struct scmi_handle *handle,
>>>> u32 pin)
>>>> +{
>>>> + return scmi_pinctrl_free(handle, pin, PIN_TYPE);
>>>> +}
>>>> +
>>>> +
>>>> +static int scmi_pinctrl_get_group_info(const struct scmi_handle
>>>> *handle,
>>>> + u32 selector,
>>>> + struct scmi_group_info *group)
>>>> +{
>>>> + int ret = 0;
>>>> + struct scmi_pinctrl_info *pi;
>>>> +
>>>> + if (!handle || !handle->pinctrl_priv || !group)
>>>> + return -EINVAL;
>>>> +
>>>> + pi = handle->pinctrl_priv;
>>>> +
>>>> + ret = scmi_pinctrl_attributes(handle, GROUP_TYPE, selector,
>>>> + &group->name,
>>>> + &group->nr_pins);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + if (!group->nr_pins) {
>>>> + dev_err(handle->dev, "Group %d has 0 elements", selector);
>>>> + return -ENODATA;
>>>> + }
>>>> +
>>>> + group->group_pins = devm_kmalloc_array(handle->dev,
>>>> group->nr_pins,
>>>> + sizeof(*group->group_pins),
>>>> + GFP_KERNEL);
>>>
>>> I think you can just use for the array allocation
>>>
>>> devm_kcalloc(dev, n, size, flags)
>>>
>>> and it will add also __GFP_ZERO internally to clear it.
>>> (indeed it calls in turn devm_kmalloc_array)
>>>
>>> ...BUT I think there is a further tricky issue here related to memory
>>> allocation...
>>>
>>> You call this and others function of this kind from some
>>> scmi_pinctrl_ops,
>>> like in scmi_pinctrl_get_group_pins (scmi_pinctrl_ops->get_group_pins),
>>> and then this is in turn called by the SCMI Pinctrl driver via
>>> pinctrl_ops->get_group_pins AND you set a present flag so that you
>>> issue a
>>> PINCTRL_LIST_ASSOCIATIONS and allocate here a new group_pins array just
>>> the first time: but these are never released anywhere, since, even
>>> though
>>> lazily dynamically allocated when asked for, these are static data that
>>> you pass to the caller/user of this protocol and so you cannot release
>>> them anytime soon, indeed.
>>>
>>> The core SCMI stack usually takes care to track and release all the
>>> devm_
>>> resources allocated by the protocol ONLY if they were allocated with
>>> devres
>>> while inside scmi_pinctrl_protocol_init() function.
>>> (see
>>> drivers/firmware/arm-scmi/driver.c:scmi_alloc_init_protocol_instance()
>>> and scmi_protocol_release)
>>>
>>> BUT you do not allocate these arrays inside the protocol-init function,
>>> you allocate them the first time these ops are called at runtime.
>>>
>>> If you unbind/unload all the drivers using this protocol and then reload
>>> them, all the devm_ allocations in protocol_init will be freed and
>>> reallocated BUT these arrays will never be freed (they are boudn to
>>> handle->dev)
>>> and instead they will be reallocated multiple times (present flag
>>> will be cleared
>>> on unload), remained unused and freed finally only when the whole
>>> SCMI stack is
>>> unbind/unloaded.
>>>
>>> You use a present flag to avoid reissuing the same query and
>>> reallocating all the arrays each time a driver calls these
>>> protocol_ops one, but really all these data is available early on at
>>> protocol init time and they are not supposed to change at runtime,
>>> dont they ?
>>>
>>> Even in a virtualized environment, you boot an agent and the SCMI
>>> platform server provides to the agent the list of associations when
>>> queried but then this does not change until the next reboot right ?
>>> (indeed you do not query more than once...)
>>>
>>> The agent can only change the PIN status with CONFIG_SET or
>>> FUNCTION_SELECT or REQUEST the exclusive use of a pin/group, but it is
>>> not that the platform can change the pin/groups associaion for the same
>>> agent at run time, this are static data for the whole life of the agent.
>>>
>>> Am I right ?
>>>
>>> IOW I think there is some potential memory leak on unbind/bind and it
>>> would
>>> be better to query and allocate all of these resources at init time
>>> and keep
>>> them ready to be retrieved by subsequent operations, since the lifetime
>>> of these resources is pretty long and they are basically representing
>>> static data that does not change after the init/probe phases.
>>>
>>> Indeed, all the other protocols usually allocate all the needed
>>> resources and query all the available SCMI resources once for all during
>>> the protocol_init, storing all the retrieved info in some struct *_info
>>> exposed in scmi_protocol.h and then provide some related protocol_ops to
>>> get the number of resources and to retrieve specific domain info
>>> descriptors.
>>> (voltage.c is an example and more on this down below...)
>>>
>>> This way, any dynamic allocation is done during protocol_init, so
>>> it can be automatically freed by the SCMI core once there are no more
>>> users of that protocol, and all of this static info data is queried
>>> and retrieved once for all at protocol initialization time, avoiding
>>> unneeded message exchanges to retrieve always the same data.
>>> (which you avoid anyway with the present flag)
>>>
>>> If you have a good reason to instead perform this sort of lazy
>>> allocation/query performed only at the last minute when someone ask for
>>> that specific resource, you will have to provide also a
>>> .instance_deinit
>>> function to clean anything you allocated out of the .instance_init
>>> routine; but this would seem strange to me since any resource that is
>>> discovered at init will be eventually immediately queried by a driver
>>> which uses this protocol...am I missing something ?
>>>
>>>> + if (!group->group_pins) {
>>>> + ret = -ENOMEM;
>>>> + goto err;
>>>> + }
>>>> +
>>>> + ret = scmi_pinctrl_list_associations(handle, selector, GROUP_TYPE,
>>>> + group->nr_pins, group->group_pins);
>>>> + if (ret)
>>>> + goto err_groups;
>>>> +
>>>> + group->present = true;
>>>> + return 0;
>>>> +
>>>> + err_groups:
>>>> + kfree(group->group_pins);
>>>> + err:
>>>> + kfree(group->name);
>>>> + return ret;
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_get_group_name(const struct scmi_handle
>>>> *handle,
>>>> + u32 selector, const char **name)
>>>> +{
>>>> + int ret;
>>>> + struct scmi_pinctrl_info *pi;
>>>> +
>>>> + if (!handle || !handle->pinctrl_priv || !name)
>>>> + return -EINVAL;
>>>> +
>>>> + pi = handle->pinctrl_priv;
>>>> +
>>>> + if (selector > pi->nr_groups)
>>>> + return -EINVAL;
>>>> +
>>>> + if (!pi->groups[selector].present) {
>>>> + ret = scmi_pinctrl_get_group_info(handle, selector,
>>>> + &pi->groups[selector]);
>>>> + if (ret)
>>>> + return ret;
>>>> + }
>>>> +
>>>> + *name = pi->groups[selector].name;
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_get_group_pins(const struct scmi_handle
>>>> *handle,
>>>> + u32 selector, const unsigned int **pins,
>>>> + unsigned int *nr_pins)
>>>> +{
>>>> + int ret;
>>>> + struct scmi_pinctrl_info *pi;
>>>> +
>>>> + if (!handle || !handle->pinctrl_priv || !pins || !nr_pins)
>>>> + return -EINVAL;
>>>> +
>>>> + pi = handle->pinctrl_priv;
>>>> +
>>>> + if (selector > pi->nr_groups)
>>>> + return -EINVAL;
>>>> +
>>>> + if (!pi->groups[selector].present) {
>>>> + ret = scmi_pinctrl_get_group_info(handle, selector,
>>>> + &pi->groups[selector]);
>>>> + if (ret)
>>>> + return ret;
>>>> + }
>>>> +
>>>> + *pins = pi->groups[selector].group_pins;
>>>> + *nr_pins = pi->groups[selector].nr_pins;
>>>> +
>>>> + return ret;
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_get_function_info(const struct scmi_handle
>>>> *handle,
>>>> + u32 selector,
>>>> + struct scmi_function_info *func)
>>>> +{
>>>> + int ret = 0;
>>>> + struct scmi_pinctrl_info *pi;
>>>> +
>>>> + if (!handle || !handle->pinctrl_priv || !func)
>>>> + return -EINVAL;
>>>> +
>>>> + pi = handle->pinctrl_priv;
>>>> +
>>>> + ret = scmi_pinctrl_attributes(handle, FUNCTION_TYPE, selector,
>>>> + &func->name,
>>>> + &func->nr_groups);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + if (!func->nr_groups) {
>>>> + dev_err(handle->dev, "Function %d has 0 elements", selector);
>>>> + return -ENODATA;
>>>> + }
>>>> +
>>>> + func->groups = devm_kmalloc_array(handle->dev, func->nr_groups,
>>>> + sizeof(*func->groups),
>>>> + GFP_KERNEL);
>>>> + if (!func->groups) {
>>>> + ret = -ENOMEM;
>>>> + goto err;
>>>> + }
>>>> +
>>>> + ret = scmi_pinctrl_list_associations(handle, selector,
>>>> FUNCTION_TYPE,
>>>> + func->nr_groups, func->groups);
>>>> + if (ret)
>>>> + goto err_funcs;
>>>> +
>>>> + func->present = true;
>>>> + return 0;
>>>> +
>>>> + err_funcs:
>>>> + kfree(func->groups);
>>>> + err:
>>>> + kfree(func->name);
>>>> + return ret;
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_get_function_name(const struct scmi_handle
>>>> *handle,
>>>> + u32 selector, const char **name)
>>>> +{
>>>> + int ret;
>>>> + struct scmi_pinctrl_info *pi;
>>>> +
>>>> + if (!handle || !handle->pinctrl_priv || !name)
>>>> + return -EINVAL;
>>>> +
>>>> + pi = handle->pinctrl_priv;
>>>> +
>>>> + if (selector > pi->nr_functions)
>>>> + return -EINVAL;
>>>> +
>>>> + if (!pi->functions[selector].present) {
>>>> + ret = scmi_pinctrl_get_function_info(handle, selector,
>>>> + &pi->functions[selector]);
>>>> + if (ret)
>>>> + return ret;
>>>> + }
>>>> +
>>>> + *name = pi->functions[selector].name;
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_get_function_groups(const struct
>>>> scmi_handle *handle,
>>>> + u32 selector,
>>>> + unsigned int *nr_groups,
>>>> + const unsigned int **groups)
>>>> +{
>>>> + int ret;
>>>> + struct scmi_pinctrl_info *pi;
>>>> +
>>>> + if (!handle || !handle->pinctrl_priv || !groups || !nr_groups)
>>>> + return -EINVAL;
>>>> +
>>>> + pi = handle->pinctrl_priv;
>>>> +
>>>> + if (selector > pi->nr_functions)
>>>> + return -EINVAL;
>>>> +
>>>> + if (!pi->functions[selector].present) {
>>>> + ret = scmi_pinctrl_get_function_info(handle, selector,
>>>> + &pi->functions[selector]);
>>>> + if (ret)
>>>> + return ret;
>>>> + }
>>>> +
>>>> + *groups = pi->functions[selector].groups;
>>>> + *nr_groups = pi->functions[selector].nr_groups;
>>>> +
>>>> + return ret;
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_set_mux(const struct scmi_handle *handle,
>>>> u32 selector,
>>>> + u32 group)
>>>> +{
>>>> + return scmi_pinctrl_function_select(handle, group, GROUP_TYPE,
>>>> + selector);
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_get_pin_info(const struct scmi_handle *handle,
>>>> + u32 selector, struct scmi_pin_info *pin)
>>>> +{
>>>> + int ret = 0;
>>>> + struct scmi_pinctrl_info *pi;
>>>> + unsigned int n_elems;
>>>> +
>>>> + if (!handle || !handle->pinctrl_priv || !pin)
>>>> + return -EINVAL;
>>>> +
>>>> + pi = handle->pinctrl_priv;
>>>> +
>>>> + ret = scmi_pinctrl_attributes(handle, PIN_TYPE, selector,
>>>> + &pin->name,
>>>> + &n_elems);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + if (n_elems != pi->nr_pins) {
>>>> + dev_err(handle->dev, "Wrong pin count expected %d has %d",
>>>> + pi->nr_pins, n_elems);
>>>> + return -ENODATA;
>>>> + }
>>>> +
>>>> + if (*(pin->name) == 0) {
>>>> + dev_err(handle->dev, "Pin name is empty");
>>>> + goto err;
>>>> + }
>>>> +
>>>> + pin->present = true;
>>>> + return 0;
>>>> +
>>>> + err:
>>>> + kfree(pin->name);
>>>> + return ret;
>>>> +}
>>>> +
>>>> +static int scmi_pinctrl_get_pin_name(const struct scmi_handle
>>>> *handle, u32 selector,
>>>> + const char **name)
>>>> +{
>>>> +
>>>> + int ret;
>>>> + struct scmi_pinctrl_info *pi;
>>>> +
>>>> + if (!handle || !handle->pinctrl_priv || !name)
>>>> + return -EINVAL;
>>>> +
>>>> + pi = handle->pinctrl_priv;
>>>> +
>>>> + if (selector > pi->nr_pins)
>>>> + return -EINVAL;
>>>> +
>>>> + if (!pi->pins[selector].present) {
>>>> + ret = scmi_pinctrl_get_pin_info(handle, selector,
>>>> + &pi->pins[selector]);
>>>> + if (ret)
>>>> + return ret;
>>>> + }
>>>> +
>>>> + *name = pi->pins[selector].name;
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +
>>>> +static const struct scmi_pinctrl_ops pinctrl_ops = {
>>>> + .get_groups_count = scmi_pinctrl_get_groups_count,
>>>> + .get_group_name = scmi_pinctrl_get_group_name,
>>>> + .get_group_pins = scmi_pinctrl_get_group_pins,
>>>> + .get_functions_count = scmi_pinctrl_get_functions_count,
>>>> + .get_function_name = scmi_pinctrl_get_function_name,
>>>> + .get_function_groups = scmi_pinctrl_get_function_groups,
>>>> + .set_mux = scmi_pinctrl_set_mux,
>>>> + .get_pin_name = scmi_pinctrl_get_pin_name,
>>>> + .get_pins_count = scmi_pinctrl_get_pins_count,
>>>> + .get_config = scmi_pinctrl_get_config,
>>>> + .set_config = scmi_pinctrl_set_config,
>>>> + .get_config_group = scmi_pinctrl_get_config_group,
>>>> + .set_config_group = scmi_pinctrl_set_config_group,
>>>> + .request_pin = scmi_pinctrl_request_pin,
>>>> + .free_pin = scmi_pinctrl_free_pin
>>>> +};
>>>> +
>>>> +static int scmi_pinctrl_protocol_init(struct scmi_handle *handle)
>>>> +{
>>>> + u32 version;
>>>> + struct scmi_pinctrl_info *pinfo;
>>>> + int ret;
>>>> +
>>>> + if (!handle)
>>>> + return -EINVAL;
>>>> +
>>>> + scmi_version_get(handle, SCMI_PROTOCOL_PINCTRL, &version);
>>>> +
>>>> + dev_dbg(handle->dev, "Pinctrl Version %d.%d\n",
>>>> + PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
>>>> +
>>>> + pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL);
>>>> + if (!pinfo)
>>>> + return -ENOMEM;
>>>> +
>>>> + ret = scmi_pinctrl_attributes_get(handle, pinfo);
>>>> + if (ret)
>>>> + goto free;
>>>> +
>>>> + pinfo->pins = devm_kmalloc_array(handle->dev, pinfo->nr_pins,
>>>> + sizeof(*pinfo->pins),
>>>> + GFP_KERNEL | __GFP_ZERO);
>>>
>>> devm_kcalloc() zeroes on its own
>>>
>>>> + if (!pinfo->pins) {
>>>> + ret = -ENOMEM;
>>>> + goto free;
>>>> + }
>>>> +
>>>> + pinfo->groups = devm_kmalloc_array(handle->dev, pinfo->nr_groups,
>>>> + sizeof(*pinfo->groups),
>>>> + GFP_KERNEL | __GFP_ZERO);
>>>
>>> Ditto.
>>>> + if (!pinfo->groups) {
>>>> + ret = -ENOMEM;
>>>> + goto free;
>>>> + }
>>>> +
>>>> + pinfo->functions = devm_kmalloc_array(handle->dev,
>>>> pinfo->nr_functions,
>>>> + sizeof(*pinfo->functions),
>>>> + GFP_KERNEL | __GFP_ZERO);
>>>> + if (!pinfo->functions) {
>>>> + ret = -ENOMEM;
>>>> + goto free;
>>>> + }
>>>> +
>>>> + pinfo->version = version;
>>>> + handle->pinctrl_ops = &pinctrl_ops;
>>>> + handle->pinctrl_priv = pinfo;
>>>> +
>>>> + return 0;
>>>> +free:
>>>> + if (pinfo) {
>>>> + devm_kfree(handle->dev, pinfo->pins);
>>>> + devm_kfree(handle->dev, pinfo->functions);
>>>> + devm_kfree(handle->dev, pinfo->groups);
>>>> + }
>>>
>>> These frees are really not needed...if this function return failure any
>>> devres allocated in it is freed by the SCMI core. (as said above...in a
>>> recent kernel with the new API of course)
>>>
>>>> +
>>>> + devm_kfree(handle->dev, pinfo);
>>>> +
>>>> + return ret;
>>>> +}
>>>> +
>>>> +DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_PINCTRL,
>>>> pinctrl)
>>>> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
>>>> index 815095326e2d..68add4d06e8c 100644
>>>> --- a/drivers/pinctrl/Kconfig
>>>> +++ b/drivers/pinctrl/Kconfig
>>>> @@ -431,4 +431,13 @@ config PINCTRL_EQUILIBRIUM
>>>> pin functions, configure GPIO attributes for LGM SoC pins.
>>>> Pinmux and
>>>> pinconf settings are retrieved from device tree.
>>>> +config PINCTRL_SCMI
>>>> + bool "Pinctrl driver controlled via SCMI interface"
>>>> + depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
>>>> + help
>>>> + This driver provides support for pinctrl which is controlled
>>>> + by firmware that implements the SCMI interface.
>>>> + It uses SCMI Message Protocol to interact with the
>>>> + firmware providing all the pinctrl controls.
>>>> +
>>>
>>> This does NOT belong to this patch, but to the next right ?
>>>
>>>> endif
>>>> diff --git a/include/linux/scmi_protocol.h
>>>> b/include/linux/scmi_protocol.h
>>>> index 9cd312a1ff92..6a909ef3bf51 100644
>>>> --- a/include/linux/scmi_protocol.h
>>>> +++ b/include/linux/scmi_protocol.h
>>>> @@ -12,7 +12,8 @@
>>>> #include <linux/notifier.h>
>>>> #include <linux/types.h>
>>>> -#define SCMI_MAX_STR_SIZE 16
>>>> +#define SCMI_MAX_STR_SIZE 16
>>>> +#define SCMI_MAX_STR_EXT_SIZE 64
>>>
>>> This is handled as part of how the extended names are handled with
>>> ph->hops
>>> in a common way, as I was saying, so please move this if you need it in
>>> the protocol code, then I'll port to the ph->hops interface and clean
>>> up.
>>>
>>>> #define SCMI_MAX_NUM_RATES 16
>>>> /**
>>>> @@ -252,6 +253,55 @@ struct scmi_notify_ops {
>>>> struct notifier_block *nb);
>>>> };
>>>> +/**
>>>> + * struct scmi_pinctrl_ops - represents the various operations
>>>> provided
>>>> + * by SCMI Pinctrl Protocol
>>>> + *
>>>> + * @get_groups_count: returns count of the registered groups
>>>> + * @get_group_name: returns group name by index
>>>> + * @get_group_pins: returns the set of pins, assigned to the
>>>> specified group
>>>> + * @get_functions_count: returns count of the registered fucntions
>>>> + * @get_function_name: returns function name by indes
>>>> + * @get_function_groups: returns the set of groups, assigned to the
>>>> specified
>>>> + * function
>>>> + * @set_mux: set muxing function for groups of pins
>>>> + * @get_pins: returns the set of pins, registered in driver
>>>> + * @get_config: returns configuration parameter for pin
>>>> + * @set_config: sets the configuration parameter for pin
>>>> + * @get_config_group: returns the configuration parameter for a
>>>> group of pins
>>>> + * @set_config_group: sets the configuration parameter for a groups
>>>> of pins
>>>> + * @request_pin: aquire pin before selecting mux setting
>>>> + * @free_pin: frees pin, acquired by request_pin call
>>>> + */
>>>> +struct scmi_pinctrl_ops {
>>>> + int (*get_groups_count)(const struct scmi_handle *handle);
>>>> + int (*get_group_name)(const struct scmi_handle *handles, u32
>>>> selector,
>>>> + const char **name);
>>>> + int (*get_group_pins)(const struct scmi_handle *handle, u32
>>>> selector,
>>>> + const unsigned int **pins, unsigned int *nr_pins);
>>>> + int (*get_functions_count)(const struct scmi_handle *handle);
>>>> + int (*get_function_name)(const struct scmi_handle *handle, u32
>>>> selector,
>>>> + const char **name);
>>>> + int (*get_function_groups)(const struct scmi_handle *handle,
>>>> + u32 selector, unsigned int *nr_groups,
>>>> + const unsigned int **groups);
>>>> + int (*set_mux)(const struct scmi_handle *handle, u32 selector,
>>>> + u32 group);
>>>> + int (*get_pin_name)(const struct scmi_handle *handle, u32
>>>> selector,
>>>> + const char **name);
>>>> + int (*get_pins_count)(const struct scmi_handle *handle);
>>>> + int (*get_config)(const struct scmi_handle *handle, u32 pin,
>>>> + u32 *config);
>>>> + int (*set_config)(const struct scmi_handle *handle, u32 pin,
>>>> + u32 config);
>>>> + int (*get_config_group)(const struct scmi_handle *handle, u32 pin,
>>>> + u32 *config);
>>>> + int (*set_config_group)(const struct scmi_handle *handle, u32 pin,
>>>> + u32 config);
>>>> + int (*request_pin)(const struct scmi_handle *handle, u32 pin);
>>>> + int (*free_pin)(const struct scmi_handle *handle, u32 pin);
>>>> +};
>>>> +
>>>
>>> As mentioned above, here you could drop a lot of this
>>> get_X_count/name/pins
>>> and instead expose a few of the internal proocol struct scmi__X_info
>>> and then
>>> provide just a mean to query how many resource are there and then get
>>> the info
>>> descriptor you want for the specific domain_id, i.e.:
>>>
>>> int (*num_domains_get)(ph, type)
>>> void *(*info_get)(ph, type, domain_id);
>>>
>>> Thanks,
>>> Cristian
>>>
>>>
>>> _______________________________________________
>>> linux-arm-kernel mailing list
>>> [email protected]
>>> https://urldefense.com/v3/__http://lists.infradead.org/mailman/listinfo/linux-arm-kernel__;!!GF_29dbcQIUBPA!y2hR3PEGGxiPjVeXBcgGyV03DPDhzgUKR0uHvsTpiafKgBar8Egc6oOOs-IkFIquhSf-qBzltqEMyzRZrZi7qlk$
>>> [lists[.]infradead[.]org]
On 4/21/2023 5:48 PM, Oleksii Moisieiev wrote:
> Hi Cristian,
>
> On 21.04.23 12:30, Cristian Marussi wrote:
>> On Fri, Apr 21, 2023 at 08:40:47AM +0000, Oleksii Moisieiev wrote:
>>> Hi Peng Fan,
>>>
>>> On 17.04.23 05:55, Peng Fan wrote:
>>>>
>>>> On 4/13/2023 6:04 AM, Cristian Marussi wrote:
>>>>> On Fri, Apr 07, 2023 at 10:18:27AM +0000, Oleksii Moisieiev wrote:
>>>>>> Implementation of the SCMI client driver, which implements
>>>>>> PINCTRL_PROTOCOL. This protocol has ID 19 and is described
>>>>>> in the latest DEN0056 document.
>>>>> Hi,
>>>>>
>>>>>> This protocol is part of the feature that was designed to
>>>>>> separate the pinctrl subsystem from the SCP firmware.
>>>>>> The idea is to separate communication of the pin control
>>>>>> subsystem with the hardware to SCP firmware
>>>>>> (or a similar system, such as ATF), which provides an interface
>>>>>> to give the OS ability to control the hardware through SCMI protocol.
>>>>>> This is a generic driver that implements SCMI protocol,
>>>>>> independent of the platform type.
>>>>>>
>>>>>> DEN0056 document:
>>>>>> https://urldefense.com/v3/__https://developer.arm.com/documentation/den0056/latest__;!!GF_29dbcQIUBPA!y2hR3PEGGxiPjVeXBcgGyV03DPDhzgUKR0uHvsTpiafKgBar8Egc6oOOs-IkFIquhSf-qBzltqEMyzRZHq8eC4g$
>>>>>> [developer[.]arm[.]com]
>>>>>>
>>>>> No need to specify all of this in the commit message, just a note that
>>>>> you are adding a new SCMIv3.2 Pincontrol protocol, highlighting anything
>>>>> that has been left out in this patch (if any) will be enough.
>>>> Is it possible to extend the spec to support multilple uint32_t for PIN
>>>> CONFIG SET?
>>>>
>>>> With only one uint32_t could not satisfy i.MX requirement.
>>>>
>>>> Thanks,
>>>> Peng.
>>>>
>>> IIUC you are expecting to have an ability to set some kind of array of
>>> uint32_t config values to some specific ConfigType?
>>>
>>> I'm not sure if it's supported by pintctrl subsystem right now. I was
>>> unable to find an example in the existing device-tree pinctrl bindings.
>>> This makes me think that this kind of binding is OEM specific.
>>>
>>> Maybe it can be implemented by adding new IDs to OEM specific range
>>> (192-255) which is reserved for OEM specific units (See Table 23 of
>>> DEN0056E).
>>>
>> If I understood correctly the aim of Peng multi-valued request, I think
>> that even if Linux does not support using this kind of multiple valued
>> requests (as of now), if it is useful or required by some of the possibly
>> supported hardware, it should be described and allowed by the specification
>> and supported by the core SCMI protocol support at least, while the pinctrl
>> SCMI driver can ignore this and keep using a one-sized array protocol_ops
>> call internally (since it cannot do any different anyway as of now)
>>
>> IOW I dont think we should model too strictly the SCMI spec against only
>> what the Linux pinctrl subsystem support today, since Linux it is just
>> really only one of the possible SCMI agents and Linux implementation itself
>> can possibly change: it is better to model the spec on the HW requirements
>> or the possible usage patterns across all the possibly participating agents.
>>
>> As an example, for similar reasons, when the SCMI Voltage protocol was added
>> to the spec, at the very last minute, a change was made to the spec to allow
>> for negative voltages, even though the Linux regulator subsystem was not
>> and still is not supporting at all negative voltages as of now; so basically
>> the SCMI voltage protocol API now exposes a per-domain flag (negative_volts_allowed),
>> that allows any kind of voltage domain to be enumerated and handled at the SCMI
>> spec and core layer but that also allows any SCMI driver user, like the SCMI
>> Regulator driver, to decide on his own if negative voltages domains can be
>> supported: indeed the scmi-regulator driver just skips the initialization of
>> any voltage domain that is found to be describing negative voltages.
>>
>> Here is a bit different, it is more of an optimization in the call path
>> than an HW difference, but I would follow the same approach: with the
>> SCMI spec and the core SCMI stack (the protocol) that supports a multi-uint32
>> call as a general case, if useful for some scenarios, and instead the SCMI
>> pinctrl driver that just ignores this possibility and keep using a single-value
>> array anyway....then, it will be up to the guys leveraging this multi-valued
>> call to come up with a way to use it on their systems, possibly maybe contributing
>> back to upstream any needed modification if general enough
>> (not sure about the details of how this multi-vals operation should be...we'll have
>> to discuss that about the spec all together I think.)
>>
>> In any case, I would definitely NOT relegate such possibility to vendor space,
>> since it is something generic and, especially being just (as it seems to me) an
>> optimization on the call path at the end, it will just lead to uneeded duplication
>> of functionalities in the vendor implementation of stuff that it is already
>> very slightly differently supported by the standard.
>>
>> ...just my opinion anyway, I'll happily let other guys in this thread discuss and
>> decide about this :P
>>
>> Thanks,
>> Cristian
>
> That sounds reasonable for me, although I can't imagine the use case of
> multi-valued config values (most likely this is the problem of my
> imagination). So I'd appreciate if Peng Fan could provide us with some
> examples.
>
> From my standpoint - ConfigTypes are meant to be simple value because
> they are mostly related to the electronic properties. But I agree that
> protocol should be platform-agnostic.
>
> It will be great if Peng Fan could provide some examples, so we can
> think about the best solution.
Just replied here:
https://lore.kernel.org/all/[email protected]/
Thanks,
Peng.
>
> Best regards,
>
> Oleksii
On 24.04.23 04:52, Peng Fan wrote:
>
>
> On 4/21/2023 4:40 PM, Oleksii Moisieiev wrote:
>> Hi Peng Fan,
>>
>> On 17.04.23 05:55, Peng Fan wrote:
>>>
>>>
>>> On 4/13/2023 6:04 AM, Cristian Marussi wrote:
>>>> On Fri, Apr 07, 2023 at 10:18:27AM +0000, Oleksii Moisieiev wrote:
>>>>> Implementation of the SCMI client driver, which implements
>>>>> PINCTRL_PROTOCOL. This protocol has ID 19 and is described
>>>>> in the latest DEN0056 document.
>>>>
>>>> Hi,
>>>>
>>>>> This protocol is part of the feature that was designed to
>>>>> separate the pinctrl subsystem from the SCP firmware.
>>>>> The idea is to separate communication of the pin control
>>>>> subsystem with the hardware to SCP firmware
>>>>> (or a similar system, such as ATF), which provides an interface
>>>>> to give the OS ability to control the hardware through SCMI protocol.
>>>>> This is a generic driver that implements SCMI protocol,
>>>>> independent of the platform type.
>>>>>
>>>>> DEN0056 document:
>>>>> https://urldefense.com/v3/__https://developer.arm.com/documentation/den0056/latest__;!!GF_29dbcQIUBPA!y2hR3PEGGxiPjVeXBcgGyV03DPDhzgUKR0uHvsTpiafKgBar8Egc6oOOs-IkFIquhSf-qBzltqEMyzRZHq8eC4g$
>>>>>
>>>>> [developer[.]arm[.]com]
>>>>>
>>>>
>>>> No need to specify all of this in the commit message, just a note that
>>>> you are adding a new SCMIv3.2 Pincontrol protocol, highlighting
>>>> anything
>>>> that has been left out in this patch (if any) will be enough.
>>>
>>> Is it possible to extend the spec to support multilple uint32_t for PIN
>>> CONFIG SET?
>>>
>>> With only one uint32_t could not satisfy i.MX requirement.
>>>
>>> Thanks,
>>> Peng.
>>>
>> IIUC you are expecting to have an ability to set some kind of array of
>> uint32_t config values to some specific ConfigType?
>>
>> I'm not sure if it's supported by pintctrl subsystem right now. I was
>> unable to find an example in the existing device-tree pinctrl bindings.
>> This makes me think that this kind of binding is OEM specific.
>
> Look at arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts
>
> Take lpuart for example:
> MX93_PAD_UART1_RXD__LPUART1_RX 0x31e
>
> The macro MX93_PAD_UART1_RXD__LPUART1_RX is an array.
>
> i.MX pinctrl driver use
> mux reg, mux value, select reg, select value, pad reg, pad value
>
> pinctrl driver will be handled by SCMI firmware for new i.MX SoC.
>
> With current spec, only one uint32 is low speed. So if the spec
> could extended to support more uint32 with pin config set,
> we could use one SCMI call to support i.MX pinctrl.
>
> Thanks
> Peng.
>
Hi Peng Fan,
Thank you for the example.
According to the comment for the function imx_pinctrl_parse_pin_mmio:
/*
* Each pin represented in fsl,pins consists of a number of u32 PIN_FUNC_ID
* and 1 u32 CONFIG, the total size is PIN_FUNC_ID + CONFIG for each pin.
* For generic_pinconf case, there's no extra u32 CONFIG.
*
* PIN_FUNC_ID format:
* Default:
* <mux_reg conf_reg input_reg mux_mode input_val>
* SHARE_MUX_CONF_REG:
* <mux_conf_reg input_reg mux_mode input_val>
* IMX_USE_SCU:
* <pin_id mux_mode>
*/
IIUC this says that in scu format it is expected to use <pin_id
mux_mode>. This fit's protocol expectations.
The idea was to make all platform specific implementation, such as
<mux_reg conf_reg input_reg mux_mode input_val>
on the SCP Firmware side and hide it under Pin ID and Config Type so
Client side can apply generic config in the device tree.
Can't you use scu format here? Or execute
imx_pinconf_decode_generic_config on SCMI server side?
Best regards,
Oleksii.
>>
>> Maybe it can be implemented by adding new IDs to OEM specific range
>> (192-255) which is reserved for OEM specific units (See Table 23 of
>> DEN0056E).
>>
>> Best regards,
>>
>> Oleksii
>>
>>
>>>> You can look at the very first commit logs of existing protos as an
>>>> example like: drivers/firmware/arm_scmi/powercap.c
>>>>
>>>> Some more comments down below, I'll mostly skip anything related to
>>>> the
>>>> SCMI API change I mentioned before...
>>>>
>>>> I'll also wont comment on more trivial stuff related to style, BUT
>>>> there
>>>> are lots of them: you should run
>>>>
>>>> ./scripts/checkpatch.pl --strict <your-git-format-patch-file>
>>>>
>>>> for each patch in the series. (and fix accordingly..spacing,
>>>> brackets...etc)
>>>>
>>>>> Signed-off-by: Oleksii Moisieiev <[email protected]>
>>>>> ---
>>>>> MAINTAINERS | 6 +
>>>>> drivers/firmware/arm_scmi/Makefile | 2 +-
>>>>> drivers/firmware/arm_scmi/common.h | 1 +
>>>>> drivers/firmware/arm_scmi/driver.c | 3 +
>>>>> drivers/firmware/arm_scmi/pinctrl.c | 905
>>>>> ++++++++++++++++++++++++++++
>>>>> drivers/pinctrl/Kconfig | 9 +
>>>>> include/linux/scmi_protocol.h | 58 +-
>>>>> 7 files changed, 982 insertions(+), 2 deletions(-)
>>>>> create mode 100644 drivers/firmware/arm_scmi/pinctrl.c
>>>>>
>>>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>>>> index 281de213ef47..abc543fd7544 100644
>>>>> --- a/MAINTAINERS
>>>>> +++ b/MAINTAINERS
>>>>> @@ -16961,6 +16961,12 @@ F: drivers/reset/reset-scmi.c
>>>>> F: include/linux/sc[mp]i_protocol.h
>>>>> F: include/trace/events/scmi.h
>>>>> +PINCTRL DRIVER FOR SYSTEM CONTROL & POWER/MANAGEMENT INTERFACE
>>>>> (SCPI/SCMI)
>>>>> +M: Oleksii Moisieiev <[email protected]>
>>>>> +L: [email protected]
>>>>> +S: Maintained
>>>>> +F: drivers/firmware/arm_scmi/pinctrl.c
>>>>> +
>>>>> SYSTEM RESET/SHUTDOWN DRIVERS
>>>>> M: Sebastian Reichel <[email protected]>
>>>>> L: [email protected]
>>>>> diff --git a/drivers/firmware/arm_scmi/Makefile
>>>>> b/drivers/firmware/arm_scmi/Makefile
>>>>> index bc0d54f8e861..5cec357fbfa8 100644
>>>>> --- a/drivers/firmware/arm_scmi/Makefile
>>>>> +++ b/drivers/firmware/arm_scmi/Makefile
>>>>> @@ -4,7 +4,7 @@ scmi-driver-y = driver.o notify.o
>>>>> scmi-transport-y = shmem.o
>>>>> scmi-transport-$(CONFIG_MAILBOX) += mailbox.o
>>>>> scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o
>>>>> -scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o
>>>>> system.o
>>>>> +scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o
>>>>> system.o pinctrl.o
>>>>> scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y)
>>>>> $(scmi-protocols-y) \
>>>>> $(scmi-transport-y)
>>>>> obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
>>>>> diff --git a/drivers/firmware/arm_scmi/common.h
>>>>> b/drivers/firmware/arm_scmi/common.h
>>>>> index 65063fa948d4..8bbb404abe8d 100644
>>>>> --- a/drivers/firmware/arm_scmi/common.h
>>>>> +++ b/drivers/firmware/arm_scmi/common.h
>>>>> @@ -170,6 +170,7 @@ DECLARE_SCMI_REGISTER_UNREGISTER(power);
>>>>> DECLARE_SCMI_REGISTER_UNREGISTER(reset);
>>>>> DECLARE_SCMI_REGISTER_UNREGISTER(sensors);
>>>>> DECLARE_SCMI_REGISTER_UNREGISTER(system);
>>>>> +DECLARE_SCMI_REGISTER_UNREGISTER(pinctrl);
>>>>> #define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(id, name) \
>>>>> int __init scmi_##name##_register(void) \
>>>>> diff --git a/drivers/firmware/arm_scmi/driver.c
>>>>> b/drivers/firmware/arm_scmi/driver.c
>>>>> index 3dfd8b6a0ebf..fb9525fb3c24 100644
>>>>> --- a/drivers/firmware/arm_scmi/driver.c
>>>>> +++ b/drivers/firmware/arm_scmi/driver.c
>>>>> @@ -743,6 +743,7 @@ static struct scmi_prot_devnames devnames[] = {
>>>>> { SCMI_PROTOCOL_CLOCK, { "clocks" },},
>>>>> { SCMI_PROTOCOL_SENSOR, { "hwmon" },},
>>>>> { SCMI_PROTOCOL_RESET, { "reset" },},
>>>>> + { SCMI_PROTOCOL_PINCTRL, { "pinctrl" },},
>>>>> };
>>>>> static inline void
>>>>> @@ -947,6 +948,7 @@ static int __init scmi_driver_init(void)
>>>>> scmi_reset_register();
>>>>> scmi_sensors_register();
>>>>> scmi_system_register();
>>>>> + scmi_pinctrl_register();
>>>>> return platform_driver_register(&scmi_driver);
>>>>> }
>>>>> @@ -962,6 +964,7 @@ static void __exit scmi_driver_exit(void)
>>>>> scmi_reset_unregister();
>>>>> scmi_sensors_unregister();
>>>>> scmi_system_unregister();
>>>>> + scmi_pinctrl_unregister();
>>>>> platform_driver_unregister(&scmi_driver);
>>>>> }
>>>>> diff --git a/drivers/firmware/arm_scmi/pinctrl.c
>>>>> b/drivers/firmware/arm_scmi/pinctrl.c
>>>>> new file mode 100644
>>>>> index 000000000000..037270d7f39b
>>>>> --- /dev/null
>>>>> +++ b/drivers/firmware/arm_scmi/pinctrl.c
>>>>> @@ -0,0 +1,905 @@
>>>>> +// SPDX-License-Identifier: GPL-2.0
>>>>> +/*
>>>>> + * System Control and Management Interface (SCMI) Pinctrl Protocol
>>>>> + *
>>>>> + * Copyright (C) 2021 EPAM.
>>>>
>>>> nitpick: update (C) years
>>>>
>>>>> + */
>>>>> +
>>>>> +#define pr_fmt(fmt) "SCMI Notifications PINCTRL - " fmt
>>>>> +
>>>>
>>>> This is not needed, no notifs in this proto.
>>>>
>>>>> +#include <linux/scmi_protocol.h>
>>>>> +#include <linux/slab.h>
>>>>> +
>>>>> +#include "common.h"
>>>>> +#include "notify.h"
>>>>
>>>> Notifs not needed, and in the new API world you'll just need a:
>>>>
>>>> #include "protocols.h"
>>>>
>>>>> +
>>>>> +#define SET_TYPE(x) ((x) & 0x3)
>>>>> +
>>>>
>>>> Even if trivial better to use std bitfield.h macros like
>>>> FIELD_GET() BIT() ... etc
>>>>
>>>>> +enum scmi_pinctrl_protocol_cmd {
>>>>> + PINCTRL_ATTRIBUTES = 0x3,
>>>>> + PINCTRL_LIST_ASSOCIATIONS = 0x4,
>>>>> + PINCTRL_CONFIG_GET = 0x5,
>>>>> + PINCTRL_CONFIG_SET = 0x6,
>>>>> + PINCTRL_FUNCTION_SELECT = 0x7,
>>>>> + PINCTRL_REQUEST = 0x8,
>>>>> + PINCTRL_RELEASE = 0x9,
>>>>> + PINCTRL_NAME_GET = 0xa,
>>>>> + PINCTRL_SET_PERMISSIONS = 0xb
>>>>> +};
>>>>> +
>>>>> +enum scmi_pinctrl_selector_type {
>>>>> + PIN_TYPE = 0,
>>>>> + GROUP_TYPE,
>>>>> + FUNCTION_TYPE
>>>>> +};
>>>>> +
>>>>> +struct scmi_group_info {
>>>>> + bool present;
>>>>> + char *name;
>>>>> + unsigned int *group_pins;
>>>>> + unsigned int nr_pins;
>>>>> +};
>>>>> +
>>>>> +struct scmi_function_info {
>>>>> + bool present;
>>>>> + char *name;
>>>>> + unsigned int *groups;
>>>>> + unsigned int nr_groups;
>>>>> +};
>>>>> +
>>>>> +struct scmi_pin_info {
>>>>> + bool present;
>>>>> + char *name;
>>>>> +};
>>>>> +
>>>>> +struct scmi_pinctrl_info {
>>>>> + u32 version;
>>>>> + u16 nr_groups;
>>>>> + u16 nr_functions;
>>>>> + u16 nr_pins;
>>>>
>>>> Since these vars are not related to stricly spaced message fields
>>>> (even though
>>>> derived from such messages) do not use sized types, you can just
>>>> stick with
>>>> unsigned int. (it is also better not to mix sized and unsized types
>>>> in the same
>>>> struct). This also could come handy if these will be exposed to the
>>>> user
>>>> in scmi_protocol.h in the future (more on this down below)
>>>>
>>>>> + struct scmi_group_info *groups;
>>>>> + struct scmi_function_info *functions;
>>>>> + struct scmi_pin_info *pins;
>>>>> +};
>>>>> +
>>>>> +struct scmi_conf_tx {
>>>>> + __le32 identifier;
>>>>> +#define SET_TYPE_BITS(attr, x) (((attr) & 0xFFFFFCFF) | (x & 0x3)
>>>>> << 8)
>>>>> +#define SET_CONFIG(attr, x) (((attr) & 0xFF) | (x & 0xFF))
>>>>
>>>> Use bitfield.h like FIELD_SET / GENMASK etc
>>>>
>>>>> + __le32 attributes;
>>>>> +};
>>>>> +
>>>>> +static int scmi_pinctrl_attributes_get(const struct scmi_handle
>>>>> *handle,
>>>>> + struct scmi_pinctrl_info *pi)
>>>>> +{
>>>>> + int ret;
>>>>> + struct scmi_xfer *t;
>>>>> + struct scmi_msg_pinctrl_protocol_attributes {
>>>>> +#define GROUPS_NR(x) ((x) >> 16)
>>>>> +#define PINS_NR(x) ((x) & 0xffff)
>>>>> + __le32 attributes_low;
>>>>> +#define FUNCTIONS_NR(x) ((x) & 0xffff)
>>>>> + __le32 attributes_high;
>>>>> + } *attr;
>>>>
>>>> For consistency with the rest of the stack (mostly :D), please move
>>>> this struct
>>>> definition and related macros outside in the global scope right after
>>>> command
>>>> enum. (and use bitfield macros ...)
>>>>
>>>>> +
>>>>> + if (!pi)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
>>>>> + SCMI_PROTOCOL_PINCTRL, 0, sizeof(*attr), &t);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + attr = t->rx.buf;
>>>>> +
>>>>> + ret = scmi_do_xfer(handle, t);
>>>>> + if (!ret) {
>>>>> + pi->nr_functions =
>>>>> + le16_to_cpu(FUNCTIONS_NR(attr->attributes_high));
>>>>> + pi->nr_groups =
>>>>> le16_to_cpu(GROUPS_NR(attr->attributes_low));
>>>>> + pi->nr_pins = le16_to_cpu(PINS_NR(attr->attributes_low));
>>>>> + }
>>>>> +
>>>>> + scmi_xfer_put(handle, t);
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_get_groups_count(const struct scmi_handle
>>>>> *handle)
>>>>> +{
>>>>> + struct scmi_pinctrl_info *pi;
>>>>> +
>>>>> + if (!handle || !handle->pinctrl_priv)
>>>>> + return -ENODEV;
>>>>> +
>>>>> + pi = handle->pinctrl_priv;
>>>>> +
>>>>> + return pi->nr_groups;
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_get_pins_count(const struct scmi_handle
>>>>> *handle)
>>>>> +{
>>>>> + struct scmi_pinctrl_info *pi;
>>>>> +
>>>>> + if (!handle || !handle->pinctrl_priv)
>>>>> + return -ENODEV;
>>>>> +
>>>>> + pi = handle->pinctrl_priv;
>>>>> +
>>>>> + return pi->nr_pins;
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_get_functions_count(const struct
>>>>> scmi_handle *handle)
>>>>> +{
>>>>> + struct scmi_pinctrl_info *pi;
>>>>> +
>>>>> + if (!handle || !handle->pinctrl_priv)
>>>>> + return -ENODEV;
>>>>> +
>>>>> + pi = handle->pinctrl_priv;
>>>>> +
>>>>> + return pi->nr_functions;
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_validate_id(const struct scmi_handle
>>>>> *handle,
>>>>> + u32 identifier,
>>>>> + enum scmi_pinctrl_selector_type type)
>>>>> +{
>>>>> + struct scmi_pinctrl_info *pi;
>>>>> +
>>>>> + if (!handle || !handle->pinctrl_priv)
>>>>> + return -ENODEV;
>>>>> +
>>>>> + switch (type) {
>>>>> + case PIN_TYPE:
>>>>> + pi = handle->pinctrl_priv;
>>>>> +
>>>>> + return (identifier < pi->nr_pins) ? 0 : -EINVAL;
>>>>> + case GROUP_TYPE:
>>>>> + return (identifier <
>>>>> + scmi_pinctrl_get_groups_count(handle)) ?
>>>>> + 0 : -EINVAL;
>>>>> + case FUNCTION_TYPE:
>>>>> + return (identifier <
>>>>> + scmi_pinctrl_get_functions_count(handle)) ?
>>>>> + 0 : -EINVAL;
>>>>> + default:
>>>>> + return -EINVAL;
>>>>> + }
>>>>
>>>> Here I would just pick the right value to compare, break and then
>>>> compare and exit, something aroundf the lines of:
>>>>
>>>> case PIN_TYPE:
>>>> ...
>>>> val = pi->nr_pins;
>>>> break;
>>>> ...
>>>> case GROUP_TYPE:
>>>> val = scmi_pinctrl_get_groups_count());
>>>> break;
>>>>
>>>> ....
>>>> ....
>>>> default:
>>>> return -EINVAL;
>>>> }
>>>>
>>>> if (identifier >= val)
>>>> return -EINVAL;
>>>>
>>>> return 0;
>>>> }
>>>>
>>>> ... it's easier to read. What do you think ?
>>>>
>>>>
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_get_name(const struct scmi_handle *handle,
>>>>> + u32 identifier,
>>>>> + enum scmi_pinctrl_selector_type type,
>>>>> + char **name)
>>>>> +{
>>>>
>>>> As said, there is common helper for this, but it will need some small
>>>> adaptation in the SCMI core to work here so keep it as it is, and
>>>> I'll take
>>>> care of this later, if it sounds fine for you.
>>>>
>>>>> + struct scmi_xfer *t;
>>>>> + int ret = 0;
>>>>> + struct scmi_name_tx {
>>>>> + __le32 identifier;
>>>>> + __le32 flags;
>>>>> + } *tx;
>>>>> + struct scmi_name_rx {
>>>>> + __le32 flags;
>>>>> + u8 name[64];
>>>>> + } *rx;
>>>>> +
>>>>> + if (!handle || !name)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_NAME_GET,
>>>>> + SCMI_PROTOCOL_PINCTRL,
>>>>> + sizeof(*tx), sizeof(*rx), &t);
>>>>> +
>>>>> + tx = t->tx.buf;
>>>>> + rx = t->rx.buf;
>>>>> + tx->identifier = identifier;
>>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>>> +
>>>>> + ret = scmi_do_xfer(handle, t);
>>>>> + if (ret)
>>>>> + goto out;
>>>>> +
>>>>> + if (rx->flags) {
>>>>> + ret = -EINVAL;
>>>>> + goto out;
>>>>> + }
>>>>> +
>>>>> + *name = kasprintf(GFP_KERNEL, "%s", rx->name);
>>>>> + if (!*name)
>>>>> + ret = -ENOMEM;
>>>>> + out:
>>>>> + scmi_xfer_put(handle, t);
>>>>> +
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_attributes(const struct scmi_handle *handle,
>>>>> + enum scmi_pinctrl_selector_type type,
>>>>> + u32 selector, char **name,
>>>>> + unsigned int *n_elems)
>>>>> +{
>>>>> + int ret = 0;
>>>>> + struct scmi_xfer *t;
>>>>> + struct scmi_pinctrl_attributes_tx {
>>>>> + __le32 identifier;
>>>>> + __le32 flags;
>>>>> + } *tx;
>>>>> + struct scmi_pinctrl_attributes_rx {
>>>>> +#define EXT_NAME_FLAG(x) ((x) & BIT(31))
>>>>> +#define NUM_ELEMS(x) ((x) & 0xffff)
>>>>> + __le32 attributes;
>>>>> + u8 name[16];
>>>>> + } *rx;
>>>>
>>>> Ditto. Move these defs outside, bitfield.h for macros and try to
>>>> use the
>>>> same naming style for message structs as in other protos, i.e.
>>>>
>>>> for commands: struct scmi_msg_pinctrl_attributes
>>>> for replies: struct scmi_resp_pinctrl_attributes
>>>>
>>>> (or some variations around that...
>>>> scmi_msg_cmd_* scmi_msg_resp_*
>>>>
>>>> we have not been fully consistent really, so I dont want to be
>>>> pedantic here, but we never used tx/rx in message context since
>>>> it is
>>>> already (mis)-used in SCMI channel context...)
>>>>
>>>>> +
>>>>> + if (!handle || !name)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_ATTRIBUTES,
>>>>> + SCMI_PROTOCOL_PINCTRL, sizeof(*tx), sizeof(*rx), &t);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + tx = t->tx.buf;
>>>>> + rx = t->rx.buf;
>>>>> + tx->identifier = selector;
>>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>>> +
>>>>> + ret = scmi_do_xfer(handle, t);
>>>>> + if (ret)
>>>>> + goto out;
>>>>> +
>>>>> + *n_elems = NUM_ELEMS(rx->attributes);
>>>>> +
>>>>> + if (!EXT_NAME_FLAG(rx->attributes)) {
>>>>> + *name = kasprintf(GFP_KERNEL, "%s", rx->name);
>>>>> + if (!*name)
>>>>> + ret = -ENOMEM;
>>>>> + } else
>>>>> + ret = scmi_pinctrl_get_name(handle, selector, type, name);
>>>>> + out:
>>>>> + scmi_xfer_put(handle, t);
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_list_associations(const struct scmi_handle
>>>>> *handle,
>>>>> + u32 selector,
>>>>> + enum scmi_pinctrl_selector_type type,
>>>>> + uint16_t size, unsigned int *array)
>>>>> +{
>>>>
>>>> This is the other functionalities you could implement straight away
>>>> using
>>>> ph->hops helpers (iterators) but just leave it this way, and I'll
>>>> port it later
>>>> (once we retested all of this as working with the new API but without
>>>> any
>>>> ph->hops usage..I think it is safer to change one bit at time... :P)
>>>>
>>>>> + struct scmi_xfer *t;
>>>>> + struct scmi_pinctrl_list_assoc_tx {
>>>>> + __le32 identifier;
>>>>> + __le32 flags;
>>>>> + __le32 index;
>>>>> + } *tx;
>>>>> + struct scmi_pinctrl_list_assoc_rx {
>>>>> +#define RETURNED(x) ((x) & 0xFFF)
>>>>> +#define REMAINING(x) ((x) >> 16)
>>>>> + __le32 flags;
>>>>> + __le16 array[];
>>>>> + } *rx;
>>>>
>>>> Ditto, about struct naming and macros.
>>>>
>>>>> + u16 tot_num_ret = 0, loop_num_ret;
>>>>> + u16 remaining_num_ret;
>>>>> + int ret, loop;
>>>>> +
>>>>> + if (!handle || !array || !size)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + if (type == PIN_TYPE)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_LIST_ASSOCIATIONS,
>>>>> + SCMI_PROTOCOL_PINCTRL, sizeof(*tx),
>>>>> + 0, &t);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + tx = t->tx.buf;
>>>>> + rx = t->rx.buf;
>>>>> +
>>>>> + do {
>>>>> + tx->identifier = cpu_to_le32(selector);
>>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>>> + tx->index = cpu_to_le32(tot_num_ret);
>>>>> +
>>>>> + ret = scmi_do_xfer(handle, t);
>>>>> + if (ret)
>>>>> + break;
>>>>> +
>>>>> + loop_num_ret = le32_to_cpu(RETURNED(rx->flags));
>>>>> + remaining_num_ret = le32_to_cpu(REMAINING(rx->flags));
>>>>> +
>>>>> + for (loop = 0; loop < loop_num_ret; loop++) {
>>>>> + if (tot_num_ret + loop >= size) {
>>>>> + ret = -EMSGSIZE;
>>>>> + goto out;
>>>>> + }
>>>>> +
>>>>> + array[tot_num_ret + loop] =
>>>>> + le16_to_cpu(rx->array[loop]);
>>>>> + }
>>>>> +
>>>>> + tot_num_ret += loop_num_ret;
>>>>> +
>>>>> + scmi_reset_rx_to_maxsz(handle, t);
>>>>> + } while (remaining_num_ret > 0);
>>>>> +out:
>>>>> + scmi_xfer_put(handle, t);
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_request_config(const struct scmi_handle
>>>>> *handle,
>>>>> + u32 selector,
>>>>> + enum scmi_pinctrl_selector_type type,
>>>>> + u32 *config)
>>>>> +{
>>>>> + struct scmi_xfer *t;
>>>>> + struct scmi_conf_tx *tx;
>>>>> + __le32 *packed_config;
>>>>> + u32 attributes = 0;
>>>>> + int ret;
>>>>> +
>>>>> + if (!handle || !config || type == FUNCTION_TYPE)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_GET,
>>>>> + SCMI_PROTOCOL_PINCTRL,
>>>>> + sizeof(*tx), sizeof(*packed_config), &t);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + tx = t->tx.buf;
>>>>> + packed_config = t->rx.buf;
>>>>> + tx->identifier = cpu_to_le32(selector);
>>>>> + attributes = SET_TYPE_BITS(attributes, type);
>>>>> + attributes = SET_CONFIG(attributes, *config);
>>>>> +
>>>>> + tx->attributes = cpu_to_le32(attributes);
>>>>> +
>>>>> + ret = scmi_do_xfer(handle, t);
>>>>> +
>>>>> + if (!ret)
>>>>> + *config = le32_to_cpu(*packed_config);
>>>>> +
>>>>> + scmi_xfer_put(handle, t);
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_get_config(const struct scmi_handle
>>>>> *handle, u32 pin,
>>>>> + u32 *config)
>>>>> +{
>>>>> + return scmi_pinctrl_request_config(handle, pin, PIN_TYPE,
>>>>> config);
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_apply_config(const struct scmi_handle
>>>>> *handle,
>>>>> + u32 selector,
>>>>> + enum scmi_pinctrl_selector_type type,
>>>>> + u32 config)
>>>>> +{
>>>>> + struct scmi_xfer *t;
>>>>> + struct scmi_conf_tx *tx;
>>>>> + u32 attributes = 0;
>>>>> + int ret;
>>>>> +
>>>>> + if (!handle || type == FUNCTION_TYPE)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_SET,
>>>>> + SCMI_PROTOCOL_PINCTRL,
>>>>> + sizeof(*tx), 0, &t);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + tx = t->tx.buf;
>>>>> + tx->identifier = cpu_to_le32(selector);
>>>>> + attributes = SET_TYPE_BITS(attributes, type);
>>>>> + attributes = SET_CONFIG(attributes, config);
>>>>> + tx->attributes = cpu_to_le32(attributes);
>>>>> +
>>>>> + ret = scmi_do_xfer(handle, t);
>>>>> +
>>>>> + scmi_xfer_put(handle, t);
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_set_config(const struct scmi_handle
>>>>> *handle, u32 pin,
>>>>> + u32 config)
>>>>> +{
>>>>> + return scmi_pinctrl_apply_config(handle, pin, PIN_TYPE, config);
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_get_config_group(const struct scmi_handle
>>>>> *handle,
>>>>> + u32 group, u32 *config)
>>>>> +{
>>>>> + return scmi_pinctrl_request_config(handle, group, GROUP_TYPE,
>>>>> config);
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_set_config_group(const struct scmi_handle
>>>>> *handle,
>>>>> + u32 group, u32 config)
>>>>> +{
>>>>> + return scmi_pinctrl_apply_config(handle, group, GROUP_TYPE,
>>>>> config);
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_function_select(const struct scmi_handle
>>>>> *handle,
>>>>> + u32 identifier,
>>>>> + enum scmi_pinctrl_selector_type type,
>>>>> + u32 function_id)
>>>>> +{
>>>>> + struct scmi_xfer *t;
>>>>> + struct scmi_func_set_tx {
>>>>> + __le32 identifier;
>>>>> + __le32 function_id;
>>>>> + __le32 flags;
>>>>> + } *tx;
>>>>
>>>> Ditto.
>>>>
>>>>> + int ret;
>>>>> +
>>>>> + if (!handle || type == FUNCTION_TYPE)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_FUNCTION_SELECT,
>>>>> + SCMI_PROTOCOL_PINCTRL,
>>>>> + sizeof(*tx), 0, &t);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + tx = t->tx.buf;
>>>>> + tx->identifier = cpu_to_le32(identifier);
>>>>> + tx->function_id = cpu_to_le32(function_id);
>>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>>> +
>>>>> + ret = scmi_do_xfer(handle, t);
>>>>> + scmi_xfer_put(handle, t);
>>>>> +
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_request(const struct scmi_handle *handle,
>>>>> + u32 identifier,
>>>>> + enum scmi_pinctrl_selector_type type)
>>>>> +{
>>>>> + struct scmi_xfer *t;
>>>>> + int ret;
>>>>> + struct scmi_request_tx {
>>>>> + __le32 identifier;
>>>>> + __le32 flags;
>>>>> + } *tx;
>>>>> +
>>>>
>>>> Ditto.
>>>>
>>>>> + if (!handle || type == FUNCTION_TYPE)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_REQUEST,
>>>>> SCMI_PROTOCOL_PINCTRL,
>>>>> + sizeof(*tx), 0, &t);
>>>>> +
>>>>> + tx = t->tx.buf;
>>>>> + tx->identifier = identifier;
>>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>>> +
>>>>> + ret = scmi_do_xfer(handle, t);
>>>>> + scmi_xfer_put(handle, t);
>>>>> +
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_request_pin(const struct scmi_handle
>>>>> *handle, u32 pin)
>>>>> +{
>>>>> + return scmi_pinctrl_request(handle, pin, PIN_TYPE);
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_free(const struct scmi_handle *handle, u32
>>>>> identifier,
>>>>> + enum scmi_pinctrl_selector_type type)
>>>>> +{
>>>>> + struct scmi_xfer *t;
>>>>> + int ret;
>>>>> + struct scmi_request_tx {
>>>>> + __le32 identifier;
>>>>> + __le32 flags;
>>>>> + } *tx;
>>>>> +
>>>> Ditto.
>>>>
>>>>> + if (!handle || type == FUNCTION_TYPE)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_RELEASE,
>>>>> SCMI_PROTOCOL_PINCTRL,
>>>>> + sizeof(*tx), 0, &t);
>>>>> +
>>>>> + tx = t->tx.buf;
>>>>> + tx->identifier = identifier;
>>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>>> +
>>>>> + ret = scmi_do_xfer(handle, t);
>>>>> + scmi_xfer_put(handle, t);
>>>>> +
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_free_pin(const struct scmi_handle *handle,
>>>>> u32 pin)
>>>>> +{
>>>>> + return scmi_pinctrl_free(handle, pin, PIN_TYPE);
>>>>> +}
>>>>> +
>>>>> +
>>>>> +static int scmi_pinctrl_get_group_info(const struct scmi_handle
>>>>> *handle,
>>>>> + u32 selector,
>>>>> + struct scmi_group_info *group)
>>>>> +{
>>>>> + int ret = 0;
>>>>> + struct scmi_pinctrl_info *pi;
>>>>> +
>>>>> + if (!handle || !handle->pinctrl_priv || !group)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + pi = handle->pinctrl_priv;
>>>>> +
>>>>> + ret = scmi_pinctrl_attributes(handle, GROUP_TYPE, selector,
>>>>> + &group->name,
>>>>> + &group->nr_pins);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + if (!group->nr_pins) {
>>>>> + dev_err(handle->dev, "Group %d has 0 elements", selector);
>>>>> + return -ENODATA;
>>>>> + }
>>>>> +
>>>>> + group->group_pins = devm_kmalloc_array(handle->dev,
>>>>> group->nr_pins,
>>>>> + sizeof(*group->group_pins),
>>>>> + GFP_KERNEL);
>>>>
>>>> I think you can just use for the array allocation
>>>>
>>>> devm_kcalloc(dev, n, size, flags)
>>>>
>>>> and it will add also __GFP_ZERO internally to clear it.
>>>> (indeed it calls in turn devm_kmalloc_array)
>>>>
>>>> ...BUT I think there is a further tricky issue here related to memory
>>>> allocation...
>>>>
>>>> You call this and others function of this kind from some
>>>> scmi_pinctrl_ops,
>>>> like in scmi_pinctrl_get_group_pins
>>>> (scmi_pinctrl_ops->get_group_pins),
>>>> and then this is in turn called by the SCMI Pinctrl driver via
>>>> pinctrl_ops->get_group_pins AND you set a present flag so that you
>>>> issue a
>>>> PINCTRL_LIST_ASSOCIATIONS and allocate here a new group_pins array
>>>> just
>>>> the first time: but these are never released anywhere, since, even
>>>> though
>>>> lazily dynamically allocated when asked for, these are static data
>>>> that
>>>> you pass to the caller/user of this protocol and so you cannot release
>>>> them anytime soon, indeed.
>>>>
>>>> The core SCMI stack usually takes care to track and release all the
>>>> devm_
>>>> resources allocated by the protocol ONLY if they were allocated with
>>>> devres
>>>> while inside scmi_pinctrl_protocol_init() function.
>>>> (see
>>>> drivers/firmware/arm-scmi/driver.c:scmi_alloc_init_protocol_instance()
>>>> and scmi_protocol_release)
>>>>
>>>> BUT you do not allocate these arrays inside the protocol-init
>>>> function,
>>>> you allocate them the first time these ops are called at runtime.
>>>>
>>>> If you unbind/unload all the drivers using this protocol and then
>>>> reload
>>>> them, all the devm_ allocations in protocol_init will be freed and
>>>> reallocated BUT these arrays will never be freed (they are boudn to
>>>> handle->dev)
>>>> and instead they will be reallocated multiple times (present flag
>>>> will be cleared
>>>> on unload), remained unused and freed finally only when the whole
>>>> SCMI stack is
>>>> unbind/unloaded.
>>>>
>>>> You use a present flag to avoid reissuing the same query and
>>>> reallocating all the arrays each time a driver calls these
>>>> protocol_ops one, but really all these data is available early on at
>>>> protocol init time and they are not supposed to change at runtime,
>>>> dont they ?
>>>>
>>>> Even in a virtualized environment, you boot an agent and the SCMI
>>>> platform server provides to the agent the list of associations when
>>>> queried but then this does not change until the next reboot right ?
>>>> (indeed you do not query more than once...)
>>>>
>>>> The agent can only change the PIN status with CONFIG_SET or
>>>> FUNCTION_SELECT or REQUEST the exclusive use of a pin/group, but it is
>>>> not that the platform can change the pin/groups associaion for the
>>>> same
>>>> agent at run time, this are static data for the whole life of the
>>>> agent.
>>>>
>>>> Am I right ?
>>>>
>>>> IOW I think there is some potential memory leak on unbind/bind and it
>>>> would
>>>> be better to query and allocate all of these resources at init time
>>>> and keep
>>>> them ready to be retrieved by subsequent operations, since the
>>>> lifetime
>>>> of these resources is pretty long and they are basically representing
>>>> static data that does not change after the init/probe phases.
>>>>
>>>> Indeed, all the other protocols usually allocate all the needed
>>>> resources and query all the available SCMI resources once for all
>>>> during
>>>> the protocol_init, storing all the retrieved info in some struct
>>>> *_info
>>>> exposed in scmi_protocol.h and then provide some related
>>>> protocol_ops to
>>>> get the number of resources and to retrieve specific domain info
>>>> descriptors.
>>>> (voltage.c is an example and more on this down below...)
>>>>
>>>> This way, any dynamic allocation is done during protocol_init, so
>>>> it can be automatically freed by the SCMI core once there are no more
>>>> users of that protocol, and all of this static info data is queried
>>>> and retrieved once for all at protocol initialization time, avoiding
>>>> unneeded message exchanges to retrieve always the same data.
>>>> (which you avoid anyway with the present flag)
>>>>
>>>> If you have a good reason to instead perform this sort of lazy
>>>> allocation/query performed only at the last minute when someone ask
>>>> for
>>>> that specific resource, you will have to provide also a
>>>> .instance_deinit
>>>> function to clean anything you allocated out of the .instance_init
>>>> routine; but this would seem strange to me since any resource that is
>>>> discovered at init will be eventually immediately queried by a driver
>>>> which uses this protocol...am I missing something ?
>>>>
>>>>> + if (!group->group_pins) {
>>>>> + ret = -ENOMEM;
>>>>> + goto err;
>>>>> + }
>>>>> +
>>>>> + ret = scmi_pinctrl_list_associations(handle, selector,
>>>>> GROUP_TYPE,
>>>>> + group->nr_pins, group->group_pins);
>>>>> + if (ret)
>>>>> + goto err_groups;
>>>>> +
>>>>> + group->present = true;
>>>>> + return 0;
>>>>> +
>>>>> + err_groups:
>>>>> + kfree(group->group_pins);
>>>>> + err:
>>>>> + kfree(group->name);
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_get_group_name(const struct scmi_handle
>>>>> *handle,
>>>>> + u32 selector, const char **name)
>>>>> +{
>>>>> + int ret;
>>>>> + struct scmi_pinctrl_info *pi;
>>>>> +
>>>>> + if (!handle || !handle->pinctrl_priv || !name)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + pi = handle->pinctrl_priv;
>>>>> +
>>>>> + if (selector > pi->nr_groups)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + if (!pi->groups[selector].present) {
>>>>> + ret = scmi_pinctrl_get_group_info(handle, selector,
>>>>> + &pi->groups[selector]);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + *name = pi->groups[selector].name;
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_get_group_pins(const struct scmi_handle
>>>>> *handle,
>>>>> + u32 selector, const unsigned int **pins,
>>>>> + unsigned int *nr_pins)
>>>>> +{
>>>>> + int ret;
>>>>> + struct scmi_pinctrl_info *pi;
>>>>> +
>>>>> + if (!handle || !handle->pinctrl_priv || !pins || !nr_pins)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + pi = handle->pinctrl_priv;
>>>>> +
>>>>> + if (selector > pi->nr_groups)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + if (!pi->groups[selector].present) {
>>>>> + ret = scmi_pinctrl_get_group_info(handle, selector,
>>>>> + &pi->groups[selector]);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + *pins = pi->groups[selector].group_pins;
>>>>> + *nr_pins = pi->groups[selector].nr_pins;
>>>>> +
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_get_function_info(const struct scmi_handle
>>>>> *handle,
>>>>> + u32 selector,
>>>>> + struct scmi_function_info *func)
>>>>> +{
>>>>> + int ret = 0;
>>>>> + struct scmi_pinctrl_info *pi;
>>>>> +
>>>>> + if (!handle || !handle->pinctrl_priv || !func)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + pi = handle->pinctrl_priv;
>>>>> +
>>>>> + ret = scmi_pinctrl_attributes(handle, FUNCTION_TYPE, selector,
>>>>> + &func->name,
>>>>> + &func->nr_groups);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + if (!func->nr_groups) {
>>>>> + dev_err(handle->dev, "Function %d has 0 elements",
>>>>> selector);
>>>>> + return -ENODATA;
>>>>> + }
>>>>> +
>>>>> + func->groups = devm_kmalloc_array(handle->dev, func->nr_groups,
>>>>> + sizeof(*func->groups),
>>>>> + GFP_KERNEL);
>>>>> + if (!func->groups) {
>>>>> + ret = -ENOMEM;
>>>>> + goto err;
>>>>> + }
>>>>> +
>>>>> + ret = scmi_pinctrl_list_associations(handle, selector,
>>>>> FUNCTION_TYPE,
>>>>> + func->nr_groups, func->groups);
>>>>> + if (ret)
>>>>> + goto err_funcs;
>>>>> +
>>>>> + func->present = true;
>>>>> + return 0;
>>>>> +
>>>>> + err_funcs:
>>>>> + kfree(func->groups);
>>>>> + err:
>>>>> + kfree(func->name);
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_get_function_name(const struct scmi_handle
>>>>> *handle,
>>>>> + u32 selector, const char **name)
>>>>> +{
>>>>> + int ret;
>>>>> + struct scmi_pinctrl_info *pi;
>>>>> +
>>>>> + if (!handle || !handle->pinctrl_priv || !name)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + pi = handle->pinctrl_priv;
>>>>> +
>>>>> + if (selector > pi->nr_functions)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + if (!pi->functions[selector].present) {
>>>>> + ret = scmi_pinctrl_get_function_info(handle, selector,
>>>>> + &pi->functions[selector]);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + *name = pi->functions[selector].name;
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_get_function_groups(const struct
>>>>> scmi_handle *handle,
>>>>> + u32 selector,
>>>>> + unsigned int *nr_groups,
>>>>> + const unsigned int **groups)
>>>>> +{
>>>>> + int ret;
>>>>> + struct scmi_pinctrl_info *pi;
>>>>> +
>>>>> + if (!handle || !handle->pinctrl_priv || !groups || !nr_groups)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + pi = handle->pinctrl_priv;
>>>>> +
>>>>> + if (selector > pi->nr_functions)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + if (!pi->functions[selector].present) {
>>>>> + ret = scmi_pinctrl_get_function_info(handle, selector,
>>>>> + &pi->functions[selector]);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + *groups = pi->functions[selector].groups;
>>>>> + *nr_groups = pi->functions[selector].nr_groups;
>>>>> +
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_set_mux(const struct scmi_handle *handle,
>>>>> u32 selector,
>>>>> + u32 group)
>>>>> +{
>>>>> + return scmi_pinctrl_function_select(handle, group, GROUP_TYPE,
>>>>> + selector);
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_get_pin_info(const struct scmi_handle
>>>>> *handle,
>>>>> + u32 selector, struct scmi_pin_info *pin)
>>>>> +{
>>>>> + int ret = 0;
>>>>> + struct scmi_pinctrl_info *pi;
>>>>> + unsigned int n_elems;
>>>>> +
>>>>> + if (!handle || !handle->pinctrl_priv || !pin)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + pi = handle->pinctrl_priv;
>>>>> +
>>>>> + ret = scmi_pinctrl_attributes(handle, PIN_TYPE, selector,
>>>>> + &pin->name,
>>>>> + &n_elems);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + if (n_elems != pi->nr_pins) {
>>>>> + dev_err(handle->dev, "Wrong pin count expected %d has %d",
>>>>> + pi->nr_pins, n_elems);
>>>>> + return -ENODATA;
>>>>> + }
>>>>> +
>>>>> + if (*(pin->name) == 0) {
>>>>> + dev_err(handle->dev, "Pin name is empty");
>>>>> + goto err;
>>>>> + }
>>>>> +
>>>>> + pin->present = true;
>>>>> + return 0;
>>>>> +
>>>>> + err:
>>>>> + kfree(pin->name);
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +static int scmi_pinctrl_get_pin_name(const struct scmi_handle
>>>>> *handle, u32 selector,
>>>>> + const char **name)
>>>>> +{
>>>>> +
>>>>> + int ret;
>>>>> + struct scmi_pinctrl_info *pi;
>>>>> +
>>>>> + if (!handle || !handle->pinctrl_priv || !name)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + pi = handle->pinctrl_priv;
>>>>> +
>>>>> + if (selector > pi->nr_pins)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + if (!pi->pins[selector].present) {
>>>>> + ret = scmi_pinctrl_get_pin_info(handle, selector,
>>>>> + &pi->pins[selector]);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + *name = pi->pins[selector].name;
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +
>>>>> +static const struct scmi_pinctrl_ops pinctrl_ops = {
>>>>> + .get_groups_count = scmi_pinctrl_get_groups_count,
>>>>> + .get_group_name = scmi_pinctrl_get_group_name,
>>>>> + .get_group_pins = scmi_pinctrl_get_group_pins,
>>>>> + .get_functions_count = scmi_pinctrl_get_functions_count,
>>>>> + .get_function_name = scmi_pinctrl_get_function_name,
>>>>> + .get_function_groups = scmi_pinctrl_get_function_groups,
>>>>> + .set_mux = scmi_pinctrl_set_mux,
>>>>> + .get_pin_name = scmi_pinctrl_get_pin_name,
>>>>> + .get_pins_count = scmi_pinctrl_get_pins_count,
>>>>> + .get_config = scmi_pinctrl_get_config,
>>>>> + .set_config = scmi_pinctrl_set_config,
>>>>> + .get_config_group = scmi_pinctrl_get_config_group,
>>>>> + .set_config_group = scmi_pinctrl_set_config_group,
>>>>> + .request_pin = scmi_pinctrl_request_pin,
>>>>> + .free_pin = scmi_pinctrl_free_pin
>>>>> +};
>>>>> +
>>>>> +static int scmi_pinctrl_protocol_init(struct scmi_handle *handle)
>>>>> +{
>>>>> + u32 version;
>>>>> + struct scmi_pinctrl_info *pinfo;
>>>>> + int ret;
>>>>> +
>>>>> + if (!handle)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + scmi_version_get(handle, SCMI_PROTOCOL_PINCTRL, &version);
>>>>> +
>>>>> + dev_dbg(handle->dev, "Pinctrl Version %d.%d\n",
>>>>> + PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
>>>>> +
>>>>> + pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL);
>>>>> + if (!pinfo)
>>>>> + return -ENOMEM;
>>>>> +
>>>>> + ret = scmi_pinctrl_attributes_get(handle, pinfo);
>>>>> + if (ret)
>>>>> + goto free;
>>>>> +
>>>>> + pinfo->pins = devm_kmalloc_array(handle->dev, pinfo->nr_pins,
>>>>> + sizeof(*pinfo->pins),
>>>>> + GFP_KERNEL | __GFP_ZERO);
>>>>
>>>> devm_kcalloc() zeroes on its own
>>>>
>>>>> + if (!pinfo->pins) {
>>>>> + ret = -ENOMEM;
>>>>> + goto free;
>>>>> + }
>>>>> +
>>>>> + pinfo->groups = devm_kmalloc_array(handle->dev,
>>>>> pinfo->nr_groups,
>>>>> + sizeof(*pinfo->groups),
>>>>> + GFP_KERNEL | __GFP_ZERO);
>>>>
>>>> Ditto.
>>>>> + if (!pinfo->groups) {
>>>>> + ret = -ENOMEM;
>>>>> + goto free;
>>>>> + }
>>>>> +
>>>>> + pinfo->functions = devm_kmalloc_array(handle->dev,
>>>>> pinfo->nr_functions,
>>>>> + sizeof(*pinfo->functions),
>>>>> + GFP_KERNEL | __GFP_ZERO);
>>>>> + if (!pinfo->functions) {
>>>>> + ret = -ENOMEM;
>>>>> + goto free;
>>>>> + }
>>>>> +
>>>>> + pinfo->version = version;
>>>>> + handle->pinctrl_ops = &pinctrl_ops;
>>>>> + handle->pinctrl_priv = pinfo;
>>>>> +
>>>>> + return 0;
>>>>> +free:
>>>>> + if (pinfo) {
>>>>> + devm_kfree(handle->dev, pinfo->pins);
>>>>> + devm_kfree(handle->dev, pinfo->functions);
>>>>> + devm_kfree(handle->dev, pinfo->groups);
>>>>> + }
>>>>
>>>> These frees are really not needed...if this function return failure
>>>> any
>>>> devres allocated in it is freed by the SCMI core. (as said
>>>> above...in a
>>>> recent kernel with the new API of course)
>>>>
>>>>> +
>>>>> + devm_kfree(handle->dev, pinfo);
>>>>> +
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_PINCTRL,
>>>>> pinctrl)
>>>>> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
>>>>> index 815095326e2d..68add4d06e8c 100644
>>>>> --- a/drivers/pinctrl/Kconfig
>>>>> +++ b/drivers/pinctrl/Kconfig
>>>>> @@ -431,4 +431,13 @@ config PINCTRL_EQUILIBRIUM
>>>>> pin functions, configure GPIO attributes for LGM SoC pins.
>>>>> Pinmux and
>>>>> pinconf settings are retrieved from device tree.
>>>>> +config PINCTRL_SCMI
>>>>> + bool "Pinctrl driver controlled via SCMI interface"
>>>>> + depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
>>>>> + help
>>>>> + This driver provides support for pinctrl which is controlled
>>>>> + by firmware that implements the SCMI interface.
>>>>> + It uses SCMI Message Protocol to interact with the
>>>>> + firmware providing all the pinctrl controls.
>>>>> +
>>>>
>>>> This does NOT belong to this patch, but to the next right ?
>>>>
>>>>> endif
>>>>> diff --git a/include/linux/scmi_protocol.h
>>>>> b/include/linux/scmi_protocol.h
>>>>> index 9cd312a1ff92..6a909ef3bf51 100644
>>>>> --- a/include/linux/scmi_protocol.h
>>>>> +++ b/include/linux/scmi_protocol.h
>>>>> @@ -12,7 +12,8 @@
>>>>> #include <linux/notifier.h>
>>>>> #include <linux/types.h>
>>>>> -#define SCMI_MAX_STR_SIZE 16
>>>>> +#define SCMI_MAX_STR_SIZE 16
>>>>> +#define SCMI_MAX_STR_EXT_SIZE 64
>>>>
>>>> This is handled as part of how the extended names are handled with
>>>> ph->hops
>>>> in a common way, as I was saying, so please move this if you need
>>>> it in
>>>> the protocol code, then I'll port to the ph->hops interface and clean
>>>> up.
>>>>
>>>>> #define SCMI_MAX_NUM_RATES 16
>>>>> /**
>>>>> @@ -252,6 +253,55 @@ struct scmi_notify_ops {
>>>>> struct notifier_block *nb);
>>>>> };
>>>>> +/**
>>>>> + * struct scmi_pinctrl_ops - represents the various operations
>>>>> provided
>>>>> + * by SCMI Pinctrl Protocol
>>>>> + *
>>>>> + * @get_groups_count: returns count of the registered groups
>>>>> + * @get_group_name: returns group name by index
>>>>> + * @get_group_pins: returns the set of pins, assigned to the
>>>>> specified group
>>>>> + * @get_functions_count: returns count of the registered fucntions
>>>>> + * @get_function_name: returns function name by indes
>>>>> + * @get_function_groups: returns the set of groups, assigned to the
>>>>> specified
>>>>> + * function
>>>>> + * @set_mux: set muxing function for groups of pins
>>>>> + * @get_pins: returns the set of pins, registered in driver
>>>>> + * @get_config: returns configuration parameter for pin
>>>>> + * @set_config: sets the configuration parameter for pin
>>>>> + * @get_config_group: returns the configuration parameter for a
>>>>> group of pins
>>>>> + * @set_config_group: sets the configuration parameter for a groups
>>>>> of pins
>>>>> + * @request_pin: aquire pin before selecting mux setting
>>>>> + * @free_pin: frees pin, acquired by request_pin call
>>>>> + */
>>>>> +struct scmi_pinctrl_ops {
>>>>> + int (*get_groups_count)(const struct scmi_handle *handle);
>>>>> + int (*get_group_name)(const struct scmi_handle *handles, u32
>>>>> selector,
>>>>> + const char **name);
>>>>> + int (*get_group_pins)(const struct scmi_handle *handle, u32
>>>>> selector,
>>>>> + const unsigned int **pins, unsigned int *nr_pins);
>>>>> + int (*get_functions_count)(const struct scmi_handle *handle);
>>>>> + int (*get_function_name)(const struct scmi_handle *handle, u32
>>>>> selector,
>>>>> + const char **name);
>>>>> + int (*get_function_groups)(const struct scmi_handle *handle,
>>>>> + u32 selector, unsigned int *nr_groups,
>>>>> + const unsigned int **groups);
>>>>> + int (*set_mux)(const struct scmi_handle *handle, u32 selector,
>>>>> + u32 group);
>>>>> + int (*get_pin_name)(const struct scmi_handle *handle, u32
>>>>> selector,
>>>>> + const char **name);
>>>>> + int (*get_pins_count)(const struct scmi_handle *handle);
>>>>> + int (*get_config)(const struct scmi_handle *handle, u32 pin,
>>>>> + u32 *config);
>>>>> + int (*set_config)(const struct scmi_handle *handle, u32 pin,
>>>>> + u32 config);
>>>>> + int (*get_config_group)(const struct scmi_handle *handle, u32
>>>>> pin,
>>>>> + u32 *config);
>>>>> + int (*set_config_group)(const struct scmi_handle *handle, u32
>>>>> pin,
>>>>> + u32 config);
>>>>> + int (*request_pin)(const struct scmi_handle *handle, u32 pin);
>>>>> + int (*free_pin)(const struct scmi_handle *handle, u32 pin);
>>>>> +};
>>>>> +
>>>>
>>>> As mentioned above, here you could drop a lot of this
>>>> get_X_count/name/pins
>>>> and instead expose a few of the internal proocol struct scmi__X_info
>>>> and then
>>>> provide just a mean to query how many resource are there and then get
>>>> the info
>>>> descriptor you want for the specific domain_id, i.e.:
>>>>
>>>> int (*num_domains_get)(ph, type)
>>>> void *(*info_get)(ph, type, domain_id);
>>>>
>>>> Thanks,
>>>> Cristian
>>>>
>>>>
>>>> _______________________________________________
>>>> linux-arm-kernel mailing list
>>>> [email protected]
>>>> https://urldefense.com/v3/__http://lists.infradead.org/mailman/listinfo/linux-arm-kernel__;!!GF_29dbcQIUBPA!y2hR3PEGGxiPjVeXBcgGyV03DPDhzgUKR0uHvsTpiafKgBar8Egc6oOOs-IkFIquhSf-qBzltqEMyzRZrZi7qlk$
>>>>
>>>> [lists[.]infradead[.]org]
On 4/24/2023 6:33 PM, Oleksii Moisieiev wrote:
>
> On 24.04.23 04:52, Peng Fan wrote:
>>
>>
>> On 4/21/2023 4:40 PM, Oleksii Moisieiev wrote:
>>> Hi Peng Fan,
>>>
>>> On 17.04.23 05:55, Peng Fan wrote:
>>>>
>>>>
>>>> On 4/13/2023 6:04 AM, Cristian Marussi wrote:
>>>>> On Fri, Apr 07, 2023 at 10:18:27AM +0000, Oleksii Moisieiev wrote:
>>>>>> Implementation of the SCMI client driver, which implements
>>>>>> PINCTRL_PROTOCOL. This protocol has ID 19 and is described
>>>>>> in the latest DEN0056 document.
>>>>>
>>>>> Hi,
>>>>>
>>>>>> This protocol is part of the feature that was designed to
>>>>>> separate the pinctrl subsystem from the SCP firmware.
>>>>>> The idea is to separate communication of the pin control
>>>>>> subsystem with the hardware to SCP firmware
>>>>>> (or a similar system, such as ATF), which provides an interface
>>>>>> to give the OS ability to control the hardware through SCMI protocol.
>>>>>> This is a generic driver that implements SCMI protocol,
>>>>>> independent of the platform type.
>>>>>>
>>>>>> DEN0056 document:
>>>>>> https://urldefense.com/v3/__https://developer.arm.com/documentation/den0056/latest__;!!GF_29dbcQIUBPA!y2hR3PEGGxiPjVeXBcgGyV03DPDhzgUKR0uHvsTpiafKgBar8Egc6oOOs-IkFIquhSf-qBzltqEMyzRZHq8eC4g$
>>>>>>
>>>>>> [developer[.]arm[.]com]
>>>>>>
>>>>>
>>>>> No need to specify all of this in the commit message, just a note that
>>>>> you are adding a new SCMIv3.2 Pincontrol protocol, highlighting
>>>>> anything
>>>>> that has been left out in this patch (if any) will be enough.
>>>>
>>>> Is it possible to extend the spec to support multilple uint32_t for PIN
>>>> CONFIG SET?
>>>>
>>>> With only one uint32_t could not satisfy i.MX requirement.
>>>>
>>>> Thanks,
>>>> Peng.
>>>>
>>> IIUC you are expecting to have an ability to set some kind of array of
>>> uint32_t config values to some specific ConfigType?
>>>
>>> I'm not sure if it's supported by pintctrl subsystem right now. I was
>>> unable to find an example in the existing device-tree pinctrl bindings.
>>> This makes me think that this kind of binding is OEM specific.
>>
>> Look at arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts
>>
>> Take lpuart for example:
>> MX93_PAD_UART1_RXD__LPUART1_RX 0x31e
>>
>> The macro MX93_PAD_UART1_RXD__LPUART1_RX is an array.
>>
>> i.MX pinctrl driver use
>> mux reg, mux value, select reg, select value, pad reg, pad value
>>
>> pinctrl driver will be handled by SCMI firmware for new i.MX SoC.
>>
>> With current spec, only one uint32 is low speed. So if the spec
>> could extended to support more uint32 with pin config set,
>> we could use one SCMI call to support i.MX pinctrl.
>>
>> Thanks
>> Peng.
>>
> Hi Peng Fan,
>
> Thank you for the example.
>
> According to the comment for the function imx_pinctrl_parse_pin_mmio:
>
> /*
> * Each pin represented in fsl,pins consists of a number of u32 PIN_FUNC_ID
> * and 1 u32 CONFIG, the total size is PIN_FUNC_ID + CONFIG for each pin.
> * For generic_pinconf case, there's no extra u32 CONFIG.
> *
> * PIN_FUNC_ID format:
> * Default:
> * <mux_reg conf_reg input_reg mux_mode input_val>
> * SHARE_MUX_CONF_REG:
> * <mux_conf_reg input_reg mux_mode input_val>
> * IMX_USE_SCU:
> * <pin_id mux_mode>
> */
>
> IIUC this says that in scu format it is expected to use <pin_id
> mux_mode>. This fit's protocol expectations.
The comments are misleading, it should be pin_id mux_mode config_setting
See arch/arm64/boot/dts/freescale/imx8qm-mek.dts
>
> The idea was to make all platform specific implementation, such as
> <mux_reg conf_reg input_reg mux_mode input_val>
>
> on the SCP Firmware side and hide it under Pin ID and Config Type so
> Client side can apply generic config in the device tree.
>
> Can't you use scu format here? Or execute
> imx_pinconf_decode_generic_config on SCMI server side?
Technically with one config value is doable, but that means
SCMI needs expose many pin ids, such as pin id SDHC_CD,
it could be muxed as SDHC_CD, GPIO1_5, SAI_RX. Then
scmi needs export:
SDHC_CD_SDHC_CD
SDHC_CD_GPIO1_5
SDHC_CD_SAI_RX
If with multiple uint32_value support by spec,
scmi just needs exports SDHC_CD, with other values passed
through uint32 value.
Regards,
Peng.
>
> Best regards,
>
> Oleksii.
>
>>>
>>> Maybe it can be implemented by adding new IDs to OEM specific range
>>> (192-255) which is reserved for OEM specific units (See Table 23 of
>>> DEN0056E).
>>>
>>> Best regards,
>>>
>>> Oleksii
>>>
>>>
>>>>> You can look at the very first commit logs of existing protos as an
>>>>> example like: drivers/firmware/arm_scmi/powercap.c
>>>>>
>>>>> Some more comments down below, I'll mostly skip anything related to
>>>>> the
>>>>> SCMI API change I mentioned before...
>>>>>
>>>>> I'll also wont comment on more trivial stuff related to style, BUT
>>>>> there
>>>>> are lots of them: you should run
>>>>>
>>>>> ./scripts/checkpatch.pl --strict <your-git-format-patch-file>
>>>>>
>>>>> for each patch in the series. (and fix accordingly..spacing,
>>>>> brackets...etc)
>>>>>
>>>>>> Signed-off-by: Oleksii Moisieiev <[email protected]>
>>>>>> ---
>>>>>> MAINTAINERS | 6 +
>>>>>> drivers/firmware/arm_scmi/Makefile | 2 +-
>>>>>> drivers/firmware/arm_scmi/common.h | 1 +
>>>>>> drivers/firmware/arm_scmi/driver.c | 3 +
>>>>>> drivers/firmware/arm_scmi/pinctrl.c | 905
>>>>>> ++++++++++++++++++++++++++++
>>>>>> drivers/pinctrl/Kconfig | 9 +
>>>>>> include/linux/scmi_protocol.h | 58 +-
>>>>>> 7 files changed, 982 insertions(+), 2 deletions(-)
>>>>>> create mode 100644 drivers/firmware/arm_scmi/pinctrl.c
>>>>>>
>>>>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>>>>> index 281de213ef47..abc543fd7544 100644
>>>>>> --- a/MAINTAINERS
>>>>>> +++ b/MAINTAINERS
>>>>>> @@ -16961,6 +16961,12 @@ F: drivers/reset/reset-scmi.c
>>>>>> F: include/linux/sc[mp]i_protocol.h
>>>>>> F: include/trace/events/scmi.h
>>>>>> +PINCTRL DRIVER FOR SYSTEM CONTROL & POWER/MANAGEMENT INTERFACE
>>>>>> (SCPI/SCMI)
>>>>>> +M: Oleksii Moisieiev <[email protected]>
>>>>>> +L: [email protected]
>>>>>> +S: Maintained
>>>>>> +F: drivers/firmware/arm_scmi/pinctrl.c
>>>>>> +
>>>>>> SYSTEM RESET/SHUTDOWN DRIVERS
>>>>>> M: Sebastian Reichel <[email protected]>
>>>>>> L: [email protected]
>>>>>> diff --git a/drivers/firmware/arm_scmi/Makefile
>>>>>> b/drivers/firmware/arm_scmi/Makefile
>>>>>> index bc0d54f8e861..5cec357fbfa8 100644
>>>>>> --- a/drivers/firmware/arm_scmi/Makefile
>>>>>> +++ b/drivers/firmware/arm_scmi/Makefile
>>>>>> @@ -4,7 +4,7 @@ scmi-driver-y = driver.o notify.o
>>>>>> scmi-transport-y = shmem.o
>>>>>> scmi-transport-$(CONFIG_MAILBOX) += mailbox.o
>>>>>> scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o
>>>>>> -scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o
>>>>>> system.o
>>>>>> +scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o
>>>>>> system.o pinctrl.o
>>>>>> scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y)
>>>>>> $(scmi-protocols-y) \
>>>>>> $(scmi-transport-y)
>>>>>> obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
>>>>>> diff --git a/drivers/firmware/arm_scmi/common.h
>>>>>> b/drivers/firmware/arm_scmi/common.h
>>>>>> index 65063fa948d4..8bbb404abe8d 100644
>>>>>> --- a/drivers/firmware/arm_scmi/common.h
>>>>>> +++ b/drivers/firmware/arm_scmi/common.h
>>>>>> @@ -170,6 +170,7 @@ DECLARE_SCMI_REGISTER_UNREGISTER(power);
>>>>>> DECLARE_SCMI_REGISTER_UNREGISTER(reset);
>>>>>> DECLARE_SCMI_REGISTER_UNREGISTER(sensors);
>>>>>> DECLARE_SCMI_REGISTER_UNREGISTER(system);
>>>>>> +DECLARE_SCMI_REGISTER_UNREGISTER(pinctrl);
>>>>>> #define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(id, name) \
>>>>>> int __init scmi_##name##_register(void) \
>>>>>> diff --git a/drivers/firmware/arm_scmi/driver.c
>>>>>> b/drivers/firmware/arm_scmi/driver.c
>>>>>> index 3dfd8b6a0ebf..fb9525fb3c24 100644
>>>>>> --- a/drivers/firmware/arm_scmi/driver.c
>>>>>> +++ b/drivers/firmware/arm_scmi/driver.c
>>>>>> @@ -743,6 +743,7 @@ static struct scmi_prot_devnames devnames[] = {
>>>>>> { SCMI_PROTOCOL_CLOCK, { "clocks" },},
>>>>>> { SCMI_PROTOCOL_SENSOR, { "hwmon" },},
>>>>>> { SCMI_PROTOCOL_RESET, { "reset" },},
>>>>>> + { SCMI_PROTOCOL_PINCTRL, { "pinctrl" },},
>>>>>> };
>>>>>> static inline void
>>>>>> @@ -947,6 +948,7 @@ static int __init scmi_driver_init(void)
>>>>>> scmi_reset_register();
>>>>>> scmi_sensors_register();
>>>>>> scmi_system_register();
>>>>>> + scmi_pinctrl_register();
>>>>>> return platform_driver_register(&scmi_driver);
>>>>>> }
>>>>>> @@ -962,6 +964,7 @@ static void __exit scmi_driver_exit(void)
>>>>>> scmi_reset_unregister();
>>>>>> scmi_sensors_unregister();
>>>>>> scmi_system_unregister();
>>>>>> + scmi_pinctrl_unregister();
>>>>>> platform_driver_unregister(&scmi_driver);
>>>>>> }
>>>>>> diff --git a/drivers/firmware/arm_scmi/pinctrl.c
>>>>>> b/drivers/firmware/arm_scmi/pinctrl.c
>>>>>> new file mode 100644
>>>>>> index 000000000000..037270d7f39b
>>>>>> --- /dev/null
>>>>>> +++ b/drivers/firmware/arm_scmi/pinctrl.c
>>>>>> @@ -0,0 +1,905 @@
>>>>>> +// SPDX-License-Identifier: GPL-2.0
>>>>>> +/*
>>>>>> + * System Control and Management Interface (SCMI) Pinctrl Protocol
>>>>>> + *
>>>>>> + * Copyright (C) 2021 EPAM.
>>>>>
>>>>> nitpick: update (C) years
>>>>>
>>>>>> + */
>>>>>> +
>>>>>> +#define pr_fmt(fmt) "SCMI Notifications PINCTRL - " fmt
>>>>>> +
>>>>>
>>>>> This is not needed, no notifs in this proto.
>>>>>
>>>>>> +#include <linux/scmi_protocol.h>
>>>>>> +#include <linux/slab.h>
>>>>>> +
>>>>>> +#include "common.h"
>>>>>> +#include "notify.h"
>>>>>
>>>>> Notifs not needed, and in the new API world you'll just need a:
>>>>>
>>>>> #include "protocols.h"
>>>>>
>>>>>> +
>>>>>> +#define SET_TYPE(x) ((x) & 0x3)
>>>>>> +
>>>>>
>>>>> Even if trivial better to use std bitfield.h macros like
>>>>> FIELD_GET() BIT() ... etc
>>>>>
>>>>>> +enum scmi_pinctrl_protocol_cmd {
>>>>>> + PINCTRL_ATTRIBUTES = 0x3,
>>>>>> + PINCTRL_LIST_ASSOCIATIONS = 0x4,
>>>>>> + PINCTRL_CONFIG_GET = 0x5,
>>>>>> + PINCTRL_CONFIG_SET = 0x6,
>>>>>> + PINCTRL_FUNCTION_SELECT = 0x7,
>>>>>> + PINCTRL_REQUEST = 0x8,
>>>>>> + PINCTRL_RELEASE = 0x9,
>>>>>> + PINCTRL_NAME_GET = 0xa,
>>>>>> + PINCTRL_SET_PERMISSIONS = 0xb
>>>>>> +};
>>>>>> +
>>>>>> +enum scmi_pinctrl_selector_type {
>>>>>> + PIN_TYPE = 0,
>>>>>> + GROUP_TYPE,
>>>>>> + FUNCTION_TYPE
>>>>>> +};
>>>>>> +
>>>>>> +struct scmi_group_info {
>>>>>> + bool present;
>>>>>> + char *name;
>>>>>> + unsigned int *group_pins;
>>>>>> + unsigned int nr_pins;
>>>>>> +};
>>>>>> +
>>>>>> +struct scmi_function_info {
>>>>>> + bool present;
>>>>>> + char *name;
>>>>>> + unsigned int *groups;
>>>>>> + unsigned int nr_groups;
>>>>>> +};
>>>>>> +
>>>>>> +struct scmi_pin_info {
>>>>>> + bool present;
>>>>>> + char *name;
>>>>>> +};
>>>>>> +
>>>>>> +struct scmi_pinctrl_info {
>>>>>> + u32 version;
>>>>>> + u16 nr_groups;
>>>>>> + u16 nr_functions;
>>>>>> + u16 nr_pins;
>>>>>
>>>>> Since these vars are not related to stricly spaced message fields
>>>>> (even though
>>>>> derived from such messages) do not use sized types, you can just
>>>>> stick with
>>>>> unsigned int. (it is also better not to mix sized and unsized types
>>>>> in the same
>>>>> struct). This also could come handy if these will be exposed to the
>>>>> user
>>>>> in scmi_protocol.h in the future (more on this down below)
>>>>>
>>>>>> + struct scmi_group_info *groups;
>>>>>> + struct scmi_function_info *functions;
>>>>>> + struct scmi_pin_info *pins;
>>>>>> +};
>>>>>> +
>>>>>> +struct scmi_conf_tx {
>>>>>> + __le32 identifier;
>>>>>> +#define SET_TYPE_BITS(attr, x) (((attr) & 0xFFFFFCFF) | (x & 0x3)
>>>>>> << 8)
>>>>>> +#define SET_CONFIG(attr, x) (((attr) & 0xFF) | (x & 0xFF))
>>>>>
>>>>> Use bitfield.h like FIELD_SET / GENMASK etc
>>>>>
>>>>>> + __le32 attributes;
>>>>>> +};
>>>>>> +
>>>>>> +static int scmi_pinctrl_attributes_get(const struct scmi_handle
>>>>>> *handle,
>>>>>> + struct scmi_pinctrl_info *pi)
>>>>>> +{
>>>>>> + int ret;
>>>>>> + struct scmi_xfer *t;
>>>>>> + struct scmi_msg_pinctrl_protocol_attributes {
>>>>>> +#define GROUPS_NR(x) ((x) >> 16)
>>>>>> +#define PINS_NR(x) ((x) & 0xffff)
>>>>>> + __le32 attributes_low;
>>>>>> +#define FUNCTIONS_NR(x) ((x) & 0xffff)
>>>>>> + __le32 attributes_high;
>>>>>> + } *attr;
>>>>>
>>>>> For consistency with the rest of the stack (mostly :D), please move
>>>>> this struct
>>>>> definition and related macros outside in the global scope right after
>>>>> command
>>>>> enum. (and use bitfield macros ...)
>>>>>
>>>>>> +
>>>>>> + if (!pi)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
>>>>>> + SCMI_PROTOCOL_PINCTRL, 0, sizeof(*attr), &t);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> + attr = t->rx.buf;
>>>>>> +
>>>>>> + ret = scmi_do_xfer(handle, t);
>>>>>> + if (!ret) {
>>>>>> + pi->nr_functions =
>>>>>> + le16_to_cpu(FUNCTIONS_NR(attr->attributes_high));
>>>>>> + pi->nr_groups =
>>>>>> le16_to_cpu(GROUPS_NR(attr->attributes_low));
>>>>>> + pi->nr_pins = le16_to_cpu(PINS_NR(attr->attributes_low));
>>>>>> + }
>>>>>> +
>>>>>> + scmi_xfer_put(handle, t);
>>>>>> + return ret;
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_get_groups_count(const struct scmi_handle
>>>>>> *handle)
>>>>>> +{
>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>> +
>>>>>> + if (!handle || !handle->pinctrl_priv)
>>>>>> + return -ENODEV;
>>>>>> +
>>>>>> + pi = handle->pinctrl_priv;
>>>>>> +
>>>>>> + return pi->nr_groups;
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_get_pins_count(const struct scmi_handle
>>>>>> *handle)
>>>>>> +{
>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>> +
>>>>>> + if (!handle || !handle->pinctrl_priv)
>>>>>> + return -ENODEV;
>>>>>> +
>>>>>> + pi = handle->pinctrl_priv;
>>>>>> +
>>>>>> + return pi->nr_pins;
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_get_functions_count(const struct
>>>>>> scmi_handle *handle)
>>>>>> +{
>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>> +
>>>>>> + if (!handle || !handle->pinctrl_priv)
>>>>>> + return -ENODEV;
>>>>>> +
>>>>>> + pi = handle->pinctrl_priv;
>>>>>> +
>>>>>> + return pi->nr_functions;
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_validate_id(const struct scmi_handle
>>>>>> *handle,
>>>>>> + u32 identifier,
>>>>>> + enum scmi_pinctrl_selector_type type)
>>>>>> +{
>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>> +
>>>>>> + if (!handle || !handle->pinctrl_priv)
>>>>>> + return -ENODEV;
>>>>>> +
>>>>>> + switch (type) {
>>>>>> + case PIN_TYPE:
>>>>>> + pi = handle->pinctrl_priv;
>>>>>> +
>>>>>> + return (identifier < pi->nr_pins) ? 0 : -EINVAL;
>>>>>> + case GROUP_TYPE:
>>>>>> + return (identifier <
>>>>>> + scmi_pinctrl_get_groups_count(handle)) ?
>>>>>> + 0 : -EINVAL;
>>>>>> + case FUNCTION_TYPE:
>>>>>> + return (identifier <
>>>>>> + scmi_pinctrl_get_functions_count(handle)) ?
>>>>>> + 0 : -EINVAL;
>>>>>> + default:
>>>>>> + return -EINVAL;
>>>>>> + }
>>>>>
>>>>> Here I would just pick the right value to compare, break and then
>>>>> compare and exit, something aroundf the lines of:
>>>>>
>>>>> case PIN_TYPE:
>>>>> ...
>>>>> val = pi->nr_pins;
>>>>> break;
>>>>> ...
>>>>> case GROUP_TYPE:
>>>>> val = scmi_pinctrl_get_groups_count());
>>>>> break;
>>>>>
>>>>> ....
>>>>> ....
>>>>> default:
>>>>> return -EINVAL;
>>>>> }
>>>>>
>>>>> if (identifier >= val)
>>>>> return -EINVAL;
>>>>>
>>>>> return 0;
>>>>> }
>>>>>
>>>>> ... it's easier to read. What do you think ?
>>>>>
>>>>>
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_get_name(const struct scmi_handle *handle,
>>>>>> + u32 identifier,
>>>>>> + enum scmi_pinctrl_selector_type type,
>>>>>> + char **name)
>>>>>> +{
>>>>>
>>>>> As said, there is common helper for this, but it will need some small
>>>>> adaptation in the SCMI core to work here so keep it as it is, and
>>>>> I'll take
>>>>> care of this later, if it sounds fine for you.
>>>>>
>>>>>> + struct scmi_xfer *t;
>>>>>> + int ret = 0;
>>>>>> + struct scmi_name_tx {
>>>>>> + __le32 identifier;
>>>>>> + __le32 flags;
>>>>>> + } *tx;
>>>>>> + struct scmi_name_rx {
>>>>>> + __le32 flags;
>>>>>> + u8 name[64];
>>>>>> + } *rx;
>>>>>> +
>>>>>> + if (!handle || !name)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_NAME_GET,
>>>>>> + SCMI_PROTOCOL_PINCTRL,
>>>>>> + sizeof(*tx), sizeof(*rx), &t);
>>>>>> +
>>>>>> + tx = t->tx.buf;
>>>>>> + rx = t->rx.buf;
>>>>>> + tx->identifier = identifier;
>>>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>>>> +
>>>>>> + ret = scmi_do_xfer(handle, t);
>>>>>> + if (ret)
>>>>>> + goto out;
>>>>>> +
>>>>>> + if (rx->flags) {
>>>>>> + ret = -EINVAL;
>>>>>> + goto out;
>>>>>> + }
>>>>>> +
>>>>>> + *name = kasprintf(GFP_KERNEL, "%s", rx->name);
>>>>>> + if (!*name)
>>>>>> + ret = -ENOMEM;
>>>>>> + out:
>>>>>> + scmi_xfer_put(handle, t);
>>>>>> +
>>>>>> + return ret;
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_attributes(const struct scmi_handle *handle,
>>>>>> + enum scmi_pinctrl_selector_type type,
>>>>>> + u32 selector, char **name,
>>>>>> + unsigned int *n_elems)
>>>>>> +{
>>>>>> + int ret = 0;
>>>>>> + struct scmi_xfer *t;
>>>>>> + struct scmi_pinctrl_attributes_tx {
>>>>>> + __le32 identifier;
>>>>>> + __le32 flags;
>>>>>> + } *tx;
>>>>>> + struct scmi_pinctrl_attributes_rx {
>>>>>> +#define EXT_NAME_FLAG(x) ((x) & BIT(31))
>>>>>> +#define NUM_ELEMS(x) ((x) & 0xffff)
>>>>>> + __le32 attributes;
>>>>>> + u8 name[16];
>>>>>> + } *rx;
>>>>>
>>>>> Ditto. Move these defs outside, bitfield.h for macros and try to
>>>>> use the
>>>>> same naming style for message structs as in other protos, i.e.
>>>>>
>>>>> for commands: struct scmi_msg_pinctrl_attributes
>>>>> for replies: struct scmi_resp_pinctrl_attributes
>>>>>
>>>>> (or some variations around that...
>>>>> scmi_msg_cmd_* scmi_msg_resp_*
>>>>>
>>>>> we have not been fully consistent really, so I dont want to be
>>>>> pedantic here, but we never used tx/rx in message context since
>>>>> it is
>>>>> already (mis)-used in SCMI channel context...)
>>>>>
>>>>>> +
>>>>>> + if (!handle || !name)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_ATTRIBUTES,
>>>>>> + SCMI_PROTOCOL_PINCTRL, sizeof(*tx), sizeof(*rx), &t);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> + tx = t->tx.buf;
>>>>>> + rx = t->rx.buf;
>>>>>> + tx->identifier = selector;
>>>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>>>> +
>>>>>> + ret = scmi_do_xfer(handle, t);
>>>>>> + if (ret)
>>>>>> + goto out;
>>>>>> +
>>>>>> + *n_elems = NUM_ELEMS(rx->attributes);
>>>>>> +
>>>>>> + if (!EXT_NAME_FLAG(rx->attributes)) {
>>>>>> + *name = kasprintf(GFP_KERNEL, "%s", rx->name);
>>>>>> + if (!*name)
>>>>>> + ret = -ENOMEM;
>>>>>> + } else
>>>>>> + ret = scmi_pinctrl_get_name(handle, selector, type, name);
>>>>>> + out:
>>>>>> + scmi_xfer_put(handle, t);
>>>>>> + return ret;
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_list_associations(const struct scmi_handle
>>>>>> *handle,
>>>>>> + u32 selector,
>>>>>> + enum scmi_pinctrl_selector_type type,
>>>>>> + uint16_t size, unsigned int *array)
>>>>>> +{
>>>>>
>>>>> This is the other functionalities you could implement straight away
>>>>> using
>>>>> ph->hops helpers (iterators) but just leave it this way, and I'll
>>>>> port it later
>>>>> (once we retested all of this as working with the new API but without
>>>>> any
>>>>> ph->hops usage..I think it is safer to change one bit at time... :P)
>>>>>
>>>>>> + struct scmi_xfer *t;
>>>>>> + struct scmi_pinctrl_list_assoc_tx {
>>>>>> + __le32 identifier;
>>>>>> + __le32 flags;
>>>>>> + __le32 index;
>>>>>> + } *tx;
>>>>>> + struct scmi_pinctrl_list_assoc_rx {
>>>>>> +#define RETURNED(x) ((x) & 0xFFF)
>>>>>> +#define REMAINING(x) ((x) >> 16)
>>>>>> + __le32 flags;
>>>>>> + __le16 array[];
>>>>>> + } *rx;
>>>>>
>>>>> Ditto, about struct naming and macros.
>>>>>
>>>>>> + u16 tot_num_ret = 0, loop_num_ret;
>>>>>> + u16 remaining_num_ret;
>>>>>> + int ret, loop;
>>>>>> +
>>>>>> + if (!handle || !array || !size)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + if (type == PIN_TYPE)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_LIST_ASSOCIATIONS,
>>>>>> + SCMI_PROTOCOL_PINCTRL, sizeof(*tx),
>>>>>> + 0, &t);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> + tx = t->tx.buf;
>>>>>> + rx = t->rx.buf;
>>>>>> +
>>>>>> + do {
>>>>>> + tx->identifier = cpu_to_le32(selector);
>>>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>>>> + tx->index = cpu_to_le32(tot_num_ret);
>>>>>> +
>>>>>> + ret = scmi_do_xfer(handle, t);
>>>>>> + if (ret)
>>>>>> + break;
>>>>>> +
>>>>>> + loop_num_ret = le32_to_cpu(RETURNED(rx->flags));
>>>>>> + remaining_num_ret = le32_to_cpu(REMAINING(rx->flags));
>>>>>> +
>>>>>> + for (loop = 0; loop < loop_num_ret; loop++) {
>>>>>> + if (tot_num_ret + loop >= size) {
>>>>>> + ret = -EMSGSIZE;
>>>>>> + goto out;
>>>>>> + }
>>>>>> +
>>>>>> + array[tot_num_ret + loop] =
>>>>>> + le16_to_cpu(rx->array[loop]);
>>>>>> + }
>>>>>> +
>>>>>> + tot_num_ret += loop_num_ret;
>>>>>> +
>>>>>> + scmi_reset_rx_to_maxsz(handle, t);
>>>>>> + } while (remaining_num_ret > 0);
>>>>>> +out:
>>>>>> + scmi_xfer_put(handle, t);
>>>>>> + return ret;
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_request_config(const struct scmi_handle
>>>>>> *handle,
>>>>>> + u32 selector,
>>>>>> + enum scmi_pinctrl_selector_type type,
>>>>>> + u32 *config)
>>>>>> +{
>>>>>> + struct scmi_xfer *t;
>>>>>> + struct scmi_conf_tx *tx;
>>>>>> + __le32 *packed_config;
>>>>>> + u32 attributes = 0;
>>>>>> + int ret;
>>>>>> +
>>>>>> + if (!handle || !config || type == FUNCTION_TYPE)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_GET,
>>>>>> + SCMI_PROTOCOL_PINCTRL,
>>>>>> + sizeof(*tx), sizeof(*packed_config), &t);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> + tx = t->tx.buf;
>>>>>> + packed_config = t->rx.buf;
>>>>>> + tx->identifier = cpu_to_le32(selector);
>>>>>> + attributes = SET_TYPE_BITS(attributes, type);
>>>>>> + attributes = SET_CONFIG(attributes, *config);
>>>>>> +
>>>>>> + tx->attributes = cpu_to_le32(attributes);
>>>>>> +
>>>>>> + ret = scmi_do_xfer(handle, t);
>>>>>> +
>>>>>> + if (!ret)
>>>>>> + *config = le32_to_cpu(*packed_config);
>>>>>> +
>>>>>> + scmi_xfer_put(handle, t);
>>>>>> + return ret;
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_get_config(const struct scmi_handle
>>>>>> *handle, u32 pin,
>>>>>> + u32 *config)
>>>>>> +{
>>>>>> + return scmi_pinctrl_request_config(handle, pin, PIN_TYPE,
>>>>>> config);
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_apply_config(const struct scmi_handle
>>>>>> *handle,
>>>>>> + u32 selector,
>>>>>> + enum scmi_pinctrl_selector_type type,
>>>>>> + u32 config)
>>>>>> +{
>>>>>> + struct scmi_xfer *t;
>>>>>> + struct scmi_conf_tx *tx;
>>>>>> + u32 attributes = 0;
>>>>>> + int ret;
>>>>>> +
>>>>>> + if (!handle || type == FUNCTION_TYPE)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_SET,
>>>>>> + SCMI_PROTOCOL_PINCTRL,
>>>>>> + sizeof(*tx), 0, &t);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> + tx = t->tx.buf;
>>>>>> + tx->identifier = cpu_to_le32(selector);
>>>>>> + attributes = SET_TYPE_BITS(attributes, type);
>>>>>> + attributes = SET_CONFIG(attributes, config);
>>>>>> + tx->attributes = cpu_to_le32(attributes);
>>>>>> +
>>>>>> + ret = scmi_do_xfer(handle, t);
>>>>>> +
>>>>>> + scmi_xfer_put(handle, t);
>>>>>> + return ret;
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_set_config(const struct scmi_handle
>>>>>> *handle, u32 pin,
>>>>>> + u32 config)
>>>>>> +{
>>>>>> + return scmi_pinctrl_apply_config(handle, pin, PIN_TYPE, config);
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_get_config_group(const struct scmi_handle
>>>>>> *handle,
>>>>>> + u32 group, u32 *config)
>>>>>> +{
>>>>>> + return scmi_pinctrl_request_config(handle, group, GROUP_TYPE,
>>>>>> config);
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_set_config_group(const struct scmi_handle
>>>>>> *handle,
>>>>>> + u32 group, u32 config)
>>>>>> +{
>>>>>> + return scmi_pinctrl_apply_config(handle, group, GROUP_TYPE,
>>>>>> config);
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_function_select(const struct scmi_handle
>>>>>> *handle,
>>>>>> + u32 identifier,
>>>>>> + enum scmi_pinctrl_selector_type type,
>>>>>> + u32 function_id)
>>>>>> +{
>>>>>> + struct scmi_xfer *t;
>>>>>> + struct scmi_func_set_tx {
>>>>>> + __le32 identifier;
>>>>>> + __le32 function_id;
>>>>>> + __le32 flags;
>>>>>> + } *tx;
>>>>>
>>>>> Ditto.
>>>>>
>>>>>> + int ret;
>>>>>> +
>>>>>> + if (!handle || type == FUNCTION_TYPE)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_FUNCTION_SELECT,
>>>>>> + SCMI_PROTOCOL_PINCTRL,
>>>>>> + sizeof(*tx), 0, &t);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> + tx = t->tx.buf;
>>>>>> + tx->identifier = cpu_to_le32(identifier);
>>>>>> + tx->function_id = cpu_to_le32(function_id);
>>>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>>>> +
>>>>>> + ret = scmi_do_xfer(handle, t);
>>>>>> + scmi_xfer_put(handle, t);
>>>>>> +
>>>>>> + return ret;
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_request(const struct scmi_handle *handle,
>>>>>> + u32 identifier,
>>>>>> + enum scmi_pinctrl_selector_type type)
>>>>>> +{
>>>>>> + struct scmi_xfer *t;
>>>>>> + int ret;
>>>>>> + struct scmi_request_tx {
>>>>>> + __le32 identifier;
>>>>>> + __le32 flags;
>>>>>> + } *tx;
>>>>>> +
>>>>>
>>>>> Ditto.
>>>>>
>>>>>> + if (!handle || type == FUNCTION_TYPE)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_REQUEST,
>>>>>> SCMI_PROTOCOL_PINCTRL,
>>>>>> + sizeof(*tx), 0, &t);
>>>>>> +
>>>>>> + tx = t->tx.buf;
>>>>>> + tx->identifier = identifier;
>>>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>>>> +
>>>>>> + ret = scmi_do_xfer(handle, t);
>>>>>> + scmi_xfer_put(handle, t);
>>>>>> +
>>>>>> + return ret;
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_request_pin(const struct scmi_handle
>>>>>> *handle, u32 pin)
>>>>>> +{
>>>>>> + return scmi_pinctrl_request(handle, pin, PIN_TYPE);
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_free(const struct scmi_handle *handle, u32
>>>>>> identifier,
>>>>>> + enum scmi_pinctrl_selector_type type)
>>>>>> +{
>>>>>> + struct scmi_xfer *t;
>>>>>> + int ret;
>>>>>> + struct scmi_request_tx {
>>>>>> + __le32 identifier;
>>>>>> + __le32 flags;
>>>>>> + } *tx;
>>>>>> +
>>>>> Ditto.
>>>>>
>>>>>> + if (!handle || type == FUNCTION_TYPE)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_RELEASE,
>>>>>> SCMI_PROTOCOL_PINCTRL,
>>>>>> + sizeof(*tx), 0, &t);
>>>>>> +
>>>>>> + tx = t->tx.buf;
>>>>>> + tx->identifier = identifier;
>>>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>>>> +
>>>>>> + ret = scmi_do_xfer(handle, t);
>>>>>> + scmi_xfer_put(handle, t);
>>>>>> +
>>>>>> + return ret;
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_free_pin(const struct scmi_handle *handle,
>>>>>> u32 pin)
>>>>>> +{
>>>>>> + return scmi_pinctrl_free(handle, pin, PIN_TYPE);
>>>>>> +}
>>>>>> +
>>>>>> +
>>>>>> +static int scmi_pinctrl_get_group_info(const struct scmi_handle
>>>>>> *handle,
>>>>>> + u32 selector,
>>>>>> + struct scmi_group_info *group)
>>>>>> +{
>>>>>> + int ret = 0;
>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>> +
>>>>>> + if (!handle || !handle->pinctrl_priv || !group)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + pi = handle->pinctrl_priv;
>>>>>> +
>>>>>> + ret = scmi_pinctrl_attributes(handle, GROUP_TYPE, selector,
>>>>>> + &group->name,
>>>>>> + &group->nr_pins);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> + if (!group->nr_pins) {
>>>>>> + dev_err(handle->dev, "Group %d has 0 elements", selector);
>>>>>> + return -ENODATA;
>>>>>> + }
>>>>>> +
>>>>>> + group->group_pins = devm_kmalloc_array(handle->dev,
>>>>>> group->nr_pins,
>>>>>> + sizeof(*group->group_pins),
>>>>>> + GFP_KERNEL);
>>>>>
>>>>> I think you can just use for the array allocation
>>>>>
>>>>> devm_kcalloc(dev, n, size, flags)
>>>>>
>>>>> and it will add also __GFP_ZERO internally to clear it.
>>>>> (indeed it calls in turn devm_kmalloc_array)
>>>>>
>>>>> ...BUT I think there is a further tricky issue here related to memory
>>>>> allocation...
>>>>>
>>>>> You call this and others function of this kind from some
>>>>> scmi_pinctrl_ops,
>>>>> like in scmi_pinctrl_get_group_pins
>>>>> (scmi_pinctrl_ops->get_group_pins),
>>>>> and then this is in turn called by the SCMI Pinctrl driver via
>>>>> pinctrl_ops->get_group_pins AND you set a present flag so that you
>>>>> issue a
>>>>> PINCTRL_LIST_ASSOCIATIONS and allocate here a new group_pins array
>>>>> just
>>>>> the first time: but these are never released anywhere, since, even
>>>>> though
>>>>> lazily dynamically allocated when asked for, these are static data
>>>>> that
>>>>> you pass to the caller/user of this protocol and so you cannot release
>>>>> them anytime soon, indeed.
>>>>>
>>>>> The core SCMI stack usually takes care to track and release all the
>>>>> devm_
>>>>> resources allocated by the protocol ONLY if they were allocated with
>>>>> devres
>>>>> while inside scmi_pinctrl_protocol_init() function.
>>>>> (see
>>>>> drivers/firmware/arm-scmi/driver.c:scmi_alloc_init_protocol_instance()
>>>>> and scmi_protocol_release)
>>>>>
>>>>> BUT you do not allocate these arrays inside the protocol-init
>>>>> function,
>>>>> you allocate them the first time these ops are called at runtime.
>>>>>
>>>>> If you unbind/unload all the drivers using this protocol and then
>>>>> reload
>>>>> them, all the devm_ allocations in protocol_init will be freed and
>>>>> reallocated BUT these arrays will never be freed (they are boudn to
>>>>> handle->dev)
>>>>> and instead they will be reallocated multiple times (present flag
>>>>> will be cleared
>>>>> on unload), remained unused and freed finally only when the whole
>>>>> SCMI stack is
>>>>> unbind/unloaded.
>>>>>
>>>>> You use a present flag to avoid reissuing the same query and
>>>>> reallocating all the arrays each time a driver calls these
>>>>> protocol_ops one, but really all these data is available early on at
>>>>> protocol init time and they are not supposed to change at runtime,
>>>>> dont they ?
>>>>>
>>>>> Even in a virtualized environment, you boot an agent and the SCMI
>>>>> platform server provides to the agent the list of associations when
>>>>> queried but then this does not change until the next reboot right ?
>>>>> (indeed you do not query more than once...)
>>>>>
>>>>> The agent can only change the PIN status with CONFIG_SET or
>>>>> FUNCTION_SELECT or REQUEST the exclusive use of a pin/group, but it is
>>>>> not that the platform can change the pin/groups associaion for the
>>>>> same
>>>>> agent at run time, this are static data for the whole life of the
>>>>> agent.
>>>>>
>>>>> Am I right ?
>>>>>
>>>>> IOW I think there is some potential memory leak on unbind/bind and it
>>>>> would
>>>>> be better to query and allocate all of these resources at init time
>>>>> and keep
>>>>> them ready to be retrieved by subsequent operations, since the
>>>>> lifetime
>>>>> of these resources is pretty long and they are basically representing
>>>>> static data that does not change after the init/probe phases.
>>>>>
>>>>> Indeed, all the other protocols usually allocate all the needed
>>>>> resources and query all the available SCMI resources once for all
>>>>> during
>>>>> the protocol_init, storing all the retrieved info in some struct
>>>>> *_info
>>>>> exposed in scmi_protocol.h and then provide some related
>>>>> protocol_ops to
>>>>> get the number of resources and to retrieve specific domain info
>>>>> descriptors.
>>>>> (voltage.c is an example and more on this down below...)
>>>>>
>>>>> This way, any dynamic allocation is done during protocol_init, so
>>>>> it can be automatically freed by the SCMI core once there are no more
>>>>> users of that protocol, and all of this static info data is queried
>>>>> and retrieved once for all at protocol initialization time, avoiding
>>>>> unneeded message exchanges to retrieve always the same data.
>>>>> (which you avoid anyway with the present flag)
>>>>>
>>>>> If you have a good reason to instead perform this sort of lazy
>>>>> allocation/query performed only at the last minute when someone ask
>>>>> for
>>>>> that specific resource, you will have to provide also a
>>>>> .instance_deinit
>>>>> function to clean anything you allocated out of the .instance_init
>>>>> routine; but this would seem strange to me since any resource that is
>>>>> discovered at init will be eventually immediately queried by a driver
>>>>> which uses this protocol...am I missing something ?
>>>>>
>>>>>> + if (!group->group_pins) {
>>>>>> + ret = -ENOMEM;
>>>>>> + goto err;
>>>>>> + }
>>>>>> +
>>>>>> + ret = scmi_pinctrl_list_associations(handle, selector,
>>>>>> GROUP_TYPE,
>>>>>> + group->nr_pins, group->group_pins);
>>>>>> + if (ret)
>>>>>> + goto err_groups;
>>>>>> +
>>>>>> + group->present = true;
>>>>>> + return 0;
>>>>>> +
>>>>>> + err_groups:
>>>>>> + kfree(group->group_pins);
>>>>>> + err:
>>>>>> + kfree(group->name);
>>>>>> + return ret;
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_get_group_name(const struct scmi_handle
>>>>>> *handle,
>>>>>> + u32 selector, const char **name)
>>>>>> +{
>>>>>> + int ret;
>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>> +
>>>>>> + if (!handle || !handle->pinctrl_priv || !name)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + pi = handle->pinctrl_priv;
>>>>>> +
>>>>>> + if (selector > pi->nr_groups)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + if (!pi->groups[selector].present) {
>>>>>> + ret = scmi_pinctrl_get_group_info(handle, selector,
>>>>>> + &pi->groups[selector]);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> + }
>>>>>> +
>>>>>> + *name = pi->groups[selector].name;
>>>>>> +
>>>>>> + return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_get_group_pins(const struct scmi_handle
>>>>>> *handle,
>>>>>> + u32 selector, const unsigned int **pins,
>>>>>> + unsigned int *nr_pins)
>>>>>> +{
>>>>>> + int ret;
>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>> +
>>>>>> + if (!handle || !handle->pinctrl_priv || !pins || !nr_pins)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + pi = handle->pinctrl_priv;
>>>>>> +
>>>>>> + if (selector > pi->nr_groups)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + if (!pi->groups[selector].present) {
>>>>>> + ret = scmi_pinctrl_get_group_info(handle, selector,
>>>>>> + &pi->groups[selector]);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> + }
>>>>>> +
>>>>>> + *pins = pi->groups[selector].group_pins;
>>>>>> + *nr_pins = pi->groups[selector].nr_pins;
>>>>>> +
>>>>>> + return ret;
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_get_function_info(const struct scmi_handle
>>>>>> *handle,
>>>>>> + u32 selector,
>>>>>> + struct scmi_function_info *func)
>>>>>> +{
>>>>>> + int ret = 0;
>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>> +
>>>>>> + if (!handle || !handle->pinctrl_priv || !func)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + pi = handle->pinctrl_priv;
>>>>>> +
>>>>>> + ret = scmi_pinctrl_attributes(handle, FUNCTION_TYPE, selector,
>>>>>> + &func->name,
>>>>>> + &func->nr_groups);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> + if (!func->nr_groups) {
>>>>>> + dev_err(handle->dev, "Function %d has 0 elements",
>>>>>> selector);
>>>>>> + return -ENODATA;
>>>>>> + }
>>>>>> +
>>>>>> + func->groups = devm_kmalloc_array(handle->dev, func->nr_groups,
>>>>>> + sizeof(*func->groups),
>>>>>> + GFP_KERNEL);
>>>>>> + if (!func->groups) {
>>>>>> + ret = -ENOMEM;
>>>>>> + goto err;
>>>>>> + }
>>>>>> +
>>>>>> + ret = scmi_pinctrl_list_associations(handle, selector,
>>>>>> FUNCTION_TYPE,
>>>>>> + func->nr_groups, func->groups);
>>>>>> + if (ret)
>>>>>> + goto err_funcs;
>>>>>> +
>>>>>> + func->present = true;
>>>>>> + return 0;
>>>>>> +
>>>>>> + err_funcs:
>>>>>> + kfree(func->groups);
>>>>>> + err:
>>>>>> + kfree(func->name);
>>>>>> + return ret;
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_get_function_name(const struct scmi_handle
>>>>>> *handle,
>>>>>> + u32 selector, const char **name)
>>>>>> +{
>>>>>> + int ret;
>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>> +
>>>>>> + if (!handle || !handle->pinctrl_priv || !name)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + pi = handle->pinctrl_priv;
>>>>>> +
>>>>>> + if (selector > pi->nr_functions)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + if (!pi->functions[selector].present) {
>>>>>> + ret = scmi_pinctrl_get_function_info(handle, selector,
>>>>>> + &pi->functions[selector]);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> + }
>>>>>> +
>>>>>> + *name = pi->functions[selector].name;
>>>>>> + return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_get_function_groups(const struct
>>>>>> scmi_handle *handle,
>>>>>> + u32 selector,
>>>>>> + unsigned int *nr_groups,
>>>>>> + const unsigned int **groups)
>>>>>> +{
>>>>>> + int ret;
>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>> +
>>>>>> + if (!handle || !handle->pinctrl_priv || !groups || !nr_groups)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + pi = handle->pinctrl_priv;
>>>>>> +
>>>>>> + if (selector > pi->nr_functions)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + if (!pi->functions[selector].present) {
>>>>>> + ret = scmi_pinctrl_get_function_info(handle, selector,
>>>>>> + &pi->functions[selector]);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> + }
>>>>>> +
>>>>>> + *groups = pi->functions[selector].groups;
>>>>>> + *nr_groups = pi->functions[selector].nr_groups;
>>>>>> +
>>>>>> + return ret;
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_set_mux(const struct scmi_handle *handle,
>>>>>> u32 selector,
>>>>>> + u32 group)
>>>>>> +{
>>>>>> + return scmi_pinctrl_function_select(handle, group, GROUP_TYPE,
>>>>>> + selector);
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_get_pin_info(const struct scmi_handle
>>>>>> *handle,
>>>>>> + u32 selector, struct scmi_pin_info *pin)
>>>>>> +{
>>>>>> + int ret = 0;
>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>> + unsigned int n_elems;
>>>>>> +
>>>>>> + if (!handle || !handle->pinctrl_priv || !pin)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + pi = handle->pinctrl_priv;
>>>>>> +
>>>>>> + ret = scmi_pinctrl_attributes(handle, PIN_TYPE, selector,
>>>>>> + &pin->name,
>>>>>> + &n_elems);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> + if (n_elems != pi->nr_pins) {
>>>>>> + dev_err(handle->dev, "Wrong pin count expected %d has %d",
>>>>>> + pi->nr_pins, n_elems);
>>>>>> + return -ENODATA;
>>>>>> + }
>>>>>> +
>>>>>> + if (*(pin->name) == 0) {
>>>>>> + dev_err(handle->dev, "Pin name is empty");
>>>>>> + goto err;
>>>>>> + }
>>>>>> +
>>>>>> + pin->present = true;
>>>>>> + return 0;
>>>>>> +
>>>>>> + err:
>>>>>> + kfree(pin->name);
>>>>>> + return ret;
>>>>>> +}
>>>>>> +
>>>>>> +static int scmi_pinctrl_get_pin_name(const struct scmi_handle
>>>>>> *handle, u32 selector,
>>>>>> + const char **name)
>>>>>> +{
>>>>>> +
>>>>>> + int ret;
>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>> +
>>>>>> + if (!handle || !handle->pinctrl_priv || !name)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + pi = handle->pinctrl_priv;
>>>>>> +
>>>>>> + if (selector > pi->nr_pins)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + if (!pi->pins[selector].present) {
>>>>>> + ret = scmi_pinctrl_get_pin_info(handle, selector,
>>>>>> + &pi->pins[selector]);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> + }
>>>>>> +
>>>>>> + *name = pi->pins[selector].name;
>>>>>> +
>>>>>> + return 0;
>>>>>> +}
>>>>>> +
>>>>>> +
>>>>>> +static const struct scmi_pinctrl_ops pinctrl_ops = {
>>>>>> + .get_groups_count = scmi_pinctrl_get_groups_count,
>>>>>> + .get_group_name = scmi_pinctrl_get_group_name,
>>>>>> + .get_group_pins = scmi_pinctrl_get_group_pins,
>>>>>> + .get_functions_count = scmi_pinctrl_get_functions_count,
>>>>>> + .get_function_name = scmi_pinctrl_get_function_name,
>>>>>> + .get_function_groups = scmi_pinctrl_get_function_groups,
>>>>>> + .set_mux = scmi_pinctrl_set_mux,
>>>>>> + .get_pin_name = scmi_pinctrl_get_pin_name,
>>>>>> + .get_pins_count = scmi_pinctrl_get_pins_count,
>>>>>> + .get_config = scmi_pinctrl_get_config,
>>>>>> + .set_config = scmi_pinctrl_set_config,
>>>>>> + .get_config_group = scmi_pinctrl_get_config_group,
>>>>>> + .set_config_group = scmi_pinctrl_set_config_group,
>>>>>> + .request_pin = scmi_pinctrl_request_pin,
>>>>>> + .free_pin = scmi_pinctrl_free_pin
>>>>>> +};
>>>>>> +
>>>>>> +static int scmi_pinctrl_protocol_init(struct scmi_handle *handle)
>>>>>> +{
>>>>>> + u32 version;
>>>>>> + struct scmi_pinctrl_info *pinfo;
>>>>>> + int ret;
>>>>>> +
>>>>>> + if (!handle)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + scmi_version_get(handle, SCMI_PROTOCOL_PINCTRL, &version);
>>>>>> +
>>>>>> + dev_dbg(handle->dev, "Pinctrl Version %d.%d\n",
>>>>>> + PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
>>>>>> +
>>>>>> + pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL);
>>>>>> + if (!pinfo)
>>>>>> + return -ENOMEM;
>>>>>> +
>>>>>> + ret = scmi_pinctrl_attributes_get(handle, pinfo);
>>>>>> + if (ret)
>>>>>> + goto free;
>>>>>> +
>>>>>> + pinfo->pins = devm_kmalloc_array(handle->dev, pinfo->nr_pins,
>>>>>> + sizeof(*pinfo->pins),
>>>>>> + GFP_KERNEL | __GFP_ZERO);
>>>>>
>>>>> devm_kcalloc() zeroes on its own
>>>>>
>>>>>> + if (!pinfo->pins) {
>>>>>> + ret = -ENOMEM;
>>>>>> + goto free;
>>>>>> + }
>>>>>> +
>>>>>> + pinfo->groups = devm_kmalloc_array(handle->dev,
>>>>>> pinfo->nr_groups,
>>>>>> + sizeof(*pinfo->groups),
>>>>>> + GFP_KERNEL | __GFP_ZERO);
>>>>>
>>>>> Ditto.
>>>>>> + if (!pinfo->groups) {
>>>>>> + ret = -ENOMEM;
>>>>>> + goto free;
>>>>>> + }
>>>>>> +
>>>>>> + pinfo->functions = devm_kmalloc_array(handle->dev,
>>>>>> pinfo->nr_functions,
>>>>>> + sizeof(*pinfo->functions),
>>>>>> + GFP_KERNEL | __GFP_ZERO);
>>>>>> + if (!pinfo->functions) {
>>>>>> + ret = -ENOMEM;
>>>>>> + goto free;
>>>>>> + }
>>>>>> +
>>>>>> + pinfo->version = version;
>>>>>> + handle->pinctrl_ops = &pinctrl_ops;
>>>>>> + handle->pinctrl_priv = pinfo;
>>>>>> +
>>>>>> + return 0;
>>>>>> +free:
>>>>>> + if (pinfo) {
>>>>>> + devm_kfree(handle->dev, pinfo->pins);
>>>>>> + devm_kfree(handle->dev, pinfo->functions);
>>>>>> + devm_kfree(handle->dev, pinfo->groups);
>>>>>> + }
>>>>>
>>>>> These frees are really not needed...if this function return failure
>>>>> any
>>>>> devres allocated in it is freed by the SCMI core. (as said
>>>>> above...in a
>>>>> recent kernel with the new API of course)
>>>>>
>>>>>> +
>>>>>> + devm_kfree(handle->dev, pinfo);
>>>>>> +
>>>>>> + return ret;
>>>>>> +}
>>>>>> +
>>>>>> +DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_PINCTRL,
>>>>>> pinctrl)
>>>>>> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
>>>>>> index 815095326e2d..68add4d06e8c 100644
>>>>>> --- a/drivers/pinctrl/Kconfig
>>>>>> +++ b/drivers/pinctrl/Kconfig
>>>>>> @@ -431,4 +431,13 @@ config PINCTRL_EQUILIBRIUM
>>>>>> pin functions, configure GPIO attributes for LGM SoC pins.
>>>>>> Pinmux and
>>>>>> pinconf settings are retrieved from device tree.
>>>>>> +config PINCTRL_SCMI
>>>>>> + bool "Pinctrl driver controlled via SCMI interface"
>>>>>> + depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
>>>>>> + help
>>>>>> + This driver provides support for pinctrl which is controlled
>>>>>> + by firmware that implements the SCMI interface.
>>>>>> + It uses SCMI Message Protocol to interact with the
>>>>>> + firmware providing all the pinctrl controls.
>>>>>> +
>>>>>
>>>>> This does NOT belong to this patch, but to the next right ?
>>>>>
>>>>>> endif
>>>>>> diff --git a/include/linux/scmi_protocol.h
>>>>>> b/include/linux/scmi_protocol.h
>>>>>> index 9cd312a1ff92..6a909ef3bf51 100644
>>>>>> --- a/include/linux/scmi_protocol.h
>>>>>> +++ b/include/linux/scmi_protocol.h
>>>>>> @@ -12,7 +12,8 @@
>>>>>> #include <linux/notifier.h>
>>>>>> #include <linux/types.h>
>>>>>> -#define SCMI_MAX_STR_SIZE 16
>>>>>> +#define SCMI_MAX_STR_SIZE 16
>>>>>> +#define SCMI_MAX_STR_EXT_SIZE 64
>>>>>
>>>>> This is handled as part of how the extended names are handled with
>>>>> ph->hops
>>>>> in a common way, as I was saying, so please move this if you need
>>>>> it in
>>>>> the protocol code, then I'll port to the ph->hops interface and clean
>>>>> up.
>>>>>
>>>>>> #define SCMI_MAX_NUM_RATES 16
>>>>>> /**
>>>>>> @@ -252,6 +253,55 @@ struct scmi_notify_ops {
>>>>>> struct notifier_block *nb);
>>>>>> };
>>>>>> +/**
>>>>>> + * struct scmi_pinctrl_ops - represents the various operations
>>>>>> provided
>>>>>> + * by SCMI Pinctrl Protocol
>>>>>> + *
>>>>>> + * @get_groups_count: returns count of the registered groups
>>>>>> + * @get_group_name: returns group name by index
>>>>>> + * @get_group_pins: returns the set of pins, assigned to the
>>>>>> specified group
>>>>>> + * @get_functions_count: returns count of the registered fucntions
>>>>>> + * @get_function_name: returns function name by indes
>>>>>> + * @get_function_groups: returns the set of groups, assigned to the
>>>>>> specified
>>>>>> + * function
>>>>>> + * @set_mux: set muxing function for groups of pins
>>>>>> + * @get_pins: returns the set of pins, registered in driver
>>>>>> + * @get_config: returns configuration parameter for pin
>>>>>> + * @set_config: sets the configuration parameter for pin
>>>>>> + * @get_config_group: returns the configuration parameter for a
>>>>>> group of pins
>>>>>> + * @set_config_group: sets the configuration parameter for a groups
>>>>>> of pins
>>>>>> + * @request_pin: aquire pin before selecting mux setting
>>>>>> + * @free_pin: frees pin, acquired by request_pin call
>>>>>> + */
>>>>>> +struct scmi_pinctrl_ops {
>>>>>> + int (*get_groups_count)(const struct scmi_handle *handle);
>>>>>> + int (*get_group_name)(const struct scmi_handle *handles, u32
>>>>>> selector,
>>>>>> + const char **name);
>>>>>> + int (*get_group_pins)(const struct scmi_handle *handle, u32
>>>>>> selector,
>>>>>> + const unsigned int **pins, unsigned int *nr_pins);
>>>>>> + int (*get_functions_count)(const struct scmi_handle *handle);
>>>>>> + int (*get_function_name)(const struct scmi_handle *handle, u32
>>>>>> selector,
>>>>>> + const char **name);
>>>>>> + int (*get_function_groups)(const struct scmi_handle *handle,
>>>>>> + u32 selector, unsigned int *nr_groups,
>>>>>> + const unsigned int **groups);
>>>>>> + int (*set_mux)(const struct scmi_handle *handle, u32 selector,
>>>>>> + u32 group);
>>>>>> + int (*get_pin_name)(const struct scmi_handle *handle, u32
>>>>>> selector,
>>>>>> + const char **name);
>>>>>> + int (*get_pins_count)(const struct scmi_handle *handle);
>>>>>> + int (*get_config)(const struct scmi_handle *handle, u32 pin,
>>>>>> + u32 *config);
>>>>>> + int (*set_config)(const struct scmi_handle *handle, u32 pin,
>>>>>> + u32 config);
>>>>>> + int (*get_config_group)(const struct scmi_handle *handle, u32
>>>>>> pin,
>>>>>> + u32 *config);
>>>>>> + int (*set_config_group)(const struct scmi_handle *handle, u32
>>>>>> pin,
>>>>>> + u32 config);
>>>>>> + int (*request_pin)(const struct scmi_handle *handle, u32 pin);
>>>>>> + int (*free_pin)(const struct scmi_handle *handle, u32 pin);
>>>>>> +};
>>>>>> +
>>>>>
>>>>> As mentioned above, here you could drop a lot of this
>>>>> get_X_count/name/pins
>>>>> and instead expose a few of the internal proocol struct scmi__X_info
>>>>> and then
>>>>> provide just a mean to query how many resource are there and then get
>>>>> the info
>>>>> descriptor you want for the specific domain_id, i.e.:
>>>>>
>>>>> int (*num_domains_get)(ph, type)
>>>>> void *(*info_get)(ph, type, domain_id);
>>>>>
>>>>> Thanks,
>>>>> Cristian
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> linux-arm-kernel mailing list
>>>>> [email protected]
>>>>> https://urldefense.com/v3/__http://lists.infradead.org/mailman/listinfo/linux-arm-kernel__;!!GF_29dbcQIUBPA!y2hR3PEGGxiPjVeXBcgGyV03DPDhzgUKR0uHvsTpiafKgBar8Egc6oOOs-IkFIquhSf-qBzltqEMyzRZrZi7qlk$
>>>>>
>>>>> [lists[.]infradead[.]org]
On 25.04.23 05:13, Peng Fan wrote:
>
>
> On 4/24/2023 6:33 PM, Oleksii Moisieiev wrote:
>>
>> On 24.04.23 04:52, Peng Fan wrote:
>>>
>>>
>>> On 4/21/2023 4:40 PM, Oleksii Moisieiev wrote:
>>>> Hi Peng Fan,
>>>>
>>>> On 17.04.23 05:55, Peng Fan wrote:
>>>>>
>>>>>
>>>>> On 4/13/2023 6:04 AM, Cristian Marussi wrote:
>>>>>> On Fri, Apr 07, 2023 at 10:18:27AM +0000, Oleksii Moisieiev wrote:
>>>>>>> Implementation of the SCMI client driver, which implements
>>>>>>> PINCTRL_PROTOCOL. This protocol has ID 19 and is described
>>>>>>> in the latest DEN0056 document.
>>>>>>
>>>>>> Hi,
>>>>>>
>>>>>>> This protocol is part of the feature that was designed to
>>>>>>> separate the pinctrl subsystem from the SCP firmware.
>>>>>>> The idea is to separate communication of the pin control
>>>>>>> subsystem with the hardware to SCP firmware
>>>>>>> (or a similar system, such as ATF), which provides an interface
>>>>>>> to give the OS ability to control the hardware through SCMI
>>>>>>> protocol.
>>>>>>> This is a generic driver that implements SCMI protocol,
>>>>>>> independent of the platform type.
>>>>>>>
>>>>>>> DEN0056 document:
>>>>>>> https://urldefense.com/v3/__https://developer.arm.com/documentation/den0056/latest__;!!GF_29dbcQIUBPA!y2hR3PEGGxiPjVeXBcgGyV03DPDhzgUKR0uHvsTpiafKgBar8Egc6oOOs-IkFIquhSf-qBzltqEMyzRZHq8eC4g$
>>>>>>>
>>>>>>>
>>>>>>> [developer[.]arm[.]com]
>>>>>>>
>>>>>>
>>>>>> No need to specify all of this in the commit message, just a note
>>>>>> that
>>>>>> you are adding a new SCMIv3.2 Pincontrol protocol, highlighting
>>>>>> anything
>>>>>> that has been left out in this patch (if any) will be enough.
>>>>>
>>>>> Is it possible to extend the spec to support multilple uint32_t
>>>>> for PIN
>>>>> CONFIG SET?
>>>>>
>>>>> With only one uint32_t could not satisfy i.MX requirement.
>>>>>
>>>>> Thanks,
>>>>> Peng.
>>>>>
>>>> IIUC you are expecting to have an ability to set some kind of array of
>>>> uint32_t config values to some specific ConfigType?
>>>>
>>>> I'm not sure if it's supported by pintctrl subsystem right now. I was
>>>> unable to find an example in the existing device-tree pinctrl
>>>> bindings.
>>>> This makes me think that this kind of binding is OEM specific.
>>>
>>> Look at arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts
>>>
>>> Take lpuart for example:
>>> MX93_PAD_UART1_RXD__LPUART1_RX 0x31e
>>>
>>> The macro MX93_PAD_UART1_RXD__LPUART1_RX is an array.
>>>
>>> i.MX pinctrl driver use
>>> mux reg, mux value, select reg, select value, pad reg, pad value
>>>
>>> pinctrl driver will be handled by SCMI firmware for new i.MX SoC.
>>>
>>> With current spec, only one uint32 is low speed. So if the spec
>>> could extended to support more uint32 with pin config set,
>>> we could use one SCMI call to support i.MX pinctrl.
>>>
>>> Thanks
>>> Peng.
>>>
>> Hi Peng Fan,
>>
>> Thank you for the example.
>>
>> According to the comment for the function imx_pinctrl_parse_pin_mmio:
>>
>> /*
>> * Each pin represented in fsl,pins consists of a number of u32
>> PIN_FUNC_ID
>> * and 1 u32 CONFIG, the total size is PIN_FUNC_ID + CONFIG for
>> each pin.
>> * For generic_pinconf case, there's no extra u32 CONFIG.
>> *
>> * PIN_FUNC_ID format:
>> * Default:
>> * <mux_reg conf_reg input_reg mux_mode input_val>
>> * SHARE_MUX_CONF_REG:
>> * <mux_conf_reg input_reg mux_mode input_val>
>> * IMX_USE_SCU:
>> * <pin_id mux_mode>
>> */
>>
>> IIUC this says that in scu format it is expected to use <pin_id
>> mux_mode>. This fit's protocol expectations.
>
> The comments are misleading, it should be pin_id mux_mode config_setting
> See arch/arm64/boot/dts/freescale/imx8qm-mek.dts
>
>>
>> The idea was to make all platform specific implementation, such as
>> <mux_reg conf_reg input_reg mux_mode input_val>
>>
>> on the SCP Firmware side and hide it under Pin ID and Config Type so
>> Client side can apply generic config in the device tree.
>>
>> Can't you use scu format here? Or execute
>> imx_pinconf_decode_generic_config on SCMI server side?
>
> Technically with one config value is doable, but that means
> SCMI needs expose many pin ids, such as pin id SDHC_CD,
> it could be muxed as SDHC_CD, GPIO1_5, SAI_RX. Then
> scmi needs export:
> SDHC_CD_SDHC_CD
> SDHC_CD_GPIO1_5
> SDHC_CD_SAI_RX
>
>
> If with multiple uint32_value support by spec,
> scmi just needs exports SDHC_CD, with other values passed
> through uint32 value.
>
> Regards,
> Peng.
>
>
Hi Peng,
I think SCMI protocol can be modified to accept array of uint32_t of
config_values on PINCTRL_CONFIG_SET
and return array for config_values on PINCTRL_CONFIG_GET. Let's see what
others think about that.
Thanks,
Oleksii.
>>
>> Best regards,
>>
>> Oleksii.
>>
>>>>
>>>> Maybe it can be implemented by adding new IDs to OEM specific range
>>>> (192-255) which is reserved for OEM specific units (See Table 23 of
>>>> DEN0056E).
>>>>
>>>> Best regards,
>>>>
>>>> Oleksii
>>>>
>>>>
>>>>>> You can look at the very first commit logs of existing protos as an
>>>>>> example like: drivers/firmware/arm_scmi/powercap.c
>>>>>>
>>>>>> Some more comments down below, I'll mostly skip anything related to
>>>>>> the
>>>>>> SCMI API change I mentioned before...
>>>>>>
>>>>>> I'll also wont comment on more trivial stuff related to style, BUT
>>>>>> there
>>>>>> are lots of them: you should run
>>>>>>
>>>>>> ./scripts/checkpatch.pl --strict <your-git-format-patch-file>
>>>>>>
>>>>>> for each patch in the series. (and fix accordingly..spacing,
>>>>>> brackets...etc)
>>>>>>
>>>>>>> Signed-off-by: Oleksii Moisieiev <[email protected]>
>>>>>>> ---
>>>>>>> MAINTAINERS | 6 +
>>>>>>> drivers/firmware/arm_scmi/Makefile | 2 +-
>>>>>>> drivers/firmware/arm_scmi/common.h | 1 +
>>>>>>> drivers/firmware/arm_scmi/driver.c | 3 +
>>>>>>> drivers/firmware/arm_scmi/pinctrl.c | 905
>>>>>>> ++++++++++++++++++++++++++++
>>>>>>> drivers/pinctrl/Kconfig | 9 +
>>>>>>> include/linux/scmi_protocol.h | 58 +-
>>>>>>> 7 files changed, 982 insertions(+), 2 deletions(-)
>>>>>>> create mode 100644 drivers/firmware/arm_scmi/pinctrl.c
>>>>>>>
>>>>>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>>>>>> index 281de213ef47..abc543fd7544 100644
>>>>>>> --- a/MAINTAINERS
>>>>>>> +++ b/MAINTAINERS
>>>>>>> @@ -16961,6 +16961,12 @@ F: drivers/reset/reset-scmi.c
>>>>>>> F: include/linux/sc[mp]i_protocol.h
>>>>>>> F: include/trace/events/scmi.h
>>>>>>> +PINCTRL DRIVER FOR SYSTEM CONTROL & POWER/MANAGEMENT INTERFACE
>>>>>>> (SCPI/SCMI)
>>>>>>> +M: Oleksii Moisieiev <[email protected]>
>>>>>>> +L: [email protected]
>>>>>>> +S: Maintained
>>>>>>> +F: drivers/firmware/arm_scmi/pinctrl.c
>>>>>>> +
>>>>>>> SYSTEM RESET/SHUTDOWN DRIVERS
>>>>>>> M: Sebastian Reichel <[email protected]>
>>>>>>> L: [email protected]
>>>>>>> diff --git a/drivers/firmware/arm_scmi/Makefile
>>>>>>> b/drivers/firmware/arm_scmi/Makefile
>>>>>>> index bc0d54f8e861..5cec357fbfa8 100644
>>>>>>> --- a/drivers/firmware/arm_scmi/Makefile
>>>>>>> +++ b/drivers/firmware/arm_scmi/Makefile
>>>>>>> @@ -4,7 +4,7 @@ scmi-driver-y = driver.o notify.o
>>>>>>> scmi-transport-y = shmem.o
>>>>>>> scmi-transport-$(CONFIG_MAILBOX) += mailbox.o
>>>>>>> scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o
>>>>>>> -scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o
>>>>>>> system.o
>>>>>>> +scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o
>>>>>>> system.o pinctrl.o
>>>>>>> scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y)
>>>>>>> $(scmi-protocols-y) \
>>>>>>> $(scmi-transport-y)
>>>>>>> obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
>>>>>>> diff --git a/drivers/firmware/arm_scmi/common.h
>>>>>>> b/drivers/firmware/arm_scmi/common.h
>>>>>>> index 65063fa948d4..8bbb404abe8d 100644
>>>>>>> --- a/drivers/firmware/arm_scmi/common.h
>>>>>>> +++ b/drivers/firmware/arm_scmi/common.h
>>>>>>> @@ -170,6 +170,7 @@ DECLARE_SCMI_REGISTER_UNREGISTER(power);
>>>>>>> DECLARE_SCMI_REGISTER_UNREGISTER(reset);
>>>>>>> DECLARE_SCMI_REGISTER_UNREGISTER(sensors);
>>>>>>> DECLARE_SCMI_REGISTER_UNREGISTER(system);
>>>>>>> +DECLARE_SCMI_REGISTER_UNREGISTER(pinctrl);
>>>>>>> #define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(id, name) \
>>>>>>> int __init scmi_##name##_register(void) \
>>>>>>> diff --git a/drivers/firmware/arm_scmi/driver.c
>>>>>>> b/drivers/firmware/arm_scmi/driver.c
>>>>>>> index 3dfd8b6a0ebf..fb9525fb3c24 100644
>>>>>>> --- a/drivers/firmware/arm_scmi/driver.c
>>>>>>> +++ b/drivers/firmware/arm_scmi/driver.c
>>>>>>> @@ -743,6 +743,7 @@ static struct scmi_prot_devnames devnames[] = {
>>>>>>> { SCMI_PROTOCOL_CLOCK, { "clocks" },},
>>>>>>> { SCMI_PROTOCOL_SENSOR, { "hwmon" },},
>>>>>>> { SCMI_PROTOCOL_RESET, { "reset" },},
>>>>>>> + { SCMI_PROTOCOL_PINCTRL, { "pinctrl" },},
>>>>>>> };
>>>>>>> static inline void
>>>>>>> @@ -947,6 +948,7 @@ static int __init scmi_driver_init(void)
>>>>>>> scmi_reset_register();
>>>>>>> scmi_sensors_register();
>>>>>>> scmi_system_register();
>>>>>>> + scmi_pinctrl_register();
>>>>>>> return platform_driver_register(&scmi_driver);
>>>>>>> }
>>>>>>> @@ -962,6 +964,7 @@ static void __exit scmi_driver_exit(void)
>>>>>>> scmi_reset_unregister();
>>>>>>> scmi_sensors_unregister();
>>>>>>> scmi_system_unregister();
>>>>>>> + scmi_pinctrl_unregister();
>>>>>>> platform_driver_unregister(&scmi_driver);
>>>>>>> }
>>>>>>> diff --git a/drivers/firmware/arm_scmi/pinctrl.c
>>>>>>> b/drivers/firmware/arm_scmi/pinctrl.c
>>>>>>> new file mode 100644
>>>>>>> index 000000000000..037270d7f39b
>>>>>>> --- /dev/null
>>>>>>> +++ b/drivers/firmware/arm_scmi/pinctrl.c
>>>>>>> @@ -0,0 +1,905 @@
>>>>>>> +// SPDX-License-Identifier: GPL-2.0
>>>>>>> +/*
>>>>>>> + * System Control and Management Interface (SCMI) Pinctrl Protocol
>>>>>>> + *
>>>>>>> + * Copyright (C) 2021 EPAM.
>>>>>>
>>>>>> nitpick: update (C) years
>>>>>>
>>>>>>> + */
>>>>>>> +
>>>>>>> +#define pr_fmt(fmt) "SCMI Notifications PINCTRL - " fmt
>>>>>>> +
>>>>>>
>>>>>> This is not needed, no notifs in this proto.
>>>>>>
>>>>>>> +#include <linux/scmi_protocol.h>
>>>>>>> +#include <linux/slab.h>
>>>>>>> +
>>>>>>> +#include "common.h"
>>>>>>> +#include "notify.h"
>>>>>>
>>>>>> Notifs not needed, and in the new API world you'll just need a:
>>>>>>
>>>>>> #include "protocols.h"
>>>>>>
>>>>>>> +
>>>>>>> +#define SET_TYPE(x) ((x) & 0x3)
>>>>>>> +
>>>>>>
>>>>>> Even if trivial better to use std bitfield.h macros like
>>>>>> FIELD_GET() BIT() ... etc
>>>>>>
>>>>>>> +enum scmi_pinctrl_protocol_cmd {
>>>>>>> + PINCTRL_ATTRIBUTES = 0x3,
>>>>>>> + PINCTRL_LIST_ASSOCIATIONS = 0x4,
>>>>>>> + PINCTRL_CONFIG_GET = 0x5,
>>>>>>> + PINCTRL_CONFIG_SET = 0x6,
>>>>>>> + PINCTRL_FUNCTION_SELECT = 0x7,
>>>>>>> + PINCTRL_REQUEST = 0x8,
>>>>>>> + PINCTRL_RELEASE = 0x9,
>>>>>>> + PINCTRL_NAME_GET = 0xa,
>>>>>>> + PINCTRL_SET_PERMISSIONS = 0xb
>>>>>>> +};
>>>>>>> +
>>>>>>> +enum scmi_pinctrl_selector_type {
>>>>>>> + PIN_TYPE = 0,
>>>>>>> + GROUP_TYPE,
>>>>>>> + FUNCTION_TYPE
>>>>>>> +};
>>>>>>> +
>>>>>>> +struct scmi_group_info {
>>>>>>> + bool present;
>>>>>>> + char *name;
>>>>>>> + unsigned int *group_pins;
>>>>>>> + unsigned int nr_pins;
>>>>>>> +};
>>>>>>> +
>>>>>>> +struct scmi_function_info {
>>>>>>> + bool present;
>>>>>>> + char *name;
>>>>>>> + unsigned int *groups;
>>>>>>> + unsigned int nr_groups;
>>>>>>> +};
>>>>>>> +
>>>>>>> +struct scmi_pin_info {
>>>>>>> + bool present;
>>>>>>> + char *name;
>>>>>>> +};
>>>>>>> +
>>>>>>> +struct scmi_pinctrl_info {
>>>>>>> + u32 version;
>>>>>>> + u16 nr_groups;
>>>>>>> + u16 nr_functions;
>>>>>>> + u16 nr_pins;
>>>>>>
>>>>>> Since these vars are not related to stricly spaced message fields
>>>>>> (even though
>>>>>> derived from such messages) do not use sized types, you can just
>>>>>> stick with
>>>>>> unsigned int. (it is also better not to mix sized and unsized types
>>>>>> in the same
>>>>>> struct). This also could come handy if these will be exposed to the
>>>>>> user
>>>>>> in scmi_protocol.h in the future (more on this down below)
>>>>>>
>>>>>>> + struct scmi_group_info *groups;
>>>>>>> + struct scmi_function_info *functions;
>>>>>>> + struct scmi_pin_info *pins;
>>>>>>> +};
>>>>>>> +
>>>>>>> +struct scmi_conf_tx {
>>>>>>> + __le32 identifier;
>>>>>>> +#define SET_TYPE_BITS(attr, x) (((attr) & 0xFFFFFCFF) | (x & 0x3)
>>>>>>> << 8)
>>>>>>> +#define SET_CONFIG(attr, x) (((attr) & 0xFF) | (x & 0xFF))
>>>>>>
>>>>>> Use bitfield.h like FIELD_SET / GENMASK etc
>>>>>>
>>>>>>> + __le32 attributes;
>>>>>>> +};
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_attributes_get(const struct scmi_handle
>>>>>>> *handle,
>>>>>>> + struct scmi_pinctrl_info *pi)
>>>>>>> +{
>>>>>>> + int ret;
>>>>>>> + struct scmi_xfer *t;
>>>>>>> + struct scmi_msg_pinctrl_protocol_attributes {
>>>>>>> +#define GROUPS_NR(x) ((x) >> 16)
>>>>>>> +#define PINS_NR(x) ((x) & 0xffff)
>>>>>>> + __le32 attributes_low;
>>>>>>> +#define FUNCTIONS_NR(x) ((x) & 0xffff)
>>>>>>> + __le32 attributes_high;
>>>>>>> + } *attr;
>>>>>>
>>>>>> For consistency with the rest of the stack (mostly :D), please move
>>>>>> this struct
>>>>>> definition and related macros outside in the global scope right
>>>>>> after
>>>>>> command
>>>>>> enum. (and use bitfield macros ...)
>>>>>>
>>>>>>> +
>>>>>>> + if (!pi)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
>>>>>>> + SCMI_PROTOCOL_PINCTRL, 0, sizeof(*attr), &t);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> +
>>>>>>> + attr = t->rx.buf;
>>>>>>> +
>>>>>>> + ret = scmi_do_xfer(handle, t);
>>>>>>> + if (!ret) {
>>>>>>> + pi->nr_functions =
>>>>>>> + le16_to_cpu(FUNCTIONS_NR(attr->attributes_high));
>>>>>>> + pi->nr_groups =
>>>>>>> le16_to_cpu(GROUPS_NR(attr->attributes_low));
>>>>>>> + pi->nr_pins = le16_to_cpu(PINS_NR(attr->attributes_low));
>>>>>>> + }
>>>>>>> +
>>>>>>> + scmi_xfer_put(handle, t);
>>>>>>> + return ret;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_get_groups_count(const struct scmi_handle
>>>>>>> *handle)
>>>>>>> +{
>>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>>> +
>>>>>>> + if (!handle || !handle->pinctrl_priv)
>>>>>>> + return -ENODEV;
>>>>>>> +
>>>>>>> + pi = handle->pinctrl_priv;
>>>>>>> +
>>>>>>> + return pi->nr_groups;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_get_pins_count(const struct scmi_handle
>>>>>>> *handle)
>>>>>>> +{
>>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>>> +
>>>>>>> + if (!handle || !handle->pinctrl_priv)
>>>>>>> + return -ENODEV;
>>>>>>> +
>>>>>>> + pi = handle->pinctrl_priv;
>>>>>>> +
>>>>>>> + return pi->nr_pins;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_get_functions_count(const struct
>>>>>>> scmi_handle *handle)
>>>>>>> +{
>>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>>> +
>>>>>>> + if (!handle || !handle->pinctrl_priv)
>>>>>>> + return -ENODEV;
>>>>>>> +
>>>>>>> + pi = handle->pinctrl_priv;
>>>>>>> +
>>>>>>> + return pi->nr_functions;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_validate_id(const struct scmi_handle
>>>>>>> *handle,
>>>>>>> + u32 identifier,
>>>>>>> + enum scmi_pinctrl_selector_type type)
>>>>>>> +{
>>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>>> +
>>>>>>> + if (!handle || !handle->pinctrl_priv)
>>>>>>> + return -ENODEV;
>>>>>>> +
>>>>>>> + switch (type) {
>>>>>>> + case PIN_TYPE:
>>>>>>> + pi = handle->pinctrl_priv;
>>>>>>> +
>>>>>>> + return (identifier < pi->nr_pins) ? 0 : -EINVAL;
>>>>>>> + case GROUP_TYPE:
>>>>>>> + return (identifier <
>>>>>>> + scmi_pinctrl_get_groups_count(handle)) ?
>>>>>>> + 0 : -EINVAL;
>>>>>>> + case FUNCTION_TYPE:
>>>>>>> + return (identifier <
>>>>>>> + scmi_pinctrl_get_functions_count(handle)) ?
>>>>>>> + 0 : -EINVAL;
>>>>>>> + default:
>>>>>>> + return -EINVAL;
>>>>>>> + }
>>>>>>
>>>>>> Here I would just pick the right value to compare, break and then
>>>>>> compare and exit, something aroundf the lines of:
>>>>>>
>>>>>> case PIN_TYPE:
>>>>>> ...
>>>>>> val = pi->nr_pins;
>>>>>> break;
>>>>>> ...
>>>>>> case GROUP_TYPE:
>>>>>> val = scmi_pinctrl_get_groups_count());
>>>>>> break;
>>>>>>
>>>>>> ....
>>>>>> ....
>>>>>> default:
>>>>>> return -EINVAL;
>>>>>> }
>>>>>>
>>>>>> if (identifier >= val)
>>>>>> return -EINVAL;
>>>>>>
>>>>>> return 0;
>>>>>> }
>>>>>>
>>>>>> ... it's easier to read. What do you think ?
>>>>>>
>>>>>>
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_get_name(const struct scmi_handle *handle,
>>>>>>> + u32 identifier,
>>>>>>> + enum scmi_pinctrl_selector_type type,
>>>>>>> + char **name)
>>>>>>> +{
>>>>>>
>>>>>> As said, there is common helper for this, but it will need some
>>>>>> small
>>>>>> adaptation in the SCMI core to work here so keep it as it is, and
>>>>>> I'll take
>>>>>> care of this later, if it sounds fine for you.
>>>>>>
>>>>>>> + struct scmi_xfer *t;
>>>>>>> + int ret = 0;
>>>>>>> + struct scmi_name_tx {
>>>>>>> + __le32 identifier;
>>>>>>> + __le32 flags;
>>>>>>> + } *tx;
>>>>>>> + struct scmi_name_rx {
>>>>>>> + __le32 flags;
>>>>>>> + u8 name[64];
>>>>>>> + } *rx;
>>>>>>> +
>>>>>>> + if (!handle || !name)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> +
>>>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_NAME_GET,
>>>>>>> + SCMI_PROTOCOL_PINCTRL,
>>>>>>> + sizeof(*tx), sizeof(*rx), &t);
>>>>>>> +
>>>>>>> + tx = t->tx.buf;
>>>>>>> + rx = t->rx.buf;
>>>>>>> + tx->identifier = identifier;
>>>>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>>>>> +
>>>>>>> + ret = scmi_do_xfer(handle, t);
>>>>>>> + if (ret)
>>>>>>> + goto out;
>>>>>>> +
>>>>>>> + if (rx->flags) {
>>>>>>> + ret = -EINVAL;
>>>>>>> + goto out;
>>>>>>> + }
>>>>>>> +
>>>>>>> + *name = kasprintf(GFP_KERNEL, "%s", rx->name);
>>>>>>> + if (!*name)
>>>>>>> + ret = -ENOMEM;
>>>>>>> + out:
>>>>>>> + scmi_xfer_put(handle, t);
>>>>>>> +
>>>>>>> + return ret;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_attributes(const struct scmi_handle
>>>>>>> *handle,
>>>>>>> + enum scmi_pinctrl_selector_type type,
>>>>>>> + u32 selector, char **name,
>>>>>>> + unsigned int *n_elems)
>>>>>>> +{
>>>>>>> + int ret = 0;
>>>>>>> + struct scmi_xfer *t;
>>>>>>> + struct scmi_pinctrl_attributes_tx {
>>>>>>> + __le32 identifier;
>>>>>>> + __le32 flags;
>>>>>>> + } *tx;
>>>>>>> + struct scmi_pinctrl_attributes_rx {
>>>>>>> +#define EXT_NAME_FLAG(x) ((x) & BIT(31))
>>>>>>> +#define NUM_ELEMS(x) ((x) & 0xffff)
>>>>>>> + __le32 attributes;
>>>>>>> + u8 name[16];
>>>>>>> + } *rx;
>>>>>>
>>>>>> Ditto. Move these defs outside, bitfield.h for macros and try to
>>>>>> use the
>>>>>> same naming style for message structs as in other protos, i.e.
>>>>>>
>>>>>> for commands: struct scmi_msg_pinctrl_attributes
>>>>>> for replies: struct scmi_resp_pinctrl_attributes
>>>>>>
>>>>>> (or some variations around that...
>>>>>> scmi_msg_cmd_* scmi_msg_resp_*
>>>>>>
>>>>>> we have not been fully consistent really, so I dont want to be
>>>>>> pedantic here, but we never used tx/rx in message context since
>>>>>> it is
>>>>>> already (mis)-used in SCMI channel context...)
>>>>>>
>>>>>>> +
>>>>>>> + if (!handle || !name)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> +
>>>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_ATTRIBUTES,
>>>>>>> + SCMI_PROTOCOL_PINCTRL, sizeof(*tx), sizeof(*rx), &t);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> +
>>>>>>> + tx = t->tx.buf;
>>>>>>> + rx = t->rx.buf;
>>>>>>> + tx->identifier = selector;
>>>>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>>>>> +
>>>>>>> + ret = scmi_do_xfer(handle, t);
>>>>>>> + if (ret)
>>>>>>> + goto out;
>>>>>>> +
>>>>>>> + *n_elems = NUM_ELEMS(rx->attributes);
>>>>>>> +
>>>>>>> + if (!EXT_NAME_FLAG(rx->attributes)) {
>>>>>>> + *name = kasprintf(GFP_KERNEL, "%s", rx->name);
>>>>>>> + if (!*name)
>>>>>>> + ret = -ENOMEM;
>>>>>>> + } else
>>>>>>> + ret = scmi_pinctrl_get_name(handle, selector, type, name);
>>>>>>> + out:
>>>>>>> + scmi_xfer_put(handle, t);
>>>>>>> + return ret;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_list_associations(const struct scmi_handle
>>>>>>> *handle,
>>>>>>> + u32 selector,
>>>>>>> + enum scmi_pinctrl_selector_type type,
>>>>>>> + uint16_t size, unsigned int *array)
>>>>>>> +{
>>>>>>
>>>>>> This is the other functionalities you could implement straight away
>>>>>> using
>>>>>> ph->hops helpers (iterators) but just leave it this way, and I'll
>>>>>> port it later
>>>>>> (once we retested all of this as working with the new API but
>>>>>> without
>>>>>> any
>>>>>> ph->hops usage..I think it is safer to change one bit at time... :P)
>>>>>>
>>>>>>> + struct scmi_xfer *t;
>>>>>>> + struct scmi_pinctrl_list_assoc_tx {
>>>>>>> + __le32 identifier;
>>>>>>> + __le32 flags;
>>>>>>> + __le32 index;
>>>>>>> + } *tx;
>>>>>>> + struct scmi_pinctrl_list_assoc_rx {
>>>>>>> +#define RETURNED(x) ((x) & 0xFFF)
>>>>>>> +#define REMAINING(x) ((x) >> 16)
>>>>>>> + __le32 flags;
>>>>>>> + __le16 array[];
>>>>>>> + } *rx;
>>>>>>
>>>>>> Ditto, about struct naming and macros.
>>>>>>
>>>>>>> + u16 tot_num_ret = 0, loop_num_ret;
>>>>>>> + u16 remaining_num_ret;
>>>>>>> + int ret, loop;
>>>>>>> +
>>>>>>> + if (!handle || !array || !size)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + if (type == PIN_TYPE)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> +
>>>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_LIST_ASSOCIATIONS,
>>>>>>> + SCMI_PROTOCOL_PINCTRL, sizeof(*tx),
>>>>>>> + 0, &t);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> +
>>>>>>> + tx = t->tx.buf;
>>>>>>> + rx = t->rx.buf;
>>>>>>> +
>>>>>>> + do {
>>>>>>> + tx->identifier = cpu_to_le32(selector);
>>>>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>>>>> + tx->index = cpu_to_le32(tot_num_ret);
>>>>>>> +
>>>>>>> + ret = scmi_do_xfer(handle, t);
>>>>>>> + if (ret)
>>>>>>> + break;
>>>>>>> +
>>>>>>> + loop_num_ret = le32_to_cpu(RETURNED(rx->flags));
>>>>>>> + remaining_num_ret = le32_to_cpu(REMAINING(rx->flags));
>>>>>>> +
>>>>>>> + for (loop = 0; loop < loop_num_ret; loop++) {
>>>>>>> + if (tot_num_ret + loop >= size) {
>>>>>>> + ret = -EMSGSIZE;
>>>>>>> + goto out;
>>>>>>> + }
>>>>>>> +
>>>>>>> + array[tot_num_ret + loop] =
>>>>>>> + le16_to_cpu(rx->array[loop]);
>>>>>>> + }
>>>>>>> +
>>>>>>> + tot_num_ret += loop_num_ret;
>>>>>>> +
>>>>>>> + scmi_reset_rx_to_maxsz(handle, t);
>>>>>>> + } while (remaining_num_ret > 0);
>>>>>>> +out:
>>>>>>> + scmi_xfer_put(handle, t);
>>>>>>> + return ret;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_request_config(const struct scmi_handle
>>>>>>> *handle,
>>>>>>> + u32 selector,
>>>>>>> + enum scmi_pinctrl_selector_type type,
>>>>>>> + u32 *config)
>>>>>>> +{
>>>>>>> + struct scmi_xfer *t;
>>>>>>> + struct scmi_conf_tx *tx;
>>>>>>> + __le32 *packed_config;
>>>>>>> + u32 attributes = 0;
>>>>>>> + int ret;
>>>>>>> +
>>>>>>> + if (!handle || !config || type == FUNCTION_TYPE)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> +
>>>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_GET,
>>>>>>> + SCMI_PROTOCOL_PINCTRL,
>>>>>>> + sizeof(*tx), sizeof(*packed_config), &t);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> +
>>>>>>> + tx = t->tx.buf;
>>>>>>> + packed_config = t->rx.buf;
>>>>>>> + tx->identifier = cpu_to_le32(selector);
>>>>>>> + attributes = SET_TYPE_BITS(attributes, type);
>>>>>>> + attributes = SET_CONFIG(attributes, *config);
>>>>>>> +
>>>>>>> + tx->attributes = cpu_to_le32(attributes);
>>>>>>> +
>>>>>>> + ret = scmi_do_xfer(handle, t);
>>>>>>> +
>>>>>>> + if (!ret)
>>>>>>> + *config = le32_to_cpu(*packed_config);
>>>>>>> +
>>>>>>> + scmi_xfer_put(handle, t);
>>>>>>> + return ret;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_get_config(const struct scmi_handle
>>>>>>> *handle, u32 pin,
>>>>>>> + u32 *config)
>>>>>>> +{
>>>>>>> + return scmi_pinctrl_request_config(handle, pin, PIN_TYPE,
>>>>>>> config);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_apply_config(const struct scmi_handle
>>>>>>> *handle,
>>>>>>> + u32 selector,
>>>>>>> + enum scmi_pinctrl_selector_type type,
>>>>>>> + u32 config)
>>>>>>> +{
>>>>>>> + struct scmi_xfer *t;
>>>>>>> + struct scmi_conf_tx *tx;
>>>>>>> + u32 attributes = 0;
>>>>>>> + int ret;
>>>>>>> +
>>>>>>> + if (!handle || type == FUNCTION_TYPE)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + ret = scmi_pinctrl_validate_id(handle, selector, type);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> +
>>>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_CONFIG_SET,
>>>>>>> + SCMI_PROTOCOL_PINCTRL,
>>>>>>> + sizeof(*tx), 0, &t);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> +
>>>>>>> + tx = t->tx.buf;
>>>>>>> + tx->identifier = cpu_to_le32(selector);
>>>>>>> + attributes = SET_TYPE_BITS(attributes, type);
>>>>>>> + attributes = SET_CONFIG(attributes, config);
>>>>>>> + tx->attributes = cpu_to_le32(attributes);
>>>>>>> +
>>>>>>> + ret = scmi_do_xfer(handle, t);
>>>>>>> +
>>>>>>> + scmi_xfer_put(handle, t);
>>>>>>> + return ret;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_set_config(const struct scmi_handle
>>>>>>> *handle, u32 pin,
>>>>>>> + u32 config)
>>>>>>> +{
>>>>>>> + return scmi_pinctrl_apply_config(handle, pin, PIN_TYPE,
>>>>>>> config);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_get_config_group(const struct scmi_handle
>>>>>>> *handle,
>>>>>>> + u32 group, u32 *config)
>>>>>>> +{
>>>>>>> + return scmi_pinctrl_request_config(handle, group, GROUP_TYPE,
>>>>>>> config);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_set_config_group(const struct scmi_handle
>>>>>>> *handle,
>>>>>>> + u32 group, u32 config)
>>>>>>> +{
>>>>>>> + return scmi_pinctrl_apply_config(handle, group, GROUP_TYPE,
>>>>>>> config);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_function_select(const struct scmi_handle
>>>>>>> *handle,
>>>>>>> + u32 identifier,
>>>>>>> + enum scmi_pinctrl_selector_type type,
>>>>>>> + u32 function_id)
>>>>>>> +{
>>>>>>> + struct scmi_xfer *t;
>>>>>>> + struct scmi_func_set_tx {
>>>>>>> + __le32 identifier;
>>>>>>> + __le32 function_id;
>>>>>>> + __le32 flags;
>>>>>>> + } *tx;
>>>>>>
>>>>>> Ditto.
>>>>>>
>>>>>>> + int ret;
>>>>>>> +
>>>>>>> + if (!handle || type == FUNCTION_TYPE)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> +
>>>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_FUNCTION_SELECT,
>>>>>>> + SCMI_PROTOCOL_PINCTRL,
>>>>>>> + sizeof(*tx), 0, &t);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> +
>>>>>>> + tx = t->tx.buf;
>>>>>>> + tx->identifier = cpu_to_le32(identifier);
>>>>>>> + tx->function_id = cpu_to_le32(function_id);
>>>>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>>>>> +
>>>>>>> + ret = scmi_do_xfer(handle, t);
>>>>>>> + scmi_xfer_put(handle, t);
>>>>>>> +
>>>>>>> + return ret;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_request(const struct scmi_handle *handle,
>>>>>>> + u32 identifier,
>>>>>>> + enum scmi_pinctrl_selector_type type)
>>>>>>> +{
>>>>>>> + struct scmi_xfer *t;
>>>>>>> + int ret;
>>>>>>> + struct scmi_request_tx {
>>>>>>> + __le32 identifier;
>>>>>>> + __le32 flags;
>>>>>>> + } *tx;
>>>>>>> +
>>>>>>
>>>>>> Ditto.
>>>>>>
>>>>>>> + if (!handle || type == FUNCTION_TYPE)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> +
>>>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_REQUEST,
>>>>>>> SCMI_PROTOCOL_PINCTRL,
>>>>>>> + sizeof(*tx), 0, &t);
>>>>>>> +
>>>>>>> + tx = t->tx.buf;
>>>>>>> + tx->identifier = identifier;
>>>>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>>>>> +
>>>>>>> + ret = scmi_do_xfer(handle, t);
>>>>>>> + scmi_xfer_put(handle, t);
>>>>>>> +
>>>>>>> + return ret;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_request_pin(const struct scmi_handle
>>>>>>> *handle, u32 pin)
>>>>>>> +{
>>>>>>> + return scmi_pinctrl_request(handle, pin, PIN_TYPE);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_free(const struct scmi_handle *handle, u32
>>>>>>> identifier,
>>>>>>> + enum scmi_pinctrl_selector_type type)
>>>>>>> +{
>>>>>>> + struct scmi_xfer *t;
>>>>>>> + int ret;
>>>>>>> + struct scmi_request_tx {
>>>>>>> + __le32 identifier;
>>>>>>> + __le32 flags;
>>>>>>> + } *tx;
>>>>>>> +
>>>>>> Ditto.
>>>>>>
>>>>>>> + if (!handle || type == FUNCTION_TYPE)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + ret = scmi_pinctrl_validate_id(handle, identifier, type);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> +
>>>>>>> + ret = scmi_xfer_get_init(handle, PINCTRL_RELEASE,
>>>>>>> SCMI_PROTOCOL_PINCTRL,
>>>>>>> + sizeof(*tx), 0, &t);
>>>>>>> +
>>>>>>> + tx = t->tx.buf;
>>>>>>> + tx->identifier = identifier;
>>>>>>> + tx->flags = SET_TYPE(cpu_to_le32(type));
>>>>>>> +
>>>>>>> + ret = scmi_do_xfer(handle, t);
>>>>>>> + scmi_xfer_put(handle, t);
>>>>>>> +
>>>>>>> + return ret;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_free_pin(const struct scmi_handle *handle,
>>>>>>> u32 pin)
>>>>>>> +{
>>>>>>> + return scmi_pinctrl_free(handle, pin, PIN_TYPE);
>>>>>>> +}
>>>>>>> +
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_get_group_info(const struct scmi_handle
>>>>>>> *handle,
>>>>>>> + u32 selector,
>>>>>>> + struct scmi_group_info *group)
>>>>>>> +{
>>>>>>> + int ret = 0;
>>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>>> +
>>>>>>> + if (!handle || !handle->pinctrl_priv || !group)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + pi = handle->pinctrl_priv;
>>>>>>> +
>>>>>>> + ret = scmi_pinctrl_attributes(handle, GROUP_TYPE, selector,
>>>>>>> + &group->name,
>>>>>>> + &group->nr_pins);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> +
>>>>>>> + if (!group->nr_pins) {
>>>>>>> + dev_err(handle->dev, "Group %d has 0 elements", selector);
>>>>>>> + return -ENODATA;
>>>>>>> + }
>>>>>>> +
>>>>>>> + group->group_pins = devm_kmalloc_array(handle->dev,
>>>>>>> group->nr_pins,
>>>>>>> + sizeof(*group->group_pins),
>>>>>>> + GFP_KERNEL);
>>>>>>
>>>>>> I think you can just use for the array allocation
>>>>>>
>>>>>> devm_kcalloc(dev, n, size, flags)
>>>>>>
>>>>>> and it will add also __GFP_ZERO internally to clear it.
>>>>>> (indeed it calls in turn devm_kmalloc_array)
>>>>>>
>>>>>> ...BUT I think there is a further tricky issue here related to
>>>>>> memory
>>>>>> allocation...
>>>>>>
>>>>>> You call this and others function of this kind from some
>>>>>> scmi_pinctrl_ops,
>>>>>> like in scmi_pinctrl_get_group_pins
>>>>>> (scmi_pinctrl_ops->get_group_pins),
>>>>>> and then this is in turn called by the SCMI Pinctrl driver via
>>>>>> pinctrl_ops->get_group_pins AND you set a present flag so that you
>>>>>> issue a
>>>>>> PINCTRL_LIST_ASSOCIATIONS and allocate here a new group_pins array
>>>>>> just
>>>>>> the first time: but these are never released anywhere, since, even
>>>>>> though
>>>>>> lazily dynamically allocated when asked for, these are static data
>>>>>> that
>>>>>> you pass to the caller/user of this protocol and so you cannot
>>>>>> release
>>>>>> them anytime soon, indeed.
>>>>>>
>>>>>> The core SCMI stack usually takes care to track and release all the
>>>>>> devm_
>>>>>> resources allocated by the protocol ONLY if they were allocated with
>>>>>> devres
>>>>>> while inside scmi_pinctrl_protocol_init() function.
>>>>>> (see
>>>>>> drivers/firmware/arm-scmi/driver.c:scmi_alloc_init_protocol_instance()
>>>>>>
>>>>>> and scmi_protocol_release)
>>>>>>
>>>>>> BUT you do not allocate these arrays inside the protocol-init
>>>>>> function,
>>>>>> you allocate them the first time these ops are called at runtime.
>>>>>>
>>>>>> If you unbind/unload all the drivers using this protocol and then
>>>>>> reload
>>>>>> them, all the devm_ allocations in protocol_init will be freed and
>>>>>> reallocated BUT these arrays will never be freed (they are boudn to
>>>>>> handle->dev)
>>>>>> and instead they will be reallocated multiple times (present flag
>>>>>> will be cleared
>>>>>> on unload), remained unused and freed finally only when the whole
>>>>>> SCMI stack is
>>>>>> unbind/unloaded.
>>>>>>
>>>>>> You use a present flag to avoid reissuing the same query and
>>>>>> reallocating all the arrays each time a driver calls these
>>>>>> protocol_ops one, but really all these data is available early on at
>>>>>> protocol init time and they are not supposed to change at runtime,
>>>>>> dont they ?
>>>>>>
>>>>>> Even in a virtualized environment, you boot an agent and the SCMI
>>>>>> platform server provides to the agent the list of associations when
>>>>>> queried but then this does not change until the next reboot right ?
>>>>>> (indeed you do not query more than once...)
>>>>>>
>>>>>> The agent can only change the PIN status with CONFIG_SET or
>>>>>> FUNCTION_SELECT or REQUEST the exclusive use of a pin/group, but
>>>>>> it is
>>>>>> not that the platform can change the pin/groups associaion for the
>>>>>> same
>>>>>> agent at run time, this are static data for the whole life of the
>>>>>> agent.
>>>>>>
>>>>>> Am I right ?
>>>>>>
>>>>>> IOW I think there is some potential memory leak on unbind/bind
>>>>>> and it
>>>>>> would
>>>>>> be better to query and allocate all of these resources at init time
>>>>>> and keep
>>>>>> them ready to be retrieved by subsequent operations, since the
>>>>>> lifetime
>>>>>> of these resources is pretty long and they are basically
>>>>>> representing
>>>>>> static data that does not change after the init/probe phases.
>>>>>>
>>>>>> Indeed, all the other protocols usually allocate all the needed
>>>>>> resources and query all the available SCMI resources once for all
>>>>>> during
>>>>>> the protocol_init, storing all the retrieved info in some struct
>>>>>> *_info
>>>>>> exposed in scmi_protocol.h and then provide some related
>>>>>> protocol_ops to
>>>>>> get the number of resources and to retrieve specific domain info
>>>>>> descriptors.
>>>>>> (voltage.c is an example and more on this down below...)
>>>>>>
>>>>>> This way, any dynamic allocation is done during protocol_init, so
>>>>>> it can be automatically freed by the SCMI core once there are no
>>>>>> more
>>>>>> users of that protocol, and all of this static info data is queried
>>>>>> and retrieved once for all at protocol initialization time, avoiding
>>>>>> unneeded message exchanges to retrieve always the same data.
>>>>>> (which you avoid anyway with the present flag)
>>>>>>
>>>>>> If you have a good reason to instead perform this sort of lazy
>>>>>> allocation/query performed only at the last minute when someone ask
>>>>>> for
>>>>>> that specific resource, you will have to provide also a
>>>>>> .instance_deinit
>>>>>> function to clean anything you allocated out of the .instance_init
>>>>>> routine; but this would seem strange to me since any resource
>>>>>> that is
>>>>>> discovered at init will be eventually immediately queried by a
>>>>>> driver
>>>>>> which uses this protocol...am I missing something ?
>>>>>>
>>>>>>> + if (!group->group_pins) {
>>>>>>> + ret = -ENOMEM;
>>>>>>> + goto err;
>>>>>>> + }
>>>>>>> +
>>>>>>> + ret = scmi_pinctrl_list_associations(handle, selector,
>>>>>>> GROUP_TYPE,
>>>>>>> + group->nr_pins, group->group_pins);
>>>>>>> + if (ret)
>>>>>>> + goto err_groups;
>>>>>>> +
>>>>>>> + group->present = true;
>>>>>>> + return 0;
>>>>>>> +
>>>>>>> + err_groups:
>>>>>>> + kfree(group->group_pins);
>>>>>>> + err:
>>>>>>> + kfree(group->name);
>>>>>>> + return ret;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_get_group_name(const struct scmi_handle
>>>>>>> *handle,
>>>>>>> + u32 selector, const char **name)
>>>>>>> +{
>>>>>>> + int ret;
>>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>>> +
>>>>>>> + if (!handle || !handle->pinctrl_priv || !name)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + pi = handle->pinctrl_priv;
>>>>>>> +
>>>>>>> + if (selector > pi->nr_groups)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + if (!pi->groups[selector].present) {
>>>>>>> + ret = scmi_pinctrl_get_group_info(handle, selector,
>>>>>>> + &pi->groups[selector]);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> + }
>>>>>>> +
>>>>>>> + *name = pi->groups[selector].name;
>>>>>>> +
>>>>>>> + return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_get_group_pins(const struct scmi_handle
>>>>>>> *handle,
>>>>>>> + u32 selector, const unsigned int **pins,
>>>>>>> + unsigned int *nr_pins)
>>>>>>> +{
>>>>>>> + int ret;
>>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>>> +
>>>>>>> + if (!handle || !handle->pinctrl_priv || !pins || !nr_pins)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + pi = handle->pinctrl_priv;
>>>>>>> +
>>>>>>> + if (selector > pi->nr_groups)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + if (!pi->groups[selector].present) {
>>>>>>> + ret = scmi_pinctrl_get_group_info(handle, selector,
>>>>>>> + &pi->groups[selector]);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> + }
>>>>>>> +
>>>>>>> + *pins = pi->groups[selector].group_pins;
>>>>>>> + *nr_pins = pi->groups[selector].nr_pins;
>>>>>>> +
>>>>>>> + return ret;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_get_function_info(const struct scmi_handle
>>>>>>> *handle,
>>>>>>> + u32 selector,
>>>>>>> + struct scmi_function_info *func)
>>>>>>> +{
>>>>>>> + int ret = 0;
>>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>>> +
>>>>>>> + if (!handle || !handle->pinctrl_priv || !func)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + pi = handle->pinctrl_priv;
>>>>>>> +
>>>>>>> + ret = scmi_pinctrl_attributes(handle, FUNCTION_TYPE, selector,
>>>>>>> + &func->name,
>>>>>>> + &func->nr_groups);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> +
>>>>>>> + if (!func->nr_groups) {
>>>>>>> + dev_err(handle->dev, "Function %d has 0 elements",
>>>>>>> selector);
>>>>>>> + return -ENODATA;
>>>>>>> + }
>>>>>>> +
>>>>>>> + func->groups = devm_kmalloc_array(handle->dev,
>>>>>>> func->nr_groups,
>>>>>>> + sizeof(*func->groups),
>>>>>>> + GFP_KERNEL);
>>>>>>> + if (!func->groups) {
>>>>>>> + ret = -ENOMEM;
>>>>>>> + goto err;
>>>>>>> + }
>>>>>>> +
>>>>>>> + ret = scmi_pinctrl_list_associations(handle, selector,
>>>>>>> FUNCTION_TYPE,
>>>>>>> + func->nr_groups, func->groups);
>>>>>>> + if (ret)
>>>>>>> + goto err_funcs;
>>>>>>> +
>>>>>>> + func->present = true;
>>>>>>> + return 0;
>>>>>>> +
>>>>>>> + err_funcs:
>>>>>>> + kfree(func->groups);
>>>>>>> + err:
>>>>>>> + kfree(func->name);
>>>>>>> + return ret;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_get_function_name(const struct scmi_handle
>>>>>>> *handle,
>>>>>>> + u32 selector, const char **name)
>>>>>>> +{
>>>>>>> + int ret;
>>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>>> +
>>>>>>> + if (!handle || !handle->pinctrl_priv || !name)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + pi = handle->pinctrl_priv;
>>>>>>> +
>>>>>>> + if (selector > pi->nr_functions)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + if (!pi->functions[selector].present) {
>>>>>>> + ret = scmi_pinctrl_get_function_info(handle, selector,
>>>>>>> + &pi->functions[selector]);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> + }
>>>>>>> +
>>>>>>> + *name = pi->functions[selector].name;
>>>>>>> + return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_get_function_groups(const struct
>>>>>>> scmi_handle *handle,
>>>>>>> + u32 selector,
>>>>>>> + unsigned int *nr_groups,
>>>>>>> + const unsigned int **groups)
>>>>>>> +{
>>>>>>> + int ret;
>>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>>> +
>>>>>>> + if (!handle || !handle->pinctrl_priv || !groups || !nr_groups)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + pi = handle->pinctrl_priv;
>>>>>>> +
>>>>>>> + if (selector > pi->nr_functions)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + if (!pi->functions[selector].present) {
>>>>>>> + ret = scmi_pinctrl_get_function_info(handle, selector,
>>>>>>> + &pi->functions[selector]);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> + }
>>>>>>> +
>>>>>>> + *groups = pi->functions[selector].groups;
>>>>>>> + *nr_groups = pi->functions[selector].nr_groups;
>>>>>>> +
>>>>>>> + return ret;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_set_mux(const struct scmi_handle *handle,
>>>>>>> u32 selector,
>>>>>>> + u32 group)
>>>>>>> +{
>>>>>>> + return scmi_pinctrl_function_select(handle, group, GROUP_TYPE,
>>>>>>> + selector);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_get_pin_info(const struct scmi_handle
>>>>>>> *handle,
>>>>>>> + u32 selector, struct scmi_pin_info *pin)
>>>>>>> +{
>>>>>>> + int ret = 0;
>>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>>> + unsigned int n_elems;
>>>>>>> +
>>>>>>> + if (!handle || !handle->pinctrl_priv || !pin)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + pi = handle->pinctrl_priv;
>>>>>>> +
>>>>>>> + ret = scmi_pinctrl_attributes(handle, PIN_TYPE, selector,
>>>>>>> + &pin->name,
>>>>>>> + &n_elems);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> +
>>>>>>> + if (n_elems != pi->nr_pins) {
>>>>>>> + dev_err(handle->dev, "Wrong pin count expected %d has %d",
>>>>>>> + pi->nr_pins, n_elems);
>>>>>>> + return -ENODATA;
>>>>>>> + }
>>>>>>> +
>>>>>>> + if (*(pin->name) == 0) {
>>>>>>> + dev_err(handle->dev, "Pin name is empty");
>>>>>>> + goto err;
>>>>>>> + }
>>>>>>> +
>>>>>>> + pin->present = true;
>>>>>>> + return 0;
>>>>>>> +
>>>>>>> + err:
>>>>>>> + kfree(pin->name);
>>>>>>> + return ret;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_get_pin_name(const struct scmi_handle
>>>>>>> *handle, u32 selector,
>>>>>>> + const char **name)
>>>>>>> +{
>>>>>>> +
>>>>>>> + int ret;
>>>>>>> + struct scmi_pinctrl_info *pi;
>>>>>>> +
>>>>>>> + if (!handle || !handle->pinctrl_priv || !name)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + pi = handle->pinctrl_priv;
>>>>>>> +
>>>>>>> + if (selector > pi->nr_pins)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + if (!pi->pins[selector].present) {
>>>>>>> + ret = scmi_pinctrl_get_pin_info(handle, selector,
>>>>>>> + &pi->pins[selector]);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> + }
>>>>>>> +
>>>>>>> + *name = pi->pins[selector].name;
>>>>>>> +
>>>>>>> + return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +
>>>>>>> +static const struct scmi_pinctrl_ops pinctrl_ops = {
>>>>>>> + .get_groups_count = scmi_pinctrl_get_groups_count,
>>>>>>> + .get_group_name = scmi_pinctrl_get_group_name,
>>>>>>> + .get_group_pins = scmi_pinctrl_get_group_pins,
>>>>>>> + .get_functions_count = scmi_pinctrl_get_functions_count,
>>>>>>> + .get_function_name = scmi_pinctrl_get_function_name,
>>>>>>> + .get_function_groups = scmi_pinctrl_get_function_groups,
>>>>>>> + .set_mux = scmi_pinctrl_set_mux,
>>>>>>> + .get_pin_name = scmi_pinctrl_get_pin_name,
>>>>>>> + .get_pins_count = scmi_pinctrl_get_pins_count,
>>>>>>> + .get_config = scmi_pinctrl_get_config,
>>>>>>> + .set_config = scmi_pinctrl_set_config,
>>>>>>> + .get_config_group = scmi_pinctrl_get_config_group,
>>>>>>> + .set_config_group = scmi_pinctrl_set_config_group,
>>>>>>> + .request_pin = scmi_pinctrl_request_pin,
>>>>>>> + .free_pin = scmi_pinctrl_free_pin
>>>>>>> +};
>>>>>>> +
>>>>>>> +static int scmi_pinctrl_protocol_init(struct scmi_handle *handle)
>>>>>>> +{
>>>>>>> + u32 version;
>>>>>>> + struct scmi_pinctrl_info *pinfo;
>>>>>>> + int ret;
>>>>>>> +
>>>>>>> + if (!handle)
>>>>>>> + return -EINVAL;
>>>>>>> +
>>>>>>> + scmi_version_get(handle, SCMI_PROTOCOL_PINCTRL, &version);
>>>>>>> +
>>>>>>> + dev_dbg(handle->dev, "Pinctrl Version %d.%d\n",
>>>>>>> + PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
>>>>>>> +
>>>>>>> + pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL);
>>>>>>> + if (!pinfo)
>>>>>>> + return -ENOMEM;
>>>>>>> +
>>>>>>> + ret = scmi_pinctrl_attributes_get(handle, pinfo);
>>>>>>> + if (ret)
>>>>>>> + goto free;
>>>>>>> +
>>>>>>> + pinfo->pins = devm_kmalloc_array(handle->dev, pinfo->nr_pins,
>>>>>>> + sizeof(*pinfo->pins),
>>>>>>> + GFP_KERNEL | __GFP_ZERO);
>>>>>>
>>>>>> devm_kcalloc() zeroes on its own
>>>>>>
>>>>>>> + if (!pinfo->pins) {
>>>>>>> + ret = -ENOMEM;
>>>>>>> + goto free;
>>>>>>> + }
>>>>>>> +
>>>>>>> + pinfo->groups = devm_kmalloc_array(handle->dev,
>>>>>>> pinfo->nr_groups,
>>>>>>> + sizeof(*pinfo->groups),
>>>>>>> + GFP_KERNEL | __GFP_ZERO);
>>>>>>
>>>>>> Ditto.
>>>>>>> + if (!pinfo->groups) {
>>>>>>> + ret = -ENOMEM;
>>>>>>> + goto free;
>>>>>>> + }
>>>>>>> +
>>>>>>> + pinfo->functions = devm_kmalloc_array(handle->dev,
>>>>>>> pinfo->nr_functions,
>>>>>>> + sizeof(*pinfo->functions),
>>>>>>> + GFP_KERNEL | __GFP_ZERO);
>>>>>>> + if (!pinfo->functions) {
>>>>>>> + ret = -ENOMEM;
>>>>>>> + goto free;
>>>>>>> + }
>>>>>>> +
>>>>>>> + pinfo->version = version;
>>>>>>> + handle->pinctrl_ops = &pinctrl_ops;
>>>>>>> + handle->pinctrl_priv = pinfo;
>>>>>>> +
>>>>>>> + return 0;
>>>>>>> +free:
>>>>>>> + if (pinfo) {
>>>>>>> + devm_kfree(handle->dev, pinfo->pins);
>>>>>>> + devm_kfree(handle->dev, pinfo->functions);
>>>>>>> + devm_kfree(handle->dev, pinfo->groups);
>>>>>>> + }
>>>>>>
>>>>>> These frees are really not needed...if this function return failure
>>>>>> any
>>>>>> devres allocated in it is freed by the SCMI core. (as said
>>>>>> above...in a
>>>>>> recent kernel with the new API of course)
>>>>>>
>>>>>>> +
>>>>>>> + devm_kfree(handle->dev, pinfo);
>>>>>>> +
>>>>>>> + return ret;
>>>>>>> +}
>>>>>>> +
>>>>>>> +DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_PINCTRL,
>>>>>>> pinctrl)
>>>>>>> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
>>>>>>> index 815095326e2d..68add4d06e8c 100644
>>>>>>> --- a/drivers/pinctrl/Kconfig
>>>>>>> +++ b/drivers/pinctrl/Kconfig
>>>>>>> @@ -431,4 +431,13 @@ config PINCTRL_EQUILIBRIUM
>>>>>>> pin functions, configure GPIO attributes for LGM SoC
>>>>>>> pins.
>>>>>>> Pinmux and
>>>>>>> pinconf settings are retrieved from device tree.
>>>>>>> +config PINCTRL_SCMI
>>>>>>> + bool "Pinctrl driver controlled via SCMI interface"
>>>>>>> + depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
>>>>>>> + help
>>>>>>> + This driver provides support for pinctrl which is controlled
>>>>>>> + by firmware that implements the SCMI interface.
>>>>>>> + It uses SCMI Message Protocol to interact with the
>>>>>>> + firmware providing all the pinctrl controls.
>>>>>>> +
>>>>>>
>>>>>> This does NOT belong to this patch, but to the next right ?
>>>>>>
>>>>>>> endif
>>>>>>> diff --git a/include/linux/scmi_protocol.h
>>>>>>> b/include/linux/scmi_protocol.h
>>>>>>> index 9cd312a1ff92..6a909ef3bf51 100644
>>>>>>> --- a/include/linux/scmi_protocol.h
>>>>>>> +++ b/include/linux/scmi_protocol.h
>>>>>>> @@ -12,7 +12,8 @@
>>>>>>> #include <linux/notifier.h>
>>>>>>> #include <linux/types.h>
>>>>>>> -#define SCMI_MAX_STR_SIZE 16
>>>>>>> +#define SCMI_MAX_STR_SIZE 16
>>>>>>> +#define SCMI_MAX_STR_EXT_SIZE 64
>>>>>>
>>>>>> This is handled as part of how the extended names are handled with
>>>>>> ph->hops
>>>>>> in a common way, as I was saying, so please move this if you need
>>>>>> it in
>>>>>> the protocol code, then I'll port to the ph->hops interface and
>>>>>> clean
>>>>>> up.
>>>>>>
>>>>>>> #define SCMI_MAX_NUM_RATES 16
>>>>>>> /**
>>>>>>> @@ -252,6 +253,55 @@ struct scmi_notify_ops {
>>>>>>> struct notifier_block *nb);
>>>>>>> };
>>>>>>> +/**
>>>>>>> + * struct scmi_pinctrl_ops - represents the various operations
>>>>>>> provided
>>>>>>> + * by SCMI Pinctrl Protocol
>>>>>>> + *
>>>>>>> + * @get_groups_count: returns count of the registered groups
>>>>>>> + * @get_group_name: returns group name by index
>>>>>>> + * @get_group_pins: returns the set of pins, assigned to the
>>>>>>> specified group
>>>>>>> + * @get_functions_count: returns count of the registered fucntions
>>>>>>> + * @get_function_name: returns function name by indes
>>>>>>> + * @get_function_groups: returns the set of groups, assigned to
>>>>>>> the
>>>>>>> specified
>>>>>>> + * function
>>>>>>> + * @set_mux: set muxing function for groups of pins
>>>>>>> + * @get_pins: returns the set of pins, registered in driver
>>>>>>> + * @get_config: returns configuration parameter for pin
>>>>>>> + * @set_config: sets the configuration parameter for pin
>>>>>>> + * @get_config_group: returns the configuration parameter for a
>>>>>>> group of pins
>>>>>>> + * @set_config_group: sets the configuration parameter for a
>>>>>>> groups
>>>>>>> of pins
>>>>>>> + * @request_pin: aquire pin before selecting mux setting
>>>>>>> + * @free_pin: frees pin, acquired by request_pin call
>>>>>>> + */
>>>>>>> +struct scmi_pinctrl_ops {
>>>>>>> + int (*get_groups_count)(const struct scmi_handle *handle);
>>>>>>> + int (*get_group_name)(const struct scmi_handle *handles, u32
>>>>>>> selector,
>>>>>>> + const char **name);
>>>>>>> + int (*get_group_pins)(const struct scmi_handle *handle, u32
>>>>>>> selector,
>>>>>>> + const unsigned int **pins, unsigned int
>>>>>>> *nr_pins);
>>>>>>> + int (*get_functions_count)(const struct scmi_handle *handle);
>>>>>>> + int (*get_function_name)(const struct scmi_handle *handle, u32
>>>>>>> selector,
>>>>>>> + const char **name);
>>>>>>> + int (*get_function_groups)(const struct scmi_handle *handle,
>>>>>>> + u32 selector, unsigned int *nr_groups,
>>>>>>> + const unsigned int **groups);
>>>>>>> + int (*set_mux)(const struct scmi_handle *handle, u32 selector,
>>>>>>> + u32 group);
>>>>>>> + int (*get_pin_name)(const struct scmi_handle *handle, u32
>>>>>>> selector,
>>>>>>> + const char **name);
>>>>>>> + int (*get_pins_count)(const struct scmi_handle *handle);
>>>>>>> + int (*get_config)(const struct scmi_handle *handle, u32 pin,
>>>>>>> + u32 *config);
>>>>>>> + int (*set_config)(const struct scmi_handle *handle, u32 pin,
>>>>>>> + u32 config);
>>>>>>> + int (*get_config_group)(const struct scmi_handle *handle, u32
>>>>>>> pin,
>>>>>>> + u32 *config);
>>>>>>> + int (*set_config_group)(const struct scmi_handle *handle, u32
>>>>>>> pin,
>>>>>>> + u32 config);
>>>>>>> + int (*request_pin)(const struct scmi_handle *handle, u32 pin);
>>>>>>> + int (*free_pin)(const struct scmi_handle *handle, u32 pin);
>>>>>>> +};
>>>>>>> +
>>>>>>
>>>>>> As mentioned above, here you could drop a lot of this
>>>>>> get_X_count/name/pins
>>>>>> and instead expose a few of the internal proocol struct scmi__X_info
>>>>>> and then
>>>>>> provide just a mean to query how many resource are there and then
>>>>>> get
>>>>>> the info
>>>>>> descriptor you want for the specific domain_id, i.e.:
>>>>>>
>>>>>> int (*num_domains_get)(ph, type)
>>>>>> void *(*info_get)(ph, type, domain_id);
>>>>>>
>>>>>> Thanks,
>>>>>> Cristian
>>>>>>
>>>>>>
>>>>>> _______________________________________________
>>>>>> linux-arm-kernel mailing list
>>>>>> [email protected]
>>>>>> https://urldefense.com/v3/__http://lists.infradead.org/mailman/listinfo/linux-arm-kernel__;!!GF_29dbcQIUBPA!y2hR3PEGGxiPjVeXBcgGyV03DPDhzgUKR0uHvsTpiafKgBar8Egc6oOOs-IkFIquhSf-qBzltqEMyzRZrZi7qlk$
>>>>>>
>>>>>>
>>>>>> [lists[.]infradead[.]org]
On Tue, Apr 25, 2023 at 07:51:34PM +0000, Oleksii Moisieiev wrote:
> Hi Cristian,
>
> On 13.04.23 01:04, Cristian Marussi wrote:
>
> On Fri, Apr 07, 2023 at 10:18:27AM +0000, Oleksii Moisieiev wrote:
>
> Implementation of the SCMI client driver, which implements
> PINCTRL_PROTOCOL. This protocol has ID 19 and is described
> in the latest DEN0056 document.
>
> Hi,
>
Hi Oleksii,
please do NOT post mail with html on the mailing list, it is very hard to
read and comment while useing text-only mail-reader (and not so much
appreciated :D) ; even readers with an UI can be setup to properly avoid
html stuff and properly format for Kernel upstream work:
https://www.kernel.org/doc/html/v4.10/process/email-clients.html
Indeed your original message now cannot even be found on lore, probably
discarded for the same reason ? (not sure).
My comments inline.
>
> This protocol is part of the feature that was designed to
> separate the pinctrl subsystem from the SCP firmware.
> The idea is to separate communication of the pin control
> subsystem with the hardware to SCP firmware
> (or a similar system, such as ATF), which provides an interface
> to give the OS ability to control the hardware through SCMI protocol.
> This is a generic driver that implements SCMI protocol,
> independent of the platform type.
>
> DEN0056 document:
> [1]https://urldefense.com/v3/__https://developer.arm.com/documentation/den0056/l
> atest__;!!GF_29dbcQIUBPA!wnEVmBR_V-0llnzQFsQfeZq5Ov7xJ87H364gqo1_UvsilzKNfJoy81u
> 5GR1f0EBIyXOGyesjURGdxT_U5tzLvqT5lgjNpw$ [developer[.]arm[.]com]
>
>
> No need to specify all of this in the commit message, just a note that
> you are adding a new SCMIv3.2 Pincontrol protocol, highlighting anything
> that has been left out in this patch (if any) will be enough.
> You can look at the very first commit logs of existing protos as an
> example like: drivers/firmware/arm_scmi/powercap.c
>
> Some more comments down below, I'll mostly skip anything related to the
> SCMI API change I mentioned before...
>
> I'll also wont comment on more trivial stuff related to style, BUT there
> are lots of them: you should run
>
> ./scripts/checkpatch.pl --strict <your-git-format-patch-file>
>
> for each patch in the series. (and fix accordingly..spacing, brackets...etc)
>
> Done.
>
> +static int scmi_pinctrl_list_associations(const struct scmi_handle *handle,
[snip]
> + u32 selector,
> + enum scmi_pinctrl_selector_type type,
> + uint16_t size, unsigned int *array)
> +{
>
> This is the other functionalities you could implement straight away using
> ph->hops helpers (iterators) but just leave it this way, and I'll port it later
> (once we retested all of this as working with the new API but without any
> ph->hops usage..I think it is safer to change one bit at time... :P)
>
> Rewritten using iterators and checked with the unittests.
Good, thanks.
[snip]
> +static int scmi_pinctrl_get_group_info(const struct scmi_handle *handle,
> + u32 selector,
> + struct scmi_group_info *group)
> +{
> + int ret = 0;
> + struct scmi_pinctrl_info *pi;
> +
> + if (!handle || !handle->pinctrl_priv || !group)
> + return -EINVAL;
> +
> + pi = handle->pinctrl_priv;
> +
> + ret = scmi_pinctrl_attributes(handle, GROUP_TYPE, selector,
> + &group->name,
> + &group->nr_pins);
> + if (ret)
> + return ret;
> +
> + if (!group->nr_pins) {
> + dev_err(handle->dev, "Group %d has 0 elements", selector);
> + return -ENODATA;
> + }
> +
> + group->group_pins = devm_kmalloc_array(handle->dev, group->nr_pins,
> + sizeof(*group->group_pins),
> + GFP_KERNEL);
>
> I think you can just use for the array allocation
>
> devm_kcalloc(dev, n, size, flags)
>
> and it will add also __GFP_ZERO internally to clear it.
> (indeed it calls in turn devm_kmalloc_array)
>
> ...BUT I think there is a further tricky issue here related to memory allocation
> ...
>
> You call this and others function of this kind from some scmi_pinctrl_ops,
> like in scmi_pinctrl_get_group_pins (scmi_pinctrl_ops->get_group_pins),
> and then this is in turn called by the SCMI Pinctrl driver via
> pinctrl_ops->get_group_pins AND you set a present flag so that you issue a
> PINCTRL_LIST_ASSOCIATIONS and allocate here a new group_pins array just
> the first time: but these are never released anywhere, since, even though
> lazily dynamically allocated when asked for, these are static data that
> you pass to the caller/user of this protocol and so you cannot release
> them anytime soon, indeed.
>
> The core SCMI stack usually takes care to track and release all the devm_
> resources allocated by the protocol ONLY if they were allocated with devres
> while inside scmi_pinctrl_protocol_init() function.
> (see drivers/firmware/arm-scmi/driver.c:scmi_alloc_init_protocol_instance()
> and scmi_protocol_release)
>
> BUT you do not allocate these arrays inside the protocol-init function,
> you allocate them the first time these ops are called at runtime.
>
> If you unbind/unload all the drivers using this protocol and then reload
> them, all the devm_ allocations in protocol_init will be freed and
> reallocated BUT these arrays will never be freed (they are boudn to handle->dev)
> and instead they will be reallocated multiple times (present flag will be cleare
> d
> on unload), remained unused and freed finally only when the whole SCMI stack is
> unbind/unloaded.
>
> You use a present flag to avoid reissuing the same query and
> reallocating all the arrays each time a driver calls these
> protocol_ops one, but really all these data is available early on at
> protocol init time and they are not supposed to change at runtime, dont they ?
>
> Even in a virtualized environment, you boot an agent and the SCMI
> platform server provides to the agent the list of associations when
> queried but then this does not change until the next reboot right ?
> (indeed you do not query more than once...)
>
> The agent can only change the PIN status with CONFIG_SET or
> FUNCTION_SELECT or REQUEST the exclusive use of a pin/group, but it is
> not that the platform can change the pin/groups associaion for the same
> agent at run time, this are static data for the whole life of the agent.
>
> Am I right ?
>
> IOW I think there is some potential memory leak on unbind/bind and it would
> be better to query and allocate all of these resources at init time and keep
> them ready to be retrieved by subsequent operations, since the lifetime
> of these resources is pretty long and they are basically representing
> static data that does not change after the init/probe phases.
>
> Indeed, all the other protocols usually allocate all the needed
> resources and query all the available SCMI resources once for all during
> the protocol_init, storing all the retrieved info in some struct *_info
> exposed in scmi_protocol.h and then provide some related protocol_ops to
> get the number of resources and to retrieve specific domain info descriptors.
> (voltage.c is an example and more on this down below...)
>
> This way, any dynamic allocation is done during protocol_init, so
> it can be automatically freed by the SCMI core once there are no more
> users of that protocol, and all of this static info data is queried
> and retrieved once for all at protocol initialization time, avoiding
> unneeded message exchanges to retrieve always the same data.
> (which you avoid anyway with the present flag)
>
> If you have a good reason to instead perform this sort of lazy
> allocation/query performed only at the last minute when someone ask for
> that specific resource, you will have to provide also a .instance_deinit
> function to clean anything you allocated out of the .instance_init
> routine; but this would seem strange to me since any resource that is
> discovered at init will be eventually immediately queried by a driver
> which uses this protocol...am I missing something ?
>
>
> This is a good point. But there is some reason why I've made such lazy
> allocations:
>
> I agree that we have all data on the early stage, but we probably do
> not want to request all associations.
>
> Let's assume we have partial pinctrl configuration with 2-3 groups, 2
> functions and 10-15 pins involved.
>
> We don't want to request all 250 groups and 32 functions info during
> init because this will impact boottime and memory consumption.
Yes I supposed this was the reason, and it could be reasonable to just
query the associations last minute when you need them for the stuff you
need which was put in the DT...I want to think a bit more about this,
being the only protocol that needs this behaviour. Good for now.
>
> Pinctrl subsystem will request all needed data during device-tree node
> parsing.
>
> I have an idea to implement .instance_deinit callback from
> scmi_protocol, which will cleanup all allocated data.
>
> What do you think about that? If it is ok for you - I'll push v2.
>
I'd say do the cleanup with the available .instance_deinit as proposed
in the meantime, so we can see how all the changes and the update to
mainline kernel pans out...then we can discuss this further down the
line, maybe finding a better way to serve you lazy allocation from the
SCMI core (or not) and also see what Sudeep thinks abou these lazy
allocations.
[snip]
> + * struct scmi_pinctrl_ops - represents the various operations provided
> + * by SCMI Pinctrl Protocol
> + *
> + * @get_groups_count: returns count of the registered groups
> + * @get_group_name: returns group name by index
> + * @get_group_pins: returns the set of pins, assigned to the specified group
> + * @get_functions_count: returns count of the registered fucntions
> + * @get_function_name: returns function name by indes
> + * @get_function_groups: returns the set of groups, assigned to the specified
> + * function
> + * @set_mux: set muxing function for groups of pins
> + * @get_pins: returns the set of pins, registered in driver
> + * @get_config: returns configuration parameter for pin
> + * @set_config: sets the configuration parameter for pin
> + * @get_config_group: returns the configuration parameter for a group of pins
> + * @set_config_group: sets the configuration parameter for a groups of pins
> + * @request_pin: aquire pin before selecting mux setting
> + * @free_pin: frees pin, acquired by request_pin call
> + */
> +struct scmi_pinctrl_ops {
> + int (*get_groups_count)(const struct scmi_handle *handle);
> + int (*get_group_name)(const struct scmi_handle *handles, u32 selector,
> + const char **name);
> + int (*get_group_pins)(const struct scmi_handle *handle, u32 selector,
> + const unsigned int **pins, unsigned int *nr_pins);
> + int (*get_functions_count)(const struct scmi_handle *handle);
> + int (*get_function_name)(const struct scmi_handle *handle, u32 selector,
> + const char **name);
> + int (*get_function_groups)(const struct scmi_handle *handle,
> + u32 selector, unsigned int *nr_groups,
> + const unsigned int **groups);
> + int (*set_mux)(const struct scmi_handle *handle, u32 selector,
> + u32 group);
> + int (*get_pin_name)(const struct scmi_handle *handle, u32 selector,
> + const char **name);
> + int (*get_pins_count)(const struct scmi_handle *handle);
> + int (*get_config)(const struct scmi_handle *handle, u32 pin,
> + u32 *config);
> + int (*set_config)(const struct scmi_handle *handle, u32 pin,
> + u32 config);
> + int (*get_config_group)(const struct scmi_handle *handle, u32 pin,
> + u32 *config);
> + int (*set_config_group)(const struct scmi_handle *handle, u32 pin,
> + u32 config);
> + int (*request_pin)(const struct scmi_handle *handle, u32 pin);
> + int (*free_pin)(const struct scmi_handle *handle, u32 pin);
> +};
> +
>
> As mentioned above, here you could drop a lot of this get_X_count/name/pins
> and instead expose a few of the internal proocol struct scmi__X_info and then
> provide just a mean to query how many resource are there and then get the info
> descriptor you want for the specific domain_id, i.e.:
>
> int (*num_domains_get)(ph, type)
> void *(*info_get)(ph, type, domain_id);
>
> Thanks,
> Cristian
>
> Updated. Exposed selector_type to scmi_protocol, which helped me to
> reduce number of call.
>
> Looking forward for your thoughts about .instance_deinit callback
> implementation I've mentioned above and I will be ready to push v2.
>
As said, let'see V2 with cleanups in .instance_deinit and lazy
allocations as they are now and move on from there.
Thanks,
Cristian
Hi,
On 4/7/23 12:18, Oleksii Moisieiev wrote:
> This RFC patch series is intended to introduce the potential generic driver for
> pin controls over SCMI protocol, provided in the latest beta version of DEN0056 [0].
>
> On ARM-based systems, a separate Cortex-M based System Control Processor (SCP)
> provides control on pins, as well as with power, clocks, reset controllers. In this case,
> kernel should use one of the possible transports, described in [0] to access SCP and
> control clocks/power-domains etc. This driver is using SMC transport to communicate with SCP via
> SCMI protocol and access to the Pin Control Subsystem.
>
> The provided driver consists of 2 parts:
> - firmware/arm_scmi/pinctrl.c - the SCMI pinctrl protocol inmplementation
> responsible for the communication with SCP firmware.
>
> - drivers/pinctrl/pinctrl-scmi.c - pinctrl driver, which is using pinctrl
> protocol implementation to access all necessary data.
>
> Configuration:
> The scmi-pinctrl driver can be configured using DT bindings.
> For example:
> / {
> cpu_scp_shm: scp-shmem@0x53FF0000 {
> compatible = "arm,scmi-shmem";
> reg = <0x0 0x53FF0000 0x0 0x1000>;
> };
>
> firmware {
> scmi {
> compatible = "arm,scmi-smc";
> arm,smc-id = <0x82000002>;
> shmem = <&cpu_scp_shm>;
> #address-cells = <1>;
> #size-cells = <0>;
>
> scmi_pinctrl: protocol@19 {
> reg = <0x18>;
> #pinctrl-cells = <0>;
>
> i2c2_pins: i2c2 {
> groups = "i2c2_a";
> function = "i2c2";
> };
> };
> };
> };
> };
Spec itself has as the part of pinctrl also gpio support.
Here the example there is no gpio-cells property that's why I am curious if you
plan to also add it here.
Thanks,
Michal
Hi Cristian,
On 26.04.23 14:19, Cristian Marussi wrote:
> On Tue, Apr 25, 2023 at 07:51:34PM +0000, Oleksii Moisieiev wrote:
>> Hi Cristian,
>>
>> On 13.04.23 01:04, Cristian Marussi wrote:
>>
>> On Fri, Apr 07, 2023 at 10:18:27AM +0000, Oleksii Moisieiev wrote:
>>
>> Implementation of the SCMI client driver, which implements
>> PINCTRL_PROTOCOL. This protocol has ID 19 and is described
>> in the latest DEN0056 document.
>>
>> Hi,
>>
>
> Hi Oleksii,
>
> please do NOT post mail with html on the mailing list, it is very hard to
> read and comment while useing text-only mail-reader (and not so much
> appreciated :D) ; even readers with an UI can be setup to properly avoid
> html stuff and properly format for Kernel upstream work:
>
> https://urldefense.com/v3/__https://www.kernel.org/doc/html/v4.10/process/email-clients.html__;!!GF_29dbcQIUBPA!w2wEJQrXQbVSfn4q6_Sot9NQhyl_GTC4UNjmHUcubxpIHDt5dPU6emDEkfOvtSSvjjzZR2J-VQDTOovNXDto8RP_FSJatQ$ [kernel[.]org]
>
> Indeed your original message now cannot even be found on lore, probably
> discarded for the same reason ? (not sure).
>
> My comments inline.
>
I'm sorry for that. Somehow Thunderbird managed to switch to HTML after
update. I've sent v2.
>>
>> This protocol is part of the feature that was designed to
>> separate the pinctrl subsystem from the SCP firmware.
>> The idea is to separate communication of the pin control
>> subsystem with the hardware to SCP firmware
>> (or a similar system, such as ATF), which provides an interface
>> to give the OS ability to control the hardware through SCMI protocol.
>> This is a generic driver that implements SCMI protocol,
>> independent of the platform type.
>>
>> DEN0056 document:
>> [1]https://urldefense.com/v3/__https://developer.arm.com/documentation/den0056/l
>> atest__;!!GF_29dbcQIUBPA!wnEVmBR_V-0llnzQFsQfeZq5Ov7xJ87H364gqo1_UvsilzKNfJoy81u
>> 5GR1f0EBIyXOGyesjURGdxT_U5tzLvqT5lgjNpw$ [developer[.]arm[.]com]
>>
>>
>> No need to specify all of this in the commit message, just a note that
>> you are adding a new SCMIv3.2 Pincontrol protocol, highlighting anything
>> that has been left out in this patch (if any) will be enough.
>> You can look at the very first commit logs of existing protos as an
>> example like: drivers/firmware/arm_scmi/powercap.c
>>
>> Some more comments down below, I'll mostly skip anything related to the
>> SCMI API change I mentioned before...
>>
>> I'll also wont comment on more trivial stuff related to style, BUT there
>> are lots of them: you should run
>>
>> ./scripts/checkpatch.pl --strict <your-git-format-patch-file>
>>
>> for each patch in the series. (and fix accordingly..spacing, brackets...etc)
>>
>> Done.
>>
>> +static int scmi_pinctrl_list_associations(const struct scmi_handle *handle,
>
> [snip]
>
>> + u32 selector,
>> + enum scmi_pinctrl_selector_type type,
>> + uint16_t size, unsigned int *array)
>> +{
>>
>> This is the other functionalities you could implement straight away using
>> ph->hops helpers (iterators) but just leave it this way, and I'll port it later
>> (once we retested all of this as working with the new API but without any
>> ph->hops usage..I think it is safer to change one bit at time... :P)
>>
>> Rewritten using iterators and checked with the unittests.
>
> Good, thanks.
>
> [snip]
>
>> +static int scmi_pinctrl_get_group_info(const struct scmi_handle *handle,
>> + u32 selector,
>> + struct scmi_group_info *group)
>> +{
>> + int ret = 0;
>> + struct scmi_pinctrl_info *pi;
>> +
>> + if (!handle || !handle->pinctrl_priv || !group)
>> + return -EINVAL;
>> +
>> + pi = handle->pinctrl_priv;
>> +
>> + ret = scmi_pinctrl_attributes(handle, GROUP_TYPE, selector,
>> + &group->name,
>> + &group->nr_pins);
>> + if (ret)
>> + return ret;
>> +
>> + if (!group->nr_pins) {
>> + dev_err(handle->dev, "Group %d has 0 elements", selector);
>> + return -ENODATA;
>> + }
>> +
>> + group->group_pins = devm_kmalloc_array(handle->dev, group->nr_pins,
>> + sizeof(*group->group_pins),
>> + GFP_KERNEL);
>>
>> I think you can just use for the array allocation
>>
>> devm_kcalloc(dev, n, size, flags)
>>
>> and it will add also __GFP_ZERO internally to clear it.
>> (indeed it calls in turn devm_kmalloc_array)
>>
>> ...BUT I think there is a further tricky issue here related to memory allocation
>> ...
>>
>> You call this and others function of this kind from some scmi_pinctrl_ops,
>> like in scmi_pinctrl_get_group_pins (scmi_pinctrl_ops->get_group_pins),
>> and then this is in turn called by the SCMI Pinctrl driver via
>> pinctrl_ops->get_group_pins AND you set a present flag so that you issue a
>> PINCTRL_LIST_ASSOCIATIONS and allocate here a new group_pins array just
>> the first time: but these are never released anywhere, since, even though
>> lazily dynamically allocated when asked for, these are static data that
>> you pass to the caller/user of this protocol and so you cannot release
>> them anytime soon, indeed.
>>
>> The core SCMI stack usually takes care to track and release all the devm_
>> resources allocated by the protocol ONLY if they were allocated with devres
>> while inside scmi_pinctrl_protocol_init() function.
>> (see drivers/firmware/arm-scmi/driver.c:scmi_alloc_init_protocol_instance()
>> and scmi_protocol_release)
>>
>> BUT you do not allocate these arrays inside the protocol-init function,
>> you allocate them the first time these ops are called at runtime.
>>
>> If you unbind/unload all the drivers using this protocol and then reload
>> them, all the devm_ allocations in protocol_init will be freed and
>> reallocated BUT these arrays will never be freed (they are boudn to handle->dev)
>> and instead they will be reallocated multiple times (present flag will be cleare
>> d
>> on unload), remained unused and freed finally only when the whole SCMI stack is
>> unbind/unloaded.
>>
>> You use a present flag to avoid reissuing the same query and
>> reallocating all the arrays each time a driver calls these
>> protocol_ops one, but really all these data is available early on at
>> protocol init time and they are not supposed to change at runtime, dont they ?
>>
>> Even in a virtualized environment, you boot an agent and the SCMI
>> platform server provides to the agent the list of associations when
>> queried but then this does not change until the next reboot right ?
>> (indeed you do not query more than once...)
>>
>> The agent can only change the PIN status with CONFIG_SET or
>> FUNCTION_SELECT or REQUEST the exclusive use of a pin/group, but it is
>> not that the platform can change the pin/groups associaion for the same
>> agent at run time, this are static data for the whole life of the agent.
>>
>> Am I right ?
>>
>> IOW I think there is some potential memory leak on unbind/bind and it would
>> be better to query and allocate all of these resources at init time and keep
>> them ready to be retrieved by subsequent operations, since the lifetime
>> of these resources is pretty long and they are basically representing
>> static data that does not change after the init/probe phases.
>>
>> Indeed, all the other protocols usually allocate all the needed
>> resources and query all the available SCMI resources once for all during
>> the protocol_init, storing all the retrieved info in some struct *_info
>> exposed in scmi_protocol.h and then provide some related protocol_ops to
>> get the number of resources and to retrieve specific domain info descriptors.
>> (voltage.c is an example and more on this down below...)
>>
>> This way, any dynamic allocation is done during protocol_init, so
>> it can be automatically freed by the SCMI core once there are no more
>> users of that protocol, and all of this static info data is queried
>> and retrieved once for all at protocol initialization time, avoiding
>> unneeded message exchanges to retrieve always the same data.
>> (which you avoid anyway with the present flag)
>>
>> If you have a good reason to instead perform this sort of lazy
>> allocation/query performed only at the last minute when someone ask for
>> that specific resource, you will have to provide also a .instance_deinit
>> function to clean anything you allocated out of the .instance_init
>> routine; but this would seem strange to me since any resource that is
>> discovered at init will be eventually immediately queried by a driver
>> which uses this protocol...am I missing something ?
>>
>>
>> This is a good point. But there is some reason why I've made such lazy
>> allocations:
>>
>> I agree that we have all data on the early stage, but we probably do
>> not want to request all associations.
>>
>> Let's assume we have partial pinctrl configuration with 2-3 groups, 2
>> functions and 10-15 pins involved.
>>
>> We don't want to request all 250 groups and 32 functions info during
>> init because this will impact boottime and memory consumption.
>
> Yes I supposed this was the reason, and it could be reasonable to just
> query the associations last minute when you need them for the stuff you
> need which was put in the DT...I want to think a bit more about this,
> being the only protocol that needs this behaviour. Good for now.
>
>>
>> Pinctrl subsystem will request all needed data during device-tree node
>> parsing.
>>
>> I have an idea to implement .instance_deinit callback from
>> scmi_protocol, which will cleanup all allocated data.
>>
>> What do you think about that? If it is ok for you - I'll push v2.
>>
>
> I'd say do the cleanup with the available .instance_deinit as proposed
> in the meantime, so we can see how all the changes and the update to
> mainline kernel pans out...then we can discuss this further down the
> line, maybe finding a better way to serve you lazy allocation from the
> SCMI core (or not) and also see what Sudeep thinks abou these lazy
> allocations.
>
> [snip]
>
>> + * struct scmi_pinctrl_ops - represents the various operations provided
>> + * by SCMI Pinctrl Protocol
>> + *
>> + * @get_groups_count: returns count of the registered groups
>> + * @get_group_name: returns group name by index
>> + * @get_group_pins: returns the set of pins, assigned to the specified group
>> + * @get_functions_count: returns count of the registered fucntions
>> + * @get_function_name: returns function name by indes
>> + * @get_function_groups: returns the set of groups, assigned to the specified
>> + * function
>> + * @set_mux: set muxing function for groups of pins
>> + * @get_pins: returns the set of pins, registered in driver
>> + * @get_config: returns configuration parameter for pin
>> + * @set_config: sets the configuration parameter for pin
>> + * @get_config_group: returns the configuration parameter for a group of pins
>> + * @set_config_group: sets the configuration parameter for a groups of pins
>> + * @request_pin: aquire pin before selecting mux setting
>> + * @free_pin: frees pin, acquired by request_pin call
>> + */
>> +struct scmi_pinctrl_ops {
>> + int (*get_groups_count)(const struct scmi_handle *handle);
>> + int (*get_group_name)(const struct scmi_handle *handles, u32 selector,
>> + const char **name);
>> + int (*get_group_pins)(const struct scmi_handle *handle, u32 selector,
>> + const unsigned int **pins, unsigned int *nr_pins);
>> + int (*get_functions_count)(const struct scmi_handle *handle);
>> + int (*get_function_name)(const struct scmi_handle *handle, u32 selector,
>> + const char **name);
>> + int (*get_function_groups)(const struct scmi_handle *handle,
>> + u32 selector, unsigned int *nr_groups,
>> + const unsigned int **groups);
>> + int (*set_mux)(const struct scmi_handle *handle, u32 selector,
>> + u32 group);
>> + int (*get_pin_name)(const struct scmi_handle *handle, u32 selector,
>> + const char **name);
>> + int (*get_pins_count)(const struct scmi_handle *handle);
>> + int (*get_config)(const struct scmi_handle *handle, u32 pin,
>> + u32 *config);
>> + int (*set_config)(const struct scmi_handle *handle, u32 pin,
>> + u32 config);
>> + int (*get_config_group)(const struct scmi_handle *handle, u32 pin,
>> + u32 *config);
>> + int (*set_config_group)(const struct scmi_handle *handle, u32 pin,
>> + u32 config);
>> + int (*request_pin)(const struct scmi_handle *handle, u32 pin);
>> + int (*free_pin)(const struct scmi_handle *handle, u32 pin);
>> +};
>> +
>>
>> As mentioned above, here you could drop a lot of this get_X_count/name/pins
>> and instead expose a few of the internal proocol struct scmi__X_info and then
>> provide just a mean to query how many resource are there and then get the info
>> descriptor you want for the specific domain_id, i.e.:
>>
>> int (*num_domains_get)(ph, type)
>> void *(*info_get)(ph, type, domain_id);
>>
>> Thanks,
>> Cristian
>>
>> Updated. Exposed selector_type to scmi_protocol, which helped me to
>> reduce number of call.
>>
>> Looking forward for your thoughts about .instance_deinit callback
>> implementation I've mentioned above and I will be ready to push v2.
>>
>
> As said, let'see V2 with cleanups in .instance_deinit and lazy
> allocations as they are now and move on from there.
>
> Thanks,
> Cristian