diff --git a/arch/mips/include/pic32mz/chip.h b/arch/mips/include/pic32mz/chip.h index c54679c66af..e6ef4ba62bf 100644 --- a/arch/mips/include/pic32mz/chip.h +++ b/arch/mips/include/pic32mz/chip.h @@ -195,7 +195,7 @@ # define CHIP_NTRACE 1 /* Has trace capability */ #else -# error "Unrecognized PIC32MZ device +# error "Unrecognized PIC32MZ device" #endif /**************************************************************************** diff --git a/arch/mips/src/pic32mz/Kconfig b/arch/mips/src/pic32mz/Kconfig index 8db953ea860..ad3346bd694 100644 --- a/arch/mips/src/pic32mz/Kconfig +++ b/arch/mips/src/pic32mz/Kconfig @@ -61,6 +61,10 @@ config PIC32MZ_SPI bool default n +config PIC32MZ_I2C + bool + default n + config PIC32MZ_T1 bool default y @@ -146,22 +150,27 @@ config PIC32MZ_OC5 config PIC32MZ_I2C1 bool "I2C1" default n + select PIC32MZ_I2C config PIC32MZ_I2C2 bool "I2C2" default n + select PIC32MZ_I2C config PIC32MZ_I2C3 bool "I2C3" default n + select PIC32MZ_I2C config PIC32MZ_I2C4 bool "I2C4" default n + select PIC32MZ_I2C config PIC32MZ_I2C5 bool "I2C5" default n + select PIC32MZ_I2C config PIC32MZ_SPI1 bool "SPI1" @@ -364,6 +373,41 @@ config PIC32MZ_SPI_REGDEBUG endmenu # SPI Driver Configuration +menu "I2C Driver Configuration" + depends on PIC32MZ_I2C + +config PIC32MZ_I2C_DYNTIMEO + bool "Use dynamic timeouts" + default n + depends on PIC32MZ_I2C + +config PIC32MZ_I2C_DYNTIMEO_USECPERBYTE + int "Timeout Microseconds per Byte" + default 500 + depends on PIC32MZ_I2C_DYNTIMEO + +config PIC32MZ_I2C_DYNTIMEO_STARTSTOP + int "Timeout for Idle state (Milliseconds)" + default 1000 + depends on PIC32MZ_I2C_DYNTIMEO + +config PIC32MZ_I2CTIMEOSEC + int "Timeout seconds" + default 0 + depends on PIC32MZ_I2C + +config PIC32MZ_I2CTIMEOMS + int "Timeout Milliseconds" + default 500 + depends on PIC32MZ_I2C && !PIC32MZ_I2C_DYNTIMEO + +config PIC32MZ_I2CTIMEOTICKS + int "Timeout for Idle state (ticks)" + default 500 + depends on PIC32MZ_I2C && !PIC32MZ_I2C_DYNTIMEO + +endmenu # I2C Configuration + config PIC32MZ_T1_SOSC bool default n diff --git a/arch/mips/src/pic32mz/Make.defs b/arch/mips/src/pic32mz/Make.defs index 878efb39d2b..5577d868a65 100644 --- a/arch/mips/src/pic32mz/Make.defs +++ b/arch/mips/src/pic32mz/Make.defs @@ -86,6 +86,10 @@ ifeq ($(CONFIG_PIC32MZ_SPI),y) CHIP_CSRCS += pic32mz-spi.c endif +ifeq ($(CONFIG_PIC32MZ_I2C),y) +CHIP_CSRCS += pic32mz-i2c.c +endif + ifeq ($(CONFIG_PIC32MZ_ETHERNET),y) CHIP_CSRCS += pic32mz-ethernet.c endif diff --git a/arch/mips/src/pic32mz/chip/pic32mz-i2c.h b/arch/mips/src/pic32mz/chip/pic32mz-i2c.h index aaa4621de20..ee064a466e4 100644 --- a/arch/mips/src/pic32mz/chip/pic32mz-i2c.h +++ b/arch/mips/src/pic32mz/chip/pic32mz-i2c.h @@ -1,7 +1,7 @@ /************************************************************************************ * arch/mips/src/pic32mz/pic32mz-i2c.h * - * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Copyright (C) 2015, 2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -50,6 +50,7 @@ /************************************************************************************ * Pre-processor Definitions ************************************************************************************/ + /* I2C Peripheral Offsets ***********************************************************/ #define PIC32MZ_I2Cn_OFFSET(n) ((n) << 9) @@ -58,7 +59,7 @@ # define PIC32MZ_I2C3_OFFSET 0x0400 # define PIC32MZ_I2C4_OFFSET 0x0600 # define PIC32MZ_I2C5_OFFSET 0x0800 -# + /* I2C Register Offsets *************************************************************/ #define PIC32MZ_I2C_CON_OFFSET 0x0000 /* I2C control register */ @@ -95,52 +96,47 @@ /* I2C Peripheral Addresses *********************************************************/ -#define PIC32MZ_I2Cn_K1BASE(n) (PIC32MZ_I2C_K1BASE+ -PIC32MZ_I2Cn_OFFSET(n)) -# define PIC32MZ_I2C1_K1BASE (PIC32MZ_I2C_K1BASE+ -PIC32MZ_I2C1_OFFSET) -# define PIC32MZ_I2C2_K1BASE (PIC32MZ_I2C_K1BASE+ -PIC32MZ_I2C2_OFFSET) -# define PIC32MZ_I2C3_K1BASE (PIC32MZ_I2C_K1BASE+ -PIC32MZ_I2C3_OFFSET) -# define PIC32MZ_I2C4_K1BASE (PIC32MZ_I2C_K1BASE+ -PIC32MZ_I2C4_OFFSET 0x0600 -# define PIC32MZ_I2C5_K1BASE) -PIC32MZ_I2C5_OFFSET 0x0800 +#define PIC32MZ_I2Cn_K1BASE(n) (PIC32MZ_I2C_K1BASE+PIC32MZ_I2Cn_OFFSET(n)) +# define PIC32MZ_I2C1_K1BASE (PIC32MZ_I2C_K1BASE+PIC32MZ_I2C1_OFFSET) +# define PIC32MZ_I2C2_K1BASE (PIC32MZ_I2C_K1BASE+PIC32MZ_I2C2_OFFSET) +# define PIC32MZ_I2C3_K1BASE (PIC32MZ_I2C_K1BASE+PIC32MZ_I2C3_OFFSET) +# define PIC32MZ_I2C4_K1BASE (PIC32MZ_I2C_K1BASE+PIC32MZ_I2C4_OFFSET) +# define PIC32MZ_I2C5_K1BASE (PIC32MZ_I2C_K1BASE+PIC32MZ_I2C5_OFFSET) /* I2C Register Addresses ***********************************************************/ -#define PIC32MZ_I2C1_CON (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_CON_OFFSET) -#define PIC32MZ_I2C1_CONCLR (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_CONCLR_OFFSET) -#define PIC32MZ_I2C1_CONSET (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_CONSET_OFFSET) -#define PIC32MZ_I2C1_CONINV (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_CONINV_OFFSET) +# define PIC32MZ_I2C1_CON (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_CON_OFFSET) +# define PIC32MZ_I2C1_CONCLR (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_CONCLR_OFFSET) +# define PIC32MZ_I2C1_CONSET (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_CONSET_OFFSET) +# define PIC32MZ_I2C1_CONINV (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_CONINV_OFFSET) -#define PIC32MZ_I2C1_STAT (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_STAT_OFFSET) -#define PIC32MZ_I2C1_STATCLR (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_STATCLR_OFFSET) -#define PIC32MZ_I2C1_STATSET (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_STATSET_OFFSET) -#define PIC32MZ_I2C1_STATINV (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_STATINV_OFFSET) +# define PIC32MZ_I2C1_STAT (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_STAT_OFFSET) +# define PIC32MZ_I2C1_STATCLR (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_STATCLR_OFFSET) +# define PIC32MZ_I2C1_STATSET (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_STATSET_OFFSET) +# define PIC32MZ_I2C1_STATINV (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_STATINV_OFFSET) -#define PIC32MZ_I2C1_ADD (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_ADD_OFFSET) -#define PIC32MZ_I2C1_ADDCLR (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_ADDCLR_OFFSET) -#define PIC32MZ_I2C1_ADDSET (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_ADDSET_OFFSET) -#define PIC32MZ_I2C1_ADDINV (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_ADDINV_OFFSET) +# define PIC32MZ_I2C1_ADD (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_ADD_OFFSET) +# define PIC32MZ_I2C1_ADDCLR (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_ADDCLR_OFFSET) +# define PIC32MZ_I2C1_ADDSET (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_ADDSET_OFFSET) +# define PIC32MZ_I2C1_ADDINV (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_ADDINV_OFFSET) -#define PIC32MZ_I2C1_MSK (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_MSK_OFFSET) -#define PIC32MZ_I2C1_MSKCLR (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_MSKCLR_OFFSET) -#define PIC32MZ_I2C1_MSKSET (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_MSKSET_OFFSET) -#define PIC32MZ_I2C1_MSKINV (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_MSKINV_OFFSET) +# define PIC32MZ_I2C1_MSK (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_MSK_OFFSET) +# define PIC32MZ_I2C1_MSKCLR (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_MSKCLR_OFFSET) +# define PIC32MZ_I2C1_MSKSET (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_MSKSET_OFFSET) +# define PIC32MZ_I2C1_MSKINV (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_MSKINV_OFFSET) -#define PIC32MZ_I2C1_BRG (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_BRG_OFFSET) -#define PIC32MZ_I2C1_BRGSET (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_BRGSET_OFFSET) -#define PIC32MZ_I2C1_BRGCLR (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_BRGCLR_OFFSET) -#define PIC32MZ_I2C1_BRGINV (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_BRGINV_OFFSET) +# define PIC32MZ_I2C1_BRG (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_BRG_OFFSET) +# define PIC32MZ_I2C1_BRGSET (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_BRGSET_OFFSET) +# define PIC32MZ_I2C1_BRGCLR (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_BRGCLR_OFFSET) +# define PIC32MZ_I2C1_BRGINV (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_BRGINV_OFFSET) -#define PIC32MZ_I2C1_TRN (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_TRN_OFFSET) -#define PIC32MZ_I2C1_TRNCLR (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_TRNCLR_OFFSET) -#define PIC32MZ_I2C1_TRNSET (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_TRNSET_OFFSET) -#define PIC32MZ_I2C1_TRNINV (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_TRNINV_OFFSET) +# define PIC32MZ_I2C1_TRN (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_TRN_OFFSET) +# define PIC32MZ_I2C1_TRNCLR (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_TRNCLR_OFFSET) +# define PIC32MZ_I2C1_TRNSET (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_TRNSET_OFFSET) +# define PIC32MZ_I2C1_TRNINV (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_TRNINV_OFFSET) -#define PIC32MZ_I2C1_RCV (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_RCV_OFFSET) +# define PIC32MZ_I2C1_RCV (PIC32MZ_I2C1_K1BASE+PIC32MZ_I2C_RCV_OFFSET) +#endif #if CHIP_NI2C > 1 # define PIC32MZ_I2C2_CON (PIC32MZ_I2C2_K1BASE+PIC32MZ_I2C_CON_OFFSET) @@ -306,6 +302,8 @@ PIC32MZ_I2C5_OFFSET 0x0800 #define I2C_CON_SCIE (1 << 21) /* Bit 21: Start Condition Interrupt Enable (Slave mode) */ #define I2C_CON_PCIE (1 << 22) /* Bit 22: Stop Condition Interrupt Enable (Slave mode) */ /* Bits 23-31: Reserved */ +#define I2C_CON_IDLEMASK (I2C_CON_SEN | I2C_CON_RSEN | I2C_CON_PEN |\ + I2C_CON_RCEN | I2C_CON_ACKEN) /* I2C status register */ @@ -321,7 +319,7 @@ PIC32MZ_I2C5_OFFSET 0x0800 #define I2C_STAT_GCSTAT (1 << 9) /* Bit 9: General call status */ #define I2C_STAT_BCL (1 << 10) /* Bit 10: Master bus collision detect */ /* Bits 11-12 */ -#define I2C_STAT_ACKTIM: (1 << 13) /* Bit 13: Acknowledge Time Status bit (Slave mode) */ +#define I2C_STAT_ACKTIM (1 << 13) /* Bit 13: Acknowledge Time Status bit (Slave mode) */ #define I2C_STAT_TRSTAT (1 << 14) /* Bit 14: Transmit status (Master mode) */ #define I2C_STAT_ACKSTAT (1 << 15) /* Bit 15: Acknowledge status (Master mode) */ @@ -339,7 +337,7 @@ PIC32MZ_I2C5_OFFSET 0x0800 /* I2C transmit register */ -#define I2C_TRN_MASK 00000x00ff /* 8-bit transmit data */ +#define I2C_TRN_MASK 0x000000ff /* 8-bit transmit data */ /* I2C receive buffer register */ diff --git a/arch/mips/src/pic32mz/pic32mz-config.h b/arch/mips/src/pic32mz/pic32mz-config.h index dfec8ec1eb2..60944d0642d 100644 --- a/arch/mips/src/pic32mz/pic32mz-config.h +++ b/arch/mips/src/pic32mz/pic32mz-config.h @@ -232,6 +232,40 @@ # define CONFIG_PIC32MZ_SPI 1 #endif +/* I2C ******************************************************************************/ +/* Don't enable I2C peripherals not supported by the chip. */ + +#if CHIP_NI2C < 1 +# undef CONFIG_PIC32MZ_I2C1 +# undef CONFIG_PIC32MZ_I2C2 +# undef CONFIG_PIC32MZ_I2C3 +# undef CONFIG_PIC32MZ_I2C4 +# undef CONFIG_PIC32MZ_I2C5 +#elif CHIP_NI2C < 2 +# undef CONFIG_PIC32MZ_I2C2 +# undef CONFIG_PIC32MZ_I2C3 +# undef CONFIG_PIC32MZ_I2C4 +# undef CONFIG_PIC32MZ_I2C5 +#elif CHIP_NI2C < 3 +# undef CONFIG_PIC32MZ_I2C3 +# undef CONFIG_PIC32MZ_I2C4 +# undef CONFIG_PIC32MZ_I2C5 +#elif CHIP_NI2C < 4 +# undef CONFIG_PIC32MZ_I2C4 +# undef CONFIG_PIC32MZ_I2C5 +#elif CHIP_NI2C < 5 +# undef CONFIG_PIC32MZ_I2C5 +#endif + +/* Are any I2C peripherals enabled? */ + +#undef CONFIG_PIC32MZ_I2C +#if defined(CONFIG_PIC32MZ_I2C1) || defined(CONFIG_PIC32MZ_I2C2) || \ + defined(CONFIG_PIC32MZ_I2C4) || defined(CONFIG_PIC32MZ_I2C4) || \ + defined(CONFIG_PIC32MZ_I2C5) +# define CONFIG_PIC32MZ_I2C 1 +#endif + /* Device Configuration *************************************************************/ /* DEVCFG3 */ /* Configurable settings */ diff --git a/arch/mips/src/pic32mz/pic32mz-i2c.c b/arch/mips/src/pic32mz/pic32mz-i2c.c new file mode 100644 index 00000000000..a03816053a5 --- /dev/null +++ b/arch/mips/src/pic32mz/pic32mz-i2c.c @@ -0,0 +1,1937 @@ +/************************************************************************************ + * arch/mips/src/pic32mz/chip/pic32mz-i2c.c + * + * Copyright (C) 2018 Abdelatif Guettouche. All rights reserved. + * Author: Abdelatif Guettouche + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "up_internal.h" +#include "up_arch.h" + +#include "chip/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 +{ + const struct i2c_ops_s *ops; /* Standard I2C operations */ + const struct pic32mz_i2c_config_s *config; /* Port configuration */ + int refs; /* Referernce count */ + sem_t sem_excl; /* Mutual exclusion semaphore */ +#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(FAR struct pic32mz_i2c_priv_s *priv, + uint8_t offset); +static inline void pic32mz_i2c_putreg(FAR struct pic32mz_i2c_priv_s *priv, + uint8_t offset, uint32_t value); +static inline void pic32mz_i2c_modifyreg(FAR struct pic32mz_i2c_priv_s *priv, + uint8_t offset, uint32_t clearbits, + uint32_t setbits); +static inline void pic32mz_i2c_sem_wait(FAR struct pic32mz_i2c_priv_s *priv); + +#ifdef CONFIG_PICM32MZ_I2C_DYNTIMEO +static useconds_t pic32mz_i2c_tousecs(int msgc, FAR struct i2c_msg_s *msgs); +#endif /* CONFIG_PIC32MZ_I2C_DYNTIMEO */ + +static inline int pic32mz_i2c_sem_waitdone(FAR struct pic32mz_i2c_priv_s *priv); +static inline void pic32mz_i2c_sem_waitidle(FAR struct pic32mz_i2c_priv_s *priv); +static inline void pic32mz_i2c_sem_post(FAR struct pic32mz_i2c_priv_s *priv); +static inline void pic32mz_i2c_sem_init(FAR struct pic32mz_i2c_priv_s *priv); +static inline void pic32mz_i2c_sem_destroy(FAR struct pic32mz_i2c_priv_s *priv); + +#ifdef CONFIG_I2C_TRACE +static void pic32mz_i2c_tracereset(FAR struct pic32mz_i2c_priv_s *priv); +static void pic32mz_i2c_tracenew(FAR struct pic32mz_i2c_priv_s *priv, + uint32_t status); +static void pic32mz_i2c_traceevent(FAR struct pic32mz_i2c_priv_s *priv, + enum pic32mz_trace_e event, uint32_t parm); +static void pic32mz_i2c_tracedump(FAR struct pic32mz_i2c_priv_s *priv); +#endif /* CONFIG_I2C_TRACE */ + +static inline int pic32mz_i2c_setbaudrate(FAR struct pic32mz_i2c_priv_s *priv, + uint32_t frequency); +static inline void pic32mz_i2c_send_start(FAR struct pic32mz_i2c_priv_s *priv); +static inline void pic32mz_i2c_send_stop(FAR struct pic32mz_i2c_priv_s *priv); +static inline void pic32mz_i2c_send_repeatedstart(FAR struct pic32mz_i2c_priv_s *priv); +static inline void pic32mz_i2c_send_ack(FAR 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(FAR struct pic32mz_i2c_priv_s *priv); +static inline bool pic32mz_i2c_master_inactive(FAR 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, FAR void *arg); +#endif /* !CONFIG_I2C_POLLED */ + +static int pic32mz_i2c_init(FAR struct pic32mz_i2c_priv_s *priv); +static int pic32mz_i2c_deinit(FAR struct pic32mz_i2c_priv_s *priv); + +static int pic32mz_i2c_transfer(FAR struct i2c_master_s *dev, + FAR struct i2c_msg_s *msgs, int count); +#ifdef CONFIG_I2C_RESET +static int pic32mz_i2c_reset(FAR 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, + .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, + .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, + .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, + .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, + .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(FAR 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(FAR struct pic32mz_i2c_priv_s *priv) +{ + /* Reset the trace info for a new data collection */ + + priv->tndx = 0; + priv->start_time = clock_systimer(); + pic32mz_i2c_traceclear(priv); +} + +static void pic32mz_i2c_tracenew(FAR 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 we are 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_systimer(); + } + else + { + /* Just increment the count of times that we have seen this status */ + + trace->count++; + } +} + +static void pic32mz_i2c_traceevent(FAR 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(FAR struct pic32mz_i2c_priv_s *priv) +{ + struct pic32mz_trace_s *trace; + int i; + + syslog(LOG_DEBUG, "Elapsed time: %ld\n", + (long)(clock_systimer() - 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(FAR 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(FAR 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(FAR 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_sem_wait + * + * Description: + * Take the exclusive access, waiting as necessary + * + ************************************************************************************/ + +static inline void pic32mz_i2c_sem_wait(FAR struct pic32mz_i2c_priv_s *priv) +{ + int ret; + + do + { + /* Take the semaphore (perhaps waiting) */ + + ret = nxsem_wait(&priv->sem_excl); + + /* The only case that an error should occur here is if the wait was + * awakened by a signal. + */ + + DEBUGASSERT(ret == OK || ret == -EINTR); + } + while (ret == -EINTR); +} + +/************************************************************************************ + * Name: pic32mz_i2c_tousecs + * + * Description: + * Return a micro-second delay based on the number of bytes left to be processed. + * + ************************************************************************************/ + +#ifdef CONFIG_PIC32MZ_I2C_DYNTIMEO +static useconds_t pic32mz_i2c_tousecs(int msgc, FAR 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 (useconds_t)(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(FAR struct pic32mz_i2c_priv_s *priv) +{ + struct timespec abstime; + irqstate_t flags; + int ret; + + flags = enter_critical_section(); + + /* Signal the interrupt handler that we are waiting */ + + priv->intstate = INTSTATE_WAITING; + + do + { + (void)clock_gettime(CLOCK_REALTIME, &abstime); + + /* Calculate a time in the future */ + +#if CONFIG_PIC32MZ_I2CTIMEOSEC > 0 + abstime.tv_sec += CONFIG_PIC32MZ_I2CTIMEOSEC; +#endif + + /* Add a value proportional to the number of bytes in the transfer */ + +#ifdef CONFIG_PIC32MZ_I2C_DYNTIMEO + abstime.tv_nsec += 1000 * pic32mz_i2c_tousecs(priv->msgc, priv->msgv); + if (abstime.tv_nsec >= 1000 * 1000 * 1000) + { + abstime.tv_sec++; + abstime.tv_nsec -= 1000 * 1000 * 1000; + } + +#elif CONFIG_PIC32MZ_I2CTIMEOMS > 0 + abstime.tv_nsec += CONFIG_PIC32MZ_I2CTIMEOMS * 1000 * 1000; + if (abstime.tv_nsec >= 1000 * 1000 * 1000) + { + abstime.tv_sec++; + abstime.tv_nsec -= 1000 * 1000 * 1000; + } +#endif + + /* Wait until either the transfer is complete or the timeout expires */ + + ret = nxsem_timedwait(&priv->sem_isr, &abstime); + if (ret < 0 && ret != -EINTR) + { + /* Break out of the loop on irrecoverable errors. This would + * include timeouts and mystery errors reported by nxsem_timedwait. + * NOTE that we try again if we are awakened by a signal (EINTR). + */ + + 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(FAR 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 = USEC2TICK(pic32mz_i2c_tousecs(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_timedwait() sleeps. + */ + + priv->intstate = INTSTATE_WAITING; + start = clock_systimer(); + + do + { + /* Calculate the elapsed time */ + + elapsed = clock_systimer() - 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(FAR 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_systimer(); + do + { + elapsed = clock_systimer() - start; + + /* The bus is idle if the five least signifcant bits of I2CxCON are cleared, + * and the I2CxSTAT 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_sem_post + * + * Description: + * Release the mutual exclusion semaphore + * + ************************************************************************************/ + +static inline void pic32mz_i2c_sem_post(FAR struct pic32mz_i2c_priv_s *priv) +{ + nxsem_post(&priv->sem_excl); +} + +/************************************************************************************ + * Name: pic32mz_i2c_sem_init + * + * Description: + * Initialize semaphores + * + ************************************************************************************/ + +static inline void pic32mz_i2c_sem_init(FAR struct pic32mz_i2c_priv_s *priv) +{ + nxsem_init(&priv->sem_excl, 0, 1); + +#ifndef CONFIG_I2C_POLLED + /* This semaphore is used for signaling and, hence, should not have + * priority inheritance enabled. + */ + + nxsem_init(&priv->sem_isr, 0, 0); + nxsem_setprotocol(&priv->sem_isr, SEM_PRIO_NONE); +#endif +} + +/************************************************************************************ + * Name: pic32mz_i2c_sem_destroy + * + * Description: + * Destroy semaphores. + * + ************************************************************************************/ + +static inline void pic32mz_i2c_sem_destroy(FAR struct pic32mz_i2c_priv_s *priv) +{ + nxsem_destroy(&priv->sem_excl); +#ifndef CONFIG_I2C_POLLED + nxsem_destroy(&priv->sem_isr); +#endif +} + +/************************************************************************************ + * 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 + up_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 transmition 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 (up_pending_irq(priv->config->ev_irq)) + { + up_clrpend_irq(priv->config->ev_irq); + } +#endif + + /* If an error interrupt has accured. */ + +#ifndef CONFIG_I2C_POLLED + if (up_pending_irq(priv->config->er_irq)) + { + pic32mz_i2c_traceevent(priv, I2CEVENT_ERROR, 0); + + up_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, FAR 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(FAR 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(FAR 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(FAR 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(FAR 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(FAR 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(FAR 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(FAR 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(FAR 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(FAR struct pic32mz_i2c_priv_s *priv) +{ + /* Disable I2C */ + + pic32mz_i2c_putreg(priv, PIC32MZ_I2C_CONCLR_OFFSET, I2C_CON_ON); + + /* Disable and dettach 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(FAR struct i2c_master_s *dev, + FAR struct i2c_msg_s *msgs, int count) +{ + FAR struct pic32mz_i2c_priv_s *priv = (struct pic32mz_i2c_priv_s *)dev; + int ret = OK; + uint32_t status = 0; + + /* Acquire the semaphore. */ + + pic32mz_i2c_sem_wait(priv); + + /* Wait for the bus to be in an idle state. */ + + pic32mz_i2c_sem_waitidle(priv); + + /* Clear any pending error interrupts. */ + +#ifndef CONFIG_I2C_POLLED + up_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 erros. */ + + 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 that any ISR happening after we finish can't overwrite any user data */ + + priv->dcnt = 0; + priv->ptr = NULL; + + pic32mz_i2c_sem_post(priv); + + 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(FAR struct i2c_master_s *dev) +{ + FAR struct pic32mz_i2c_priv_s *priv = (struct pic32mz_i2c_priv_s *)dev; + unsigned int clock_count; + unsigned int stretch_count; + uint32_t scl_gpio; + uint32_t sda_gpio; + uint32_t frequency; + int ret = ERROR; + + DEBUGASSERT(dev); + + /* Our caller must own a ref */ + + DEBUGASSERT(priv->refs > 0); + + /* Lock out other clients */ + + pic32mz_i2c_sem_wait(priv); + + /* 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(scl_gpio); + pic32mz_configgpio(sda_gpio); + + /* Let SDA go high */ + + pic32mz_gpiowrite(sda_gpio, 1); + + /* Clock the bus until any slaves currently driving it let it go. */ + + clock_count = 0; + while (!pic32mz_gpioread(sda_gpio)) + { + /* 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(scl_gpio)) + { + /* Give up if we have tried too hard */ + + if (stretch_count++ > 10) + { + goto out; + } + + up_udelay(10); + } + + /* Drive SCL low */ + + pic32mz_gpiowrite(scl_gpio, 0); + up_udelay(10); + + /* Drive SCL high again */ + + pic32mz_gpiowrite(scl_gpio, 1); + up_udelay(10); + } + + /* Generate a start followed by a stop to reset slave + * state machines. + */ + + pic32mz_gpiowrite(sda_gpio, 0); + up_udelay(10); + pic32mz_gpiowrite(scl_gpio, 0); + up_udelay(10); + pic32mz_gpiowrite(scl_gpio, 1); + up_udelay(10); + pic32mz_gpiowrite(sda_gpio, 1); + up_udelay(10); + + /* Revert the GPIO configuration. */ + + pic32mz_unconfiggpio(sda_gpio); + pic32mz_unconfiggpio(scl_gpio); + + /* Re-init the port */ + + pic32mz_i2c_init(priv); + + /* Restore the frequency */ + + pic32mz_i2c_setclock(priv, frequency); + ret = OK; + +out: + + /* Release the port for re-use by other clients */ + + pic32mz_i2c_sem_post(priv); + +} +#endif /* CONFIG_I2C_RESET */ + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: pic32mz_i2cbus_initialize + * + * Description: + * Initialize one I2C bus + * + ************************************************************************************/ + +FAR struct i2c_master_s *pic32mz_i2cbus_initialize(int port) +{ + struct pic32mz_i2c_priv_s * priv = NULL; + irqstate_t flags; + + /* 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. + */ + + flags = enter_critical_section(); + + if ((volatile int)priv->refs++ == 0) + { + pic32mz_i2c_sem_init(priv); + pic32mz_i2c_init(priv); + } + + leave_critical_section(flags); + return (struct i2c_master_s *)priv; +} + +/************************************************************************************ + * Name: pic32mz_i2cbus_uninitialize + * + * Description: + * Uninitialize an I2C bus + * + ************************************************************************************/ + +int pic32mz_i2cbus_uninitialize(FAR struct i2c_master_s *dev) +{ + FAR struct pic32mz_i2c_priv_s *priv = (struct pic32mz_i2c_priv_s *)dev; + irqstate_t flags; + + DEBUGASSERT(dev); + + /* Decrement reference count and check for underflow */ + + if (priv->refs == 0) + { + return ERROR; + } + + flags = enter_critical_section(); + + if (--priv->refs) + { + leave_critical_section(flags); + return OK; + } + + leave_critical_section(flags); + + /* Disable I2C hardware */ + + pic32mz_i2c_deinit(priv); + + /* Release unused resources */ + + pic32mz_i2c_sem_destroy(priv); + return OK; +} + +#endif /* CONFIG_PIC32MZ_I2C */ + diff --git a/arch/mips/src/pic32mz/pic32mz-i2c.h b/arch/mips/src/pic32mz/pic32mz-i2c.h new file mode 100644 index 00000000000..3617ebda285 --- /dev/null +++ b/arch/mips/src/pic32mz/pic32mz-i2c.h @@ -0,0 +1,90 @@ +/************************************************************************************ + * arch/mips/src/pic32mz/chip/pic32mz-i2c.h + * + * Copyright (C) 2018 Abdelatif Guettouche. All rights reserved. + * Author: Abdelatif Guettouche + * + * 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. + * + ************************************************************************************/ + +#ifndef __ARCH_MIPS_SRC_PIC32MZ_PIC32MZ_I2C_H +#define __ARCH_MIPS_SRC_PIC32MZ_PIC32MZ_I2C_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: pic32mz_i2cbus_initialize + * + * Description: + * Initialize the selected I2C port. And return a unique instance of struct + * struct i2c_master_s. This function may be called to obtain multiple + * instances of the interface, each of which may be set up with a + * different frequency and slave address. + * + * Input Parameters: + * Port number (for hardware that has multiple I2C interfaces) + * + * Returned Value: + * Valid I2C device structure reference on succcess; a NULL on failure + * + ****************************************************************************/ + +FAR struct i2c_master_s *pic32mz_i2cbus_initialize(int port); + +/**************************************************************************** + * Name: pic32mz_i2cbus_uninitialize + * + * Description: + * De-initialize the selected I2C port, and power down the device. + * + * Input Parameters: + * Device structure as returned by the pic32mz_i2cbus_initialize() + * + * Returned Value: + * OK on success, ERROR when internal reference count mismatch or dev + * points to invalid hardware device. + * + ****************************************************************************/ + +int pic32mz_i2cbus_uninitialize(FAR struct i2c_master_s *dev); + +#endif /* __ARCH_MIPS_SRC_PIC32MZ_PIC32MZ_I2C_H */ diff --git a/include/nuttx/i2c/i2c_master.h b/include/nuttx/i2c/i2c_master.h index 6a252690b07..1e6bead0ad2 100644 --- a/include/nuttx/i2c/i2c_master.h +++ b/include/nuttx/i2c/i2c_master.h @@ -78,7 +78,7 @@ * START/STOP Rules: * * 1. The lower half I2C driver will always issue the START condition at the - * beginning of a message unless I2C_M_NOSTART flat is set in the + * beginning of a message unless I2C_M_NOSTART flag is set in the * message. * * 2. The lower half I2C driver will always issue the STOP condition at the @@ -213,7 +213,7 @@ struct i2c_config_s uint8_t addrlen; /* I2C address length (7 or 10 bits) */ }; -/* I2C transaction segment beginning with a START. A number of these can +/* I2C transaction segment beginning with a START. A number of these can * be transferred together to form an arbitrary sequence of write/read transfer * to an I2C slave device. */