From 6d10d5deb9b553dde3c7080e05d0cc157b2a4d23 Mon Sep 17 00:00:00 2001
From: patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>
Date: Sat, 4 Apr 2009 20:53:06 +0000
Subject: [PATCH] Add z8 I2C driver

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@1680 42af7a65-404d-4744-a932-0658087f49c3
---
 ChangeLog                 |   3 +
 Documentation/NuttX.html  |   9 +-
 TODO                      |  10 +-
 arch/z80/src/z8/Make.defs |   4 +-
 arch/z80/src/z8/chip.h    |  42 +--
 arch/z80/src/z8/z8_i2c.c  | 603 ++++++++++++++++++++++++++++++++++++++
 6 files changed, 648 insertions(+), 23 deletions(-)
 create mode 100755 arch/z80/src/z8/z8_i2c.c

diff --git a/ChangeLog b/ChangeLog
index 94676ceff5..5c3ce1218d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -688,3 +688,6 @@
 	* eZ80Acclaim!: Add a generic SPI driver for all eZ80 boards.
 	* Add a setmode() method to the SPI interface to handle parts with differing
 	  mode requirements.
+	* include/nuttx/i2c.h: Defined a standard I2C interface
+	* eZ80Acclaim!: Add an I2C driver.
+	* eZ8Encore!: Add an I2C driver.
diff --git a/Documentation/NuttX.html b/Documentation/NuttX.html
index e8e9e3e622..ff562da0de 100644
--- a/Documentation/NuttX.html
+++ b/Documentation/NuttX.html
@@ -8,7 +8,7 @@
   <tr align="center" bgcolor="#e4e4e4">
     <td>
       <h1><big><font color="#3c34ec"><i>NuttX RTOS</i></font></big></h1>
-      <p>Last Updated: March 29, 2009</p>
+      <p>Last Updated: April 4, 2009</p>
     </td>
   </tr>
 </table>
@@ -948,7 +948,7 @@ BFD_ASSERT (*plt_offset != (bfd_vma) -1);
       Integration and testing of NuttX on the  ZiLOG ez80f0910200zcog-d is complete.
       The first integrated version was released in NuttX version 0.4.2 (with important early bugfixes
       in 0.4.3 and 0.4.4).
-      As of this writing, that port provides basic board support with a serial console and eZ80F91 EMAC driver.
+      As of this writing, that port provides basic board support with a serial console, SPI, and eZ80F91 EMAC driver.
     </p>
    </td>
 </tr>
@@ -1361,11 +1361,16 @@ nuttx-0.4.5 2009-xx-xx Gregory Nutt &lt;spudmonkey@racsa.co.cr&gt;
 	* eZ80Acclaim!: Add a generic SPI driver for all eZ80 boards.
 	* Add a setmode() method to the SPI interface to handle parts with differing
 	  mode requirements.
+	* include/nuttx/i2c.h: Defined a standard I2C interface
+	* eZ80Acclaim!: Add an I2C driver.
+	* eZ8Encore!: Add an I2C driver.
 
 pascal-0.1.3 2009-xx-xx Gregory Nutt &lt;spudmonkey@racsa.co.cr&gt;
 
 buildroot-0.1.4 2009-xx-xx &lt;spudmonkey@racsa.co.cr&gt;
 
+	* Add support for a blackfin toolchain using GCC 4.2.4 and binutils 2.19
+
 </pre></ul>
 
 <table width ="100%">
diff --git a/TODO b/TODO
index 01c527f3df..751716a0b8 100644
--- a/TODO
+++ b/TODO
@@ -688,8 +688,14 @@ o z80/z8/ez80 (arch/z80)
   Status:      Open
   Priority:    High if you happen to be working with XTRS.
 
-  Description: A "generic" SPI driver has been coded for the eZ80Acclaim!
-               However, this remains untested since I have no SPI devices for
+  Description: A "generic" SPI and I2C drivers have been coded for the eZ80Acclaim!
+               However, these remains untested since I have no SPI or I2C devices for
+               the board (yet).
+  Status:      Open
+  Priority:    Med
+
+  Description: A "generic" I2C driver has been coded for the eZ8Encore!
+               However, this remains untested since I have no I2C devices for
                the board (yet).
   Status:      Open
   Priority:    Med
diff --git a/arch/z80/src/z8/Make.defs b/arch/z80/src/z8/Make.defs
index a44b029ff0..ae4f2a48c9 100644
--- a/arch/z80/src/z8/Make.defs
+++ b/arch/z80/src/z8/Make.defs
@@ -1,7 +1,7 @@
 ############################################################################
 # arch/z80/src/z8/Make.defs
 #
-#   Copyright (C) 2008 Gregory Nutt. All rights reserved.
+#   Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
 #   Author: Gregory Nutt <spudmonkey@racsa.co.cr>
 #
 # Redistribution and use in source and binary forms, with or without
@@ -45,5 +45,5 @@ CMN_CSRCS	= up_initialize.c up_allocateheap.c up_createstack.c \
 CHIP_SSRCS	= z8_vector.S z8_saveusercontext.S z8_restorecontext.S
 CHIP_CSRCS	= z8_initialstate.c z8_irq.c z8_saveirqcontext.c \
 		  z8_schedulesigaction.c z8_sigdeliver.c z8_timerisr.c \
-		  z8_lowuart.c z8_serial.c z8_registerdump.c
+		  z8_lowuart.c z8_serial.c z8_i2c.c z8_registerdump.c
 
diff --git a/arch/z80/src/z8/chip.h b/arch/z80/src/z8/chip.h
index 912dd65e11..702edd5fe9 100644
--- a/arch/z80/src/z8/chip.h
+++ b/arch/z80/src/z8/chip.h
@@ -2,7 +2,7 @@
  * arch/z80/src/z8/chip.h
  * arch/z80/src/chip/chip.h
  *
- *   Copyright (C) 2008 Gregory Nutt. All rights reserved.
+ *   Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
  *   Author: Gregory Nutt <spudmonkey@racsa.co.cr>
  *
  * Redistribution and use in source and binary forms, with or without
@@ -167,27 +167,35 @@
 /* I2C Status Register Bit Definitions **********************************************/
 
 #if defined(_Z8FMC16) || defined(_Z8F1680)
+#  define I2C_ISTAT_NCKI   (1 << 0) /* Bit 0: 1=NAK Interrupt */
+#  define I2C_ISTAT_SPRS   (1 << 1) /* Bit 1: 1=STOP/RESTART condition Interrupt */
+#  define I2C_ISTAT_ARBLST (1 << 2) /* Bit 2: 1=Arbitration lost */
+#  define I2C_ISTAT_RD     (1 << 3) /* Bit 3: 1=Read */
+#  define I2C_ISTAT_GCA    (1 << 4) /* Bit 4: 1=General Call Address */
+#  define I2C_ISTAT_SAM    (1 << 5) /* Bit 5: 1=Slave address match */
+#  define I2C_ISTAT_RDRF   (1 << 6) /* Bit 6: 1=Receive Data Register Full */
+#  define I2C_ISTAT_TDRE   (1 << 7) /* Bit 7: 1=Transmit Data Register Empty */
 #else
-#  define I2C_STAT_NCKI (1 << 0) /* Bit 0: 1=NACK Interrupt */
-#  define I2C_STAT_DSS  (1 << 1) /* Bit 1: 1=Data Shift State */
-#  define I2C_STAT_TAS  (1 << 2) /* Bit 2: 1=Transmit Address State */
-#  define I2C_STAT_RD   (1 << 3) /* Bit 3: 1=Read */
-#  define I2C_STAT_10B  (1 << 4) /* Bit 4: 1=10-Bit Address */
-#  define I2C_STAT_ACK  (1 << 5) /* Bit 5: 1=Acknowledge */
-#  define I2C_STAT_RDRF (1 << 6) /* Bit 6: 1=Receive Data Register Full */
-#  define I2C_STAT_TDRE (1 << 7) /* Bit 7: 1=Transmit Data Register Empty */
+#  define I2C_STAT_NCKI    (1 << 0) /* Bit 0: 1=NAK Interrupt */
+#  define I2C_STAT_DSS     (1 << 1) /* Bit 1: 1=Data Shift State */
+#  define I2C_STAT_TAS     (1 << 2) /* Bit 2: 1=Transmit Address State */
+#  define I2C_STAT_RD      (1 << 3) /* Bit 3: 1=Read */
+#  define I2C_STAT_10B     (1 << 4) /* Bit 4: 1=10-Bit Address */
+#  define I2C_STAT_ACK     (1 << 5) /* Bit 5: 1=Acknowledge */
+#  define I2C_STAT_RDRF    (1 << 6) /* Bit 6: 1=Receive Data Register Full */
+#  define I2C_STAT_TDRE    (1 << 7) /* Bit 7: 1=Transmit Data Register Empty */
 #endif
 
 /* I2C Control Register Bit Definitions *********************************************/
 
-#define I2C_CTL_FILTEN  (1 << 0) /* Bit 0: 1=I2C Signal Filter Enable */
-#define I2C_CTL_FLUSH   (1 << 1) /* Bit 1: 1=Flush Data */
-#define I2C_CTL_NAK     (1 << 2) /* Bit 2: 1=Send NAK */
-#define I2C_CTL_TXI     (1 << 3) /* Bit 3: 1=Enable TDRE interrupts */
-#define I2C_CTL_BIRQ    (1 << 4) /* Bit 4: 1=Baud Rate Generator Interrupt Request */
-#define I2C_CTL_STOP    (1 << 5) /* Bit 5: 1=Send Stop Condition */
-#define I2C_CTL_START   (1 << 6) /* Bit 6: 1=Send Start Condition */
-#define I2C_CTL_IEN     (1 << 7) /* Bit 7: 1=I2C Enable */
+#define I2C_CTL_FILTEN     (1 << 0) /* Bit 0: 1=I2C Signal Filter Enable */
+#define I2C_CTL_FLUSH      (1 << 1) /* Bit 1: 1=Flush Data */
+#define I2C_CTL_NAK        (1 << 2) /* Bit 2: 1=Send NAK */
+#define I2C_CTL_TXI        (1 << 3) /* Bit 3: 1=Enable TDRE interrupts */
+#define I2C_CTL_BIRQ       (1 << 4) /* Bit 4: 1=Baud Rate Generator Interrupt Request */
+#define I2C_CTL_STOP       (1 << 5) /* Bit 5: 1=Send Stop Condition */
+#define I2C_CTL_START      (1 << 6) /* Bit 6: 1=Send Start Condition */
+#define I2C_CTL_IEN        (1 << 7) /* Bit 7: 1=I2C Enable */
 
 /* Register access macros ***********************************************************
  *
diff --git a/arch/z80/src/z8/z8_i2c.c b/arch/z80/src/z8/z8_i2c.c
new file mode 100755
index 0000000000..1fb87d2915
--- /dev/null
+++ b/arch/z80/src/z8/z8_i2c.c
@@ -0,0 +1,603 @@
+/****************************************************************************
+ * arch/z80/src/z8/z8_i2c.c
+ *
+ *   Copyright(C) 2009 Gregory Nutt. All rights reserved.
+ *   Author: Gregory Nutt <spudmonkey@racsa.co.cr>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <semaphore.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/i2c.h>
+#include <arch/board/board.h>
+
+#include <eZ8.h>  /* eZ8 Register definitions */
+#include "chip.h" /* Register bit definitions */
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct z8_i2cdev_s
+{
+  const struct i2c_ops_s *ops; /* I2C vtable */
+  uint16 brg;                  /* Baud rate generator value */
+  ubyte  addr;                 /* 8-bit address */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Misc. Helpers */
+
+static void i2c_waittxempty(void);
+static void i2c_waitrxavail(void);
+static void i2c_setbrg(uint16 brg);
+static uint16 i2c_getbrg(uint32 frequency);
+
+/* I2C methods */
+
+static uint32 i2c_setfrequency(FAR struct i2c_dev_s *dev, uint32 frequency);
+static int i2c_setaddress(FAR struct i2c_dev_s *dev, int addr, int nbits);
+static int i2c_write(FAR struct i2c_dev_s *dev, const ubyte *buffer, int buflen);
+static int i2c_read(FAR struct i2c_dev_s *dev, ubyte *buffer, int buflen);
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/* This function is normally prototyped int the ZiLOG header file sio.h */
+
+extern uint32 get_freq(void);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static uint16  g_currbrg;      /* Current BRG setting */
+static boolean g_initialized;  /* TRUE:I2C has been initialized */
+static sem_t   g_i2csem;       /* Serialize I2C transfers */
+
+const struct i2c_ops_s g_ops =
+{
+  i2c_setfrequency,
+  i2c_setaddress,
+  i2c_write,
+  i2c_read,
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+/****************************************************************************
+ * Name: i2c_semtake/i2c_semgive
+ *
+ * Description:
+ *   Take/Give the I2C semaphore.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void i2c_semtake(void)
+{
+  /* Take the I2C semaphore (perhaps waiting) */
+
+  while (sem_wait(&g_i2csem) != 0)
+    {
+      /* The only case that an error should occr here is if
+       * the wait was awakened by a signal.
+       */
+
+      ASSERT(errno == EINTR);
+    }
+}
+
+#define i2c_semgive() sem_post(&g_i2csem)
+
+/****************************************************************************
+ * Name: i2c_waittxempty
+ *
+ * Description:
+ *   Wait for the transmit data register to become empty.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void i2c_waittxempty(void)
+{
+  int i;  
+  for (i = 0; i < 10000 && (I2CSTAT & I2C_STAT_TDRE) == 0;  i++);
+}
+
+/****************************************************************************
+ * Name: i2c_waitrxavail
+ *
+ * Description:
+ *   Wait until we have received a full byte of data.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void i2c_waitrxavail(void)  
+{
+  int i;
+  for (i = 0; i <= 10000 && (I2CSTAT & (I2C_STAT_RDRF | I2C_STAT_NCKI)) == 0; i++);
+}
+
+/****************************************************************************
+ * Name: i2c_setbrg
+ *
+ * Description:
+ *   Set the current BRG value for this transaction
+ *
+ * Input Parameters:
+ *   brg - BRG to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void i2c_setbrg(uint16 brg)
+{
+  if (g_currbrg != brg)
+    {
+      I2CBRH    = (ubyte)(brg >> 8);
+      I2CBRL    = (ubyte)(brg & 0xff);
+      g_currbrg = brg;
+    }
+}
+
+/****************************************************************************
+ * Name: i2c_getbrg
+ *
+ * Description:
+ *   Calculate the BRG value
+ *
+ * Input Parameters:
+ *   frequency - The I2C frequency requested
+ *
+ * Returned Value:
+ *   Returns the actual frequency selected
+ *
+ ****************************************************************************/
+
+static uint16 i2c_getbrg(uint32 frequency)
+{
+  uint32 sysclock = get_freq();
+
+  /* Max is 400 Kb/sec */
+
+  if (frequency > 400*1000)
+    {
+      dbg("Invalid inputs\n");
+      frequency = 400*1000;
+    }
+
+  /* BRG = sysclock / (4 * frequency) */
+
+  return ((sysclock >> 2) + (frequency >> 1)) / frequency;
+}
+
+/****************************************************************************
+ * Name: i2c_setfrequency
+ *
+ * Description:
+ *   Set the I2C frequency. This frequency will be retained in the struct
+ *   i2c_dev_s instance and will be used with all transfers.  Required.
+ *
+ * Input Parameters:
+ *   dev -       Device-specific state data
+ *   frequency - The I2C frequency requested
+ *
+ * Returned Value:
+ *   Returns the actual frequency selected
+ *
+ ****************************************************************************/
+
+static uint32 i2c_setfrequency(FAR struct i2c_dev_s *dev, uint32 frequency)
+{
+  FAR struct z8_i2cdev_s *priv = (FAR struct z8_i2cdev_s *)dev;
+
+  /* Sanity Check */
+
+#ifdef CONFIG_DEBUG
+  if (!dev)
+    {
+      dbg("Invalid inputs\n");
+      return -EINVAL;
+    }
+#endif
+
+  /* Calculate and save the BRG (we won't apply it until the first transfer) */
+
+  priv->brg = i2c_getbrg(frequency);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: i2c_setaddress
+ *
+ * Description:
+ *   Set the I2C slave address. This frequency will be retained in the struct
+ *   i2c_dev_s instance and will be used with all transfers.  Required.
+ *
+ * Input Parameters:
+ *   dev -     Device-specific state data
+ *   address - The I2C slave address
+ *   nbits -   The number of address bits provided (7 or 10)
+ *
+ * Returned Value:
+ *   Returns the actual frequency selected
+ *
+ ****************************************************************************/
+
+static int i2c_setaddress(FAR struct i2c_dev_s *dev, int addr, int nbits)
+{
+  FAR struct z8_i2cdev_s *priv = (FAR struct z8_i2cdev_s *)dev;
+
+  /* Sanity Check */
+
+#ifdef CONFIG_DEBUG
+  if (!dev || (unsigned)addr > 0x7f)
+    {
+      dbg("Invalid inputs\n");
+      return -EINVAL;
+    }
+#endif
+
+  /* Save the 7-bit address (10-bit address not yet supported) */
+
+  DEBUGASSERT(nbits == 7);
+  priv->addr = (ubyte)addr;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: i2c_write
+ *
+ * Description:
+ *   Send a block of data on I2C using the previously selected I2C
+ *   frequency and slave address. Each write operational will be an 'atomic'
+ *   operation in the sense that any other I2C actions will be serialized
+ *   and pend until this write completes. Required.
+ *
+ * Input Parameters:
+ *   dev -    Device-specific state data
+ *   buffer - A pointer to the read-only buffer of data to be written to device
+ *   buflen - The number of bytes to send from the buffer
+ *
+ * Returned Value:
+ *   0: success, <0: A negated errno
+ *
+ ****************************************************************************/
+
+static int i2c_write(FAR struct i2c_dev_s *dev, const ubyte *buffer, int buflen)
+{
+  FAR struct z8_i2cdev_s *priv = (FAR struct z8_i2cdev_s *)dev;
+  const ubyte *ptr;
+  int retry;
+  int count;
+
+#ifdef CONFIG_DEBUG
+  if (!priv || !buffer || buflen < 1)
+    {
+      dbg("Invalid inputs\n");
+      return -EINVAL;
+    }
+#endif
+
+  /* Get exclusive access */
+
+  i2c_semtake();
+
+  /* Set the frequency */
+
+  i2c_setbrg(priv->brg);
+
+  /* Retry as necessary to send this whole message */
+
+  for (retry = 0; retry < 100; retry++)
+    {
+      /* Load the address into the transmit register.  It is not sent
+       * until the START bit is set.
+       */
+
+      I2CD    = I2C_WRITEADDR8(priv->addr);
+      I2CCTL |= I2C_CTL_START;
+ 
+      /* Wait for the xmt buffer to become empty */
+
+      i2c_waittxempty();
+
+      /* Then send all of the bytes in the buffer */
+
+      ptr = buffer;
+      for (count = buflen; count; count--)
+        {
+          /* Send a byte of data and wait for it to be sent */
+
+          I2CD = *ptr++;
+          i2c_waittxempty();
+
+          /* If this was the last byte, then send STOP immediately.  This
+           * is because the ACK will not be valid until the STOP clocks out
+           * the last bit.. Hmmm.  If this true then we will never be
+           * able to send more than one data byte???
+           */
+ 
+          if (count == 1)
+            {
+              I2CCTL |= I2C_CTL_STOP;
+
+              /* If this last byte was ACKed, then the whole buffer
+               * was successfully sent and we can return success.
+               */
+
+              if ((I2CSTAT & I2C_STAT_ACK) != 0)
+                {
+                  i2c_semgive();
+                  return OK;
+                }
+
+              /* If was was not ACKed, then this inner loop will
+               * terminated (because count will decrement to zero
+               * and the whole message will be resent
+               */
+            }
+
+          /* Not the last byte... was this byte ACKed? */
+
+          else if ((I2CSTAT & I2C_STAT_ACK) == 0)
+            {
+              /* No, flush the buffer and toggle the I2C on and off */
+
+              I2CCTL |= I2C_CTL_FLUSH;
+              I2CCTL &= ~I2C_CTL_IEN;
+              I2CCTL |= I2C_CTL_IEN;
+
+              /* Break out of the loop early and try again */
+
+              break;
+            }
+        }
+    }
+  i2c_semgive();
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Name: i2c_read
+ *
+ * Description:
+ *   Receive a block of data from I2C using the previously selected I2C
+ *   frequency and slave address. Each read operational will be an 'atomic'
+ *   operation in the sense that any other I2C actions will be serialized
+ *   and pend until this read completes. Required.
+ *
+ * Input Parameters:
+ *   dev -   Device-specific state data
+ *   buffer - A pointer to a buffer of data to receive the data from the device
+ *   buflen - The requested number of bytes to be read
+ *
+ * Returned Value:
+ *   0: success, <0: A negated errno
+ *
+ ****************************************************************************/
+
+static int i2c_read(FAR struct i2c_dev_s *dev, ubyte *buffer, int buflen)
+{
+  FAR struct z8_i2cdev_s *priv = (FAR struct z8_i2cdev_s *)dev;
+  ubyte *ptr;
+  int retry;
+  int count;
+
+#ifdef CONFIG_DEBUG
+  if (!priv || !buffer || buflen < 1)
+    {
+      dbg("Invalid inputs\n");
+      return -EINVAL;
+    }
+#endif
+
+  /* Get exclusive access */
+
+  i2c_semtake();
+
+  /* Set the frequency */
+
+  i2c_setbrg(priv->brg);
+
+  /* Retry as necessary to receive the whole message */
+
+  for (retry = 0; retry < 100; retry++)    
+    {
+      /* Load the address into the transmit register.  It is not sent
+       * until the START bit is set.
+       */
+
+      I2CD = I2C_READADDR8(priv->addr);
+
+      /* If we want only a single byte of data, then set the NACK
+       * bit now.
+       */
+ 
+      I2CCTL |= I2C_CTL_NAK;
+
+      /* The START bit begins the transaction */
+
+      I2CCTL |= I2C_CTL_START;
+
+      /* Now loop to receive each data byte */
+
+      ptr = buffer;
+      for (count = buflen; count; count--)
+        {
+          /* Wait for the receive buffer to fill */
+
+          i2c_waitrxavail();
+
+          /* Did we get a byte?  Or did an error occur? */
+
+          if (I2CSTAT & I2C_STAT_RDRF)
+            {
+              /* Save the data byte */
+
+              *ptr++ = I2CD;
+
+              /* If the next byte is the last byte, then set NAK now */
+
+              if (count == 2)
+                {
+                  I2CCTL |= I2C_CTL_NAK;
+                }
+
+              /* If this was the last byte, then set STOP and return success */
+
+              else if (count == 1)
+                {
+                  I2CCTL |= I2C_CTL_STOP;
+                  i2c_semgive();
+                  return OK;
+                }
+            }
+          /* An error occurred.  Clear byte bus and break out of the loop
+           * to retry now.
+           */
+
+          else
+            {
+              /* No, flush the buffer and toggle the I2C on and off */
+
+              I2CCTL |= I2C_CTL_FLUSH;
+              I2CCTL &= ~I2C_CTL_IEN;
+              I2CCTL |= I2C_CTL_IEN;
+
+              /* Break out of the loop early and try again */
+
+              break;
+            }
+        }
+    }
+  i2c_semgive();
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_i2cinitialize
+ *
+ * Description:
+ *   Initialize the selected I2C port. And return a unique instance of struct
+ *   struct i2c_dev_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 Parameter:
+ *   Port number (for hardware that has mutiple I2C interfaces)
+ *
+ * Returned Value:
+ *   Valid I2C device structre reference on succcess; a NULL on failure
+ *
+ ****************************************************************************/
+
+FAR struct i2c_dev_s *up_i2cinitialize(int port)
+{
+  FAR struct z8_i2cdev_s *i2c;
+ 
+  if (!g_initialized)
+    {
+      /* Set up some initial BRG value */
+
+      uint16 brg = i2c_getbrg(100*1000);
+      i2c_setbrg(brg);
+
+      /* Make sure that GPIOs are configured for the alternate function (this
+       * varies with silicon revisions).
+       */
+  
+      PAADDR = 0x02;
+      PACTL |= 0xc0;
+
+      /* This semaphore enforces serialized access for I2C transfers */
+
+      sem_init(&g_i2csem, 0, 1);
+
+      /* Enable I2C -- no interrupts */
+
+      I2CCTL = I2C_CTL_IEN;
+    }
+
+  /* Now, allocate an I2C instance for this caller */
+
+  i2c = (FAR struct z8_i2cdev_s *)malloc(sizeof(FAR struct z8_i2cdev_s));
+  if (i2c)
+    {
+      /* Initialize the allocated instance */
+
+      i2c->ops = &g_ops;
+      i2c->brg = g_currbrg;
+    }
+  return (FAR struct i2c_dev_s *)i2c;
+} 
-- 
GitLab