Add support for Xilinx ZynqMP pinctrl driver and also update the Xilinx
firmware driver to support pinctrl functionality.
This driver queries the pin information from the firmware and allow
configuring the pins as per the request.
changes in v4:
- Added comment for ignoring the return value for GET_FUNCTION_NAME qid.
- Updated the zynqmp_pinctrl_get_function_name() API prototype to void
as it always returns zero.
changes in v3:
- Fixed binding doc comments from Rob.
- Used 'maxItems' for groups and pins properties.
- Updated commit subject and description to have present tense statements.
changes in v2:
- Use pattern for pin names in yaml file.
- Updated to support multiple groups and pins.
- Added type ref for the vendor specific properties.
- Removed 'schmitt-cmos', instead used common properties.
- Removed macros for drive-strength property.
Sai Krishna Potthuri (3):
firmware: xilinx: Add pinctrl support
dt-bindings: pinctrl: Add binding for ZynqMP pinctrl driver
pinctrl: Add Xilinx ZynqMP pinctrl driver support
.../bindings/pinctrl/xlnx,zynqmp-pinctrl.yaml | 339 ++++++
drivers/firmware/xilinx/zynqmp.c | 114 ++
drivers/pinctrl/Kconfig | 13 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-zynqmp.c | 1030 +++++++++++++++++
include/dt-bindings/pinctrl/pinctrl-zynqmp.h | 19 +
include/linux/firmware/xlnx-zynqmp.h | 90 ++
7 files changed, 1606 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pinctrl/xlnx,zynqmp-pinctrl.yaml
create mode 100644 drivers/pinctrl/pinctrl-zynqmp.c
create mode 100644 include/dt-bindings/pinctrl/pinctrl-zynqmp.h
--
2.17.1
Adding pinctrl support to query platform specific information (pins)
from firmware.
Signed-off-by: Sai Krishna Potthuri <[email protected]>
Acked-by: Michal Simek <[email protected]>
---
drivers/firmware/xilinx/zynqmp.c | 114 +++++++++++++++++++++++++++
include/linux/firmware/xlnx-zynqmp.h | 90 +++++++++++++++++++++
2 files changed, 204 insertions(+)
diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c
index efb8a66efc68..299c3d5a9ebd 100644
--- a/drivers/firmware/xilinx/zynqmp.c
+++ b/drivers/firmware/xilinx/zynqmp.c
@@ -784,6 +784,120 @@ int zynqmp_pm_fpga_get_status(u32 *value)
}
EXPORT_SYMBOL_GPL(zynqmp_pm_fpga_get_status);
+/**
+ * zynqmp_pm_pinctrl_request - Request Pin from firmware
+ * @pin: Pin number to request
+ *
+ * This function requests pin from firmware.
+ *
+ * Return: Returns status, either success or error+reason.
+ */
+int zynqmp_pm_pinctrl_request(const u32 pin)
+{
+ return zynqmp_pm_invoke_fn(PM_PINCTRL_REQUEST, pin, 0, 0, 0, NULL);
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_request);
+
+/**
+ * zynqmp_pm_pinctrl_release - Inform firmware that Pin control is released
+ * @pin: Pin number to release
+ *
+ * This function release pin from firmware.
+ *
+ * Return: Returns status, either success or error+reason.
+ */
+int zynqmp_pm_pinctrl_release(const u32 pin)
+{
+ return zynqmp_pm_invoke_fn(PM_PINCTRL_RELEASE, pin, 0, 0, 0, NULL);
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_release);
+
+/**
+ * zynqmp_pm_pinctrl_get_function - Read function id set for the given pin
+ * @pin: Pin number
+ * @id: Buffer to store function ID
+ *
+ * This function provides the function currently set for the given pin.
+ *
+ * Return: Returns status, either success or error+reason
+ */
+int zynqmp_pm_pinctrl_get_function(const u32 pin, u32 *id)
+{
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ int ret;
+
+ if (!id)
+ return -EINVAL;
+
+ ret = zynqmp_pm_invoke_fn(PM_PINCTRL_GET_FUNCTION, pin, 0,
+ 0, 0, ret_payload);
+ *id = ret_payload[1];
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_get_function);
+
+/**
+ * zynqmp_pm_pinctrl_set_function - Set requested function for the pin
+ * @pin: Pin number
+ * @id: Function ID to set
+ *
+ * This function sets requested function for the given pin.
+ *
+ * Return: Returns status, either success or error+reason.
+ */
+int zynqmp_pm_pinctrl_set_function(const u32 pin, const u32 id)
+{
+ return zynqmp_pm_invoke_fn(PM_PINCTRL_SET_FUNCTION, pin, id,
+ 0, 0, NULL);
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_set_function);
+
+/**
+ * zynqmp_pm_pinctrl_get_config - Get configuration parameter for the pin
+ * @pin: Pin number
+ * @param: Parameter to get
+ * @value: Buffer to store parameter value
+ *
+ * This function gets requested configuration parameter for the given pin.
+ *
+ * Return: Returns status, either success or error+reason.
+ */
+int zynqmp_pm_pinctrl_get_config(const u32 pin, const u32 param,
+ u32 *value)
+{
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ int ret;
+
+ if (!value)
+ return -EINVAL;
+
+ ret = zynqmp_pm_invoke_fn(PM_PINCTRL_CONFIG_PARAM_GET, pin, param,
+ 0, 0, ret_payload);
+ *value = ret_payload[1];
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_get_config);
+
+/**
+ * zynqmp_pm_pinctrl_set_config - Set configuration parameter for the pin
+ * @pin: Pin number
+ * @param: Parameter to set
+ * @value: Parameter value to set
+ *
+ * This function sets requested configuration parameter for the given pin.
+ *
+ * Return: Returns status, either success or error+reason.
+ */
+int zynqmp_pm_pinctrl_set_config(const u32 pin, const u32 param,
+ u32 value)
+{
+ return zynqmp_pm_invoke_fn(PM_PINCTRL_CONFIG_PARAM_SET, pin,
+ param, value, 0, NULL);
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_set_config);
+
/**
* zynqmp_pm_init_finalize() - PM call to inform firmware that the caller
* master has initialized its own power management
diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h
index 5968df82b991..75aa6a5afa28 100644
--- a/include/linux/firmware/xlnx-zynqmp.h
+++ b/include/linux/firmware/xlnx-zynqmp.h
@@ -74,6 +74,12 @@ enum pm_api_id {
PM_FPGA_LOAD,
PM_FPGA_GET_STATUS,
PM_GET_CHIPID = 24,
+ PM_PINCTRL_REQUEST = 28,
+ PM_PINCTRL_RELEASE = 29,
+ PM_PINCTRL_GET_FUNCTION = 30,
+ PM_PINCTRL_SET_FUNCTION = 31,
+ PM_PINCTRL_CONFIG_PARAM_GET = 32,
+ PM_PINCTRL_CONFIG_PARAM_SET = 33,
PM_IOCTL = 34,
PM_QUERY_DATA,
PM_CLOCK_ENABLE,
@@ -125,6 +131,12 @@ enum pm_query_id {
PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS,
PM_QID_CLOCK_GET_PARENTS,
PM_QID_CLOCK_GET_ATTRIBUTES,
+ PM_QID_PINCTRL_GET_NUM_PINS = 6,
+ PM_QID_PINCTRL_GET_NUM_FUNCTIONS = 7,
+ PM_QID_PINCTRL_GET_NUM_FUNCTION_GROUPS = 8,
+ PM_QID_PINCTRL_GET_FUNCTION_NAME = 9,
+ PM_QID_PINCTRL_GET_FUNCTION_GROUPS = 10,
+ PM_QID_PINCTRL_GET_PIN_GROUPS = 11,
PM_QID_CLOCK_GET_NUM_CLOCKS = 12,
PM_QID_CLOCK_GET_MAX_DIVISOR,
};
@@ -288,6 +300,44 @@ enum dll_reset_type {
PM_DLL_RESET_PULSE,
};
+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 zynqmp_pm_shutdown_type {
ZYNQMP_PM_SHUTDOWN_TYPE_SHUTDOWN,
ZYNQMP_PM_SHUTDOWN_TYPE_RESET,
@@ -357,6 +407,14 @@ int zynqmp_pm_write_pggs(u32 index, u32 value);
int zynqmp_pm_read_pggs(u32 index, u32 *value);
int zynqmp_pm_system_shutdown(const u32 type, const u32 subtype);
int zynqmp_pm_set_boot_health_status(u32 value);
+int zynqmp_pm_pinctrl_request(const u32 pin);
+int zynqmp_pm_pinctrl_release(const u32 pin);
+int zynqmp_pm_pinctrl_get_function(const u32 pin, u32 *id);
+int zynqmp_pm_pinctrl_set_function(const u32 pin, const u32 id);
+int zynqmp_pm_pinctrl_get_config(const u32 pin, const u32 param,
+ u32 *value);
+int zynqmp_pm_pinctrl_set_config(const u32 pin, const u32 param,
+ u32 value);
#else
static inline struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void)
{
@@ -507,6 +565,38 @@ static inline int zynqmp_pm_set_boot_health_status(u32 value)
{
return -ENODEV;
}
+
+static inline int zynqmp_pm_pinctrl_request(const u32 pin)
+{
+ return -ENODEV;
+}
+
+static inline int zynqmp_pm_pinctrl_release(const u32 pin)
+{
+ return -ENODEV;
+}
+
+static inline int zynqmp_pm_pinctrl_get_function(const u32 pin, u32 *id)
+{
+ return -ENODEV;
+}
+
+static inline int zynqmp_pm_pinctrl_set_function(const u32 pin, const u32 id)
+{
+ return -ENODEV;
+}
+
+static inline int zynqmp_pm_pinctrl_get_config(const u32 pin, const u32 param,
+ u32 *value)
+{
+ return -ENODEV;
+}
+
+static inline int zynqmp_pm_pinctrl_set_config(const u32 pin, const u32 param,
+ u32 value)
+{
+ return -ENODEV;
+}
#endif
#endif /* __FIRMWARE_ZYNQMP_H__ */
--
2.17.1
Adding pinctrl driver for Xilinx ZynqMP platform.
This driver queries pin information from firmware and registers
pin control accordingly.
Signed-off-by: Sai Krishna Potthuri <[email protected]>
---
drivers/pinctrl/Kconfig | 13 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-zynqmp.c | 1030 ++++++++++++++++++++++++++++++
3 files changed, 1044 insertions(+)
create mode 100644 drivers/pinctrl/pinctrl-zynqmp.c
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 815095326e2d..25d3c7208975 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -341,6 +341,19 @@ config PINCTRL_ZYNQ
help
This selects the pinctrl driver for Xilinx Zynq.
+config PINCTRL_ZYNQMP
+ bool "Pinctrl driver for Xilinx ZynqMP"
+ depends on ARCH_ZYNQMP
+ select PINMUX
+ select GENERIC_PINCONF
+ help
+ This selects the pinctrl driver for Xilinx ZynqMP platform.
+ This driver will query the pin information from the firmware
+ and allow configuring the pins.
+ Configuration can include the mux function to select on those
+ pin(s)/group(s), and various pin configuration parameters
+ such as pull-up, slew rate, etc.
+
config PINCTRL_INGENIC
bool "Pinctrl driver for the Ingenic JZ47xx SoCs"
default MACH_INGENIC
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index f53933b2ff02..7e058739f0d5 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_PINCTRL_TB10X) += pinctrl-tb10x.o
obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o
obj-$(CONFIG_PINCTRL_STMFX) += pinctrl-stmfx.o
obj-$(CONFIG_PINCTRL_ZYNQ) += pinctrl-zynq.o
+obj-$(CONFIG_PINCTRL_ZYNQMP) += pinctrl-zynqmp.o
obj-$(CONFIG_PINCTRL_INGENIC) += pinctrl-ingenic.o
obj-$(CONFIG_PINCTRL_RK805) += pinctrl-rk805.o
obj-$(CONFIG_PINCTRL_OCELOT) += pinctrl-ocelot.o
diff --git a/drivers/pinctrl/pinctrl-zynqmp.c b/drivers/pinctrl/pinctrl-zynqmp.c
new file mode 100644
index 000000000000..63edde400e85
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-zynqmp.c
@@ -0,0 +1,1030 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ZynqMP pin controller
+ *
+ * Copyright (C) 2020 Xilinx, Inc.
+ *
+ * Sai Krishna Potthuri <[email protected]>
+ * Rajan Vaja <[email protected]>
+ */
+
+#include <linux/init.h>
+#include <linux/of_address.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <dt-bindings/pinctrl/pinctrl-zynqmp.h>
+#include <linux/platform_device.h>
+#include <linux/firmware/xlnx-zynqmp.h>
+#include "core.h"
+#include "pinctrl-utils.h"
+
+#define ZYNQMP_PIN_PREFIX "MIO"
+#define PINCTRL_GET_FUNC_NAME_RESP_LEN 16
+#define MAX_FUNC_NAME_LEN 16
+#define MAX_GROUP_PIN 50
+#define END_OF_FUNCTIONS "END_OF_FUNCTIONS"
+#define NUM_GROUPS_PER_RESP 6
+
+#define PINCTRL_GET_FUNC_GROUPS_RESP_LEN 12
+#define PINCTRL_GET_PIN_GROUPS_RESP_LEN 12
+#define NA_GROUP -1
+#define RESERVED_GROUP -2
+
+#define DRIVE_STRENGTH_2MA 2
+#define DRIVE_STRENGTH_4MA 4
+#define DRIVE_STRENGTH_8MA 8
+#define DRIVE_STRENGTH_12MA 12
+
+/**
+ * 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
+ * @node:` Firmware node matching with for function
+ *
+ * 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_pinctrl - driver data
+ * @pctrl: Pinctrl device
+ * @groups: Pingroups
+ * @ngroups: Number of @groups
+ * @funcs: Pinmux functions
+ * @nfuncs: Number of @funcs
+ *
+ * This struct is stored as driver data and used to retrieve
+ * information regarding pin control functions, groups and
+ * group pins.
+ */
+struct zynqmp_pinctrl {
+ struct pinctrl_dev *pctrl;
+ const struct zynqmp_pctrl_group *groups;
+ unsigned int ngroups;
+ const struct zynqmp_pmux_function *funcs;
+ unsigned int nfuncs;
+};
+
+/**
+ * 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;
+};
+
+/**
+ * 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
+ */
+enum zynqmp_pin_config_param {
+ PIN_CONFIG_IOSTANDARD = PIN_CONFIG_END + 1,
+};
+
+static const struct pinconf_generic_params zynqmp_dt_params[] = {
+ {"io-standard", PIN_CONFIG_IOSTANDARD, IO_STANDARD_LVCMOS18},
+};
+
+#ifdef CONFIG_DEBUG_FS
+static const struct
+pin_config_item zynqmp_conf_items[ARRAY_SIZE(zynqmp_dt_params)] = {
+ PCONFDUMP(PIN_CONFIG_IOSTANDARD, "io-standard", NULL, true),
+};
+#endif
+
+static struct pinctrl_desc zynqmp_desc;
+
+/**
+ * zynqmp_pctrl_get_groups_count() - get group count
+ * @pctldev: Pincontrol device pointer.
+ *
+ * Get total groups count.
+ *
+ * Return: group count.
+ */
+static int zynqmp_pctrl_get_groups_count(struct pinctrl_dev *pctldev)
+{
+ struct zynqmp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+
+ return pctrl->ngroups;
+}
+
+/**
+ * zynqmp_pctrl_get_group_name() - get group name
+ * @pctldev: Pincontrol device pointer.
+ * @selector: Group ID.
+ *
+ * Get gorup's name.
+ *
+ * Return: group name.
+ */
+static const char *zynqmp_pctrl_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned int selector)
+{
+ struct zynqmp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+
+ return pctrl->groups[selector].name;
+}
+
+/**
+ * zynqmp_pctrl_get_group_pins() - get group pins
+ * @pctldev: Pincontrol device pointer.
+ * @selector: Group ID.
+ * @pins: Pin numbers.
+ * @npins: Number of pins in group.
+ *
+ * Get gorup's pin count and pin number.
+ *
+ * Return: Success.
+ */
+static int zynqmp_pctrl_get_group_pins(struct pinctrl_dev *pctldev,
+ unsigned int selector,
+ const unsigned int **pins,
+ unsigned int *npins)
+{
+ struct zynqmp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+
+ *pins = pctrl->groups[selector].pins;
+ *npins = pctrl->groups[selector].npins;
+
+ return 0;
+}
+
+static const struct pinctrl_ops zynqmp_pctrl_ops = {
+ .get_groups_count = zynqmp_pctrl_get_groups_count,
+ .get_group_name = zynqmp_pctrl_get_group_name,
+ .get_group_pins = zynqmp_pctrl_get_group_pins,
+ .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
+ .dt_free_map = pinctrl_utils_free_map,
+};
+
+/**
+ * zynqmp_pinmux_request_pin() - Request a pin for muxing
+ * @pctldev: Pincontrol device pointer.
+ * @pin: Pin number.
+ *
+ * Request a pin from firmware for muxing.
+ *
+ * Return: 0 on success else error code.
+ */
+static int zynqmp_pinmux_request_pin(struct pinctrl_dev *pctldev,
+ unsigned int pin)
+{
+ int ret;
+
+ ret = zynqmp_pm_pinctrl_request(pin);
+ if (ret) {
+ dev_err(pctldev->dev, "request failed for pin %u\n", pin);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * zynqmp_pmux_get_functions_count() - get number of functions
+ * @pctldev: Pincontrol device pointer.
+ *
+ * Get total function count.
+ *
+ * Return: function count.
+ */
+static int zynqmp_pmux_get_functions_count(struct pinctrl_dev *pctldev)
+{
+ struct zynqmp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+
+ return pctrl->nfuncs;
+}
+
+/**
+ * zynqmp_pmux_get_function_name() - get function name
+ * @pctldev: Pincontrol device pointer.
+ * @selector: Function ID.
+ *
+ * Get function's name.
+ *
+ * Return: function name.
+ */
+static const char *zynqmp_pmux_get_function_name(struct pinctrl_dev *pctldev,
+ unsigned int selector)
+{
+ struct zynqmp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+
+ return pctrl->funcs[selector].name;
+}
+
+/**
+ * zynqmp_pmux_get_function_groups() - Get groups for the function
+ * @pctldev: Pincontrol device pointer.
+ * @selector: Function ID
+ * @groups: Group names.
+ * @num_groups: Number of function groups.
+ *
+ * Get function's group count and group names.
+ *
+ * Return: Success.
+ */
+static int zynqmp_pmux_get_function_groups(struct pinctrl_dev *pctldev,
+ unsigned int selector,
+ const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct zynqmp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+
+ *groups = pctrl->funcs[selector].groups;
+ *num_groups = pctrl->funcs[selector].ngroups;
+
+ return 0;
+}
+
+/**
+ * zynqmp_pinmux_set_mux() - Set requested function for the group
+ * @pctldev: Pincontrol device pointer.
+ * @function: Function ID.
+ * @group: Group ID.
+ *
+ * Loop though all pins of group and call firmware API
+ * to set requested function for all pins in group.
+ *
+ * Return: 0 on success else error code.
+ */
+static int zynqmp_pinmux_set_mux(struct pinctrl_dev *pctldev,
+ unsigned int function,
+ unsigned int group)
+{
+ struct zynqmp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+ const struct zynqmp_pctrl_group *pgrp = &pctrl->groups[group];
+ int ret, i;
+
+ for (i = 0; i < pgrp->npins; i++) {
+ unsigned int pin = pgrp->pins[i];
+
+ ret = zynqmp_pm_pinctrl_set_function(pin, function);
+ if (ret) {
+ dev_err(pctldev->dev, "set mux failed for pin %u\n",
+ pin);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * zynqmp_pinmux_release_pin() - Release a pin
+ * @pctldev: Pincontrol device pointer.
+ * @pin: Pin number.
+ *
+ * Release a pin from firmware.
+ *
+ * Return: 0 on success else error code.
+ */
+static int zynqmp_pinmux_release_pin(struct pinctrl_dev *pctldev,
+ unsigned int pin)
+{
+ int ret;
+
+ ret = zynqmp_pm_pinctrl_release(pin);
+ if (ret) {
+ dev_err(pctldev->dev, "free pin failed for pin %u\n",
+ pin);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static const struct pinmux_ops zynqmp_pinmux_ops = {
+ .request = zynqmp_pinmux_request_pin,
+ .get_functions_count = zynqmp_pmux_get_functions_count,
+ .get_function_name = zynqmp_pmux_get_function_name,
+ .get_function_groups = zynqmp_pmux_get_function_groups,
+ .set_mux = zynqmp_pinmux_set_mux,
+ .free = zynqmp_pinmux_release_pin,
+};
+
+/**
+ * zynqmp_pinconf_cfg_get() - get config value for the pin
+ * @pctldev: Pin control device pointer.
+ * @pin: Pin number.
+ * @config: Value of config param.
+ *
+ * Get value of the requested configuration parameter for the
+ * given pin.
+ *
+ * Return: 0 on success else error code.
+ */
+static int zynqmp_pinconf_cfg_get(struct pinctrl_dev *pctldev,
+ unsigned int pin,
+ unsigned long *config)
+{
+ int ret;
+ unsigned int arg = 0, param = pinconf_to_config_param(*config);
+
+ if (pin >= zynqmp_desc.npins)
+ return -EOPNOTSUPP;
+
+ switch (param) {
+ case PIN_CONFIG_SLEW_RATE:
+ param = PM_PINCTRL_CONFIG_SLEW_RATE;
+ ret = zynqmp_pm_pinctrl_get_config(pin, param, &arg);
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ param = PM_PINCTRL_CONFIG_PULL_CTRL;
+ ret = zynqmp_pm_pinctrl_get_config(pin, param, &arg);
+ if (arg != PM_PINCTRL_BIAS_PULL_UP)
+ return -EINVAL;
+
+ arg = 1;
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ param = PM_PINCTRL_CONFIG_PULL_CTRL;
+ ret = zynqmp_pm_pinctrl_get_config(pin, param, &arg);
+ if (arg != PM_PINCTRL_BIAS_PULL_DOWN)
+ return -EINVAL;
+
+ arg = 1;
+ break;
+ case PIN_CONFIG_BIAS_DISABLE:
+ param = PM_PINCTRL_CONFIG_BIAS_STATUS;
+ ret = zynqmp_pm_pinctrl_get_config(pin, param, &arg);
+ if (arg != PM_PINCTRL_BIAS_DISABLE)
+ return -EINVAL;
+
+ arg = 1;
+ break;
+ case PIN_CONFIG_IOSTANDARD:
+ param = PM_PINCTRL_CONFIG_VOLTAGE_STATUS;
+ ret = zynqmp_pm_pinctrl_get_config(pin, param, &arg);
+ break;
+ case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
+ param = PM_PINCTRL_CONFIG_SCHMITT_CMOS;
+ ret = zynqmp_pm_pinctrl_get_config(pin, param, &arg);
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ param = PM_PINCTRL_CONFIG_DRIVE_STRENGTH;
+ ret = zynqmp_pm_pinctrl_get_config(pin, param, &arg);
+ switch (arg) {
+ case PM_PINCTRL_DRIVE_STRENGTH_2MA:
+ arg = DRIVE_STRENGTH_2MA;
+ break;
+ case PM_PINCTRL_DRIVE_STRENGTH_4MA:
+ arg = DRIVE_STRENGTH_4MA;
+ break;
+ case PM_PINCTRL_DRIVE_STRENGTH_8MA:
+ arg = DRIVE_STRENGTH_8MA;
+ break;
+ case PM_PINCTRL_DRIVE_STRENGTH_12MA:
+ arg = DRIVE_STRENGTH_12MA;
+ break;
+ default:
+ /* Invalid drive strength */
+ dev_warn(pctldev->dev,
+ "Invalid drive strength for pin %d\n",
+ pin);
+ return -EINVAL;
+ }
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ param = pinconf_to_config_param(*config);
+ *config = pinconf_to_config_packed(param, arg);
+
+ return ret;
+}
+
+/**
+ * zynqmp_pinconf_cfg_set() - Set requested config for the pin
+ * @pctldev: Pincontrol device pointer.
+ * @pin: Pin number.
+ * @configs: Configuration to set.
+ * @num_configs: Number of configurations.
+ *
+ * Loop though all configurations and call firmware API
+ * to set requested configurations for the pin.
+ *
+ * Return: 0 on success else error code.
+ */
+static int zynqmp_pinconf_cfg_set(struct pinctrl_dev *pctldev,
+ unsigned int pin, unsigned long *configs,
+ unsigned int num_configs)
+{
+ int i, ret;
+
+ if (pin >= zynqmp_desc.npins)
+ return -EOPNOTSUPP;
+
+ for (i = 0; i < num_configs; i++) {
+ unsigned int param = pinconf_to_config_param(configs[i]);
+ unsigned int arg = pinconf_to_config_argument(configs[i]);
+ 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_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(pctldev->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(pctldev->dev,
+ "Invalid IO Standard requested for pin %d\n",
+ pin);
+
+ break;
+ case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+ case PIN_CONFIG_LOW_POWER_MODE:
+ /*
+ * These 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(pctldev->dev,
+ "unsupported configuration parameter '%u'\n",
+ param);
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ param = pinconf_to_config_param(configs[i]);
+ arg = pinconf_to_config_argument(configs[i]);
+ if (ret)
+ dev_warn(pctldev->dev,
+ "%s failed: pin %u param %u value %u\n",
+ __func__, pin, param, arg);
+ }
+
+ return 0;
+}
+
+/**
+ * zynqmp_pinconf_group_set() - Set requested config for the group
+ * @pctldev: Pincontrol device pointer.
+ * @selector: Group ID.
+ * @configs: Configuration to set.
+ * @num_configs: Number of configurations.
+ *
+ * Call function to set configs for each pin in group.
+ *
+ * Return: 0 on success else error code.
+ */
+static int zynqmp_pinconf_group_set(struct pinctrl_dev *pctldev,
+ unsigned int selector,
+ unsigned long *configs,
+ unsigned int num_configs)
+{
+ int i, ret;
+ struct zynqmp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+ const struct zynqmp_pctrl_group *pgrp = &pctrl->groups[selector];
+
+ for (i = 0; i < pgrp->npins; i++) {
+ ret = zynqmp_pinconf_cfg_set(pctldev, pgrp->pins[i], configs,
+ num_configs);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct pinconf_ops zynqmp_pinconf_ops = {
+ .is_generic = true,
+ .pin_config_get = zynqmp_pinconf_cfg_get,
+ .pin_config_set = zynqmp_pinconf_cfg_set,
+ .pin_config_group_set = zynqmp_pinconf_group_set,
+};
+
+static struct pinctrl_desc zynqmp_desc = {
+ .name = "zynqmp_pinctrl",
+ .owner = THIS_MODULE,
+ .pctlops = &zynqmp_pctrl_ops,
+ .pmxops = &zynqmp_pinmux_ops,
+ .confops = &zynqmp_pinconf_ops,
+#ifdef CONFIG_DEBUG_FS
+ .custom_conf_items = zynqmp_conf_items,
+#endif
+};
+
+/**
+ * zynqmp_pinctrl_get_function_groups() - get groups for the function
+ * @fid: Function ID.
+ * @index: Group index.
+ * @groups: Groups data.
+ *
+ * Call firmware API to get groups for the given function.
+ *
+ * Return: 0 on success else error code.
+ */
+static int zynqmp_pinctrl_get_function_groups(u32 fid, u32 index, u16 *groups)
+{
+ struct zynqmp_pm_query_data qdata = {0};
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ int ret;
+
+ qdata.qid = PM_QID_PINCTRL_GET_FUNCTION_GROUPS;
+ qdata.arg1 = fid;
+ qdata.arg2 = index;
+
+ ret = zynqmp_pm_query_data(qdata, ret_payload);
+ if (ret)
+ return ret;
+
+ memcpy(groups, &ret_payload[1], PINCTRL_GET_FUNC_GROUPS_RESP_LEN);
+
+ return ret;
+}
+
+/**
+ * zynqmp_pinctrl_get_func_num_groups() - get number of groups in function
+ * @fid: Function ID.
+ * @ngroups: Number of groups in function.
+ *
+ * Call firmware API to get number of group in function.
+ *
+ * Return: 0 on success else error code.
+ */
+static int zynqmp_pinctrl_get_func_num_groups(u32 fid, unsigned int *ngroups)
+{
+ struct zynqmp_pm_query_data qdata = {0};
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ int ret;
+
+ qdata.qid = PM_QID_PINCTRL_GET_NUM_FUNCTION_GROUPS;
+ qdata.arg1 = fid;
+
+ ret = zynqmp_pm_query_data(qdata, ret_payload);
+ if (ret)
+ return ret;
+
+ *ngroups = ret_payload[1];
+
+ return ret;
+}
+
+/**
+ * zynqmp_pinctrl_prepare_func_groups() - prepare function and groups data
+ * @dev: Device pointer.
+ * @fid: Function ID.
+ * @func: Function data.
+ * @groups: Groups data.
+ *
+ * Query firmware to get group IDs for each function. Firmware returns
+ * group IDs. Based on gorup index for the function, group names in
+ * function are stored. For example, first gorup in "eth0" function
+ * is named as "eth0_0", second as "eth0_1" and so on.
+ *
+ * Based on group ID received from firmware, function stores name of
+ * group for that group ID. For an example, if "eth0" first group ID
+ * is x, groups[x] name will be stored as "eth0_0".
+ *
+ * Once done for each function, each function would have its group names,
+ * and each groups would also have their names.
+ *
+ * Return: 0 on success else error code.
+ */
+static int zynqmp_pinctrl_prepare_func_groups(struct device *dev, u32 fid,
+ struct zynqmp_pmux_function *func,
+ struct zynqmp_pctrl_group *groups)
+{
+ u16 resp[NUM_GROUPS_PER_RESP] = {0};
+ const char **fgroups;
+ int ret = 0, index, i;
+
+ fgroups = devm_kzalloc(dev, sizeof(*fgroups) * func->ngroups,
+ 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;
+
+ fgroups[index + i] = devm_kasprintf(dev, GFP_KERNEL,
+ "%s_%d_grp",
+ func->name,
+ index + i);
+ groups[resp[i]].name = devm_kasprintf(dev, GFP_KERNEL,
+ "%s_%d_grp",
+ func->name,
+ index + i);
+ }
+ }
+done:
+ func->groups = fgroups;
+
+ return ret;
+}
+
+/**
+ * zynqmp_pinctrl_get_function_name() - get function name
+ * @fid: Function ID.
+ * @name: Function name
+ *
+ * Call firmware API to get name of given function.
+ */
+static void zynqmp_pinctrl_get_function_name(u32 fid, char *name)
+{
+ struct zynqmp_pm_query_data qdata = {0};
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+
+ qdata.qid = PM_QID_PINCTRL_GET_FUNCTION_NAME;
+ qdata.arg1 = fid;
+
+ /*
+ * Name of the function is maximum 16 bytes and cannot
+ * accommodate the return value in SMC buffers, hence ignoring
+ * the return value for this specific qid.
+ */
+ zynqmp_pm_query_data(qdata, ret_payload);
+ memcpy(name, ret_payload, PINCTRL_GET_FUNC_NAME_RESP_LEN);
+}
+
+/**
+ * zynqmp_pinctrl_get_num_functions() - get number of supported functions
+ * @nfuncs: Number of functions.
+ *
+ * Call firmware API to get number of functions supported by system/board.
+ *
+ * Return: 0 on success else error code.
+ */
+static int zynqmp_pinctrl_get_num_functions(unsigned int *nfuncs)
+{
+ struct zynqmp_pm_query_data qdata = {0};
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ int ret;
+
+ qdata.qid = PM_QID_PINCTRL_GET_NUM_FUNCTIONS;
+
+ ret = zynqmp_pm_query_data(qdata, ret_payload);
+ if (ret)
+ return ret;
+
+ *nfuncs = ret_payload[1];
+
+ return ret;
+}
+
+/**
+ * zynqmp_pinctrl_get_pin_groups() - get groups for the pin
+ * @pin: Pin number.
+ * @index: Group index.
+ * @groups: Groups data.
+ *
+ * Call firmware API to get groups for the given pin.
+ *
+ * Return: 0 on success else error code.
+ */
+static int zynqmp_pinctrl_get_pin_groups(u32 pin, u32 index, u16 *groups)
+{
+ struct zynqmp_pm_query_data qdata = {0};
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ int ret;
+
+ qdata.qid = PM_QID_PINCTRL_GET_PIN_GROUPS;
+ qdata.arg1 = pin;
+ qdata.arg2 = index;
+
+ ret = zynqmp_pm_query_data(qdata, ret_payload);
+ if (ret)
+ return ret;
+
+ memcpy(groups, &ret_payload[1], PINCTRL_GET_PIN_GROUPS_RESP_LEN);
+
+ return ret;
+}
+
+/**
+ * zynqmp_pinctrl_group_add_pin() - add pin to given group
+ * @group: Group data.
+ * @pin: Pin number.
+ *
+ * Add pin number to respective group's pin array at end and
+ * increment pin count for the group.
+ */
+static void zynqmp_pinctrl_group_add_pin(struct zynqmp_pctrl_group *group,
+ unsigned int pin)
+{
+ group->pins[group->npins++] = pin;
+}
+
+/**
+ * zynqmp_pinctrl_create_pin_groups() - assign pins to respective groups
+ * @dev: Device pointer.
+ * @groups: Groups data.
+ * @pin: Pin number.
+ *
+ * Query firmware to get groups available for the given pin.
+ * Based on firmware response(group IDs for the pin), add
+ * pin number to respective group's pin array.
+ *
+ * Once all pins are queries, each groups would have its number
+ * of pins and pin numbers data.
+ *
+ * Return: 0 on success else error code.
+ */
+static int zynqmp_pinctrl_create_pin_groups(struct device *dev,
+ struct zynqmp_pctrl_group *groups,
+ unsigned int pin)
+{
+ int ret, i, index = 0;
+ u16 resp[NUM_GROUPS_PER_RESP] = {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)
+ return ret;
+
+ if (resp[i] == (u16)RESERVED_GROUP)
+ continue;
+
+ zynqmp_pinctrl_group_add_pin(&groups[resp[i]], pin);
+ }
+ index += NUM_GROUPS_PER_RESP;
+ } while (1);
+
+ return ret;
+}
+
+/**
+ * zynqmp_pinctrl_prepare_group_pins() - prepare each group's pin data
+ * @dev: Device pointer.
+ * @groups: Groups data.
+ * @ngroups: Number of groups.
+ *
+ * Prepare pin number and number of pins data for each pins.
+ *
+ * Return: 0 on success else error code.
+ */
+static int zynqmp_pinctrl_prepare_group_pins(struct device *dev,
+ struct zynqmp_pctrl_group *groups,
+ unsigned int ngroups)
+{
+ unsigned int pin;
+ int ret = 0;
+
+ for (pin = 0; pin < zynqmp_desc.npins; pin++) {
+ ret = zynqmp_pinctrl_create_pin_groups(dev, groups, pin);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+/**
+ * zynqmp_pinctrl_prepare_function_info() - prepare function info
+ * @dev: Device pointer.
+ * @pctrl: Pin control driver data.
+ *
+ * Query firmware for functions, groups and pin information and
+ * prepare pin control driver data.
+ *
+ * Query number of functions and number of function groups (number
+ * of groups in given function) to allocate required memory buffers
+ * for functions and groups. Once buffers are allocated to store
+ * functions and groups data, query and store required information
+ * (numbe of groups and group names for each function, number of
+ * pins and pin numbers for each group).
+ *
+ * Return: 0 on success else error code.
+ */
+static int zynqmp_pinctrl_prepare_function_info(struct device *dev,
+ struct zynqmp_pinctrl *pctrl)
+{
+ struct zynqmp_pmux_function *funcs;
+ struct zynqmp_pctrl_group *groups;
+ int ret, i;
+
+ ret = zynqmp_pinctrl_get_num_functions(&pctrl->nfuncs);
+ if (ret)
+ return ret;
+
+ funcs = devm_kzalloc(dev, sizeof(*funcs) * pctrl->nfuncs, GFP_KERNEL);
+ if (!funcs)
+ return -ENOMEM;
+
+ for (i = 0; i < pctrl->nfuncs; i++) {
+ zynqmp_pinctrl_get_function_name(i, funcs[i].name);
+
+ ret = zynqmp_pinctrl_get_func_num_groups(i, &funcs[i].ngroups);
+ if (ret)
+ return ret;
+
+ pctrl->ngroups += funcs[i].ngroups;
+ }
+
+ groups = devm_kzalloc(dev, sizeof(*groups) * pctrl->ngroups,
+ GFP_KERNEL);
+ if (!groups)
+ return -ENOMEM;
+
+ for (i = 0; i < pctrl->nfuncs; i++) {
+ ret = zynqmp_pinctrl_prepare_func_groups(dev, i, &funcs[i],
+ groups);
+ if (ret)
+ return ret;
+ }
+
+ ret = zynqmp_pinctrl_prepare_group_pins(dev, groups, pctrl->ngroups);
+ if (ret)
+ return ret;
+
+ pctrl->funcs = funcs;
+ pctrl->groups = groups;
+
+ return ret;
+}
+
+/**
+ * zynqmp_pinctrl_get_num_pins() - get number of pins in system
+ * @npins: Number of pins in system/board.
+ *
+ * Call firmware API to get number of pins.
+ *
+ * Return: 0 on success else error code.
+ */
+static int zynqmp_pinctrl_get_num_pins(unsigned int *npins)
+{
+ struct zynqmp_pm_query_data qdata = {0};
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ int ret;
+
+ qdata.qid = PM_QID_PINCTRL_GET_NUM_PINS;
+
+ ret = zynqmp_pm_query_data(qdata, ret_payload);
+ if (ret)
+ return ret;
+
+ *npins = ret_payload[1];
+
+ return ret;
+}
+
+/**
+ * zynqmp_pinctrl_prepare_pin_desc() - prepare pin description info
+ * @dev: Device pointer.
+ * @zynqmp_pins: Pin information.
+ * @npins: Number of pins.
+ *
+ * Query number of pins information from firmware and prepare pin
+ * description containing pin number and pin name.
+ *
+ * Return: 0 on success else error code.
+ */
+static int zynqmp_pinctrl_prepare_pin_desc(struct device *dev,
+ const struct pinctrl_pin_desc
+ **zynqmp_pins,
+ unsigned int *npins)
+{
+ struct pinctrl_pin_desc *pins, *pin;
+ int ret;
+ int i;
+
+ ret = zynqmp_pinctrl_get_num_pins(npins);
+ if (ret)
+ return ret;
+
+ pins = devm_kzalloc(dev, sizeof(*pins) * *npins, GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+
+ for (i = 0; i < *npins; i++) {
+ pin = &pins[i];
+ pin->number = i;
+ pin->name = devm_kasprintf(dev, GFP_KERNEL, "%s%d",
+ ZYNQMP_PIN_PREFIX, i);
+ }
+
+ *zynqmp_pins = pins;
+
+ return 0;
+}
+
+static int zynqmp_pinctrl_probe(struct platform_device *pdev)
+{
+ struct zynqmp_pinctrl *pctrl;
+ int ret;
+
+ pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL);
+ if (!pctrl)
+ return -ENOMEM;
+
+ ret = zynqmp_pinctrl_prepare_pin_desc(&pdev->dev,
+ &zynqmp_desc.pins,
+ &zynqmp_desc.npins);
+ if (ret) {
+ dev_err(&pdev->dev, "%s() pin desc prepare fail with %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = zynqmp_pinctrl_prepare_function_info(&pdev->dev, pctrl);
+ if (ret) {
+ dev_err(&pdev->dev, "%s() function info prepare fail with %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ pctrl->pctrl = pinctrl_register(&zynqmp_desc, &pdev->dev, pctrl);
+ if (IS_ERR(pctrl->pctrl)) {
+ ret = PTR_ERR(pctrl->pctrl);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, pctrl);
+
+ dev_info(&pdev->dev, "zynqmp pinctrl initialized\n");
+
+ return ret;
+}
+
+static const struct of_device_id zynqmp_pinctrl_of_match[] = {
+ { .compatible = "xlnx,zynqmp-pinctrl" },
+ { }
+};
+
+static struct platform_driver zynqmp_pinctrl_driver = {
+ .driver = {
+ .name = "zynqmp-pinctrl",
+ .of_match_table = zynqmp_pinctrl_of_match,
+ },
+ .probe = zynqmp_pinctrl_probe,
+};
+builtin_platform_driver(zynqmp_pinctrl_driver);
--
2.17.1
Adding documentation and dt-bindings file which contains MIO pin
configuration defines for Xilinx ZynqMP pinctrl driver.
Signed-off-by: Sai Krishna Potthuri <[email protected]>
Reviewed-by: Rob Herring <[email protected]>
---
.../bindings/pinctrl/xlnx,zynqmp-pinctrl.yaml | 339 ++++++++++++++++++
include/dt-bindings/pinctrl/pinctrl-zynqmp.h | 19 +
2 files changed, 358 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pinctrl/xlnx,zynqmp-pinctrl.yaml
create mode 100644 include/dt-bindings/pinctrl/pinctrl-zynqmp.h
diff --git a/Documentation/devicetree/bindings/pinctrl/xlnx,zynqmp-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/xlnx,zynqmp-pinctrl.yaml
new file mode 100644
index 000000000000..0c1149706f8b
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/xlnx,zynqmp-pinctrl.yaml
@@ -0,0 +1,339 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pinctrl/xlnx,zynqmp-pinctrl.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Xilinx ZynqMP Pinctrl
+
+maintainers:
+ - Sai Krishna Potthuri <[email protected]>
+ - Rajan Vaja <[email protected]>
+
+description: |
+ Please refer to pinctrl-bindings.txt in this directory for details of the
+ common pinctrl bindings used by client devices, including the meaning of the
+ phrase "pin configuration node".
+
+ ZynqMP's pin configuration nodes act as a container for an arbitrary number of
+ subnodes. Each of these subnodes represents some desired configuration for a
+ pin, a group, or a list of pins or groups. This configuration can include the
+ mux function to select on those pin(s)/group(s), and various pin configuration
+ parameters, such as pull-up, slew rate, etc.
+
+ Each configuration node can consist of multiple nodes describing the pinmux and
+ pinconf options. Those nodes can be pinmux nodes or pinconf nodes.
+
+ The name of each subnode is not important; all subnodes should be enumerated
+ and processed purely based on their content.
+
+properties:
+ compatible:
+ const: xlnx,zynqmp-pinctrl
+
+patternProperties:
+ '^(.*-)?(default|gpio)$':
+ type: object
+ patternProperties:
+ '^mux':
+ type: object
+ description:
+ Pinctrl node's client devices use subnodes for pin muxes,
+ which in turn use below standard properties.
+ $ref: pinmux-node.yaml#
+
+ properties:
+ groups:
+ description:
+ List of groups to select (either this or "pins" must be
+ specified), available groups for this subnode.
+ items:
+ enum: [ethernet0_0_grp, ethernet1_0_grp, ethernet2_0_grp,
+ ethernet3_0_grp, gemtsu0_0_grp, gemtsu0_1_grp,
+ gemtsu0_2_grp, mdio0_0_grp, mdio1_0_grp,
+ mdio1_1_grp, mdio2_0_grp, mdio3_0_grp,
+ qspi0_0_grp, qspi_ss_0_grp, qspi_fbclk_0_grp,
+ spi0_0_grp, spi0_ss_0_grp, spi0_ss_1_grp,
+ spi0_ss_2_grp, spi0_1_grp, spi0_ss_3_grp,
+ spi0_ss_4_grp, spi0_ss_5_grp, spi0_2_grp,
+ spi0_ss_6_grp, spi0_ss_7_grp, spi0_ss_8_grp,
+ spi0_3_grp, spi0_ss_9_grp, spi0_ss_10_grp,
+ spi0_ss_11_grp, spi0_4_grp, spi0_ss_12_grp,
+ spi0_ss_13_grp, spi0_ss_14_grp, spi0_5_grp,
+ spi0_ss_15_grp, spi0_ss_16_grp, spi0_ss_17_grp,
+ spi1_0_grp, spi1_ss_0_grp, spi1_ss_1_grp,
+ spi1_ss_2_grp, spi1_1_grp, spi1_ss_3_grp,
+ spi1_ss_4_grp, spi1_ss_5_grp, spi1_2_grp,
+ spi1_ss_6_grp, spi1_ss_7_grp, spi1_ss_8_grp,
+ spi1_3_grp, spi1_ss_9_grp, spi1_ss_10_grp,
+ spi1_ss_11_grp, spi1_4_grp, spi1_ss_12_grp,
+ spi1_ss_13_grp, spi1_ss_14_grp, spi1_5_grp,
+ spi1_ss_15_grp, spi1_ss_16_grp, spi1_ss_17_grp,
+ sdio0_0_grp, sdio0_1_grp, sdio0_2_grp,
+ sdio0_3_grp, sdio0_4_grp, sdio0_5_grp,
+ sdio0_6_grp, sdio0_7_grp, sdio0_8_grp,
+ sdio0_9_grp, sdio0_10_grp, sdio0_11_grp,
+ sdio0_12_grp, sdio0_13_grp, sdio0_14_grp,
+ sdio0_15_grp, sdio0_16_grp, sdio0_17_grp,
+ sdio0_18_grp, sdio0_19_grp, sdio0_20_grp,
+ sdio0_21_grp, sdio0_22_grp, sdio0_23_grp,
+ sdio0_24_grp, sdio0_25_grp, sdio0_26_grp,
+ sdio0_27_grp, sdio0_28_grp, sdio0_29_grp,
+ sdio0_30_grp, sdio0_31_grp, sdio0_32_grp,
+ sdio0_pc_0_grp, sdio0_cd_0_grp, sdio0_wp_0_grp,
+ sdio0_pc_1_grp, sdio0_cd_1_grp, sdio0_wp_1_grp,
+ sdio0_pc_2_grp, sdio0_cd_2_grp, sdio0_wp_2_grp,
+ sdio1_0_grp, sdio1_1_grp, sdio1_2_grp,
+ sdio1_3_grp, sdio1_4_grp, sdio1_5_grp,
+ sdio1_6_grp, sdio1_7_grp, sdio1_8_grp,
+ sdio1_9_grp, sdio1_10_grp, sdio1_11_grp,
+ sdio1_12_grp, sdio1_13_grp, sdio1_14_grp,
+ sdio1_15_grp, sdio1_pc_0_grp, sdio1_cd_0_grp,
+ sdio1_wp_0_grp, sdio1_pc_1_grp, sdio1_cd_1_grp,
+ sdio1_wp_1_grp, nand0_0_grp, nand0_ce_0_grp,
+ nand0_rb_0_grp, nand0_dqs_0_grp, nand0_ce_1_grp,
+ nand0_rb_1_grp, nand0_dqs_1_grp, can0_0_grp,
+ can0_1_grp, can0_2_grp, can0_3_grp,
+ can0_4_grp, can0_5_grp, can0_6_grp,
+ can0_7_grp, can0_8_grp, can0_9_grp,
+ can0_10_grp, can0_11_grp, can0_12_grp,
+ can0_13_grp, can0_14_grp, can0_15_grp,
+ can0_16_grp, can0_17_grp, can0_18_grp,
+ can1_0_grp, can1_1_grp, can1_2_grp,
+ can1_3_grp, can1_4_grp, can1_5_grp,
+ can1_6_grp, can1_7_grp, can1_8_grp,
+ can1_9_grp, can1_10_grp, can1_11_grp,
+ can1_12_grp, can1_13_grp, can1_14_grp,
+ can1_15_grp, can1_16_grp, can1_17_grp,
+ can1_18_grp, can1_19_grp, uart0_0_grp,
+ uart0_1_grp, uart0_2_grp, uart0_3_grp,
+ uart0_4_grp, uart0_5_grp, uart0_6_grp,
+ uart0_7_grp, uart0_8_grp, uart0_9_grp,
+ uart0_10_grp, uart0_11_grp, uart0_12_grp,
+ uart0_13_grp, uart0_14_grp, uart0_15_grp,
+ uart0_16_grp, uart0_17_grp, uart0_18_grp,
+ uart1_0_grp, uart1_1_grp, uart1_2_grp,
+ uart1_3_grp, uart1_4_grp, uart1_5_grp,
+ uart1_6_grp, uart1_7_grp, uart1_8_grp,
+ uart1_9_grp, uart1_10_grp, uart1_11_grp,
+ uart1_12_grp, uart1_13_grp, uart1_14_grp,
+ uart1_15_grp, uart1_16_grp, uart1_17_grp,
+ uart1_18_grp, i2c0_0_grp, i2c0_1_grp,
+ i2c0_2_grp, i2c0_3_grp, i2c0_4_grp,
+ i2c0_5_grp, i2c0_6_grp, i2c0_7_grp,
+ i2c0_8_grp, i2c0_9_grp, i2c0_10_grp,
+ i2c0_11_grp, i2c0_12_grp, i2c0_13_grp,
+ i2c0_14_grp, i2c0_15_grp, i2c0_16_grp,
+ i2c0_17_grp, i2c0_18_grp, i2c1_0_grp,
+ i2c1_1_grp, i2c1_2_grp, i2c1_3_grp,
+ i2c1_4_grp, i2c1_5_grp, i2c1_6_grp,
+ i2c1_7_grp, i2c1_8_grp, i2c1_9_grp,
+ i2c1_10_grp, i2c1_11_grp, i2c1_12_grp,
+ i2c1_13_grp, i2c1_14_grp, i2c1_15_grp,
+ i2c1_16_grp, i2c1_17_grp, i2c1_18_grp,
+ i2c1_19_grp, ttc0_clk_0_grp, ttc0_wav_0_grp,
+ ttc0_clk_1_grp, ttc0_wav_1_grp, ttc0_clk_2_grp,
+ ttc0_wav_2_grp, ttc0_clk_3_grp, ttc0_wav_3_grp,
+ ttc0_clk_4_grp, ttc0_wav_4_grp, ttc0_clk_5_grp,
+ ttc0_wav_5_grp, ttc0_clk_6_grp, ttc0_wav_6_grp,
+ ttc0_clk_7_grp, ttc0_wav_7_grp, ttc0_clk_8_grp,
+ ttc0_wav_8_grp, ttc1_clk_0_grp, ttc1_wav_0_grp,
+ ttc1_clk_1_grp, ttc1_wav_1_grp, ttc1_clk_2_grp,
+ ttc1_wav_2_grp, ttc1_clk_3_grp, ttc1_wav_3_grp,
+ ttc1_clk_4_grp, ttc1_wav_4_grp, ttc1_clk_5_grp,
+ ttc1_wav_5_grp, ttc1_clk_6_grp, ttc1_wav_6_grp,
+ ttc1_clk_7_grp, ttc1_wav_7_grp, ttc1_clk_8_grp,
+ ttc1_wav_8_grp, ttc2_clk_0_grp, ttc2_wav_0_grp,
+ ttc2_clk_1_grp, ttc2_wav_1_grp, ttc2_clk_2_grp,
+ ttc2_wav_2_grp, ttc2_clk_3_grp, ttc2_wav_3_grp,
+ ttc2_clk_4_grp, ttc2_wav_4_grp, ttc2_clk_5_grp,
+ ttc2_wav_5_grp, ttc2_clk_6_grp, ttc2_wav_6_grp,
+ ttc2_clk_7_grp, ttc2_wav_7_grp, ttc2_clk_8_grp,
+ ttc2_wav_8_grp, ttc3_clk_0_grp, ttc3_wav_0_grp,
+ ttc3_clk_1_grp, ttc3_wav_1_grp, ttc3_clk_2_grp,
+ ttc3_wav_2_grp, ttc3_clk_3_grp, ttc3_wav_3_grp,
+ ttc3_clk_4_grp, ttc3_wav_4_grp, ttc3_clk_5_grp,
+ ttc3_wav_5_grp, ttc3_clk_6_grp, ttc3_wav_6_grp,
+ ttc3_clk_7_grp, ttc3_wav_7_grp, ttc3_clk_8_grp,
+ ttc3_wav_8_grp, swdt0_clk_0_grp, swdt0_rst_0_grp,
+ swdt0_clk_1_grp, swdt0_rst_1_grp, swdt0_clk_2_grp,
+ swdt0_rst_2_grp, swdt0_clk_3_grp, swdt0_rst_3_grp,
+ swdt0_clk_4_grp, swdt0_rst_4_grp, swdt0_clk_5_grp,
+ swdt0_rst_5_grp, swdt0_clk_6_grp, swdt0_rst_6_grp,
+ swdt0_clk_7_grp, swdt0_rst_7_grp, swdt0_clk_8_grp,
+ swdt0_rst_8_grp, swdt0_clk_9_grp, swdt0_rst_9_grp,
+ swdt0_clk_10_grp, swdt0_rst_10_grp, swdt0_clk_11_grp,
+ swdt0_rst_11_grp, swdt0_clk_12_grp, swdt0_rst_12_grp,
+ swdt1_clk_0_grp, swdt1_rst_0_grp, swdt1_clk_1_grp,
+ swdt1_rst_1_grp, swdt1_clk_2_grp, swdt1_rst_2_grp,
+ swdt1_clk_3_grp, swdt1_rst_3_grp, swdt1_clk_4_grp,
+ swdt1_rst_4_grp, swdt1_clk_5_grp, swdt1_rst_5_grp,
+ swdt1_clk_6_grp, swdt1_rst_6_grp, swdt1_clk_7_grp,
+ swdt1_rst_7_grp, swdt1_clk_8_grp, swdt1_rst_8_grp,
+ swdt1_clk_9_grp, swdt1_rst_9_grp, swdt1_clk_10_grp,
+ swdt1_rst_10_grp, swdt1_clk_11_grp, swdt1_rst_11_grp,
+ swdt1_clk_12_grp, swdt1_rst_12_grp, gpio0_0_grp,
+ gpio0_1_grp, gpio0_2_grp, gpio0_3_grp,
+ gpio0_4_grp, gpio0_5_grp, gpio0_6_grp,
+ gpio0_7_grp, gpio0_8_grp, gpio0_9_grp,
+ gpio0_10_grp, gpio0_11_grp, gpio0_12_grp,
+ gpio0_13_grp, gpio0_14_grp, gpio0_15_grp,
+ gpio0_16_grp, gpio0_17_grp, gpio0_18_grp,
+ gpio0_19_grp, gpio0_20_grp, gpio0_21_grp,
+ gpio0_22_grp, gpio0_23_grp, gpio0_24_grp,
+ gpio0_25_grp, gpio0_26_grp, gpio0_27_grp,
+ gpio0_28_grp, gpio0_29_grp, gpio0_30_grp,
+ gpio0_31_grp, gpio0_32_grp, gpio0_33_grp,
+ gpio0_34_grp, gpio0_35_grp, gpio0_36_grp,
+ gpio0_37_grp, gpio0_38_grp, gpio0_39_grp,
+ gpio0_40_grp, gpio0_41_grp, gpio0_42_grp,
+ gpio0_43_grp, gpio0_44_grp, gpio0_45_grp,
+ gpio0_46_grp, gpio0_47_grp, gpio0_48_grp,
+ gpio0_49_grp, gpio0_50_grp, gpio0_51_grp,
+ gpio0_52_grp, gpio0_53_grp, gpio0_54_grp,
+ gpio0_55_grp, gpio0_56_grp, gpio0_57_grp,
+ gpio0_58_grp, gpio0_59_grp, gpio0_60_grp,
+ gpio0_61_grp, gpio0_62_grp, gpio0_63_grp,
+ gpio0_64_grp, gpio0_65_grp, gpio0_66_grp,
+ gpio0_67_grp, gpio0_68_grp, gpio0_69_grp,
+ gpio0_70_grp, gpio0_71_grp, gpio0_72_grp,
+ gpio0_73_grp, gpio0_74_grp, gpio0_75_grp,
+ gpio0_76_grp, gpio0_77_grp, usb0_0_grp,
+ usb1_0_grp, pmu0_0_grp, pmu0_1_grp,
+ pmu0_2_grp, pmu0_3_grp, pmu0_4_grp,
+ pmu0_5_grp, pmu0_6_grp, pmu0_7_grp,
+ pmu0_8_grp, pmu0_9_grp, pmu0_10_grp,
+ pmu0_11_grp, pcie0_0_grp, pcie0_1_grp,
+ pcie0_2_grp, pcie0_3_grp, pcie0_4_grp,
+ pcie0_5_grp, pcie0_6_grp, pcie0_7_grp,
+ csu0_0_grp, csu0_1_grp, csu0_2_grp,
+ csu0_3_grp, csu0_4_grp, csu0_5_grp,
+ csu0_6_grp, csu0_7_grp, csu0_8_grp,
+ csu0_9_grp, csu0_10_grp, csu0_11_grp,
+ dpaux0_0_grp, dpaux0_1_grp, dpaux0_2_grp,
+ dpaux0_3_grp, pjtag0_0_grp, pjtag0_1_grp,
+ pjtag0_2_grp, pjtag0_3_grp, pjtag0_4_grp,
+ pjtag0_5_grp, trace0_0_grp, trace0_clk_0_grp,
+ trace0_1_grp, trace0_clk_1_grp, trace0_2_grp,
+ trace0_clk_2_grp, testscan0_0_grp]
+ maxItems: 78
+
+ function:
+ description:
+ Specify the alternative function to be configured for the
+ given pin groups.
+ enum: [ethernet0, ethernet1, ethernet2, ethernet3, gemtsu0, usb0, usb1, mdio0,
+ mdio1, mdio2, mdio3, qspi0, qspi_fbclk, qspi_ss, spi0, spi1, spi0_ss,
+ spi1_ss, sdio0, sdio0_pc, sdio0_wp, sdio0_cd, sdio1, sdio1_pc, sdio1_wp,
+ sdio1_cd, nand0, nand0_ce, nand0_rb, nand0_dqs, can0, can1, uart0, uart1,
+ i2c0, i2c1, ttc0_clk, ttc0_wav, ttc1_clk, ttc1_wav, ttc2_clk, ttc2_wav,
+ ttc3_clk, ttc3_wav, swdt0_clk, swdt0_rst, swdt1_clk, swdt1_rst, gpio0, pmu0,
+ pcie0, csu0, dpaux0, pjtag0, trace0, trace0_clk, testscan0]
+
+ required:
+ - groups
+ - function
+
+ additionalProperties: false
+
+ '^conf':
+ type: object
+ description:
+ Pinctrl node's client devices use subnodes for pin configurations,
+ which in turn use the standard properties below.
+ $ref: pincfg-node.yaml#
+
+ properties:
+ groups:
+ description:
+ List of pin groups as mentioned above.
+
+ pins:
+ description:
+ List of pin names to select in this subnode.
+ items:
+ pattern: '^MIO([0-9]|[1-6][0-9]|7[0-7])$'
+ maxItems: 78
+
+ bias-pull-up: true
+
+ bias-pull-down: true
+
+ bias-disable: true
+
+ input-schmitt-enable: true
+
+ input-schmitt-disable: true
+
+ bias-high-impedance: true
+
+ low-power-enable: true
+
+ low-power-disable: true
+
+ slew-rate:
+ enum: [0, 1]
+
+ drive-strength:
+ description:
+ Selects the drive strength for MIO pins, in mA.
+ enum: [2, 4, 8, 12]
+
+ io-standard:
+ description:
+ Selects the IO standard for MIO pins, this is driver specific.
+ $ref: "/schemas/types.yaml#/definitions/uint32"
+ enum: [0, 1]
+
+ oneOf:
+ - required: [ groups ]
+ - required: [ pins ]
+
+ additionalProperties: false
+
+ additionalProperties: false
+
+required:
+ - compatible
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/pinctrl/pinctrl-zynqmp.h>
+ zynqmp_firmware: zynqmp-firmware {
+ pinctrl0: pinctrl {
+ compatible = "xlnx,zynqmp-pinctrl";
+
+ pinctrl_uart1_default: uart1-default {
+ mux {
+ groups = "uart0_4_grp", "uart0_5_grp";
+ function = "uart0";
+ };
+
+ conf {
+ groups = "uart0_4_grp";
+ slew-rate = <SLEW_RATE_SLOW>;
+ io-standard = <IO_STANDARD_LVCMOS18>;
+ };
+
+ conf-rx {
+ pins = "MIO18";
+ bias-pull-up;
+ };
+
+ conf-tx {
+ pins = "MIO19";
+ bias-disable;
+ input-schmitt-disable;
+ };
+ };
+ };
+ };
+
+ uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1_default>;
+ };
+
+...
diff --git a/include/dt-bindings/pinctrl/pinctrl-zynqmp.h b/include/dt-bindings/pinctrl/pinctrl-zynqmp.h
new file mode 100644
index 000000000000..b86b29d41bb4
--- /dev/null
+++ b/include/dt-bindings/pinctrl/pinctrl-zynqmp.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * MIO pin configuration defines for Xilinx ZynqMP
+ *
+ * Copyright (C) 2020 Xilinx, Inc.
+ */
+
+#ifndef _DT_BINDINGS_PINCTRL_ZYNQMP_H
+#define _DT_BINDINGS_PINCTRL_ZYNQMP_H
+
+/* Bit value for IO standards */
+#define IO_STANDARD_LVCMOS33 0
+#define IO_STANDARD_LVCMOS18 1
+
+/* Bit values for Slew Rates */
+#define SLEW_RATE_FAST 0
+#define SLEW_RATE_SLOW 1
+
+#endif /* _DT_BINDINGS_PINCTRL_ZYNQMP_H */
--
2.17.1
On Wed, Mar 17, 2021 at 10:27 AM Sai Krishna Potthuri
<[email protected]> wrote:
>
> Adding pinctrl driver for Xilinx ZynqMP platform.
> This driver queries pin information from firmware and registers
> pin control accordingly.
>
> Signed-off-by: Sai Krishna Potthuri <[email protected]>
> ---
> drivers/pinctrl/Kconfig | 13 +
> drivers/pinctrl/Makefile | 1 +
> drivers/pinctrl/pinctrl-zynqmp.c | 1030 ++++++++++++++++++++++++++++++
> 3 files changed, 1044 insertions(+)
> create mode 100644 drivers/pinctrl/pinctrl-zynqmp.c
>
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index 815095326e2d..25d3c7208975 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -341,6 +341,19 @@ config PINCTRL_ZYNQ
> help
> This selects the pinctrl driver for Xilinx Zynq.
>
> +config PINCTRL_ZYNQMP
> + bool "Pinctrl driver for Xilinx ZynqMP"
Why not module?
> + depends on ARCH_ZYNQMP
> + select PINMUX
> + select GENERIC_PINCONF
> + help
> + This selects the pinctrl driver for Xilinx ZynqMP platform.
> + This driver will query the pin information from the firmware
> + and allow configuring the pins.
> + Configuration can include the mux function to select on those
> + pin(s)/group(s), and various pin configuration parameters
> + such as pull-up, slew rate, etc.
> +
> config PINCTRL_INGENIC
> bool "Pinctrl driver for the Ingenic JZ47xx SoCs"
> default MACH_INGENIC
> diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
> index f53933b2ff02..7e058739f0d5 100644
> --- a/drivers/pinctrl/Makefile
> +++ b/drivers/pinctrl/Makefile
> @@ -43,6 +43,7 @@ obj-$(CONFIG_PINCTRL_TB10X) += pinctrl-tb10x.o
> obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o
> obj-$(CONFIG_PINCTRL_STMFX) += pinctrl-stmfx.o
> obj-$(CONFIG_PINCTRL_ZYNQ) += pinctrl-zynq.o
> +obj-$(CONFIG_PINCTRL_ZYNQMP) += pinctrl-zynqmp.o
> obj-$(CONFIG_PINCTRL_INGENIC) += pinctrl-ingenic.o
> obj-$(CONFIG_PINCTRL_RK805) += pinctrl-rk805.o
> obj-$(CONFIG_PINCTRL_OCELOT) += pinctrl-ocelot.o
> diff --git a/drivers/pinctrl/pinctrl-zynqmp.c b/drivers/pinctrl/pinctrl-zynqmp.c
> new file mode 100644
> index 000000000000..63edde400e85
> --- /dev/null
> +++ b/drivers/pinctrl/pinctrl-zynqmp.c
> @@ -0,0 +1,1030 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * ZynqMP pin controller
> + *
> + * Copyright (C) 2020 Xilinx, Inc.
> + *
> + * Sai Krishna Potthuri <[email protected]>
> + * Rajan Vaja <[email protected]>
> + */
> +
> +#include <linux/init.h>
> +#include <linux/of_address.h>
> +#include <linux/pinctrl/pinmux.h>
> +#include <linux/pinctrl/pinconf-generic.h>
Can you move this group of headers after linux/* ?
> +#include <dt-bindings/pinctrl/pinctrl-zynqmp.h>
Can it be moved outside of these headers
> +#include <linux/platform_device.h>
> +#include <linux/firmware/xlnx-zynqmp.h>
Ordering (for all groups of headers)?
> +#include "core.h"
> +#include "pinctrl-utils.h"
> +
> +#define ZYNQMP_PIN_PREFIX "MIO"
> +#define PINCTRL_GET_FUNC_NAME_RESP_LEN 16
> +#define MAX_FUNC_NAME_LEN 16
> +#define MAX_GROUP_PIN 50
> +#define END_OF_FUNCTIONS "END_OF_FUNCTIONS"
> +#define NUM_GROUPS_PER_RESP 6
> +
> +#define PINCTRL_GET_FUNC_GROUPS_RESP_LEN 12
> +#define PINCTRL_GET_PIN_GROUPS_RESP_LEN 12
> +#define NA_GROUP -1
> +#define RESERVED_GROUP -2
> +
> +#define DRIVE_STRENGTH_2MA 2
> +#define DRIVE_STRENGTH_4MA 4
> +#define DRIVE_STRENGTH_8MA 8
> +#define DRIVE_STRENGTH_12MA 12
> +
> +/**
> + * 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
> + * @node:` Firmware node matching with for function
Typo
> + *
> + * 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_pinctrl - driver data
> + * @pctrl: Pinctrl device
Pin control
> + * @groups: Pingroups
Pin groups
> + * @ngroups: Number of @groups
> + * @funcs: Pinmux functions
Pin mux functions
> + * @nfuncs: Number of @funcs
> + *
> + * This struct is stored as driver data and used to retrieve
> + * information regarding pin control functions, groups and
> + * group pins.
> + */
> +struct zynqmp_pinctrl {
> + struct pinctrl_dev *pctrl;
> + const struct zynqmp_pctrl_group *groups;
> + unsigned int ngroups;
> + const struct zynqmp_pmux_function *funcs;
> + unsigned int nfuncs;
> +};
> +
> +/**
> + * 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;
> +};
> +
> +/**
> + * 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
> + */
> +enum zynqmp_pin_config_param {
> + PIN_CONFIG_IOSTANDARD = PIN_CONFIG_END + 1,
> +};
I'm lost here. What is IO standard exactly? Why it can't be in generic headers?
> +static const struct pinconf_generic_params zynqmp_dt_params[] = {
> + {"io-standard", PIN_CONFIG_IOSTANDARD, IO_STANDARD_LVCMOS18},
> +};
> +
> +#ifdef CONFIG_DEBUG_FS
> +static const struct
> +pin_config_item zynqmp_conf_items[ARRAY_SIZE(zynqmp_dt_params)] = {
> + PCONFDUMP(PIN_CONFIG_IOSTANDARD, "io-standard", NULL, true),
> +};
> +#endif
> +
> +static struct pinctrl_desc zynqmp_desc;
> +
> +/**
> + * zynqmp_pctrl_get_groups_count() - get group count
> + * @pctldev: Pincontrol device pointer.
> + *
> + * Get total groups count.
> + *
> + * Return: group count.
> + */
> +static int zynqmp_pctrl_get_groups_count(struct pinctrl_dev *pctldev)
> +{
> + struct zynqmp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
> +
> + return pctrl->ngroups;
> +}
> +
> +/**
> + * zynqmp_pctrl_get_group_name() - get group name
> + * @pctldev: Pincontrol device pointer.
> + * @selector: Group ID.
> + *
> + * Get gorup's name.
> + *
> + * Return: group name.
Isn't too much duplication without any added value for the reader?
> + */
> +static const char *zynqmp_pctrl_get_group_name(struct pinctrl_dev *pctldev,
> + unsigned int selector)
> +{
> + struct zynqmp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
> +
> + return pctrl->groups[selector].name;
> +}
> +
> +/**
> + * zynqmp_pctrl_get_group_pins() - get group pins
> + * @pctldev: Pincontrol device pointer.
> + * @selector: Group ID.
> + * @pins: Pin numbers.
> + * @npins: Number of pins in group.
> + *
> + * Get gorup's pin count and pin number.
> + * Return: Success.
Clean up all your kernel docs from noise like this.
> + */
> +static int zynqmp_pctrl_get_group_pins(struct pinctrl_dev *pctldev,
> + unsigned int selector,
> + const unsigned int **pins,
> + unsigned int *npins)
> +{
> + struct zynqmp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
> +
> + *pins = pctrl->groups[selector].pins;
> + *npins = pctrl->groups[selector].npins;
> +
> + return 0;
> +}
> +
> +static const struct pinctrl_ops zynqmp_pctrl_ops = {
> + .get_groups_count = zynqmp_pctrl_get_groups_count,
> + .get_group_name = zynqmp_pctrl_get_group_name,
> + .get_group_pins = zynqmp_pctrl_get_group_pins,
> + .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
> + .dt_free_map = pinctrl_utils_free_map,
> +};
> +
> +/**
> + * zynqmp_pinmux_request_pin() - Request a pin for muxing
> + * @pctldev: Pincontrol device pointer.
> + * @pin: Pin number.
> + *
> + * Request a pin from firmware for muxing.
> + *
> + * Return: 0 on success else error code.
> + */
> +static int zynqmp_pinmux_request_pin(struct pinctrl_dev *pctldev,
> + unsigned int pin)
> +{
> + int ret;
> +
> + ret = zynqmp_pm_pinctrl_request(pin);
> + if (ret) {
> + dev_err(pctldev->dev, "request failed for pin %u\n", pin);
> + return -EIO;
Why shadowing error code? Since it's the only possible error, why is
it not reflected in the kernel doc?
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * zynqmp_pmux_get_functions_count() - get number of functions
> + * @pctldev: Pincontrol device pointer.
> + *
> + * Get total function count.
> + *
> + * Return: function count.
> + */
> +static int zynqmp_pmux_get_functions_count(struct pinctrl_dev *pctldev)
> +{
> + struct zynqmp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
> +
> + return pctrl->nfuncs;
> +}
> +
> +/**
> + * zynqmp_pmux_get_function_name() - get function name
> + * @pctldev: Pincontrol device pointer.
> + * @selector: Function ID.
> + *
> + * Get function's name.
> + *
> + * Return: function name.
> + */
> +static const char *zynqmp_pmux_get_function_name(struct pinctrl_dev *pctldev,
> + unsigned int selector)
> +{
> + struct zynqmp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
> +
> + return pctrl->funcs[selector].name;
> +}
> +
> +/**
> + * zynqmp_pmux_get_function_groups() - Get groups for the function
> + * @pctldev: Pincontrol device pointer.
> + * @selector: Function ID
> + * @groups: Group names.
> + * @num_groups: Number of function groups.
> + *
> + * Get function's group count and group names.
> + *
> + * Return: Success.
> + */
> +static int zynqmp_pmux_get_function_groups(struct pinctrl_dev *pctldev,
> + unsigned int selector,
> + const char * const **groups,
> + unsigned * const num_groups)
> +{
> + struct zynqmp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
> +
> + *groups = pctrl->funcs[selector].groups;
> + *num_groups = pctrl->funcs[selector].ngroups;
> +
> + return 0;
> +}
> +
> +/**
> + * zynqmp_pinmux_set_mux() - Set requested function for the group
> + * @pctldev: Pincontrol device pointer.
> + * @function: Function ID.
> + * @group: Group ID.
> + *
> + * Loop though all pins of group and call firmware API
> + * to set requested function for all pins in group.
through
of the group
in the group
> + *
> + * Return: 0 on success else error code.
> + */
> +static int zynqmp_pinmux_set_mux(struct pinctrl_dev *pctldev,
> + unsigned int function,
> + unsigned int group)
> +{
> + struct zynqmp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
> + const struct zynqmp_pctrl_group *pgrp = &pctrl->groups[group];
> + int ret, i;
> +
> + for (i = 0; i < pgrp->npins; i++) {
> + unsigned int pin = pgrp->pins[i];
> +
> + ret = zynqmp_pm_pinctrl_set_function(pin, function);
> + if (ret) {
> + dev_err(pctldev->dev, "set mux failed for pin %u\n",
> + pin);
> + return -EIO;
Same as above.
And applies to all similar cases.
> + }
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * zynqmp_pinmux_release_pin() - Release a pin
> + * @pctldev: Pincontrol device pointer.
> + * @pin: Pin number.
> + *
> + * Release a pin from firmware.
> + *
> + * Return: 0 on success else error code.
> + */
> +static int zynqmp_pinmux_release_pin(struct pinctrl_dev *pctldev,
> + unsigned int pin)
> +{
> + int ret;
> +
> + ret = zynqmp_pm_pinctrl_release(pin);
> + if (ret) {
> + dev_err(pctldev->dev, "free pin failed for pin %u\n",
> + pin);
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +static const struct pinmux_ops zynqmp_pinmux_ops = {
> + .request = zynqmp_pinmux_request_pin,
> + .get_functions_count = zynqmp_pmux_get_functions_count,
> + .get_function_name = zynqmp_pmux_get_function_name,
> + .get_function_groups = zynqmp_pmux_get_function_groups,
> + .set_mux = zynqmp_pinmux_set_mux,
> + .free = zynqmp_pinmux_release_pin,
> +};
> +
> +/**
> + * zynqmp_pinconf_cfg_get() - get config value for the pin
> + * @pctldev: Pin control device pointer.
> + * @pin: Pin number.
> + * @config: Value of config param.
> + *
> + * Get value of the requested configuration parameter for the
> + * given pin.
> + *
> + * Return: 0 on success else error code.
> + */
> +static int zynqmp_pinconf_cfg_get(struct pinctrl_dev *pctldev,
> + unsigned int pin,
> + unsigned long *config)
> +{
> + int ret;
> + unsigned int arg = 0, param = pinconf_to_config_param(*config);
Reversed xmas tree order?
Assignment of arg AFAICS is redundant.
> + if (pin >= zynqmp_desc.npins)
> + return -EOPNOTSUPP;
> +
> + switch (param) {
> + case PIN_CONFIG_SLEW_RATE:
> + param = PM_PINCTRL_CONFIG_SLEW_RATE;
> + ret = zynqmp_pm_pinctrl_get_config(pin, param, &arg);
> + break;
> + case PIN_CONFIG_BIAS_PULL_UP:
> + param = PM_PINCTRL_CONFIG_PULL_CTRL;
> + ret = zynqmp_pm_pinctrl_get_config(pin, param, &arg);
> + if (arg != PM_PINCTRL_BIAS_PULL_UP)
> + return -EINVAL;
> +
> + arg = 1;
> + break;
> + case PIN_CONFIG_BIAS_PULL_DOWN:
> + param = PM_PINCTRL_CONFIG_PULL_CTRL;
> + ret = zynqmp_pm_pinctrl_get_config(pin, param, &arg);
> + if (arg != PM_PINCTRL_BIAS_PULL_DOWN)
> + return -EINVAL;
> +
> + arg = 1;
> + break;
> + case PIN_CONFIG_BIAS_DISABLE:
> + param = PM_PINCTRL_CONFIG_BIAS_STATUS;
> + ret = zynqmp_pm_pinctrl_get_config(pin, param, &arg);
> + if (arg != PM_PINCTRL_BIAS_DISABLE)
> + return -EINVAL;
> +
> + arg = 1;
> + break;
> + case PIN_CONFIG_IOSTANDARD:
> + param = PM_PINCTRL_CONFIG_VOLTAGE_STATUS;
> + ret = zynqmp_pm_pinctrl_get_config(pin, param, &arg);
> + break;
> + case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
> + param = PM_PINCTRL_CONFIG_SCHMITT_CMOS;
> + ret = zynqmp_pm_pinctrl_get_config(pin, param, &arg);
> + break;
> + case PIN_CONFIG_DRIVE_STRENGTH:
> + param = PM_PINCTRL_CONFIG_DRIVE_STRENGTH;
> + ret = zynqmp_pm_pinctrl_get_config(pin, param, &arg);
> + switch (arg) {
> + case PM_PINCTRL_DRIVE_STRENGTH_2MA:
> + arg = DRIVE_STRENGTH_2MA;
> + break;
> + case PM_PINCTRL_DRIVE_STRENGTH_4MA:
> + arg = DRIVE_STRENGTH_4MA;
> + break;
> + case PM_PINCTRL_DRIVE_STRENGTH_8MA:
> + arg = DRIVE_STRENGTH_8MA;
> + break;
> + case PM_PINCTRL_DRIVE_STRENGTH_12MA:
> + arg = DRIVE_STRENGTH_12MA;
> + break;
> + default:
> + /* Invalid drive strength */
> + dev_warn(pctldev->dev,
> + "Invalid drive strength for pin %d\n",
> + pin);
> + return -EINVAL;
> + }
> + break;
> + default:
> + ret = -EOPNOTSUPP;
> + break;
> + }
> +
> + param = pinconf_to_config_param(*config);
> + *config = pinconf_to_config_packed(param, arg);
> +
> + return ret;
This is wrong. You have to return the error codes directly and do not
touch *config as long as error happens.
> +}
> +
> +/**
> + * zynqmp_pinconf_cfg_set() - Set requested config for the pin
> + * @pctldev: Pincontrol device pointer.
> + * @pin: Pin number.
> + * @configs: Configuration to set.
> + * @num_configs: Number of configurations.
> + *
> + * Loop though all configurations and call firmware API
through
> + * to set requested configurations for the pin.
> + *
> + * Return: 0 on success else error code.
> + */
> +static int zynqmp_pinconf_cfg_set(struct pinctrl_dev *pctldev,
> + unsigned int pin, unsigned long *configs,
> + unsigned int num_configs)
> +{
> + int i, ret;
> +
> + if (pin >= zynqmp_desc.npins)
> + return -EOPNOTSUPP;
> +
> + for (i = 0; i < num_configs; i++) {
> + unsigned int param = pinconf_to_config_param(configs[i]);
> + unsigned int arg = pinconf_to_config_argument(configs[i]);
> + 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_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(pctldev->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(pctldev->dev,
> + "Invalid IO Standard requested for pin %d\n",
> + pin);
> +
> + break;
> + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
> + case PIN_CONFIG_LOW_POWER_MODE:
> + /*
> + * These 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(pctldev->dev,
> + "unsupported configuration parameter '%u'\n",
> + param);
> + ret = -EOPNOTSUPP;
> + break;
> + }
> +
> + param = pinconf_to_config_param(configs[i]);
> + arg = pinconf_to_config_argument(configs[i]);
> + if (ret)
> + dev_warn(pctldev->dev,
> + "%s failed: pin %u param %u value %u\n",
> + __func__, pin, param, arg);
> + }
Remove all these __func__. In a properly formed (unique) message base
you don't need it. For the debugging Dynamic debug and other
mechanisms allow to get it at run-time w/o code recompilation.
> + return 0;
> +}
> +
> +/**
> + * zynqmp_pinconf_group_set() - Set requested config for the group
> + * @pctldev: Pincontrol device pointer.
> + * @selector: Group ID.
> + * @configs: Configuration to set.
> + * @num_configs: Number of configurations.
> + *
> + * Call function to set configs for each pin in group.
in the group
> + *
> + * Return: 0 on success else error code.
> + */
> +static int zynqmp_pinconf_group_set(struct pinctrl_dev *pctldev,
> + unsigned int selector,
> + unsigned long *configs,
> + unsigned int num_configs)
> +{
> + int i, ret;
> + struct zynqmp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
> + const struct zynqmp_pctrl_group *pgrp = &pctrl->groups[selector];
> +
> + for (i = 0; i < pgrp->npins; i++) {
> + ret = zynqmp_pinconf_cfg_set(pctldev, pgrp->pins[i], configs,
> + num_configs);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static const struct pinconf_ops zynqmp_pinconf_ops = {
> + .is_generic = true,
> + .pin_config_get = zynqmp_pinconf_cfg_get,
> + .pin_config_set = zynqmp_pinconf_cfg_set,
> + .pin_config_group_set = zynqmp_pinconf_group_set,
> +};
> +
> +static struct pinctrl_desc zynqmp_desc = {
> + .name = "zynqmp_pinctrl",
> + .owner = THIS_MODULE,
> + .pctlops = &zynqmp_pctrl_ops,
> + .pmxops = &zynqmp_pinmux_ops,
> + .confops = &zynqmp_pinconf_ops,
> +#ifdef CONFIG_DEBUG_FS
> + .custom_conf_items = zynqmp_conf_items,
> +#endif
> +};
> +
> +/**
> + * zynqmp_pinctrl_get_function_groups() - get groups for the function
> + * @fid: Function ID.
> + * @index: Group index.
> + * @groups: Groups data.
> + *
> + * Call firmware API to get groups for the given function.
> + *
> + * Return: 0 on success else error code.
> + */
> +static int zynqmp_pinctrl_get_function_groups(u32 fid, u32 index, u16 *groups)
> +{
> + struct zynqmp_pm_query_data qdata = {0};
> + u32 ret_payload[PAYLOAD_ARG_CNT];
ret_ is confusing. Drop it.
> + int ret;
> +
> + qdata.qid = PM_QID_PINCTRL_GET_FUNCTION_GROUPS;
> + qdata.arg1 = fid;
> + qdata.arg2 = index;
> +
> + ret = zynqmp_pm_query_data(qdata, ret_payload);
> + if (ret)
> + return ret;
> +
> + memcpy(groups, &ret_payload[1], PINCTRL_GET_FUNC_GROUPS_RESP_LEN);
> +
> + return ret;
> +}
> +
> +/**
> + * zynqmp_pinctrl_get_func_num_groups() - get number of groups in function
> + * @fid: Function ID.
> + * @ngroups: Number of groups in function.
> + *
> + * Call firmware API to get number of group in function.
> + *
> + * Return: 0 on success else error code.
> + */
> +static int zynqmp_pinctrl_get_func_num_groups(u32 fid, unsigned int *ngroups)
> +{
> + struct zynqmp_pm_query_data qdata = {0};
> + u32 ret_payload[PAYLOAD_ARG_CNT];
> + int ret;
> +
> + qdata.qid = PM_QID_PINCTRL_GET_NUM_FUNCTION_GROUPS;
> + qdata.arg1 = fid;
> +
> + ret = zynqmp_pm_query_data(qdata, ret_payload);
> + if (ret)
> + return ret;
> +
> + *ngroups = ret_payload[1];
> +
> + return ret;
> +}
> +
> +/**
> + * zynqmp_pinctrl_prepare_func_groups() - prepare function and groups data
> + * @dev: Device pointer.
> + * @fid: Function ID.
> + * @func: Function data.
> + * @groups: Groups data.
> + *
> + * Query firmware to get group IDs for each function. Firmware returns
> + * group IDs. Based on gorup index for the function, group names in
group
> + * function are stored. For example, first gorup in "eth0" function
the function
the first
group
> + * is named as "eth0_0", second as "eth0_1" and so on.
Spell check your comments.
> + *
> + * Based on group ID received from firmware, function stores name of
> + * group for that group ID. For an example, if "eth0" first group ID
> + * is x, groups[x] name will be stored as "eth0_0".
> + *
> + * Once done for each function, each function would have its group names,
> + * and each groups would also have their names.
> + *
> + * Return: 0 on success else error code.
> + */
> +static int zynqmp_pinctrl_prepare_func_groups(struct device *dev, u32 fid,
> + struct zynqmp_pmux_function *func,
> + struct zynqmp_pctrl_group *groups)
> +{
> + u16 resp[NUM_GROUPS_PER_RESP] = {0};
> + const char **fgroups;
> + int ret = 0, index, i;
> + fgroups = devm_kzalloc(dev, sizeof(*fgroups) * func->ngroups,
> + GFP_KERNEL);
One line
> + 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;
> +
> + fgroups[index + i] = devm_kasprintf(dev, GFP_KERNEL,
> + "%s_%d_grp",
> + func->name,
> + index + i);
> + groups[resp[i]].name = devm_kasprintf(dev, GFP_KERNEL,
> + "%s_%d_grp",
> + func->name,
> + index + i);
No NULL checks?!
> + }
> + }
> +done:
> + func->groups = fgroups;
> +
> + return ret;
> +}
> +
> +/**
> + * zynqmp_pinctrl_get_function_name() - get function name
> + * @fid: Function ID.
> + * @name: Function name
> + *
> + * Call firmware API to get name of given function.
> + */
> +static void zynqmp_pinctrl_get_function_name(u32 fid, char *name)
> +{
> + struct zynqmp_pm_query_data qdata = {0};
> + u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> + qdata.qid = PM_QID_PINCTRL_GET_FUNCTION_NAME;
> + qdata.arg1 = fid;
> +
> + /*
> + * Name of the function is maximum 16 bytes and cannot
> + * accommodate the return value in SMC buffers, hence ignoring
> + * the return value for this specific qid.
> + */
> + zynqmp_pm_query_data(qdata, ret_payload);
> + memcpy(name, ret_payload, PINCTRL_GET_FUNC_NAME_RESP_LEN);
> +}
> +
> +/**
> + * zynqmp_pinctrl_get_num_functions() - get number of supported functions
> + * @nfuncs: Number of functions.
> + *
> + * Call firmware API to get number of functions supported by system/board.
> + *
> + * Return: 0 on success else error code.
> + */
> +static int zynqmp_pinctrl_get_num_functions(unsigned int *nfuncs)
> +{
> + struct zynqmp_pm_query_data qdata = {0};
> + u32 ret_payload[PAYLOAD_ARG_CNT];
Drop in all cases those redundant prefixes.
> + int ret;
> +
> + qdata.qid = PM_QID_PINCTRL_GET_NUM_FUNCTIONS;
> +
> + ret = zynqmp_pm_query_data(qdata, ret_payload);
> + if (ret)
> + return ret;
> +
> + *nfuncs = ret_payload[1];
> +
> + return ret;
> +}
> +
> +/**
> + * zynqmp_pinctrl_get_pin_groups() - get groups for the pin
> + * @pin: Pin number.
> + * @index: Group index.
> + * @groups: Groups data.
> + *
> + * Call firmware API to get groups for the given pin.
> + *
> + * Return: 0 on success else error code.
> + */
> +static int zynqmp_pinctrl_get_pin_groups(u32 pin, u32 index, u16 *groups)
> +{
> + struct zynqmp_pm_query_data qdata = {0};
> + u32 ret_payload[PAYLOAD_ARG_CNT];
> + int ret;
> +
> + qdata.qid = PM_QID_PINCTRL_GET_PIN_GROUPS;
> + qdata.arg1 = pin;
> + qdata.arg2 = index;
> +
> + ret = zynqmp_pm_query_data(qdata, ret_payload);
> + if (ret)
> + return ret;
> +
> + memcpy(groups, &ret_payload[1], PINCTRL_GET_PIN_GROUPS_RESP_LEN);
> +
> + return ret;
> +}
> +
> +/**
> + * zynqmp_pinctrl_group_add_pin() - add pin to given group
> + * @group: Group data.
> + * @pin: Pin number.
> + *
> + * Add pin number to respective group's pin array at end and
> + * increment pin count for the group.
> + */
> +static void zynqmp_pinctrl_group_add_pin(struct zynqmp_pctrl_group *group,
> + unsigned int pin)
> +{
> + group->pins[group->npins++] = pin;
> +}
> +
> +/**
> + * zynqmp_pinctrl_create_pin_groups() - assign pins to respective groups
> + * @dev: Device pointer.
> + * @groups: Groups data.
> + * @pin: Pin number.
> + *
> + * Query firmware to get groups available for the given pin.
> + * Based on firmware response(group IDs for the pin), add
> + * pin number to respective group's pin array.
> + *
> + * Once all pins are queries, each groups would have its number
> + * of pins and pin numbers data.
> + *
> + * Return: 0 on success else error code.
> + */
> +static int zynqmp_pinctrl_create_pin_groups(struct device *dev,
> + struct zynqmp_pctrl_group *groups,
> + unsigned int pin)
> +{
> + int ret, i, index = 0;
> + u16 resp[NUM_GROUPS_PER_RESP] = {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)
> + return ret;
> +
> + if (resp[i] == (u16)RESERVED_GROUP)
> + continue;
In the entire driver remove all explicit castings where it's not
needed, like above. If something is not okay with it, fix the root
cause.
> + zynqmp_pinctrl_group_add_pin(&groups[resp[i]], pin);
> + }
> + index += NUM_GROUPS_PER_RESP;
> + } while (1);
Try to refactor to avoid infinite loops.
> +
> + return ret;
> +}
> +
> +/**
> + * zynqmp_pinctrl_prepare_group_pins() - prepare each group's pin data
> + * @dev: Device pointer.
> + * @groups: Groups data.
> + * @ngroups: Number of groups.
> + *
> + * Prepare pin number and number of pins data for each pins.
> + *
> + * Return: 0 on success else error code.
> + */
> +static int zynqmp_pinctrl_prepare_group_pins(struct device *dev,
> + struct zynqmp_pctrl_group *groups,
> + unsigned int ngroups)
> +{
> + unsigned int pin;
> + int ret = 0;
Redundant assignment.
> + for (pin = 0; pin < zynqmp_desc.npins; pin++) {
> + ret = zynqmp_pinctrl_create_pin_groups(dev, groups, pin);
> + if (ret)
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +/**
> + * zynqmp_pinctrl_prepare_function_info() - prepare function info
> + * @dev: Device pointer.
> + * @pctrl: Pin control driver data.
> + *
> + * Query firmware for functions, groups and pin information and
> + * prepare pin control driver data.
> + *
> + * Query number of functions and number of function groups (number
> + * of groups in given function) to allocate required memory buffers
> + * for functions and groups. Once buffers are allocated to store
> + * functions and groups data, query and store required information
> + * (numbe of groups and group names for each function, number of
number
> + * pins and pin numbers for each group).
> + *
> + * Return: 0 on success else error code.
> + */
> +static int zynqmp_pinctrl_prepare_function_info(struct device *dev,
> + struct zynqmp_pinctrl *pctrl)
> +{
> + struct zynqmp_pmux_function *funcs;
> + struct zynqmp_pctrl_group *groups;
> + int ret, i;
> +
> + ret = zynqmp_pinctrl_get_num_functions(&pctrl->nfuncs);
> + if (ret)
> + return ret;
> +
> + funcs = devm_kzalloc(dev, sizeof(*funcs) * pctrl->nfuncs, GFP_KERNEL);
> + if (!funcs)
> + return -ENOMEM;
> +
> + for (i = 0; i < pctrl->nfuncs; i++) {
> + zynqmp_pinctrl_get_function_name(i, funcs[i].name);
> +
> + ret = zynqmp_pinctrl_get_func_num_groups(i, &funcs[i].ngroups);
> + if (ret)
> + return ret;
> +
> + pctrl->ngroups += funcs[i].ngroups;
> + }
> + groups = devm_kzalloc(dev, sizeof(*groups) * pctrl->ngroups,
> + GFP_KERNEL);
One line.
> + if (!groups)
> + return -ENOMEM;
> +
> + for (i = 0; i < pctrl->nfuncs; i++) {
> + ret = zynqmp_pinctrl_prepare_func_groups(dev, i, &funcs[i],
> + groups);
> + if (ret)
> + return ret;
> + }
> +
> + ret = zynqmp_pinctrl_prepare_group_pins(dev, groups, pctrl->ngroups);
> + if (ret)
> + return ret;
> +
> + pctrl->funcs = funcs;
> + pctrl->groups = groups;
> +
> + return ret;
> +}
> +
> +/**
> + * zynqmp_pinctrl_get_num_pins() - get number of pins in system
> + * @npins: Number of pins in system/board.
> + *
> + * Call firmware API to get number of pins.
> + *
> + * Return: 0 on success else error code.
> + */
> +static int zynqmp_pinctrl_get_num_pins(unsigned int *npins)
> +{
> + struct zynqmp_pm_query_data qdata = {0};
> + u32 ret_payload[PAYLOAD_ARG_CNT];
> + int ret;
> +
> + qdata.qid = PM_QID_PINCTRL_GET_NUM_PINS;
> +
> + ret = zynqmp_pm_query_data(qdata, ret_payload);
> + if (ret)
> + return ret;
> +
> + *npins = ret_payload[1];
> +
> + return ret;
> +}
> +
> +/**
> + * zynqmp_pinctrl_prepare_pin_desc() - prepare pin description info
> + * @dev: Device pointer.
> + * @zynqmp_pins: Pin information.
> + * @npins: Number of pins.
> + *
> + * Query number of pins information from firmware and prepare pin
> + * description containing pin number and pin name.
> + *
> + * Return: 0 on success else error code.
> + */
> +static int zynqmp_pinctrl_prepare_pin_desc(struct device *dev,
> + const struct pinctrl_pin_desc
> + **zynqmp_pins,
> + unsigned int *npins)
> +{
> + struct pinctrl_pin_desc *pins, *pin;
> + int ret;
> + int i;
> +
> + ret = zynqmp_pinctrl_get_num_pins(npins);
> + if (ret)
> + return ret;
> +
> + pins = devm_kzalloc(dev, sizeof(*pins) * *npins, GFP_KERNEL);
> + if (!pins)
> + return -ENOMEM;
> +
> + for (i = 0; i < *npins; i++) {
> + pin = &pins[i];
> + pin->number = i;
> + pin->name = devm_kasprintf(dev, GFP_KERNEL, "%s%d",
> + ZYNQMP_PIN_PREFIX, i);
no NULL check?!
> + }
> +
> + *zynqmp_pins = pins;
> +
> + return 0;
> +}
> +
> +static int zynqmp_pinctrl_probe(struct platform_device *pdev)
> +{
> + struct zynqmp_pinctrl *pctrl;
> + int ret;
> +
> + pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL);
> + if (!pctrl)
> + return -ENOMEM;
> +
> + ret = zynqmp_pinctrl_prepare_pin_desc(&pdev->dev,
> + &zynqmp_desc.pins,
> + &zynqmp_desc.npins);
> + if (ret) {
> + dev_err(&pdev->dev, "%s() pin desc prepare fail with %d\n",
> + __func__, ret);
> + return ret;
> + }
> +
> + ret = zynqmp_pinctrl_prepare_function_info(&pdev->dev, pctrl);
> + if (ret) {
> + dev_err(&pdev->dev, "%s() function info prepare fail with %d\n",
> + __func__, ret);
> + return ret;
> + }
> +
> + pctrl->pctrl = pinctrl_register(&zynqmp_desc, &pdev->dev, pctrl);
> + if (IS_ERR(pctrl->pctrl)) {
> + ret = PTR_ERR(pctrl->pctrl);
> + return ret;
Fold it and drop {}.
> + }
> +
> + platform_set_drvdata(pdev, pctrl);
> + dev_info(&pdev->dev, "zynqmp pinctrl initialized\n");
Unneeded noise.
> + return ret;
> +}
> +
> +static const struct of_device_id zynqmp_pinctrl_of_match[] = {
> + { .compatible = "xlnx,zynqmp-pinctrl" },
> + { }
> +};
> +
> +static struct platform_driver zynqmp_pinctrl_driver = {
> + .driver = {
> + .name = "zynqmp-pinctrl",
> + .of_match_table = zynqmp_pinctrl_of_match,
> + },
> + .probe = zynqmp_pinctrl_probe,
> +};
> +builtin_platform_driver(zynqmp_pinctrl_driver);
I believe the code base can be easily shrinked by 10%. So, next
version I would expect something like < 1000 LOCs in the stat.
--
With Best Regards,
Andy Shevchenko
Hi Andy Shevchenko,
Thanks for the review.
> -----Original Message-----
> From: Andy Shevchenko <[email protected]>
> Sent: Wednesday, March 17, 2021 6:26 PM
> To: Sai Krishna Potthuri <[email protected]>
> Cc: Linus Walleij <[email protected]>; Rob Herring
> <[email protected]>; Michal Simek <[email protected]>; Greg Kroah-
> Hartman <[email protected]>; linux-arm Mailing List <linux-arm-
> [email protected]>; Linux Kernel Mailing List <linux-
> [email protected]>; devicetree <[email protected]>; open
> list:GPIO SUBSYSTEM <[email protected]>; git <[email protected]>;
> [email protected]
> Subject: Re: [PATCH v4 3/3] pinctrl: Add Xilinx ZynqMP pinctrl driver support
>
> On Wed, Mar 17, 2021 at 10:27 AM Sai Krishna Potthuri
> <[email protected]> wrote:
> >
> > Adding pinctrl driver for Xilinx ZynqMP platform.
> > This driver queries pin information from firmware and registers pin
> > control accordingly.
> >
> > Signed-off-by: Sai Krishna Potthuri
> > <[email protected]>
> > ---
> > drivers/pinctrl/Kconfig | 13 +
> > drivers/pinctrl/Makefile | 1 +
> > drivers/pinctrl/pinctrl-zynqmp.c | 1030
> > ++++++++++++++++++++++++++++++
> > 3 files changed, 1044 insertions(+)
> > create mode 100644 drivers/pinctrl/pinctrl-zynqmp.c
> >
> > diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index
> > 815095326e2d..25d3c7208975 100644
> > --- a/drivers/pinctrl/Kconfig
> > +++ b/drivers/pinctrl/Kconfig
> > @@ -341,6 +341,19 @@ config PINCTRL_ZYNQ
> > help
> > This selects the pinctrl driver for Xilinx Zynq.
> >
> > +config PINCTRL_ZYNQMP
> > + bool "Pinctrl driver for Xilinx ZynqMP"
>
> Why not module?
As most of the Xilinx drivers depending on the pin controller driver for
configuring the MIO pins, we are not supporting to build this driver as
a module.
>
> > + depends on ARCH_ZYNQMP
> > + select PINMUX
> > + select GENERIC_PINCONF
> > + help
> > + This selects the pinctrl driver for Xilinx ZynqMP platform.
> > + This driver will query the pin information from the firmware
> > + and allow configuring the pins.
> > + Configuration can include the mux function to select on those
> > + pin(s)/group(s), and various pin configuration parameters
> > + such as pull-up, slew rate, etc.
> > +
> > config PINCTRL_INGENIC
> > bool "Pinctrl driver for the Ingenic JZ47xx SoCs"
> > default MACH_INGENIC
> > diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index
> > f53933b2ff02..7e058739f0d5 100644
> > --- a/drivers/pinctrl/Makefile
> > +++ b/drivers/pinctrl/Makefile
> > @@ -43,6 +43,7 @@ obj-$(CONFIG_PINCTRL_TB10X) += pinctrl-tb10x.o
> > obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o
> > obj-$(CONFIG_PINCTRL_STMFX) += pinctrl-stmfx.o
> > obj-$(CONFIG_PINCTRL_ZYNQ) += pinctrl-zynq.o
> > +obj-$(CONFIG_PINCTRL_ZYNQMP) += pinctrl-zynqmp.o
> > obj-$(CONFIG_PINCTRL_INGENIC) += pinctrl-ingenic.o
> > obj-$(CONFIG_PINCTRL_RK805) += pinctrl-rk805.o
> > obj-$(CONFIG_PINCTRL_OCELOT) += pinctrl-ocelot.o
> > diff --git a/drivers/pinctrl/pinctrl-zynqmp.c
> > b/drivers/pinctrl/pinctrl-zynqmp.c
> > new file mode 100644
> > index 000000000000..63edde400e85
> > --- /dev/null
> > +++ b/drivers/pinctrl/pinctrl-zynqmp.c
> > @@ -0,0 +1,1030 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * ZynqMP pin controller
> > + *
> > + * Copyright (C) 2020 Xilinx, Inc.
> > + *
> > + * Sai Krishna Potthuri <[email protected]>
> > + * Rajan Vaja <[email protected]>
> > + */
> > +
> > +#include <linux/init.h>
> > +#include <linux/of_address.h>
>
> > +#include <linux/pinctrl/pinmux.h>
> > +#include <linux/pinctrl/pinconf-generic.h>
>
> Can you move this group of headers after linux/* ?
>
> > +#include <dt-bindings/pinctrl/pinctrl-zynqmp.h>
>
> Can it be moved outside of these headers
>
> > +#include <linux/platform_device.h>
> > +#include <linux/firmware/xlnx-zynqmp.h>
>
> Ordering (for all groups of headers)?
Ok, I will order the headers in the below order
#include <linux/*>
#include <linux/firmware/xlnx-zynqmp.h>
#include <linux/pinctrl/*>
#include <dt-bindings/pinctrl/pinctrl-zynqmp.h>
>
> > +#include "core.h"
> > +#include "pinctrl-utils.h"
> > +
> > +#define ZYNQMP_PIN_PREFIX "MIO"
> > +#define PINCTRL_GET_FUNC_NAME_RESP_LEN 16
> > +#define MAX_FUNC_NAME_LEN 16
> > +#define MAX_GROUP_PIN 50
> > +#define END_OF_FUNCTIONS "END_OF_FUNCTIONS"
> > +#define NUM_GROUPS_PER_RESP 6
> > +
> > +#define PINCTRL_GET_FUNC_GROUPS_RESP_LEN 12
> > +#define PINCTRL_GET_PIN_GROUPS_RESP_LEN 12
> > +#define NA_GROUP -1
> > +#define RESERVED_GROUP -2
> > +
> > +#define DRIVE_STRENGTH_2MA 2
> > +#define DRIVE_STRENGTH_4MA 4
> > +#define DRIVE_STRENGTH_8MA 8
> > +#define DRIVE_STRENGTH_12MA 12
> > +
> > +/**
> > + * 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
>
> > + * @node:` Firmware node matching with for function
>
> Typo
>
> > + *
> > + * 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_pinctrl - driver data
> > + * @pctrl: Pinctrl device
>
> Pin control
>
> > + * @groups: Pingroups
>
> Pin groups
>
> > + * @ngroups: Number of @groups
> > + * @funcs: Pinmux functions
>
> Pin mux functions
I will fix all of them in the next version.
>
> > + * @nfuncs: Number of @funcs
> > + *
> > + * This struct is stored as driver data and used to retrieve
> > + * information regarding pin control functions, groups and
> > + * group pins.
> > + */
> > +struct zynqmp_pinctrl {
> > + struct pinctrl_dev *pctrl;
> > + const struct zynqmp_pctrl_group *groups;
> > + unsigned int ngroups;
> > + const struct zynqmp_pmux_function *funcs;
> > + unsigned int nfuncs;
> > +};
> > +
> > +/**
> > + * 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;
> > +};
> > +
> > +/**
> > + * 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
> > + */
> > +enum zynqmp_pin_config_param {
> > + PIN_CONFIG_IOSTANDARD = PIN_CONFIG_END + 1, };
>
> I'm lost here. What is IO standard exactly? Why it can't be in generic
> headers?
It represents LVCMOS 3.3 volts/ LVCMOS 1.8 volts.
Since this is specific to Xilinx ZynqMP platform, considered to be added in
the driver file.
>
> > +static const struct pinconf_generic_params zynqmp_dt_params[] = {
> > + {"io-standard", PIN_CONFIG_IOSTANDARD,
> IO_STANDARD_LVCMOS18},
> > +};
> > +
> > +#ifdef CONFIG_DEBUG_FS
> > +static const struct
> > +pin_config_item zynqmp_conf_items[ARRAY_SIZE(zynqmp_dt_params)] =
> {
> > + PCONFDUMP(PIN_CONFIG_IOSTANDARD, "io-standard", NULL, true),
> > +}; #endif
> > +
> > +static struct pinctrl_desc zynqmp_desc;
> > +
> > +/**
> > + * zynqmp_pctrl_get_groups_count() - get group count
> > + * @pctldev: Pincontrol device pointer.
> > + *
> > + * Get total groups count.
> > + *
> > + * Return: group count.
> > + */
> > +static int zynqmp_pctrl_get_groups_count(struct pinctrl_dev *pctldev)
> > +{
> > + struct zynqmp_pinctrl *pctrl =
> > +pinctrl_dev_get_drvdata(pctldev);
> > +
> > + return pctrl->ngroups;
> > +}
> > +
> > +/**
> > + * zynqmp_pctrl_get_group_name() - get group name
> > + * @pctldev: Pincontrol device pointer.
> > + * @selector: Group ID.
>
> > + *
> > + * Get gorup's name.
> > + *
> > + * Return: group name.
>
> Isn't too much duplication without any added value for the reader?
I will remove the kernel doc for the obvious ones in the next series.
>
> > + */
> > +static const char *zynqmp_pctrl_get_group_name(struct pinctrl_dev
> *pctldev,
> > + unsigned int selector)
> > +{
> > + struct zynqmp_pinctrl *pctrl =
> > +pinctrl_dev_get_drvdata(pctldev);
> > +
> > + return pctrl->groups[selector].name; }
> > +
> > +/**
> > + * zynqmp_pctrl_get_group_pins() - get group pins
> > + * @pctldev: Pincontrol device pointer.
> > + * @selector: Group ID.
> > + * @pins: Pin numbers.
> > + * @npins: Number of pins in group.
> > + *
> > + * Get gorup's pin count and pin number.
>
> > + * Return: Success.
>
> Clean up all your kernel docs from noise like this.
I will remove the documentation for the obvious ones in the next
series.
>
> > + */
> > +static int zynqmp_pctrl_get_group_pins(struct pinctrl_dev *pctldev,
> > + unsigned int selector,
> > + const unsigned int **pins,
> > + unsigned int *npins) {
> > + struct zynqmp_pinctrl *pctrl =
> > +pinctrl_dev_get_drvdata(pctldev);
> > +
> > + *pins = pctrl->groups[selector].pins;
> > + *npins = pctrl->groups[selector].npins;
> > +
> > + return 0;
> > +}
> > +
> > +static const struct pinctrl_ops zynqmp_pctrl_ops = {
> > + .get_groups_count = zynqmp_pctrl_get_groups_count,
> > + .get_group_name = zynqmp_pctrl_get_group_name,
> > + .get_group_pins = zynqmp_pctrl_get_group_pins,
> > + .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
> > + .dt_free_map = pinctrl_utils_free_map, };
> > +
> > +/**
> > + * zynqmp_pinmux_request_pin() - Request a pin for muxing
> > + * @pctldev: Pincontrol device pointer.
> > + * @pin: Pin number.
> > + *
> > + * Request a pin from firmware for muxing.
> > + *
> > + * Return: 0 on success else error code.
> > + */
> > +static int zynqmp_pinmux_request_pin(struct pinctrl_dev *pctldev,
> > + unsigned int pin) {
> > + int ret;
> > +
> > + ret = zynqmp_pm_pinctrl_request(pin);
> > + if (ret) {
> > + dev_err(pctldev->dev, "request failed for pin %u\n",
> > + pin);
>
> > + return -EIO;
>
> Why shadowing error code? Since it's the only possible error, why is it not
> reflected in the kernel doc?
I will update the kernel doc with the error value for such cases.
>
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * zynqmp_pmux_get_functions_count() - get number of functions
> > + * @pctldev: Pincontrol device pointer.
> > + *
> > + * Get total function count.
> > + *
> > + * Return: function count.
> > + */
> > +static int zynqmp_pmux_get_functions_count(struct pinctrl_dev
> > +*pctldev) {
> > + struct zynqmp_pinctrl *pctrl =
> > +pinctrl_dev_get_drvdata(pctldev);
> > +
> > + return pctrl->nfuncs;
> > +}
> > +
> > +/**
> > + * zynqmp_pmux_get_function_name() - get function name
> > + * @pctldev: Pincontrol device pointer.
> > + * @selector: Function ID.
> > + *
> > + * Get function's name.
> > + *
> > + * Return: function name.
> > + */
> > +static const char *zynqmp_pmux_get_function_name(struct pinctrl_dev
> *pctldev,
> > + unsigned int
> > +selector) {
> > + struct zynqmp_pinctrl *pctrl =
> > +pinctrl_dev_get_drvdata(pctldev);
> > +
> > + return pctrl->funcs[selector].name; }
> > +
> > +/**
> > + * zynqmp_pmux_get_function_groups() - Get groups for the function
> > + * @pctldev: Pincontrol device pointer.
> > + * @selector: Function ID
> > + * @groups: Group names.
> > + * @num_groups: Number of function groups.
> > + *
> > + * Get function's group count and group names.
> > + *
> > + * Return: Success.
> > + */
> > +static int zynqmp_pmux_get_function_groups(struct pinctrl_dev *pctldev,
> > + unsigned int selector,
> > + const char * const **groups,
> > + unsigned * const
> > +num_groups) {
> > + struct zynqmp_pinctrl *pctrl =
> > +pinctrl_dev_get_drvdata(pctldev);
> > +
> > + *groups = pctrl->funcs[selector].groups;
> > + *num_groups = pctrl->funcs[selector].ngroups;
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * zynqmp_pinmux_set_mux() - Set requested function for the group
> > + * @pctldev: Pincontrol device pointer.
> > + * @function: Function ID.
> > + * @group: Group ID.
> > + *
> > + * Loop though all pins of group and call firmware API
> > + * to set requested function for all pins in group.
>
> through
> of the group
> in the group
>
> > + *
> > + * Return: 0 on success else error code.
> > + */
> > +static int zynqmp_pinmux_set_mux(struct pinctrl_dev *pctldev,
> > + unsigned int function,
> > + unsigned int group) {
> > + struct zynqmp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
> > + const struct zynqmp_pctrl_group *pgrp = &pctrl->groups[group];
> > + int ret, i;
> > +
> > + for (i = 0; i < pgrp->npins; i++) {
> > + unsigned int pin = pgrp->pins[i];
> > +
> > + ret = zynqmp_pm_pinctrl_set_function(pin, function);
> > + if (ret) {
> > + dev_err(pctldev->dev, "set mux failed for pin %u\n",
> > + pin);
>
> > + return -EIO;
>
> Same as above.
> And applies to all similar cases.
>
> > + }
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * zynqmp_pinmux_release_pin() - Release a pin
> > + * @pctldev: Pincontrol device pointer.
> > + * @pin: Pin number.
> > + *
> > + * Release a pin from firmware.
> > + *
> > + * Return: 0 on success else error code.
> > + */
> > +static int zynqmp_pinmux_release_pin(struct pinctrl_dev *pctldev,
> > + unsigned int pin) {
> > + int ret;
> > +
> > + ret = zynqmp_pm_pinctrl_release(pin);
> > + if (ret) {
> > + dev_err(pctldev->dev, "free pin failed for pin %u\n",
> > + pin);
> > + return -EIO;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static const struct pinmux_ops zynqmp_pinmux_ops = {
> > + .request = zynqmp_pinmux_request_pin,
> > + .get_functions_count = zynqmp_pmux_get_functions_count,
> > + .get_function_name = zynqmp_pmux_get_function_name,
> > + .get_function_groups = zynqmp_pmux_get_function_groups,
> > + .set_mux = zynqmp_pinmux_set_mux,
> > + .free = zynqmp_pinmux_release_pin, };
> > +
> > +/**
> > + * zynqmp_pinconf_cfg_get() - get config value for the pin
> > + * @pctldev: Pin control device pointer.
> > + * @pin: Pin number.
> > + * @config: Value of config param.
> > + *
> > + * Get value of the requested configuration parameter for the
> > + * given pin.
> > + *
> > + * Return: 0 on success else error code.
> > + */
> > +static int zynqmp_pinconf_cfg_get(struct pinctrl_dev *pctldev,
> > + unsigned int pin,
> > + unsigned long *config) {
> > + int ret;
> > + unsigned int arg = 0, param =
> > +pinconf_to_config_param(*config);
>
> Reversed xmas tree order?
> Assignment of arg AFAICS is redundant.
I will update this in the next version.
>
> > + if (pin >= zynqmp_desc.npins)
> > + return -EOPNOTSUPP;
> > +
> > + switch (param) {
> > + case PIN_CONFIG_SLEW_RATE:
> > + param = PM_PINCTRL_CONFIG_SLEW_RATE;
> > + ret = zynqmp_pm_pinctrl_get_config(pin, param, &arg);
> > + break;
> > + case PIN_CONFIG_BIAS_PULL_UP:
> > + param = PM_PINCTRL_CONFIG_PULL_CTRL;
> > + ret = zynqmp_pm_pinctrl_get_config(pin, param, &arg);
> > + if (arg != PM_PINCTRL_BIAS_PULL_UP)
> > + return -EINVAL;
> > +
> > + arg = 1;
> > + break;
> > + case PIN_CONFIG_BIAS_PULL_DOWN:
> > + param = PM_PINCTRL_CONFIG_PULL_CTRL;
> > + ret = zynqmp_pm_pinctrl_get_config(pin, param, &arg);
> > + if (arg != PM_PINCTRL_BIAS_PULL_DOWN)
> > + return -EINVAL;
> > +
> > + arg = 1;
> > + break;
> > + case PIN_CONFIG_BIAS_DISABLE:
> > + param = PM_PINCTRL_CONFIG_BIAS_STATUS;
> > + ret = zynqmp_pm_pinctrl_get_config(pin, param, &arg);
> > + if (arg != PM_PINCTRL_BIAS_DISABLE)
> > + return -EINVAL;
> > +
> > + arg = 1;
> > + break;
> > + case PIN_CONFIG_IOSTANDARD:
> > + param = PM_PINCTRL_CONFIG_VOLTAGE_STATUS;
> > + ret = zynqmp_pm_pinctrl_get_config(pin, param, &arg);
> > + break;
> > + case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
> > + param = PM_PINCTRL_CONFIG_SCHMITT_CMOS;
> > + ret = zynqmp_pm_pinctrl_get_config(pin, param, &arg);
> > + break;
> > + case PIN_CONFIG_DRIVE_STRENGTH:
> > + param = PM_PINCTRL_CONFIG_DRIVE_STRENGTH;
> > + ret = zynqmp_pm_pinctrl_get_config(pin, param, &arg);
> > + switch (arg) {
> > + case PM_PINCTRL_DRIVE_STRENGTH_2MA:
> > + arg = DRIVE_STRENGTH_2MA;
> > + break;
> > + case PM_PINCTRL_DRIVE_STRENGTH_4MA:
> > + arg = DRIVE_STRENGTH_4MA;
> > + break;
> > + case PM_PINCTRL_DRIVE_STRENGTH_8MA:
> > + arg = DRIVE_STRENGTH_8MA;
> > + break;
> > + case PM_PINCTRL_DRIVE_STRENGTH_12MA:
> > + arg = DRIVE_STRENGTH_12MA;
> > + break;
> > + default:
> > + /* Invalid drive strength */
> > + dev_warn(pctldev->dev,
> > + "Invalid drive strength for pin %d\n",
> > + pin);
> > + return -EINVAL;
> > + }
> > + break;
> > + default:
> > + ret = -EOPNOTSUPP;
> > + break;
> > + }
> > +
> > + param = pinconf_to_config_param(*config);
> > + *config = pinconf_to_config_packed(param, arg);
> > +
> > + return ret;
>
> This is wrong. You have to return the error codes directly and do not touch
> *config as long as error happens.
I will update the *config and param under if (!ret) condition.
>
> > +}
> > +
> > +/**
> > + * zynqmp_pinconf_cfg_set() - Set requested config for the pin
> > + * @pctldev: Pincontrol device pointer.
> > + * @pin: Pin number.
> > + * @configs: Configuration to set.
> > + * @num_configs: Number of configurations.
> > + *
>
> > + * Loop though all configurations and call firmware API
>
> through
>
> > + * to set requested configurations for the pin.
> > + *
> > + * Return: 0 on success else error code.
> > + */
> > +static int zynqmp_pinconf_cfg_set(struct pinctrl_dev *pctldev,
> > + unsigned int pin, unsigned long *configs,
> > + unsigned int num_configs) {
> > + int i, ret;
> > +
> > + if (pin >= zynqmp_desc.npins)
> > + return -EOPNOTSUPP;
> > +
> > + for (i = 0; i < num_configs; i++) {
> > + unsigned int param = pinconf_to_config_param(configs[i]);
> > + unsigned int arg = pinconf_to_config_argument(configs[i]);
> > + 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_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(pctldev->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(pctldev->dev,
> > + "Invalid IO Standard requested for pin %d\n",
> > + pin);
> > +
> > + break;
> > + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
> > + case PIN_CONFIG_LOW_POWER_MODE:
> > + /*
> > + * These 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(pctldev->dev,
> > + "unsupported configuration parameter '%u'\n",
> > + param);
> > + ret = -EOPNOTSUPP;
> > + break;
> > + }
> > +
> > + param = pinconf_to_config_param(configs[i]);
> > + arg = pinconf_to_config_argument(configs[i]);
> > + if (ret)
> > + dev_warn(pctldev->dev,
> > + "%s failed: pin %u param %u value %u\n",
> > + __func__, pin, param, arg);
> > + }
>
> Remove all these __func__. In a properly formed (unique) message base you
> don't need it. For the debugging Dynamic debug and other mechanisms allow
> to get it at run-time w/o code recompilation.
I will remove using '__func__' in the next series.
>
> > + return 0;
> > +}
> > +
> > +/**
> > + * zynqmp_pinconf_group_set() - Set requested config for the group
> > + * @pctldev: Pincontrol device pointer.
> > + * @selector: Group ID.
> > + * @configs: Configuration to set.
> > + * @num_configs: Number of configurations.
> > + *
> > + * Call function to set configs for each pin in group.
>
> in the group
I will update in next series.
>
> > + *
> > + * Return: 0 on success else error code.
> > + */
> > +static int zynqmp_pinconf_group_set(struct pinctrl_dev *pctldev,
> > + unsigned int selector,
> > + unsigned long *configs,
> > + unsigned int num_configs) {
> > + int i, ret;
> > + struct zynqmp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
> > + const struct zynqmp_pctrl_group *pgrp =
> > +&pctrl->groups[selector];
> > +
> > + for (i = 0; i < pgrp->npins; i++) {
> > + ret = zynqmp_pinconf_cfg_set(pctldev, pgrp->pins[i], configs,
> > + num_configs);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static const struct pinconf_ops zynqmp_pinconf_ops = {
> > + .is_generic = true,
> > + .pin_config_get = zynqmp_pinconf_cfg_get,
> > + .pin_config_set = zynqmp_pinconf_cfg_set,
> > + .pin_config_group_set = zynqmp_pinconf_group_set, };
> > +
> > +static struct pinctrl_desc zynqmp_desc = {
> > + .name = "zynqmp_pinctrl",
> > + .owner = THIS_MODULE,
> > + .pctlops = &zynqmp_pctrl_ops,
> > + .pmxops = &zynqmp_pinmux_ops,
> > + .confops = &zynqmp_pinconf_ops, #ifdef CONFIG_DEBUG_FS
> > + .custom_conf_items = zynqmp_conf_items, #endif };
> > +
> > +/**
> > + * zynqmp_pinctrl_get_function_groups() - get groups for the function
> > + * @fid: Function ID.
> > + * @index: Group index.
> > + * @groups: Groups data.
> > + *
> > + * Call firmware API to get groups for the given function.
> > + *
> > + * Return: 0 on success else error code.
> > + */
> > +static int zynqmp_pinctrl_get_function_groups(u32 fid, u32 index, u16
> > +*groups) {
> > + struct zynqmp_pm_query_data qdata = {0};
> > + u32 ret_payload[PAYLOAD_ARG_CNT];
>
> ret_ is confusing. Drop it.
Ok, will just drop 'ret_' prefix and simply use paylod in next version.
>
> > + int ret;
> > +
> > + qdata.qid = PM_QID_PINCTRL_GET_FUNCTION_GROUPS;
> > + qdata.arg1 = fid;
> > + qdata.arg2 = index;
> > +
> > + ret = zynqmp_pm_query_data(qdata, ret_payload);
> > + if (ret)
> > + return ret;
> > +
> > + memcpy(groups, &ret_payload[1],
> > + PINCTRL_GET_FUNC_GROUPS_RESP_LEN);
> > +
> > + return ret;
> > +}
> > +
> > +/**
> > + * zynqmp_pinctrl_get_func_num_groups() - get number of groups in
> function
> > + * @fid: Function ID.
> > + * @ngroups: Number of groups in function.
> > + *
> > + * Call firmware API to get number of group in function.
> > + *
> > + * Return: 0 on success else error code.
> > + */
> > +static int zynqmp_pinctrl_get_func_num_groups(u32 fid, unsigned int
> > +*ngroups) {
> > + struct zynqmp_pm_query_data qdata = {0};
> > + u32 ret_payload[PAYLOAD_ARG_CNT];
> > + int ret;
> > +
> > + qdata.qid = PM_QID_PINCTRL_GET_NUM_FUNCTION_GROUPS;
> > + qdata.arg1 = fid;
> > +
> > + ret = zynqmp_pm_query_data(qdata, ret_payload);
> > + if (ret)
> > + return ret;
> > +
> > + *ngroups = ret_payload[1];
> > +
> > + return ret;
> > +}
> > +
> > +/**
> > + * zynqmp_pinctrl_prepare_func_groups() - prepare function and groups
> data
> > + * @dev: Device pointer.
> > + * @fid: Function ID.
> > + * @func: Function data.
> > + * @groups: Groups data.
> > + *
> > + * Query firmware to get group IDs for each function. Firmware
> > +returns
> > + * group IDs. Based on gorup index for the function, group names in
>
> group
>
> > + * function are stored. For example, first gorup in "eth0" function
>
> the function
> the first
> group
>
> > + * is named as "eth0_0", second as "eth0_1" and so on.
>
> Spell check your comments.
Will check and fix in the next version.
>
> > + *
> > + * Based on group ID received from firmware, function stores name of
> > + * group for that group ID. For an example, if "eth0" first group ID
> > + * is x, groups[x] name will be stored as "eth0_0".
> > + *
> > + * Once done for each function, each function would have its group
> > +names,
> > + * and each groups would also have their names.
> > + *
> > + * Return: 0 on success else error code.
> > + */
> > +static int zynqmp_pinctrl_prepare_func_groups(struct device *dev, u32
> fid,
> > + struct zynqmp_pmux_function *func,
> > + struct
> > +zynqmp_pctrl_group *groups) {
> > + u16 resp[NUM_GROUPS_PER_RESP] = {0};
> > + const char **fgroups;
> > + int ret = 0, index, i;
>
> > + fgroups = devm_kzalloc(dev, sizeof(*fgroups) * func->ngroups,
> > + GFP_KERNEL);
>
> One line
With single line it is crossing 80 line bar and getting the checkpatch warning,
hence split into two lines.
>
> > + 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;
> > +
> > + fgroups[index + i] = devm_kasprintf(dev, GFP_KERNEL,
> > + "%s_%d_grp",
> > + func->name,
> > + index + i);
> > + groups[resp[i]].name = devm_kasprintf(dev, GFP_KERNEL,
> > + "%s_%d_grp",
> > + func->name,
> > + index +
> > + i);
>
> No NULL checks?!
I will update in the next series.
>
> > + }
> > + }
> > +done:
> > + func->groups = fgroups;
> > +
> > + return ret;
> > +}
> > +
> > +/**
> > + * zynqmp_pinctrl_get_function_name() - get function name
> > + * @fid: Function ID.
> > + * @name: Function name
> > + *
> > + * Call firmware API to get name of given function.
> > + */
> > +static void zynqmp_pinctrl_get_function_name(u32 fid, char *name) {
> > + struct zynqmp_pm_query_data qdata = {0};
> > + u32 ret_payload[PAYLOAD_ARG_CNT];
> > +
> > + qdata.qid = PM_QID_PINCTRL_GET_FUNCTION_NAME;
> > + qdata.arg1 = fid;
> > +
> > + /*
> > + * Name of the function is maximum 16 bytes and cannot
> > + * accommodate the return value in SMC buffers, hence ignoring
> > + * the return value for this specific qid.
> > + */
> > + zynqmp_pm_query_data(qdata, ret_payload);
> > + memcpy(name, ret_payload, PINCTRL_GET_FUNC_NAME_RESP_LEN);
> }
> > +
> > +/**
> > + * zynqmp_pinctrl_get_num_functions() - get number of supported
> functions
> > + * @nfuncs: Number of functions.
> > + *
> > + * Call firmware API to get number of functions supported by
> system/board.
> > + *
> > + * Return: 0 on success else error code.
> > + */
> > +static int zynqmp_pinctrl_get_num_functions(unsigned int *nfuncs) {
>
> > + struct zynqmp_pm_query_data qdata = {0};
> > + u32 ret_payload[PAYLOAD_ARG_CNT];
>
> Drop in all cases those redundant prefixes.
>
> > + int ret;
> > +
> > + qdata.qid = PM_QID_PINCTRL_GET_NUM_FUNCTIONS;
> > +
> > + ret = zynqmp_pm_query_data(qdata, ret_payload);
> > + if (ret)
> > + return ret;
> > +
> > + *nfuncs = ret_payload[1];
> > +
> > + return ret;
> > +}
> > +
> > +/**
> > + * zynqmp_pinctrl_get_pin_groups() - get groups for the pin
> > + * @pin: Pin number.
> > + * @index: Group index.
> > + * @groups: Groups data.
> > + *
> > + * Call firmware API to get groups for the given pin.
> > + *
> > + * Return: 0 on success else error code.
> > + */
> > +static int zynqmp_pinctrl_get_pin_groups(u32 pin, u32 index, u16
> > +*groups) {
> > + struct zynqmp_pm_query_data qdata = {0};
> > + u32 ret_payload[PAYLOAD_ARG_CNT];
> > + int ret;
> > +
> > + qdata.qid = PM_QID_PINCTRL_GET_PIN_GROUPS;
> > + qdata.arg1 = pin;
> > + qdata.arg2 = index;
> > +
> > + ret = zynqmp_pm_query_data(qdata, ret_payload);
> > + if (ret)
> > + return ret;
> > +
> > + memcpy(groups, &ret_payload[1],
> > + PINCTRL_GET_PIN_GROUPS_RESP_LEN);
> > +
> > + return ret;
> > +}
> > +
> > +/**
> > + * zynqmp_pinctrl_group_add_pin() - add pin to given group
> > + * @group: Group data.
> > + * @pin: Pin number.
> > + *
> > + * Add pin number to respective group's pin array at end and
> > + * increment pin count for the group.
> > + */
> > +static void zynqmp_pinctrl_group_add_pin(struct zynqmp_pctrl_group
> *group,
> > + unsigned int pin) {
> > + group->pins[group->npins++] = pin; }
> > +
> > +/**
> > + * zynqmp_pinctrl_create_pin_groups() - assign pins to respective groups
> > + * @dev: Device pointer.
> > + * @groups: Groups data.
> > + * @pin: Pin number.
> > + *
> > + * Query firmware to get groups available for the given pin.
> > + * Based on firmware response(group IDs for the pin), add
> > + * pin number to respective group's pin array.
> > + *
> > + * Once all pins are queries, each groups would have its number
> > + * of pins and pin numbers data.
> > + *
> > + * Return: 0 on success else error code.
> > + */
> > +static int zynqmp_pinctrl_create_pin_groups(struct device *dev,
> > + struct zynqmp_pctrl_group *groups,
> > + unsigned int pin) {
> > + int ret, i, index = 0;
> > + u16 resp[NUM_GROUPS_PER_RESP] = {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)
> > + return ret;
> > +
> > + if (resp[i] == (u16)RESERVED_GROUP)
> > + continue;
>
> In the entire driver remove all explicit castings where it's not needed, like
> above. If something is not okay with it, fix the root cause.
Ok, I will check this and fix in next version.
>
> > + zynqmp_pinctrl_group_add_pin(&groups[resp[i]], pin);
> > + }
> > + index += NUM_GROUPS_PER_RESP;
> > + } while (1);
>
> Try to refactor to avoid infinite loops.
I will check and update this in next version.
>
> > +
> > + return ret;
> > +}
> > +
> > +/**
> > + * zynqmp_pinctrl_prepare_group_pins() - prepare each group's pin data
> > + * @dev: Device pointer.
> > + * @groups: Groups data.
> > + * @ngroups: Number of groups.
> > + *
> > + * Prepare pin number and number of pins data for each pins.
> > + *
> > + * Return: 0 on success else error code.
> > + */
> > +static int zynqmp_pinctrl_prepare_group_pins(struct device *dev,
> > + struct zynqmp_pctrl_group *groups,
> > + unsigned int ngroups) {
> > + unsigned int pin;
> > + int ret = 0;
>
> Redundant assignment.
Static analyzer tool will throw warning as it expects the variable to be
Initialized in all possible paths.
I will cross check on this and remove if it is not the case.
>
> > + for (pin = 0; pin < zynqmp_desc.npins; pin++) {
> > + ret = zynqmp_pinctrl_create_pin_groups(dev, groups, pin);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +/**
> > + * zynqmp_pinctrl_prepare_function_info() - prepare function info
> > + * @dev: Device pointer.
> > + * @pctrl: Pin control driver data.
> > + *
> > + * Query firmware for functions, groups and pin information and
> > + * prepare pin control driver data.
> > + *
> > + * Query number of functions and number of function groups (number
> > + * of groups in given function) to allocate required memory buffers
> > + * for functions and groups. Once buffers are allocated to store
> > + * functions and groups data, query and store required information
> > + * (numbe of groups and group names for each function, number of
>
> number
>
> > + * pins and pin numbers for each group).
> > + *
> > + * Return: 0 on success else error code.
> > + */
> > +static int zynqmp_pinctrl_prepare_function_info(struct device *dev,
> > + struct zynqmp_pinctrl
> > +*pctrl) {
> > + struct zynqmp_pmux_function *funcs;
> > + struct zynqmp_pctrl_group *groups;
> > + int ret, i;
> > +
> > + ret = zynqmp_pinctrl_get_num_functions(&pctrl->nfuncs);
> > + if (ret)
> > + return ret;
> > +
> > + funcs = devm_kzalloc(dev, sizeof(*funcs) * pctrl->nfuncs,
> GFP_KERNEL);
> > + if (!funcs)
> > + return -ENOMEM;
> > +
> > + for (i = 0; i < pctrl->nfuncs; i++) {
> > + zynqmp_pinctrl_get_function_name(i, funcs[i].name);
> > +
> > + ret = zynqmp_pinctrl_get_func_num_groups(i,
> &funcs[i].ngroups);
> > + if (ret)
> > + return ret;
> > +
> > + pctrl->ngroups += funcs[i].ngroups;
> > + }
>
> > + groups = devm_kzalloc(dev, sizeof(*groups) * pctrl->ngroups,
> > + GFP_KERNEL);
>
> One line.
It will cross 80 line mark if we make it to a single line.
>
> > + if (!groups)
> > + return -ENOMEM;
> > +
> > + for (i = 0; i < pctrl->nfuncs; i++) {
> > + ret = zynqmp_pinctrl_prepare_func_groups(dev, i, &funcs[i],
> > + groups);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + ret = zynqmp_pinctrl_prepare_group_pins(dev, groups, pctrl-
> >ngroups);
> > + if (ret)
> > + return ret;
> > +
> > + pctrl->funcs = funcs;
> > + pctrl->groups = groups;
> > +
> > + return ret;
> > +}
> > +
> > +/**
> > + * zynqmp_pinctrl_get_num_pins() - get number of pins in system
> > + * @npins: Number of pins in system/board.
> > + *
> > + * Call firmware API to get number of pins.
> > + *
> > + * Return: 0 on success else error code.
> > + */
> > +static int zynqmp_pinctrl_get_num_pins(unsigned int *npins) {
> > + struct zynqmp_pm_query_data qdata = {0};
> > + u32 ret_payload[PAYLOAD_ARG_CNT];
> > + int ret;
> > +
> > + qdata.qid = PM_QID_PINCTRL_GET_NUM_PINS;
> > +
> > + ret = zynqmp_pm_query_data(qdata, ret_payload);
> > + if (ret)
> > + return ret;
> > +
> > + *npins = ret_payload[1];
> > +
> > + return ret;
> > +}
> > +
> > +/**
> > + * zynqmp_pinctrl_prepare_pin_desc() - prepare pin description info
> > + * @dev: Device pointer.
> > + * @zynqmp_pins: Pin information.
> > + * @npins: Number of pins.
> > + *
> > + * Query number of pins information from firmware and prepare pin
> > + * description containing pin number and pin name.
> > + *
> > + * Return: 0 on success else error code.
> > + */
> > +static int zynqmp_pinctrl_prepare_pin_desc(struct device *dev,
> > + const struct pinctrl_pin_desc
> > + **zynqmp_pins,
> > + unsigned int *npins) {
> > + struct pinctrl_pin_desc *pins, *pin;
> > + int ret;
> > + int i;
> > +
> > + ret = zynqmp_pinctrl_get_num_pins(npins);
> > + if (ret)
> > + return ret;
> > +
> > + pins = devm_kzalloc(dev, sizeof(*pins) * *npins, GFP_KERNEL);
> > + if (!pins)
> > + return -ENOMEM;
> > +
> > + for (i = 0; i < *npins; i++) {
> > + pin = &pins[i];
> > + pin->number = i;
> > + pin->name = devm_kasprintf(dev, GFP_KERNEL, "%s%d",
> > + ZYNQMP_PIN_PREFIX, i);
>
> no NULL check?!
I will update in the next series.
> > + }
> > +
> > + *zynqmp_pins = pins;
> > +
> > + return 0;
> > +}
> > +
> > +static int zynqmp_pinctrl_probe(struct platform_device *pdev) {
> > + struct zynqmp_pinctrl *pctrl;
> > + int ret;
> > +
> > + pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL);
> > + if (!pctrl)
> > + return -ENOMEM;
> > +
> > + ret = zynqmp_pinctrl_prepare_pin_desc(&pdev->dev,
> > + &zynqmp_desc.pins,
> > + &zynqmp_desc.npins);
> > + if (ret) {
> > + dev_err(&pdev->dev, "%s() pin desc prepare fail with %d\n",
> > + __func__, ret);
> > + return ret;
> > + }
> > +
> > + ret = zynqmp_pinctrl_prepare_function_info(&pdev->dev, pctrl);
> > + if (ret) {
> > + dev_err(&pdev->dev, "%s() function info prepare fail with %d\n",
> > + __func__, ret);
> > + return ret;
> > + }
> > +
> > + pctrl->pctrl = pinctrl_register(&zynqmp_desc, &pdev->dev, pctrl);
> > + if (IS_ERR(pctrl->pctrl)) {
>
> > + ret = PTR_ERR(pctrl->pctrl);
> > + return ret;
>
> Fold it and drop {}.
I will change this to 'return PTR_ERR(pctrl->pctrl);' in next version.
>
> > + }
> > +
> > + platform_set_drvdata(pdev, pctrl);
>
> > + dev_info(&pdev->dev, "zynqmp pinctrl initialized\n");
>
> Unneeded noise.
I will remove this in next version.
>
> > + return ret;
> > +}
> > +
> > +static const struct of_device_id zynqmp_pinctrl_of_match[] = {
> > + { .compatible = "xlnx,zynqmp-pinctrl" },
> > + { }
> > +};
> > +
> > +static struct platform_driver zynqmp_pinctrl_driver = {
> > + .driver = {
> > + .name = "zynqmp-pinctrl",
> > + .of_match_table = zynqmp_pinctrl_of_match,
> > + },
> > + .probe = zynqmp_pinctrl_probe, };
> > +builtin_platform_driver(zynqmp_pinctrl_driver);
>
> I believe the code base can be easily shrinked by 10%. So, next version I
> would expect something like < 1000 LOCs in the stat.
By removing the kernel docs for the obvious ones will definitely reach.
Regards
Sai Krishna
>
> --
> With Best Regards,
> Andy Shevchenko
On Thu, Mar 18, 2021 at 4:42 PM Sai Krishna Potthuri
<[email protected]> wrote:
> > From: Andy Shevchenko <[email protected]>
> > Sent: Wednesday, March 17, 2021 6:26 PM
> > On Wed, Mar 17, 2021 at 10:27 AM Sai Krishna Potthuri
> > <[email protected]> wrote:
...
> > > +config PINCTRL_ZYNQMP
> > > + bool "Pinctrl driver for Xilinx ZynqMP"
> >
> > Why not module?
> As most of the Xilinx drivers depending on the pin controller driver for
> configuring the MIO pins, we are not supporting to build this driver as
> a module.
OK.
> > > + depends on ARCH_ZYNQMP
> > > + select PINMUX
> > > + select GENERIC_PINCONF
...
> > > +#include <linux/init.h>
> > > +#include <linux/of_address.h>
> >
> > > +#include <linux/pinctrl/pinmux.h>
> > > +#include <linux/pinctrl/pinconf-generic.h>
> >
> > Can you move this group of headers after linux/* ?
> >
> > > +#include <dt-bindings/pinctrl/pinctrl-zynqmp.h>
> >
> > Can it be moved outside of these headers
> >
> > > +#include <linux/platform_device.h>
> > > +#include <linux/firmware/xlnx-zynqmp.h>
> >
> > Ordering (for all groups of headers)?
> Ok, I will order the headers in the below order
> #include <linux/*>
> #include <linux/firmware/xlnx-zynqmp.h>
+ blank line
> #include <linux/pinctrl/*>
+ blank line
> #include <dt-bindings/pinctrl/pinctrl-zynqmp.h>
Looking into other drivers with similar includes, shouldn't it go
first in the file before any other linux/* asm/* etc?
> > > +#include "core.h"
> > > +#include "pinctrl-utils.h"
...
> > > + PIN_CONFIG_IOSTANDARD = PIN_CONFIG_END + 1, };
> >
> > I'm lost here. What is IO standard exactly? Why it can't be in generic
> > headers?
> It represents LVCMOS 3.3 volts/ LVCMOS 1.8 volts.
> Since this is specific to Xilinx ZynqMP platform, considered to be added in
> the driver file.
So, why can't we create a couple of bits to represent this voltages in
the generic header and gain usability for others as well?
Linus?
...
> > > + ret = zynqmp_pm_pinctrl_request(pin);
> > > + if (ret) {
> > > + dev_err(pctldev->dev, "request failed for pin %u\n",
> > > + pin);
> >
> > > + return -EIO;
> >
> > Why shadowing error code?
So, any comments on the initial Q?
>> Since it's the only possible error, why is it not
> > reflected in the kernel doc?
> I will update the kernel doc with the error value for such cases.
> >
> > > + }
...
> > > + default:
> > > + /* Invalid drive strength */
> > > + dev_warn(pctldev->dev,
> > > + "Invalid drive strength for pin %d\n",
> > > + pin);
> > > + return -EINVAL;
> > > + }
> > > + break;
> > > + default:
> > > + ret = -EOPNOTSUPP;
> > > + break;
> > > + }
> > > +
> > > + param = pinconf_to_config_param(*config);
> > > + *config = pinconf_to_config_packed(param, arg);
> > > +
> > > + return ret;
> >
> > This is wrong. You have to return the error codes directly and do not touch
> > *config as long as error happens.
> I will update the *config and param under if (!ret) condition.
Simpler and better just to return errors immediately from the
switch-case entries.
...
> > > + fgroups = devm_kzalloc(dev, sizeof(*fgroups) * func->ngroups,
> > > + GFP_KERNEL);
> >
> > One line
> With single line it is crossing 80 line bar and getting the checkpatch warning,
> hence split into two lines.
No, you may not get a checkpatch warning. Are you working on v5.4
kernels or earlier?
> > > + if (!fgroups)
> > > + return -ENOMEM;
...
> > > +static int zynqmp_pinctrl_prepare_group_pins(struct device *dev,
> > > + struct zynqmp_pctrl_group *groups,
> > > + unsigned int ngroups) {
> > > + unsigned int pin;
> > > + int ret = 0;
> >
> > Redundant assignment.
> Static analyzer tool will throw warning as it expects the variable to be
> Initialized in all possible paths.
Because you need to explicitly return 0 at the end of the function.
Don't follow static analyzers or other tools blindly. Think about all aspects.
> I will cross check on this and remove if it is not the case.
> >
> > > + for (pin = 0; pin < zynqmp_desc.npins; pin++) {
> > > + ret = zynqmp_pinctrl_create_pin_groups(dev, groups, pin);
> > > + if (ret)
> > > + return ret;
> > > + }
> > > +
> > > + return ret;
> > > +}
...
> > > + groups = devm_kzalloc(dev, sizeof(*groups) * pctrl->ngroups,
> > > + GFP_KERNEL);
> >
> > One line.
> It will cross 80 line mark if we make it to a single line.
I don't think it's a problem in this case.
> > > + if (!groups)
> > > + return -ENOMEM;
--
With Best Regards,
Andy Shevchenko
Hi Andy Shevchenko,
> -----Original Message-----
> From: Andy Shevchenko <[email protected]>
> Sent: Friday, March 19, 2021 3:53 PM
> To: Sai Krishna Potthuri <[email protected]>
> Cc: Linus Walleij <[email protected]>; Rob Herring
> <[email protected]>; Michal Simek <[email protected]>; Greg Kroah-
> Hartman <[email protected]>; linux-arm Mailing List <linux-arm-
> [email protected]>; Linux Kernel Mailing List <linux-
> [email protected]>; devicetree <[email protected]>; open
> list:GPIO SUBSYSTEM <[email protected]>; git <[email protected]>;
> [email protected]
> Subject: Re: [PATCH v4 3/3] pinctrl: Add Xilinx ZynqMP pinctrl driver support
>
> On Thu, Mar 18, 2021 at 4:42 PM Sai Krishna Potthuri <[email protected]>
> wrote:
> > > From: Andy Shevchenko <[email protected]>
> > > Sent: Wednesday, March 17, 2021 6:26 PM On Wed, Mar 17, 2021 at
> > > 10:27 AM Sai Krishna Potthuri
> > > <[email protected]> wrote:
>
> ...
>
> > > > +config PINCTRL_ZYNQMP
> > > > + bool "Pinctrl driver for Xilinx ZynqMP"
> > >
> > > Why not module?
> > As most of the Xilinx drivers depending on the pin controller driver
> > for configuring the MIO pins, we are not supporting to build this
> > driver as a module.
>
> OK.
>
> > > > + depends on ARCH_ZYNQMP
> > > > + select PINMUX
> > > > + select GENERIC_PINCONF
>
> ...
>
> > > > +#include <linux/init.h>
> > > > +#include <linux/of_address.h>
> > >
> > > > +#include <linux/pinctrl/pinmux.h> #include
> > > > +<linux/pinctrl/pinconf-generic.h>
> > >
> > > Can you move this group of headers after linux/* ?
> > >
> > > > +#include <dt-bindings/pinctrl/pinctrl-zynqmp.h>
> > >
> > > Can it be moved outside of these headers
> > >
> > > > +#include <linux/platform_device.h> #include
> > > > +<linux/firmware/xlnx-zynqmp.h>
> > >
> > > Ordering (for all groups of headers)?
> > Ok, I will order the headers in the below order #include <linux/*>
> > #include <linux/firmware/xlnx-zynqmp.h>
>
> + blank line
>
> > #include <linux/pinctrl/*>
>
> + blank line
Ok, I will add above two blank lines.
>
> > #include <dt-bindings/pinctrl/pinctrl-zynqmp.h>
>
> Looking into other drivers with similar includes, shouldn't it go first in the file
> before any other linux/* asm/* etc?
I see some drivers are including this header before linux/* and some are using
after linux/*.
>
> > > > +#include "core.h"
> > > > +#include "pinctrl-utils.h"
>
> ...
>
> > > > + PIN_CONFIG_IOSTANDARD = PIN_CONFIG_END + 1, };
> > >
> > > I'm lost here. What is IO standard exactly? Why it can't be in
> > > generic headers?
> > It represents LVCMOS 3.3 volts/ LVCMOS 1.8 volts.
> > Since this is specific to Xilinx ZynqMP platform, considered to be
> > added in the driver file.
>
> So, why can't we create a couple of bits to represent this voltages in the
> generic header and gain usability for others as well?
I see some drivers are maintaining the configuration list in the driver file, if
the configuration is specific to the driver.
We can move this to generic header if it is used by others as well.
Ok, will wait for Linus to comment.
>
> Linus?
>
> ...
>
> > > > + ret = zynqmp_pm_pinctrl_request(pin);
> > > > + if (ret) {
> > > > + dev_err(pctldev->dev, "request failed for pin
> > > > + %u\n", pin);
> > >
> > > > + return -EIO;
> > >
> > > Why shadowing error code?
>
> So, any comments on the initial Q?
Xilinx low level secure firmware error codes are different from Linux error codes.
Secure firmware maintains list of error codes (positive values other than zero).
Hence we return -EIO, if the return value from firmware is non-zero.
>
> >> Since it's the only possible error, why is it not
> > > reflected in the kernel doc?
> > I will update the kernel doc with the error value for such cases.
> > >
> > > > + }
>
> ...
>
> > > > + default:
> > > > + /* Invalid drive strength */
> > > > + dev_warn(pctldev->dev,
> > > > + "Invalid drive strength for pin %d\n",
> > > > + pin);
> > > > + return -EINVAL;
> > > > + }
> > > > + break;
> > > > + default:
> > > > + ret = -EOPNOTSUPP;
> > > > + break;
> > > > + }
> > > > +
> > > > + param = pinconf_to_config_param(*config);
> > > > + *config = pinconf_to_config_packed(param, arg);
> > > > +
> > > > + return ret;
> > >
> > > This is wrong. You have to return the error codes directly and do
> > > not touch *config as long as error happens.
> > I will update the *config and param under if (!ret) condition.
>
> Simpler and better just to return errors immediately from the switch-case
> entries.
Ok, I will update.
>
> ...
>
> > > > + fgroups = devm_kzalloc(dev, sizeof(*fgroups) * func->ngroups,
> > > > + GFP_KERNEL);
> > >
> > > One line
> > With single line it is crossing 80 line bar and getting the checkpatch
> > warning, hence split into two lines.
>
> No, you may not get a checkpatch warning. Are you working on v5.4 kernels
> or earlier?
Ok, got it.
Driver is developed on top of the older kernel (<=5.4) and we see this warning at that
time and fixed by splitting into two lines.
Yes, with the latest kernel I am not seeing the warning by keeping it in one
line, I will update this.
>
> > > > + if (!fgroups)
> > > > + return -ENOMEM;
>
> ...
>
> > > > +static int zynqmp_pinctrl_prepare_group_pins(struct device *dev,
> > > > + struct zynqmp_pctrl_group *groups,
> > > > + unsigned int ngroups) {
> > > > + unsigned int pin;
> > > > + int ret = 0;
> > >
> > > Redundant assignment.
> > Static analyzer tool will throw warning as it expects the variable to
> > be Initialized in all possible paths.
>
> Because you need to explicitly return 0 at the end of the function.
> Don't follow static analyzers or other tools blindly. Think about all aspects.
Ok, I will update this.
>
> > I will cross check on this and remove if it is not the case.
> > >
> > > > + for (pin = 0; pin < zynqmp_desc.npins; pin++) {
> > > > + ret = zynqmp_pinctrl_create_pin_groups(dev, groups, pin);
> > > > + if (ret)
> > > > + return ret;
> > > > + }
> > > > +
> > > > + return ret;
> > > > +}
>
> ...
>
> > > > + groups = devm_kzalloc(dev, sizeof(*groups) * pctrl->ngroups,
> > > > + GFP_KERNEL);
> > >
> > > One line.
> > It will cross 80 line mark if we make it to a single line.
>
> I don't think it's a problem in this case.
I will update this.
>
> > > > + if (!groups)
> > > > + return -ENOMEM;
>
> --
> With Best Regards,
> Andy Shevchenko
On Mon, Mar 22, 2021 at 5:25 PM Sai Krishna Potthuri
<[email protected]> wrote:
> > From: Andy Shevchenko <[email protected]>
> > Sent: Friday, March 19, 2021 3:53 PM
> > On Thu, Mar 18, 2021 at 4:42 PM Sai Krishna Potthuri <[email protected]>
> > wrote:
> > > > From: Andy Shevchenko <[email protected]>
> > > > Sent: Wednesday, March 17, 2021 6:26 PM On Wed, Mar 17, 2021 at
> > > > 10:27 AM Sai Krishna Potthuri
> > > > <[email protected]> wrote:
...
> > > #include <dt-bindings/pinctrl/pinctrl-zynqmp.h>
> >
> > Looking into other drivers with similar includes, shouldn't it go first in the file
> > before any other linux/* asm/* etc?
> I see some drivers are including this header before linux/* and some are using
> after linux/*.
The rule of thumb is that: more generic headers are going first.
I consider dt/* ones are more generic than linux/* ones because they
are covering more than just the Linux kernel.
...
> > > > I'm lost here. What is IO standard exactly? Why it can't be in
> > > > generic headers?
> > > It represents LVCMOS 3.3 volts/ LVCMOS 1.8 volts.
> > > Since this is specific to Xilinx ZynqMP platform, considered to be
> > > added in the driver file.
> >
> > So, why can't we create a couple of bits to represent this voltages in the
> > generic header and gain usability for others as well?
> I see some drivers are maintaining the configuration list in the driver file, if
> the configuration is specific to the driver.
Yes, my point is that this case doesn't sound too specific to the
driver. Many pin control buffers (in hardware way of speaking) have
properties to be different voltage tolerant / produce.
> We can move this to generic header if it is used by others as well.
> Ok, will wait for Linus to comment.
> >
> > Linus?
...
> > > > > + ret = zynqmp_pm_pinctrl_request(pin);
> > > > > + if (ret) {
> > > > > + dev_err(pctldev->dev, "request failed for pin
> > > > > + %u\n", pin);
> > > >
> > > > > + return -EIO;
> > > >
> > > > Why shadowing error code?
> >
> > So, any comments on the initial Q?
> Xilinx low level secure firmware error codes are different from Linux error codes.
> Secure firmware maintains list of error codes (positive values other than zero).
> Hence we return -EIO, if the return value from firmware is non-zero.
Why the zynqmp_pm_pinctrl_request() can't return codes in Linux error
code space?
> > >> Since it's the only possible error, why is it not
> > > > reflected in the kernel doc?
> > > I will update the kernel doc with the error value for such cases.
> > > >
> > > > > + }
--
With Best Regards,
Andy Shevchenko
Hi Andy Shevchenko,
> -----Original Message-----
> From: Andy Shevchenko <[email protected]>
> Sent: Monday, March 22, 2021 10:47 PM
> To: Sai Krishna Potthuri <[email protected]>
> Cc: Linus Walleij <[email protected]>; Rob Herring
> <[email protected]>; Michal Simek <[email protected]>; Greg Kroah-
> Hartman <[email protected]>; linux-arm Mailing List <linux-arm-
> [email protected]>; Linux Kernel Mailing List <linux-
> [email protected]>; devicetree <[email protected]>; open
> list:GPIO SUBSYSTEM <[email protected]>; git <[email protected]>;
> [email protected]
> Subject: Re: [PATCH v4 3/3] pinctrl: Add Xilinx ZynqMP pinctrl driver support
>
> On Mon, Mar 22, 2021 at 5:25 PM Sai Krishna Potthuri
> <[email protected]> wrote:
> > > From: Andy Shevchenko <[email protected]>
> > > Sent: Friday, March 19, 2021 3:53 PM On Thu, Mar 18, 2021 at 4:42 PM
> > > Sai Krishna Potthuri <[email protected]>
> > > wrote:
> > > > > From: Andy Shevchenko <[email protected]>
> > > > > Sent: Wednesday, March 17, 2021 6:26 PM On Wed, Mar 17, 2021 at
> > > > > 10:27 AM Sai Krishna Potthuri
> > > > > <[email protected]> wrote:
>
> ...
>
> > > > #include <dt-bindings/pinctrl/pinctrl-zynqmp.h>
> > >
> > > Looking into other drivers with similar includes, shouldn't it go
> > > first in the file before any other linux/* asm/* etc?
> > I see some drivers are including this header before linux/* and some
> > are using after linux/*.
>
> The rule of thumb is that: more generic headers are going first.
>
> I consider dt/* ones are more generic than linux/* ones because they are
> covering more than just the Linux kernel.
Ok, I will reorder accordingly.
>
> ...
>
> > > > > I'm lost here. What is IO standard exactly? Why it can't be in
> > > > > generic headers?
> > > > It represents LVCMOS 3.3 volts/ LVCMOS 1.8 volts.
> > > > Since this is specific to Xilinx ZynqMP platform, considered to be
> > > > added in the driver file.
> > >
> > > So, why can't we create a couple of bits to represent this voltages
> > > in the generic header and gain usability for others as well?
> > I see some drivers are maintaining the configuration list in the
> > driver file, if the configuration is specific to the driver.
>
> Yes, my point is that this case doesn't sound too specific to the driver. Many
> pin control buffers (in hardware way of speaking) have properties to be
> different voltage tolerant / produce.
Ok, you want me to proceed with this change or shall we wait for
Linus to comment?
>
> > We can move this to generic header if it is used by others as well.
> > Ok, will wait for Linus to comment.
> > >
> > > Linus?
>
> ...
>
> > > > > > + ret = zynqmp_pm_pinctrl_request(pin);
> > > > > > + if (ret) {
> > > > > > + dev_err(pctldev->dev, "request failed for pin
> > > > > > + %u\n", pin);
> > > > >
> > > > > > + return -EIO;
> > > > >
> > > > > Why shadowing error code?
> > >
> > > So, any comments on the initial Q?
> > Xilinx low level secure firmware error codes are different from Linux error
> codes.
> > Secure firmware maintains list of error codes (positive values other than
> zero).
> > Hence we return -EIO, if the return value from firmware is non-zero.
>
> Why the zynqmp_pm_pinctrl_request() can't return codes in Linux error
> code space?
I cross checked with the Xilinx firmware team, firmware driver is now returning
Linux error codes in the latest kernel but while developing the driver it was a
different approach. I will update the driver to simply return the values from
firmware calls.
>
> > > >> Since it's the only possible error, why is it not
> > > > > reflected in the kernel doc?
> > > > I will update the kernel doc with the error value for such cases.
> > > > >
> > > > > > + }
>
> --
> With Best Regards,
> Andy Shevchenko
On Wed, Mar 17, 2021 at 01:55:16PM +0530, Sai Krishna Potthuri wrote:
> Adding pinctrl driver for Xilinx ZynqMP platform.
> This driver queries pin information from firmware and registers
> pin control accordingly.
>
> Signed-off-by: Sai Krishna Potthuri <[email protected]>
> ---
> drivers/pinctrl/Kconfig | 13 +
> drivers/pinctrl/Makefile | 1 +
> drivers/pinctrl/pinctrl-zynqmp.c | 1030 ++++++++++++++++++++++++++++++
> 3 files changed, 1044 insertions(+)
> create mode 100644 drivers/pinctrl/pinctrl-zynqmp.c
>
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index 815095326e2d..25d3c7208975 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -341,6 +341,19 @@ config PINCTRL_ZYNQ
> help
> This selects the pinctrl driver for Xilinx Zynq.
>
> +config PINCTRL_ZYNQMP
> + bool "Pinctrl driver for Xilinx ZynqMP"
Please make this work as a module.
> --- a/drivers/pinctrl/Makefile
> +++ b/drivers/pinctrl/Makefile
> @@ -43,6 +43,7 @@ obj-$(CONFIG_PINCTRL_TB10X) += pinctrl-tb10x.o
> obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o
> obj-$(CONFIG_PINCTRL_STMFX) += pinctrl-stmfx.o
> obj-$(CONFIG_PINCTRL_ZYNQ) += pinctrl-zynq.o
> +obj-$(CONFIG_PINCTRL_ZYNQMP) += pinctrl-zynqmp.o
Please use a tab like the other lines in this file.
> +
> + dev_info(&pdev->dev, "zynqmp pinctrl initialized\n");
When drivers work properly, they are quiet. Please remove this.
thanks,
greg k-h
On Wed, Mar 24, 2021 at 10:02:53AM +0100, Michal Simek wrote:
>
>
> On 3/24/21 9:49 AM, Greg Kroah-Hartman wrote:
> > On Wed, Mar 24, 2021 at 09:29:12AM +0100, Michal Simek wrote:
> >> On 3/23/21 2:42 PM, Greg Kroah-Hartman wrote:
> >>> On Wed, Mar 17, 2021 at 01:55:16PM +0530, Sai Krishna Potthuri wrote:
> >>>> Adding pinctrl driver for Xilinx ZynqMP platform.
> >>>> This driver queries pin information from firmware and registers
> >>>> pin control accordingly.
> >>>>
> >>>> Signed-off-by: Sai Krishna Potthuri <[email protected]>
> >>>> ---
> >>>> drivers/pinctrl/Kconfig | 13 +
> >>>> drivers/pinctrl/Makefile | 1 +
> >>>> drivers/pinctrl/pinctrl-zynqmp.c | 1030 ++++++++++++++++++++++++++++++
> >>>> 3 files changed, 1044 insertions(+)
> >>>> create mode 100644 drivers/pinctrl/pinctrl-zynqmp.c
> >>>>
> >>>> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> >>>> index 815095326e2d..25d3c7208975 100644
> >>>> --- a/drivers/pinctrl/Kconfig
> >>>> +++ b/drivers/pinctrl/Kconfig
> >>>> @@ -341,6 +341,19 @@ config PINCTRL_ZYNQ
> >>>> help
> >>>> This selects the pinctrl driver for Xilinx Zynq.
> >>>>
> >>>> +config PINCTRL_ZYNQMP
> >>>> + bool "Pinctrl driver for Xilinx ZynqMP"
> >>>
> >>> Please make this work as a module.
> >>
> >> The most of pinctrl drivers are builtin modules now which is not excuse
> >> it is just fact.
> >> $ git grep module_pla drivers/pinctrl/ | wc -l
> >> 40
> >> $ git grep builtin_pla drivers/pinctrl/ | wc -l
> >> 64
> >
> > For new ones, we can do better, don't make us have to go back and fix
> > this up later.
>
> As I said not a big deal. If this is the way to go then I these rules
> should be followed which is not what it is happening based on 3 latest
> pinctrl drivers below.
I do not disagree, but I point out issues when I see them, you got
unlucky :)
On 3/23/21 2:42 PM, Greg Kroah-Hartman wrote:
> On Wed, Mar 17, 2021 at 01:55:16PM +0530, Sai Krishna Potthuri wrote:
>> Adding pinctrl driver for Xilinx ZynqMP platform.
>> This driver queries pin information from firmware and registers
>> pin control accordingly.
>>
>> Signed-off-by: Sai Krishna Potthuri <[email protected]>
>> ---
>> drivers/pinctrl/Kconfig | 13 +
>> drivers/pinctrl/Makefile | 1 +
>> drivers/pinctrl/pinctrl-zynqmp.c | 1030 ++++++++++++++++++++++++++++++
>> 3 files changed, 1044 insertions(+)
>> create mode 100644 drivers/pinctrl/pinctrl-zynqmp.c
>>
>> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
>> index 815095326e2d..25d3c7208975 100644
>> --- a/drivers/pinctrl/Kconfig
>> +++ b/drivers/pinctrl/Kconfig
>> @@ -341,6 +341,19 @@ config PINCTRL_ZYNQ
>> help
>> This selects the pinctrl driver for Xilinx Zynq.
>>
>> +config PINCTRL_ZYNQMP
>> + bool "Pinctrl driver for Xilinx ZynqMP"
>
> Please make this work as a module.
The most of pinctrl drivers are builtin modules now which is not excuse
it is just fact.
$ git grep module_pla drivers/pinctrl/ | wc -l
40
$ git grep builtin_pla drivers/pinctrl/ | wc -l
64
Also at least last 3 pinctrl drivers which have been merged are not modules.
d4c34d09ab03 ("pinctrl: Add RISC-V Canaan Kendryte K210 FPIOA driver")
7e5ea974e61c ("pinctrl: pinctrl-microchip-sgpio: Add pinctrl driver for
Microsemi Serial GPIO")
a68a7844264e ("pinctrl: visconti: Add Toshiba Visconti SoCs pinctrl
support")
None is saying that it can't be done but that cases where you use
pinctrl as module are really very limited. When you start to use pinctrl
and its functionality you need to have it as the part of the kernel to
be to get console, mmc, ethernet, usb, etc.
That's why I would like to know what functionality and use case you have
in mind that this driver should be made module.
Thanks,
Michal
On Wed, Mar 24, 2021 at 09:29:12AM +0100, Michal Simek wrote:
> On 3/23/21 2:42 PM, Greg Kroah-Hartman wrote:
> > On Wed, Mar 17, 2021 at 01:55:16PM +0530, Sai Krishna Potthuri wrote:
> >> Adding pinctrl driver for Xilinx ZynqMP platform.
> >> This driver queries pin information from firmware and registers
> >> pin control accordingly.
> >>
> >> Signed-off-by: Sai Krishna Potthuri <[email protected]>
> >> ---
> >> drivers/pinctrl/Kconfig | 13 +
> >> drivers/pinctrl/Makefile | 1 +
> >> drivers/pinctrl/pinctrl-zynqmp.c | 1030 ++++++++++++++++++++++++++++++
> >> 3 files changed, 1044 insertions(+)
> >> create mode 100644 drivers/pinctrl/pinctrl-zynqmp.c
> >>
> >> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> >> index 815095326e2d..25d3c7208975 100644
> >> --- a/drivers/pinctrl/Kconfig
> >> +++ b/drivers/pinctrl/Kconfig
> >> @@ -341,6 +341,19 @@ config PINCTRL_ZYNQ
> >> help
> >> This selects the pinctrl driver for Xilinx Zynq.
> >>
> >> +config PINCTRL_ZYNQMP
> >> + bool "Pinctrl driver for Xilinx ZynqMP"
> >
> > Please make this work as a module.
>
> The most of pinctrl drivers are builtin modules now which is not excuse
> it is just fact.
> $ git grep module_pla drivers/pinctrl/ | wc -l
> 40
> $ git grep builtin_pla drivers/pinctrl/ | wc -l
> 64
For new ones, we can do better, don't make us have to go back and fix
this up later.
> Also at least last 3 pinctrl drivers which have been merged are not modules.
> d4c34d09ab03 ("pinctrl: Add RISC-V Canaan Kendryte K210 FPIOA driver")
> 7e5ea974e61c ("pinctrl: pinctrl-microchip-sgpio: Add pinctrl driver for
> Microsemi Serial GPIO")
> a68a7844264e ("pinctrl: visconti: Add Toshiba Visconti SoCs pinctrl
> support")
>
> None is saying that it can't be done but that cases where you use
> pinctrl as module are really very limited. When you start to use pinctrl
> and its functionality you need to have it as the part of the kernel to
> be to get console, mmc, ethernet, usb, etc.
>
> That's why I would like to know what functionality and use case you have
> in mind that this driver should be made module.
The "functionality" of building a kernel image that works on all
hardware types. Just like x86-64 has been for a very long time :)
thanks,
greg k-h
On 3/24/21 9:49 AM, Greg Kroah-Hartman wrote:
> On Wed, Mar 24, 2021 at 09:29:12AM +0100, Michal Simek wrote:
>> On 3/23/21 2:42 PM, Greg Kroah-Hartman wrote:
>>> On Wed, Mar 17, 2021 at 01:55:16PM +0530, Sai Krishna Potthuri wrote:
>>>> Adding pinctrl driver for Xilinx ZynqMP platform.
>>>> This driver queries pin information from firmware and registers
>>>> pin control accordingly.
>>>>
>>>> Signed-off-by: Sai Krishna Potthuri <[email protected]>
>>>> ---
>>>> drivers/pinctrl/Kconfig | 13 +
>>>> drivers/pinctrl/Makefile | 1 +
>>>> drivers/pinctrl/pinctrl-zynqmp.c | 1030 ++++++++++++++++++++++++++++++
>>>> 3 files changed, 1044 insertions(+)
>>>> create mode 100644 drivers/pinctrl/pinctrl-zynqmp.c
>>>>
>>>> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
>>>> index 815095326e2d..25d3c7208975 100644
>>>> --- a/drivers/pinctrl/Kconfig
>>>> +++ b/drivers/pinctrl/Kconfig
>>>> @@ -341,6 +341,19 @@ config PINCTRL_ZYNQ
>>>> help
>>>> This selects the pinctrl driver for Xilinx Zynq.
>>>>
>>>> +config PINCTRL_ZYNQMP
>>>> + bool "Pinctrl driver for Xilinx ZynqMP"
>>>
>>> Please make this work as a module.
>>
>> The most of pinctrl drivers are builtin modules now which is not excuse
>> it is just fact.
>> $ git grep module_pla drivers/pinctrl/ | wc -l
>> 40
>> $ git grep builtin_pla drivers/pinctrl/ | wc -l
>> 64
>
> For new ones, we can do better, don't make us have to go back and fix
> this up later.
As I said not a big deal. If this is the way to go then I these rules
should be followed which is not what it is happening based on 3 latest
pinctrl drivers below.
>
>> Also at least last 3 pinctrl drivers which have been merged are not modules.
>> d4c34d09ab03 ("pinctrl: Add RISC-V Canaan Kendryte K210 FPIOA driver")
>> 7e5ea974e61c ("pinctrl: pinctrl-microchip-sgpio: Add pinctrl driver for
>> Microsemi Serial GPIO")
>> a68a7844264e ("pinctrl: visconti: Add Toshiba Visconti SoCs pinctrl
>> support")
>>
>> None is saying that it can't be done but that cases where you use
>> pinctrl as module are really very limited. When you start to use pinctrl
>> and its functionality you need to have it as the part of the kernel to
>> be to get console, mmc, ethernet, usb, etc.
>>
>> That's why I would like to know what functionality and use case you have
>> in mind that this driver should be made module.
>
> The "functionality" of building a kernel image that works on all
> hardware types. Just like x86-64 has been for a very long time :)
ok.
Thanks,
Michal
On 3/24/21 10:13 AM, Greg Kroah-Hartman wrote:
> On Wed, Mar 24, 2021 at 10:02:53AM +0100, Michal Simek wrote:
>>
>>
>> On 3/24/21 9:49 AM, Greg Kroah-Hartman wrote:
>>> On Wed, Mar 24, 2021 at 09:29:12AM +0100, Michal Simek wrote:
>>>> On 3/23/21 2:42 PM, Greg Kroah-Hartman wrote:
>>>>> On Wed, Mar 17, 2021 at 01:55:16PM +0530, Sai Krishna Potthuri wrote:
>>>>>> Adding pinctrl driver for Xilinx ZynqMP platform.
>>>>>> This driver queries pin information from firmware and registers
>>>>>> pin control accordingly.
>>>>>>
>>>>>> Signed-off-by: Sai Krishna Potthuri <[email protected]>
>>>>>> ---
>>>>>> drivers/pinctrl/Kconfig | 13 +
>>>>>> drivers/pinctrl/Makefile | 1 +
>>>>>> drivers/pinctrl/pinctrl-zynqmp.c | 1030 ++++++++++++++++++++++++++++++
>>>>>> 3 files changed, 1044 insertions(+)
>>>>>> create mode 100644 drivers/pinctrl/pinctrl-zynqmp.c
>>>>>>
>>>>>> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
>>>>>> index 815095326e2d..25d3c7208975 100644
>>>>>> --- a/drivers/pinctrl/Kconfig
>>>>>> +++ b/drivers/pinctrl/Kconfig
>>>>>> @@ -341,6 +341,19 @@ config PINCTRL_ZYNQ
>>>>>> help
>>>>>> This selects the pinctrl driver for Xilinx Zynq.
>>>>>>
>>>>>> +config PINCTRL_ZYNQMP
>>>>>> + bool "Pinctrl driver for Xilinx ZynqMP"
>>>>>
>>>>> Please make this work as a module.
>>>>
>>>> The most of pinctrl drivers are builtin modules now which is not excuse
>>>> it is just fact.
>>>> $ git grep module_pla drivers/pinctrl/ | wc -l
>>>> 40
>>>> $ git grep builtin_pla drivers/pinctrl/ | wc -l
>>>> 64
>>>
>>> For new ones, we can do better, don't make us have to go back and fix
>>> this up later.
>>
>> As I said not a big deal. If this is the way to go then I these rules
>> should be followed which is not what it is happening based on 3 latest
>> pinctrl drivers below.
>
> I do not disagree, but I point out issues when I see them, you got
> unlucky :)
I feel we were lucky that our driver got your attention and we do it
properly from the beginning. :-)
Thanks,
Michal
On Mon, Mar 22, 2021 at 4:25 PM Sai Krishna Potthuri
<[email protected]> wrote:
> [Andy]
> > > > > + PIN_CONFIG_IOSTANDARD = PIN_CONFIG_END + 1, };
> > > >
> > > > I'm lost here. What is IO standard exactly? Why it can't be in
> > > > generic headers?
> > > It represents LVCMOS 3.3 volts/ LVCMOS 1.8 volts.
> > > Since this is specific to Xilinx ZynqMP platform, considered to be
> > > added in the driver file.
> >
> > So, why can't we create a couple of bits to represent this voltages in the
> > generic header and gain usability for others as well?
>
> I see some drivers are maintaining the configuration list in the driver file, if
> the configuration is specific to the driver.
>
> We can move this to generic header if it is used by others as well.
> Ok, will wait for Linus to comment.
> >
> > Linus?
While it is fine to add custom pin config options to pin controllers
for hopelessly idiomatic stuff, this does look like it should be
using PIN_CONFIG_POWER_SOURCE with the voltage rail
as parameter, see
include/linux/pinctrl/pinconf-generic.h
If you're not using that then tell us why.
Yours,
Linus Walleij
On Wed, Mar 17, 2021 at 9:26 AM Sai Krishna Potthuri
<[email protected]> wrote:
> + io-standard:
> + description:
> + Selects the IO standard for MIO pins, this is driver specific.
> + $ref: "/schemas/types.yaml#/definitions/uint32"
> + enum: [0, 1]
As concluded from driver review, replace this with
power-source which is already defined in
Documentation/devicetree/bindings/pinctrl/pincfg-node.yaml
Yours,
Linus Walleij
Hi Linus,
> -----Original Message-----
> From: Linus Walleij <[email protected]>
> Sent: Thursday, March 25, 2021 2:27 PM
> To: Sai Krishna Potthuri <[email protected]>
> Cc: Andy Shevchenko <[email protected]>; Rob Herring
> <[email protected]>; Michal Simek <[email protected]>; Greg Kroah-
> Hartman <[email protected]>; linux-arm Mailing List <linux-arm-
> [email protected]>; Linux Kernel Mailing List <linux-
> [email protected]>; devicetree <[email protected]>; open
> list:GPIO SUBSYSTEM <[email protected]>; git <[email protected]>;
> [email protected]
> Subject: Re: [PATCH v4 3/3] pinctrl: Add Xilinx ZynqMP pinctrl driver support
>
> On Mon, Mar 22, 2021 at 4:25 PM Sai Krishna Potthuri
> <[email protected]> wrote:
> > [Andy]
>
> > > > > > + PIN_CONFIG_IOSTANDARD = PIN_CONFIG_END + 1, };
> > > > >
> > > > > I'm lost here. What is IO standard exactly? Why it can't be in
> > > > > generic headers?
> > > > It represents LVCMOS 3.3 volts/ LVCMOS 1.8 volts.
> > > > Since this is specific to Xilinx ZynqMP platform, considered to be
> > > > added in the driver file.
> > >
> > > So, why can't we create a couple of bits to represent this voltages
> > > in the generic header and gain usability for others as well?
> >
> > I see some drivers are maintaining the configuration list in the
> > driver file, if the configuration is specific to the driver.
> >
> > We can move this to generic header if it is used by others as well.
> > Ok, will wait for Linus to comment.
> > >
> > > Linus?
>
> While it is fine to add custom pin config options to pin controllers for
> hopelessly idiomatic stuff, this does look like it should be using
> PIN_CONFIG_POWER_SOURCE with the voltage rail as parameter, see
> include/linux/pinctrl/pinconf-generic.h
Thanks.
I will update the driver to use 'power-source' option.
Regards
Sai Krishna
>
> If you're not using that then tell us why.
>
> Yours,
> Linus Walleij