* arch/arm/src/nrf53/nrf53_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 <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/irq.h>
#include <nuttx/arch.h>
#include <nuttx/mutex.h>
#include <nuttx/kmalloc.h>
#include <arch/board/board.h>
#include "arm_internal.h"
#include "nrf53_gpio.h"
#include "nrf53_i2c.h"
#include "hardware/nrf53_twi.h"
#include "hardware/nrf53_utils.h"
* Private Types
****************************************************************************/
struct nrf53_i2c_priv_s
{
const struct i2c_ops_s *ops;
uint32_t base;
uint32_t scl_pin;
uint32_t sda_pin;
int refs;
int status;
#ifndef CONFIG_I2C_POLLED
uint32_t irq;
#endif
uint8_t msgc;
struct i2c_msg_s *msgv;
uint8_t *ptr;
#ifdef CONFIG_NRF53_I2C_MASTER_COPY_BUF_SIZE
uint8_t copy_buf[CONFIG_NRF53_I2C_MASTER_COPY_BUF_SIZE];
#endif
uint32_t freq;
int dcnt;
uint16_t flags;
uint16_t addr;
mutex_t lock;
#ifndef CONFIG_I2C_POLLED
sem_t sem_isr;
#endif
};
* Private Function Prototypes
****************************************************************************/
static inline void nrf53_i2c_putreg(struct nrf53_i2c_priv_s *priv,
uint32_t offset,
uint32_t value);
static inline uint32_t nrf53_i2c_getreg(struct nrf53_i2c_priv_s *priv,
uint32_t offset);
static int nrf53_i2c_transfer(struct i2c_master_s *dev,
struct i2c_msg_s *msgs,
int count);
#ifdef CONFIG_I2C_RESET
static int nrf53_i2c_reset(struct i2c_master_s *dev);
#endif
#ifndef CONFIG_I2C_POLLED
static int nrf53_i2c_isr(int irq, void *context, void *arg);
#endif
static int nrf53_i2c_deinit(struct nrf53_i2c_priv_s *priv);
static int nrf53_i2c_init(struct nrf53_i2c_priv_s *priv);
* Private Data
****************************************************************************/
static const struct i2c_ops_s g_nrf53_i2c_ops =
{
.transfer = nrf53_i2c_transfer
#ifdef CONFIG_I2C_RESET
, .reset = nrf53_i2c_reset
#endif
};
#ifdef CONFIG_NRF53_I2C0_MASTER
static struct nrf53_i2c_priv_s g_nrf53_i2c0_priv =
{
.ops = &g_nrf53_i2c_ops,
.base = NRF53_TWIM0_BASE,
.scl_pin = BOARD_I2C0_SCL_PIN,
.sda_pin = BOARD_I2C0_SDA_PIN,
.refs = 0,
.lock = NXMUTEX_INITIALIZER,
#ifndef CONFIG_I2C_POLLED
.sem_isr = SEM_INITIALIZER(0),
.irq = NRF53_IRQ_SERIAL0,
#endif
.msgc = 0,
.msgv = NULL,
.ptr = NULL,
.freq = 0,
.dcnt = 0,
.flags = 0,
.addr = 0,
};
#endif
#ifdef CONFIG_NRF53_I2C1_MASTER
static struct nrf53_i2c_priv_s g_nrf53_i2c1_priv =
{
.ops = &g_nrf53_i2c_ops,
.base = NRF53_TWIM1_BASE,
.scl_pin = BOARD_I2C1_SCL_PIN,
.sda_pin = BOARD_I2C1_SDA_PIN,
.refs = 0,
.lock = NXMUTEX_INITIALIZER,
#ifndef CONFIG_I2C_POLLED
.sem_isr = SEM_INITIALIZER(0),
.irq = NRF53_IRQ_SERIAL1,
#endif
.msgc = 0,
.msgv = NULL,
.ptr = NULL,
.freq = 0,
.dcnt = 0,
.flags = 0,
.addr = 0,
};
#endif
#ifdef CONFIG_NRF53_I2C2_MASTER
static struct nrf53_i2c_priv_s g_nrf53_i2c2_priv =
{
.ops = &g_nrf53_i2c_ops,
.base = NRF53_TWIM2_BASE,
.scl_pin = BOARD_I2C2_SCL_PIN,
.sda_pin = BOARD_I2C2_SDA_PIN,
.refs = 0,
.lock = NXMUTEX_INITIALIZER,
#ifndef CONFIG_I2C_POLLED
.sem_isr = SEM_INITIALIZER(0),
.irq = NRF53_IRQ_SERIAL2,
#endif
.msgc = 0,
.msgv = NULL,
.ptr = NULL,
.freq = 0,
.dcnt = 0,
.flags = 0,
.addr = 0,
};
#endif
#ifdef CONFIG_NRF53_I2C3_MASTER
static struct nrf53_i2c_priv_s g_nrf53_i2c3_priv =
{
.ops = &g_nrf53_i2c_ops,
.base = NRF53_TWIM2_BASE,
.scl_pin = BOARD_I2C3_SCL_PIN,
.sda_pin = BOARD_I2C3_SDA_PIN,
.refs = 0,
.lock = NXMUTEX_INITIALIZER,
#ifndef CONFIG_I2C_POLLED
.sem_isr = SEM_INITIALIZER(0),
.irq = NRF53_IRQ_SERIAL3,
#endif
.msgc = 0,
.msgv = NULL,
.ptr = NULL,
.freq = 0,
.dcnt = 0,
.flags = 0,
.addr = 0,
};
#endif
* Private Functions
****************************************************************************/
* Name: nrf53_i2c_putreg
*
* Description:
* Put a 32-bit register value by offset
*
****************************************************************************/
static inline void nrf53_i2c_putreg(struct nrf53_i2c_priv_s *priv,
uint32_t offset,
uint32_t value)
{
putreg32(value, priv->base + offset);
}
* Name: nrf53_i2c_getreg
*
* Description:
* Get a 32-bit register value by offset
*
****************************************************************************/
static inline uint32_t nrf53_i2c_getreg(struct nrf53_i2c_priv_s *priv,
uint32_t offset)
{
return getreg32(priv->base + offset);
}
* Name: nrf53_i2c_transfer
*
* Description:
* Generic I2C transfer function
*
****************************************************************************/
static int nrf53_i2c_transfer(struct i2c_master_s *dev,
struct i2c_msg_s *msgs,
int count)
{
struct nrf53_i2c_priv_s *priv = (struct nrf53_i2c_priv_s *)dev;
uint32_t regval = 0;
int ret = OK;
#ifndef CONFIG_NRF53_I2C_MASTER_DISABLE_NOSTART
uint8_t *pack_buf = NULL;
#endif
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return ret;
}
priv->dcnt = 0;
priv->ptr = NULL;
priv->msgv = msgs;
priv->msgc = count;
priv->status = OK;
i2cinfo("I2C TRANSFER count=%d\n", count);
if (priv->msgv->frequency != priv->freq)
{
switch (priv->msgv->frequency)
{
case 100000:
{
regval = TWIM_FREQUENCY_100KBPS;
break;
}
case 250000:
{
regval = TWIM_FREQUENCY_250KBPS;
break;
}
case 400000:
{
regval = TWIM_FREQUENCY_400KBPS;
break;
}
default:
{
ret = -EINVAL;
goto errout;
}
}
nrf53_i2c_putreg(priv, NRF53_TWIM_FREQUENCY_OFFSET, regval);
priv->freq = priv->msgv->frequency;
}
do
{
priv->ptr = priv->msgv->buffer;
priv->dcnt = priv->msgv->length;
priv->flags = priv->msgv->flags;
priv->addr = priv->msgv->addr;
i2cinfo("ptr=%p dcnt=%d flags=%d addr=%d\n",
priv->ptr, priv->dcnt, priv->flags, priv->addr);
regval = priv->addr;
nrf53_i2c_putreg(priv, NRF53_TWIM_ADDRESS_OFFSET, regval);
if ((priv->flags & I2C_M_READ) == 0)
{
#ifndef CONFIG_NRF53_I2C_MASTER_DISABLE_NOSTART
if (priv->msgc > 1)
{
if (priv->msgv[1].flags & I2C_M_NOSTART)
{
DEBUGASSERT(priv->msgc < 3);
if ((priv->msgv[0].length +
priv->msgv[1].length) <=
CONFIG_NRF53_I2C_MASTER_COPY_BUF_SIZE)
{
pack_buf = priv->copy_buf;
}
else
{
pack_buf = kmm_malloc(priv->msgv[0].length +
priv->msgv[1].length);
if (pack_buf == NULL)
{
return -ENOMEM;
}
}
memcpy(pack_buf, priv->msgv[0].buffer,
priv->msgv[0].length);
memcpy(pack_buf + priv->msgv[0].length,
priv->msgv[1].buffer, priv->msgv[1].length);
priv->ptr = pack_buf;
priv->dcnt = priv->msgv[0].length + priv->msgv[1].length;
priv->msgc -= 1;
priv->msgv += 1;
}
}
#else
if (priv->msgc > 1)
{
if (priv->msgv[1].flags & I2C_M_NOSTART)
{
DEBUGPANIC();
}
}
#endif
regval = (uint32_t)priv->ptr;
DEBUGASSERT(nrf53_easydma_valid(regval));
nrf53_i2c_putreg(priv, NRF53_TWIM_TXDPTR_OFFSET, regval);
regval = priv->dcnt;
nrf53_i2c_putreg(priv, NRF53_TWIM_TXDMAXCNT_OFFSET, regval);
nrf53_i2c_putreg(priv, NRF53_TWIM_TASKS_STARTTX_OFFSET, 1);
#ifdef CONFIG_I2C_POLLED
while (nrf53_i2c_getreg(priv,
NRF53_TWIM_EVENTS_LASTTX_OFFSET) != 1);
while (1)
{
regval = nrf53_i2c_getreg(priv,
NRF53_TWIM_ERRORSRC_OFFSET) & 0x7;
if (regval != 0)
{
i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval);
ret = -1;
nrf53_i2c_putreg(priv,
NRF53_TWIM_ERRORSRC_OFFSET, 0x7);
goto errout;
}
if (nrf53_i2c_getreg(priv,
NRF53_TWIM_EVENTS_LASTTX_OFFSET) == 1)
{
break;
}
}
nrf53_i2c_putreg(priv, NRF53_TWIM_EVENTS_LASTTX_OFFSET, 0);
#else
nxsem_wait(&priv->sem_isr);
if (priv->status < 0)
{
ret = priv->status;
goto errout;
}
#endif
}
else
{
regval = (uint32_t)priv->ptr;
DEBUGASSERT(nrf53_easydma_valid(regval));
nrf53_i2c_putreg(priv, NRF53_TWIM_RXDPTR_OFFSET, regval);
regval = priv->dcnt;
nrf53_i2c_putreg(priv, NRF53_TWIM_RXDMAXCNT_OFFSET, regval);
nrf53_i2c_putreg(priv, NRF53_TWIM_TASKS_STARTRX_OFFSET, 1);
#ifdef CONFIG_I2C_POLLED
while (1)
{
regval = nrf53_i2c_getreg(priv,
NRF53_TWIM_ERRORSRC_OFFSET) & 0x7;
if (regval != 0)
{
i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval);
ret = -1;
nrf53_i2c_putreg(priv,
NRF53_TWIM_ERRORSRC_OFFSET, 0x7);
goto errout;
}
if (nrf53_i2c_getreg(priv,
NRF53_TWIM_EVENTS_LASTRX_OFFSET) == 1)
{
break;
}
}
nrf53_i2c_putreg(priv, NRF53_TWIM_EVENTS_LASTRX_OFFSET, 0);
#else
nxsem_wait(&priv->sem_isr);
if (priv->status < 0)
{
ret = priv->status;
goto errout;
}
#endif
}
priv->msgc -= 1;
priv->msgv += 1;
}
while (priv->msgc > 0);
nrf53_i2c_putreg(priv, NRF53_TWIM_TASKS_STOP_OFFSET, 1);
#ifdef CONFIG_I2C_POLLED
while (1)
{
regval = nrf53_i2c_getreg(priv,
NRF53_TWIM_ERRORSRC_OFFSET) & 0x7;
if (regval != 0)
{
i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval);
ret = -1;
nrf53_i2c_putreg(priv,
NRF53_TWIM_ERRORSRC_OFFSET, 0x7);
goto errout;
}
if (nrf53_i2c_getreg(priv,
NRF53_TWIM_EVENTS_STOPPED_OFFSET) == 1)
{
break;
}
}
nrf53_i2c_putreg(priv, NRF53_TWIM_EVENTS_STOPPED_OFFSET, 0);
#else
nxsem_wait(&priv->sem_isr);
if (priv->status < 0)
{
ret = priv->status;
goto errout;
}
#endif
errout:
#ifndef CONFIG_NRF53_I2C_MASTER_DISABLE_NOSTART
if (pack_buf != NULL && pack_buf != priv->copy_buf)
{
kmm_free(pack_buf);
}
#endif
nxmutex_unlock(&priv->lock);
return ret;
}
* Name: nrf53_i2c_reset
*
* Description:
* Perform an I2C bus reset in an attempt to break loose stuck I2C devices.
*
* Input Parameters:
* dev - Device-specific state data
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
#ifdef CONFIG_I2C_RESET
static int nrf53_i2c_reset(struct i2c_master_s *dev)
{
#error not implemented
}
#endif
* Name: nrf53_i2c_isr
*
* Description:
* Common I2C interrupt service routine
*
****************************************************************************/
#ifndef CONFIG_I2C_POLLED
static int nrf53_i2c_isr(int irq, void *context, void *arg)
{
struct nrf53_i2c_priv_s *priv = (struct nrf53_i2c_priv_s *)arg;
uint32_t regval = 0;
priv->status = OK;
if ((priv->flags & I2C_M_READ) == 0)
{
if (nrf53_i2c_getreg(priv, NRF53_TWIM_EVENTS_LASTTX_OFFSET) == 1)
{
i2cinfo("I2C LASTTX\n");
nxsem_post(&priv->sem_isr);
nrf53_i2c_putreg(priv, NRF53_TWIM_EVENTS_LASTTX_OFFSET, 0);
return OK;
}
}
else
{
if (nrf53_i2c_getreg(priv, NRF53_TWIM_EVENTS_LASTRX_OFFSET) == 1)
{
i2cinfo("I2C LASTRX\n");
nxsem_post(&priv->sem_isr);
nrf53_i2c_putreg(priv, NRF53_TWIM_EVENTS_LASTRX_OFFSET, 0);
return OK;
}
}
if (nrf53_i2c_getreg(priv, NRF53_TWIM_EVENTS_STOPPED_OFFSET) == 1)
{
i2cinfo("I2C STOPPED\n");
nxsem_post(&priv->sem_isr);
nrf53_i2c_putreg(priv, NRF53_TWIM_EVENTS_STOPPED_OFFSET, 0);
}
if (nrf53_i2c_getreg(priv, NRF53_TWIM_EVENTS_ERROR_OFFSET) == 1)
{
regval = nrf53_i2c_getreg(priv, NRF53_TWIM_ERRORSRC_OFFSET) & 0x7;
i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval);
if (regval & TWIM_ERRORSRC_OVERRUN)
{
priv->status = -EIO;
}
else
{
priv->status = -ENXIO;
}
nxsem_post(&priv->sem_isr);
nrf53_i2c_putreg(priv, NRF53_TWIM_EVENTS_ERROR_OFFSET, 0);
nrf53_i2c_putreg(priv, NRF53_TWIM_ERRORSRC_OFFSET, 0x7);
}
return OK;
}
#endif
* Name: nrf53_i2c_init
*
* Description:
* Setup the I2C hardware, ready for operation with defaults
*
****************************************************************************/
static int nrf53_i2c_init(struct nrf53_i2c_priv_s *priv)
{
uint32_t regval = 0;
int pin = 0;
int port = 0;
nrf53_i2c_putreg(priv, NRF53_TWIM_ENABLE_OFFSET, TWIM_ENABLE_DIS);
nrf53_gpio_config(priv->scl_pin);
nrf53_gpio_config(priv->sda_pin);
pin = GPIO_PIN_DECODE(priv->scl_pin);
port = GPIO_PORT_DECODE(priv->scl_pin);
regval = (pin << TWIM_PSELSCL_PIN_SHIFT);
regval |= (port << TWIM_PSELSCL_PORT_SHIFT);
nrf53_i2c_putreg(priv, NRF53_TWIM_PSELSCL_OFFSET, regval);
pin = GPIO_PIN_DECODE(priv->sda_pin);
port = GPIO_PORT_DECODE(priv->sda_pin);
regval = (pin << TWIM_PSELSDA_PIN_SHIFT);
regval |= (port << TWIM_PSELSDA_PORT_SHIFT);
nrf53_i2c_putreg(priv, NRF53_TWIM_PSELSDA_OFFSET, regval);
nrf53_i2c_putreg(priv, NRF53_TWIM_ENABLE_OFFSET, TWIM_ENABLE_EN);
#ifndef CONFIG_I2C_POLLED
regval = (TWIM_INT_LASTRX | TWIM_INT_LASTTX | TWIM_INT_STOPPED |
TWIM_INT_ERROR);
nrf53_i2c_putreg(priv, NRF53_TWIM_INTEN_OFFSET, regval);
irq_attach(priv->irq, nrf53_i2c_isr, priv);
up_enable_irq(priv->irq);
#endif
return OK;
}
* Name: nrf53_i2c_deinit
*
* Description:
* Shutdown the I2C hardware
*
****************************************************************************/
static int nrf53_i2c_deinit(struct nrf53_i2c_priv_s *priv)
{
nrf53_i2c_putreg(priv, NRF53_TWIM_ENABLE_OFFSET, TWIM_ENABLE_DIS);
nrf53_gpio_unconfig(priv->scl_pin);
nrf53_gpio_unconfig(priv->sda_pin);
nrf53_i2c_putreg(priv, NRF53_TWIM_PSELSCL_OFFSET, TWIM_PSELSCL_RESET);
nrf53_i2c_putreg(priv, NRF53_TWIM_PSELSDA_OFFSET, TWIM_PSELSDA_RESET);
#ifndef CONFIG_I2C_POLLED
up_disable_irq(priv->irq);
irq_detach(priv->irq);
#endif
return OK;
}
* Public Functions
****************************************************************************/
* Name: nrf53_i2cbus_initialize
*
* Description:
* Initialize one I2C bus
*
****************************************************************************/
struct i2c_master_s *nrf53_i2cbus_initialize(int port)
{
struct nrf53_i2c_priv_s *priv = NULL;
i2cinfo("I2C INITIALIZE port=%d\n", port);
switch (port)
{
#ifdef CONFIG_NRF53_I2C0_MASTER
case 0:
{
priv = (struct nrf53_i2c_priv_s *)&g_nrf53_i2c0_priv;
break;
}
#endif
#ifdef CONFIG_NRF53_I2C1_MASTER
case 1:
{
priv = (struct nrf53_i2c_priv_s *)&g_nrf53_i2c1_priv;
break;
}
#endif
#ifdef CONFIG_NRF53_I2C2_MASTER
case 2:
{
priv = (struct nrf53_i2c_priv_s *)&g_nrf53_i2c2_priv;
break;
}
#endif
#ifdef CONFIG_NRF53_I2C3_MASTER
case 3:
{
priv = (struct nrf53_i2c_priv_s *)&g_nrf53_i2c3_priv;
break;
}
#endif
default:
{
return NULL;
}
}
* power-up hardware and configure GPIOs.
*/
nxmutex_lock(&priv->lock);
if (priv->refs++ == 0)
{
nrf53_i2c_init(priv);
}
nxmutex_unlock(&priv->lock);
return (struct i2c_master_s *)priv;
}
* Name: nrf53_i2cbus_uninitialize
*
* Description:
* Uninitialize an I2C bus
*
****************************************************************************/
int nrf53_i2cbus_uninitialize(struct i2c_master_s *dev)
{
struct nrf53_i2c_priv_s *priv = (struct nrf53_i2c_priv_s *)dev;
DEBUGASSERT(dev);
if (priv->refs == 0)
{
return ERROR;
}
nxmutex_lock(&priv->lock);
if (--priv->refs)
{
nxmutex_unlock(&priv->lock);
return OK;
}
nrf53_i2c_deinit(priv);
nxmutex_unlock(&priv->lock);
return OK;
}