[U-Boot] [RFC v3 PATCH 2/4] pinctrl: add pin control uclass support
Simon Glass
sjg at chromium.org
Wed Aug 12 16:16:09 CEST 2015
Hi Masahiro,
On 10 August 2015 at 10:05, Masahiro Yamada
<yamada.masahiro at socionext.com> wrote:
> This creates a new framework for handling of pin control devices,
> i.e. devices that control different aspects of package pins.
>
> This uclass handles pinmuxing and pin configuration; pinmuxing
> controls switching among silicon blocks that share certain physical
> pins, pin configuration handles electronic properties such as pin-
> biasing, load capacitance etc.
>
> This framework supports the same device tree bindings, but if you
> do not need full interface support, you can disable some features
> to reduce memory foot print.
>
> Signed-off-by: Masahiro Yamada <yamada.masahiro at socionext.com>
> ---
>
> drivers/Kconfig | 2 +
> drivers/Makefile | 1 +
> drivers/core/device.c | 4 +
> drivers/pinctrl/Kconfig | 42 +++++
> drivers/pinctrl/Makefile | 2 +
> drivers/pinctrl/pinctrl-generic.c | 351 ++++++++++++++++++++++++++++++++++++++
> drivers/pinctrl/pinctrl-uclass.c | 151 ++++++++++++++++
> include/dm/pinctrl.h | 218 +++++++++++++++++++++++
> include/dm/uclass-id.h | 2 +
> 9 files changed, 773 insertions(+)
> create mode 100644 drivers/pinctrl/Kconfig
> create mode 100644 drivers/pinctrl/Makefile
> create mode 100644 drivers/pinctrl/pinctrl-generic.c
> create mode 100644 drivers/pinctrl/pinctrl-uclass.c
> create mode 100644 include/dm/pinctrl.h
>
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index 092bc02..2b9933f 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -32,6 +32,8 @@ source "drivers/i2c/Kconfig"
>
> source "drivers/spi/Kconfig"
>
> +source "drivers/pinctrl/Kconfig"
> +
> source "drivers/gpio/Kconfig"
>
> source "drivers/power/Kconfig"
> diff --git a/drivers/Makefile b/drivers/Makefile
> index a721ec8..9d0a595 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -1,6 +1,7 @@
> obj-$(CONFIG_$(SPL_)DM) += core/
> obj-$(CONFIG_$(SPL_)CLK) += clk/
> obj-$(CONFIG_$(SPL_)LED) += led/
> +obj-$(CONFIG_$(SPL_)PINCTRL) += pinctrl/
> obj-$(CONFIG_$(SPL_)RAM) += ram/
>
> ifdef CONFIG_SPL_BUILD
> diff --git a/drivers/core/device.c b/drivers/core/device.c
> index 634070c..767b7fe 100644
> --- a/drivers/core/device.c
> +++ b/drivers/core/device.c
> @@ -15,6 +15,7 @@
> #include <dm/device.h>
> #include <dm/device-internal.h>
> #include <dm/lists.h>
> +#include <dm/pinctrl.h>
> #include <dm/platdata.h>
> #include <dm/uclass.h>
> #include <dm/uclass-internal.h>
> @@ -277,6 +278,9 @@ int device_probe_child(struct udevice *dev, void *parent_priv)
>
> dev->flags |= DM_FLAG_ACTIVATED;
>
> + /* continue regardless of the result of pinctrl */
> + pinctrl_select_state(dev, "default");
> +
> ret = uclass_pre_probe_device(dev);
> if (ret)
> goto fail;
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> new file mode 100644
> index 0000000..3f4f4b3
> --- /dev/null
> +++ b/drivers/pinctrl/Kconfig
> @@ -0,0 +1,42 @@
> +#
> +# PINCTRL infrastructure and drivers
> +#
> +
> +menu "Pin controllers"
> +
> +config PINCTRL
> + bool "Support pin controllers"
> +
> +config PINCTRL_GENERIC
> + bool "Support generic pin controllers"
> + depends on PINCTRL
Can you add some help for these - explaining what each one means and
why to enable it.
> +
> +config PINMUX
> + bool "Support pin multiplexing controllers"
> + depends on PINCTRL_GENERIC
> +
> +config PINCONF
> + bool "Support pin configuration controllers"
> + depends on PINCTRL_GENERIC
> +
> +config SPL_PINCTRL
> + bool "Support pin controlloers in SPL"
> + depends on SPL
> +
> +config SPL_PINCTRL_GENERIC
> + bool "Support generic pin controllers in SPL"
> + depends on SPL_PINCTRL
> +
> +config SPL_PINMUX
> + bool "Support pin multiplexing controllers in SPL"
> + depends on SPL_PINCTRL_GENERIC
> +
> +config SPL_PINCONF
> + bool "Support pin configuration controllers in SPL"
> + depends on SPL_PINCTRL_GENERIC
> +
> +if PINCTRL || SPL_PINCTRL
> +
> +endif
> +
> +endmenu
> diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
> new file mode 100644
> index 0000000..a713c7d
> --- /dev/null
> +++ b/drivers/pinctrl/Makefile
Does this need a license header?
> @@ -0,0 +1,2 @@
> +obj-y += pinctrl-uclass.o
> +obj-$(CONFIG_$(SPL_)PINCTRL_GENERIC) += pinctrl-generic.o
> diff --git a/drivers/pinctrl/pinctrl-generic.c b/drivers/pinctrl/pinctrl-generic.c
> new file mode 100644
> index 0000000..5d5d245
> --- /dev/null
> +++ b/drivers/pinctrl/pinctrl-generic.c
> @@ -0,0 +1,351 @@
> +/*
> + * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro at socionext.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <linux/compat.h>
> +#include <dm/device.h>
> +#include <dm/pinctrl.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +/**
> + * pinctrl_pin_name_to_selector() - return the pin selector for a pin
> + * @dev: pin controller device
> + * @pin: the pin name to look up
@return, please fix globally.
> + */
> +static int pinctrl_pin_name_to_selector(struct udevice *dev, const char *pin)
> +{
> + const struct pinctrl_ops *ops = dev->driver->ops;
Please create a #define for this as with spi.h for example.
> + unsigned npins, selector = 0;
> +
> + if (!ops->get_pins_count || !ops->get_pin_name) {
> + dev_err(dev, "get_pins_count or get_pin_name missing\n");
Should this be debug() or dev_warn()? It would be nice to compile
these out unless debugging.
> + return -EINVAL;
That is normally used for an invalid device tree arg. How about -ENOSYS?
> + }
> +
> + npins = ops->get_pins_count(dev);
> +
> + /* See if this pctldev has this pin */
> + while (selector < npins) {
How about:
for (selector = 0; selector < npins; selected++)
> + const char *pname = ops->get_pin_name(dev, selector);
> +
> + if (!strcmp(pin, pname))
> + return selector;
> +
> + selector++;
> + }
> +
> + return -EINVAL;
> +}
> +
> +/**
> + * pinctrl_group_name_to_selector() - return the group selector for a group
> + * @dev: pin controller device
> + * @group: the pin group name to look up
@return
> + */
> +static int pinctrl_group_name_to_selector(struct udevice *dev,
> + const char *group)
> +{
> + const struct pinctrl_ops *ops = dev->driver->ops;
> + unsigned ngroups, selector = 0;
> +
> + if (!ops->get_groups_count || !ops->get_group_name) {
> + dev_err(dev, "get_groups_count or get_group_name missing\n");
> + return -EINVAL;
> + }
> +
> + ngroups = ops->get_groups_count(dev);
> +
> + /* See if this pctldev has this group */
> + while (selector < ngroups) {
> + const char *gname = ops->get_group_name(dev, selector);
> +
> + if (!strcmp(group, gname))
> + return selector;
> +
> + selector++;
> + }
> +
> + return -EINVAL;
I think this means that the device tree is missing something, in which
case this is fine. If not you might consider -ENOENT.
> +}
> +
> +#if CONFIG_IS_ENABLED(PINMUX)
> +/**
> + * pinmux_func_name_to_selector() - return the function selector for a function
> + * @dev: pin controller device
> + * @function: the function name to look up
> + */
> +static int pinmux_func_name_to_selector(struct udevice *dev,
> + const char *function)
> +{
> + const struct pinctrl_ops *ops = dev->driver->ops;
> + unsigned nfuncs, selector = 0;
> +
> + if (!ops->get_functions_count || !ops->get_function_name) {
> + dev_err(dev,
> + "get_functions_count or get_function_name missing\n");
> + return -EINVAL;
> + }
> +
> + nfuncs = ops->get_functions_count(dev);
> +
> + /* See if this pctldev has this function */
> + while (selector < nfuncs) {
> + const char *fname = ops->get_function_name(dev, selector);
> +
> + if (!strcmp(function, fname))
> + return selector;
> +
> + selector++;
> + }
> +
> + return -EINVAL;
> +}
> +
> +/**
> + * pinmux_enable_setting() - enable pin-mux setting for a certain pin/group
> + * @dev: pin controller device
> + * @is_group: target of operation (true: pin group, false: pin)
> + * @selector: pin selector or group selector, depending on @is_group
> + * @func_selector: function selector
> + */
> +static int pinmux_enable_setting(struct udevice *dev, bool is_group,
> + unsigned selector, unsigned func_selector)
> +{
> + const struct pinctrl_ops *ops = dev->driver->ops;
> +
> + if (is_group) {
> + if (!ops->pinmux_group_set) {
> + dev_err(dev, "pinmux_group_set op missing\n");
> + return -EINVAL;
> + }
> +
> + return ops->pinmux_group_set(dev, selector, func_selector);
> + } else {
> + if (!ops->pinmux_set) {
> + dev_err(dev, "pinmux_set op missing\n");
> + return -EINVAL;
> + }
> + return ops->pinmux_set(dev, selector, func_selector);
> + }
> +}
> +#else
> +static int pinmux_func_name_to_selector(struct udevice *dev,
> + const char *function)
> +{
> + return 0;
> +}
> +
> +static int pinmux_enable_setting(struct udevice *dev, bool is_group,
> + unsigned selector, unsigned func_selector)
> +{
> + return 0;
> +}
> +#endif
> +
> +#if CONFIG_IS_ENABLED(PINCONF)
> +/**
> + * pinconf_prop_name_to_param() - return parameter ID for a property name
> + * @dev: pin controller device
> + * @property: property name in DTS, such as "bias-pull-up", "slew-rate", etc.
> + * @default_value: return default value in case no value is specified in DTS
> + */
> +static int pinconf_prop_name_to_param(struct udevice *dev,
> + const char *property, u32 *default_value)
> +{
> + const struct pinctrl_ops *ops = dev->driver->ops;
> + const struct pinconf_param *p, *end;
> +
> + if (!ops->pinconf_num_params || !ops->pinconf_params) {
> + dev_err(dev, "pinconf_num_params or pinconf_params missing\n");
> + return -EINVAL;
> + }
> +
> + p = ops->pinconf_params;
> + end = p + ops->pinconf_num_params;
> +
> + /* See if this pctldev supports this parameter */
> + while (p < end) {
> + if (!strcmp(property, p->property)) {
> + *default_value = p->default_value;
> + return p->param;
> + }
> +
> + p++;
> + }
> +
> + return -EINVAL;
> +}
> +
> +/**
> + * pinconf_enable_setting() - apply pin configuration for a certain pin/group
> + * @dev: pin controller device
> + * @is_group: target of operation (true: pin group, false: pin)
> + * @selector: pin selector or group selector, depending on @is_group
> + * @param: configuration paramter
> + * @argument: argument taken by some configuration parameters
> + */
> +static int pinconf_enable_setting(struct udevice *dev, bool is_group,
> + unsigned selector, unsigned param,
> + u32 argument)
> +{
> + const struct pinctrl_ops *ops = dev->driver->ops;
> +
> + if (is_group) {
> + if (!ops->pinconf_group_set) {
> + dev_err(dev, "pinconf_group_set op missing\n");
> + return -EINVAL;
> + }
> +
> + return ops->pinconf_group_set(dev, selector, param,
> + argument);
> + } else {
> + if (!ops->pinconf_set) {
> + dev_err(dev, "pinconf_set op missing\n");
> + return -EINVAL;
> + }
> + return ops->pinconf_set(dev, selector, param, argument);
> + }
> +}
> +#else
> +static int pinconf_prop_name_to_param(struct udevice *dev,
> + const char *property, u32 *default_value)
> +{
> + return -EINVAL;
> +}
> +
> +static int pinconf_enable_setting(struct udevice *dev, bool is_group,
> + unsigned selector, unsigned param,
> + u32 argument)
> +{
> + return 0;
> +}
> +#endif
> +
> +/**
> + * pinctrl_generic_set_state_one() - set state for a certain pin/group
> + * Apply all pin-muxing and pin configurations specified by @config
> + * for a given pin or pin group.
> + *
> + * @dev: pin controller device
> + * @config: pseudo device pointing to config node
> + * @is_group: target of operation (true: pin group, false: pin)
> + * @selector: pin selector or group selector, depending on @is_group
> + */
> +static int pinctrl_generic_set_state_one(struct udevice *dev,
> + struct udevice *config,
> + bool is_group, unsigned selector)
> +{
> + const void *fdt = gd->fdt_blob;
> + int node_offset = config->of_offset;
> + const char *propname;
> + const void *value;
> + int prop_offset, len, func_selector, param, ret;
> + u32 arg, default_val;
> +
> + for (prop_offset = fdt_first_property_offset(fdt, node_offset);
> + prop_offset > 0;
> + prop_offset = fdt_next_property_offset(fdt, prop_offset)) {
> + value = fdt_getprop_by_offset(fdt, prop_offset,
> + &propname, &len);
> + if (!value)
> + return -EINVAL;
> +
> + if (!strcmp(propname, "function")) {
> + func_selector = pinmux_func_name_to_selector(dev,
> + value);
> + if (func_selector < 0)
> + return func_selector;
> + ret = pinmux_enable_setting(dev, is_group,
> + selector,
> + func_selector);
> + } else {
> + param = pinconf_prop_name_to_param(dev, propname,
> + &default_val);
> + if (param < 0)
> + continue; /* just skip unknown properties */
> +
> + if (len > 0)
Strictly speaking, len > sizeof(fdt32_t)
> + arg = fdt32_to_cpu(*(fdt32_t *)value);
> + else
> + arg = default_val;
> +
> + ret = pinconf_enable_setting(dev, is_group,
> + selector, param, arg);
> + }
> +
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * pinctrl_generic_set_state_subnode() - apply all settings in config node
> + * @dev: pin controller device
> + * @config: pseudo device pointing to config node
> + */
> +static int pinctrl_generic_set_state_subnode(struct udevice *dev,
> + struct udevice *config)
> +{
> + const void *fdt = gd->fdt_blob;
> + int node = config->of_offset;
> + const char *subnode_target_type = "pins";
> + bool is_group = false;
> + const char *name;
> + int strings_count, selector, i, ret;
> +
> + strings_count = fdt_count_strings(fdt, node, subnode_target_type);
> + if (strings_count < 0) {
> + subnode_target_type = "groups";
> + is_group = true;
> + strings_count = fdt_count_strings(fdt, node,
> + subnode_target_type);
> + if (strings_count < 0)
> + return -EINVAL;
> + }
> +
> + for (i = 0; i < strings_count; i++) {
> + ret = fdt_get_string_index(fdt, node, subnode_target_type,
> + i, &name);
> + if (ret < 0)
> + return -EINVAL;
> +
> + if (is_group)
> + selector = pinctrl_group_name_to_selector(dev, name);
> + else
> + selector = pinctrl_pin_name_to_selector(dev, name);
> + if (selector < 0)
> + return selector;
> +
> + ret = pinctrl_generic_set_state_one(dev, config,
> + is_group, selector);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +int pinctrl_generic_set_state(struct udevice *dev, struct udevice *config)
> +{
> + struct udevice *child;
> + int ret;
> +
> + ret = pinctrl_generic_set_state_subnode(dev, config);
> + if (ret)
> + return ret;
> +
> + list_for_each_entry(child, &config->child_head, sibling_node) {
> + ret = pinctrl_generic_set_state_subnode(dev, child);
> + if (ret)
> + return ret;
I try to keep access to internal lists within driver/core. Consider
using device_find_first/next_child instead. Or perhaps create an
iterator device_foreach_child().
> + }
> +
> + return 0;
> +}
> +
> diff --git a/drivers/pinctrl/pinctrl-uclass.c b/drivers/pinctrl/pinctrl-uclass.c
> new file mode 100644
> index 0000000..ab3c146
> --- /dev/null
> +++ b/drivers/pinctrl/pinctrl-uclass.c
> @@ -0,0 +1,151 @@
> +/*
> + * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro at socionext.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <libfdt.h>
> +#include <linux/err.h>
> +#include <linux/list.h>
> +#include <dm/device.h>
> +#include <dm/lists.h>
> +#include <dm/pinctrl.h>
> +#include <dm/uclass.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +static int pinctrl_config_one(struct udevice *config)
> +{
> + struct udevice *pctldev;
> + const struct pinctrl_ops *ops;
> +
> + pctldev = config;
> + for (;;) {
> + pctldev = dev_get_parent(pctldev);
> + if (!pctldev) {
> + dev_err(config, "could not find pctldev\n");
> + return -EINVAL;
> + }
> + if (pctldev->uclass->uc_drv->id == UCLASS_PINCTRL)
> + break;
> + }
> +
> + ops = pctldev->driver->ops;
Use a #define for this as other uclasses do.
assert(ops), or check for NULL and return -ENOSYS.
> + return ops->set_state(pctldev, config);
> +}
> +
> +int pinctrl_select_state(struct udevice *dev, const char *statename)
> +{
> + DECLARE_GLOBAL_DATA_PTR;
Can we put that at the top of the file?
> + const void *fdt = gd->fdt_blob;
> + int node = dev->of_offset;
> + char propname[32]; /* long enough */
> + const fdt32_t *list;
> + uint32_t phandle;
> + int config_node;
> + struct udevice *config;
> + int state, size, i, ret;
> +
> + state = fdt_find_string(fdt, node, "pinctrl-names", statename);
> + if (state < 0) {
> + char *end;
> + /*
> + * If statename is not found in "pinctrl-names",
> + * assume statename is just the integer state ID.
> + */
> + state = simple_strtoul(statename, &end, 10);
> + if (*end)
> + return -EINVAL;
> + }
> +
> + snprintf(propname, sizeof(propname), "pinctrl-%d", state);
> + list = fdt_getprop(fdt, node, propname, &size);
> + if (!list)
> + return -EINVAL;
> +
> + size /= sizeof(*list);
> + for (i = 0; i < size; i++) {
> + phandle = fdt32_to_cpu(*list++);
> +
> + config_node = fdt_node_offset_by_phandle(fdt, phandle);
> + if (config_node < 0) {
> + dev_err(dev, "prop %s index %d invalid phandle\n",
> + propname, i);
> + return -EINVAL;
> + }
> + ret = uclass_get_device_by_of_offset(UCLASS_PINCONFIG,
> + config_node, &config);
> + if (ret)
> + return ret;
> +
> + ret = pinctrl_config_one(config);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * pinconfig_post-bind() - post binding for PINCONFIG uclass
> + * Recursively bind its children as pinconfig devices.
What is the use case for recursive binding?
> + *
> + * @dev: pinconfig device
> + */
> +static int pinconfig_post_bind(struct udevice *dev)
> +{
> + const void *fdt = gd->fdt_blob;
> + int offset = dev->of_offset;
> + const char *name;
> + int ret;
> +
> + for (offset = fdt_first_subnode(fdt, offset);
> + offset > 0;
> + offset = fdt_next_subnode(fdt, offset)) {
> + name = fdt_get_name(fdt, offset, NULL);
> + if (!name)
> + return -EINVAL;
> + ret = device_bind_driver_to_node(dev, "pinconfig", name,
> + offset, NULL);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * pinconfig_post-bind() - post binding for PINCTRL uclass
> + * Check the mandatory operation and bind its children as pinconfig devices.
> + *
> + * @dev: pinctrl device
> + */
> +static int pinctrl_post_bind(struct udevice *dev)
> +{
> + const struct pinctrl_ops *ops = dev->driver->ops;
> +
> + if (!ops || !ops->set_state) {
> + dev_err(dev, "set_state op missing\n");
> + return -EINVAL;
> + }
> +
> + return pinconfig_post_bind(dev);
> +}
> +
> +UCLASS_DRIVER(pinctrl) = {
> + .id = UCLASS_PINCTRL,
> + .post_bind = pinctrl_post_bind,
> + .name = "pinctrl",
> +};
> +
> +UCLASS_DRIVER(pinconfig) = {
> + .id = UCLASS_PINCONFIG,
> + .post_bind = pinconfig_post_bind,
> + .name = "pinconfig",
> +};
> +
> +U_BOOT_DRIVER(pinconfig_generic) = {
> + .name = "pinconfig",
> + .id = UCLASS_PINCONFIG,
> +};
> diff --git a/include/dm/pinctrl.h b/include/dm/pinctrl.h
> new file mode 100644
> index 0000000..42008da
> --- /dev/null
> +++ b/include/dm/pinctrl.h
> @@ -0,0 +1,218 @@
> +/*
> + * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro at socionext.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0+
> + */
> +
> +#ifndef __PINCTRL_H
> +#define __PINCTRL_H
> +
> +/**
> + * struct pinconf_param - pin config parameters
> + *
> + * @property: property name in DT nodes
> + * @param: ID for this config parameter
> + * @default_value: default value for this config parameter used in case
> + * no value is specified in DT nodes
> + */
> +struct pinconf_param {
> + const char * const property;
> + unsigned int param;
> + u32 default_value;
> +};
> +
> +/**
> + * struct pinctrl_ops - pin control operations, to be implemented by
> + * pin controller drivers.
> + *
> + * The @set_state is the only mandatory operation. You can implement your
> + * pinctrl driver with its own @set_state. In this case, the other callbacks
> + * are not required. Otherwise, generic pinctrl framework is also available;
> + * use pinctrl_generic_set_state for @set_state, and implement other operations
> + * depending on your necessity.
> + *
> + * @get_pins_count: return number of selectable named pins available
> + * in this driver. (necessary to parse "pins" property in DTS)
> + * @get_pin_name: return the pin name of the pin selector,
> + * called by the core to figure out which pin it shall do
> + * operations to. (necessary to parse "pins" property in DTS)
> + * @get_groups_count: return number of selectable named groups available
> + * in this driver. (necessary to parse "groups" property in DTS)
> + * @get_group_name: return the group name of the group selector,
> + * called by the core to figure out which pin group it shall do
> + * operations to. (necessary to parse "groups" property in DTS)
> + * @get_functions_count: return number of selectable named functions available
> + * in this driver. (necessary for pin-muxing)
> + * @get_function_name: return the function name of the muxing selector,
> + * called by the core to figure out which mux setting it shall map a
> + * certain device to. (necessary for pin-muxing)
> + * @pinmux_set: enable a certain muxing function with a certain pin.
> + * The @func_selector selects a certain function whereas @pin_selector
> + * selects a certain pin to be used. On simple controllers one of them
> + * may be ignored. (necessary for pin-muxing against a single pin)
> + * @pinmux_group_set: enable a certain muxing function with a certain pin
> + * group. The @func_selector selects a certain function whereas
> + * @group_selector selects a certain set of pins to be used. On simple
> + * controllers one of them may be ignored.
> + * (necessary for pin-muxing against a pin group)
> + * @pinconf_num_params: number of driver-specific parameters to be parsed
> + * from device trees (necessary for pin-configuration)
> + * @pinconf_params: list of driver_specific parameters to be parsed from
> + * device trees (necessary for pin-configuration)
> + * @pinconf_set: configure an individual pin with a given parameter.
> + * (necessary for pin-configuration against a single pin)
> + * @pinconf_group_set: configure all pins in a group with a given parameter.
> + * (necessary for pin-configuration against a pin group)
> + * @set_state: enable pin-mux and pinconf settings specified by @config, a
> + * pseudo device pointing a config node. (mandatory for all drivers)
> + */
> +struct pinctrl_ops {
> + int (*get_pins_count)(struct udevice *dev);
> + const char *(*get_pin_name)(struct udevice *dev, unsigned selector);
> + int (*get_groups_count)(struct udevice *dev);
> + const char *(*get_group_name)(struct udevice *dev, unsigned selector);
> + int (*get_functions_count)(struct udevice *dev);
> + const char *(*get_function_name)(struct udevice *dev,
> + unsigned selector);
> + int (*pinmux_set)(struct udevice *dev, unsigned pin_selector,
> + unsigned func_selector);
> + int (*pinmux_group_set)(struct udevice *dev, unsigned group_selector,
> + unsigned func_selector);
> + unsigned int pinconf_num_params;
> + const struct pinconf_param *pinconf_params;
> + int (*pinconf_set)(struct udevice *dev, unsigned pin_selector,
> + unsigned param, unsigned argument);
> + int (*pinconf_group_set)(struct udevice *dev, unsigned group_selector,
> + unsigned param, unsigned argument);
> + int (*set_state)(struct udevice *dev, struct udevice *config);
> +};
> +
> +/**
> + * Generic pin configuration paramters
> + *
> + * @PIN_CONFIG_BIAS_DISABLE: disable any pin bias on the pin, a
> + * transition from say pull-up to pull-down implies that you disable
> + * pull-up in the process, this setting disables all biasing.
> + * @PIN_CONFIG_BIAS_HIGH_IMPEDANCE: the pin will be set to a high impedance
> + * mode, also know as "third-state" (tristate) or "high-Z" or "floating".
> + * On output pins this effectively disconnects the pin, which is useful
> + * if for example some other pin is going to drive the signal connected
> + * to it for a while. Pins used for input are usually always high
> + * impedance.
> + * @PIN_CONFIG_BIAS_BUS_HOLD: the pin will be set to weakly latch so that it
> + * weakly drives the last value on a tristate bus, also known as a "bus
> + * holder", "bus keeper" or "repeater". This allows another device on the
> + * bus to change the value by driving the bus high or low and switching to
> + * tristate. The argument is ignored.
> + * @PIN_CONFIG_BIAS_PULL_UP: the pin will be pulled up (usually with high
> + * impedance to VDD). If the argument is != 0 pull-up is enabled,
> + * if it is 0, pull-up is total, i.e. the pin is connected to VDD.
> + * @PIN_CONFIG_BIAS_PULL_DOWN: the pin will be pulled down (usually with high
> + * impedance to GROUND). If the argument is != 0 pull-down is enabled,
> + * if it is 0, pull-down is total, i.e. the pin is connected to GROUND.
> + * @PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: the pin will be pulled up or down based
> + * on embedded knowledge of the controller hardware, like current mux
> + * function. The pull direction and possibly strength too will normally
> + * be decided completely inside the hardware block and not be readable
> + * from the kernel side.
> + * If the argument is != 0 pull up/down is enabled, if it is 0, the
> + * configuration is ignored. The proper way to disable it is to use
> + * @PIN_CONFIG_BIAS_DISABLE.
> + * @PIN_CONFIG_DRIVE_PUSH_PULL: the pin will be driven actively high and
> + * low, this is the most typical case and is typically achieved with two
> + * active transistors on the output. Setting this config will enable
> + * push-pull mode, the argument is ignored.
> + * @PIN_CONFIG_DRIVE_OPEN_DRAIN: the pin will be driven with open drain (open
> + * collector) which means it is usually wired with other output ports
> + * which are then pulled up with an external resistor. Setting this
> + * config will enable open drain mode, the argument is ignored.
> + * @PIN_CONFIG_DRIVE_OPEN_SOURCE: the pin will be driven with open source
> + * (open emitter). Setting this config will enable open source mode, the
> + * argument is ignored.
> + * @PIN_CONFIG_DRIVE_STRENGTH: the pin will sink or source at most the current
> + * passed as argument. The argument is in mA.
> + * @PIN_CONFIG_INPUT_ENABLE: enable the pin's input. Note that this does not
> + * affect the pin's ability to drive output. 1 enables input, 0 disables
> + * input.
> + * @PIN_CONFIG_INPUT_SCHMITT_ENABLE: control schmitt-trigger mode on the pin.
> + * If the argument != 0, schmitt-trigger mode is enabled. If it's 0,
> + * schmitt-trigger mode is disabled.
> + * @PIN_CONFIG_INPUT_SCHMITT: this will configure an input pin to run in
> + * schmitt-trigger mode. If the schmitt-trigger has adjustable hysteresis,
> + * the threshold value is given on a custom format as argument when
> + * setting pins to this mode.
> + * @PIN_CONFIG_INPUT_DEBOUNCE: this will configure the pin to debounce mode,
> + * which means it will wait for signals to settle when reading inputs. The
> + * argument gives the debounce time in usecs. Setting the
> + * argument to zero turns debouncing off.
> + * @PIN_CONFIG_POWER_SOURCE: if the pin can select between different power
> + * supplies, the argument to this parameter (on a custom format) tells
> + * the driver which alternative power source to use.
> + * @PIN_CONFIG_SLEW_RATE: if the pin can select slew rate, the argument to
> + * this parameter (on a custom format) tells the driver which alternative
> + * slew rate to use.
> + * @PIN_CONFIG_LOW_POWER_MODE: this will configure the pin for low power
> + * operation, if several modes of operation are supported these can be
> + * passed in the argument on a custom form, else just use argument 1
> + * to indicate low power mode, argument 0 turns low power mode off.
> + * @PIN_CONFIG_OUTPUT: this will configure the pin as an output. Use argument
> + * 1 to indicate high level, argument 0 to indicate low level. (Please
> + * see Documentation/pinctrl.txt, section "GPIO mode pitfalls" for a
> + * discussion around this parameter.)
> + * @PIN_CONFIG_END: this is the last enumerator for pin configurations, if
> + * you need to pass in custom configurations to the pin controller, use
> + * PIN_CONFIG_END+1 as the base offset.
> + */
> +#define PIN_CONFIG_BIAS_DISABLE 0
> +#define PIN_CONFIG_BIAS_HIGH_IMPEDANCE 1
> +#define PIN_CONFIG_BIAS_BUS_HOLD 2
> +#define PIN_CONFIG_BIAS_PULL_UP 3
> +#define PIN_CONFIG_BIAS_PULL_DOWN 4
> +#define PIN_CONFIG_BIAS_PULL_PIN_DEFAULT 5
> +#define PIN_CONFIG_DRIVE_PUSH_PULL 6
> +#define PIN_CONFIG_DRIVE_OPEN_DRAIN 7
> +#define PIN_CONFIG_DRIVE_OPEN_SOURCE 8
> +#define PIN_CONFIG_DRIVE_STRENGTH 9
> +#define PIN_CONFIG_INPUT_ENABLE 10
> +#define PIN_CONFIG_INPUT_SCHMITT_ENABLE 11
> +#define PIN_CONFIG_INPUT_SCHMITT 12
> +#define PIN_CONFIG_INPUT_DEBOUNCE 13
> +#define PIN_CONFIG_POWER_SOURCE 14
> +#define PIN_CONFIG_SLEW_RATE 15
> +#define PIN_CONFIG_LOW_POWER_MODE 16
> +#define PIN_CONFIG_OUTPUT 17
Use enum?
> +#define PIN_CONFIG_END 0x7FFF
> +
> +#if CONFIG_IS_ENABLED(PINCTRL)
> +/**
> + * pinctrl_select_state() - set a device to a given state
> + *
> + * @dev: peripheral device
> + * @statename: state name, like "default"
> + */
> +int pinctrl_select_state(struct udevice *dev, const char *statename);
> +#else
> +static inline int pinctrl_select_state(struct udevice *dev,
> + const char *statename)
> +{
> + return 0;
> +}
> +#endif
> +
> +#if CONFIG_IS_ENABLED(PINCTRL_GENERIC)
Do you think PINCTRL_SIMPLE might be a better name?
> +/**
> + * pinctrl_generic_set_state() - generic set_state operation
> + *
> + * @pctldev: pinctrl device
> + * @config: config device (pseudo device), pointing a config node in DTS
> + */
> +int pinctrl_generic_set_state(struct udevice *pctldev, struct udevice *config);
> +#else
> +static inline int pinctrl_generic_set_state(struct udevice *pctldev,
> + struct udevice *config)
> +{
> + return 0;
> +}
> +#endif
> +
> +#endif /* __PINCTRL_H */
> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
> index c744044..a2fb6de 100644
> --- a/include/dm/uclass-id.h
> +++ b/include/dm/uclass-id.h
> @@ -44,6 +44,8 @@ enum uclass_id {
> UCLASS_PCH, /* x86 platform controller hub */
> UCLASS_PCI, /* PCI bus */
> UCLASS_PCI_GENERIC, /* Generic PCI bus device */
> + UCLASS_PINCTRL, /* Pinctrl device */
Expand - e.g. Pin control and multiplexing device
> + UCLASS_PINCONFIG, /* Pinconfig node device */
Pin configuration
> UCLASS_PMIC, /* PMIC I/O device */
> UCLASS_REGULATOR, /* Regulator device */
> UCLASS_RESET, /* Reset device */
> --
> 1.9.1
>
Regards,
Simon
More information about the U-Boot
mailing list