diff --git a/arch/arm/src/rp2040/Kconfig b/arch/arm/src/rp2040/Kconfig index 7753430cd7..f11f77526b 100644 --- a/arch/arm/src/rp2040/Kconfig +++ b/arch/arm/src/rp2040/Kconfig @@ -72,3 +72,27 @@ config RP2040_UART1_2STOP 0=1 stop bit, 1=Two stop bits. Default: 1 stop bit endif + +config RP2040_I2C + bool "I2C" + select I2C + +if RP2040_I2C + +config RP2040_I2C0 + bool "I2C0" + +config RP2040_I2C1 + bool "I2C1" + +config RP2040_I2C_DRIVER + bool "I2C character driver" + default n + select I2C_DRIVER + ---help--- + Build in support for a character driver at /dev/i2c[N] that may be + used to perform I2C bus transfers from applications. The intent of + this driver is to support I2C testing. It is not suitable for use + in any real driver application. + +endif diff --git a/arch/arm/src/rp2040/Make.defs b/arch/arm/src/rp2040/Make.defs index 330c3c2b29..f48a4c46bd 100644 --- a/arch/arm/src/rp2040/Make.defs +++ b/arch/arm/src/rp2040/Make.defs @@ -67,6 +67,10 @@ CHIP_CSRCS += rp2040_cpuidlestack.c CHIP_CSRCS += rp2040_testset.c endif +ifeq ($(CONFIG_RP2040_I2C),y) +CHIP_CSRCS += rp2040_i2c.c +endif + ifeq ($(CONFIG_RP2040_FLASH_BOOT),y) ifneq ($(PICO_SDK_PATH),) include chip/boot2/Make.defs diff --git a/arch/arm/src/rp2040/hardware/rp2040_i2c.h b/arch/arm/src/rp2040/hardware/rp2040_i2c.h new file mode 100644 index 0000000000..407f689f97 --- /dev/null +++ b/arch/arm/src/rp2040/hardware/rp2040_i2c.h @@ -0,0 +1,322 @@ +/**************************************************************************** + * arch/arm/src/rp2040/hardware/rp2040_i2c.h + * + * Generated from rp2040.svd originally provided by + * Raspberry Pi (Trading) Ltd. + * + * Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_RP2040_HARDWARE_RP2040_I2C_H +#define __ARCH_ARM_SRC_RP2040_HARDWARE_RP2040_I2C_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "hardware/rp2040_memorymap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register offsets *********************************************************/ + +#define RP2040_I2C_IC_CON_OFFSET 0x000000 /* I2C Control Register */ +#define RP2040_I2C_IC_TAR_OFFSET 0x000004 /* I2C Target Address Register */ +#define RP2040_I2C_IC_SAR_OFFSET 0x000008 /* I2C Slave Address Register */ +#define RP2040_I2C_IC_DATA_CMD_OFFSET 0x000010 /* I2C Rx/Tx Data Buffer and Command Register */ +#define RP2040_I2C_IC_SS_SCL_HCNT_OFFSET 0x000014 /* Standard Speed I2C Clock SCL High Count Register */ +#define RP2040_I2C_IC_SS_SCL_LCNT_OFFSET 0x000018 /* Standard Speed I2C Clock SCL Low Count Register */ +#define RP2040_I2C_IC_FS_SCL_HCNT_OFFSET 0x00001c /* Fast Mode or Fast Mode Plus I2C Clock SCL High Count Register */ +#define RP2040_I2C_IC_FS_SCL_LCNT_OFFSET 0x000020 /* Fast Mode or Fast Mode Plus I2C Clock SCL Low Count Register */ +#define RP2040_I2C_IC_INTR_STAT_OFFSET 0x00002c /* I2C Interrupt Status Register */ +#define RP2040_I2C_IC_INTR_MASK_OFFSET 0x000030 /* I2C Interrupt Mask Register */ +#define RP2040_I2C_IC_RAW_INTR_STAT_OFFSET 0x000034 /* I2C Raw Interrupt Status Register */ +#define RP2040_I2C_IC_RX_TL_OFFSET 0x000038 /* I2C Receive FIFO Threshold Register */ +#define RP2040_I2C_IC_TX_TL_OFFSET 0x00003c /* I2C Transmit FIFO Threshold Register */ +#define RP2040_I2C_IC_CLR_INTR_OFFSET 0x000040 /* Clear Combined and Individual Interrupt Register */ +#define RP2040_I2C_IC_CLR_RX_UNDER_OFFSET 0x000044 /* Clear RX_UNDER Interrupt Register */ +#define RP2040_I2C_IC_CLR_RX_OVER_OFFSET 0x000048 /* Clear RX_OVER Interrupt Register */ +#define RP2040_I2C_IC_CLR_TX_OVER_OFFSET 0x00004c /* Clear TX_OVER Interrupt Register */ +#define RP2040_I2C_IC_CLR_RD_REQ_OFFSET 0x000050 /* Clear RD_REQ Interrupt Register */ +#define RP2040_I2C_IC_CLR_TX_ABRT_OFFSET 0x000054 /* Clear TX_ABRT Interrupt Register */ +#define RP2040_I2C_IC_CLR_RX_DONE_OFFSET 0x000058 /* Clear RX_DONE Interrupt Register */ +#define RP2040_I2C_IC_CLR_ACTIVITY_OFFSET 0x00005c /* Clear ACTIVITY Interrupt Register */ +#define RP2040_I2C_IC_CLR_STOP_DET_OFFSET 0x000060 /* Clear STOP_DET Interrupt Register */ +#define RP2040_I2C_IC_CLR_START_DET_OFFSET 0x000064 /* Clear START_DET Interrupt Register */ +#define RP2040_I2C_IC_CLR_GEN_CALL_OFFSET 0x000068 /* Clear GEN_CALL Interrupt Register */ +#define RP2040_I2C_IC_ENABLE_OFFSET 0x00006c /* I2C Enable Register */ +#define RP2040_I2C_IC_STATUS_OFFSET 0x000070 /* I2C Status Register */ +#define RP2040_I2C_IC_TXFLR_OFFSET 0x000074 /* I2C Transmit FIFO Level Register */ +#define RP2040_I2C_IC_RXFLR_OFFSET 0x000078 /* I2C Receive FIFO Level Register */ +#define RP2040_I2C_IC_SDA_HOLD_OFFSET 0x00007c /* I2C SDA Hold Time Length Register */ +#define RP2040_I2C_IC_TX_ABRT_SOURCE_OFFSET 0x000080 /* I2C Transmit Abort Source Register */ +#define RP2040_I2C_IC_SLV_DATA_NACK_ONLY_OFFSET 0x000084 /* Generate Slave Data NACK Register */ +#define RP2040_I2C_IC_DMA_CR_OFFSET 0x000088 /* DMA Control Register */ +#define RP2040_I2C_IC_DMA_TDLR_OFFSET 0x00008c /* DMA Transmit Data Level Register */ +#define RP2040_I2C_IC_DMA_RDLR_OFFSET 0x000090 /* I2C Receive Data Level Register */ +#define RP2040_I2C_IC_SDA_SETUP_OFFSET 0x000094 /* I2C SDA Setup Register */ +#define RP2040_I2C_IC_ACK_GENERAL_CALL_OFFSET 0x000098 /* I2C ACK General Call Register */ +#define RP2040_I2C_IC_ENABLE_STATUS_OFFSET 0x00009c /* I2C Enable Status Register */ +#define RP2040_I2C_IC_FS_SPKLEN_OFFSET 0x0000a0 /* I2C SS, FS or FM+ spike suppression limit */ +#define RP2040_I2C_IC_CLR_RESTART_DET_OFFSET 0x0000a8 /* Clear RESTART_DET Interrupt Register */ +#define RP2040_I2C_IC_COMP_PARAM_1_OFFSET 0x0000f4 /* Component Parameter Register 1 */ +#define RP2040_I2C_IC_COMP_VERSION_OFFSET 0x0000f8 /* I2C Component Version Register */ +#define RP2040_I2C_IC_COMP_TYPE_OFFSET 0x0000fc /* I2C Component Type Register */ + +/* Register definitions *****************************************************/ + +#define RP2040_I2C_IC_CON(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_CON_OFFSET) +#define RP2040_I2C_IC_TAR(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_TAR_OFFSET) +#define RP2040_I2C_IC_SAR(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_SAR_OFFSET) +#define RP2040_I2C_IC_DATA_CMD(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_DATA_CMD_OFFSET) +#define RP2040_I2C_IC_SS_SCL_HCNT(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_SS_SCL_HCNT_OFFSET) +#define RP2040_I2C_IC_SS_SCL_LCNT(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_SS_SCL_LCNT_OFFSET) +#define RP2040_I2C_IC_FS_SCL_HCNT(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_FS_SCL_HCNT_OFFSET) +#define RP2040_I2C_IC_FS_SCL_LCNT(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_FS_SCL_LCNT_OFFSET) +#define RP2040_I2C_IC_INTR_STAT(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_INTR_STAT_OFFSET) +#define RP2040_I2C_IC_INTR_MASK(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_INTR_MASK_OFFSET) +#define RP2040_I2C_IC_RAW_INTR_STAT(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_RAW_INTR_STAT_OFFSET) +#define RP2040_I2C_IC_RX_TL(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_RX_TL_OFFSET) +#define RP2040_I2C_IC_TX_TL(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_TX_TL_OFFSET) +#define RP2040_I2C_IC_CLR_INTR(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_CLR_INTR_OFFSET) +#define RP2040_I2C_IC_CLR_RX_UNDER(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_CLR_RX_UNDER_OFFSET) +#define RP2040_I2C_IC_CLR_RX_OVER(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_CLR_RX_OVER_OFFSET) +#define RP2040_I2C_IC_CLR_TX_OVER(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_CLR_TX_OVER_OFFSET) +#define RP2040_I2C_IC_CLR_RD_REQ(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_CLR_RD_REQ_OFFSET) +#define RP2040_I2C_IC_CLR_TX_ABRT(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_CLR_TX_ABRT_OFFSET) +#define RP2040_I2C_IC_CLR_RX_DONE(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_CLR_RX_DONE_OFFSET) +#define RP2040_I2C_IC_CLR_ACTIVITY(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_CLR_ACTIVITY_OFFSET) +#define RP2040_I2C_IC_CLR_STOP_DET(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_CLR_STOP_DET_OFFSET) +#define RP2040_I2C_IC_CLR_START_DET(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_CLR_START_DET_OFFSET) +#define RP2040_I2C_IC_CLR_GEN_CALL(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_CLR_GEN_CALL_OFFSET) +#define RP2040_I2C_IC_ENABLE(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_ENABLE_OFFSET) +#define RP2040_I2C_IC_STATUS(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_STATUS_OFFSET) +#define RP2040_I2C_IC_TXFLR(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_TXFLR_OFFSET) +#define RP2040_I2C_IC_RXFLR(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_RXFLR_OFFSET) +#define RP2040_I2C_IC_SDA_HOLD(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_SDA_HOLD_OFFSET) +#define RP2040_I2C_IC_TX_ABRT_SOURCE(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_TX_ABRT_SOURCE_OFFSET) +#define RP2040_I2C_IC_SLV_DATA_NACK_ONLY(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_SLV_DATA_NACK_ONLY_OFFSET) +#define RP2040_I2C_IC_DMA_CR(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_DMA_CR_OFFSET) +#define RP2040_I2C_IC_DMA_TDLR(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_DMA_TDLR_OFFSET) +#define RP2040_I2C_IC_DMA_RDLR(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_DMA_RDLR_OFFSET) +#define RP2040_I2C_IC_SDA_SETUP(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_SDA_SETUP_OFFSET) +#define RP2040_I2C_IC_ACK_GENERAL_CALL(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_ACK_GENERAL_CALL_OFFSET) +#define RP2040_I2C_IC_ENABLE_STATUS(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_ENABLE_STATUS_OFFSET) +#define RP2040_I2C_IC_FS_SPKLEN(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_FS_SPKLEN_OFFSET) +#define RP2040_I2C_IC_CLR_RESTART_DET(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_CLR_RESTART_DET_OFFSET) +#define RP2040_I2C_IC_COMP_PARAM_1(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_COMP_PARAM_1_OFFSET) +#define RP2040_I2C_IC_COMP_VERSION(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_COMP_VERSION_OFFSET) +#define RP2040_I2C_IC_COMP_TYPE(n) (RP2040_I2C_BASE(n) + RP2040_I2C_IC_COMP_TYPE_OFFSET) + +/* Register bit definitions *************************************************/ + +#define RP2040_I2C_IC_CON_STOP_DET_IF_MASTER_ACTIVE (1 << 10) /* Master issues the STOP_DET interrupt irrespective of whether master is active or not */ +#define RP2040_I2C_IC_CON_RX_FIFO_FULL_HLD_CTRL (1 << 9) /* Hold bus when RX_FIFO is full */ +#define RP2040_I2C_IC_CON_TX_EMPTY_CTRL (1 << 8) /* Controlled generation of TX_EMPTY interrupt */ +#define RP2040_I2C_IC_CON_STOP_DET_IFADDRESSED (1 << 7) /* slave issues STOP_DET intr only if addressed */ +#define RP2040_I2C_IC_CON_IC_SLAVE_DISABLE (1 << 6) /* Slave mode is disabled */ +#define RP2040_I2C_IC_CON_IC_RESTART_EN (1 << 5) /* Master restart enabled */ +#define RP2040_I2C_IC_CON_IC_10BITADDR_MASTER (1 << 4) /* Master 10Bit addressing mode */ +#define RP2040_I2C_IC_CON_IC_10BITADDR_SLAVE (1 << 3) /* Slave 10Bit addressing */ +#define RP2040_I2C_IC_CON_SPEED_SHIFT (1) /* These bits control at which speed the DW_apb_i2c operates */ +#define RP2040_I2C_IC_CON_SPEED_MASK (0x03 << RP2040_I2C_IC_CON_SPEED_SHIFT) +#define RP2040_I2C_IC_CON_SPEED_STANDARD (0x1 << RP2040_I2C_IC_CON_SPEED_SHIFT) +#define RP2040_I2C_IC_CON_SPEED_FAST (0x2 << RP2040_I2C_IC_CON_SPEED_SHIFT) +#define RP2040_I2C_IC_CON_SPEED_HIGH (0x3 << RP2040_I2C_IC_CON_SPEED_SHIFT) +#define RP2040_I2C_IC_CON_MASTER_MODE (1 << 0) /* Master mode is enabled */ + +#define RP2040_I2C_IC_TAR_SPECIAL (1 << 11) /* Enables programming of GENERAL_CALL or START_BYTE transmission */ +#define RP2040_I2C_IC_TAR_GC_OR_START (1 << 10) /* START byte transmission */ +#define RP2040_I2C_IC_TAR_MASK (0x3ff) /* This is the target address for any master transaction. */ + +#define RP2040_I2C_IC_SAR_MASK (0x3ff) /* The IC_SAR holds the slave address when the I2C is operating as a slave. */ + +#define RP2040_I2C_IC_DATA_CMD_FIRST_DATA_BYTE (1 << 11) /* Non sequential data byte received */ +#define RP2040_I2C_IC_DATA_CMD_RESTART (1 << 10) /* Issue RESTART before this command */ +#define RP2040_I2C_IC_DATA_CMD_STOP (1 << 9) /* Issue STOP after this command */ +#define RP2040_I2C_IC_DATA_CMD_CMD (1 << 8) /* Master Read Command */ +#define RP2040_I2C_IC_DATA_CMD_DAT_MASK (0xff) /* This register contains the data to be transmitted or received on the I2C bus. */ + +#define RP2040_I2C_IC_SS_SCL_HCNT_MASK (0xffff) /* This register must be set before any I2C bus transaction can take place to ensure proper I/O timing. */ + +#define RP2040_I2C_IC_SS_SCL_LCNT_MASK (0xffff) /* This register must be set before any I2C bus transaction can take place to ensure proper I/O timing. */ + +#define RP2040_I2C_IC_FS_SCL_HCNT_MASK (0xffff) /* This register must be set before any I2C bus transaction can take place to ensure proper I/O timing. */ + +#define RP2040_I2C_IC_FS_SCL_LCNT_MASK (0xffff) /* This register must be set before any I2C bus transaction can take place to ensure proper I/O timing. */ + +#define RP2040_I2C_IC_INTR_STAT_R_MASTER_ON_HOLD (1 << 13) /* R_MASTER_ON_HOLD interrupt is active */ +#define RP2040_I2C_IC_INTR_STAT_R_RESTART_DET (1 << 12) /* R_RESTART_DET interrupt is active */ +#define RP2040_I2C_IC_INTR_STAT_R_GEN_CALL (1 << 11) /* R_GEN_CALL interrupt is active */ +#define RP2040_I2C_IC_INTR_STAT_R_START_DET (1 << 10) /* R_START_DET interrupt is active */ +#define RP2040_I2C_IC_INTR_STAT_R_STOP_DET (1 << 9) /* R_STOP_DET interrupt is active */ +#define RP2040_I2C_IC_INTR_STAT_R_ACTIVITY (1 << 8) /* R_ACTIVITY interrupt is active */ +#define RP2040_I2C_IC_INTR_STAT_R_RX_DONE (1 << 7) /* R_RX_DONE interrupt is active */ +#define RP2040_I2C_IC_INTR_STAT_R_TX_ABRT (1 << 6) /* R_TX_ABRT interrupt is active */ +#define RP2040_I2C_IC_INTR_STAT_R_RD_REQ (1 << 5) /* R_RD_REQ interrupt is active */ +#define RP2040_I2C_IC_INTR_STAT_R_TX_EMPTY (1 << 4) /* R_TX_EMPTY interrupt is active */ +#define RP2040_I2C_IC_INTR_STAT_R_TX_OVER (1 << 3) /* R_TX_OVER interrupt is active */ +#define RP2040_I2C_IC_INTR_STAT_R_RX_FULL (1 << 2) /* R_RX_FULL interrupt is active */ +#define RP2040_I2C_IC_INTR_STAT_R_RX_OVER (1 << 1) /* R_RX_OVER interrupt is active */ +#define RP2040_I2C_IC_INTR_STAT_R_RX_UNDER (1 << 0) /* RX_UNDER interrupt is active */ + +#define RP2040_I2C_IC_INTR_MASK_M_MASTER_ON_HOLD_READ_ONLY (1 << 13) /* MASTER_ON_HOLD interrupt is unmasked */ +#define RP2040_I2C_IC_INTR_MASK_M_RESTART_DET (1 << 12) /* RESTART_DET interrupt is unmasked */ +#define RP2040_I2C_IC_INTR_MASK_M_GEN_CALL (1 << 11) /* GEN_CALL interrupt is unmasked */ +#define RP2040_I2C_IC_INTR_MASK_M_START_DET (1 << 10) /* START_DET interrupt is unmasked */ +#define RP2040_I2C_IC_INTR_MASK_M_STOP_DET (1 << 9) /* STOP_DET interrupt is unmasked */ +#define RP2040_I2C_IC_INTR_MASK_M_ACTIVITY (1 << 8) /* ACTIVITY interrupt is unmasked */ +#define RP2040_I2C_IC_INTR_MASK_M_RX_DONE (1 << 7) /* RX_DONE interrupt is unmasked */ +#define RP2040_I2C_IC_INTR_MASK_M_TX_ABRT (1 << 6) /* TX_ABORT interrupt is unmasked */ +#define RP2040_I2C_IC_INTR_MASK_M_RD_REQ (1 << 5) /* RD_REQ interrupt is unmasked */ +#define RP2040_I2C_IC_INTR_MASK_M_TX_EMPTY (1 << 4) /* TX_EMPTY interrupt is unmasked */ +#define RP2040_I2C_IC_INTR_MASK_M_TX_OVER (1 << 3) /* TX_OVER interrupt is unmasked */ +#define RP2040_I2C_IC_INTR_MASK_M_RX_FULL (1 << 2) /* RX_FULL interrupt is unmasked */ +#define RP2040_I2C_IC_INTR_MASK_M_RX_OVER (1 << 1) /* RX_OVER interrupt is unmasked */ +#define RP2040_I2C_IC_INTR_MASK_M_RX_UNDER (1 << 0) /* RX_UNDER interrupt is unmasked */ + +#define RP2040_I2C_IC_RAW_INTR_STAT_MASTER_ON_HOLD (1 << 13) /* MASTER_ON_HOLD interrupt is active */ +#define RP2040_I2C_IC_RAW_INTR_STAT_RESTART_DET (1 << 12) /* RESTART_DET interrupt is active */ +#define RP2040_I2C_IC_RAW_INTR_STAT_GEN_CALL (1 << 11) /* GEN_CALL interrupt is active */ +#define RP2040_I2C_IC_RAW_INTR_STAT_START_DET (1 << 10) /* START_DET interrupt is active */ +#define RP2040_I2C_IC_RAW_INTR_STAT_STOP_DET (1 << 9) /* STOP_DET interrupt is active */ +#define RP2040_I2C_IC_RAW_INTR_STAT_ACTIVITY (1 << 8) /* RAW_INTR_ACTIVITY interrupt is active */ +#define RP2040_I2C_IC_RAW_INTR_STAT_RX_DONE (1 << 7) /* RX_DONE interrupt is active */ +#define RP2040_I2C_IC_RAW_INTR_STAT_TX_ABRT (1 << 6) /* TX_ABRT interrupt is active */ +#define RP2040_I2C_IC_RAW_INTR_STAT_RD_REQ (1 << 5) /* RD_REQ interrupt is active */ +#define RP2040_I2C_IC_RAW_INTR_STAT_TX_EMPTY (1 << 4) /* TX_EMPTY interrupt is active */ +#define RP2040_I2C_IC_RAW_INTR_STAT_TX_OVER (1 << 3) /* TX_OVER interrupt is active */ +#define RP2040_I2C_IC_RAW_INTR_STAT_RX_FULL (1 << 2) /* RX_FULL interrupt is active */ +#define RP2040_I2C_IC_RAW_INTR_STAT_RX_OVER (1 << 1) /* RX_OVER interrupt is active */ +#define RP2040_I2C_IC_RAW_INTR_STAT_RX_UNDER (1 << 0) /* RX_UNDER interrupt is active */ + +#define RP2040_I2C_IC_RX_TL_RX_TL_MASK (0xff) /* Receive FIFO Threshold Level. */ + +#define RP2040_I2C_IC_TX_TL_TX_TL_MASK (0xff) /* Transmit FIFO Threshold Level. */ + +#define RP2040_I2C_IC_CLR_INTR_CLR_INTR (1 << 0) /* Read this register to clear the combined interrupt, all individual interrupts, and the IC_TX_ABRT_SOURCE register. */ + +#define RP2040_I2C_IC_CLR_RX_UNDER_CLR_RX_UNDER (1 << 0) /* Read this register to clear the RX_UNDER interrupt (bit 0) of the IC_RAW_INTR_STAT register. */ + +#define RP2040_I2C_IC_CLR_RX_OVER_CLR_RX_OVER (1 << 0) /* Read this register to clear the RX_OVER interrupt (bit 1) of the IC_RAW_INTR_STAT register. */ + +#define RP2040_I2C_IC_CLR_TX_OVER_CLR_TX_OVER (1 << 0) /* Read this register to clear the TX_OVER interrupt (bit 3) of the IC_RAW_INTR_STAT register. */ + +#define RP2040_I2C_IC_CLR_RD_REQ_CLR_RD_REQ (1 << 0) /* Read this register to clear the RD_REQ interrupt (bit 5) of the IC_RAW_INTR_STAT register. */ + +#define RP2040_I2C_IC_CLR_TX_ABRT_CLR_TX_ABRT (1 << 0) /* Read this register to clear the TX_ABRT interrupt (bit 6) of the IC_RAW_INTR_STAT register, and the IC_TX_ABRT_SOURCE register. */ + +#define RP2040_I2C_IC_CLR_RX_DONE_CLR_RX_DONE (1 << 0) /* Read this register to clear the RX_DONE interrupt (bit 7) of the IC_RAW_INTR_STAT register. */ + +#define RP2040_I2C_IC_CLR_ACTIVITY_CLR_ACTIVITY (1 << 0) /* Reading this register clears the ACTIVITY interrupt if the I2C is not active anymore. */ + +#define RP2040_I2C_IC_CLR_STOP_DET_CLR_STOP_DET (1 << 0) /* Read this register to clear the STOP_DET interrupt (bit 9) of the IC_RAW_INTR_STAT register. */ + +#define RP2040_I2C_IC_CLR_START_DET_CLR_START_DET (1 << 0) /* Read this register to clear the START_DET interrupt (bit 10) of the IC_RAW_INTR_STAT register. */ + +#define RP2040_I2C_IC_CLR_GEN_CALL_CLR_GEN_CALL (1 << 0) /* Read this register to clear the GEN_CALL interrupt (bit 11) of IC_RAW_INTR_STAT register. */ + +#define RP2040_I2C_IC_ENABLE_TX_CMD_BLOCK (1 << 2) /* Tx Command execution blocked */ +#define RP2040_I2C_IC_ENABLE_ABORT (1 << 1) /* ABORT operation in progress */ +#define RP2040_I2C_IC_ENABLE_ENABLE (1 << 0) /* I2C is enabled */ + +#define RP2040_I2C_IC_STATUS_SLV_ACTIVITY (1 << 6) /* Slave not idle */ +#define RP2040_I2C_IC_STATUS_MST_ACTIVITY (1 << 5) /* Master not idle */ +#define RP2040_I2C_IC_STATUS_RFF (1 << 4) /* Rx FIFO is full */ +#define RP2040_I2C_IC_STATUS_RFNE (1 << 3) /* Rx FIFO not empty */ +#define RP2040_I2C_IC_STATUS_TFE (1 << 2) /* Tx FIFO is empty */ +#define RP2040_I2C_IC_STATUS_TFNF (1 << 1) /* Tx FIFO not full */ +#define RP2040_I2C_IC_STATUS_ACTIVITY (1 << 0) /* I2C is active */ + +#define RP2040_I2C_IC_TXFLR_TXFLR_MASK (0x1f) /* Transmit FIFO Level. */ + +#define RP2040_I2C_IC_RXFLR_RXFLR_MASK (0x1f) /* Receive FIFO Level. */ + +#define RP2040_I2C_IC_SDA_HOLD_IC_SDA_RX_HOLD_SHIFT (16) /* Sets the required SDA hold time in units of ic_clk period, when DW_apb_i2c acts as a receiver. */ +#define RP2040_I2C_IC_SDA_HOLD_IC_SDA_RX_HOLD_MASK (0xff << RP2040_I2C_IC_SDA_HOLD_IC_SDA_RX_HOLD_SHIFT) +#define RP2040_I2C_IC_SDA_HOLD_IC_SDA_TX_HOLD_MASK (0xffff) /* Sets the required SDA hold time in units of ic_clk period, when DW_apb_i2c acts as a transmitter. */ + +#define RP2040_I2C_IC_TX_ABRT_SOURCE_TX_FLUSH_CNT_SHIFT (23) /* This field indicates the number of Tx FIFO Data Commands which are flushed due to TX_ABRT interrupt. */ +#define RP2040_I2C_IC_TX_ABRT_SOURCE_TX_FLUSH_CNT_MASK (0x1ff << RP2040_I2C_IC_TX_ABRT_SOURCE_TX_FLUSH_CNT_SHIFT) +#define RP2040_I2C_IC_TX_ABRT_SOURCE_ABRT_USER_ABRT (1 << 16) /* Transfer abort detected by master */ +#define RP2040_I2C_IC_TX_ABRT_SOURCE_ABRT_SLVRD_INTX (1 << 15) /* Slave trying to transmit to remote master in read mode */ +#define RP2040_I2C_IC_TX_ABRT_SOURCE_ABRT_SLV_ARBLOST (1 << 14) /* Slave lost arbitration to remote master */ +#define RP2040_I2C_IC_TX_ABRT_SOURCE_ABRT_SLVFLUSH_TXFIFO (1 << 13) /* Slave flushes existing data in TX-FIFO upon getting read command */ +#define RP2040_I2C_IC_TX_ABRT_SOURCE_ARB_LOST (1 << 12) /* Master or Slave-Transmitter lost arbitration */ +#define RP2040_I2C_IC_TX_ABRT_SOURCE_ABRT_MASTER_DIS (1 << 11) /* User initiating master operation when MASTER disabled */ +#define RP2040_I2C_IC_TX_ABRT_SOURCE_ABRT_10B_RD_NORSTRT (1 << 10) /* Master trying to read in 10Bit addressing mode when RESTART disabled */ +#define RP2040_I2C_IC_TX_ABRT_SOURCE_ABRT_SBYTE_NORSTRT (1 << 9) /* User trying to send START byte when RESTART disabled */ +#define RP2040_I2C_IC_TX_ABRT_SOURCE_ABRT_HS_NORSTRT (1 << 8) /* User trying to switch Master to HS mode when RESTART disabled */ +#define RP2040_I2C_IC_TX_ABRT_SOURCE_ABRT_SBYTE_ACKDET (1 << 7) /* ACK detected for START byte */ +#define RP2040_I2C_IC_TX_ABRT_SOURCE_ABRT_HS_ACKDET (1 << 6) /* HS Master code ACKed in HS Mode */ +#define RP2040_I2C_IC_TX_ABRT_SOURCE_ABRT_GCALL_READ (1 << 5) /* GCALL is followed by read from bus */ +#define RP2040_I2C_IC_TX_ABRT_SOURCE_ABRT_GCALL_NOACK (1 << 4) /* GCALL not ACKed by any slave */ +#define RP2040_I2C_IC_TX_ABRT_SOURCE_ABRT_TXDATA_NOACK (1 << 3) /* Transmitted data not ACKed by addressed slave */ +#define RP2040_I2C_IC_TX_ABRT_SOURCE_ABRT_10ADDR2_NOACK (1 << 2) /* Byte 2 of 10Bit Address not ACKed by any slave */ +#define RP2040_I2C_IC_TX_ABRT_SOURCE_ABRT_10ADDR1_NOACK (1 << 1) /* Byte 1 of 10Bit Address not ACKed by any slave */ +#define RP2040_I2C_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK (1 << 0) /* This abort is generated because of NOACK for 7-bit address */ + +#define RP2040_I2C_IC_SLV_DATA_NACK_ONLY_NACK (1 << 0) /* Slave receiver generates NACK upon data reception only */ + +#define RP2040_I2C_IC_DMA_CR_TDMAE (1 << 1) /* Transmit FIFO DMA channel enabled */ +#define RP2040_I2C_IC_DMA_CR_RDMAE (1 << 0) /* Receive FIFO DMA channel enabled */ + +#define RP2040_I2C_IC_DMA_TDLR_DMATDL_MASK (0x0f) /* Transmit Data Level. */ + +#define RP2040_I2C_IC_DMA_RDLR_DMARDL_MASK (0x0f) /* Receive Data Level. */ + +#define RP2040_I2C_IC_SDA_SETUP_SDA_SETUP_MASK (0xff) /* SDA Setup. */ + +#define RP2040_I2C_IC_ACK_GENERAL_CALL_ACK_GEN_CALL (1 << 0) /* Generate ACK for a General Call */ + +#define RP2040_I2C_IC_ENABLE_STATUS_SLV_RX_DATA_LOST (1 << 2) /* Slave RX Data is lost */ +#define RP2040_I2C_IC_ENABLE_STATUS_SLV_DISABLED_WHILE_BUSY (1 << 1) /* Slave is disabled when it is active */ +#define RP2040_I2C_IC_ENABLE_STATUS_IC_EN (1 << 0) /* I2C enabled */ + +#define RP2040_I2C_IC_FS_SPKLEN_MASK (0xff) /* This register must be set before any I2C bus transaction can take place to ensure stable operation. */ + +#define RP2040_I2C_IC_CLR_RESTART_DET_CLR_RESTART_DET (1 << 0) /* Read this register to clear the RESTART_DET interrupt (bit 12) of IC_RAW_INTR_STAT register. */ + +#define RP2040_I2C_IC_COMP_PARAM_1_TX_BUFFER_DEPTH_SHIFT (16) /* TX Buffer Depth = 16 */ +#define RP2040_I2C_IC_COMP_PARAM_1_TX_BUFFER_DEPTH_MASK (0xff << RP2040_I2C_IC_COMP_PARAM_1_TX_BUFFER_DEPTH_SHIFT) +#define RP2040_I2C_IC_COMP_PARAM_1_RX_BUFFER_DEPTH_SHIFT (8) /* RX Buffer Depth = 16 */ +#define RP2040_I2C_IC_COMP_PARAM_1_RX_BUFFER_DEPTH_MASK (0xff << RP2040_I2C_IC_COMP_PARAM_1_RX_BUFFER_DEPTH_SHIFT) +#define RP2040_I2C_IC_COMP_PARAM_1_ADD_ENCODED_PARAMS (1 << 7) /* Encoded parameters not visible */ +#define RP2040_I2C_IC_COMP_PARAM_1_HAS_DMA (1 << 6) /* DMA handshaking signals are enabled */ +#define RP2040_I2C_IC_COMP_PARAM_1_INTR_IO (1 << 5) /* COMBINED Interrupt outputs */ +#define RP2040_I2C_IC_COMP_PARAM_1_HC_COUNT_VALUES (1 << 4) /* Programmable count values for each mode. */ +#define RP2040_I2C_IC_COMP_PARAM_1_MAX_SPEED_MODE_SHIFT (2) /* MAX SPEED MODE = FAST MODE */ +#define RP2040_I2C_IC_COMP_PARAM_1_MAX_SPEED_MODE_MASK (0x03 << RP2040_I2C_IC_COMP_PARAM_1_MAX_SPEED_MODE_SHIFT) +#define RP2040_I2C_IC_COMP_PARAM_1_APB_DATA_WIDTH_MASK (0x03) /* APB data bus width is 32 bits */ + +#endif /* __ARCH_ARM_SRC_RP2040_HARDWARE_RP2040_I2C_H */ diff --git a/arch/arm/src/rp2040/hardware/rp2040_memorymap.h b/arch/arm/src/rp2040/hardware/rp2040_memorymap.h index 4156c524f2..6405f6021e 100644 --- a/arch/arm/src/rp2040/hardware/rp2040_memorymap.h +++ b/arch/arm/src/rp2040/hardware/rp2040_memorymap.h @@ -72,10 +72,13 @@ #define RP2040_BUSCTRL_BASE 0x40030000 /* Register block for busfabric control signals and performance counters */ #define RP2040_UART0_BASE 0x40034000 #define RP2040_UART1_BASE 0x40038000 +#define RP2040_UART_BASE(n) (0x40034000 + (n) * 0x4000) #define RP2040_SPI0_BASE 0x4003c000 #define RP2040_SPI1_BASE 0x40040000 +#define RP2040_SPI_BASE(n) (0x4003c000 + (n) * 0x4000) #define RP2040_I2C0_BASE 0x40044000 /* DW_apb_i2c address block */ #define RP2040_I2C1_BASE 0x40048000 /* DW_apb_i2c address block */ +#define RP2040_I2C_BASE(n) (0x40044000 + (n) * 0x4000) #define RP2040_ADC_BASE 0x4004c000 /* Control and data interface to SAR ADC */ #define RP2040_PWM_BASE 0x40050000 /* Simple PWM */ #define RP2040_TIMER_BASE 0x40054000 /* Controls time and alarms time is a 64 bit value indicating the time in usec since power-on timeh is the top 32 bits of time & timel is the bottom 32 bits to change time write to timelw before timehw to read time read from timelr before timehr An alarm is set by setting alarm_enable and writing to the corresponding alarm register When an alarm is pending, the corresponding alarm_running signal will be high An alarm can be cancelled before it has finished by clearing the alarm_enable When an alarm fires, the corresponding alarm_irq is set and alarm_running is cleared To clear the interrupt write a 1 to the corresponding alarm_irq */ @@ -88,6 +91,7 @@ #define RP2040_USBCTRL_REGS_BASE 0x50110000 /* USB FS/LS controller device registers */ #define RP2040_PIO0_BASE 0x50200000 /* Programmable IO block */ #define RP2040_PIO1_BASE 0x50300000 /* Programmable IO block */ +#define RP2040_PIO_BASE(n) (0x50200000 + (n) * 0x100000) #define RP2040_SIO_BASE 0xd0000000 /* Single-cycle IO block Provides core-local and inter-core hardware for the two processors, with single-cycle access. */ #define RP2040_PPB_BASE 0xe0000000 diff --git a/arch/arm/src/rp2040/rp2040_i2c.c b/arch/arm/src/rp2040/rp2040_i2c.c new file mode 100644 index 0000000000..3df00c4141 --- /dev/null +++ b/arch/arm/src/rp2040/rp2040_i2c.c @@ -0,0 +1,833 @@ +/**************************************************************************** + * arch/arm/src/rp2040/rp2040_i2c.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "chip.h" +#include "arm_arch.h" +#include "arm_internal.h" + +#include "rp2040_i2c.h" +#include "hardware/rp2040_i2c.h" + +#ifdef CONFIG_RP2040_I2C + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define I2C_TIMEOUT (20*1000/CONFIG_USEC_PER_TICK) /* 20 mS */ + +#define I2C_DEFAULT_FREQUENCY 400000 +#define I2C_FIFO_MAX_SIZE 32 + +#define I2C_INTR_ENABLE ((RP2040_I2C_IC_INTR_STAT_R_STOP_DET) | \ + (RP2040_I2C_IC_INTR_STAT_R_TX_ABRT) | \ + (RP2040_I2C_IC_INTR_STAT_R_TX_OVER) | \ + (RP2040_I2C_IC_INTR_STAT_R_RX_OVER) | \ + (RP2040_I2C_IC_INTR_STAT_R_RX_UNDER)) + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +struct rp2040_i2cdev_s +{ + struct i2c_master_s dev; /* Generic I2C device */ + unsigned int base; /* Base address of registers */ + uint16_t irqid; /* IRQ for this device */ + int8_t port; /* Port number */ + uint32_t base_freq; /* branch frequency */ + + sem_t mutex; /* Only one thread can access at a time */ + sem_t wait; /* Place to wait for transfer completion */ + struct wdog_s timeout; /* watchdog to timeout when bus hung */ + uint32_t frequency; /* Current I2C frequency */ + ssize_t reg_buff_offset; + ssize_t rw_size; + + struct i2c_msg_s *msgs; + + int error; /* Error status of each transfers */ + int refs; /* Reference count */ +}; + +#ifdef CONFIG_RP2040_I2C0 +static struct rp2040_i2cdev_s g_i2c0dev = +{ + .port = 0, + .base = RP2040_I2C0_BASE, + .irqid = RP2040_I2C0_IRQ, + .refs = 0, +}; +#endif +#ifdef CONFIG_RP2040_I2C1 +static struct rp2040_i2cdev_s g_i2c1dev = +{ + .port = 1, + .base = RP2040_I2C1_BASE, + .irqid = RP2040_I2C1_IRQ, + .refs = 0, +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static inline int i2c_takesem(FAR sem_t *sem); +static inline int i2c_givesem(FAR sem_t *sem); + +static inline uint32_t i2c_reg_read(struct rp2040_i2cdev_s *priv, + uint32_t offset); +static inline void i2c_reg_write(struct rp2040_i2cdev_s *priv, + uint32_t offset, + uint32_t val); +static inline void i2c_reg_rmw(struct rp2040_i2cdev_s *dev, + uint32_t offset, + uint32_t val, uint32_t mask); + +static int rp2040_i2c_disable(struct rp2040_i2cdev_s *priv); +static void rp2040_i2c_enable(struct rp2040_i2cdev_s *priv); + +static int rp2040_i2c_interrupt(int irq, FAR void *context, FAR void *arg); +static void rp2040_i2c_timeout(wdparm_t arg); +static void rp2040_i2c_setfrequency(struct rp2040_i2cdev_s *priv, + uint32_t frequency); +static int rp2040_i2c_transfer(FAR struct i2c_master_s *dev, + FAR struct i2c_msg_s *msgs, int count); +#ifdef CONFIG_I2C_RESET +static int rp2040_i2c_reset(FAR struct i2c_master_s *dev); +#endif + +/**************************************************************************** + * Name: i2c_takesem + ****************************************************************************/ + +static inline int i2c_takesem(FAR sem_t *sem) +{ + return nxsem_wait_uninterruptible(sem); +} + +/**************************************************************************** + * Name: i2c_givesem + ****************************************************************************/ + +static inline int i2c_givesem(FAR sem_t *sem) +{ + return nxsem_post(sem); +} + +/**************************************************************************** + * I2C device operations + ****************************************************************************/ + +struct i2c_ops_s rp2040_i2c_ops = +{ + .transfer = rp2040_i2c_transfer, +#ifdef CONFIG_I2C_RESET + .reset = rp2040_i2c_reset, +#endif +}; + +/**************************************************************************** + * Name: rp2040_i2c_setfrequency + * + * Description: + * Set the frequency for the next transfer + * + ****************************************************************************/ + +static void rp2040_i2c_setfrequency(struct rp2040_i2cdev_s *priv, + uint32_t frequency) +{ + int32_t lcnt; + int32_t hcnt; + uint64_t lcnt64; + uint64_t hcnt64; + uint64_t speed; + uint64_t t_low; + uint64_t t_high; + uint32_t base = BOARD_PERI_FREQ; + uint32_t spklen; + + ASSERT(base); + + if ((priv->frequency == frequency) && (priv->base_freq == base)) + { + return; + } + + priv->frequency = frequency; + priv->base_freq = base; + + base /= 1000; + + if (frequency <= 100000) + { + t_low = 4700000; + t_high = 4000000; + } + else if (frequency <= 400000) + { + t_low = 1300000; + t_high = 600000; + } + else + { + t_low = 500000; + t_high = 260000; + } + + if (frequency > 100000) + { + if (base < 20032) + { + spklen = 1; + } + else if (base < 40064) + { + spklen = 2; + } + else + { + spklen = 3; + } + } + else + { + spklen = 1; + } + + lcnt64 = (t_low + 6500ull / 20000ull) * base; + lcnt = ((lcnt64 + 999999999ull) / 1000000000ull) - 1; /* ceil */ + lcnt = lcnt < 8 ? 8 : lcnt; + + hcnt64 = (t_high - 6500ull) * base; + hcnt = ((hcnt64 + 999999999ull) / 1000000000ull) - 6 - spklen; /* ceil */ + hcnt = hcnt < 6 ? 6 : hcnt; + + speed = + 1000000000000000000ull / + (((lcnt + 1) * 1000000000000ull + + (hcnt + 6 + spklen) * 1000000000000ull) / base + + 20000ull / 1000ull * 1000000ull); + + if (speed > (frequency * 1000ull)) + { + uint64_t adj; + adj = ((1000000000000000000ull / (frequency * 1000ull)) - + (1000000000000000000ull / speed)) * + base; + hcnt += (adj + 999999999999ull) / 1000000000000ull; + } + + /* use FS register in SS and FS mode */ + + i2c_reg_write(priv, RP2040_I2C_IC_FS_SCL_HCNT_OFFSET, hcnt); + i2c_reg_write(priv, RP2040_I2C_IC_FS_SCL_LCNT_OFFSET, lcnt); + i2c_reg_rmw(priv, RP2040_I2C_IC_CON_OFFSET, + RP2040_I2C_IC_CON_SPEED_FAST, + RP2040_I2C_IC_CON_SPEED_MASK); + + i2c_reg_write(priv, RP2040_I2C_IC_FS_SPKLEN_OFFSET, spklen); +} + +/**************************************************************************** + * Name: rp2040_i2c_timeout + * + * Description: + * Watchdog timer for timeout of I2C operation + * + ****************************************************************************/ + +static void rp2040_i2c_timeout(wdparm_t arg) +{ + struct rp2040_i2cdev_s *priv = (struct rp2040_i2cdev_s *)arg; + irqstate_t flags = enter_critical_section(); + + priv->error = -ENODEV; + i2c_givesem(&priv->wait); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: rp2040_i2c_drainrxfifo + * + * Description: + * Receive I2C data + * + ****************************************************************************/ + +static void rp2040_i2c_drainrxfifo(struct rp2040_i2cdev_s *priv) +{ + struct i2c_msg_s *msg = priv->msgs; + uint32_t status; + uint32_t dat; + ssize_t i; + + DEBUGASSERT(msg != NULL); + + status = i2c_reg_read(priv, RP2040_I2C_IC_STATUS_OFFSET); + + for (i = 0; i < priv->rw_size && status & RP2040_I2C_IC_STATUS_RFNE; i++) + { + dat = i2c_reg_read(priv, RP2040_I2C_IC_DATA_CMD_OFFSET); + msg->buffer[priv->reg_buff_offset + i] = dat & 0xff; + status = i2c_reg_read(priv, RP2040_I2C_IC_STATUS_OFFSET); + } + + priv->reg_buff_offset += priv->rw_size; +} + +/**************************************************************************** + * Name: rp2040_i2c_interrupt + * + * Description: + * The I2C Interrupt Handler + * + ****************************************************************************/ + +static int rp2040_i2c_interrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct rp2040_i2cdev_s *priv = (FAR struct rp2040_i2cdev_s *)arg; + uint32_t state; + int ret; + + state = i2c_reg_read(priv, RP2040_I2C_IC_INTR_STAT_OFFSET); + + if (state & RP2040_I2C_IC_INTR_STAT_R_TX_ABRT) + { + i2c_reg_read(priv, RP2040_I2C_IC_CLR_TX_ABRT_OFFSET); + priv->error = -ENODEV; + } + + if (state & RP2040_I2C_IC_INTR_STAT_R_TX_OVER) + { + i2c_reg_read(priv, RP2040_I2C_IC_CLR_TX_OVER_OFFSET); + priv->error = -EIO; + } + + if (state & RP2040_I2C_IC_INTR_STAT_R_RX_OVER) + { + i2c_reg_read(priv, RP2040_I2C_IC_CLR_RX_OVER_OFFSET); + priv->error = -EIO; + } + + if (state & RP2040_I2C_IC_INTR_STAT_R_RX_UNDER) + { + i2c_reg_read(priv, RP2040_I2C_IC_CLR_RX_UNDER_OFFSET); + priv->error = -EIO; + } + + if (state & RP2040_I2C_IC_INTR_STAT_R_TX_EMPTY) + { + /* TX_EMPTY is automatically cleared by hardware + * when the buffer level goes above the threshold. + */ + + i2c_reg_rmw(priv, RP2040_I2C_IC_INTR_MASK_OFFSET, + 0, RP2040_I2C_IC_INTR_MASK_M_TX_EMPTY); + } + + if (state & RP2040_I2C_IC_INTR_STAT_R_RX_FULL) + { + /* RX_FULL is automatically cleared by hardware + * when the buffer level goes below the threshold. + */ + + i2c_reg_rmw(priv, RP2040_I2C_IC_INTR_MASK_OFFSET, + 0, RP2040_I2C_IC_INTR_MASK_M_RX_FULL); + rp2040_i2c_drainrxfifo(priv); + } + + if (state & RP2040_I2C_IC_INTR_STAT_R_STOP_DET) + { + i2c_reg_read(priv, RP2040_I2C_IC_CLR_STOP_DET_OFFSET); + } + + if ((priv->error) || (state & RP2040_I2C_IC_INTR_STAT_R_TX_EMPTY) || + (state & RP2040_I2C_IC_INTR_STAT_R_RX_FULL)) + { + /* Failure of wd_cancel() means that the timer expired. + * In this case, nxsem_post() has already been called. + * Therefore, call nxsem_post() only when wd_cancel() succeeds. + */ + + ret = wd_cancel(&priv->timeout); + if (ret == OK) + { + i2c_givesem(&priv->wait); + } + } + + return OK; +} + +/**************************************************************************** + * Name: rp2040_i2c_receive + * + * Description: + * Receive data from I2C bus. + * Prohibit all interrupt because the STOP condition might happen + * if the interrupt occurs when the writing request. + * Actual receiving data is in RX_FULL interrupt handler. + * + * TODO : The argument "last" is not used. + ****************************************************************************/ + +static int rp2040_i2c_receive(struct rp2040_i2cdev_s *priv, int last) +{ + struct i2c_msg_s *msg = priv->msgs; + int i; + int en; + ssize_t msg_length; + irqstate_t flags; + + priv->reg_buff_offset = 0; + + DEBUGASSERT(msg != NULL); + + for (msg_length = msg->length; msg_length > 0; msg_length -= priv->rw_size) + { + if (msg_length <= I2C_FIFO_MAX_SIZE) + { + priv->rw_size = msg_length; + en = 1; + } + else + { + priv->rw_size = I2C_FIFO_MAX_SIZE; + en = 0; + } + + /* update threshold value of the receive buffer */ + + i2c_reg_write(priv, RP2040_I2C_IC_RX_TL_OFFSET, priv->rw_size - 1); + + for (i = 0; i < priv->rw_size - 1; i++) + { + i2c_reg_write(priv, RP2040_I2C_IC_DATA_CMD_OFFSET, + RP2040_I2C_IC_DATA_CMD_CMD); + } + + flags = enter_critical_section(); + wd_start(&priv->timeout, I2C_TIMEOUT, + rp2040_i2c_timeout, (wdparm_t)priv); + + /* Set stop flag for indicate the last data */ + + i2c_reg_write(priv, RP2040_I2C_IC_DATA_CMD_OFFSET, + RP2040_I2C_IC_DATA_CMD_CMD | + (en ? RP2040_I2C_IC_DATA_CMD_STOP : 0)); + + i2c_reg_rmw(priv, RP2040_I2C_IC_INTR_MASK_OFFSET, + RP2040_I2C_IC_INTR_STAT_R_RX_FULL, + RP2040_I2C_IC_INTR_STAT_R_RX_FULL); + leave_critical_section(flags); + i2c_takesem(&priv->wait); + + if (priv->error != OK) + { + break; + } + } + + return 0; +} + +/**************************************************************************** + * Name: rp2040_i2c_send + * + * Description: + * Send data to I2C bus. + * + ****************************************************************************/ + +static int rp2040_i2c_send(struct rp2040_i2cdev_s *priv, int last) +{ + struct i2c_msg_s *msg = priv->msgs; + ssize_t i; + irqstate_t flags; + + DEBUGASSERT(msg != NULL); + + for (i = 0; i < msg->length - 1; i++) + { + while (!(i2c_reg_read(priv, RP2040_I2C_IC_STATUS_OFFSET) + & RP2040_I2C_IC_STATUS_TFNF)) + ; + + i2c_reg_write(priv, RP2040_I2C_IC_DATA_CMD_OFFSET, + (uint32_t)msg->buffer[i]); + } + + while (!(i2c_reg_read(priv, RP2040_I2C_IC_STATUS_OFFSET) + & RP2040_I2C_IC_STATUS_TFNF)) + ; + + flags = enter_critical_section(); + wd_start(&priv->timeout, I2C_TIMEOUT, + rp2040_i2c_timeout, (wdparm_t)priv); + i2c_reg_write(priv, RP2040_I2C_IC_DATA_CMD_OFFSET, + (uint32_t)msg->buffer[i] | + (last ? RP2040_I2C_IC_DATA_CMD_STOP : 0)); + + /* Enable TX_EMPTY interrupt for determine transfer done. */ + + i2c_reg_rmw(priv, RP2040_I2C_IC_INTR_MASK_OFFSET, + RP2040_I2C_IC_INTR_STAT_R_TX_EMPTY, + RP2040_I2C_IC_INTR_STAT_R_TX_EMPTY); + leave_critical_section(flags); + + i2c_takesem(&priv->wait); + + return 0; +} + +/**************************************************************************** + * Name: rp2040_i2c_transfer + * + * Description: + * Perform a sequence of I2C transfers + * + * TODO: Multiple i2c_msg_s read operations with the same address are not + * currently guaranteed. + ****************************************************************************/ + +static int rp2040_i2c_transfer(FAR struct i2c_master_s *dev, + FAR struct i2c_msg_s *msgs, int count) +{ + struct rp2040_i2cdev_s *priv = (struct rp2040_i2cdev_s *)dev; + int i; + int ret = 0; + int semval = 0; + int addr = -1; + static int wostop = 0; + + DEBUGASSERT(dev != NULL); + + /* Get exclusive access to the I2C bus */ + + i2c_takesem(&priv->mutex); + + /* Check wait semaphore value. If the value is not 0, the transfer can not + * be performed normally. + */ + + ret = nxsem_get_value(&priv->wait, &semval); + DEBUGASSERT(ret == OK && semval == 0); + + for (i = 0; i < count; i++, msgs++) + { + /* Pass msg descriptor via device context */ + + priv->msgs = msgs; + priv->error = OK; + + if ((addr != msgs->addr) && !wostop) + { + rp2040_i2c_disable(priv); + + rp2040_i2c_setfrequency(priv, msgs->frequency); + + i2c_reg_rmw(priv, RP2040_I2C_IC_CON_OFFSET, + RP2040_I2C_IC_CON_IC_RESTART_EN, + RP2040_I2C_IC_CON_IC_RESTART_EN); + i2c_reg_write(priv, RP2040_I2C_IC_TAR_OFFSET, msgs->addr & 0x7f); + + rp2040_i2c_enable(priv); + addr = msgs->addr; + } + + if (msgs->flags & I2C_M_NOSTOP) + { + /* Don't send stop condition even if the last data */ + + wostop = 1; + } + else + { + wostop = 0; + } + + if (msgs->flags & I2C_M_READ) + { + ret = rp2040_i2c_receive(priv, (wostop) ? 0 : (i + 1 == count)); + } + else + { + ret = rp2040_i2c_send(priv, (wostop) ? 0 : (i + 1 == count)); + } + + if (ret < 0) + { + break; + } + + if (priv->error != OK) + { + ret = priv->error; + break; + } + + /* Clear msg descriptor for prevent illegal access in interrupt */ + + priv->msgs = NULL; + } + + if (!wostop) + { + rp2040_i2c_disable(priv); + } + + i2c_givesem(&priv->mutex); + + return ret; +} + +/**************************************************************************** + * Name: rp2040_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 rp2040_i2c_reset(FAR struct i2c_master_s *dev) +{ + return OK; +} +#endif /* CONFIG_I2C_RESET */ + +static inline uint32_t i2c_reg_read(struct rp2040_i2cdev_s *priv, + uint32_t offset) +{ + return getreg32(priv->base + offset); +} + +static inline void i2c_reg_write(struct rp2040_i2cdev_s *priv, + uint32_t offset, uint32_t val) +{ + putreg32(val, priv->base + offset); +} + +static inline void i2c_reg_rmw(struct rp2040_i2cdev_s *priv, uint32_t offset, + uint32_t val, uint32_t mask) +{ + modbits_reg32(val, mask, priv->base + offset); +} + +static int rp2040_i2c_disable(struct rp2040_i2cdev_s *priv) +{ + int retry = 25000; + uint32_t stat; + + /* disable all interrupt */ + + i2c_reg_write(priv, RP2040_I2C_IC_INTR_MASK_OFFSET, 0x0); + + /* clear all interrupt status */ + + i2c_reg_read(priv, RP2040_I2C_IC_CLR_INTR_OFFSET); + i2c_reg_write(priv, RP2040_I2C_IC_ENABLE_OFFSET, 0); + + do + { + stat = i2c_reg_read(priv, RP2040_I2C_IC_ENABLE_STATUS_OFFSET); + } + while (--retry && (stat & RP2040_I2C_IC_ENABLE_STATUS_IC_EN)); + + if (!retry) + { + i2cerr("i2c wait timeout.\n"); + return -EBUSY; + } + + /* clear all interrupt status again */ + + i2c_reg_read(priv, RP2040_I2C_IC_CLR_INTR_OFFSET); + + return 0; +} + +static void rp2040_i2c_enable(struct rp2040_i2cdev_s *priv) +{ + i2c_reg_write(priv, RP2040_I2C_IC_INTR_MASK_OFFSET, I2C_INTR_ENABLE); + i2c_reg_write(priv, RP2040_I2C_IC_ENABLE_OFFSET, 1); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: rp2040_i2cbus_initialize + * + * Description: + * Initialise an I2C device + * + ****************************************************************************/ + +struct i2c_master_s *rp2040_i2cbus_initialize(int port) +{ + struct rp2040_i2cdev_s *priv; + + irqstate_t flags; + + flags = enter_critical_section(); + +#ifdef CONFIG_RP2040_I2C0 + if (port == 0) + { + priv = &g_i2c0dev; + priv->dev.ops = &rp2040_i2c_ops; + } + else +#endif +#ifdef CONFIG_RP2040_I2C1 + if (port == 1) + { + priv = &g_i2c1dev; + priv->dev.ops = &rp2040_i2c_ops; + } + else +#endif + { + leave_critical_section(flags); + i2cerr("I2C Only support 0,1\n"); + return NULL; + } + + priv->refs++; + + /* Test if already initialized or not */ + + if (1 < priv->refs) + { + leave_critical_section(flags); + return &priv->dev; + } + + priv->port = port; + priv->frequency = 0; + + priv->base_freq = BOARD_PERI_FREQ; + + rp2040_i2c_disable(priv); + + i2c_reg_write(priv, RP2040_I2C_IC_INTR_MASK_OFFSET, 0x00); + i2c_reg_read(priv, RP2040_I2C_IC_CLR_INTR_OFFSET); + + /* set threshold level of the Rx/Tx FIFO */ + + i2c_reg_write(priv, RP2040_I2C_IC_RX_TL_OFFSET, 0xff); + i2c_reg_write(priv, RP2040_I2C_IC_TX_TL_OFFSET, 0); + + /* set hold time for margin */ + + i2c_reg_write(priv, RP2040_I2C_IC_SDA_HOLD_OFFSET, 1); + + i2c_reg_write(priv, RP2040_I2C_IC_CON_OFFSET, + (RP2040_I2C_IC_CON_IC_SLAVE_DISABLE | + RP2040_I2C_IC_CON_MASTER_MODE | + RP2040_I2C_IC_CON_TX_EMPTY_CTRL)); + rp2040_i2c_setfrequency(priv, I2C_DEFAULT_FREQUENCY); + + leave_critical_section(flags); + + nxsem_init(&priv->mutex, 0, 1); + nxsem_init(&priv->wait, 0, 0); + nxsem_set_protocol(&priv->wait, SEM_PRIO_NONE); + + /* Attach Interrupt Handler */ + + irq_attach(priv->irqid, rp2040_i2c_interrupt, priv); + + /* Enable Interrupt Handler */ + + up_enable_irq(priv->irqid); + + return &priv->dev; +} + +/**************************************************************************** + * Name: rp2040_i2cbus_uninitialize + * + * Description: + * Uninitialise an I2C device + * + ****************************************************************************/ + +int rp2040_i2cbus_uninitialize(FAR struct i2c_master_s *dev) +{ + struct rp2040_i2cdev_s *priv = (struct rp2040_i2cdev_s *)dev; + + /* Decrement reference count and check for underflow */ + + if (priv->refs == 0) + { + return ERROR; + } + + if (--priv->refs) + { + return OK; + } + + rp2040_i2c_disable(priv); + + up_disable_irq(priv->irqid); + irq_detach(priv->irqid); + + wd_cancel(&priv->timeout); + nxsem_destroy(&priv->mutex); + nxsem_destroy(&priv->wait); + + return OK; +} + +#endif /* CONFIG_RP2040_I2C */ diff --git a/arch/arm/src/rp2040/rp2040_i2c.h b/arch/arm/src/rp2040/rp2040_i2c.h new file mode 100644 index 0000000000..fc700efb3a --- /dev/null +++ b/arch/arm/src/rp2040/rp2040_i2c.h @@ -0,0 +1,87 @@ +/**************************************************************************** + * arch/arm/src/rp2040/rp2040_i2c.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_RP2040_RP2040_I2C_H +#define __ARCH_ARM_SRC_RP2040_RP2040_I2C_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include "hardware/rp2040_i2c.h" + +#ifndef __ASSEMBLY__ +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: rp2040_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 Parameter: + * Port number (for hardware that has multiple I2C interfaces) + * + * Returned Value: + * Valid I2C device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +FAR struct i2c_master_s *rp2040_i2cbus_initialize(int port); + +/**************************************************************************** + * Name: rp2040_i2cbus_uninitialize + * + * Description: + * De-initialize the selected I2C port, and power down the device. + * + * Input Parameter: + * Device structure as returned by the rp2040_i2cbus_initialize() + * + * Returned Value: + * OK on success, ERROR when internal reference count mismatch or dev + * points to invalid hardware device. + * + ****************************************************************************/ + +int rp2040_i2cbus_uninitialize(FAR struct i2c_master_s *dev); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_RP2040_RP2040_I2C_H */