From 6bdc3317c41f7d23ecc7a9d4f9719b8a07be04c9 Mon Sep 17 00:00:00 2001 From: halak0013 Date: Thu, 10 Sep 2026 09:08:51 +0300 Subject: [PATCH 1/2] arch/arm/am67: Add GPIO and MCU_MCSPI0 master driver. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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ç Assisted-by: Cursor Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Ulaş Sertan Kemeç --- arch/arm/src/am67/Kconfig | 24 + arch/arm/src/am67/Make.defs | 7 + arch/arm/src/am67/am67_gpio.c | 301 ++++++++++ arch/arm/src/am67/am67_gpio.h | 77 +++ 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 +- .../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, 1362 insertions(+), 15 deletions(-) create mode 100644 arch/arm/src/am67/am67_gpio.c create mode 100644 arch/arm/src/am67/am67_gpio.h create mode 100644 arch/arm/src/am67/am67_mcspi.c create mode 100644 arch/arm/src/am67/am67_mcspi.h create mode 100644 boards/arm/am67/t3-gem-o1/src/am67_spi.c diff --git a/arch/arm/src/am67/Kconfig b/arch/arm/src/am67/Kconfig index d461a787ab490..5ae7c094a1ce1 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 89a92240ab4a3..f464f63ae7312 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 0000000000000..a63f887f9746c --- /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 +#include +#include + +#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/arch/arm/src/am67/am67_gpio.h b/arch/arm/src/am67/am67_gpio.h new file mode 100644 index 0000000000000..6710328020444 --- /dev/null +++ b/arch/arm/src/am67/am67_gpio.h @@ -0,0 +1,77 @@ +/**************************************************************************** + * arch/arm/src/am67/am67_gpio.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_GPIO_H +#define __ARCH_ARM_SRC_AM67_AM67_GPIO_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* PX4 board_config.h compatibility */ + +#define GPIO_OUTPUT 0 +#define GPIO_INPUT 1 + +typedef uint32_t am67_gpio_t; + +enum am67_gpio_id_e +{ + 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 */ + +#define AM67_GPIO_HAT(n) am67_gpio_hat(n) + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +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); + +#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 0000000000000..56db769f48f45 --- /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 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 0000000000000..6630a2e044a47 --- /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 +#include + +#include + +/**************************************************************************** + * 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 3b99c13827eff..b68da465fc53d 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 121b31f79d6fa..32a941e526927 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 fb7b0717470c3..68d2167dbad44 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 4e1b75b15aa51..eb4baa5b7c5ab 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 33fcd40a109d2..dea600d17f269 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 0000000000000..5ebb81f21a0f2 --- /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 + +#include +#include +#include + +#include +#include + +#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 fe5bb6e7dae33..ad977b38ce589 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 */ From b283fcba951fce97607146264b02d2831742d304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ula=C5=9F=20Sertan=20Keme=C3=A7?= Date: Thu, 10 Sep 2026 10:35:25 +0300 Subject: [PATCH 2/2] Documentation/am67: Document GPIO and SPI support on t3-gem-o1. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a Peripheral Support section to the board page listing the GPIO and MCU_MCSPI0 drivers, and replace the "UART console only" warning on both the chip and board pages -- it no longer describes the port. The replacement states what actually constrains the port: NuttX runs on the R5F under RemoteProc and depends on the bootloader or Linux Device Manager having powered and clocked the peripherals, because there is no TISCI client yet. Assisted-by: Claude Code:claude-opus-5 Signed-off-by: Ulaş Sertan Kemeç --- .../arm/am67/boards/t3-gem-o1/index.rst | 18 ++- Documentation/platforms/arm/am67/index.rst | 8 +- arch/arm/src/am67/am67_pinmux.c | 116 ++++++++++++++++-- .../arm/am67/t3-gem-o1/configs/nsh/defconfig | 2 +- 4 files changed, 129 insertions(+), 15 deletions(-) diff --git a/Documentation/platforms/arm/am67/boards/t3-gem-o1/index.rst b/Documentation/platforms/arm/am67/boards/t3-gem-o1/index.rst index c660fa9bf4adc..5b1eee618a04d 100644 --- a/Documentation/platforms/arm/am67/boards/t3-gem-o1/index.rst +++ b/Documentation/platforms/arm/am67/boards/t3-gem-o1/index.rst @@ -76,9 +76,11 @@ Features .. warning:: - This board currently only supports a basic implementation of NuttX with - only UART console as a supported peripheral. Please see the contributing - documentation if you would like to help contribute to the support. + NuttX runs on the main-domain R5F core, loaded by U-Boot or Linux via + RemoteProc. It relies on the bootloader / Linux Device Manager to have + powered and clocked the peripherals it uses (NuttX does not yet run a TISCI + client of its own). Support is a work in progress -- please see the + contributing documentation if you would like to help. Serial console ============== @@ -89,6 +91,16 @@ HAT: - **UART-MAIN1 TX:** GPIO-14 - **UART-MAIN1 RX:** GPIO-15 +Peripheral Support +================== + +Beyond the serial console, the following peripherals have drivers for the +main-domain R5F core: + +- **GPIO:** pad configuration and read/write over the AM67 GPIO controller. +- **SPI:** MCU_MCSPI0 master, with the hardware chip-select driven per channel + (used for the on-board ICM-20948 IMU and LPS22DF barometer). + Installation ============ diff --git a/Documentation/platforms/arm/am67/index.rst b/Documentation/platforms/arm/am67/index.rst index 9d8fd59c09dc8..d98afe7dc134d 100644 --- a/Documentation/platforms/arm/am67/index.rst +++ b/Documentation/platforms/arm/am67/index.rst @@ -25,9 +25,11 @@ The TI AM67 platform integrates a dual‑domain architecture comprising: .. warning:: - This chip currently only supports a basic implementation of NuttX with - only UART console as a supported peripheral. Please see the contributing - documentation if you would like to help contribute to the support. + NuttX runs on the Cortex-R5F cores, loaded by U-Boot or Linux via + RemoteProc, and relies on the bootloader / Linux Device Manager to have + powered and clocked the peripherals it uses (NuttX does not yet run a TISCI + client of its own). Support is a work in progress -- please see the + contributing documentation if you would like to help. Supported Boards ================ diff --git a/arch/arm/src/am67/am67_pinmux.c b/arch/arm/src/am67/am67_pinmux.c index b68da465fc53d..c0e2cdd8d462c 100644 --- a/arch/arm/src/am67/am67_pinmux.c +++ b/arch/arm/src/am67/am67_pinmux.c @@ -10,7 +10,7 @@ * "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 + * 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 @@ -68,6 +68,42 @@ static struct pinmux_conf_s g_am67_pinmux_conf[] = {PINMUX_END, PINMUX_END} }; +static struct pinmux_conf_s g_am67_epwm0_pinmux_conf[] = +{ + /* EPWM0 A output pin (B20, mode 2 = EHRPWM0_A) */ + + { + PIN_SPI0_CS0, + (PIN_MODE(2) | PIN_PULL_DISABLE) + }, + + /* EPWM0 B output pin (C20, mode 2 = EHRPWM0_B) */ + + { + PIN_SPI0_CS1, + (PIN_MODE(2) | PIN_PULL_DISABLE) + }, + {PINMUX_END, PINMUX_END} +}; + +static struct pinmux_conf_s g_am67_epwm1_pinmux_conf[] = +{ + /* EPWM1 A output pin (D20, mode 2 = EHRPWM1_A) */ + + { + PIN_SPI0_CLK, + (PIN_MODE(2) | PIN_PULL_DISABLE) + }, + + /* EPWM1 B output pin (E19, mode 2 = EHRPWM1_B) */ + + { + PIN_SPI0_D0, + (PIN_MODE(2) | PIN_PULL_DISABLE) + }, + {PINMUX_END, PINMUX_END} +}; + static struct pinmux_conf_s g_am67_mcu_spi_pinmux_conf[] = { /* MCU_SPI0_CLK */ @@ -107,13 +143,6 @@ static struct pinmux_conf_s g_am67_mcu_spi_pinmux_conf[] = (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 */ { @@ -130,6 +159,42 @@ static struct pinmux_conf_s g_am67_mcu_spi_pinmux_conf[] = {PINMUX_END, PINMUX_END} }; +static struct pinmux_conf_s g_am67_mcu_i2c_pinmux_conf[] = +{ +#ifdef CONFIG_AM67_I2C0 + /* MCU_I2C0_SCL */ + + { + PIN_MCU_I2C0_SCL, + (PIN_MODE(0) | PIN_INPUT_ENABLE | PIN_PULL_DISABLE) + }, + + /* MCU_I2C0_SDA */ + + { + PIN_MCU_I2C0_SDA, + (PIN_MODE(0) | PIN_INPUT_ENABLE | PIN_PULL_DISABLE) + }, +#endif + +#ifdef CONFIG_AM67_WKUP_I2C0 + /* WKUP_I2C0_SCL */ + + { + PIN_WKUP_I2C0_SCL, + (PIN_MODE(0) | PIN_INPUT_ENABLE | PIN_PULL_DISABLE) + }, + + /* WKUP_I2C0_SDA */ + + { + PIN_WKUP_I2C0_SDA, + (PIN_MODE(0) | PIN_INPUT_ENABLE | PIN_PULL_DISABLE) + }, +#endif + {PINMUX_END, PINMUX_END} +}; + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -260,3 +325,38 @@ void am67_spi_pinmux_init(void) { am67_mcu_pinmux_config(g_am67_mcu_spi_pinmux_conf); } + +/**************************************************************************** + * Name: am67_i2c_pinmux_init + * + * Description: + * Configure MCU_I2C0 pin multiplexing for onboard sensors. + * + ****************************************************************************/ + +void am67_i2c_pinmux_init(void) +{ + am67_mcu_pinmux_config(g_am67_mcu_i2c_pinmux_conf); +} + +/**************************************************************************** + * Name: am67_epwm_pinmux_init + * + * Description: + * Configure the output pin multiplexing (A and B pads) for the given + * EPWM instance. + * + ****************************************************************************/ + +void am67_epwm_pinmux_init(int epwm) +{ + if (epwm == 0) + { + am67_pinmux_config(g_am67_epwm0_pinmux_conf); + } + else + { + am67_pinmux_config(g_am67_epwm1_pinmux_conf); + } +} + diff --git a/boards/arm/am67/t3-gem-o1/configs/nsh/defconfig b/boards/arm/am67/t3-gem-o1/configs/nsh/defconfig index 68d2167dbad44..bb1161f5b32bf 100644 --- a/boards/arm/am67/t3-gem-o1/configs/nsh/defconfig +++ b/boards/arm/am67/t3-gem-o1/configs/nsh/defconfig @@ -47,6 +47,6 @@ 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_SYSTEM_SPITOOL=y CONFIG_TESTING_OSTEST=y