nuttx/arch/arm/src/cxd56xx/cxd56_i2c.c
Gregory Nutt 037c9ea0a4 arch/arm: Rename all up_*.h files to arm_*.h
Summary

The naming standard at https://cwiki.apache.org/confluence/display/NUTTX/Naming+FAQ requires that all MCU-private files begin with the name of the architecture, not up_.

This PR addresses only these name changes for the up_*.h files.  There are only three, but almost 1680 files that include them:

    up_arch.h
    up_internal.h
    up_vfork.h

The only change to the files is from including up_arch.h to arm_arch.h (for example).

The entire job required to be compatible with that Naming Convention will also require changing the naming of the up_() functions that are used only within arch/arm and board/arm.

Impact

There should be not impact of this change (other that one step toward more consistent naming).

Testing

stm32f4discovery:netnsh
2020-05-01 03:43:44 +01:00

1107 lines
27 KiB
C

/****************************************************************************
* arch/arm/src/cxd56xx/cxd56_i2c.c
*
* Copyright 2018 Sony Semiconductor Solutions Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name of Sony Semiconductor Solutions Corporation nor
* the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <debug.h>
#include <assert.h>
#include <nuttx/arch.h>
#include <nuttx/i2c/i2c_master.h>
#include <nuttx/irq.h>
#include <arch/board/board.h>
#include "chip.h"
#include "arm_arch.h"
#include "arm_internal.h"
#include "cxd56_clock.h"
#include "cxd56_i2c.h"
#include "hardware/cxd56_i2c.h"
#include "cxd56_pinconfig.h"
#if defined(CONFIG_CXD56_I2C0_SCUSEQ) || defined(CONFIG_CXD56_I2C1_SCUSEQ)
#include <arch/chip/scu.h>
#endif
#ifdef CONFIG_CXD56_I2C
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define I2C_TIMEOUT (20*1000/CONFIG_USEC_PER_TICK) /* 20 mS */
#define I2C_DEFAULT_FREQUENCY 400000
#define I2C_FIFO_MAX_SIZE 32
#define I2C_INTR_ENABLE ((INTR_STOP_DET) | \
(INTR_TX_ABRT) | \
(INTR_TX_OVER) | \
(INTR_RX_OVER) | \
(INTR_RX_UNDER))
/****************************************************************************
* Private Data
****************************************************************************/
struct cxd56_i2cdev_s
{
struct i2c_master_s dev; /* Generic I2C device */
unsigned int base; /* Base address of registers */
uint16_t irqid; /* IRQ for this device */
int8_t port; /* Port number */
uint32_t base_freq; /* branch frequency */
sem_t mutex; /* Only one thread can access at a time */
sem_t wait; /* Place to wait for transfer completion */
WDOG_ID timeout; /* watchdog to timeout when bus hung */
uint32_t frequency; /* Current I2C frequency */
ssize_t reg_buff_offset;
ssize_t rw_size;
struct i2c_msg_s *msgs;
int error; /* Error status of each transfers */
int refs; /* Reference count */
};
/* Channel 0 as SCU_I2C0
* Channel 1 as SCU_I2C1
* Channel 2 as I2CM
*/
#ifdef CONFIG_CXD56_I2C0
static struct cxd56_i2cdev_s g_i2c0dev =
{
.port = 0,
.base = CXD56_SCU_I2C0_BASE,
.irqid = CXD56_IRQ_SCU_I2C0,
.refs = 0,
};
#endif
#ifdef CONFIG_CXD56_I2C1
static struct cxd56_i2cdev_s g_i2c1dev =
{
.port = 1,
.base = CXD56_SCU_I2C1_BASE,
.irqid = CXD56_IRQ_SCU_I2C1,
.refs = 0,
};
#endif
#ifdef CONFIG_CXD56_I2C2
static struct cxd56_i2cdev_s g_i2c2dev =
{
.port = 2,
.base = CXD56_I2CM_BASE,
.irqid = CXD56_IRQ_I2CM,
.refs = 0,
};
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
static inline uint32_t i2c_reg_read(struct cxd56_i2cdev_s *priv,
uint32_t offset);
static inline void i2c_reg_write(struct cxd56_i2cdev_s *priv,
uint32_t offset,
uint32_t val);
static inline void i2c_reg_rmw(struct cxd56_i2cdev_s *dev,
uint32_t offset,
uint32_t val, uint32_t mask);
static int cxd56_i2c_disable(struct cxd56_i2cdev_s *priv);
static void cxd56_i2c_enable(struct cxd56_i2cdev_s *priv);
static int cxd56_i2c_interrupt(int irq, FAR void *context, FAR void *arg);
static void cxd56_i2c_timeout(int argc, uint32_t arg, ...);
static void cxd56_i2c_setfrequency(struct cxd56_i2cdev_s *priv,
uint32_t frequency);
static int cxd56_i2c_transfer(FAR struct i2c_master_s *dev,
FAR struct i2c_msg_s *msgs, int count);
#ifdef CONFIG_I2C_RESET
static int cxd56_i2c_reset(FAR struct i2c_master_s * dev);
#endif
#if defined(CONFIG_CXD56_I2C0_SCUSEQ) || defined(CONFIG_CXD56_I2C1_SCUSEQ)
static int cxd56_i2c_transfer_scu(FAR struct i2c_master_s *dev,
FAR struct i2c_msg_s *msgs, int count);
#endif
/****************************************************************************
* Name: cxd56_i2c_pincontrol
*
* Description:
* Configure the I2C pin
*
* Input Parameter:
* on - true: enable pin, false: disable pin
*
****************************************************************************/
static void cxd56_i2c_pincontrol(int ch, bool on)
{
switch (ch)
{
#ifdef CONFIG_CXD56_I2C0
case 0:
if (on)
{
CXD56_PIN_CONFIGS(PINCONFS_I2C0);
}
else
{
CXD56_PIN_CONFIGS(PINCONFS_I2C0_GPIO);
}
break;
#endif /* CONFIG_CXD56_I2C0 */
#ifdef CONFIG_CXD56_I2C1
case 1:
if (on)
{
CXD56_PIN_CONFIGS(PINCONFS_PWMB_I2C1);
}
else
{
CXD56_PIN_CONFIGS(PINCONFS_PWMB_GPIO);
}
break;
#endif /* CONFIG_CXD56_I2C1 */
#ifdef CONFIG_CXD56_I2C2
case 2:
if (on)
{
CXD56_PIN_CONFIGS(PINCONFS_SPI0B_I2C2);
}
else
{
CXD56_PIN_CONFIGS(PINCONFS_SPI0B_GPIO);
}
break;
#endif /* CONFIG_CXD56_I2C2 */
default:
break;
}
}
/****************************************************************************
* I2C device operations
****************************************************************************/
struct i2c_ops_s cxd56_i2c_ops =
{
.transfer = cxd56_i2c_transfer,
#ifdef CONFIG_I2C_RESET
.reset = cxd56_i2c_reset,
#endif
};
#if defined(CONFIG_CXD56_I2C0_SCUSEQ) || defined(CONFIG_CXD56_I2C1_SCUSEQ)
struct i2c_ops_s cxd56_i2c_scu_ops =
{
.transfer = cxd56_i2c_transfer_scu,
#ifdef CONFIG_I2C_RESET
.reset = cxd56_i2c_reset,
#endif
};
#endif
/****************************************************************************
* Name: cxd56_i2c_setfrequency
*
* Description:
* Set the frequency for the next transfer
*
****************************************************************************/
static void cxd56_i2c_setfrequency(struct cxd56_i2cdev_s *priv,
uint32_t frequency)
{
int32_t lcnt;
int32_t hcnt;
uint64_t lcnt64;
uint64_t hcnt64;
uint64_t speed;
uint64_t t_low;
uint64_t t_high;
uint32_t base = cxd56_get_i2c_baseclock(priv->port);
uint32_t spklen;
ASSERT(base);
if ((priv->frequency == frequency) && (priv->base_freq == base))
{
return;
}
priv->frequency = frequency;
priv->base_freq = base;
base /= 1000;
if (frequency <= 100000)
{
t_low = 4700000;
t_high = 4000000;
}
else if (frequency <= 400000)
{
t_low = 1300000;
t_high = 600000;
}
else
{
t_low = 500000;
t_high = 260000;
}
if (frequency > 100000)
{
if (base < 20032)
{
spklen = 1;
}
else if (base < 40064)
{
spklen = 2;
}
else
{
spklen = 3;
}
}
else
{
spklen = 1;
}
lcnt64 = (t_low + 6500ull / 20000ull) * base;
lcnt = ((lcnt64 + 999999999ull) / 1000000000ull) - 1; /* ceil */
lcnt = lcnt < 8 ? 8 : lcnt;
hcnt64 = (t_high - 6500ull) * base;
hcnt = ((hcnt64 + 999999999ull) / 1000000000ull) - 6 - spklen; /* ceil */
hcnt = hcnt < 6 ? 6 : hcnt;
speed =
1000000000000000000ull /
(((lcnt + 1) * 1000000000000ull +
(hcnt + 6 + spklen) * 1000000000000ull) / base +
20000ull / 1000ull * 1000000ull);
if (speed > (frequency * 1000ull))
{
uint64_t adj;
adj = ((1000000000000000000ull / (frequency * 1000ull)) -
(1000000000000000000ull / speed)) *
base;
hcnt += (adj + 999999999999ull) / 1000000000000ull;
}
/* use FS register in SS and FS mode */
i2c_reg_write(priv, CXD56_IC_FS_SCL_HCNT, hcnt);
i2c_reg_write(priv, CXD56_IC_FS_SCL_LCNT, lcnt);
i2c_reg_rmw(priv, CXD56_IC_CON, IC_SPEED_FS, IC_MAX_SPEED_MODE);
i2c_reg_write(priv, CXD56_IC_FS_SPKLEN, spklen);
}
/****************************************************************************
* Name: cxd56_i2c_timeout
*
* Description:
* Watchdog timer for timeout of I2C operation
*
****************************************************************************/
static void cxd56_i2c_timeout(int argc, uint32_t arg, ...)
{
struct cxd56_i2cdev_s *priv = (struct cxd56_i2cdev_s *)arg;
irqstate_t flags = enter_critical_section();
priv->error = -ENODEV;
nxsem_post(&priv->wait);
leave_critical_section(flags);
}
/****************************************************************************
* Name: cxd56_i2c_drainrxfifo
*
* Description:
* Receive I2C data
*
****************************************************************************/
static void cxd56_i2c_drainrxfifo(struct cxd56_i2cdev_s *priv)
{
struct i2c_msg_s *msg = priv->msgs;
uint32_t status;
uint32_t dat;
ssize_t i;
DEBUGASSERT(msg != NULL);
status = i2c_reg_read(priv, CXD56_IC_STATUS);
for (i = 0; i < priv->rw_size && status & STATUS_RFNE; i++)
{
dat = i2c_reg_read(priv, CXD56_IC_DATA_CMD);
msg->buffer[priv->reg_buff_offset + i] = dat & 0xff;
status = i2c_reg_read(priv, CXD56_IC_STATUS);
}
priv->reg_buff_offset += priv->rw_size;
}
/****************************************************************************
* Name: cxd56_i2c_interrupt
*
* Description:
* The I2C Interrupt Handler
*
****************************************************************************/
static int cxd56_i2c_interrupt(int irq, FAR void *context, FAR void *arg)
{
FAR struct cxd56_i2cdev_s *priv = (FAR struct cxd56_i2cdev_s *)arg;
uint32_t state;
int ret;
state = i2c_reg_read(priv, CXD56_IC_INTR_STAT);
if (state & INTR_TX_ABRT)
{
i2c_reg_read(priv, CXD56_IC_CLR_TX_ABRT);
priv->error = -ENODEV;
}
if (state & INTR_TX_OVER)
{
i2c_reg_read(priv, CXD56_IC_CLR_TX_OVER);
priv->error = -EIO;
}
if (state & INTR_RX_OVER)
{
i2c_reg_read(priv, CXD56_IC_CLR_RX_OVER);
priv->error = -EIO;
}
if (state & INTR_RX_UNDER)
{
i2c_reg_read(priv, CXD56_IC_CLR_RX_UNDER);
priv->error = -EIO;
}
if (state & INTR_TX_EMPTY)
{
/* TX_EMPTY is automatically cleared by hardware
* when the buffer level goes above the threshold.
*/
i2c_reg_rmw(priv, CXD56_IC_INTR_MASK, 0, INTR_TX_EMPTY);
}
if (state & INTR_RX_FULL)
{
/* RX_FULL is automatically cleared by hardware
* when the buffer level goes below the threshold.
*/
i2c_reg_rmw(priv, CXD56_IC_INTR_MASK, 0, INTR_RX_FULL);
cxd56_i2c_drainrxfifo(priv);
}
if (state & INTR_STOP_DET)
{
i2c_reg_read(priv, CXD56_IC_CLR_STOP_DET);
}
if ((priv->error) || (state & INTR_TX_EMPTY) || (state & INTR_RX_FULL))
{
/* Failure of wd_cancel() means that the timer expired.
* In this case, nxsem_post() has already been called.
* Therefore, call nxsem_post() only when wd_cancel() succeeds.
*/
ret = wd_cancel(priv->timeout);
if (ret == OK)
{
nxsem_post(&priv->wait);
}
}
return OK;
}
/****************************************************************************
* Name: cxd56_i2c_receive
*
* Description:
* Receive data from I2C bus.
* Prohibit all interrupt because the STOP condition might happen
* if the interrupt occurs when the writing request.
* Actual receiving data is in RX_FULL interrupt handler.
*
* TODO : The argument "last" is not used.
****************************************************************************/
static int cxd56_i2c_receive(struct cxd56_i2cdev_s *priv, int last)
{
struct i2c_msg_s *msg = priv->msgs;
int i;
int en;
ssize_t msg_length;
irqstate_t flags;
priv->reg_buff_offset = 0;
DEBUGASSERT(msg != NULL);
for (msg_length = msg->length; msg_length > 0; msg_length -= priv->rw_size)
{
if (msg_length <= I2C_FIFO_MAX_SIZE)
{
priv->rw_size = msg_length;
en = 1;
}
else
{
priv->rw_size = I2C_FIFO_MAX_SIZE;
en = 0;
}
/* update threshold value of the receive buffer */
i2c_reg_write(priv, CXD56_IC_RX_TL, priv->rw_size - 1);
for (i = 0; i < priv->rw_size - 1; i++)
{
i2c_reg_write(priv, CXD56_IC_DATA_CMD, CMD_READ);
}
flags = enter_critical_section();
wd_start(priv->timeout, I2C_TIMEOUT, cxd56_i2c_timeout, 1,
(uint32_t)priv);
/* Set stop flag for indicate the last data */
i2c_reg_write(priv, CXD56_IC_DATA_CMD, CMD_READ | (en ? CMD_STOP : 0));
i2c_reg_rmw(priv, CXD56_IC_INTR_MASK, INTR_RX_FULL, INTR_RX_FULL);
leave_critical_section(flags);
nxsem_wait(&priv->wait);
if (priv->error != OK)
{
break;
}
}
return 0;
}
/****************************************************************************
* Name: cxd56_i2c_send
*
* Description:
* Send data to I2C bus.
*
****************************************************************************/
static int cxd56_i2c_send(struct cxd56_i2cdev_s *priv, int last)
{
struct i2c_msg_s *msg = priv->msgs;
ssize_t i;
irqstate_t flags;
DEBUGASSERT(msg != NULL);
for (i = 0; i < msg->length - 1; i++)
{
while (!(i2c_reg_read(priv, CXD56_IC_STATUS) & STATUS_TFNF));
i2c_reg_write(priv, CXD56_IC_DATA_CMD, (uint32_t)msg->buffer[i]);
}
while (!(i2c_reg_read(priv, CXD56_IC_STATUS) & STATUS_TFNF));
flags = enter_critical_section();
wd_start(priv->timeout, I2C_TIMEOUT, cxd56_i2c_timeout, 1, (uint32_t)priv);
i2c_reg_write(priv, CXD56_IC_DATA_CMD,
(uint32_t)msg->buffer[i] | (last ? CMD_STOP : 0));
/* Enable TX_EMPTY interrupt for determine transfer done. */
i2c_reg_rmw(priv, CXD56_IC_INTR_MASK, INTR_TX_EMPTY, INTR_TX_EMPTY);
leave_critical_section(flags);
nxsem_wait(&priv->wait);
return 0;
}
/****************************************************************************
* Name: cxd56_i2c_transfer
*
* Description:
* Perform a sequence of I2C transfers
*
* TODO: Multiple i2c_msg_s read operations with the same address are not
* currently guaranteed.
****************************************************************************/
static int cxd56_i2c_transfer(FAR struct i2c_master_s *dev,
FAR struct i2c_msg_s *msgs, int count)
{
struct cxd56_i2cdev_s *priv = (struct cxd56_i2cdev_s *)dev;
int i;
int ret = 0;
int semval = 0;
int addr = -1;
static int wostop = 0;
DEBUGASSERT(dev != NULL);
/* Get exclusive access to the I2C bus */
nxsem_wait(&priv->mutex);
/* Check wait semaphore value. If the value is not 0, the transfer can not
* be performed normally.
*/
ret = nxsem_getvalue(&priv->wait, &semval);
DEBUGASSERT(ret == OK && semval == 0);
/* Disable clock gating (clock enable) */
cxd56_i2c_clock_gate_disable(priv->port);
for (i = 0; i < count; i++, msgs++)
{
/* Pass msg descriptor via device context */
priv->msgs = msgs;
priv->error = OK;
if ((addr != msgs->addr) && !wostop)
{
cxd56_i2c_disable(priv);
cxd56_i2c_setfrequency(priv, msgs->frequency);
i2c_reg_rmw(priv, CXD56_IC_CON, IC_RESTART_EN, IC_RESTART_EN);
i2c_reg_write(priv, CXD56_IC_TAR, msgs->addr & 0x7f);
cxd56_i2c_enable(priv);
addr = msgs->addr;
}
if (msgs->flags & I2C_M_NOSTOP)
{
/* Don't send stop condition even if the last data */
wostop = 1;
}
else
{
wostop = 0;
}
if (msgs->flags & I2C_M_READ)
{
ret = cxd56_i2c_receive(priv, (wostop) ? 0 : (i + 1 == count));
}
else
{
ret = cxd56_i2c_send(priv, (wostop) ? 0 : (i + 1 == count));
}
if (ret < 0)
{
break;
}
if (priv->error != OK)
{
ret = priv->error;
break;
}
/* Clear msg descriptor for prevent illegal access in interrupt */
priv->msgs = NULL;
}
if (!wostop)
{
cxd56_i2c_disable(priv);
}
/* Enable clock gating (clock disable) */
cxd56_i2c_clock_gate_enable(priv->port);
nxsem_post(&priv->mutex);
return ret;
}
/****************************************************************************
* Name: cxd56_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 cxd56_i2c_reset(FAR struct i2c_master_s *dev)
{
return OK;
}
#endif /* CONFIG_I2C_RESET */
/****************************************************************************
* Name: cxd56_i2c_transfer_scu
*
* Description:
* Perform a sequence of I2C transfers with scu oneshot sequencer.
*
****************************************************************************/
#if defined(CONFIG_CXD56_I2C0_SCUSEQ) || defined(CONFIG_CXD56_I2C1_SCUSEQ)
static int cxd56_i2c_scurecv(int port, int addr,
uint8_t *buf, ssize_t buflen)
{
uint16_t inst[2];
int instn;
int len0;
int len1;
ssize_t rem;
int ret = OK;
/* Ignore buffer is NULL */
if (buf == NULL)
{
return OK;
}
if (buflen > 16)
{
return -EINVAL;
}
rem = buflen;
len0 = rem > 8 ? 8 : rem;
rem -= len0;
len1 = rem > 8 ? 8 : rem;
rem -= len1;
inst[0] = SCU_INST_RECV(len0);
if (len1)
{
inst[1] = SCU_INST_RECV(len1);
instn = 2;
}
else
{
instn = 1;
}
inst[instn - 1] |= SCU_INST_LAST;
ret = scu_i2ctransfer(port, addr, inst, instn, buf, buflen);
if (ret < 0)
{
syslog(LOG_ERR, "I2C receive failed. port %d addr %d\n",
port, addr);
}
return ret;
}
static int cxd56_i2c_scusend(int port, int addr,
uint8_t *buf, ssize_t buflen)
{
uint16_t inst[12];
ssize_t rem;
int i;
int ret = OK;
rem = buflen;
while (rem)
{
for (i = 0; i < 12 && rem > 0; i++)
{
inst[i] = SCU_INST_SEND(*buf++);
rem--;
}
if (rem == 0)
{
inst[i - 1] |= SCU_INST_LAST;
}
if (i > 0)
{
ret = scu_i2ctransfer(port, addr, inst, i, NULL, i);
if (ret < 0)
{
syslog(LOG_ERR, "I2C send failed. port %d addr %d\n",
port, addr);
break;
}
}
}
return ret;
}
static int cxd56_i2c_transfer_scu(FAR struct i2c_master_s *dev,
FAR struct i2c_msg_s *msgs, int count)
{
FAR struct cxd56_i2cdev_s *priv = (FAR struct cxd56_i2cdev_s *)dev;
ssize_t len = 0;
uint8_t *buf = NULL;
uint8_t addr = msgs->addr;
int i;
int ret = 0;
DEBUGASSERT(dev != NULL);
/* Get exclusive access to the I2C bus */
nxsem_wait(&priv->mutex);
/* Apply frequency for request msgs */
if (priv->frequency != msgs->frequency)
{
cxd56_i2c_clock_gate_disable(priv->port);
cxd56_i2c_disable(priv);
cxd56_i2c_setfrequency(priv, msgs->frequency);
i2c_reg_rmw(priv, CXD56_IC_CON, IC_RESTART_EN, IC_RESTART_EN);
i2c_reg_write(priv, CXD56_IC_TAR, msgs->addr & 0x7f);
cxd56_i2c_enable(priv);
cxd56_i2c_clock_gate_enable(priv->port);
priv->frequency = msgs->frequency;
}
for (i = 0; i < count; i++, msgs++)
{
len = msgs->length;
buf = msgs->buffer;
if (msgs->flags & I2C_M_READ)
{
ret = cxd56_i2c_scurecv(priv->port, addr, buf, len);
}
else
{
ret = cxd56_i2c_scusend(priv->port, addr, buf, len);
}
if (ret < 0)
{
break;
}
}
nxsem_post(&priv->mutex);
return ret;
}
#endif
static inline uint32_t i2c_reg_read(struct cxd56_i2cdev_s *priv,
uint32_t offset)
{
return getreg32(priv->base + offset);
}
static inline void i2c_reg_write(struct cxd56_i2cdev_s *priv,
uint32_t offset, uint32_t val)
{
putreg32(val, priv->base + offset);
}
static inline void i2c_reg_rmw(struct cxd56_i2cdev_s *priv, uint32_t offset,
uint32_t val, uint32_t mask)
{
uint32_t regval;
regval = getreg32(priv->base + offset);
putreg32((regval & ~mask) | val, priv->base + offset);
}
static int cxd56_i2c_disable(struct cxd56_i2cdev_s *priv)
{
int retry = 25000;
uint32_t stat;
/* disable all interrupt */
i2c_reg_write(priv, CXD56_IC_INTR_MASK, 0x0);
/* clear all interrupt status */
i2c_reg_read(priv, CXD56_IC_CLR_INTR);
i2c_reg_write(priv, CXD56_IC_ENABLE, 0);
do
{
stat = i2c_reg_read(priv, CXD56_IC_ENABLE_STATUS);
}
while (--retry && (stat & ESTATUS_IC_EN));
if (!retry)
{
i2cerr("i2c wait timeout.\n");
return -EBUSY;
}
/* clear all interrupt status again */
i2c_reg_read(priv, CXD56_IC_CLR_INTR);
return 0;
}
static void cxd56_i2c_enable(struct cxd56_i2cdev_s *priv)
{
i2c_reg_write(priv, CXD56_IC_INTR_MASK, I2C_INTR_ENABLE);
i2c_reg_write(priv, CXD56_IC_ENABLE, 1);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: cxd56_i2cbus_initialize
*
* Description:
* Initialise an I2C device
*
****************************************************************************/
struct i2c_master_s *cxd56_i2cbus_initialize(int port)
{
struct cxd56_i2cdev_s *priv;
irqstate_t flags;
flags = enter_critical_section();
#ifdef CONFIG_CXD56_I2C0
if (port == 0)
{
priv = &g_i2c0dev;
# ifndef CONFIG_CXD56_I2C0_SCUSEQ
priv->dev.ops = &cxd56_i2c_ops;
# else
priv->dev.ops = &cxd56_i2c_scu_ops;
# endif
}
else
#endif
#ifdef CONFIG_CXD56_I2C1
if (port == 1)
{
priv = &g_i2c1dev;
# ifndef CONFIG_CXD56_I2C1_SCUSEQ
priv->dev.ops = &cxd56_i2c_ops;
# else
priv->dev.ops = &cxd56_i2c_scu_ops;
# endif
}
else
#endif
#ifdef CONFIG_CXD56_I2C2
if (port == 2)
{
priv = &g_i2c2dev;
priv->dev.ops = &cxd56_i2c_ops;
}
else
#endif
{
leave_critical_section(flags);
i2cerr("I2C Only support 0,1,2\n");
return NULL;
}
priv->refs++;
/* Test if already initialized or not */
if (1 < priv->refs)
{
leave_critical_section(flags);
return &priv->dev;
}
priv->port = port;
priv->frequency = 0;
cxd56_i2c_clock_enable(priv->port);
priv->base_freq = cxd56_get_i2c_baseclock(priv->port);
cxd56_i2c_disable(priv);
i2c_reg_write(priv, CXD56_IC_INTR_MASK, 0x00);
i2c_reg_read(priv, CXD56_IC_CLR_INTR);
/* set threshold level of the Rx/Tx FIFO */
i2c_reg_write(priv, CXD56_IC_RX_TL, 0xff);
i2c_reg_write(priv, CXD56_IC_TX_TL, 0);
/* set hold time for margin */
i2c_reg_write(priv, CXD56_IC_SDA_HOLD, 1);
i2c_reg_write(priv, CXD56_IC_CON,
(IC_SLAVE_DISABLE | IC_MASTER_MODE | IC_TX_EMPTY_CTRL));
cxd56_i2c_setfrequency(priv, I2C_DEFAULT_FREQUENCY);
leave_critical_section(flags);
/* Configure pin */
cxd56_i2c_pincontrol(port, true);
nxsem_init(&priv->mutex, 0, 1);
nxsem_init(&priv->wait, 0, 0);
priv->timeout = wd_create();
/* Attach Interrupt Handler */
irq_attach(priv->irqid, cxd56_i2c_interrupt, priv);
/* Enable Interrupt Handler */
up_enable_irq(priv->irqid);
/* Enable Interrupt in SCU */
if (port == 0 || port == 1)
{
putreg32(getreg32(CXD56_SCU_BASE + 0x400) | (1u << (port + 1)),
CXD56_SCU_BASE + 0x400);
}
/* Enable clock gating (clock disable) */
cxd56_i2c_clock_gate_enable(port);
return &priv->dev;
}
/****************************************************************************
* Name: cxd56_i2cbus_uninitialize
*
* Description:
* Uninitialise an I2C device
*
****************************************************************************/
int cxd56_i2cbus_uninitialize(FAR struct i2c_master_s *dev)
{
struct cxd56_i2cdev_s *priv = (struct cxd56_i2cdev_s *)dev;
/* Decrement reference count and check for underflow */
if (priv->refs == 0)
{
return ERROR;
}
if (--priv->refs)
{
return OK;
}
/* Configure pin */
cxd56_i2c_pincontrol(priv->port, false);
/* Disable clock gating (clock enable) */
cxd56_i2c_clock_gate_disable(priv->port);
cxd56_i2c_disable(priv);
cxd56_i2c_clock_disable(priv->port);
up_disable_irq(priv->irqid);
irq_detach(priv->irqid);
wd_delete(priv->timeout);
priv->timeout = NULL;
nxsem_destroy(&priv->mutex);
nxsem_destroy(&priv->wait);
return OK;
}
#endif