This is an automated email from the ASF dual-hosted git repository.
GUIDINGLI pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 771c84068be drivers/pinctrl: Add pad read-back and a procfs entry.
771c84068be is described below
commit 771c84068be1a6dc001c7bab2407ba0745ead78c
Author: Justin Hammond <[email protected]>
AuthorDate: Sun Aug 2 15:58:01 2026 +0800
drivers/pinctrl: Add pad read-back and a procfs entry.
The pinctrl interface is write only: every operation sets a property,
and nothing reports what a pin currently holds.
Adds an optional get_pad method describing one pad as a structure: the
settable fields, each with a validity bit because a pad need not have
them all, and a text member for the controller specific fields the
structure does not cover. The structure embeds its strings rather than
pointing at them, so the same shape serves both callers.
The first caller is /proc/pinctrl, which renders one key:value line per
pad, every line the same tokens in the same order with - for a field the
pad does not have, so the file is machine parseable. The framework owns
the format; controllers only supply data.
The second is a new PINCTRLC_GETPAD ioctl, which gives userspace the
read-back that text cannot: reading a pad back after setting it.
PINCTRL_PADNAME() and two lookup helpers let a controller declare its
pad and function-select names in one table instead of inventing its own.
Registration keeps a list, which the renderer iterates; pinctrl_dev_s
gains the pad count. /proc/pinctrl is claimed when the first controller
appears; procfs_register() requires that procfs is not yet mounted,
which holds because controllers register during board or architecture
start up.
Documents the method, the validity bits and the optional naming, and
records that /proc/pinctrl exists.
Off by default and costs nothing when off. No in-tree configuration
enables PINCTRL, so this builds only when a board turns it on.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <[email protected]>
---
.../components/drivers/special/pinctrl.rst | 138 +++++-
drivers/pinctrl/Kconfig | 13 +
drivers/pinctrl/pinctrl.c | 541 ++++++++++++++++++++-
include/nuttx/pinctrl/pinctrl.h | 136 +++++-
4 files changed, 822 insertions(+), 6 deletions(-)
diff --git a/Documentation/components/drivers/special/pinctrl.rst
b/Documentation/components/drivers/special/pinctrl.rst
index 4dd9789dd76..bf1542adabe 100644
--- a/Documentation/components/drivers/special/pinctrl.rst
+++ b/Documentation/components/drivers/special/pinctrl.rst
@@ -25,6 +25,8 @@ Pinctrl Device Drivers
#. **set_slewrate**: Enables the configuration of pin slew rate, which is
crucial
for high-speed digital signal transmission, optimizing signal rise and
fall times.
#. **select_gpio**: Configures the pin function as GPIO.
+ #. **get_pad**: Reports what a pad currently holds. Optional; see
+ `Reading a pad back`_.
- Convenience macros are provided to map these operations directly:
``PINCTRL_SETFUNCTION``,``PINCTRL_SETSTRENGTH``,``PINCTRL_SETDRIVER``,``PINCTRL_SETSLEWRATE``,
@@ -33,6 +35,138 @@ Pinctrl Device Drivers
- Application developers can configure and control pins by opening
/dev/pinctrl0 nodes
and using the ioctl system call.
cmd: PINCTRLC_SETFUNCTION, PINCTRLC_SETSTRENGTH, PINCTRLC_SETDRIVER,
PINCTRLC_SETSLEWRATE,
- PINCTRLC_SELECTGPIO.
- parameters: struct pinctrl_param_s.
+ PINCTRLC_SELECTGPIO, PINCTRLC_GETPAD.
+ parameters: struct pinctrl_param_s, and struct pinctrl_getpad_s for
+ PINCTRLC_GETPAD.
+
+Reading a pad back
+==================
+
+The five operations above only write. ``get_pad`` is the one that reads:
+it fills a ``struct pinctrl_padinfo_s`` describing what a pad currently
+holds, and serves both the ``PINCTRLC_GETPAD`` ioctl and ``/proc/pinctrl``.
+
+The method is **optional**. A controller that does not implement it is
+still listed in ``/proc/pinctrl``, and ``PINCTRLC_GETPAD`` returns
+``-ENOTSUP``.
+
+A pad need not have every field. Fill only what the pad really has and
+set the matching validity bit in ``have``; a field whose bit is clear
+renders as ``-``:
+
+ ============================ ==========================================
+ Bit Field
+ ============================ ==========================================
+ ``PINCTRL_HAVE_FUNCTION`` ``function``
+ ``PINCTRL_HAVE_STRENGTH`` ``strength``
+ ``PINCTRL_HAVE_PULL`` ``pullup`` and ``pulldown``
+ ``PINCTRL_HAVE_SLEWRATE`` ``slewrate``
+ ``PINCTRL_HAVE_INPUT`` ``input``
+ ``PINCTRL_HAVE_SCHMITT`` ``schmitt``
+ ============================ ==========================================
+
+Anything the structure has no member for goes in ``extra`` as further
+``key:value`` text, which is appended to the pad's line.
+
+``npins`` in ``struct pinctrl_dev_s`` bounds the pins ``get_pad`` is asked
+about.
+
+A controller implementing the method looks about like this:
+
+.. code-block:: c
+
+ static int mychip_getpad(struct pinctrl_dev_s *dev, uint32_t pin,
+ struct pinctrl_padinfo_s *info)
+ {
+ uint32_t val = getreg32(MYCHIP_PAD(pin));
+ FAR const char *name;
+
+ info->have = PINCTRL_HAVE_FUNCTION | PINCTRL_HAVE_PULL;
+ info->function = (val & PAD_FUNC_MASK) >> PAD_FUNC_SHIFT;
+ info->pullup = (val & PAD_PU) != 0;
+ info->pulldown = (val & PAD_PD) != 0;
+
+ /* Names are optional; both helpers return NULL when a name is
+ * absent, which leaves the strings empty.
+ */
+
+ name = pinctrl_padname(g_mychip_padnames,
+ nitems(g_mychip_padnames), pin);
+ if (name != NULL)
+ {
+ strlcpy(info->name, name, sizeof(info->name));
+ }
+
+ name = pinctrl_funcname(g_mychip_padnames,
+ nitems(g_mychip_padnames), pin,
+ info->function);
+ if (name != NULL)
+ {
+ strlcpy(info->funcname, name, sizeof(info->funcname));
+ }
+
+ /* Anything the structure has no member for */
+
+ snprintf(info->extra, sizeof(info->extra), "ms:%u",
+ (val >> 8) & 3);
+ return OK;
+ }
+
+ static const struct pinctrl_ops_s g_mychip_ops =
+ {
+ ...
+ .get_pad = mychip_getpad,
+ };
+
+Naming pads and functions
+-------------------------
+
+The ``name`` and ``funcname`` members are **entirely optional**: leave
+them empty and the pad is reported by number alone. A controller that
+wants names may declare them, one entry per pad, with
+``PINCTRL_PADNAME()`` giving the pad's name followed by the name of each
+function select in select order. ``NULL`` marks a select the hardware
+documentation does not name:
+
+.. code-block:: c
+
+ static const struct pinctrl_padname_s g_mychip_padnames[] =
+ {
+ [MYCHIP_PAD_I2C0_SCL] = PINCTRL_PADNAME("I2C0_SCL", "I2C0_SCL",
+ NULL, "GPIO44"),
+ [MYCHIP_PAD_SPI0_CLK] = PINCTRL_PADNAME("SPI0_CLK", "SPI0_CLK"),
+ [MYCHIP_PAD_XIN] = PINCTRL_PADNAME("XIN", NULL),
+ };
+
+The designated initializers make the array index the pad id, which is what
+the lookups assume, and a pad left out of the table reports as a number.
+``I2C0_SCL`` above has three selects with the middle one undocumented,
+``SPI0_CLK`` has one, and ``XIN`` has a pad name but no named function.
+
+``pinctrl_padname()`` and ``pinctrl_funcname()`` look up that table and
+return ``NULL`` when a name is absent, so a controller can pass their
+results straight through. Both take the table's length and bound the pin
+against it, so a pin beyond the table returns ``NULL`` rather than reading
+past the end.
+
+Since the names exist only to be printed, guard the table with the
+configuration that prints them and let the lookups return ``NULL``
+otherwise; the output falls back to numbers.
+
+/proc/pinctrl
+=============
+
+``CONFIG_PINCTRL_PROCFS`` adds ``/proc/pinctrl``, which lists every
+registered controller and, for those implementing ``get_pad``, one line
+per pad. Every line carries the same tokens in the same order, so the
+file can be parsed:
+
+.. code-block:: text
+
+ pinctrl0: 166 pads
+ 0 CHIP_MODE func:0 sel:CHIP_MODE ds:0 pu:0 pd:1 ie:1
smt:1 slew:-
+ 91 I2C0_SCL func:0 sel:I2C0_SCL ds:1 pu:0 pd:0 ie:1
smt:0 slew:-
+ 164 ADDR_RGMII0_SEL_MODE func:- sel:- ds:- pu:- pd:- ie:-
smt:- slew:- ms1:1 ms2:1
+
+The option depends on ``FS_PROCFS_REGISTER`` and is off by default.
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 3ce0f90473e..be148b61bf5 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -12,4 +12,17 @@ config PINCTRL
This selection enables selection of common PINCTRL options.
This option
should be enabled by all platforms that support PINCTRL
interfaces.
See include/nuttx/pinctrl/pinctrl.h for further PINCTRL driver
information.
+
+config PINCTRL_PROCFS
+ bool "PINCTRL procfs entry"
+ default n
+ depends on PINCTRL && FS_PROCFS && FS_PROCFS_REGISTER
+ ---help---
+ Create /proc/pinctrl, listing every registered pin controller
and,
+ for those that supply a get_pad method, one line per pad
describing
+ its current configuration in key:value form.
+
+ The rest of the pinctrl interface only writes, so without this
there
+ is no way to ask a controller what a pin is configured as.
+
endmenu
diff --git a/drivers/pinctrl/pinctrl.c b/drivers/pinctrl/pinctrl.c
index e2be6448d1b..2e5bf9ee306 100644
--- a/drivers/pinctrl/pinctrl.c
+++ b/drivers/pinctrl/pinctrl.c
@@ -28,12 +28,42 @@
#include <sys/types.h>
#include <stdio.h>
+#include <stdarg.h>
#include <assert.h>
#include <errno.h>
+#include <inttypes.h>
+#include <string.h>
#include <nuttx/fs/fs.h>
#include <nuttx/pinctrl/pinctrl.h>
+#ifdef CONFIG_PINCTRL_PROCFS
+# include <sys/stat.h>
+# include <fcntl.h>
+# include <nuttx/kmalloc.h>
+# include <nuttx/list.h>
+# include <nuttx/mutex.h>
+# include <nuttx/fs/procfs.h>
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+#ifdef CONFIG_PINCTRL_PROCFS
+
+/* One registered controller. struct pinctrl_dev_s belongs to the caller
+ * and holds only an operations pointer, so the list node lives here.
+ */
+
+struct pinctrl_entry_s
+{
+ struct list_node node;
+ FAR struct pinctrl_dev_s *dev;
+ int minor;
+};
+#endif
+
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
@@ -47,6 +77,22 @@ static ssize_t pinctrl_write(FAR struct file *filep, FAR
const char *buffer,
static int pinctrl_ioctl(FAR struct file *filep, int cmd,
unsigned long arg);
+#ifdef CONFIG_PINCTRL_PROCFS
+static int pinctrl_procfs_open(FAR struct file *filep,
+ FAR const char *relpath,
+ int oflags, mode_t mode);
+static int pinctrl_procfs_close(FAR struct file *filep);
+static ssize_t pinctrl_procfs_read(FAR struct file *filep,
+ FAR char *buffer, size_t buflen);
+static int pinctrl_procfs_dup(FAR const struct file *oldp,
+ FAR struct file *newp);
+static int pinctrl_procfs_stat(FAR const char *relpath,
+ FAR struct stat *buf);
+static void pinctrl_procfs_add(FAR struct pinctrl_dev_s *dev, int minor);
+static void pinctrl_procfs_remove(FAR struct pinctrl_dev_s *dev,
+ int minor);
+#endif
+
/****************************************************************************
* Private Data
****************************************************************************/
@@ -61,6 +107,464 @@ static const struct file_operations g_pinctrl_drvrops =
pinctrl_ioctl /* ioctl */
};
+#ifdef CONFIG_PINCTRL_PROCFS
+
+static struct list_node g_pinctrl_list =
+ LIST_INITIAL_VALUE(g_pinctrl_list);
+static mutex_t g_pinctrl_lock = NXMUTEX_INITIALIZER;
+static bool g_pinctrl_procfs_added;
+
+static const struct procfs_operations g_pinctrl_procfs_ops =
+{
+ pinctrl_procfs_open, /* open */
+ pinctrl_procfs_close, /* close */
+ pinctrl_procfs_read, /* read */
+ NULL, /* write */
+ NULL, /* poll */
+
+ pinctrl_procfs_dup, /* dup */
+
+ NULL, /* opendir */
+ NULL, /* closedir */
+ NULL, /* readdir */
+ NULL, /* rewinddir */
+
+ pinctrl_procfs_stat, /* stat */
+};
+
+static const struct procfs_entry_s g_pinctrl_procfs =
+{
+ "pinctrl", &g_pinctrl_procfs_ops, PROCFS_FILE_TYPE
+};
+
+#endif /* CONFIG_PINCTRL_PROCFS */
+
+#ifdef CONFIG_PINCTRL_PROCFS
+
+/****************************************************************************
+ * Name: pinctrl_procfs_open
+ *
+ * Description:
+ * Open /proc/pinctrl. The entry is read only, and holds no state of
+ * its own beyond the position accounting procfs does for every
+ * file.
+ *
+ * Input Parameters:
+ * filep - The file structure to attach the open file to
+ * relpath - The path below /proc being opened
+ * oflags - Open flags; anything but read only is refused
+ * mode - Ignored, the entry cannot be created
+ *
+ * Returned Value:
+ * Zero on success, or a negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int pinctrl_procfs_open(FAR struct file *filep,
+ FAR const char *relpath,
+ int oflags, mode_t mode)
+{
+ FAR struct procfs_file_s *priv;
+
+ if ((oflags & O_ACCMODE) != O_RDONLY)
+ {
+ return -EACCES;
+ }
+
+ priv = kmm_zalloc(sizeof(struct procfs_file_s));
+ if (priv == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ filep->f_priv = priv;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: pinctrl_procfs_close
+ *
+ * Description:
+ * Close /proc/pinctrl and free what open() allocated.
+ *
+ * Input Parameters:
+ * filep - The open file
+ *
+ * Returned Value:
+ * Zero on success, or a negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int pinctrl_procfs_close(FAR struct file *filep)
+{
+ kmm_free(filep->f_priv);
+ filep->f_priv = NULL;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: pinctrl_procfs_append
+ *
+ * Description:
+ * Append formatted text at offset n, clamping to the buffer. snprintf
+ * returns the length it wanted, so an unclamped sum would carry the
+ * offset past the buffer and wrap the remaining size. The offset
+ * returned never exceeds len - 1.
+ *
+ * Input Parameters:
+ * line - The line being built
+ * len - Size of line
+ * n - Offset to append at
+ * fmt - Format string, followed by its arguments
+ *
+ * Returned Value:
+ * The offset after the text, never more than len - 1.
+ *
+ ****************************************************************************/
+
+static size_t pinctrl_procfs_append(FAR char *line, size_t len, size_t n,
+ FAR const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ n += vsnprintf(line + n, len - n, fmt, ap);
+ va_end(ap);
+
+ return n < len ? n : len - 1;
+}
+
+/****************************************************************************
+ * Name: pinctrl_procfs_field
+ *
+ * Description:
+ * Append one key:value token, or key:- when the field's validity bit is
+ * clear, so every line carries the same tokens and absence is explicit.
+ *
+ * Input Parameters:
+ * line - The line being built
+ * len - Size of line
+ * n - Offset to append at
+ * key - Token name
+ * have - The pad's PINCTRL_HAVE_* validity bits
+ * bit - The bit that makes this field meaningful
+ * val - The value, used only when that bit is set
+ *
+ * Returned Value:
+ * The offset after the token.
+ *
+ ****************************************************************************/
+
+static size_t pinctrl_procfs_field(FAR char *line, size_t len, size_t n,
+ FAR const char *key, uint32_t have,
+ uint32_t bit, uint32_t val)
+{
+ if ((have & bit) != 0)
+ {
+ return pinctrl_procfs_append(line, len, n, "%s:%" PRIu32 " ",
+ key, val);
+ }
+
+ return pinctrl_procfs_append(line, len, n, "%s:- ", key);
+}
+
+/****************************************************************************
+ * Name: pinctrl_procfs_pad
+ *
+ * Description:
+ * Render one pad as a single line of key:value tokens, every line the
+ * same tokens in the same order, then the controller's extra fields.
+ *
+ * Input Parameters:
+ * line - Where to render the line
+ * len - Size of line
+ * pin - The pad's number
+ * info - What the controller reported for it
+ *
+ * Returned Value:
+ * The length of the rendered line.
+ *
+ ****************************************************************************/
+
+static size_t pinctrl_procfs_pad(FAR char *line, size_t len, uint32_t pin,
+ FAR const struct pinctrl_padinfo_s *info)
+{
+ size_t n;
+
+ n = pinctrl_procfs_append(line, len, 0, "%-4" PRIu32 " %-20s ", pin,
+ info->name[0] != '\0' ? info->name : "-");
+
+ n = pinctrl_procfs_field(line, len, n, "func", info->have,
+ PINCTRL_HAVE_FUNCTION, info->function);
+ n = pinctrl_procfs_append(line, len, n, "sel:%-16s ",
+ info->funcname[0] != '\0' ?
+ info->funcname : "-");
+ n = pinctrl_procfs_field(line, len, n, "ds", info->have,
+ PINCTRL_HAVE_STRENGTH, info->strength);
+ n = pinctrl_procfs_field(line, len, n, "pu", info->have,
+ PINCTRL_HAVE_PULL, info->pullup);
+ n = pinctrl_procfs_field(line, len, n, "pd", info->have,
+ PINCTRL_HAVE_PULL, info->pulldown);
+ n = pinctrl_procfs_field(line, len, n, "ie", info->have,
+ PINCTRL_HAVE_INPUT, info->input);
+ n = pinctrl_procfs_field(line, len, n, "smt", info->have,
+ PINCTRL_HAVE_SCHMITT, info->schmitt);
+ n = pinctrl_procfs_field(line, len, n, "slew", info->have,
+ PINCTRL_HAVE_SLEWRATE, info->slewrate);
+
+ /* The fields end with a separator; take it back so a line with no extra
+ * text does not end in a blank.
+ */
+
+ if (info->extra[0] != '\0')
+ {
+ n = pinctrl_procfs_append(line, len, n, "%s\n", info->extra);
+ }
+ else
+ {
+ if (n > 0 && line[n - 1] == ' ')
+ {
+ n--;
+ }
+
+ n = pinctrl_procfs_append(line, len, n, "\n");
+ }
+
+ /* A truncated line still has to end the record */
+
+ if (line[n - 1] != '\n')
+ {
+ line[n - 1] = '\n';
+ }
+
+ return n;
+}
+
+/****************************************************************************
+ * Name: pinctrl_procfs_read
+ *
+ * Description:
+ * Ask every registered controller to describe each of its pads, in
+ * registration order. A controller with no get_pad method contributes
+ * its name and a note.
+ *
+ * Input Parameters:
+ * filep - The open file, carrying the offset reached so far
+ * buffer - Where to return the text
+ * buflen - Size of buffer
+ *
+ * Returned Value:
+ * The number of bytes returned, zero at end of file, or a negated errno
+ * on failure.
+ *
+ ****************************************************************************/
+
+static ssize_t pinctrl_procfs_read(FAR struct file *filep,
+ FAR char *buffer, size_t buflen)
+{
+ struct pinctrl_padinfo_s info;
+ FAR struct pinctrl_entry_s *entry;
+ size_t remaining = buflen;
+ FAR char *dest = buffer;
+ off_t pos = filep->f_pos;
+ char line[192];
+ uint32_t pin;
+ size_t n;
+ int ret;
+
+ ret = nxmutex_lock(&g_pinctrl_lock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ list_for_every_entry(&g_pinctrl_list, entry, struct pinctrl_entry_s, node)
+ {
+ if (remaining == 0)
+ {
+ break;
+ }
+
+ n = snprintf(line, sizeof(line), "pinctrl%d: %" PRIu32 " pads\n",
+ entry->minor, entry->dev->npins);
+ n = procfs_memcpy(line, n, dest, remaining, &pos);
+ dest += n;
+ remaining -= n;
+
+ if (entry->dev->ops->get_pad == NULL)
+ {
+ n = snprintf(line, sizeof(line),
+ " no detail, configuration is write only\n");
+ n = procfs_memcpy(line, n, dest, remaining, &pos);
+ dest += n;
+ remaining -= n;
+ continue;
+ }
+
+ for (pin = 0; pin < entry->dev->npins && remaining > 0; pin++)
+ {
+ memset(&info, 0, sizeof(info));
+ if (entry->dev->ops->get_pad(entry->dev, pin, &info) < 0)
+ {
+ continue;
+ }
+
+ n = pinctrl_procfs_pad(line, sizeof(line), pin, &info);
+ n = procfs_memcpy(line, n, dest, remaining, &pos);
+ dest += n;
+ remaining -= n;
+ }
+ }
+
+ nxmutex_unlock(&g_pinctrl_lock);
+
+ filep->f_pos += (dest - buffer);
+ return dest - buffer;
+}
+
+/****************************************************************************
+ * Name: pinctrl_procfs_dup
+ *
+ * Description:
+ * Duplicate an open /proc/pinctrl, copying the position reached so
+ * that the new file continues where the old one had got to.
+ *
+ * Input Parameters:
+ * oldp - The open file being duplicated
+ * newp - The file structure to attach the duplicate to
+ *
+ * Returned Value:
+ * Zero on success, or a negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int pinctrl_procfs_dup(FAR const struct file *oldp,
+ FAR struct file *newp)
+{
+ FAR struct procfs_file_s *priv;
+
+ priv = kmm_zalloc(sizeof(struct procfs_file_s));
+ if (priv == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ memcpy(priv, oldp->f_priv, sizeof(struct procfs_file_s));
+ newp->f_priv = priv;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: pinctrl_procfs_stat
+ *
+ * Description:
+ * Report /proc/pinctrl as a read only regular file.
+ *
+ * Input Parameters:
+ * relpath - The path below /proc being queried
+ * buf - Where to return the status
+ *
+ * Returned Value:
+ * Zero on success, or a negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int pinctrl_procfs_stat(FAR const char *relpath, FAR struct stat *buf)
+{
+ buf->st_mode = S_IFREG | S_IROTH | S_IRGRP | S_IRUSR;
+ buf->st_size = 0;
+ buf->st_blksize = 0;
+ buf->st_blocks = 0;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: pinctrl_procfs_add
+ *
+ * Description:
+ * Remember a controller, and create /proc/pinctrl the first time one
+ * appears. procfs_register() requires that procfs is not yet mounted,
+ * which holds because controllers register during board or architecture
+ * start up.
+ *
+ * Input Parameters:
+ * dev - The controller being registered
+ * minor - Its /dev/pinctrl number, used as the /proc/pinctrl label
+ *
+ * Returned Value:
+ * None. A controller that cannot be listed is still usable, so a
+ * failure here does not fail the registration.
+ *
+ ****************************************************************************/
+
+static void pinctrl_procfs_add(FAR struct pinctrl_dev_s *dev, int minor)
+{
+ FAR struct pinctrl_entry_s *entry;
+
+ entry = kmm_zalloc(sizeof(struct pinctrl_entry_s));
+ if (entry == NULL)
+ {
+ return;
+ }
+
+ entry->dev = dev;
+ entry->minor = minor;
+
+ nxmutex_lock(&g_pinctrl_lock);
+
+ /* procfs_register() appends without checking for a duplicate, so the
+ * entry is claimed once for the lifetime of the system rather than
+ * whenever the list is empty.
+ */
+
+ if (!g_pinctrl_procfs_added)
+ {
+ procfs_register(&g_pinctrl_procfs);
+ g_pinctrl_procfs_added = true;
+ }
+
+ list_add_tail(&g_pinctrl_list, &entry->node);
+ nxmutex_unlock(&g_pinctrl_lock);
+}
+
+/****************************************************************************
+ * Name: pinctrl_procfs_remove
+ *
+ * Description:
+ * Forget a controller. /proc/pinctrl stays, since procfs has no
+ * way to withdraw an entry, and lists nothing once the last
+ * controller has gone.
+ *
+ * Input Parameters:
+ * dev - The controller being unregistered
+ * minor - Its /dev/pinctrl number
+ *
+ * Returned Value:
+ * None.
+ *
+ ****************************************************************************/
+
+static void pinctrl_procfs_remove(FAR struct pinctrl_dev_s *dev, int minor)
+{
+ FAR struct pinctrl_entry_s *entry;
+
+ nxmutex_lock(&g_pinctrl_lock);
+
+ list_for_every_entry(&g_pinctrl_list, entry, struct pinctrl_entry_s, node)
+ {
+ if (entry->dev == dev && entry->minor == minor)
+ {
+ list_delete(&entry->node);
+ kmm_free(entry);
+ break;
+ }
+ }
+
+ nxmutex_unlock(&g_pinctrl_lock);
+}
+
+#endif /* CONFIG_PINCTRL_PROCFS */
+
/****************************************************************************
* Private Functions
****************************************************************************/
@@ -200,6 +704,27 @@ static int pinctrl_ioctl(FAR struct file *filep, int cmd,
unsigned long arg)
}
break;
+ /* Command: PINCTRLC_GETPAD
+ * Description: Describe the current configuration of one pad
+ * Argument: A pointer to an instance of struct pinctrl_getpad_s
+ */
+
+ case PINCTRLC_GETPAD:
+ {
+ FAR struct pinctrl_getpad_s *getpad =
+ (FAR struct pinctrl_getpad_s *)((uintptr_t)arg);
+
+ if (dev->ops->get_pad == NULL)
+ {
+ ret = -ENOTSUP;
+ break;
+ }
+
+ memset(&getpad->info, 0, sizeof(getpad->info));
+ ret = dev->ops->get_pad(dev, getpad->pin, &getpad->info);
+ }
+ break;
+
/* Unrecognized command */
default:
@@ -225,9 +750,19 @@ static int pinctrl_ioctl(FAR struct file *filep, int cmd,
unsigned long arg)
int pinctrl_register(FAR struct pinctrl_dev_s *dev, int minor)
{
char devname[32];
+ int ret;
snprintf(devname, 16, "/dev/pinctrl%u", (unsigned int)minor);
- return register_driver(devname, &g_pinctrl_drvrops, 0600, dev);
+ ret = register_driver(devname, &g_pinctrl_drvrops, 0600, dev);
+
+#ifdef CONFIG_PINCTRL_PROCFS
+ if (ret >= 0)
+ {
+ pinctrl_procfs_add(dev, minor);
+ }
+#endif
+
+ return ret;
}
/****************************************************************************
@@ -242,6 +777,10 @@ void pinctrl_unregister(FAR struct pinctrl_dev_s *dev, int
minor)
{
char devname[32];
+#ifdef CONFIG_PINCTRL_PROCFS
+ pinctrl_procfs_remove(dev, minor);
+#endif
+
snprintf(devname, 16, "/dev/pinctrl%u", (unsigned int)minor);
(void)unregister_driver(devname);
}
diff --git a/include/nuttx/pinctrl/pinctrl.h b/include/nuttx/pinctrl/pinctrl.h
index 676b829e053..3ac3c0bac24 100644
--- a/include/nuttx/pinctrl/pinctrl.h
+++ b/include/nuttx/pinctrl/pinctrl.h
@@ -28,6 +28,10 @@
****************************************************************************/
#include <nuttx/config.h>
+#include <sys/param.h>
+
+#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
#include <nuttx/fs/ioctl.h>
@@ -56,6 +60,10 @@
* Description: Select gpio function of pinctrl pin
* Argument: The uint32_t pinctrl number
*
+ * Command: PINCTRLC_GETPAD
+ * Description: Describe the current configuration of one pad
+ * Argument: A pointer to an instance of struct pinctrl_getpad_s
+ *
*/
#define PINCTRLC_SETFUNCTION _PINCTRLIOC(1)
@@ -63,6 +71,35 @@
#define PINCTRLC_SETDRIVER _PINCTRLIOC(3)
#define PINCTRLC_SETSLEWRATE _PINCTRLIOC(4)
#define PINCTRLC_SELECTGPIO _PINCTRLIOC(5)
+#define PINCTRLC_GETPAD _PINCTRLIOC(6)
+
+/* Validity bits for struct pinctrl_padinfo_s. A field is meaningful only
+ * when its bit is set in the have member: a pad may have no function
+ * select, no bias, no drive strength.
+ */
+
+#define PINCTRL_HAVE_FUNCTION (1 << 0)
+#define PINCTRL_HAVE_STRENGTH (1 << 1)
+#define PINCTRL_HAVE_PULL (1 << 2)
+#define PINCTRL_HAVE_SLEWRATE (1 << 3)
+#define PINCTRL_HAVE_INPUT (1 << 4)
+#define PINCTRL_HAVE_SCHMITT (1 << 5)
+
+#define PINCTRL_NAME_MAX 24 /* Longest name plus a terminator */
+#define PINCTRL_EXTRA_MAX 48
+
+/* One pad in a controller's name table: the pad's name, then the name of
+ * each documented function select in select order. Pass NULL in a slot
+ * whose select the manual does not document. The compound literal sizes
+ * the array to exactly what is listed; at file scope it has static
+ * storage.
+ */
+
+#define PINCTRL_PADNAME(padname, ...) \
+ { \
+ (padname), (FAR const char *const[]){__VA_ARGS__}, \
+ nitems(((FAR const char *const[]){__VA_ARGS__})) \
+ }
/* Access macros ************************************************************/
@@ -189,6 +226,48 @@ struct pinctrl_param_s
} para;
};
+/* What a controller can say about one pad. Self contained: the strings
+ * are embedded, so the same structure serves the get_pad method and the
+ * PINCTRLC_GETPAD ioctl across the user/kernel boundary. An empty name
+ * means unnamed.
+ */
+
+struct pinctrl_padinfo_s
+{
+ uint32_t have; /* PINCTRL_HAVE_* validity bits */
+ char name[PINCTRL_NAME_MAX]; /* Pad name */
+ uint32_t function; /* Current function select */
+ char funcname[PINCTRL_NAME_MAX]; /* What that function selects */
+ uint32_t strength; /* Drive strength, hardware units */
+ bool pullup; /* Pull up enabled */
+ bool pulldown; /* Pull down enabled */
+ uint32_t slewrate; /* Slew rate, hardware units */
+ bool input; /* Input buffer enabled */
+ bool schmitt; /* Schmitt trigger enabled */
+ char extra[PINCTRL_EXTRA_MAX]; /* Controller specific key:value
+ * fields, appended to the pad's
+ * /proc/pinctrl line */
+};
+
+/* PINCTRLC_GETPAD argument */
+
+struct pinctrl_getpad_s
+{
+ uint32_t pin; /* In */
+ struct pinctrl_padinfo_s info; /* Out */
+};
+
+/* One pad in a controller's name table; declare entries with
+ * PINCTRL_PADNAME().
+ */
+
+struct pinctrl_padname_s
+{
+ FAR const char *name; /* Pad name */
+ FAR const char *const *funcs; /* Function select names, in order */
+ uint32_t nfuncs; /* Entries in funcs */
+};
+
/* pinctrl interface methods */
struct pinctrl_dev_s;
@@ -203,6 +282,20 @@ struct pinctrl_ops_s
int (*set_slewrate)(FAR struct pinctrl_dev_s *dev, uint32_t pin,
uint32_t slewrate);
int (*select_gpio)(FAR struct pinctrl_dev_s *dev, uint32_t pin);
+
+ /* Describe one pad. The only member here that reads rather than
+ * writes. Optional; a controller without it is listed in /proc/pinctrl
+ * with a note and PINCTRLC_GETPAD returns -ENOTSUP.
+ *
+ * Zero the structure, fill only what the pad really has, and set the
+ * matching PINCTRL_HAVE_* bits. Fields the structure has no member for
+ * go in extra, in the same key:value form the /proc/pinctrl renderer
+ * uses. Returns OK, or -EINVAL for a pin this controller does not
+ * have.
+ */
+
+ int (*get_pad)(FAR struct pinctrl_dev_s *dev, uint32_t pin,
+ FAR struct pinctrl_padinfo_s *info);
};
struct pinctrl_dev_s
@@ -211,11 +304,48 @@ struct pinctrl_dev_s
FAR const struct pinctrl_ops_s *ops;
- /* Internal storage used by the pinctrl may (internal to the pinctrl
- * implementation).
- */
+ /* Pads this controller has; get_pad answers for pins 0..npins-1 */
+
+ uint32_t npins;
};
+/****************************************************************************
+ * Inline Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pinctrl_padname
+ *
+ * Description:
+ * The name of pin in a PINCTRL_PADNAME() table of npads entries, or
+ * NULL if the pin is out of range or unnamed.
+ *
+ ****************************************************************************/
+
+static inline FAR const char *
+pinctrl_padname(FAR const struct pinctrl_padname_s *table, size_t npads,
+ uint32_t pin)
+{
+ return pin < npads ? table[pin].name : NULL;
+}
+
+/****************************************************************************
+ * Name: pinctrl_funcname
+ *
+ * Description:
+ * The name of function select func on pin, or NULL if the table does
+ * not document it.
+ *
+ ****************************************************************************/
+
+static inline FAR const char *
+pinctrl_funcname(FAR const struct pinctrl_padname_s *table, size_t npads,
+ uint32_t pin, uint32_t func)
+{
+ return pin < npads && func < table[pin].nfuncs ?
+ table[pin].funcs[func] : NULL;
+}
+
/****************************************************************************
* Public Function Prototypes
****************************************************************************/