From 02bbacbf21bf06756cae9ba0b8ccc2a7ca5aa16b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ula=C5=9F=20Sertan=20Keme=C3=A7?= Date: Thu, 10 Sep 2026 09:15:45 +0300 Subject: [PATCH 1/2] arch/arm/am67: Add I2C0/WKUP_I2C0 master driver. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an I2C master driver for the AM67 I2C instances, completing transfers on ARDY as the K3 controller signals. Bring-up is deferred to the first transfer, because the Linux Device Manager enables the I2C clocks late and touching the bus during early board init is not safe here. The last reference drop clears the flag so the next transfer re-initialises the hardware. t3-gem-o1 registers WKUP_I2C0 as /dev/i2c2. Verified on t3-gem-o1: i2c dev finds 0x30, 0x40, 0x51 and 0x68, the RTC at 0x68 reads a ticking BCD seconds register, repeated reads are consistent, and NACK recovery returns the bus to a usable state. Assisted-by: Claude Code:claude-fable-5 Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Ulaş Sertan Kemeç --- arch/arm/include/am67/irq.h | 3 +- arch/arm/src/am67/Kconfig | 21 + arch/arm/src/am67/Make.defs | 3 + arch/arm/src/am67/am67_i2c.c | 1482 +++++++++++++++++ arch/arm/src/am67/am67_i2c.h | 73 + arch/arm/src/am67/am67_i2c_hw.h | 212 +++ arch/arm/src/am67/am67_pinmux.h | 1 + .../arm/am67/t3-gem-o1/configs/nsh/defconfig | 4 + boards/arm/am67/t3-gem-o1/src/Makefile | 4 + boards/arm/am67/t3-gem-o1/src/am67_bringup.c | 8 + boards/arm/am67/t3-gem-o1/src/am67_i2c.c | 82 + boards/arm/am67/t3-gem-o1/src/t3-gem-o1.h | 4 + 12 files changed, 1896 insertions(+), 1 deletion(-) create mode 100644 arch/arm/src/am67/am67_i2c.c create mode 100644 arch/arm/src/am67/am67_i2c.h create mode 100644 arch/arm/src/am67/am67_i2c_hw.h create mode 100644 boards/arm/am67/t3-gem-o1/src/am67_i2c.c diff --git a/arch/arm/include/am67/irq.h b/arch/arm/include/am67/irq.h index 2b32acd7e06e7..3150b90672e50 100644 --- a/arch/arm/include/am67/irq.h +++ b/arch/arm/include/am67/irq.h @@ -292,7 +292,8 @@ /**************************************************************************** * Public Data ****************************************************************************/ - +#define AM67_IRQ_I2C0 CSLR_R5FSS0_CORE0_INTR_MCU_I2C0_POINTRPEND_0 +#define AM67_IRQ_WKUP_I2C0 CSLR_R5FSS0_CORE0_INTR_WKUP_I2C0_POINTRPEND_0 /**************************************************************************** * Inline Functions ****************************************************************************/ diff --git a/arch/arm/src/am67/Kconfig b/arch/arm/src/am67/Kconfig index 5ae7c094a1ce1..352105c0bf037 100644 --- a/arch/arm/src/am67/Kconfig +++ b/arch/arm/src/am67/Kconfig @@ -28,3 +28,24 @@ config AM67_MCSPI0_FCLK default 48000000 endif + +config AM67_I2C + bool "AM67 I2C support" + default n + depends on I2C + +if AM67_I2C + +config AM67_I2C0 + bool "AM67 MCU_I2C0 driver (MCU domain, 0x04900000)" + default n + ---help--- + Enable the I2C master driver for MCU_I2C0. + +config AM67_WKUP_I2C0 + bool "AM67 WKUP_I2C0 driver (WKUP domain, 0x2b200000)" + default n + ---help--- + Enable the I2C master driver for WKUP_I2C0. + +endif # AM67_I2C diff --git a/arch/arm/src/am67/Make.defs b/arch/arm/src/am67/Make.defs index f464f63ae7312..ba6ca865ea04d 100644 --- a/arch/arm/src/am67/Make.defs +++ b/arch/arm/src/am67/Make.defs @@ -36,4 +36,7 @@ endif ifeq ($(CONFIG_AM67_MCSPI0),y) CHIP_CSRCS += am67_mcspi.c endif +ifeq ($(CONFIG_AM67_I2C),y) +CHIP_CSRCS += am67_i2c.c +endif CHIP_CSRCS += arm_mpu.c diff --git a/arch/arm/src/am67/am67_i2c.c b/arch/arm/src/am67/am67_i2c.c new file mode 100644 index 0000000000000..29731b7100e1f --- /dev/null +++ b/arch/arm/src/am67/am67_i2c.c @@ -0,0 +1,1482 @@ +/**************************************************************************** + * arch/arm/src/am67/am67_i2c.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 +#include +#include +#include +#include +#include + +#include + +#include "arm_internal.h" +#include "am67_pinmux.h" +#include "am67_i2c.h" +#include "am67_i2c_hw.h" + +/* At least one I2C peripheral must be enabled */ + +#if defined(CONFIG_AM67_I2C0) || defined(CONFIG_AM67_I2C1) || \ + defined(CONFIG_AM67_WKUP_I2C0) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define AM67_I2C_SCLK (96000000) /* 96 MHz */ + +/* Configuration ************************************************************/ + +/* CONFIG_I2C_POLLED may be set so that I2C interrupts will not be used. + * Instead, CPU-intensive polling will be used. + */ + +/* Interrupt wait timeout in seconds and milliseconds */ + +#if !defined(CONFIG_AM67_I2CTIMEOSEC) && !defined(CONFIG_AM67_I2CTIMEOMS) +# define CONFIG_AM67_I2CTIMEOSEC 0 +# define CONFIG_AM67_I2CTIMEOMS 500 /* Default is 500 milliseconds */ +#elif !defined(CONFIG_AM67_I2CTIMEOSEC) +# define CONFIG_AM67_I2CTIMEOSEC 0 /* User provided milliseconds */ +#elif !defined(CONFIG_AM67_I2CTIMEOMS) +# define CONFIG_AM67_I2CTIMEOMS 0 /* User provided seconds */ +#endif + +/* Interrupt wait time timeout in system timer ticks */ + +#ifndef CONFIG_AM67_I2CTIMEOTICKS +# define CONFIG_AM67_I2CTIMEOTICKS \ + (SEC2TICK(CONFIG_AM67_I2CTIMEOSEC) + MSEC2TICK(CONFIG_AM67_I2CTIMEOMS)) +#endif + +#ifndef CONFIG_AM67_I2C_DYNTIMEO_STARTSTOP +# define CONFIG_AM67_I2C_DYNTIMEO_STARTSTOP TICK2USEC(CONFIG_AM67_I2CTIMEOTICKS) +#endif + +/* Debug ********************************************************************/ + +/* I2C event trace logic. NOTE: trace uses the internal, non-standard, + * low-level debug interface syslog() but does not require that any other + * debug is enabled. + */ + +#ifndef CONFIG_I2C_TRACE +# define am67_i2c_tracereset(p) +# define am67_i2c_tracenew(p,s) +# define am67_i2c_traceevent(p,e,a) +# define am67_i2c_tracedump(p) +#endif + +#ifndef CONFIG_I2C_NTRACE +# define CONFIG_I2C_NTRACE 32 +#endif + +#ifdef CONFIG_I2C_SLAVE +# error I2C slave logic is not supported yet for AM67 +#endif + +#define I2C_MASTER 1 +#define I2C_SLAVE 2 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Interrupt state */ + +enum am67_intstate_e +{ + INTSTATE_IDLE = 0, /* No I2C activity */ + INTSTATE_WAITING, /* Waiting for completion of interrupt activity */ + INTSTATE_DONE, /* Interrupt activity complete */ +}; + +/* Trace events */ + +enum am67_trace_e +{ + I2CEVENT_NONE = 0, /* No events have occurred with this status */ + I2CEVENT_SENDADDR, /* Start/Master bit set and address sent, param = msgc */ + I2CEVENT_SENDBYTE, /* Send byte, param = dcnt */ + I2CEVENT_RCVBYTE, /* Read more dta, param = dcnt */ + I2CEVENT_NOSTART, /* BTF on last byte with no restart, param = msgc */ + I2CEVENT_STARTRESTART, /* Last byte sent, re-starting, param = msgc */ + I2CEVENT_STOP, /* Last byte sten, send stop, param = 0 */ + I2CEVENT_ERROR /* Error occurred, param = 0 */ +}; + +/* Trace data */ + +struct am67_trace_s +{ + uint32_t status; /* IRQSTATUS at the time of the event */ + uint32_t count; /* Interrupt count when status change */ + enum am67_trace_e event; /* Last event that occurred with this status */ + uint32_t parm; /* Parameter associated with the event */ + clock_t time; /* First of event or first status */ +}; + +/* I2C Device hardware configuration */ + +struct am67_i2c_config_s +{ + uint32_t base; /* I2C base address */ + uint8_t mode; /* Master or Slave mode */ +#ifndef CONFIG_I2C_POLLED + uint32_t irq; /* Event IRQ */ +#endif +}; + +/* I2C Device Private Data */ + +struct am67_i2c_priv_s +{ + /* Standard I2C operations */ + + const struct i2c_ops_s *ops; + + /* Port configuration */ + + const struct am67_i2c_config_s *config; + + int refs; /* Reference count */ + bool inited; /* HW brought up lazily on the first transfer */ + mutex_t lock; /* Mutual exclusion mutex */ +#ifndef CONFIG_I2C_POLLED + sem_t sem_isr; /* Interrupt wait semaphore */ +#endif + volatile uint8_t intstate; /* Interrupt handshake (see enum am67_intstate_e) */ + + uint8_t msgc; /* Message count */ + struct i2c_msg_s *msgv; /* Message list */ + uint8_t *ptr; /* Current message buffer */ + uint32_t frequency; /* Current I2C frequency */ + int dcnt; /* Current message length */ + uint16_t flags; /* Current message flags */ + + /* I2C trace support */ + +#ifdef CONFIG_I2C_TRACE + int tndx; /* Trace array index */ + clock_t start_time; /* Time when the trace was started */ + + /* The actual trace data */ + + struct am67_trace_s trace[CONFIG_I2C_NTRACE]; +#endif + + uint32_t status; /* End of transfer IRQSTATUS */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static inline uint32_t am67_i2c_getreg(struct am67_i2c_priv_s *priv, + uint16_t offset); +static inline void am67_i2c_putreg(struct am67_i2c_priv_s *priv, + uint16_t offset, uint32_t value); +static inline void am67_i2c_modifyreg(struct am67_i2c_priv_s *priv, + uint16_t offset, uint32_t clearbits, + uint32_t setbits); + +#ifdef CONFIG_AM67_I2C_DYNTIMEO +static uint32_t am67_i2c_toticks(int msgc, struct i2c_msg_s *msgs); +#endif /* CONFIG_AM67_I2C_DYNTIMEO */ + +static inline int +am67_i2c_sem_waitdone(struct am67_i2c_priv_s *priv); +static inline bool +am67_i2c_sem_waitstop(struct am67_i2c_priv_s *priv); + +#ifdef CONFIG_I2C_TRACE +static void am67_i2c_tracereset(struct am67_i2c_priv_s *priv); +static void am67_i2c_tracenew(struct am67_i2c_priv_s *priv, + uint32_t status); +static void am67_i2c_traceevent(struct am67_i2c_priv_s *priv, + enum am67_trace_e event, uint32_t parm); +static void am67_i2c_tracedump(struct am67_i2c_priv_s *priv); +#endif /* CONFIG_I2C_TRACE */ + +static void am67_i2c_setclock(struct am67_i2c_priv_s *priv, + uint32_t frequency); +static void am67_i2c_startmsg(struct am67_i2c_priv_s *priv); +static inline void am67_i2c_sendstop(struct am67_i2c_priv_s *priv); +static inline uint32_t +am67_i2c_getstatus(struct am67_i2c_priv_s *priv); + +static int am67_i2c_isr_process(struct am67_i2c_priv_s *priv); + +#ifndef CONFIG_I2C_POLLED +static int am67_i2c_isr(int irq, void *context, void *arg); +#endif /* !CONFIG_I2C_POLLED */ + +static int am67_i2c_init(struct am67_i2c_priv_s *priv); +static int am67_i2c_deinit(struct am67_i2c_priv_s *priv); +static int am67_i2c_transfer(struct i2c_master_s *dev, + struct i2c_msg_s *msgs, int count); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Trace events strings */ + +#ifdef CONFIG_I2C_TRACE +static const char *g_trace_names[] = +{ + "NONE ", + "SENDADDR ", + "SENDBYTE ", + "RCVBYTE ", + "NOSTART ", + "START/RESTART ", + "STOP ", + "ERROR " +}; +#endif + +/* I2C interface */ + +static const struct i2c_ops_s am67_i2c_ops = +{ + .transfer = am67_i2c_transfer +}; + +/* I2C device structures */ + +#ifdef CONFIG_AM67_I2C0 +static const struct am67_i2c_config_s am67_i2c0_config = +{ + .base = AM67_I2C0_VADDR, +#ifndef CONFIG_I2C_SLAVE + .mode = I2C_MASTER, +#else + .mode = I2C_SLAVE, +#endif +#ifndef CONFIG_I2C_POLLED + .irq = AM67_IRQ_I2C0, +#endif +}; + +static struct am67_i2c_priv_s am67_i2c0_priv = +{ + .ops = &am67_i2c_ops, + .config = &am67_i2c0_config, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, +#ifndef CONFIG_I2C_POLLED + .sem_isr = SEM_INITIALIZER(0), +#endif + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .dcnt = 0, + .flags = 0, + .status = 0 +}; +#endif + +#ifdef CONFIG_AM67_WKUP_I2C0 +static const struct am67_i2c_config_s am67_i2c2_config = +{ + .base = AM67_WKUP_I2C0_VADDR, +#ifndef CONFIG_I2C_SLAVE + .mode = I2C_MASTER, +#else + .mode = I2C_SLAVE, +#endif +#ifndef CONFIG_I2C_POLLED + .irq = AM67_IRQ_WKUP_I2C0, +#endif +}; + +static struct am67_i2c_priv_s am67_i2c2_priv = +{ + .ops = &am67_i2c_ops, + .config = &am67_i2c2_config, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, +#ifndef CONFIG_I2C_POLLED + .sem_isr = SEM_INITIALIZER(0), +#endif + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .dcnt = 0, + .flags = 0, + .status = 0 +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: am67_i2c_getreg + * + * Description: + * Get a 32-bit register value by offset + * + ****************************************************************************/ + +static inline uint32_t am67_i2c_getreg(struct am67_i2c_priv_s *priv, + uint16_t offset) +{ + return getreg32(priv->config->base + offset); +} + +/**************************************************************************** + * Name: am67_i2c_putreg + * + * Description: + * Put a 32-bit register value by offset + * + ****************************************************************************/ + +static inline void am67_i2c_putreg(struct am67_i2c_priv_s *priv, + uint16_t offset, uint32_t value) +{ + putreg32(value, priv->config->base + offset); +} + +/**************************************************************************** + * Name: am67_i2c_modifyreg + * + * Description: + * Modify a 32-bit register value by offset + * + ****************************************************************************/ + +static inline void am67_i2c_modifyreg(struct am67_i2c_priv_s *priv, + uint16_t offset, uint32_t clearbits, + uint32_t setbits) +{ + modifyreg32(priv->config->base + offset, clearbits, setbits); +} + +/**************************************************************************** + * Name: am67_i2c_toticks + * + * Description: + * Return a micro-second delay based on the number of bytes left to be + * processed. + * + ****************************************************************************/ + +#ifdef CONFIG_AM67_I2C_DYNTIMEO +static uint32_t am67_i2c_toticks(int msgc, struct i2c_msg_s *msgs) +{ + size_t bytecount = 0; + int i; + + /* Count the number of bytes left to process */ + + for (i = 0; i < msgc; i++) + { + bytecount += msgs[i].length; + } + + /* Then return a number of microseconds based on a user provided scaling + * factor. + */ + + return USEC2TICK(CONFIG_AM67_I2C_DYNTIMEO_USECPERBYTE * bytecount); +} +#endif + +/**************************************************************************** + * Name: am67_i2c_sem_waitdone + * + * Description: + * Wait for a transfer to complete + * + ****************************************************************************/ + +#ifndef CONFIG_I2C_POLLED +static inline int am67_i2c_sem_waitdone(struct am67_i2c_priv_s *priv) +{ + irqstate_t flags; + int ret; + + flags = enter_critical_section(); + + /* Signal the interrupt handler that we are waiting, then start the + * first segment. startmsg() arms exactly the interrupts the segment + * needs; the ISR sequences all remaining segments on ARDY. NOTE: + * Interrupts are currently disabled but will be temporarily re-enabled + * below when nxsem_tickwait() sleeps. + */ + + priv->intstate = INTSTATE_WAITING; + am67_i2c_startmsg(priv); + + do + { + /* Wait until either the transfer is complete or the timeout expires */ + +#ifdef CONFIG_AM67_I2C_DYNTIMEO + ret = nxsem_tickwait(&priv->sem_isr, + am67_i2c_toticks(priv->msgc, priv->msgv)); +#else + ret = nxsem_tickwait(&priv->sem_isr, + CONFIG_AM67_I2CTIMEOTICKS); +#endif + if (ret < 0 && ret != -EINTR) + { + /* Break out of the loop on irrecoverable errors. This would + * include timeouts and mystery errors reported by nxsem_tickwait. + * NOTE that we try again if we are awakened by a signal (EINTR). + */ + + break; + } + } + + /* Loop until the interrupt level transfer is complete. */ + + while (priv->intstate != INTSTATE_DONE); + + /* Set the interrupt state back to IDLE */ + + priv->intstate = INTSTATE_IDLE; + + /* Disable I2C interrupts */ + + am67_i2c_putreg(priv, AM67_I2C_IRQ_EN_CLR_OFFSET, I2C_ICR_CLEARMASK); + + leave_critical_section(flags); + return ret; +} +#else +static inline int am67_i2c_sem_waitdone(struct am67_i2c_priv_s *priv) +{ + clock_t timeout; + clock_t start; + clock_t elapsed; + int ret; + + /* Get the timeout value */ + +#ifdef CONFIG_AM67_I2C_DYNTIMEO + timeout = am67_i2c_toticks(priv->msgc, priv->msgv); +#else + timeout = CONFIG_AM67_I2CTIMEOTICKS; +#endif + + priv->intstate = INTSTATE_WAITING; + + /* Start the first segment; the polling loop below sequences the rest */ + + am67_i2c_startmsg(priv); + + start = clock_systime_ticks(); + + do + { + /* Calculate the elapsed time */ + + elapsed = clock_systime_ticks() - start; + + /* Poll by simply calling the timer interrupt handler until it + * reports that it is done. + */ + + am67_i2c_isr_process(priv); + } + + /* Loop until the transfer is complete. */ + + while (priv->intstate != INTSTATE_DONE && elapsed < timeout); + + i2cinfo("intstate: %d elapsed: %ld threshold: %ld status: %08" PRIx32 + "\n", priv->intstate, (long)elapsed, (long)timeout, priv->status); + + /* Set the interrupt state back to IDLE */ + + ret = priv->intstate == INTSTATE_DONE ? OK : -ETIMEDOUT; + priv->intstate = INTSTATE_IDLE; + return ret; +} +#endif + +/**************************************************************************** + * Name: am67_i2c_sem_waitstop + * + * Description: + * Wait for a STOP to complete + * + ****************************************************************************/ + +static inline bool +am67_i2c_sem_waitstop(struct am67_i2c_priv_s *priv) +{ + clock_t start; + clock_t elapsed; + clock_t timeout; + uint32_t regval; + + /* Select a timeout */ + +#ifdef CONFIG_AM67_I2C_DYNTIMEO + timeout = USEC2TICK(CONFIG_AM67_I2C_DYNTIMEO_STARTSTOP); +#else + timeout = CONFIG_AM67_I2CTIMEOTICKS; +#endif + + /* Wait as stop might still be in progress; but stop might also + * be set because of a timeout error: "The [STOP] bit is set and + * cleared by software, cleared by hardware when a Stop condition is + * detected, set by hardware when a timeout error is detected." + */ + + start = clock_systime_ticks(); + do + { + /* Calculate the elapsed time */ + + elapsed = clock_systime_ticks() - start; + + /* Check for Bus Free condition */ + + regval = am67_i2c_getreg(priv, AM67_I2C_IRQ_STAT_RAW_OFFSET); + if ((regval & I2C_IRQ_BB) == 0) + { + return true; + } + } + + /* Loop until the stop is complete or a timeout occurs. */ + + while (elapsed < timeout); + + /* If we get here then a timeout occurred with the STOP condition + * still pending. + */ + + i2cinfo("Timeout with Status Register: %" PRIx32 "\n", regval); + return false; +} + +/**************************************************************************** + * Name: am67_i2c_trace* + * + * Description: + * I2C trace instrumentation + * + ****************************************************************************/ + +#ifdef CONFIG_I2C_TRACE +static void am67_i2c_traceclear(struct am67_i2c_priv_s *priv) +{ + struct am67_trace_s *trace = &priv->trace[priv->tndx]; + + trace->status = 0; /* I2C 32-bit SR2|SR1 status */ + trace->count = 0; /* Interrupt count when status change */ + trace->event = I2CEVENT_NONE; /* Last event that occurred with this status */ + trace->parm = 0; /* Parameter associated with the event */ + trace->time = 0; /* Time of first status or event */ +} + +static void am67_i2c_tracereset(struct am67_i2c_priv_s *priv) +{ + /* Reset the trace info for a new data collection */ + + priv->tndx = 0; + priv->start_time = clock_systime_ticks(); + am67_i2c_traceclear(priv); +} + +static void am67_i2c_tracenew(struct am67_i2c_priv_s *priv, + uint32_t status) +{ + struct am67_trace_s *trace = &priv->trace[priv->tndx]; + + /* Is the current entry uninitialized? Has the status changed? */ + + if (trace->count == 0 || status != trace->status) + { + /* Yes.. Was it the status changed? */ + + if (trace->count != 0) + { + /* Yes.. bump up the trace index (unless out of trace entries) */ + + if (priv->tndx >= (CONFIG_I2C_NTRACE - 1)) + { + i2cerr("ERROR: Trace table overflow\n"); + return; + } + + priv->tndx++; + trace = &priv->trace[priv->tndx]; + } + + /* Initialize the new trace entry */ + + am67_i2c_traceclear(priv); + trace->status = status; + trace->count = 1; + trace->time = clock_systime_ticks(); + } + else + { + /* Just increment the count of times that we have seen this status */ + + trace->count++; + } +} + +static void am67_i2c_traceevent(struct am67_i2c_priv_s *priv, + enum am67_trace_e event, uint32_t parm) +{ + struct am67_trace_s *trace; + + if (event != I2CEVENT_NONE) + { + trace = &priv->trace[priv->tndx]; + + /* Initialize the new trace entry */ + + trace->event = event; + trace->parm = parm; + + /* Bump up the trace index (unless we are out of trace entries) */ + + if (priv->tndx >= (CONFIG_I2C_NTRACE - 1)) + { + i2cerr("ERROR: Trace table overflow\n"); + return; + } + + priv->tndx++; + am67_i2c_traceclear(priv); + } +} + +static void am67_i2c_tracedump(struct am67_i2c_priv_s *priv) +{ + struct am67_trace_s *trace; + int i; + + syslog(LOG_DEBUG, "Elapsed time: %ld\n", + (long)(clock_systime_ticks() - priv->start_time)); + + for (i = 0; i < priv->tndx; i++) + { + trace = &priv->trace[i]; + syslog(LOG_DEBUG, + "%2d. STATUS: %08x COUNT: %3d EVENT: %s(%2d) PARM: %08x " + "TIME: %d\n", + i + 1, trace->status, trace->count, + g_trace_names[trace->event], + trace->event, trace->parm, trace->time - priv->start_time); + } +} +#endif /* CONFIG_I2C_TRACE */ + +/**************************************************************************** + * Name: am67_i2c_setclock + * + * Description: + * Set the I2C clock + * + ****************************************************************************/ + +static void am67_i2c_setclock(struct am67_i2c_priv_s *priv, + uint32_t frequency) +{ + uint32_t src_freq = AM67_I2C_SCLK; + uint32_t men; + uint32_t prescale = 0; + uint32_t scl = 0; + uint32_t scl_low = 0; + uint32_t scl_hi = 0; + uint32_t best_prescale = 0; + uint32_t best_scl_low = 0; + uint32_t best_scl_hi = 0; + uint32_t abs_error = 0; + uint32_t best_error = 0xffffffff; + uint32_t computed_rate; + + /* Has the I2C bus frequency changed? */ + + if (priv->config->mode == I2C_MASTER) + { + if (frequency != priv->frequency) + { + /* Disable the selected I2C peripheral to configure the new + * clock if it is enabled. + */ + + men = am67_i2c_getreg(priv, AM67_I2C_CON_OFFSET) & I2C_CON_EN; + if (men) + { + am67_i2c_modifyreg(priv, AM67_I2C_CON_OFFSET, + I2C_CON_EN, 0); + } + + /* I2C bus clock is: + * Source Clock (Hz) / ((psc + 1) * (scll + 7 + sclh + 5)) + */ + + for (scl = 14; scl < 522; scl += 2) + { + for (prescale = 3; prescale < 256; prescale++) + { + scl_low = (scl / 2) - 7; + scl_hi = (scl / 2) - 5; + + computed_rate = src_freq / (prescale + 1); + computed_rate /= scl_low + 7 + scl_hi + 5; + + if (frequency > computed_rate) + { + abs_error = frequency - computed_rate; + } + else + { + abs_error = computed_rate - frequency; + } + + if (abs_error < best_error) + { + best_prescale = prescale; + best_scl_low = scl_low; + best_scl_hi = scl_hi; + best_error = abs_error; + + if (abs_error == 0) + { + scl = 522; + break; + } + } + } + } + + am67_i2c_putreg(priv, AM67_I2C_PSC_OFFSET, best_prescale); + + am67_i2c_putreg(priv, AM67_I2C_SCLL_OFFSET, best_scl_low); + + am67_i2c_putreg(priv, AM67_I2C_SCLH_OFFSET, best_scl_hi); + + /* Re-enable I2C if it was enabled previously */ + + if (men) + { + am67_i2c_modifyreg(priv, AM67_I2C_CON_OFFSET, + 0, I2C_CON_EN); + } + + /* Save the new I2C frequency */ + + priv->frequency = frequency; + } + } +} + +/**************************************************************************** + * Name: am67_i2c_startmsg + * + * Description: + * Program the hardware for the next message segment and issue a + * (repeated) START. Must only be called while the module is register- + * accessible: before any transfer has started, or from the ISR when + * ARDY is set. + * + ****************************************************************************/ + +static void am67_i2c_startmsg(struct am67_i2c_priv_s *priv) +{ + struct i2c_msg_s *msg = priv->msgv; + uint32_t regval; + + /* Bookkeeping: this segment's buffer/count/flags, advance the list */ + + priv->ptr = msg->buffer; + priv->dcnt = msg->length; + priv->flags = msg->flags; + priv->msgv++; + priv->msgc--; + + /* SA and DCOUNT before anything starts. DCOUNT is written exactly + * once per segment and never touched again while it runs. + */ + + am67_i2c_putreg(priv, AM67_I2C_SA_OFFSET, msg->addr); + am67_i2c_putreg(priv, AM67_I2C_CNT_OFFSET, msg->length); + + /* Direction + master mode. STP is deliberately NOT pre-armed here: + * when priming from the ARDY window the master is holding an open + * transaction, and writing STP=1 there generates an immediate STOP + * (this is why the TI SDK never sets STOP in its restart path). Every + * segment therefore ends with the bus held, and the ISR issues the + * final STOP explicitly once the last segment's data is done. + */ + + regval = I2C_CON_EN | I2C_CON_MST; + if ((priv->flags & I2C_M_READ) == 0) + { + regval |= I2C_CON_TRX; + } + + if ((priv->flags & I2C_M_TEN) != 0) + { + regval |= I2C_CON_XSA; + } + + am67_i2c_putreg(priv, AM67_I2C_CON_OFFSET, regval); + +#ifndef CONFIG_I2C_POLLED + /* Arm this segment's interrupts: errors, ARDY (segment done), and the + * data direction actually in use. BF stays off until we need it. + */ + + am67_i2c_putreg(priv, AM67_I2C_IRQ_EN_CLR_OFFSET, + I2C_IRQ_XRDY | I2C_IRQ_RRDY | I2C_IRQ_BF); + am67_i2c_putreg(priv, AM67_I2C_IRQ_EN_SET_OFFSET, + I2C_IRQ_AL | I2C_IRQ_NACK | I2C_IRQ_AERR | I2C_IRQ_ARDY | + (((priv->flags & I2C_M_READ) != 0) ? + I2C_IRQ_RRDY : I2C_IRQ_XRDY)); +#endif + + /* Everything is described — now press go */ + + if ((priv->flags & I2C_M_NOSTART) == 0) + { + am67_i2c_traceevent(priv, I2CEVENT_SENDADDR, priv->msgc); + am67_i2c_modifyreg(priv, AM67_I2C_CON_OFFSET, 0, I2C_CON_STT); + } + else + { + am67_i2c_traceevent(priv, I2CEVENT_NOSTART, priv->msgc); + } +} + +/**************************************************************************** + * Name: am67_i2c_sendstop + * + * Description: + * Send the STOP conditions + * + ****************************************************************************/ + +static inline void am67_i2c_sendstop(struct am67_i2c_priv_s *priv) +{ + am67_i2c_modifyreg(priv, AM67_I2C_CON_OFFSET, 0, I2C_CON_STP); +} + +/**************************************************************************** + * Name: am67_i2c_getstatus + * + * Description: + * Get 32-bit status + * + ****************************************************************************/ + +static inline uint32_t +am67_i2c_getstatus(struct am67_i2c_priv_s *priv) +{ +#ifndef CONFIG_I2C_POLLED + return am67_i2c_getreg(priv, AM67_I2C_IRQ_STAT_OFFSET); +#else + return am67_i2c_getreg(priv, AM67_I2C_IRQ_STAT_RAW_OFFSET); +#endif +} + +/**************************************************************************** + * Name: am67_i2c_isr_process + * + * Description: + * Common Interrupt Service Routine + * + ****************************************************************************/ + +static int am67_i2c_isr_process(struct am67_i2c_priv_s *priv) +{ + uint32_t status = am67_i2c_getstatus(priv); + uint32_t raw = am67_i2c_getreg(priv, AM67_I2C_IRQ_STAT_RAW_OFFSET); + uint32_t fatal = raw & (I2C_IRQ_NACK | I2C_IRQ_AL | I2C_IRQ_AERR); + + /* Check for new trace setup */ + + am67_i2c_tracenew(priv, status); + + /* Rung 1: fatal errors preempt everything. Checked in the RAW status + * so an error can never hide behind interrupt masking. + */ + + if (fatal != 0) + { + am67_i2c_traceevent(priv, I2CEVENT_ERROR, fatal); + + /* Release the bus and reset the transaction engine, mirroring the + * TI SDK fatal-error path. Without this a single NACK leaves the + * master mid-transaction and every later transfer times out. + */ + + am67_i2c_sendstop(priv); + + am67_i2c_putreg(priv, AM67_I2C_IRQ_STAT_OFFSET, I2C_STS_CLEARMASK); + am67_i2c_putreg(priv, AM67_I2C_BUF_OFFSET, + I2C_BUF_TXFIFO_CLR | I2C_BUF_RXFIFO_CLR); + am67_i2c_putreg(priv, AM67_I2C_CNT_OFFSET, 0); + + priv->status = status | fatal; + priv->msgv = NULL; + priv->dcnt = 0; + priv->ptr = NULL; + +#ifndef CONFIG_I2C_POLLED + am67_i2c_putreg(priv, AM67_I2C_IRQ_EN_CLR_OFFSET, I2C_ICR_CLEARMASK); + + if (priv->intstate == INTSTATE_WAITING) + { + nxsem_post(&priv->sem_isr); + priv->intstate = INTSTATE_DONE; + } +#else + priv->intstate = INTSTATE_DONE; +#endif + return OK; + } + + /* Rung 2: a byte arrived — drain it BEFORE any phase decision so a + * pending restart/stop cannot strand it in the FIFO. + */ + + if ((status & I2C_IRQ_RRDY) != 0) + { + if (priv->dcnt > 0 && priv->ptr != NULL) + { + am67_i2c_traceevent(priv, I2CEVENT_RCVBYTE, priv->dcnt); + *priv->ptr++ = am67_i2c_getreg(priv, AM67_I2C_DATA_OFFSET) & + I2C_DATA_MASK; + priv->dcnt--; + } + else + { + /* Unexpected byte: consume it so the FIFO cannot wedge */ + + am67_i2c_getreg(priv, AM67_I2C_DATA_OFFSET); + } + + am67_i2c_putreg(priv, AM67_I2C_IRQ_STAT_OFFSET, I2C_IRQ_RRDY); + } + + /* Rung 3: the transmitter wants a byte */ + + if ((status & I2C_IRQ_XRDY) != 0) + { + if (priv->dcnt > 0 && priv->ptr != NULL) + { + am67_i2c_traceevent(priv, I2CEVENT_SENDBYTE, priv->dcnt); + am67_i2c_putreg(priv, AM67_I2C_DATA_OFFSET, *priv->ptr++); + priv->dcnt--; + } +#ifndef CONFIG_I2C_POLLED + else + { + /* Nothing left in this segment's buffer */ + + am67_i2c_putreg(priv, AM67_I2C_IRQ_EN_CLR_OFFSET, I2C_IRQ_XRDY); + } +#endif + + am67_i2c_putreg(priv, AM67_I2C_IRQ_STAT_OFFSET, I2C_IRQ_XRDY); + } + + /* Rung 4: segment finished (ARDY), or bus released (BF). Only act if + * this segment's data really is done — in polled mode the raw status + * contains incidental BF/ARDY bits from past bus activity. + */ + + if ((status & (I2C_IRQ_ARDY | I2C_IRQ_BF)) != 0) + { + am67_i2c_putreg(priv, AM67_I2C_IRQ_STAT_OFFSET, + status & (I2C_IRQ_ARDY | I2C_IRQ_BF)); + + if (priv->dcnt <= 0 && priv->msgv != NULL) + { + if (priv->msgc > 0) + { + /* More segments: reprogram now — ARDY means the counter is + * idle and the hardware is holding the bus for us. + */ + + am67_i2c_traceevent(priv, I2CEVENT_STARTRESTART, priv->msgc); + am67_i2c_startmsg(priv); + } + else if ((raw & I2C_IRQ_BB) != 0 && + (status & I2C_IRQ_BF) == 0 && + (priv->flags & I2C_M_NOSTOP) == 0) + { + /* All data done but the bus is still held: issue the final + * STOP (legal here — this is the ARDY window) and wait for + * the bus-free event. Skip STP if a STOP is already in + * flight; hardware clears the bit when the STOP completes. + */ + + if ((am67_i2c_getreg(priv, AM67_I2C_CON_OFFSET) & + I2C_CON_STP) == 0) + { + am67_i2c_sendstop(priv); + } + +#ifndef CONFIG_I2C_POLLED + am67_i2c_putreg(priv, AM67_I2C_IRQ_EN_SET_OFFSET, I2C_IRQ_BF); +#endif + } + else + { + /* Transfer complete */ + + am67_i2c_traceevent(priv, I2CEVENT_STOP, 0); + +#ifndef CONFIG_I2C_POLLED + am67_i2c_putreg(priv, AM67_I2C_IRQ_EN_CLR_OFFSET, + I2C_ICR_CLEARMASK); + + if (priv->intstate == INTSTATE_WAITING) + { + nxsem_post(&priv->sem_isr); + priv->intstate = INTSTATE_DONE; + } +#else + priv->intstate = INTSTATE_DONE; +#endif + priv->msgv = NULL; + } + } + } + + priv->status = status; + return OK; +} + +/**************************************************************************** + * Name: am67_i2c_isr + * + * Description: + * Common I2C interrupt service routine + * + ****************************************************************************/ + +#ifndef CONFIG_I2C_POLLED +static int am67_i2c_isr(int irq, void *context, void *arg) +{ + struct am67_i2c_priv_s *priv = (struct am67_i2c_priv_s *)arg; + + DEBUGASSERT(priv != NULL); + return am67_i2c_isr_process(priv); +} +#endif + +/**************************************************************************** + * Name: am67_i2c_init + * + * Description: + * Setup the I2C hardware, ready for operation with defaults + * + ****************************************************************************/ + +static int am67_i2c_init(struct am67_i2c_priv_s *priv) +{ + /* Configure pins */ + + am67_i2c_pinmux_init(); + + /* Disable I2C module before reset */ + + am67_i2c_putreg(priv, AM67_I2C_CON_OFFSET, 0); + + /* Apply soft reset */ + + am67_i2c_putreg(priv, AM67_I2C_SYSC_OFFSET, I2C_SYSC_SRST); + + /* TI I2C requires CON.EN=1 for the internal reset to complete; + * without it RST_DONE never asserts. + */ + + am67_i2c_modifyreg(priv, AM67_I2C_CON_OFFSET, 0, I2C_CON_EN); + + while (!(am67_i2c_getreg(priv, AM67_I2C_SYSS_OFFSET) & + I2C_SYSS_RST_DONE)) + { + } + + /* No-idle mode + keep both clocks active so the WKUP domain power + * controller cannot gate WKUP_I2C0 clocks between transfers when the + * CPU enters WFI. Without this the peripheral resets between NSH + * commands and the second transfer always fails. + */ + + am67_i2c_putreg(priv, AM67_I2C_SYSC_OFFSET, + I2C_SYSC_IDLE_NO | I2C_SYSC_CLK_BOTH); + + /* Force a frequency update; setclock temporarily disables EN to write + * PSC/SCLL/SCLH, then re-enables it. + */ + + priv->frequency = 0; + am67_i2c_setclock(priv, 100000); + +#ifndef CONFIG_I2C_POLLED + /* Attach ISRs */ + + irq_attach(priv->config->irq, am67_i2c_isr, priv); + up_enable_irq(priv->config->irq); +#endif + + /* Ensure module is enabled after setclock may have re-enabled it */ + + am67_i2c_modifyreg(priv, AM67_I2C_CON_OFFSET, 0, I2C_CON_EN); + + /* Enable all wakeup sources. The TI Linux driver does this explicitly + * "to stop I2C freezing on WFI instruction" on all AM6x/J7 class + * devices. Without WE_ALL the I2C interrupt cannot wake the CPU from + * WFI, so the second transfer hangs until sem_waitdone times out. + */ + + am67_i2c_putreg(priv, AM67_I2C_WE_OFFSET, + I2C_WE_AL | I2C_WE_NACK | I2C_WE_ARDY | I2C_WE_DRDY | + I2C_WE_GC | I2C_WE_STC | I2C_WE_BF | I2C_WE_AAS | + I2C_WE_XUDF | I2C_WE_ROVR | I2C_WE_RDR | I2C_WE_XDR); + + /* Free-running mode keeps the module active under a JTAG halt */ + + am67_i2c_modifyreg(priv, AM67_I2C_SYSTEST_OFFSET, 0, I2C_SYSTEST_FREE); + + return OK; +} + +/**************************************************************************** + * Name: am67_i2c_deinit + * + * Description: + * Shutdown the I2C hardware + * + ****************************************************************************/ + +static int am67_i2c_deinit(struct am67_i2c_priv_s *priv) +{ + /* Disable I2C module */ + + am67_i2c_putreg(priv, AM67_I2C_CON_OFFSET, 0); + + /* Apply soft reset */ + + am67_i2c_putreg(priv, AM67_I2C_SYSC_OFFSET, I2C_SYSC_SRST); + + /* Disable and detach interrupts */ + +#ifndef CONFIG_I2C_POLLED + up_disable_irq(priv->config->irq); + irq_detach(priv->config->irq); +#endif + + return OK; +} + +/**************************************************************************** + * Device Driver Operations + ****************************************************************************/ + +/**************************************************************************** + * Name: am67_i2c_transfer + * + * Description: + * Generic I2C transfer function + * + ****************************************************************************/ + +static int am67_i2c_transfer(struct i2c_master_s *dev, + struct i2c_msg_s *msgs, int count) +{ + struct am67_i2c_priv_s *priv = (struct am67_i2c_priv_s *)dev; + int ret; + + DEBUGASSERT(count > 0); + + /* Ensure that address or flags don't change meanwhile */ + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + /* Bring the hardware up on first use. The bus is registered early during + * board bring-up (before the console), but the I2C functional clock is + * only enabled by the DM afterwards, so the reset is deferred to here. + */ + + if (!priv->inited) + { + am67_i2c_init(priv); + priv->inited = true; + } + + /* Wait for any STOP in progress */ + + ret = -EBUSY; + if (am67_i2c_sem_waitstop(priv)) + { + /* Clear TX and RX FIFOs before each transfer. Stale FIFO data from + * the previous transfer can cause XRDY/RRDY to fire at unexpected + * points in the state machine. The TI Linux driver does this too. + */ + + am67_i2c_putreg(priv, AM67_I2C_BUF_OFFSET, + I2C_BUF_TXFIFO_CLR | I2C_BUF_RXFIFO_CLR); + + /* Clear any pending error interrupts */ + + am67_i2c_putreg(priv, AM67_I2C_IRQ_STAT_OFFSET, + I2C_STS_CLEARMASK); + am67_i2c_putreg(priv, AM67_I2C_IRQ_EN_CLR_OFFSET, + I2C_ICR_CLEARMASK); + + /* Old transfers are done */ + + /* Reset ptr and dcnt to ensure an unexpected data interrupt doesn't + * overwrite stale data. + */ + + priv->dcnt = 0; + priv->ptr = NULL; + + priv->msgv = msgs; + priv->msgc = count; + + i2cinfo("Flags %x, len %d\n", msgs->flags, msgs->length); + + /* Reset I2C trace logic */ + + am67_i2c_tracereset(priv); + + /* Set I2C clock frequency (only reprograms the divisors when the + * requested frequency actually changes). + */ + + am67_i2c_setclock(priv, msgs->frequency); + + priv->status = 0; + + /* Wait for an ISR, if there was a timeout, fetch latest status to get + * the BUSY flag. + */ + + if (am67_i2c_sem_waitdone(priv) < 0) + { + ret = -ETIMEDOUT; + + i2cerr("ERROR: Timed out: IRQ_RAW: status: 0x%" PRIx32 "\n", + priv->status); + } + + /* Check for error status conditions */ + + else if ((priv->status & I2C_IRQ_ERRORMASK) != 0) + { + /* I2C_IRQ_ERRORMASK is the OR of the following individual bits: */ + + if (priv->status & I2C_IRQ_AL) + { + /* Arbitration Lost (master mode) */ + + i2cerr("Arbitration lost\n"); + ret = -EAGAIN; + } + else if (priv->status & I2C_IRQ_NACK) + { + /* Acknowledge Failure */ + + i2cerr("Ack failure\n"); + ret = -ENXIO; + } + else if (priv->status & (I2C_IRQ_XUDF | I2C_IRQ_ROVR)) + { + /* Overrun/Underrun */ + + i2cerr("Overrun/Underrun status\n"); + ret = -EIO; + } + else if (priv->status & I2C_IRQ_AERR) + { + /* Access Error in reception or transmission */ + + i2cerr("Access Error\n"); + ret = -EPROTO; + } + else if (priv->status & I2C_IRQ_BB) + { + /* Bus busy Error */ + + i2cerr("Bus busy error\n"); + ret = -EIO; + } + else + { + i2cerr("Unspecified error\n"); + ret = -EINTR; + } + } + else + { + ret = OK; + } + + /* Dump the trace result */ + + am67_i2c_tracedump(priv); + + /* Ensure ISR happening after we finish can't overwrite any user data */ + + priv->dcnt = 0; + priv->ptr = NULL; + } + else + { + i2cerr("ERROR: Bus busy, raw status: 0x%" PRIx32 "\n", + am67_i2c_getreg(priv, AM67_I2C_IRQ_STAT_RAW_OFFSET)); + } + + nxmutex_unlock(&priv->lock); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: am67_i2cbus_initialize + * + * Description: + * Initialize one I2C bus + * + ****************************************************************************/ + +struct i2c_master_s *am67_i2cbus_initialize(int port) +{ + struct am67_i2c_priv_s *priv = NULL; + + /* Get I2C private structure */ + + switch (port) + { +#ifdef CONFIG_AM67_I2C0 + case 0: + priv = (struct am67_i2c_priv_s *)&am67_i2c0_priv; + break; +#endif +#ifdef CONFIG_AM67_WKUP_I2C0 + case 2: + priv = (struct am67_i2c_priv_s *)&am67_i2c2_priv; + break; +#endif + default: + return NULL; + } + + /* Reference count only. The hardware is brought up lazily on the first + * transfer (see am67_i2c_transfer): bring-up registers the bus early, + * before the console, but the I2C functional clock is enabled by the DM + * later, so resetting the module here would spin on RST_DONE forever and + * hang boot. + */ + + nxmutex_lock(&priv->lock); + priv->refs++; + nxmutex_unlock(&priv->lock); + return (struct i2c_master_s *)priv; +} + +/**************************************************************************** + * Name: am67_i2cbus_uninitialize + * + * Description: + * Uninitialize an I2C bus + * + ****************************************************************************/ + +int am67_i2cbus_uninitialize(struct i2c_master_s *dev) +{ + struct am67_i2c_priv_s *priv = (struct am67_i2c_priv_s *)dev; + + DEBUGASSERT(dev); + + /* Decrement reference count and check for underflow */ + + if (priv->refs == 0) + { + return ERROR; + } + + nxmutex_lock(&priv->lock); + if (--priv->refs > 0) + { + nxmutex_unlock(&priv->lock); + return OK; + } + + /* Disable power and other HW resource (GPIO's), only if it was ever + * brought up (lazy init may never have run). + */ + + if (priv->inited) + { + am67_i2c_deinit(priv); + + /* Re-arm lazy bring-up so the next transfer re-runs am67_i2c_init(). */ + + priv->inited = false; + } + + nxmutex_unlock(&priv->lock); + + return OK; +} + +#endif /* CONFIG_AM67_I2C0 || CONFIG_AM67_I2C1 || CONFIG_AM67_WKUP_I2C0 */ diff --git a/arch/arm/src/am67/am67_i2c.h b/arch/arm/src/am67/am67_i2c.h new file mode 100644 index 0000000000000..460200bb98f35 --- /dev/null +++ b/arch/arm/src/am67/am67_i2c.h @@ -0,0 +1,73 @@ +/**************************************************************************** + * arch/arm/src/am67/am67_i2c.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_I2C_H +#define __ARCH_ARM_SRC_AM67_AM67_I2C_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: am67_i2cbus_initialize + * + * Description: + * Initialize the selected I2C port. And return a unique instance of struct + * struct i2c_master_s. This function may be called to obtain multiple + * instances of the interface, each of which may be set up with a + * different frequency and slave address. + * + * Input Parameters: + * Port number (for hardware that has multiple I2C interfaces) + * + * Returned Value: + * Valid I2C device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct i2c_master_s *am67_i2cbus_initialize(int port); + +/**************************************************************************** + * Name: am67_i2cbus_uninitialize + * + * Description: + * De-initialize the selected I2C port, and power down the device. + * + * Input Parameters: + * Device structure as returned by the am67_i2cbus_initialize() + * + * Returned Value: + * OK on success, ERROR when internal reference count mismatch or dev + * points to invalid hardware device. + * + ****************************************************************************/ + +int am67_i2cbus_uninitialize(struct i2c_master_s *dev); + +#endif /* __ARCH_ARM_SRC_AM67_AM67_I2C_H */ diff --git a/arch/arm/src/am67/am67_i2c_hw.h b/arch/arm/src/am67/am67_i2c_hw.h new file mode 100644 index 0000000000000..ab8ac414d13e5 --- /dev/null +++ b/arch/arm/src/am67/am67_i2c_hw.h @@ -0,0 +1,212 @@ +/**************************************************************************** + * arch/arm/src/am67/am67_i2c_hw.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_I2C_HW_H +#define __ARCH_ARM_SRC_AM67_AM67_I2C_HW_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register offsets *********************************************************/ + +#define AM67_I2C_SYSC_OFFSET 0x0010 +#define AM67_I2C_IRQ_STAT_RAW_OFFSET 0x0024 +#define AM67_I2C_IRQ_STAT_OFFSET 0x0028 +#define AM67_I2C_IRQ_EN_SET_OFFSET 0x002c +#define AM67_I2C_IRQ_EN_CLR_OFFSET 0x0030 +#define AM67_I2C_WE_OFFSET 0x0034 +#define AM67_I2C_DMA_RX_EN_SET_OFFSET 0x0038 +#define AM67_I2C_DMA_TX_EN_SET_OFFSET 0x003c +#define AM67_I2C_DMA_RX_EN_CLR_OFFSET 0x0040 +#define AM67_I2C_DMA_TX_EN_CLR_OFFSET 0x0044 +#define AM67_I2C_DMA_RX_WAKE_EN_OFFSET 0x0048 +#define AM67_I2C_DMA_TX_WAKE_EN_OFFSET 0x004c +#define AM67_I2C_SYSS_OFFSET 0x0090 +#define AM67_I2C_BUF_OFFSET 0x0094 +#define AM67_I2C_CNT_OFFSET 0x0098 +#define AM67_I2C_DATA_OFFSET 0x009c +#define AM67_I2C_CON_OFFSET 0x00a4 +#define AM67_I2C_OA_OFFSET 0x00a8 +#define AM67_I2C_SA_OFFSET 0x00ac +#define AM67_I2C_PSC_OFFSET 0x00b0 +#define AM67_I2C_SCLL_OFFSET 0x00b4 +#define AM67_I2C_SCLH_OFFSET 0x00b8 +#define AM67_I2C_SYSTEST_OFFSET 0x00bc +#define AM67_I2C_BUFSTAT_OFFSET 0x00c0 +#define AM67_I2C_OA1_OFFSET 0x00c4 +#define AM67_I2C_OA2_OFFSET 0x00c8 +#define AM67_I2C_OA3_OFFSET 0x00cc +#define AM67_I2C_ACTOA_OFFSET 0x00d0 +#define AM67_I2C_SBLOCK_OFFSET 0x00d4 + +/* Peripheral base addresses ************************************************/ + +#define AM67_I2C0_VADDR 0x04900000 +#define AM67_WKUP_I2C0_VADDR 0x2b200000 + +/* Register bit field definitions *******************************************/ + +#define I2C_SYSC_AUTOIDLE (1 << 0) /* Bit 0: Auto-idle */ +#define I2C_SYSC_SRST (1 << 1) /* Bit 1: SoftReset */ +#define I2C_SYSC_WAKEUP (1 << 2) /* Bit 2: Enable Wakeup control */ +#define I2C_SYSC_IDLE_SHIFT (3) /* Bits 3-4: Idle Mode selection */ +#define I2C_SYSC_IDLE_MASK (3 << I2C_SYSC_IDLE_SHIFT) +# define I2C_SYSC_IDLE_FORCE (0 << I2C_SYSC_IDLE_SHIFT) /* Force-idle mode */ +# define I2C_SYSC_IDLE_NO (1 << I2C_SYSC_IDLE_SHIFT) /* No-idle mode */ +# define I2C_SYSC_IDLE_SMART (2 << I2C_SYSC_IDLE_SHIFT) /* Smart-idle mode */ +# define I2C_SYSC_IDLE_SMART_WKUP (3 << I2C_SYSC_IDLE_SHIFT) /* Smart-idle Wakeup mode */ + +#define I2C_SYSC_CLK_SHIFT (8) /* Bits 8-9: Clock Activity selection */ +#define I2C_SYSC_CLK_MASK (3 << I2C_SYSC_CLK_SHIFT) +# define I2C_SYSC_CLK_NONE (0 << I2C_SYSC_CLK_SHIFT) /* Both clocks can be cut off */ +# define I2C_SYSC_CLK_OCP (1 << I2C_SYSC_CLK_SHIFT) /* Only Interface/OCP clock must be kept active */ +# define I2C_SYSC_CLK_FUNC (2 << I2C_SYSC_CLK_SHIFT) /* Only functions clock must be kept active */ +# define I2C_SYSC_CLK_BOTH (3 << I2C_SYSC_CLK_SHIFT) /* Both clocks must be kept active */ + +#define I2C_IRQ_AL (1 << 0) /* Bit 0: Arbitration lost */ +#define I2C_IRQ_NACK (1 << 1) /* Bit 1: No acknowledgment */ +#define I2C_IRQ_ARDY (1 << 2) /* Bit 2: Register access ready */ +#define I2C_IRQ_RRDY (1 << 3) /* Bit 3: Receive data ready */ +#define I2C_IRQ_XRDY (1 << 4) /* Bit 4: Transmit data ready */ +#define I2C_IRQ_GC (1 << 5) /* Bit 5: General call */ +#define I2C_IRQ_STC (1 << 6) /* Bit 6: Start Condition */ +#define I2C_IRQ_AERR (1 << 7) /* Bit 7: Access Error */ +#define I2C_IRQ_BF (1 << 8) /* Bit 8: Bus Free */ +#define I2C_IRQ_AAS (1 << 9) /* Bit 9: Address recognized as slave */ +#define I2C_IRQ_XUDF (1 << 10) /* Bit 10: Transmit underflow */ +#define I2C_IRQ_ROVR (1 << 11) /* Bit 11: Receive overrun */ +#define I2C_IRQ_BB (1 << 12) /* Bit 12: Bus busy */ +#define I2C_IRQ_RDR (1 << 13) /* Bit 13: Receive draining IRQ */ +#define I2C_IRQ_XDR (1 << 14) /* Bit 14: Transmit draining IRQ */ + +#define I2C_IRQ_ERRORMASK (I2C_IRQ_AL | I2C_IRQ_NACK | I2C_IRQ_AERR | I2C_IRQ_XUDF | I2C_IRQ_ROVR) + +#define I2C_STS_CLEARMASK (I2C_IRQ_AL | I2C_IRQ_NACK | I2C_IRQ_ARDY | I2C_IRQ_RRDY | I2C_IRQ_XRDY \ + | I2C_IRQ_GC | I2C_IRQ_STC | I2C_IRQ_AERR | I2C_IRQ_BF | I2C_IRQ_AAS \ + | I2C_IRQ_XUDF | I2C_IRQ_ROVR | I2C_IRQ_BB | I2C_IRQ_RDR | I2C_IRQ_XDR) + +#define I2C_ICR_CLEARMASK (I2C_IRQ_AL | I2C_IRQ_NACK | I2C_IRQ_ARDY | I2C_IRQ_RRDY | I2C_IRQ_XRDY \ + | I2C_IRQ_GC | I2C_IRQ_STC | I2C_IRQ_AERR | I2C_IRQ_BF | I2C_IRQ_AAS \ + | I2C_IRQ_XUDF | I2C_IRQ_ROVR | I2C_IRQ_RDR | I2C_IRQ_XDR) + +#define I2C_WE_AL (1 << 0) /* Bit 0: Arbitration lost */ +#define I2C_WE_NACK (1 << 1) /* Bit 1: No acknowledgment */ +#define I2C_WE_ARDY (1 << 2) /* Bit 2: Register access ready */ +#define I2C_WE_DRDY (1 << 3) /* Bit 3: Receive/Transmit data ready */ +#define I2C_WE_GC (1 << 5) /* Bit 5: General call */ +#define I2C_WE_STC (1 << 6) /* Bit 6: Start Condition */ +#define I2C_WE_BF (1 << 8) /* Bit 8: Bus Free */ +#define I2C_WE_AAS (1 << 9) /* Bit 9: Address recognized as slave */ +#define I2C_WE_XUDF (1 << 10) /* Bit 10: Transmit underflow */ +#define I2C_WE_ROVR (1 << 11) /* Bit 11: Receive overrun */ +#define I2C_WE_RDR (1 << 13) /* Bit 13: Receive draining IRQ */ +#define I2C_WE_XDR (1 << 14) /* Bit 14: Transmit draining IRQ */ + +#define I2C_DMA_ENABLE (1 << 0) /* Bit 0: DMA channel enable */ + +#define I2C_SYSS_RST_DONE (1 << 0) /* Bit 0: Reset done */ + +#define I2C_BUF_TXTRSH_SHIFT (0) /* Bits 0-5: Threshold value for FIFO buffer in TX mode */ +#define I2C_BUF_TXTRSH_MASK (63 << I2C_BUF_TXTRSH_SHIFT) +#define I2C_BUF_TXFIFO_CLR (1 << 6) /* Bit 6: Transmit FIFO clear */ +#define I2C_BUF_XDMA_EN (1 << 7) /* Bit 7: Transmit DMA channel enable */ + +#define I2C_BUF_RXTRSH_SHIFT (8) /* Bits 8-13: Threshold value for FIFO buffer in RX mode */ +#define I2C_BUF_RXTRSH_MASK (63 << I2C_BUF_RXTRSH_SHIFT) +#define I2C_BUF_RXFIFO_CLR (1 << 14) /* Bit 14: Receive FIFO clear */ +#define I2C_BUF_RDMA_EN (1 << 15) /* Bit 15: Receive DMA channel enable */ + +#define I2C_CNT_SHIFT (0) /* Bits 0-15: Data count */ +#define I2C_CNT_MASK (65535 << I2C_CNT_SHIFT) + +#define I2C_DATA_SHIFT (0) /* Bits 0-7: Transmit/Receive data FIFO endpoint */ +#define I2C_DATA_MASK (255 << I2C_DATA_SHIFT) + +#define I2C_CON_STT (1 << 0) /* Bit 0: Start condition (I2C master mode only) */ +#define I2C_CON_STP (1 << 1) /* Bit 1: Stop condition (I2C master mode only) */ +#define I2C_CON_XOA3 (1 << 4) /* Bit 4: Expand own address 3 */ +#define I2C_CON_XOA2 (1 << 5) /* Bit 5: Expand own address 2 */ +#define I2C_CON_XOA1 (1 << 6) /* Bit 6: Expand own address 1 */ +#define I2C_CON_XOA0 (1 << 7) /* Bit 7: Expand own address 0 */ +#define I2C_CON_XSA (1 << 8) /* Bit 8: Expand slave address */ +#define I2C_CON_TRX (1 << 9) /* Bit 9: Transmitter/receiver mode (I2C master mode only) */ +#define I2C_CON_MST (1 << 10) /* Bit 10: Master/slave mode */ +#define I2C_CON_STB (1 << 11) /* Bit 11: Start byte mode (I2C master mode only) */ +#define I2C_CON_OPMODE_SHIFT (12) /* Bits 12-13: Operation mode selection */ +#define I2C_CON_OPMODE_MASK (3 << I2C_CON_OPMODE_SHIFT) +# define I2C_CON_OPMODE_FAST (0 << I2C_CON_OPMODE_SHIFT) +#define I2C_CON_EN (1 << 15) /* Bit 15: I2C module enable */ + +#define I2C_SA_SHIFT (0) /* Bits 0-9: Slave address */ +#define I2C_SA_MASK (0x3ff << I2C_SA_SHIFT) + +#define I2C_PSC_SHIFT (0) /* Bits 0-7: Fast/Standard mode prescale sampling clock divider */ +#define I2C_PSC_MASK (255 << I2C_PSC_SHIFT) + +#define I2C_SCLL_SHIFT (0) /* Bits 0-7: Fast/Standard mode SCL low time */ +#define I2C_SCLL_MASK (255 << I2C_SCLL_SHIFT) + +#define I2C_SCLH_SHIFT (0) /* Bits 0-7: Fast/Standard mode SCL high time. */ +#define I2C_SCLH_MASK (255 << I2C_SCLH_SHIFT) + +#define I2C_SYSTEST_SDA_O (1 << 0) /* Bit 0: SDA line drive output value */ +#define I2C_SYSTEST_SDA_I (1 << 1) /* Bit 1: SDA line sense input value */ +#define I2C_SYSTEST_SCL_O (1 << 2) /* Bit 2: SCL line drive output value */ +#define I2C_SYSTEST_SCL_I (1 << 3) /* Bit 3: SCL line sense input value */ +#define I2C_SYSTEST_SDA_O_FUNC (1 << 5) /* Bit 5: SDA line output value (functional mode) */ +#define I2C_SYSTEST_SDA_I_FUNC (1 << 6) /* Bit 6: SDA line input value (functional mode) */ +#define I2C_SYSTEST_SCL_O_FUNC (1 << 7) /* Bit 7: SCL line output value (functional mode) */ +#define I2C_SYSTEST_SCL_I_FUNC (1 << 8) /* Bit 8: SCL line input value (functional mode) */ +#define I2C_SYSTEST_SSB (1 << 11) /* Bit 11: Set status bits */ + +#define I2C_SYSTEST_TMODE_SHIFT (12) /* Bits 12-13: Test mode select */ +#define I2C_SYSTEST_TMODE_MASK (3 << I2C_SYSTEST_TMODE_SHIFT) +# define I2C_SYSTEST_TMODE_FUNC (0 << I2C_SYSTEST_TMODE_SHIFT) /* Functional mode */ +# define I2C_SYSTEST_TMODE_SCL (2 << I2C_SYSTEST_TMODE_SHIFT) /* Test of SCL counters (SCLL, SCLH, PSC) */ +# define I2C_SYSTEST_TMODE_LOOPBACK (3 << I2C_SYSTEST_TMODE_SHIFT) /* Loop back mode select + SDA/SCL IO mode select */ + +#define I2C_SYSTEST_FREE (1 << 14) /* Bit 14: Free running mode (on breakpoint) */ +#define I2C_SYSTEST_ST_EN (1 << 15) /* Bit 15: System test enable */ + +#define I2C_BUFSTAT_TXSTAT_SHIFT (0) /* Bits 0-5: TX buffer status */ +#define I2C_BUFSTAT_TXSTAT_MASK (63 << I2C_BUFSTAT_TXSTAT_SHIFT) +#define I2C_BUFSTAT_RXSTAT_SHIFT (8) /* Bits 8-13: RX buffer status */ +#define I2C_BUFSTAT_RXSTAT_MASK (63 << I2C_BUFSTAT_RXSTAT_SHIFT) +#define I2C_BUFSTAT_FIFODEPTH_SHIFT (14) /* Bits 14-15: Internal FIFO buffers depth */ +#define I2C_BUFSTAT_FIFODEPTH_MASK (3 << I2C_BUFSTAT_FIFODEPTH_SHIFT) + +#define I2C_OA_SHIFT (0) /* Bits 0-9: Own address */ +#define I2C_OA_MASK (0x3ff << I2C_OA_SHIFT) + +#define I2C_OA0_SELECT (1 << 0) /* Bit 0: Own address 0 */ +#define I2C_OA1_SELECT (1 << 1) /* Bit 1: Own address 1 */ +#define I2C_OA2_SELECT (1 << 2) /* Bit 2: Own address 2 */ +#define I2C_OA3_SELECT (1 << 3) /* Bit 3: Own address 3 */ + +#endif /* __ARCH_ARM_SRC_AM67_AM67_I2C_HW_H */ diff --git a/arch/arm/src/am67/am67_pinmux.h b/arch/arm/src/am67/am67_pinmux.h index 32a941e526927..cc336a89ccda5 100644 --- a/arch/arm/src/am67/am67_pinmux.h +++ b/arch/arm/src/am67/am67_pinmux.h @@ -314,5 +314,6 @@ void am67_mcu_pinmux_config(const struct pinmux_conf_s *pinmux_conf); void am67_pinmux_init(void); void am67_spi_pinmux_init(void); +void am67_i2c_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 bb1161f5b32bf..6504186a010f3 100644 --- a/boards/arm/am67/t3-gem-o1/configs/nsh/defconfig +++ b/boards/arm/am67/t3-gem-o1/configs/nsh/defconfig @@ -14,7 +14,9 @@ CONFIG_16550_UART0_CLOCK=48000000 CONFIG_16550_UART0_IRQ=211 CONFIG_16550_UART0_SERIAL_CONSOLE=y CONFIG_16550_UART=y +CONFIG_AM67_I2C=y CONFIG_AM67_MCSPI0=y +CONFIG_AM67_WKUP_I2C0=y CONFIG_ARCH="arm" CONFIG_ARCH_BOARD="t3-gem-o1" CONFIG_ARCH_BOARD_T3_GEM_O1=y @@ -31,6 +33,7 @@ CONFIG_FS_LINKS=y CONFIG_FS_PROCFS=y CONFIG_HAVE_CXX=y CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_I2C=y CONFIG_INIT_ENTRYPOINT="nsh_main" CONFIG_NSH_BUILTIN_APPS=y CONFIG_NSH_FILEIOSIZE=512 @@ -47,6 +50,7 @@ CONFIG_SCHED_HAVE_PARENT=y CONFIG_SCHED_WAITPID=y CONFIG_SIG_DEFAULT=y CONFIG_SPI=y +CONFIG_SYSTEM_I2CTOOL=y CONFIG_SYSTEM_NSH=y CONFIG_SYSTEM_SPITOOL=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 eb4baa5b7c5ab..2adc979343503 100644 --- a/boards/arm/am67/t3-gem-o1/src/Makefile +++ b/boards/arm/am67/t3-gem-o1/src/Makefile @@ -29,4 +29,8 @@ CSRCS += am67_spi.c CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)/arch/arm/src/am67 endif +ifeq ($(CONFIG_AM67_I2C),y) +CSRCS += am67_i2c.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 dea600d17f269..461956e98fb52 100644 --- a/boards/arm/am67/t3-gem-o1/src/am67_bringup.c +++ b/boards/arm/am67/t3-gem-o1/src/am67_bringup.c @@ -35,6 +35,10 @@ #include "am67_gpio.h" #endif +#if defined(CONFIG_AM67_I2C0) || defined(CONFIG_AM67_WKUP_I2C0) +#include "am67_i2c.h" +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -63,6 +67,10 @@ int am67_bringup(void) am67_spidev_initialize(); #endif +#if defined(CONFIG_AM67_I2C0) || defined(CONFIG_AM67_WKUP_I2C0) + am67_i2cdev_initialize(); +#endif + #ifdef CONFIG_FS_PROCFS /* Mount the procfs file system */ diff --git a/boards/arm/am67/t3-gem-o1/src/am67_i2c.c b/boards/arm/am67/t3-gem-o1/src/am67_i2c.c new file mode 100644 index 0000000000000..f842988c74827 --- /dev/null +++ b/boards/arm/am67/t3-gem-o1/src/am67_i2c.c @@ -0,0 +1,82 @@ +/**************************************************************************** + * boards/arm/am67/t3-gem-o1/src/am67_i2c.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_i2c.h" + +#if defined(CONFIG_AM67_I2C0) || defined(CONFIG_AM67_WKUP_I2C0) + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: am67_i2cdev_initialize + ****************************************************************************/ + +void am67_i2cdev_initialize(void) +{ + FAR struct i2c_master_s *i2c; + int ret; + +#ifdef CONFIG_AM67_I2C0 + i2c = am67_i2cbus_initialize(0); + if (i2c == NULL) + { + i2cerr("ERROR: Failed to initialize I2C0\n"); + } + else + { + ret = i2c_register(i2c, 0); + if (ret < 0) + { + i2cerr("ERROR: Failed to register /dev/i2c0: %d\n", ret); + } + } +#endif + +#ifdef CONFIG_AM67_WKUP_I2C0 + i2c = am67_i2cbus_initialize(2); + if (i2c == NULL) + { + i2cerr("ERROR: Failed to initialize WKUP_I2C0\n"); + } + else + { + ret = i2c_register(i2c, 2); + if (ret < 0) + { + i2cerr("ERROR: Failed to register /dev/i2c2: %d\n", ret); + } + } +#endif +} + +#endif /* CONFIG_AM67_I2C0 || CONFIG_AM67_WKUP_I2C0 */ 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 ad977b38ce589..948bace4a33ba 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 @@ -46,5 +46,9 @@ uint8_t am67_spi0status(FAR struct spi_dev_s *dev, uint32_t devid); void am67_spidev_initialize(void); #endif +#if defined(CONFIG_AM67_I2C0) || defined(CONFIG_AM67_WKUP_I2C0) +void am67_i2cdev_initialize(void); +#endif + #endif /* __ASSEMBLY__ */ #endif /* __BOARDS_ARM_T3_GEM_O1_SRC_T3_GEM_O1_H */ From 93edc28809ddfe78b88a5f32e8e71dc502939039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ula=C5=9F=20Sertan=20Keme=C3=A7?= Date: Tue, 15 Sep 2026 14:40:00 +0300 Subject: [PATCH 2/2] Documentation/am67: Document I2C support on t3-gem-o1. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Record the WKUP_I2C0 master in the board's Peripheral Support list. Assisted-by: Claude Code:claude-opus-5 Signed-off-by: Ulaş Sertan Kemeç --- Documentation/platforms/arm/am67/boards/t3-gem-o1/index.rst | 1 + arch/arm/src/am67/am67_i2c.c | 4 ---- boards/arm/am67/t3-gem-o1/src/am67_i2c.c | 3 --- 3 files changed, 1 insertion(+), 7 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 5b1eee618a04d..aad627e51b381 100644 --- a/Documentation/platforms/arm/am67/boards/t3-gem-o1/index.rst +++ b/Documentation/platforms/arm/am67/boards/t3-gem-o1/index.rst @@ -100,6 +100,7 @@ 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). +- **I2C:** WKUP_I2C0 master, registered as /dev/i2c2. Installation ============ diff --git a/arch/arm/src/am67/am67_i2c.c b/arch/arm/src/am67/am67_i2c.c index 29731b7100e1f..bd4d74cb15b14 100644 --- a/arch/arm/src/am67/am67_i2c.c +++ b/arch/arm/src/am67/am67_i2c.c @@ -52,9 +52,6 @@ /* At least one I2C peripheral must be enabled */ -#if defined(CONFIG_AM67_I2C0) || defined(CONFIG_AM67_I2C1) || \ - defined(CONFIG_AM67_WKUP_I2C0) - /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -1479,4 +1476,3 @@ int am67_i2cbus_uninitialize(struct i2c_master_s *dev) return OK; } -#endif /* CONFIG_AM67_I2C0 || CONFIG_AM67_I2C1 || CONFIG_AM67_WKUP_I2C0 */ diff --git a/boards/arm/am67/t3-gem-o1/src/am67_i2c.c b/boards/arm/am67/t3-gem-o1/src/am67_i2c.c index f842988c74827..fd4e1c0181f68 100644 --- a/boards/arm/am67/t3-gem-o1/src/am67_i2c.c +++ b/boards/arm/am67/t3-gem-o1/src/am67_i2c.c @@ -31,8 +31,6 @@ #include #include "am67_i2c.h" -#if defined(CONFIG_AM67_I2C0) || defined(CONFIG_AM67_WKUP_I2C0) - /**************************************************************************** * Public Functions ****************************************************************************/ @@ -79,4 +77,3 @@ void am67_i2cdev_initialize(void) #endif } -#endif /* CONFIG_AM67_I2C0 || CONFIG_AM67_WKUP_I2C0 */