This is an automated email from the ASF dual-hosted git repository.
xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 3adaf8e0437 arm/nrf54l: add TWIM support
3adaf8e0437 is described below
commit 3adaf8e043719aa8d60cf86586f75473713b8202
Author: raiden00pl <[email protected]>
AuthorDate: Sun Sep 20 16:23:54 2026 +0200
arm/nrf54l: add TWIM support
arm/nrf54l: add TWIM support
Assisted-by: Codex:GPT-6
Signed-off-by: raiden00pl <[email protected]>
---
Documentation/platforms/arm/nrf54l/index.rst | 20 +-
arch/arm/src/nrf54l/CMakeLists.txt | 3 +
arch/arm/src/nrf54l/Kconfig | 86 ++-
arch/arm/src/nrf54l/Make.defs | 4 +
arch/arm/src/nrf54l/hardware/nrf54l_twi.h | 214 +++++++
arch/arm/src/nrf54l/nrf54l_i2c.c | 894 +++++++++++++++++++++++++++
arch/arm/src/nrf54l/nrf54l_i2c.h | 75 +++
7 files changed, 1288 insertions(+), 8 deletions(-)
diff --git a/Documentation/platforms/arm/nrf54l/index.rst
b/Documentation/platforms/arm/nrf54l/index.rst
index b7318e976f9..b685cc74e74 100644
--- a/Documentation/platforms/arm/nrf54l/index.rst
+++ b/Documentation/platforms/arm/nrf54l/index.rst
@@ -24,7 +24,7 @@ RRAMC Yes Progmem erase/write interface
SAADC Yes
SPIM Yes
TIMER Yes
-TWIM No
+TWIM Yes
UARTE Yes No hardware flow control
USBHS No
WDT No
@@ -110,7 +110,7 @@ and optional command/data callbacks declared in
``nrf54l_spi.h``.
The driver supports 8-bit transfers, modes 0 through 3 and configurable
bit order. SPI4 supports up to 32 MHz; other instances support up to 8 MHz.
``SPI_SETFREQUENCY()`` returns the clock selected using the hardware divider.
-SPI and UART cannot use the same SERIAL instance concurrently.
+SPI, I2C and UART cannot use the same SERIAL instance concurrently.
TIMER
-----
@@ -119,6 +119,22 @@ TIMER0 through TIMER6 correspond to TIMER20, TIMER21,
TIMER22, TIMER23,
TIMER24, TIMER00 and TIMER10. The timer lower-half driver uses a 1 MHz
counter clock on each instance.
+TWIM
+----
+
+I2C0 through I2C3 correspond to TWIM20, TWIM21, TWIM22 and TWIM30.
+nRF54LM20 also provides I2C4 and I2C5 using TWIM23 and TWIM24. Each bus
+requires ``BOARD_I2Cn_SCL_PIN`` and ``BOARD_I2Cn_SDA_PIN`` definitions.
+UART and I2C cannot use the same SERIAL instance concurrently.
+
+The driver supports 7-bit addressing at 100, 250 and 400 kHz, including
+write/read transfers with a repeated START and two-part continued writes.
+Other message chains requiring an uninterrupted bus return ``-ENOTSUP``.
+Continued writes use a per-bus buffer sized by
+``CONFIG_NRF54L_I2C_MASTER_COPY_BUF_SIZE``. A combined write exceeding this
+size returns ``-E2BIG`` before starting the transaction.
+Bus reset is not supported.
+
UARTE
-----
diff --git a/arch/arm/src/nrf54l/CMakeLists.txt
b/arch/arm/src/nrf54l/CMakeLists.txt
index 0cbf364e664..cb33809d1a5 100644
--- a/arch/arm/src/nrf54l/CMakeLists.txt
+++ b/arch/arm/src/nrf54l/CMakeLists.txt
@@ -46,6 +46,9 @@ endif()
if(CONFIG_NRF54L_UART)
list(APPEND SRCS nrf54l_serial.c)
endif()
+if(CONFIG_NRF54L_I2C_MASTER)
+ list(APPEND SRCS nrf54l_i2c.c)
+endif()
if(CONFIG_NRF54L_SPI_MASTER)
list(APPEND SRCS nrf54l_spi.c)
endif()
diff --git a/arch/arm/src/nrf54l/Kconfig b/arch/arm/src/nrf54l/Kconfig
index 81e5ce4277b..f562280d332 100644
--- a/arch/arm/src/nrf54l/Kconfig
+++ b/arch/arm/src/nrf54l/Kconfig
@@ -25,6 +25,11 @@ endchoice # nRF54L Chip Selection
# Peripheral Selection
+config NRF54L_I2C_MASTER
+ bool
+ default n
+ select I2C
+
config NRF54L_SPI_MASTER
bool
default n
@@ -49,28 +54,64 @@ config NRF54L_GPIOTE
bool "GPIOTE (GPIO interrupts)"
default n
+config NRF54L_I2C0_MASTER
+ bool "I2C0 Master (TWIM20)"
+ default n
+ depends on !NRF54L_UART0
+ select NRF54L_I2C_MASTER
+
+config NRF54L_I2C1_MASTER
+ bool "I2C1 Master (TWIM21)"
+ default n
+ depends on !NRF54L_UART1
+ select NRF54L_I2C_MASTER
+
+config NRF54L_I2C2_MASTER
+ bool "I2C2 Master (TWIM22)"
+ default n
+ depends on !NRF54L_UART2
+ select NRF54L_I2C_MASTER
+
+config NRF54L_I2C3_MASTER
+ bool "I2C3 Master (TWIM30)"
+ default n
+ depends on !NRF54L_UART3
+ select NRF54L_I2C_MASTER
+
+config NRF54L_I2C4_MASTER
+ bool "I2C4 Master (TWIM23)"
+ default n
+ depends on !ARCH_CHIP_NRF54L15 && !NRF54L_UART5
+ select NRF54L_I2C_MASTER
+
+config NRF54L_I2C5_MASTER
+ bool "I2C5 Master (TWIM24)"
+ default n
+ depends on !ARCH_CHIP_NRF54L15 && !NRF54L_UART6
+ select NRF54L_I2C_MASTER
+
config NRF54L_SPI0_MASTER
bool "SPI0 Master (SPIM20)"
default n
- depends on !NRF54L_UART0
+ depends on !NRF54L_UART0 && !NRF54L_I2C0_MASTER
select NRF54L_SPI_MASTER
config NRF54L_SPI1_MASTER
bool "SPI1 Master (SPIM21)"
default n
- depends on !NRF54L_UART1
+ depends on !NRF54L_UART1 && !NRF54L_I2C1_MASTER
select NRF54L_SPI_MASTER
config NRF54L_SPI2_MASTER
bool "SPI2 Master (SPIM22)"
default n
- depends on !NRF54L_UART2
+ depends on !NRF54L_UART2 && !NRF54L_I2C2_MASTER
select NRF54L_SPI_MASTER
config NRF54L_SPI3_MASTER
bool "SPI3 Master (SPIM30)"
default n
- depends on !NRF54L_UART3
+ depends on !NRF54L_UART3 && !NRF54L_I2C3_MASTER
select NRF54L_SPI_MASTER
config NRF54L_SPI4_MASTER
@@ -82,13 +123,13 @@ config NRF54L_SPI4_MASTER
config NRF54L_SPI5_MASTER
bool "SPI5 Master (SPIM23)"
default n
- depends on !ARCH_CHIP_NRF54L15 && !NRF54L_UART5
+ depends on !ARCH_CHIP_NRF54L15 && !NRF54L_UART5 && !NRF54L_I2C4_MASTER
select NRF54L_SPI_MASTER
config NRF54L_SPI6_MASTER
bool "SPI6 Master (SPIM24)"
default n
- depends on !ARCH_CHIP_NRF54L15 && !NRF54L_UART6
+ depends on !ARCH_CHIP_NRF54L15 && !NRF54L_UART6 && !NRF54L_I2C5_MASTER
select NRF54L_SPI_MASTER
config NRF54L_UART0
@@ -496,6 +537,39 @@ endif # NRF54L_SPI_MASTER
endmenu # SPI Configuration
+menu "I2C Master Configuration"
+
+if NRF54L_I2C_MASTER
+
+config NRF54L_I2C_MASTER_DISABLE_NOSTART
+ bool "Disable the I2C Master NOSTART flag support"
+ default n
+ ---help---
+ Disable combining two write messages into one DMA buffer.
+
+config NRF54L_I2C_MASTER_COPY_BUF_SIZE
+ int "Static buffer size for NOSTART flag support"
+ depends on !NRF54L_I2C_MASTER_DISABLE_NOSTART
+ default 4
+ range 1 65535
+ ---help---
+ Size of the per-bus buffer used to combine two continued write
+ messages. Their combined length must fit in this buffer; larger
+ transfers return E2BIG before starting the transaction.
+
+config NRF54L_I2C_MASTER_TIMEOUT
+ int "I2C transfer timeout (milliseconds)"
+ default 1000
+ range 1 60000
+ ---help---
+ Send STOP if the transfer does not finish within this interval.
+ Completion still waits for STOPPED before releasing DMA buffers;
+ a device holding the bus can delay this cleanup.
+
+endif # NRF54L_I2C_MASTER
+
+endmenu # I2C Master Configuration
+
menuconfig NRF54L_SOFTDEVICE_CONTROLLER
bool "SoftDevice Controller"
depends on ALLOW_BSDNORDIC_COMPONENTS
diff --git a/arch/arm/src/nrf54l/Make.defs b/arch/arm/src/nrf54l/Make.defs
index 85565492eca..ba6fb4d5c8e 100644
--- a/arch/arm/src/nrf54l/Make.defs
+++ b/arch/arm/src/nrf54l/Make.defs
@@ -46,6 +46,10 @@ ifeq ($(CONFIG_NRF54L_UART),y)
CHIP_CSRCS += nrf54l_serial.c
endif
+ifeq ($(CONFIG_NRF54L_I2C_MASTER),y)
+CHIP_CSRCS += nrf54l_i2c.c
+endif
+
ifeq ($(CONFIG_NRF54L_SPI_MASTER),y)
CHIP_CSRCS += nrf54l_spi.c
endif
diff --git a/arch/arm/src/nrf54l/hardware/nrf54l_twi.h
b/arch/arm/src/nrf54l/hardware/nrf54l_twi.h
new file mode 100644
index 00000000000..c65dea3965f
--- /dev/null
+++ b/arch/arm/src/nrf54l/hardware/nrf54l_twi.h
@@ -0,0 +1,214 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/hardware/nrf54l_twi.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_NRF54L_HARDWARE_NRF54L_TWI_H
+#define __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_TWI_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "hardware/nrf54l_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register offsets for TWI master (TWIM) ***********************************/
+
+#define NRF54L_TWIM_TASKS_STOP_OFFSET 0x0004
/* Stop TWIM transaction */
+#define NRF54L_TWIM_TASKS_SUSPEND_OFFSET 0x000c
/* Suspend TWIM transaction */
+#define NRF54L_TWIM_TASKS_RESUME_OFFSET 0x0010
/* Resume TWIM transaction */
+#define NRF54L_TWIM_TASKS_DMA_RX_START_OFFSET 0x0028
/* Start receive sequence */
+#define NRF54L_TWIM_TASKS_DMA_RX_STOP_OFFSET 0x002c
/* Stop RX DMA */
+#define NRF54L_TWIM_TASKS_DMA_RX_ENABLEMATCH_OFFSET(n) (0x0030 + 4 * (n))
/* Enable RX match event n */
+#define NRF54L_TWIM_TASKS_DMA_RX_DISABLEMATCH_OFFSET(n) (0x0040 + 4 * (n))
/* Disable RX match event n */
+#define NRF54L_TWIM_TASKS_DMA_TX_START_OFFSET 0x0050
/* Start transmit sequence */
+#define NRF54L_TWIM_TASKS_DMA_TX_STOP_OFFSET 0x0054
/* Stop TX DMA */
+#define NRF54L_TWIM_SUBSCRIBE_STOP_OFFSET 0x0084
/* Subscribe to STOP task */
+#define NRF54L_TWIM_SUBSCRIBE_SUSPEND_OFFSET 0x008c
/* Subscribe to SUSPEND task */
+#define NRF54L_TWIM_SUBSCRIBE_RESUME_OFFSET 0x0090
/* Subscribe to RESUME task */
+#define NRF54L_TWIM_SUBSCRIBE_DMA_RX_START_OFFSET 0x00a8
/* Subscribe to RX START task */
+#define NRF54L_TWIM_SUBSCRIBE_DMA_RX_STOP_OFFSET 0x00ac
/* Subscribe to RX STOP task */
+#define NRF54L_TWIM_SUBSCRIBE_DMA_RX_ENABLEMATCH_OFFSET(n) (0x00b0 + 4 * (n))
/* Subscribe to RX ENABLEMATCH task n */
+#define NRF54L_TWIM_SUBSCRIBE_DMA_RX_DISABLEMATCH_OFFSET(n) (0x00c0 + 4 * (n))
/* Subscribe to RX DISABLEMATCH task n */
+#define NRF54L_TWIM_SUBSCRIBE_DMA_TX_START_OFFSET 0x00d0
/* Subscribe to TX START task */
+#define NRF54L_TWIM_SUBSCRIBE_DMA_TX_STOP_OFFSET 0x00d4
/* Subscribe to TX STOP task */
+#define NRF54L_TWIM_EVENTS_STOPPED_OFFSET 0x0104
/* TWIM stopped */
+#define NRF54L_TWIM_EVENTS_ERROR_OFFSET 0x0114
/* TWIM error */
+#define NRF54L_TWIM_EVENTS_SUSPENDED_OFFSET 0x0128
/* TWIM suspended */
+#define NRF54L_TWIM_EVENTS_LASTRX_OFFSET 0x0134
/* Starting to receive the last byte */
+#define NRF54L_TWIM_EVENTS_LASTTX_OFFSET 0x0138
/* Starting to transmit the last byte */
+#define NRF54L_TWIM_EVENTS_DMA_RX_END_OFFSET 0x014c
/* RX DMA buffer completed */
+#define NRF54L_TWIM_EVENTS_DMA_RX_READY_OFFSET 0x0150
/* RX DMA buffer prepared */
+#define NRF54L_TWIM_EVENTS_DMA_RX_BUSERROR_OFFSET 0x0154
/* RX DMA bus error */
+#define NRF54L_TWIM_EVENTS_DMA_RX_MATCH_OFFSET(n) (0x0158 + 4 * (n))
/* RX data matched candidate n */
+#define NRF54L_TWIM_EVENTS_DMA_TX_END_OFFSET 0x0168
/* TX DMA buffer completed */
+#define NRF54L_TWIM_EVENTS_DMA_TX_READY_OFFSET 0x016c
/* TX DMA buffer prepared */
+#define NRF54L_TWIM_EVENTS_DMA_TX_BUSERROR_OFFSET 0x0170
/* TX DMA bus error */
+#define NRF54L_TWIM_PUBLISH_STOPPED_OFFSET 0x0184
/* Publish STOPPED event */
+#define NRF54L_TWIM_PUBLISH_ERROR_OFFSET 0x0194
/* Publish ERROR event */
+#define NRF54L_TWIM_PUBLISH_SUSPENDED_OFFSET 0x01a8
/* Publish SUSPENDED event */
+#define NRF54L_TWIM_PUBLISH_LASTRX_OFFSET 0x01b4
/* Publish LASTRX event */
+#define NRF54L_TWIM_PUBLISH_LASTTX_OFFSET 0x01b8
/* Publish LASTTX event */
+#define NRF54L_TWIM_PUBLISH_DMA_RX_END_OFFSET 0x01cc
/* Publish RX END event */
+#define NRF54L_TWIM_PUBLISH_DMA_RX_READY_OFFSET 0x01d0
/* Publish RX READY event */
+#define NRF54L_TWIM_PUBLISH_DMA_RX_BUSERROR_OFFSET 0x01d4
/* Publish RX BUSERROR event */
+#define NRF54L_TWIM_PUBLISH_DMA_RX_MATCH_OFFSET(n) (0x01d8 + 4 * (n))
/* Publish RX MATCH event n */
+#define NRF54L_TWIM_PUBLISH_DMA_TX_END_OFFSET 0x01e8
/* Publish TX END event */
+#define NRF54L_TWIM_PUBLISH_DMA_TX_READY_OFFSET 0x01ec
/* Publish TX READY event */
+#define NRF54L_TWIM_PUBLISH_DMA_TX_BUSERROR_OFFSET 0x01f0
/* Publish TX BUSERROR event */
+#define NRF54L_TWIM_SHORTS_OFFSET 0x0200
/* Shortcuts between local events and tasks */
+#define NRF54L_TWIM_INTEN_OFFSET 0x0300
/* Enable or disable interrupt */
+#define NRF54L_TWIM_INTENSET_OFFSET 0x0304
/* Enable interrupt */
+#define NRF54L_TWIM_INTENCLR_OFFSET 0x0308
/* Disable interrupt */
+#define NRF54L_TWIM_ERRORSRC_OFFSET 0x04c4
/* Error source */
+#define NRF54L_TWIM_ENABLE_OFFSET 0x0500
/* Enable TWIM */
+#define NRF54L_TWIM_PSELSCL_OFFSET 0x0600
/* Pin select for SCL signal */
+#define NRF54L_TWIM_PSELSDA_OFFSET 0x0604
/* Pin select for SDA signal */
+#define NRF54L_TWIM_FREQUENCY_OFFSET 0x0524
/* TWIM frequency */
+#define NRF54L_TWIM_DMA_RX_PTR_OFFSET 0x0704
/* RX buffer address */
+#define NRF54L_TWIM_DMA_RX_MAXCNT_OFFSET 0x0708
/* RX buffer size */
+#define NRF54L_TWIM_DMA_RX_AMOUNT_OFFSET 0x070c
/* Number of received bytes */
+#define NRF54L_TWIM_DMA_RX_LIST_OFFSET 0x0714
/* RX list type */
+#define NRF54L_TWIM_DMA_RX_TERMINATEONBUSERROR_OFFSET 0x071c
/* Terminate RX DMA on bus error */
+#define NRF54L_TWIM_DMA_RX_BUSERRORADDRESS_OFFSET 0x0720
/* Address of last RX DMA bus error */
+#define NRF54L_TWIM_DMA_RX_MATCH_CONFIG_OFFSET 0x0724
/* RX DMA match configuration */
+#define NRF54L_TWIM_DMA_RX_MATCH_CANDIDATE_OFFSET(n) (0x0728 + 4 * (n))
/* RX DMA match candidate n */
+#define NRF54L_TWIM_DMA_TX_PTR_OFFSET 0x073c
/* TX buffer address */
+#define NRF54L_TWIM_DMA_TX_MAXCNT_OFFSET 0x0740
/* TX buffer size */
+#define NRF54L_TWIM_DMA_TX_AMOUNT_OFFSET 0x0744
/* Number of transmitted bytes */
+#define NRF54L_TWIM_DMA_TX_LIST_OFFSET 0x074c
/* TX list type */
+#define NRF54L_TWIM_DMA_TX_TERMINATEONBUSERROR_OFFSET 0x0754
/* Terminate TX DMA on bus error */
+#define NRF54L_TWIM_DMA_TX_BUSERRORADDRESS_OFFSET 0x0758
/* Address of last TX DMA bus error */
+#define NRF54L_TWIM_ADDRESS_OFFSET 0x0588
/* TWIM address */
+
+/* Register Bitfield Definitions for TWIM ***********************************/
+
+/* SHORTS Register */
+
+#define TWIM_SHORTS_LASTTX_STARTRX (1 << 7) /* Bit 7: Shortcut
between event LASTTX and task STARTRX */
+#define TWIM_SHORTS_LASTTX_SUSPEND (1 << 8) /* Bit 8: Shortcut
between event LASTTX and task SUSPEND */
+#define TWIM_SHORTS_LASTTX_STOP (1 << 9) /* Bit 9: Shortcut
between event LASTTX and task STOP */
+#define TWIM_SHORTS_LASTRX_STARTTX (1 << 10) /* Bit 10: Shortcut
between event LASTRX and task STARTTX */
+#define TWIM_SHORTS_LASTRX_STOP (1 << 12) /* Bit 12: Shortcut
between event LASTRX and task STOP */
+
+/* INTEN/INTENSET/INTENCLR Register */
+
+#define TWIM_INT_STOPPED (1 << 1) /* Bit 1: Interrupt for
event STOPPED */
+#define TWIM_INT_ERROR (1 << 5) /* Bit 5: ERROR */
+#define TWIM_INT_SUSPENDED (1 << 10) /* Bit 10: SUSPENDED */
+#define TWIM_INT_LASTRX (1 << 13) /* Bit 13: LASTRX */
+#define TWIM_INT_LASTTX (1 << 14) /* Bit 14: LASTTX */
+#define TWIM_INT_DMA_RX_END (1 << 19)
+#define TWIM_INT_DMA_RX_READY (1 << 20)
+#define TWIM_INT_DMA_RX_BUSERROR (1 << 21)
+#define TWIM_INT_DMA_RX_MATCH(n) (1 << (22 + (n)))
+#define TWIM_INT_DMA_TX_END (1 << 26)
+#define TWIM_INT_DMA_TX_READY (1 << 27)
+#define TWIM_INT_DMA_TX_BUSERROR (1 << 28)
+
+/* ERRORSRC Register */
+
+#define TWIM_ERRORSRC_OVERRUN (1 << 0) /* Bit 0: Overrun error
*/
+#define TWIM_ERRORSRC_ANACK (1 << 1) /* Bit 1: NACK received
after sending the address */
+#define TWIM_ERRORSRC_DNACK (1 << 2) /* Bit 2: NACK received
after sending a data byte */
+
+/* ENABLE Register */
+
+#define TWIM_ENABLE_DIS (0) /* Disable TWIM */
+#define TWIM_ENABLE_EN (0x6 << 0) /* Enable TWIM */
+
+/* PSELSCL Register */
+
+#define TWIM_PSELSCL_PIN_SHIFT (0) /* Bits 0-4: SCL pin
number */
+#define TWIM_PSELSCL_PIN_MASK (0x1f << TWIM_PSELSCL_PIN_SHIFT)
+#define TWIM_PSELSCL_PORT_SHIFT (5) /* Bits 5-7: SCL port
number */
+#define TWIM_PSELSCL_PORT_MASK (0x7 << TWIM_PSELSCL_PORT_SHIFT)
+#define TWIM_PSELSCL_CONNECTED (0 << 31) /* Bit 31: Connection */
+#define TWIM_PSELSCL_DISCONNECTED (1 << 31)
+#define TWIM_PSELSCL_RESET (0xffffffff)
+
+/* PSELSDA Register */
+
+#define TWIM_PSELSDA_PIN_SHIFT (0) /* Bits 0-4: SDA pin
number */
+#define TWIM_PSELSDA_PIN_MASK (0x1f << TWIM_PSELSDA_PIN_SHIFT)
+#define TWIM_PSELSDA_PORT_SHIFT (5) /* Bits 5-7: SDA port
number */
+#define TWIM_PSELSDA_PORT_MASK (0x7 << TWIM_PSELSDA_PORT_SHIFT)
+#define TWIM_PSELSDA_CONNECTED (0 << 31) /* Bit 31: Connection */
+#define TWIM_PSELSDA_DISCONNECTED (1 << 31)
+#define TWIM_PSELSDA_RESET (0xffffffff)
+
+/* FREQUENCY Register */
+
+#define TWIM_FREQUENCY_100KBPS (0x01980000) /* 100 kbps */
+#define TWIM_FREQUENCY_250KBPS (0x04000000) /* 250 kbps */
+#define TWIM_FREQUENCY_400KBPS (0x06400000) /* 400 kbps */
+#define TWIM_FREQUENCY_1000KBPS (0x0ff00000) /* 1000 kbps */
+
+/* RXDMAXCNT Register */
+
+#define TWIM_RXDMAXCNT_SHIFT (0) /* Bits 0-15: Maximum
number of bytes in receive buffer */
+#define TWIM_RXDMAXCNT_MASK (0xffff << TWIM_RXDMAXCNT_SHIFT)
+
+/* RXDAMOUNT Register */
+
+#define TWIM_RXDAMOUNT_SHIFT (0) /* Bits 0-15: Number of
bytes transferred in the last transaction */
+#define TWIM_RXDAMOUNT_MASK (0xffff << TWIM_RXDAMOUNT_SHIFT)
+
+/* TXDMAXCNT Register */
+
+#define TWIM_TXDMAXCNT_SHIFT (0) /* Bits 0-15: Maximum
number of bytes in transmit buffer */
+#define TWIM_TXDMAXCNT_MASK (0xffff << TWIM_TXDMAXCNT_SHIFT)
+
+/* TXDAMOUNT Register */
+
+#define TWIM_TXDAMOUNT_SHIFT (0) /* Bits 0-15: Number of
bytes transferred in the last transaction */
+#define TWIM_TXDAMOUNT_MASK (0xffff << TWIM_TXDAMOUNT_SHIFT)
+
+/* ADDRESS Register */
+
+#define TWIM_ADDRESS_SHIFT (0) /* Bits 0-6: Address
used in the TWI transfer */
+#define TWIM_ADDRESS_MASK (0x7f << TWIM_ADDRESS_SHIFT)
+
+/* DPPI subscribe and publish registers */
+
+#define TWIM_DPPI_CHIDX_SHIFT (0)
+#define TWIM_DPPI_CHIDX_MASK (0xff << TWIM_DPPI_CHIDX_SHIFT)
+#define TWIM_DPPI_EN (1 << 31)
+
+/* DMA registers */
+
+#define TWIM_DMA_LIST_DISABLED (0)
+#define TWIM_DMA_LIST_ARRAYLIST (1)
+#define TWIM_DMA_TERMINATEONBUSERROR (1)
+#define TWIM_DMA_MATCH_ENABLE(n) (1 << (n))
+#define TWIM_DMA_MATCH_ONESHOT(n) (1 << (16 + (n)))
+#define TWIM_DMA_MATCH_CANDIDATE_MASK (0xff)
+
+/* Pattern matcher shortcuts */
+
+#define TWIM_SHORTS_MATCH_ENABLE(n) (1 << (21 + (n)))
+#define TWIM_SHORTS_MATCH_DISABLE(n) (1 << (25 + (n)))
+
+#endif /* __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_TWI_H */
diff --git a/arch/arm/src/nrf54l/nrf54l_i2c.c b/arch/arm/src/nrf54l/nrf54l_i2c.c
new file mode 100644
index 00000000000..1b668c78e6d
--- /dev/null
+++ b/arch/arm/src/nrf54l/nrf54l_i2c.c
@@ -0,0 +1,894 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/nrf54l_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 <nuttx/config.h>
+
+#include <assert.h>
+#include <errno.h>
+
+#include <nuttx/debug.h>
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/mutex.h>
+#include <nuttx/kmalloc.h>
+#include <arch/board/board.h>
+
+#include "arm_internal.h"
+#include "nrf54l_gpio.h"
+#include "nrf54l_i2c.h"
+
+#include "hardware/nrf54l_twi.h"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* I2C Device Private Data */
+
+struct nrf54l_i2c_priv_s
+{
+ const struct i2c_ops_s *ops; /* Standard I2C operations */
+ uint32_t base; /* TWI base address */
+ uint32_t scl_pin; /* SCL pin configuration */
+ uint32_t sda_pin; /* SDA pin configuration */
+ int refs; /* Reference count */
+ int status; /* I2C transfer status */
+#ifndef CONFIG_I2C_POLLED
+ uint32_t irq; /* TWI interrupt */
+#endif
+ uint8_t msgc; /* Message count */
+ struct i2c_msg_s *msgv; /* Message list */
+ uint8_t *ptr; /* Current message buffer */
+#ifdef CONFIG_NRF54L_I2C_MASTER_COPY_BUF_SIZE
+ /* Static buffer used for continued messages */
+
+ uint8_t copy_buf[CONFIG_NRF54L_I2C_MASTER_COPY_BUF_SIZE];
+#endif
+ uint32_t freq; /* Current I2C frequency */
+ int dcnt; /* Current message length */
+ uint16_t flags; /* Current message flags */
+ uint16_t addr; /* Current I2C address */
+ mutex_t lock; /* Mutual exclusion mutex */
+#ifndef CONFIG_I2C_POLLED
+ sem_t sem_isr; /* Interrupt wait semaphore */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static inline void nrf54l_i2c_putreg(struct nrf54l_i2c_priv_s *priv,
+ uint32_t offset,
+ uint32_t value);
+static inline uint32_t nrf54l_i2c_getreg(struct nrf54l_i2c_priv_s *priv,
+ uint32_t offset);
+static int nrf54l_i2c_transfer(struct i2c_master_s *dev,
+ struct i2c_msg_s *msgs,
+ int count);
+#ifdef CONFIG_I2C_RESET
+static int nrf54l_i2c_reset(struct i2c_master_s *dev);
+#endif
+#ifndef CONFIG_I2C_POLLED
+static int nrf54l_i2c_isr(int irq, void *context, void *arg);
+#endif
+static int nrf54l_i2c_deinit(struct nrf54l_i2c_priv_s *priv);
+static int nrf54l_i2c_init(struct nrf54l_i2c_priv_s *priv);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* I2C operations */
+
+static const struct i2c_ops_s g_nrf54l_i2c_ops =
+{
+ .transfer = nrf54l_i2c_transfer
+#ifdef CONFIG_I2C_RESET
+ , .reset = nrf54l_i2c_reset
+#endif
+};
+
+/* I2C0 (TWIM20) device */
+
+#ifdef CONFIG_NRF54L_I2C0_MASTER
+
+static struct nrf54l_i2c_priv_s g_nrf54l_i2c0_priv =
+{
+ .ops = &g_nrf54l_i2c_ops,
+ .base = NRF54L_TWIM20_BASE,
+ .scl_pin = BOARD_I2C0_SCL_PIN,
+ .sda_pin = BOARD_I2C0_SDA_PIN,
+ .refs = 0,
+ .lock = NXMUTEX_INITIALIZER,
+#ifndef CONFIG_I2C_POLLED
+ .sem_isr = SEM_INITIALIZER(0),
+ .irq = NRF54L_IRQ_SERIAL20,
+#endif
+ .msgc = 0,
+ .msgv = NULL,
+ .ptr = NULL,
+ .freq = 0,
+ .dcnt = 0,
+ .flags = 0,
+ .addr = 0,
+};
+#endif
+
+/* I2C1 (TWIM21) device */
+
+#ifdef CONFIG_NRF54L_I2C1_MASTER
+static struct nrf54l_i2c_priv_s g_nrf54l_i2c1_priv =
+{
+ .ops = &g_nrf54l_i2c_ops,
+ .base = NRF54L_TWIM21_BASE,
+ .scl_pin = BOARD_I2C1_SCL_PIN,
+ .sda_pin = BOARD_I2C1_SDA_PIN,
+ .refs = 0,
+ .lock = NXMUTEX_INITIALIZER,
+#ifndef CONFIG_I2C_POLLED
+ .sem_isr = SEM_INITIALIZER(0),
+ .irq = NRF54L_IRQ_SERIAL21,
+#endif
+ .msgc = 0,
+ .msgv = NULL,
+ .ptr = NULL,
+ .freq = 0,
+ .dcnt = 0,
+ .flags = 0,
+ .addr = 0,
+};
+#endif
+
+/* I2C2 (TWIM22) device */
+
+#ifdef CONFIG_NRF54L_I2C2_MASTER
+static struct nrf54l_i2c_priv_s g_nrf54l_i2c2_priv =
+{
+ .ops = &g_nrf54l_i2c_ops,
+ .base = NRF54L_TWIM22_BASE,
+ .scl_pin = BOARD_I2C2_SCL_PIN,
+ .sda_pin = BOARD_I2C2_SDA_PIN,
+ .refs = 0,
+ .lock = NXMUTEX_INITIALIZER,
+#ifndef CONFIG_I2C_POLLED
+ .sem_isr = SEM_INITIALIZER(0),
+ .irq = NRF54L_IRQ_SERIAL22,
+#endif
+ .msgc = 0,
+ .msgv = NULL,
+ .ptr = NULL,
+ .freq = 0,
+ .dcnt = 0,
+ .flags = 0,
+ .addr = 0,
+};
+#endif
+
+/* I2C3 (TWIM30) device */
+
+#ifdef CONFIG_NRF54L_I2C3_MASTER
+static struct nrf54l_i2c_priv_s g_nrf54l_i2c3_priv =
+{
+ .ops = &g_nrf54l_i2c_ops,
+ .base = NRF54L_TWIM30_BASE,
+ .scl_pin = BOARD_I2C3_SCL_PIN,
+ .sda_pin = BOARD_I2C3_SDA_PIN,
+ .refs = 0,
+ .lock = NXMUTEX_INITIALIZER,
+#ifndef CONFIG_I2C_POLLED
+ .sem_isr = SEM_INITIALIZER(0),
+ .irq = NRF54L_IRQ_SERIAL30,
+#endif
+ .msgc = 0,
+ .msgv = NULL,
+ .ptr = NULL,
+ .freq = 0,
+ .dcnt = 0,
+ .flags = 0,
+ .addr = 0,
+};
+#endif
+
+/* I2C4 (TWIM23) device */
+
+#ifdef CONFIG_NRF54L_I2C4_MASTER
+static struct nrf54l_i2c_priv_s g_nrf54l_i2c4_priv =
+{
+ .ops = &g_nrf54l_i2c_ops,
+ .base = NRF54L_TWIM23_BASE,
+ .scl_pin = BOARD_I2C4_SCL_PIN,
+ .sda_pin = BOARD_I2C4_SDA_PIN,
+ .refs = 0,
+ .lock = NXMUTEX_INITIALIZER,
+#ifndef CONFIG_I2C_POLLED
+ .sem_isr = SEM_INITIALIZER(0),
+ .irq = NRF54L_IRQ_SERIAL23,
+#endif
+ .msgc = 0,
+ .msgv = NULL,
+ .ptr = NULL,
+ .freq = 0,
+ .dcnt = 0,
+ .flags = 0,
+ .addr = 0,
+};
+#endif
+
+/* I2C5 (TWIM24) device */
+
+#ifdef CONFIG_NRF54L_I2C5_MASTER
+static struct nrf54l_i2c_priv_s g_nrf54l_i2c5_priv =
+{
+ .ops = &g_nrf54l_i2c_ops,
+ .base = NRF54L_TWIM24_BASE,
+ .scl_pin = BOARD_I2C5_SCL_PIN,
+ .sda_pin = BOARD_I2C5_SDA_PIN,
+ .refs = 0,
+ .lock = NXMUTEX_INITIALIZER,
+#ifndef CONFIG_I2C_POLLED
+ .sem_isr = SEM_INITIALIZER(0),
+ .irq = NRF54L_IRQ_SERIAL24,
+#endif
+ .msgc = 0,
+ .msgv = NULL,
+ .ptr = NULL,
+ .freq = 0,
+ .dcnt = 0,
+ .flags = 0,
+ .addr = 0,
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf54l_i2c_putreg
+ *
+ * Description:
+ * Put a 32-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline void nrf54l_i2c_putreg(struct nrf54l_i2c_priv_s *priv,
+ uint32_t offset,
+ uint32_t value)
+{
+ putreg32(value, priv->base + offset);
+}
+
+/****************************************************************************
+ * Name: nrf54l_i2c_getreg
+ *
+ * Description:
+ * Get a 32-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline uint32_t nrf54l_i2c_getreg(struct nrf54l_i2c_priv_s *priv,
+ uint32_t offset)
+{
+ return getreg32(priv->base + offset);
+}
+
+/****************************************************************************
+ * Name: nrf54l_i2c_transfer
+ *
+ * Description:
+ * Generic I2C transfer function
+ *
+ ****************************************************************************/
+
+static int nrf54l_i2c_transfer(struct i2c_master_s *dev,
+ struct i2c_msg_s *msgs,
+ int count)
+{
+ struct nrf54l_i2c_priv_s *priv = (struct nrf54l_i2c_priv_s *)dev;
+ uint32_t regval = 0;
+ int ret = OK;
+#ifndef CONFIG_NRF54L_I2C_MASTER_DISABLE_NOSTART
+ uint8_t *pack_buf = NULL;
+#endif
+
+ ret = nxmutex_lock(&priv->lock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Reset ptr and dcnt */
+
+ priv->dcnt = 0;
+ priv->ptr = NULL;
+
+ priv->msgv = msgs;
+ priv->msgc = count;
+
+ /* Reset I2C transfer status */
+
+ priv->status = OK;
+
+ i2cinfo("I2C TRANSFER count=%d\n", count);
+
+ /* Do we need change I2C bus frequency ? */
+
+ if (priv->msgv->frequency != priv->freq)
+ {
+ /* Get TWI frequency */
+
+ switch (priv->msgv->frequency)
+ {
+ case 100000:
+ {
+ regval = TWIM_FREQUENCY_100KBPS;
+ break;
+ }
+
+ case 250000:
+ {
+ regval = TWIM_FREQUENCY_250KBPS;
+ break;
+ }
+
+ case 400000:
+ {
+ regval = TWIM_FREQUENCY_400KBPS;
+ break;
+ }
+
+ default:
+ {
+ ret = -EINVAL;
+ goto errout;
+ }
+ }
+
+ /* Write TWI frequency */
+
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_FREQUENCY_OFFSET, regval);
+
+ /* Save the new I2C frequency */
+
+ priv->freq = priv->msgv->frequency;
+ }
+
+ /* I2C transfer */
+
+ do
+ {
+ /* Get current message data */
+
+ priv->ptr = priv->msgv->buffer;
+ priv->dcnt = priv->msgv->length;
+ priv->flags = priv->msgv->flags;
+ priv->addr = priv->msgv->addr;
+
+ i2cinfo("ptr=%p dcnt=%d flags=%d addr=%d\n",
+ priv->ptr, priv->dcnt, priv->flags, priv->addr);
+
+ /* Write TWI address */
+
+ regval = priv->addr;
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_ADDRESS_OFFSET, regval);
+
+ if ((priv->flags & I2C_M_READ) == 0)
+ {
+#ifndef CONFIG_NRF54L_I2C_MASTER_DISABLE_NOSTART
+ /* Check if we need to combine messages */
+
+ if (priv->msgc > 1)
+ {
+ if (priv->msgv[1].flags & I2C_M_NOSTART)
+ {
+ /* More than 2 messages not supported */
+
+ DEBUGASSERT(priv->msgc < 3);
+
+ /* Combine buffers */
+
+ if ((priv->msgv[0].length +
+ priv->msgv[1].length) <=
+ CONFIG_NRF54L_I2C_MASTER_COPY_BUF_SIZE)
+ {
+ pack_buf = priv->copy_buf;
+ }
+ else
+ {
+ pack_buf = kmm_malloc(priv->msgv[0].length +
+ priv->msgv[1].length);
+ if (pack_buf == NULL)
+ {
+ return -ENOMEM;
+ }
+ }
+
+ /* Combine messages */
+
+ memcpy(pack_buf, priv->msgv[0].buffer,
+ priv->msgv[0].length);
+ memcpy(pack_buf + priv->msgv[0].length,
+ priv->msgv[1].buffer, priv->msgv[1].length);
+
+ /* Use new buffer to transmit data */
+
+ priv->ptr = pack_buf;
+ priv->dcnt = priv->msgv[0].length + priv->msgv[1].length;
+
+ /* Next message */
+
+ priv->msgc -= 1;
+ priv->msgv += 1;
+ }
+ }
+#else
+ if (priv->msgc > 1)
+ {
+ if (priv->msgv[1].flags & I2C_M_NOSTART)
+ {
+ /* Not supported */
+
+ DEBUGPANIC();
+ }
+ }
+#endif
+
+ /* Write TXD data pointer. Zero-length transfers (used for bus
+ * scanning) never access the buffer, so any pointer is valid.
+ */
+
+ regval = (uintptr_t)priv->ptr;
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_DMA_TX_PTR_OFFSET, regval);
+
+ /* Write number of bytes in TXD buffer */
+
+ regval = priv->dcnt;
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_DMA_TX_MAXCNT_OFFSET, regval);
+
+ /* Shortcut from LASTTX to STOP */
+
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_SHORTS_OFFSET,
+ TWIM_SHORTS_LASTTX_STOP);
+
+ /* Start TX sequence */
+
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_TASKS_DMA_TX_START_OFFSET, 1);
+ }
+ else
+ {
+ /* Write RXD data pointer. Zero-length transfers (used for bus
+ * scanning) never access the buffer, so any pointer is valid.
+ */
+
+ regval = (uintptr_t)priv->ptr;
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_DMA_RX_PTR_OFFSET, regval);
+
+ /* Write number of bytes in RXD buffer */
+
+ regval = priv->dcnt;
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_DMA_RX_MAXCNT_OFFSET, regval);
+
+ /* Shortcuts from LASTRX to STOP */
+
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_SHORTS_OFFSET,
+ TWIM_SHORTS_LASTRX_STOP);
+
+ /* Start RX sequence */
+
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_TASKS_DMA_RX_START_OFFSET, 1);
+ }
+
+ /* Wait for stop event */
+
+#ifdef CONFIG_I2C_POLLED
+ while (1)
+ {
+ regval = nrf54l_i2c_getreg(priv,
+ NRF54L_TWIM_ERRORSRC_OFFSET) & 0x7;
+ if (regval != 0)
+ {
+ i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval);
+ ret = -1;
+ nrf54l_i2c_putreg(priv,
+ NRF54L_TWIM_ERRORSRC_OFFSET, 0x7);
+ goto errout;
+ }
+
+ if (nrf54l_i2c_getreg(priv,
+ NRF54L_TWIM_EVENTS_STOPPED_OFFSET) == 1)
+ {
+ break;
+ }
+ }
+
+ /* Clear event */
+
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_EVENTS_STOPPED_OFFSET, 0);
+#else
+ nxsem_wait(&priv->sem_isr);
+
+ if (priv->status < 0)
+ {
+ ret = priv->status;
+ goto errout;
+ }
+#endif
+
+ /* Next message */
+
+ priv->msgc -= 1;
+ priv->msgv += 1;
+ }
+ while (priv->msgc > 0);
+
+errout:
+#ifndef CONFIG_NRF54L_I2C_MASTER_DISABLE_NOSTART
+ if (pack_buf != NULL && pack_buf != priv->copy_buf)
+ {
+ kmm_free(pack_buf);
+ }
+#endif
+
+ nxmutex_unlock(&priv->lock);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nrf54l_i2c_reset
+ *
+ * Description:
+ * Perform an I2C bus reset in an attempt to break loose stuck I2C devices.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_I2C_RESET
+static int nrf54l_i2c_reset(struct i2c_master_s *dev)
+{
+#error not implemented
+}
+#endif
+
+/****************************************************************************
+ * Name: nrf54l_i2c_isr
+ *
+ * Description:
+ * Common I2C interrupt service routine
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_I2C_POLLED
+static int nrf54l_i2c_isr(int irq, void *context, void *arg)
+{
+ struct nrf54l_i2c_priv_s *priv = (struct nrf54l_i2c_priv_s *)arg;
+ uint32_t regval = 0;
+
+ /* Reset I2C status */
+
+ priv->status = OK;
+
+ if ((priv->flags & I2C_M_READ) == 0)
+ {
+ if (nrf54l_i2c_getreg(priv, NRF54L_TWIM_EVENTS_LASTTX_OFFSET) == 1)
+ {
+ i2cinfo("I2C LASTTX\n");
+
+ /* Clear event */
+
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_EVENTS_LASTTX_OFFSET, 0);
+
+ return OK;
+ }
+ }
+ else
+ {
+ if (nrf54l_i2c_getreg(priv, NRF54L_TWIM_EVENTS_LASTRX_OFFSET) == 1)
+ {
+ i2cinfo("I2C LASTRX\n");
+
+ /* Clear event */
+
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_EVENTS_LASTRX_OFFSET, 0);
+
+ return OK;
+ }
+ }
+
+ if (nrf54l_i2c_getreg(priv, NRF54L_TWIM_EVENTS_STOPPED_OFFSET) == 1)
+ {
+ i2cinfo("I2C STOPPED\n");
+
+ /* STOPPED event */
+
+ nxsem_post(&priv->sem_isr);
+
+ /* Clear event */
+
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_EVENTS_STOPPED_OFFSET, 0);
+ }
+
+ if (nrf54l_i2c_getreg(priv, NRF54L_TWIM_EVENTS_ERROR_OFFSET) == 1)
+ {
+ regval = nrf54l_i2c_getreg(priv, NRF54L_TWIM_ERRORSRC_OFFSET) & 0x7;
+
+ i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval);
+
+ /* Set ERROR status */
+
+ if (regval & TWIM_ERRORSRC_OVERRUN)
+ {
+ /* Overrun error */
+
+ priv->status = -EIO;
+ }
+ else
+ {
+ /* NACK */
+
+ priv->status = -ENXIO;
+ }
+
+ /* ERROR event */
+
+ nxsem_post(&priv->sem_isr);
+
+ /* Clear event */
+
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_EVENTS_ERROR_OFFSET, 0);
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_ERRORSRC_OFFSET, 0x7);
+ }
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: nrf54l_i2c_init
+ *
+ * Description:
+ * Setup the I2C hardware, ready for operation with defaults
+ *
+ ****************************************************************************/
+
+static int nrf54l_i2c_init(struct nrf54l_i2c_priv_s *priv)
+{
+ uint32_t regval = 0;
+ int pin = 0;
+ int port = 0;
+
+ /* Disable TWI interface */
+
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_ENABLE_OFFSET, TWIM_ENABLE_DIS);
+
+ /* Configure SCL and SDA pins */
+
+ nrf54l_gpio_config(priv->scl_pin);
+ nrf54l_gpio_config(priv->sda_pin);
+
+ /* Select SCL pin */
+
+ pin = GPIO_PIN_DECODE(priv->scl_pin);
+ port = GPIO_PORT_DECODE(priv->scl_pin);
+
+ regval = (pin << TWIM_PSELSCL_PIN_SHIFT);
+ regval |= (port << TWIM_PSELSCL_PORT_SHIFT);
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_PSELSCL_OFFSET, regval);
+
+ /* Select SDA pin */
+
+ pin = GPIO_PIN_DECODE(priv->sda_pin);
+ port = GPIO_PORT_DECODE(priv->sda_pin);
+
+ regval = (pin << TWIM_PSELSDA_PIN_SHIFT);
+ regval |= (port << TWIM_PSELSDA_PORT_SHIFT);
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_PSELSDA_OFFSET, regval);
+
+ /* Enable TWI interface */
+
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_ENABLE_OFFSET, TWIM_ENABLE_EN);
+
+#ifndef CONFIG_I2C_POLLED
+ /* Enable I2C interrupts */
+
+ regval = (TWIM_INT_STOPPED | TWIM_INT_ERROR);
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_INTEN_OFFSET, regval);
+
+ /* Attach error and event interrupts to the ISRs */
+
+ irq_attach(priv->irq, nrf54l_i2c_isr, priv);
+ up_enable_irq(priv->irq);
+#endif
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nrf54l_i2c_deinit
+ *
+ * Description:
+ * Shutdown the I2C hardware
+ *
+ ****************************************************************************/
+
+static int nrf54l_i2c_deinit(struct nrf54l_i2c_priv_s *priv)
+{
+ /* Disable TWI interface */
+
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_ENABLE_OFFSET, TWIM_ENABLE_DIS);
+
+ /* Unconfigure GPIO pins */
+
+ nrf54l_gpio_unconfig(priv->scl_pin);
+ nrf54l_gpio_unconfig(priv->sda_pin);
+
+ /* Deatach TWI from GPIO */
+
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_PSELSCL_OFFSET, TWIM_PSELSCL_RESET);
+ nrf54l_i2c_putreg(priv, NRF54L_TWIM_PSELSDA_OFFSET, TWIM_PSELSDA_RESET);
+
+ /* Disable and detach interrupts */
+
+#ifndef CONFIG_I2C_POLLED
+ up_disable_irq(priv->irq);
+ irq_detach(priv->irq);
+#endif
+
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf54l_i2cbus_initialize
+ *
+ * Description:
+ * Initialize one I2C bus
+ *
+ ****************************************************************************/
+
+struct i2c_master_s *nrf54l_i2cbus_initialize(int port)
+{
+ struct nrf54l_i2c_priv_s *priv = NULL;
+
+ i2cinfo("I2C INITIALIZE port=%d\n", port);
+
+ /* Get interface */
+
+ switch (port)
+ {
+#ifdef CONFIG_NRF54L_I2C0_MASTER
+ case 0:
+ {
+ priv = (struct nrf54l_i2c_priv_s *)&g_nrf54l_i2c0_priv;
+ break;
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_I2C1_MASTER
+ case 1:
+ {
+ priv = (struct nrf54l_i2c_priv_s *)&g_nrf54l_i2c1_priv;
+ break;
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_I2C2_MASTER
+ case 2:
+ {
+ priv = (struct nrf54l_i2c_priv_s *)&g_nrf54l_i2c2_priv;
+ break;
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_I2C3_MASTER
+ case 3:
+ {
+ priv = (struct nrf54l_i2c_priv_s *)&g_nrf54l_i2c3_priv;
+ break;
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_I2C4_MASTER
+ case 4:
+ {
+ priv = (struct nrf54l_i2c_priv_s *)&g_nrf54l_i2c4_priv;
+ break;
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_I2C5_MASTER
+ case 5:
+ {
+ priv = (struct nrf54l_i2c_priv_s *)&g_nrf54l_i2c5_priv;
+ break;
+ }
+#endif
+
+ default:
+ {
+ return NULL;
+ }
+ }
+
+ /* Initialize private data for the first time, increment reference count,
+ * power-up hardware and configure GPIOs.
+ */
+
+ nxmutex_lock(&priv->lock);
+ if (priv->refs++ == 0)
+ {
+ /* Initialize I2C */
+
+ nrf54l_i2c_init(priv);
+ }
+
+ nxmutex_unlock(&priv->lock);
+ return (struct i2c_master_s *)priv;
+}
+
+/****************************************************************************
+ * Name: nrf54l_i2cbus_uninitialize
+ *
+ * Description:
+ * Uninitialize an I2C bus
+ *
+ ****************************************************************************/
+
+int nrf54l_i2cbus_uninitialize(struct i2c_master_s *dev)
+{
+ struct nrf54l_i2c_priv_s *priv = (struct nrf54l_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)
+ {
+ nxmutex_unlock(&priv->lock);
+ return OK;
+ }
+
+ /* Disable power and other HW resource (GPIO's) */
+
+ nrf54l_i2c_deinit(priv);
+ nxmutex_unlock(&priv->lock);
+
+ return OK;
+}
diff --git a/arch/arm/src/nrf54l/nrf54l_i2c.h b/arch/arm/src/nrf54l/nrf54l_i2c.h
new file mode 100644
index 00000000000..782a0db2c1e
--- /dev/null
+++ b/arch/arm/src/nrf54l/nrf54l_i2c.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/nrf54l_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_NRF54L_NRF54L_I2C_H
+#define __ARCH_ARM_SRC_NRF54L_NRF54L_I2C_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/i2c/i2c_master.h>
+
+#include "chip.h"
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf54l_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 TWI interfaces)
+ *
+ * Returned Value:
+ * Valid I2C device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+struct i2c_master_s *nrf54l_i2cbus_initialize(int port);
+
+/****************************************************************************
+ * Name: nrf54l_i2cbus_uninitialize
+ *
+ * Description:
+ * De-initialize the selected I2C port, and power down the device.
+ *
+ * Input Parameters:
+ * Device structure as returned by the nrf54l_i2cbus_initialize()
+ *
+ * Returned Value:
+ * OK on success, ERROR when internal reference count mismatch or dev
+ * points to invalid hardware device.
+ *
+ ****************************************************************************/
+
+int nrf54l_i2cbus_uninitialize(struct i2c_master_s *dev);
+
+#endif /* __ARCH_ARM_SRC_NRF54L_NRF54L_I2C_H */