This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit f745b101d02c7f16d40182ebafbd73a2b3a6f780
Author: yangguangcai <[email protected]>
AuthorDate: Fri Dec 29 16:13:27 2023 +0800

    driver/ioexpander: Support pl061 GPIO controller
    
    Add an I/O expander driver for the ARM PrimeCell PL061 GPIO
    controller (8 pins), with optional pin interrupt support
    (CONFIG_PL061_INT_ENABLE).
    
    Signed-off-by: yangguangcai <[email protected]>
---
 drivers/ioexpander/CMakeLists.txt |   4 +
 drivers/ioexpander/Kconfig        |  17 ++
 drivers/ioexpander/Make.defs      |   4 +
 drivers/ioexpander/pl061.c        | 332 ++++++++++++++++++++++++++++++++++++++
 include/nuttx/ioexpander/pl061.h  |  38 +++++
 5 files changed, 395 insertions(+)

diff --git a/drivers/ioexpander/CMakeLists.txt 
b/drivers/ioexpander/CMakeLists.txt
index 62d66604928..d574e061924 100644
--- a/drivers/ioexpander/CMakeLists.txt
+++ b/drivers/ioexpander/CMakeLists.txt
@@ -83,6 +83,10 @@ if(CONFIG_IOEXPANDER)
     list(APPEND SRCS mcp23x08.c)
   endif()
 
+  if(CONFIG_IOEXPANDER_PL061)
+    list(APPEND SRCS pl061.c)
+  endif()
+
   if(CONFIG_IOEXPANDER_MCP23X17)
     list(APPEND SRCS mcp23x17.c)
   endif()
diff --git a/drivers/ioexpander/Kconfig b/drivers/ioexpander/Kconfig
index 3f6cac94970..fa273b0325e 100644
--- a/drivers/ioexpander/Kconfig
+++ b/drivers/ioexpander/Kconfig
@@ -383,6 +383,23 @@ config PCA9557_RETRY
 
 endif # IOEXPANDER_PCA9557
 
+config IOEXPANDER_PL061
+       bool "PL061 IO expander"
+       default n
+       ---help---
+               Enable support for the PL061 IO Expander
+
+if IOEXPANDER_PL061
+
+config PL061_INT_ENABLE
+       bool "Enable PL061 Interrupt Support"
+       default n
+       select IOEXPANDER_INT_ENABLE
+       ---help---
+               Enable driver interrupt functionality
+
+endif # IOEXPANDER_PL061
+
 config IOEXPANDER_TCA64XX
        bool "TCA64XX I2C IO expander"
        default n
diff --git a/drivers/ioexpander/Make.defs b/drivers/ioexpander/Make.defs
index 7ca38196d81..998fb372ef1 100644
--- a/drivers/ioexpander/Make.defs
+++ b/drivers/ioexpander/Make.defs
@@ -78,6 +78,10 @@ ifeq ($(CONFIG_IOEXPANDER_PCF8575),y)
   CSRCS += pcf8575.c
 endif
 
+ifeq ($(CONFIG_IOEXPANDER_PL061),y)
+  CSRCS += pl061.c
+endif
+
 ifeq ($(CONFIG_IOEXPANDER_MCP23X08),y)
   CSRCS += mcp23x08.c
 endif
diff --git a/drivers/ioexpander/pl061.c b/drivers/ioexpander/pl061.c
new file mode 100644
index 00000000000..b47ff926da4
--- /dev/null
+++ b/drivers/ioexpander/pl061.c
@@ -0,0 +1,332 @@
+/****************************************************************************
+ * drivers/ioexpander/pl061.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <errno.h>
+#include <nuttx/config.h>
+#include <nuttx/ioexpander/ioexpander.h>
+#include <nuttx/ioexpander/pl061.h>
+#include <nuttx/irq.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/spinlock.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define PL061_GETREG(d,o)   (*(FAR volatile uint8_t *)((d)->base + (o)))
+#define PL061_PUTREG(d,v,o) (*((FAR volatile uint8_t *)((d)->base + (o))) = 
(v))
+#define PL061_MODREG(d,v,m,o) \
+  do \
+    { \
+      irqstate_t flags = spin_lock_irqsave(&d->lock); \
+      PL061_PUTREG(d, ((PL061_GETREG(d,o) & ~(m)) | ((v) & (m))), o); \
+      spin_unlock_irqrestore(&d->lock, flags); \
+    } \
+  while (0)
+
+#define PL061_GPIODIR 0x400
+#define PL061_GPIOIS  0x404
+#define PL061_GPIOIBE 0x408
+#define PL061_GPIOIEV 0x40C
+#define PL061_GPIOIE  0x410
+#define PL061_GPIORIS 0x414
+#define PL061_GPIOMIS 0x418
+#define PL061_GPIOIC  0x41C
+
+#define PL061_GPIO_NR 8
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+#ifdef CONFIG_PL061_INT_ENABLE
+/* This type represents on registered pin interrupt callback */
+
+struct pl061_callback_s
+{
+  ioe_pinset_t pinset;   /* Set of pin interrupts that the callback */
+  ioe_callback_t cbfunc; /* The saved callback function pointer */
+  FAR void *cbarg;
+};
+#endif
+
+struct pl061_dev_s
+{
+  struct ioexpander_dev_s dev; /* Allow casting as public gpio expander */
+  uintptr_t base;
+  spinlock_t lock;
+
+#ifdef CONFIG_PL061_INT_ENABLE
+  /* Saved callback information for each I/O expander client */
+
+  struct pl061_callback_s cb[PL061_GPIO_NR];
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int pl061_direction(FAR struct ioexpander_dev_s *dev,
+                           uint8_t pin, int dir);
+static int pl061_option(FAR struct ioexpander_dev_s *dev, uint8_t pin,
+                        int opt, FAR void *val);
+static int pl061_writepin(FAR struct ioexpander_dev_s *dev, uint8_t pin,
+                          bool value);
+static int pl061_readpin(FAR struct ioexpander_dev_s *dev, uint8_t pin,
+                         FAR bool *value);
+#ifdef CONFIG_PL061_INT_ENABLE
+static FAR void *pl061_attach(FAR struct ioexpander_dev_s *dev,
+                              ioe_pinset_t pinset, ioe_callback_t callback,
+                              FAR void *arg);
+static int pl061_detach(FAR struct ioexpander_dev_s *dev,
+                        FAR void *handle);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct ioexpander_ops_s g_pl061_ops =
+{
+  pl061_direction,
+  pl061_option,
+  pl061_writepin,
+  pl061_readpin,
+  NULL
+#ifdef CONFIG_IOEXPANDER_MULTIPIN
+  , NULL
+  , NULL
+  , NULL
+#endif
+#ifdef CONFIG_PL061_INT_ENABLE
+  , pl061_attach
+  , pl061_detach
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static int pl061_direction(FAR struct ioexpander_dev_s *dev,
+                           uint8_t pin, int dir)
+{
+  FAR struct pl061_dev_s *priv = (FAR struct pl061_dev_s *)dev;
+
+  DEBUGASSERT(priv != NULL &&  pin < PL061_GPIO_NR);
+
+  if (dir == IOEXPANDER_DIRECTION_OUT)
+    {
+      PL061_MODREG(priv, 1 << pin, 1 << pin, PL061_GPIODIR);
+    }
+  else if (dir == IOEXPANDER_DIRECTION_IN)
+    {
+      PL061_MODREG(priv, 0, 1 << pin, PL061_GPIODIR);
+    }
+  else
+    {
+      return -EINVAL;
+    }
+
+  return 0;
+}
+
+static int pl061_option(FAR struct ioexpander_dev_s *dev, uint8_t pin,
+                        int opt, FAR void *val)
+{
+  FAR struct pl061_dev_s *priv = (FAR struct pl061_dev_s *)dev;
+  unsigned int ival = (unsigned int)((uintptr_t)val);
+
+  if (opt == IOEXPANDER_OPTION_INTCFG)
+    {
+      /* Set the pin option */
+
+      switch (ival)
+        {
+          case IOEXPANDER_VAL_HIGH:    /* Interrupt on high level */
+            PL061_MODREG(priv, 1 << pin, 1 << pin, PL061_GPIOIS);
+            PL061_MODREG(priv, 1 << pin, 1 << pin, PL061_GPIOIEV);
+            PL061_MODREG(priv, 1 << pin, 1 << pin, PL061_GPIOIE);
+            break;
+
+          case IOEXPANDER_VAL_LOW:     /* Interrupt on low level */
+            PL061_MODREG(priv, 1 << pin, 1 << pin, PL061_GPIOIS);
+            PL061_MODREG(priv, 0, 1 << pin, PL061_GPIOIEV);
+            PL061_MODREG(priv, 1 << pin, 1 << pin, PL061_GPIOIE);
+            break;
+
+          case IOEXPANDER_VAL_EDGE:  /* Interrupt on edge */
+            PL061_MODREG(priv, 0, 1 << pin, PL061_GPIOIS);
+            PL061_MODREG(priv, 1 << pin, 1 << pin, PL061_GPIOIBE);
+            PL061_MODREG(priv, 1 << pin, 1 << pin, PL061_GPIOIE);
+            break;
+
+          case IOEXPANDER_VAL_RISING:  /* Interrupt on rising edge */
+            PL061_MODREG(priv, 0, 1 << pin, PL061_GPIOIS);
+            PL061_MODREG(priv, 0, 1 << pin, PL061_GPIOIBE);
+            PL061_MODREG(priv, 1 << pin, 1 << pin, PL061_GPIOIEV);
+            PL061_MODREG(priv, 1 << pin, 1 << pin, PL061_GPIOIE);
+            break;
+
+          case IOEXPANDER_VAL_FALLING: /* Interrupt on falling edge */
+            PL061_MODREG(priv, 0, 1 << pin, PL061_GPIOIS);
+            PL061_MODREG(priv, 0, 1 << pin, PL061_GPIOIBE);
+            PL061_MODREG(priv, 0, 1 << pin, PL061_GPIOIEV);
+            PL061_MODREG(priv, 1 << pin, 1 << pin, PL061_GPIOIE);
+            break;
+
+          case IOEXPANDER_VAL_DISABLE: /* Disable Interrupt */
+            PL061_MODREG(priv, 0, 1 << pin, PL061_GPIOIE);
+            break;
+
+          default:
+            return -EINVAL;
+        }
+    }
+  else
+    {
+      return -EINVAL;
+    }
+
+  return 0;
+}
+
+static int pl061_writepin(FAR struct ioexpander_dev_s *dev, uint8_t pin,
+                          bool value)
+{
+  FAR struct pl061_dev_s *priv = (FAR struct pl061_dev_s *)dev;
+
+  PL061_PUTREG(priv, value << pin, 1 << (pin + 2));
+  return 0;
+}
+
+static int pl061_readpin(FAR struct ioexpander_dev_s *dev, uint8_t pin,
+                         FAR bool *value)
+{
+  FAR struct pl061_dev_s *priv = (FAR struct pl061_dev_s *)dev;
+
+  *value = (PL061_GETREG(priv, 1 << (pin + 2)) >> pin) & 1;
+  return 0;
+}
+
+#ifdef CONFIG_PL061_INT_ENABLE
+static FAR void *pl061_attach(FAR struct ioexpander_dev_s *dev,
+                              ioe_pinset_t pinset, ioe_callback_t callback,
+                              FAR void *arg)
+{
+  FAR struct pl061_dev_s *priv = (FAR struct pl061_dev_s *)dev;
+  FAR void *handle = NULL;
+  irqstate_t flags;
+  int i;
+
+  flags = spin_lock_irqsave(&priv->lock);
+  for (i = 0; i < PL061_GPIO_NR; i++)
+    {
+      /* Is this entry available (i.e., no callback attached) */
+
+      if (priv->cb[i].cbfunc == NULL)
+        {
+          /* Yes.. use this entry */
+
+          priv->cb[i].pinset = pinset;
+          priv->cb[i].cbfunc = callback;
+          priv->cb[i].cbarg = arg;
+          handle = &priv->cb[i];
+          break;
+        }
+    }
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+  return handle;
+}
+
+static int pl061_detach(FAR struct ioexpander_dev_s *dev,
+                        FAR void *handle)
+{
+  FAR struct pl061_callback_s *cb = handle;
+
+  cb->pinset = 0;
+  cb->cbfunc = NULL;
+  cb->cbarg  = NULL;
+  return 0;
+}
+
+static int pl061_interrupt(int irq, void *context, void *arg)
+{
+  FAR struct pl061_dev_s *priv = arg;
+  uint8_t pinset = PL061_GETREG(priv, PL061_GPIOMIS);
+  int i;
+
+  /* Perform pin interrupt callbacks */
+
+  for (i = 0; i < PL061_GPIO_NR; i++)
+    {
+      /* Is this entry valid (i.e., callback attached)?  If so, did
+       * any of the requested pin interrupts occur?
+       */
+
+      if (priv->cb[i].cbfunc != NULL)
+        {
+          /* Did any of the requested pin interrupts occur? */
+
+          ioe_pinset_t match = pinset & priv->cb[i].pinset;
+
+          if (match != 0)
+            {
+              /* Yes.. perform the callback */
+
+              priv->cb[i].cbfunc(&priv->dev, match,
+                                 priv->cb[i].cbarg);
+            }
+        }
+    }
+
+  PL061_PUTREG(priv, pinset, PL061_GPIOIC);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+FAR struct ioexpander_dev_s *pl061_ioe_initialize(uintptr_t base, int irq)
+{
+  FAR struct pl061_dev_s *priv = kmm_zalloc(sizeof(struct pl061_dev_s));
+
+  priv->dev.ops = &g_pl061_ops;
+  priv->base = base;
+
+#ifdef CONFIG_PL061_INT_ENABLE
+  /* Attach the I/O expander interrupt handler and enable interrupts */
+
+  irq_attach(irq, pl061_interrupt, priv);
+  up_enable_irq(irq);
+
+#endif
+
+  return &priv->dev;
+}
diff --git a/include/nuttx/ioexpander/pl061.h b/include/nuttx/ioexpander/pl061.h
new file mode 100644
index 00000000000..3996e17e271
--- /dev/null
+++ b/include/nuttx/ioexpander/pl061.h
@@ -0,0 +1,38 @@
+/****************************************************************************
+ * include/nuttx/ioexpander/pl061.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_NUTTX_IOEXPANDER_PL061_H
+#define __INCLUDE_NUTTX_IOEXPANDER_PL061_H
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pl061_ioe_initialize
+ *
+ * Description:
+ *   Initialize pl061 device.
+ *
+ ****************************************************************************/
+
+FAR struct ioexpander_dev_s *pl061_ioe_initialize(uintptr_t base, int irq);
+
+#endif  //__INCLUDE_NUTTX_IOEXPANDER_PL061_H

Reply via email to