blob: d0cbcac846592dffb1b3bd043378468b2ba9c6de [file] [log] [blame]
/****************************************************************************
* arch/mips/src/pic32mz/pic32mz_i2c.c
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/irq.h>
#include <nuttx/clock.h>
#include <nuttx/mutex.h>
#include <nuttx/semaphore.h>
#include <nuttx/i2c/i2c_master.h>
#include <arch/board/board.h>
#include "mips_internal.h"
#include "hardware/pic32mz_i2c.h"
#include "pic32mz_i2c.h"
/* At least one I2C peripheral must be enabled */
#ifdef CONFIG_PIC32MZ_I2C
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
/* CONFIG_I2C_POLLED may be set so that I2C interrupts will not be used.
* Instead CPU-intensive polling will be used.
*/
/* Interrupt wait timeout in seconds and milliseconds */
#if !defined(CONFIG_PIC32MZ_I2CTIMEOSEC) && !defined(CONFIG_PIC32MZ_I2CTIMEOMS)
# define CONFIG_PIC32MZ_I2CTIMEOSEC 0
# define CONFIG_PIC32MZ_I2CTIMEOMS 500 /* Default is 500 milliseconds */
#elif !defined(CONFIG_PIC32MZ_I2CTIMEOSEC)
# define CONFIG_PIC32MZ_I2CTIMEOSEC 0 /* User provided milliseconds */
#elif !defined(CONFIG_PIC32MZ_I2CTIMEOMS)
# define CONFIG_PIC32MZ_I2CTIMEOMS 0 /* User provided seconds */
#endif
/* Interrupt wait time timeout in system timer ticks */
#ifndef CONFIG_PIC32MZ_I2CTIMEOTICKS
# define CONFIG_PIC32MZ_I2CTIMEOTICKS \
(SEC2TICK(CONFIG_PIC32MZ_I2CTIMEOSEC) +\
MSEC2TICK(CONFIG_PIC32MZ_I2CTIMEOMS))
#endif
#ifndef CONFIG_PIC32MZ_I2C_DYNTIMEO_STARTSTOP
# define CONFIG_PIC32MZ_I2C_DYNTIMEO_STARTSTOP \
TICK2USEC(CONFIG_PIC32MZ_I2CTIMEOTICKS)
#endif
/* Debug ********************************************************************/
/* I2C event trace logic. */
#ifndef CONFIG_I2C_TRACE
# define pic32mz_i2c_tracereset(p)
# define pic32mz_i2c_tracenew(p,s)
# define pic32mz_i2c_traceevent(p,e,a)
# define pic32mz_i2c_tracedump(p)
#endif
#ifndef CONFIG_I2C_NTRACE
# define CONFIG_I2C_NTRACE 32
#endif
#if defined(CONFIG_I2C_TRACE) && defined(CONFIG_I2C_POLLED)
# warning Default trace events might not be enough during polling.
#endif
#ifdef CONFIG_I2C_SLAVE
# error I2C slave logic is not supported yet for PIC32MZ
#endif
/****************************************************************************
* Private Types
****************************************************************************/
/* Interrupt state */
enum pic32mz_intstate_e
{
INTSTATE_IDLE = 0, /* No I2C activity */
INTSTATE_WAITING, /* Waiting for completion of interrupt activity */
INTSTATE_DONE, /* Interrupt activity complete */
};
/* Process state */
enum pic32mz_process_state_e
{
PROCESS_STATE_SEND_ADDR = 0,
PROCESS_STATE_SEND_DATA,
PROCESS_STATE_ENABLE_READ,
PROCESS_STATE_READ_DATA,
PROCESS_STATE_FETCH_NEXT,
PROCESS_STATE_TRANSFERT_DONE
};
/* Trace events */
enum pic32mz_trace_e
{
I2CEVENT_NONE = 0, /* No events have occurred with this status */
I2CEVENT_SENDADDR, /* Start/Master bit set and address sent, param = msgc */
I2CEVENT_SENDBYTE, /* Send byte, param = dcnt */
I2CEVENT_RCVMODEEN, /* Receive mode enabled, param = 0 */
I2CEVENT_RCVBYTE, /* Read more data, param = dcnt */
I2CEVENT_NOSTART, /* BTF on last byte with no restart, param = dcnt */
I2CEVENT_STARTRESTART, /* Last byte sent, re-starting, param = 0 */
I2CEVENT_STOP, /* Last byte sten, send stop, param = 0 */
I2CEVENT_WAKEUP, /* Everything is done, wake up caller, param = 0 */
I2CEVENT_ERROR /* Bus collision error, param = 0 */
};
/* Trace data */
struct pic32mz_trace_s
{
uint32_t status; /* I2C 32-bit SR2|SR1 status */
uint32_t count; /* Interrupt count when status change */
enum pic32mz_trace_e event; /* Last event that occurred with this status */
uint32_t parm; /* Parameter associated with the event */
clock_t time; /* First of event or first status */
};
/* I2C Device hardware configuration */
struct pic32mz_i2c_config_s
{
uint32_t base; /* I2C base address */
uint32_t scl_pin; /* GPIO configuration for SCL as SCL */
uint32_t sda_pin; /* GPIO configuration for SDA as SDA */
uint8_t mode; /* Master or Slave mode */
#ifndef CONFIG_I2C_POLLED
uint32_t ev_irq; /* Event IRQ */
uint32_t er_irq; /* Error IRQ */
#endif
};
/* I2C Device Private Data */
struct pic32mz_i2c_priv_s
{
/* Standard I2C operations */
const struct i2c_ops_s *ops;
/* Port configuration */
const struct pic32mz_i2c_config_s *config;
int refs; /* Reference count */
mutex_t lock; /* Mutual exclusion mutex */
#ifndef CONFIG_I2C_POLLED
sem_t sem_isr; /* Interrupt wait semaphore */
#endif
volatile uint8_t intstate; /* Interrupt handshake (see enum
* pic32mz_intstate_e) */
volatile uint8_t process_state; /* State of the isr process */
uint8_t msgc; /* Message count */
struct i2c_msg_s *msgv; /* Message list */
uint8_t *ptr; /* Current message buffer */
uint32_t frequency; /* Current I2C frequency */
int dcnt; /* Current message length */
uint16_t flags; /* Current message flags */
/* I2C trace support */
#ifdef CONFIG_I2C_TRACE
int tndx; /* Trace array index */
clock_t start_time; /* Time when the trace was started */
/* The actual trace data */
struct pic32mz_trace_s trace[CONFIG_I2C_NTRACE];
#endif
uint32_t status; /* End of transfer status */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static inline uint32_t
pic32mz_i2c_getreg(struct pic32mz_i2c_priv_s *priv,
uint8_t offset);
static inline void pic32mz_i2c_putreg(struct pic32mz_i2c_priv_s *priv,
uint8_t offset, uint32_t value);
static inline void pic32mz_i2c_modifyreg(struct pic32mz_i2c_priv_s *priv,
uint8_t offset, uint32_t clearbits,
uint32_t setbits);
#ifdef CONFIG_PICM32MZ_I2C_DYNTIMEO
static uint32_t pic32mz_i2c_toticks(int msgc, struct i2c_msg_s *msgs);
#endif /* CONFIG_PIC32MZ_I2C_DYNTIMEO */
static inline int
pic32mz_i2c_sem_waitdone(struct pic32mz_i2c_priv_s *priv);
static inline void
pic32mz_i2c_sem_waitidle(struct pic32mz_i2c_priv_s *priv);
#ifdef CONFIG_I2C_TRACE
static void pic32mz_i2c_tracereset(struct pic32mz_i2c_priv_s *priv);
static void pic32mz_i2c_tracenew(struct pic32mz_i2c_priv_s *priv,
uint32_t status);
static void
pic32mz_i2c_traceevent(struct pic32mz_i2c_priv_s *priv,
enum pic32mz_trace_e event, uint32_t parm);
static void pic32mz_i2c_tracedump(struct pic32mz_i2c_priv_s *priv);
#endif /* CONFIG_I2C_TRACE */
static inline int
pic32mz_i2c_setbaudrate(struct pic32mz_i2c_priv_s *priv,
uint32_t frequency);
static inline void
pic32mz_i2c_send_start(struct pic32mz_i2c_priv_s *priv);
static inline void
pic32mz_i2c_send_stop(struct pic32mz_i2c_priv_s *priv);
static inline void
pic32mz_i2c_send_repeatedstart(struct pic32mz_i2c_priv_s *priv);
static inline void pic32mz_i2c_send_ack(struct pic32mz_i2c_priv_s *priv,
bool ack);
static inline void pic32mz_i2c_transmitbyte(struct pic32mz_i2c_priv_s *priv,
uint8_t data);
static inline uint32_t
pic32mz_i2c_receivebyte(struct pic32mz_i2c_priv_s *priv);
static inline uint32_t
pic32mz_i2c_getstatus(struct pic32mz_i2c_priv_s *priv);
static inline bool
pic32mz_i2c_master_inactive(struct pic32mz_i2c_priv_s *priv);
static int pic32mz_i2c_isr_process(struct pic32mz_i2c_priv_s *priv);
#ifndef CONFIG_I2C_POLLED
static int pic32mz_i2c_isr(int irq, void *context, void *arg);
#endif /* !CONFIG_I2C_POLLED */
static int pic32mz_i2c_init(struct pic32mz_i2c_priv_s *priv);
static int pic32mz_i2c_deinit(struct pic32mz_i2c_priv_s *priv);
static int pic32mz_i2c_transfer(struct i2c_master_s *dev,
struct i2c_msg_s *msgs, int count);
#ifdef CONFIG_I2C_RESET
static int pic32mz_i2c_reset(struct i2c_master_s *dev);
#endif
/****************************************************************************
* Private Data
****************************************************************************/
/* Trace events strings */
#ifdef CONFIG_I2C_TRACE
static const char *g_trace_names[] =
{
"NONE ",
"SENDADDR ",
"SENDBYTE ",
"RCVMODEEN ",
"RCVBYTE ",
"NOSTART ",
"RESTART ",
"STOP ",
"WAKEUP ",
"ERROR "
};
#endif
/* I2C interface */
static const struct i2c_ops_s pic32mz_i2c_ops =
{
.transfer = pic32mz_i2c_transfer,
#ifdef CONFIG_I2C_RESET
.reset = pic32mz_i2c_reset,
#endif
};
/* I2C device structures */
#ifdef CONFIG_PIC32MZ_I2C1
static const struct pic32mz_i2c_config_s pic32mz_i2c1_config =
{
.base = PIC32MZ_I2C1_K1BASE,
.scl_pin = GPIO_I2C1_SCL,
.sda_pin = GPIO_I2C1_SDA,
#ifndef CONFIG_I2C_POLLED
.ev_irq = PIC32MZ_IRQ_I2C1M,
.er_irq = PIC32MZ_IRQ_I2C1COL
#endif
};
static struct pic32mz_i2c_priv_s pic32mz_i2c1_priv =
{
.ops = &pic32mz_i2c_ops,
.config = &pic32mz_i2c1_config,
.refs = 0,
.lock = NXMUTEX_INITIALIZER,
#ifndef CONFIG_I2C_POLLED
.sem_isr = SEM_INITIALIZER(0),
#endif
.intstate = INTSTATE_IDLE,
.msgc = 0,
.msgv = NULL,
.ptr = NULL,
.frequency = 0,
.dcnt = 0,
.flags = 0,
.status = 0,
};
#endif
#ifdef CONFIG_PIC32MZ_I2C2
static const struct pic32mz_i2c_config_s pic32mz_i2c2_config =
{
.base = PIC32MZ_I2C2_K1BASE,
.scl_pin = GPIO_I2C2_SCL,
.sda_pin = GPIO_I2C2_SDA,
#ifndef CONFIG_I2C_POLLED
.ev_irq = PIC32MZ_IRQ_I2C2M,
.er_irq = PIC32MZ_IRQ_I2C2COL
#endif
};
static struct pic32mz_i2c_priv_s pic32mz_i2c2_priv =
{
.ops = &pic32mz_i2c_ops,
.config = &pic32mz_i2c2_config,
.refs = 0,
.lock = NXMUTEX_INITIALIZER,
#ifndef CONFIG_I2C_POLLED
.sem_isr = SEM_INITIALIZER(0),
#endif
.intstate = INTSTATE_IDLE,
.msgc = 0,
.msgv = NULL,
.ptr = NULL,
.frequency = 0,
.dcnt = 0,
.flags = 0,
.status = 0,
};
#endif
#ifdef CONFIG_PIC32MZ_I2C3
static const struct pic32mz_i2c_config_s pic32mz_i2c3_config =
{
.base = PIC32MZ_I2C3_K1BASE,
.scl_pin = GPIO_I2C3_SCL,
.sda_pin = GPIO_I2C3_SDA,
#ifndef CONFIG_I2C_POLLED
.ev_irq = PIC32MZ_IRQ_I2C3M,
.er_irq = PIC32MZ_IRQ_I2C3COL
#endif
};
static struct pic32mz_i2c_priv_s pic32mz_i2c3_priv =
{
.ops = &pic32mz_i2c_ops,
.config = &pic32mz_i2c3_config,
.refs = 0,
.lock = NXMUTEX_INITIALIZER,
#ifndef CONFIG_I2C_POLLED
.sem_isr = SEM_INITIALIZER(0),
#endif
.intstate = INTSTATE_IDLE,
.msgc = 0,
.msgv = NULL,
.ptr = NULL,
.frequency = 0,
.dcnt = 0,
.flags = 0,
.status = 0,
};
#endif
#ifdef CONFIG_PIC32MZ_I2C4
static const struct pic32mz_i2c_config_s pic32mz_i2c4_config =
{
.base = PIC32MZ_I2C4_K1BASE,
.scl_pin = GPIO_I2C4_SCL,
.sda_pin = GPIO_I2C4_SDA,
#ifndef CONFIG_I2C_POLLED
.ev_irq = PIC32MZ_IRQ_I2C4M,
.er_irq = PIC32MZ_IRQ_I2C4COL
#endif
};
static struct pic32mz_i2c_priv_s pic32mz_i2c4_priv =
{
.ops = &pic32mz_i2c_ops,
.config = &pic32mz_i2c4_config,
.refs = 0,
.lock = NXMUTEX_INITIALIZER,
#ifndef CONFIG_I2C_POLLED
.sem_isr = SEM_INITIALIZER(0),
#endif
.intstate = INTSTATE_IDLE,
.msgc = 0,
.msgv = NULL,
.ptr = NULL,
.frequency = 0,
.dcnt = 0,
.flags = 0,
.status = 0,
};
#endif
#ifdef CONFIG_PIC32MZ_I2C5
static const struct pic32mz_i2c_config_s pic32mz_i2c5_config =
{
.base = PIC32MZ_I2C5_K1BASE,
.scl_pin = GPIO_I2C5_SCL,
.sda_pin = GPIO_I2C5_SDA,
#ifndef CONFIG_I2C_POLLED
.ev_irq = PIC32MZ_IRQ_I2C5M,
.er_irq = PIC32MZ_IRQ_I2C5COL
#endif
};
static struct pic32mz_i2c_priv_s pic32mz_i2c5_priv =
{
.ops = &pic32mz_i2c_ops,
.config = &pic32mz_i2c5_config,
.refs = 0,
.lock = NXMUTEX_INITIALIZER,
#ifndef CONFIG_I2C_POLLED
.sem_isr = SEM_INITIALIZER(0),
#endif
.intstate = INTSTATE_IDLE,
.msgc = 0,
.msgv = NULL,
.ptr = NULL,
.frequency = 0,
.dcnt = 0,
.flags = 0,
.status = 0,
};
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
#ifdef CONFIG_I2C_TRACE
static void pic32mz_i2c_traceclear(struct pic32mz_i2c_priv_s *priv)
{
struct pic32mz_trace_s *trace = &priv->trace[priv->tndx];
trace->status = 0; /* I2C 32-bit status */
trace->count = 0; /* Interrupt count when status change */
trace->event = I2CEVENT_NONE; /* Last event that occurred with this status */
trace->parm = 0; /* Parameter associated with the event */
trace->time = 0; /* Time of first status or event */
}
static void pic32mz_i2c_tracereset(struct pic32mz_i2c_priv_s *priv)
{
/* Reset the trace info for a new data collection */
priv->tndx = 0;
priv->start_time = clock_systime_ticks();
pic32mz_i2c_traceclear(priv);
}
static void pic32mz_i2c_tracenew(struct pic32mz_i2c_priv_s *priv,
uint32_t status)
{
struct pic32mz_trace_s *trace = &priv->trace[priv->tndx];
/* Is the current entry uninitialized? Has the status changed? */
if (trace->count == 0 || status != trace->status)
{
/* Yes.. Was it the status changed? */
if (trace->count != 0)
{
/* Yes.. bump up the trace index (unless out of trace entries) */
if (priv->tndx >= (CONFIG_I2C_NTRACE - 1))
{
i2cerr("ERROR: Trace table overflow\n");
return;
}
priv->tndx++;
trace = &priv->trace[priv->tndx];
}
/* Initialize the new trace entry */
pic32mz_i2c_traceclear(priv);
trace->status = status;
trace->count = 1;
trace->time = clock_systime_ticks();
}
else
{
/* Just increment the count of times that we have seen this status */
trace->count++;
}
}
static void pic32mz_i2c_traceevent(struct pic32mz_i2c_priv_s *priv,
enum pic32mz_trace_e event, uint32_t parm)
{
struct pic32mz_trace_s *trace;
if (event != I2CEVENT_NONE)
{
trace = &priv->trace[priv->tndx];
/* Initialize the new trace entry */
trace->event = event;
trace->parm = parm;
/* Bump up the trace index (unless we are out of trace entries) */
if (priv->tndx >= (CONFIG_I2C_NTRACE - 1))
{
i2cerr("ERROR: Trace table overflow\n");
return;
}
priv->tndx++;
pic32mz_i2c_traceclear(priv);
}
}
static void pic32mz_i2c_tracedump(struct pic32mz_i2c_priv_s *priv)
{
struct pic32mz_trace_s *trace;
int i;
syslog(LOG_DEBUG, "Elapsed time: %ld\n",
(long)(clock_systime_ticks() - priv->start_time));
for (i = 0; i < priv->tndx; i++)
{
trace = &priv->trace[i];
syslog(LOG_DEBUG,
"%2d. STATUS: %04x COUNT: %3d EVENT: %s(%2d) "
"PARM: %08x TIME: %d\n",
i + 1, trace->status, trace->count, g_trace_names[trace->event],
trace->event, trace->parm, trace->time - priv->start_time);
}
}
#endif /* CONFIG_I2C_TRACE */
/****************************************************************************
* Name: pic32mz_i2c_getreg
*
* Description:
* Get a 32-bit register value by offset
*
****************************************************************************/
static inline uint32_t
pic32mz_i2c_getreg(struct pic32mz_i2c_priv_s *priv, uint8_t offset)
{
return getreg32(priv->config->base + offset);
}
/****************************************************************************
* Name: pic32mz_i2c_putreg
*
* Description:
* Put a 32-bit register value by offset
*
****************************************************************************/
static inline void pic32mz_i2c_putreg(struct pic32mz_i2c_priv_s *priv,
uint8_t offset, uint32_t value)
{
putreg32(value, priv->config->base + offset);
}
/****************************************************************************
* Name: pic32mz_i2c_modifyreg
*
* Description:
* Modify a 32-bit register value by offset
*
****************************************************************************/
static inline void pic32mz_i2c_modifyreg(struct pic32mz_i2c_priv_s *priv,
uint8_t offset, uint32_t clearbits,
uint32_t setbits)
{
modifyreg32(priv->config->base + offset, clearbits, setbits);
}
/****************************************************************************
* Name: pic32mz_i2c_toticks
*
* Description:
* Return a micro-second delay based on the number of bytes left to be
* processed.
*
****************************************************************************/
#ifdef CONFIG_PIC32MZ_I2C_DYNTIMEO
static uint32_t pic32mz_i2c_toticks(int msgc, struct i2c_msg_s *msgs)
{
size_t bytecount = 0;
int i;
/* Count the number of bytes left to process */
for (i = 0; i < msgc; i++)
{
bytecount += msgs[i].length;
}
/* Then return a number of microseconds based on a user provided scaling
* factor.
*/
return USEC2TICK(CONFIG_PIC32MZ_I2C_DYNTIMEO_USECPERBYTE * bytecount);
}
#endif
/****************************************************************************
* Name: pic32mz_i2c_sem_waitdone
*
* Description:
* Wait for a transfer to complete
*
* There are two versions of this function. The first is included when using
* interrupts while the second is used if polling (CONFIG_I2C_POLLED=y).
*
****************************************************************************/
#ifndef CONFIG_I2C_POLLED
static inline int
pic32mz_i2c_sem_waitdone(struct pic32mz_i2c_priv_s *priv)
{
irqstate_t flags;
int ret;
flags = enter_critical_section();
/* Signal the interrupt handler that we are waiting */
priv->intstate = INTSTATE_WAITING;
do
{
/* Wait until either the transfer is complete or the timeout expires */
#ifdef CONFIG_PIC32MZ_I2C_DYNTIMEO
ret = nxsem_tickwait_uninterruptible(&priv->sem_isr,
pic32mz_i2c_toticks(priv->msgc, priv->msgv));
#else
ret = nxsem_tickwait_uninterruptible(&priv->sem_isr,
CONFIG_PIC32MZ_I2CTIMEOTICKS);
#endif
if (ret < 0)
{
/* Break out of the loop on irrecoverable errors. This would
* include timeouts and mystery errors reported by
* nxsem_tickwait_uninterruptible.
*/
break;
}
}
/* Loop until the interrupt level transfer is complete. */
while (priv->intstate != INTSTATE_DONE);
/* Set the interrupt state back to idle. */
priv->intstate = INTSTATE_IDLE;
/* Disable I2C interrupts. */
up_disable_irq(priv->config->ev_irq);
up_disable_irq(priv->config->er_irq);
leave_critical_section(flags);
return ret;
}
#else
static inline int
pic32mz_i2c_sem_waitdone(struct pic32mz_i2c_priv_s *priv)
{
clock_t timeout;
clock_t start;
clock_t elapsed;
int ret;
/* Get the timeout value */
#ifdef CONFIG_PIC32MZ_I2C_DYNTIMEO
timeout = pic32mz_i2c_toticks(priv->msgc, priv->msgv);
#else
timeout = CONFIG_PIC32MZ_I2CTIMEOTICKS;
#endif
/* Signal the interrupt handler that we are waiting. NOTE: Interrupts
* are currently disabled but will be temporarily re-enabled below when
* nxsem_tickwait_uninterruptible() sleeps.
*/
priv->intstate = INTSTATE_WAITING;
start = clock_systime_ticks();
do
{
/* Calculate the elapsed time */
elapsed = clock_systime_ticks() - start;
/* Poll by simply calling the timer interrupt handler until it
* reports that it is done.
*/
pic32mz_i2c_isr_process(priv);
}
/* Loop until the transfer is complete. */
while (priv->intstate != INTSTATE_DONE && elapsed < timeout);
i2cinfo("intstate: %d elapsed: %ld threshold: %ld status: %04x\n",
priv->intstate, (long)elapsed, (long)timeout, priv->status);
/* Set the interrupt state back to IDLE */
ret = priv->intstate == INTSTATE_DONE ? OK : -ETIMEDOUT;
priv->intstate = INTSTATE_IDLE;
return ret;
}
#endif
/****************************************************************************
* Name: pic32mz_i2c_sem_waitidle
*
* Description:
* Wait for the bus to be in idle.
*
****************************************************************************/
static inline void
pic32mz_i2c_sem_waitidle(struct pic32mz_i2c_priv_s *priv)
{
uint32_t timeout;
uint32_t start;
uint32_t elapsed;
uint32_t con;
uint32_t stat;
/* Select a timeout */
#ifdef CONFIG_PIC32MZ_I2C_DYNTIMEO
timeout = USEC2TICK(CONFIG_PIC32MZ_I2C_DYNTIMEO_STARTSTOP);
#else
timeout = CONFIG_PIC32MZ_I2CTIMEOTICKS;
#endif
start = clock_systime_ticks();
do
{
elapsed = clock_systime_ticks() - start;
/* The bus is idle if the five least significant bits of I2CxCON
* are cleared and the I2CxSTAT<TRSTAT> flag is cleared.
*/
con = pic32mz_i2c_getreg(priv, PIC32MZ_I2C_CON_OFFSET);
stat = pic32mz_i2c_getreg(priv, PIC32MZ_I2C_STAT_OFFSET);
if (((con & I2C_CON_IDLEMASK) == 0) && ((stat & I2C_STAT_TRSTAT) == 0))
{
return;
}
}
while (elapsed < timeout);
/* If we get here then a timeout occurred with the bus still in idle */
i2cinfo("Timeout with I2CxCON: %04x I2CxSTAT: %04x\n", con, stat);
}
/****************************************************************************
* Name: pic32mz_i2c_isr_process
*
* Description:
* Common interrupt service routine
*
****************************************************************************/
static int pic32mz_i2c_isr_process(struct pic32mz_i2c_priv_s *priv)
{
uint32_t status;
status = pic32mz_i2c_getstatus(priv);
/* Check for new trace setup */
pic32mz_i2c_tracenew(priv, status);
switch (priv->process_state)
{
/* The process starts from this state after a call to i2c_transfer.
* It may return here in the case of a write/read transaction,
* to send the address with the READ bit set.
*/
case PROCESS_STATE_SEND_ADDR:
pic32mz_i2c_traceevent(priv, I2CEVENT_SENDADDR, priv->msgc);
if (priv->msgc > 0 && priv->msgv != NULL)
{
priv->ptr = priv->msgv->buffer;
priv->dcnt = priv->msgv->length;
priv->flags = priv->msgv->flags;
/* Send the address byte and set the next state to either
* read or transmit the data.
*/
if (priv->flags & I2C_M_READ)
{
pic32mz_i2c_transmitbyte(priv, I2C_M_READ | priv->msgv->addr);
priv->process_state = PROCESS_STATE_ENABLE_READ;
}
else
{
pic32mz_i2c_transmitbyte(priv, priv->msgv->addr);
priv->process_state = PROCESS_STATE_SEND_DATA;
}
}
else
{
#ifndef CONFIG_I2C_POLLED
mips_clrpend_irq(priv->config->ev_irq);
#endif
}
break;
/* This state is reached either after sending the address to the slave,
* or, in the case of multi-byte buffer, after sending a byte.
* We should first check that the previous transmission is not in
* progress, and that the slave had acknowledged it.
*/
case PROCESS_STATE_SEND_DATA:
pic32mz_i2c_traceevent(priv, I2CEVENT_SENDBYTE, priv->dcnt);
/* No transmission is in progress. */
if ((status & I2C_STAT_TRSTAT) == 0)
{
/* ACK received from the slave. */
if ((status & I2C_STAT_ACKSTAT) == 0)
{
/* We need to keep one byte to send before we leave this state.
* This way we can trigger an interrupt and move to the next
* state.
*/
if (priv->dcnt > 1)
{
pic32mz_i2c_transmitbyte(priv, *priv->ptr++);
priv->dcnt--;
}
else
{
pic32mz_i2c_transmitbyte(priv, *priv->ptr++);
priv->dcnt--;
priv->process_state = PROCESS_STATE_FETCH_NEXT;
}
}
}
break;
/* This state is reached after sending the address to the slave with
* the read bit set, or, in the case of multi-byte transfer,
* after reading the first byte.
* We should first check that the previous transmission is not in
* progress, and that the slave had acknowledged it.
*/
case PROCESS_STATE_ENABLE_READ:
pic32mz_i2c_traceevent(priv, I2CEVENT_RCVMODEEN, 0);
/* No transmit is in progress. */
if ((status & I2C_STAT_TRSTAT) == 0)
{
/* ACK received from the slave. */
if ((status & I2C_STAT_ACKSTAT) == 0)
{
/* The master logic should be inactive before
* attempting to enable receive mode.
*/
if (pic32mz_i2c_master_inactive(priv))
{
pic32mz_i2c_putreg(priv, PIC32MZ_I2C_CONSET_OFFSET,
I2C_CON_RCEN);
priv->process_state = PROCESS_STATE_READ_DATA;
}
}
}
break;
/* This state reads a byte from the receive buffer.
* If there are more than one byte to read,
* it should go back to the previous state to enable
* the receive mode.
*/
case PROCESS_STATE_READ_DATA:
pic32mz_i2c_traceevent(priv, I2CEVENT_RCVBYTE, priv->dcnt);
/* Is data available in the receiver buffer? */
if ((status & I2C_STAT_RBF) != 0)
{
/* Read and send an ACK */
if (priv->dcnt > 1)
{
#ifdef CONFIG_I2C_POLLED
irqstate_t flags = enter_critical_section();
#endif
*priv->ptr++ = pic32mz_i2c_receivebyte(priv);
priv->dcnt--;
/* The master logic should be inactive before
* attempting to issue an ACK.
*/
if (pic32mz_i2c_master_inactive(priv))
{
pic32mz_i2c_send_ack(priv, true);
}
#ifdef CONFIG_I2C_POLLED
leave_critical_section(flags);
#endif
/* Go back and re-enable read mode to handle the rest of
* the data.
* It is cleared by the hardware at the end of the eighth bit.
*/
priv->process_state = PROCESS_STATE_ENABLE_READ;
}
/* Last byte, read and send a NACK */
else
{
#ifdef CONFIG_I2C_POLLED
irqstate_t flags = enter_critical_section();
#endif
*priv->ptr++ = pic32mz_i2c_receivebyte(priv);
priv->dcnt--;
/* The master logic should be inactive before
* attempting to issue a NACK.
*/
if (pic32mz_i2c_master_inactive(priv))
{
pic32mz_i2c_send_ack(priv, false);
}
#ifdef CONFIG_I2C_POLLED
leave_critical_section(flags);
#endif
priv->process_state = PROCESS_STATE_FETCH_NEXT;
}
}
break;
/* In this state we fetch the next mssage.
* Increment to next pointer and decrement message count.
* If we have an other set of data we will:
* - Issue a repeated start (I2C_M_NOSTOP flag set).
* - Continue with no start (I2C_M_NOSTART flag set).
* - Issue a start (No flag set).
* If no more data to send, issue a stop.
*/
case PROCESS_STATE_FETCH_NEXT:
priv->msgv++;
priv->msgc--;
if (priv->msgc > 0 && priv->msgv != NULL)
{
/* If the previous message had the I2C_M_NOSTOP flag set,
* this implies that we should issue a repeated start.
* (Note: priv->flags still has the previous flags.)
*/
if (priv->flags & I2C_M_NOSTOP)
{
pic32mz_i2c_traceevent(priv, I2CEVENT_STARTRESTART, 0);
/* The bus should be in idle before issuing a repeated start. */
if ((pic32mz_i2c_master_inactive(priv)) &&
(status & I2C_STAT_TRSTAT) == 0)
{
pic32mz_i2c_send_repeatedstart(priv);
priv->process_state = PROCESS_STATE_SEND_ADDR;
}
}
/* If the new message has the I2C_M_NOSTART flag set,
* this means that it's a continuation of the same transfer.
* We can't just move back to SEND_DATA as we need an interrupt.
* So one byte must be sent from here first.
*/
else if (priv->msgv->flags & I2C_M_NOSTART)
{
priv->ptr = priv->msgv->buffer;
priv->dcnt = priv->msgv->length;
priv->flags = priv->msgv->flags;
pic32mz_i2c_traceevent(priv, I2CEVENT_NOSTART, priv->dcnt);
if ((status & I2C_STAT_TRSTAT) == 0)
{
if ((status & I2C_STAT_ACKSTAT) == 0)
{
/* We have more than one byte.
* Send the first one, this will trigger an interrupt
* the rest will get sent later.
*/
if (priv->dcnt > 1)
{
pic32mz_i2c_transmitbyte(priv, *priv->ptr++);
priv->dcnt--;
priv->process_state = PROCESS_STATE_SEND_DATA;
}
/* Send the only byte we have and stay in this state
* to fetch the next message.
*/
else
{
pic32mz_i2c_transmitbyte(priv, *priv->ptr++);
priv->dcnt--;
}
}
}
}
/* If neither the I2C_M_NOSTOP nor the I2C_M_NOSTART is set,
* just issue a start and let the isr process the data.
*/
else
{
/* The bus should be in idle before issuing a start. */
if ((pic32mz_i2c_master_inactive(priv)) &&
(status & I2C_STAT_TRSTAT) == 0)
{
pic32mz_i2c_send_start(priv);
priv->process_state = PROCESS_STATE_SEND_ADDR;
}
}
}
else
{
/* The stop should be initiated here,
* as there is no other way to trigger an interrupt.
*/
pic32mz_i2c_traceevent(priv, I2CEVENT_STOP, 0);
/* The master logic should be inactive before
* attempting to issue a STOP.
*/
if (pic32mz_i2c_master_inactive(priv))
{
pic32mz_i2c_send_stop(priv);
priv->process_state = PROCESS_STATE_TRANSFERT_DONE;
}
}
break;
/* Arriving here, the transfer is complete.
* Wake up any thread that has been waiting for this event.
*/
case PROCESS_STATE_TRANSFERT_DONE:
pic32mz_i2c_traceevent(priv, I2CEVENT_WAKEUP, 0);
if (priv->msgv)
{
/* Is there a thread waiting for this event (there should be) */
if (priv->intstate == INTSTATE_WAITING)
{
#ifndef CONFIG_I2C_POLLED
nxsem_post(&priv->sem_isr);
#endif
priv->intstate = INTSTATE_DONE;
}
/* Mark that we have stopped with this transaction. */
priv->msgv = NULL;
}
break;
default:
/* Nothing goes here! */
break;
}
/* Clear the master interrupt flag. */
#ifndef CONFIG_I2C_POLLED
if (mips_pending_irq(priv->config->ev_irq))
{
mips_clrpend_irq(priv->config->ev_irq);
}
#endif
/* If an error interrupt has occurred. */
#ifndef CONFIG_I2C_POLLED
if (mips_pending_irq(priv->config->er_irq))
{
pic32mz_i2c_traceevent(priv, I2CEVENT_ERROR, 0);
mips_clrpend_irq(priv->config->er_irq);
}
#endif
priv->status = status;
return OK;
}
/****************************************************************************
* Name: pic32mz_i2c_isr
*
* Description:
* Common I2C interrupt service routine.
*
****************************************************************************/
#ifndef CONFIG_I2C_POLLED
static int pic32mz_i2c_isr(int irq, void *context, void *arg)
{
struct pic32mz_i2c_priv_s *priv = (struct pic32mz_i2c_priv_s *)arg;
DEBUGASSERT(priv != NULL);
return pic32mz_i2c_isr_process(priv);
}
#endif
/****************************************************************************
* Name: pic32mz_i2c_setbaudrate
*
* Description:
* Calculates the value of the baudrate.
*
****************************************************************************/
static inline int
pic32mz_i2c_setbaudrate(struct pic32mz_i2c_priv_s *priv,
uint32_t frequency)
{
uint32_t baudrate;
if (frequency != priv->frequency)
{
/* BOARD_PBCLK and frequency are both given in Hz. */
baudrate = (uint32_t)(((BOARD_PBCLK2 / (2 * frequency)) -
(BOARD_PBCLK2 / 10000000) - 2));
/* Values of 0x0 and 0x1 are prohibited. */
if (baudrate == 0x0 || baudrate == 0x1)
{
return ERROR;
}
else
{
pic32mz_i2c_putreg(priv, PIC32MZ_I2C_BRG_OFFSET, baudrate);
priv->frequency = frequency;
/* Enable Slew Rate Control when operating on High Speed mode. */
if (frequency == 400000)
{
pic32mz_i2c_putreg(priv, PIC32MZ_I2C_CONCLR_OFFSET,
I2C_CON_DISSLW);
}
else
{
pic32mz_i2c_putreg(priv, PIC32MZ_I2C_CONSET_OFFSET,
I2C_CON_DISSLW);
}
}
}
return OK;
}
/****************************************************************************
* Name: pic32mz_i2c_send_start
*
* Description:
* Initiate a start condition.
*
****************************************************************************/
static inline void
pic32mz_i2c_send_start(struct pic32mz_i2c_priv_s *priv)
{
pic32mz_i2c_putreg(priv, PIC32MZ_I2C_CONSET_OFFSET, I2C_CON_SEN);
/* To avoid bus collision during polling. */
#ifdef CONFIG_I2C_POLLED
while ((pic32mz_i2c_getreg(priv, PIC32MZ_I2C_CON_OFFSET) &
I2C_CON_SEN) != 0);
#endif
}
/****************************************************************************
* Name: pic32mz_i2c_send_stop
*
* Description:
* Initiate a stop condition.
*
****************************************************************************/
static inline void pic32mz_i2c_send_stop(struct pic32mz_i2c_priv_s *priv)
{
pic32mz_i2c_putreg(priv, PIC32MZ_I2C_CONSET_OFFSET, I2C_CON_PEN);
/* To avoid bus collision during polling. */
#ifdef CONFIG_I2C_POLLED
while ((pic32mz_i2c_getreg(priv, PIC32MZ_I2C_CON_OFFSET) &
I2C_CON_PEN) != 0);
#endif
}
/****************************************************************************
* Name: pic32mz_i2c_send_repeatedstart
*
* Description:
* Initiate a repeated start condition.
*
****************************************************************************/
static inline void
pic32mz_i2c_send_repeatedstart(struct pic32mz_i2c_priv_s *priv)
{
pic32mz_i2c_putreg(priv, PIC32MZ_I2C_CONSET_OFFSET, I2C_CON_RSEN);
/* To avoid bus collision during polling. */
#ifdef CONFIG_I2C_POLLED
while ((pic32mz_i2c_getreg(priv, PIC32MZ_I2C_CON_OFFSET) &
I2C_CON_RSEN) != 0);
#endif
}
/****************************************************************************
* Name: pic32mz_i2c_send_ack
*
* Description:
* Issue an ACK or a NACK.
*
****************************************************************************/
static inline void pic32mz_i2c_send_ack(struct pic32mz_i2c_priv_s *priv,
bool ack)
{
if (ack)
{
pic32mz_i2c_putreg(priv, PIC32MZ_I2C_CONCLR_OFFSET, I2C_CON_ACKDT);
}
else
{
pic32mz_i2c_putreg(priv, PIC32MZ_I2C_CONSET_OFFSET, I2C_CON_ACKDT);
}
pic32mz_i2c_putreg(priv, PIC32MZ_I2C_CONSET_OFFSET, I2C_CON_ACKEN);
/* To avoid bus collision during polling. */
#ifdef CONFIG_I2C_POLLED
while ((pic32mz_i2c_getreg(priv, PIC32MZ_I2C_CON_OFFSET) &
I2C_CON_ACKEN) != 0);
#endif
}
/****************************************************************************
* Name: pic32mz_i2c_transmitbyte
*
* Description:
* Transmit a byte.
*
****************************************************************************/
static inline void pic32mz_i2c_transmitbyte(struct pic32mz_i2c_priv_s *priv,
uint8_t data)
{
pic32mz_i2c_putreg(priv, PIC32MZ_I2C_TRN_OFFSET, data);
/* To avoid bus collision during polling. */
#ifdef CONFIG_I2C_POLLED
while ((pic32mz_i2c_getreg(priv, PIC32MZ_I2C_STAT_OFFSET) &
I2C_STAT_TRSTAT) != 0);
#endif
}
/****************************************************************************
* Name: pic32mz_i2c_receivebyte
*
* Description:
* Receive a byte.
*
****************************************************************************/
static inline uint32_t
pic32mz_i2c_receivebyte(struct pic32mz_i2c_priv_s *priv)
{
uint32_t val;
/* To avoid bus collision during polling. */
#ifdef CONFIG_I2C_POLLED
while ((pic32mz_i2c_getreg(priv, PIC32MZ_I2C_STAT_OFFSET) &
I2C_CON_RCEN) != 0);
#endif
val = pic32mz_i2c_getreg(priv, PIC32MZ_I2C_RCV_OFFSET);
return val;
}
/****************************************************************************
* Name: pic32mz_i2c_master_inactive
*
* Description:
* Check if the bus is inactive.
* No start, stop, ACK is in progress.
*
****************************************************************************/
static inline bool
pic32mz_i2c_master_inactive(struct pic32mz_i2c_priv_s *priv)
{
uint32_t con;
con = pic32mz_i2c_getreg(priv, PIC32MZ_I2C_CON_OFFSET);
return ((con & I2C_CON_IDLEMASK) ? false:true);
}
/****************************************************************************
* Name: pic32mz_i2c_getstatus
*
* Description:
* Get the STAT register.
*
****************************************************************************/
static inline uint32_t
pic32mz_i2c_getstatus(struct pic32mz_i2c_priv_s *priv)
{
return pic32mz_i2c_getreg(priv, PIC32MZ_I2C_STAT_OFFSET);
}
/****************************************************************************
* Name: pic32mz_i2c_init
*
* Description:
* Setup the I2C hardware, ready for operation with defaults
*
****************************************************************************/
static int pic32mz_i2c_init(struct pic32mz_i2c_priv_s *priv)
{
/* Force a frequency update */
priv->frequency = 0;
pic32mz_i2c_setbaudrate(priv, 100000);
/* Attach ISRs */
#ifndef CONFIG_I2C_POLLED
irq_attach(priv->config->ev_irq, pic32mz_i2c_isr, priv);
irq_attach(priv->config->er_irq, pic32mz_i2c_isr, priv);
#endif
/* Enable the I2C hardware.
* The I2C hardware, when enabled, takes control over the pins.
* The module overrides the port state and direction.
* No need to configure the pins here.
*/
pic32mz_i2c_putreg(priv, PIC32MZ_I2C_CONSET_OFFSET, I2C_CON_ON);
return OK;
}
/****************************************************************************
* Name: pic32mz_i2c_deinit
*
* Description:
* Shutdown the I2C hardware
*
****************************************************************************/
static int pic32mz_i2c_deinit(struct pic32mz_i2c_priv_s *priv)
{
/* Disable I2C */
pic32mz_i2c_putreg(priv, PIC32MZ_I2C_CONCLR_OFFSET, I2C_CON_ON);
/* Disable and detach ISRs */
#ifndef CONFIG_I2C_POLLED
up_disable_irq(priv->config->ev_irq);
up_disable_irq(priv->config->er_irq);
irq_detach(priv->config->ev_irq);
irq_detach(priv->config->er_irq);
#endif
return OK;
}
/****************************************************************************
* Device Driver Operations
****************************************************************************/
/****************************************************************************
* Name: pic32mz_i2c_transfer
*
* Description:
* Generic I2C transfer function
*
****************************************************************************/
static int pic32mz_i2c_transfer(struct i2c_master_s *dev,
struct i2c_msg_s *msgs, int count)
{
struct pic32mz_i2c_priv_s *priv = (struct pic32mz_i2c_priv_s *)dev;
uint32_t status = 0;
int ret;
/* Acquire the mutex. */
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return ret;
}
/* Wait for the bus to be in an idle state. */
pic32mz_i2c_sem_waitidle(priv);
/* Clear any pending error interrupts. */
#ifndef CONFIG_I2C_POLLED
mips_clrpend_irq(priv->config->er_irq);
#endif
/* Old transfers are done */
/* Reset ptr and dcnt to ensure an unexpected data interrupt doesn't
* overwrite stale data.
*/
priv->dcnt = 0;
priv->ptr = NULL;
priv->msgv = msgs;
priv->msgc = count;
/* Reset I2C trace logic */
pic32mz_i2c_tracereset(priv);
/* Set the baudrate. */
pic32mz_i2c_setbaudrate(priv, priv->frequency);
#ifndef CONFIG_I2C_POLLED
/* Enable interrupts here so when we send the start condition
* below the ISR will fire if the data was sent.
*/
up_enable_irq(priv->config->ev_irq);
up_enable_irq(priv->config->er_irq);
#endif
priv->status = 0;
pic32mz_i2c_send_start(priv);
/* Indicate to the process where to start. */
priv->process_state = PROCESS_STATE_SEND_ADDR;
if (pic32mz_i2c_sem_waitdone(priv) < 0)
{
status = pic32mz_i2c_getstatus(priv);
ret = -ETIMEDOUT;
i2cerr("ERROR: Timed out: CON: 0x%04x status: 0x%04x\n",
pic32mz_i2c_getreg(priv, PIC32MZ_I2C_CON_OFFSET), status);
}
else
{
status = pic32mz_i2c_getstatus(priv);
}
/* Check for errors. */
if ((status & I2C_STAT_BCL))
{
ret = -EIO;
}
else if ((status & I2C_STAT_IWCOL) != 0)
{
ret = -EIO;
}
else if ((status & I2C_STAT_I2COV) != 0)
{
ret = -EIO;
}
/* Dump the trace result */
pic32mz_i2c_tracedump(priv);
/* Ensure ISR happening after we finish can't overwrite any user data */
priv->dcnt = 0;
priv->ptr = NULL;
nxmutex_unlock(&priv->lock);
return ret;
}
/****************************************************************************
* Name: pic32mz_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 pic32mz_i2c_reset(struct i2c_master_s *dev)
{
struct pic32mz_i2c_priv_s *priv = (struct pic32mz_i2c_priv_s *)dev;
unsigned int clock_count;
unsigned int stretch_count;
uint32_t frequency;
int ret = ERROR;
DEBUGASSERT(dev);
/* Our caller must own a ref */
DEBUGASSERT(priv->refs > 0);
/* Lock out other clients */
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return ret;
}
/* Save the current frequency */
frequency = priv->frequency;
/* De-init the port */
pic32mz_i2c_deinit(priv);
/* Use GPIO configuration to un-wedge the bus */
pic32mz_configgpio(priv->scl);
pic32mz_configgpio(priv->sda);
/* Let SDA go high */
pic32mz_gpiowrite(priv->sda, 1);
/* Clock the bus until any slaves currently driving it let it go. */
clock_count = 0;
while (!pic32mz_gpioread(priv->sda))
{
/* Give up if we have tried too hard */
if (clock_count++ > 10)
{
goto out;
}
/* Sniff to make sure that clock stretching has finished.
*
* If the bus never relaxes, the reset has failed.
*/
stretch_count = 0;
while (!pic32mz_gpioread(priv->scl))
{
/* Give up if we have tried too hard */
if (stretch_count++ > 10)
{
goto out;
}
up_udelay(10);
}
/* Drive SCL low */
pic32mz_gpiowrite(priv->scl, 0);
up_udelay(10);
/* Drive SCL high again */
pic32mz_gpiowrite(priv->scl, 1);
up_udelay(10);
}
/* Generate a start followed by a stop to reset slave
* state machines.
*/
pic32mz_gpiowrite(priv->sda, 0);
up_udelay(10);
pic32mz_gpiowrite(priv->scl, 0);
up_udelay(10);
pic32mz_gpiowrite(priv->scl, 1);
up_udelay(10);
pic32mz_gpiowrite(priv->sda, 1);
up_udelay(10);
/* Revert the GPIO configuration. */
pic32mz_unconfiggpio(priv->sda);
pic32mz_unconfiggpio(priv->scl);
/* Re-init the port */
pic32mz_i2c_init(priv);
/* Restore the frequency */
pic32mz_i2c_setclock(priv, frequency);
ret = OK;
out:
/* Release the port for reuse by other clients */
nxmutex_unlock(&priv->lock);
}
#endif /* CONFIG_I2C_RESET */
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: pic32mz_i2cbus_initialize
*
* Description:
* Initialize one I2C bus
*
****************************************************************************/
struct i2c_master_s *pic32mz_i2cbus_initialize(int port)
{
struct pic32mz_i2c_priv_s *priv = NULL;
/* Get I2C private structure */
switch (port)
{
#ifdef CONFIG_PIC32MZ_I2C1
case 1:
priv = (struct pic32mz_i2c_priv_s *)&pic32mz_i2c1_priv;
break;
#endif
#ifdef CONFIG_PIC32MZ_I2C2
case 2:
priv = (struct pic32mz_i2c_priv_s *)&pic32mz_i2c2_priv;
break;
#endif
#ifdef CONFIG_PIC32MZ_I2C3
case 3:
priv = (struct pic32mz_i2c_priv_s *)&pic32mz_i2c3_priv;
break;
#endif
#ifdef CONFIG_PIC32MZ_I2C4
case 4:
priv = (struct pic32mz_i2c_priv_s *)&pic32mz_i2c4_priv;
break;
#endif
#ifdef CONFIG_PIC32MZ_I2C5
case 5:
priv = (struct pic32mz_i2c_priv_s *)&pic32mz_i2c5_priv;
break;
#endif
default:
return NULL;
}
/* Initialize private data for the first time, increment reference count,
* power-up hardware and configure GPIOs.
*/
nxmutex_lock(&priv->lock);
if (priv->refs++ == 0)
{
pic32mz_i2c_init(priv);
}
nxmutex_unlock(&priv->lock);
return (struct i2c_master_s *)priv;
}
/****************************************************************************
* Name: pic32mz_i2cbus_uninitialize
*
* Description:
* Uninitialize an I2C bus
*
****************************************************************************/
int pic32mz_i2cbus_uninitialize(struct i2c_master_s *dev)
{
struct pic32mz_i2c_priv_s *priv = (struct pic32mz_i2c_priv_s *)dev;
DEBUGASSERT(dev);
/* Decrement reference count and check for underflow */
if (priv->refs == 0)
{
return ERROR;
}
nxmutex_lock(&priv->lock);
if (--priv->refs)
{
nxmutex_unlock(&priv->lock);
return OK;
}
/* Disable I2C hardware */
pic32mz_i2c_deinit(priv);
nxmutex_unlock(&priv->lock);
return OK;
}
#endif /* CONFIG_PIC32MZ_I2C */