diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 37b632f9ae..b9fc7bdb06 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -94,6 +94,7 @@ config ARCH_CHIP_IMX8 config ARCH_CHIP_IMX9 bool "NXP i.MX9 Platform (ARMv8.2a)" select ARCH_HAVE_ADDRENV + select ARCH_HAVE_I2CRESET select ARCH_HAVE_IRQTRIGGER select ARCH_NEED_ADDRENV_MAPPING ---help--- diff --git a/arch/arm64/src/imx9/Kconfig b/arch/arm64/src/imx9/Kconfig index 9ab957be01..9368c961c1 100644 --- a/arch/arm64/src/imx9/Kconfig +++ b/arch/arm64/src/imx9/Kconfig @@ -218,8 +218,257 @@ config IMX9_GPIO_IRQ bool "GPIO Interrupt Support" default n +config IMX9_LPI2C + bool "LPI2C support" + default n + config IMX9_PLL bool "PLL setup support (WIP)" default n +menu "LPI2C Peripherals" + +menuconfig IMX9_LPI2C1 + bool "LPI2C1" + default n + select IMX9_LPI2C + +if IMX9_LPI2C1 + +config IMX9_LPI2C1_BUSYIDLE + int "Bus idle timeout period in clock cycles" + default 0 + +config IMX9_LPI2C1_DMA + bool "Enable DMA for I2C1" + default n + depends on IMX9_LPI2C_DMA + +config IMX9_LPI2C1_FILTSCL + int "I2C master digital glitch filters for SCL input in clock cycles" + default 0 + +config IMX9_LPI2C1_FILTSDA + int "I2C master digital glitch filters for SDA input in clock cycles" + default 0 + +endif # IMX9_LPI2C1 + +menuconfig IMX9_LPI2C2 + bool "LPI2C2" + default n + select IMX9_LPI2C + +if IMX9_LPI2C2 + +config IMX9_LPI2C2_BUSYIDLE + int "Bus idle timeout period in clock cycles" + default 0 + +config IMX9_LPI2C2_DMA + bool "Enable DMA for I2C2" + default n + depends on IMX9_LPI2C_DMA + +config IMX9_LPI2C2_FILTSCL + int "I2C master digital glitch filters for SCL input in clock cycles" + default 0 + +config IMX9_LPI2C2_FILTSDA + int "I2C master digital glitch filters for SDA input in clock cycles" + default 0 + +endif # IMX9_LPI2C2 + +menuconfig IMX9_LPI2C3 + bool "LPI2C3" + default n + select IMX9_LPI2C + +if IMX9_LPI2C3 + +config IMX9_LPI2C3_BUSYIDLE + int "Bus idle timeout period in clock cycles" + default 0 + +config IMX9_LPI2C3_DMA + bool "Enable DMA for I2C3" + default n + depends on IMX9_LPI2C_DMA + +config IMX9_LPI2C3_FILTSCL + int "I2C master digital glitch filters for SCL input in clock cycles" + default 0 + +config IMX9_LPI2C3_FILTSDA + int "I2C master digital glitch filters for SDA input in clock cycles" + default 0 + +endif # IMX9_LPI2C3 + +menuconfig IMX9_LPI2C4 + bool "LPI2C4" + default n + select IMX9_LPI2C + +if IMX9_LPI2C4 + +config IMX9_LPI2C4_BUSYIDLE + int "Bus idle timeout period in clock cycles" + default 0 + +config IMX9_LPI2C4_DMA + bool "Enable DMA for I2C4" + default n + depends on IMX9_LPI2C_DMA + +config IMX9_LPI2C4_FILTSCL + int "I2C master digital glitch filters for SCL input in clock cycles" + default 0 + +config IMX9_LPI2C4_FILTSDA + int "I2C master digital glitch filters for SDA input in clock cycles" + default 0 + +endif # IMX9_LPI2C4 + +menuconfig IMX9_LPI2C5 + bool "LPI2C5" + default n + select IMX9_LPI2C + +if IMX9_LPI2C5 + +config IMX9_LPI2C5_BUSYIDLE + int "Bus idle timeout period in clock cycles" + default 0 + +config IMX9_LPI2C5_FILTSCL + int "I2C master digital glitch filters for SCL input in clock cycles" + default 0 + +config IMX9_LPI2C5_FILTSDA + int "I2C master digital glitch filters for SDA input in clock cycles" + default 0 + +endif # IMX9_LPI2C5 + +menuconfig IMX9_LPI2C6 + bool "LPI2C6" + default n + select IMX9_LPI2C + +if IMX9_LPI2C6 + +config IMX9_LPI2C6_BUSYIDLE + int "Bus idle timeout period in clock cycles" + default 0 + +config IMX9_LPI2C6_FILTSCL + int "I2C master digital glitch filters for SCL input in clock cycles" + default 0 + +config IMX9_LPI2C6_FILTSDA + int "I2C master digital glitch filters for SDA input in clock cycles" + default 0 + +endif # IMX9_LPI2C6 + +menuconfig IMX9_LPI2C7 + bool "LPI2C7" + default n + select IMX9_LPI2C + +if IMX9_LPI2C7 + +config IMX9_LPI2C7_BUSYIDLE + int "Bus idle timeout period in clock cycles" + default 0 + +config IMX9_LPI2C7_FILTSCL + int "I2C master digital glitch filters for SCL input in clock cycles" + default 0 + +config IMX9_LPI2C7_FILTSDA + int "I2C master digital glitch filters for SDA input in clock cycles" + default 0 + +endif # IMX9_LPI2C7 + +menuconfig IMX9_LPI2C8 + bool "LPI2C8" + default n + select IMX9_LPI2C + +if IMX9_LPI2C8 + +config IMX9_LPI2C8_BUSYIDLE + int "Bus idle timeout period in clock cycles" + default 0 + +config IMX9_LPI2C8_FILTSCL + int "I2C master digital glitch filters for SCL input in clock cycles" + default 0 + +config IMX9_LPI2C8_FILTSDA + int "I2C master digital glitch filters for SDA input in clock cycles" + default 0 + +endif # IMX9_LPI2C8 + +endmenu # LPI2C Peripherals +menu "LPI2C Configuration" + depends on IMX9_LPI2C + +config IMX9_LPI2C_DMA + bool "I2C DMA Support" + default n + depends on IMX9_LPI2C && IMX9_EDMA && !I2C_POLLED + ---help--- + This option enables the DMA for I2C transfers. + Note: The user can define CONFIG_I2C_DMAPRIO: a custom priority value + for the I2C dma streams, else the default priority level is set to + medium. + +config IMX9_LPI2C_DMA_MAXMSG + int "Maximum number messages that will be DMAed" + default 8 + depends on IMX9_LPI2C_DMA + ---help--- + This option set the mumber of mesg that can be in a transfer. + It is used to allocate space for the 16 bit LPI2C commands + that will be DMA-ed to the LPI2C device. + +config IMX9_LPI2C_DYNTIMEO + bool "Use dynamic timeouts" + default n + depends on IMX9_LPI2C + +config IMX9_LPI2C_DYNTIMEO_USECPERBYTE + int "Timeout Microseconds per Byte" + default 500 + depends on IMX9_LPI2C_DYNTIMEO + +config IMX9_LPI2C_DYNTIMEO_STARTSTOP + int "Timeout for Start/Stop (Milliseconds)" + default 1000 + depends on IMX9_LPI2C_DYNTIMEO + +config IMX9_LPI2C_TIMEOSEC + int "Timeout seconds" + default 0 + depends on IMX9_LPI2C + +config IMX9_LPI2C_TIMEOMS + int "Timeout Milliseconds" + default 500 + depends on IMX9_LPI2C && !IMX9_LPI2C_DYNTIMEO + +config IMX9_LPI2C_TIMEOTICKS + int "Timeout for Done and Stop (ticks)" + default 500 + depends on IMX9_LPI2C && !IMX9_LPI2C_DYNTIMEO + +endmenu # LPI2C Configuration + endif # ARCH_CHIP_IMX9 diff --git a/arch/arm64/src/imx9/Make.defs b/arch/arm64/src/imx9/Make.defs index 0d7427aec3..27a19cb357 100644 --- a/arch/arm64/src/imx9/Make.defs +++ b/arch/arm64/src/imx9/Make.defs @@ -46,3 +46,7 @@ endif ifeq ($(CONFIG_IMX9_USBDEV),y) CHIP_CSRCS += imx9_usbdev.c endif + +ifeq ($(CONFIG_IMX9_LPI2C),y) + CHIP_CSRCS += imx9_lpi2c.c +endif diff --git a/arch/arm64/src/imx9/hardware/imx9_lpi2c.h b/arch/arm64/src/imx9/hardware/imx9_lpi2c.h new file mode 100644 index 0000000000..1c7e47aa88 --- /dev/null +++ b/arch/arm64/src/imx9/hardware/imx9_lpi2c.h @@ -0,0 +1,622 @@ +/**************************************************************************** + * arch/arm64/src/imx9/hardware/imx9_lpi2c.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_ARM64_SRC_IMX9_HARDWARE_IMX9_LPI2C_H_ +#define __ARCH_ARM64_SRC_IMX9_HARDWARE_IMX9_LPI2C_H_ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register offsets *********************************************************/ + +#define IMX9_LPI2C_VERID_OFFSET 0x0000 /* Version ID Register offset */ +#define IMX9_LPI2C_PARAM_OFFSET 0x0004 /* Parameter Register offset */ +#define IMX9_LPI2C_MCR_OFFSET 0x0010 /* Master Control Register offset */ +#define IMX9_LPI2C_MSR_OFFSET 0x0014 /* Master Status Register offset */ +#define IMX9_LPI2C_MIER_OFFSET 0x0018 /* Master Interrupt Enable Register offset */ +#define IMX9_LPI2C_MDER_OFFSET 0x001c /* Master DMA Enable Register offset */ +#define IMX9_LPI2C_MCFGR0_OFFSET 0x0020 /* Master Config Register 0 offset */ +#define IMX9_LPI2C_MCFGR1_OFFSET 0x0024 /* Master Config Register 1 offset */ +#define IMX9_LPI2C_MCFGR2_OFFSET 0x0028 /* Master Config Register 2 offset */ +#define IMX9_LPI2C_MCFGR3_OFFSET 0x002c /* Master Config Register 3 offset */ +#define IMX9_LPI2C_MDMR_OFFSET 0x0040 /* Master Data Match Register offset */ +#define IMX9_LPI2C_MCCR0_OFFSET 0x0048 /* Master Clock Configuration Register 0 offset */ +#define IMX9_LPI2C_MCCR1_OFFSET 0x0050 /* Master Clock Configuration Register 1 offset */ +#define IMX9_LPI2C_MFCR_OFFSET 0x0058 /* Master FIFO Control Register offset */ +#define IMX9_LPI2C_MFSR_OFFSET 0x005C /* Master FIFO Status Register offset */ +#define IMX9_LPI2C_MTDR_OFFSET 0x0060 /* Master Transmit Data Register offset */ +#define IMX9_LPI2C_MRDR_OFFSET 0x0070 /* Master Receive Data Register offset */ +#define IMX9_LPI2C_SCR_OFFSET 0x0110 /* Slave Control Register offset */ +#define IMX9_LPI2C_SSR_OFFSET 0x0114 /* Slave Status Register offset */ +#define IMX9_LPI2C_SIER_OFFSET 0x0118 /* Slave Interrupt Enable Register offset */ +#define IMX9_LPI2C_SDER_OFFSET 0x011c /* Slave DMA Enable Register offset */ +#define IMX9_LPI2C_SCFGR1_OFFSET 0x0124 /* Slave Config Register 1 offset */ +#define IMX9_LPI2C_SCFGR2_OFFSET 0x0128 /* Slave Config Register 2 offset */ +#define IMX9_LPI2C_SAMR_OFFSET 0x0140 /* Slave Address Match Register offset */ +#define IMX9_LPI2C_SASR_OFFSET 0x0150 /* Slave Address Status Register offset */ +#define IMX9_LPI2C_STAR_OFFSET 0x0154 /* Slave Transmit ACK Register offset */ +#define IMX9_LPI2C_STDR_OFFSET 0x0160 /* Slave Transmit Data Register offset */ +#define IMX9_LPI2C_SRDR_OFFSET 0x0170 /* Slave Receive Data Register offset */ + +/* Register addresses *******************************************************/ + +/* LPI2C1 Registers */ + +#define IMX9_LPI2C1_VERID (IMX9_LPI2C1_BASE + IMX9_LPI2C_VERID_OFFSET) /* Version ID Register */ +#define IMX9_LPI2C1_PARAM (IMX9_LPI2C1_BASE + IMX9_LPI2C_PARAM_OFFSET) /* Parameter Register */ +#define IMX9_LPI2C1_MCR (IMX9_LPI2C1_BASE + IMX9_LPI2C_MCR_OFFSET) /* Master Control Register */ +#define IMX9_LPI2C1_MSR (IMX9_LPI2C1_BASE + IMX9_LPI2C_MSR_OFFSET) /* Master Status Register */ +#define IMX9_LPI2C1_MIER (IMX9_LPI2C1_BASE + IMX9_LPI2C_MIER_OFFSET) /* Master Interrupt Enable Register */ +#define IMX9_LPI2C1_MDER (IMX9_LPI2C1_BASE + IMX9_LPI2C_MDER_OFFSET) /* Master DMA Enable Register */ +#define IMX9_LPI2C1_MCFGR0 (IMX9_LPI2C1_BASE + IMX9_LPI2C_MCFGR0_OFFSET) /* Master Config Register 0 */ +#define IMX9_LPI2C1_MCFGR1 (IMX9_LPI2C1_BASE + IMX9_LPI2C_MCFGR1_OFFSET) /* Master Config Register 1 */ +#define IMX9_LPI2C1_MCFGR2 (IMX9_LPI2C1_BASE + IMX9_LPI2C_MCFGR2_OFFSET) /* Master Config Register 2 */ +#define IMX9_LPI2C1_MCFGR3 (IMX9_LPI2C1_BASE + IMX9_LPI2C_MCFGR3_OFFSET) /* Master Config Register 3 */ +#define IMX9_LPI2C1_MDMR (IMX9_LPI2C1_BASE + IMX9_LPI2C_MDMR_OFFSET) /* Master Data Match Register */ +#define IMX9_LPI2C1_MCCR0 (IMX9_LPI2C1_BASE + IMX9_LPI2C_MCCR0_OFFSET) /* Master Clock Configuration Register 0 */ +#define IMX9_LPI2C1_MCCR1 (IMX9_LPI2C1_BASE + IMX9_LPI2C_MCCR1_OFFSET) /* Master Clock Configuration Register 1 */ +#define IMX9_LPI2C1_MFCR (IMX9_LPI2C1_BASE + IMX9_LPI2C_MFCR_OFFSET) /* Master FIFO Control Register */ +#define IMX9_LPI2C1_MFSR (IMX9_LPI2C1_BASE + IMX9_LPI2C_MFSR_OFFSET) /* Master FIFO Status Register */ +#define IMX9_LPI2C1_MTDR (IMX9_LPI2C1_BASE + IMX9_LPI2C_MTDR_OFFSET) /* Master Transmit Data Register */ +#define IMX9_LPI2C1_MRDR (IMX9_LPI2C1_BASE + IMX9_LPI2C_MRDR_OFFSET) /* Master Receive Data Register */ +#define IMX9_LPI2C1_SCR (IMX9_LPI2C1_BASE + IMX9_LPI2C_SCR_OFFSET) /* Slave Control Register */ +#define IMX9_LPI2C1_SSR (IMX9_LPI2C1_BASE + IMX9_LPI2C_SSR_OFFSET) /* Slave Status Register */ +#define IMX9_LPI2C1_SIER (IMX9_LPI2C1_BASE + IMX9_LPI2C_SIER_OFFSET) /* Slave Interrupt Enable Register */ +#define IMX9_LPI2C1_SDER (IMX9_LPI2C1_BASE + IMX9_LPI2C_SDER_OFFSET) /* Slave DMA Enable Register */ +#define IMX9_LPI2C1_SCFGR1 (IMX9_LPI2C1_BASE + IMX9_LPI2C_SCFGR1_OFFSET) /* Slave Config Register 1 */ +#define IMX9_LPI2C1_SCFGR2 (IMX9_LPI2C1_BASE + IMX9_LPI2C_SCFGR2_OFFSET) /* Slave Config Register 2 */ +#define IMX9_LPI2C1_SAMR (IMX9_LPI2C1_BASE + IMX9_LPI2C_SAMR_OFFSET) /* Slave Address Match Register */ +#define IMX9_LPI2C1_SASR (IMX9_LPI2C1_BASE + IMX9_LPI2C_SASR_OFFSET) /* Slave Address Status Register */ +#define IMX9_LPI2C1_STAR (IMX9_LPI2C1_BASE + IMX9_LPI2C_STAR_OFFSET) /* Slave Transmit ACK Register */ +#define IMX9_LPI2C1_STDR (IMX9_LPI2C1_BASE + IMX9_LPI2C_STDR_OFFSET) /* Slave Transmit Data Register */ +#define IMX9_LPI2C1_SRDR (IMX9_LPI2C1_BASE + IMX9_LPI2C_SRDR_OFFSET) /* Slave Receive Data Register */ + +/* LPI2C2 Registers */ + +#define IMX9_LPI2C2_VERID (IMX9_LPI2C2_BASE + IMX9_LPI2C_VERID_OFFSET) /* Version ID Register */ +#define IMX9_LPI2C2_PARAM (IMX9_LPI2C2_BASE + IMX9_LPI2C_PARAM_OFFSET) /* Parameter Register */ +#define IMX9_LPI2C2_MCR (IMX9_LPI2C2_BASE + IMX9_LPI2C_MCR_OFFSET) /* Master Control Register */ +#define IMX9_LPI2C2_MSR (IMX9_LPI2C2_BASE + IMX9_LPI2C_MSR_OFFSET) /* Master Status Register */ +#define IMX9_LPI2C2_MIER (IMX9_LPI2C2_BASE + IMX9_LPI2C_MIER_OFFSET) /* Master Interrupt Enable Register */ +#define IMX9_LPI2C2_MDER (IMX9_LPI2C2_BASE + IMX9_LPI2C_MDER_OFFSET) /* Master DMA Enable Register */ +#define IMX9_LPI2C2_MCFGR0 (IMX9_LPI2C2_BASE + IMX9_LPI2C_MCFGR0_OFFSET) /* Master Config Register 0 */ +#define IMX9_LPI2C2_MCFGR1 (IMX9_LPI2C2_BASE + IMX9_LPI2C_MCFGR1_OFFSET) /* Master Config Register 1 */ +#define IMX9_LPI2C2_MCFGR2 (IMX9_LPI2C2_BASE + IMX9_LPI2C_MCFGR2_OFFSET) /* Master Config Register 2 */ +#define IMX9_LPI2C2_MCFGR3 (IMX9_LPI2C2_BASE + IMX9_LPI2C_MCFGR3_OFFSET) /* Master Config Register 3 */ +#define IMX9_LPI2C2_MDMR (IMX9_LPI2C2_BASE + IMX9_LPI2C_MDMR_OFFSET) /* Master Data Match Register */ +#define IMX9_LPI2C2_MCCR0 (IMX9_LPI2C2_BASE + IMX9_LPI2C_MCCR0_OFFSET) /* Master Clock Configuration Register 0 */ +#define IMX9_LPI2C2_MCCR1 (IMX9_LPI2C2_BASE + IMX9_LPI2C_MCCR1_OFFSET) /* Master Clock Configuration Register 1 */ +#define IMX9_LPI2C2_MFCR (IMX9_LPI2C2_BASE + IMX9_LPI2C_MFCR_OFFSET) /* Master FIFO Control Register */ +#define IMX9_LPI2C2_MFSR (IMX9_LPI2C2_BASE + IMX9_LPI2C_MFSR_OFFSET) /* Master FIFO Status Register */ +#define IMX9_LPI2C2_MTDR (IMX9_LPI2C2_BASE + IMX9_LPI2C_MTDR_OFFSET) /* Master Transmit Data Register */ +#define IMX9_LPI2C2_MRDR (IMX9_LPI2C2_BASE + IMX9_LPI2C_MRDR_OFFSET) /* Master Receive Data Register */ +#define IMX9_LPI2C2_SCR (IMX9_LPI2C2_BASE + IMX9_LPI2C_SCR_OFFSET) /* Slave Control Register */ +#define IMX9_LPI2C2_SSR (IMX9_LPI2C2_BASE + IMX9_LPI2C_SSR_OFFSET) /* Slave Status Register */ +#define IMX9_LPI2C2_SIER (IMX9_LPI2C2_BASE + IMX9_LPI2C_SIER_OFFSET) /* Slave Interrupt Enable Register */ +#define IMX9_LPI2C2_SDER (IMX9_LPI2C2_BASE + IMX9_LPI2C_SDER_OFFSET) /* Slave DMA Enable Register */ +#define IMX9_LPI2C2_SCFGR1 (IMX9_LPI2C2_BASE + IMX9_LPI2C_SCFGR1_OFFSET) /* Slave Config Register 1 */ +#define IMX9_LPI2C2_SCFGR2 (IMX9_LPI2C2_BASE + IMX9_LPI2C_SCFGR2_OFFSET) /* Slave Config Register 2 */ +#define IMX9_LPI2C2_SAMR (IMX9_LPI2C2_BASE + IMX9_LPI2C_SAMR_OFFSET) /* Slave Address Match Register */ +#define IMX9_LPI2C2_SASR (IMX9_LPI2C2_BASE + IMX9_LPI2C_SASR_OFFSET) /* Slave Address Status Register */ +#define IMX9_LPI2C2_STAR (IMX9_LPI2C2_BASE + IMX9_LPI2C_STAR_OFFSET) /* Slave Transmit ACK Register */ +#define IMX9_LPI2C2_STDR (IMX9_LPI2C2_BASE + IMX9_LPI2C_STDR_OFFSET) /* Slave Transmit Data Register */ +#define IMX9_LPI2C2_SRDR (IMX9_LPI2C2_BASE + IMX9_LPI2C_SRDR_OFFSET) /* Slave Receive Data Register */ + +/* LPI2C3 Registers */ + +#define IMX9_LPI2C3_VERID (IMX9_LPI2C3_BASE + IMX9_LPI2C_VERID_OFFSET) /* Version ID Register */ +#define IMX9_LPI2C3_PARAM (IMX9_LPI2C3_BASE + IMX9_LPI2C_PARAM_OFFSET) /* Parameter Register */ +#define IMX9_LPI2C3_MCR (IMX9_LPI2C3_BASE + IMX9_LPI2C_MCR_OFFSET) /* Master Control Register */ +#define IMX9_LPI2C3_MSR (IMX9_LPI2C3_BASE + IMX9_LPI2C_MSR_OFFSET) /* Master Status Register */ +#define IMX9_LPI2C3_MIER (IMX9_LPI2C3_BASE + IMX9_LPI2C_MIER_OFFSET) /* Master Interrupt Enable Register */ +#define IMX9_LPI2C3_MDER (IMX9_LPI2C3_BASE + IMX9_LPI2C_MDER_OFFSET) /* Master DMA Enable Register */ +#define IMX9_LPI2C3_MCFGR0 (IMX9_LPI2C3_BASE + IMX9_LPI2C_MCFGR0_OFFSET) /* Master Config Register 0 */ +#define IMX9_LPI2C3_MCFGR1 (IMX9_LPI2C3_BASE + IMX9_LPI2C_MCFGR1_OFFSET) /* Master Config Register 1 */ +#define IMX9_LPI2C3_MCFGR2 (IMX9_LPI2C3_BASE + IMX9_LPI2C_MCFGR2_OFFSET) /* Master Config Register 2 */ +#define IMX9_LPI2C3_MCFGR3 (IMX9_LPI2C3_BASE + IMX9_LPI2C_MCFGR3_OFFSET) /* Master Config Register 3 */ +#define IMX9_LPI2C3_MDMR (IMX9_LPI2C3_BASE + IMX9_LPI2C_MDMR_OFFSET) /* Master Data Match Register */ +#define IMX9_LPI2C3_MCCR0 (IMX9_LPI2C3_BASE + IMX9_LPI2C_MCCR0_OFFSET) /* Master Clock Configuration Register 0 */ +#define IMX9_LPI2C3_MCCR1 (IMX9_LPI2C3_BASE + IMX9_LPI2C_MCCR1_OFFSET) /* Master Clock Configuration Register 1 */ +#define IMX9_LPI2C3_MFCR (IMX9_LPI2C3_BASE + IMX9_LPI2C_MFCR_OFFSET) /* Master FIFO Control Register */ +#define IMX9_LPI2C3_MFSR (IMX9_LPI2C3_BASE + IMX9_LPI2C_MFSR_OFFSET) /* Master FIFO Status Register */ +#define IMX9_LPI2C3_MTDR (IMX9_LPI2C3_BASE + IMX9_LPI2C_MTDR_OFFSET) /* Master Transmit Data Register */ +#define IMX9_LPI2C3_MRDR (IMX9_LPI2C3_BASE + IMX9_LPI2C_MRDR_OFFSET) /* Master Receive Data Register */ +#define IMX9_LPI2C3_SCR (IMX9_LPI2C3_BASE + IMX9_LPI2C_SCR_OFFSET) /* Slave Control Register */ +#define IMX9_LPI2C3_SSR (IMX9_LPI2C3_BASE + IMX9_LPI2C_SSR_OFFSET) /* Slave Status Register */ +#define IMX9_LPI2C3_SIER (IMX9_LPI2C3_BASE + IMX9_LPI2C_SIER_OFFSET) /* Slave Interrupt Enable Register */ +#define IMX9_LPI2C3_SDER (IMX9_LPI2C3_BASE + IMX9_LPI2C_SDER_OFFSET) /* Slave DMA Enable Register */ +#define IMX9_LPI2C3_SCFGR1 (IMX9_LPI2C3_BASE + IMX9_LPI2C_SCFGR1_OFFSET) /* Slave Config Register 1 */ +#define IMX9_LPI2C3_SCFGR2 (IMX9_LPI2C3_BASE + IMX9_LPI2C_SCFGR2_OFFSET) /* Slave Config Register 2 */ +#define IMX9_LPI2C3_SAMR (IMX9_LPI2C3_BASE + IMX9_LPI2C_SAMR_OFFSET) /* Slave Address Match Register */ +#define IMX9_LPI2C3_SASR (IMX9_LPI2C3_BASE + IMX9_LPI2C_SASR_OFFSET) /* Slave Address Status Register */ +#define IMX9_LPI2C3_STAR (IMX9_LPI2C3_BASE + IMX9_LPI2C_STAR_OFFSET) /* Slave Transmit ACK Register */ +#define IMX9_LPI2C3_STDR (IMX9_LPI2C3_BASE + IMX9_LPI2C_STDR_OFFSET) /* Slave Transmit Data Register */ +#define IMX9_LPI2C3_SRDR (IMX9_LPI2C3_BASE + IMX9_LPI2C_SRDR_OFFSET) /* Slave Receive Data Register */ + +/* LPI2C4 Registers */ + +#define IMX9_LPI2C4_VERID (IMX9_LPI2C4_BASE + IMX9_LPI2C_VERID_OFFSET) /* Version ID Register */ +#define IMX9_LPI2C4_PARAM (IMX9_LPI2C4_BASE + IMX9_LPI2C_PARAM_OFFSET) /* Parameter Register */ +#define IMX9_LPI2C4_MCR (IMX9_LPI2C4_BASE + IMX9_LPI2C_MCR_OFFSET) /* Master Control Register */ +#define IMX9_LPI2C4_MSR (IMX9_LPI2C4_BASE + IMX9_LPI2C_MSR_OFFSET) /* Master Status Register */ +#define IMX9_LPI2C4_MIER (IMX9_LPI2C4_BASE + IMX9_LPI2C_MIER_OFFSET) /* Master Interrupt Enable Register */ +#define IMX9_LPI2C4_MDER (IMX9_LPI2C4_BASE + IMX9_LPI2C_MDER_OFFSET) /* Master DMA Enable Register */ +#define IMX9_LPI2C4_MCFGR0 (IMX9_LPI2C4_BASE + IMX9_LPI2C_MCFGR0_OFFSET) /* Master Config Register 0 */ +#define IMX9_LPI2C4_MCFGR1 (IMX9_LPI2C4_BASE + IMX9_LPI2C_MCFGR1_OFFSET) /* Master Config Register 1 */ +#define IMX9_LPI2C4_MCFGR2 (IMX9_LPI2C4_BASE + IMX9_LPI2C_MCFGR2_OFFSET) /* Master Config Register 2 */ +#define IMX9_LPI2C4_MCFGR3 (IMX9_LPI2C4_BASE + IMX9_LPI2C_MCFGR3_OFFSET) /* Master Config Register 3 */ +#define IMX9_LPI2C4_MDMR (IMX9_LPI2C4_BASE + IMX9_LPI2C_MDMR_OFFSET) /* Master Data Match Register */ +#define IMX9_LPI2C4_MCCR0 (IMX9_LPI2C4_BASE + IMX9_LPI2C_MCCR0_OFFSET) /* Master Clock Configuration Register 0 */ +#define IMX9_LPI2C4_MCCR1 (IMX9_LPI2C4_BASE + IMX9_LPI2C_MCCR1_OFFSET) /* Master Clock Configuration Register 1 */ +#define IMX9_LPI2C4_MFCR (IMX9_LPI2C4_BASE + IMX9_LPI2C_MFCR_OFFSET) /* Master FIFO Control Register */ +#define IMX9_LPI2C4_MFSR (IMX9_LPI2C4_BASE + IMX9_LPI2C_MFSR_OFFSET) /* Master FIFO Status Register */ +#define IMX9_LPI2C4_MTDR (IMX9_LPI2C4_BASE + IMX9_LPI2C_MTDR_OFFSET) /* Master Transmit Data Register */ +#define IMX9_LPI2C4_MRDR (IMX9_LPI2C4_BASE + IMX9_LPI2C_MRDR_OFFSET) /* Master Receive Data Register */ +#define IMX9_LPI2C4_SCR (IMX9_LPI2C4_BASE + IMX9_LPI2C_SCR_OFFSET) /* Slave Control Register */ +#define IMX9_LPI2C4_SSR (IMX9_LPI2C4_BASE + IMX9_LPI2C_SSR_OFFSET) /* Slave Status Register */ +#define IMX9_LPI2C4_SIER (IMX9_LPI2C4_BASE + IMX9_LPI2C_SIER_OFFSET) /* Slave Interrupt Enable Register */ +#define IMX9_LPI2C4_SDER (IMX9_LPI2C4_BASE + IMX9_LPI2C_SDER_OFFSET) /* Slave DMA Enable Register */ +#define IMX9_LPI2C4_SCFGR1 (IMX9_LPI2C4_BASE + IMX9_LPI2C_SCFGR1_OFFSET) /* Slave Config Register 1 */ +#define IMX9_LPI2C4_SCFGR2 (IMX9_LPI2C4_BASE + IMX9_LPI2C_SCFGR2_OFFSET) /* Slave Config Register 2 */ +#define IMX9_LPI2C4_SAMR (IMX9_LPI2C4_BASE + IMX9_LPI2C_SAMR_OFFSET) /* Slave Address Match Register */ +#define IMX9_LPI2C4_SASR (IMX9_LPI2C4_BASE + IMX9_LPI2C_SASR_OFFSET) /* Slave Address Status Register */ +#define IMX9_LPI2C4_STAR (IMX9_LPI2C4_BASE + IMX9_LPI2C_STAR_OFFSET) /* Slave Transmit ACK Register */ +#define IMX9_LPI2C4_STDR (IMX9_LPI2C4_BASE + IMX9_LPI2C_STDR_OFFSET) /* Slave Transmit Data Register */ +#define IMX9_LPI2C4_SRDR (IMX9_LPI2C4_BASE + IMX9_LPI2C_SRDR_OFFSET) /* Slave Receive Data Register */ + +/* Register bit definitions *************************************************/ + +/* LPI2C Version ID Register */ + +#define LPI2C_VERID_FEATURE_SHIFT (0) +#define LPI2C_VERID_FEATURE_MASK (0xffff << LPI2C_VERID_FEATURE_SHIFT) +#define LPI2C_VERID_MINOR_SHIFT (16) +#define LPI2C_VERID_MINOR_MASK (0xff << LPI2C_VERID_MINOR_SHIFT) +#define LPI2C_VERID_MAJOR_SHIFT (24) +#define LPI2C_VERID_MAJOR_MASK (0xff << LPI2C_VERID_MAJOR_SHIFT) + +/* LPI2C Parameter Register */ + +#define LPI2C_PARAM_MTXFIFO_MASK (0x0f) /* Config number of words in master transmit fifo to 2^MTXFIFO (pow(2,MTXFIFO )) */ +# define LPI2C_PARAM_MTXFIFO_1_WORDS (0) +# define LPI2C_PARAM_MTXFIFO_2_WORDS (1) +# define LPI2C_PARAM_MTXFIFO_4_WORDS (2) +# define LPI2C_PARAM_MTXFIFO_8_WORDS (3) +# define LPI2C_PARAM_MTXFIFO_16_WORDS (4) +# define LPI2C_PARAM_MTXFIFO_32_WORDS (5) +# define LPI2C_PARAM_MTXFIFO_64_WORDS (6) +# define LPI2C_PARAM_MTXFIFO_128_WORDS (7) +# define LPI2C_PARAM_MTXFIFO_256_WORDS (8) +# define LPI2C_PARAM_MTXFIFO_512_WORDS (9) +# define LPI2C_PARAM_MTXFIFO_1024_WORDS (10) +# define LPI2C_PARAM_MTXFIFO_2048_WORDS (11) +# define LPI2C_PARAM_MTXFIFO_4096_WORDS (12) +# define LPI2C_PARAM_MTXFIFO_8192_WORDS (13) +# define LPI2C_PARAM_MTXFIFO_16384_WORDS (14) +# define LPI2C_PARAM_MTXFIFO_32768_WORDS (15) + +#define LPI2C_PARAM_MRXFIFO_SHIFT (8) +#define LPI2C_PARAM_MRXFIFO_MASK (0x0f << LPI2C_PARAM_MRXFIFO_SHIFT) /* Config number of words in master receive fifo 2^MRXFIFO (pow(2,MTRFIFO )) */ +# define LPI2C_PARAM_MRXFIFO_1_WORDS (0 << LPI2C_PARAM_MRXFIFO_SHIFT) +# define LPI2C_PARAM_MRXFIFO_2_WORDS (1 << LPI2C_PARAM_MRXFIFO_SHIFT) +# define LPI2C_PARAM_MRXFIFO_4_WORDS (2 << LPI2C_PARAM_MRXFIFO_SHIFT) +# define LPI2C_PARAM_MRXFIFO_8_WORDS (3 << LPI2C_PARAM_MRXFIFO_SHIFT) +# define LPI2C_PARAM_MRXFIFO_16_WORDS (4 << LPI2C_PARAM_MRXFIFO_SHIFT) +# define LPI2C_PARAM_MRXFIFO_32_WORDS (5 << LPI2C_PARAM_MRXFIFO_SHIFT) +# define LPI2C_PARAM_MRXFIFO_64_WORDS (6 << LPI2C_PARAM_MRXFIFO_SHIFT) +# define LPI2C_PARAM_MRXFIFO_128_WORDS (7 << LPI2C_PARAM_MRXFIFO_SHIFT) +# define LPI2C_PARAM_MRXFIFO_256_WORDS (8 << LPI2C_PARAM_MRXFIFO_SHIFT) +# define LPI2C_PARAM_MRXFIFO_512_WORDS (9 << LPI2C_PARAM_MRXFIFO_SHIFT) +# define LPI2C_PARAM_MRXFIFO_1024_WORDS (10 << LPI2C_PARAM_MRXFIFO_SHIFT) +# define LPI2C_PARAM_MRXFIFO_2048_WORDS (11 << LPI2C_PARAM_MRXFIFO_SHIFT) +# define LPI2C_PARAM_MRXFIFO_4096_WORDS (12 << LPI2C_PARAM_MRXFIFO_SHIFT) +# define LPI2C_PARAM_MRXFIFO_8192_WORDS (13 << LPI2C_PARAM_MRXFIFO_SHIFT) +# define LPI2C_PARAM_MRXFIFO_16384_WORDS (14 << LPI2C_PARAM_MRXFIFO_SHIFT) +# define LPI2C_PARAM_MRXFIFO_32768_WORDS (15 << LPI2C_PARAM_MRXFIFO_SHIFT) + +/* LPI2C Master Control Register */ + +#define LPI2C_MCR_MEN (1 << 0) /* Master Enable Bit */ +#define LPI2C_MCR_RST (1 << 1) /* Software Reset Bit */ +#define LPI2C_MCR_DOZEN (1 << 2) /* Doze Mode Enable Bit */ +#define LPI2C_MCR_DBGEN (1 << 3) /* Debug Enable Bit */ + /* Bits 7-4 Reserved */ +#define LPI2C_MCR_RTF (1 << 8) /* Reset Transmit FIFO Bit */ +#define LPI2C_MCR_RRF (1 << 9) /* Reset Receive FIFO Bit */ + /* Bits 31-10 Reserved */ + +/* LPI2C Master Status Register */ + +#define LPI2C_MSR_TDF (1 << 0) /* Transmit Data Flag Bit */ +#define LPI2C_MSR_RDF (1 << 1) /* Receive Data Flag Bit */ + /* Bits 7-2 Reserved */ +#define LPI2C_MSR_EPF (1 << 8) /* End Packet Flag Bit */ +#define LPI2C_MSR_SDF (1 << 9) /* STOP Detect Flag Bit */ +#define LPI2C_MSR_NDF (1 << 10) /* NACK Detect Flag Bit */ +#define LPI2C_MSR_ALF (1 << 11) /* Arbitration Lost Flag Bit */ +#define LPI2C_MSR_FEF (1 << 12) /* FIFO Error Flag Bit */ +#define LPI2C_MSR_PLTF (1 << 13) /* Pin Low Timeout Flag Bit */ +#define LPI2C_MSR_DMF (1 << 14) /* Data Match Flag Bit */ + /* Bits 23-15 Reserved */ +#define LPI2C_MSR_MBF (1 << 24) /* Master Busy Flag Bit */ +#define LPI2C_MSR_BBF (1 << 25) /* Bus Busy Flag Bit */ + /* Bits 31-26 Reserved */ +#define LPI2C_MSR_ERROR_MASK (LPI2C_MSR_NDF | LPI2C_MSR_ALF | \ + LPI2C_MSR_FEF) + +/* LPI2C Master Interrupt Enable Register */ + +#define LPI2C_MIER_TDIE (1 << 0) /* Transmit Data Interrupt Enable Bit */ +#define LPI2C_MIER_RDIE (1 << 1) /* Receive Data Interrupt Enable Bit */ + /* Bits 7-2 Reserved */ +#define LPI2C_MIER_EPIE (1 << 8) /* End Packet Interrupt Enable Bit */ +#define LPI2C_MIER_SDIE (1 << 9) /* STOP Detect Interrupt Enable Bit */ +#define LPI2C_MIER_NDIE (1 << 10) /* NACK Detect Interrupt Enable Bit */ +#define LPI2C_MIER_ALIE (1 << 11) /* Arbitration Lost Interrupt Enable Bit */ +#define LPI2C_MIER_FEIE (1 << 12) /* FIFO Error Interrupt Enable Bit */ +#define LPI2C_MIER_PLTIE (1 << 13) /* Pin Low Timeout Interrupt Enable Bit */ +#define LPI2C_MIER_DMIE (1 << 14) /* Data Match Interrupt Enable Bit */ + /* Bits 31-15 Reserved */ + +/* LPI2C Master DMA Enable Register */ + +#define LPI2C_MDER_TDDE (1 << 0) /* Transmit Data DMA Enable Bit */ +#define LPI2C_MDER_RDDE (1 << 1) /* Transmit Data DMA Enable Bit */ + /* Bits 31-2 Reserved */ + +/* LPI2C Master Config Register 0 */ + +#define LPI2C_MCFG0_HREN (1 << 0) /* Host Request Enable Bit */ +#define LPI2C_MCFG0_HRPOL (1 << 1) /* Host Request Polarity Bit */ +#define LPI2C_MCFG0_HRSEL (1 << 2) /* Host Request Select Bit */ + /* Bits 7-3 Reserved */ +#define LPI2C_MCFG0_CIRFIFO (1 << 8) /* Circular FIFO Enable Bit */ +#define LPI2C_MCFG0_RDMO (1 << 9) /* Receive Data Match Only Bit */ + /* Bits 31-10 Reserved */ + +/* LPI2C Master Config Register 1 */ + +#define LPI2C_MCFGR1_PRESCALE_MASK (7 << 0) /* Clock Prescaler Bit Mask */ +#define LPI2C_MCFGR1_PRESCALE(n) ((n) & LPI2C_MCFGR1_PRESCALE_MASK) +# define LPI2C_MCFGR1_PRESCALE_1 (0) +# define LPI2C_MCFGR1_PRESCALE_2 (1) +# define LPI2C_MCFGR1_PRESCALE_4 (2) +# define LPI2C_MCFGR1_PRESCALE_8 (3) +# define LPI2C_MCFGR1_PRESCALE_16 (4) +# define LPI2C_MCFGR1_PRESCALE_32 (5) +# define LPI2C_MCFGR1_PRESCALE_64 (6) +# define LPI2C_MCFGR1_PRESCALE_128 (7) +#define LPI2C_MCFGR1_AUTOSTOP (1 << 8) /* Automatic STOP Generation Bit */ +#define LPI2C_MCFGR1_IGNACK (1 << 9) /* Ignore NACK Bit */ +#define LPI2C_MCFGR1_TIMECFG (1 << 10) /* Timeout Configuration Bit */ + /* Bits 15-11 Reserved */ +#define LPI2C_MCFGR1_MATCFG_SHIFT (16) +#define LPI2C_MCFGR1_MATCFG_MASK (7 << LPI2C_MCFGR1_MATCFG_SHIFT) /* Match Configuration Bit Mask */ +#define LPI2C_MCFGR1_MATCFG(n) (((n) << LPI2C_MCFGR1_MATCFG_SHIFT) & LPI2C_MCFGR1_MATCFG_MASK) +# define LPI2C_MCFGR1_MATCFG_DISABLE (0 << LPI2C_MCFGR1_MATCFG_SHIFT) + /* LPI2C_MCFG1_MATCFG = 001b Reserved */ +# define LPI2C_MCFGR1_MATCFG2 (2 << LPI2C_MCFGR1_MATCFG_SHIFT) +# define LPI2C_MCFGR1_MATCFG3 (3 << LPI2C_MCFGR1_MATCFG_SHIFT) +# define LPI2C_MCFGR1_MATCFG4 (4 << LPI2C_MCFGR1_MATCFG_SHIFT) +# define LPI2C_MCFGR1_MATCFG5 (5 << LPI2C_MCFGR1_MATCFG_SHIFT) +# define LPI2C_MCFGR1_MATCFG6 (6 << LPI2C_MCFGR1_MATCFG_SHIFT) +# define LPI2C_MCFGR1_MATCFG7 (7 << LPI2C_MCFGR1_MATCFG_SHIFT) + /* Bits 23-19 Reserved */ +#define LPI2C_MCFGR1_PINCFG_SHIFT (24) +#define LPI2C_MCFGR1_PINCFG_MASK (7 << LPI2C_MCFGR1_PINCFG_SHIFT) /* Pin Configuration Bit Mask */ +#define LPI2C_MCFGR1_PINCFG(n) (((n) << LPI2C_MCFGR1_PINCFG_SHIFT) & LPI2C_MCFGR1_PINCFG_MASK) +# define LPI2C_MCFGR1_PINCFG0 (0 << LPI2C_MCFGR1_PINCFG_SHIFT) +# define LPI2C_MCFGR1_PINCFG1 (1 << LPI2C_MCFGR1_PINCFG_SHIFT) +# define LPI2C_MCFGR1_PINCFG2 (2 << LPI2C_MCFGR1_PINCFG_SHIFT) +# define LPI2C_MCFGR1_PINCFG3 (3 << LPI2C_MCFGR1_PINCFG_SHIFT) +# define LPI2C_MCFGR1_PINCFG4 (4 << LPI2C_MCFGR1_PINCFG_SHIFT) +# define LPI2C_MCFGR1_PINCFG5 (5 << LPI2C_MCFGR1_PINCFG_SHIFT) +# define LPI2C_MCFGR1_PINCFG6 (6 << LPI2C_MCFGR1_PINCFG_SHIFT) +# define LPI2C_MCFGR1_PINCFG7 (7 << LPI2C_MCFGR1_PINCFG_SHIFT) + /* Bits 31-27 Reserved */ + +/* LPI2C Master Config Register 2 */ + +#define LPI2C_MCFG2_BUSIDLE_MASK (0xfff << 0) /* Bus Idle Timeout Period in Clock Cycles */ +#define LPI2C_MCFG2_BUSIDLE_DISABLE (0) +#define LPI2C_MCFG2_BUSIDLE(n) ((n) & LPI2C_MCFG2_BUSIDLE_MASK) + /* Bits 15-12 Reserved */ +#define LPI2C_MCFG2_FILTSCL_SHIFT (16) +#define LPI2C_MCFG2_FILTSCL_MASK (15 << LPI2C_MCFG2_FILTSCL_SHIFT) /* Glitch Filter SCL */ +#define LPI2C_MCFG2_FILTSCL_DISABLE (0 << LPI2C_MCFG2_FILTSCL_SHIFT) +#define LPI2C_MCFG2_FILTSCL_CYCLES(n) (((n) << LPI2C_MCFG2_FILTSCL_SHIFT) & LPI2C_MCFG2_FILTSCL_MASK) + /* Bits 23-20 Reserved */ +#define LPI2C_MCFG2_FILTSDA_SHIFT (24) +#define LPI2C_MCFG2_FILTSDA_MASK (15 << LPI2C_MCFG2_FILTSDA_SHIFT) /* Glitch Filter SDA */ +#define LPI2C_MCFG2_FILTSDA_DISABLE (0 << LPI2C_MCFG2_FILTSDA_SHIFT) +#define LPI2C_MCFG2_FILTSDA_CYCLES(n) (((n) << LPI2C_MCFG2_FILTSDA_SHIFT) & LPI2C_MCFG2_FILTSDA_MASK) + /* Bits 31-28 Reserved */ + +/* LPI2C Master Config Register 3 */ + + /* Bits 7-0 Reserved */ +#define LPI2C_MCFG3_PINLOW_SHIFT (8) +#define LPI2C_MCFG3_PINLOW_MASK (0xfff << LPI2C_MCFG3_PINLOW_SHIFT) /* Configure The Pin Low Timeout in Clock Cycles */ +#define LPI2C_MCFG3_PINLOW_CYCLES(n) (((n) << LPI2C_MCFG3_PINLOW_SHIFT) & LPI2C_MCFG3_PINLOW_MASK) + /* Bits 31-20 Reserved */ + +/* LPI2C Master Data Match Register */ + +#define LPI2C_MDMR_MATCH0_SHIFT (0) +#define LPI2C_MDMR_MATCH0_MASK (0xff << LPI2C_MDMR_MATCH0_SHIFT) /* Match 0 Value */ +#define LPI2C_MDMR_MATCH0(n) (((n) << LPI2C_MDMR_MATCH0_SHIFT) & LPI2C_MDMR_MATCH0_MASK) + /* Bits 15-8 Reserved */ +#define LPI2C_MDMR_MATCH1_SHIFT (16) +#define LPI2C_MDMR_MATCH1_MASK (0xff << LPI2C_MDMR_MATCH1_SHIFT) /* Match 1 Value */ +#define LPI2C_MDMR_MATCH1(n) (((n) << LPI2C_MDMR_MATCH1_SHIFT) & LPI2C_MDMR_MATCH1_MASK) + /* Bits 31-24 Reserved */ + +/* LPI2C Master Clock Configuration Register 0 */ + +#define LPI2C_MCCR0_CLKLO_SHIFT (0) +#define LPI2C_MCCR0_CLKLO_MASK (0x3f << LPI2C_MCCR0_CLKLO_SHIFT) /* Clock Low Period */ +#define LPI2C_MCCR0_CLKLO(n) (((n) << LPI2C_MCCR0_CLKLO_SHIFT) & LPI2C_MCCR0_CLKLO_MASK) + /* Bits 7-6 Reserved */ +#define LPI2C_MCCR0_CLKHI_SHIFT (8) +#define LPI2C_MCCR0_CLKHI_MASK (0x3f << LPI2C_MCCR0_CLKHI_SHIFT) /* Clock High Period */ +#define LPI2C_MCCR0_CLKHI(n) (((n) << LPI2C_MCCR0_CLKHI_SHIFT) & LPI2C_MCCR0_CLKHI_MASK) + /* Bits 15-14 Reserved */ +#define LPI2C_MCCR0_SETHOLD_SHIFT (16) +#define LPI2C_MCCR0_SETHOLD_MASK (0x3f << LPI2C_MCCR0_SETHOLD_SHIFT) /* Setup Hold Delay */ +#define LPI2C_MCCR0_SETHOLD(n) (((n) << LPI2C_MCCR0_SETHOLD_SHIFT) & LPI2C_MCCR0_SETHOLD_MASK) + /* Bits 23-22 Reserved */ +#define LPI2C_MCCR0_DATAVD_SHIFT (24) +#define LPI2C_MCCR0_DATAVD_MASK (0x3f << LPI2C_MCCR0_DATAVD_SHIFT) /* Setup Hold Delay */ +#define LPI2C_MCCR0_DATAVD(n) (((n) << LPI2C_MCCR0_DATAVD_SHIFT) & LPI2C_MCCR0_DATAVD_MASK) + /* Bits 31-30 Reserved */ + +/* LPI2C Master Clock Configuration Register 1 */ + +#define LPI2C_MCCR1_CLKLO_SHIFT (0) +#define LPI2C_MCCR1_CLKLO_MASK (0x3f << LPI2C_MCCR1_CLKLO_SHIFT) /* Clock Low Period */ +#define LPI2C_MCCR1_CLKLO(n) (((n) << LPI2C_MCCR1_CLKLO_SHIFT) & LPI2C_MCCR1_CLKLO_MASK) + /* Bits 7-6 Reserved */ +#define LPI2C_MCCR1_CLKHI_SHIFT (8) +#define LPI2C_MCCR1_CLKHI_MASK (0x3f << LPI2C_MCCR1_CLKHI_SHIFT) /* Clock High Period */ +#define LPI2C_MCCR1_CLKHI(n) (((n) << LPI2C_MCCR1_CLKHI_SHIFT) & LPI2C_MCCR1_CLKHI_MASK) + /* Bits 15-14 Reserved */ +#define LPI2C_MCCR1_SETHOLD_SHIFT (16) +#define LPI2C_MCCR1_SETHOLD_MASK (0x3f << LPI2C_MCCR1_SETHOLD_SHIFT) /* Setup Hold Delay */ +#define LPI2C_MCCR1_SETHOLD(n) (((n) << LPI2C_MCCR1_SETHOLD_SHIFT) & LPI2C_MCCR1_SETHOLD_MASK) + + /* Bits 23-22 Reserved */ +#define LPI2C_MCCR1_DATAVD_SHIFT (24) +#define LPI2C_MCCR1_DATAVD_MASK (0x3f << LPI2C_MCCR1_DATAVD_SHIFT) /* Setup Hold Delay */ +#define LPI2C_MCCR1_DATAVD(n) (((n) << LPI2C_MCCR1_DATAVD_SHIFT) & LPI2C_MCCR1_DATAVD_MASK) + + /* Bits 31-30 Reserved */ + +/* LPI2C Master FIFO Control Register */ + +#define LPI2C_MFCR_TXWATER_SHIFT (0) +#define LPI2C_MFCR_TXWATER_MASK (3 << LPI2C_MFCR_TXWATER_SHIFT) /* Transmit FIFO Watermark*/ + +#define LPI2C_MFCR_TXWATER(n) (((n) << LPI2C_MFCR_TXWATER_SHIFT) & LPI2C_MFCR_TXWATER_MASK) /* Transmit FIFO Watermark */ + + /* Bits 15-2 Reserved */ +#define LPI2C_MFCR_RXWATER_SHIFT (16) +#define LPI2C_MFCR_RXWATER_MASK (3 << LPI2C_MFCR_RXWATER_SHIFT) /* Receive FIFO Watermark */ + +#define LPI2C_MFCR_RXWATER(n) (((n) << LPI2C_MFCR_RXWATER_SHIFT) & LPI2C_MFCR_RXWATER_MASK) /* Transmit FIFO Watermark */ + + /* Bits 31-18 Reserved */ + +/* LPI2C Master FIFO Status Register */ + +#define LPI2C_MFSR_TXCOUNT_SHIFT (0) +#define LPI2C_MFSR_TXCOUNT_MASK (3 << LPI2C_MFSR_TXCOUNT_SHIFT) /* Transmit FIFO Count */ + + /* Bits 15-2 Reserved */ +#define LPI2C_MFSR_RXCOUNT_SHIFT (16) +#define LPI2C_MFSR_RXCOUNT_MASK (3 << LPI2C_MFSR_RXCOUNT_SHIFT) /* Receive FIFO Count */ + + /* Bits 31-18 Reserved */ + +/* LPI2C Master Transmit Data Register */ + +#define LPI2C_MTDR_DATA_SHIFT (0) +#define LPI2C_MTDR_DATA_MASK (0xff << LPI2C_MTDR_DATA_SHIFT) /* Transmit Data */ +#define LPI2C_MTDR_DATA(n) ((n) & LPI2C_MTDR_DATA_MASK) +#define LPI2C_MTDR_CMD_SHIFT (8) +#define LPI2C_MTDR_CMD_MASK (7 << LPI2C_MTDR_CMD_SHIFT) /* Command Data */ +#define LPI2C_MTDR_CMD(n) (((n) << LPI2C_MTDR_CMD_SHIFT) & LPI2C_MTDR_CMD_MASK) +# define LPI2C_MTDR_CMD_TXD (0 << LPI2C_MTDR_CMD_SHIFT) +# define LPI2C_MTDR_CMD_RXD (1 << LPI2C_MTDR_CMD_SHIFT) +# define LPI2C_MTDR_CMD_STOP (2 << LPI2C_MTDR_CMD_SHIFT) +# define LPI2C_MTDR_CMD_RXD_DISC (3 << LPI2C_MTDR_CMD_SHIFT) +# define LPI2C_MTDR_CMD_START (4 << LPI2C_MTDR_CMD_SHIFT) +# define LPI2C_MTDR_CMD_START_NACK (5 << LPI2C_MTDR_CMD_SHIFT) +# define LPI2C_MTDR_CMD_START_HI (6 << LPI2C_MTDR_CMD_SHIFT) +# define LPI2C_MTDR_CMD_START_HI_NACK (7 << LPI2C_MTDR_CMD_SHIFT) + + /* Bits 31-11 Reserved */ + +/* LPI2C Master Receive Data Register */ + +#define LPI2C_MRDR_DATA_SHIFT (0) +#define LPI2C_MRDR_DATA_MASK (0xff << LPI2C_MRDR_DATA_SHIFT) /* Receive Data */ + + /* Bits 13-8 Reserved */ +#define LPI2C_MRDR_RXEMPTY_SHIFT (14) +#define LPI2C_MRDR_RXEMPTY_MASK (1 << LPI2C_MRDR_RXEMPTY_SHIFT) /* Rx Empty */ + + /* Bits 31-15 Reserved */ + +/* LPI2C Slave Control Register */ + +#define LPI2C_SCR_SEN (1 << 0) /* Slave Enable Bit */ +#define LPI2C_SCR_RST (1 << 1) /* Software Reset Bit */ + /* Bits 3-2 Reserved */ +#define LPI2C_SCR_FILTEN (1 << 4) /* Filter Enable Bit */ +#define LPI2C_SCR_FILTDZ (1 << 5) /* Filter Doze Enable Bit */ + /* Bits 7-4 Reserved */ +#define LPI2C_SCR_RTF (1 << 8) /* Reset Transmit FIFO Bit */ +#define LPI2C_SCR_RRF (1 << 9) /* Reset Receive FIFO Bit */ + /* Bits 31-10 Reserved */ + +/* LPI2C Slave Status Register */ + +#define LPI2C_SSR_TDF (1 << 0) /* Transmit Data Flag Bit */ +#define LPI2C_SSR_RDF (1 << 1) /* Receive Data Flag Bit */ +#define LPI2C_SSR_AVF (1 << 2) /* Address Valid Flag Bit */ +#define LPI2C_SSR_TAF (1 << 3) /* Transmit ACK Flag Bit */ + /* Bits 7-4 Reserved */ +#define LPI2C_SSR_RSF (1 << 8) /* Repeated Start Flag Bit */ +#define LPI2C_SSR_SDF (1 << 9) /* STOP Detect Flag Bit */ +#define LPI2C_SSR_BEF (1 << 10) /* Bit Error Flag Bit */ +#define LPI2C_SSR_FEF (1 << 11) /* FIFO Error Flag Bit */ +#define LPI2C_SSR_AM0F (1 << 12) /* Address Match 0 Flag Bit */ +#define LPI2C_SSR_AM1F (1 << 13) /* Address Match 1 Flag Bit */ +#define LPI2C_SSR_GCF (1 << 14) /* General Call Flag Bit */ +#define LPI2C_SSR_SARF (1 << 15) /* SMBus Alert Response Flag Bit */ + /* Bits 23-16 Reserved */ +#define LPI2C_MSR_SBF (1 << 24) /* Slave Busy Flag Bit */ +#define LPI2C_MSR_BBF (1 << 25) /* Bus Busy Flag Bit */ + /* Bits 31-26 Reserved */ + +/* LPI2C Slave Interrupt Enable Register */ + +#define LPI2C_SIER_TDIE (1 << 0) /* Transmit Data Interrupt Enable Bit */ +#define LPI2C_SIER_RDIE (1 << 1) /* Receive Data Interrupt Enable Bit */ +#define LPI2C_SIER_AVIE (1 << 2) /* Address Valid Interrupt Enable Bit */ +#define LPI2C_SIER_TAIE (1 << 3) /* Transmit ACK Interrupt Enable Bit */ + /* Bits 7-4 Reserved */ +#define LPI2C_SIER_RSIE (1 << 8) /* Repeated Start Interrupt Enable Bit */ +#define LPI2C_SIER_SDIE (1 << 9) /* STOP Detect Interrupt Enable Bit */ +#define LPI2C_SIER_BEIE (1 << 10) /* Bit Error Interrupt Enable Bit */ +#define LPI2C_SIER_FEIE (1 << 11) /* FIFO Error Interrupt Enable Bit */ +#define LPI2C_SIER_AM0IE (1 << 12) /* Address Match 0 Interrupt Enable Bit */ +#define LPI2C_SIER_AM1IE (1 << 13) /* Address Match 1 Interrupt Enable Bit */ +#define LPI2C_SIER_GCIE (1 << 14) /* General Call Interrupt Enable Bit */ +#define LPI2C_SIER_SARIE (1 << 15) /* SMBus Alert Response Interrupt Enable Bit */ + /* Bits 31-16 Reserved */ + +/* LPI2C Slave DMA Enable Register */ + +#define LPI2C_SDER_TDDE (1 << 0) /* Transmit Data DMA Enable Bit */ +#define LPI2C_SDER_RDDE (1 << 1) /* Transmit Data DMA Enable Bit */ +#define LPI2C_SDER_AVDE (1 << 2) /* Address Valid DMA Enable Bit */ + /* Bits 31-3 Reserved */ + +/* LPI2C Slave Configuration Register 1 */ + +#define LPI2C_SCFGR1_ADRSTALL (1 << 0) /* Address SCL Stall */ +#define LPI2C_SCFGR1_RXSTALL (1 << 1) /* RX SCL Stall */ +#define LPI2C_SCFGR1_TXSTALL (1 << 2) /* TX Data SCL Stall */ +#define LPI2C_SCFGR1_ACKSTALL (1 << 3) /* ACK SCL Stall */ + /* Bits 7-4 Reserved */ +#define LPI2C_SCFGR1_GCEN (1 << 8) /* General Call Enable */ +#define LPI2C_SCFGR1_SAEN (1 << 9) /* SMBus Alert Enable */ +#define LPI2C_SCFGR1_TXCFG (1 << 10) /* Transmit Flag Configuration */ +#define LPI2C_SCFGR1_RXCFG (1 << 11) /* Receive Data Configuration */ +#define LPI2C_SCFGR1_IFNACK (1 << 12) /* Ignore NACK */ +#define LPI2C_SCFGR1_HSMEN (1 << 13) /* High Speed Mode Enable */ + /* Bits 15-14 Reserved */ +#define LPI2C_SCFG1_ADDRCFG_SHIFT (16) +#define LPI2C_SCFG1_ADDRCFG_MASK (7 << LPI2C_SCFG1_ADDRCFG_SHIFT) /* Address Configuration Bit Mask */ +#define LPI2C_SCFG1_ADDRCFG(n) (((n) << LPI2C_SCFG1_ADDRCFG_SHIFT) & LPI2C_SCFG1_ADDRCFG_MASK) +# define LPI2C_SCFG1_ADDRCFG0 (0 << LPI2C_SCFG1_ADDRCFG_SHIFT) +# define LPI2C_SCFG1_ADDRCFG1 (2 << LPI2C_SCFG1_ADDRCFG_SHIFT) +# define LPI2C_SCFG1_ADDRCFG2 (2 << LPI2C_SCFG1_ADDRCFG_SHIFT) +# define LPI2C_SCFG1_ADDRCFG3 (3 << LPI2C_SCFG1_ADDRCFG_SHIFT) +# define LPI2C_SCFG1_ADDRCFG4 (4 << LPI2C_SCFG1_ADDRCFG_SHIFT) +# define LPI2C_SCFG1_ADDRCFG5 (5 << LPI2C_SCFG1_ADDRCFG_SHIFT) +# define LPI2C_SCFG1_ADDRCFG6 (6 << LPI2C_SCFG1_ADDRCFG_SHIFT) +# define LPI2C_SCFG1_ADDRCFG7 (7 << LPI2C_SCFG1_ADDRCFG_SHIFT) + /* Bits 31-19 Reserved */ + +/* LPI2C Slave Configuration Register 2 */ + +#define LPI2C_SCFG2_CLKHOLD_MASK (15 << 0) /* Clock Hold Time */ +#define LPI2C_SCFG2_CLKHOLD(n) ((n) & LPI2C_SCFG2_CLKHOLD_MASK) + /* Bits 7-4 Reserved */ +#define LPI2C_SCFG2_DATAVD_SHIFT (8) +#define LPI2C_SCFG2_DATAVD_MASK (0x3f << LPI2C_SCFG2_DATAVD_SHIFT) /* Data Valid Delay */ +#define LPI2C_SCFG2_DATAVD(n) (((n) << LPI2C_SCFG2_DATAVD_SHIFT) & LPI2C_SCFG2_DATAVD_MASK) + /* Bits 15-14 Reserved */ +#define LPI2C_SCFG2_FILTSCL_SHIFT (16) +#define LPI2C_SCFG2_FILTSCL_MASK (15 << LPI2C_SCFG2_FILTSCL_SHIFT) /* Glitch Filter SCL */ +#define LPI2C_SCFG2_FILTSCL_DISABLE (0 << LPI2C_SCFG2_FILTSCL_SHIFT) +#define LPI2C_SCFG2_FILTSCL_CYCLES(n) (((n) << LPI2C_SCFG2_FILTSCL_SHIFT) & LPI2C_SCFG2_FILTSCL_MASK) + /* Bits 23-20 Reserved */ +#define LPI2C_SCFG2_FILTSDA_SHIFT (24) +#define LPI2C_SCFG2_FILTSDA_MASK (15 << LPI2C_SCFG2_FILTSDA_SHIFT) /* Glitch Filter SDA */ +#define LPI2C_SCFG2_FILTSDA_DISABLE (0 << LPI2C_SCFG2_FILTSDA_SHIFT) +#define LPI2C_SCFG2_FILTSDA_CYCLES(n) (((n) << LPI2C_SCFG2_FILTSDA_SHIFT) & LPI2C_SCFG2_FILTSDA_MASK) + /* Bits 31-28 Reserved */ + +/* LPI2C Slave Address Match Register */ + + /* Bit 0 Reserved */ +#define LPI2C_SAMR_ADDR0_SHIFT (1) +#define LPI2C_SAMR_ADDR0_MASK (0x3ff << LPI2C_SAMR_ADDR0_SHIFT) /* Address 0 Value */ +#define LPI2C_SAMR_ADDR0(n) (((n) << LPI2C_SAMR_ADDR0_SHIFT) & LPI2C_SAMR_ADDR0_MASK) + /* Bits 16-11 Reserved */ +#define LPI2C_SAMR_ADDR1_SHIFT (17) +#define LPI2C_SAMR_ADDR1_MASK (0x3ff << LPI2C_SAMR_ADDR1_SHIFT) /* Address 1 Value */ +#define LPI2C_SAMR_ADDR1(n) (((n) << LPI2C_SAMR_ADDR1_SHIFT) & LPI2C_SAMR_ADDR1_MASK) + /* Bits 31-27 Reserved */ + +/* LPI2C Slave Address Status Register */ + +#define LPI2C_SASR_RADDR_MASK (0x7ff << 0) /* Received Address */ + +/* Bits 16-11 + * Reserved + */ + +#define LPI2C_SASR_ANV (1 << 14) /* Address Not Valid */ + /* Bits 31-15 Reserved */ + +/* LPI2C Slave Transmit ACK Register */ + +#define LPI2C_STAR_TXNACK (1 << 0) /* Transmit NACK */ + /* Bits 31-1 Reserved */ + +/* LPI2C Slave Transmit Data Register */ + +#define LPI2C_STDR_DATA_SHIFT (0) +#define LPI2C_STDR_DATA_MASK (0xff << LPI2C_STDR_DATA_SHIFT) /* Transmit Data */ +#define LPI2C_STDR_DATA(n) (((n) << LPI2C_STDR_DATA_SHIFT) & LPI2C_STDR_DATA_MASK) + /* Bits 31-8 Reserved */ + +/* LPI2C Slave Receive Data Register */ + +#define LPI2C_SRDR_DATA_SHIFT (0) +#define LPI2C_SRDR_DATA_MASK (0xff << LPI2C_SRDR_DATA_SHIFT) /* Receive Data */ +#define LPI2C_SRDR_DATA(n) (((n) << LPI2C_SRDR_DATA_SHIFT) & LPI2C_SRDR_DATA_MASK) + /* Bits 13-8 Reserved */ +#define LPI2C_STAR_SOF (1 << 14) /* RX Empty */ +#define LPI2C_STAR_RXEMPTY (1 << 15) /* Start Of Frame */ + /* Bits 31-16 Reserved */ + +#endif /* __ARCH_ARM64_SRC_IMX9_HARDWARE_IMX9_LPI2C_H_ */ diff --git a/arch/arm64/src/imx9/imx9_lpi2c.c b/arch/arm64/src/imx9/imx9_lpi2c.c new file mode 100644 index 0000000000..c1122b1ddc --- /dev/null +++ b/arch/arm64/src/imx9/imx9_lpi2c.c @@ -0,0 +1,2579 @@ +/**************************************************************************** + * arch/arm64/src/imx9/imx9_lpi2c.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "arm64_internal.h" +#include "imx9_ccm.h" +#include "imx9_clockconfig.h" +#include "imx9_gpio.h" +#include "imx9_iomuxc.h" +#include "imx9_lpi2c.h" + +#include "hardware/imx9_ccm.h" +#include "hardware/imx9_pinmux.h" +#include "hardware/imx9_lpi2c.h" + +#ifdef CONFIG_IMX9_LPI2C_DMA +# include "chip.h" +# include "imx9_edma.h" +# include "hardware/imx9_dmamux.h" +#endif + +/* At least one I2C peripheral must be enabled */ + +#ifdef CONFIG_IMX9_LPI2C + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* CONFIG_I2C_POLLED may be set so that I2C interrupts will not be used. + * Instead, CPU-intensive polling will be used. + */ + +/* Interrupt wait timeout in seconds and milliseconds */ + +#if !defined(CONFIG_IMX9_LPI2C_TIMEOSEC) && \ + !defined(CONFIG_IMX9_LPI2C_TIMEOMS) +# define CONFIG_IMX9_LPI2C_TIMEOSEC 0 +# define CONFIG_IMX9_LPI2C_TIMEOMS 500 /* Default is 500 milliseconds */ +#elif !defined(CONFIG_IMX9_LPI2C_TIMEOSEC) +# define CONFIG_IMX9_LPI2C_TIMEOSEC 0 /* User provided milliseconds */ +#elif !defined(CONFIG_IMX9_LPI2C_TIMEOMS) +# define CONFIG_IMX9_LPI2C_TIMEOMS 0 /* User provided seconds */ +#endif + +/* Interrupt wait time timeout in system timer ticks */ + +#ifndef CONFIG_IMX9_LPI2C_TIMEOTICKS +# define CONFIG_IMX9_LPI2C_TIMEOTICKS \ + (SEC2TICK(CONFIG_IMX9_LPI2C_TIMEOSEC) + \ + MSEC2TICK(CONFIG_IMX9_LPI2C_TIMEOMS)) +#endif + +#ifndef CONFIG_IMX9_LPI2C_DYNTIMEO_STARTSTOP +# define CONFIG_IMX9_LPI2C_DYNTIMEO_STARTSTOP \ + TICK2USEC(CONFIG_IMX9_LPI2C_TIMEOTICKS) +#endif + +/* Debug ********************************************************************/ + +/* I2C event trace logic. NOTE: trace uses the internal, non-standard, + * low-level debug interface syslog() but does not require that any other + * debug is enabled. + */ + +#ifndef CONFIG_I2C_TRACE +# define imx9_lpi2c_tracereset(p) +# define imx9_lpi2c_tracenew(p,s) +# define imx9_lpi2c_traceevent(p,e,a) +# define imx9_lpi2c_tracedump(p) +#endif + +#ifndef CONFIG_I2C_NTRACE +# define CONFIG_I2C_NTRACE 32 +#endif + +#ifdef CONFIG_I2C_SLAVE +# error I2C slave logic is not supported yet for IMX9 +#endif + +#define LPI2C_MASTER 1 +#define LPI2C_SLAVE 2 + +#define LPI2C_MSR_LIMITED_ERROR_MASK (LPI2C_MSR_ERROR_MASK & ~(LPI2C_MSR_FEF)) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Interrupt state */ + +enum imx9_intstate_e +{ + INTSTATE_IDLE = 0, /* No I2C activity */ + INTSTATE_WAITING, /* Waiting for completion of interrupt activity */ + INTSTATE_DONE, /* Interrupt activity complete */ +}; + +/* Trace events */ + +enum imx9_trace_e +{ + I2CEVENT_NONE = 0, /* No events have occurred with this status */ + I2CEVENT_SENDADDR, /* Start/Master bit set and address sent, param = msgc */ + I2CEVENT_SENDBYTE, /* Send byte, param = dcnt */ + I2CEVENT_RCVBYTE, /* Read more dta, param = dcnt */ + I2CEVENT_NOSTART, /* BTF on last byte with no restart, param = msgc */ + I2CEVENT_STARTRESTART, /* Last byte sent, re-starting, param = msgc */ + I2CEVENT_STOP, /* Last byte sten, send stop, param = 0 */ + I2CEVENT_ERROR /* Error occurred, param = 0 */ +}; + +/* Trace data */ + +struct imx9_trace_s +{ + uint32_t status; /* I2C 32-bit SR2|SR1 status */ + uint32_t count; /* Interrupt count when status change */ + enum imx9_intstate_e event; /* Last event that occurred with this status */ + uint32_t parm; /* Parameter associated with the event */ + clock_t time; /* First of event or first status */ +}; + +/* I2C Device hardware configuration */ + +struct imx9_lpi2c_config_s +{ + uint32_t base; /* LPI2C base address */ + uint8_t clk_root; /* LPI2C clock root */ + uint8_t clk_gate; /* LPI2C clock gate */ + uint16_t busy_idle; /* LPI2C Bus Idle Timeout */ + uint8_t filtscl; /* Glitch Filter for SCL pin */ + uint8_t filtsda; /* Glitch Filter for SDA pin */ + iomux_cfg_t scl_pin; /* Peripheral configuration for SCL as SCL */ + iomux_cfg_t sda_pin; /* Peripheral configuration for SDA as SDA */ +#if defined(CONFIG_I2C_RESET) + gpio_pinset_t reset_scl_pin; /* GPIO configuration for SCL as GPIO */ + gpio_pinset_t reset_sda_pin; /* GPIO configuration for SDA as GPIO */ +#endif + uint8_t mode; /* Master or Slave mode */ +#ifndef CONFIG_I2C_POLLED + uint32_t irq; /* Event IRQ */ +#endif +#ifdef CONFIG_IMX9_LPI2C_DMA + uint32_t dma_rxreqsrc; /* DMA mux rx source */ + uint32_t dma_txreqsrc; /* DMA mux tx source */ +#endif +}; + +/* I2C Device Private Data */ + +struct imx9_lpi2c_priv_s +{ + /* Standard I2C operations */ + + const struct i2c_ops_s *ops; + + /* Port configuration */ + + const struct imx9_lpi2c_config_s *config; + + int refs; /* Reference count */ + mutex_t lock; /* Mutual exclusion mutex */ +#ifndef CONFIG_I2C_POLLED + sem_t sem_isr; /* Interrupt wait semaphore */ +#endif + volatile uint8_t intstate; /* Interrupt handshake (see enum imx9_intstate_e) */ + + uint8_t msgc; /* Message count */ + struct i2c_msg_s *msgv; /* Message list */ + uint8_t *ptr; /* Current message buffer */ + uint32_t frequency; /* Current I2C frequency */ + int dcnt; /* Current message length */ + uint16_t flags; /* Current message flags */ + + /* I2C trace support */ + +#ifdef CONFIG_I2C_TRACE + int tndx; /* Trace array index */ + clock_t start_time; /* Time when the trace was started */ + + /* The actual trace data */ + + struct imx9_trace_s trace[CONFIG_I2C_NTRACE]; +#endif + + uint32_t status; /* End of transfer SR2|SR1 status */ + +#ifdef CONFIG_IMX9_LPI2C_DMA + DMACH_HANDLE rxdma; /* rx DMA handle */ + DMACH_HANDLE txdma; /* tx DMA handle */ + uint16_t cmnds[CONFIG_IMX9_LPI2C_DMA_MAXMSG]; /* Commands */ +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static inline uint32_t +imx9_lpi2c_getreg(struct imx9_lpi2c_priv_s *priv, uint16_t offset); +static inline void imx9_lpi2c_putreg(struct imx9_lpi2c_priv_s *priv, + uint16_t offset, uint32_t value); +static inline void imx9_lpi2c_modifyreg(struct imx9_lpi2c_priv_s *priv, + uint16_t offset, uint32_t clearbits, + uint32_t setbits); + +#ifdef CONFIG_IMX9_LPI2C_DYNTIMEO +static uint32_t imx9_lpi2c_toticks(int msgc, struct i2c_msg_s *msgs); +#endif /* CONFIG_IMX9_LPI2C_DYNTIMEO */ + +static inline int +imx9_lpi2c_sem_waitdone(struct imx9_lpi2c_priv_s *priv); + +#ifdef CONFIG_I2C_TRACE +static void imx9_lpi2c_tracereset(struct imx9_lpi2c_priv_s *priv); +static void imx9_lpi2c_tracenew(struct imx9_lpi2c_priv_s *priv, + uint32_t status); +static void imx9_lpi2c_traceevent(struct imx9_lpi2c_priv_s *priv, + enum imx9_trace_e event, uint32_t parm); +static void imx9_lpi2c_tracedump(struct imx9_lpi2c_priv_s *priv); +#endif /* CONFIG_I2C_TRACE */ + +static void imx9_lpi2c_setclock(struct imx9_lpi2c_priv_s *priv, + uint32_t frequency); +static inline void imx9_lpi2c_sendstart(struct imx9_lpi2c_priv_s *priv, + uint8_t address); +static inline void imx9_lpi2c_sendstop(struct imx9_lpi2c_priv_s *priv); +static inline uint32_t +imx9_lpi2c_getstatus(struct imx9_lpi2c_priv_s *priv); + +static int imx9_lpi2c_isr_process(struct imx9_lpi2c_priv_s *priv); + +#ifndef CONFIG_I2C_POLLED +static int imx9_lpi2c_isr(int irq, void *context, void *arg); +#endif /* !CONFIG_I2C_POLLED */ + +static void imx9_lpi2c_clock_enable(struct imx9_lpi2c_priv_s *priv); +static void imx9_lpi2c_clock_disable(struct imx9_lpi2c_priv_s *priv); +static int imx9_lpi2c_init(struct imx9_lpi2c_priv_s *priv); +static int imx9_lpi2c_deinit(struct imx9_lpi2c_priv_s *priv); +static int imx9_lpi2c_transfer(struct i2c_master_s *dev, + struct i2c_msg_s *msgs, int count); +#ifdef CONFIG_I2C_RESET +static int imx9_lpi2c_reset(struct i2c_master_s *dev); +#endif + +#ifdef CONFIG_IMX9_LPI2C_DMA +static void imx9_rxdma_callback(DMACH_HANDLE handle, void *arg, bool done, + int result); +static void imx9_txdma_callback(DMACH_HANDLE handle, void *arg, bool done, + int result); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Trace events strings */ + +#ifdef CONFIG_I2C_TRACE +static const char *g_trace_names[] = +{ + "NONE ", + "SENDADDR ", + "SENDBYTE ", + "RCVBYTE ", + "NOSTART ", + "START/RESTART ", + "STOP ", + "ERROR " +}; +#endif + +/* I2C interface */ + +static const struct i2c_ops_s imx9_lpi2c_ops = +{ + .transfer = imx9_lpi2c_transfer, +#ifdef CONFIG_I2C_RESET + .reset = imx9_lpi2c_reset, +#endif +}; + +/* I2C device structures */ + +#ifdef CONFIG_IMX9_LPI2C1 +static const struct imx9_lpi2c_config_s imx9_lpi2c1_config = +{ + .base = IMX9_LPI2C1_BASE, + .clk_root = CCM_CR_LPI2C1, + .clk_gate = CCM_LPCG_LPI2C1, + .busy_idle = CONFIG_IMX9_LPI2C1_BUSYIDLE, + .filtscl = CONFIG_IMX9_LPI2C1_FILTSCL, + .filtsda = CONFIG_IMX9_LPI2C1_FILTSDA, + .scl_pin = MUX_LPI2C1_SCL, + .sda_pin = MUX_LPI2C1_SDA, +#if defined(CONFIG_I2C_RESET) + .reset_scl_pin = GPIO_LPI2C1_SCL_RESET, + .reset_sda_pin = GPIO_LPI2C1_SDA_RESET, +#endif +#ifndef CONFIG_I2C_SLAVE + .mode = LPI2C_MASTER, +#else + .mode = LPI2C_SLAVE, +#endif +#ifndef CONFIG_I2C_POLLED + .irq = IMX9_IRQ_LPI2C1, +#endif +#ifdef CONFIG_IMX9_LPI2C1_DMA + .dma_rxreqsrc = DMA_REQUEST_MUXLPI2C1RX, + .dma_txreqsrc = DMA_REQUEST_MUXLPI2C1TX, +#endif +}; + +static struct imx9_lpi2c_priv_s imx9_lpi2c1_priv = +{ + .ops = &imx9_lpi2c_ops, + .config = &imx9_lpi2c1_config, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, +#ifndef CONFIG_I2C_POLLED + .sem_isr = SEM_INITIALIZER(0), +#endif + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .dcnt = 0, + .flags = 0, + .status = 0 +}; +#endif + +#ifdef CONFIG_IMX9_LPI2C2 +static const struct imx9_lpi2c_config_s imx9_lpi2c2_config = +{ + .base = IMX9_LPI2C2_BASE, + .clk_root = CCM_CR_LPI2C2, + .clk_gate = CCM_LPCG_LPI2C2, + .busy_idle = CONFIG_IMX9_LPI2C2_BUSYIDLE, + .filtscl = CONFIG_IMX9_LPI2C2_FILTSCL, + .filtsda = CONFIG_IMX9_LPI2C2_FILTSDA, + .scl_pin = MUX_LPI2C2_SCL, + .sda_pin = MUX_LPI2C2_SDA, +#if defined(CONFIG_I2C_RESET) + .reset_scl_pin = GPIO_LPI2C2_SCL_RESET, + .reset_sda_pin = GPIO_LPI2C2_SDA_RESET, +#endif +#ifndef CONFIG_I2C_SLAVE + .mode = LPI2C_MASTER, +#else + .mode = LPI2C_SLAVE, +#endif +#ifndef CONFIG_I2C_POLLED + .irq = IMX9_IRQ_LPI2C2, +#endif +#ifdef CONFIG_IMX9_LPI2C2_DMA + .dma_rxreqsrc = DMA_REQUEST_MUXLPI2C2RX, + .dma_txreqsrc = DMA_REQUEST_MUXLPI2C2TX, +#endif +}; + +static struct imx9_lpi2c_priv_s imx9_lpi2c2_priv = +{ + .ops = &imx9_lpi2c_ops, + .config = &imx9_lpi2c2_config, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, +#ifndef CONFIG_I2C_POLLED + .sem_isr = SEM_INITIALIZER(0), +#endif + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .dcnt = 0, + .flags = 0, + .status = 0 +}; +#endif + +#ifdef CONFIG_IMX9_LPI2C3 +static const struct imx9_lpi2c_config_s imx9_lpi2c3_config = +{ + .base = IMX9_LPI2C3_BASE, + .clk_root = CCM_CR_LPI2C3, + .clk_gate = CCM_LPCG_LPI2C3, + .busy_idle = CONFIG_IMX9_LPI2C3_BUSYIDLE, + .filtscl = CONFIG_IMX9_LPI2C3_FILTSCL, + .filtsda = CONFIG_IMX9_LPI2C3_FILTSDA, + .scl_pin = MUX_LPI2C3_SCL, + .sda_pin = MUX_LPI2C3_SDA, +#if defined(CONFIG_I2C_RESET) + .reset_scl_pin = GPIO_LPI2C3_SCL_RESET, + .reset_sda_pin = GPIO_LPI2C3_SDA_RESET, +#endif +#ifndef CONFIG_I2C_SLAVE + .mode = LPI2C_MASTER, +#else + .mode = LPI2C_SLAVE, +#endif +#ifndef CONFIG_I2C_POLLED + .irq = IMX9_IRQ_LPI2C3, +#endif +#ifdef CONFIG_IMX9_LPI2C3_DMA + .dma_rxreqsrc = DMA_REQUEST_MUXLPI2C3RX, + .dma_txreqsrc = DMA_REQUEST_MUXLPI2C3TX, +#endif +}; + +static struct imx9_lpi2c_priv_s imx9_lpi2c3_priv = +{ + .ops = &imx9_lpi2c_ops, + .config = &imx9_lpi2c3_config, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, +#ifndef CONFIG_I2C_POLLED + .sem_isr = SEM_INITIALIZER(0), +#endif + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .dcnt = 0, + .flags = 0, + .status = 0 +}; +#endif + +#ifdef CONFIG_IMX9_LPI2C4 +static const struct imx9_lpi2c_config_s imx9_lpi2c4_config = +{ + .base = IMX9_LPI2C4_BASE, + .clk_root = CCM_CR_LPI2C4, + .clk_gate = CCM_LPCG_LPI2C4, + .busy_idle = CONFIG_IMX9_LPI2C4_BUSYIDLE, + .filtscl = CONFIG_IMX9_LPI2C4_FILTSCL, + .filtsda = CONFIG_IMX9_LPI2C4_FILTSDA, + .scl_pin = MUX_LPI2C4_SCL, + .sda_pin = MUX_LPI2C4_SDA, +#if defined(CONFIG_I2C_RESET) + .reset_scl_pin = GPIO_LPI2C4_SCL_RESET, + .reset_sda_pin = GPIO_LPI2C4_SDA_RESET, +#endif +#ifndef CONFIG_I2C_SLAVE + .mode = LPI2C_MASTER, +#else + .mode = LPI2C_SLAVE, +#endif +#ifndef CONFIG_I2C_POLLED + .irq = IMX9_IRQ_LPI2C4, +#endif +#ifdef CONFIG_IMX9_LPI2C4_DMA + .dma_rxreqsrc = DMA_REQUEST_MUXLPI2C4RX, + .dma_txreqsrc = DMA_REQUEST_MUXLPI2C4TX, +#endif +}; + +static struct imx9_lpi2c_priv_s imx9_lpi2c4_priv = +{ + .ops = &imx9_lpi2c_ops, + .config = &imx9_lpi2c4_config, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, +#ifndef CONFIG_I2C_POLLED + .sem_isr = SEM_INITIALIZER(0), +#endif + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .dcnt = 0, + .flags = 0, + .status = 0 +}; +#endif + +#ifdef CONFIG_IMX9_LPI2C5 +static const struct imx9_lpi2c_config_s imx9_lpi2c5_config = +{ + .base = IMX9_LPI2C5_BASE, + .clk_root = CCM_CR_LPI2C5, + .clk_gate = CCM_LPCG_LPI2C5, + .busy_idle = CONFIG_IMX9_LPI2C5_BUSYIDLE, + .filtscl = CONFIG_IMX9_LPI2C5_FILTSCL, + .filtsda = CONFIG_IMX9_LPI2C5_FILTSDA, + .scl_pin = MUX_LPI2C5_SCL, + .sda_pin = MUX_LPI2C5_SDA, +#if defined(CONFIG_I2C_RESET) + .reset_scl_pin = GPIO_LPI2C5_SCL_RESET, + .reset_sda_pin = GPIO_LPI2C5_SDA_RESET, +#endif +#ifndef CONFIG_I2C_SLAVE + .mode = LPI2C_MASTER, +#else + .mode = LPI2C_SLAVE, +#endif +#ifndef CONFIG_I2C_POLLED + .irq = IMX9_IRQ_LPI2C5, +#endif +#ifdef CONFIG_IMX9_LPI2C5_DMA + .dma_rxreqsrc = DMA_REQUEST_MUXLPI2C5RX, + .dma_txreqsrc = DMA_REQUEST_MUXLPI2C5TX, +#endif +}; + +static struct imx9_lpi2c_priv_s imx9_lpi2c5_priv = +{ + .ops = &imx9_lpi2c_ops, + .config = &imx9_lpi2c5_config, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, +#ifndef CONFIG_I2C_POLLED + .sem_isr = SEM_INITIALIZER(0), +#endif + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .dcnt = 0, + .flags = 0, + .status = 0 +}; +#endif + +#ifdef CONFIG_IMX9_LPI2C6 +static const struct imx9_lpi2c_config_s imx9_lpi2c6_config = +{ + .base = IMX9_LPI2C6_BASE, + .clk_root = CCM_CR_LPI2C6, + .clk_gate = CCM_LPCG_LPI2C6, + .busy_idle = CONFIG_IMX9_LPI2C6_BUSYIDLE, + .filtscl = CONFIG_IMX9_LPI2C6_FILTSCL, + .filtsda = CONFIG_IMX9_LPI2C6_FILTSDA, + .scl_pin = MUX_LPI2C6_SCL, + .sda_pin = MUX_LPI2C6_SDA, +#if defined(CONFIG_I2C_RESET) + .reset_scl_pin = GPIO_LPI2C6_SCL_RESET, + .reset_sda_pin = GPIO_LPI2C6_SDA_RESET, +#endif +#ifndef CONFIG_I2C_SLAVE + .mode = LPI2C_MASTER, +#else + .mode = LPI2C_SLAVE, +#endif +#ifndef CONFIG_I2C_POLLED + .irq = IMX9_IRQ_LPI2C6, +#endif +#ifdef CONFIG_IMX9_LPI2C6_DMA + .dma_rxreqsrc = DMA_REQUEST_MUXLPI2C6RX, + .dma_txreqsrc = DMA_REQUEST_MUXLPI2C6TX, +#endif +}; + +static struct imx9_lpi2c_priv_s imx9_lpi2c6_priv = +{ + .ops = &imx9_lpi2c_ops, + .config = &imx9_lpi2c6_config, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, +#ifndef CONFIG_I2C_POLLED + .sem_isr = SEM_INITIALIZER(0), +#endif + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .dcnt = 0, + .flags = 0, + .status = 0 +}; +#endif + +#ifdef CONFIG_IMX9_LPI2C7 +static const struct imx9_lpi2c_config_s imx9_lpi2c7_config = +{ + .base = IMX9_LPI2C7_BASE, + .clk_root = CCM_CR_LPI2C7, + .clk_gate = CCM_LPCG_LPI2C7, + .busy_idle = CONFIG_IMX9_LPI2C7_BUSYIDLE, + .filtscl = CONFIG_IMX9_LPI2C7_FILTSCL, + .filtsda = CONFIG_IMX9_LPI2C7_FILTSDA, + .scl_pin = MUX_LPI2C7_SCL, + .sda_pin = MUX_LPI2C7_SDA, +#if defined(CONFIG_I2C_RESET) + .reset_scl_pin = GPIO_LPI2C7_SCL_RESET, + .reset_sda_pin = GPIO_LPI2C7_SDA_RESET, +#endif +#ifndef CONFIG_I2C_SLAVE + .mode = LPI2C_MASTER, +#else + .mode = LPI2C_SLAVE, +#endif +#ifndef CONFIG_I2C_POLLED + .irq = IMX9_IRQ_LPI2C7, +#endif +#ifdef CONFIG_IMX9_LPI2C7_DMA + .dma_rxreqsrc = DMA_REQUEST_MUXLPI2C7RX, + .dma_txreqsrc = DMA_REQUEST_MUXLPI2C7TX, +#endif +}; + +static struct imx9_lpi2c_priv_s imx9_lpi2c7_priv = +{ + .ops = &imx9_lpi2c_ops, + .config = &imx9_lpi2c7_config, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, +#ifndef CONFIG_I2C_POLLED + .sem_isr = SEM_INITIALIZER(0), +#endif + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .dcnt = 0, + .flags = 0, + .status = 0 +}; +#endif + +#ifdef CONFIG_IMX9_LPI2C8 +static const struct imx9_lpi2c_config_s imx9_lpi2c8_config = +{ + .base = IMX9_LPI2C8_BASE, + .clk_root = CCM_CR_LPI2C8, + .clk_gate = CCM_LPCG_LPI2C8, + .busy_idle = CONFIG_IMX9_LPI2C8_BUSYIDLE, + .filtscl = CONFIG_IMX9_LPI2C8_FILTSCL, + .filtsda = CONFIG_IMX9_LPI2C8_FILTSDA, + .scl_pin = MUX_LPI2C8_SCL, + .sda_pin = MUX_LPI2C8_SDA, +#if defined(CONFIG_I2C_RESET) + .reset_scl_pin = GPIO_LPI2C8_SCL_RESET, + .reset_sda_pin = GPIO_LPI2C8_SDA_RESET, +#endif +#ifndef CONFIG_I2C_SLAVE + .mode = LPI2C_MASTER, +#else + .mode = LPI2C_SLAVE, +#endif +#ifndef CONFIG_I2C_POLLED + .irq = IMX9_IRQ_LPI2C8, +#endif +#ifdef CONFIG_IMX9_LPI2C8_DMA + .dma_rxreqsrc = DMA_REQUEST_MUXLPI2C8RX, + .dma_txreqsrc = DMA_REQUEST_MUXLPI2C8TX, +#endif +}; + +static struct imx9_lpi2c_priv_s imx9_lpi2c8_priv = +{ + .ops = &imx9_lpi2c_ops, + .config = &imx9_lpi2c8_config, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, +#ifndef CONFIG_I2C_POLLED + .sem_isr = SEM_INITIALIZER(0), +#endif + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .dcnt = 0, + .flags = 0, + .status = 0 +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_lpi2c_getreg + * + * Description: + * Get a 32-bit register value by offset + * + ****************************************************************************/ + +static inline uint32_t +imx9_lpi2c_getreg(struct imx9_lpi2c_priv_s *priv, uint16_t offset) +{ + return getreg32(priv->config->base + offset); +} + +/**************************************************************************** + * Name: imx9_lpi2c_putreg + * + * Description: + * Put a 32-bit register value by offset + * + ****************************************************************************/ + +static inline void imx9_lpi2c_putreg(struct imx9_lpi2c_priv_s *priv, + uint16_t offset, uint32_t value) +{ + putreg32(value, priv->config->base + offset); +} + +/**************************************************************************** + * Name: imx9_lpi2c_modifyreg + * + * Description: + * Modify a 32-bit register value by offset + * + ****************************************************************************/ + +static inline void imx9_lpi2c_modifyreg(struct imx9_lpi2c_priv_s *priv, + uint16_t offset, uint32_t clearbits, + uint32_t setbits) +{ + modifyreg32(priv->config->base + offset, clearbits, setbits); +} + +/**************************************************************************** + * Name: imx9_lpi2c_toticks + * + * Description: + * Return a micro-second delay based on the number of bytes left to be + * processed. + * + ****************************************************************************/ + +#ifdef CONFIG_IMX9_LPI2C_DYNTIMEO +static uint32_t imx9_lpi2c_toticks(int msgc, struct i2c_msg_s *msgs) +{ + int i; + size_t bytecount = 0; + uint32_t tick = 0; + + /* Count the number of bytes left to process */ + + for (i = 0; i < msgc; i++) + { + bytecount += msgs[i].length; + } + + /* Then return a number of microseconds based on a user provided scaling + * factor. + */ + + tick = USEC2TICK(CONFIG_IMX9_LPI2C_DYNTIMEO_USECPERBYTE * bytecount); + if (tick == 0) + { + tick = 1; + } + + return tick; +} +#endif + +/**************************************************************************** + * Name: imx9_lpi2c_sem_waitdone + * + * Description: + * Wait for a transfer to complete + * + ****************************************************************************/ + +#ifndef CONFIG_I2C_POLLED +static inline int +imx9_lpi2c_sem_waitdone(struct imx9_lpi2c_priv_s *priv) +{ + irqstate_t flags; + uint32_t regval; + int ret; + + flags = enter_critical_section(); + +#ifdef CONFIG_IMX9_LPI2C_DMA + if (priv->rxdma == NULL && priv->txdma == NULL) + { +#endif + /* Clear the TX and RX FIFOs */ + + imx9_lpi2c_modifyreg(priv, IMX9_LPI2C_MCR_OFFSET, 0, + LPI2C_MCR_RTF | LPI2C_MCR_RRF); + + /* Enable Interrupts when master mode */ + + if (priv->config->mode == LPI2C_MASTER) + { + if ((priv->flags & I2C_M_READ) != 0) + { + regval = LPI2C_MIER_TDIE | LPI2C_MIER_RDIE | + LPI2C_MIER_NDIE | LPI2C_MIER_ALIE | + LPI2C_MIER_SDIE; + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MIER_OFFSET, regval); + } + else + { + regval = LPI2C_MIER_TDIE | LPI2C_MIER_NDIE | + LPI2C_MIER_ALIE | LPI2C_MIER_SDIE; + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MIER_OFFSET, regval); + } + } + +#ifdef CONFIG_I2C_SLAVE + /* Enable Interrupts when slave mode */ + + else + { + /* REVISIT: Slave mode has not been implemented */ + } +#endif + +#ifdef CONFIG_IMX9_LPI2C_DMA + } +#endif + + do + { + /* Wait until either the transfer is complete or the timeout expires */ + +#ifdef CONFIG_IMX9_LPI2C_DYNTIMEO + ret = nxsem_tickwait_uninterruptible(&priv->sem_isr, + imx9_lpi2c_toticks(priv->msgc, priv->msgv)); +#else + ret = nxsem_tickwait_uninterruptible(&priv->sem_isr, + CONFIG_IMX9_LPI2C_TIMEOTICKS); +#endif + if (ret < 0) + { + /* Break out of the loop on irrecoverable errors. This would + * include timeouts and mystery errors reported by + * nxsem_tickwait_uninterruptible. + */ + + break; + } + } + + /* Loop until the interrupt level transfer is complete. */ + + while (priv->intstate != INTSTATE_DONE); + + /* Set the interrupt state back to IDLE */ + + priv->intstate = INTSTATE_IDLE; + + /* Disable I2C interrupts */ + + if (priv->config->mode == LPI2C_MASTER) + { + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MIER_OFFSET, 0); + } + else + { + imx9_lpi2c_putreg(priv, IMX9_LPI2C_SIER_OFFSET, 0); + } + + leave_critical_section(flags); + return ret; +} +#else +static inline int +imx9_lpi2c_sem_waitdone(struct imx9_lpi2c_priv_s *priv) +{ + clock_t timeout; + clock_t start; + clock_t elapsed; + int ret; + + /* Get the timeout value */ + +#ifdef CONFIG_IMX9_LPI2C_DYNTIMEO + timeout = imx9_lpi2c_toticks(priv->msgc, priv->msgv); +#else + timeout = CONFIG_IMX9_LPI2C_TIMEOTICKS; +#endif + start = clock_systime_ticks(); + + do + { + /* Calculate the elapsed time */ + + elapsed = clock_systime_ticks() - start; + + /* Poll by simply calling the timer interrupt handler until it + * reports that it is done. + */ + + imx9_lpi2c_isr_process(priv); + } + + /* Loop until the transfer is complete. */ + + while (priv->intstate != INTSTATE_DONE && elapsed < timeout); + + i2cinfo("intstate: %d elapsed: %ld threshold: %ld status: %08x\n", + priv->intstate, (long)elapsed, (long)timeout, priv->status); + + /* Set the interrupt state back to IDLE */ + + ret = priv->intstate == INTSTATE_DONE ? OK : -ETIMEDOUT; + priv->intstate = INTSTATE_IDLE; + return ret; +} +#endif + +/**************************************************************************** + * Name: imx9_lpi2c_sem_waitstop + * + * Description: + * Wait for a STOP to complete + * + ****************************************************************************/ + +static inline void +imx9_lpi2c_sem_waitstop(struct imx9_lpi2c_priv_s *priv) +{ + clock_t start; + clock_t elapsed; + clock_t timeout; + uint32_t regval; + + /* Select a timeout */ + +#ifdef CONFIG_IMX9_LPI2C_DYNTIMEO + timeout = USEC2TICK(CONFIG_IMX9_LPI2C_DYNTIMEO_STARTSTOP); +#else + timeout = CONFIG_IMX9_LPI2C_TIMEOTICKS; +#endif + + /* Wait as stop might still be in progress; but stop might also + * be set because of a timeout error: "The [STOP] bit is set and + * cleared by software, cleared by hardware when a Stop condition is + * detected, set by hardware when a timeout error is detected." + */ + + start = clock_systime_ticks(); + do + { + /* Calculate the elapsed time */ + + elapsed = clock_systime_ticks() - start; + + /* Check for STOP condition */ + + if (priv->config->mode == LPI2C_MASTER) + { + regval = imx9_lpi2c_getreg(priv, IMX9_LPI2C_MSR_OFFSET); + if ((regval & LPI2C_MSR_SDF) == LPI2C_MSR_SDF) + { + return; + } + } + + /* Enable Interrupts when slave mode */ + + else + { + regval = imx9_lpi2c_getreg(priv, IMX9_LPI2C_SSR_OFFSET); + if ((regval & LPI2C_SSR_SDF) == LPI2C_SSR_SDF) + { + return; + } + } + + /* Check for NACK error */ + + if (priv->config->mode == LPI2C_MASTER) + { + regval = imx9_lpi2c_getreg(priv, IMX9_LPI2C_MSR_OFFSET); + if ((regval & LPI2C_MSR_NDF) == LPI2C_MSR_NDF) + { + return; + } + } + +#ifdef CONFIG_I2C_SLAVE + /* Enable Interrupts when slave mode */ + + else + { + /* REVISIT: Slave mode has not been implemented */ + } +#endif + } + + /* Loop until the stop is complete or a timeout occurs. */ + + while (elapsed < timeout); + + /* If we get here then a timeout occurred with the STOP condition + * still pending. + */ + + i2cinfo("Timeout with Status Register: %" PRIx32 "\n", regval); +} + +/**************************************************************************** + * Name: imx9_rxdma_callback + * + * Description: + * This function performs the next I2C operation + * + ****************************************************************************/ + +#ifdef CONFIG_IMX9_LPI2C_DMA +static void imx9_rxdma_callback(DMACH_HANDLE handle, void *arg, bool done, + int result) +{ + struct imx9_lpi2c_priv_s *priv = (struct imx9_lpi2c_priv_s *)arg; + + imx9_lpi2c_modifyreg(priv, IMX9_LPI2C_MIER_OFFSET, 0, + LPI2C_MIER_SDIE); + + if (result == OK) + { + if ((priv->flags & I2C_M_NOSTOP) == 0) + { + imx9_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); + imx9_lpi2c_sendstop(priv); + } + } + else + { + uint32_t status = imx9_lpi2c_getstatus(priv); + + if ((status & LPI2C_MSR_ERROR_MASK) != 0) + { + i2cerr("ERROR: MSR: status: 0x0%" PRIx32 "\n", status); + + imx9_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0); + } + } +} +#endif + +/**************************************************************************** + * Name: imx9_txdma_callback + * + * Description: + * This function performs the next I2C operation + * + ****************************************************************************/ +#ifdef CONFIG_IMX9_LPI2C_DMA +static void imx9_txdma_callback(DMACH_HANDLE handle, void *arg, bool done, + int result) +{ + struct imx9_lpi2c_priv_s *priv = (struct imx9_lpi2c_priv_s *)arg; + + imx9_lpi2c_modifyreg(priv, IMX9_LPI2C_MIER_OFFSET, 0, + LPI2C_MIER_SDIE); + + if (result == OK) + { + if ((priv->flags & I2C_M_NOSTOP) == 0) + { + imx9_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); + imx9_lpi2c_sendstop(priv); + } + } + else + { + uint32_t status = imx9_lpi2c_getstatus(priv); + + if ((status & LPI2C_MSR_ERROR_MASK) != 0) + { + i2cerr("ERROR: MSR: status: 0x0%" PRIx32 "\n", status); + + imx9_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0); + } + } +} +#endif + +/**************************************************************************** + * Name: imx9_lpi2c_trace* + * + * Description: + * I2C trace instrumentation + * + ****************************************************************************/ + +#ifdef CONFIG_I2C_TRACE +static void imx9_lpi2c_traceclear(struct imx9_lpi2c_priv_s *priv) +{ + struct imx9_trace_s *trace = &priv->trace[priv->tndx]; + + trace->status = 0; /* I2C 32-bit SR2|SR1 status */ + trace->count = 0; /* Interrupt count when status change */ + trace->event = I2CEVENT_NONE; /* Last event that occurred with this status */ + trace->parm = 0; /* Parameter associated with the event */ + trace->time = 0; /* Time of first status or event */ +} + +static void imx9_lpi2c_tracereset(struct imx9_lpi2c_priv_s *priv) +{ + /* Reset the trace info for a new data collection */ + + priv->tndx = 0; + priv->start_time = clock_systime_ticks(); + imx9_lpi2c_traceclear(priv); +} + +static void imx9_lpi2c_tracenew(struct imx9_lpi2c_priv_s *priv, + uint32_t status) +{ + struct imx9_trace_s *trace = &priv->trace[priv->tndx]; + + /* Is the current entry uninitialized? Has the status changed? */ + + if (trace->count == 0 || status != trace->status) + { + /* Yes.. Was it the status changed? */ + + if (trace->count != 0) + { + /* Yes.. bump up the trace index (unless out of trace entries) */ + + if (priv->tndx >= (CONFIG_I2C_NTRACE - 1)) + { + i2cerr("ERROR: Trace table overflow\n"); + return; + } + + priv->tndx++; + trace = &priv->trace[priv->tndx]; + } + + /* Initialize the new trace entry */ + + imx9_lpi2c_traceclear(priv); + trace->status = status; + trace->count = 1; + trace->time = clock_systime_ticks(); + } + else + { + /* Just increment the count of times that we have seen this status */ + + trace->count++; + } +} + +static void imx9_lpi2c_traceevent(struct imx9_lpi2c_priv_s *priv, + enum imx9_trace_e event, uint32_t parm) +{ + struct imx9_trace_s *trace; + + if (event != I2CEVENT_NONE) + { + trace = &priv->trace[priv->tndx]; + + /* Initialize the new trace entry */ + + trace->event = event; + trace->parm = parm; + + /* Bump up the trace index (unless we are out of trace entries) */ + + if (priv->tndx >= (CONFIG_I2C_NTRACE - 1)) + { + i2cerr("ERROR: Trace table overflow\n"); + return; + } + + priv->tndx++; + imx9_lpi2c_traceclear(priv); + } +} + +static void imx9_lpi2c_tracedump(struct imx9_lpi2c_priv_s *priv) +{ + struct imx9_trace_s *trace; + int i; + + syslog(LOG_DEBUG, "Elapsed time: %ld\n", + (long)(clock_systime_ticks() - priv->start_time)); + + for (i = 0; i < priv->tndx; i++) + { + trace = &priv->trace[i]; + syslog(LOG_DEBUG, + "%2d. STATUS: %08x COUNT: %3d EVENT: %s(%2d) PARM: %08x " + "TIME: %d\n", + i + 1, trace->status, trace->count, + g_trace_names[trace->event], + trace->event, trace->parm, trace->time - priv->start_time); + } +} +#endif /* CONFIG_I2C_TRACE */ + +/**************************************************************************** + * Name: imx9_lpi2c_setclock + * + * Description: + * Set the I2C clock + * + ****************************************************************************/ + +static void imx9_lpi2c_setclock(struct imx9_lpi2c_priv_s *priv, + uint32_t frequency) +{ + uint32_t src_freq = 0; + uint32_t regval; + uint32_t men; + uint32_t prescale = 0; + uint32_t best_prescale = 0; + uint32_t best_clk_hi = 0; + uint32_t abs_error = 0; + uint32_t best_error = 0xffffffff; + uint32_t clk_hi_cycle; + uint32_t computed_rate; + uint32_t count; + + /* Has the I2C bus frequency changed? */ + + if (priv->config->mode == LPI2C_MASTER) + { + if (frequency != priv->frequency) + { + /* Disable the selected LPI2C peripheral to configure the new + * clock if it is enabled. + */ + + men = imx9_lpi2c_getreg(priv, IMX9_LPI2C_MCR_OFFSET) & + LPI2C_MCR_MEN; + if (men) + { + imx9_lpi2c_modifyreg(priv, IMX9_LPI2C_MCR_OFFSET, + LPI2C_MCR_MEN, 0); + } + + /* Get the LPI2C clock source frequency */ + + imx9_get_rootclock(priv->config->clk_root, &src_freq); + + /* LPI2C output frequency = (Source Clock (Hz)/ 2^prescale) / + * (CLKLO + 1 + CLKHI + 1 + ROUNDDOWN((2 + FILTSCL) / 2^prescale) + * + * Assume CLKLO = 2 * CLKHI, SETHOLD = CLKHI, DATAVD = CLKHI / 2 + */ + + for (prescale = 1; + (prescale <= 128) && (best_error != 0); + prescale *= 2) + { + for (clk_hi_cycle = 1; clk_hi_cycle < 32; clk_hi_cycle++) + { + if (clk_hi_cycle == 1) + { + computed_rate = (src_freq / prescale) / + (6 + (2 / prescale)); + } + else + { + computed_rate = (src_freq / prescale) / + ((3 * clk_hi_cycle + 2) + + (2 / prescale)); + } + + if (frequency > computed_rate) + { + abs_error = frequency - computed_rate; + } + else + { + abs_error = computed_rate - frequency; + } + + if (abs_error < best_error) + { + best_prescale = prescale; + best_clk_hi = clk_hi_cycle; + best_error = abs_error; + + if (abs_error == 0) + { + break; + } + } + } + } + + regval = LPI2C_MCCR0_CLKHI(best_clk_hi); + + if (best_clk_hi < 2) + { + regval |= LPI2C_MCCR0_CLKLO(3) | LPI2C_MCCR0_SETHOLD(2) | + LPI2C_MCCR0_DATAVD(1); + } + else + { + regval |= LPI2C_MCCR0_CLKLO(2 * best_clk_hi) | + LPI2C_MCCR0_SETHOLD(best_clk_hi) | + LPI2C_MCCR0_DATAVD(best_clk_hi / 2); + } + + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MCCR0_OFFSET, regval); + + for (count = 0; count < 8; count++) + { + if (best_prescale == (1 << count)) + { + best_prescale = count; + break; + } + } + + imx9_lpi2c_modifyreg(priv, IMX9_LPI2C_MCFGR1_OFFSET, + LPI2C_MCFGR1_PRESCALE_MASK, + LPI2C_MCFGR1_PRESCALE(best_prescale)); + + /* Re-enable LPI2C if it was enabled previously */ + + if (men) + { + imx9_lpi2c_modifyreg(priv, IMX9_LPI2C_MCR_OFFSET, 0, + LPI2C_MCR_MEN); + } + + /* Save the new LPI2C frequency */ + + priv->frequency = frequency; + } + } +} + +/**************************************************************************** + * Name: imx9_lpi2c_sendstart + * + * Description: + * Send the START conditions/force Master mode + * + ****************************************************************************/ + +static inline void imx9_lpi2c_sendstart(struct imx9_lpi2c_priv_s *priv, + uint8_t address) +{ + uint32_t txcount = 0; + uint32_t status = 0; + uint8_t addr; + + /* Generate START condition and send the address */ + + /* Disable AUTOSTOP and turn NAK Ignore off */ + + imx9_lpi2c_modifyreg(priv, IMX9_LPI2C_MCFGR1_OFFSET, + LPI2C_MCFGR1_IGNACK | LPI2C_MCFGR1_AUTOSTOP, 0); + + do + { + txcount = (imx9_lpi2c_getreg(priv, IMX9_LPI2C_MFSR_OFFSET) & + LPI2C_MFSR_TXCOUNT_MASK) >> LPI2C_MFSR_TXCOUNT_SHIFT; + txcount = 4 - txcount; + + status = imx9_lpi2c_getreg(priv, IMX9_LPI2C_MSR_OFFSET); + + if (status & LPI2C_MSR_ERROR_MASK) + { + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MSR_OFFSET, + status & LPI2C_MSR_ERROR_MASK); + } + } + while (txcount == 0); + + if ((priv->flags & I2C_M_READ) != 0) + { + addr = I2C_READADDR8(address); + } + else + { + addr = I2C_WRITEADDR8(address); + } + + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MTDR_OFFSET, + (LPI2C_MTDR_CMD_START | LPI2C_MTDR_DATA(addr))); +} + +/**************************************************************************** + * Name: imx9_lpi2c_sendstop + * + * Description: + * Send the STOP conditions + * + ****************************************************************************/ + +static inline void imx9_lpi2c_sendstop(struct imx9_lpi2c_priv_s *priv) +{ + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MTDR_OFFSET, LPI2C_MTDR_CMD_STOP); +} + +/**************************************************************************** + * Name: imx9_lpi2c_getstatus + * + * Description: + * Get 32-bit status + * + ****************************************************************************/ + +static inline uint32_t +imx9_lpi2c_getstatus(struct imx9_lpi2c_priv_s *priv) +{ + return imx9_lpi2c_getreg(priv, IMX9_LPI2C_MSR_OFFSET); +} + +/**************************************************************************** + * Name: imx9_lpi2c_getenabledints + * + * Description: + * Get 32-bit status + * + ****************************************************************************/ + +static inline uint32_t +imx9_lpi2c_getenabledints(struct imx9_lpi2c_priv_s *priv) +{ + return imx9_lpi2c_getreg(priv, IMX9_LPI2C_MIER_OFFSET); +} + +/**************************************************************************** + * Name: imx9_lpi2c_isr_process + * + * Description: + * Common Interrupt Service Routine + * + ****************************************************************************/ + +static int imx9_lpi2c_isr_process(struct imx9_lpi2c_priv_s *priv) +{ + uint32_t status = imx9_lpi2c_getstatus(priv); + +#ifdef CONFIG_IMX9_LPI2C_DMA + uint32_t current_status = status; + + if (priv->rxdma != NULL || priv->txdma != NULL) + { + /* Condition the status with only the enabled interrupts */ + + status &= imx9_lpi2c_getenabledints(priv); + + /* Is there an Error condition */ + + if (current_status & LPI2C_MSR_LIMITED_ERROR_MASK) + { + imx9_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0); + + /* Return the full error status */ + + priv->status = current_status; + } + + /* End of packet or Stop */ + + if ((status & (LPI2C_MSR_SDF | LPI2C_MSR_EPF)) != 0) + { + imx9_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); + + /* Acknowledge End of packet or Stop */ + + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MSR_OFFSET, status & + (LPI2C_MSR_SDF | + LPI2C_MSR_EPF)); + + /* Mark that this transaction stopped */ + + priv->msgv = NULL; + priv->msgc = 0; + priv->dcnt = -1; + + if (priv->intstate == INTSTATE_WAITING) + { + /* inform the thread that transfer is complete + * and wake it up + */ + + imx9_dmach_stop(priv->txdma); + imx9_dmach_stop(priv->rxdma); + + priv->intstate = INTSTATE_DONE; + nxsem_post(&priv->sem_isr); + } + } + + /* Clear the error */ + + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MSR_OFFSET, + (current_status & (LPI2C_MSR_NDF | + LPI2C_MSR_ALF | + LPI2C_MSR_FEF))); + return OK; + } + +#endif + /* Check for new trace setup */ + + imx9_lpi2c_tracenew(priv, status); + + if ((status & LPI2C_MSR_LIMITED_ERROR_MASK) == 0) + { + /* Check if there is more bytes to send */ + + if (((priv->flags & I2C_M_READ) == 0) && + (status & LPI2C_MSR_TDF) != 0) + { + if (priv->dcnt > 0) + { + imx9_lpi2c_traceevent(priv, I2CEVENT_SENDBYTE, priv->dcnt); + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MTDR_OFFSET, + LPI2C_MTDR_CMD_TXD | + LPI2C_MTDR_DATA(*priv->ptr++)); + priv->dcnt--; + + /* Last byte of last message? */ + + if ((priv->msgc <= 0) && (priv->dcnt == 0)) + { + if ((priv->flags & I2C_M_NOSTOP) == 0) + { + imx9_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); + + /* Do this once */ + + priv->flags |= I2C_M_NOSTOP; + imx9_lpi2c_sendstop(priv); + } + } + } + } + + /* Check if there is more bytes to read */ + + else if (((priv->flags & I2C_M_READ) != 0) && + (status & LPI2C_MSR_RDF) != 0) + { + /* Read a byte, if dcnt goes < 0, read dummy bytes to ack ISRs */ + + if (priv->dcnt > 0) + { + imx9_lpi2c_traceevent(priv, I2CEVENT_RCVBYTE, priv->dcnt); + + /* No interrupts or context switches should occur in the + * following sequence. Otherwise, additional bytes may be + * sent by the device. + */ + + #ifdef CONFIG_I2C_POLLED + irqstate_t flags = enter_critical_section(); + #endif + + /* Receive a byte */ + + *priv->ptr++ = imx9_lpi2c_getreg(priv, + IMX9_LPI2C_MRDR_OFFSET) & + LPI2C_MRDR_DATA_MASK; + priv->dcnt--; + + #ifdef CONFIG_I2C_POLLED + leave_critical_section(flags); + #endif + /* Last byte of last message? */ + + if ((priv->msgc <= 0) && (priv->dcnt == 0)) + { + if ((priv->flags & I2C_M_NOSTOP) == 0) + { + imx9_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); + + /* Do this once */ + + priv->flags |= I2C_M_NOSTOP; + imx9_lpi2c_sendstop(priv); + } + } + } + else + { + /* Read and discard data */ + + imx9_lpi2c_getreg(priv, IMX9_LPI2C_MRDR_OFFSET); + } + } + + /* Start the first or next message */ + + if (priv->dcnt <= 0 && (status & (LPI2C_MSR_EPF | LPI2C_MSR_SDF)) == 0) + { + if (priv->msgc > 0 && priv->msgv != NULL) + { + priv->ptr = priv->msgv->buffer; + priv->dcnt = priv->msgv->length; + priv->flags = priv->msgv->flags; + + if ((priv->flags & I2C_M_NOSTART) == 0) + { + imx9_lpi2c_traceevent(priv, I2CEVENT_STARTRESTART, + priv->msgc); + + /* Do this once */ + + priv->flags |= I2C_M_NOSTART; + + imx9_lpi2c_sendstart(priv, priv->msgv->addr); + } + else + { + imx9_lpi2c_traceevent(priv, I2CEVENT_NOSTART, priv->msgc); + } + + priv->msgv++; + priv->msgc--; + + if ((priv->flags & I2C_M_READ) != 0) + { +#ifndef CONFIG_I2C_POLLED + /* Stop TX interrupt */ + + imx9_lpi2c_modifyreg(priv, IMX9_LPI2C_MIER_OFFSET, + LPI2C_MIER_TDIE, LPI2C_MIER_RDIE); +#endif + /* Set LPI2C in read mode */ + + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MTDR_OFFSET, + LPI2C_MTDR_CMD_RXD | + LPI2C_MTDR_DATA((priv->dcnt - 1))); + } + else + { + /* Send the first byte from tx buffer */ + + imx9_lpi2c_traceevent(priv, I2CEVENT_SENDBYTE, + priv->dcnt); + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MTDR_OFFSET, + LPI2C_MTDR_CMD_TXD | + LPI2C_MTDR_DATA(*priv->ptr++)); + priv->dcnt--; + + /* Last byte of last message? */ + + if ((priv->msgc <= 0) && (priv->dcnt == 0)) + { + if ((priv->flags & I2C_M_NOSTOP) == 0) + { + imx9_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); + + /* Do this once */ + + priv->flags |= I2C_M_NOSTOP; + imx9_lpi2c_sendstop(priv); + } + } + } + } + } + } + else + { + imx9_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0); + + priv->status = status; + + if ((priv->flags & I2C_M_NOSTOP) == 0) + { + imx9_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); + + /* Do this once */ + + priv->flags |= I2C_M_NOSTOP; + imx9_lpi2c_sendstop(priv); + } + + /* Clear the error */ + + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MSR_OFFSET, + (status & (LPI2C_MSR_NDF | LPI2C_MSR_ALF | + LPI2C_MSR_FEF | LPI2C_MSR_EPF))); + } + + /* Check for endof packet or Stop */ + + if ((status & (LPI2C_MSR_EPF | LPI2C_MSR_SDF)) != 0) + { + /* Reset either or both */ + + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MSR_OFFSET, status & + (LPI2C_MSR_EPF | LPI2C_MSR_SDF)); + + /* Was it both End of packet and Stop */ + + if ((status & (LPI2C_MSR_EPF | LPI2C_MSR_SDF)) == + (LPI2C_MSR_EPF | LPI2C_MSR_SDF)) + { +#ifndef CONFIG_I2C_POLLED + if (priv->intstate == INTSTATE_WAITING) + { + /* inform the thread that transfer is complete + * and wake it up + */ + + priv->intstate = INTSTATE_DONE; + + imx9_lpi2c_modifyreg(priv, IMX9_LPI2C_MIER_OFFSET, + LPI2C_MIER_TDIE | LPI2C_MIER_RDIE | + LPI2C_MIER_NDIE | LPI2C_MIER_ALIE | + LPI2C_MIER_SDIE | LPI2C_MIER_EPIE, 0); + nxsem_post(&priv->sem_isr); + } +#else + priv->intstate = INTSTATE_DONE; +#endif + } + } + + return OK; +} + +/**************************************************************************** + * Name: imx9_lpi2c_isr + * + * Description: + * Common I2C interrupt service routine + * + ****************************************************************************/ + +#ifndef CONFIG_I2C_POLLED +static int imx9_lpi2c_isr(int irq, void *context, void *arg) +{ + struct imx9_lpi2c_priv_s *priv = (struct imx9_lpi2c_priv_s *)arg; + + DEBUGASSERT(priv != NULL); + return imx9_lpi2c_isr_process(priv); +} +#endif + +/**************************************************************************** + * Name: imx9_lpi2c_clock_enable + * + * Description: + * Ungate LPI2C clock + * + ****************************************************************************/ + +static void imx9_lpi2c_clock_enable(struct imx9_lpi2c_priv_s *priv) +{ + imx9_ccm_gate_on(priv->config->clk_gate, true); +} + +/**************************************************************************** + * Name: imx9_lpi2c_clock_disable + * + * Description: + * Gate LPI2C clock + * + ****************************************************************************/ + +void imx9_lpi2c_clock_disable(struct imx9_lpi2c_priv_s *priv) +{ + imx9_ccm_gate_on(priv->config->clk_gate, false); +} + +/**************************************************************************** + * Name: imx9_lpi2c_init + * + * Description: + * Setup the I2C hardware, ready for operation with defaults + * + ****************************************************************************/ + +static int imx9_lpi2c_init(struct imx9_lpi2c_priv_s *priv) +{ + /* Power-up and configure GPIOs */ + + /* Configure pins */ + + imx9_iomux_configure(priv->config->scl_pin); + imx9_iomux_configure(priv->config->sda_pin); + + /* Enable power and reset the peripheral */ + + imx9_lpi2c_clock_enable(priv); + + /* Reset LPI2C before configuring it */ + + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MCR_OFFSET, LPI2C_MCR_RST); + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MCR_OFFSET, 0); + + /* Disable doze mode (Set DOZEN bit in 1 to disable) */ + + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MCR_OFFSET, LPI2C_MCR_DOZEN); + + /* Disable host request */ + + imx9_lpi2c_modifyreg(priv, IMX9_LPI2C_MCFGR0_OFFSET, + LPI2C_MCFG0_HREN | LPI2C_MCFG0_HRSEL, + LPI2C_MCFG0_HRPOL); + + /* Disable AUTOSTOP and turn NAK Ignore off */ + + imx9_lpi2c_modifyreg(priv, IMX9_LPI2C_MCFGR1_OFFSET, + LPI2C_MCFGR1_IGNACK | LPI2C_MCFGR1_AUTOSTOP, 0); + + /* Set tx and rx watermarks */ + + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MFCR_OFFSET, + LPI2C_MFCR_TXWATER(0) | LPI2C_MFCR_RXWATER(0)); + + /* Force a frequency update */ + + priv->frequency = 0; + imx9_lpi2c_setclock(priv, 100000); + + /* Set scl, sda glitch filters and busy idle */ + + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MCFGR2_OFFSET, + LPI2C_MCFG2_BUSIDLE(priv->config->busy_idle) | + LPI2C_MCFG2_FILTSCL_CYCLES(priv->config->filtscl) | + LPI2C_MCFG2_FILTSDA_CYCLES(priv->config->filtsda)); + + /* Set pin low cycles to 0 (disable) */ + + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MCFGR3_OFFSET, + LPI2C_MCFG3_PINLOW_CYCLES(0)); + + /* Attach ISRs */ + +#ifndef CONFIG_I2C_POLLED + irq_attach(priv->config->irq, imx9_lpi2c_isr, priv); + up_enable_irq(priv->config->irq); +#endif + + /* Enable I2C */ + + imx9_lpi2c_modifyreg(priv, IMX9_LPI2C_MCR_OFFSET, 0, LPI2C_MCR_MEN); + return OK; +} + +/**************************************************************************** + * Name: imx9_lpi2c_deinit + * + * Description: + * Shutdown the I2C hardware + * + ****************************************************************************/ + +static int imx9_lpi2c_deinit(struct imx9_lpi2c_priv_s *priv) +{ + /* Disable I2C */ + + imx9_lpi2c_modifyreg(priv, IMX9_LPI2C_MCR_OFFSET, LPI2C_MCR_MEN, 0); + + /* Reset LPI2C */ + + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MCR_OFFSET, LPI2C_MCR_RST); + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MCR_OFFSET, 0); + + /* Disable and detach interrupts */ + +#ifndef CONFIG_I2C_POLLED + up_disable_irq(priv->config->irq); + irq_detach(priv->config->irq); +#endif + + /* Disable clocking */ + + imx9_lpi2c_clock_disable(priv); + + return OK; +} + +/**************************************************************************** + * Device Driver Operations + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_lpi2c_dma_command_configure + * + * Description: + * Create a command TCD + * + ****************************************************************************/ + +#ifdef CONFIG_IMX9_LPI2C_DMA +static int imx9_lpi2c_dma_command_configure(struct imx9_lpi2c_priv_s *priv, + uint16_t *ccmd, uint32_t ncmd) +{ + struct imx9_edma_xfrconfig_s config; + memset(&config, 0, sizeof(config)); + + config.saddr = (uintptr_t) ccmd; + config.daddr = priv->config->base + IMX9_LPI2C_MTDR_OFFSET; + config.soff = sizeof(uint16_t); + config.doff = 0; + config.iter = 1; + config.flags = EDMA_CONFIG_LINKTYPE_LINKNONE; + config.ssize = EDMA_16BIT; + config.dsize = EDMA_16BIT; + config.nbytes = sizeof(uint16_t) * ncmd; + + up_clean_dcache((uintptr_t)config.saddr, + (uintptr_t)config.saddr + config.nbytes); + + return imx9_dmach_xfrsetup(priv->txdma, &config); +} +#endif + +/**************************************************************************** + * Name: imx9_lpi2c_dma_data_configure + * + * Description: + * Create a data TCD + * + ****************************************************************************/ + +#ifdef CONFIG_IMX9_LPI2C_DMA +static int imx9_lpi2c_dma_data_configure(struct imx9_lpi2c_priv_s *priv, + struct i2c_msg_s *msg) +{ + DMACH_HANDLE dma; + struct imx9_edma_xfrconfig_s config; + memset(&config, 0, sizeof(config)); + + config.iter = msg->length; + config.flags = EDMA_CONFIG_LINKTYPE_LINKNONE; + config.ssize = EDMA_8BIT; + config.dsize = EDMA_8BIT; + config.nbytes = sizeof(msg->buffer[0]); + + if (msg->flags & I2C_M_READ) + { + dma = priv->rxdma; + config.saddr = priv->config->base + IMX9_LPI2C_MRDR_OFFSET; + config.daddr = (uintptr_t) msg->buffer; + config.soff = 0; + config.doff = sizeof(msg->buffer[0]); + up_invalidate_dcache((uintptr_t)msg->buffer, + (uintptr_t)msg->buffer + msg->length); + } + else + { + dma = priv->txdma; + config.saddr = (uintptr_t) msg->buffer; + config.daddr = priv->config->base + IMX9_LPI2C_MTDR_OFFSET; + config.soff = sizeof(msg->buffer[0]); + config.doff = 0; + up_clean_dcache((uintptr_t)msg->buffer, + (uintptr_t)msg->buffer + msg->length); + } + + return imx9_dmach_xfrsetup(dma, &config) ? 0 : msg->length; +} +#endif + +/**************************************************************************** + * Name: imx9_lpi2c_form_command_list + * + * Description: + * Form the DMA command list + * + ****************************************************************************/ + +#ifdef CONFIG_IMX9_LPI2C_DMA +static int imx9_lpi2c_form_command_list(struct imx9_lpi2c_priv_s *priv, + struct i2c_msg_s *msg, int ncmds) +{ + ssize_t length = 0; + + if (priv->flags & I2C_M_NOSTART) + { + if (priv->flags & I2C_M_READ) + { + /* No start read operation */ + + priv->cmnds[ncmds++] = LPI2C_MTDR_CMD_RXD | + LPI2C_MTDR_DATA(msg->length - 1); + } + } + else + { + /* A start based read or write operation */ + + /* Create bus address with R/W */ + + uint16_t badd = (priv->flags & I2C_M_READ) ? I2C_READADDR8(msg->addr) : + I2C_WRITEADDR8(msg->addr); + + priv->cmnds[ncmds++] = LPI2C_MTDR_CMD_START | LPI2C_MTDR_DATA(badd); + + if (badd & I2C_READBIT) + { + length = msg->length; + while (length) + { + if (length > 256u) + { + priv->cmnds[ncmds++] = LPI2C_MTDR_CMD_RXD | + LPI2C_MTDR_DATA(256u - 1); + length -= 256u; + } + else + { + priv->cmnds[ncmds++] = LPI2C_MTDR_CMD_RXD | + LPI2C_MTDR_DATA(length - 1); + length = 0; + } + } + } + } + + return ncmds; +} +#endif + +/**************************************************************************** + * Name: imx9_lpi2c_dma_transfer + * + * Description: + * DMA based I2C transfer function + * + ****************************************************************************/ + +#ifdef CONFIG_IMX9_LPI2C_DMA +static int imx9_lpi2c_dma_transfer(struct imx9_lpi2c_priv_s *priv) +{ + int m; + int ntotcmds = 0; + int ncmds = 0; + uint16_t *ccmnd = NULL; + + /* Disable Interrupts */ + + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MIER_OFFSET, 0); + + /* Disable DMA */ + + imx9_lpi2c_modifyreg(priv, IMX9_LPI2C_MDER_OFFSET, LPI2C_MDER_TDDE | + LPI2C_MDER_RDDE, 0); + + /* Enable AUTOSTOP and NAK Ignore */ + + imx9_lpi2c_modifyreg(priv, IMX9_LPI2C_MCFGR1_OFFSET, 0, + LPI2C_MCFGR1_IGNACK | LPI2C_MCFGR1_AUTOSTOP); + + /* Form chains of TCDs to process the messages */ + + for (m = 0; m < priv->msgc; m++) + { + ncmds = 0; + priv->flags = priv->msgv[m].flags; + + /* Form a command list */ + + ccmnd = &priv->cmnds[ntotcmds]; + + ncmds = imx9_lpi2c_form_command_list(priv, &priv->msgv[m], ntotcmds); + + /* Have commands for this message ? */ + + if (ncmds != 0) + { + /* Build up a TCD with the command from this message */ + + imx9_lpi2c_dma_command_configure(priv, ccmnd, ncmds - ntotcmds); + + ntotcmds += ncmds; + + DEBUGASSERT(ntotcmds < CONFIG_IMX9_LPI2C_DMA_MAXMSG); + + imx9_lpi2c_dma_data_configure(priv, &priv->msgv[m]); + } + } + + /* Clear the TX and RX FIFOs */ + + imx9_lpi2c_modifyreg(priv, IMX9_LPI2C_MCR_OFFSET, 0, + LPI2C_MCR_RTF | LPI2C_MCR_RRF); + + /* Reset the Error bits */ + + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MSR_OFFSET, LPI2C_MSR_NDF | + LPI2C_MSR_ALF | + LPI2C_MSR_FEF); + + /* Enable the Iterrupts */ + + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MIER_OFFSET, + LPI2C_MIER_NDIE | LPI2C_MIER_ALIE | + LPI2C_MIER_PLTIE); + + /* Start The DMA */ + + imx9_dmach_start(priv->rxdma, imx9_rxdma_callback, (void *)priv); + imx9_dmach_start(priv->txdma, imx9_txdma_callback, (void *)priv); + + /* Enable the DMA Request */ + + imx9_lpi2c_modifyreg(priv, IMX9_LPI2C_MDER_OFFSET, 0, + LPI2C_MDER_TDDE | LPI2C_MDER_RDDE); + return OK; +} +#endif + +/**************************************************************************** + * Name: imx9_lpi2c_transfer + * + * Description: + * Generic I2C transfer function + * + ****************************************************************************/ + +static int imx9_lpi2c_transfer(struct i2c_master_s *dev, + struct i2c_msg_s *msgs, int count) +{ + struct imx9_lpi2c_priv_s *priv = (struct imx9_lpi2c_priv_s *)dev; + int ret; +#ifdef CONFIG_IMX9_LPI2C_DMA + int m; +#endif + + DEBUGASSERT(count > 0); + + /* Ensure that address or flags don't change meanwhile */ + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + /* Clear any pending error interrupts */ + + imx9_lpi2c_putreg(priv, IMX9_LPI2C_MSR_OFFSET, 0xffffffff); + + /* Old transfers are done */ + + /* Reset ptr and dcnt to ensure an unexpected data interrupt doesn't + * overwrite stale data. + */ + + priv->dcnt = 0; + priv->ptr = NULL; + + priv->msgv = msgs; + priv->msgc = count; + priv->flags = msgs->flags; + + i2cinfo("Flags %x, len %ld\n", msgs->flags, msgs->length); + + /* Reset I2C trace logic */ + + imx9_lpi2c_tracereset(priv); + + /* Set I2C clock frequency */ + + imx9_lpi2c_setclock(priv, msgs->frequency); + + priv->status = 0; + + /* Signal the interrupt handler that we are waiting. NOTE: Interrupts + * are currently disabled but will be temporarily re-enabled below when + * nxsem_tickwait_uninterruptible() sleeps. + */ + + priv->intstate = INTSTATE_WAITING; + + /* Wait for an ISR, if there was a timeout, fetch latest status to get + * the BUSY flag. + */ + +#ifdef CONFIG_IMX9_LPI2C_DMA + if (priv->rxdma || priv->txdma) + { + imx9_lpi2c_dma_transfer(priv); + } +#endif + + if (imx9_lpi2c_sem_waitdone(priv) < 0) + { +#ifdef CONFIG_IMX9_LPI2C_DMA + if (priv->rxdma != NULL) + { + imx9_dmach_stop(priv->rxdma); + } + + if (priv->txdma != NULL) + { + imx9_dmach_stop(priv->txdma); + } + +#endif + ret = -ETIMEDOUT; + i2cerr("ERROR: Timed out: MSR: status: 0x0%" PRIx32 "\n", + priv->status); + } + + /* Check for error status conditions */ + + else if ((priv->status & LPI2C_MSR_ERROR_MASK) != 0) + { + /* I2C_SR1_ERRORMASK is the 'OR' of the following individual bits: */ + + if (priv->status & LPI2C_MSR_ALF) + { + /* Arbitration Lost (master mode) */ + + i2cerr("Arbitration lost\n"); + ret = -EAGAIN; + } + else if (priv->status & LPI2C_MSR_NDF) + { + /* Acknowledge Failure */ + + i2cerr("Ack failure\n"); + ret = -ENXIO; + } + else + { + /* FIFO Error */ + + i2cerr("Transfer without start condition\n"); + ret = -EINVAL; + } + } + + /* Dump the trace result */ + + imx9_lpi2c_tracedump(priv); + + /* Ensure that any ISR happening after we finish can't overwrite any user + * data. + */ + + priv->dcnt = 0; + priv->ptr = NULL; + +#ifdef CONFIG_IMX9_LPI2C_DMA + if (priv->rxdma) + { + for (m = 0; m < count; m++) + { + if (msgs[m].flags & I2C_M_READ) + { + up_invalidate_dcache((uintptr_t)msgs[m].buffer, + (uintptr_t)msgs[m].buffer + + msgs[m].length); + } + } + } +#endif + + nxmutex_unlock(&priv->lock); + return ret; +} + +/**************************************************************************** + * Name: imx9_lpi2c_reset + * + * Description: + * Perform an I2C bus reset in an attempt to break loose stuck I2C devices. + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_I2C_RESET +static int imx9_lpi2c_reset(struct i2c_master_s *dev) +{ + struct imx9_lpi2c_priv_s *priv = (struct imx9_lpi2c_priv_s *)dev; + unsigned int clock_count; + unsigned int stretch_count; + uint32_t scl_gpio; + uint32_t sda_gpio; + uint32_t frequency; + int ret; + + DEBUGASSERT(dev); + + /* Our caller must own a ref */ + + DEBUGASSERT(priv->refs > 0); + + /* Lock out other clients */ + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + ret = -EIO; + + /* Save the current frequency */ + + frequency = priv->frequency; + + /* De-init the port */ + + imx9_lpi2c_deinit(priv); + + /* Use GPIO configuration to un-wedge the bus */ + + imx9_iomux_gpio(priv->config->scl_pin, true); + imx9_iomux_gpio(priv->config->sda_pin, true); + + scl_gpio = priv->config->reset_scl_pin; + sda_gpio = priv->config->reset_sda_pin; + + imx9_config_gpio(scl_gpio); + imx9_config_gpio(sda_gpio); + + /* Let SDA go high */ + + imx9_gpio_write(sda_gpio, 1); + + /* Clock the bus until any slaves currently driving it let it go. */ + + clock_count = 0; + while (!imx9_gpio_read(sda_gpio)) + { + /* Give up if we have tried too hard */ + + if (clock_count++ > 10) + { + goto out; + } + + /* Sniff to make sure that clock stretching has finished. + * + * If the bus never relaxes, the reset has failed. + */ + + stretch_count = 0; + while (!imx9_gpio_read(scl_gpio)) + { + /* Give up if we have tried too hard */ + + if (stretch_count++ > 10) + { + goto out; + } + + up_udelay(10); + } + + /* Drive SCL low */ + + imx9_gpio_write(scl_gpio, 0); + up_udelay(10); + + /* Drive SCL high again */ + + imx9_gpio_write(scl_gpio, 1); + up_udelay(10); + } + + /* Generate a start followed by a stop to reset slave + * state machines. + */ + + imx9_gpio_write(sda_gpio, 0); + up_udelay(10); + imx9_gpio_write(scl_gpio, 0); + up_udelay(10); + imx9_gpio_write(scl_gpio, 1); + up_udelay(10); + imx9_gpio_write(sda_gpio, 1); + up_udelay(10); + + /* Re-init the port */ + + imx9_lpi2c_init(priv); + + /* Restore the frequency */ + + imx9_lpi2c_setclock(priv, frequency); + ret = OK; + +out: + + /* Release the port for re-use by other clients */ + + nxmutex_unlock(&priv->lock); + return ret; +} +#endif /* CONFIG_I2C_RESET */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_i2cbus_initialize + * + * Description: + * Initialize one I2C bus + * + ****************************************************************************/ + +struct i2c_master_s *imx9_i2cbus_initialize(int port) +{ + struct imx9_lpi2c_priv_s * priv = NULL; + irqstate_t flags; + + /* Get I2C private structure */ + + switch (port) + { +#ifdef CONFIG_IMX9_LPI2C1 + case 1: + priv = (struct imx9_lpi2c_priv_s *)&imx9_lpi2c1_priv; + break; +#endif +#ifdef CONFIG_IMX9_LPI2C2 + case 2: + priv = (struct imx9_lpi2c_priv_s *)&imx9_lpi2c2_priv; + break; +#endif +#ifdef CONFIG_IMX9_LPI2C3 + case 3: + priv = (struct imx9_lpi2c_priv_s *)&imx9_lpi2c3_priv; + break; +#endif +#ifdef CONFIG_IMX9_LPI2C4 + case 4: + priv = (struct imx9_lpi2c_priv_s *)&imx9_lpi2c4_priv; + break; +#endif +#ifdef CONFIG_IMX9_LPI2C5 + case 5: + priv = (struct imx9_lpi2c_priv_s *)&imx9_lpi2c5_priv; + break; +#endif +#ifdef CONFIG_IMX9_LPI2C6 + case 6: + priv = (struct imx9_lpi2c_priv_s *)&imx9_lpi2c6_priv; + break; +#endif +#ifdef CONFIG_IMX9_LPI2C7 + case 7: + priv = (struct imx9_lpi2c_priv_s *)&imx9_lpi2c7_priv; + break; +#endif +#ifdef CONFIG_IMX9_LPI2C8 + case 8: + priv = (struct imx9_lpi2c_priv_s *)&imx9_lpi2c8_priv; + break; +#endif + default: + return NULL; + } + + /* Initialize private data for the first time, increment reference count, + * power-up hardware and configure GPIOs. + */ + + flags = enter_critical_section(); + + if ((volatile int)priv->refs++ == 0) + { + imx9_lpi2c_init(priv); + +#ifdef CONFIG_IMX9_LPI2C_DMA + if (priv->config->dma_txreqsrc != 0) + { + priv->txdma = imx9_dmach_alloc(priv->config->dma_txreqsrc, 0); + DEBUGASSERT(priv->txdma != NULL); + } + + if (priv->config->dma_rxreqsrc != 0) + { + priv->rxdma = imx9_dmach_alloc(priv->config->dma_rxreqsrc, 0); + DEBUGASSERT(priv->rxdma != NULL); + } +#endif + } + + leave_critical_section(flags); + + return (struct i2c_master_s *)priv; +} + +/**************************************************************************** + * Name: imx9_i2cbus_uninitialize + * + * Description: + * Uninitialize an I2C bus + * + ****************************************************************************/ + +int imx9_i2cbus_uninitialize(struct i2c_master_s *dev) +{ + struct imx9_lpi2c_priv_s *priv = (struct imx9_lpi2c_priv_s *)dev; + irqstate_t flags; + + DEBUGASSERT(dev); + + /* Decrement reference count and check for underflow */ + + if (priv->refs == 0) + { + return ERROR; + } + + flags = enter_critical_section(); + + if (--priv->refs > 0) + { + leave_critical_section(flags); + return OK; + } + + leave_critical_section(flags); + + /* Disable power and other HW resource (GPIO's) */ + +#ifdef CONFIG_IMX9_LPI2C_DMA + if (priv->rxdma != NULL) + { + imx9_dmach_stop(priv->rxdma); + imx9_dmach_free(priv->rxdma); + priv->rxdma = NULL; + } + + if (priv->txdma != NULL) + { + imx9_dmach_stop(priv->txdma); + imx9_dmach_free(priv->txdma); + priv->txdma = NULL; + } +#endif + + imx9_lpi2c_deinit(priv); + + return OK; +} + +#endif /* CONFIG_IMX9_LPI2C */ diff --git a/arch/arm64/src/imx9/imx9_lpi2c.h b/arch/arm64/src/imx9/imx9_lpi2c.h new file mode 100644 index 0000000000..d323871fa3 --- /dev/null +++ b/arch/arm64/src/imx9/imx9_lpi2c.h @@ -0,0 +1,71 @@ +/**************************************************************************** + * arch/arm64/src/imx9/imx9_lpi2c.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_ARM64_SRC_IMX9_IMX9_LPI2C_H +#define __ARCH_ARM64_SRC_IMX9_IMX9_LPI2C_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_i2cbus_initialize + * + * Description: + * Initialize the selected I2C port. And return a unique instance of struct + * struct i2c_master_s. This function may be called to obtain multiple + * instances of the interface, each of which may be set up with a + * different frequency and slave address. + * + * Input Parameters: + * Port number (for hardware that has multiple I2C interfaces) + * + * Returned Value: + * Valid I2C device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct i2c_master_s *imx9_i2cbus_initialize(int port); + +/**************************************************************************** + * Name: imx9_i2cbus_uninitialize + * + * Description: + * De-initialize the selected I2C port, and power down the device. + * + * Input Parameters: + * Device structure as returned by the imx9_i2cbus_initialize() + * + * Returned Value: + * OK on success, ERROR when internal reference count mismatch or dev + * points to invalid hardware device. + * + ****************************************************************************/ + +int imx9_i2cbus_uninitialize(struct i2c_master_s *dev); + +#endif /* __ARCH_ARM64_SRC_IMX9_IMX9_LPI2C_H */ diff --git a/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig b/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig index e1834dc8ed..64c7f53357 100644 --- a/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig +++ b/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig @@ -28,9 +28,14 @@ CONFIG_FS_PROCFS=y CONFIG_FS_ROMFS=y CONFIG_HAVE_CXX=y CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_I2C=y +CONFIG_I2C_RESET=y CONFIG_IDLETHREAD_STACKSIZE=8192 CONFIG_IMX9_FLEXIO1_PWM=y CONFIG_IMX9_GPIO_IRQ=y +CONFIG_IMX9_LPI2C1=y +CONFIG_IMX9_LPI2C_DYNTIMEO=y +CONFIG_IMX9_LPI2C_DYNTIMEO_STARTSTOP=10 CONFIG_IMX9_TPM3_PWM=y CONFIG_IMX9_TPM3_PWM_CHMUX=0x00000003 CONFIG_IMX9_UART1=y @@ -60,6 +65,7 @@ CONFIG_START_MONTH=3 CONFIG_START_YEAR=2022 CONFIG_SYMTAB_ORDEREDBYNAME=y CONFIG_SYSTEM_CDCACM=y +CONFIG_SYSTEM_I2CTOOL=y CONFIG_SYSTEM_NSH=y CONFIG_SYSTEM_SYSTEM=y CONFIG_SYSTEM_TIME64=y diff --git a/boards/arm64/imx9/imx93-evk/include/board.h b/boards/arm64/imx9/imx93-evk/include/board.h index bd78f1cf79..6cd4c26e56 100644 --- a/boards/arm64/imx9/imx93-evk/include/board.h +++ b/boards/arm64/imx9/imx93-evk/include/board.h @@ -31,6 +31,8 @@ * Pre-processor Definitions ****************************************************************************/ +#define IOMUX_LPI2C_DEFAULT (IOMUXC_PAD_OD_ENABLE | IOMUXC_PAD_FSEL_SFAST | IOMUXC_PAD_DSE_X6) + /* FLEXIO to PWM pin muxings */ /* EVK signals @@ -49,6 +51,16 @@ #define TPM3_PWM3_MUX IOMUX_CFG(IOMUXC_PAD_GPIO_IO24_TPM3_CH3, IOMUXC_PAD_FSEL_SFAST | IOMUXC_PAD_DSE_X6, 0) +/* LPI2Cs */ + +#define MUX_LPI2C1_SCL IOMUX_CFG(IOMUXC_PAD_I2C1_SCL_LPI2C1_SCL, IOMUX_LPI2C_DEFAULT, IOMUXC_MUX_SION_ON) +#define MUX_LPI2C1_SDA IOMUX_CFG(IOMUXC_PAD_I2C1_SDA_LPI2C1_SDA, IOMUX_LPI2C_DEFAULT, IOMUXC_MUX_SION_ON) + +/* I2C reset functionality */ + +#define GPIO_LPI2C1_SCL_RESET (GPIO_PORT1 | GPIO_PIN0 | GPIO_OUTPUT | GPIO_OUTPUT_ONE) +#define GPIO_LPI2C1_SDA_RESET (GPIO_PORT1 | GPIO_PIN1 | GPIO_OUTPUT | GPIO_OUTPUT_ONE) + /**************************************************************************** * Public Data ****************************************************************************/ diff --git a/boards/arm64/imx9/imx93-evk/src/Makefile b/boards/arm64/imx9/imx93-evk/src/Makefile index fc2ce19a0c..564a2db926 100644 --- a/boards/arm64/imx9/imx93-evk/src/Makefile +++ b/boards/arm64/imx9/imx93-evk/src/Makefile @@ -30,4 +30,8 @@ ifeq ($(CONFIG_PWM),y) CSRCS += imx9_pwm.c endif +ifeq ($(CONFIG_IMX9_LPI2C),y) +CSRCS += imx9_i2c.c +endif + include $(TOPDIR)/boards/Board.mk diff --git a/boards/arm64/imx9/imx93-evk/src/imx93-evk.h b/boards/arm64/imx9/imx93-evk/src/imx93-evk.h index 4b9c0f3a46..1da0b3b88e 100644 --- a/boards/arm64/imx9/imx93-evk/src/imx93-evk.h +++ b/boards/arm64/imx9/imx93-evk/src/imx93-evk.h @@ -67,5 +67,17 @@ int imx9_bringup(void); int imx9_pwm_setup(void); #endif +/**************************************************************************** + * Name: imx9_i2c_setup + * + * Description: + * Initialize I2C devices and driver + * + ****************************************************************************/ + +#if defined(CONFIG_I2C_DRIVER) +int imx9_i2c_initialize(void); +#endif + #endif /* __ASSEMBLY__ */ #endif /* __BOARDS_ARM64_IMX9_IMX93_EVK_SRC_IMX93_EVK_H */ diff --git a/boards/arm64/imx9/imx93-evk/src/imx9_boardinit.c b/boards/arm64/imx9/imx93-evk/src/imx9_boardinit.c index 7443f92ccd..876b9014de 100644 --- a/boards/arm64/imx9/imx93-evk/src/imx9_boardinit.c +++ b/boards/arm64/imx9/imx93-evk/src/imx9_boardinit.c @@ -23,8 +23,11 @@ ****************************************************************************/ #include + #include + #include + #include "imx93-evk.h" /**************************************************************************** diff --git a/boards/arm64/imx9/imx93-evk/src/imx9_bringup.c b/boards/arm64/imx9/imx93-evk/src/imx9_bringup.c index fc30e67cff..6048509b67 100644 --- a/boards/arm64/imx9/imx93-evk/src/imx9_bringup.c +++ b/boards/arm64/imx9/imx93-evk/src/imx9_bringup.c @@ -67,6 +67,16 @@ int imx9_bringup(void) } #endif +#if defined(CONFIG_I2C_DRIVER) + /* Configure I2C peripheral interfaces */ + + ret = imx9_i2c_initialize(); + if (ret < 0) + { + syslog(LOG_ERR, "Failed to initialize I2C driver: %d\n", ret); + } +#endif + UNUSED(ret); return OK; } diff --git a/boards/arm64/imx9/imx93-evk/src/imx9_i2c.c b/boards/arm64/imx9/imx93-evk/src/imx9_i2c.c new file mode 100644 index 0000000000..ac30b5da2d --- /dev/null +++ b/boards/arm64/imx9/imx93-evk/src/imx9_i2c.c @@ -0,0 +1,77 @@ +/**************************************************************************** + * boards/arm64/imx9/imx93-evk/src/imx9_i2c.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include + +#include "imx9_lpi2c.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: board_i2c_init + * + * Description: + * Configure the I2C driver. + * + * Returned Value: + * Zero (OK) is returned on success; A negated errno value is returned + * to indicate the nature of any failure. + * + ****************************************************************************/ + +int imx9_i2c_initialize(void) +{ + int ret = OK; + +#ifdef CONFIG_IMX9_LPI2C1 + struct i2c_master_s *i2c; + + i2c = imx9_i2cbus_initialize(1); + if (i2c == NULL) + { + i2cerr("ERROR: Failed to init I2C0 interface\n"); + return -ENODEV; + } +#endif + +#ifdef CONFIG_I2C_DRIVER + ret = i2c_register(i2c, 0); + if (ret < 0) + { + i2cerr("ERROR: Failed to register I2C0 driver: %d\n", ret); + imx9_i2cbus_uninitialize(i2c); + return ret; + } +#endif + + return OK; +}