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

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

commit 4f3b818c3f66d0906f8dd8c282021d5005477c3b
Author: halak0013 <[email protected]>
AuthorDate: Thu Sep 10 09:08:51 2026 +0300

    arch/arm/am67: Add GPIO and MCU_MCSPI0 master driver.
    
    Adds the AM67 GPIO lower half and a polled MCU_MCSPI0 master driver, with 
the
    pad configuration both need.  The K3 instance is not the OMAP2 layout: an HL
    header block precedes the functional registers.
    
    Chip select is released only after CHSTAT.EOT, since a high SCLK otherwise
    drops it mid-word and truncates the write, and CHCTRL.EN stays asserted 
between
    transfers.
    
    t3-gem-o1 registers /dev/spi0 for its ICM-20948 (CS3) and LPS22DF (CS1), and
    raises NSH_MAXARGUMENTS to 16 so the spi tool can address a device.
    
    Verified on t3-gem-o1: WHO_AM_I reads 0xEA on CS3 and 0xB4 on CS1, and the
    ICM-20948 streams continuous accelerometer samples over the bus.
    
    Co-authored-by: Ulaş Sertan Kemeç <[email protected]>
    Assisted-by: Cursor
    Assisted-by: Claude Code:claude-opus-4-8
    Signed-off-by: Ulaş Sertan Kemeç <[email protected]>
---
 arch/arm/src/am67/Kconfig                          |  24 +
 arch/arm/src/am67/Make.defs                        |   7 +
 arch/arm/src/am67/am67_gpio.c                      | 301 +++++++++++
 .../arm/src/am67/am67_gpio.h                       |  73 +--
 arch/arm/src/am67/am67_mcspi.c                     | 554 +++++++++++++++++++++
 arch/arm/src/am67/am67_mcspi.h                     | 126 +++++
 arch/arm/src/am67/am67_pinmux.c                    | 114 +++++
 arch/arm/src/am67/am67_pinmux.h                    |   4 +-
 boards/arm/am67/t3-gem-o1/configs/nsh/defconfig    |   4 +
 boards/arm/am67/t3-gem-o1/src/Makefile             |   5 +
 boards/arm/am67/t3-gem-o1/src/am67_bringup.c       |  11 +
 boards/arm/am67/t3-gem-o1/src/am67_spi.c           | 127 +++++
 boards/arm/am67/t3-gem-o1/src/t3-gem-o1.h          |  23 +-
 13 files changed, 1327 insertions(+), 46 deletions(-)

diff --git a/arch/arm/src/am67/Kconfig b/arch/arm/src/am67/Kconfig
index d461a787ab4..5ae7c094a1c 100644
--- a/arch/arm/src/am67/Kconfig
+++ b/arch/arm/src/am67/Kconfig
@@ -4,3 +4,27 @@
 #
 
 comment "AM67 Configuration Options"
+
+config AM67_GPIO
+       bool "AM67 padconfig GPIO driver"
+       default y
+       ---help---
+               Enable the AM67 GPIO driver used by PX4 board support and
+               example applications (LED, IMU_EN, SPI CS padconfig).
+
+
+config AM67_MCSPI0
+       bool "AM67 MCU_MCSPI0 master driver"
+       default n
+       depends on SPI
+       ---help---
+               Enable polled SPI master on MCU_MCSPI0 @ 0x04b00000 (onboard
+               ICM-20948 CS3 and LPS22DF CS1).
+
+if AM67_MCSPI0
+
+config AM67_MCSPI0_FCLK
+       int "MCU_MCSPI0 functional clock (Hz)"
+       default 48000000
+
+endif
diff --git a/arch/arm/src/am67/Make.defs b/arch/arm/src/am67/Make.defs
index 89a92240ab4..f464f63ae73 100644
--- a/arch/arm/src/am67/Make.defs
+++ b/arch/arm/src/am67/Make.defs
@@ -30,3 +30,10 @@ CHIP_CSRCS += am67_mpuinit.c
 CHIP_CSRCS += am67_pinmux.c
 CHIP_CSRCS += am67_serial.c
 CHIP_CSRCS += am67_timer.c
+ifeq ($(CONFIG_AM67_GPIO),y)
+CHIP_CSRCS += am67_gpio.c
+endif
+ifeq ($(CONFIG_AM67_MCSPI0),y)
+CHIP_CSRCS += am67_mcspi.c
+endif
+CHIP_CSRCS += arm_mpu.c
diff --git a/arch/arm/src/am67/am67_gpio.c b/arch/arm/src/am67/am67_gpio.c
new file mode 100644
index 00000000000..a63f887f974
--- /dev/null
+++ b/arch/arm/src/am67/am67_gpio.c
@@ -0,0 +1,301 @@
+/****************************************************************************
+ * arch/arm/src/am67/am67_gpio.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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 <nuttx/config.h>
+#include <assert.h>
+#include <stdint.h>
+
+#include "am67_gpio.h"
+#include "am67_pinmux.h"
+#include "chip.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define GPIO_PINS_PER_REG_SHIFT  (5U)
+#define GPIO_DIRECTION_OUTPUT    (0U)
+#define GPIO_DIRECTION_INPUT     (1U)
+
+#define MCU_GPIO0_BASE           0x4201000UL
+#define MAIN_GPIO1_BASE          0x601000UL
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct am67_gpio_desc_s
+{
+  uint32_t base;
+  uint32_t pin;
+  int16_t  main_pad;
+  int16_t  mcu_pad;
+  uint32_t pad_setting;
+  bool     active_low;
+  bool     pad_only;
+};
+
+typedef struct
+{
+  volatile uint32_t DIR;
+  volatile uint32_t OUT_DATA;
+  volatile uint32_t SET_DATA;
+  volatile uint32_t CLR_DATA;
+  volatile uint32_t IN_DATA;
+  volatile uint32_t SET_RIS_TRIG;
+  volatile uint32_t CLR_RIS_TRIG;
+  volatile uint32_t SET_FAL_TRIG;
+  volatile uint32_t CLR_FAL_TRIG;
+  volatile uint32_t INTSTAT;
+} gpio_bank_regs_t;
+
+typedef struct
+{
+  volatile uint32_t PID;
+  volatile uint32_t PCR;
+  volatile uint32_t BINTEN;
+  volatile uint8_t  RSVD0[4];
+  gpio_bank_regs_t  BANK_REGISTERS[9];
+} gpio_regs_t;
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct am67_gpio_desc_s g_am67_gpio_table[AM67_GPIO_ID_COUNT] =
+{
+  {
+    MAIN_GPIO1_BASE, 52,
+    PIN_OLDI0_A0P, -1,
+    (PIN_MODE(7) | PIN_PULL_DISABLE),
+    false, false
+  },
+  {
+    MAIN_GPIO1_BASE, 53,
+    PIN_OLDI0_A0N, -1,
+    (PIN_MODE(7) | PIN_PULL_DISABLE),
+    false, false
+  },
+  {
+    0, 0,
+    -1, PIN_MCU_SPI0_CS1,
+    (PIN_MODE(0) | PIN_PULL_DISABLE),
+    true, true
+  },
+  {
+    0, 0,
+    -1, PIN_MCU_MCAN0_TX,
+    (PIN_MODE(2) | PIN_PULL_DISABLE),
+    true, true
+  },
+  {
+    MCU_GPIO0_BASE, 12,
+    -1, PIN_WKUP_UART0_RTSN,
+    (PIN_MODE(7) | PIN_PULL_DISABLE),
+    true, false   /* active_low: LOW activates IMU per board design */
+  },
+  {
+    0, 0,
+    -1, PIN_WKUP_UART0_RXD,
+    (PIN_MODE(2) | PIN_PULL_DISABLE),
+    true, true   /* CS2: hardware pin, pad_only */
+  },
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static inline uint32_t gpio_reg_index(uint32_t pin)
+{
+  return pin >> GPIO_PINS_PER_REG_SHIFT;
+}
+
+static inline uint32_t gpio_bit_pos(uint32_t pin)
+{
+  return pin - (gpio_reg_index(pin) << GPIO_PINS_PER_REG_SHIFT);
+}
+
+static inline uint32_t gpio_bit_mask(uint32_t pin)
+{
+  return ((uint32_t)1U) << gpio_bit_pos(pin);
+}
+
+static inline void gpio_set_dir(uint32_t base, uint32_t pin, uint32_t dir)
+{
+  gpio_regs_t *gpio = (gpio_regs_t *)(uintptr_t)base;
+  uint32_t reg_index = gpio_reg_index(pin);
+  uint32_t bit_pos = gpio_bit_pos(pin);
+
+  gpio->BANK_REGISTERS[reg_index].DIR &= ~gpio_bit_mask(pin);
+  gpio->BANK_REGISTERS[reg_index].DIR |= ((dir & 0x01u) << bit_pos);
+}
+
+static inline void gpio_write_high(uint32_t base, uint32_t pin)
+{
+  gpio_regs_t *gpio = (gpio_regs_t *)(uintptr_t)base;
+  uint32_t reg_index = gpio_reg_index(pin);
+
+  gpio->BANK_REGISTERS[reg_index].SET_DATA = gpio_bit_mask(pin);
+}
+
+static inline void gpio_write_low(uint32_t base, uint32_t pin)
+{
+  gpio_regs_t *gpio = (gpio_regs_t *)(uintptr_t)base;
+  uint32_t reg_index = gpio_reg_index(pin);
+
+  gpio->BANK_REGISTERS[reg_index].CLR_DATA = gpio_bit_mask(pin);
+}
+
+static inline bool gpio_read_level(uint32_t base, uint32_t pin)
+{
+  gpio_regs_t *gpio = (gpio_regs_t *)(uintptr_t)base;
+  uint32_t reg_index = gpio_reg_index(pin);
+  uint32_t reg_val = gpio->BANK_REGISTERS[reg_index].IN_DATA;
+
+  return (reg_val & gpio_bit_mask(pin)) != 0;
+}
+
+static const struct am67_gpio_desc_s *
+am67_gpio_desc(am67_gpio_t gpio)
+{
+  if (gpio >= AM67_GPIO_ID_COUNT)
+    {
+      return NULL;
+    }
+
+  return &g_am67_gpio_table[gpio];
+}
+
+static void am67_gpio_apply_padconfig(const struct am67_gpio_desc_s *desc)
+{
+  struct pinmux_conf_s conf[2];
+
+  if (desc->main_pad >= 0)
+    {
+      conf[0].offset = desc->main_pad;
+      conf[0].setting = desc->pad_setting;
+      conf[1].offset = PINMUX_END;
+      conf[1].setting = PINMUX_END;
+      am67_pinmux_config(conf);
+    }
+  else if (desc->mcu_pad >= 0)
+    {
+      conf[0].offset = desc->mcu_pad;
+      conf[0].setting = desc->pad_setting;
+      conf[1].offset = PINMUX_END;
+      conf[1].setting = PINMUX_END;
+      am67_mcu_pinmux_config(conf);
+    }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+am67_gpio_t am67_gpio_hat(unsigned int hat_pin)
+{
+  (void)hat_pin;
+  return AM67_GPIO_ID_COUNT;
+}
+
+void am67_configgpio(am67_gpio_t gpio, int pintype)
+{
+  const struct am67_gpio_desc_s *desc = am67_gpio_desc(gpio);
+
+  DEBUGASSERT(desc != NULL);
+
+  am67_gpio_apply_padconfig(desc);
+
+  if (desc->pad_only || desc->base == 0)
+    {
+      return;
+    }
+
+  if (pintype == GPIO_INPUT)
+    {
+      gpio_set_dir(desc->base, desc->pin, GPIO_DIRECTION_INPUT);
+    }
+  else
+    {
+      gpio_set_dir(desc->base, desc->pin, GPIO_DIRECTION_OUTPUT);
+    }
+}
+
+void am67_gpiowrite(am67_gpio_t gpio, bool value)
+{
+  const struct am67_gpio_desc_s *desc = am67_gpio_desc(gpio);
+  bool level = value;
+
+  DEBUGASSERT(desc != NULL);
+
+  if (desc->pad_only || desc->base == 0)
+    {
+      return;
+    }
+
+  if (desc->active_low)
+    {
+      level = !level;
+    }
+
+  if (level)
+    {
+      gpio_write_high(desc->base, desc->pin);
+    }
+  else
+    {
+      gpio_write_low(desc->base, desc->pin);
+    }
+}
+
+bool am67_gpioread(am67_gpio_t gpio)
+{
+  const struct am67_gpio_desc_s *desc = am67_gpio_desc(gpio);
+  bool level;
+
+  DEBUGASSERT(desc != NULL);
+
+  if (desc->pad_only || desc->base == 0)
+    {
+      return false;
+    }
+
+  level = gpio_read_level(desc->base, desc->pin);
+
+  if (desc->active_low)
+    {
+      level = !level;
+    }
+
+  return level;
+}
+
+void am67_sensors_power_enable(bool enable)
+{
+  am67_configgpio(AM67_GPIO_MCU0_PIN12, GPIO_OUTPUT);
+  am67_gpiowrite(AM67_GPIO_MCU0_PIN12, enable);
+}
diff --git a/boards/arm/am67/t3-gem-o1/src/am67_bringup.c 
b/arch/arm/src/am67/am67_gpio.h
similarity index 53%
copy from boards/arm/am67/t3-gem-o1/src/am67_bringup.c
copy to arch/arm/src/am67/am67_gpio.h
index 33fcd40a109..67103280204 100644
--- a/boards/arm/am67/t3-gem-o1/src/am67_bringup.c
+++ b/arch/arm/src/am67/am67_gpio.h
@@ -1,5 +1,5 @@
 /****************************************************************************
- * boards/arm/am67/t3-gem-o1/src/am67_bringup.c
+ * arch/arm/src/am67/am67_gpio.h
  *
  * SPDX-License-Identifier: Apache-2.0
  *
@@ -20,47 +20,58 @@
  *
  ****************************************************************************/
 
+#ifndef __ARCH_ARM_SRC_AM67_AM67_GPIO_H
+#define __ARCH_ARM_SRC_AM67_AM67_GPIO_H
+
 /****************************************************************************
  * Included Files
  ****************************************************************************/
 
 #include <nuttx/config.h>
-#include <nuttx/fs/fs.h>
-#include <debug.h>
-
-#include "t3-gem-o1.h"
+#include <stdbool.h>
+#include <stdint.h>
 
 /****************************************************************************
- * Public Functions
+ * Pre-processor Definitions
  ****************************************************************************/
 
-/****************************************************************************
- * Name: am67_bringup
- *
- * Description:
- *   Perform architecture-specific initialization.
- *
- *   CONFIG_BOARD_LATE_INITIALIZE=y :
- *     Called from board_late_initialize().
- *
- *   CONFIG_BOARD_LATE_INITIALIZE=n && CONFIG_BOARDCTL=y :
- *     Called from the NSH library.
- *
- ****************************************************************************/
+/* PX4 board_config.h compatibility */
 
-int am67_bringup(void)
+#define GPIO_OUTPUT           0
+#define GPIO_INPUT            1
+
+typedef uint32_t am67_gpio_t;
+
+enum am67_gpio_id_e
 {
-  int ret = OK;
+  AM67_GPIO_ID_LED_GREEN = 0,
+  AM67_GPIO_ID_LED_RED,
+  AM67_GPIO_ID_SPI_CS1,
+  AM67_GPIO_ID_SPI_CS3,
+  AM67_GPIO_ID_IMU_EN,
+  AM67_GPIO_ID_SPI_CS2,
+  AM67_GPIO_ID_COUNT
+};
+
+#define AM67_GPIO_OLDI0_A0P  ((am67_gpio_t)AM67_GPIO_ID_LED_GREEN)
+#define AM67_GPIO_OLDI0_A0N  ((am67_gpio_t)AM67_GPIO_ID_LED_RED)
+#define AM67_GPIO_HAT_CS1    ((am67_gpio_t)AM67_GPIO_ID_SPI_CS1)
+#define AM67_GPIO_HAT_CS3    ((am67_gpio_t)AM67_GPIO_ID_SPI_CS3)
+#define AM67_GPIO_MCU0_PIN12 ((am67_gpio_t)AM67_GPIO_ID_IMU_EN)
+#define AM67_GPIO_HAT_CS2    ((am67_gpio_t)AM67_GPIO_ID_SPI_CS2)
+
+/* HAT header GPIO mapping — filled in during F4 PWM bring-up */
 
-#ifdef CONFIG_FS_PROCFS
-  /* Mount the procfs file system */
+#define AM67_GPIO_HAT(n)     am67_gpio_hat(n)
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
 
-  ret = nx_mount(NULL, "/proc", "procfs", 0, NULL);
-  if (ret < 0)
-    {
-      syslog(LOG_ERR, "ERROR: Failed to mount procfs at /proc: %d\n", ret);
-    }
-#endif
+am67_gpio_t am67_gpio_hat(unsigned int hat_pin);
+void am67_configgpio(am67_gpio_t gpio, int pintype);
+void am67_gpiowrite(am67_gpio_t gpio, bool value);
+bool am67_gpioread(am67_gpio_t gpio);
+void am67_sensors_power_enable(bool enable);
 
-  return ret;
-}
+#endif /* __ARCH_ARM_SRC_AM67_AM67_GPIO_H */
diff --git a/arch/arm/src/am67/am67_mcspi.c b/arch/arm/src/am67/am67_mcspi.c
new file mode 100644
index 00000000000..56db769f48f
--- /dev/null
+++ b/arch/arm/src/am67/am67_mcspi.c
@@ -0,0 +1,554 @@
+/****************************************************************************
+ * arch/arm/src/am67/am67_mcspi.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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 <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/mutex.h>
+#include <nuttx/spi/spi.h>
+
+#include "am67_mcspi.h"
+#include "am67_pinmux.h"
+#include "arm_internal.h"
+
+#ifdef CONFIG_AM67_MCSPI0
+
+/****************************************************************************
+ * Private Function Prototypes (board-provided)
+ ****************************************************************************/
+
+void am67_spi0select(FAR struct spi_dev_s *dev, uint32_t devid,
+                     bool selected);
+uint8_t am67_spi0status(FAR struct spi_dev_s *dev, uint32_t devid);
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define AM67_MCSPI_MAX_DIVIDER      4096u
+#define AM67_MCSPI_POLL_TIMEOUT     1000000u
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct am67_mcspi_dev_s
+{
+  struct spi_dev_s spidev;
+  uint32_t         base;
+  mutex_t          lock;
+  uint8_t          channel;
+  uint32_t         frequency;
+  uint8_t          mode;
+  uint8_t          nbits;
+  uint32_t         chconf;
+  uint32_t         chctrl;
+  bool             selected;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int       am67_mcspi_lock(FAR struct spi_dev_s *dev, bool lock);
+static uint32_t  am67_mcspi_setfrequency(FAR struct spi_dev_s *dev,
+                                         uint32_t frequency);
+static void      am67_mcspi_setmode(FAR struct spi_dev_s *dev,
+                                    enum spi_mode_e mode);
+static void      am67_mcspi_setbits(FAR struct spi_dev_s *dev, int nbits);
+static uint32_t  am67_mcspi_send(FAR struct spi_dev_s *dev, uint32_t wd);
+#ifdef CONFIG_SPI_EXCHANGE
+static void      am67_mcspi_exchange(FAR struct spi_dev_s *dev,
+                                     FAR const void *txbuffer,
+                                     FAR void *rxbuffer, size_t nwords);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct spi_ops_s g_am67_spi0ops =
+{
+  .lock         = am67_mcspi_lock,
+  .select       = am67_spi0select,
+  .setfrequency = am67_mcspi_setfrequency,
+  .setmode      = am67_mcspi_setmode,
+  .setbits      = am67_mcspi_setbits,
+  .status       = am67_spi0status,
+  .send         = am67_mcspi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange     = am67_mcspi_exchange,
+#endif
+};
+
+static struct am67_mcspi_dev_s g_spi0dev =
+{
+  .spidev =
+    {
+      &g_am67_spi0ops
+    },
+  .base       = AM67_MCSPI0_BASE,
+  .lock       = NXMUTEX_INITIALIZER,
+  .channel    = 0,
+  .frequency  = 1000000,
+  .mode       = SPIDEV_MODE3,
+  .nbits      = 8,
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static inline uint32_t am67_mcspi_getreg(uint32_t base, uint32_t offset)
+{
+  return getreg32(base + offset);
+}
+
+static inline void am67_mcspi_putreg(uint32_t base, uint32_t offset,
+                                     uint32_t value)
+{
+  putreg32(value, base + offset);
+}
+
+static inline FAR struct am67_mcspi_dev_s *
+am67_mcspi_dev(FAR struct spi_dev_s *dev)
+{
+  return (FAR struct am67_mcspi_dev_s *)dev;
+}
+
+static bool am67_mcspi_waitstat(uint32_t base, uint8_t channel,
+                                uint32_t mask)
+{
+  volatile uint32_t count = AM67_MCSPI_POLL_TIMEOUT;
+
+  while ((am67_mcspi_getreg(base, AM67_MCSPI_CHSTAT0 +
+                            AM67_MCSPI_CH_OFFSET(channel)) & mask) == 0)
+    {
+      if (--count == 0)
+        {
+          return false;
+        }
+    }
+
+  return true;
+}
+
+static void am67_mcspi_channel_enable(FAR struct am67_mcspi_dev_s *priv,
+                                      bool enable)
+{
+  uint32_t chctrl = priv->chctrl;
+
+  if (enable)
+    {
+      chctrl |= AM67_MCSPI_CHCTRL_EN;
+    }
+  else
+    {
+      chctrl &= ~AM67_MCSPI_CHCTRL_EN;
+    }
+
+  priv->chctrl = chctrl;
+  am67_mcspi_putreg(priv->base,
+                    AM67_MCSPI_CHCTRL0 + AM67_MCSPI_CH_OFFSET(priv->channel),
+                    chctrl);
+}
+
+static void am67_mcspi_cs_force(FAR struct am67_mcspi_dev_s *priv,
+                                bool deassert)
+{
+  uint32_t chconf = priv->chconf;
+
+  if (deassert)
+    {
+      /* Wait for EOT so CS isn't dropped mid-word at high SCLK, which
+       * truncates writes (e.g. the ICM-20948 bank-switch).
+       */
+
+      am67_mcspi_waitstat(priv->base, priv->channel, AM67_MCSPI_CHSTAT_EOT);
+      chconf &= ~AM67_MCSPI_CHCONF_FORCE;  /* FORCE=0: CS deasserted (high) */
+    }
+  else
+    {
+      chconf |= AM67_MCSPI_CHCONF_FORCE;   /* FORCE=1: CS asserted (low, 
EPOL=1) */
+    }
+
+  priv->chconf = chconf;
+  am67_mcspi_putreg(priv->base,
+                    AM67_MCSPI_CHCONF0 + AM67_MCSPI_CH_OFFSET(priv->channel),
+                    chconf);
+
+  /* Keep the channel enabled between transfers; toggling it re-inits the
+   * shift logic and can drop the next transfer's first word. CS is driven
+   * by FORCE above, so an idle enabled channel generates no clock.
+   */
+
+  if (!deassert)
+    {
+      am67_mcspi_channel_enable(priv, true);
+    }
+}
+
+static uint8_t am67_mcspi_calc_divisor(uint32_t speed_hz,
+                                       uint32_t ref_clk_hz)
+{
+  uint8_t clkd;
+
+  for (clkd = 0; clkd < 15; clkd++)
+    {
+      if (speed_hz >= (ref_clk_hz >> (clkd + 1)))
+        {
+          break;
+        }
+    }
+
+  return clkd;
+}
+
+static void am67_mcspi_apply_hwconfig(FAR struct am67_mcspi_dev_s *priv)
+{
+  uint32_t ref_clk = CONFIG_AM67_MCSPI0_FCLK;
+  uint32_t speed_hz = priv->frequency;
+  uint32_t chconf;
+  uint32_t chctrl = priv->chctrl & AM67_MCSPI_CHCTRL_EN; /* preserve EN bit */
+  uint8_t clkd;
+  uint32_t div;
+  uint32_t extclk = 0;
+
+  if (speed_hz > ref_clk)
+    {
+      speed_hz = ref_clk;
+    }
+
+  if (speed_hz < (ref_clk / AM67_MCSPI_MAX_DIVIDER))
+    {
+      clkd = am67_mcspi_calc_divisor(speed_hz, ref_clk);
+      speed_hz = ref_clk >> (clkd + 1);
+      chconf = 0;
+      chctrl &= ~AM67_MCSPI_CHCTRL_EXTCLK_MASK;
+    }
+  else
+    {
+      div = (ref_clk + speed_hz - 1) / speed_hz;
+      speed_hz = ref_clk / div;
+      clkd = (div - 1) & 0x0fu;
+      extclk = (div - 1) >> 4;
+      chconf = AM67_MCSPI_CHCONF_CLKG;
+      chctrl &= ~AM67_MCSPI_CHCTRL_EXTCLK_MASK;
+      chctrl |= (extclk << AM67_MCSPI_CHCTRL_EXTCLK_SHIFT) &
+                AM67_MCSPI_CHCTRL_EXTCLK_MASK;
+    }
+
+  priv->frequency = speed_hz;
+
+  chconf |= AM67_MCSPI_CHCONF_IS;    /* IS=1: receive from D1 (MISO) */
+  chconf &= ~AM67_MCSPI_CHCONF_DPE0; /* DPE0=0: TX enabled on D0 (MOSI) */
+  chconf |= AM67_MCSPI_CHCONF_DPE1;  /* DPE1=1: TX disabled on D1 */
+  chconf |= AM67_MCSPI_CHCONF_EPOL;  /* EPOL=1: CS active-low */
+  chconf &= ~AM67_MCSPI_CHCONF_WL_MASK;
+  chconf |= ((uint32_t)(priv->nbits - 1) << AM67_MCSPI_CHCONF_WL_SHIFT);
+  chconf &= ~AM67_MCSPI_CHCONF_CLKD_MASK;
+  chconf |= ((uint32_t)clkd << AM67_MCSPI_CHCONF_CLKD_SHIFT);
+
+  if ((priv->mode & SPIDEV_MODE2) != 0)
+    {
+      chconf |= AM67_MCSPI_CHCONF_POL;
+    }
+  else
+    {
+      chconf &= ~AM67_MCSPI_CHCONF_POL;
+    }
+
+  if ((priv->mode & SPIDEV_MODE1) != 0)
+    {
+      chconf |= AM67_MCSPI_CHCONF_PHA;
+    }
+  else
+    {
+      chconf &= ~AM67_MCSPI_CHCONF_PHA;
+    }
+
+  /* SPIENSLV: route this channel to its natural CS pin (chN uses CSN) */
+
+  chconf &= ~AM67_MCSPI_CHCONF_SPIENSLV_MASK;
+  chconf |= ((uint32_t)priv->channel << AM67_MCSPI_CHCONF_SPIENSLV_SHIFT) &
+             AM67_MCSPI_CHCONF_SPIENSLV_MASK;
+
+  /* Re-assert FORCE if the channel is currently selected so that
+   * reconfiguring mid-transaction does not glitch the CS line.
+   */
+
+  if (priv->selected)
+    {
+      chconf |= AM67_MCSPI_CHCONF_FORCE;
+    }
+
+  priv->chconf = chconf;
+  priv->chctrl = chctrl;
+
+  am67_mcspi_putreg(priv->base,
+                    AM67_MCSPI_CHCONF0 + AM67_MCSPI_CH_OFFSET(priv->channel),
+                    chconf);
+  am67_mcspi_putreg(priv->base,
+                    AM67_MCSPI_CHCTRL0 + AM67_MCSPI_CH_OFFSET(priv->channel),
+                    chctrl);
+
+  if (priv->selected)
+    {
+      am67_mcspi_channel_enable(priv, true);
+    }
+}
+
+static void am67_mcspi_select_channel(FAR struct am67_mcspi_dev_s *priv,
+                                      uint8_t channel)
+{
+  if (priv->channel == channel)
+    {
+      return;
+    }
+
+  am67_mcspi_channel_enable(priv, false);
+  priv->channel = channel;
+  am67_mcspi_apply_hwconfig(priv);
+}
+
+static void am67_mcspi_controller_init(FAR struct am67_mcspi_dev_s *priv)
+{
+  uint32_t regval;
+  uint32_t modulctrl;
+
+  /* Set HL_SYSCONFIG to no-idle so the OCP interconnect does not gate
+   * the functional clock while we poll status registers.
+   */
+
+  am67_mcspi_putreg(priv->base, AM67_MCSPI_HL_SYSCONFIG,
+                    AM67_MCSPI_HL_SYSCONFIG_NOIDLE);
+
+  /* Trigger the hardware soft reset */
+
+  regval = am67_mcspi_getreg(priv->base, AM67_MCSPI_SYSCONFIG);
+  regval |= AM67_MCSPI_SYSCONFIG_SOFTRESET;
+  am67_mcspi_putreg(priv->base, AM67_MCSPI_SYSCONFIG, regval);
+
+  /* Wait until SYSSTATUS.RESETDONE is 1 */
+
+  do
+    {
+      regval = am67_mcspi_getreg(priv->base, AM67_MCSPI_SYSSTATUS);
+    }
+  while ((regval & AM67_MCSPI_SYSSTATUS_RESETDONE) == 0);
+
+  /* Configure CLOCKACTIVITY and SIDLEMODE */
+
+  regval = AM67_MCSPI_SYSCONFIG_CLKACT_BOTH |
+           AM67_MCSPI_SYSCONFIG_SIDLEMODE_NO;
+  am67_mcspi_putreg(priv->base, AM67_MCSPI_SYSCONFIG, regval);
+
+  /* SINGLE=1: one channel active at a time; CS controlled via FORCE bit */
+
+  modulctrl = AM67_MCSPI_MODULCTRL_SINGLE;
+  am67_mcspi_putreg(priv->base, AM67_MCSPI_MODULCTRL, modulctrl);
+
+  am67_mcspi_apply_hwconfig(priv);
+}
+
+static uint32_t am67_mcspi_transfer_word(FAR struct am67_mcspi_dev_s *priv,
+                                         uint32_t wd)
+{
+  uint32_t choff = AM67_MCSPI_CH_OFFSET(priv->channel);
+
+  if (!am67_mcspi_waitstat(priv->base, priv->channel,
+                           AM67_MCSPI_CHSTAT_TXS))
+    {
+      spierr("ERROR: TX timeout ch%u\n", priv->channel);
+      return 0;
+    }
+
+  am67_mcspi_putreg(priv->base, AM67_MCSPI_TX0 + choff, wd);
+
+  if (!am67_mcspi_waitstat(priv->base, priv->channel,
+                           AM67_MCSPI_CHSTAT_RXS))
+    {
+      spierr("ERROR: RX timeout ch%u\n", priv->channel);
+      return 0;
+    }
+
+  return am67_mcspi_getreg(priv->base, AM67_MCSPI_RX0 + choff);
+}
+
+/****************************************************************************
+ * SPI driver methods
+ ****************************************************************************/
+
+static int am67_mcspi_lock(FAR struct spi_dev_s *dev, bool lock)
+{
+  FAR struct am67_mcspi_dev_s *priv = am67_mcspi_dev(dev);
+
+  if (lock)
+    {
+      return nxmutex_lock(&priv->lock);
+    }
+
+  nxmutex_unlock(&priv->lock);
+  return OK;
+}
+
+static uint32_t am67_mcspi_setfrequency(FAR struct spi_dev_s *dev,
+                                        uint32_t frequency)
+{
+  FAR struct am67_mcspi_dev_s *priv = am67_mcspi_dev(dev);
+
+  priv->frequency = frequency;
+  am67_mcspi_apply_hwconfig(priv);
+  return priv->frequency;
+}
+
+static void am67_mcspi_setmode(FAR struct spi_dev_s *dev,
+                               enum spi_mode_e mode)
+{
+  FAR struct am67_mcspi_dev_s *priv = am67_mcspi_dev(dev);
+
+  priv->mode = (uint8_t)mode;
+  am67_mcspi_apply_hwconfig(priv);
+}
+
+static void am67_mcspi_setbits(FAR struct spi_dev_s *dev, int nbits)
+{
+  FAR struct am67_mcspi_dev_s *priv = am67_mcspi_dev(dev);
+
+  DEBUGASSERT(nbits == 8 || nbits == 16);
+  priv->nbits = (uint8_t)nbits;
+  am67_mcspi_apply_hwconfig(priv);
+}
+
+static uint32_t am67_mcspi_send(FAR struct spi_dev_s *dev, uint32_t wd)
+{
+  FAR struct am67_mcspi_dev_s *priv = am67_mcspi_dev(dev);
+
+  if (!priv->selected)
+    {
+      return 0;
+    }
+
+  return am67_mcspi_transfer_word(priv, wd);
+}
+
+#ifdef CONFIG_SPI_EXCHANGE
+static void am67_mcspi_exchange(FAR struct spi_dev_s *dev,
+                                FAR const void *txbuffer,
+                                FAR void *rxbuffer, size_t nwords)
+{
+  FAR struct am67_mcspi_dev_s *priv = am67_mcspi_dev(dev);
+  size_t i;
+
+  if (!priv->selected)
+    {
+      return;
+    }
+
+  if (priv->nbits > 8)
+    {
+      FAR const uint16_t *tx16 = txbuffer;
+      FAR uint16_t *rx16 = rxbuffer;
+
+      for (i = 0; i < nwords; i++)
+        {
+          uint32_t wd = (tx16 != NULL) ? (uint32_t)tx16[i] : 0xffffu;
+          uint32_t rd = am67_mcspi_transfer_word(priv, wd);
+
+          if (rx16 != NULL)
+            {
+              rx16[i] = (uint16_t)rd;
+            }
+        }
+    }
+  else
+    {
+      FAR const uint8_t *tx8 = txbuffer;
+      FAR uint8_t *rx8 = rxbuffer;
+
+      for (i = 0; i < nwords; i++)
+        {
+          uint32_t wd = (tx8 != NULL) ? (uint32_t)tx8[i] : 0xffu;
+          uint32_t rd = am67_mcspi_transfer_word(priv, wd);
+
+          if (rx8 != NULL)
+            {
+              rx8[i] = (uint8_t)rd;
+            }
+        }
+    }
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+void am67_spiinitialize(void)
+{
+  am67_spi_pinmux_init();
+  am67_mcspi_controller_init(&g_spi0dev);
+  spiinfo("MCU_MCSPI0 @ 0x%08" PRIx32 " rev=0x%08" PRIx32 "\n",
+          g_spi0dev.base,
+          am67_mcspi_getreg(g_spi0dev.base, AM67_MCSPI_REVISION));
+}
+
+FAR struct spi_dev_s *am67_spibus_initialize(int port)
+{
+  if (port != 0)
+    {
+      return NULL;
+    }
+
+  return &g_spi0dev.spidev;
+}
+
+void am67_mcspi_board_select(FAR struct spi_dev_s *dev, uint8_t channel,
+                             bool selected)
+{
+  FAR struct am67_mcspi_dev_s *priv = am67_mcspi_dev(dev);
+
+  if (selected)
+    {
+      am67_mcspi_select_channel(priv, channel);
+      am67_mcspi_cs_force(priv, false);
+      priv->selected = true;
+    }
+  else
+    {
+      am67_mcspi_cs_force(priv, true);
+      priv->selected = false;
+    }
+}
+
+#endif /* CONFIG_AM67_MCSPI0 */
diff --git a/arch/arm/src/am67/am67_mcspi.h b/arch/arm/src/am67/am67_mcspi.h
new file mode 100644
index 00000000000..6630a2e044a
--- /dev/null
+++ b/arch/arm/src/am67/am67_mcspi.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+ * arch/arm/src/am67/am67_mcspi.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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 __ARCH_ARM_SRC_AM67_AM67_MCSPI_H
+#define __ARCH_ARM_SRC_AM67_AM67_MCSPI_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <stdint.h>
+
+#include <nuttx/spi/spi.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* MCU_MCSPI0 on Gemstone O1 (MCU domain) */
+
+#define AM67_MCSPI0_BASE           0x04b00000u
+#define AM67_MCSPI0_IRQ            207
+
+/* K3 MCU_MCSPI0 register map (TRM offsets).  The peripheral prepends an
+ * HL header block (HL_REV/HL_HWINFO/HL_SYSCONFIG) at 0x000-0x01f; the
+ * functional registers therefore start at 0x100, not 0x000 as in the
+ * older OMAP2 map.
+ */
+
+/* HL header registers (0x000-0x01f) */
+
+#define AM67_MCSPI_HL_SYSCONFIG    0x010
+
+/* HL_SYSCONFIG bits */
+
+#define AM67_MCSPI_HL_SYSCONFIG_NOIDLE             (1u << 2)
+
+/* Functional registers start at 0x100 */
+
+#define AM67_MCSPI_REVISION        0x100
+#define AM67_MCSPI_SYSCONFIG       0x110
+#define AM67_MCSPI_SYSSTATUS       0x114
+#define AM67_MCSPI_IRQSTATUS       0x118
+#define AM67_MCSPI_IRQENABLE       0x11c
+#define AM67_MCSPI_WAKEUPENABLE    0x120
+#define AM67_MCSPI_SYST            0x124
+#define AM67_MCSPI_MODULCTRL       0x128
+#define AM67_MCSPI_CHCONF0         0x12c
+#define AM67_MCSPI_CHSTAT0         0x130
+#define AM67_MCSPI_CHCTRL0         0x134
+#define AM67_MCSPI_TX0             0x138
+#define AM67_MCSPI_RX0             0x13c
+
+#define AM67_MCSPI_CH_OFFSET(n)    ((uint32_t)(n) * 0x14u)
+
+#define AM67_MCSPI_SYSCONFIG_AUTOIDLE           (1u << 0)
+#define AM67_MCSPI_SYSCONFIG_SOFTRESET          (1u << 1)
+#define AM67_MCSPI_SYSCONFIG_SIDLEMODE_SMART    (2u << 3)
+#define AM67_MCSPI_SYSCONFIG_SIDLEMODE_NO       (1u << 3)
+#define AM67_MCSPI_SYSCONFIG_CLKACT_BOTH        (3u << 8)
+#define AM67_MCSPI_SYSSTATUS_RESETDONE          (1u << 0)
+
+#define AM67_MCSPI_MODULCTRL_SINGLE (1u << 0)
+#define AM67_MCSPI_MODULCTRL_MS     (1u << 2)
+
+#define AM67_MCSPI_CHCONF_PHA       (1u << 0)
+#define AM67_MCSPI_CHCONF_POL       (1u << 1)
+#define AM67_MCSPI_CHCONF_CLKD_SHIFT 2
+#define AM67_MCSPI_CHCONF_CLKD_MASK (0x0fu << AM67_MCSPI_CHCONF_CLKD_SHIFT)
+#define AM67_MCSPI_CHCONF_EPOL      (1u << 6)
+#define AM67_MCSPI_CHCONF_WL_SHIFT  7
+#define AM67_MCSPI_CHCONF_WL_MASK   (0x1fu << AM67_MCSPI_CHCONF_WL_SHIFT)
+#define AM67_MCSPI_CHCONF_TRM_RX    (1u << 12)
+#define AM67_MCSPI_CHCONF_TRM_TX    (1u << 13)
+#define AM67_MCSPI_CHCONF_DPE0      (1u << 16)
+#define AM67_MCSPI_CHCONF_DPE1      (1u << 17)
+#define AM67_MCSPI_CHCONF_IS        (1u << 18)
+#define AM67_MCSPI_CHCONF_FORCE           (1u << 20)
+#define AM67_MCSPI_CHCONF_SPIENSLV_SHIFT  21
+#define AM67_MCSPI_CHCONF_SPIENSLV_MASK   (3u << 
AM67_MCSPI_CHCONF_SPIENSLV_SHIFT)
+#define AM67_MCSPI_CHCONF_CLKG            (1u << 29)
+
+#define AM67_MCSPI_CHSTAT_RXS       (1u << 0)
+#define AM67_MCSPI_CHSTAT_TXS       (1u << 1)
+#define AM67_MCSPI_CHSTAT_EOT       (1u << 2)
+
+#define AM67_MCSPI_CHCTRL_EN        (1u << 0)
+#define AM67_MCSPI_CHCTRL_EXTCLK_SHIFT 8
+#define AM67_MCSPI_CHCTRL_EXTCLK_MASK  (0xffu << 
AM67_MCSPI_CHCTRL_EXTCLK_SHIFT)
+
+/* Functional clock (MCU_PLL0 path); the divider is applied in the driver */
+
+#ifndef CONFIG_AM67_MCSPI0_FCLK
+#  define CONFIG_AM67_MCSPI0_FCLK  48000000
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+void am67_spiinitialize(void);
+FAR struct spi_dev_s *am67_spibus_initialize(int port);
+void am67_mcspi_board_select(FAR struct spi_dev_s *dev, uint8_t channel,
+                             bool selected);
+
+#endif /* __ARCH_ARM_SRC_AM67_AM67_MCSPI_H */
diff --git a/arch/arm/src/am67/am67_pinmux.c b/arch/arm/src/am67/am67_pinmux.c
index 3b99c13827e..b68da465fc5 100644
--- a/arch/arm/src/am67/am67_pinmux.c
+++ b/arch/arm/src/am67/am67_pinmux.c
@@ -68,6 +68,68 @@ static struct pinmux_conf_s g_am67_pinmux_conf[] =
   {PINMUX_END, PINMUX_END}
 };
 
+static struct pinmux_conf_s g_am67_mcu_spi_pinmux_conf[] =
+{
+  /* MCU_SPI0_CLK */
+
+  {
+    PIN_MCU_SPI0_CLK,
+    (PIN_MODE(0) | PIN_INPUT_ENABLE | PIN_PULL_DISABLE)
+  },
+
+  /* MCU_SPI0_D0 (MOSI) - output to sensor SDI; INPUT_ENABLE allows the
+   * D0 self-loopback test
+   */
+
+  {
+    PIN_MCU_SPI0_D0,
+    (PIN_MODE(0) | PIN_INPUT_ENABLE | PIN_PULL_DISABLE)
+  },
+
+  /* MCU_SPI0_D1 (MISO) - sensor SDO to D1 (ti,pindir-d0-out-d1-in) */
+
+  {
+    PIN_MCU_SPI0_D1,
+    (PIN_MODE(0) | PIN_INPUT_ENABLE | PIN_PULL_DISABLE)
+  },
+
+  /* MCU_SPI0_CS0 (unused, configures pad to SPI CS0 function) */
+
+  {
+    PIN_MCU_SPI0_CS0,
+    (PIN_MODE(0) | PIN_PULL_DISABLE)
+  },
+
+  /* MCU_SPI0_CS1 (LPS22DF, channel 1) */
+
+  {
+    PIN_MCU_SPI0_CS1,
+    (PIN_MODE(0) | PIN_PULL_DISABLE)
+  },
+
+  /* MCU_SPI0_CS2 (HAT spidev, channel 2) - WKUP_UART0_RXD pad, mode 2 */
+
+  {
+    PIN_WKUP_UART0_RXD,
+    (PIN_MODE(2) | PIN_PULL_DISABLE)
+  },
+
+  /* MCU_SPI0_CS3 (ICM20948, channel 3) - MCU_MCAN0_TX pad, mode 2 */
+
+  {
+    PIN_MCU_MCAN0_TX,
+    (PIN_MODE(2) | PIN_PULL_DISABLE)
+  },
+
+  /* IMU_EN -> MCU_GPIO0_12 (C3) - WKUP_UART0_RTSN pad at mode 7 */
+
+  {
+    PIN_WKUP_UART0_RTSN,
+    (PIN_MODE(7) | PIN_PULL_DISABLE)
+  },
+  {PINMUX_END, PINMUX_END}
+};
+
 /****************************************************************************
  * Private Functions
  ****************************************************************************/
@@ -103,6 +165,24 @@ static void am67_pinmux_unlock(void)
   putreg32(KICK1_UNLOCK_VAL, kick_addr);
 }
 
+static void am67_mcu_pinmux_unlock(void)
+{
+  uint32_t base_addr;
+  uint32_t kick_addr;
+
+  base_addr = CSL_MCU_PADCFG_CTRL0_CFG0_BASE;
+
+  kick_addr = base_addr + CSL_MCU_PADCONFIG_LOCK0_KICK0_OFFSET;
+  putreg32(KICK0_UNLOCK_VAL, kick_addr);
+  kick_addr += 4;
+  putreg32(KICK1_UNLOCK_VAL, kick_addr);
+
+  kick_addr = base_addr + CSL_MCU_PADCONFIG_LOCK1_KICK0_OFFSET;
+  putreg32(KICK0_UNLOCK_VAL, kick_addr);
+  kick_addr += 4;
+  putreg32(KICK1_UNLOCK_VAL, kick_addr);
+}
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -134,6 +214,27 @@ void am67_pinmux_config(const struct pinmux_conf_s 
*pinmux_conf)
     }
 }
 
+/****************************************************************************
+ * Name: am67_mcu_pinmux_config
+ ****************************************************************************/
+
+void am67_mcu_pinmux_config(const struct pinmux_conf_s *pinmux_conf)
+{
+  if (pinmux_conf != NULL)
+    {
+      uint32_t base_addr = CSL_MCU_PADCFG_CTRL0_CFG0_BASE +
+                           PADCFG_PMUX_OFFSET;
+
+      am67_mcu_pinmux_unlock();
+
+      while (pinmux_conf->offset != PINMUX_END)
+        {
+          putreg32(pinmux_conf->setting, base_addr + pinmux_conf->offset);
+          pinmux_conf++;
+        }
+    }
+}
+
 /****************************************************************************
  * Name: am67_pinmux_init
  *
@@ -146,3 +247,16 @@ void am67_pinmux_init(void)
 {
   am67_pinmux_config(g_am67_pinmux_conf);
 }
+
+/****************************************************************************
+ * Name: am67_spi_pinmux_init
+ *
+ * Description:
+ *   Configure MCU_SPI0 pin multiplexing for onboard sensors.
+ *
+ ****************************************************************************/
+
+void am67_spi_pinmux_init(void)
+{
+  am67_mcu_pinmux_config(g_am67_mcu_spi_pinmux_conf);
+}
diff --git a/arch/arm/src/am67/am67_pinmux.h b/arch/arm/src/am67/am67_pinmux.h
index 121b31f79d6..32a941e5269 100644
--- a/arch/arm/src/am67/am67_pinmux.h
+++ b/arch/arm/src/am67/am67_pinmux.h
@@ -37,7 +37,7 @@
 #define CSL_PADCFG_CTRL0_CFG0_BASE            (0xf0000ul)
 #define CSL_PADCFG_CTRL0_CFG0_SIZE            (0x8000ul)
 
-#define CSL_MCU_PADCFG_CTRL0_CFG0_BASE        (0x4080000ul)
+#define CSL_MCU_PADCFG_CTRL0_CFG0_BASE        (0x04080000ul)
 #define CSL_MCU_PADCFG_CTRL0_CFG0_SIZE        (0x8000ul)
 
 #define PADCFG_PMUX_OFFSET                    (0x4000u)
@@ -302,6 +302,7 @@ struct pinmux_conf_s
  ****************************************************************************/
 
 void am67_pinmux_config(const struct pinmux_conf_s *pinmux_conf);
+void am67_mcu_pinmux_config(const struct pinmux_conf_s *pinmux_conf);
 
 /****************************************************************************
  * Name: am67_pinmux_init
@@ -312,5 +313,6 @@ void am67_pinmux_config(const struct pinmux_conf_s 
*pinmux_conf);
  ****************************************************************************/
 
 void am67_pinmux_init(void);
+void am67_spi_pinmux_init(void);
 
 #endif /* __ARCH_ARM_SRC_AM67_AM67_PINMUX_H */
diff --git a/boards/arm/am67/t3-gem-o1/configs/nsh/defconfig 
b/boards/arm/am67/t3-gem-o1/configs/nsh/defconfig
index fb7b0717470..68d2167dbad 100644
--- a/boards/arm/am67/t3-gem-o1/configs/nsh/defconfig
+++ b/boards/arm/am67/t3-gem-o1/configs/nsh/defconfig
@@ -14,6 +14,7 @@ CONFIG_16550_UART0_CLOCK=48000000
 CONFIG_16550_UART0_IRQ=211
 CONFIG_16550_UART0_SERIAL_CONSOLE=y
 CONFIG_16550_UART=y
+CONFIG_AM67_MCSPI0=y
 CONFIG_ARCH="arm"
 CONFIG_ARCH_BOARD="t3-gem-o1"
 CONFIG_ARCH_BOARD_T3_GEM_O1=y
@@ -33,6 +34,7 @@ CONFIG_HAVE_CXXINITIALIZE=y
 CONFIG_INIT_ENTRYPOINT="nsh_main"
 CONFIG_NSH_BUILTIN_APPS=y
 CONFIG_NSH_FILEIOSIZE=512
+CONFIG_NSH_MAXARGUMENTS=16
 CONFIG_NSH_READLINE=y
 CONFIG_PSEUDOFS_ATTRIBUTES=y
 CONFIG_PSEUDOFS_FILE=y
@@ -44,5 +46,7 @@ CONFIG_SCHED_CHILD_STATUS=y
 CONFIG_SCHED_HAVE_PARENT=y
 CONFIG_SCHED_WAITPID=y
 CONFIG_SIG_DEFAULT=y
+CONFIG_SPI=y
+CONFIG_SPI_DRIVER=y
 CONFIG_SYSTEM_NSH=y
 CONFIG_TESTING_OSTEST=y
diff --git a/boards/arm/am67/t3-gem-o1/src/Makefile 
b/boards/arm/am67/t3-gem-o1/src/Makefile
index 4e1b75b15aa..eb4baa5b7c5 100644
--- a/boards/arm/am67/t3-gem-o1/src/Makefile
+++ b/boards/arm/am67/t3-gem-o1/src/Makefile
@@ -24,4 +24,9 @@ include $(TOPDIR)/Make.defs
 
 CSRCS = am67_bringup.c am67_appinit.c
 
+ifeq ($(CONFIG_AM67_MCSPI0),y)
+CSRCS += am67_spi.c
+CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)/arch/arm/src/am67
+endif
+
 include $(TOPDIR)/boards/Board.mk
diff --git a/boards/arm/am67/t3-gem-o1/src/am67_bringup.c 
b/boards/arm/am67/t3-gem-o1/src/am67_bringup.c
index 33fcd40a109..dea600d17f2 100644
--- a/boards/arm/am67/t3-gem-o1/src/am67_bringup.c
+++ b/boards/arm/am67/t3-gem-o1/src/am67_bringup.c
@@ -30,6 +30,11 @@
 
 #include "t3-gem-o1.h"
 
+#ifdef CONFIG_AM67_MCSPI0
+#include "am67_mcspi.h"
+#include "am67_gpio.h"
+#endif
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -52,6 +57,12 @@ int am67_bringup(void)
 {
   int ret = OK;
 
+#ifdef CONFIG_AM67_MCSPI0
+  am67_sensors_power_enable(true);
+  am67_spiinitialize();
+  am67_spidev_initialize();
+#endif
+
 #ifdef CONFIG_FS_PROCFS
   /* Mount the procfs file system */
 
diff --git a/boards/arm/am67/t3-gem-o1/src/am67_spi.c 
b/boards/arm/am67/t3-gem-o1/src/am67_spi.c
new file mode 100644
index 00000000000..5ebb81f21a0
--- /dev/null
+++ b/boards/arm/am67/t3-gem-o1/src/am67_spi.c
@@ -0,0 +1,127 @@
+/****************************************************************************
+ * boards/arm/am67/t3-gem-o1/src/am67_spi.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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 <nuttx/config.h>
+
+#include <debug.h>
+#include <inttypes.h>
+#include <stdint.h>
+
+#include <nuttx/spi/spi.h>
+#include <nuttx/spi/spi_transfer.h>
+
+#include "am67_gpio.h"
+#include "am67_mcspi.h"
+
+#ifdef CONFIG_AM67_MCSPI0
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* SPIDEV_USER(n) = SPIDEV_ID(SPIDEVTYPE_USER, n): the spi tool's -t 0
+ * selects SPIDEVTYPE_USER, so devid = SPIDEV_USER(csn).
+ */
+
+#define AM67_SPIDEV_LPS22DF   SPIDEV_USER(1)   /* CS1 per Linux DTS */
+#define AM67_SPIDEV_ICM20948  SPIDEV_USER(3)   /* CS3 per Linux DTS */
+
+/* MCSPI channel numbers for each device (low byte of SPIDEV_USER(n)) */
+
+#define AM67_CH_LPS22DF       1u
+#define AM67_CH_ICM20948      3u
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: am67_spi0select
+ ****************************************************************************/
+
+void am67_spi0select(FAR struct spi_dev_s *dev, uint32_t devid,
+                     bool selected)
+{
+  switch (devid)
+    {
+      case AM67_SPIDEV_LPS22DF:
+        am67_mcspi_board_select(dev, AM67_CH_LPS22DF, selected);
+        break;
+      case AM67_SPIDEV_ICM20948:
+        am67_mcspi_board_select(dev, AM67_CH_ICM20948, selected);
+        break;
+
+      default:
+        spierr("ERROR: Unsupported SPI devid: %" PRIu32 "\n", devid);
+        break;
+    }
+}
+
+/****************************************************************************
+ * Name: am67_spi0status
+ ****************************************************************************/
+
+uint8_t am67_spi0status(FAR struct spi_dev_s *dev, uint32_t devid)
+{
+  switch (devid)
+    {
+      case AM67_SPIDEV_LPS22DF:
+      case AM67_SPIDEV_ICM20948:
+        return SPI_STATUS_PRESENT;
+
+      default:
+        return 0;
+    }
+}
+
+/****************************************************************************
+ * Name: am67_spidev_initialize
+ ****************************************************************************/
+
+void am67_spidev_initialize(void)
+{
+  FAR struct spi_dev_s *spi;
+  int ret;
+
+  am67_configgpio(AM67_GPIO_HAT_CS1, GPIO_OUTPUT);
+  am67_configgpio(AM67_GPIO_HAT_CS2, GPIO_OUTPUT);
+  am67_configgpio(AM67_GPIO_HAT_CS3, GPIO_OUTPUT);
+
+  spi = am67_spibus_initialize(0);
+  if (spi == NULL)
+    {
+      spierr("ERROR: Failed to initialize SPI0\n");
+      return;
+    }
+
+  ret = spi_register(spi, 0);
+  if (ret < 0)
+    {
+      spierr("ERROR: Failed to register /dev/spi0: %d\n", ret);
+    }
+}
+
+#endif /* CONFIG_AM67_MCSPI0 */
diff --git a/boards/arm/am67/t3-gem-o1/src/t3-gem-o1.h 
b/boards/arm/am67/t3-gem-o1/src/t3-gem-o1.h
index fe5bb6e7dae..ad977b38ce5 100644
--- a/boards/arm/am67/t3-gem-o1/src/t3-gem-o1.h
+++ b/boards/arm/am67/t3-gem-o1/src/t3-gem-o1.h
@@ -31,25 +31,20 @@
 
 #ifndef __ASSEMBLY__
 
-/****************************************************************************
- * Public Functions Definitions
- ****************************************************************************/
+struct spi_dev_s;
 
 /****************************************************************************
- * Name: am67_bringup
- *
- * Description:
- *   Perform architecture-specific initialization
- *
- *   CONFIG_BOARD_LATE_INITIALIZE=y :
- *     Called from board_late_initialize().
- *
- *   CONFIG_BOARD_LATE_INITIALIZE=n && CONFIG_BOARDCTL=y :
- *     Called from the NSH library
- *
+ * Public Functions Definitions
  ****************************************************************************/
 
 int am67_bringup(void);
 
+#ifdef CONFIG_AM67_MCSPI0
+void am67_spi0select(FAR struct spi_dev_s *dev, uint32_t devid,
+                     bool selected);
+uint8_t am67_spi0status(FAR struct spi_dev_s *dev, uint32_t devid);
+void am67_spidev_initialize(void);
+#endif
+
 #endif /* __ASSEMBLY__ */
 #endif /* __BOARDS_ARM_T3_GEM_O1_SRC_T3_GEM_O1_H */

Reply via email to