Hi Peng Fan,
On 17.04.23 05:55, Peng Fan wrote:
IIUC you are expecting to have an ability to set some kind of array of
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.
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 <oleksii_moisieiev@xxxxxxxx>
---
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 <oleksii_moisieiev@xxxxxxxx>
+L: linux-arm-kernel@xxxxxxxxxxxxxxxxxxx
+S: Maintained
+F: drivers/firmware/arm_scmi/pinctrl.c
+
SYSTEM RESET/SHUTDOWN DRIVERS
M: Sebastian Reichel <sre@xxxxxxxxxx>
L: linux-pm@xxxxxxxxxxxxxxx
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)Ditto.
+ 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);
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
linux-arm-kernel@xxxxxxxxxxxxxxxxxxx
https://urldefense.com/v3/__http://lists.infradead.org/mailman/listinfo/linux-arm-kernel__;!!GF_29dbcQIUBPA!y2hR3PEGGxiPjVeXBcgGyV03DPDhzgUKR0uHvsTpiafKgBar8Egc6oOOs-IkFIquhSf-qBzltqEMyzRZrZi7qlk$
[lists[.]infradead[.]org]