You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2021/06/12 02:03:51 UTC
[incubator-nuttx] branch master updated: mpfs: add i2c driver
This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 1bce864 mpfs: add i2c driver
1bce864 is described below
commit 1bce864ef771bb5178cfa09d4cbff9432d8caa24
Author: Eero Nurkkala <ee...@offcode.fi>
AuthorDate: Wed May 12 12:13:14 2021 +0300
mpfs: add i2c driver
This adds mpfs i2c driver.
Signed-off-by: Eero Nurkkala <ee...@offcode.fi>
---
Documentation/platforms/risc-v/mpfs/index.rst | 2 +-
arch/risc-v/src/mpfs/Kconfig | 8 +
arch/risc-v/src/mpfs/Make.defs | 4 +
arch/risc-v/src/mpfs/hardware/mpfs_i2c.h | 60 ++
arch/risc-v/src/mpfs/mpfs_i2c.c | 810 +++++++++++++++++++++
arch/risc-v/src/mpfs/mpfs_i2c.h | 93 +++
boards/risc-v/mpfs/icicle/src/Makefile | 4 +
boards/risc-v/mpfs/icicle/src/mpfs_bringup.c | 11 +
.../mpfs/icicle/src/{mpfs_bringup.c => mpfs_i2c.c} | 67 +-
boards/risc-v/mpfs/icicle/src/mpfsicicle.h | 1 +
10 files changed, 1040 insertions(+), 20 deletions(-)
diff --git a/Documentation/platforms/risc-v/mpfs/index.rst b/Documentation/platforms/risc-v/mpfs/index.rst
index 8b4d6d0..3ab77a1 100644
--- a/Documentation/platforms/risc-v/mpfs/index.rst
+++ b/Documentation/platforms/risc-v/mpfs/index.rst
@@ -58,7 +58,7 @@ Peripheral Support NOTES
GPIO Yes
MMUART Yes Uart mode only
SPI Yes
-I2C No
+I2C Yes
Timers No
Watchdog No
RTC No
diff --git a/arch/risc-v/src/mpfs/Kconfig b/arch/risc-v/src/mpfs/Kconfig
index 0756cc2..a0fd0bb 100755
--- a/arch/risc-v/src/mpfs/Kconfig
+++ b/arch/risc-v/src/mpfs/Kconfig
@@ -93,6 +93,14 @@ config MPFS_UART4
select ARCH_HAVE_SERIAL_TERMIOS
select MPFS_HAVE_UART4
+config MPFS_I2C0
+ bool "I2C 0"
+ default n
+
+config MPFS_I2C1
+ bool "I2C 1"
+ default n
+
endmenu
menu "MPFS Others"
diff --git a/arch/risc-v/src/mpfs/Make.defs b/arch/risc-v/src/mpfs/Make.defs
index 2267b24..5582815 100755
--- a/arch/risc-v/src/mpfs/Make.defs
+++ b/arch/risc-v/src/mpfs/Make.defs
@@ -65,3 +65,7 @@ endif
ifeq ($(CONFIG_SPI),y)
CHIP_CSRCS += mpfs_spi.c
endif
+
+ifeq ($(CONFIG_I2C),y)
+CHIP_CSRCS += mpfs_i2c.c
+endif
diff --git a/arch/risc-v/src/mpfs/hardware/mpfs_i2c.h b/arch/risc-v/src/mpfs/hardware/mpfs_i2c.h
new file mode 100755
index 0000000..d90fcde
--- /dev/null
+++ b/arch/risc-v/src/mpfs/hardware/mpfs_i2c.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/hardware/mpfs_i2c.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_I2C_H
+#define __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_I2C_H
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MPFS_I2C_CTRL_CR2 7
+#define MPFS_I2C_CTRL_ENS1 6
+#define MPFS_I2C_CTRL_STA 5
+#define MPFS_I2C_CTRL_STO 4
+#define MPFS_I2C_CTRL_SI 3
+#define MPFS_I2C_CTRL_AA 2
+#define MPFS_I2C_CTRL_CR1 1
+#define MPFS_I2C_CTRL_CR0 0
+
+#define MPFS_I2C_CTRL_CR2_MASK (1 << 7)
+#define MPFS_I2C_CTRL_ENS1_MASK (1 << 6)
+#define MPFS_I2C_CTRL_STA_MASK (1 << 5)
+#define MPFS_I2C_CTRL_STO_MASK (1 << 4)
+#define MPFS_I2C_CTRL_SI_MASK (1 << 3)
+#define MPFS_I2C_CTRL_AA_MASK (1 << 2)
+#define MPFS_I2C_CTRL_CR1_MASK (1 << 1)
+#define MPFS_I2C_CTRL_CR0_MASK (1 << 0)
+
+#define MPFS_I2C_ST_RESET_ACTIVATED 0xD0 /* Master reset is activated */
+#define MPFS_I2C_ST_RX_DATA_NACK 0x58 /* Data received, NACK sent */
+#define MPFS_I2C_ST_RX_DATA_ACK 0x50 /* Data received, ACK sent */
+#define MPFS_I2C_ST_SLAR_NACK 0x48 /* SLA+R sent, NACK'ed */
+#define MPFS_I2C_ST_SLAR_ACK 0x40 /* SLA+R sent, ACK'ed */
+#define MPFS_I2C_ST_LOST_ARB 0x38 /* Master lost arbitration */
+#define MPFS_I2C_ST_TX_DATA_NACK 0x30 /* Data sent, NACK'ed */
+#define MPFS_I2C_ST_TX_DATA_ACK 0x28 /* Data sent, ACK'ed */
+#define MPFS_I2C_ST_SLAW_NACK 0x20 /* SLA + W sent, nack received */
+#define MPFS_I2C_ST_SLAW_ACK 0x18 /* SLA + W sent, ack received */
+#define MPFS_I2C_ST_RESTART 0x10 /* Repeated start */
+#define MPFS_I2C_ST_START 0x08 /* Start condition sent */
+#define MPFS_I2C_ST_BUS_ERROR 0x00 /* Bus error */
+
+#endif /* __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_I2C_H */
diff --git a/arch/risc-v/src/mpfs/mpfs_i2c.c b/arch/risc-v/src/mpfs/mpfs_i2c.c
new file mode 100755
index 0000000..ad8dc3c
--- /dev/null
+++ b/arch/risc-v/src/mpfs/mpfs_i2c.c
@@ -0,0 +1,810 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_i2c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <debug.h>
+#include <assert.h>
+#include <time.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/clock.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/i2c/i2c_master.h>
+
+#include <arch/board/board.h>
+
+#include "mpfs_i2c.h"
+
+#include "riscv_arch.h"
+#include "hardware/mpfs_i2c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MPFS_SYSREG_SOFT_RESET_CR (MPFS_SYSREG_BASE + \
+ MPFS_SYSREG_SOFT_RESET_CR_OFFSET)
+#define MPFS_SYSREG_SUBBLK_CLOCK_CR (MPFS_SYSREG_BASE + \
+ MPFS_SYSREG_SUBBLK_CLOCK_CR_OFFSET)
+
+#define MPFS_I2C_CTRL_OFFSET 0x00
+#define MPFS_I2C_STATUS_OFFSET 0x04
+#define MPFS_I2C_DATA_OFFSET 0x08
+#define MPFS_I2C_SLAVE0ADR_OFFSET 0x0C
+#define MPFS_I2C_SMBUS_OFFSET 0x10
+#define MPFS_I2C_FREQ_OFFSET 0x14
+
+#define MPFS_I2C_CTRL (priv->hw_base + MPFS_I2C_CTRL_OFFSET)
+#define MPFS_I2C_STATUS (priv->hw_base + MPFS_I2C_STATUS_OFFSET)
+#define MPFS_I2C_DATA (priv->hw_base + MPFS_I2C_DATA_OFFSET)
+#define MPFS_I2C_ADDR (priv->hw_base + MPFS_I2C_SLAVE0ADR_OFFSET)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+typedef enum mpfs_i2c_status
+{
+ MPFS_I2C_SUCCESS = 0u,
+ MPFS_I2C_IN_PROGRESS,
+ MPFS_I2C_FAILED,
+ MPFS_I2C_TIMED_OUT
+} mpfs_i2c_status_t;
+
+typedef enum mpfs_i2c_clock_divider
+{
+ MPFS_I2C_PCLK_DIV_256 = 0u,
+ MPFS_I2C_PCLK_DIV_224,
+ MPFS_I2C_PCLK_DIV_192,
+ MPFS_I2C_PCLK_DIV_160,
+ MPFS_I2C_PCLK_DIV_960,
+ MPFS_I2C_PCLK_DIV_120,
+ MPFS_I2C_PCLK_DIV_60,
+ MPFS_I2C_BCLK_DIV_8
+} mpfs_i2c_clk_div_t;
+
+static int mpfs_i2c_transfer(struct i2c_master_s *dev,
+ struct i2c_msg_s *msgs,
+ int count);
+
+static const struct i2c_ops_s mpfs_i2c_ops =
+{
+ .transfer = mpfs_i2c_transfer
+};
+
+struct mpfs_i2c_priv_s
+{
+ const struct i2c_ops_s *ops; /* Standard I2C operations */
+ uint32_t id; /* I2C hardware identification */
+ uintptr_t hw_base; /* I2C bus base address */
+ uint16_t plic_irq; /* Platform PLIC irq */
+ struct i2c_msg_s *msgv; /* Message list */
+ mpfs_i2c_clk_div_t bus_divider; /* Bus divider */
+
+ uint8_t msgid; /* Current message ID */
+ ssize_t bytes; /* Processed data bytes */
+
+ uint8_t ser_address; /* Own i2c address */
+ uint8_t target_addr; /* Target i2c address */
+ sem_t sem_excl; /* Mutual exclusion semaphore */
+ sem_t sem_isr; /* Interrupt wait semaphore */
+ int refs; /* Reference count */
+
+ const uint8_t *tx_buffer; /* Tx buffer location */
+ uint16_t tx_size; /* Tx buffer size */
+ uint16_t tx_idx; /* Currently accessed index */
+
+ uint8_t *rx_buffer; /* Rx buffer location */
+ uint16_t rx_size; /* Rx buffer size */
+ uint16_t rx_idx; /* Currently accessed index */
+
+ mpfs_i2c_status_t status; /* Bus driver status */
+};
+
+#ifdef CONFIG_MPFS_I2C0
+static struct mpfs_i2c_priv_s g_mpfs_i2c0_lo_priv =
+{
+ .ops = &mpfs_i2c_ops,
+ .id = 0,
+ .hw_base = MPFS_I2C0_LO_BASE,
+ .plic_irq = MPFS_IRQ_I2C0_MAIN,
+ .msgv = NULL,
+ .bus_divider = MPFS_I2C_PCLK_DIV_256,
+ .ser_address = 0x21,
+ .target_addr = 0,
+ .refs = 0,
+ .tx_size = 0,
+ .tx_idx = 0,
+ .rx_size = 0,
+ .rx_idx = 0,
+ .status = MPFS_I2C_SUCCESS
+};
+#endif /* CONFIG_MPFS_I2C0 */
+
+#ifdef CONFIG_MPFS_I2C1
+static struct mpfs_i2c_priv_s g_mpfs_i2c1_lo_priv =
+{
+ .ops = &mpfs_i2c_ops,
+ .id = 1,
+ .hw_base = MPFS_I2C1_LO_BASE,
+ .plic_irq = MPFS_IRQ_I2C1_MAIN,
+ .msgv = NULL,
+ .bus_divider = MPFS_I2C_PCLK_DIV_256,
+ .ser_address = 0x21,
+ .target_addr = 0,
+ .refs = 0,
+ .tx_size = 0,
+ .tx_idx = 0,
+ .rx_size = 0,
+ .rx_idx = 0,
+ .status = MPFS_I2C_SUCCESS
+};
+#endif /* CONFIG_MPFS_I2C1 */
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mpfs_disable_interrupts
+ *
+ * Description:
+ * Disable all interrupts.
+ *
+ * Returned Value:
+ * primask (current interrupt status)
+ *
+ ****************************************************************************/
+
+static irqstate_t mpfs_disable_interrupts(void)
+{
+ irqstate_t primask;
+ primask = up_irq_save();
+ return primask;
+}
+
+/****************************************************************************
+ * Name: mpfs_restore_interrupts
+ *
+ * Description:
+ * Restore interrupts.
+ *
+ * Parameters:
+ * primask - Earlier stored irqstate
+ *
+ ****************************************************************************/
+
+static void mpfs_restore_interrupts(irqstate_t primask)
+{
+ up_irq_restore(primask);
+}
+
+/****************************************************************************
+ * Name: mpfs_i2c_init
+ *
+ * Description:
+ * Initialize and enable an I2C bus
+ *
+ * Parameters:
+ * priv - Pointer to the internal driver state structure.
+ *
+ ****************************************************************************/
+
+static void mpfs_i2c_init(struct mpfs_i2c_priv_s *priv)
+{
+ uint32_t clock_speed;
+ uint32_t primask;
+
+ primask = mpfs_disable_interrupts();
+
+ if (priv->id == 0)
+ {
+ modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, 0,
+ (1 << SYSREG_SOFT_RESET_CR_I2C0));
+
+ modifyreg32(MPFS_SYSREG_SOFT_RESET_CR,
+ (1 << SYSREG_SOFT_RESET_CR_I2C0), 0);
+
+ modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR, 0,
+ (1 << SYSREG_SUBBLK_CLOCK_CR_I2C0));
+ }
+ else
+ {
+ modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, 0,
+ (1 << SYSREG_SOFT_RESET_CR_I2C1));
+
+ modifyreg32(MPFS_SYSREG_SOFT_RESET_CR,
+ (1 << SYSREG_SOFT_RESET_CR_I2C1), 0);
+
+ modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR, 0,
+ (1 << SYSREG_SUBBLK_CLOCK_CR_I2C1));
+ }
+
+ clock_speed = priv->bus_divider;
+
+ /* Update the clock divider */
+
+ modifyreg32(MPFS_I2C_CTRL,
+ MPFS_I2C_CTRL_CR2_MASK |
+ MPFS_I2C_CTRL_CR1_MASK |
+ MPFS_I2C_CTRL_CR0_MASK,
+ (((clock_speed >> 2u) & 0x01u) << MPFS_I2C_CTRL_CR2) |
+ (((clock_speed >> 1u) & 0x01u) << MPFS_I2C_CTRL_CR1) |
+ (((clock_speed & 0x01u) << MPFS_I2C_CTRL_CR0)));
+
+ /* This is our own address, not the target chip */
+
+ putreg32(priv->ser_address, MPFS_I2C_ADDR);
+
+ /* Enable i2c bus */
+
+ modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_ENS1_MASK,
+ MPFS_I2C_CTRL_ENS1_MASK);
+
+ mpfs_restore_interrupts(primask);
+}
+
+/****************************************************************************
+ * Name: mpfs_i2c_deinit
+ *
+ * Description:
+ * Disable I2C hardware.
+ *
+ * Parameters:
+ * priv - Pointer to the internal driver state structure.
+ *
+ ****************************************************************************/
+
+static void mpfs_i2c_deinit(struct mpfs_i2c_priv_s *priv)
+{
+ up_disable_irq(priv->plic_irq);
+ irq_detach(priv->plic_irq);
+}
+
+/****************************************************************************
+ * Name: mpfs_i2c_sem_waitdone
+ *
+ * Description:
+ * Wait for a transfer to complete.
+ *
+ * Parameters:
+ * priv - Pointer to the internal driver state structure.
+ *
+ * Returned Value:
+ * Zero (OK) is returned on success. A negated errno value is returned on
+ * failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_i2c_sem_waitdone(struct mpfs_i2c_priv_s *priv)
+{
+ struct timespec abstime;
+ int ret;
+
+ clock_gettime(CLOCK_REALTIME, &abstime);
+
+ abstime.tv_sec += 10;
+ abstime.tv_nsec += 0;
+
+ ret = nxsem_timedwait_uninterruptible(&priv->sem_isr, &abstime);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mpfs_i2c_sem_wait
+ *
+ * Description:
+ * Take the exclusive access, waiting as necessary.
+ *
+ * Parameters:
+ * priv - Pointer to the internal driver state structure.
+ *
+ * Returned Value:
+ * Zero (OK) is returned on success. A negated errno value is returned on
+ * failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_i2c_sem_wait(struct mpfs_i2c_priv_s *priv)
+{
+ return nxsem_wait_uninterruptible(&priv->sem_excl);
+}
+
+/****************************************************************************
+ * Name: mpfs_i2c_sem_post
+ *
+ * Description:
+ * Release the mutual exclusion semaphore.
+ *
+ * Parameters:
+ * priv - Pointer to the internal driver state structure.
+ *
+ ****************************************************************************/
+
+static void mpfs_i2c_sem_post(struct mpfs_i2c_priv_s *priv)
+{
+ nxsem_post(&priv->sem_excl);
+}
+
+/****************************************************************************
+ * Name: mpfs_i2c_sem_destroy
+ *
+ * Description:
+ * Destroy semaphores.
+ *
+ * Parameters:
+ * priv - Pointer to the internal driver state structure.
+ *
+ ****************************************************************************/
+
+static void mpfs_i2c_sem_destroy(struct mpfs_i2c_priv_s *priv)
+{
+ nxsem_destroy(&priv->sem_excl);
+ nxsem_destroy(&priv->sem_isr);
+}
+
+/****************************************************************************
+ * Name: mpfs_i2c_sem_init
+ *
+ * Description:
+ * Initialize semaphores.
+ *
+ * Parameters:
+ * priv - Pointer to the internal driver state structure.
+ *
+ ****************************************************************************/
+
+static inline void mpfs_i2c_sem_init(struct mpfs_i2c_priv_s *priv)
+{
+ nxsem_init(&priv->sem_excl, 0, 1);
+
+ /* This semaphore is used for signaling and, hence, should not have
+ * priority inheritance enabled.
+ */
+
+ nxsem_init(&priv->sem_isr, 0, 0);
+ nxsem_set_protocol(&priv->sem_isr, SEM_PRIO_NONE);
+}
+
+/****************************************************************************
+ * Name: mpfs_i2c_irq
+ *
+ * Description:
+ * This is the common I2C interrupt handler. It will be invoked when an
+ * interrupt is received on the device.
+ *
+ * Parameters:
+ * cpuint - CPU interrupt index
+ * context - Context data from the ISR
+ * arg - Opaque pointer to the internal driver state structure.
+ *
+ * Returned Value:
+ * Zero (OK) is returned on success. A negated errno value is returned on
+ * failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_i2c_irq(int cpuint, void *context, void *arg)
+{
+ struct mpfs_i2c_priv_s *priv = (struct mpfs_i2c_priv_s *)arg;
+ struct i2c_msg_s *msg = &priv->msgv[priv->msgid];
+ volatile uint32_t status;
+ uint8_t clear_irq = 1u;
+
+ DEBUGASSERT(msg != NULL);
+
+ status = getreg32(MPFS_I2C_STATUS);
+
+ switch (status)
+ {
+ case MPFS_I2C_ST_START:
+ case MPFS_I2C_ST_RESTART:
+ modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_STA_MASK, 0);
+ putreg32((priv->target_addr << 1) | (msg->flags & I2C_M_READ),
+ MPFS_I2C_DATA);
+
+ if (msg->flags & I2C_M_READ)
+ {
+ priv->rx_idx = 0u;
+ }
+ else
+ {
+ priv->tx_idx = 0u;
+ }
+ break;
+
+ case MPFS_I2C_ST_LOST_ARB:
+ modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_STA_MASK,
+ MPFS_I2C_CTRL_STA_MASK);
+ break;
+
+ case MPFS_I2C_ST_SLAW_NACK:
+ modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_STO_MASK,
+ MPFS_I2C_CTRL_STO_MASK);
+ priv->status = MPFS_I2C_FAILED;
+ break;
+
+ case MPFS_I2C_ST_SLAW_ACK:
+ case MPFS_I2C_ST_TX_DATA_ACK:
+ if (priv->tx_idx < priv->tx_size)
+ {
+ DEBUGASSERT(priv->tx_buffer != NULL);
+ putreg32(priv->tx_buffer[priv->tx_idx], MPFS_I2C_DATA);
+ priv->tx_idx++;
+ }
+ else if (msg->flags & I2C_M_NOSTOP)
+ {
+ modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_STA_MASK,
+ MPFS_I2C_CTRL_STA_MASK);
+
+ /* Jump to the next message */
+
+ priv->msgid++;
+ }
+ else
+ {
+ /* Send stop condition */
+
+ modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_STO_MASK,
+ MPFS_I2C_CTRL_STO_MASK);
+ priv->status = MPFS_I2C_SUCCESS;
+ }
+ break;
+
+ case MPFS_I2C_ST_TX_DATA_NACK:
+ modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_STO_MASK,
+ MPFS_I2C_CTRL_STO_MASK);
+ priv->status = MPFS_I2C_FAILED;
+ break;
+
+ case MPFS_I2C_ST_SLAR_ACK: /* SLA+R tx'ed. */
+ if (priv->rx_size > 1u)
+ {
+ modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_AA_MASK,
+ MPFS_I2C_CTRL_AA_MASK);
+ }
+ else if (priv->rx_size == 1u)
+ {
+ modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_AA_MASK, 0);
+ }
+ else /* priv->rx_size == 0u */
+ {
+ modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_AA_MASK,
+ MPFS_I2C_CTRL_AA_MASK);
+ modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_STO_MASK,
+ MPFS_I2C_CTRL_STO_MASK);
+ priv->status = MPFS_I2C_SUCCESS;
+ }
+ break;
+
+ case MPFS_I2C_ST_SLAR_NACK: /* SLA+R tx'ed; send a stop condition */
+ modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_STO_MASK,
+ MPFS_I2C_CTRL_STO_MASK);
+ priv->status = MPFS_I2C_FAILED;
+ break;
+
+ case MPFS_I2C_ST_RX_DATA_ACK:
+
+ /* Data byte received, ACK returned */
+
+ DEBUGASSERT(priv->rx_buffer != NULL);
+ priv->rx_buffer[priv->rx_idx] = (uint8_t)getreg32(MPFS_I2C_DATA);
+ priv->rx_idx++;
+
+ if (priv->rx_idx >= (priv->rx_size - 1u))
+ {
+ modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_AA_MASK, 0);
+ }
+ break;
+
+ case MPFS_I2C_ST_RX_DATA_NACK:
+
+ /* Data byte received, NACK returned */
+
+ DEBUGASSERT(priv->rx_buffer != NULL);
+ priv->rx_buffer[priv->rx_idx] = (uint8_t)getreg32(MPFS_I2C_DATA);
+
+ modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_STO_MASK,
+ MPFS_I2C_CTRL_STO_MASK);
+
+ priv->status = MPFS_I2C_SUCCESS;
+ break;
+
+ case MPFS_I2C_ST_RESET_ACTIVATED:
+ case MPFS_I2C_ST_BUS_ERROR: /* Bus errors */
+ default:
+
+ modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_STA_MASK, 0);
+
+ if (priv->status == MPFS_I2C_IN_PROGRESS)
+ {
+ priv->status = MPFS_I2C_FAILED;
+ }
+
+ break;
+ }
+
+ if (priv->status != MPFS_I2C_IN_PROGRESS)
+ {
+ nxsem_post(&priv->sem_isr);
+ }
+
+ if (clear_irq)
+ {
+ /* Clear interrupt. */
+
+ modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_SI_MASK, 0);
+ }
+
+ /* Read back the status register to ensure the last I2C registers write
+ * took place in a system built around a bus making use of posted writes.
+ */
+
+ status = getreg32(MPFS_I2C_STATUS);
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: mpfs_i2c_sendstart
+ *
+ * Description:
+ * Send I2C start condition and enable the PLIC irq
+ *
+ * Parameters:
+ * priv - Pointer to the internal driver state structure.
+ *
+ ****************************************************************************/
+
+static void mpfs_i2c_sendstart(struct mpfs_i2c_priv_s *priv)
+{
+ uint32_t primask;
+
+ primask = mpfs_disable_interrupts();
+
+ modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_STA_MASK, MPFS_I2C_CTRL_STA_MASK);
+
+ up_enable_irq(priv->plic_irq);
+
+ mpfs_restore_interrupts(primask);
+}
+
+static int mpfs_i2c_transfer(struct i2c_master_s *dev,
+ struct i2c_msg_s *msgs,
+ int count)
+{
+ struct mpfs_i2c_priv_s *priv = (struct mpfs_i2c_priv_s *)dev;
+ int ret = OK;
+
+ i2cinfo("Starting transfer request of %d message(s):\n", count);
+ DEBUGASSERT(count > 0);
+
+ ret = mpfs_i2c_sem_wait(priv);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (priv->status != MPFS_I2C_SUCCESS)
+ {
+ priv->status = MPFS_I2C_SUCCESS;
+ }
+
+ priv->msgv = msgs;
+
+ for (int i = 0; i < count; i++)
+ {
+ priv->bytes = 0;
+ priv->msgid = i;
+ priv->status = MPFS_I2C_IN_PROGRESS;
+ priv->target_addr = msgs[i].addr;
+
+ if (msgs[i].flags & I2C_M_READ)
+ {
+ priv->rx_buffer = msgs[i].buffer;
+ priv->rx_size = msgs[i].length;
+ priv->rx_idx = 0;
+ }
+ else
+ {
+ priv->tx_buffer = msgs[i].buffer;
+ priv->tx_size = msgs[i].length;
+ priv->tx_idx = 0;
+
+ if (msgs[i].flags & I2C_M_NOSTOP)
+ {
+ /* Support only write + read combinations */
+
+ DEBUGASSERT(!(msgs[i].flags & I2C_M_READ));
+
+ /* Combine write + read transaction into one */
+
+ if (((i + 1) < count) && (msgs[i + 1].flags & I2C_M_READ))
+ {
+ priv->rx_buffer = msgs[i + 1].buffer;
+ priv->rx_size = msgs[i + 1].length;
+ priv->rx_idx = 0;
+ i++;
+ }
+ }
+ }
+
+ i2cinfo("Sending message %" PRIu8 "...\n", priv->msgid);
+
+ mpfs_i2c_sendstart(priv);
+
+ if (mpfs_i2c_sem_waitdone(priv) < 0)
+ {
+ i2cinfo("Message %" PRIu8 " timed out.\n", priv->msgid);
+ ret = -ETIMEDOUT;
+ break;
+ }
+ else
+ {
+ if (priv->status != MPFS_I2C_SUCCESS)
+ {
+ i2cinfo("Transfer error %" PRIu32 "\n", priv->status);
+ ret = -EIO;
+ break;
+ }
+ else
+ {
+ priv->status = MPFS_I2C_SUCCESS;
+ ret = OK;
+ }
+ }
+
+ i2cinfo("Message %" PRIu8 " transfer complete.\n", priv->msgid);
+ }
+
+ mpfs_i2c_sem_post(priv);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mpfs_i2cbus_initialize
+ *
+ * Description:
+ * Initialize the selected I2C port. And return a pointer to an unique
+ * instance of struct i2c_master_s. This function may be called to obtain
+ * multiple instances of the interface.
+ *
+ * Parameters:
+ * port - Port number of the I2C interface to be initialized.
+ *
+ * Returned Value:
+ * Pointer to valid I2C device structure is returned on success.
+ * A NULL pointer is returned on failure.
+ *
+ ****************************************************************************/
+
+struct i2c_master_s *mpfs_i2cbus_initialize(int port)
+{
+ struct mpfs_i2c_priv_s *priv;
+ irqstate_t flags;
+ int ret;
+
+ switch (port)
+ {
+#ifdef CONFIG_MPFS_I2C0
+ case 0:
+ priv = &g_mpfs_i2c0_lo_priv;
+ break;
+#endif /* CONFIG_MPFS_I2C0 */
+#ifdef CONFIG_MPFS_I2C0
+ case 1:
+ priv = &g_mpfs_i2c1_lo_priv;
+ break;
+#endif /* CONFIG_MPFS_I2C0 */
+ default:
+ return NULL;
+ }
+
+ flags = enter_critical_section();
+
+ if ((volatile int)priv->refs++ != 0)
+ {
+ leave_critical_section(flags);
+
+ i2cinfo("Returning previously initialized I2C bus. "
+ "Handler: %" PRIxPTR "\n",
+ (uintptr_t)priv);
+
+ return (struct i2c_master_s *)priv;
+ }
+
+ ret = irq_attach(priv->plic_irq, mpfs_i2c_irq, priv);
+ if (ret != OK)
+ {
+ leave_critical_section(flags);
+ return NULL;
+ }
+
+ mpfs_i2c_sem_init(priv);
+ mpfs_i2c_init(priv);
+
+ leave_critical_section(flags);
+
+ i2cinfo("I2C bus initialized! Handler: %" PRIxPTR "\n", (uintptr_t)priv);
+
+ return (struct i2c_master_s *)priv;
+}
+
+/****************************************************************************
+ * Name: mpfs_i2cbus_uninitialize
+ *
+ * Description:
+ * De-initialize the selected I2C bus.
+ *
+ * Parameters:
+ * dev - Device structure as returned by
+ * mpfs_i2cbus_initialize()
+ *
+ * Returned Value:
+ * OK is returned on success. ERROR is returned when internal reference
+ * count mismatches or dev points to invalid hardware device.
+ *
+ ****************************************************************************/
+
+int mpfs_i2cbus_uninitialize(struct i2c_master_s *dev)
+{
+ struct mpfs_i2c_priv_s *priv = (struct mpfs_i2c_priv_s *)dev;
+ irqstate_t flags;
+
+ DEBUGASSERT(dev);
+
+ if (priv->refs == 0)
+ {
+ return ERROR;
+ }
+
+ flags = enter_critical_section();
+
+ if (--priv->refs)
+ {
+ leave_critical_section(flags);
+ return OK;
+ }
+
+ leave_critical_section(flags);
+
+ mpfs_i2c_deinit(priv);
+ mpfs_i2c_sem_destroy(priv);
+
+ return OK;
+}
diff --git a/arch/risc-v/src/mpfs/mpfs_i2c.h b/arch/risc-v/src/mpfs/mpfs_i2c.h
new file mode 100755
index 0000000..3761acb
--- /dev/null
+++ b/arch/risc-v/src/mpfs/mpfs_i2c.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_i2c.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_MPFS_I2C_H
+#define __ARCH_RISCV_SRC_MPFS_I2C_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/i2c/i2c_master.h>
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mpfs_i2cbus_initialize
+ *
+ * Description:
+ * Initialize the selected I2C port. And return a pointer to an unique
+ * instance of struct i2c_master_s. This function may be called to obtain
+ * multiple instances of the interface.
+ *
+ * Input Parameters:
+ * port - Port number of the I2C interface to be initialized.
+ *
+ * Returned Value:
+ * Pointer to valid I2C device structure is returned on success.
+ * A NULL pointer is returned on failure.
+ *
+ ****************************************************************************/
+
+struct i2c_master_s *mpfs_i2cbus_initialize(int port);
+
+/****************************************************************************
+ * Name: mpfs_i2cbus_uninitialize
+ *
+ * Description:
+ * De-initialize the selected I2C port and power down the device.
+ *
+ * Input Parameters:
+ * dev - Device structure as returned by
+ * mpfs_i2cbus_initialize()
+ *
+ * Returned Value:
+ * OK is returned on success. ERROR is returned when internal reference
+ * count mismatches or dev points to invalid hardware device.
+ *
+ ****************************************************************************/
+
+int mpfs_i2cbus_uninitialize(struct i2c_master_s *dev);
+
+#ifdef __cplusplus
+}
+#endif
+#undef EXTERN
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ARCH_RISCV_SRC_MPFS_I2C_H */
diff --git a/boards/risc-v/mpfs/icicle/src/Makefile b/boards/risc-v/mpfs/icicle/src/Makefile
index 462e28c..fc67532 100755
--- a/boards/risc-v/mpfs/icicle/src/Makefile
+++ b/boards/risc-v/mpfs/icicle/src/Makefile
@@ -26,6 +26,10 @@ ifeq ($(CONFIG_LIB_BOARDCTL),y)
CSRCS += mpfs_appinit.c
endif
+ifeq ($(CONFIG_I2C),y)
+CSRCS += mpfs_i2c.c
+endif
+
ifeq ($(CONFIG_ARCH_LEDS),y)
CSRCS += mpfs_autoleds.c
endif
diff --git a/boards/risc-v/mpfs/icicle/src/mpfs_bringup.c b/boards/risc-v/mpfs/icicle/src/mpfs_bringup.c
index 0563248..983ba94 100755
--- a/boards/risc-v/mpfs/icicle/src/mpfs_bringup.c
+++ b/boards/risc-v/mpfs/icicle/src/mpfs_bringup.c
@@ -48,6 +48,17 @@ int mpfs_bringup(void)
{
int ret = OK;
+#if defined(CONFIG_I2C_DRIVER)
+ /* Configure I2C peripheral interfaces */
+
+ ret = mpfs_board_i2c_init();
+
+ if (ret < 0)
+ {
+ syslog(LOG_ERR, "Failed to initialize I2C driver: %d\n", ret);
+ }
+#endif
+
#ifdef CONFIG_FS_PROCFS
/* Mount the procfs file system */
diff --git a/boards/risc-v/mpfs/icicle/src/mpfs_bringup.c b/boards/risc-v/mpfs/icicle/src/mpfs_i2c.c
old mode 100755
new mode 100644
similarity index 57%
copy from boards/risc-v/mpfs/icicle/src/mpfs_bringup.c
copy to boards/risc-v/mpfs/icicle/src/mpfs_i2c.c
index 0563248..f08b50f
--- a/boards/risc-v/mpfs/icicle/src/mpfs_bringup.c
+++ b/boards/risc-v/mpfs/icicle/src/mpfs_i2c.c
@@ -1,5 +1,5 @@
/****************************************************************************
- * boards/risc-v/mpfs/icicle/src/mpfs_bringup.c
+ * boards/risc-v/mpfs/icicle/src/mpfs_i2c.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@@ -24,50 +24,79 @@
#include <nuttx/config.h>
-#include <sys/mount.h>
-#include <stdbool.h>
-#include <stdio.h>
#include <debug.h>
#include <errno.h>
+#include <sys/types.h>
+#include <nuttx/i2c/i2c_master.h>
-#include <nuttx/board.h>
-#include <nuttx/drivers/ramdisk.h>
-
-#include "mpfsicicle.h"
-#include "mpfs.h"
+#include "mpfs_i2c.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
- * Name: mpfs_bringup
+ * 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 mpfs_bringup(void)
+int mpfs_board_i2c_init(void)
{
int ret = OK;
-#ifdef CONFIG_FS_PROCFS
- /* Mount the procfs file system */
+#if defined(CONFIG_MPFS_I2C0) || defined(CONFIG_MPFS_I2C1)
+#ifdef CONFIG_I2C_DRIVER
+ int bus = 0;
+#endif
+ FAR struct i2c_master_s *i2c;
+#endif
+
+#ifdef CONFIG_MPFS_I2C0
+ i2c = mpfs_i2cbus_initialize(0);
- ret = mount(NULL, "/proc", "procfs", 0, NULL);
+ if (i2c == NULL)
+ {
+ i2cerr("ERROR: Failed to init I2C0 interface\n");
+ return -ENODEV;
+ }
+
+#ifdef CONFIG_I2C_DRIVER
+ ret = i2c_register(i2c, bus++);
if (ret < 0)
{
- serr("ERROR: Failed to mount procfs at %s: %d\n", "/proc", ret);
+ i2cerr("ERROR: Failed to register I2C0 driver: %d\n", ret);
+ mpfs_i2cbus_uninitialize(i2c);
+ return ret;
}
#endif
+#endif
-#if defined(CONFIG_MPFS_SPI0) || defined(CONFIG_MPFS_SPI1)
- /* Configure SPI peripheral interfaces */
+#ifdef CONFIG_MPFS_I2C1
+ i2c = mpfs_i2cbus_initialize(1);
- ret = mpfs_board_spi_init();
+ if (i2c == NULL)
+ {
+ i2cerr("ERROR: Failed to init I2C1 interface\n");
+ return -ENODEV;
+ }
+#ifdef CONFIG_I2C_DRIVER
+ ret = i2c_register(i2c, bus);
if (ret < 0)
{
- syslog(LOG_ERR, "Failed to initialize SPI driver: %d\n", ret);
+ i2cerr("ERROR: Failed to register I2C1 driver: %d\n", ret);
+ mpfs_i2cbus_uninitialize(i2c);
+ return ret;
}
#endif
+#endif
return ret;
}
diff --git a/boards/risc-v/mpfs/icicle/src/mpfsicicle.h b/boards/risc-v/mpfs/icicle/src/mpfsicicle.h
index 6666cb8..2d00e3a 100755
--- a/boards/risc-v/mpfs/icicle/src/mpfsicicle.h
+++ b/boards/risc-v/mpfs/icicle/src/mpfsicicle.h
@@ -43,5 +43,6 @@
int mpfs_bringup(void);
int mpfs_board_spi_init(void);
+int mpfs_board_i2c_init(void);
#endif /* __BOARDS_RISCV_ICICLE_MPFS_SRC_MPFSICICLE_H */