This is the mail archive of the ecos-patches@sources.redhat.com mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

I2C support


This patch adds generic I2C support to eCos. Previously this was only
available as part of eCosPro. It contains:

1) an application-level API for accessing I2C devices, not dissimilar
   to the SPI code contributed last year
2) a specification of how to write I2C device drivers and include
   the platform-specific support for attached I2C devices
3) a generic bit-bang implementation of I2C, handling all the timing
   and protocol issues, requiring just one platform-specific function
   to do the actual line waggling
4) full documentation.

Bart

Index: ecos.db
===================================================================
RCS file: /cvs/ecos/ecos/packages/ecos.db,v
retrieving revision 1.141
diff -u -r1.141 ecos.db
--- ecos.db	17 Apr 2005 12:44:37 -0000	1.141
+++ ecos.db	20 Apr 2005 11:58:02 -0000
@@ -2085,6 +2085,18 @@
           bus drivers and for defining SPI device structures."
 }
 
+package CYGPKG_IO_I2C {
+        alias           { "Generic I2C support" i2c io_i2c i2c_io }
+        directory       io/i2c
+        script          i2c.cdl
+        hardware
+        description "
+            The generic I2C package provides an API for accessing devices
+            attached to an I2C bus. It specifies how I2C bus drivers should
+            be written and how I2C devices should be defined. There is also
+            support for bit-banged I2C buses."
+}
+
 package CYGPKG_KERNEL {
 	alias		{ "eCos kernel" kernel }
 	directory	kernel
Index: io/i2c/current/ChangeLog
===================================================================
RCS file: io/i2c/current/ChangeLog
diff -N io/i2c/current/ChangeLog
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ io/i2c/current/ChangeLog	20 Apr 2005 11:55:29 -0000
@@ -0,0 +1,40 @@
+2005-03-03  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/i2c.cxx: cope with ports which define HAL_DELAY_US() in
+	hal_diag.h instead of hal_intr.h
+
+2004-10-05  Bart Veer  <bartv@ecoscentric.com>
+
+	* Generic I2C package created
+
+//===========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2004, 2005 eCosCentric Limited
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//===========================================================================
Index: io/i2c/current/cdl/i2c.cdl
===================================================================
RCS file: io/i2c/current/cdl/i2c.cdl
diff -N io/i2c/current/cdl/i2c.cdl
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ io/i2c/current/cdl/i2c.cdl	20 Apr 2005 11:55:30 -0000
@@ -0,0 +1,102 @@
+# ====================================================================
+#
+#      i2c.cdl
+#
+#      Generic I2C package configuration data
+#
+# ====================================================================
+#####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 2004 eCosCentric Limited
+##
+## eCos is free software; you can redistribute it and/or modify it under
+## the terms of the GNU General Public License as published by the Free
+## Software Foundation; either version 2 or (at your option) any later version.
+##
+## eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+## WARRANTY; without even the implied warranty of MERCHANTABILITY or
+## FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+## for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with eCos; if not, write to the Free Software Foundation, Inc.,
+## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+##
+## As a special exception, if other files instantiate templates or use macros
+## or inline functions from this file, or you compile this file and link it
+## with other works to produce a work based on this file, this file does not
+## by itself cause the resulting work to be covered by the GNU General Public
+## License. However the source code for this file must still be made available
+## in accordance with section (3) of the GNU General Public License.
+##
+## This exception does not invalidate any other reasons why a work based on
+## this file might be covered by the GNU General Public License.
+## -------------------------------------------
+#####ECOSGPLCOPYRIGHTEND####
+# ====================================================================
+######DESCRIPTIONBEGIN####
+#
+# Author(s):     bartv
+# Date:          2004-10-05
+#
+#####DESCRIPTIONEND####
+#========================================================================
+
+cdl_package CYGPKG_IO_I2C {
+    display		"I2C support"
+    requires		CYGPKG_INFRA CYGPKG_HAL
+    hardware
+    include_dir		cyg/io
+    compile		i2c.cxx
+    
+    description "
+        The generic I2C package provides an API for accessing devices
+        attached to an I2C bus. It specifies how I2C bus drivers should
+        be written and how I2C devices should be defined. There is also
+        support for bit-banged I2C buses."
+
+    cdl_option CYGNUM_I2C_INIT_PRIORITY {
+	display		"I2C initialization priority"
+	flavor		data
+	default_value	{ "CYG_INIT_DRIVERS" }
+
+	description "
+            The generic I2C package will initialize each I2C bus during
+            system startup, using a prioritized static constructor. This
+            option controls the priority that is used. The default value,
+            CYG_INIT_DRIVERS, means that I2C buses get initialized early
+            on."
+    }
+
+    cdl_component CYGPKG_IO_I2C_OPTIONS {
+        display "I2C build options"
+        flavor  none
+        description   "
+	    Package specific build options including control over
+	    compiler flags used only in building the generic I2C
+            package, and details of which tests are built."
+
+        cdl_option CYGPKG_IO_I2C_CFLAGS_ADD {
+            display "Additional compiler flags"
+            flavor  data
+            no_define
+            default_value { "" }
+            description   "
+                This option modifies the set of compiler flags for
+                building the generic I2C package. These flags are
+                used in addition to the set of global flags."
+        }
+
+        cdl_option CYGPKG_IO_I2C_CFLAGS_REMOVE {
+            display "Suppressed compiler flags"
+            flavor  data
+            no_define
+            default_value { "" }
+            description   "
+                This option modifies the set of compiler flags for
+                building the generic I2C package. These flags are
+                removed from the set of global flags if present."
+        }
+    }
+}
Index: io/i2c/current/doc/i2c.sgml
===================================================================
RCS file: io/i2c/current/doc/i2c.sgml
diff -N io/i2c/current/doc/i2c.sgml
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ io/i2c/current/doc/i2c.sgml	20 Apr 2005 11:55:36 -0000
@@ -0,0 +1,941 @@
+<!-- DOCTYPE part  PUBLIC "-//OASIS//DTD DocBook V3.1//EN" -->
+
+<!-- {{{ Banner                         -->
+
+<!-- =============================================================== -->
+<!--                                                                 -->
+<!--     i2c.sgml                                                    -->
+<!--                                                                 -->
+<!--     Generic I2C documentation.                                  -->
+<!--                                                                 -->
+<!-- =============================================================== -->
+<!-- ####COPYRIGHTBEGIN####                                          -->
+<!--                                                                 -->
+<!-- =============================================================== -->
+<!-- Copyright (C) 2004 eCosCentric Limited                          -->
+<!-- This material may be distributed only subject to the terms      -->
+<!-- and conditions set forth in the Open Publication License, v1.0  -->
+<!-- or later (the latest version is presently available at          -->
+<!-- http://www.opencontent.org/openpub/)                            -->
+<!-- Distribution of the work or derivative of the work in any       -->
+<!-- standard (paper) book form is prohibited unless prior           -->
+<!-- permission obtained from the copyright holder                   -->
+<!-- =============================================================== -->
+<!--                                                                 -->      
+<!-- ####COPYRIGHTEND####                                            -->
+<!-- =============================================================== -->
+<!-- #####DESCRIPTIONBEGIN####                                       -->
+<!--                                                                 -->
+<!-- Author(s):   bartv                                              -->
+<!-- Date:        2004/10/05                                         -->
+<!--                                                                 -->
+<!-- ####DESCRIPTIONEND####                                          -->
+<!-- =============================================================== -->
+
+<!-- }}} -->
+
+<part id="io-i2c"><title>I2C Support</title>
+
+<refentry id="i2c">
+  <refmeta>
+    <refentrytitle>Overview</refentrytitle>
+  </refmeta>
+  <refnamediv>
+    <refname>Overview</refname>
+    <refpurpose>eCos Support for I2C, the Inter IC Bus</refpurpose>
+  </refnamediv>
+
+  <refsect1 id="i2c-description"><title>Description</title>
+    <para>
+The Inter IC Bus (I2C) is one of a number of serial bus technologies.
+It can be used to connect a processor to one or more peripheral chips,
+for example analog-to-digital convertors or real time clocks, using
+only a small number of pins and PCB tracks. The technology was
+originally developed by Philips Semiconductors but is supported by
+many other vendors. The bus specification is freely available.
+    </para>
+    <para>
+In a typical I2C system the processor acts as the I2C bus master. The
+peripheral chips act as slaves. The bus consists of just two wires:
+SCL carries a clock signal generated by the master, and SDA is a
+bi-directional data line. The normal clock frequency is 100KHz. Each
+slave has a 7-bit address. With some chips the address is hard-wired,
+and it is impossible to have two of these chips on the same bus. With
+other chips it is possible to choose between one of a small number of
+addresses by connecting spare pins to either VDD or GND.
+    </para>
+    <para>
+An I2C data transfer involves a number of stages:
+    </para>
+    <orderedlist>
+      <listitem><para>
+The bus master generates a start condition, a high-to-low transition
+on the SDA line while SCL is kept high. This signalling cannot occur
+during data transfer.
+      </para></listitem>
+      <listitem><para>
+The bus master clocks the 7-bit slave address onto the SDA line,
+followed by a direction bit to distinguish between reads and writes.
+      </para></listitem>
+      <listitem><para>
+The addressed device acknowledges. If the master does not see an
+acknowledgement then this suggests it is using the wrong address for
+the slave device.
+      </para></listitem>
+      <listitem><para>
+If the master is transmitting data to the slave then it will send this
+data one byte at a time. The slave acknowledges each byte. If the
+slave is unable to accept more data, for example because it has run
+out of buffer space, then it will generate a nack and the master
+should stop sending.
+      </para></listitem>
+      <listitem><para>
+If the master is receiving data from the slave then the slave will
+send this data one byte at a time. The master should acknowledge each
+byte, until the last one. When the master has received all the data it
+wants it should generate a nack and the slave will stop sending. This
+nack is essential because it causes the slave to stop driving the SDA
+line, releasing it back to the master.
+      </para></listitem>
+      <listitem><para>
+It is possible to switch direction in a single transfer, using what is
+known as a repeated start. This involves generating another start
+condition, sending the 7-bit address again, followed by a new
+direction bit.
+      </para></listitem>
+      <listitem><para>
+At the end of a transfer the master should generate a stop condition,
+a low-to-high transition on the SDA line while SCL is kept high. Again
+this signalling does not occur at other times.
+      </para></listitem>
+    </orderedlist>
+    <para>
+There are a number of extensions. The I2C bus supports multiple bus
+masters and there is an arbitration procedure to allow a master to
+claim the bus. Some devices can have 10-bit addresses rather than
+7-bit addresses. There is a fast mode operating at 400KHz instead of
+the usual 100KHz, and a high-speed mode operating at 3.4MHz. Currently
+most I2C-based systems do not involve any of these extensions.
+    </para>
+    <para>
+At the hardware level I2C bus master support can be implemented in one
+of two ways. Some processors provide a dedicated I2C device, with the
+hardware performing much of the work. On other processors the I2C
+device is implemented in software, by bit-banging some GPIO pins. The
+latter approach can consume a significant number of cpu cycles, but is
+often acceptable because only occasional access to the I2C devices is
+needed.
+    </para>
+  </refsect1>
+
+  <refsect1 id="i2c-ecos-implementation"><title>eCos Support for I2C</title>
+    <para>
+The eCos I2C support for any given platform is spread over a number of
+different packages:
+    </para>
+    <itemizedlist>
+      <listitem><para>
+This package, <varname>CYGPKG_IO_I2C</varname>, exports a generic API
+for accessing devices attached to an I2C bus. This API handles issues
+such as locking between threads. The package does not contain any
+hardware-specific code. Instead it will use a separate I2C bus driver
+to handle the hardware, and it defines the interface that such bus
+drivers should provide. The package only provides support for a bus
+master, not for acting as a slave device.
+      </para>
+      <para>
+<varname>CYGPKG_IO_I2C</varname> also provides the
+hardware-independent portion of a bit-banged bus implementation. This
+needs to be complemented by a hardware-specific function that actually
+manipulates the SDA and SCL lines.
+      </para></listitem>
+      <listitem><para>
+If the processor has a dedicated I2C device then there will be a bus
+driver package for that hardware. The processor may be used on
+many different platforms and the same bus driver can be used on each one.
+The actual I2C devices attached to the bus will vary from one platform to
+the next.
+      </para></listitem>
+      <listitem><para>
+The generic API depends on <structname>cyg_i2c_device</structname>
+data structures. These contain the information needed by a bus driver,
+for example the device address. Usually the data structures are
+provided by the platform HAL since it is that package which knows
+about all the devices on the platform.
+      </para>
+      <para>
+On some development boards the I2C lines are brought out to expansion
+connectors, allowing end users to add extra devices. In such cases the
+platform HAL may not know about all the devices on the board. Data
+structures for the additional devices can instead be supplied by
+application code.
+      </para></listitem>
+      <listitem><para>
+If the board uses a bit-banged bus then typically the platform HAL
+will also instantiate the bus instance, providing the function that
+handles the low-level SDA and SCL manipulation. Usually this code
+cannot be shared because each board may use different GPIO pins for
+driving SCL and SDA, so the code belongs in the platform HAL rather
+than in a separate package.
+      </para></listitem>
+      <listitem><para>
+Some types of I2C devices may have their own driver package. For
+example a common type of I2C device is a battery-backed wallclock, and
+eCos defines how these devices should be supported. Such an I2C device
+will have its own wallclock device driver and the device will not be
+accessed directly by application code. For other types of device eCos
+does not define an API and there will not be separate device driver
+packages. Instead application code is expected to use the
+<structname>cyg_i2c_device</structname> structures directly to access
+the hardware.
+      </para></listitem>
+    </itemizedlist>
+    <para>
+Typically all appropriate packages will be loaded automatically when
+you configure eCos for a given platform. If the application does not use
+any of the I2C I/O facilities, directly or indirectly, then linker
+garbage collection should eliminate all unnecessary code and data. All
+necessary initialization should happen automatically. However the
+exact details may depend on the platform, so the platform HAL
+documentation should be checked for further details.
+    </para>
+    <para>
+There is one important exception to this: if the I2C devices are
+attached to an expansion connector then the platform HAL will not know
+about these devices. Instead more work will have to be done by
+application code.
+    </para>
+  </refsect1>
+
+</refentry>
+
+<refentry id="i2c-api">
+  <refmeta>
+    <refentrytitle>I2C Interface</refentrytitle>
+  </refmeta>
+  <refnamediv>
+    <refname>I2C Functions</refname>
+    <refpurpose>allow applications and other packages to access I2C devices</refpurpose>
+  </refnamediv>
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>
+#include &lt;cyg/io/i2c.h&gt;
+      </funcsynopsisinfo>
+      <funcprototype>
+        <funcdef>cyg_uint32 <function>cyg_i2c_tx</function></funcdef>
+        <paramdef>const cyg_i2c_device* <parameter>device</parameter></paramdef>
+        <paramdef>const cyg_uint8* <parameter>tx_data</parameter></paramdef>
+        <paramdef>cyg_uint32 <parameter>count</parameter></paramdef>
+      </funcprototype>
+      <funcprototype>
+        <funcdef>cyg_uint32 <function>cyg_i2c_rx</function></funcdef>
+        <paramdef>const cyg_i2c_device* <parameter>device</parameter></paramdef>
+        <paramdef>const cyg_uint8* <parameter>tx_data</parameter></paramdef>
+        <paramdef>cyg_uint32 <parameter>count</parameter></paramdef>
+      </funcprototype>
+      <funcprototype>
+        <funcdef>void <function>cyg_i2c_transaction_begin</function></funcdef>
+        <paramdef>const cyg_i2c_device* <parameter>device</parameter></paramdef>
+      </funcprototype>
+      <funcprototype>
+        <funcdef>cyg_bool <function>cyg_i2c_transaction_begin_nb</function></funcdef>
+        <paramdef>const cyg_i2c_device* <parameter>device</parameter></paramdef>
+      </funcprototype>
+      <funcprototype>
+        <funcdef>cyg_uint32 <function>cyg_i2c_transaction_tx</function></funcdef>
+        <paramdef>const cyg_i2c_device* <parameter>device</parameter></paramdef>
+        <paramdef>cyg_bool <parameter>send_start</parameter></paramdef>
+        <paramdef>const cyg_uint8* <parameter>tx_data</parameter></paramdef>
+        <paramdef>cyg_uint32 <parameter>count</parameter></paramdef>
+        <paramdef>cyg_bool <parameter>send_stop</parameter></paramdef>
+      </funcprototype>
+      <funcprototype>
+        <funcdef>cyg_uint32 <function>cyg_i2c_transaction_rx</function></funcdef>
+        <paramdef>const cyg_i2c_device* <parameter>device</parameter></paramdef>
+        <paramdef>cyg_bool <parameter>send_start</parameter></paramdef>
+        <paramdef>const cyg_uint8* <parameter>tx_data</parameter></paramdef>
+        <paramdef>cyg_uint32 <parameter>count</parameter></paramdef>
+        <paramdef>cyg_bool <parameter>send_nack</parameter></paramdef>
+        <paramdef>cyg_bool <parameter>send_stop</parameter></paramdef>
+      </funcprototype>
+      <funcprototype>
+        <funcdef>void <function>cyg_i2c_transaction_stop</function></funcdef>
+        <paramdef>const cyg_i2c_device* <parameter>device</parameter></paramdef>
+      </funcprototype>
+      <funcprototype>
+        <funcdef>void <function>cyg_i2c_transaction_end</function></funcdef>
+        <paramdef>const cyg_i2c_device* <parameter>device</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1 id="i2c-api-description"><title>Description</title>
+    <para>
+All I2C functions take a pointer to a
+<structname>cyg_i2c_device</structname> structure as their first
+argument. These structures are usually provided by the platform HAL.
+They contain the information needed by the I2C bus driver to interact
+with the device, for example the device address.
+    </para>
+    <para>
+An I2C transaction involves the following stages:
+    </para>
+    <orderedlist>
+      <listitem><para>
+Perform thread-level locking on the bus. Only one thread at a time is
+allowed to access an I2C bus. This eliminates the need to worry about
+locking at the bus driver level. If a platform involves multiple I2C
+buses then each one will have its own lock.
+      </para></listitem>
+      <listitem><para>
+Generate a start condition, send the address and direction bit, and
+wait for an acknowledgement from the addressed device.
+      </para></listitem>
+      <listitem><para>
+Either transmit data to or receive data from the addressed device.
+      </para></listitem>
+      <listitem><para>
+The previous two steps may be repeated several times, allowing data to
+move in both directions during a single transfer.
+      </para></listitem>
+      <listitem><para>
+Generate a stop condition, ending the current data transfer. It is
+now possible to start another data transfer while the bus is still
+locked, if desired.
+      </para></listitem>
+      <listitem><para>
+End the transaction by unlocking the bus, allowing other threads to
+access other devices on the bus.
+      </para></listitem>
+    </orderedlist>
+    <para>
+The simple functions <function>cyg_i2c_tx</function> and
+<function>cyg_i2c_rx</function> perform all these steps in a single
+call, making them suitable for many I/O operations. The alternative
+transaction-oriented functions provide greater control when
+appropriate, for example if a repeated start is necessary for a
+bi-directional data transfer.
+    </para>
+    <para>
+With the exception of
+<function>cyg_i2c_transaction_begin_nb</function> all the functions
+will block until completion. The tx routines will return 0 if the
+specified device does not respond to its address, or the number of
+bytes actually transferred. This may be less than the number requested
+if the device sends an early nack, for example because it has run out
+of buffer space. The rx routines will return 0 or the number of bytes
+received. Usually this will be the same as the
+<varname>count</varname> parameter. A slave device has no way of
+indicating to the master that no more data is available, so the rx
+operation cannot complete early.
+    </para>
+    <para>
+I2C operations should always be performed at thread-level or during
+system initialization, and not inside an ISR or DSR. This greatly
+simplifies locking. Also a typical ISR or DSR should not perform a
+blocking operation such as an I2C transfer.
+    </para>
+  </refsect1>
+
+<refsect1 id="i2c-api-transfer"><title>Simple Transfers</title>
+    <para>
+<function>cyg_i2c_tx</function> and <function>cyg_i2c_rx</function>
+can be used for simple data transfers. They both go through the
+following steps: lock the bus, generate the start condition, send the
+device address and the direction bit, either send or receive the data,
+generate the stop condition, and unlock the bus. At the end of a
+transfer the bus is back in its idle state, ready for the next
+transfer.
+    </para>
+    <para>
+<function>cyg_i2c_tx</function> returns the number of bytes actually
+transmitted. This may be 0 if the device does not respond when its
+address is sent out. It may be less than the number of bytes requested
+if the device generates an early nack, typically because it has run
+out of buffer space.
+    </para>
+    <para>
+<function>cyg_i2c_rx</function> returns 0 if the device does not
+respond when its address is sent out, or the number of bytes actually
+received. Usually this will be the number of bytes requested because
+an I2C slave device has no way of aborting an rx operation early.
+    </para>
+  </refsect1>
+
+  <refsect1 id="i2c-api-transaction"><title>Transactions</title>
+    <para>
+To allow multiple threads to access devices on the I2C some locking is
+required. This is encapsulated inside transactions. The
+<function>cyg_i2c_tx</function> and <function>cyg_i2c_rx</function>
+functions implicitly use such transactions, but the functionality is
+also available directly to application code. Amongst other things
+transactions can be used for more complicated interactions with I2C
+devices, in particular ones involving repeated starts.
+    </para>
+    <para>
+<function>cyg_i2c_transaction_begin</function> must be used at the
+start of a transaction. This performs thread-level locking on the bus,
+blocking if it is currently in use by another thread.
+    </para>
+    <para>
+<function>cyg_i2c_transaction_begin_nb</function> is a non-blocking
+variant, useful for threads which cannot afford to block for an
+indefinite period. If the bus is currently locked the function returns
+false immediately. If the bus is not locked then it acts as
+<filename>cyg_i2c_transaction_begin</filename> and returns true.
+    </para>
+    <para>
+Once the bus has been locked it is possible to perform one or more
+data transfers by calling
+<function>cyg_i2c_transaction_tx</function>,
+<function>cyg_i2c_transaction_rx</function> and
+<function>cyg_i2c_transaction_stop</function>. Code should ensure that
+a stop condition has been generated by the end of a transaction.
+    </para>
+    <para>
+Once the transaction is complete
+<function>cyg_i2c_transaction_end</function> should be called. This
+unlocks the bus, allowing other threads to perform I2C I/O to devices
+on the same bus.
+    </para>
+    <para>
+As an example consider reading the registers in an FS6377 programmable
+clock generator. The first step is to write a byte 0 to the device,
+setting the current register to 0. Then a repeated start condition
+should be generated and it is possible to read the 16 byte-wide
+registers, starting with the current one. Typical code for this might
+look like:
+    </para>
+    <programlisting width=72>
+    cyg_uint8  tx_data[1];
+    cyg_uint8  rx_data[16];
+
+    cyg_i2c_transaction_begin(&amp;hal_alaia_i2c_fs6377);
+    tx_data[0] = 0x00;
+    cyg_i2c_transaction_tx(&amp;hal_alaia_i2c_fs6377,
+                           true, tx_data, 1, false);
+    cyg_i2c_transaction_rx(&amp;hal_alaia_i2c_fs6377,
+                           true, rx_data, 16, true, true);
+    cyg_i2c_transaction_end(&amp;hal_alaia_i2c_fs6377);
+    </programlisting>
+    <para>
+Here <varname>hal_alaia_i2c_fs6377</varname> is a
+<structname>cyg_i2c_device</structname> structure provided by the
+platform HAL. A transaction is begun, locking the bus. Then there is a
+transmit for a single byte. This transmit involves generating a start
+condition and sending the address and direction bit, but not a stop
+condition. Next there is a receive for 16 bytes. This also involves a
+start condition, which the device will interpret as a repeated start
+because it has not yet seen a stop. The start condition will be
+followed by the address and direction bit, and then the device will
+start transmitting the register contents. Once all 16 bytes have been
+received the rx routine will send a nack rather than an ack, halting
+the transfer, and then a stop condition is generated. Finally the
+transaction is ended, unlocking the bus.
+    </para>
+    <para>
+The arguments to <function>cyg_i2c_transaction_tx</function> are as
+follows:
+    </para>
+    <variablelist>
+      <varlistentry>
+        <term><type>const cyg_i2c_device*</type> <varname>device</varname></term>
+        <listitem><para>
+This identifies the I2C device that should be used.
+        </para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><type>cyg_bool</type> <varname>send_start</varname></term>
+        <listitem><para>
+If true, generate a start condition and send the address and direction
+bit. If false, skip those steps and go straight to transmitting the
+actual data. The latter can be useful if the data to be transmitted is
+spread over several buffers. The first tx call will involve generating
+the start condition but subsequent tx calls can skip this and just
+continue from the previous one.
+        </para><para>
+<varname>send_start</varname> must be true if the tx call is the first
+operation in a transaction, or if the previous call was an rx or stop.
+        </para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><type>const cyg_uint8*</type> <varname>tx_data</varname></term>
+        <term><type>cyg_uint32</type> <varname>count</varname></term>
+        <listitem><para>
+These arguments specify the data to be transmitted to the device.
+        </para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><type>cyg_bool</type> <varname>send_stop</varname></term>
+        <listitem><para>
+If true, generate a stop condition at the end of the transmit. Usually
+this is done only if the transmit is the last operation in a
+transaction.
+        </para></listitem>
+      </varlistentry>
+    </variablelist>
+    <para>
+The arguments to <function>cyg_i2c_transaction_rx</function> are as
+follows:
+    </para>
+    <variablelist>
+      <varlistentry>
+        <term><type>const cyg_i2c_device*</type> <varname>device</varname></term>
+        <listitem><para>
+This identifies the I2C device that should be used.
+        </para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><type>cyg_bool</type> <varname>send_start</varname></term>
+        <listitem><para>
+If true, generate a start condition and send the address and direction
+bit. If false, skip those steps and go straight to receiving the
+actual data. The latter can be useful if the incoming data should be
+spread over several buffers. The first rx call will involve generating
+the start condition but subsequent rx calls can skip this and just
+continue from the previous one. Another use is for devices which can
+send variable length data, consisting of an initial length and then
+the actual data. The first rx will involve generating the start
+condition and reading the length, a subsequent rx will then just read
+the data.
+        </para><para>
+<varname>send_start</varname> must be true if the rx call is the first
+operation in a transaction, if the previous call was a tx or stop, or
+if the previous call was an an rx and the <varname>send_nack</varname>
+flag was set.
+        </para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><type>cyg_uint8*</type> <varname>rx_data</varname></term>
+        <term><type>cyg_uint32</type> <varname>count</varname></term>
+        <listitem><para>
+These arguments specify how much data should be received and where it
+should be placed.
+        </para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><type>cyg_bool</type> <varname>send_nack</varname></term>
+        <listitem><para>
+If true generate a nack instead of an ack for the last byte received.
+This causes the slave to end its transmit. The next operation should
+either involve a repeated start or a stop.
+<varname>send_nack</varname> should be set to false only if
+<varname>send_stop</varname> is also false, the next operation will be
+another rx, and that rx does not specify <varname>send_start</varname>.
+        </para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><type>cyg_bool</type> <varname>send_stop</varname></term>
+        <listitem><para>
+If true, generate a stop condition at the end of the transmit. Usually
+this is done only if the transmit is the last operation in a
+transaction.
+        </para></listitem>
+      </varlistentry>
+    </variablelist>
+    <para>
+The final transaction-oriented function is
+<function>cyg_i2c_transaction_stop</function>. This just generates a
+stop condition. It should be used if the previous operation was a tx
+or rx that, for some reason, did not set the
+<varname>send_stop</varname> flag. A stop condition must be generated
+before the transaction is ended.
+    </para>
+  </refsect1>
+
+  <refsect1 id="i2c-api_initialization"><title>Initialization</title>
+    <para>
+The generic package <varname>CYGPKG_IO_I2C</varname> arranges for all
+I2C bus devices to be initialized via a single prioritized C++ static
+constructor. Usually this constructor will run early on during system
+startup, before any application code. The default priority is
+<literal>CYG_INIT_DRIVERS</literal>, but this can be changed via the
+configuration option <varname>CYGNUM_I2C_INIT_PRIORITY</varname>.
+Other code should not try to access any of the I2C devices until after
+the buses have been initialized.
+    </para>
+  </refsect1>
+
+</refentry>
+
+<refentry id="i2c-porting">
+  <refmeta>
+    <refentrytitle>Porting to New Hardware</refentrytitle>
+  </refmeta>
+  <refnamediv>
+    <refname>Porting</refname>
+    <refpurpose>Adding I2C support to new hardware</refpurpose>
+  </refnamediv>
+
+  <refsect1 id="i2c-porting-description"><title>Description</title>
+    <para>
+Adding I2C support to an eCos port involves a number of steps. The
+generic I2C package <varname>CYGPKG_IO_I2C</varname> should be
+included in the appropriate <database>ecos.db</database> target entry
+or entries. Next <structname>cyg_i2c_device</structname> structures
+should be provided for every device on the bus. Usually this is the
+responsibility of the platform HAL. In the case of development boards
+where the I2C SDA and SCL lines are accessible via an expansion
+connector, more devices may have been added and it will be the
+application's responsibility to provide the structures. Finally
+there is a need for one or more <structname>cyg_i2c_bus</structname>
+structures. Amongst other things these structures provide functions
+for actually driving the bus. If the processor has dedicated I2C
+hardware then this structure will usually be provided by a device
+driver package. If the bus is implemented by bit-banging then the bus
+structure will usually be provided by the platform HAL.
+    </para>
+  </refsect1>
+
+  <refsect1 id="i2c-porting-devices"><title>Adding a Device</title>
+    <para>
+The eCos I2C API works in terms of
+<structname>cyg_i2c_device</structname> structures, and these provide
+the information needed to access the hardware. A
+<structname>cyg_i2c_device</structname> structure contains the
+following fields:
+    </para>
+    <variablelist>
+      <varlistentry>
+        <term><type>cyg_i2c_bus*</type> <varname>i2c_bus</varname></term>
+        <listitem><para>
+This specifies the bus which the slave device is connected to. Most
+boards will only have a single I2C bus, but multiple buses are possible.
+        </para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><type>cyg_uint16</type> <varname>i2c_address</varname></term>
+        <listitem><para>
+For most devices this will be the 7-bit I2C address the device will
+respond to. There is room for future expansion, for example to support
+10-bit addresses.
+        </para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><type>cyg_uint16</type> <varname>i2c_flags</varname></term>
+        <listitem><para>
+This field is not used at present. It exists for future expansion, for
+example to allow for fast mode or high-speed mode, and incidentally
+pads the structure to a 32-bit boundary.
+        </para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><type>cyg_uint32</type> <varname>i2c_delay</varname></term>
+        <listitem><para>
+This holds the clock period which should be used when interacting with
+the device, in nanoseconds. Usually this will be 10000 ns,
+corresponding to a 100KHz clock, and the header <filename
+class="headerfile">cyg/io/i2c.h</filename> provides a
+<literal>#define</literal> <varname>CYG_I2C_DEFAULT_DELAY</varname>
+for this. Sometimes it may be desirable to use a slower clock, for
+example to reduce noise problems.
+        </para></listitem>
+      </varlistentry>
+    </variablelist>
+    <para>
+The normal way to instantiate a <varname>cyg_i2c_device</varname>
+structure uses the <function>CYG_I2C_DEVICE</function> macro, also
+provided by <filename class="headerfile">cyg/io/i2c.h</filename>:
+    </para>
+    <programlisting width=72>
+#include &lt;cyg/io/i2c.h&gt;
+
+CYG_I2C_DEVICE(cyg_i2c_wallclock_ds1307,            \
+               &amp;hal_alaia_i2c_bus,                  \
+               0x68,                                \
+               0x00,                                \
+               CYG_I2C_DEFAULT_DELAY);
+
+CYG_I2C_DEVICE(hal_alaia_i2c_fs6377,                \
+               &amp;hal_alaia_i2c_bus,                  \
+               0x58,                                \
+               0x00,                                \
+               CYG_I2C_DEFAULT_DELAY);
+    </programlisting>
+    <para>
+The arguments to the macro are the variable name, an I2C bus pointer,
+the device address, the flags field, and the delay field. The above
+code fragment defines two I2C device variables,
+<varname>cyg_i2c_wallclock_ds1307</varname> and
+<varname>hal_alaia_i2c_fs6377</varname>, which can be used for the
+first argument to the <literal>cyg_i2c_</literal> functions. Both
+devices are on the same bus. The device addresses are 0x68 and 0x58
+respectively, and the devices do not have any special requirements.
+    </para>
+    <para>
+When the platform HAL provides these structures it should also export
+them for use by the application and other packages. Usually this
+involves an entry in <filename
+class="headerfile">cyg/hal/plf_io.h</filename>, which gets included
+automatically via one of the main exported HAL header files <filename
+class="headerfile">cyg/hal/hal_io.h</filename>. Unfortunately
+exporting the structures directly can be problematical because of
+circular dependencies between the I2C header and the HAL headers.
+Instead the platform HAL should define a macro
+<varname>HAL_I2C_EXPORTED_DEVICES</varname>: 
+    </para>
+    <programlisting width=72>
+# define HAL_I2C_EXPORTED_DEVICES                                   \
+    extern cyg_i2c_bus                  hal_alaia_i2c_bus;          \
+    extern cyg_i2c_device               cyg_i2c_wallclock_ds1307;   \
+    extern cyg_i2c_device               hal_alaia_i2c_fs6377;
+    </programlisting>
+    <para>
+This macro gets expanded automatically by <filename
+class="headerfile">cyg/io/i2c.h</filename> once the data structures
+themselves have been defined, so application code can just include
+that header and all the buses and devices will be properly exported
+and usable.
+    </para>
+    <para>
+There is no single convention for naming the I2C devices. If the
+device will be used by some other package then typically that
+specifies the name that should be used. For example the DS1307
+wallclock driver expects the I2C device to be called
+<varname>cyg_i2c_wallclock_ds1307</varname>, so failing to observe
+that convention will lead to compile-time and link-time errors. If the
+device will not be used by any other package then it is up to the
+platform HAL to select the name, and as long as reasonable care is
+taken to avoid name space pollution the exact name does not matter.
+    </para>
+  </refsect1>
+
+  <refsect1 id="i2c-porting-bitbang"><title>Bit-banged Bus</title>
+    <para>
+Some processors come with dedicated I2C hardware. On other hardware
+the I2C bus involves simply connecting some GPIO pins to the SCL and
+SDA lines and then using software to implement the I2C protocol. This
+is usually referred to as bit-banging the bus. The generic I2C package
+<varname>CYGPKG_IO_I2C</varname> provides the main code for a
+bit-banged implementation, requiring one platform-specific function
+that does the actual GPIO pin manipulation. This function is usually
+hardware-specific because different boards will use different pins for
+the I2C bus, so typically it is left to the platform HAL to provide
+this function and instantiate the I2C bus object. There is no point in
+creating a separate package for this because the code cannot be
+re-used for other platforms.
+    </para>
+    <para>
+Instantiating a bit-banged I2C bus requires the following:
+    </para>
+    <programlisting width=72>
+#include &lt;cyg/io/i2c.h&gt;
+
+static cyg_bool
+hal_alaia_i2c_bitbang(cyg_i2c_bus* bus, cyg_i2c_bitbang_op op)
+{
+    cyg_bool result    = 0;
+    switch(op) {
+        &hellip;
+    }
+    return result;
+}
+
+CYG_I2C_BITBANG_BUS(&amp;hal_alaia_i2c_bus, &amp;hal_alaia_i2c_bitbang);
+    </programlisting>
+    <para>
+This gives a structure <varname>hal_alaia_i2c_bus</varname> which can
+be used when defining the <varname>cyg_i2c_device</varname>
+structures. The second argument specifies the function which will
+do the actual bit-banging. It takes two arguments. The first
+identifies the bus, which can be useful if the hardware has multiple
+I2C buses. The second specifies the bit-bang operation that should be
+performed. To understand these operations consider how I2C devices
+should be wired up according to the specification:
+    </para>
+    <informalfigure PgWide=1>
+      <mediaobject>
+        <imageobject>
+          <imagedata fileref="i2c_hw.png" Scalefit=1 Align="Center">
+        </imageobject>
+      </mediaobject>
+    </informalfigure>
+    <para>
+Master and slave devices are interfaced to the bus in exactly the same
+way. The default state of the bus is to have both lines high via the
+pull-up resistors. Any device on the bus can lower either line, when
+allowed to do so by the protocol. Usually the SDA line only changes
+while SCL is low, but the start and stop conditions involve SDA
+changing while SCL is high. All devices have the ability to both read
+and write both lines. In reality not all bit-banged hardware works
+quite like this. Instead just two GPIO pins are used, and these are
+switched between input and output mode as required.
+    </para>
+    <para>
+The bitbang function should support the following operations:
+    </para>
+    <variablelist>
+      <varlistentry>
+        <term><literal>CYG_I2C_BITBANG_INIT</literal></term>
+        <listitem><para>
+This will be called during system initialization, as a side effect of
+a prioritized C++ static constructor. By default this constructor will
+run at <literal>CYG_INIT_DRIVERS</literal> priority. The bitbang
+function should ensure that both SCL and SDA are driven high.
+        </para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><literal>CYG_I2C_BITBANG_SCL_HIGH</literal></term>
+        <term><literal>CYG_I2C_BITBANG_SCL_LOW</literal></term>
+        <term><literal>CYG_I2C_BITBANG_SDA_HIGH</literal></term>
+        <term><literal>CYG_I2C_BITBANG_SDA_LOW</literal></term>
+        <listitem><para>
+These operations simply set the appropriate lines high or low.
+        </para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><literal>CYG_I2C_BITBANG_SCL_HIGH_CLOCKSTRETCH</literal></term>
+        <listitem><para>
+In its simplest form this operation should simply set the SCL line
+high, indicating that the data on the SDA line is stable. However
+there is a complication: if a device is not ready yet then it can
+throttle back the master by keeping the SCL line low. This is known as
+clock-stretching. Hence for this operation the bitbang function should
+allow the SCL line to float high, then poll it until it really has
+become high. If a single pin is used for the SCL line then this pin
+should be turned back into a high output at the end of the call.
+        </para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><literal>CYG_I2C_BITBANG_SCL_LOW_SDA_INPUT</literal></term>
+        <listitem><para>
+This is used when there is a change of direction and the slave device
+is about to start driving the SDA line. This can be significant if a
+single pin is used to handle both input and output of SDA, to avoid
+a situation where both the master and the slave are driving the SDA
+line for an extended period of time. The operation combines dropping
+the SCL line and switching SDA to an input in an atomic or near-atomic
+operation.
+        </para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><literal>CYG_I2C_BITBANG_SDA_READ</literal></term>
+        <listitem><para>
+The SDA line is currently set as an input and the bitbang function
+should sample and return the current state.
+        </para></listitem>
+      </varlistentry>
+    </variablelist>
+    <para>
+The bitbang function returns a boolean. For most operations this
+return value is ignored. For
+<literal>CYG_I2C_BITBANG_SDA_READ</literal> it should be the current
+level of the SDA line.
+    </para>
+    <para>
+Depending on the hardware some care may have to be taken when
+manipulating the GPIO pins. Although the I2C subsystem performs the
+required locking at the bus level, the device registers controlling
+the GPIO pins may get used by other subsystems or by the application.
+It is the responsibility of the bitbang function to perform
+appropriate locking, whether via a mutex or by briefly disabling
+interrupts around the register accesses.
+    </para>
+  </refsect1>
+
+  <refsect1 id="i2c-porting-bus"><title>Full Bus Driver</title>
+    <para>
+If the processor has dedicated I2C hardware then usually this will
+involve a separate device driver package in the
+<filename>devs/i2c</filename> hierarchy of the eCos component
+repository. That package should also be included in the appropriate
+<database>ecos.db</database> target entry or entries. The device
+driver may exist already, or it may have to be written from scratch.
+    </para>
+    <para>
+A new I2C driver basically involves creating an
+<structname>cyg_i2c_bus</structname> structure. The device driver
+should supply the following fields:
+    </para>
+    <variablelist>
+      <varlistentry>
+        <term><function>i2c_init_fn</function></term>
+        <listitem><para>
+This function will be called during system initialization to set up
+the I2C hardware. The generic I2C code creates a static object with a
+prioritized constructor, and this constructor will invoke the init
+functions for the various I2C buses in the system.
+        </para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><function>i2c_tx_fn</function></term>
+        <term><function>i2c_rx_fn</function></term>
+        <term><function>i2c_stop_fn</function></term>
+        <listitem><para>
+These functions implement the core I2C functionality. The arguments
+and results are the same as for the transaction functions
+<function>cyg_i2c_transaction_tx</function>,
+<function>cyg_i2c_transaction_rx</function> and
+<function>cyg_i2c_transaction_stop</function>.
+        </para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><type>void*</type> <varname>i2c_extra</varname></term>
+        <listitem><para>
+This field holds any extra information that may be needed by the
+device driver. Typically it will be a pointer to some driver-specific
+data structure.
+        </para></listitem>
+      </varlistentry>
+    </variablelist>
+    <para>
+To assist with instantiating a <structname>cyg_i2c_bus</structname>
+object the header file <filename
+class="headerfile">cyg/io/i2c.h</filename> provides a macro. Typical
+usage would be:
+    </para>
+    <programlisting width=72>
+struct xyzzy_data {
+    &hellip;
+} xyzzy_object;
+
+static void
+xyzzy_i2c_init(struct cyg_i2c_bus* bus)
+{
+    &hellip;
+}
+
+static cyg_uint32
+xyzzy_i2c_tx(const cyg_i2c_device* dev,
+             cyg_bool send_start,
+             const cyg_uint8* tx_data, cyg_uint32 count,
+             cyg_bool send_stop)
+{
+    &hellip;
+}
+
+static cyg_uint32
+xyzzy_i2c_tx(const cyg_i2c_device* dev,
+             cyg_bool send_start,
+             const cyg_uint8* tx_data, cyg_uint32 count,
+             cyg_bool send_stop)
+{
+    &hellip;
+}
+
+static void
+xyzzy_i2c_stop(const cyg_i2c_device* dev)
+{
+    &hellip;
+}
+
+CYG_I2C_BUS(cyg_i2c_xyzzy_bus,
+            &amp;xyzzy_i2c_init,
+            &amp;xyzzy_i2c_tx,
+            &amp;xyzzy_i2c_rx,
+            &amp;xyzzy_i2c_stop,
+            (void*) &amp;xyzzy_object);
+    </programlisting>
+    <para>
+The generic I2C code contains these functions for a bit-banged I2C bus
+device. It can be used as a starting point for new drivers. Note that
+the bit-bang code uses the <varname>i2c_extra</varname> field to hold
+the hardware-specific bitbang function rather than a pointer to some
+data structure.
+    </para>
+
+  </refsect1>
+
+</refentry>
+</part>
Index: io/i2c/current/doc/i2c_hw.fig
===================================================================
RCS file: io/i2c/current/doc/i2c_hw.fig
diff -N io/i2c/current/doc/i2c_hw.fig
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ io/i2c/current/doc/i2c_hw.fig	20 Apr 2005 11:55:36 -0000
@@ -0,0 +1,182 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter  
+100.00
+Single
+-2
+1200 2
+6 450 1725 4275 6000
+6 450 1725 4275 6000
+6 450 1725 4275 6000
+6 450 1725 4275 6000
+6 600 1725 4125 5700
+6 2925 2325 4125 5700
+6 2925 2400 4125 5700
+6 2925 2400 4125 5700
+6 2925 3300 4125 5700
+6 2925 3300 4125 5700
+6 2925 3300 4125 5700
+6 2925 3300 4125 5700
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 2925 3600 3225 3600 3525 3300 3525 3900 3225 3600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 3525 3600 3825 3600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 3525 5400 4125 5400
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 3645 5550 4005 5550
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 3750 5700 3900 5700
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	1 1 1.00 60.00 120.00
+	 2925 4650 3525 4650
+-6
+1 4 0 1 0 7 50 -1 -1 0.000 1 0.0000 3525 4650 300 300 3525 4350 3525 4950
+-6
+-6
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+	 3525 4800 3825 4800 3825 5400
+-6
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+	 3825 2400 3825 4500 3525 4500
+-6
+2 1 0 4 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 3525 4425 3525 4875
+-6
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 3825 2400 18 18 3807 2400 3843 2400
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 3825 3600 18 18 3807 3600 3843 3600
+-6
+1 4 0 1 0 7 50 -1 -1 0.000 1 0.0000 1800 4650 300 300 1800 4350 1800 4950
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 2100 1800 18 18 2082 1800 2118 1800
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 1200 3600 1500 3600 1800 3300 1800 3900 1500 3600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 1800 3600 2100 3600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 1800 5400 2400 5400
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 1920 5550 2280 5550
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 2025 5700 2175 5700
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	1 1 1.00 60.00 120.00
+	 1200 4650 1800 4650
+4 0 0 50 -1 0 12 0.0000 4 135 555 600 3525 SDA in\001
+4 0 0 50 -1 0 12 0.0000 4 135 660 600 4575 SDA out\001
+4 0 0 50 -1 0 12 0.0000 4 135 510 2550 3525 SCL in\001
+4 0 0 50 -1 0 12 0.0000 4 135 660 2475 4575 SDA out\001
+-6
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+	 2100 1800 2100 4500 1800 4500
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 450 2700 4275 2700 4275 6000 450 6000 450 2700
+-6
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+	 1800 4800 2100 4800 2100 5400
+-6
+2 1 0 4 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 1800 4425 1800 4875
+-6
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 2100 3600 18 18 2082 3600 2118 3600
+-6
+6 5025 1725 8850 6000
+6 5025 1725 8850 6000
+6 5025 1725 8850 6000
+6 5025 1725 8850 6000
+6 5175 1725 8700 5700
+6 7500 2325 8700 5700
+6 7500 2400 8700 5700
+6 7500 2400 8700 5700
+6 7500 3300 8700 5700
+6 7500 3300 8700 5700
+6 7500 3300 8700 5700
+6 7500 3300 8700 5700
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 7500 3600 7800 3600 8100 3300 8100 3900 7800 3600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 8100 3600 8400 3600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 8100 5400 8700 5400
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 8220 5550 8580 5550
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 8325 5700 8475 5700
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	1 1 1.00 60.00 120.00
+	 7500 4650 8100 4650
+-6
+1 4 0 1 0 7 50 -1 -1 0.000 1 0.0000 8100 4650 300 300 8100 4350 8100 4950
+-6
+-6
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+	 8100 4800 8400 4800 8400 5400
+-6
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+	 8400 2400 8400 4500 8100 4500
+-6
+2 1 0 4 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 8100 4425 8100 4875
+-6
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 8400 2400 18 18 8382 2400 8418 2400
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 8400 3600 18 18 8382 3600 8418 3600
+-6
+1 4 0 1 0 7 50 -1 -1 0.000 1 0.0000 6375 4650 300 300 6375 4350 6375 4950
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 6675 1800 18 18 6657 1800 6693 1800
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 5775 3600 6075 3600 6375 3300 6375 3900 6075 3600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 6375 3600 6675 3600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 6375 5400 6975 5400
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 6495 5550 6855 5550
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 6600 5700 6750 5700
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+	1 1 1.00 60.00 120.00
+	 5775 4650 6375 4650
+4 0 0 50 -1 0 12 0.0000 4 135 555 5175 3525 SDA in\001
+4 0 0 50 -1 0 12 0.0000 4 135 660 5175 4575 SDA out\001
+4 0 0 50 -1 0 12 0.0000 4 135 510 7125 3525 SCL in\001
+4 0 0 50 -1 0 12 0.0000 4 135 660 7050 4575 SDA out\001
+-6
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+	 6675 1800 6675 4500 6375 4500
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 5025 2700 8850 2700 8850 6000 5025 6000 5025 2700
+-6
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+	 6375 4800 6675 4800 6675 5400
+-6
+2 1 0 4 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 6375 4425 6375 4875
+-6
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 6675 3600 18 18 6657 3600 6693 3600
+-6
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 3900 600 18 18 3882 600 3918 600
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 4500 600 18 18 4482 600 4518 600
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 3900 1800 18 18 3882 1800 3918 1800
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 4500 2400 18 18 4482 2400 4518 2400
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 525 600 9000 600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 3900 600 3900 900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 4500 600 4500 900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 3750 900 4050 900 4050 1500 3750 1500 3750 900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 4350 900 4650 900 4650 1500 4350 1500 4350 900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 525 1800 9000 1800
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 525 2400 9000 2400
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 3900 1500 3900 1800
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+	 4500 1500 4500 2400
+4 0 0 50 -1 0 12 0.0000 4 135 405 600 525 VDD\001
+4 0 0 50 -1 0 12 0.0000 4 135 375 600 1725 SDA\001
+4 0 0 50 -1 0 12 0.0000 4 135 330 600 2325 SCL\001
Index: io/i2c/current/doc/i2c_hw.png
===================================================================
RCS file: io/i2c/current/doc/i2c_hw.png
diff -N io/i2c/current/doc/i2c_hw.png
Binary files /dev/null and i2c_hw.png differ
Index: io/i2c/current/include/i2c.h
===================================================================
RCS file: io/i2c/current/include/i2c.h
diff -N io/i2c/current/include/i2c.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ io/i2c/current/include/i2c.h	20 Apr 2005 11:55:40 -0000
@@ -0,0 +1,168 @@
+#ifndef CYGONCE_IO_I2C_H
+#define CYGONCE_IO_I2C_H
+
+//=============================================================================
+//
+//      i2c.h
+//
+//      Generic API for accessing devices on an I2C bus
+//
+//=============================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2004 eCosCentric Limited
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//=============================================================================
+//####DESCRIPTIONBEGIN####
+//
+// Author(s): 	bartv
+// Date:	    2004-10-05
+//####DESCRIPTIONEND####
+//=============================================================================
+
+#include <pkgconf/infra.h>
+#include <cyg/infra/cyg_type.h>
+#include <cyg/hal/drv_api.h>
+#include <cyg/hal/hal_tables.h>
+#include <cyg/hal/hal_io.h>
+
+// The information needed for interacting with a specific I2C device.
+// The bus provides the tx and rx functions. The address is (usually)
+// a 7-bit number sent at the start of each operation. The flags is
+// not currently used, but provides room for future expansion such as
+// 10-bit addresses. The delay should be a clock period in
+// nanoseconds. For the default 100KHz clock that gives a value of
+// 10000
+
+typedef struct cyg_i2c_device {
+    struct cyg_i2c_bus* i2c_bus;
+    cyg_uint16          i2c_address;
+    cyg_uint16          i2c_flags;
+    cyg_uint32          i2c_delay;
+} cyg_i2c_device;
+
+#define CYG_I2C_DEFAULT_DELAY   10000
+
+// A utility macro for defining an I2C device
+#define CYG_I2C_DEVICE(_name_, _bus_, _address_, _flags_, _delay_)  \
+    cyg_i2c_device _name_ = {                                       \
+        .i2c_bus        = _bus_,                                    \
+        .i2c_address    = _address_,                                \
+        .i2c_flags      = _flags_,                                  \
+        .i2c_delay      = _delay_                                   \
+    }
+
+// The information needed for interacting over a particular I2C bus.
+// Most hardware will only have one bus, but multiple buses are
+// supported. Thread synchronization happens on a per-bus level.
+typedef struct cyg_i2c_bus {
+    cyg_drv_mutex_t         i2c_lock;
+#ifdef CYGDBG_USE_ASSERTS
+    const cyg_i2c_device*   i2c_current_device;
+#endif
+    // The hardware-specific functions that do the real work
+    void                    (*i2c_init_fn)(struct cyg_i2c_bus*);
+    cyg_uint32              (*i2c_tx_fn)(const cyg_i2c_device*, cyg_bool, const cyg_uint8*, cyg_uint32, cyg_bool);
+    cyg_uint32              (*i2c_rx_fn)(const cyg_i2c_device*, cyg_bool, cyg_uint8*, cyg_uint32, cyg_bool, cyg_bool);
+    void                    (*i2c_stop_fn)(const cyg_i2c_device*);
+    // A spare field for use by the driver
+    void*                   i2c_extra;
+} cyg_i2c_bus;
+
+#define CYG_I2C_BUS(_name_, _init_fn_, _tx_fn_, _rx_fn_, _stop_fn_, _extra_)    \
+    cyg_i2c_bus _name_  CYG_HAL_TABLE_ENTRY( i2c_buses ) = {                    \
+        .i2c_init_fn    = _init_fn_,                                            \
+        .i2c_tx_fn      = _tx_fn_,                                              \
+        .i2c_rx_fn      = _rx_fn_,                                              \
+        .i2c_stop_fn    = _stop_fn_,                                            \
+        .i2c_extra      = _extra_                                               \
+    }
+
+// To support static initialization all buses are held in a table.
+// Of course usually this table will only contain a single entry
+extern cyg_i2c_bus cyg_i2c_buses[], cyg_i2c_buses_end;
+
+// The main exported interface. There are high-level operations for
+// simple transmits and receives, and transaction-oriented routines
+// for more complicated operations including those involving repeated
+// starts.
+externC cyg_uint32  cyg_i2c_tx(const cyg_i2c_device*, const cyg_uint8*, cyg_uint32);
+externC cyg_uint32  cyg_i2c_rx(const cyg_i2c_device*, cyg_uint8*, cyg_uint32);
+externC void        cyg_i2c_transaction_begin(const cyg_i2c_device*);
+externC cyg_bool    cyg_i2c_transaction_begin_nb(const cyg_i2c_device*);
+externC cyg_uint32  cyg_i2c_transaction_tx(const cyg_i2c_device*, cyg_bool, const cyg_uint8*, cyg_uint32, cyg_bool);
+externC cyg_uint32  cyg_i2c_transaction_rx(const cyg_i2c_device*, cyg_bool, cyg_uint8*, cyg_uint32, cyg_bool, cyg_bool);
+externC void        cyg_i2c_transaction_stop(const cyg_i2c_device*);
+externC void        cyg_i2c_transaction_end(const cyg_i2c_device*);
+
+// Bit-bang support. This merely requires the platform HAL to provide
+// a function for the actual bit-bang operation. The rest is handled
+// by the generic code.
+typedef enum cyg_i2c_bitbang_op {
+    CYG_I2C_BITBANG_INIT                    = 0,
+    CYG_I2C_BITBANG_SCL_HIGH                = 1,
+    CYG_I2C_BITBANG_SCL_LOW                 = 2,
+    CYG_I2C_BITBANG_SCL_HIGH_CLOCKSTRETCH   = 3,
+    CYG_I2C_BITBANG_SCL_LOW_SDA_INPUT       = 4,
+    CYG_I2C_BITBANG_SDA_OUTPUT              = 5,
+    CYG_I2C_BITBANG_SDA_HIGH                = 6,
+    CYG_I2C_BITBANG_SDA_LOW                 = 7,
+    CYG_I2C_BITBANG_SDA_READ                = 8
+} cyg_i2c_bitbang_op;
+
+typedef cyg_bool (*cyg_i2c_bitbang_fn)(cyg_i2c_bus*, cyg_i2c_bitbang_op);
+
+// A bitbang bus can be instantiated by just providing the bitbang function.
+// That is held in the extra field.
+#define CYG_I2C_BITBANG_BUS(_name_, _bitbang_fn_)                   \
+    CYG_I2C_BUS(_name_,                                             \
+                &cyg_i2c_bitbang_init,                              \
+                &cyg_i2c_bitbang_tx,                                \
+                &cyg_i2c_bitbang_rx,                                \
+                &cyg_i2c_bitbang_stop,                              \
+                (void*) _bitbang_fn_)
+
+// The generic bit-bang functions. These are not called directly,
+// but must be exported for use by the above macro.
+externC void        cyg_i2c_bitbang_init(cyg_i2c_bus*);
+externC cyg_uint32  cyg_i2c_bitbang_tx(const cyg_i2c_device*, cyg_bool, const cyg_uint8*, cyg_uint32, cyg_bool);
+externC cyg_uint32  cyg_i2c_bitbang_rx(const cyg_i2c_device*, cyg_bool, cyg_uint8*, cyg_uint32, cyg_bool, cyg_bool);
+externC void        cyg_i2c_bitbang_stop(const cyg_i2c_device*);
+
+// Allow the HAL to export the buses and named devices. There are
+// circular dependencies between this header and the HAL ones so
+// it is not easy for the HAL headers to define cyg_i2c_device
+// externs directly. Instead the HAL can define this macro which
+// provides the externs.
+#ifdef HAL_I2C_EXPORTED_DEVICES
+  HAL_I2C_EXPORTED_DEVICES
+#endif
+
+//-----------------------------------------------------------------------------
+#endif // ifndef CYGONCE_IO_I2C_H
+// End of i2c.h
Index: io/i2c/current/src/i2c.cxx
===================================================================
RCS file: io/i2c/current/src/i2c.cxx
diff -N io/i2c/current/src/i2c.cxx
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ io/i2c/current/src/i2c.cxx	20 Apr 2005 11:55:40 -0000
@@ -0,0 +1,519 @@
+//==========================================================================
+//
+//      i2c.cxx
+//
+//      Generic API for accessing devices attached to an I2C bus
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2004, 2005 eCosCentric Limited
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//###DESCRIPTIONBEGIN####
+//
+// Author(s):     bartv
+// Date:          2004-10-05
+//
+//###DESCRIPTIONEND####
+//========================================================================
+
+#include <pkgconf/infra.h>
+#include <pkgconf/io_i2c.h>
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/cyg_type.h>
+#include <cyg/infra/diag.h>
+#include <cyg/io/i2c.h>
+#include <cyg/hal/hal_io.h>
+#include <cyg/hal/hal_intr.h>
+#include <cyg/hal/hal_diag.h>
+
+// ----------------------------------------------------------------------------
+// Static initialization.
+//
+// This is somewhat tricky. There is a strong argument for leaving the
+// initialization to the platform HAL since with most I2C buses that
+// code will be straightforward. However that would mean that the
+// cyg_i2c_bus structure(s) and associated code would always get
+// linked in, even if the application does not use any i2c devices.
+// Instead to get the maximum benefit of linker garbage collection
+// initialization is handled by the generic I2C code, using the usual
+// dummy C++ object with a prioritized constructor. There is a dummy
+// references to this object from the transaction end function. The
+// result should be that if the application uses any I2C functionality
+// then all required code and data should get included, otherwise it
+// will all be elided.
+//
+// The init priority is configurable, defaulting to CYG_INIT_DRIVERS.
+// Arguably it should happen a bit earlier to allow other drivers to
+// perform I2C operations, but there is no CYG_INIT_BUS.
+//
+// All I2C buses are kept in a table, so that the init code can
+// iterate through each one.
+
+CYG_HAL_TABLE_BEGIN(cyg_i2c_buses, i2c_buses);
+CYG_HAL_TABLE_END(cyg_i2c_buses_end, i2c_buses);
+
+class cyg_i2c_init {
+  public:
+    cyg_uint8   i2c_dummy;
+    cyg_i2c_init();
+};
+
+static cyg_i2c_init cyg_i2c_init_object CYGBLD_ATTRIB_INIT_PRI(CYGNUM_I2C_INIT_PRIORITY);
+
+cyg_i2c_init::cyg_i2c_init()
+{
+    cyg_i2c_bus*    bus;
+
+    cyg_i2c_init_object.i2c_dummy   = 0;
+    for (bus = &(cyg_i2c_buses[0]); bus != &cyg_i2c_buses_end; bus++) {
+        cyg_drv_mutex_init(&(bus->i2c_lock));
+#ifdef CYGDBG_USE_ASSERTS
+        bus->i2c_current_device = (const cyg_i2c_device*) 0;
+#endif        
+        if ((void (*)(const cyg_i2c_bus*))0 != bus->i2c_init_fn) {
+            (*bus->i2c_init_fn)(bus);
+        }
+    }
+}
+
+// ----------------------------------------------------------------------------
+// The main exported routines just operate in terms of the transaction ones.
+
+extern "C" cyg_uint32
+cyg_i2c_tx(const cyg_i2c_device* dev, const cyg_uint8* buf, cyg_uint32 count)
+{
+    cyg_uint32  result;
+    cyg_i2c_transaction_begin(dev);
+    result = cyg_i2c_transaction_tx(dev, true, buf, count, true);
+    cyg_i2c_transaction_end(dev);
+    return result;
+}
+
+extern "C" cyg_uint32
+cyg_i2c_rx(const cyg_i2c_device* dev, cyg_uint8* buf, cyg_uint32 count)
+{
+    cyg_uint32  result;
+    cyg_i2c_transaction_begin(dev);
+    result = cyg_i2c_transaction_rx(dev, true, buf, count, true, true);
+    cyg_i2c_transaction_end(dev);
+    return result;
+}
+
+// Transaction begin/end relate to the per-bus locking. There does not
+// seem to be any need to interact with the hardware at this point.
+extern "C" void
+cyg_i2c_transaction_begin(const cyg_i2c_device* dev)
+{
+    cyg_i2c_bus*    bus;
+    
+    CYG_CHECK_DATA_PTR(dev, "valid I2C device pointer required");
+    bus = dev->i2c_bus;
+    CYG_CHECK_DATA_PTR(bus, "I2C device does not point at a valid bus structure");
+
+    while (! cyg_drv_mutex_lock(&(bus->i2c_lock)));
+#ifdef CYGDBG_USE_ASSERTS
+    bus->i2c_current_device = dev;
+#endif
+    // All done, return with the bus locked.
+}
+
+extern "C" cyg_bool
+cyg_i2c_transaction_begin_nb(const cyg_i2c_device* dev)
+{
+    cyg_bool        result  = false;
+    cyg_i2c_bus*    bus;
+
+    CYG_CHECK_DATA_PTR(dev, "valid I2C device pointer required");
+    bus = dev->i2c_bus;
+    CYG_CHECK_DATA_PTR(bus, "I2C device does not point at a valid bus structure");
+
+    if (cyg_drv_mutex_trylock(&(bus->i2c_lock))) {
+#ifdef CYGDBG_USE_ASSERTS
+        bus->i2c_current_device = dev;
+#endif
+        result = true;
+    }
+
+    return result;
+}
+
+extern "C" void
+cyg_i2c_transaction_end(const cyg_i2c_device* dev)
+{
+    cyg_i2c_bus*    bus;
+    
+    CYG_CHECK_DATA_PTR(dev, "valid I2C device pointer required");
+    bus = dev->i2c_bus;
+    CYG_CHECK_DATA_PTR(bus, "I2C device does not point at a valid bus structure");
+    CYG_PRECONDITION(bus->i2c_current_device == dev, "I2C transaction end requested without claiming the bus");
+
+    cyg_drv_mutex_unlock(&(bus->i2c_lock));
+#ifdef CYGDBG_USE_ASSERTS
+    bus->i2c_current_device = (const cyg_i2c_device*)0;
+#endif
+    // Avoid problems with linker garbage collection
+    cyg_i2c_init_object.i2c_dummy   = 0;
+}
+
+// The I/O operations just indirect through the per-bus function pointers
+extern "C" cyg_uint32
+cyg_i2c_transaction_tx(const cyg_i2c_device* dev, cyg_bool start, const cyg_uint8* buf, cyg_uint32 count, cyg_bool stop)
+{
+    cyg_i2c_bus*    bus;
+    cyg_uint32      result;
+    
+    CYG_CHECK_DATA_PTR(dev, "valid I2C device pointer required");
+    bus = dev->i2c_bus;
+    CYG_CHECK_DATA_PTR(bus, "I2C device does not point at a valid bus structure");
+    CYG_PRECONDITION(bus->i2c_current_device == dev, "I2C transaction end requested without claiming the bus");
+    CYG_CHECK_FUNC_PTR(bus->i2c_tx_fn, "I2C bus has not provided a tx function");
+    
+    result = (*(bus->i2c_tx_fn))(dev, start, buf, count, stop);
+    return result;
+}
+
+extern "C" cyg_uint32
+cyg_i2c_transaction_rx(const cyg_i2c_device* dev, cyg_bool start, cyg_uint8* buf, cyg_uint32 count, cyg_bool nak, cyg_bool stop)
+{
+    cyg_i2c_bus*    bus;
+    cyg_uint32      result;
+    
+    CYG_CHECK_DATA_PTR(dev, "valid I2C device pointer required");
+    bus = dev->i2c_bus;
+    CYG_CHECK_DATA_PTR(bus, "I2C device does not point at a valid bus structure");
+    CYG_PRECONDITION(bus->i2c_current_device == dev, "I2C transaction end requested without claiming the bus");
+    CYG_CHECK_FUNC_PTR(bus->i2c_rx_fn, "I2C bus has not provided an rx function");
+    
+    result = (*(bus->i2c_rx_fn))(dev, start, buf, count, nak, stop);
+    return result;
+}
+
+extern "C" void
+cyg_i2c_stop(const cyg_i2c_device* dev)
+{
+    cyg_i2c_bus*    bus;
+    
+    CYG_CHECK_DATA_PTR(dev, "valid I2C device pointer required");
+    bus = dev->i2c_bus;
+    CYG_CHECK_DATA_PTR(bus, "I2C device does not point at a valid bus structure");
+    CYG_PRECONDITION(bus->i2c_current_device == dev, "I2C transaction end requested without claiming the bus");
+    CYG_CHECK_FUNC_PTR(bus->i2c_stop_fn, "I2C bus has not provided a stop function");
+    
+    (*(bus->i2c_stop_fn))(dev);
+}
+
+// ----------------------------------------------------------------------------
+// The bit-banging support
+//
+// Optional debug support, useful while sorting out the platform-specific
+// bitbang function.
+#if 1
+# define DEBUG(_format_, ...)
+#else
+# define DEBUG(_format_, ...) diag_printf(format, ## __VA_ARGS__)
+#endif
+
+
+// Initialization calls into the h/w specific bit-bang function to set
+// up the GPIO pins and to set both SCL and SDA as outputs and high.
+
+extern "C" void
+cyg_i2c_bitbang_init(cyg_i2c_bus* mash)
+{
+    cyg_i2c_bitbang_fn      banger  = (cyg_i2c_bitbang_fn)(mash->i2c_extra);
+    CYG_CHECK_FUNC_PTR(banger, "bitbanged i2c bus has not provided a bitbang function");
+    
+    DEBUG("i2c bitbang init\n");
+    (*banger)(mash, CYG_I2C_BITBANG_INIT);
+}
+
+// Send a single byte out of the bus and get back the acknowledgement.
+// This may be the addressing byte or a data byte.
+// Preconditions:
+//     SCL     output low. The previous operation has completed, the
+//             acknowledgement bit has been received and processed.
+//             None of the devices should be driving the bus.
+//     SDA     output, indeterminate value.
+// Postconditions:
+//     SCL     output low, the acknowledgement has been received.
+//     SDA     may be left as an input or an output, depending on the
+//             last argument
+//
+// The return value is the acknowledge bit, i.e. 0 for ack, 1 for
+// nak.
+
+static cyg_bool
+cyg_i2c_bitbang_send_byte(cyg_i2c_bus* mash, const cyg_i2c_device* dev, cyg_uint32 delay, cyg_uint8 data, cyg_bool leave_as_input)
+{
+    cyg_i2c_bitbang_fn      banger  = (cyg_i2c_bitbang_fn)(mash->i2c_extra);
+    cyg_uint8               mask;
+    cyg_bool                result;
+
+    DEBUG("i2c bitbang send_byte %02x, leave_as_input %d\n", data, leave_as_input);
+    
+    for (mask = 0x0080; 0x00 != mask; ) {
+        // Transfer the next bit of data
+        (*banger)(mash, (0 != (data & mask)) ? CYG_I2C_BITBANG_SDA_HIGH : CYG_I2C_BITBANG_SDA_LOW);
+        mask = mask >> 1;
+        // We should now be able to just set the clock high and wait
+        // for the delay period. However if the device is having
+        // difficulty keeping up then it may perform clock stretching,
+        // basically keeping the clock low for a period of time.
+        // SCL_CLOCKSTRETCH means set the clock high, then wait
+        // until it really has gone high.
+        (*banger)(mash, CYG_I2C_BITBANG_SCL_HIGH_CLOCKSTRETCH);
+        // The device is now busy sampling the bit.
+        HAL_DELAY_US(delay);
+        // Unless this was the last bit, just drop the clock. If it is
+        // the last bit switch SDA to an input as well.
+        (*banger)(mash, (0 != mask) ? CYG_I2C_BITBANG_SCL_LOW : CYG_I2C_BITBANG_SCL_LOW_SDA_INPUT);
+        HAL_DELAY_US(delay);
+    }
+    // The last bit has been sent, SCL is low, and SDA has been turned
+    // into an input. The device should be placing the acknowledge bit
+    // onto SDA. Reading the acknowledge bit still needs a clock cycle.
+    (*banger)(mash, CYG_I2C_BITBANG_SCL_HIGH);
+    HAL_DELAY_US(delay);
+    result  = (*banger)(mash, CYG_I2C_BITBANG_SDA_READ);
+    DEBUG("i2c bitbang send_byte, got ack %d\n", result);
+    
+    // Drop SCL again, and leave SDA as either an input or an output
+    // depending on the calling code's requirements and whether or not
+    // the device ack'd.
+    (*banger)(mash, CYG_I2C_BITBANG_SCL_LOW);
+    HAL_DELAY_US(delay);
+    if ((1 == result) || !leave_as_input) {
+        (*banger)(mash, CYG_I2C_BITBANG_SDA_OUTPUT);
+    }
+
+    return result;
+}
+
+// Read a single byte from the bus and generate an ack/nak.
+// Preconditions:
+//    SCL       output low. The previous operation has completed.
+//              The device should have placed the data on the bus.
+//    SDA       input
+// Postconditions:
+//    SCL       output low. The acknowledgement has been sent.
+//    SDA       may be left as an input or an output, depending on
+//              whether or not this is the last byte in a transfer.
+//              For the last byte we want to send a nak.
+//
+// The result is the byte read.
+
+static cyg_uint8
+cyg_i2c_bitbang_read_byte(cyg_i2c_bus* mash, const cyg_i2c_device* dev, cyg_uint32 delay, cyg_bool nak)
+{
+    cyg_i2c_bitbang_fn  banger  = (cyg_i2c_bitbang_fn)(mash->i2c_extra);
+    cyg_uint32          i;
+    cyg_uint8           result  = 0;
+
+    DEBUG("i2c bitbang read_byte, nak %d\n", nak);
+    
+    for (i = 0; i < 8; i++) {
+        (*banger)(mash, CYG_I2C_BITBANG_SCL_HIGH);
+        HAL_DELAY_US(delay);
+        result = result << 1;
+        if (0 != (*banger)(mash, CYG_I2C_BITBANG_SDA_READ)) {
+            result |= 0x01;
+        }
+        (*banger)(mash, CYG_I2C_BITBANG_SCL_LOW);
+        HAL_DELAY_US(delay);
+    }
+    // We have read the last bit. SDA is still an input, SCL is low.
+    // We need to switch SDA to an output and send the ack/nak
+    (*banger)(mash, CYG_I2C_BITBANG_SDA_OUTPUT);
+    (*banger)(mash, nak ? CYG_I2C_BITBANG_SDA_HIGH : CYG_I2C_BITBANG_SDA_LOW);
+    (*banger)(mash, CYG_I2C_BITBANG_SCL_HIGH);
+    HAL_DELAY_US(delay);
+    (*banger)(mash, nak ? CYG_I2C_BITBANG_SCL_LOW : CYG_I2C_BITBANG_SCL_LOW_SDA_INPUT);
+    HAL_DELAY_US(delay);
+
+    DEBUG("i2c bitbang read_byte, result %02x\n", result);
+    return result;
+}
+
+// Generate a start condition.
+//
+// Preconditions:
+//    SCL       output, indeterminate
+//    SDA       output, indeterminate
+// Postconditions:
+//    SCL       output low
+//    SDA       output low
+//
+// At the start of a transaction we know that both SCL and SDA will be
+// high, but for a repeated start
+static void
+cyg_i2c_bitbang_send_start(cyg_i2c_bus* mash, cyg_uint32 delay)
+{
+    cyg_i2c_bitbang_fn      banger  = (cyg_i2c_bitbang_fn)(mash->i2c_extra);
+
+    DEBUG("i2c bitbang, generating start\n");
+    // First get both SCL and SDA back into a known state
+    (*banger)(mash, CYG_I2C_BITBANG_SDA_HIGH);
+    (*banger)(mash, CYG_I2C_BITBANG_SCL_HIGH);
+    HAL_DELAY_US(delay);
+    // A start condition involves dropping SDA while SCL stays high.
+    (*banger)(mash, CYG_I2C_BITBANG_SDA_LOW);
+    // Make sure that all devices have seen the start condition and
+    // reacted
+    HAL_DELAY_US(delay);
+    // Drop the clock, so that we can send the address/direction byte
+    (*banger)(mash, CYG_I2C_BITBANG_SCL_LOW);
+    HAL_DELAY_US(delay);
+}
+
+// Generate a stop condition
+// Preconditions:
+//    SCL       output, low
+//    SDA       output, indeterminate
+// Postconditions:
+//    SCL       output, high
+//    SDA       output, high
+static void
+cyg_i2c_bitbang_send_stop(cyg_i2c_bus* mash, cyg_uint32 delay)
+{
+    cyg_i2c_bitbang_fn      banger  = (cyg_i2c_bitbang_fn)(mash->i2c_extra);
+    
+    DEBUG("i2c bitbang, generating stop\n");
+    // We need SDA low, then we can generate the stop signal
+    (*banger)(mash, CYG_I2C_BITBANG_SDA_LOW);
+    (*banger)(mash, CYG_I2C_BITBANG_SCL_HIGH);
+    HAL_DELAY_US(delay);
+    (*banger)(mash, CYG_I2C_BITBANG_SDA_HIGH);
+    // Ensure that the minimum delay between stop and start is observed
+    HAL_DELAY_US(delay);
+}
+
+// A full transfer to a given device, within in a transaction.
+extern "C" cyg_uint32
+cyg_i2c_bitbang_tx(const cyg_i2c_device* dev, cyg_bool send_start, const cyg_uint8* buf, cyg_uint32 count, cyg_bool send_stop)
+{
+    cyg_uint32      result  = 0;
+    // The bit-bang code works in terms of us delays rather than ns
+    cyg_uint32      delay = (0 == dev->i2c_delay) ? 1 : (dev->i2c_delay + 1999) / 2000;
+    
+    CYG_CHECK_DATA_PTR(buf, "i2c tx operation requested but no data supplied");
+    CYG_PRECONDITION(count > 0, "at least one byte should be sent");
+
+    DEBUG("i2c bitbang tx, dev %08x, addr %02x, delay %d, send_start %d, buf %08x, count %d, send_stop %d\n",
+          dev, dev->i2c_address, delay, send_start, buf, count, send_stop);
+    
+    if (send_start) {
+        cyg_i2c_bitbang_send_start(dev->i2c_bus, delay);
+
+        // Now send a single byte, holding the address shifted left and a
+        // 0 to indicate a write. A nak indicates that the device has not
+        // responded. SDA should stay as an output.
+        if (0 != cyg_i2c_bitbang_send_byte(dev->i2c_bus, dev, delay, (dev->i2c_address << 1) | 0x00, false) ) {
+            // Get the bus back in a consistent state
+            DEBUG("i2 bitbang tx, no device has responded to address %02x\n", dev->i2c_address);
+            cyg_i2c_bitbang_send_stop(dev->i2c_bus, delay);
+            return 0;
+        }
+        // The device has ack'd so we can continue sending
+    }
+    // Send all bytes, unless we receive a nak. SDA should remain an output
+    do {
+        result += 1;
+        if (0 != cyg_i2c_bitbang_send_byte(dev->i2c_bus, dev, delay, buf[result - 1], false)) {
+            break;
+        }
+    } while (result < count);
+
+    // At this point we have SCL low and SDA an indeterminate output
+    if (send_stop) {
+        cyg_i2c_bitbang_send_stop(dev->i2c_bus, delay);
+    }
+    DEBUG("i2c bitbang tx, %d bytes sent out of %d\n", result, count);
+    
+    return result;
+}
+
+// A full transfer from a given device. The bus has already been locked
+// by higher-level code. On entry both SDA and SCL should be outputs
+// and both should be high. The same conditions should hold on exit.
+extern "C" cyg_uint32
+cyg_i2c_bitbang_rx(const cyg_i2c_device* dev, cyg_bool send_start, cyg_uint8* buf, cyg_uint32 count, cyg_bool send_nak, cyg_bool send_stop)
+{
+    cyg_uint32      result  = 0;
+    // The bit-bang code works in terms of us delays rather than ns
+    cyg_uint32      delay = (0 == dev->i2c_delay) ? 1 : (dev->i2c_delay + 1999) / 2000;
+    
+    DEBUG("i2c bitbang rx, dev %08x, addr %02x, delay %d, send_start %d, buf %08x, count %d, send_nak %d, send_stop %d\n",
+          dev, dev->i2c_address, delay, send_start, buf, count, send_nak, send_stop);
+    
+    CYG_CHECK_DATA_PTR(buf, "i2c rx operation requested but no data supplied");
+    CYG_PRECONDITION(count > 0, "at least one byte should be sent");
+    CYG_PRECONDITION(send_nak || !send_stop, "a stop can only be generated after the receive has been nak'd");
+
+    if (send_start) {
+        cyg_i2c_bitbang_send_start(dev->i2c_bus, delay);
+
+        // Now send a single byte, holding the address shifted left and a
+        // 1 to indicate a read. A nak indicates that the device has not
+        // responded. SDA should become an output.
+        if (0 != cyg_i2c_bitbang_send_byte(dev->i2c_bus, dev, delay, (dev->i2c_address << 1) | 0x01, true) ) {
+            // Get the bus back in a consistent state
+            DEBUG("i2c bitbang rx, no device has responded to address %02x\n", dev->i2c_address); 
+            cyg_i2c_bitbang_send_stop(dev->i2c_bus, delay);
+            return 0;
+        }
+        // The device has ack'd so we can continue sending
+    }
+
+    // The device has ack'd. Read the number of bytes specified. The
+    // device cannot stop sending, although it may clock-stretch. For
+    // the last byte we usually we want to NAK and SDA should become
+    // an input, but if the data stream is variable length with e.g.
+    // the first byte indicating the length then we want to support
+    // partial reads.
+    for (result = 0; result < count; result++) {
+        buf[result] = cyg_i2c_bitbang_read_byte(dev->i2c_bus, dev, delay, (send_nak && (result == (count - 1))) ? true : false);
+    }
+
+    // At this point we have SCL low and SDA an indeterminate output
+    if (send_stop) {
+        cyg_i2c_bitbang_send_stop(dev->i2c_bus, delay);
+    }
+
+    DEBUG("i2c bitbang rx, read %d bytes\n", result);
+    return result;
+}
+
+extern "C" void
+cyg_i2c_bitbang_stop(const cyg_i2c_device* dev)
+{
+    // The bit-bang code works in terms of us delays rather than ns
+    cyg_uint32      delay = (0 == dev->i2c_delay) ? 1 : (dev->i2c_delay + 1999) / 2000;
+    cyg_i2c_bitbang_send_stop(dev->i2c_bus, delay);
+}



Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]