nuttx/arch/arm/src/lpc54xx/lpc54_i2c_master.c
Xiang Xiao acca9fcc3b sched/wdog: Remove MAX_WDOGPARMS and related stuff
since the variable arguments are error prone and seldom used.

Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2020-08-14 08:19:50 -06:00

1274 lines
34 KiB
C

/****************************************************************************
* arch/arm/src/lpc54xx/lpc54_i2c_master.c
*
* Copyright (C) 2017 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Parts of this file were adapted from sample code provided for the LPC54xx
* family from NXP which has a compatible BSD license.
*
* Copyright (c) 2016, Freescale Semiconductor, Inc.
* Copyright 2016-2017 NXP
*
* 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 NuttX 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 <nuttx/arch.h>
#include <nuttx/wdog.h>
#include <nuttx/clock.h>
#include <nuttx/semaphore.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 "hardware/lpc54_pinmux.h"
#include "hardware/lpc54_syscon.h"
#include "hardware/lpc54_flexcomm.h"
#include "hardware/lpc54_i2c.h"
#include "lpc54_config.h"
#include "lpc54_clockconfig.h"
#include "lpc54_enableclk.h"
#include "lpc54_gpio.h"
#include "lpc54_i2c_master.h"
#include <arch/board/board.h>
#ifdef HAVE_I2C_MASTER_DEVICE
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* 20 Millisecond timeout in system clock ticks. */
#ifdef CONFIG_DEBUG_I2C_INFO
# define I2C_WDOG_TIMEOUT MSEC2TICK(50)
#else
# define I2C_WDOG_TIMEOUT MSEC2TICK(20)
#endif
/* Default I2C frequency */
#if defined(CONFIG_LPC54_I2C_FAST) || defined(CONFIG_LPC54_I2C_HIGH)
# define I2C_DEFAULT_FREQUENCY 1000000
#else
# define I2C_DEFAULT_FREQUENCY 400000
#endif
/* I2C Master Interrupts */
#define I2C_MASTER_INTS \
(I2C_INT_MSTPENDING | I2C_INT_MSTARBLOSS | I2C_INT_MSTSTSTPERR)
/****************************************************************************
* Private Types
****************************************************************************/
/* I2C state */
enum lpc54_i2cstate_e
{
I2CSTATE_IDLE = 0,
I2CSTATE_TRANSMIT,
I2CSTATE_RECEIVE,
I2CSTATE_START,
I2CSTATE_STOP,
I2CSTATE_WAITSTOP
};
/* This structure provides the overall state of the I2C driver */
struct lpc54_i2cdev_s
{
struct i2c_master_s dev; /* Generic I2C device */
uintptr_t base; /* Base address of Flexcomm registers */
struct wdog_s timeout; /* Watchdog to timeout when bus hung */
uint32_t frequency; /* Current I2C frequency */
uint32_t fclock; /* Flexcomm function clock frequency */
struct i2c_msg_s *msgs; /* Remaining transfers (first is in progress) */
int16_t nmsgs; /* Number of transfer remaining */
int16_t result; /* The result of the transfer */
sem_t exclsem; /* Only one thread can access at a time */
#ifndef CONFIG_I2C_POLLED
sem_t waitsem; /* Supports wait for state machine completion */
uint16_t irq; /* Flexcomm IRQ number */
#endif
uint16_t xfrd; /* Number of bytes transferred */
volatile uint8_t state; /* State of state machine */
};
/****************************************************************************
* Private Functions
****************************************************************************/
static inline void lpc54_i2c_putreg(struct lpc54_i2cdev_s *priv,
unsigned int regoffset, uint32_t regval);
static inline uint32_t lpc54_i2c_getreg(struct lpc54_i2cdev_s *priv,
unsigned int regoffset);
static void lpc54_i2c_setfrequency(struct lpc54_i2cdev_s *priv,
uint32_t frequency);
static void lpc54_i2c_timeout(wdparm_t arg);
static void lpc54_i2c_xfrsetup(struct lpc54_i2cdev_s *priv);
static bool lpc54_i2c_nextmsg(struct lpc54_i2cdev_s *priv);
static bool lpc54_i2c_statemachine(struct lpc54_i2cdev_s *priv);
#ifndef CONFIG_I2C_POLLED
static int lpc54_i2c_interrupt(int irq, FAR void *context, FAR void *arg);
#else
static int lpc54_i2c_poll(struct lpc54_i2cdev_s *priv);
#endif
static int lpc54_i2c_transfer(FAR struct i2c_master_s *dev,
FAR struct i2c_msg_s *msgs, int count);
#ifdef CONFIG_I2C_RESET
static int lpc54_i2c_reset(FAR struct i2c_master_s * dev);
#endif
/****************************************************************************
* Private Data
****************************************************************************/
struct i2c_ops_s lpc54_i2c_ops =
{
.transfer = lpc54_i2c_transfer
#ifdef CONFIG_I2C_RESET
, .reset = lpc54_i2c_reset
#endif
};
#ifdef CONFIG_LPC54_I2C0_MASTER
static struct lpc54_i2cdev_s g_i2c0_dev;
#endif
#ifdef CONFIG_LPC54_I2C1_MASTER
static struct lpc54_i2cdev_s g_i2c1_dev;
#endif
#ifdef CONFIG_LPC54_I2C2_MASTER
static struct lpc54_i2cdev_s g_i2c2_dev;
#endif
#ifdef CONFIG_LPC54_I2C3_MASTER
static struct lpc54_i2cdev_s g_i2c3_dev;
#endif
#ifdef CONFIG_LPC54_I2C4_MASTER
static struct lpc54_i2cdev_s g_i2c4_dev;
#endif
#ifdef CONFIG_LPC54_I2C5_MASTER
static struct lpc54_i2cdev_s g_i2c5_dev;
#endif
#ifdef CONFIG_LPC54_I2C6_MASTER
static struct lpc54_i2cdev_s g_i2c6_dev;
#endif
#ifdef CONFIG_LPC54_I2C7_MASTER
static struct lpc54_i2cdev_s g_i2c7_dev;
#endif
#ifdef CONFIG_LPC54_I2C8_MASTER
static struct lpc54_i2cdev_s g_i2c8_dev;
#endif
#ifdef CONFIG_LPC54_I2C9_MASTER
static struct lpc54_i2cdev_s g_i2c9_dev;
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: lpc54_i2c_putreg
*
* Description:
* Write a value to a register at the offset from the Flexcomm base.
*
****************************************************************************/
static inline void lpc54_i2c_putreg(struct lpc54_i2cdev_s *priv,
unsigned int regoffset, uint32_t regval)
{
putreg32(regval, priv->base + regoffset);
}
/****************************************************************************
* Name: lpc54_i2c_getreg
*
* Description:
* Read the content of a register at the offset from the Flexcomm base.
*
****************************************************************************/
static inline uint32_t lpc54_i2c_getreg(struct lpc54_i2cdev_s *priv,
unsigned int regoffset)
{
return getreg32(priv->base + regoffset);
}
/****************************************************************************
* Name: lpc54_i2c_setfrequency
*
* Description:
* Set the frequency for the next transfer
*
****************************************************************************/
static void lpc54_i2c_setfrequency(struct lpc54_i2cdev_s *priv,
uint32_t frequency)
{
uint32_t scl;
uint32_t divider;
uint32_t best_scl;
uint32_t best_div;
uint32_t err;
uint32_t best_err;
uint32_t regval;
i2cinfo("frequency %ld (%ld)\n", (long)frequency, (long)priv->frequency);
/* Has the I2C frequency changed? */
if (frequency != priv->frequency)
{
/* Yes.. instantiate the new I2C frequency */
best_err = 0;
for (scl = 9; scl >= 2; scl--)
{
/* Calculate ideal divider value for the current scl candidate.
*
* SCL High Time: Thi = divider * SCLhi
* SCL High Time: Tlo = divider * SCLlo
* Fscl = Finput / (Thi + Tlo)
*
* If Thi == TloL: Fscl = Finput / (divider * SCL * 2)
* Or: divider = Finput / (Fscl * SCL * 2)
*/
divider = priv->fclock / (frequency * scl * 2);
/* Adjust it if it is out of range */
if (divider > 0x00010000)
{
divider = 0x00010000;
}
/* Calculate the frequency error */
err = priv->fclock - (frequency * scl * 2 * divider);
if (err < best_err || best_err == 0)
{
best_div = divider;
best_scl = scl;
best_err = err;
}
if (err == 0 || divider >= 0x10000)
{
/* Break out of the loop early ifeither exact value was found
* or the divider is at its maximum value.
*/
break;
}
}
/* Instantiate the new I2C frequency */
regval = I2C_CLKDIV(best_div);
lpc54_i2c_putreg(priv, LPC54_I2C_CLKDIV_OFFSET, regval);
regval = I2C_MSTTIME_SCLLOW(best_scl) | I2C_MSTTIME_SCLHIGH(best_scl);
lpc54_i2c_putreg(priv, LPC54_I2C_MSTTIME_OFFSET, regval);
priv->frequency = frequency;
}
}
/****************************************************************************
* Name: lpc54_i2c_timeout
*
* Description:
* Watchdog timer for timeout of I2C operation
*
****************************************************************************/
static void lpc54_i2c_timeout(wdparm_t arg)
{
struct lpc54_i2cdev_s *priv = (struct lpc54_i2cdev_s *)arg;
#ifndef CONFIG_I2C_POLLED
irqstate_t flags = enter_critical_section();
#endif
i2cerr("ERROR: Timeout! state=%u\n", priv->state);
/* Disable further I2C interrupts and return to the IDLE state with the
* timeout result.
*/
lpc54_i2c_putreg(priv, LPC54_I2C_INTENCLR_OFFSET, I2C_MASTER_INTS);
priv->state = I2CSTATE_IDLE;
if (priv->result == OK)
{
priv->result = -ETIMEDOUT;
}
#ifndef CONFIG_I2C_POLLED
/* Wake up any waiters */
nxsem_post(&priv->waitsem);
leave_critical_section(flags);
#endif
}
/****************************************************************************
* Name: lpc54_i2c_xfrsetup
*
* Description:
* Setup to initiate a transfer.
*
****************************************************************************/
static void lpc54_i2c_xfrsetup(struct lpc54_i2cdev_s *priv)
{
struct i2c_msg_s *msg;
DEBUGASSERT(priv != NULL && priv->msgs != NULL);
msg = priv->msgs;
/* Disable I2C interrupts while configuring for the transfer */
lpc54_i2c_putreg(priv, LPC54_I2C_INTENCLR_OFFSET, I2C_MASTER_INTS);
/* Set up for the transfer */
priv->xfrd = 0;
/* Select the initial state */
if ((msg->flags & I2C_M_NOSTART) != 0)
{
/* Start condition will be omitted. Begin the transfer in the data
* phase.
*/
if (msg->length == 0)
{
priv->state = I2CSTATE_STOP;
}
else if ((I2C_M_READ & msg->flags) == I2C_M_READ)
{
priv->state = I2CSTATE_RECEIVE;
}
else
{
priv->state = I2CSTATE_TRANSMIT;
}
}
else
{
priv->state = I2CSTATE_START;
}
/* Set the I2C frequency if provided in this message. Otherwise, use the
* current I2C frequency setting.
*/
if (msg->frequency > 0)
{
lpc54_i2c_setfrequency(priv, msg->frequency);
}
/* Clear error status bits */
lpc54_i2c_putreg(priv, LPC54_I2C_STAT_OFFSET, I2C_INT_MSTARBLOSS |
I2C_INT_MSTSTSTPERR);
#ifndef CONFIG_I2C_POLLED
/* Enable I2C master interrupts */
lpc54_i2c_putreg(priv, LPC54_I2C_INTENSET_OFFSET, I2C_MASTER_INTS);
#endif
}
/****************************************************************************
* Name: lpc54_i2c_nextmsg
*
* Description:
* Called at the completion of each message. If there are more messages,
* this function will perform the setup for the next message.
*
****************************************************************************/
static bool lpc54_i2c_nextmsg(struct lpc54_i2cdev_s *priv)
{
irqstate_t flags;
/* Disable interrupts to prevent the timeout while we make the decision
* here.
*/
flags = enter_critical_section();
i2cinfo("nmsgs=%u\n", priv->nmsgs - 1);
/* Decrement the number of messages remaining. */
if (--priv->nmsgs > 0 && priv->result == OK)
{
/* There are more messages, set up for the next message */
priv->msgs++;
lpc54_i2c_xfrsetup(priv);
i2cinfo("state=%u\n", priv->state);
leave_critical_section(flags);
return false;
}
else
{
/* That was the last message... we are done.
* Cancel any timeout
*/
wd_cancel(&priv->timeout);
/* Disable further I2C interrupts and return to the IDLE state */
lpc54_i2c_putreg(priv, LPC54_I2C_INTENCLR_OFFSET, I2C_MASTER_INTS);
priv->state = I2CSTATE_IDLE;
i2cinfo("state=%u\n", priv->state);
leave_critical_section(flags);
return true;
}
}
/****************************************************************************
* Name: lpc54_i2c_statemachine
*
* Description:
* This is the I2C transfer state machine that implements the actual
* transfer. It may be called from the interrupt level or is may be used
* without interrupts in a polled mode.
*
****************************************************************************/
static bool lpc54_i2c_statemachine(struct lpc54_i2cdev_s *priv)
{
struct i2c_msg_s *msg;
uint32_t status;
uint32_t mstate;
DEBUGASSERT(priv != NULL && priv->msgs != NULL);
msg = priv->msgs;
i2cinfo("state=%u\n", priv->state);
status = lpc54_i2c_getreg(priv, LPC54_I2C_STAT_OFFSET);
if (status & I2C_INT_MSTARBLOSS)
{
i2cerr("ERROR: Arbitation loss\n");
lpc54_i2c_putreg(priv, LPC54_I2C_STAT_OFFSET, I2C_INT_MSTARBLOSS);
priv->result = -EIO;
return true;
}
if (status & I2C_INT_MSTSTSTPERR)
{
i2cerr("ERROR: Start/stop error\n");
lpc54_i2c_putreg(priv, LPC54_I2C_STAT_OFFSET, I2C_INT_MSTSTSTPERR);
priv->result = -EIO;
return true;
}
if ((status & I2C_INT_MSTPENDING) == 0)
{
i2cerr("ERROR: Busy\n");
priv->result = -EBUSY;
return true;
}
/* Get the state of the I2C module */
mstate = (status & I2C_STAT_MSTSTATE_MASK) >> I2C_STAT_MSTSTATE_SHIFT;
i2cinfo("mstate=%u\n", mstate);
if ((mstate == I2C_MASTER_STATE_ADDRNAK) ||
(mstate == I2C_MASTER_STATE_DATANAK))
{
/* Slave NACKed last byte, issue stop and return error */
lpc54_i2c_putreg(priv, LPC54_I2C_MSTCTL_OFFSET, I2C_MSTCTL_MSTSTOP);
priv->result = -EPERM;
priv->state = I2CSTATE_WAITSTOP;
i2cerr("ERROR: NAKed, state=%u\n", priv->state);
return false;
}
switch (priv->state)
{
case I2CSTATE_START:
{
enum lpc54_i2cstate_e newstate;
if ((msg->flags & I2C_M_READ) == I2C_M_READ)
{
lpc54_i2c_putreg(priv, LPC54_I2C_MSTDAT_OFFSET,
I2C_READADDR8(msg->addr));
newstate = I2CSTATE_RECEIVE;
}
else
{
lpc54_i2c_putreg(priv, LPC54_I2C_MSTDAT_OFFSET,
I2C_WRITEADDR8(msg->addr));
newstate = I2CSTATE_TRANSMIT;
}
if (priv->xfrd >= msg->length)
{
/* No more data, setup for STOP */
newstate = I2CSTATE_STOP;
}
priv->state = newstate;
/* Send START condition */
lpc54_i2c_putreg(priv, LPC54_I2C_MSTCTL_OFFSET,
I2C_MSTCTL_MSTSTART);
}
break;
case I2CSTATE_TRANSMIT:
{
if (mstate != I2C_MASTER_STATE_TXOK)
{
i2cerr("ERROR bad state=%u, expected %u\n",
mstate, I2C_MASTER_STATE_TXOK);
priv->result = -EINVAL;
return true;
}
lpc54_i2c_putreg(priv, LPC54_I2C_MSTDAT_OFFSET,
msg->buffer[priv->xfrd]);
lpc54_i2c_putreg(priv, LPC54_I2C_MSTCTL_OFFSET,
I2C_MSTCTL_MSTCONTINUE);
priv->xfrd++;
if (priv->xfrd >= msg->length)
{
/* No more data, schedule stop condition */
priv->state = I2CSTATE_STOP;
}
}
break;
case I2CSTATE_RECEIVE:
{
if (mstate != I2C_MASTER_STATE_RXAVAIL)
{
i2cerr("ERROR bad state=%u, expected %u\n",
mstate, I2C_MASTER_STATE_RXAVAIL);
priv->result = -EINVAL;
return true;
}
msg->buffer[priv->xfrd] =
lpc54_i2c_getreg(priv, LPC54_I2C_MSTDAT_OFFSET);
priv->xfrd++;
if (priv->xfrd < msg->length)
{
lpc54_i2c_putreg(priv, LPC54_I2C_MSTCTL_OFFSET,
I2C_MSTCTL_MSTCONTINUE);
}
else
{
/* No more data expected, issue NACK and STOP right away */
lpc54_i2c_putreg(priv, LPC54_I2C_MSTCTL_OFFSET,
I2C_MSTCTL_MSTSTOP);
priv->state = I2CSTATE_WAITSTOP;
}
}
break;
case I2CSTATE_STOP:
{
bool dostop = true;
/* Is this the last message? */
if (priv->nmsgs > 1)
{
struct i2c_msg_s *nextmsg;
/* No.. Is there a start on the next message? If so, it
* should be preceded by a STOP.
*/
nextmsg = msg + 1;
dostop = ((nextmsg->flags & I2C_M_NOSTART) != 0);
}
if (dostop)
{
/* Stop condition is omitted, we are done. Start the next
* message (or return to the IDLE state if none).
*/
return lpc54_i2c_nextmsg(priv);
}
else
{
/* Send stop condition */
lpc54_i2c_putreg(priv, LPC54_I2C_MSTCTL_OFFSET,
I2C_MSTCTL_MSTSTOP);
priv->state = I2CSTATE_WAITSTOP;
}
}
break;
case I2CSTATE_WAITSTOP:
{
/* Start the next message (or return to the IDLE state if none). */
return lpc54_i2c_nextmsg(priv);
}
break;
case I2CSTATE_IDLE:
default:
priv->result = -EINVAL;
return true;
}
i2cinfo("state=%u\n", priv->state);
return false;
}
/****************************************************************************
* Name: lpc54_i2c_interrupt
*
* Description:
* The I2C Interrupt Handler
*
****************************************************************************/
#ifndef CONFIG_I2C_POLLED
static int lpc54_i2c_interrupt(int irq, FAR void *context, FAR void *arg)
{
struct lpc54_i2cdev_s *priv = (struct lpc54_i2cdev_s *)arg;
bool done;
DEBUGASSERT(priv != NULL);
/* Run the I2C state machine */
done = lpc54_i2c_statemachine(priv);
if (done)
{
/* Disable further I2C interrupts. */
lpc54_i2c_putreg(priv, LPC54_I2C_INTENCLR_OFFSET, I2C_MASTER_INTS);
/* Wake up wake up any waiters */
nxsem_post(&priv->waitsem);
}
return OK;
}
#endif /* CONFIG_I2C_POLLED */
/****************************************************************************
* Name: lpc54_i2c_transfer
*
* Description:
* Perform a sequence of I2C transfers
*
****************************************************************************/
static int lpc54_i2c_transfer(FAR struct i2c_master_s *dev,
FAR struct i2c_msg_s *msgs, int count)
{
struct lpc54_i2cdev_s *priv = (struct lpc54_i2cdev_s *)dev;
int ret;
i2cinfo("count=%d\n", count);
DEBUGASSERT(dev != NULL && msgs != NULL);
/* Get exclusive access to the I2C bus */
nxsem_wait(&priv->exclsem);
/* Set up for the transfer */
priv->xfrd = 0;
priv->msgs = msgs;
priv->nmsgs = count;
priv->result = OK;
/* Set up the transfer timeout */
wd_start(&priv->timeout, priv->nmsgs * I2C_WDOG_TIMEOUT,
lpc54_i2c_timeout, (wdparm_t)priv);
/* Initiate the transfer */
lpc54_i2c_xfrsetup(priv);
/* Loop until the transfer is complete or until a timeout occurs */
do
{
#ifndef CONFIG_I2C_POLLED
nxsem_wait(&priv->waitsem);
#else
lpc54_i2c_statemachine(priv);
#endif
}
while (priv->state != I2CSTATE_IDLE);
ret = priv->result;
i2cinfo("Done, result=%d\n", ret);
nxsem_post(&priv->exclsem);
return ret;
}
/****************************************************************************
* Name: lpc54_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 lpc54_i2c_reset(FAR struct i2c_master_s * dev)
{
#warning Missing logic
return OK;
}
#endif /* CONFIG_I2C_RESET */
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: lpc54_i2cbus_initialize
*
* Description:
* Initialise an I2C device
*
****************************************************************************/
struct i2c_master_s *lpc54_i2cbus_initialize(int port)
{
struct lpc54_i2cdev_s *priv;
irqstate_t flags;
uint32_t regval;
i2cinfo("port=%d\n", port);
flags = enter_critical_section();
/* Configure the requestin I2C peripheral */
/* NOTE: The basic FLEXCOMM initialization was performed in
* lpc54_lowputc.c.
*/
#ifdef CONFIG_LPC54_I2C0_MASTER
if (port == 0)
{
/* Attach 12 MHz clock to FLEXCOMM0 */
lpc54_flexcomm0_enableclk();
/* Set FLEXCOMM0 to the I2C peripheral, locking that configuration
* in place.
*/
putreg32(FLEXCOMM_PSELID_PERSEL_I2C | FLEXCOMM_PSELID_LOCK,
LPC54_FLEXCOMM0_PSELID);
/* Initialize the state structure */
priv = &g_i2c0_dev;
priv->base = LPC54_FLEXCOMM0_BASE;
priv->fclock = BOARD_FLEXCOMM0_FCLK;
#ifndef CONFIG_I2C_POLLED
priv->irq = LPC54_IRQ_FLEXCOMM0;
#endif
/* Configure I2C pins (defined in board.h) */
lpc54_gpio_config(GPIO_I2C0_SCL);
lpc54_gpio_config(GPIO_I2C0_SDA);
/* Set up the FLEXCOMM0 function clock */
putreg32(BOARD_FLEXCOMM0_CLKSEL, LPC54_SYSCON_FCLKSEL0);
}
else
#endif
#ifdef CONFIG_LPC54_I2C1_MASTER
if (port == 1)
{
/* Attach 12 MHz clock to FLEXCOMM1 */
lpc54_flexcomm1_enableclk();
/* Set FLEXCOMM1 to the I2C peripheral, locking that configuration
* in place.
*/
putreg32(FLEXCOMM_PSELID_PERSEL_I2C | FLEXCOMM_PSELID_LOCK,
LPC54_FLEXCOMM1_PSELID);
/* Initialize the state structure */
priv = &g_i2c1_dev;
priv->base = LPC54_FLEXCOMM1_BASE;
priv->fclock = BOARD_FLEXCOMM1_FCLK;
#ifndef CONFIG_I2C_POLLED
priv->irq = LPC54_IRQ_FLEXCOMM1;
#endif
/* Configure I2C pins (defined in board.h) */
lpc54_gpio_config(GPIO_I2C1_SCL);
lpc54_gpio_config(GPIO_I2C1_SDA);
/* Set up the FLEXCOMM1 function clock */
putreg32(BOARD_FLEXCOMM1_CLKSEL, LPC54_SYSCON_FCLKSEL1);
}
else
#endif
#ifdef CONFIG_LPC54_I2C2_MASTER
if (port == 2)
{
/* Attach 12 MHz clock to FLEXCOMM2 */
lpc54_flexcomm2_enableclk();
/* Set FLEXCOMM2 to the I2C peripheral, locking that configuration
* in place.
*/
putreg32(FLEXCOMM_PSELID_PERSEL_I2C | FLEXCOMM_PSELID_LOCK,
LPC54_FLEXCOMM2_PSELID);
/* Initialize the state structure */
priv = &g_i2c2_dev;
priv->base = LPC54_FLEXCOMM2_BASE;
priv->fclock = BOARD_FLEXCOMM2_FCLK;
#ifndef CONFIG_I2C_POLLED
priv->irq = LPC54_IRQ_FLEXCOMM2;
#endif
/* Configure I2C pins (defined in board.h) */
lpc54_gpio_config(GPIO_I2C2_SCL);
lpc54_gpio_config(GPIO_I2C2_SDA);
/* Set up the FLEXCOMM2 function clock */
putreg32(BOARD_FLEXCOMM2_CLKSEL, LPC54_SYSCON_FCLKSEL2);
}
else
#endif
#ifdef CONFIG_LPC54_I2C3_MASTER
if (port == 3)
{
/* Attach 12 MHz clock to FLEXCOMM3 */
lpc54_flexcomm3_enableclk();
/* Set FLEXCOMM3 to the I2C peripheral, locking that configuration
* in place.
*/
putreg32(FLEXCOMM_PSELID_PERSEL_I2C | FLEXCOMM_PSELID_LOCK,
LPC54_FLEXCOMM3_PSELID);
/* Initialize the state structure */
priv = &g_i2c3_dev;
priv->base = LPC54_FLEXCOMM3_BASE;
priv->fclock = BOARD_FLEXCOMM3_FCLK;
#ifndef CONFIG_I2C_POLLED
priv->irq = LPC54_IRQ_FLEXCOMM3;
#endif
/* Configure I2C pins (defined in board.h) */
lpc54_gpio_config(GPIO_I2C3_SCL);
lpc54_gpio_config(GPIO_I2C3_SDA);
/* Set up the FLEXCOMM3 function clock */
putreg32(BOARD_FLEXCOMM3_CLKSEL, LPC54_SYSCON_FCLKSEL3);
}
else
#endif
#ifdef CONFIG_LPC54_I2C4_MASTER
if (port == 4)
{
/* Attach 12 MHz clock to FLEXCOMM4 */
lpc54_flexcomm4_enableclk();
/* Set FLEXCOMM4 to the I2C peripheral, locking that configuration
* in place.
*/
putreg32(FLEXCOMM_PSELID_PERSEL_I2C | FLEXCOMM_PSELID_LOCK,
LPC54_FLEXCOMM4_PSELID);
/* Initialize the state structure */
priv = &g_i2c4_dev;
priv->base = LPC54_FLEXCOMM4_BASE;
priv->fclock = BOARD_FLEXCOMM4_FCLK;
#ifndef CONFIG_I2C_POLLED
priv->irq = LPC54_IRQ_FLEXCOMM4;
#endif
/* Configure I2C pins (defined in board.h) */
lpc54_gpio_config(GPIO_I2C4_SCL);
lpc54_gpio_config(GPIO_I2C4_SDA);
/* Set up the FLEXCOMM4 function clock */
putreg32(BOARD_FLEXCOMM4_CLKSEL, LPC54_SYSCON_FCLKSEL4);
}
else
#endif
#ifdef CONFIG_LPC54_I2C5_MASTER
if (port == 5)
{
/* Attach 12 MHz clock to FLEXCOMM5 */
lpc54_flexcomm5_enableclk();
/* Set FLEXCOMM5 to the I2C peripheral, locking that configuration
* in place.
*/
putreg32(FLEXCOMM_PSELID_PERSEL_I2C | FLEXCOMM_PSELID_LOCK,
LPC54_FLEXCOMM5_PSELID);
/* Initialize the state structure */
priv = &g_i2c5_dev;
priv->base = LPC54_FLEXCOMM5_BASE;
priv->fclock = BOARD_FLEXCOMM5_FCLK;
#ifndef CONFIG_I2C_POLLED
priv->irq = LPC54_IRQ_FLEXCOMM5;
#endif
/* Configure I2C pins (defined in board.h) */
lpc54_gpio_config(GPIO_I2C5_SCL);
lpc54_gpio_config(GPIO_I2C5_SDA);
/* Set up the FLEXCOMM5 function clock */
putreg32(BOARD_FLEXCOMM5_CLKSEL, LPC54_SYSCON_FCLKSEL5);
}
else
#endif
#ifdef CONFIG_LPC54_I2C6_MASTER
if (port == 6)
{
/* Attach 12 MHz clock to FLEXCOMM6 */
lpc54_flexcomm6_enableclk();
/* Set FLEXCOMM6 to the I2C peripheral, locking that configuration
* in place.
*/
putreg32(FLEXCOMM_PSELID_PERSEL_I2C | FLEXCOMM_PSELID_LOCK,
LPC54_FLEXCOMM6_PSELID);
/* Initialize the state structure */
priv = &g_i2c6_dev;
priv->base = LPC54_FLEXCOMM6_BASE;
priv->fclock = BOARD_FLEXCOMM6_FCLK;
#ifndef CONFIG_I2C_POLLED
priv->irq = LPC54_IRQ_FLEXCOMM6;
#endif
/* Configure I2C pins (defined in board.h) */
lpc54_gpio_config(GPIO_I2C6_SCL);
lpc54_gpio_config(GPIO_I2C6_SDA);
/* Set up the FLEXCOMM6 function clock */
putreg32(BOARD_FLEXCOMM6_CLKSEL, LPC54_SYSCON_FCLKSEL6);
}
else
#endif
#ifdef CONFIG_LPC54_I2C7_MASTER
if (port == 7)
{
/* Attach 12 MHz clock to FLEXCOMM7 */
lpc54_flexcomm7_enableclk();
/* Set FLEXCOMM7 to the I2C peripheral, locking that configuration
* in place.
*/
putreg32(FLEXCOMM_PSELID_PERSEL_I2C | FLEXCOMM_PSELID_LOCK,
LPC54_FLEXCOMM7_PSELID);
/* Initialize the state structure */
priv = &g_i2c7_dev;
priv->base = LPC54_FLEXCOMM7_BASE;
priv->fclock = BOARD_FLEXCOMM7_FCLK;
#ifndef CONFIG_I2C_POLLED
priv->irq = LPC54_IRQ_FLEXCOMM7;
#endif
/* Configure I2C pins (defined in board.h) */
lpc54_gpio_config(GPIO_I2C7_SCL);
lpc54_gpio_config(GPIO_I2C7_SDA);
/* Set up the FLEXCOMM7 function clock */
putreg32(BOARD_FLEXCOMM7_CLKSEL, LPC54_SYSCON_FCLKSEL7);
}
else
#endif
#ifdef CONFIG_LPC54_I2C8_MASTER
if (port == 8)
{
/* Attach 12 MHz clock to FLEXCOMM8 */
lpc54_flexcomm8_enableclk();
/* Set FLEXCOMM8 to the I2C peripheral, locking that configuration
* in place.
*/
putreg32(FLEXCOMM_PSELID_PERSEL_I2C | FLEXCOMM_PSELID_LOCK,
LPC54_FLEXCOMM8_PSELID);
/* Initialize the state structure */
priv = &g_i2c8_dev;
priv->base = LPC54_FLEXCOMM8_BASE;
priv->fclock = BOARD_FLEXCOMM8_FCLK;
#ifndef CONFIG_I2C_POLLED
priv->irq = LPC54_IRQ_FLEXCOMM8;
#endif
/* Configure I2C pins (defined in board.h) */
lpc54_gpio_config(GPIO_I2C8_SCL);
lpc54_gpio_config(GPIO_I2C8_SDA);
/* Set up the FLEXCOMM8 function clock */
putreg32(BOARD_FLEXCOMM8_CLKSEL, LPC54_SYSCON_FCLKSEL8);
}
else
#endif
#ifdef CONFIG_LPC54_I2C9_MASTER
if (port == 9)
{
/* Attach 12 MHz clock to FLEXCOMM9 */
lpc54_flexcomm9_enableclk();
/* Set FLEXCOMM9 to the I2C peripheral, locking that configuration
* in place.
*/
putreg32(FLEXCOMM_PSELID_PERSEL_I2C | FLEXCOMM_PSELID_LOCK,
LPC54_FLEXCOMM9_PSELID);
/* Initialize the state structure */
priv = &g_i2c9_dev;
priv->base = LPC54_FLEXCOMM9_BASE;
priv->fclock = BOARD_FLEXCOMM9_FCLK;
#ifndef CONFIG_I2C_POLLED
priv->irq = LPC54_IRQ_FLEXCOMM9;
#endif
/* Configure I2C pins (defined in board.h) */
lpc54_gpio_config(GPIO_I2C9_SCL);
lpc54_gpio_config(GPIO_I2C9_SDA);
/* Set up the FLEXCOMM9 function clock */
putreg32(BOARD_FLEXCOMM9_CLKSEL, LPC54_SYSCON_FCLKSEL9);
}
else
#endif
{
i2cerr("ERROR: Unsupported port=%d\n", port);
return NULL;
}
leave_critical_section(flags);
/* Install our operations */
priv->dev.ops = &lpc54_i2c_ops;
/* Enable the I2C peripheral in the master mode */
regval = lpc54_i2c_getreg(priv, LPC54_I2C_CFG_OFFSET);
regval &= I2C_CFG_ALLENABLES;
regval |= I2C_CFG_MSTEN;
lpc54_i2c_putreg(priv, LPC54_I2C_CFG_OFFSET, regval);
/* Set the default I2C frequency */
lpc54_i2c_setfrequency(priv, I2C_DEFAULT_FREQUENCY);
/* Initialize semaphores */
nxsem_init(&priv->exclsem, 0, 1);
#ifndef CONFIG_I2C_POLLED
nxsem_init(&priv->waitsem, 0, 0);
/* The waitsem semaphore is used for signaling and, hence, should not have
* priority inheritance enabled.
*/
nxsem_set_protocol(&priv->waitsem, SEM_PRIO_NONE);
#endif
#ifndef CONFIG_I2C_POLLED
/* Attach Interrupt Handler */
irq_attach(priv->irq, lpc54_i2c_interrupt, priv);
#endif
/* Disable interrupts at the I2C peripheral */
lpc54_i2c_putreg(priv, LPC54_I2C_INTENCLR_OFFSET, I2C_INT_ALL);
#ifndef CONFIG_I2C_POLLED
/* Enable interrupts at the NVIC */
up_enable_irq(priv->irq);
#endif
return &priv->dev;
}
/****************************************************************************
* Name: lpc54_i2cbus_uninitialize
*
* Description:
* Uninitialise an I2C device
*
****************************************************************************/
int lpc54_i2cbus_uninitialize(FAR struct i2c_master_s * dev)
{
struct lpc54_i2cdev_s *priv = (struct lpc54_i2cdev_s *) dev;
uint32_t regval;
/* Disable I2C interrupts */
lpc54_i2c_putreg(priv, LPC54_I2C_INTENCLR_OFFSET, I2C_MASTER_INTS);
/* Disable the I2C peripheral */
regval = lpc54_i2c_getreg(priv, LPC54_I2C_CFG_OFFSET);
regval &= I2C_CFG_ALLENABLES;
regval &= ~I2C_CFG_MSTEN;
lpc54_i2c_putreg(priv, LPC54_I2C_CFG_OFFSET, regval);
#ifndef CONFIG_I2C_POLLED
/* Disable the Flexcomm interface at the NVIC and detach the interrupt. */
up_disable_irq(priv->irq);
irq_detach(priv->irq);
#endif
return OK;
}
#endif /* HAVE_I2C_MASTER_DEVICE */