arm/rp2040: Add RP2040 I2C device support

This commit is contained in:
Yuichi Nakamura 2021-03-02 22:50:57 +09:00 committed by Alan Carvalho de Assis
parent 60b18467f3
commit 9d0b3594f6
6 changed files with 1274 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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 <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <debug.h>
#include <assert.h>
#include <nuttx/arch.h>
#include <nuttx/i2c/i2c_master.h>
#include <nuttx/irq.h>
#include <arch/board/board.h>
#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 */

View File

@ -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 <nuttx/config.h>
#include <nuttx/i2c/i2c_master.h>
#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 */