This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit f500ec5020245bf67872354e0a3fdc32d4ada4c8 Author: leocafonso <[email protected]> AuthorDate: Tue Oct 13 21:41:16 2026 -0700 arch/arm/ra8m1: Add I2C support -- dedicated IIC and SCI_B Simple IIC mode Adds I2C master support through two independent peripheral paths, both exposed through the generic struct i2c_master_s interface: the dedicated I2C Bus Interface (IIC0 and IIC1, CONFIG_RA_IIC0/RA_IIC1) and any SCI_B channel's Simple IIC mode in place of its UART mode (CONFIG_RA_SCIn_I2C), the latter letting a board use more I2C buses than there are dedicated IIC channels and grouped per channel in Kconfig as a single UART/I2C mode choice so an existing config's setting migrates through olddefconfig unchanged. Both backends support optional DTC offload (CONFIG_RA_IICn_DTC/RA_SCIn_DTC) so a message's data bytes move through the DTC instead of one byte per interrupt once its address byte is acknowledged, with the last couple of read bytes staying CPU-driven for NACK timing. Tested on hardware: IIC1 via EK-RA8M1's Grove2 connector and SCI2 in Simple IIC mode, both with and without DTC offload, against a real I2C sensor through apps/system/i2c (i2ctool get/dump), single-byte and multi-byte repeated-start transfers. Co-Authored-By: Claude Sonnet 5 <[email protected]> Assisted-by: Claude:claude-sonnet-5 Signed-off-by: leocafonso <[email protected]> --- arch/arm/include/ra8m1/chip.h | 10 + arch/arm/include/ra8m1/ra8m1_irq.h | 24 +- arch/arm/src/ra8m1/CMakeLists.txt | 13 + arch/arm/src/ra8m1/Kconfig | 323 ++++++- arch/arm/src/ra8m1/Make.defs | 8 + arch/arm/src/ra8m1/hardware/ra8m1_iic.h | 271 ++++++ arch/arm/src/ra8m1/hardware/ra8m1_pinmap.h | 25 + arch/arm/src/ra8m1/ra_icu.c | 30 +- arch/arm/src/ra8m1/ra_iic_i2c.c | 1005 ++++++++++++++++++++++ arch/arm/src/ra8m1/ra_iic_i2c.h | 49 ++ arch/arm/src/ra8m1/ra_sci_i2c.c | 1267 ++++++++++++++++++++++++++++ arch/arm/src/ra8m1/ra_sci_i2c.h | 49 ++ 12 files changed, 3041 insertions(+), 33 deletions(-) diff --git a/arch/arm/include/ra8m1/chip.h b/arch/arm/include/ra8m1/chip.h index 8d84efb4a20..bd03a4de433 100644 --- a/arch/arm/include/ra8m1/chip.h +++ b/arch/arm/include/ra8m1/chip.h @@ -36,6 +36,7 @@ #if defined(CONFIG_ARCH_CHIP_R7FA8M1AFECAM) # define RA_NSCI_B 6 /* SCI_B0-4, SCI_B9 (not contiguous: no SCI_B5-8) */ # define RA_NGPT 8 /* GPT0-7 */ +# define RA_NIIC 2 /* IIC0-1 */ # define RA_RAM_SIZE (896*1024) /* 896Kb RAM */ # define RA_FLASH_SIZE (1024*1024) /* 1024Kb FLASH */ @@ -54,6 +55,7 @@ #elif defined(CONFIG_ARCH_CHIP_R7FA8M1AFECBD) # define RA_NSCI_B 6 /* SCI_B0-4, SCI_B9 (not contiguous: no SCI_B5-8) */ # define RA_NGPT 8 /* GPT0-7 */ +# define RA_NIIC 2 /* IIC0-1 */ # define RA_RAM_SIZE (896*1024) /* 896Kb RAM */ # define RA_FLASH_SIZE (1024*1024) /* 1024Kb FLASH */ @@ -72,6 +74,7 @@ #elif defined(CONFIG_ARCH_CHIP_R7FA8M1AFECFB) # define RA_NSCI_B 6 /* SCI_B0-4, SCI_B9 (not contiguous: no SCI_B5-8) */ # define RA_NGPT 8 /* GPT0-7 */ +# define RA_NIIC 2 /* IIC0-1 */ # define RA_RAM_SIZE (896*1024) /* 896Kb RAM */ # define RA_FLASH_SIZE (1024*1024) /* 1024Kb FLASH */ @@ -90,6 +93,7 @@ #elif defined(CONFIG_ARCH_CHIP_R7FA8M1AFECFC) # define RA_NSCI_B 6 /* SCI_B0-4, SCI_B9 (not contiguous: no SCI_B5-8) */ # define RA_NGPT 8 /* GPT0-7 */ +# define RA_NIIC 2 /* IIC0-1 */ # define RA_RAM_SIZE (896*1024) /* 896Kb RAM */ # define RA_FLASH_SIZE (1024*1024) /* 1024Kb FLASH */ @@ -108,6 +112,7 @@ #elif defined(CONFIG_ARCH_CHIP_R7FA8M1AFECFP) # define RA_NSCI_B 6 /* SCI_B0-4, SCI_B9 (not contiguous: no SCI_B5-8) */ # define RA_NGPT 8 /* GPT0-7 */ +# define RA_NIIC 2 /* IIC0-1 */ # define RA_RAM_SIZE (896*1024) /* 896Kb RAM */ # define RA_FLASH_SIZE (1024*1024) /* 1024Kb FLASH */ @@ -126,6 +131,7 @@ #elif defined(CONFIG_ARCH_CHIP_R7FA8M1AHECAM) # define RA_NSCI_B 6 /* SCI_B0-4, SCI_B9 (not contiguous: no SCI_B5-8) */ # define RA_NGPT 8 /* GPT0-7 */ +# define RA_NIIC 2 /* IIC0-1 */ # define RA_RAM_SIZE (896*1024) /* 896Kb RAM */ # define RA_FLASH_SIZE (2016*1024) /* 2016Kb FLASH */ @@ -144,6 +150,7 @@ #elif defined(CONFIG_ARCH_CHIP_R7FA8M1AHECBD) # define RA_NSCI_B 6 /* SCI_B0-4, SCI_B9 (not contiguous: no SCI_B5-8) */ # define RA_NGPT 8 /* GPT0-7 */ +# define RA_NIIC 2 /* IIC0-1 */ # define RA_RAM_SIZE (896*1024) /* 896Kb RAM */ # define RA_FLASH_SIZE (2016*1024) /* 2016Kb FLASH */ @@ -162,6 +169,7 @@ #elif defined(CONFIG_ARCH_CHIP_R7FA8M1AHECFB) # define RA_NSCI_B 6 /* SCI_B0-4, SCI_B9 (not contiguous: no SCI_B5-8) */ # define RA_NGPT 8 /* GPT0-7 */ +# define RA_NIIC 2 /* IIC0-1 */ # define RA_RAM_SIZE (896*1024) /* 896Kb RAM */ # define RA_FLASH_SIZE (2016*1024) /* 2016Kb FLASH */ @@ -180,6 +188,7 @@ #elif defined(CONFIG_ARCH_CHIP_R7FA8M1AHECFC) # define RA_NSCI_B 6 /* SCI_B0-4, SCI_B9 (not contiguous: no SCI_B5-8) */ # define RA_NGPT 8 /* GPT0-7 */ +# define RA_NIIC 2 /* IIC0-1 */ # define RA_RAM_SIZE (896*1024) /* 896Kb RAM */ # define RA_FLASH_SIZE (2016*1024) /* 2016Kb FLASH */ @@ -198,6 +207,7 @@ #elif defined(CONFIG_ARCH_CHIP_R7FA8M1AHECFP) # define RA_NSCI_B 6 /* SCI_B0-4, SCI_B9 (not contiguous: no SCI_B5-8) */ # define RA_NGPT 8 /* GPT0-7 */ +# define RA_NIIC 2 /* IIC0-1 */ # define RA_RAM_SIZE (896*1024) /* 896Kb RAM */ # define RA_FLASH_SIZE (2016*1024) /* 2016Kb FLASH */ diff --git a/arch/arm/include/ra8m1/ra8m1_irq.h b/arch/arm/include/ra8m1/ra8m1_irq.h index c680e68358f..dc6b0ca942d 100644 --- a/arch/arm/include/ra8m1/ra8m1_irq.h +++ b/arch/arm/include/ra8m1/ra8m1_irq.h @@ -170,42 +170,42 @@ enum ra_icu_event_irq_e { RA_IRQ_EVT_START = RA_IRQ_FIRST - 1, /* Not an IRQ: first event = FIRST */ -#ifdef CONFIG_RA_SCI0_UART +#if defined(CONFIG_RA_SCI0_UART) || defined(CONFIG_RA_SCI0_I2C) SCI0_RXI, /* Receive data full */ SCI0_TXI, /* Transmit data empty */ SCI0_TEI, /* Transmit end */ SCI0_ERI, /* Receive error */ #endif -#ifdef CONFIG_RA_SCI1_UART +#if defined(CONFIG_RA_SCI1_UART) || defined(CONFIG_RA_SCI1_I2C) SCI1_RXI, /* Receive data full */ SCI1_TXI, /* Transmit data empty */ SCI1_TEI, /* Transmit end */ SCI1_ERI, /* Receive error */ #endif -#ifdef CONFIG_RA_SCI2_UART +#if defined(CONFIG_RA_SCI2_UART) || defined(CONFIG_RA_SCI2_I2C) SCI2_RXI, /* Receive data full */ SCI2_TXI, /* Transmit data empty */ SCI2_TEI, /* Transmit end */ SCI2_ERI, /* Receive error */ #endif -#ifdef CONFIG_RA_SCI3_UART +#if defined(CONFIG_RA_SCI3_UART) || defined(CONFIG_RA_SCI3_I2C) SCI3_RXI, /* Receive data full */ SCI3_TXI, /* Transmit data empty */ SCI3_TEI, /* Transmit end */ SCI3_ERI, /* Receive error */ #endif -#ifdef CONFIG_RA_SCI4_UART +#if defined(CONFIG_RA_SCI4_UART) || defined(CONFIG_RA_SCI4_I2C) SCI4_RXI, /* Receive data full */ SCI4_TXI, /* Transmit data empty */ SCI4_TEI, /* Transmit end */ SCI4_ERI, /* Receive error */ #endif -#ifdef CONFIG_RA_SCI9_UART +#if defined(CONFIG_RA_SCI9_UART) || defined(CONFIG_RA_SCI9_I2C) SCI9_RXI, /* Receive data full */ SCI9_TXI, /* Transmit data empty */ SCI9_TEI, /* Transmit end */ @@ -256,6 +256,18 @@ enum ra_icu_event_irq_e #ifdef CONFIG_RA_GPT13_GPT GPT13_COUNTER_OVERFLOW, #endif +#ifdef CONFIG_RA_IIC0 + IIC0_ERI, /* Transfer error or event occurrence */ + IIC0_RXI, /* Receive data full */ + IIC0_TXI, /* Transmit data empty */ + IIC0_TEI, /* Transmit end */ +#endif +#ifdef CONFIG_RA_IIC1 + IIC1_ERI, /* Transfer error or event occurrence */ + IIC1_RXI, /* Receive data full */ + IIC1_TXI, /* Transmit data empty */ + IIC1_TEI, /* Transmit end */ +#endif RA_IRQ_EVT_END /* Not an IRQ: one past the last */ }; diff --git a/arch/arm/src/ra8m1/CMakeLists.txt b/arch/arm/src/ra8m1/CMakeLists.txt index 8f679252baa..ee0291c1e73 100644 --- a/arch/arm/src/ra8m1/CMakeLists.txt +++ b/arch/arm/src/ra8m1/CMakeLists.txt @@ -38,4 +38,17 @@ if(CONFIG_RA_DTC) list(APPEND SRCS ra_dtc.c) endif() +if(CONFIG_RA_IIC0 OR CONFIG_RA_IIC1) + list(APPEND SRCS ra_iic_i2c.c) +endif() + +if(CONFIG_RA_SCI0_I2C + OR CONFIG_RA_SCI1_I2C + OR CONFIG_RA_SCI2_I2C + OR CONFIG_RA_SCI3_I2C + OR CONFIG_RA_SCI4_I2C + OR CONFIG_RA_SCI9_I2C) + list(APPEND SRCS ra_sci_i2c.c) +endif() + target_sources(arch PRIVATE ${SRCS}) diff --git a/arch/arm/src/ra8m1/Kconfig b/arch/arm/src/ra8m1/Kconfig index 20fd2bf1345..a9677eb89b8 100644 --- a/arch/arm/src/ra8m1/Kconfig +++ b/arch/arm/src/ra8m1/Kconfig @@ -32,6 +32,8 @@ config ARCH_CHIP_R7FA8M1AFECAM select RA_HAVE_GPT11 select RA_HAVE_GPT12 select RA_HAVE_GPT13 + select RA_HAVE_IIC0 + select RA_HAVE_IIC1 select RA8M1_FAMILY config ARCH_CHIP_R7FA8M1AFECBD @@ -56,6 +58,8 @@ config ARCH_CHIP_R7FA8M1AFECBD select RA_HAVE_GPT11 select RA_HAVE_GPT12 select RA_HAVE_GPT13 + select RA_HAVE_IIC0 + select RA_HAVE_IIC1 select RA8M1_FAMILY config ARCH_CHIP_R7FA8M1AFECFB @@ -80,6 +84,8 @@ config ARCH_CHIP_R7FA8M1AFECFB select RA_HAVE_GPT11 select RA_HAVE_GPT12 select RA_HAVE_GPT13 + select RA_HAVE_IIC0 + select RA_HAVE_IIC1 select RA8M1_FAMILY config ARCH_CHIP_R7FA8M1AFECFC @@ -104,6 +110,8 @@ config ARCH_CHIP_R7FA8M1AFECFC select RA_HAVE_GPT11 select RA_HAVE_GPT12 select RA_HAVE_GPT13 + select RA_HAVE_IIC0 + select RA_HAVE_IIC1 select RA8M1_FAMILY config ARCH_CHIP_R7FA8M1AFECFP @@ -128,6 +136,8 @@ config ARCH_CHIP_R7FA8M1AFECFP select RA_HAVE_GPT11 select RA_HAVE_GPT12 select RA_HAVE_GPT13 + select RA_HAVE_IIC0 + select RA_HAVE_IIC1 select RA8M1_FAMILY config ARCH_CHIP_R7FA8M1AHECAM @@ -152,6 +162,8 @@ config ARCH_CHIP_R7FA8M1AHECAM select RA_HAVE_GPT11 select RA_HAVE_GPT12 select RA_HAVE_GPT13 + select RA_HAVE_IIC0 + select RA_HAVE_IIC1 select RA8M1_FAMILY config ARCH_CHIP_R7FA8M1AHECBD @@ -176,6 +188,8 @@ config ARCH_CHIP_R7FA8M1AHECBD select RA_HAVE_GPT11 select RA_HAVE_GPT12 select RA_HAVE_GPT13 + select RA_HAVE_IIC0 + select RA_HAVE_IIC1 select RA8M1_FAMILY config ARCH_CHIP_R7FA8M1AHECFB @@ -200,6 +214,8 @@ config ARCH_CHIP_R7FA8M1AHECFB select RA_HAVE_GPT11 select RA_HAVE_GPT12 select RA_HAVE_GPT13 + select RA_HAVE_IIC0 + select RA_HAVE_IIC1 select RA8M1_FAMILY config ARCH_CHIP_R7FA8M1AHECFC @@ -224,6 +240,8 @@ config ARCH_CHIP_R7FA8M1AHECFC select RA_HAVE_GPT11 select RA_HAVE_GPT12 select RA_HAVE_GPT13 + select RA_HAVE_IIC0 + select RA_HAVE_IIC1 select RA8M1_FAMILY config ARCH_CHIP_R7FA8M1AHECFP @@ -248,6 +266,8 @@ config ARCH_CHIP_R7FA8M1AHECFP select RA_HAVE_GPT11 select RA_HAVE_GPT12 select RA_HAVE_GPT13 + select RA_HAVE_IIC0 + select RA_HAVE_IIC1 select RA8M1_FAMILY endchoice # RA Chip Selection @@ -339,16 +359,61 @@ config RA_HAVE_GPT13 bool default n +config RA_HAVE_IIC0 + bool + default n + +config RA_HAVE_IIC1 + bool + default n + # TODO: user-facing enable options go here, one per # RA_HAVE_* above (prompt, depends on RA_HAVE_*, select # the driver it wires up -- see README "Kconfig draft"). -config RA_SCI0_UART - bool "UART 0" - default n +choice + prompt "SCI0 mode" depends on RA_HAVE_SCI_B0 + optional + +config RA_SCI0_UART + bool "UART" select SCI0_SERIALDRIVER +config RA_SCI0_I2C + bool "I2C bus interface (Simple IIC mode)" + select I2C + ---help--- + Uses SCI0's Simple IIC mode (RA8M1 User's Manual section + 31.8). Exposed as /dev/i2cN, with the device number assigned + by board bring-up (ra8m1_bringup.c), not fixed by this + driver. + +endchoice + +config RA_SCI0_DTC + bool "Transfer SCI0 I2C message data through the DTC" + default n + depends on RA_SCI0_I2C && RA_DTC + ---help--- + Once a message's address byte is acknowledged, hand its data + bytes to the DTC instead of moving them one at a time from + up_scii2c_txinterrupt(): one completion interrupt per + message instead of one per byte. + + A write's whole data phase goes through the DTC. A read's + does too, except its last two bytes: this mode's NACK timing + (ICR.IICACKT) needs software to touch it right before each + of those, which the DTC cannot do, so they stay CPU-driven + exactly as without this option. Unlike the dedicated IIC + peripheral's single DTC vector per direction, a read here + uses two at once: one on SCIn_RXI to move each received byte + out of RDR, one on SCIn_TXI to keep rewriting the dummy byte + that sustains SCL between received bytes (RA8M1 User's + Manual Table 31.46: this mode has no single "clock enable" + separate from TDR being written). Per that same table's note + 1, ACK/NACK cannot be confirmed for any byte the DTC moves. + config RA_SCI0_FIFO bool "Use SCI0's 16-stage FIFO" default n @@ -421,12 +486,49 @@ config RA_SCI0_RXDTC Mutually exclusive with CONFIG_RA_SCI0_FIFO (see its help). -config RA_SCI1_UART - bool "UART 1" - default n +choice + prompt "SCI1 mode" depends on RA_HAVE_SCI_B1 + optional + +config RA_SCI1_UART + bool "UART" select SCI1_SERIALDRIVER +config RA_SCI1_I2C + bool "I2C bus interface (Simple IIC mode)" + select I2C + ---help--- + Uses SCI1's Simple IIC mode (RA8M1 User's Manual section + 31.8). Exposed as /dev/i2cN, with the device number assigned + by board bring-up (ra8m1_bringup.c), not fixed by this + driver. + +endchoice + +config RA_SCI1_DTC + bool "Transfer SCI1 I2C message data through the DTC" + default n + depends on RA_SCI1_I2C && RA_DTC + ---help--- + Once a message's address byte is acknowledged, hand its data + bytes to the DTC instead of moving them one at a time from + up_scii2c_txinterrupt(): one completion interrupt per + message instead of one per byte. + + A write's whole data phase goes through the DTC. A read's + does too, except its last two bytes: this mode's NACK timing + (ICR.IICACKT) needs software to touch it right before each + of those, which the DTC cannot do, so they stay CPU-driven + exactly as without this option. Unlike the dedicated IIC + peripheral's single DTC vector per direction, a read here + uses two at once: one on SCIn_RXI to move each received byte + out of RDR, one on SCIn_TXI to keep rewriting the dummy byte + that sustains SCL between received bytes (RA8M1 User's + Manual Table 31.46: this mode has no single "clock enable" + separate from TDR being written). Per that same table's note + 1, ACK/NACK cannot be confirmed for any byte the DTC moves. + config RA_SCI1_FIFO bool "Use SCI1's 16-stage FIFO" default n @@ -499,12 +601,49 @@ config RA_SCI1_RXDTC Mutually exclusive with CONFIG_RA_SCI1_FIFO (see its help). -config RA_SCI2_UART - bool "UART 2" - default n +choice + prompt "SCI2 mode" depends on RA_HAVE_SCI_B2 + optional + +config RA_SCI2_UART + bool "UART" select SCI2_SERIALDRIVER +config RA_SCI2_I2C + bool "I2C bus interface (Simple IIC mode)" + select I2C + ---help--- + Uses SCI2's Simple IIC mode (RA8M1 User's Manual section + 31.8). Exposed as /dev/i2cN, with the device number assigned + by board bring-up (ra8m1_bringup.c), not fixed by this + driver. + +endchoice + +config RA_SCI2_DTC + bool "Transfer SCI2 I2C message data through the DTC" + default n + depends on RA_SCI2_I2C && RA_DTC + ---help--- + Once a message's address byte is acknowledged, hand its data + bytes to the DTC instead of moving them one at a time from + up_scii2c_txinterrupt(): one completion interrupt per + message instead of one per byte. + + A write's whole data phase goes through the DTC. A read's + does too, except its last two bytes: this mode's NACK timing + (ICR.IICACKT) needs software to touch it right before each + of those, which the DTC cannot do, so they stay CPU-driven + exactly as without this option. Unlike the dedicated IIC + peripheral's single DTC vector per direction, a read here + uses two at once: one on SCIn_RXI to move each received byte + out of RDR, one on SCIn_TXI to keep rewriting the dummy byte + that sustains SCL between received bytes (RA8M1 User's + Manual Table 31.46: this mode has no single "clock enable" + separate from TDR being written). Per that same table's note + 1, ACK/NACK cannot be confirmed for any byte the DTC moves. + config RA_SCI2_FIFO bool "Use SCI2's 16-stage FIFO" default n @@ -577,12 +716,49 @@ config RA_SCI2_RXDTC Mutually exclusive with CONFIG_RA_SCI2_FIFO (see its help). -config RA_SCI3_UART - bool "UART 3" - default n +choice + prompt "SCI3 mode" depends on RA_HAVE_SCI_B3 + optional + +config RA_SCI3_UART + bool "UART" select SCI3_SERIALDRIVER +config RA_SCI3_I2C + bool "I2C bus interface (Simple IIC mode)" + select I2C + ---help--- + Uses SCI3's Simple IIC mode (RA8M1 User's Manual section + 31.8). Exposed as /dev/i2cN, with the device number assigned + by board bring-up (ra8m1_bringup.c), not fixed by this + driver. + +endchoice + +config RA_SCI3_DTC + bool "Transfer SCI3 I2C message data through the DTC" + default n + depends on RA_SCI3_I2C && RA_DTC + ---help--- + Once a message's address byte is acknowledged, hand its data + bytes to the DTC instead of moving them one at a time from + up_scii2c_txinterrupt(): one completion interrupt per + message instead of one per byte. + + A write's whole data phase goes through the DTC. A read's + does too, except its last two bytes: this mode's NACK timing + (ICR.IICACKT) needs software to touch it right before each + of those, which the DTC cannot do, so they stay CPU-driven + exactly as without this option. Unlike the dedicated IIC + peripheral's single DTC vector per direction, a read here + uses two at once: one on SCIn_RXI to move each received byte + out of RDR, one on SCIn_TXI to keep rewriting the dummy byte + that sustains SCL between received bytes (RA8M1 User's + Manual Table 31.46: this mode has no single "clock enable" + separate from TDR being written). Per that same table's note + 1, ACK/NACK cannot be confirmed for any byte the DTC moves. + config RA_SCI3_FIFO bool "Use SCI3's 16-stage FIFO" default n @@ -655,12 +831,49 @@ config RA_SCI3_RXDTC Mutually exclusive with CONFIG_RA_SCI3_FIFO (see its help). -config RA_SCI4_UART - bool "UART 4" - default n +choice + prompt "SCI4 mode" depends on RA_HAVE_SCI_B4 + optional + +config RA_SCI4_UART + bool "UART" select SCI4_SERIALDRIVER +config RA_SCI4_I2C + bool "I2C bus interface (Simple IIC mode)" + select I2C + ---help--- + Uses SCI4's Simple IIC mode (RA8M1 User's Manual section + 31.8). Exposed as /dev/i2cN, with the device number assigned + by board bring-up (ra8m1_bringup.c), not fixed by this + driver. + +endchoice + +config RA_SCI4_DTC + bool "Transfer SCI4 I2C message data through the DTC" + default n + depends on RA_SCI4_I2C && RA_DTC + ---help--- + Once a message's address byte is acknowledged, hand its data + bytes to the DTC instead of moving them one at a time from + up_scii2c_txinterrupt(): one completion interrupt per + message instead of one per byte. + + A write's whole data phase goes through the DTC. A read's + does too, except its last two bytes: this mode's NACK timing + (ICR.IICACKT) needs software to touch it right before each + of those, which the DTC cannot do, so they stay CPU-driven + exactly as without this option. Unlike the dedicated IIC + peripheral's single DTC vector per direction, a read here + uses two at once: one on SCIn_RXI to move each received byte + out of RDR, one on SCIn_TXI to keep rewriting the dummy byte + that sustains SCL between received bytes (RA8M1 User's + Manual Table 31.46: this mode has no single "clock enable" + separate from TDR being written). Per that same table's note + 1, ACK/NACK cannot be confirmed for any byte the DTC moves. + config RA_SCI4_FIFO bool "Use SCI4's 16-stage FIFO" default n @@ -733,12 +946,49 @@ config RA_SCI4_RXDTC Mutually exclusive with CONFIG_RA_SCI4_FIFO (see its help). -config RA_SCI9_UART - bool "UART 9" - default n +choice + prompt "SCI9 mode" depends on RA_HAVE_SCI_B9 + optional + +config RA_SCI9_UART + bool "UART" select SCI9_SERIALDRIVER +config RA_SCI9_I2C + bool "I2C bus interface (Simple IIC mode)" + select I2C + ---help--- + Uses SCI9's Simple IIC mode (RA8M1 User's Manual section + 31.8). Exposed as /dev/i2cN, with the device number assigned + by board bring-up (ra8m1_bringup.c), not fixed by this + driver. + +endchoice + +config RA_SCI9_DTC + bool "Transfer SCI9 I2C message data through the DTC" + default n + depends on RA_SCI9_I2C && RA_DTC + ---help--- + Once a message's address byte is acknowledged, hand its data + bytes to the DTC instead of moving them one at a time from + up_scii2c_txinterrupt(): one completion interrupt per + message instead of one per byte. + + A write's whole data phase goes through the DTC. A read's + does too, except its last two bytes: this mode's NACK timing + (ICR.IICACKT) needs software to touch it right before each + of those, which the DTC cannot do, so they stay CPU-driven + exactly as without this option. Unlike the dedicated IIC + peripheral's single DTC vector per direction, a read here + uses two at once: one on SCIn_RXI to move each received byte + out of RDR, one on SCIn_TXI to keep rewriting the dummy byte + that sustains SCL between received bytes (RA8M1 User's + Manual Table 31.46: this mode has no single "clock enable" + separate from TDR being written). Per that same table's note + 1, ACK/NACK cannot be confirmed for any byte the DTC moves. + config RA_SCI_FIFO bool default n @@ -831,6 +1081,43 @@ config RA_DTC CPU normally. Peripheral drivers that support it expose their own per-channel options (e.g. RA_SCI9_TXDTC, RA_SCI9_RXDTC). +config RA_IIC0 + bool "I2C bus interface 0 (IIC0)" + default n + depends on RA_HAVE_IIC0 + select I2C + +config RA_IIC0_DTC + bool "Transfer IIC0 message data through the DTC" + default n + depends on RA_IIC0 && RA_DTC + ---help--- + Once a message's address byte is acknowledged, hand its data + bytes to the DTC instead of moving them one at a time from + up_iic_txinterrupt()/up_iic_rxinterrupt(): one completion + interrupt per message instead of one per byte. + + A write's whole data phase goes through the DTC. A read's + does too, except its last two bytes: RA8M1 User's Manual + section 32.3.4's WAIT/ACKBT NACK-timing needs software to + touch ICMR3 right before each of those, which the DTC cannot + do, so they stay CPU-driven exactly as without this option. + The benefit scales with message length -- negligible for a + 1-2 byte register read, real for a longer burst read. + +config RA_IIC1 + bool "I2C bus interface 1 (IIC1)" + default n + depends on RA_HAVE_IIC1 + select I2C + +config RA_IIC1_DTC + bool "Transfer IIC1 message data through the DTC" + default n + depends on RA_IIC1 && RA_DTC + ---help--- + See RA_IIC0_DTC. + config RA_GPT_TIMER bool "GPT timer (/dev/timerN)" default n diff --git a/arch/arm/src/ra8m1/Make.defs b/arch/arm/src/ra8m1/Make.defs index 26d3345e8d9..8af8746cdc5 100644 --- a/arch/arm/src/ra8m1/Make.defs +++ b/arch/arm/src/ra8m1/Make.defs @@ -44,3 +44,11 @@ endif ifeq ($(CONFIG_RA_DTC),y) CHIP_CSRCS += ra_dtc.c endif + +ifneq ($(filter y,$(CONFIG_RA_IIC0) $(CONFIG_RA_IIC1)),) +CHIP_CSRCS += ra_iic_i2c.c +endif + +ifneq ($(filter y,$(CONFIG_RA_SCI0_I2C) $(CONFIG_RA_SCI1_I2C) $(CONFIG_RA_SCI2_I2C) $(CONFIG_RA_SCI3_I2C) $(CONFIG_RA_SCI4_I2C) $(CONFIG_RA_SCI9_I2C)),) +CHIP_CSRCS += ra_sci_i2c.c +endif diff --git a/arch/arm/src/ra8m1/hardware/ra8m1_iic.h b/arch/arm/src/ra8m1/hardware/ra8m1_iic.h new file mode 100644 index 00000000000..0eb6177055a --- /dev/null +++ b/arch/arm/src/ra8m1/hardware/ra8m1_iic.h @@ -0,0 +1,271 @@ +/**************************************************************************** + * arch/arm/src/ra8m1/hardware/ra8m1_iic.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_RA8M1_HARDWARE_RA8M1_IIC_H +#define __ARCH_ARM_SRC_RA8M1_HARDWARE_RA8M1_IIC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include "chip.h" +#include "hardware/ra8m1_memorymap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register Offsets *********************************************************/ + +#define R_IIC_ICCR1_OFFSET 0x0000 /* I2C Bus Control Register 1 (8-bits) */ +#define R_IIC_ICCR2_OFFSET 0x0001 /* I2C Bus Control Register 2 (8-bits) */ +#define R_IIC_ICMR1_OFFSET 0x0002 /* I2C Bus Mode Register 1 (8-bits) */ +#define R_IIC_ICMR2_OFFSET 0x0003 /* I2C Bus Mode Register 2 (8-bits) */ +#define R_IIC_ICMR3_OFFSET 0x0004 /* I2C Bus Mode Register 3 (8-bits) */ +#define R_IIC_ICFER_OFFSET 0x0005 /* I2C Bus Function Enable Register (8-bits) */ +#define R_IIC_ICSER_OFFSET 0x0006 /* I2C Bus Status Enable Register (8-bits) */ +#define R_IIC_ICIER_OFFSET 0x0007 /* I2C Bus Interrupt Enable Register (8-bits) */ +#define R_IIC_ICSR1_OFFSET 0x0008 /* I2C Bus Status Register 1 (8-bits) */ +#define R_IIC_ICSR2_OFFSET 0x0009 /* I2C Bus Status Register 2 (8-bits) */ +#define R_IIC_SARL_OFFSET 0x000a /* Slave Address Register L%s (8-bits) */ +#define R_IIC_SARU_OFFSET 0x000b /* Slave Address Register U%s (8-bits) */ +#define R_IIC_ICBRL_OFFSET 0x0010 /* I2C Bus Bit Rate Low-Level Register (8-bits) */ +#define R_IIC_ICBRH_OFFSET 0x0011 /* I2C Bus Bit Rate High-Level Register (8-bits) */ +#define R_IIC_ICDRT_OFFSET 0x0012 /* I2C Bus Transmit Data Register (8-bits) */ +#define R_IIC_ICDRR_OFFSET 0x0013 /* I2C Bus Receive Data Register (8-bits) */ + +/* Register Addresses *******************************************************/ + +/* IIC0 Registers */ + +#define R_IIC0_ICCR1 (R_IIC0_BASE + R_IIC_ICCR1_OFFSET) +#define R_IIC0_ICCR2 (R_IIC0_BASE + R_IIC_ICCR2_OFFSET) +#define R_IIC0_ICMR1 (R_IIC0_BASE + R_IIC_ICMR1_OFFSET) +#define R_IIC0_ICMR2 (R_IIC0_BASE + R_IIC_ICMR2_OFFSET) +#define R_IIC0_ICMR3 (R_IIC0_BASE + R_IIC_ICMR3_OFFSET) +#define R_IIC0_ICFER (R_IIC0_BASE + R_IIC_ICFER_OFFSET) +#define R_IIC0_ICSER (R_IIC0_BASE + R_IIC_ICSER_OFFSET) +#define R_IIC0_ICIER (R_IIC0_BASE + R_IIC_ICIER_OFFSET) +#define R_IIC0_ICSR1 (R_IIC0_BASE + R_IIC_ICSR1_OFFSET) +#define R_IIC0_ICSR2 (R_IIC0_BASE + R_IIC_ICSR2_OFFSET) +#define R_IIC0_SARL(p) (R_IIC0_BASE + R_IIC_SARL_OFFSET + (p)*0x0002) +#define R_IIC0_SARU(p) (R_IIC0_BASE + R_IIC_SARU_OFFSET + (p)*0x0002) +#define R_IIC0_ICBRL (R_IIC0_BASE + R_IIC_ICBRL_OFFSET) +#define R_IIC0_ICBRH (R_IIC0_BASE + R_IIC_ICBRH_OFFSET) +#define R_IIC0_ICDRT (R_IIC0_BASE + R_IIC_ICDRT_OFFSET) +#define R_IIC0_ICDRR (R_IIC0_BASE + R_IIC_ICDRR_OFFSET) + +/* IIC1 Registers */ + +#define R_IIC1_ICCR1 (R_IIC1_BASE + R_IIC_ICCR1_OFFSET) +#define R_IIC1_ICCR2 (R_IIC1_BASE + R_IIC_ICCR2_OFFSET) +#define R_IIC1_ICMR1 (R_IIC1_BASE + R_IIC_ICMR1_OFFSET) +#define R_IIC1_ICMR2 (R_IIC1_BASE + R_IIC_ICMR2_OFFSET) +#define R_IIC1_ICMR3 (R_IIC1_BASE + R_IIC_ICMR3_OFFSET) +#define R_IIC1_ICFER (R_IIC1_BASE + R_IIC_ICFER_OFFSET) +#define R_IIC1_ICSER (R_IIC1_BASE + R_IIC_ICSER_OFFSET) +#define R_IIC1_ICIER (R_IIC1_BASE + R_IIC_ICIER_OFFSET) +#define R_IIC1_ICSR1 (R_IIC1_BASE + R_IIC_ICSR1_OFFSET) +#define R_IIC1_ICSR2 (R_IIC1_BASE + R_IIC_ICSR2_OFFSET) +#define R_IIC1_SARL(p) (R_IIC1_BASE + R_IIC_SARL_OFFSET + (p)*0x0002) +#define R_IIC1_SARU(p) (R_IIC1_BASE + R_IIC_SARU_OFFSET + (p)*0x0002) +#define R_IIC1_ICBRL (R_IIC1_BASE + R_IIC_ICBRL_OFFSET) +#define R_IIC1_ICBRH (R_IIC1_BASE + R_IIC_ICBRH_OFFSET) +#define R_IIC1_ICDRT (R_IIC1_BASE + R_IIC_ICDRT_OFFSET) +#define R_IIC1_ICDRR (R_IIC1_BASE + R_IIC_ICDRR_OFFSET) + +/* Register Bitfield Definitions ********************************************/ + +/* I2C Bus Control Register 1 (8-bits) **************************************/ + +#define R_IIC_ICCR1_SDAI (1 << 0) /* 01: SDA Line Monitor */ +#define R_IIC_ICCR1_SCLI (1 << 1) /* 02: SCL Line Monitor */ +#define R_IIC_ICCR1_SDAO (1 << 2) /* 04: SDA Output Control/Monitor */ +#define R_IIC_ICCR1_SCLO (1 << 3) /* 08: SCL Output Control/Monitor */ +#define R_IIC_ICCR1_SOWP (1 << 4) /* 10: SCLO/SDAO Write Protect */ +#define R_IIC_ICCR1_CLO (1 << 5) /* 20: Extra SCL Clock Cycle Output */ +#define R_IIC_ICCR1_IICRST (1 << 6) /* 40: I2C Bus Interface Internal ResetNote:If an internal reset is initiated using the IICRST bit for a bus hang-up occurred during communication with the master device in slave mode, the states may become different between the slave device and the master device (due to the difference in the bit counter information). */ +#define R_IIC_ICCR1_ICE (1 << 7) /* 80: I2C Bus Interface Enable */ + +/* I2C Bus Control Register 2 (8-bits) **************************************/ + +#define R_IIC_ICCR2_ST (1 << 1) /* 02: Start Condition Issuance RequestSet the ST bit to 1 (start condition issuance request) when the BBSY flag is set to 0 (bus free state). */ +#define R_IIC_ICCR2_RS (1 << 2) /* 04: Restart Condition Issuance RequestNote: Do not set the RS bit to 1 while issuing a stop condition. */ +#define R_IIC_ICCR2_SP (1 << 3) /* 08: Stop Condition Issuance RequestNote: Writing to the SP bit is not possible while the setting of the BBSY flag is 0 (bus free state).Note: Do not set the SP bit to 1 while a restart condition is being issued. */ +#define R_IIC_ICCR2_TRS (1 << 5) /* 20: Transmit/Receive Mode */ +#define R_IIC_ICCR2_MST (1 << 6) /* 40: Master/Slave Mode */ +#define R_IIC_ICCR2_BBSY (1 << 7) /* 80: Bus Busy Detection Flag */ + +/* I2C Bus Mode Register 1 (8-bits) *****************************************/ + +#define R_IIC_ICMR1_BC_SHIFT (0) +#define R_IIC_ICMR1_BC_MASK (0x7) +# define R_IIC_ICMR1_BC_V9_BITS (0 << R_IIC_ICMR1_BC_SHIFT) /* 9 bits */ +# define R_IIC_ICMR1_BC_V2_BITS (1 << R_IIC_ICMR1_BC_SHIFT) /* 2 bits */ +# define R_IIC_ICMR1_BC_V3_BITS (2 << R_IIC_ICMR1_BC_SHIFT) /* 3 bits */ +# define R_IIC_ICMR1_BC_V4_BITS (3 << R_IIC_ICMR1_BC_SHIFT) /* 4 bits */ +# define R_IIC_ICMR1_BC_V5_BITS (4 << R_IIC_ICMR1_BC_SHIFT) /* 5 bits */ +# define R_IIC_ICMR1_BC_V6_BITS (5 << R_IIC_ICMR1_BC_SHIFT) /* 6 bits */ +# define R_IIC_ICMR1_BC_V7_BITS (6 << R_IIC_ICMR1_BC_SHIFT) /* 7 bits */ +# define R_IIC_ICMR1_BC_V8_BITS (7 << R_IIC_ICMR1_BC_SHIFT) /* 8 bits */ +#define R_IIC_ICMR1_BCWP (1 << 3) /* 08: BC Write Protect(This bit is read as 1.) */ +#define R_IIC_ICMR1_CKS_SHIFT (4) +#define R_IIC_ICMR1_CKS_MASK (0x7) +# define R_IIC_ICMR1_CKS_PCLKB_1_CLOCK (0 << R_IIC_ICMR1_CKS_SHIFT) /* PCLKB/1 clock */ +# define R_IIC_ICMR1_CKS_PCLKB_2_CLOCK (1 << R_IIC_ICMR1_CKS_SHIFT) /* PCLKB/2 clock */ +# define R_IIC_ICMR1_CKS_PCLKB_4_CLOCK (2 << R_IIC_ICMR1_CKS_SHIFT) /* PCLKB/4 clock */ +# define R_IIC_ICMR1_CKS_PCLKB_8_CLOCK (3 << R_IIC_ICMR1_CKS_SHIFT) /* PCLKB/8 clock */ +# define R_IIC_ICMR1_CKS_PCLKB_16_CLOCK (4 << R_IIC_ICMR1_CKS_SHIFT) /* PCLKB/16 clock */ +# define R_IIC_ICMR1_CKS_PCLKB_32_CLOCK (5 << R_IIC_ICMR1_CKS_SHIFT) /* PCLKB/32 clock */ +# define R_IIC_ICMR1_CKS_PCLKB_64_CLOCK (6 << R_IIC_ICMR1_CKS_SHIFT) /* PCLKB/64 clock */ +# define R_IIC_ICMR1_CKS_PCLKB_128_CLOCK (7 << R_IIC_ICMR1_CKS_SHIFT) /* PCLKB/128 clock */ +#define R_IIC_ICMR1_MTWP (1 << 7) /* 80: MST/TRS Write Protect */ + +/* I2C Bus Mode Register 2 (8-bits) *****************************************/ + +#define R_IIC_ICMR2_TMOS (1 << 0) /* 01: Timeout Detection Time Selection */ +#define R_IIC_ICMR2_TMOL (1 << 1) /* 02: Timeout L Count Control */ +#define R_IIC_ICMR2_TMOH (1 << 2) /* 04: Timeout H Count Control */ +#define R_IIC_ICMR2_SDDL_SHIFT (4) +#define R_IIC_ICMR2_SDDL_MASK (0x7) +# define R_IIC_ICMR2_SDDL_NO_OUTPUT_DELAY (0 << R_IIC_ICMR2_SDDL_SHIFT) /* No output delay */ +# define R_IIC_ICMR2_SDDL_V001 (1 << R_IIC_ICMR2_SDDL_SHIFT) /* 1 fIIC cycle (ICMR2.DLCS=0) / 1 or 2 fIIC cycles (ICMR2.DLCS=1) */ +# define R_IIC_ICMR2_SDDL_V010 (2 << R_IIC_ICMR2_SDDL_SHIFT) /* 2 fIIC cycles (ICMR2.DLCS=0) / 3 or 4 fIIC cycles (ICMR2.DLCS=1) */ +# define R_IIC_ICMR2_SDDL_V011 (3 << R_IIC_ICMR2_SDDL_SHIFT) /* 3 fIIC cycles (ICMR2.DLCS=0) / 5 or 6 fIIC cycles (ICMR2.DLCS=1) */ +# define R_IIC_ICMR2_SDDL_V100 (4 << R_IIC_ICMR2_SDDL_SHIFT) /* 4 fIIC cycles (ICMR2.DLCS=0) / 7 or 8 fIIC cycles (ICMR2.DLCS=1) */ +# define R_IIC_ICMR2_SDDL_V101 (5 << R_IIC_ICMR2_SDDL_SHIFT) /* 5 fIIC cycles (ICMR2.DLCS=0) / 9 or 10 fIIC cycles (ICMR2.DLCS=1) */ +# define R_IIC_ICMR2_SDDL_V110 (6 << R_IIC_ICMR2_SDDL_SHIFT) /* 6 fIIC cycles (ICMR2.DLCS=0) / 11 or 12 fIIC cycles (ICMR2.DLCS=1) */ +# define R_IIC_ICMR2_SDDL_V111 (7 << R_IIC_ICMR2_SDDL_SHIFT) /* 7 fIIC cycles (ICMR2.DLCS=0) / 13 or 14 fIIC cycles (ICMR2.DLCS=1) */ +#define R_IIC_ICMR2_DLCS (1 << 7) /* 80: SDA Output Delay Clock Source Selection */ + +/* I2C Bus Mode Register 3 (8-bits) *****************************************/ + +#define R_IIC_ICMR3_NF_SHIFT (0) +#define R_IIC_ICMR3_NF_MASK (0x3) +# define R_IIC_ICMR3_NF_V00 (0 << R_IIC_ICMR3_NF_SHIFT) /* Noise of up to one fIIC cycle is filtered out (single-stage filter). */ +# define R_IIC_ICMR3_NF_V01 (1 << R_IIC_ICMR3_NF_SHIFT) /* Noise of up to two fIIC cycles is filtered out (2-stage filter). */ +# define R_IIC_ICMR3_NF_V10 (2 << R_IIC_ICMR3_NF_SHIFT) /* Noise of up to three fIIC cycles is filtered out (3-stage filter). */ +# define R_IIC_ICMR3_NF_V11 (3 << R_IIC_ICMR3_NF_SHIFT) /* Noise of up to four fIIC cycles is filtered out (4-stage filter) */ +#define R_IIC_ICMR3_ACKBR (1 << 2) /* 04: Receive Acknowledge */ +#define R_IIC_ICMR3_ACKBT (1 << 3) /* 08: Transmit Acknowledge */ +#define R_IIC_ICMR3_ACKWP (1 << 4) /* 10: ACKBT Write Protect */ +#define R_IIC_ICMR3_RDRFS (1 << 5) /* 20: RDRF Flag Set Timing Selection */ +#define R_IIC_ICMR3_WAIT (1 << 6) /* 40: WAITNote: When the value of the WAIT bit is to be read, be sure to read the ICDRR beforehand. */ +#define R_IIC_ICMR3_SMBS (1 << 7) /* 80: SMBus/I2C Bus Selection */ + +/* I2C Bus Function Enable Register (8-bits) ********************************/ + +#define R_IIC_ICFER_TMOE (1 << 0) /* 01: Timeout Function Enable */ +#define R_IIC_ICFER_MALE (1 << 1) /* 02: Master Arbitration-Lost Detection Enable */ +#define R_IIC_ICFER_NALE (1 << 2) /* 04: NACK Transmission Arbitration-Lost Detection Enable */ +#define R_IIC_ICFER_SALE (1 << 3) /* 08: Slave Arbitration-Lost Detection Enable */ +#define R_IIC_ICFER_NACKE (1 << 4) /* 10: NACK Reception Transfer Suspension Enable */ +#define R_IIC_ICFER_NFE (1 << 5) /* 20: Digital Noise Filter Circuit Enable */ +#define R_IIC_ICFER_SCLE (1 << 6) /* 40: SCL Synchronous Circuit Enable */ +#define R_IIC_ICFER_FMPE (1 << 7) /* 80: Fast-mode Plus Enable */ + +/* I2C Bus Status Enable Register (8-bits) **********************************/ + +#define R_IIC_ICSER_SAR0E (1 << 0) /* 01: Slave Address Register 0 Enable */ +#define R_IIC_ICSER_SAR1E (1 << 1) /* 02: Slave Address Register 1 Enable */ +#define R_IIC_ICSER_SAR2E (1 << 2) /* 04: Slave Address Register 2 Enable */ +#define R_IIC_ICSER_GCAE (1 << 3) /* 08: General Call Address Enable */ +#define R_IIC_ICSER_DIDE (1 << 5) /* 20: Device-ID Address Detection Enable */ +#define R_IIC_ICSER_HOAE (1 << 7) /* 80: Host Address Enable */ + +/* I2C Bus Interrupt Enable Register (8-bits) *******************************/ + +#define R_IIC_ICIER_TMOIE (1 << 0) /* 01: Timeout Interrupt Request Enable */ +#define R_IIC_ICIER_ALIE (1 << 1) /* 02: Arbitration-Lost Interrupt Request Enable */ +#define R_IIC_ICIER_STIE (1 << 2) /* 04: Start Condition Detection Interrupt Request Enable */ +#define R_IIC_ICIER_SPIE (1 << 3) /* 08: Stop Condition Detection Interrupt Request Enable */ +#define R_IIC_ICIER_NAKIE (1 << 4) /* 10: NACK Reception Interrupt Request Enable */ +#define R_IIC_ICIER_RIE (1 << 5) /* 20: Receive Data Full Interrupt Request Enable */ +#define R_IIC_ICIER_TEIE (1 << 6) /* 40: Transmit End Interrupt Request Enable */ +#define R_IIC_ICIER_TIE (1 << 7) /* 80: Transmit Data Empty Interrupt Request Enable */ + +/* I2C Bus Status Register 1 (8-bits) ***************************************/ + +#define R_IIC_ICSR1_AAS0 (1 << 0) /* 01: Slave Address 0 Detection Flag */ +#define R_IIC_ICSR1_AAS1 (1 << 1) /* 02: Slave Address 1 Detection Flag */ +#define R_IIC_ICSR1_AAS2 (1 << 2) /* 04: Slave Address 2 Detection Flag */ +#define R_IIC_ICSR1_GCA (1 << 3) /* 08: General Call Address Detection Flag */ +#define R_IIC_ICSR1_DID (1 << 5) /* 20: Device-ID Address Detection Flag */ +#define R_IIC_ICSR1_HOA (1 << 7) /* 80: Host Address Detection Flag */ + +/* I2C Bus Status Register 2 (8-bits) ***************************************/ + +#define R_IIC_ICSR2_TMOF (1 << 0) /* 01: Timeout Detection Flag */ +#define R_IIC_ICSR2_AL (1 << 1) /* 02: Arbitration-Lost Flag */ +#define R_IIC_ICSR2_START (1 << 2) /* 04: Start Condition Detection Flag */ +#define R_IIC_ICSR2_STOP (1 << 3) /* 08: Stop Condition Detection Flag */ +#define R_IIC_ICSR2_NACKF (1 << 4) /* 10: NACK Detection Flag */ +#define R_IIC_ICSR2_RDRF (1 << 5) /* 20: Receive Data Full Flag */ +#define R_IIC_ICSR2_TEND (1 << 6) /* 40: Transmit End Flag */ +#define R_IIC_ICSR2_TDRE (1 << 7) /* 80: Transmit Data Empty Flag */ + +/* Slave Address Register L%s (8-bits) **************************************/ + +#define R_IIC_SARL_SIZE 3 +#define R_IIC_SARL_SVA_SHIFT (0) +#define R_IIC_SARL_SVA_MASK (0xff) + +/* Slave Address Register U%s (8-bits) **************************************/ + +#define R_IIC_SARU_SIZE 3 +#define R_IIC_SARU_FS (1 << 0) /* 01: 7-Bit/10-Bit Address Format Selection */ +#define R_IIC_SARU_SVA8 (1 << 1) /* 02: 10-Bit Address(bit8) */ +#define R_IIC_SARU_SVA9 (1 << 2) /* 04: 10-Bit Address(bit9) */ + +/* I2C Bus Bit Rate Low-Level Register (8-bits) *****************************/ + +#define R_IIC_ICBRL_BRL_SHIFT (0) +#define R_IIC_ICBRL_BRL_MASK (0x1f) + +/* I2C Bus Bit Rate High-Level Register (8-bits) ****************************/ + +#define R_IIC_ICBRH_BRH_SHIFT (0) +#define R_IIC_ICBRH_BRH_MASK (0x1f) + +/* I2C Bus Transmit Data Register (8-bits) **********************************/ + +#define R_IIC_ICDRT_ICDRT_SHIFT (0) +#define R_IIC_ICDRT_ICDRT_MASK (0xff) + +/* I2C Bus Receive Data Register (8-bits) ***********************************/ + +#define R_IIC_ICDRR_ICDRR_SHIFT (0) +#define R_IIC_ICDRR_ICDRR_MASK (0xff) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Public Functions Prototypes + ****************************************************************************/ + +#endif /* __ARCH_ARM_SRC_RA8M1_HARDWARE_RA8M1_IIC_H */ diff --git a/arch/arm/src/ra8m1/hardware/ra8m1_pinmap.h b/arch/arm/src/ra8m1/hardware/ra8m1_pinmap.h index e60d01d9698..c41d011e73d 100644 --- a/arch/arm/src/ra8m1/hardware/ra8m1_pinmap.h +++ b/arch/arm/src/ra8m1/hardware/ra8m1_pinmap.h @@ -146,6 +146,7 @@ #define PFS_PSEL_SCI (0x04 << R_PFS_PSEL_SHIFT) #define PFS_PSEL_SCI1 (0x05 << R_PFS_PSEL_SHIFT) +#define PFS_PSEL_IIC (0x07 << R_PFS_PSEL_SHIFT) /* GPIO ports and pins. * @@ -246,6 +247,30 @@ #define GPIO_RXD9_MISO9_SCL9_3 (gpio_pinset_t){ PORTA,PIN15, (PFS_PSEL_SCI1 | R_PFS_PMR)} #define GPIO_TXD9_MOSI9_SDA9_3 (gpio_pinset_t){ PORTA,PIN14, (PFS_PSEL_SCI1 | R_PFS_PMR)} +/* IIC Alternative ********************************************************** + * + * RA8M1 has IIC0 and IIC1 (chapter 32). Each channel's SCL/SDA pair can be + * routed to one of several alternate pin groups, same "_A"/"_B"/"_C"-style + * suffix idea as SCI above, numbered to match the pin group ("Group A"/ + * "Group B"/...) each comes from. PSEL 0x07 ("IIC/I3C") is shared with the + * I3C peripheral -- which block a given pin actually reaches depends on the + * pin, not PSEL (the manual's own pin tables label I3C's pins + * "I3C_SCLn"/"I3C_SDAn" explicitly; a bare "SCLn"/"SDAn" entry is this IIC + * peripheral instead). + * + * Source: RA8M1 User's Manual chapter 19.6, Table 19.11. + */ + +#define GPIO_SDA0_1 (gpio_pinset_t){ PORT4,PIN9, (PFS_PSEL_IIC | R_PFS_PMR)} +#define GPIO_SCL0_1 (gpio_pinset_t){ PORT4,PIN10, (PFS_PSEL_IIC | R_PFS_PMR)} +#define GPIO_SDA0_2 (gpio_pinset_t){ PORT4,PIN7, (PFS_PSEL_IIC | R_PFS_PMR)} +#define GPIO_SCL0_2 (gpio_pinset_t){ PORT4,PIN8, (PFS_PSEL_IIC | R_PFS_PMR)} + +#define GPIO_SDA1_1 (gpio_pinset_t){ PORT5,PIN11, (PFS_PSEL_IIC | R_PFS_PMR)} +#define GPIO_SCL1_1 (gpio_pinset_t){ PORT5,PIN12, (PFS_PSEL_IIC | R_PFS_PMR)} +#define GPIO_SDA1_2 (gpio_pinset_t){ PORT2,PIN6, (PFS_PSEL_IIC | R_PFS_PMR)} +#define GPIO_SCL1_2 (gpio_pinset_t){ PORT2,PIN5, (PFS_PSEL_IIC | R_PFS_PMR)} + /* GPIO Configuration */ #define GPIO_OUTPUT R_PFS_PDR diff --git a/arch/arm/src/ra8m1/ra_icu.c b/arch/arm/src/ra8m1/ra_icu.c index 7c17d7bc728..97de7a303c0 100644 --- a/arch/arm/src/ra8m1/ra_icu.c +++ b/arch/arm/src/ra8m1/ra_icu.c @@ -48,50 +48,62 @@ static_assert(RA_IRQ_EVT_END <= NR_IRQS, * * Description: * Every ICU IELSR slot is runtime-configurable -- any peripheral event - * can be routed to any slot. This wires each enabled SCI_B channel's - * RXI/TXI/TEI/ERI events into the slots that irq.h assigned them - * (SCIn_RXI/TXI/TEI/ERI), so their vector numbers actually fire. + * can be routed to any slot. This wires each enabled SCI_B/IIC channel's + * events into the slots that irq.h assigned them (SCIn_RXI/TXI/TEI/ERI, + * IICn_RXI/TXI/TEI/ERI), so their vector numbers actually fire. * ****************************************************************************/ void ra_attach_icu(void) { -#ifdef CONFIG_RA_SCI0_UART +#if defined(CONFIG_RA_SCI0_UART) || defined(CONFIG_RA_SCI0_I2C) putreg32(EVENT_SCI0_RXI, R_ICU_IELSR(SCI0_RXI - RA_IRQ_FIRST)); putreg32(EVENT_SCI0_TXI, R_ICU_IELSR(SCI0_TXI - RA_IRQ_FIRST)); putreg32(EVENT_SCI0_TEI, R_ICU_IELSR(SCI0_TEI - RA_IRQ_FIRST)); putreg32(EVENT_SCI0_ERI, R_ICU_IELSR(SCI0_ERI - RA_IRQ_FIRST)); #endif -#ifdef CONFIG_RA_SCI1_UART +#if defined(CONFIG_RA_SCI1_UART) || defined(CONFIG_RA_SCI1_I2C) putreg32(EVENT_SCI1_RXI, R_ICU_IELSR(SCI1_RXI - RA_IRQ_FIRST)); putreg32(EVENT_SCI1_TXI, R_ICU_IELSR(SCI1_TXI - RA_IRQ_FIRST)); putreg32(EVENT_SCI1_TEI, R_ICU_IELSR(SCI1_TEI - RA_IRQ_FIRST)); putreg32(EVENT_SCI1_ERI, R_ICU_IELSR(SCI1_ERI - RA_IRQ_FIRST)); #endif -#ifdef CONFIG_RA_SCI2_UART +#if defined(CONFIG_RA_SCI2_UART) || defined(CONFIG_RA_SCI2_I2C) putreg32(EVENT_SCI2_RXI, R_ICU_IELSR(SCI2_RXI - RA_IRQ_FIRST)); putreg32(EVENT_SCI2_TXI, R_ICU_IELSR(SCI2_TXI - RA_IRQ_FIRST)); putreg32(EVENT_SCI2_TEI, R_ICU_IELSR(SCI2_TEI - RA_IRQ_FIRST)); putreg32(EVENT_SCI2_ERI, R_ICU_IELSR(SCI2_ERI - RA_IRQ_FIRST)); #endif -#ifdef CONFIG_RA_SCI3_UART +#if defined(CONFIG_RA_SCI3_UART) || defined(CONFIG_RA_SCI3_I2C) putreg32(EVENT_SCI3_RXI, R_ICU_IELSR(SCI3_RXI - RA_IRQ_FIRST)); putreg32(EVENT_SCI3_TXI, R_ICU_IELSR(SCI3_TXI - RA_IRQ_FIRST)); putreg32(EVENT_SCI3_TEI, R_ICU_IELSR(SCI3_TEI - RA_IRQ_FIRST)); putreg32(EVENT_SCI3_ERI, R_ICU_IELSR(SCI3_ERI - RA_IRQ_FIRST)); #endif -#ifdef CONFIG_RA_SCI4_UART +#if defined(CONFIG_RA_SCI4_UART) || defined(CONFIG_RA_SCI4_I2C) putreg32(EVENT_SCI4_RXI, R_ICU_IELSR(SCI4_RXI - RA_IRQ_FIRST)); putreg32(EVENT_SCI4_TXI, R_ICU_IELSR(SCI4_TXI - RA_IRQ_FIRST)); putreg32(EVENT_SCI4_TEI, R_ICU_IELSR(SCI4_TEI - RA_IRQ_FIRST)); putreg32(EVENT_SCI4_ERI, R_ICU_IELSR(SCI4_ERI - RA_IRQ_FIRST)); #endif -#ifdef CONFIG_RA_SCI9_UART +#if defined(CONFIG_RA_SCI9_UART) || defined(CONFIG_RA_SCI9_I2C) putreg32(EVENT_SCI9_RXI, R_ICU_IELSR(SCI9_RXI - RA_IRQ_FIRST)); putreg32(EVENT_SCI9_TXI, R_ICU_IELSR(SCI9_TXI - RA_IRQ_FIRST)); putreg32(EVENT_SCI9_TEI, R_ICU_IELSR(SCI9_TEI - RA_IRQ_FIRST)); putreg32(EVENT_SCI9_ERI, R_ICU_IELSR(SCI9_ERI - RA_IRQ_FIRST)); #endif +#ifdef CONFIG_RA_IIC0 + putreg32(EVENT_IIC0_RXI, R_ICU_IELSR(IIC0_RXI - RA_IRQ_FIRST)); + putreg32(EVENT_IIC0_TXI, R_ICU_IELSR(IIC0_TXI - RA_IRQ_FIRST)); + putreg32(EVENT_IIC0_TEI, R_ICU_IELSR(IIC0_TEI - RA_IRQ_FIRST)); + putreg32(EVENT_IIC0_ERI, R_ICU_IELSR(IIC0_ERI - RA_IRQ_FIRST)); +#endif +#ifdef CONFIG_RA_IIC1 + putreg32(EVENT_IIC1_RXI, R_ICU_IELSR(IIC1_RXI - RA_IRQ_FIRST)); + putreg32(EVENT_IIC1_TXI, R_ICU_IELSR(IIC1_TXI - RA_IRQ_FIRST)); + putreg32(EVENT_IIC1_TEI, R_ICU_IELSR(IIC1_TEI - RA_IRQ_FIRST)); + putreg32(EVENT_IIC1_ERI, R_ICU_IELSR(IIC1_ERI - RA_IRQ_FIRST)); +#endif #ifdef CONFIG_RA_GPT0_GPT putreg32(EVENT_GPT0_COUNTER_OVERFLOW, R_ICU_IELSR(GPT0_COUNTER_OVERFLOW - RA_IRQ_FIRST)); diff --git a/arch/arm/src/ra8m1/ra_iic_i2c.c b/arch/arm/src/ra8m1/ra_iic_i2c.c new file mode 100644 index 00000000000..5a9b8574dc5 --- /dev/null +++ b/arch/arm/src/ra8m1/ra_iic_i2c.c @@ -0,0 +1,1005 @@ +/**************************************************************************** + * arch/arm/src/ra8m1/ra_iic_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 <string.h> +#include <assert.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/arch.h> +#include <nuttx/clock.h> +#include <nuttx/irq.h> +#include <nuttx/mutex.h> +#include <nuttx/semaphore.h> +#include <nuttx/i2c/i2c_master.h> + +#include <arch/board/board.h> + +#include "arm_internal.h" +#include "chip.h" + +#include "hardware/ra8m1_iic.h" +#include "hardware/ra8m1_mstp.h" +#include "hardware/ra8m1_system.h" +#include "ra_icu.h" +#include "ra_gpio.h" +#include "ra_clockconfig.h" +#include "ra_iic_i2c.h" + +#ifdef CONFIG_RA_DTC +# include <nuttx/cache.h> + +# include "hardware/ra8m1_dtc.h" +# include "ra_dtc.h" +#endif + +#if defined(CONFIG_RA_IIC0) || defined(CONFIG_RA_IIC1) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Master-only for now: slave addresses, general call, host address and + * the ICWUR wakeup-from-standby block (fixed base, IIC0 only, unrelated to + * the per-channel register map) are not implemented -- nothing in this + * driver needs them yet. + */ + +#define IIC_TIMEOUT_TICKS SEC2TICK(1) /* Per-transfer wait cap */ +#define IIC_BBSY_SPIN_COUNT 1000 /* Bus-free poll iterations */ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum up_iic_phase_e +{ + IIC_PHASE_IDLE = 0, + IIC_PHASE_ADDRESS, /* Address+R/W byte written, waiting for TDRE/RDRF */ + IIC_PHASE_WRITE, /* Clocking out message data bytes */ + IIC_PHASE_WRITE_DONE, /* Last byte written, waiting for TEND */ + IIC_PHASE_READ_DUMMY, /* Waiting for the post-address dummy RDRF */ + IIC_PHASE_READ, /* Clocking in message data bytes */ + IIC_PHASE_STOP_PENDING, /* SP (or RS) issued, waiting for completion */ +}; + +struct up_dev_s +{ + struct i2c_master_s dev; /* Generic I2C interface */ + const uint32_t iicbase; /* Base address of this channel's registers */ + uint32_t mstp; /* Module Stop Control Register bit */ + uint8_t rxirq; /* IICn_RXI */ + uint8_t txirq; /* IICn_TXI */ + uint8_t teirq; /* IICn_TEI */ + uint8_t erirq; /* IICn_ERI */ + uint32_t frequency; /* Last-configured bus frequency (0: unset) */ + + mutex_t lock; /* Serializes .transfer() calls */ + sem_t waitsem; /* ISR -> transfer() completion signal */ + int result; /* 0, or -errno, set before waitsem is posted */ + + FAR struct i2c_msg_s *msgs; /* Message array for this transfer */ + int nmsgs; /* Number of messages in the array */ + int msgndx; /* Index of the message in progress */ + FAR uint8_t *ptr; /* Next byte position in the current message */ + int remaining; /* Bytes left to move in the current message */ + enum up_iic_phase_e phase; /* Current state within the message */ + +#ifdef CONFIG_RA_DTC + bool dtc; /* true: move message data through the DTC */ + bool rxdtc_armed; /* true: the next RXI is the DTC's own + * bulk-read completion, not a real byte */ + FAR uint8_t *rxdtc_buffer; /* Start of the DTC's RX destination range */ + int rxdtc_length; /* Its length, in bytes -- dtcrxinfo.dar/cra + * cannot be reused for this: the DTC writes + * its own incremented/decremented values + * back into them as it runs */ + struct dtc_transfer_info_s dtctxinfo; + struct dtc_transfer_info_s dtcrxinfo; +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int up_iic_transfer(FAR struct i2c_master_s *dev, + FAR struct i2c_msg_s *msgs, int count); + +static int up_iic_interrupt(int irq, FAR void *context, FAR void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct i2c_ops_s g_iic_ops = +{ + .transfer = up_iic_transfer, +}; + +#ifdef CONFIG_RA_IIC0 +static struct up_dev_s g_iic0priv = +{ + .dev = + { + .ops = &g_iic_ops + }, + + .iicbase = R_IIC0_BASE, + .mstp = R_MSTP_MSTPCRB_MSTPB9, + .rxirq = IIC0_RXI, + .txirq = IIC0_TXI, + .teirq = IIC0_TEI, + .erirq = IIC0_ERI, +#ifdef CONFIG_RA_IIC0_DTC + .dtc = true, +#endif +}; +#endif + +#ifdef CONFIG_RA_IIC1 +static struct up_dev_s g_iic1priv = +{ + .dev = + { + .ops = &g_iic_ops + }, + + .iicbase = R_IIC1_BASE, + .mstp = R_MSTP_MSTPCRB_MSTPB8, + .rxirq = IIC1_RXI, + .txirq = IIC1_TXI, + .teirq = IIC1_TEI, + .erirq = IIC1_ERI, +#ifdef CONFIG_RA_IIC1_DTC + .dtc = true, +#endif +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_iic_serialin / up_iic_serialout + * + * Description: + * All IIC registers are 8 bits wide (unlike SCI_B's mixed 8/16-bit + * register set), so these are thin wrappers over the 8-bit accessors. + * + ****************************************************************************/ + +static inline uint8_t up_iic_serialin(FAR struct up_dev_s *priv, + uint32_t offset) +{ + return getreg8(priv->iicbase + offset); +} + +static inline void up_iic_serialout(FAR struct up_dev_s *priv, + uint32_t offset, uint8_t value) +{ + putreg8(value, priv->iicbase + offset); +} + +/**************************************************************************** + * Name: up_iic_setfrequency + * + * Description: + * Derive ICMR1.CKS and ICBRH/ICBRL.BR[4:0] for the requested bus + * frequency, using formula (4) of RA8M1 User's Manual section 32.2.16 + * (ICFER.SCLE=1, NFE=0, CKS != 000b): rate = IIC-phi / [(BRH+2)+(BRL+2)], + * ignoring bus rise/fall time (tr/tf are a function of external wiring, + * not something firmware can know; the manual's own example tables for + * this formula do the same). A 50% duty split (BRH == BRL) is used. + * + * Tries CKS = 1 upward (never 0: that is formula (2)/(3)'s domain, with + * a different "+3" offset) until (BRH+2) fits the 5-bit BR field, i.e. + * the smallest divider that does not overflow -- matching + * ra_sci_baud_ccr2()'s divider-search style for the same kind of + * problem. + * + ****************************************************************************/ + +static void up_iic_setfrequency(FAR struct up_dev_s *priv, + uint32_t frequency) +{ + uint32_t iicphi; + uint32_t half; + uint8_t cks; + uint8_t br; + uint8_t regval; + + if (frequency == priv->frequency) + { + return; + } + + for (cks = 1; cks < 8; cks++) + { + iicphi = RA_PCLKB_FREQUENCY >> cks; + half = iicphi / (2 * frequency); + + if (half >= 2 && half - 2 <= 31) + { + break; + } + } + + DEBUGASSERT(cks < 8); + br = (uint8_t)(half - 2); + + regval = up_iic_serialin(priv, R_IIC_ICMR1_OFFSET); + regval &= ~(R_IIC_ICMR1_CKS_MASK << R_IIC_ICMR1_CKS_SHIFT); + regval |= (cks << R_IIC_ICMR1_CKS_SHIFT); + up_iic_serialout(priv, R_IIC_ICMR1_OFFSET, regval); + + up_iic_serialout(priv, R_IIC_ICBRH_OFFSET, 0xe0 | br); + up_iic_serialout(priv, R_IIC_ICBRL_OFFSET, 0xe0 | br); + + priv->frequency = frequency; +} + +/**************************************************************************** + * Name: up_iic_setup + * + * Description: + * One-time hardware bring-up: module-stop release, pin configuration, + * and the register sequence from RA8M1 User's Manual Figure 32.5 + * (initial settings). Slave address matching is left disabled + * (ICSER left at 0): this channel is master-only. + * + ****************************************************************************/ + +static void up_iic_setup(FAR struct up_dev_s *priv, uint32_t frequency) +{ + /* Dispatch on the channel's own base address, same reasoning as + * ra_serial.c's up_setup(): more than one IIC channel may be enabled + * at once, so pin selection must be chosen per-device, not per-build. + */ + +#ifdef CONFIG_RA_IIC0 + if (priv->iicbase == R_IIC0_BASE) + { + ra_configgpio(GPIO_IIC0_SCL); + ra_configgpio(GPIO_IIC0_SDA); + } + +#endif +#ifdef CONFIG_RA_IIC1 + if (priv->iicbase == R_IIC1_BASE) + { + ra_configgpio(GPIO_IIC1_SCL); + ra_configgpio(GPIO_IIC1_SDA); + } + +#endif + + putreg16((R_SYSTEM_PRCR_S_PRKEY_V0XA5 | R_SYSTEM_PRCR_S_PRC1), + R_SYSTEM_PRCR_S); + modifyreg32(R_MSTP_MSTPCRB, priv->mstp, 0); + putreg16(R_SYSTEM_PRCR_S_PRKEY_V0XA5, R_SYSTEM_PRCR_S); + + up_iic_serialout(priv, R_IIC_ICCR1_OFFSET, 0); + up_iic_serialout(priv, R_IIC_ICCR1_OFFSET, R_IIC_ICCR1_IICRST); + up_iic_serialout(priv, R_IIC_ICCR1_OFFSET, + R_IIC_ICCR1_IICRST | R_IIC_ICCR1_ICE); + + priv->frequency = 0; + up_iic_setfrequency(priv, frequency); + + /* SCLE: SCL synchronization circuit on (needed for multi-master + * arbitration and for formula (4)'s CKS != 0 bit-rate calculation to + * apply). MALE/NALE/SALE: arbitration-lost detection in every mode + * this driver can enter. NACKE: a NACK during data transmission + * suspends the transfer automatically, instead of continuing to clock + * out the rest of the message into a device that has already given up. + */ + + up_iic_serialout(priv, R_IIC_ICFER_OFFSET, + R_IIC_ICFER_SCLE | R_IIC_ICFER_MALE | + R_IIC_ICFER_NALE | R_IIC_ICFER_SALE | + R_IIC_ICFER_NACKE); + + /* RIE/TIE/TEIE for the normal per-byte flow; NAKIE/ALIE/SPIE so the + * shared ERI vector actually fires for the three abort/completion + * conditions this driver checks for (NACK, arbitration-lost, stop + * detected). TMOIE (bus-hang timeout) and STIE (start detected) are + * not needed by anything this driver does. + */ + + up_iic_serialout(priv, R_IIC_ICIER_OFFSET, + R_IIC_ICIER_RIE | R_IIC_ICIER_TIE | R_IIC_ICIER_TEIE | + R_IIC_ICIER_NAKIE | R_IIC_ICIER_ALIE | + R_IIC_ICIER_SPIE); + + up_iic_serialout(priv, R_IIC_ICCR1_OFFSET, R_IIC_ICCR1_ICE); +} + +/**************************************************************************** + * Name: up_iic_abort + * + * Description: + * Record the failure, request a stop condition (if the bus is still + * owned by this master), and move to IIC_PHASE_STOP_PENDING so the ERI + * handler's STOP detection posts waitsem once the bus is actually idle + * again, instead of leaving it mid-transaction for the next .transfer() + * call to trip over. + * + ****************************************************************************/ + +static void up_iic_abort(FAR struct up_dev_s *priv, int result) +{ + uint8_t regval = up_iic_serialin(priv, R_IIC_ICCR2_OFFSET); + + priv->result = result; + +#ifdef CONFIG_RA_DTC + if (priv->dtc) + { + /* Stop any DTC run this abort cuts short, or a stray activation + * from leftover bus activity could still land in this message's + * buffer after .transfer() has already returned. + */ + + ra_dtc_disable(priv->txirq - RA_IRQ_FIRST); + ra_dtc_disable(priv->rxirq - RA_IRQ_FIRST); + priv->rxdtc_armed = false; + } +#endif + + if ((regval & R_IIC_ICCR2_MST) != 0) + { + up_iic_serialout(priv, R_IIC_ICCR2_OFFSET, R_IIC_ICCR2_SP); + } + + priv->phase = IIC_PHASE_STOP_PENDING; +} + +#ifdef CONFIG_RA_DTC +/**************************************************************************** + * Name: up_iic_dma_txarm + * + * Description: + * Arm the DTC to move priv->remaining data bytes from priv->ptr into + * ICDRT, one per TXI, with a single CPU interrupt once the count is + * exhausted (normal mode, MRB.DISEL=0) instead of one per byte. Called + * right after the address byte is written, so the DTC is already armed + * before that byte's own TDRE-done edge requests the first data byte -- + * see up_iic_write_address(). + * + ****************************************************************************/ + +static void up_iic_dma_txarm(FAR struct up_dev_s *priv) +{ + struct dtc_transfer_info_s *info = &priv->dtctxinfo; + uint8_t vector = priv->txirq - RA_IRQ_FIRST; + uintptr_t buffer = (uintptr_t)priv->ptr; + + DEBUGASSERT(priv->remaining > 0 && priv->remaining <= 65536); + + /* The DTC reads SRAM directly: push out whatever the CPU has cached + * for this range (i2ctool's register-address byte, in particular, is + * a stack variable the CPU just wrote), or the DTC could send stale + * data instead. + */ + + up_clean_dcache(buffer, buffer + priv->remaining); + + info->mra = R_DTC_MRA_MD_NORMAL | R_DTC_MRA_SZ_BYTE | + R_DTC_MRA_SM_INCREMENT; + info->mrb = R_DTC_MRB_DM_FIXED; + info->sar = (uint32_t)buffer; + info->dar = priv->iicbase + R_IIC_ICDRT_OFFSET; + info->cra = (uint16_t)priv->remaining; + info->crb = 0; /* Unused in normal mode */ + + ra_dtc_configure(vector, info); + ra_dtc_enable(vector); +} + +/**************************************************************************** + * Name: up_iic_dma_rxarm + * + * Description: + * Arm the DTC to move priv->remaining - 2 data bytes from ICDRR into + * priv->ptr, one per RXI, with a single CPU interrupt once exhausted. + * Stops two bytes short of the message end and leaves priv->remaining + * at 2 so the existing WAIT/ACKBT handling in up_iic_rxinterrupt() takes + * over for the last two bytes unchanged -- the DTC cannot touch ICMR3, + * which that sequencing needs. Only called when priv->remaining > 2. + * + ****************************************************************************/ + +static void up_iic_dma_rxarm(FAR struct up_dev_s *priv) +{ + struct dtc_transfer_info_s *info = &priv->dtcrxinfo; + uint8_t vector = priv->rxirq - RA_IRQ_FIRST; + uintptr_t buffer = (uintptr_t)priv->ptr; + int nbytes = priv->remaining - 2; + + DEBUGASSERT(nbytes > 0 && nbytes <= 65536); + + /* The DTC writes destination bytes directly to SRAM, bypassing the + * CPU cache: invalidate up front so a stale cache line is not read + * back later instead of what the DTC wrote (mirrors ra_serial.c's RX + * DTC comment on priv->dtcrxbyte). priv->ptr/nbytes are saved to + * rxdtc_buffer/rxdtc_length now because dtcrxinfo.dar/cra get + * overwritten with the DTC's own incremented/decremented values as it + * runs -- they cannot be read back afterwards for the invalidate this + * triggers in up_iic_rxinterrupt(). + */ + + up_invalidate_dcache(buffer, buffer + nbytes); + priv->rxdtc_buffer = priv->ptr; + priv->rxdtc_length = nbytes; + + info->mra = R_DTC_MRA_MD_NORMAL | R_DTC_MRA_SZ_BYTE | R_DTC_MRA_SM_FIXED; + info->mrb = R_DTC_MRB_DM_INCREMENT; + info->sar = priv->iicbase + R_IIC_ICDRR_OFFSET; + info->dar = (uint32_t)buffer; + info->cra = (uint16_t)nbytes; + info->crb = 0; /* Unused in normal mode */ + + priv->ptr += nbytes; + priv->remaining = 2; + priv->rxdtc_armed = true; + + ra_dtc_configure(vector, info); + ra_dtc_enable(vector); +} +#endif + +/**************************************************************************** + * Name: up_iic_write_address + * + * Description: + * Write the address+R/W byte to ICDRT and set the phase for what + * follows it (write data, or the post-address dummy read). + * + ****************************************************************************/ + +static void up_iic_write_address(FAR struct up_dev_s *priv, + FAR struct i2c_msg_s *msg) +{ + bool tenbit = (msg->flags & I2C_M_TEN) != 0; + bool read = (msg->flags & I2C_M_READ) != 0; + + DEBUGASSERT(!tenbit); /* 10-bit addressing: not implemented yet */ + + priv->phase = read ? IIC_PHASE_READ_DUMMY : IIC_PHASE_WRITE; + +#ifdef CONFIG_RA_DTC + if (!read && priv->dtc && priv->remaining > 0) + { + up_iic_dma_txarm(priv); + } +#endif + + up_iic_serialout(priv, R_IIC_ICDRT_OFFSET, + (uint8_t)((msg->addr << 1) | (read ? 1 : 0))); +} + +/**************************************************************************** + * Name: up_iic_nextmsg + * + * Description: + * Point priv->ptr/remaining at message priv->msgndx and kick off its + * start condition (fresh START for the first message, or when the + * previous message did not set I2C_M_NOSTOP; RESTART otherwise). For + * a fresh START, the address+R/W byte is written from + * up_iic_txinterrupt() once TDRE confirms the start condition + * completed (section 32.3.3/32.3.4 step 2-3). For a RESTART, this + * function writes it directly after polling ICSR2.START instead (see + * the comment below) -- TDRE is not a usable signal for that case. + * + ****************************************************************************/ + +static void up_iic_nextmsg(FAR struct up_dev_s *priv, bool restart) +{ + FAR struct i2c_msg_s *msg = &priv->msgs[priv->msgndx]; + uint8_t regval; + int i; + + priv->ptr = msg->buffer; + priv->remaining = msg->length; + priv->phase = IIC_PHASE_ADDRESS; + + if (restart) + { + /* RA8M1 User's Manual section 32.11.2 step 5 / Figure 32.46: clear + * ICSR2.START (still 1 from the start/restart that began this + * message) before requesting the next one, or the hardware's own + * start-issuance consistency check misfires as a false + * arbitration-lost on the restart that follows. + */ + + regval = up_iic_serialin(priv, R_IIC_ICSR2_OFFSET); + up_iic_serialout(priv, R_IIC_ICSR2_OFFSET, + regval & ~R_IIC_ICSR2_START); + + up_iic_serialout(priv, R_IIC_ICCR2_OFFSET, R_IIC_ICCR2_RS); + + /* Manual step 7: write the address only after ICSR2.START is + * re-set, confirming the restart condition itself has completed. + * A TXI/TDRE edge fires sooner than that -- the address byte was + * going out while the restart was still in progress on the bus, + * corrupting it (reproduced at full speed; masked by anything that + * delayed the write, such as a debugger breakpoint on TXI). + */ + + for (i = 0; i < IIC_BBSY_SPIN_COUNT; i++) + { + regval = up_iic_serialin(priv, R_IIC_ICSR2_OFFSET); + if ((regval & R_IIC_ICSR2_START) != 0) + { + break; + } + } + + if ((regval & R_IIC_ICSR2_START) == 0) + { + up_iic_abort(priv, -ETIMEDOUT); + return; + } + + up_iic_write_address(priv, msg); + } + else + { + /* Bus-free poll: BBSY clears almost immediately once idle (section + * 32.3.3 step 2); this is the one place in the driver that spins + * rather than waits on an interrupt, since there is no "bus became + * free" event to wait on cheaply. No critical section needed: the + * caller already holds priv->lock, and the ISRs this could race + * with are not "live" for this message until the ST/RS write below. + */ + + for (i = 0; i < IIC_BBSY_SPIN_COUNT; i++) + { + regval = up_iic_serialin(priv, R_IIC_ICCR2_OFFSET); + if ((regval & R_IIC_ICCR2_BBSY) == 0) + { + break; + } + } + + up_iic_serialout(priv, R_IIC_ICCR2_OFFSET, R_IIC_ICCR2_ST); + } +} + +/**************************************************************************** + * Name: up_iic_rxinterrupt + * + * Description: + * RDRF handling for master receive, implementing the WAIT/ACKBT timing + * from RA8M1 User's Manual section 32.3.4 steps 4-7 (Figure 32.10/32.11) + * uniformly for 1, 2, or 3+ byte reads, driven entirely by + * priv->remaining: + * + * - The byte that is about to make remaining reach 1 (i.e. the + * second-to-last byte, or the only byte when the message is 1 byte + * long) gets WAIT armed before it is read (2+ byte case) or ACKBT + * armed right after the dummy read (1-byte case): either way, NACK + * is in effect before the final byte's own ack phase, not after. + * - The final byte (remaining == 1 going in) gets SP requested before + * it is read, so reading it both retrieves the data and releases the + * WAIT hold into the stop sequence. + * + ****************************************************************************/ + +static void up_iic_rxinterrupt(FAR struct up_dev_s *priv) +{ + uint8_t regval; + + if (priv->phase == IIC_PHASE_READ_DUMMY) + { +#ifdef CONFIG_RA_DTC + if (priv->dtc && priv->remaining > 2) + { + /* Right after the address handshake, before the dummy read + * itself (same ordering as the TX arm in + * up_iic_write_address()): hand the bulk middle of the + * message to the DTC, leaving the last two bytes for the + * WAIT/ACKBT handling below -- see up_iic_dma_rxarm(). This + * also sets priv->remaining to 2. + */ + + up_iic_dma_rxarm(priv); + } +#endif + + up_iic_serialin(priv, R_IIC_ICDRR_OFFSET); /* Dummy read: starts SCL */ + priv->phase = IIC_PHASE_READ; + + if (priv->remaining == 1) + { + /* Single-byte message: no second-to-last byte exists to arm + * NACK during -- arm it now, right after the dummy read, + * before the one real byte's own ack phase. + */ + + regval = up_iic_serialin(priv, R_IIC_ICMR3_OFFSET); + regval |= R_IIC_ICMR3_ACKBT; + up_iic_serialout(priv, R_IIC_ICMR3_OFFSET, regval); + } + + return; + } + +#ifdef CONFIG_RA_DTC + if (priv->rxdtc_armed) + { + /* The DTC moved the last byte of its bulk run already; this RXI + * is its count-exhausted completion, not a new byte. remaining + * is already 2, set by up_iic_dma_rxarm(). + */ + + priv->rxdtc_armed = false; + + up_invalidate_dcache((uintptr_t)priv->rxdtc_buffer, + (uintptr_t)priv->rxdtc_buffer + + priv->rxdtc_length); + + /* The real second-to-last byte's RDRF can already be pending: the + * ICU drops a repeat occurrence of an event while its IR flag is + * still 1 (RA8M1 User's Manual Table 13.5 note 2), so if it rose + * while this completion's IR was still set, no further RXI will + * ever come to report it. Fall through to the normal remaining + * == 2 handling below instead of waiting for an interrupt that + * may never arrive. + */ + + if ((up_iic_serialin(priv, R_IIC_ICSR2_OFFSET) & + R_IIC_ICSR2_RDRF) == 0) + { + return; + } + } +#endif + + if (priv->remaining == 1) + { + /* Final byte: request stop before reading it, so this read both + * retrieves the data and releases the WAIT hold from below. + */ + + up_iic_serialout(priv, R_IIC_ICCR2_OFFSET, R_IIC_ICCR2_SP); + *priv->ptr++ = up_iic_serialin(priv, R_IIC_ICDRR_OFFSET); + priv->remaining = 0; + + regval = up_iic_serialin(priv, R_IIC_ICMR3_OFFSET); + regval &= ~R_IIC_ICMR3_WAIT; + up_iic_serialout(priv, R_IIC_ICMR3_OFFSET, regval); + + priv->phase = IIC_PHASE_STOP_PENDING; + return; + } + + if (priv->remaining == 2) + { + /* About to read the second-to-last byte: hold the clock after its + * ack phase so ACKBT below is in effect before the final byte's. + */ + + regval = up_iic_serialin(priv, R_IIC_ICMR3_OFFSET); + regval |= R_IIC_ICMR3_WAIT; + up_iic_serialout(priv, R_IIC_ICMR3_OFFSET, regval); + } + + *priv->ptr++ = up_iic_serialin(priv, R_IIC_ICDRR_OFFSET); + priv->remaining--; + + if (priv->remaining == 1) + { + regval = up_iic_serialin(priv, R_IIC_ICMR3_OFFSET); + regval |= R_IIC_ICMR3_ACKBT; + up_iic_serialout(priv, R_IIC_ICMR3_OFFSET, regval); + } +} + +/**************************************************************************** + * Name: up_iic_txinterrupt + * + * Description: + * TDRE handling: writes the address+R/W byte once (IIC_PHASE_ADDRESS), + * then message data bytes for a write (IIC_PHASE_WRITE), per RA8M1 + * User's Manual section 32.3.3 steps 3-4 / 32.3.4 step 3. For a read, + * nothing more happens here after the address byte -- the hardware + * flips to receive mode on its own and the next event is RDRF, not + * another TDRE. + * + ****************************************************************************/ + +static void up_iic_txinterrupt(FAR struct up_dev_s *priv) +{ + FAR struct i2c_msg_s *msg = &priv->msgs[priv->msgndx]; + + if (priv->phase == IIC_PHASE_ADDRESS) + { + up_iic_write_address(priv, msg); + return; + } + + if (priv->phase == IIC_PHASE_WRITE) + { +#ifdef CONFIG_RA_DTC + if (priv->dtc && priv->remaining > 0 && + (getreg32(R_ICU_IELSR(priv->txirq - RA_IRQ_FIRST)) & + R_ICU_IELSR_DTCE) == 0) + { + /* The DTC moved every data byte already and cleared its own + * DTCE on exhaustion: this TXI is that completion, not a + * request for another byte -- see up_iic_dma_txarm(). The + * DTCE check also guards against the restart path's known + * early TXI (see up_iic_nextmsg()), which this driver never + * issues while remaining > 0, but would otherwise be + * misread as a completion here too. + */ + + priv->remaining = 0; + priv->phase = IIC_PHASE_WRITE_DONE; + return; + } +#endif + + if (priv->remaining > 0) + { + up_iic_serialout(priv, R_IIC_ICDRT_OFFSET, *priv->ptr++); + priv->remaining--; + } + + if (priv->remaining == 0) + { + priv->phase = IIC_PHASE_WRITE_DONE; + } + } +} + +/**************************************************************************** + * Name: up_iic_teinterrupt + * + * Description: + * TEND handling: the last written byte has actually finished shifting + * out (not just left ICDRT), so it is now safe to request a stop or + * restart -- RA8M1 User's Manual section 32.3.3 step 5. + * + ****************************************************************************/ + +static void up_iic_teinterrupt(FAR struct up_dev_s *priv) +{ + FAR struct i2c_msg_s *msg; + + if (priv->phase != IIC_PHASE_WRITE_DONE) + { + return; + } + + msg = &priv->msgs[priv->msgndx]; + + if ((msg->flags & I2C_M_NOSTOP) != 0 && priv->msgndx + 1 < priv->nmsgs) + { + priv->msgndx++; + up_iic_nextmsg(priv, true); + } + else + { + up_iic_serialout(priv, R_IIC_ICCR2_OFFSET, R_IIC_ICCR2_SP); + priv->phase = IIC_PHASE_STOP_PENDING; + } +} + +/**************************************************************************** + * Name: up_iic_erinterrupt + * + * Description: + * Shared error/event vector: NACK and arbitration-lost both abort the + * transfer; stop-condition detection finalizes it (successfully, or + * following an abort -- either way the bus is idle again once this + * fires). A read's own completion also funnels through here, since + * up_iic_rxinterrupt() only requests the stop, it does not wait for it. + * + ****************************************************************************/ + +static int up_iic_erinterrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct up_dev_s *priv = (FAR struct up_dev_s *)arg; + uint8_t regval; + + /* IELSRn.IR must be cleared by software or the NVIC re-enters this + * handler indefinitely (RA8M1 User's Manual section 13.5.1) -- missing + * this call is what produced an endless stream of ERI entries reading + * ICSR2 as 0x00 after the first real event. + */ + + ra_clear_ir(irq); + + regval = up_iic_serialin(priv, R_IIC_ICSR2_OFFSET); + + if ((regval & R_IIC_ICSR2_NACKF) != 0 && + priv->phase != IIC_PHASE_STOP_PENDING) + { + up_iic_abort(priv, -ENXIO); + } + else if ((regval & R_IIC_ICSR2_AL) != 0 && + priv->phase != IIC_PHASE_STOP_PENDING) + { + up_iic_abort(priv, -EAGAIN); + } + + /* These flags are "write 0 to clear, write 1 is ignored" (section + * 32.2.10 note 1): a plain read-AND-write leaves every other bit, + * including the read-only TDRE, exactly as read. + */ + + if ((regval & R_IIC_ICSR2_STOP) != 0) + { + up_iic_serialout(priv, R_IIC_ICSR2_OFFSET, + regval & ~(R_IIC_ICSR2_STOP | R_IIC_ICSR2_NACKF | + R_IIC_ICSR2_AL)); + priv->phase = IIC_PHASE_IDLE; + nxsem_post(&priv->waitsem); + return OK; + } + + up_iic_serialout(priv, R_IIC_ICSR2_OFFSET, + regval & ~(R_IIC_ICSR2_NACKF | R_IIC_ICSR2_AL)); + return OK; +} + +/**************************************************************************** + * Name: up_iic_interrupt + * + * Description: + * Common entry point for the RXI/TXI/TEI vectors (ERI has its own, + * above, since it is the only one that can complete or fail a transfer + * outright rather than just advance it). + * + ****************************************************************************/ + +static int up_iic_interrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct up_dev_s *priv = (FAR struct up_dev_s *)arg; + + ra_clear_ir(irq); + + if (irq == priv->rxirq) + { + up_iic_rxinterrupt(priv); + } + else if (irq == priv->txirq) + { + up_iic_txinterrupt(priv); + } + else + { + DEBUGASSERT(irq == priv->teirq); + up_iic_teinterrupt(priv); + } + + return OK; +} + +/**************************************************************************** + * Name: up_iic_transfer + * + * Description: + * The .transfer() entry point: serializes callers, arms the first + * message, and blocks until the ERI handler reports completion (success + * or abort) via waitsem. 10-bit addressing and I2C_M_NOSTART are not + * implemented yet -- every message gets its own fresh address byte. + * + ****************************************************************************/ + +static int up_iic_transfer(FAR struct i2c_master_s *dev, + FAR struct i2c_msg_s *msgs, int count) +{ + FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev; + int ret; + + DEBUGASSERT(count > 0); + + nxmutex_lock(&priv->lock); + + up_iic_setfrequency(priv, msgs[0].frequency); + + priv->msgs = msgs; + priv->nmsgs = count; + priv->msgndx = 0; + priv->result = OK; + + up_iic_nextmsg(priv, false); + + ret = nxsem_tickwait(&priv->waitsem, IIC_TIMEOUT_TICKS); + if (ret < 0) + { + up_iic_abort(priv, -ETIMEDOUT); + ret = -ETIMEDOUT; + } + else + { + ret = priv->result; + } + + nxmutex_unlock(&priv->lock); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ra_iic_i2cbus_initialize + * + * Description: + * Initialize an IIC channel as the generic NuttX I2C master interface. + * + ****************************************************************************/ + +FAR struct i2c_master_s *ra_iic_i2cbus_initialize(int bus) +{ + FAR struct up_dev_s *priv; + + switch (bus) + { +#ifdef CONFIG_RA_IIC0 + case 0: + priv = &g_iic0priv; + break; +#endif +#ifdef CONFIG_RA_IIC1 + case 1: + priv = &g_iic1priv; + break; +#endif + default: + return NULL; + } + + nxmutex_init(&priv->lock); + nxsem_init(&priv->waitsem, 0, 0); + + up_iic_setup(priv, 100000); /* Standard-mode default; .transfer() can + * raise this per message's own frequency. + */ + + irq_attach(priv->rxirq, up_iic_interrupt, priv); + irq_attach(priv->txirq, up_iic_interrupt, priv); + irq_attach(priv->teirq, up_iic_interrupt, priv); + irq_attach(priv->erirq, up_iic_erinterrupt, priv); + + up_enable_irq(priv->rxirq); + up_enable_irq(priv->txirq); + up_enable_irq(priv->teirq); + up_enable_irq(priv->erirq); + + return &priv->dev; +} +#endif /* CONFIG_RA_IIC0 || CONFIG_RA_IIC1 */ diff --git a/arch/arm/src/ra8m1/ra_iic_i2c.h b/arch/arm/src/ra8m1/ra_iic_i2c.h new file mode 100644 index 00000000000..58ff675e57b --- /dev/null +++ b/arch/arm/src/ra8m1/ra_iic_i2c.h @@ -0,0 +1,49 @@ +/**************************************************************************** + * arch/arm/src/ra8m1/ra_iic_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_RA8M1_RA_IIC_I2C_H +#define __ARCH_ARM_SRC_RA8M1_RA_IIC_I2C_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <nuttx/i2c/i2c_master.h> + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: ra_iic_i2cbus_initialize + * + * Description: + * Initialize the selected IIC channel (0 or 1) as the generic NuttX + * I2C master interface. Returns NULL if that channel is not enabled + * (CONFIG_RA_IICn) or bus is out of range. Only ra_i2c.c (the + * bus-number dispatcher) calls this directly. + * + ****************************************************************************/ + +FAR struct i2c_master_s *ra_iic_i2cbus_initialize(int bus); + +#endif /* __ARCH_ARM_SRC_RA8M1_RA_IIC_I2C_H */ diff --git a/arch/arm/src/ra8m1/ra_sci_i2c.c b/arch/arm/src/ra8m1/ra_sci_i2c.c new file mode 100644 index 00000000000..727ef84384b --- /dev/null +++ b/arch/arm/src/ra8m1/ra_sci_i2c.c @@ -0,0 +1,1267 @@ +/**************************************************************************** + * arch/arm/src/ra8m1/ra_sci_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. + * + ****************************************************************************/ + +/* I2C master over an SCI_B channel's Simple IIC mode (RA8M1 User's Manual + * section 31.8), mutually exclusive on a given channel with that same + * channel's UART mode (ra_serial.c) -- CONFIG_RA_SCIn_I2C vs + * CONFIG_RA_SCIn_UART both drive the same physical peripheral instance and + * the same RXDn/TXDn pins, just in a different CCR3.MOD. + * + * Unlike the dedicated IIC peripheral (ra_iic_i2c.c), Simple IIC mode has + * no arbitration-lost detection and no separate NACK interrupt: ACK/NACK + * (ISR.IICACKR) is polled inside the TXI handler, which is the only + * per-byte vector this mode uses for BOTH directions -- in Simple IIC + * mode SCIn_TXI fires once per complete frame, not just "TDR empty", so a + * read's bytes are also collected there (RDR is read, then a dummy byte + * is written back to TDR to keep generating SCL for the next frame). + * RDR/RSR are double-buffered (section 31.2.2), so the byte is already + * safely latched in RDR by TXI (9th-bit) time regardless of whether + * CCR0.RIE is set -- RIE only gates whether the CPU is interrupted about + * the 8th-bit RSR->RDR transfer, not whether that transfer (or the clock + * generation driving it) happens at all. Confirmed against the Renesas + * FSP r_sci_b_i2c driver, which reads RDR inside its own TXI handler the + * same way and never enables SCIn_RXI outside of DTC use. + * The start/restart/stop-condition-complete interrupt (STIn) is aliased + * onto the existing SCIn_TEI vector in this mode (manual page 1197, + * CCR0.TEIE bit description) -- no new IRQ wiring is needed beyond what + * UART mode already attaches. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/types.h> +#include <stdint.h> +#include <stdbool.h> +#include <assert.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/arch.h> +#include <nuttx/clock.h> +#include <nuttx/irq.h> +#include <nuttx/mutex.h> +#include <nuttx/semaphore.h> +#include <nuttx/i2c/i2c_master.h> + +#include <arch/board/board.h> + +#include "arm_internal.h" +#include "chip.h" + +#include "hardware/ra8m1_sci.h" +#include "hardware/ra8m1_mstp.h" +#include "hardware/ra8m1_system.h" +#include "ra_icu.h" +#include "ra_gpio.h" +#include "ra_clockconfig.h" +#include "ra_sci_i2c.h" + +#ifdef CONFIG_RA_DTC +# include <nuttx/cache.h> + +# include "hardware/ra8m1_dtc.h" +# include "ra_dtc.h" +#endif + +#if defined(CONFIG_RA_SCI0_I2C) || defined(CONFIG_RA_SCI1_I2C) || \ + defined(CONFIG_RA_SCI2_I2C) || defined(CONFIG_RA_SCI3_I2C) || \ + defined(CONFIG_RA_SCI4_I2C) || defined(CONFIG_RA_SCI9_I2C) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Master-only, 7-bit addressing only: nothing in this driver implements + * slave mode or 10-bit addressing yet. + */ + +#define SCII2C_TIMEOUT_TICKS SEC2TICK(1) /* Per-transfer wait cap */ +#define SCII2C_CESR_SPIN_COUNT 1000 /* CCR0 TE/RE sync-delay poll */ + +/* TDR bits 31:8 (TSYNC, MPBT, and the rest of the upper three bytes) are + * documented "read as 1, write value should be 1" (section 31.2.3) for + * the bits this driver does not use -- only TDAT[7:0] actually carries + * data for Simple IIC mode's fixed 8-bit frames (bit 8 of TDAT is only + * meaningful in 9-bit mode). Every TDR write ORs this in, matching the + * Renesas FSP r_sci_b_i2c driver's own SCI_B_I2C_PRV_DATA_REG_MASK + * (0xFFFFFF00), applied unconditionally on every TDR write there too. + */ + +#define SCII2C_TDR_RESERVED 0xffffff00 + +/* ICR.IICDL (SDA output delay): the manual (section 31.8.3) requires this + * delay to exceed the SCLn pin's own fall time (300 ns for IIC normal/fast + * mode) or SDAn may start changing before SCLn has actually gone low. + * IICDL=0 ("no delay") would violate that; 1 ("0 to 1 cycle") is the + * smallest setting that does not, and is used unconditionally here rather + * than computed per frequency. + */ + +#define SCII2C_IICDL_DEFAULT 1 + +/* ICR.NFCS (noise filter clock select): 000b is explicitly prohibited in + * Simple IIC mode (section 31.2.6); 010b (on-chip baud rate generator + * clock divided by 2) is a reasonable default in the 001b-100b range the + * manual allows. + */ + +#define SCII2C_NFCS_DEFAULT 2 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum up_scii2c_phase_e +{ + SCII2C_PHASE_IDLE = 0, + SCII2C_PHASE_ADDRESS, /* Start/restart requested, waiting for its STIn */ + SCII2C_PHASE_DATA, /* Clocking message data, one byte per TXI */ +}; + +struct up_dev_s +{ + struct i2c_master_s dev; /* Generic I2C interface */ + const uint32_t scibase; /* Base address of this channel's registers */ + uint32_t mstp; /* Module Stop Control Register bit */ + uint8_t rxirq; /* SCIn_RXI -- only ever fires when dtc is + * set (RIE is otherwise never enabled) */ + uint8_t txirq; /* SCIn_TXI */ + uint8_t teirq; /* SCIn_TEI (STIn in this mode) */ + uint32_t frequency; /* Last-configured bus frequency (0: unset) */ + + mutex_t lock; /* Serializes .transfer() calls */ + sem_t waitsem; /* ISR -> transfer() completion signal */ + int result; /* 0, or -errno, set before waitsem is posted */ + + FAR struct i2c_msg_s *msgs; /* Message array for this transfer */ + int nmsgs; /* Number of messages in the array */ + int msgndx; /* Index of the message in progress */ + FAR uint8_t *ptr; /* Next byte position in the current message */ + int remaining; /* Bytes left to move in the current message */ + bool read; /* true: current message is a read */ + bool dummyread; /* true: next TXI is the address-ack frame, + * not a real data byte */ + bool restart; /* true: a restart (not a stop) is in + * flight to begin the next message */ + enum up_scii2c_phase_e phase; + +#ifdef CONFIG_RA_DTC + bool dtc; /* true: move message data through the DTC */ + bool rxdtc_armed; /* true: the next RXI is the RX-DTC's own + * bulk-read completion, not a byte this + * driver needs to act on */ + bool txdtc_armed; /* true: the next TXI is the TX-DTC's own + * dummy-byte-feed completion (read only -- + * a write's own completion is instead + * disambiguated via remaining>0 && DTCE==0, + * see up_scii2c_txinterrupt()) */ + FAR uint8_t *rxdtc_buffer; /* Start of the RX-DTC's destination range */ + int rxdtc_length; /* Its length, in bytes -- dtcrxinfo.dar/cra + * cannot be reused for this: the DTC writes + * its own incremented/decremented values + * back into them as it runs */ + uint8_t dtc_dummy; /* Fixed source byte for the TX-DTC's + * dummy-byte feed during a read */ + struct dtc_transfer_info_s dtctxinfo; + struct dtc_transfer_info_s dtcrxinfo; +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int up_scii2c_transfer(FAR struct i2c_master_s *dev, + FAR struct i2c_msg_s *msgs, int count); + +static int up_scii2c_interrupt(int irq, FAR void *context, FAR void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct i2c_ops_s g_scii2c_ops = +{ + .transfer = up_scii2c_transfer, +}; + +#ifdef CONFIG_RA_SCI0_I2C +static struct up_dev_s g_sci0priv = +{ + .dev = + { + .ops = &g_scii2c_ops + }, + + .scibase = R_SCI_B0_BASE, + .mstp = R_MSTP_MSTPCRB_MSTPB31, + .rxirq = SCI0_RXI, + .txirq = SCI0_TXI, + .teirq = SCI0_TEI, +#ifdef CONFIG_RA_SCI0_DTC + .dtc = true, +#endif +}; +#endif + +#ifdef CONFIG_RA_SCI1_I2C +static struct up_dev_s g_sci1priv = +{ + .dev = + { + .ops = &g_scii2c_ops + }, + + .scibase = R_SCI_B1_BASE, + .mstp = R_MSTP_MSTPCRB_MSTPB30, + .rxirq = SCI1_RXI, + .txirq = SCI1_TXI, + .teirq = SCI1_TEI, +#ifdef CONFIG_RA_SCI1_DTC + .dtc = true, +#endif +}; +#endif + +#ifdef CONFIG_RA_SCI2_I2C +static struct up_dev_s g_sci2priv = +{ + .dev = + { + .ops = &g_scii2c_ops + }, + + .scibase = R_SCI_B2_BASE, + .mstp = R_MSTP_MSTPCRB_MSTPB29, + .rxirq = SCI2_RXI, + .txirq = SCI2_TXI, + .teirq = SCI2_TEI, +#ifdef CONFIG_RA_SCI2_DTC + .dtc = true, +#endif +}; +#endif + +#ifdef CONFIG_RA_SCI3_I2C +static struct up_dev_s g_sci3priv = +{ + .dev = + { + .ops = &g_scii2c_ops + }, + + .scibase = R_SCI_B3_BASE, + .mstp = R_MSTP_MSTPCRB_MSTPB28, + .rxirq = SCI3_RXI, + .txirq = SCI3_TXI, + .teirq = SCI3_TEI, +#ifdef CONFIG_RA_SCI3_DTC + .dtc = true, +#endif +}; +#endif + +#ifdef CONFIG_RA_SCI4_I2C +static struct up_dev_s g_sci4priv = +{ + .dev = + { + .ops = &g_scii2c_ops + }, + + .scibase = R_SCI_B4_BASE, + .mstp = R_MSTP_MSTPCRB_MSTPB27, + .rxirq = SCI4_RXI, + .txirq = SCI4_TXI, + .teirq = SCI4_TEI, +#ifdef CONFIG_RA_SCI4_DTC + .dtc = true, +#endif +}; +#endif + +#ifdef CONFIG_RA_SCI9_I2C +static struct up_dev_s g_sci9priv = +{ + .dev = + { + .ops = &g_scii2c_ops + }, + + .scibase = R_SCI_B9_BASE, + .mstp = R_MSTP_MSTPCRB_MSTPB22, + .rxirq = SCI9_RXI, + .txirq = SCI9_TXI, + .teirq = SCI9_TEI, +#ifdef CONFIG_RA_SCI9_DTC + .dtc = true, +#endif +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_scii2c_serialin / up_scii2c_serialout + ****************************************************************************/ + +static inline uint32_t up_scii2c_serialin(FAR struct up_dev_s *priv, + uint32_t offset) +{ + return getreg32(priv->scibase + offset); +} + +static inline void up_scii2c_serialout(FAR struct up_dev_s *priv, + uint32_t offset, uint32_t value) +{ + putreg32(value, priv->scibase + offset); +} + +/**************************************************************************** + * Name: up_scii2c_configgpio + * + * Description: + * ra_configgpio() for an SCI pin, with the N-channel open-drain bit + * added: Simple IIC mode needs the SCLn/SDAn pins driven open-drain + * with external pull-ups (RA8M1 User's Manual Table 31.41 step 7), + * unlike UART mode's normal push-pull use of the same pins -- so this + * cannot just be baked into the shared GPIO_SCIn_RX/TX macros. + * + ****************************************************************************/ + +static void up_scii2c_configgpio(gpio_pinset_t pinset) +{ + pinset.cfg |= R_PFS_NCODR; + ra_configgpio(pinset); +} + +/**************************************************************************** + * Name: up_scii2c_cesr_wait + * + * Description: + * Spin until CESR.TIST/RIST read back "expected" under "mask": RA8M1 + * User's Manual's note on register access when TCLK is slower than the + * bus clock (PCLK) -- a CCR0 TE/RE write is not reflected in CESR + * instantly, and the FSP reference driver polls this after every such + * write rather than assume BPEN=1 (synchronizer bypass) is in effect. + * + ****************************************************************************/ + +static void up_scii2c_cesr_wait(FAR struct up_dev_s *priv, uint8_t mask, + uint8_t expected) +{ + int i; + + for (i = 0; i < SCII2C_CESR_SPIN_COUNT; i++) + { + if ((up_scii2c_serialin(priv, R_SCI_B_CESR_OFFSET) & mask) == + expected) + { + break; + } + } +} + +/**************************************************************************** + * Name: up_scii2c_setfrequency + * + * Description: + * Derive CCR2.CKS and CCR2.BRR for the requested bus frequency, using + * the Simple IIC row of RA8M1 User's Manual Table 31.7: + * N = TCLK / (64 x 2^(2n-1) x B) - 1, searching n (CKS) from 0 upward + * (divisor 32, 128, 512, 2048) for the first one that keeps N in BRR's + * 8-bit range -- the same smallest-divider-first style as + * ra_iic_i2c.c's up_iic_setfrequency() for the dedicated IIC + * peripheral's own, different, formula. + * + ****************************************************************************/ + +static void up_scii2c_setfrequency(FAR struct up_dev_s *priv, + uint32_t frequency) +{ + static const uint32_t divisor[4] = + { + 32, 128, 512, 2048 + }; + + uint32_t raw; + uint32_t denom; + uint8_t cks; + uint8_t brr = 0; + + if (frequency == priv->frequency) + { + return; + } + + for (cks = 0; cks < 4; cks++) + { + /* Round to nearest, not truncate: N+1 = TCLK/(divisor*B) is rarely + * exact, and rounding here before the "- 1" below matters -- e.g. + * TCLK=120MHz, B=100k, cks=0 gives 37.5 exactly, which must round + * up to N=37 (confirmed against the manual's own worked example, + * Table 31.20) or this comes out N=36, one low. + */ + + denom = divisor[cks] * frequency; + raw = (RA_SCICLK_FREQUENCY + denom / 2) / denom; + if (raw >= 1 && raw <= 256) + { + brr = (uint8_t)(raw - 1); + break; + } + } + + DEBUGASSERT(cks < 4); + + up_scii2c_serialout(priv, R_SCI_B_CCR2_OFFSET, + ((uint32_t)cks << R_SCI_B_CCR2_CKS_SHIFT) | + ((uint32_t)brr << R_SCI_B_CCR2_BRR_SHIFT)); + + priv->frequency = frequency; +} + +/**************************************************************************** + * Name: up_scii2c_setup + * + * Description: + * One-time hardware bring-up, following RA8M1 User's Manual Table 31.41 + * ("Example flow of SCI initialization in simple IIC mode"), confirmed + * step-for-step against the Renesas FSP r_sci_b_i2c driver's + * sci_b_i2c_open_hw_master(). CCR0 (TE/RE/TIE/TEIE) is left at 0 here + * -- it is enabled per-transfer by up_scii2c_transfer(), not once at + * setup, matching that same reference driver. + * + ****************************************************************************/ + +static void up_scii2c_setup(FAR struct up_dev_s *priv, uint32_t frequency) +{ + uint32_t icr; + +#ifdef CONFIG_RA_SCI0_I2C + if (priv->scibase == R_SCI_B0_BASE) + { + up_scii2c_configgpio(GPIO_SCI0_SCL); + up_scii2c_configgpio(GPIO_SCI0_SDA); + } + +#endif +#ifdef CONFIG_RA_SCI1_I2C + if (priv->scibase == R_SCI_B1_BASE) + { + up_scii2c_configgpio(GPIO_SCI1_SCL); + up_scii2c_configgpio(GPIO_SCI1_SDA); + } + +#endif +#ifdef CONFIG_RA_SCI2_I2C + if (priv->scibase == R_SCI_B2_BASE) + { + up_scii2c_configgpio(GPIO_SCI2_SCL); + up_scii2c_configgpio(GPIO_SCI2_SDA); + } + +#endif +#ifdef CONFIG_RA_SCI3_I2C + if (priv->scibase == R_SCI_B3_BASE) + { + up_scii2c_configgpio(GPIO_SCI3_SCL); + up_scii2c_configgpio(GPIO_SCI3_SDA); + } + +#endif +#ifdef CONFIG_RA_SCI4_I2C + if (priv->scibase == R_SCI_B4_BASE) + { + up_scii2c_configgpio(GPIO_SCI4_SCL); + up_scii2c_configgpio(GPIO_SCI4_SDA); + } + +#endif +#ifdef CONFIG_RA_SCI9_I2C + if (priv->scibase == R_SCI_B9_BASE) + { + up_scii2c_configgpio(GPIO_SCI9_SCL); + up_scii2c_configgpio(GPIO_SCI9_SDA); + } + +#endif + + putreg16((R_SYSTEM_PRCR_S_PRKEY_V0XA5 | R_SYSTEM_PRCR_S_PRC1), + R_SYSTEM_PRCR_S); + modifyreg32(R_MSTP_MSTPCRB, priv->mstp, 0); + putreg16(R_SYSTEM_PRCR_S_PRKEY_V0XA5, R_SYSTEM_PRCR_S); + + up_scii2c_serialout(priv, R_SCI_B_CCR0_OFFSET, 0); + up_scii2c_cesr_wait(priv, R_SCI_B_CESR_TIST | R_SCI_B_CESR_RIST, 0); + + /* ICR: SDA/SCL high-impedance (idle bus release), SDA output delay, + * reception/transmission interrupt mode (not ACK/NACK interrupt mode -- + * NACK is polled via ISR.IICACKR from the TXI handler instead, see the + * file header comment), clock synchronization, and default-ACK + * transmission. + */ + + icr = R_SCI_B_ICR_IICSDAS_V11 | R_SCI_B_ICR_IICSCLS_V11; + icr |= (uint32_t)SCII2C_IICDL_DEFAULT << R_SCI_B_ICR_IICDL_SHIFT; + icr |= R_SCI_B_ICR_IICINTM | R_SCI_B_ICR_IICCSC | R_SCI_B_ICR_IICACKT; + up_scii2c_serialout(priv, R_SCI_B_ICR_OFFSET, icr); + + /* CCR3: 8-bit characters, MSB-first (LSBF/SINV left at their 0 reset + * value, both required 0 in Simple IIC mode), Simple IIC mode. BPEN + * (synchronizer bypass) stays off, matching ra_serial.c's own choice + * for this same peripheral in UART mode -- see its CCR2 comment. + */ + + up_scii2c_serialout(priv, R_SCI_B_CCR3_OFFSET, + R_SCI_B_CCR3_CHR_V10 | R_SCI_B_CCR3_MOD_SIMPLE_I2C); + + priv->frequency = 0; + up_scii2c_setfrequency(priv, frequency); + + up_scii2c_serialout(priv, R_SCI_B_CCR1_OFFSET, + ((uint32_t)SCII2C_NFCS_DEFAULT << + R_SCI_B_CCR1_NFCS_SHIFT) | R_SCI_B_CCR1_NFEN); + + up_scii2c_serialout(priv, R_SCI_B_CFCLR_OFFSET, + R_SCI_B_CFCLR_RDRFC | R_SCI_B_CFCLR_FERC | + R_SCI_B_CFCLR_PERC | R_SCI_B_CFCLR_MFFC | + R_SCI_B_CFCLR_ORERC | R_SCI_B_CFCLR_DPERC | + R_SCI_B_CFCLR_DFERC | R_SCI_B_CFCLR_DCMFC | + R_SCI_B_CFCLR_ERSC | R_SCI_B_CFCLR_TDREC); + + up_scii2c_serialout(priv, R_SCI_B_ICFCLR_OFFSET, + R_SCI_B_ICFCLR_IICSTIFC); +} + +/**************************************************************************** + * Name: up_scii2c_release_condition + * + * Description: + * Return ICR's IICSDAS/IICSCLS fields to "serial data/clock output" + * (00b): the normal state while actually clocking address/data bytes, + * as opposed to the 01b ("generate a condition") value a start, + * restart, or stop request is issued with. + * + ****************************************************************************/ + +static void up_scii2c_release_condition(FAR struct up_dev_s *priv) +{ + uint32_t icr = up_scii2c_serialin(priv, R_SCI_B_ICR_OFFSET); + + icr &= ~((R_SCI_B_ICR_IICSDAS_MASK << R_SCI_B_ICR_IICSDAS_SHIFT) | + (R_SCI_B_ICR_IICSCLS_MASK << R_SCI_B_ICR_IICSCLS_SHIFT)); + up_scii2c_serialout(priv, R_SCI_B_ICR_OFFSET, icr); +} + +/**************************************************************************** + * Name: up_scii2c_request_condition + * + * Description: + * Request a start, restart, or stop condition: RA8M1 User's Manual + * section 31.8.1 and ICR register notes 3-4 require IICSTAREQ / + * IICRSTAREQ / IICSTPREQ to be set together with IICSDAS/IICSCLS = 01b + * in the same write, with no more than one of the three REQ bits set, + * and only once ISR.IICSTIF already reads 0 for whatever condition + * completed last -- guaranteed here since up_scii2c_interrupt() clears + * it before this is ever called again (see its own comment). + * + ****************************************************************************/ + +static void up_scii2c_request_condition(FAR struct up_dev_s *priv, + uint32_t req) +{ + uint32_t icr = up_scii2c_serialin(priv, R_SCI_B_ICR_OFFSET); + + icr &= ~((R_SCI_B_ICR_IICSDAS_MASK << R_SCI_B_ICR_IICSDAS_SHIFT) | + (R_SCI_B_ICR_IICSCLS_MASK << R_SCI_B_ICR_IICSCLS_SHIFT)); + icr |= R_SCI_B_ICR_IICSDAS_V01 | R_SCI_B_ICR_IICSCLS_V01 | req; + up_scii2c_serialout(priv, R_SCI_B_ICR_OFFSET, icr); +} + +#ifdef CONFIG_RA_DTC +/**************************************************************************** + * Name: up_scii2c_dma_txarm + * + * Description: + * Arm the DTC to move priv->remaining data bytes from priv->ptr into + * TDR, one per TXI, with a single CPU interrupt once the count is + * exhausted (normal mode, MRB.DISEL=0) instead of one per byte. Called + * from up_scii2c_send_address() for a write, before the address byte is + * written, so the DTC is already armed before that byte's own 9th-bit + * edge requests the first data byte. Per RA8M1 User's Manual Table + * 31.46 note 1, ACK/NACK cannot be confirmed for any byte the DTC + * moves -- only the final one, at completion. + * + ****************************************************************************/ + +static void up_scii2c_dma_txarm(FAR struct up_dev_s *priv) +{ + struct dtc_transfer_info_s *info = &priv->dtctxinfo; + uint8_t vector = priv->txirq - RA_IRQ_FIRST; + uintptr_t buffer = (uintptr_t)priv->ptr; + + DEBUGASSERT(priv->remaining > 0 && priv->remaining <= 65536); + + up_clean_dcache(buffer, buffer + priv->remaining); + + info->mra = R_DTC_MRA_MD_NORMAL | R_DTC_MRA_SZ_BYTE | + R_DTC_MRA_SM_INCREMENT; + info->mrb = R_DTC_MRB_DM_FIXED; + info->sar = (uint32_t)buffer; + info->dar = priv->scibase + R_SCI_B_TDR_BY_LL_OFFSET; + info->cra = (uint16_t)priv->remaining; + info->crb = 0; /* Unused in normal mode */ + + ra_dtc_configure(vector, info); + ra_dtc_enable(vector); +} + +/**************************************************************************** + * Name: up_scii2c_dma_rxarm + * + * Description: + * Arm the DTC to move priv->remaining - 2 data bytes, stopping two + * bytes short of the message end so the existing ICR.IICACKT timing in + * up_scii2c_txinterrupt() still handles the last two unchanged -- the + * DTC cannot touch ICR itself. Unlike the dedicated IIC peripheral's + * single-vector RX DTC (ra_iic_i2c.c), Simple IIC mode needs two + * vectors running together for this: one on RXI moves each received + * byte from RDR into the message buffer, a second on TXI keeps + * rewriting a fixed dummy byte into TDR, since nothing else sustains + * SCL between received bytes here (Table 31.46 has no "clock enable" + * separate from a TDR write). Only called when priv->remaining > 2. + * + ****************************************************************************/ + +static void up_scii2c_dma_rxarm(FAR struct up_dev_s *priv) +{ + struct dtc_transfer_info_s *txinfo = &priv->dtctxinfo; + struct dtc_transfer_info_s *rxinfo = &priv->dtcrxinfo; + uint8_t txvector = priv->txirq - RA_IRQ_FIRST; + uint8_t rxvector = priv->rxirq - RA_IRQ_FIRST; + uintptr_t buffer = (uintptr_t)priv->ptr; + uintptr_t dummy = (uintptr_t)&priv->dtc_dummy; + int nbytes = priv->remaining - 2; + + DEBUGASSERT(nbytes > 0 && nbytes <= 65536); + + /* RXI (hence RX-DTC activation) is gated on CCR0.RIE (page 1197, + * Table 31.46), which has stayed 0 since up_scii2c_transfer() -- turn + * it on now, as a full CCR0 write, not a read-modify-write. Section + * 31.2.5 note 1 literally reads the other way (once TE or RE is 1, + * only 0 may be written to them), which this write does not honor -- + * done this way regardless because the Renesas FSP r_sci_b_i2c driver + * makes this exact same full CCR0 write, with TE/RE already 1, on its + * own DTC-enable path. If RIE does not actually take, this write is + * the first thing to suspect. + * + * Dummy-read RDR first: the address frame already left RDRF set (the + * auto RSR->RDR transfer happens regardless of RIE, see the file + * header comment), and if the RXI condition this gates is level- not + * edge-sensitive, turning RIE on here could otherwise fire the + * RX-DTC immediately on that stale condition, shifting every real + * byte into the buffer one position early. + */ + + up_scii2c_serialin(priv, R_SCI_B_RDR_OFFSET); + up_scii2c_serialout(priv, R_SCI_B_CCR0_OFFSET, + R_SCI_B_CCR0_TE | R_SCI_B_CCR0_RE | + R_SCI_B_CCR0_TEIE | R_SCI_B_CCR0_TIE | + R_SCI_B_CCR0_RIE); + + /* The DTC writes destination bytes directly to SRAM, bypassing the CPU + * cache: invalidate up front so a stale cache line is not read back + * later instead of what the DTC wrote, and again in + * up_scii2c_rxinterrupt() once the run completes, in case an unrelated + * access re-cached a stale line for an overlapping address during the + * window in between (mirrors ra_iic_i2c.c's RX DTC). + */ + + up_invalidate_dcache(buffer, buffer + nbytes); + priv->rxdtc_buffer = priv->ptr; + priv->rxdtc_length = nbytes; + + rxinfo->mra = R_DTC_MRA_MD_NORMAL | R_DTC_MRA_SZ_BYTE | R_DTC_MRA_SM_FIXED; + rxinfo->mrb = R_DTC_MRB_DM_INCREMENT; + rxinfo->sar = priv->scibase + R_SCI_B_RDR_OFFSET; + rxinfo->dar = (uint32_t)buffer; + rxinfo->cra = (uint16_t)nbytes; + rxinfo->crb = 0; /* Unused in normal mode */ + + priv->dtc_dummy = 0xff; + up_clean_dcache(dummy, dummy + sizeof(priv->dtc_dummy)); + + txinfo->mra = R_DTC_MRA_MD_NORMAL | R_DTC_MRA_SZ_BYTE | R_DTC_MRA_SM_FIXED; + txinfo->mrb = R_DTC_MRB_DM_FIXED; + txinfo->sar = (uint32_t)dummy; + txinfo->dar = priv->scibase + R_SCI_B_TDR_BY_LL_OFFSET; + txinfo->cra = (uint16_t)nbytes; + txinfo->crb = 0; /* Unused in normal mode */ + + priv->ptr += nbytes; + priv->remaining = 2; + priv->rxdtc_armed = true; + priv->txdtc_armed = true; + + ra_dtc_configure(rxvector, rxinfo); + ra_dtc_configure(txvector, txinfo); + ra_dtc_enable(rxvector); + ra_dtc_enable(txvector); +} +#endif + +/**************************************************************************** + * Name: up_scii2c_send_address + * + * Description: + * Point priv->ptr/remaining/read at message priv->msgndx and write its + * address+R/W byte to TDR -- called once a start or restart condition + * for this message has completed (up_scii2c_teinterrupt()). + * + ****************************************************************************/ + +static void up_scii2c_send_address(FAR struct up_dev_s *priv) +{ + FAR struct i2c_msg_s *msg = &priv->msgs[priv->msgndx]; + bool tenbit = (msg->flags & I2C_M_TEN) != 0; + + DEBUGASSERT(!tenbit); /* 10-bit addressing: not implemented yet */ + + up_scii2c_release_condition(priv); + + priv->ptr = msg->buffer; + priv->remaining = msg->length; + priv->read = (msg->flags & I2C_M_READ) != 0; + priv->dummyread = priv->read; + priv->phase = SCII2C_PHASE_DATA; + +#ifdef CONFIG_RA_DTC + if (!priv->read && priv->dtc && priv->remaining > 0) + { + up_scii2c_dma_txarm(priv); + } +#endif + + up_scii2c_serialout(priv, R_SCI_B_TDR_OFFSET, + SCII2C_TDR_RESERVED | + (uint32_t)((msg->addr << 1) | + (priv->read ? 1 : 0))); +} + +/**************************************************************************** + * Name: up_scii2c_nack_abort + * + * Description: + * A NACK ended the transfer early: record the failure and request a + * stop condition, same as the normal end-of-message path + * (up_scii2c_issue_next_condition()) would, so the completion still + * comes through up_scii2c_teinterrupt() once the bus is idle again -- + * its final-stop handling does not need to distinguish this from a + * successful completion; priv->result already carries the difference. + * + ****************************************************************************/ + +static void up_scii2c_nack_abort(FAR struct up_dev_s *priv, int result) +{ + priv->result = result; + priv->remaining = 0; + priv->restart = false; + +#ifdef CONFIG_RA_DTC + if (priv->dtc) + { + /* Stop any DTC run this abort cuts short, or a stray activation + * from leftover bus activity could still land in this message's + * buffer after .transfer() has already returned. + */ + + ra_dtc_disable(priv->txirq - RA_IRQ_FIRST); + ra_dtc_disable(priv->rxirq - RA_IRQ_FIRST); + priv->rxdtc_armed = false; + priv->txdtc_armed = false; + } +#endif + + up_scii2c_request_condition(priv, R_SCI_B_ICR_IICSTPREQ); +} + +/**************************************************************************** + * Name: up_scii2c_force_abort + * + * Description: + * Used only by up_scii2c_transfer()'s own timeout fallback: the normal + * completion interrupt may never come, so disable the channel directly + * instead of requesting a stop condition and waiting for its STIn. + * + ****************************************************************************/ + +static void up_scii2c_force_abort(FAR struct up_dev_s *priv, int result) +{ + priv->result = result; + +#ifdef CONFIG_RA_DTC + if (priv->dtc) + { + ra_dtc_disable(priv->txirq - RA_IRQ_FIRST); + ra_dtc_disable(priv->rxirq - RA_IRQ_FIRST); + priv->rxdtc_armed = false; + priv->txdtc_armed = false; + } +#endif + + up_scii2c_serialout(priv, R_SCI_B_CCR0_OFFSET, 0); + up_scii2c_cesr_wait(priv, R_SCI_B_CESR_TIST | R_SCI_B_CESR_RIST, 0); + + up_scii2c_serialout(priv, R_SCI_B_ICR_OFFSET, + up_scii2c_serialin(priv, R_SCI_B_ICR_OFFSET) | + R_SCI_B_ICR_IICSDAS_V11 | R_SCI_B_ICR_IICSCLS_V11); + + priv->phase = SCII2C_PHASE_IDLE; +} + +/**************************************************************************** + * Name: up_scii2c_issue_next_condition + * + * Description: + * The current message's data phase is done: request a restart (if + * I2C_M_NOSTOP and another message follows) or a stop (otherwise). + * + ****************************************************************************/ + +static void up_scii2c_issue_next_condition(FAR struct up_dev_s *priv) +{ + FAR struct i2c_msg_s *msg = &priv->msgs[priv->msgndx]; + + if ((msg->flags & I2C_M_NOSTOP) != 0 && priv->msgndx + 1 < priv->nmsgs) + { + priv->restart = true; + up_scii2c_request_condition(priv, R_SCI_B_ICR_IICRSTAREQ); + } + else + { + up_scii2c_request_condition(priv, R_SCI_B_ICR_IICSTPREQ); + } +} + +#ifdef CONFIG_RA_DTC +/**************************************************************************** + * Name: up_scii2c_rxinterrupt + * + * Description: + * RXI handling. CCR0.RIE is only ever turned on for the duration of an + * RX-DTC bulk run (up_scii2c_dma_rxarm()), so every genuine call here + * while one is in flight is that run's own count-exhausted completion + * -- the data is already safely in the message buffer, moved there by + * the DTC itself. RIE stays on afterward through the last two, + * CPU-driven bytes (read via TXI instead, see up_scii2c_txinterrupt()), + * so this also fires harmlessly for those; with rxdtc_armed already + * false by then, there is nothing to do. + * + ****************************************************************************/ + +static void up_scii2c_rxinterrupt(FAR struct up_dev_s *priv) +{ + if (!priv->rxdtc_armed) + { + return; + } + + priv->rxdtc_armed = false; + + up_invalidate_dcache((uintptr_t)priv->rxdtc_buffer, + (uintptr_t)priv->rxdtc_buffer + + priv->rxdtc_length); +} +#endif + +/**************************************************************************** + * Name: up_scii2c_txinterrupt + * + * Description: + * TXI handling: one call per completed frame (address or data, in + * either direction -- see the file header comment). ISR.IICACKR polled + * here is the only ACK/NACK visibility this mode provides. + * + ****************************************************************************/ + +static void up_scii2c_txinterrupt(FAR struct up_dev_s *priv) +{ + uint32_t isr = up_scii2c_serialin(priv, R_SCI_B_ISR_OFFSET); + uint32_t icr; + + if ((isr & R_SCI_B_ISR_IICACKR) != 0 && (!priv->read || priv->dummyread)) + { + /* NACK on the address byte, or on a write's data byte: a real bus + * error, not the master's own deliberate NACK on a read's last + * byte (that case falls through below instead). + */ + + up_scii2c_nack_abort(priv, -ENXIO); + return; + } + + if (!priv->read) + { +#ifdef CONFIG_RA_DTC + if (priv->dtc && priv->remaining > 0 && + (getreg32(R_ICU_IELSR(priv->txirq - RA_IRQ_FIRST)) & + R_ICU_IELSR_DTCE) == 0) + { + /* TX-DTC moved every data byte already and cleared its own + * DTCE on exhaustion: this TXI is that completion, not a + * request for another byte -- see up_scii2c_dma_txarm(). The + * DTCE check guards against misreading a stray early TXI as a + * completion, matching ra_iic_i2c.c's own TX disambiguation. + * + * In DTC normal mode the completion interrupt fires on the + * same activation that performed the final transfer -- i.e. + * the one that just put the last data byte into TDR, not the + * one where it finishes shifting onto the bus (same timing + * ra_serial.c's own TX DTC documents). Requesting a stop or + * restart condition right now would abandon that byte before + * it is ever sent: just mark done and return without touching + * ICR. The next, genuine TXI -- that same last byte's own + * completion -- then takes the remaining == 0 path below + * exactly as a non-DTC write's last byte would, and its + * ISR.IICACKR is still checked by the NACK test above this + * block. + */ + + priv->remaining = 0; + return; + } +#endif + + if (priv->remaining > 0) + { + up_scii2c_serialout(priv, R_SCI_B_TDR_OFFSET, + SCII2C_TDR_RESERVED | *priv->ptr++); + priv->remaining--; + } + else + { + up_scii2c_issue_next_condition(priv); + } + + return; + } + +#ifdef CONFIG_RA_DTC + if (priv->txdtc_armed) + { + /* The TX-DTC's dummy-byte-feed run just exhausted. Same timing + * as the write-completion case above: this activation already + * wrote the dummy byte that triggers the next frame (the first + * CPU-driven tail byte) into TDR -- that trigger has already + * happened, so falling through to the common tail below would + * write a second, extra dummy byte on top of a frame already + * queued. Just clear the flag and return: priv->remaining is + * already 2 (set by up_scii2c_dma_rxarm()), dummyread is already + * false, so the next genuine TXI -- the tail byte's own + * completion -- correctly reaches the plain RDR-read branch + * below instead. (The RX-DTC completion for this same + * underlying byte is handled separately, and harmlessly first, + * in up_scii2c_rxinterrupt().) + */ + + priv->txdtc_armed = false; + return; + } +#endif + + if (priv->dummyread) + { + /* First TXI after the address ACK: no real data arrived with it, + * this just confirms the address and arms ACK (not NACK) for what + * follows, unless this is a 1-byte read -- RA8M1 User's Manual + * Figure 31.96 steps 4-5. + */ + + priv->dummyread = false; + + if (priv->remaining > 1) + { + icr = up_scii2c_serialin(priv, R_SCI_B_ICR_OFFSET); + icr &= ~R_SCI_B_ICR_IICACKT; + up_scii2c_serialout(priv, R_SCI_B_ICR_OFFSET, icr); + } + +#ifdef CONFIG_RA_DTC + if (priv->dtc && priv->remaining > 2) + { + up_scii2c_dma_rxarm(priv); + } +#endif + } + else + { + *priv->ptr++ = (uint8_t)up_scii2c_serialin(priv, R_SCI_B_RDR_OFFSET); + priv->remaining--; + + if (priv->remaining == 0) + { + up_scii2c_issue_next_condition(priv); + return; + } + } + + if (priv->remaining == 1) + { + /* Arm NACK now so it is in effect for the upcoming last byte's own + * ack phase, not after. + */ + + icr = up_scii2c_serialin(priv, R_SCI_B_ICR_OFFSET); + icr |= R_SCI_B_ICR_IICACKT; + up_scii2c_serialout(priv, R_SCI_B_ICR_OFFSET, icr); + } + + /* A byte must be written to TDR to keep generating SCL for the next + * frame, whether or not there is real data left to receive. + */ + + up_scii2c_serialout(priv, R_SCI_B_TDR_OFFSET, SCII2C_TDR_RESERVED | 0xff); +} + +/**************************************************************************** + * Name: up_scii2c_teinterrupt + * + * Description: + * STIn handling (aliased onto the TEI vector in this mode): a start, + * restart, or stop condition has just completed. + * + ****************************************************************************/ + +static void up_scii2c_teinterrupt(FAR struct up_dev_s *priv) +{ + /* ICFCLR.IICSTIFC (ISR.IICSTIF) is cleared by up_scii2c_interrupt() + * before this is called, not here -- see its comment for why the + * order against ra_clear_ir() matters. + */ + + if (priv->phase == SCII2C_PHASE_ADDRESS) + { + up_scii2c_send_address(priv); + return; + } + + if (priv->restart) + { + priv->restart = false; + priv->msgndx++; + priv->phase = SCII2C_PHASE_ADDRESS; + up_scii2c_send_address(priv); + return; + } + + /* Final stop -- either a successful transfer's own end, or the stop + * up_scii2c_nack_abort() requested after a NACK; priv->result already + * carries the difference, nothing else here needs to. Disable the + * transmitter and receiver (RA8M1 User's Manual Table 31.41 is the + * mirror image of this, step 9's enable) and wait for CESR to catch up + * before reporting completion. + */ + + up_scii2c_serialout(priv, R_SCI_B_CCR0_OFFSET, 0); + up_scii2c_cesr_wait(priv, R_SCI_B_CESR_TIST | R_SCI_B_CESR_RIST, 0); + + priv->phase = SCII2C_PHASE_IDLE; + nxsem_post(&priv->waitsem); +} + +/**************************************************************************** + * Name: up_scii2c_interrupt + * + * Description: + * For teirq: clear the SCI-side source (ICFCLR.IICSTIFC, i.e. + * ISR.IICSTIF) before the ICU's latched IR flag, not after. IICSTIF + * stays 1 for a brief window between the two if the order is reversed; + * if that is enough for the ICU to re-assert IR before ra_clear_ir() + * runs, the NVIC re-enters this vector the instant it returns -- with + * priv->phase already past SCII2C_PHASE_ADDRESS from the first call's + * up_scii2c_send_address(), that spurious second call falls through to + * up_scii2c_teinterrupt()'s final-stop cleanup and zeroes CCR0 moments + * after the transmitter was armed, killing SCL outright. Reproduced + * and confirmed: a breakpoint placed right after the TDR write in + * up_scii2c_send_address() (delaying this ISR's return) made transfers + * work every time. + * + ****************************************************************************/ + +static int up_scii2c_interrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct up_dev_s *priv = (FAR struct up_dev_s *)arg; + +#ifdef CONFIG_RA_DTC + if (irq == priv->rxirq) + { + ra_clear_ir(irq); + up_scii2c_rxinterrupt(priv); + } + else +#endif + if (irq == priv->txirq) + { + ra_clear_ir(irq); + up_scii2c_txinterrupt(priv); + } + else + { + DEBUGASSERT(irq == priv->teirq); + up_scii2c_serialout(priv, R_SCI_B_ICFCLR_OFFSET, + R_SCI_B_ICFCLR_IICSTIFC); + ra_clear_ir(irq); + up_scii2c_teinterrupt(priv); + } + + return OK; +} + +/**************************************************************************** + * Name: up_scii2c_transfer + * + * Description: + * The .transfer() entry point: serializes callers, enables the + * channel, requests a start condition for the first message, and + * blocks until up_scii2c_teinterrupt() reports completion (success or + * abort) via waitsem. 10-bit addressing and I2C_M_NOSTART are not + * implemented yet -- every message gets its own fresh address byte. + * + ****************************************************************************/ + +static int up_scii2c_transfer(FAR struct i2c_master_s *dev, + FAR struct i2c_msg_s *msgs, int count) +{ + FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev; + int ret; + + DEBUGASSERT(count > 0); + + nxmutex_lock(&priv->lock); + + up_scii2c_setfrequency(priv, msgs[0].frequency); + + priv->msgs = msgs; + priv->nmsgs = count; + priv->msgndx = 0; + priv->result = OK; + priv->restart = false; + priv->phase = SCII2C_PHASE_ADDRESS; + + up_scii2c_serialout(priv, R_SCI_B_CCR0_OFFSET, + R_SCI_B_CCR0_TE | R_SCI_B_CCR0_RE | + R_SCI_B_CCR0_TEIE | R_SCI_B_CCR0_TIE); + up_scii2c_cesr_wait(priv, R_SCI_B_CESR_TIST | R_SCI_B_CESR_RIST, + R_SCI_B_CESR_TIST | R_SCI_B_CESR_RIST); + + up_scii2c_request_condition(priv, R_SCI_B_ICR_IICSTAREQ); + + ret = nxsem_tickwait(&priv->waitsem, SCII2C_TIMEOUT_TICKS); + if (ret < 0) + { + up_scii2c_force_abort(priv, -ETIMEDOUT); + ret = -ETIMEDOUT; + } + else + { + ret = priv->result; + } + + nxmutex_unlock(&priv->lock); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ra_sci_i2cbus_initialize + ****************************************************************************/ + +FAR struct i2c_master_s *ra_sci_i2cbus_initialize(int channel) +{ + FAR struct up_dev_s *priv; + + switch (channel) + { +#ifdef CONFIG_RA_SCI0_I2C + case 0: + priv = &g_sci0priv; + break; +#endif +#ifdef CONFIG_RA_SCI1_I2C + case 1: + priv = &g_sci1priv; + break; +#endif +#ifdef CONFIG_RA_SCI2_I2C + case 2: + priv = &g_sci2priv; + break; +#endif +#ifdef CONFIG_RA_SCI3_I2C + case 3: + priv = &g_sci3priv; + break; +#endif +#ifdef CONFIG_RA_SCI4_I2C + case 4: + priv = &g_sci4priv; + break; +#endif +#ifdef CONFIG_RA_SCI9_I2C + case 9: + priv = &g_sci9priv; + break; +#endif + default: + return NULL; + } + + nxmutex_init(&priv->lock); + nxsem_init(&priv->waitsem, 0, 0); + + up_scii2c_setup(priv, 100000); /* Standard-mode default; .transfer() can + * raise this per message's own + * frequency. + */ + +#ifdef CONFIG_RA_DTC + irq_attach(priv->rxirq, up_scii2c_interrupt, priv); + up_enable_irq(priv->rxirq); +#endif + + irq_attach(priv->txirq, up_scii2c_interrupt, priv); + irq_attach(priv->teirq, up_scii2c_interrupt, priv); + + up_enable_irq(priv->txirq); + up_enable_irq(priv->teirq); + + return &priv->dev; +} +#endif /* CONFIG_RA_SCIn_I2C */ diff --git a/arch/arm/src/ra8m1/ra_sci_i2c.h b/arch/arm/src/ra8m1/ra_sci_i2c.h new file mode 100644 index 00000000000..2d13b03fed7 --- /dev/null +++ b/arch/arm/src/ra8m1/ra_sci_i2c.h @@ -0,0 +1,49 @@ +/**************************************************************************** + * arch/arm/src/ra8m1/ra_sci_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_RA8M1_RA_SCI_I2C_H +#define __ARCH_ARM_SRC_RA8M1_RA_SCI_I2C_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <nuttx/i2c/i2c_master.h> + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: ra_sci_i2cbus_initialize + * + * Description: + * Initialize SCI_B channel "channel" (0, 1, 2, 3, 4, or 9) in Simple IIC + * mode as the generic NuttX I2C master interface. Returns NULL if that + * channel is not built with CONFIG_RA_SCIn_I2C. Only ra_i2c.c (the + * bus-number dispatcher) calls this directly. + * + ****************************************************************************/ + +FAR struct i2c_master_s *ra_sci_i2cbus_initialize(int channel); + +#endif /* __ARCH_ARM_SRC_RA8M1_RA_SCI_I2C_H */
