mirror of
https://github.com/apache/nuttx.git
synced 2026-08-14 17:03:29 +00:00
mpfs: add i2c driver
This adds mpfs i2c driver. Signed-off-by: Eero Nurkkala <eero.nurkkala@offcode.fi>
This commit is contained in:
parent
361703a881
commit
1bce864ef7
10 changed files with 1094 additions and 1 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
60
arch/risc-v/src/mpfs/hardware/mpfs_i2c.h
Executable file
60
arch/risc-v/src/mpfs/hardware/mpfs_i2c.h
Executable 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
810
arch/risc-v/src/mpfs/mpfs_i2c.c
Executable 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
93
arch/risc-v/src/mpfs/mpfs_i2c.h
Executable 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 */
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
102
boards/risc-v/mpfs/icicle/src/mpfs_i2c.c
Normal file
102
boards/risc-v/mpfs/icicle/src/mpfs_i2c.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue