From 0a62f2b458b322445d2e1d5388f8b7f1532b2631 Mon Sep 17 00:00:00 2001 From: Oleksii Moisieiev Date: Mon, 29 Nov 2021 18:41:23 +0200 Subject: [PATCH 1/2] scmi: Introduce pinctrl scmi protocol driver Implementation of the SCMI client driver, which implements PINCTRL_PROTOCOL. This protocol has id 18 and described in DEN0056C document. This protocol is the part of the feature that was designed to separate pinctrl subsystem to SCP firmware. The idea is to separate communication of the pin control subsystemd with the HW to SCP firmware (or similar system, such as ATF), which provides interface to give OS ability to control HW through SCMI protocol. This is generic driver, which implements SCMI protocol, independent from the platform type. DEN0056C document: https://developer.arm.com/documentation/den0056/latest Signed-off-by: Oleksii Moisieiev --- 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 | 512 ++++++++++++++++++++++++++++ drivers/pinctrl/Kconfig | 9 + include/linux/scmi_protocol.h | 51 +++ 6 files changed, 577 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/arm_scmi/pinctrl.c diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile index bc0d54f8e861e..5cec357fbfa8c 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 65063fa948d41..8bbb404abe8d1 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 6b2ce3f28f7b9..c29fd5c3f8095 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 0000000000000..392e7e8a6ddc4 --- /dev/null +++ b/drivers/firmware/arm_scmi/pinctrl.c @@ -0,0 +1,512 @@ +// 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 + +#include "common.h" +#include "notify.h" + +enum scmi_pinctrl_protocol_cmd { + GET_GROUP_PINS = 0x3, + GET_FUNCTION_GROUPS = 0x4, + SET_MUX = 0x5, + GET_PINS = 0x6, + GET_CONFIG = 0x7, + SET_CONFIG = 0x8, + GET_CONFIG_GROUP = 0x9, + SET_CONFIG_GROUP = 0xa, +}; + +struct scmi_group_info { + bool has_name; + char name[SCMI_MAX_STR_SIZE]; + unsigned group_pins[SCMI_PINCTRL_MAX_PINS_CNT]; + unsigned nr_pins; +}; + +struct scmi_function_info { + bool has_name; + char name[SCMI_MAX_STR_SIZE]; + u16 groups[SCMI_PINCTRL_MAX_GROUPS_CNT]; + u8 nr_groups; +}; + +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; + u16 pins[SCMI_PINCTRL_MAX_PINS_CNT]; +}; + +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 { + __le16 nr_functions; + __le16 nr_groups; + } *attr; + + 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(attr->nr_functions); + pi->nr_groups = le16_to_cpu(attr->nr_groups); + } + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_pinctrl_get_groups_count(const struct scmi_handle *handle) +{ + struct scmi_pinctrl_info *pi = handle->pinctrl_priv; + + return pi->nr_groups; +} + +static int scmi_pinctrl_get_group_name(const struct scmi_handle *handle, + u32 selector, const char **name) +{ + struct scmi_pinctrl_info *pi = handle->pinctrl_priv; + + if (selector > SCMI_PINCTRL_MAX_GROUPS_CNT) + return -EINVAL; + + if (!pi->groups[selector].has_name) { + snprintf(pi->groups[selector].name, SCMI_MAX_STR_SIZE, "%d", selector); + pi->groups[selector].has_name = true; + } + + *name = pi->groups[selector].name; + + return 0; +} + +static int scmi_pinctrl_get_group_pins(const struct scmi_handle *handle, + u32 selector, const unsigned **pins, + unsigned *nr_pins) +{ + struct scmi_pinctrl_info *pi = handle->pinctrl_priv; + u16 *list; + int loop, ret = 0; + struct scmi_xfer *t; + __le32 *num_ret; + u16 tot_num_ret = 0, loop_num_ret; + struct scmi_group_pins_tx { + __le16 selector; + __le16 skip; + } *tx; + + if (selector > SCMI_PINCTRL_MAX_GROUPS_CNT) + return -EINVAL; + + if (pi->groups[selector].nr_pins) { + *nr_pins = pi->groups[selector].nr_pins; + *pins = pi->groups[selector].group_pins; + return 0; + } + + ret = scmi_xfer_get_init(handle, GET_GROUP_PINS, + SCMI_PROTOCOL_PINCTRL, sizeof(*tx), 0, &t); + if (ret) + return ret; + + tx = t->tx.buf; + num_ret = t->rx.buf; + list = t->rx.buf + sizeof(*num_ret); + + do { + /* Set the number of pins to be skipped/already read */ + tx->skip = cpu_to_le16(tot_num_ret); + tx->selector = cpu_to_le16(selector); + + ret = scmi_do_xfer(handle, t); + if (ret) + break; + + loop_num_ret = le32_to_cpu(*num_ret); + if (tot_num_ret + loop_num_ret > SCMI_PINCTRL_MAX_PINS_CNT) { + dev_err(handle->dev, "No. of PINS > SCMI_PINCTRL_MAX_PINS_CNT"); + break; + } + + for (loop = 0; loop < loop_num_ret; loop++) { + pi->groups[selector].group_pins[tot_num_ret + loop] = + le16_to_cpu(list[loop]); + } + + tot_num_ret += loop_num_ret; + + scmi_reset_rx_to_maxsz(handle, t); + } while (loop_num_ret); + + scmi_xfer_put(handle, t); + pi->groups[selector].nr_pins = tot_num_ret; + *pins = pi->groups[selector].group_pins; + *nr_pins = pi->groups[selector].nr_pins; + + return ret; +} + +static int scmi_pinctrl_get_functions_count(const struct scmi_handle *handle) +{ + struct scmi_pinctrl_info *pi = handle->pinctrl_priv; + + return pi->nr_functions; +} + +static int scmi_pinctrl_get_function_name(const struct scmi_handle *handle, + u32 selector, const char **name) +{ + struct scmi_pinctrl_info *pi = handle->pinctrl_priv; + + if (selector >= pi->nr_functions) + return -EINVAL; + + if (!pi->functions[selector].has_name) { + snprintf(pi->functions[selector].name, SCMI_MAX_STR_SIZE, + "%d", selector); + pi->functions[selector].has_name = true; + } + + *name = pi->functions[selector].name; + return 0; +} + +static int scmi_pinctrl_get_function_groups(const struct scmi_handle *handle, + u32 selector, u32 *nr_groups, + const u16 **groups) +{ + struct scmi_pinctrl_info *pi = handle->pinctrl_priv; + u16 *list; + int loop, ret = 0; + struct scmi_xfer *t; + struct scmi_func_groups { + __le16 selector; + __le16 skip; + } *tx; + __le32 *num_ret; + u16 tot_num_ret = 0, loop_num_ret; + + if (selector >= pi->nr_functions) + return -EINVAL; + + if (pi->functions[selector].nr_groups) { + *nr_groups = pi->functions[selector].nr_groups; + *groups = pi->functions[selector].groups; + return 0; + } + + ret = scmi_xfer_get_init(handle, GET_FUNCTION_GROUPS, + SCMI_PROTOCOL_PINCTRL, sizeof(*tx), 0, &t); + if (ret) + return ret; + + tx = t->tx.buf; + num_ret = t->rx.buf; + list = t->rx.buf + sizeof(*num_ret); + + do { + /* Set the number of pins to be skipped/already read */ + tx->skip = cpu_to_le16(tot_num_ret); + tx->selector = cpu_to_le16(selector); + + ret = scmi_do_xfer(handle, t); + if (ret) + break; + + loop_num_ret = le32_to_cpu(*num_ret); + if (tot_num_ret + loop_num_ret > SCMI_PINCTRL_MAX_GROUPS_CNT) { + dev_err(handle->dev, "No. of PINS > SCMI_PINCTRL_MAX_GROUPS_CNT"); + break; + } + + for (loop = 0; loop < loop_num_ret; loop++) { + pi->functions[selector].groups[tot_num_ret + loop] = le16_to_cpu(list[loop]); + } + + tot_num_ret += loop_num_ret; + + scmi_reset_rx_to_maxsz(handle, t); + } while (loop_num_ret); + + scmi_xfer_put(handle, t); + pi->functions[selector].nr_groups = tot_num_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) +{ + struct scmi_xfer *t; + struct scmi_mux_tx { + __le16 function; + __le16 group; + } *tx; + int ret; + + ret = scmi_xfer_get_init(handle, SET_MUX, SCMI_PROTOCOL_PINCTRL, + sizeof(*tx), 0, &t); + if (ret) + return ret; + + tx = t->tx.buf; + tx->function = cpu_to_le16(selector); + tx->group = cpu_to_le16(group); + + ret = scmi_do_xfer(handle, t); + scmi_xfer_put(handle, t); + + return ret; +} + +static int scmi_pinctrl_get_pins(const struct scmi_handle *handle, u32 *nr_pins, + const u16 **pins) +{ + struct scmi_pinctrl_info *pi = handle->pinctrl_priv; + u16 *list; + int loop, ret = 0; + struct scmi_xfer *t; + __le32 *num_skip, *num_ret; + u32 tot_num_ret = 0, loop_num_ret; + + if (pi->nr_pins) { + *nr_pins = pi->nr_pins; + *pins = pi->pins; + return 0; + } + + ret = scmi_xfer_get_init(handle, GET_PINS, + SCMI_PROTOCOL_PINCTRL, sizeof(*num_skip), 0, &t); + if (ret) + return ret; + + num_skip = t->tx.buf; + num_ret = t->rx.buf; + list = t->rx.buf + sizeof(*num_ret); + + do { + /* Set the number of pins to be skipped/already read */ + *num_skip = cpu_to_le32(tot_num_ret); + + ret = scmi_do_xfer(handle, t); + if (ret) + break; + + loop_num_ret = le32_to_cpu(*num_ret); + if (tot_num_ret + loop_num_ret > SCMI_PINCTRL_MAX_PINS_CNT) { + dev_err(handle->dev, "No. of PINS > SCMI_PINCTRL_MAX_PINS_CNT"); + break; + } + + for (loop = 0; loop < loop_num_ret; loop++) { + pi->pins[tot_num_ret + loop] = le16_to_cpu(list[loop]); + } + + tot_num_ret += loop_num_ret; + + scmi_reset_rx_to_maxsz(handle, t); + } while (loop_num_ret); + + scmi_xfer_put(handle, t); + pi->nr_pins = tot_num_ret; + *pins = pi->pins; + *nr_pins = pi->nr_pins; + + return ret; +} + +static int scmi_pinctrl_get_config(const struct scmi_handle *handle, u32 pin, + u32 *config) +{ + struct scmi_xfer *t; + struct scmi_conf_tx { + __le32 pin; + __le32 config; + } *tx; + __le32 *packed_config; + int ret; + + ret = scmi_xfer_get_init(handle, GET_CONFIG, SCMI_PROTOCOL_PINCTRL, + sizeof(*tx), sizeof(*packed_config), &t); + if (ret) + return ret; + + tx = t->tx.buf; + packed_config = t->rx.buf; + tx->pin = cpu_to_le32(pin); + tx->config = cpu_to_le32(*config); + 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_set_config(const struct scmi_handle *handle, u32 pin, + u32 config) +{ + struct scmi_xfer *t; + struct scmi_conf_tx { + __le32 pin; + __le32 config; + } *tx; + int ret; + + ret = scmi_xfer_get_init(handle, SET_CONFIG, SCMI_PROTOCOL_PINCTRL, + sizeof(*tx), 0, &t); + if (ret) + return ret; + + tx = t->tx.buf; + tx->pin = cpu_to_le32(pin); + tx->config = cpu_to_le32(config); + ret = scmi_do_xfer(handle, t); + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_pinctrl_get_config_group(const struct scmi_handle *handle, + u32 group, u32 *config) +{ + struct scmi_xfer *t; + struct scmi_conf_tx { + __le32 group; + __le32 config; + } *tx; + __le32 *packed_config; + int ret; + + ret = scmi_xfer_get_init(handle, GET_CONFIG_GROUP, SCMI_PROTOCOL_PINCTRL, + sizeof(*tx), sizeof(*packed_config), &t); + if (ret) + return ret; + + tx = t->tx.buf; + packed_config = t->rx.buf; + tx->group = cpu_to_le32(group); + tx->config = cpu_to_le32(*config); + 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_set_config_group(const struct scmi_handle *handle, + u32 group, u32 config) +{ + struct scmi_xfer *t; + struct scmi_conf_tx { + __le32 group; + __le32 config; + } *tx; + int ret; + + ret = scmi_xfer_get_init(handle, SET_CONFIG_GROUP, SCMI_PROTOCOL_PINCTRL, + sizeof(*tx), 0, &t); + if (ret) + return ret; + + tx = t->tx.buf; + tx->group = cpu_to_le32(group); + tx->config = cpu_to_le32(config); + ret = scmi_do_xfer(handle, t); + + scmi_xfer_put(handle, t); + return ret; +} + +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_pins = scmi_pinctrl_get_pins, + .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, +}; + +static int scmi_pinctrl_protocol_init(struct scmi_handle *handle) +{ + u32 version; + struct scmi_pinctrl_info *pinfo; + int ret; + + 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->groups = devm_kcalloc(handle->dev, pinfo->nr_groups, + sizeof(*pinfo->groups), GFP_KERNEL); + if (!pinfo->groups) { + ret = -ENOMEM; + goto free; + } + + pinfo->functions = devm_kcalloc(handle->dev, pinfo->nr_functions, + sizeof(*pinfo->functions), GFP_KERNEL); + if (!pinfo->functions) { + ret = -ENOMEM; + goto free; + } + + pinfo->version = version; + handle->pinctrl_ops = &pinctrl_ops; + handle->pinctrl_priv = pinfo; + + return 0; +free: + if (pinfo) { + if (pinfo->functions) + devm_kfree(handle->dev,pinfo->functions); + + if (pinfo->groups) + 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 815095326e2d6..68add4d06e8c1 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 9cd312a1ff925..cf68e919ef963 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -15,6 +15,9 @@ #define SCMI_MAX_STR_SIZE 16 #define SCMI_MAX_NUM_RATES 16 +#define SCMI_PINCTRL_MAX_PINS_CNT 512 +#define SCMI_PINCTRL_MAX_GROUPS_CNT 512 + /** * struct scmi_revision_info - version information structure * @@ -252,6 +255,48 @@ 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 funtion 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 +*/ +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 **pins, unsigned *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, + u32 *nr_groups, const u16 **groups); + int (*set_mux)(const struct scmi_handle *handle, u32 selector, u32 group); + int (*get_pins)(const struct scmi_handle *handle, u32 *nr_pins, + const u16 **pins); + 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 group, + u32 *config); + int (*set_config_group)(const struct scmi_handle *handle, u32 group, + u32 config); +}; + /** * struct scmi_handle - Handle returned to ARM SCMI clients for usage. * @@ -263,6 +308,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 +321,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 +333,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 +342,7 @@ struct scmi_handle { void *reset_priv; void *notify_priv; void *system_priv; + void *pinctrl_priv; }; enum scmi_std_protocol { @@ -303,6 +353,7 @@ enum scmi_std_protocol { SCMI_PROTOCOL_CLOCK = 0x14, SCMI_PROTOCOL_SENSOR = 0x15, SCMI_PROTOCOL_RESET = 0x16, + SCMI_PROTOCOL_PINCTRL = 0x18, }; enum scmi_system_events { From dcf1a443006487d8740a3354c12e5a416ad4a173 Mon Sep 17 00:00:00 2001 From: Oleksii Moisieiev Date: Mon, 29 Nov 2021 18:58:26 +0200 Subject: [PATCH 2/2] pinctrl: Implementation of the generic scmi-pinctrl driver 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. The following binding shows the configuration for Renesas H3ULCB for r8a77951 as an example: Documentation/devicetree/bindings/renesas-scmi,pfc.yaml Signed-off-by: Oleksii Moisieiev --- .../bindings/pinctrl/renesas-scmi,pfc.yaml | 745 ++++++++++++++++++ drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-scmi.c | 623 +++++++++++++++ 3 files changed, 1369 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/renesas-scmi,pfc.yaml create mode 100644 drivers/pinctrl/pinctrl-scmi.c diff --git a/Documentation/devicetree/bindings/pinctrl/renesas-scmi,pfc.yaml b/Documentation/devicetree/bindings/pinctrl/renesas-scmi,pfc.yaml new file mode 100644 index 0000000000000..31e57297b971d --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/renesas-scmi,pfc.yaml @@ -0,0 +1,745 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/scmi,pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SCMI Pin Function Controller for Renesas H3ULCB board + +maintainers: + - Oleksii Moisieiev + +description: + SCMI Pin Function Controller provides interface to SCP firmware pinctrl + driver. Pins and registers can be operated from the OS via SCMI protocol + (see DEN0056C document for additional information) using SMC as transport. + SCP firmware driver provides all necessary information about pin bindings + and gives functionality to configure pins. + This file describes the set of bindings, needed to work with SCP firmware + pinctrl driver for Renesas H3ULCB board based on r8a77951. + All pin states should be defined in protocol@18 node, which represents the + number of SCMI protocol, assigned to PINCTRL. + + Reference to DEN0056C document + https://developer.arm.com/documentation/den0056/latest + +properties: + compatible: + const: arm,scmi-smc # Select SCMI driver using SMC + + reg: + maxItems: 1 + + shmem: + description: Pointer to the shared memory region, needed for SCMI buffers. + + arm,smc-id: + $ref: /schemas/types.yaml#/definitions/uint32 + description: SMC Identifier, set in SCP firmware for SCMI protocol. + const: 0x82000002 + +required: + - compatible + - reg + - shmem + - arm,scm-id + +additionalProperties: + anyOf: + - type: object + allOf: + - $ref: pincfg-node.yaml# + - $ref: pinmux-node.yaml# + + description: + Pin controller client devices use pin configuration subnodes (children + and grandchildren) for desired pin configuration. + Client device subnodes use below standard properties. + + properties: + phandle: true + function: + enum: [ + 0, #audio_clk + 1, #avb + 2, #can0 + 3, #can1 + 4, #can_clk + 5, #canfd0 + 6, #canfd1 + 7, #du + 8, #hscif0 + 9, #hscif1 + 10, #hscif2 + 11, #hscif3 + 12, #hscif4 + 13, #i2c0 + 14, #i2c1 + 15, #i2c2 + 16, #i2c3 + 17, #i2c5 + 18, #i2c6 + 19, #intc_ex + 20, #mlb_3pin + 21, #msiof0 + 22, #msiof1 + 23, #msiof2 + 24, #msiof3 + 25, #pwm0 + 26, #pwm1 + 27, #pwm2 + 28, #pwm3 + 29, #pwm4 + 30, #pwm5 + 31, #pwm6 + 32, #sata0 + 33, #scif0 + 34, #scif1 + 35, #scif2 + 36, #scif3 + 37, #scif4 + 38, #scif5 + 39, #scif_clk + 40, #sdhi0 + 41, #sdhi1 + 42, #sdhi2 + 43, #sdhi3 + 44, #ssi + 45, #tmu + 46, #tpu + 47, #usb0 + 48, #usb1 + 49, #usb2 + 50, #usb2_ch3 + 51, #usb30 + 52, #vin4 + 53, #vin5 + 60, #drif0 + 61, #drif1 + 62, #drif2 + 63, #drif3 + ] + groups: + enum: [ + 0, #audio_clk_a_a + 1, #audio_clk_a_b + 2, #audio_clk_a_c + 3, #audio_clk_b_a + 4, #audio_clk_b_b + 5, #audio_clk_c_a + 6, #audio_clk_c_b + 7, #audio_clkout_a + 8, #audio_clkout_b + 9, #audio_clkout_c + 10, #audio_clkout_d + 11, #audio_clkout1_a + 12, #audio_clkout1_b + 13, #audio_clkout2_a + 14, #audio_clkout2_b + 15, #audio_clkout3_a + 16, #audio_clkout3_b + 17, #avb_link + 18, #avb_magic + 19, #avb_phy_int + 20, #avb_mdc + 21, #avb_mdio + 22, #avb_mii + 23, #avb_avtp_pps + 24, #avb_avtp_match_a + 25, #avb_avtp_capture_a + 26, #avb_avtp_match_b + 27, #avb_avtp_capture_b + 28, #can0_data_a + 29, #can0_data_b + 30, #can1_data + 31, #can_clk + 32, #canfd0_data_a + 33, #canfd0_data_b + 34, #canfd1_data + 35, #du_rgb666 + 36, #du_rgb888 + 37, #du_clk_out_0 + 38, #du_clk_out_1 + 39, #du_sync + 40, #du_oddf + 41, #du_cde + 42, #du_disp + 43, #hscif0_data + 44, #hscif0_clk + 45, #hscif0_ctrl + 46, #hscif1_data_a + 47, #hscif1_clk_a + 48, #hscif1_ctrl_a + 49, #hscif1_data_b + 50, #hscif1_clk_b + 51, #hscif1_ctrl_b + 52, #hscif2_data_a + 53, #hscif2_clk_a + 54, #hscif2_ctrl_a + 55, #hscif2_data_b + 56, #hscif2_clk_b + 57, #hscif2_ctrl_b + 58, #hscif2_data_c + 59, #hscif2_clk_c + 60, #hscif2_ctrl_c + 61, #hscif3_data_a + 62, #hscif3_clk + 63, #hscif3_ctrl + 64, #hscif3_data_b + 65, #hscif3_data_c + 66, #hscif3_data_d + 67, #hscif4_data_a + 68, #hscif4_clk + 69, #hscif4_ctrl + 70, #hscif4_data_b + 71, #i2c0 + 72, #i2c1_a + 73, #i2c1_b + 74, #i2c2_a + 75, #i2c2_b + 76, #i2c3 + 77, #i2c5 + 78, #i2c6_a + 79, #i2c6_b + 80, #i2c6_c + 81, #intc_ex_irq0 + 82, #intc_ex_irq1 + 83, #intc_ex_irq2 + 84, #intc_ex_irq3 + 85, #intc_ex_irq4 + 86, #intc_ex_irq5 + 87, #mlb_3pin + 88, #msiof0_clk + 89, #msiof0_sync + 90, #msiof0_ss1 + 91, #msiof0_ss2 + 92, #msiof0_txd + 93, #msiof0_rxd + 94, #msiof1_clk_a + 95, #msiof1_sync_a + 96, #msiof1_ss1_a + 97, #msiof1_ss2_a + 98, #msiof1_txd_a + 99, #msiof1_rxd_a + 100, #msiof1_clk_b + 101, #msiof1_sync_b + 102, #msiof1_ss1_b + 103, #msiof1_ss2_b + 104, #msiof1_txd_b + 105, #msiof1_rxd_b + 106, #msiof1_clk_c + 107, #msiof1_sync_c + 108, #msiof1_ss1_c + 109, #msiof1_ss2_c + 110, #msiof1_txd_c + 111, #msiof1_rxd_c + 112, #msiof1_clk_d + 113, #msiof1_sync_d + 114, #msiof1_ss1_d + 115, #msiof1_ss2_d + 116, #msiof1_txd_d + 117, #msiof1_rxd_d + 118, #msiof1_clk_e + 119, #msiof1_sync_e + 120, #msiof1_ss1_e + 121, #msiof1_ss2_e + 122, #msiof1_txd_e + 123, #msiof1_rxd_e + 124, #msiof1_clk_f + 125, #msiof1_sync_f + 126, #msiof1_ss1_f + 127, #msiof1_ss2_f + 128, #msiof1_txd_f + 129, #msiof1_rxd_f + 130, #msiof1_clk_g + 131, #msiof1_sync_g + 132, #msiof1_ss1_g + 133, #msiof1_ss2_g + 134, #msiof1_txd_g + 135, #msiof1_rxd_g + 136, #msiof2_clk_a + 137, #msiof2_sync_a + 138, #msiof2_ss1_a + 139, #msiof2_ss2_a + 140, #msiof2_txd_a + 141, #msiof2_rxd_a + 142, #msiof2_clk_b + 143, #msiof2_sync_b + 144, #msiof2_ss1_b + 145, #msiof2_ss2_b + 146, #msiof2_txd_b + 147, #msiof2_rxd_b + 148, #msiof2_clk_c + 149, #msiof2_sync_c + 150, #msiof2_ss1_c + 151, #msiof2_ss2_c + 152, #msiof2_txd_c + 153, #msiof2_rxd_c + 154, #msiof2_clk_d + 155, #msiof2_sync_d + 156, #msiof2_ss1_d + 157, #msiof2_ss2_d + 158, #msiof2_txd_d + 159, #msiof2_rxd_d + 160, #msiof3_clk_a + 161, #msiof3_sync_a + 162, #msiof3_ss1_a + 163, #msiof3_ss2_a + 164, #msiof3_txd_a + 165, #msiof3_rxd_a + 166, #msiof3_clk_b + 167, #msiof3_sync_b + 168, #msiof3_ss1_b + 169, #msiof3_ss2_b + 170, #msiof3_txd_b + 171, #msiof3_rxd_b + 172, #msiof3_clk_c + 173, #msiof3_sync_c + 174, #msiof3_txd_c + 175, #msiof3_rxd_c + 176, #msiof3_clk_d + 177, #msiof3_sync_d + 178, #msiof3_ss1_d + 179, #msiof3_txd_d + 180, #msiof3_rxd_d + 181, #msiof3_clk_e + 182, #msiof3_sync_e + 183, #msiof3_ss1_e + 184, #msiof3_ss2_e + 185, #msiof3_txd_e + 186, #msiof3_rxd_e + 187, #pwm0 + 188, #pwm1_a + 189, #pwm1_b + 190, #pwm2_a + 191, #pwm2_b + 192, #pwm3_a + 193, #pwm3_b + 194, #pwm4_a + 195, #pwm4_b + 196, #pwm5_a + 197, #pwm5_b + 198, #pwm6_a + 199, #pwm6_b + 200, #sata0_devslp_a + 201, #sata0_devslp_b + 202, #scif0_data + 203, #scif0_clk + 204, #scif0_ctrl + 205, #scif1_data_a + 206, #scif1_clk + 207, #scif1_ctrl + 208, #scif1_data_b + 209, #scif2_data_a + 210, #scif2_clk + 211, #scif2_data_b + 212, #scif3_data_a + 213, #scif3_clk + 214, #scif3_ctrl + 215, #scif3_data_b + 216, #scif4_data_a + 217, #scif4_clk_a + 218, #scif4_ctrl_a + 219, #scif4_data_b + 220, #scif4_clk_b + 221, #scif4_ctrl_b + 222, #scif4_data_c + 223, #scif4_clk_c + 224, #scif4_ctrl_c + 225, #scif5_data_a + 226, #scif5_clk_a + 227, #scif5_data_b + 228, #scif5_clk_b + 229, #scif_clk_a + 230, #scif_clk_b + 231, #sdhi0_data1 + 232, #sdhi0_data4 + 233, #sdhi0_ctrl + 234, #sdhi0_cd + 235, #sdhi0_wp + 236, #sdhi1_data1 + 237, #sdhi1_data4 + 238, #sdhi1_ctrl + 239, #sdhi1_cd + 240, #sdhi1_wp + 241, #sdhi2_data1 + 242, #sdhi2_data4 + 243, #sdhi2_data8 + 244, #sdhi2_ctrl + 245, #sdhi2_cd_a + 246, #sdhi2_wp_a + 247, #sdhi2_cd_b + 248, #sdhi2_wp_b + 249, #sdhi2_ds + 250, #sdhi3_data1 + 251, #sdhi3_data4 + 252, #sdhi3_data8 + 253, #sdhi3_ctrl + 254, #sdhi3_cd + 255, #sdhi3_wp + 256, #sdhi3_ds + 257, #ssi0_data + 258, #ssi01239_ctrl + 259, #ssi1_data_a + 260, #ssi1_data_b + 261, #ssi1_ctrl_a + 262, #ssi1_ctrl_b + 263, #ssi2_data_a + 264, #ssi2_data_b + 265, #ssi2_ctrl_a + 266, #ssi2_ctrl_b + 267, #ssi3_data + 268, #ssi349_ctrl + 269, #ssi4_data + 270, #ssi4_ctrl + 271, #ssi5_data + 272, #ssi5_ctrl + 273, #ssi6_data + 274, #ssi6_ctrl + 275, #ssi7_data + 276, #ssi78_ctrl + 277, #ssi8_data + 278, #ssi9_data_a + 279, #ssi9_data_b + 280, #ssi9_ctrl_a + 281, #ssi9_ctrl_b + 282, #tmu_tclk1_a + 283, #tmu_tclk1_b + 284, #tmu_tclk2_a + 285, #tmu_tclk2_b + 286, #tpu_to0 + 287, #tpu_to1 + 288, #tpu_to2 + 289, #tpu_to3 + 290, #usb0 + 291, #usb1 + 292, #usb2 + 293, #usb2_ch3 + 294, #usb30 + 295, #vin4_data1 + 296, #vin4_data2 + 297, #vin4_data3 + 298, #vin4_data4 + 299, #vin4_data18_a + 300, #vin4_data5 + 301, #vin4_data6 + 302, #vin4_data7 + 303, #vin4_data8 + 304, #vin4_data9 + 305, #vin4_data10 + 306, #vin4_data18_b + 307, #vin4_data11 + 308, #vin4_data12 + 309, #vin4_data8_sft8 + 310, #vin4_sync + 311, #vin4_field + 312, #vin4_clkenb + 313, #vin4_clk + 314, #vin5_data1 + 315, #vin5_data2 + 316, #vin5_data3 + 317, #vin5_data4 + 318, #vin5_data8_sft8 + 319, #vin5_sync + 320, #vin5_field + 321, #vin5_clkenb + 322, #vin5_clk + 323, #drif0_ctrl_a + 324, #drif0_data0_a + 325, #drif0_data1_a + 326, #drif0_ctrl_b + 327, #drif0_data0_b + 328, #drif0_data1_b + 329, #drif0_ctrl_c + 330, #drif0_data0_c + 331, #drif0_data1_c + 332, #drif1_ctrl_a + 333, #drif1_data0_a + 334, #drif1_data1_a + 335, #drif1_ctrl_b + 336, #drif1_data0_b + 337, #drif1_data1_b + 338, #drif1_ctrl_c + 339, #drif1_data0_c + 340, #drif1_data1_c + 341, #drif2_ctrl_a + 342, #drif2_data0_a + 343, #drif2_data1_a + 344, #drif2_ctrl_b + 345, #drif2_data0_b + 346, #drif2_data1_b + 347, #drif3_ctrl_a + 348, #drif3_data0_a + 349, #drif3_data1_a + 350, #drif3_ctrl_b + 351, #drif3_data0_b + 352, #drif3_data1_b + 353, #vin5_data8 + 354, #vin5_data8_a + 355, #vin5_data10 + 356, #vin5_data10_a + 357, #vin5_data10_b + 358, #vin5_data12 + 359, #vin5_data16 + 360, #vin4_data8_a + 361, #vin4_data10_a + 362, #vin4_data12_a + 363, #vin4_data16_a + 364, #vin4_data20_a + 365, #vin4_data24_a + 366, #vin4_data8_b + 367, #vin4_data10_b + 368, #vin4_data12_b + 369, #vin4_data16_b + 370, #vin4_data20_b + 371, #vin4_data24_b + ] + pins: + enum: [ + 0, # GP_0_0 + 1, # GP_0_1 + 2, # GP_0_2 + 3, # GP_0_3 + 4, # GP_0_4 + 5, # GP_0_5 + 6, # GP_0_6 + 7, # GP_0_7 + 8, # GP_0_8 + 9, # GP_0_9 + 10, # GP_0_10 + 11, # GP_0_11 + 12, # GP_0_12 + 13, # GP_0_13 + 14, # GP_0_14 + 15, # GP_0_15 + 32, # GP_1_0 + 33, # GP_1_1 + 34, # GP_1_2 + 35, # GP_1_3 + 36, # GP_1_4 + 37, # GP_1_5 + 38, # GP_1_6 + 39, # GP_1_7 + 40, # GP_1_8 + 41, # GP_1_9 + 42, # GP_1_10 + 43, # GP_1_11 + 44, # GP_1_12 + 45, # GP_1_13 + 46, # GP_1_14 + 47, # GP_1_15 + 48, # GP_1_16 + 49, # GP_1_17 + 50, # GP_1_18 + 51, # GP_1_19 + 52, # GP_1_20 + 53, # GP_1_21 + 54, # GP_1_22 + 55, # GP_1_23 + 56, # GP_1_24 + 57, # GP_1_25 + 58, # GP_1_26 + 59, # GP_1_27 + 60, # GP_1_28 + 64, # GP_2_0 + 65, # GP_2_1 + 66, # GP_2_2 + 67, # GP_2_3 + 68, # GP_2_4 + 69, # GP_2_5 + 70, # GP_2_6 + 71, # GP_2_7 + 72, # GP_2_8 + 73, # GP_2_9 + 74, # GP_2_10 + 75, # GP_2_11 + 76, # GP_2_12 + 77, # GP_2_13 + 78, # GP_2_14 + 96, # GP_3_0 + 97, # GP_3_1 + 98, # GP_3_2 + 99, # GP_3_3 + 100, # GP_3_4 + 101, # GP_3_5 + 102, # GP_3_6 + 103, # GP_3_7 + 104, # GP_3_8 + 105, # GP_3_9 + 106, # GP_3_10 + 107, # GP_3_11 + 108, # GP_3_12 + 109, # GP_3_13 + 110, # GP_3_14 + 111, # GP_3_15 + 128, # GP_4_0 + 129, # GP_4_1 + 130, # GP_4_2 + 131, # GP_4_3 + 132, # GP_4_4 + 133, # GP_4_5 + 134, # GP_4_6 + 135, # GP_4_7 + 136, # GP_4_8 + 137, # GP_4_9 + 138, # GP_4_10 + 139, # GP_4_11 + 140, # GP_4_12 + 141, # GP_4_13 + 142, # GP_4_14 + 143, # GP_4_15 + 144, # GP_4_16 + 145, # GP_4_17 + 160, # GP_5_0 + 161, # GP_5_1 + 162, # GP_5_2 + 163, # GP_5_3 + 164, # GP_5_4 + 165, # GP_5_5 + 166, # GP_5_6 + 167, # GP_5_7 + 168, # GP_5_8 + 169, # GP_5_9 + 170, # GP_5_10 + 171, # GP_5_11 + 172, # GP_5_12 + 173, # GP_5_13 + 174, # GP_5_14 + 175, # GP_5_15 + 176, # GP_5_16 + 177, # GP_5_17 + 178, # GP_5_18 + 179, # GP_5_19 + 180, # GP_5_20 + 181, # GP_5_21 + 182, # GP_5_22 + 183, # GP_5_23 + 184, # GP_5_24 + 185, # GP_5_25 + 192, # GP_6_0 + 193, # GP_6_1 + 194, # GP_6_2 + 195, # GP_6_3 + 196, # GP_6_4 + 197, # GP_6_5 + 198, # GP_6_6 + 199, # GP_6_7 + 200, # GP_6_8 + 201, # GP_6_9 + 202, # GP_6_10 + 203, # GP_6_11 + 204, # GP_6_12 + 205, # GP_6_13 + 206, # GP_6_14 + 207, # GP_6_15 + 208, # GP_6_16 + 209, # GP_6_17 + 210, # GP_6_18 + 211, # GP_6_19 + 212, # GP_6_20 + 213, # GP_6_21 + 214, # GP_6_22 + 215, # GP_6_23 + 216, # GP_6_24 + 217, # GP_6_25 + 218, # GP_6_26 + 219, # GP_6_27 + 220, # GP_6_28 + 221, # GP_6_29 + 222, # GP_6_30 + 223, # GP_6_31 + 224, # GP_7_0 + 225, # GP_7_1 + 226, # GP_7_2 + 227, # GP_7_3 + 228, # PIN_ASEBRK + 229, # PIN_AVB_MDIO + 230, # PIN_AVB_RD0 + 231, # PIN_AVB_RD1 + 232, # PIN_AVB_RD2 + 233, # PIN_AVB_RD3 + 234, # PIN_AVB_RXC + 235, # PIN_AVB_RX_CTL + 236, # PIN_AVB_TD0 + 237, # PIN_AVB_TD1 + 238, # PIN_AVB_TD2 + 239, # PIN_AVB_TD3 + 240, # PIN_AVB_TXC + 241, # PIN_AVB_TXCREFCLK + 242, # PIN_AVB_TX_CTL + 243, # PIN_DU_DOTCLKIN0 + 244, # PIN_DU_DOTCLKIN1 + 245, # PIN_DU_DOTCLKIN2 + 246, # PIN_DU_DOTCLKIN3 + 247, # PIN_EXTALR + 248, # PIN_FSCLKST# + 249, # PIN_MLB_REF + 250, # PIN_PRESETOUT# + 251, # PIN_QSPI0_IO2 + 252, # PIN_QSPI0_IO3 + 253, # PIN_QSPI0_MISO_IO1 + 254, # PIN_QSPI0_MOSI_IO0 + 255, # PIN_QSPI0_SPCLK + 256, # PIN_QSPI0_SSL + 257, # PIN_QSPI1_IO2 + 258, # PIN_QSPI1_IO3 + 259, # PIN_QSPI1_MISO_IO1 + 260, # PIN_QSPI1_MOSI_IO0 + 261, # PIN_QSPI1_SPCLK + 262, # PIN_QSPI1_SSL + 263, # PIN_RPC_INT# + 264, # PIN_RPC_RESET# + 265, # PIN_RPC_WP# + 266, # PIN_TCK + 267, # PIN_TDI + 268, # PIN_TDO + 269, # PIN_TMS + 270, # PIN_TRST + ] + bias-disable: true + bias-pull-down: true + bias-pull-up: true + drive-strength: + enum: [ 3, 6, 9, 12, 15, 18, 21, 24 ] # Superset of supported values + power-source: + enum: [ 1800, 3300 ] + input: true + output-high: true + output-low: true + + additionalProperties: false + + - type: object + properties: + phandle: true + +examples: + - | + 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@18 { + reg = <0x18>; + #pinctrl-cells = <0>; + + i2c2_pins: i2c2 { + groups = <74>; /* i2c2_a */ + function = <15>; /* i2c2 */ + }; + }; + }; + }; + + &i2c2 { + pinctrl-0 = <&i2c2_pins>; + pinctrl-names = "default"; + }; diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index f53933b2ff02e..163fe8a06878b 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 0000000000000..d21666978b253 --- /dev/null +++ b/drivers/pinctrl/pinctrl-scmi.c @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * System Control and Power Interface (SCMI) Protocol based clock driver + * + * Copyright (C) 2021 EPAM. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pinctrl-utils.h" +#include "core.h" +#include "pinconf.h" + +#define DRV_NAME "scmi-pinctrl" +#define DT_PROPERTY_NAME_BUF_MAX 32 + +struct scmi_pinctrl_funcs { + unsigned 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 nr_pins; +}; + +static struct scmi_pinctrl *pmx; + +static int pinctrl_scmi_get_groups_count(struct pinctrl_dev *pctldev) +{ + const struct scmi_handle *handle = pmx->handle; + + return handle->pinctrl_ops->get_groups_count(handle); +} + +static const char *pinctrl_scmi_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + int ret; + const char *name; + const struct scmi_handle *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 ""; + } + + return name; +} + +static int pinctrl_scmi_get_group_pins(struct pinctrl_dev *pctldev, + unsigned selector, const unsigned **pins, unsigned *num_pins) +{ + const struct scmi_handle *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 offset) +{ + seq_puts(s, DRV_NAME); +} + +static const char *int_to_str_alloc(unsigned int param) +{ + char buf[DT_PROPERTY_NAME_BUF_MAX]; + char *res; + int size; + + size = snprintf(buf, DT_PROPERTY_NAME_BUF_MAX, "%u", param); + if (!size) + return NULL; + + res = kmemdup(buf, size + 1, GFP_KERNEL); + return res; +} + +static void str_from_int_free(const char *addr) +{ + if (likely(addr)) + kfree(addr); +} + +#ifdef CONFIG_OF +static void pinctrl_scmi_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned num_maps) +{ + unsigned int i; + + if (map == NULL) + return; + + for (i = 0; i < num_maps; ++i) { + if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP || + map[i].type == PIN_MAP_TYPE_CONFIGS_PIN) { + kfree(map[i].data.configs.configs); + str_from_int_free(map[i].data.configs.group_or_pin); + } + + if (map[i].type == PIN_MAP_TYPE_MUX_GROUP) + str_from_int_free(map[i].data.mux.function); + } + + kfree(map); +} + +static int pinctrl_scmi_map_add_config(struct pinctrl_map *map, + const char *group_or_pin, + enum pinctrl_map_type type, + unsigned long *configs, + unsigned int num_configs) +{ + unsigned long *cfgs; + + cfgs = kmemdup(configs, num_configs * sizeof(*cfgs), + GFP_KERNEL); + if (cfgs == NULL) + return -ENOMEM; + + map->type = type; + map->data.configs.group_or_pin = group_or_pin; + map->data.configs.configs = cfgs; + map->data.configs.num_configs = num_configs; + + return 0; +} + +static int pinctrl_scmi_dt_subnode_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, + unsigned int *num_maps, unsigned int *index) +{ + struct device *dev = pmx->dev; + struct pinctrl_map *maps = *map; + unsigned int nmaps = *num_maps; + unsigned int idx = *index; + unsigned int num_configs; + u32 function; + bool func_set = false; + unsigned long *configs; + struct property *prop; + unsigned int num_groups; + unsigned int num_pins; + u32 group; + u32 pin; + const __be32 *cur; + + int ret; + const char *func_prop_name = "function"; + const char *groups_prop_name = "groups"; + const char *pins_prop_name = "pins"; + + ret = of_property_read_u32(np, func_prop_name, &function); + if (ret == -EINVAL) { + func_set = false; + } else if (ret < 0) { + dev_err(dev, "Invalid function in DT\n"); + return ret; + } else { + func_set = true; + } + + dev_dbg(pmx->dev, "name = %s", np->full_name); + dev_dbg(pmx->dev, "func = %d, is set = %d\n", function, func_set); + + ret = pinconf_generic_parse_dt_config(np, NULL, &configs, &num_configs); + if (ret < 0) + return ret; + + if (!func_set && num_configs == 0) { + dev_err(dev, + "DT node must contain at least a function or config\n"); + ret = -ENODEV; + goto done; + } + + ret = of_property_count_u32_elems(np, pins_prop_name); + if (ret == -EINVAL) { + num_pins = 0; + } else if (ret < 0) { + dev_err(dev, "Invalid pins list in DT\n"); + goto done; + } else { + num_pins = ret; + } + + ret = of_property_count_u32_elems(np, groups_prop_name); + if (ret == -EINVAL) { + num_groups = 0; + } else if (ret < 0) { + dev_err(dev, "Invalid pin groups list in DT\n"); + goto done; + } else { + num_groups = ret; + } + + if (!num_pins && !num_groups) { + dev_err(dev, "No pin or group provided in DT node\n"); + ret = -ENODEV; + goto done; + } + + if (func_set) + nmaps += num_groups; + + if (configs) + nmaps += num_pins + num_groups; + + maps = krealloc(maps, sizeof(*maps) * nmaps, GFP_KERNEL); + if (maps == NULL) { + ret = -ENOMEM; + goto done; + } + + *map = maps; + *num_maps = nmaps; + + of_property_for_each_u32(np, groups_prop_name, prop, cur, group) { + const char *group_name = int_to_str_alloc(group); + if (!group_name) { + ret = -EINVAL; + goto done; + } + + if (func_set) { + maps[idx].type = PIN_MAP_TYPE_MUX_GROUP; + maps[idx].data.mux.group = group_name; + + maps[idx].data.mux.function = int_to_str_alloc(function); + if (!maps[idx].data.mux.function) { + ret = -EINVAL; + goto done; + } + + idx++; + } + + if (configs) { + ret = pinctrl_scmi_map_add_config(&maps[idx], group_name, + PIN_MAP_TYPE_CONFIGS_GROUP, + configs, num_configs); + if (ret < 0) + goto done; + + idx++; + } + } + + if (!configs) { + ret = 0; + goto done; + } + + of_property_for_each_u32(np, pins_prop_name, prop, cur, pin) { + const char *pin_name = int_to_str_alloc(pin); + if (!pin_name) { + ret = -EINVAL; + goto done; + } + + ret = pinctrl_scmi_map_add_config(&maps[idx], pin_name, + PIN_MAP_TYPE_CONFIGS_PIN, + configs, num_configs); + if (ret < 0) + goto done; + + idx++; + } + +done: + *index = idx; + kfree(configs); + return ret; +} + +static int pinctrl_scmi_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, unsigned *num_maps) +{ + struct device *dev = pmx->dev; + struct device_node *child; + unsigned int index; + int ret; + + *map = NULL; + *num_maps = 0; + index = 0; + + for_each_child_of_node(np, child) { + ret = pinctrl_scmi_dt_subnode_to_map(pctldev, child, map, num_maps, + &index); + if (ret < 0) { + of_node_put(child); + goto done; + } + } + + /* If no mapping has been found in child nodes try the config node. */ + if (*num_maps == 0) { + ret = pinctrl_scmi_dt_subnode_to_map(pctldev, np, map, num_maps, + &index); + if (ret < 0) + goto done; + } + + if (*num_maps) + return 0; + + dev_err(dev, "no mapping found in node %pOF\n", np); + ret = -EINVAL; + +done: + if (ret < 0) + pinctrl_scmi_dt_free_map(pctldev, *map, *num_maps); + + return ret; +} +#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 = pmx->handle; + + return handle->pinctrl_ops->get_functions_count(handle); +} + +static const char *pinctrl_scmi_get_function_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + int ret; + const char *name; + const struct scmi_handle *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 ""; + } + + return name; +} + +static int pinctrl_scmi_get_function_groups(struct pinctrl_dev *pctldev, + unsigned selector, + const char * const **groups, + unsigned * const num_groups) +{ + const u16 *group_ids; + const struct scmi_handle *handle = pmx->handle; + int ret, i; + + if ((selector < pmx->nr_functions) + && (pmx->functions[selector].num_groups)) { + dev_dbg(pmx->dev, "1"); + *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; + + pmx->functions[selector].groups = devm_kzalloc(pmx->dev, + sizeof(*pmx->functions[selector].groups) * *num_groups, + GFP_KERNEL); + if (unlikely(!pmx->functions[selector].groups)) + return -ENOMEM; + + for (i = 0; i < *num_groups; i++) { + pmx->functions[selector].groups[i] = int_to_str_alloc(group_ids[i]); + if (unlikely(!pmx->functions[selector].groups[i])) { + ret = -ENOMEM; + goto error; + } + } + + *groups = (const char * const *)pmx->functions[selector].groups; + dev_dbg(pmx->dev, "got groups %d", *num_groups); + + return 0; + +error: + if (pmx->functions[selector].num_groups) { + for (i = 0; i < pmx->functions[selector].num_groups; i++) { + if (pmx->functions[selector].groups[i]) + str_from_int_free(pmx->functions[selector].groups[i]); + } + + kfree(pmx->functions[selector].groups); + } + + return ret; +} + +static int pinctrl_scmi_func_set_mux(struct pinctrl_dev *pctldev, + unsigned selector, unsigned group) +{ + const struct scmi_handle *handle = pmx->handle; + + return handle->pinctrl_ops->set_mux(handle, selector, group); +} + +static const struct pinmux_ops pinctrl_scmi_pinmux_ops = { + .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 _pin, + unsigned long *config) +{ + const struct scmi_handle *handle = pmx->handle; + + return handle->pinctrl_ops->get_config(handle, _pin, (u32 *)config); +} + +static int pinctrl_scmi_pinconf_set(struct pinctrl_dev *pctldev, unsigned _pin, + unsigned long *configs, unsigned num_configs) +{ + const struct scmi_handle *handle = pmx->handle; + int i, ret; + + dev_dbg(pmx->dev, "Enter pin = %d, num_configs = %d\n", _pin, num_configs); + + for (i=0; ipinctrl_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 group, + unsigned long *configs, + unsigned num_configs) +{ + const struct scmi_handle *handle = pmx->handle; + int i, ret; + + for (i=0; ipinctrl_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_handle *handle, + unsigned *nr_pins, + const struct pinctrl_pin_desc **pins) +{ + const u16 *pin_ids; + int ret, i; + + if (pmx->nr_pins) { + *pins = pmx->pins; + *nr_pins = pmx->nr_pins; + return 0; + } + + ret = handle->pinctrl_ops->get_pins(handle, nr_pins, &pin_ids); + if (ret) { + dev_err(pmx->dev, "get pins failed with err %d", ret); + return ret; + } + + pmx->nr_pins = *nr_pins; + pmx->pins = devm_kzalloc(pmx->dev, sizeof(*pmx->pins) * *nr_pins, + GFP_KERNEL); + if (unlikely(!pmx->pins)) + return -ENOMEM; + + for (i = 0; i < *nr_pins; i++) { + pmx->pins[i].number = pin_ids[i]; + pmx->pins[i].name = int_to_str_alloc(pin_ids[i]); + } + + *pins = pmx->pins; + dev_dbg(pmx->dev, "got pins %d", *nr_pins); + + return 0; +} + +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; + + pmx = devm_kzalloc(&sdev->dev, sizeof(*pmx), GFP_KERNEL); + if (unlikely(!pmx)) + return -ENOMEM; + + pmx->handle = sdev->handle; + if (unlikely(!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->handle, &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_kzalloc(&sdev->dev, sizeof(*pmx->functions) * + pmx->nr_functions, GFP_KERNEL); + if (unlikely(!pmx->functions)) { + ret = -ENOMEM; + goto clean; + } + } + + if (pmx->nr_groups) { + pmx->groups = devm_kzalloc(&sdev->dev, sizeof(*pmx->groups) * + pmx->nr_groups, GFP_KERNEL); + if (unlikely(!pmx->groups)) { + ret = -ENOMEM; + goto clean; + } + } + + return pinctrl_enable(pmx->pctldev); + +clean: + if (pmx) { + if (pmx->functions) + kfree(pmx->functions); + + if (pmx->groups) + 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 "); +MODULE_DESCRIPTION("ARM SCMI pin controller driver"); +MODULE_LICENSE("GPL v2");