mpfs: add i2c driver

This adds mpfs i2c driver.

Signed-off-by: Eero Nurkkala <eero.nurkkala@offcode.fi>
This commit is contained in:
Eero Nurkkala 2021-05-12 12:13:14 +03:00 committed by Xiang Xiao
parent 361703a881
commit 1bce864ef7
10 changed files with 1094 additions and 1 deletions

View file

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

View file

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

View file

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

View file

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

810
arch/risc-v/src/mpfs/mpfs_i2c.c Executable file
View file

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

93
arch/risc-v/src/mpfs/mpfs_i2c.h Executable file
View file

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

View file

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

View file

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

View file

@ -0,0 +1,102 @@
/****************************************************************************
* 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
* 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 <debug.h>
#include <errno.h>
#include <sys/types.h>
#include <nuttx/i2c/i2c_master.h>
#include "mpfs_i2c.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 mpfs_board_i2c_init(void)
{
int ret = OK;
#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);
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)
{
i2cerr("ERROR: Failed to register I2C0 driver: %d\n", ret);
mpfs_i2cbus_uninitialize(i2c);
return ret;
}
#endif
#endif
#ifdef CONFIG_MPFS_I2C1
i2c = mpfs_i2cbus_initialize(1);
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)
{
i2cerr("ERROR: Failed to register I2C1 driver: %d\n", ret);
mpfs_i2cbus_uninitialize(i2c);
return ret;
}
#endif
#endif
return ret;
}

View file

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