[RFC] scmi: pinctrl: support i.MX9

From: Peng Fan (OSS)
Date: Thu Aug 24 2023 - 03:02:28 EST


From: Peng Fan <peng.fan@xxxxxxx>

Based on : https://lore.kernel.org/all/cover.1691518313.git.oleksii_moisieiev@xxxxxxxx/

The upper patchset not support multiple configs in SCMI 3.2, this patch
add that in config_set, not update config_get, I suppose Oleksii will
fix.

This patch is just to introduce i.MX support to see whether people have
comments for the design.

The binding format:
<mux_reg conf_reg input_reg mux_mode input_val>
dts:
pinctrl_uart1: uart1grp {
fsl,pins = <
MX93_PAD_UART1_RXD__LPUART1_RX 0x31e
MX93_PAD_UART1_TXD__LPUART1_TX 0x31e
>;
};

i.MX pinctrl not use generic pinconf, this has been agreeed by
maintainers before. So after moving to SCMI, we will still
keep the same binding format, and i.MX SCMI firmware also use same
format when configure registers. So we need to use i.MX specific
dt_node_to_map function.

I not have good idea how to call imx dt_node_to_map, so just use
extern and of_machine_is_compatible to call imx dt_node_to_map.
Hope you could give some suggestions.

Signed-off-by: Peng Fan <peng.fan@xxxxxxx>
---
drivers/firmware/arm_scmi/pinctrl.c | 22 ++--
drivers/pinctrl/freescale/pinctrl-imx.c | 130 +++++++++++++++++++++---
drivers/pinctrl/pinctrl-scmi.c | 53 +++++-----
include/linux/scmi_protocol.h | 2 +-
4 files changed, 154 insertions(+), 53 deletions(-)

diff --git a/drivers/firmware/arm_scmi/pinctrl.c b/drivers/firmware/arm_scmi/pinctrl.c
index 868a2f9821be..93109997614d 100644
--- a/drivers/firmware/arm_scmi/pinctrl.c
+++ b/drivers/firmware/arm_scmi/pinctrl.c
@@ -6,13 +6,14 @@
*/

#include <linux/module.h>
+#include <linux/pinctrl/pinconf-generic.h>
#include <linux/scmi_protocol.h>
#include <linux/slab.h>

#include "protocols.h"

-#define REG_TYPE_BITS GENMASK(9, 8)
-#define REG_CONFIG GENMASK(7, 0)
+#define REG_TYPE_CONFIG GENMASK(1, 0)
+#define REG_NUM_CONFIG GENMASK(9, 2)

#define GET_GROUPS_NR(x) le32_get_bits((x), GENMASK(31, 16))
#define GET_PINS_NR(x) le32_get_bits((x), GENMASK(15, 0))
@@ -36,10 +37,11 @@ enum scmi_pinctrl_protocol_cmd {
PINCTRL_SET_PERMISSIONS = 0xb
};

+#define MAX_CONFIG_ENTRY 10
struct scmi_msg_conf_set {
__le32 identifier;
__le32 attributes;
- __le32 config_value;
+ __le32 config_value[MAX_CONFIG_ENTRY * 2];
};

struct scmi_msg_conf_get {
@@ -320,9 +322,11 @@ static int scmi_pinctrl_config_get(const struct scmi_protocol_handle *ph,

tx = t->tx.buf;
tx->identifier = cpu_to_le32(selector);
+ /*
attributes = FIELD_PREP(REG_TYPE_BITS, type) |
FIELD_PREP(REG_CONFIG, config_type);
tx->attributes = cpu_to_le32(attributes);
+ */

ret = ph->xops->do_xfer(ph, t);
if (!ret)
@@ -335,7 +339,7 @@ static int scmi_pinctrl_config_get(const struct scmi_protocol_handle *ph,
static int scmi_pinctrl_config_set(const struct scmi_protocol_handle *ph,
u32 selector,
enum scmi_pinctrl_selector_type type,
- u8 config_type, unsigned long config_value)
+ unsigned long *configs, unsigned int num_configs)
{
struct scmi_xfer *t;
struct scmi_msg_conf_set *tx;
@@ -355,10 +359,14 @@ static int scmi_pinctrl_config_set(const struct scmi_protocol_handle *ph,

tx = t->tx.buf;
tx->identifier = cpu_to_le32(selector);
- attributes = FIELD_PREP(REG_TYPE_BITS, type) |
- FIELD_PREP(REG_CONFIG, config_type);
+ for (int i = 0; i < num_configs; i++) {
+ tx->config_value[i * 2] = cpu_to_le32(pinconf_to_config_param(configs[i]));
+ tx->config_value[i * 2 + 1] = cpu_to_le32(pinconf_to_config_argument(configs[i]));
+ }
+
+ attributes = FIELD_PREP(REG_TYPE_CONFIG, type) |
+ FIELD_PREP(REG_NUM_CONFIG, num_configs);
tx->attributes = cpu_to_le32(attributes);
- tx->config_value = cpu_to_le32(config_value);

ret = ph->xops->do_xfer(ph, t);

diff --git a/drivers/pinctrl/freescale/pinctrl-imx.c b/drivers/pinctrl/freescale/pinctrl-imx.c
index 9bc16943014f..dd0a66a17184 100644
--- a/drivers/pinctrl/freescale/pinctrl-imx.c
+++ b/drivers/pinctrl/freescale/pinctrl-imx.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/pinctrl/pinconf-generic.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/seq_file.h>
@@ -33,6 +34,29 @@
#define IMX_NO_PAD_CTL 0x80000000 /* no pin config need */
#define IMX_PAD_SION 0x40000000 /* set SION */

+/*
+ * 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>
+ */
+#define FSL_PIN_SIZE 24
+#define FSL_PIN_SHARE_SIZE 20
+#define FSL_SCU_PIN_SIZE 12
+
+/* SCMI pin control types, aligned with SCMI firmware */
+#define IMX_PIN_TYPE_MUX 192
+#define IMX_PIN_TYPE_CONFIG 193
+#define IMX_PIN_TYPE_DAISY_ID 194
+#define IMX_PIN_TYPE_DAISY_CFG 195
+
static inline const struct group_desc *imx_pinctrl_find_group_by_name(
struct pinctrl_dev *pctldev,
const char *name)
@@ -55,6 +79,96 @@ static void imx_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s,
seq_printf(s, "%s", dev_name(pctldev->dev));
}

+#define IMX_SCMI_NUM_CFG 4
+int imx_scmi_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np,
+ struct pinctrl_map **map, unsigned *num_maps)
+{
+ struct pinctrl_map *new_map;
+ const __be32 *list;
+ unsigned long *configs = NULL;
+ unsigned long cfg[IMX_SCMI_NUM_CFG];
+ int map_num, size, pin_size, pin_id, num_pins;
+ int mux_reg, conf_reg, input_reg, mux_val, conf_val, input_val;
+ int i, j;
+ uint32_t ncfg;
+ static uint32_t daisy_off;
+
+ if (!daisy_off) {
+ if (of_machine_is_compatible("fsl,imx93"))
+ daisy_off = 0x360;
+ else if (of_machine_is_compatible("fsl,imx95"))
+ daisy_off = 0x408;
+ else
+ dev_err(pctldev->dev, "platform not support scmi pinctrl\n");
+ }
+
+ list = of_get_property(np, "fsl,pins", &size);
+ if (!list) {
+ dev_err(pctldev->dev, "no fsl,pins property in node %pOF\n", np);
+ return -EINVAL;
+ }
+
+ pin_size = FSL_PIN_SIZE;
+
+ if (!size || size % pin_size) {
+ dev_err(pctldev->dev, "Invalid fsl,pins or pins property in node %pOF\n", np);
+ return -EINVAL;
+ }
+
+ num_pins = size / pin_size;
+ map_num = num_pins;
+
+ new_map = kmalloc_array(map_num, sizeof(struct pinctrl_map),
+ GFP_KERNEL);
+ if (!new_map)
+ return -ENOMEM;
+
+ *map = new_map;
+ *num_maps = map_num;
+
+ /* create config map */
+ for (i = 0; i < num_pins; i++) {
+ j = 0;
+ ncfg = IMX_SCMI_NUM_CFG;
+ mux_reg = be32_to_cpu(*list++);
+ conf_reg = be32_to_cpu(*list++);
+ input_reg = be32_to_cpu(*list++);
+ mux_val = be32_to_cpu(*list++);
+ input_val = be32_to_cpu(*list++);
+ conf_val = be32_to_cpu(*list++);
+ if (conf_val & IMX_PAD_SION)
+ mux_val |= IOMUXC_CONFIG_SION;
+
+ pin_id = mux_reg / 4;
+
+ cfg[j++] = pinconf_to_config_packed(IMX_PIN_TYPE_MUX, mux_val);
+
+ if (!conf_reg || (conf_val & IMX_NO_PAD_CTL)) {
+ ncfg--;
+ } else {
+ cfg[j++] = pinconf_to_config_packed(IMX_PIN_TYPE_CONFIG, conf_val);
+ }
+
+ if (!input_reg) {
+ ncfg -= 2;
+ } else {
+ cfg[j++] = pinconf_to_config_packed(IMX_PIN_TYPE_DAISY_ID,
+ (input_reg - daisy_off) / 4);
+ cfg[j++] = pinconf_to_config_packed(IMX_PIN_TYPE_DAISY_CFG, input_val);
+ }
+
+ configs = kmemdup(cfg, ncfg * sizeof(unsigned long), GFP_KERNEL);
+
+ new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN;
+ new_map[i].data.configs.group_or_pin = pin_get_name(pctldev, pin_id);
+ new_map[i].data.configs.configs = configs;
+ new_map[i].data.configs.num_configs = ncfg;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imx_scmi_dt_node_to_map);
+
static int imx_dt_node_to_map(struct pinctrl_dev *pctldev,
struct device_node *np,
struct pinctrl_map **map, unsigned *num_maps)
@@ -441,22 +555,6 @@ static const struct pinconf_ops imx_pinconf_ops = {
.pin_config_group_dbg_show = imx_pinconf_group_dbg_show,
};

-/*
- * 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.
- *
- * 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>
- */
-#define FSL_PIN_SIZE 24
-#define FSL_PIN_SHARE_SIZE 20
-#define FSL_SCU_PIN_SIZE 12
-
static void imx_pinctrl_parse_pin_mmio(struct imx_pinctrl *ipctl,
unsigned int *pin_id, struct imx_pin *pin,
const __be32 **list_p,
diff --git a/drivers/pinctrl/pinctrl-scmi.c b/drivers/pinctrl/pinctrl-scmi.c
index a9304402ddf1..92cf10241aa8 100644
--- a/drivers/pinctrl/pinctrl-scmi.c
+++ b/drivers/pinctrl/pinctrl-scmi.c
@@ -8,6 +8,7 @@
#include <linux/device.h>
#include <linux/err.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/seq_file.h>
#include <linux/scmi_protocol.h>
#include <linux/slab.h>
@@ -77,13 +78,28 @@ static int pinctrl_scmi_get_group_pins(struct pinctrl_dev *pctldev,
}

#ifdef CONFIG_OF
+#ifdef CONFIG_PINCTRL_IMX
+extern int imx_scmi_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np,
+ struct pinctrl_map **map, unsigned *num_maps);
+#else
+static inline int imx_scmi_dt_node_to_map(struct pinctrl_dev *pctldev,
+ struct device_node *np,
+ struct pinctrl_map **map, unsigned *num_maps)
+{
+ return -ENOTSUPP;
+}
+#endif
static int pinctrl_scmi_dt_node_to_map(struct pinctrl_dev *pctldev,
struct device_node *np_config,
struct pinctrl_map **map,
u32 *num_maps)
{
+ if ((of_machine_is_compatible("fsl,imx93") || of_machine_is_compatible("fsl,imx95")))
+ return imx_scmi_dt_node_to_map(pctldev, np_config, map, 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,
@@ -242,25 +258,15 @@ static int pinctrl_scmi_pinconf_set(struct pinctrl_dev *pctldev,
unsigned long *configs,
unsigned int num_configs)
{
- int i, ret;
+ int ret;
struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
- enum pin_config_param config_type;
- unsigned long config_value;

if (!configs || num_configs == 0)
return -EINVAL;

- for (i = 0; i < num_configs; i++) {
- config_type = pinconf_to_config_param(configs[i]);
- config_value = pinconf_to_config_argument(configs[i]);
-
- ret = pinctrl_ops->config_set(pmx->ph, _pin, PIN_TYPE, config_type, config_value);
- if (ret) {
- dev_err(pmx->dev, "Error parsing config %ld\n",
- configs[i]);
- break;
- }
- }
+ ret = pinctrl_ops->config_set(pmx->ph, _pin, PIN_TYPE, configs, num_configs);
+ if (ret)
+ dev_err(pmx->dev, "Error parsing config %d\n", ret);

return ret;
}
@@ -270,26 +276,15 @@ static int pinctrl_scmi_pinconf_group_set(struct pinctrl_dev *pctldev,
unsigned long *configs,
unsigned int num_configs)
{
- int i, ret;
+ int ret;
struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
- enum pin_config_param config_type;
- unsigned long config_value;

if (!configs || num_configs == 0)
return -EINVAL;

- for (i = 0; i < num_configs; i++) {
- config_type = pinconf_to_config_param(configs[i]);
- config_value = pinconf_to_config_argument(configs[i]);
-
- ret = pinctrl_ops->config_set(pmx->ph, group, GROUP_TYPE, config_type,
- config_value);
- if (ret) {
- dev_err(pmx->dev, "Error parsing config = %ld",
- configs[i]);
- break;
- }
- }
+ ret = pinctrl_ops->config_set(pmx->ph, group, GROUP_TYPE, configs, num_configs);
+ if (ret)
+ dev_err(pmx->dev, "Error parsing config %d", ret);

return ret;
};
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 99c1405decd7..e58d207b56b6 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -730,7 +730,7 @@ struct scmi_pinctrl_proto_ops {
u8 config_type, unsigned long *config_value);
int (*config_set)(const struct scmi_protocol_handle *ph, u32 selector,
enum scmi_pinctrl_selector_type type,
- u8 config_type, unsigned long config_value);
+ unsigned long *configs, unsigned int num_configs);
int (*pin_request)(const struct scmi_protocol_handle *ph, u32 pin);
int (*pin_free)(const struct scmi_protocol_handle *ph, u32 pin);
};
--
2.37.1