[PATCH 2/2] pinctrl: zynqmp: Add pinctrl driver

Michal Simek michal.simek at xilinx.com
Wed Feb 23 15:23:05 CET 2022


From: Ashok Reddy Soma <ashok.reddy.soma at xilinx.com>

Add pinctrl driver for Xilinx ZynqMP SOC. This driver is compatible with
linux device tree parameters for configuring pinmux and pinconf.

Signed-off-by: Ashok Reddy Soma <ashok.reddy.soma at xilinx.com>
Signed-off-by: Michal Simek <michal.simek at xilinx.com>
---

 MAINTAINERS                      |   1 +
 drivers/pinctrl/Kconfig          |  10 +
 drivers/pinctrl/Makefile         |   1 +
 drivers/pinctrl/pinctrl-zynqmp.c | 644 +++++++++++++++++++++++++++++++
 include/zynqmp_firmware.h        |  43 +++
 5 files changed, 699 insertions(+)
 create mode 100644 drivers/pinctrl/pinctrl-zynqmp.c

diff --git a/MAINTAINERS b/MAINTAINERS
index fb171e0c687b..c1a5ac95f295 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -619,6 +619,7 @@ F:	drivers/mmc/zynq_sdhci.c
 F:	drivers/mtd/nand/raw/zynq_nand.c
 F:	drivers/net/phy/xilinx_phy.c
 F:	drivers/net/zynq_gem.c
+F:	drivers/pinctrl/pinctrl-zynqmp.c
 F:	drivers/serial/serial_zynq.c
 F:	drivers/spi/zynq_qspi.c
 F:	drivers/spi/zynq_spi.c
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 03946245c7d5..d7477d7c3364 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -318,6 +318,16 @@ config PINCTRL_K210
 	  Support pin multiplexing on the K210. The "FPIOA" can remap any
 	  supported function to any multifunctional IO pin. It can also perform
 	  basic GPIO functions, such as reading the current value of a pin.
+
+config PINCTRL_ZYNQMP
+	bool "Xilinx ZynqMP pin control driver"
+	depends on DM && PINCTRL_GENERIC && ARCH_ZYNQMP
+	default y
+	help
+	  Support pin multiplexing control on Xilinx ZynqMP. The driver uses
+	  Generic Pinctrl framework and is compatible with the Linux driver,
+	  i.e. it uses the same device tree configuration.
+
 endif
 
 source "drivers/pinctrl/broadcom/Kconfig"
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index fd736a7f640a..ddddd13433c5 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -30,3 +30,4 @@ obj-$(CONFIG_PINCTRL_STI)	+= pinctrl-sti.o
 obj-$(CONFIG_PINCTRL_STM32)	+= pinctrl_stm32.o
 obj-$(CONFIG_$(SPL_)PINCTRL_STMFX)	+= pinctrl-stmfx.o
 obj-y				+= broadcom/
+obj-$(CONFIG_PINCTRL_ZYNQMP)	+= pinctrl-zynqmp.o
diff --git a/drivers/pinctrl/pinctrl-zynqmp.c b/drivers/pinctrl/pinctrl-zynqmp.c
new file mode 100644
index 000000000000..7c5a02db1b98
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-zynqmp.c
@@ -0,0 +1,644 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx pinctrl driver for ZynqMP
+ *
+ * Author(s):   Ashok Reddy Soma <ashok.reddy.soma at xilinx.com>
+ *              Michal Simek <michal.simek at xilinx.com>
+ *
+ * Copyright (C) 2021 Xilinx, Inc. All rights reserved.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <malloc.h>
+#include <zynqmp_firmware.h>
+#include <asm/arch/sys_proto.h>
+#include <asm/io.h>
+#include <dm/device_compat.h>
+#include <dm/pinctrl.h>
+#include <linux/compat.h>
+#include <dt-bindings/pinctrl/pinctrl-zynqmp.h>
+
+#define PINCTRL_GET_FUNC_GROUPS_RESP_LEN	12
+#define PINCTRL_GET_PIN_GROUPS_RESP_LEN		12
+#define NUM_GROUPS_PER_RESP			6
+#define NA_GROUP				-1
+#define RESERVED_GROUP				-2
+#define MAX_GROUP_PIN				50
+#define MAX_PIN_GROUPS				50
+#define MAX_GROUP_NAME_LEN			32
+#define MAX_FUNC_NAME_LEN			16
+
+#define DRIVE_STRENGTH_2MA	2
+#define DRIVE_STRENGTH_4MA	4
+#define DRIVE_STRENGTH_8MA	8
+#define DRIVE_STRENGTH_12MA	12
+
+/*
+ * This driver works with very simple configuration that has the same name
+ * for group and function. This way it is compatible with the Linux Kernel
+ * driver.
+ */
+struct zynqmp_pinctrl_priv {
+	u32 npins;
+	u32 nfuncs;
+	u32 ngroups;
+	struct zynqmp_pmux_function *funcs;
+	struct zynqmp_pctrl_group *groups;
+};
+
+/**
+ * struct zynqmp_pinctrl_config - pinconfig parameters
+ * @slew:		Slew rate slow or fast
+ * @bias:		Bias enabled or disabled
+ * @pull_ctrl:		Pull control pull up or pull down
+ * @input_type:		CMOS or Schmitt
+ * @drive_strength:	Drive strength 2mA/4mA/8mA/12mA
+ * @volt_sts:		Voltage status 1.8V or 3.3V
+ * @tri_state:		Tristate enabled or disabled
+ *
+ * This structure holds information about pin control config
+ * option that can be set for each pin.
+ */
+struct zynqmp_pinctrl_config {
+	u32 slew;
+	u32 bias;
+	u32 pull_ctrl;
+	u32 input_type;
+	u32 drive_strength;
+	u32 volt_sts;
+	u32 tri_state;
+};
+
+/**
+ * enum zynqmp_pin_config_param - possible pin configuration parameters
+ * @PIN_CONFIG_IOSTANDARD:	if the pin can select an IO standard,
+ *				the argument to this parameter (on a
+ *				custom format) tells the driver which
+ *				alternative IO standard to use
+ * @PIN_CONFIG_SCHMITTCMOS:	this parameter (on a custom format) allows
+ *				to select schmitt or cmos input for MIO pins
+ */
+enum zynqmp_pin_config_param {
+	PIN_CONFIG_IOSTANDARD = PIN_CONFIG_END + 1,
+	PIN_CONFIG_SCHMITTCMOS,
+};
+
+/**
+ * struct zynqmp_pmux_function - a pinmux function
+ * @name:	Name of the pinmux function
+ * @groups:	List of pingroups for this function
+ * @ngroups:	Number of entries in @groups
+ *
+ * This structure holds information about pin control function
+ * and function group names supporting that function.
+ */
+struct zynqmp_pmux_function {
+	char name[MAX_FUNC_NAME_LEN];
+	const char * const *groups;
+	unsigned int ngroups;
+};
+
+/**
+ * struct zynqmp_pctrl_group - Pin control group info
+ * @name:	Group name
+ * @pins:	Group pin numbers
+ * @npins:	Number of pins in group
+ */
+struct zynqmp_pctrl_group {
+	const char *name;
+	unsigned int pins[MAX_GROUP_PIN];
+	unsigned int npins;
+};
+
+static char pin_name[PINNAME_SIZE];
+
+/**
+ * zynqmp_pm_query_data() - Get query data from firmware
+ * @qid:	Value of enum pm_query_id
+ * @arg1:	Argument 1
+ * @arg2:	Argument 2
+ * @out:	Returned output value
+ *
+ * Return: Returns status, either success or error+reason
+ */
+static int zynqmp_pm_query_data(enum pm_query_id qid, u32 arg1, u32 arg2, u32 *out)
+{
+	int ret;
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	ret = xilinx_pm_request(PM_QUERY_DATA, qid, arg1, arg2, 0, ret_payload);
+	if (ret)
+		return ret;
+
+	*out = ret_payload[1];
+
+	return ret;
+}
+
+static int zynqmp_pm_pinctrl_get_config(const u32 pin, const u32 param, u32 *value)
+{
+	int ret;
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	/* Get config for the pin */
+	ret = xilinx_pm_request(PM_PINCTRL_CONFIG_PARAM_GET, pin, param, 0, 0, ret_payload);
+	if (ret) {
+		printf("%s failed\n", __func__);
+		return ret;
+	}
+
+	*value = ret_payload[1];
+
+	return ret;
+}
+
+static int zynqmp_pm_pinctrl_set_config(const u32 pin, const u32 param, u32 value)
+{
+	int ret;
+
+	/* Request the pin first */
+	ret = xilinx_pm_request(PM_PINCTRL_REQUEST, pin, 0, 0, 0, NULL);
+	if (ret) {
+		printf("%s: pin request failed\n", __func__);
+		return ret;
+	}
+
+	/* Set config for the pin */
+	ret = xilinx_pm_request(PM_PINCTRL_CONFIG_PARAM_SET, pin, param, value, 0, NULL);
+	if (ret) {
+		printf("%s failed\n", __func__);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int zynqmp_pinctrl_get_function_groups(u32 fid, u32 index, u16 *groups)
+{
+	int ret;
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	ret = xilinx_pm_request(PM_QUERY_DATA, PM_QID_PINCTRL_GET_FUNCTION_GROUPS,
+				fid, index, 0, ret_payload);
+	if (ret) {
+		printf("%s failed\n", __func__);
+		return ret;
+	}
+
+	memcpy(groups, &ret_payload[1], PINCTRL_GET_FUNC_GROUPS_RESP_LEN);
+
+	return ret;
+}
+
+static int zynqmp_pinctrl_prepare_func_groups(u32 fid,
+					      struct zynqmp_pmux_function *func,
+					      struct zynqmp_pctrl_group *groups)
+{
+	const char **fgroups;
+	char name[MAX_GROUP_NAME_LEN];
+	u16 resp[NUM_GROUPS_PER_RESP] = {0};
+	int ret, index, i;
+
+	fgroups = kcalloc(func->ngroups, sizeof(*fgroups), GFP_KERNEL);
+	if (!fgroups)
+		return -ENOMEM;
+
+	for (index = 0; index < func->ngroups; index += NUM_GROUPS_PER_RESP) {
+		ret = zynqmp_pinctrl_get_function_groups(fid, index, resp);
+		if (ret)
+			return ret;
+
+		for (i = 0; i < NUM_GROUPS_PER_RESP; i++) {
+			if (resp[i] == (u16)NA_GROUP)
+				goto done;
+			if (resp[i] == (u16)RESERVED_GROUP)
+				continue;
+
+			snprintf(name, MAX_GROUP_NAME_LEN, "%s_%d_grp",
+				 func->name, index + i);
+			fgroups[index + i] = strdup(name);
+
+			snprintf(name, MAX_GROUP_NAME_LEN, "%s_%d_grp",
+				 func->name, index + i);
+			groups[resp[i]].name = strdup(name);
+		}
+	}
+done:
+	func->groups = fgroups;
+
+	return ret;
+}
+
+static int zynqmp_pinctrl_get_pin_groups(u32 pin, u32 index, u16 *groups)
+{
+	int ret;
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	ret = xilinx_pm_request(PM_QUERY_DATA, PM_QID_PINCTRL_GET_PIN_GROUPS,
+				pin, index, 0, ret_payload);
+	if (ret) {
+		printf("%s failed to get pin groups\n", __func__);
+		return ret;
+	}
+
+	memcpy(groups, &ret_payload[1], PINCTRL_GET_PIN_GROUPS_RESP_LEN);
+
+	return ret;
+}
+
+static void zynqmp_pinctrl_group_add_pin(struct zynqmp_pctrl_group *group,
+					 unsigned int pin)
+{
+	group->pins[group->npins++] = pin;
+}
+
+static int zynqmp_pinctrl_create_pin_groups(struct zynqmp_pctrl_group *groups,
+					    unsigned int pin)
+{
+	u16 resp[NUM_GROUPS_PER_RESP] = {0};
+	int ret, i, index = 0;
+
+	do {
+		ret = zynqmp_pinctrl_get_pin_groups(pin, index, resp);
+		if (ret)
+			return ret;
+
+		for (i = 0; i < NUM_GROUPS_PER_RESP; i++) {
+			if (resp[i] == (u16)NA_GROUP)
+				goto done;
+			if (resp[i] == (u16)RESERVED_GROUP)
+				continue;
+			zynqmp_pinctrl_group_add_pin(&groups[resp[i]], pin);
+		}
+		index += NUM_GROUPS_PER_RESP;
+	} while (index <= MAX_PIN_GROUPS);
+
+done:
+	return ret;
+}
+
+static int zynqmp_pinctrl_probe(struct udevice *dev)
+{
+	struct zynqmp_pinctrl_priv *priv = dev_get_priv(dev);
+	int ret, i;
+	u32 pin;
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	/* Get number of pins first */
+	ret = zynqmp_pm_query_data(PM_QID_PINCTRL_GET_NUM_PINS, 0, 0, &priv->npins);
+	if (ret) {
+		printf("%s failed to get no of pins\n", __func__);
+		return ret;
+	}
+
+	/* Get number of functions available */
+	ret = zynqmp_pm_query_data(PM_QID_PINCTRL_GET_NUM_FUNCTIONS, 0, 0, &priv->nfuncs);
+	if (ret) {
+		printf("%s failed to get no of functions\n", __func__);
+		return ret;
+	}
+
+	/* Allocating structures for functions and its groups */
+	priv->funcs = kzalloc(sizeof(*priv->funcs) * priv->nfuncs, GFP_KERNEL);
+	if (!priv->funcs)
+		return -ENOMEM;
+
+	for (i = 0; i < priv->nfuncs; i++) {
+		/* Get function name for the function and fill */
+		xilinx_pm_request(PM_QUERY_DATA, PM_QID_PINCTRL_GET_FUNCTION_NAME,
+				  i, 0, 0, ret_payload);
+
+		memcpy((void *)priv->funcs[i].name, ret_payload, MAX_FUNC_NAME_LEN);
+
+		/* And fill number of groups available for certain function */
+		xilinx_pm_request(PM_QUERY_DATA, PM_QID_PINCTRL_GET_NUM_FUNCTION_GROUPS,
+				  i, 0, 0, ret_payload);
+
+		priv->funcs[i].ngroups = ret_payload[1];
+		priv->ngroups += priv->funcs[i].ngroups;
+	}
+
+	/* Prepare all groups */
+	priv->groups = kzalloc(sizeof(*priv->groups) * priv->ngroups,
+			       GFP_KERNEL);
+	if (!priv->groups)
+		return -ENOMEM;
+
+	for (i = 0; i < priv->nfuncs; i++) {
+		ret = zynqmp_pinctrl_prepare_func_groups(i, &priv->funcs[i],
+							 priv->groups);
+		if (ret) {
+			printf("Failed to prepare_func_groups\n");
+			return ret;
+		}
+	}
+
+	for (pin = 0; pin < priv->npins; pin++) {
+		ret = zynqmp_pinctrl_create_pin_groups(priv->groups, pin);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int zynqmp_pinctrl_get_functions_count(struct udevice *dev)
+{
+	struct zynqmp_pinctrl_priv *priv = dev_get_priv(dev);
+
+	return priv->nfuncs;
+}
+
+static const char *zynqmp_pinctrl_get_function_name(struct udevice *dev,
+						    unsigned int selector)
+{
+	struct zynqmp_pinctrl_priv *priv = dev_get_priv(dev);
+
+	return priv->funcs[selector].name;
+}
+
+static int zynqmp_pinmux_set(struct udevice *dev, unsigned int selector,
+			     unsigned int func_selector)
+{
+	int ret;
+
+	/* Request the pin first */
+	ret = xilinx_pm_request(PM_PINCTRL_REQUEST, selector, 0, 0, 0, NULL);
+	if (ret) {
+		printf("%s: pin request failed\n", __func__);
+		return ret;
+	}
+
+	/* Set the pin function */
+	ret = xilinx_pm_request(PM_PINCTRL_SET_FUNCTION, selector, func_selector,
+				0, 0, NULL);
+	if (ret) {
+		printf("%s: Failed to set pinmux function\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int zynqmp_pinmux_group_set(struct udevice *dev, unsigned int selector,
+				   unsigned int func_selector)
+{
+	int i;
+	struct zynqmp_pinctrl_priv *priv = dev_get_priv(dev);
+	const struct zynqmp_pctrl_group *pgrp = &priv->groups[selector];
+
+	for (i = 0; i < pgrp->npins; i++)
+		zynqmp_pinmux_set(dev, pgrp->pins[i], func_selector);
+
+	return 0;
+}
+
+static int zynqmp_pinconf_set(struct udevice *dev, unsigned int pin,
+			      unsigned int param, unsigned int arg)
+{
+	int ret = 0;
+	unsigned int value;
+
+	switch (param) {
+	case PIN_CONFIG_SLEW_RATE:
+		param = PM_PINCTRL_CONFIG_SLEW_RATE;
+		ret = zynqmp_pm_pinctrl_set_config(pin, param, arg);
+		break;
+	case PIN_CONFIG_BIAS_PULL_UP:
+		param = PM_PINCTRL_CONFIG_PULL_CTRL;
+		arg = PM_PINCTRL_BIAS_PULL_UP;
+		ret = zynqmp_pm_pinctrl_set_config(pin, param, arg);
+		break;
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		param = PM_PINCTRL_CONFIG_PULL_CTRL;
+		arg = PM_PINCTRL_BIAS_PULL_DOWN;
+		ret = zynqmp_pm_pinctrl_set_config(pin, param, arg);
+		break;
+	case PIN_CONFIG_BIAS_DISABLE:
+		param = PM_PINCTRL_CONFIG_BIAS_STATUS;
+		arg = PM_PINCTRL_BIAS_DISABLE;
+		ret = zynqmp_pm_pinctrl_set_config(pin, param, arg);
+		break;
+	case PIN_CONFIG_SCHMITTCMOS:
+		param = PM_PINCTRL_CONFIG_SCHMITT_CMOS;
+		ret = zynqmp_pm_pinctrl_set_config(pin, param, arg);
+		break;
+	case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
+		param = PM_PINCTRL_CONFIG_SCHMITT_CMOS;
+		ret = zynqmp_pm_pinctrl_set_config(pin, param, arg);
+		break;
+	case PIN_CONFIG_DRIVE_STRENGTH:
+		switch (arg) {
+		case DRIVE_STRENGTH_2MA:
+			value = PM_PINCTRL_DRIVE_STRENGTH_2MA;
+			break;
+		case DRIVE_STRENGTH_4MA:
+			value = PM_PINCTRL_DRIVE_STRENGTH_4MA;
+			break;
+		case DRIVE_STRENGTH_8MA:
+			value = PM_PINCTRL_DRIVE_STRENGTH_8MA;
+			break;
+		case DRIVE_STRENGTH_12MA:
+			value = PM_PINCTRL_DRIVE_STRENGTH_12MA;
+			break;
+		default:
+			/* Invalid drive strength */
+			dev_warn(dev, "Invalid drive strength for pin %d\n", pin);
+			return -EINVAL;
+		}
+
+		param = PM_PINCTRL_CONFIG_DRIVE_STRENGTH;
+		ret = zynqmp_pm_pinctrl_set_config(pin, param, value);
+		break;
+	case PIN_CONFIG_IOSTANDARD:
+		param = PM_PINCTRL_CONFIG_VOLTAGE_STATUS;
+		ret = zynqmp_pm_pinctrl_get_config(pin, param, &value);
+		if (arg != value)
+			dev_warn(dev, "Invalid IO Standard requested for pin %d\n",
+				 pin);
+		break;
+	case PIN_CONFIG_POWER_SOURCE:
+		param = PM_PINCTRL_CONFIG_VOLTAGE_STATUS;
+		ret = zynqmp_pm_pinctrl_get_config(pin, param, &value);
+		if (arg != value)
+			dev_warn(dev, "Invalid IO Standard requested for pin %d\n",
+				 pin);
+		break;
+	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+	case PIN_CONFIG_LOW_POWER_MODE:
+		/*
+		 * This cases are mentioned in dts but configurable
+		 * registers are unknown. So falling through to ignore
+		 * boot time warnings as of now.
+		 */
+		ret = 0;
+		break;
+	default:
+		dev_warn(dev, "unsupported configuration parameter '%u'\n",
+			 param);
+		ret = -ENOTSUPP;
+		break;
+	}
+
+	return ret;
+}
+
+static int zynqmp_pinconf_group_set(struct udevice *dev,
+				    unsigned int group_selector,
+				    unsigned int param, unsigned int arg)
+{
+	int i;
+	struct zynqmp_pinctrl_priv *priv = dev_get_priv(dev);
+	const struct zynqmp_pctrl_group *pgrp = &priv->groups[group_selector];
+
+	for (i = 0; i < pgrp->npins; i++)
+		zynqmp_pinconf_set(dev, pgrp->pins[i], param, arg);
+
+	return 0;
+}
+
+static int zynqmp_pinctrl_get_pins_count(struct udevice *dev)
+{
+	struct zynqmp_pinctrl_priv *priv = dev_get_priv(dev);
+
+	return priv->npins;
+}
+
+static const char *zynqmp_pinctrl_get_pin_name(struct udevice *dev,
+					       unsigned int selector)
+{
+	snprintf(pin_name, PINNAME_SIZE, "MIO%d", selector);
+
+	return pin_name;
+}
+
+static int zynqmp_pinctrl_get_pin_muxing(struct udevice *dev,
+					 unsigned int selector,
+					 char *buf,
+					 int size)
+{
+	struct zynqmp_pinctrl_config pinmux;
+
+	zynqmp_pm_pinctrl_get_config(selector, PM_PINCTRL_CONFIG_SLEW_RATE,
+				     &pinmux.slew);
+	zynqmp_pm_pinctrl_get_config(selector, PM_PINCTRL_CONFIG_BIAS_STATUS,
+				     &pinmux.bias);
+	zynqmp_pm_pinctrl_get_config(selector, PM_PINCTRL_CONFIG_PULL_CTRL,
+				     &pinmux.pull_ctrl);
+	zynqmp_pm_pinctrl_get_config(selector, PM_PINCTRL_CONFIG_SCHMITT_CMOS,
+				     &pinmux.input_type);
+	zynqmp_pm_pinctrl_get_config(selector, PM_PINCTRL_CONFIG_DRIVE_STRENGTH,
+				     &pinmux.drive_strength);
+	zynqmp_pm_pinctrl_get_config(selector, PM_PINCTRL_CONFIG_VOLTAGE_STATUS,
+				     &pinmux.volt_sts);
+
+	switch (pinmux.drive_strength) {
+	case PM_PINCTRL_DRIVE_STRENGTH_2MA:
+		pinmux.drive_strength = DRIVE_STRENGTH_2MA;
+		break;
+	case PM_PINCTRL_DRIVE_STRENGTH_4MA:
+		pinmux.drive_strength = DRIVE_STRENGTH_4MA;
+		break;
+	case PM_PINCTRL_DRIVE_STRENGTH_8MA:
+		pinmux.drive_strength = DRIVE_STRENGTH_8MA;
+		break;
+	case PM_PINCTRL_DRIVE_STRENGTH_12MA:
+		pinmux.drive_strength = DRIVE_STRENGTH_12MA;
+		break;
+	default:
+		/* Invalid drive strength */
+		dev_warn(dev, "Invalid drive strength\n");
+		return -EINVAL;
+	}
+
+	snprintf(buf, size, "slew:%s\tbias:%s\tpull:%s\tinput:%s\tdrive:%dmA\tvolt:%s",
+		 pinmux.slew ? "slow" : "fast",
+		 pinmux.bias ? "enabled" : "disabled",
+		 pinmux.pull_ctrl ? "up" : "down",
+		 pinmux.input_type ? "schmitt" : "cmos",
+		 pinmux.drive_strength,
+		 pinmux.volt_sts ? "1.8" : "3.3");
+
+	return 0;
+}
+
+static int zynqmp_pinctrl_get_groups_count(struct udevice *dev)
+{
+	struct zynqmp_pinctrl_priv *priv = dev_get_priv(dev);
+
+	return priv->ngroups;
+}
+
+static const char *zynqmp_pinctrl_get_group_name(struct udevice *dev,
+						 unsigned int selector)
+{
+	struct zynqmp_pinctrl_priv *priv = dev_get_priv(dev);
+
+	return priv->groups[selector].name;
+}
+
+static const struct pinconf_param zynqmp_conf_params[] = {
+	{ "bias-bus-hold", PIN_CONFIG_BIAS_BUS_HOLD, 0 },
+	{ "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
+	{ "bias-high-impedance", PIN_CONFIG_BIAS_HIGH_IMPEDANCE, 0 },
+	{ "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 },
+	{ "bias-pull-pin-default", PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1 },
+	{ "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 },
+	{ "drive-open-drain", PIN_CONFIG_DRIVE_OPEN_DRAIN, 0 },
+	{ "drive-open-source", PIN_CONFIG_DRIVE_OPEN_SOURCE, 0 },
+	{ "drive-push-pull", PIN_CONFIG_DRIVE_PUSH_PULL, 0 },
+	{ "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 },
+	{ "drive-strength-microamp", PIN_CONFIG_DRIVE_STRENGTH_UA, 0 },
+	{ "input-debounce", PIN_CONFIG_INPUT_DEBOUNCE, 0 },
+	{ "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 },
+	{ "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 },
+	{ "input-schmitt", PIN_CONFIG_INPUT_SCHMITT, 0 },
+	{ "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 },
+	{ "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 },
+	{ "low-power-disable", PIN_CONFIG_LOW_POWER_MODE, 0 },
+	{ "low-power-enable", PIN_CONFIG_LOW_POWER_MODE, 1 },
+	{ "output-disable", PIN_CONFIG_OUTPUT_ENABLE, 0 },
+	{ "output-enable", PIN_CONFIG_OUTPUT_ENABLE, 1 },
+	{ "output-high", PIN_CONFIG_OUTPUT, 1, },
+	{ "output-low", PIN_CONFIG_OUTPUT, 0, },
+	{ "power-source", PIN_CONFIG_POWER_SOURCE, 0 },
+	{ "sleep-hardware-state", PIN_CONFIG_SLEEP_HARDWARE_STATE, 0 },
+	{ "slew-rate", PIN_CONFIG_SLEW_RATE, 0 },
+	{ "skew-delay", PIN_CONFIG_SKEW_DELAY, 0 },
+	/* zynqmp specific */
+	{"io-standard", PIN_CONFIG_IOSTANDARD, IO_STANDARD_LVCMOS18},
+	{"schmitt-cmos", PIN_CONFIG_SCHMITTCMOS, PM_PINCTRL_INPUT_TYPE_SCHMITT},
+};
+
+static struct pinctrl_ops zynqmp_pinctrl_ops = {
+	.get_pins_count = zynqmp_pinctrl_get_pins_count,
+	.get_pin_name = zynqmp_pinctrl_get_pin_name,
+	.get_pin_muxing = zynqmp_pinctrl_get_pin_muxing,
+	.set_state = pinctrl_generic_set_state,
+	.get_groups_count = zynqmp_pinctrl_get_groups_count,
+	.get_group_name = zynqmp_pinctrl_get_group_name,
+	.get_functions_count = zynqmp_pinctrl_get_functions_count,
+	.get_function_name = zynqmp_pinctrl_get_function_name,
+	.pinmux_group_set = zynqmp_pinmux_group_set,
+	.pinmux_set = zynqmp_pinmux_set,
+	.pinconf_params = zynqmp_conf_params,
+	.pinconf_group_set = zynqmp_pinconf_group_set,
+	.pinconf_set = zynqmp_pinconf_set,
+	.pinconf_num_params = ARRAY_SIZE(zynqmp_conf_params),
+};
+
+static const struct udevice_id zynqmp_pinctrl_ids[] = {
+	{ .compatible = "xlnx,zynqmp-pinctrl" },
+	{ }
+};
+
+U_BOOT_DRIVER(pinctrl_zynqmp) = {
+	.name = "zynqmp-pinctrl",
+	.id = UCLASS_PINCTRL,
+	.of_match = zynqmp_pinctrl_ids,
+	.priv_auto = sizeof(struct zynqmp_pinctrl_priv),
+	.ops = &zynqmp_pinctrl_ops,
+	.probe = zynqmp_pinctrl_probe,
+};
diff --git a/include/zynqmp_firmware.h b/include/zynqmp_firmware.h
index 50bf4ef39535..41abc2eee951 100644
--- a/include/zynqmp_firmware.h
+++ b/include/zynqmp_firmware.h
@@ -177,6 +177,49 @@ enum pm_query_id {
 	PM_QID_CLOCK_GET_MAX_DIVISOR = 13,
 };
 
+enum pm_pinctrl_config_param {
+	PM_PINCTRL_CONFIG_SLEW_RATE = 0,
+	PM_PINCTRL_CONFIG_BIAS_STATUS = 1,
+	PM_PINCTRL_CONFIG_PULL_CTRL = 2,
+	PM_PINCTRL_CONFIG_SCHMITT_CMOS = 3,
+	PM_PINCTRL_CONFIG_DRIVE_STRENGTH = 4,
+	PM_PINCTRL_CONFIG_VOLTAGE_STATUS = 5,
+	PM_PINCTRL_CONFIG_TRI_STATE = 6,
+	PM_PINCTRL_CONFIG_MAX = 7,
+};
+
+enum pm_pinctrl_slew_rate {
+	PM_PINCTRL_SLEW_RATE_FAST = 0,
+	PM_PINCTRL_SLEW_RATE_SLOW = 1,
+};
+
+enum pm_pinctrl_bias_status {
+	PM_PINCTRL_BIAS_DISABLE = 0,
+	PM_PINCTRL_BIAS_ENABLE = 1,
+};
+
+enum pm_pinctrl_pull_ctrl {
+	PM_PINCTRL_BIAS_PULL_DOWN = 0,
+	PM_PINCTRL_BIAS_PULL_UP = 1,
+};
+
+enum pm_pinctrl_schmitt_cmos {
+	PM_PINCTRL_INPUT_TYPE_CMOS = 0,
+	PM_PINCTRL_INPUT_TYPE_SCHMITT = 1,
+};
+
+enum pm_pinctrl_drive_strength {
+	PM_PINCTRL_DRIVE_STRENGTH_2MA = 0,
+	PM_PINCTRL_DRIVE_STRENGTH_4MA = 1,
+	PM_PINCTRL_DRIVE_STRENGTH_8MA = 2,
+	PM_PINCTRL_DRIVE_STRENGTH_12MA = 3,
+};
+
+enum pm_pinctrl_tri_state {
+	PM_PINCTRL_TRI_STATE_DISABLE = 0,
+	PM_PINCTRL_TRI_STATE_ENABLE = 1,
+};
+
 enum zynqmp_pm_reset_action {
 	PM_RESET_ACTION_RELEASE = 0,
 	PM_RESET_ACTION_ASSERT = 1,
-- 
2.35.1



More information about the U-Boot mailing list