summaryrefslogtreecommitdiff
path: root/ecos/packages/devs/i2c/arm/lpc2xxx
diff options
context:
space:
mode:
Diffstat (limited to 'ecos/packages/devs/i2c/arm/lpc2xxx')
-rw-r--r--ecos/packages/devs/i2c/arm/lpc2xxx/current/ChangeLog40
-rw-r--r--ecos/packages/devs/i2c/arm/lpc2xxx/current/cdl/i2c_lpc2xxx.cdl96
-rw-r--r--ecos/packages/devs/i2c/arm/lpc2xxx/current/include/i2c_lpc2xxx.h143
-rw-r--r--ecos/packages/devs/i2c/arm/lpc2xxx/current/src/i2c_lpc2xxx.c491
4 files changed, 770 insertions, 0 deletions
diff --git a/ecos/packages/devs/i2c/arm/lpc2xxx/current/ChangeLog b/ecos/packages/devs/i2c/arm/lpc2xxx/current/ChangeLog
new file mode 100644
index 0000000..b0a29e6
--- /dev/null
+++ b/ecos/packages/devs/i2c/arm/lpc2xxx/current/ChangeLog
@@ -0,0 +1,40 @@
+2008-09-16 Uwe Kindler <uwe_kindler@web.de>
+
+ * src/i2c_lpc2xxx.c: Implemented support for multiple I2C busses
+ Call DSR only if there is something to signal. Removed
+ declaration of I2C bus via CYG_I2C_BUS() macro - this belongs in
+ the platform HAL.
+
+ * include/i2c_lpc2xxx.h: New file with macro definition of macro
+ CYG_LPC2XXX_I2C_BUS() for I2C bus declaration by platform HAL
+
+ * cdl/i2c_lpc2xxx.cdl: Added CYGHWR_DEVS_I2C_ARM_LPC2XXX_MULTIPLE_BUSES
+ configuration option to support multiple I2C busses
+
+2007-07-12 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
+
+ * lpc2xxx: driver for on-chip I2C unit
+
+//===========================================================================
+// ####GPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+//
+// This program 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.
+//
+// This program 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 this program; if not, write to the
+// Free Software Foundation, Inc., 51 Franklin Street,
+// Fifth Floor, Boston, MA 02110-1301, USA.
+// -------------------------------------------
+// ####GPLCOPYRIGHTEND####
+//===========================================================================
diff --git a/ecos/packages/devs/i2c/arm/lpc2xxx/current/cdl/i2c_lpc2xxx.cdl b/ecos/packages/devs/i2c/arm/lpc2xxx/current/cdl/i2c_lpc2xxx.cdl
new file mode 100644
index 0000000..faff590
--- /dev/null
+++ b/ecos/packages/devs/i2c/arm/lpc2xxx/current/cdl/i2c_lpc2xxx.cdl
@@ -0,0 +1,96 @@
+# ====================================================================
+#
+# i2c_lpc2xxx.cdl
+#
+# eCos LPC2xxx I2C configuration data
+#
+# ====================================================================
+## ####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 2008 Free Software Foundation, Inc.
+##
+## 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.,
+## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2.
+##
+## 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): Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
+# Contributors: Uwe Kindler <uwe_kindler@web.de>
+# Date: 2007-07-12
+#
+#####DESCRIPTIONEND####
+#
+# ====================================================================
+
+cdl_package CYGPKG_DEVS_I2C_ARM_LPC2XXX {
+ display "I2C driver for LPC2xxx family of ARM controllers"
+
+ parent CYGPKG_IO_I2C
+ active_if CYGPKG_IO_I2C
+ active_if CYGPKG_HAL_ARM_LPC2XXX || CYGPKG_HAL_ARM_LPC24XX
+
+ description "
+ This package provides a generic I2C device driver for the on-chip
+ I2C peripherals in LPX2xxx processors."
+
+ include_dir cyg/io
+ compile i2c_lpc2xxx.c
+
+ cdl_option CYGHWR_DEVS_I2C_ARM_LPC2XXX_MULTIPLE_BUSES {
+ display "Target hardware may have multiple I2C buses"
+ flavor bool
+ default_value 0
+ description "
+ The LPC2xxx I2C driver can support multiple I2C bus devices. By
+ default the driver assumes only a single device is present and will
+ optimize for that case, using constant definitions provided by the
+ platform HAL rather than per-device structure fields. If the hardware
+ has multiple I2C bus devices, or if a singleton bus is instantiated
+ by some other package and hence the platform HAL cannot provide the
+ necessary definitions, then this option should be enabled."
+ }
+
+ cdl_option CYGPKG_DEVS_I2C_ARM_LPC2XXX_DEBUG_LEVEL {
+ display "Driver debug output level"
+ flavor data
+ legal_values {0 1}
+ default_value 0
+ description "
+ This option specifies the level of debug data output by
+ the LPC2XXX I2C device driver. A value of 0 signifies
+ no debug data output; 1 signifies normal debug data
+ output. The generic eCos I2C driver functions do not return any
+ error information if a I2C transfer failed. If this option is
+ 1 then the driver prints the status flags if a transfer failed
+ or was aborted. It prints the status flags in case of a missing
+ data or address acknowledge, in case of lost arbitration and in
+ case of a bus error. A missing acknowledge does not realy indicate
+ an error and may be part of a normal I2C communication. Therefore
+ this option should only be >0 for debug reasons."
+ }
+}
diff --git a/ecos/packages/devs/i2c/arm/lpc2xxx/current/include/i2c_lpc2xxx.h b/ecos/packages/devs/i2c/arm/lpc2xxx/current/include/i2c_lpc2xxx.h
new file mode 100644
index 0000000..7a942ba
--- /dev/null
+++ b/ecos/packages/devs/i2c/arm/lpc2xxx/current/include/i2c_lpc2xxx.h
@@ -0,0 +1,143 @@
+#ifndef CYGONCE_I2C_LPC2XXX_H
+#define CYGONCE_I2C_LPC2XXX_H
+//==========================================================================
+//
+// devs/i2c/arm/lpc2xxx/current/src/i2c_lpc2xxx.h
+//
+// I2C driver for NXP LPC2xxx ARM processors
+//
+//==========================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2008 Free Software Foundation, Inc.
+//
+// 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.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2.
+//
+// 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): Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
+// Contributors: Uwe Kindler <uwe_kindler@web.de>
+// Date: 2008-09-11
+// Description: I2C driver for LPC2xxx
+//####DESCRIPTIONEND####
+//==========================================================================
+
+
+//==========================================================================
+// INCLUDES
+//==========================================================================
+#include <pkgconf/devs_i2c_arm_lpc2xxx.h>
+#include <cyg/infra/cyg_type.h>
+#include <cyg/hal/drv_api.h>
+
+
+//==========================================================================
+// Single I2C bus sepecififc data
+//==========================================================================
+typedef struct cyg_lpc2xxx_i2c_extra {
+#ifdef CYGHWR_DEVS_I2C_ARM_LPC2XXX_MULTIPLE_BUSES
+ // Put statically initialized fields first.
+ cyg_uint32 i2c_base; // Per-bus h/w details
+ cyg_vector_t i2c_isrvec;
+ int i2c_isrpri;
+ cyg_uint32 i2c_pclk; // peripheral clock of
+ cyg_uint32 i2c_bus_freq;// I2C bus frequency (e.g. 100 kHz, 400 kHz)
+#endif // CYGHWR_DEVS_I2C_ARM_LPC2XXX_MULTIPLE_BUSES
+
+ cyg_uint8 i2c_addr;
+ cyg_uint32 i2c_count;
+ const cyg_uint8* i2c_txbuf;
+ cyg_uint8* i2c_rxbuf;
+ cyg_bool i2c_rxnak;
+
+ cyg_uint32 i2c_flag;
+ cyg_uint32 i2c_delay;
+
+ cyg_drv_mutex_t i2c_lock; // For synchronizing between DSR and foreground
+ cyg_drv_cond_t i2c_wait;
+ cyg_handle_t i2c_interrupt_handle;// For initializing the interrupt
+ cyg_interrupt i2c_interrupt_data;
+} cyg_lpc2xxx_i2c_extra;
+
+
+//==========================================================================
+// I2C driver interface
+//==========================================================================
+externC void cyg_lpc2xxx_i2c_init(struct cyg_i2c_bus*);
+externC cyg_uint32 cyg_lpc2xxx_i2c_tx(const cyg_i2c_device*,
+ cyg_bool, const cyg_uint8*,
+ cyg_uint32, cyg_bool);
+externC cyg_uint32 cyg_lpc2xxx_i2c_rx(const cyg_i2c_device*,
+ cyg_bool, cyg_uint8*,
+ cyg_uint32, cyg_bool, cyg_bool);
+externC void cyg_lpc2xxx_i2c_stop(const cyg_i2c_device*);
+
+//==========================================================================
+// I2C bus declaration macros
+//=========================================================================
+#ifdef CYGHWR_DEVS_I2C_ARM_LPC2XXX_MULTIPLE_BUSES
+# define CYG_LPC2XXX_I2C_BUS(_name_, _init_fn_, _base_, _isr_vec_, _isr_pri_, \
+ _pclk_, _i2c_bus_freq_) \
+ static cyg_lpc2xxx_i2c_extra _name_ ## _extra = { \
+ i2c_base : _base_, \
+ i2c_isrvec : _isr_vec_, \
+ i2c_isrpri : _isr_pri_, \
+ i2c_pclk : _pclk_, \
+ i2c_bus_freq : _i2c_bus_freq_, \
+ i2c_count : 0, \
+ i2c_txbuf : NULL, \
+ i2c_rxbuf : NULL, \
+ i2c_flag : 0 \
+ } ; \
+ CYG_I2C_BUS(_name_, \
+ _init_fn_, \
+ &cyg_lpc2xxx_i2c_tx, \
+ &cyg_lpc2xxx_i2c_rx, \
+ &cyg_lpc2xxx_i2c_stop, \
+ (void*) & ( _name_ ## _extra)) ;
+
+#else // !CYGHWR_DEVS_I2C_ARM_LPC2XXX_MULTIPLE_BUSES
+# define CYG_LPC2XXX_I2C_BUS(_name_, _init_fn_, _base_, _isr_vec_, _isr_pri_, \
+ _pclk_, _i2c_bus_freq_) \
+ static cyg_lpc2xxx_i2c_extra _name_ ## _extra = { \
+ i2c_count : 0, \
+ i2c_txbuf : NULL, \
+ i2c_rxbuf : NULL, \
+ i2c_flag : 0 \
+ } ; \
+ CYG_I2C_BUS(_name_, \
+ _init_fn_, \
+ cyg_lpc2xxx_i2c_tx, \
+ cyg_lpc2xxx_i2c_rx, \
+ cyg_lpc2xxx_i2c_stop, \
+ (void*) & ( _name_ ## _extra)) ;
+#endif // CYGHWR_DEVS_I2C_ARM_LPC2XXX_MULTIPLE_BUSES
+
+//-----------------------------------------------------------------------------
+#endif // #endif CYGONCE_I2C_LPC2XXX_H
diff --git a/ecos/packages/devs/i2c/arm/lpc2xxx/current/src/i2c_lpc2xxx.c b/ecos/packages/devs/i2c/arm/lpc2xxx/current/src/i2c_lpc2xxx.c
new file mode 100644
index 0000000..fff6a3a
--- /dev/null
+++ b/ecos/packages/devs/i2c/arm/lpc2xxx/current/src/i2c_lpc2xxx.c
@@ -0,0 +1,491 @@
+//==========================================================================
+//
+// i2c_lpc2xxx.c
+//
+// I2C driver for LPC2xxx
+//
+//==========================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2008 Free Software Foundation, Inc.
+//
+// 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.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2.
+//
+// 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): Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
+// Contributors: Uwe Kindler <uwe_kindler@web.de>
+// Date: 2007-07-12
+// Purpose:
+// Description:
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+
+//==========================================================================
+// INCLUDES
+//==========================================================================
+#include <pkgconf/system.h>
+#include <pkgconf/devs_i2c_arm_lpc2xxx.h>
+
+#include <cyg/infra/cyg_type.h>
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/diag.h>
+#include <cyg/io/i2c.h>
+#include <cyg/io/i2c_lpc2xxx.h>
+#include <cyg/hal/hal_arch.h>
+#include <cyg/hal/hal_io.h>
+#include <cyg/hal/hal_intr.h>
+#include <cyg/hal/drv_api.h>
+
+//
+// According to the Users Manual the LPC2xxx I2C module is very
+// similar to the I2C module of the Philips 8xC552/556 controllers. I
+// guess it is used in other Philips/NXP controllers, too. Using these
+// macros should make it easier to split off the common parts of the
+// driver once it's necessary.
+//
+// Optimize for the case of a single bus device, while still allowing
+// multiple devices.
+//
+#ifndef CYGHWR_DEVS_I2C_ARM_LPC2XXX_MULTIPLE_BUSES
+# define I2C_BASE(_extra_) (cyg_uint8*)HAL_LPC2XXX_I2C_SINGLETON_BASE
+# define I2C_ISRVEC(_extra_) HAL_LPC2XXX_I2C_SINGLETON_ISRVEC
+# define I2C_ISRPRI(_extra_) HAL_LPC2XXX_I2C_SINGLETON_ISRPRI
+# define I2C_CLK(_extra_) HAL_LPC2XXX_I2C_SINGLETON_CLK
+# define I2C_BUS_FREQ(_extra_) HAL_LPC2XXX_I2C_SINGLETON_BUS_FREQ
+#else
+# define I2C_BASE(_extra_) ((_extra_)->i2c_base)
+# define I2C_ISRVEC(_extra_) ((_extra_)->i2c_isrvec)
+# define I2C_ISRPRI(_extra_) ((_extra_)->i2c_isrpri)
+# define I2C_CLK(_extra_) ((_extra_)->i2c_pclk)
+# define I2C_BUS_FREQ(_extra_) ((_extra_)->i2c_bus_freq)
+#endif // CYGHWR_DEVS_I2C_ARM_LPC2XXX_MULTIPLE_BUSES
+
+#define I2C_XFER 8
+
+#define I2C_CONSET(_extra_) (I2C_BASE(_extra_) + 0x0000)
+#define I2C_CON(_extra_) I2C_CONSET(_extra_)
+#define I2C_STAT(_extra_) (I2C_BASE(_extra_) + 0x0004)
+#define I2C_DAT(_extra_) (I2C_BASE(_extra_) + 0x0008)
+#define I2C_ADR(_extra_) (I2C_BASE(_extra_) + 0x000C)
+#define I2C_SCLH(_extra_) (I2C_BASE(_extra_) + 0x0010)
+#define I2C_SCLL(_extra_) (I2C_BASE(_extra_) + 0x0014)
+#define I2C_CONCLR(_extra_) (I2C_BASE(_extra_) + 0x0018)
+
+#define I2C_R8(r, x) HAL_READ_UINT8 ((r), (x))
+#define I2C_W8(r, x) HAL_WRITE_UINT8 ((r), (x))
+#define I2C_R16(r, x) HAL_READ_UINT16 ((r), (x))
+#define I2C_W16(r, x) HAL_WRITE_UINT16((r), (x))
+
+// Special case for setting/clearing bits in I2C_CON
+#define SET_CON(_extra_, x) I2C_W8(I2C_CONSET(_extra_), (x))
+#define CLR_CON(_extra_, x) I2C_W8(I2C_CONCLR(_extra_), (x))
+
+// I2C_CONSET register bits
+#define CON_AA (1<<2)
+#define CON_SI (1<<3)
+#define CON_STO (1<<4)
+#define CON_STA (1<<5)
+#define CON_EN (1<<6)
+
+
+#define I2C_FLAG_FINISH 1 // transfer finished
+#define I2C_FLAG_ACT 2 // bus still active, no STOP condition send
+#define I2C_FLAG_ERROR (1<<31) // one of the following errors occured:
+#define I2C_FLAG_ADDR (1<<30) // - address was not ACKed
+#define I2C_FLAG_DATA (1<<29) // - data was not ACKed
+#define I2C_FLAG_LOST (1<<28) // - bus arbitration was lost
+#define I2C_FLAG_BUF (1<<27) // - no buffer for reading or writing
+#define I2C_FLAG_UNK (1<<26) // - unknown I2C status
+#define I2C_FLAG_BUS (1<<25) // - bus error
+
+#if CYGPKG_DEVS_I2C_ARM_LPC2XXX_DEBUG_LEVEL > 0
+ #define debug1_printf(args...) diag_printf(args)
+#else
+ #define debug1_printf(args...)
+#endif
+#if CYGPKG_DEVS_I2C_ARM_LPC2XXX_DEBUG_LEVEL > 1
+ #define debug2_printf(args...) diag_printf(args)
+#else
+ #define debug2_printf(args...)
+#endif
+
+//==========================================================================
+// The ISR does the actual work. It is not that much work to justify
+// putting it in the DSR, and it is also not clear whether this would
+// even work. If an error occurs we try to leave the bus in the same
+// state as we would if there was no error.
+//==========================================================================
+static cyg_uint32 lpc2xxx_i2c_isr(cyg_vector_t vec, cyg_addrword_t data)
+{
+ cyg_lpc2xxx_i2c_extra* extra = (cyg_lpc2xxx_i2c_extra*)data;
+ cyg_uint8 status;
+
+ I2C_R8(I2C_STAT(extra), status);
+ switch(status)
+ {
+ case 0x00: // bus error, stop transfer
+ SET_CON(extra, CON_STO);
+ extra->i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_BUS;
+ break;
+
+ case 0x08: // START sent, send Addr+R/W
+ case 0x10: // ReSTART sent, send Addr+R/W
+ CLR_CON(extra, CON_STA);
+ I2C_W8(I2C_DAT(extra), extra->i2c_addr);
+ break;
+
+ case 0x18: // Addr ACKed, send data
+ if(extra->i2c_txbuf == NULL)
+ {
+ extra->i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_BUF;
+ cyg_drv_interrupt_mask_intunsafe(vec);
+ cyg_drv_interrupt_acknowledge(vec);
+ return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
+ }
+ I2C_W8(I2C_DAT(extra), *extra->i2c_txbuf);
+ extra->i2c_txbuf++;
+ break;
+
+ case 0x28: // Data ACKed, send more
+ extra->i2c_count--;
+ if(extra->i2c_count == 0)
+ {
+ extra->i2c_flag = I2C_FLAG_FINISH;
+ cyg_drv_interrupt_mask_intunsafe(vec);
+ cyg_drv_interrupt_acknowledge(vec);
+ return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
+ }
+ I2C_W8(I2C_DAT(extra), *extra->i2c_txbuf);
+ extra->i2c_txbuf++;
+ break;
+
+ case 0x50: // Data ACKed, receive more
+ case 0x58: // Data not ACKed, end reception
+ if(extra->i2c_rxbuf == NULL)
+ {
+ extra->i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_BUF;
+ cyg_drv_interrupt_mask_intunsafe(vec);
+ cyg_drv_interrupt_acknowledge(vec);
+ return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
+ }
+
+ I2C_R8(I2C_DAT(extra), *extra->i2c_rxbuf);
+ extra->i2c_rxbuf++;
+ extra->i2c_count--;
+ // fall through
+
+ case 0x40: // Addr ACKed, receive data
+ if(status == 0x58 || extra->i2c_count == 0)
+ {
+ extra->i2c_flag = I2C_FLAG_FINISH;
+ cyg_drv_interrupt_mask_intunsafe(vec);
+ cyg_drv_interrupt_acknowledge(vec);
+ return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
+ }
+
+ if((extra->i2c_count == 1) && extra->i2c_rxnak)
+ {
+ CLR_CON(extra, CON_AA);
+ }
+ else
+ {
+ SET_CON(extra, CON_AA);
+ }
+ break;
+
+ case 0x20: // Addr not ACKed
+ case 0x48: // Addr not ACKed
+ SET_CON(extra, CON_STO); // tranfer failed - force stop
+ extra->i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_ADDR;
+ cyg_drv_interrupt_mask_intunsafe(vec);
+ cyg_drv_interrupt_acknowledge(vec);
+ break;
+
+ case 0x30: // Data not ACKed
+ SET_CON(extra, CON_STO); // tranfer failed - force stop
+ extra->i2c_count++;
+ extra->i2c_txbuf--;
+ extra->i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_DATA;
+ cyg_drv_interrupt_mask_intunsafe(vec);
+ cyg_drv_interrupt_acknowledge(vec);
+ return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
+ break;
+
+ case 0x38: // Arbitration lost
+ extra->i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_LOST;
+ break;
+
+ default: // lots of unused states
+ extra->i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_UNK;
+ break;
+ } // switch(status)
+
+ CLR_CON(extra, CON_SI);
+ cyg_drv_interrupt_acknowledge(vec);
+
+ //
+ // We need to call the DSR only if there is really something to signal,
+ // that means only if extra->i2c_flag != 0
+ //
+ if (extra->i2c_flag)
+ {
+ return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
+ }
+ else
+ {
+ return CYG_ISR_HANDLED;
+ }
+}
+
+
+//==========================================================================
+// DSR signals data
+//==========================================================================
+static void
+lpc2xxx_i2c_dsr(cyg_vector_t vec, cyg_ucount32 count, cyg_addrword_t data)
+{
+ cyg_lpc2xxx_i2c_extra* extra = (cyg_lpc2xxx_i2c_extra*)data;
+ if(extra->i2c_flag)
+ {
+ cyg_drv_cond_signal(&extra->i2c_wait);
+ }
+}
+
+
+//==========================================================================
+// Initialize driver & hardware state
+//==========================================================================
+void cyg_lpc2xxx_i2c_init(struct cyg_i2c_bus *bus)
+{
+ cyg_lpc2xxx_i2c_extra* extra = (cyg_lpc2xxx_i2c_extra*)bus->i2c_extra;
+ cyg_uint16 duty_cycle;
+
+ cyg_drv_mutex_init(&extra->i2c_lock);
+ cyg_drv_cond_init(&extra->i2c_wait, &extra->i2c_lock);
+ cyg_drv_interrupt_create(I2C_ISRVEC(extra),
+ I2C_ISRPRI(extra),
+ (cyg_addrword_t) extra,
+ &lpc2xxx_i2c_isr,
+ &lpc2xxx_i2c_dsr,
+ &(extra->i2c_interrupt_handle),
+ &(extra->i2c_interrupt_data));
+ cyg_drv_interrupt_attach(extra->i2c_interrupt_handle);
+
+
+ CLR_CON(extra, CON_EN | CON_STA | CON_SI | CON_AA);
+ HAL_WRITE_UINT8(I2C_ADR(extra), 0);
+
+ //
+ // Setup I2C bus frequency
+ //
+ duty_cycle = (I2C_CLK(extra) / I2C_BUS_FREQ(extra)) / 2;
+ HAL_WRITE_UINT16(I2C_SCLL(extra), duty_cycle);
+ HAL_WRITE_UINT16(I2C_SCLH(extra), duty_cycle);
+
+ SET_CON(extra, CON_EN);
+}
+
+
+//==========================================================================
+// transmit a buffer to a device
+//==========================================================================
+cyg_uint32 cyg_lpc2xxx_i2c_tx(const cyg_i2c_device *dev,
+ cyg_bool send_start,
+ const cyg_uint8 *tx_data,
+ cyg_uint32 count,
+ cyg_bool send_stop)
+{
+ cyg_lpc2xxx_i2c_extra* extra =
+ (cyg_lpc2xxx_i2c_extra*)dev->i2c_bus->i2c_extra;
+ extra->i2c_addr = dev->i2c_address << 1;
+ extra->i2c_count = count;
+ extra->i2c_txbuf = tx_data;
+
+ //
+ // for a repeated start the SI bit has to be reset
+ // if we continue a previous transfer, load the next byte
+ //
+ if(send_start)
+ {
+ SET_CON(extra, CON_STA);
+ if (I2C_FLAG_ACT == extra->i2c_flag)
+ {
+ CLR_CON(extra, CON_SI);
+ }
+ }
+ else
+ {
+ HAL_WRITE_UINT8(I2C_DAT(extra), *(extra->i2c_txbuf));
+ extra->i2c_txbuf++;
+ CLR_CON(extra, CON_SI);
+ }
+
+ extra->i2c_flag = 0;
+
+ //
+ // the isr will do most of the work, and the dsr will signal when an
+ // error occured or the transfer finished
+ //
+ cyg_drv_mutex_lock(&extra->i2c_lock);
+ cyg_drv_dsr_lock();
+ cyg_drv_interrupt_unmask(I2C_ISRVEC(extra));
+ while(!(extra->i2c_flag & (I2C_FLAG_FINISH | I2C_FLAG_ERROR)))
+ {
+ cyg_drv_cond_wait(&extra->i2c_wait);
+ }
+ cyg_drv_interrupt_mask(I2C_ISRVEC(extra));
+ cyg_drv_dsr_unlock();
+ cyg_drv_mutex_unlock(&extra->i2c_lock);
+
+ // too bad we have no way to tell the caller
+ if(extra->i2c_flag & I2C_FLAG_ERROR)
+ {
+ debug1_printf("I2C TX error flag: %x\n", extra->i2c_flag);
+ extra->i2c_flag = 0;
+ }
+ else
+ {
+ if(send_stop)
+ {
+ SET_CON(extra, CON_STO);
+ CLR_CON(extra, CON_SI | CON_STA);
+ extra->i2c_flag = 0;
+ }
+ else
+ {
+ extra->i2c_flag = I2C_FLAG_ACT;
+ }
+ }
+
+ count -= extra->i2c_count;
+
+ extra->i2c_addr = 0;
+ extra->i2c_count = 0;
+ extra->i2c_txbuf = NULL;
+
+ return count;
+}
+
+
+//==========================================================================
+// receive into a buffer from a device
+//==========================================================================
+cyg_uint32 cyg_lpc2xxx_i2c_rx(const cyg_i2c_device *dev,
+ cyg_bool send_start,
+ cyg_uint8 *rx_data,
+ cyg_uint32 count,
+ cyg_bool send_nak,
+ cyg_bool send_stop)
+{
+ cyg_lpc2xxx_i2c_extra* extra =
+ (cyg_lpc2xxx_i2c_extra*)dev->i2c_bus->i2c_extra;
+ extra->i2c_addr = (dev->i2c_address << 1) | 0x01;
+ extra->i2c_count = count;
+ extra->i2c_rxbuf = rx_data;
+ extra->i2c_rxnak = send_nak;
+
+ //
+ // for a repeated start the SI bit has to be reset
+ // if we continue a previous transfer, start reception
+ //
+ if(send_start)
+ {
+ SET_CON(extra, CON_STA);
+ if (I2C_FLAG_ACT == extra->i2c_flag)
+ {
+ CLR_CON(extra, CON_SI);
+ }
+ }
+
+ extra->i2c_flag = 0;
+
+ //
+ // the isr will do most of the work, and the dsr will signal when an
+ // error occurred or the transfer finished
+ //
+ cyg_drv_mutex_lock(&extra->i2c_lock);
+ cyg_drv_dsr_lock();
+ cyg_drv_interrupt_unmask(I2C_ISRVEC(extra));
+ while(!(extra->i2c_flag & (I2C_FLAG_FINISH | I2C_FLAG_ERROR)))
+ {
+ cyg_drv_cond_wait(&extra->i2c_wait);
+ }
+ cyg_drv_interrupt_mask(I2C_ISRVEC(extra));
+ cyg_drv_dsr_unlock();
+ cyg_drv_mutex_unlock(&extra->i2c_lock);
+
+ // too bad we have no way to tell the caller
+ if (extra->i2c_flag & I2C_FLAG_ERROR)
+ {
+ diag_printf("I2C RX error flag: %x\n", extra->i2c_flag);
+ extra->i2c_flag = 0;
+ }
+ else
+ {
+ if(send_stop)
+ {
+ SET_CON(extra, CON_STO);
+ CLR_CON(extra, CON_SI | CON_STA);
+ extra->i2c_flag = 0;
+ }
+ else
+ {
+ extra->i2c_flag = I2C_FLAG_ACT;
+ }
+ }
+
+ count -= extra->i2c_count;
+
+ extra->i2c_addr = 0;
+ extra->i2c_count = 0;
+ extra->i2c_rxbuf = NULL;
+
+ return count;
+}
+
+
+//==========================================================================
+// generate a STOP
+//==========================================================================
+void cyg_lpc2xxx_i2c_stop(const cyg_i2c_device *dev)
+{
+ cyg_lpc2xxx_i2c_extra* extra =
+ (cyg_lpc2xxx_i2c_extra*)dev->i2c_bus->i2c_extra;
+ extra = extra; // avoid compiler warning in case of singleton
+ SET_CON(extra, CON_STO);
+ extra->i2c_flag = 0;
+ extra->i2c_count = 0;
+}
+
+//---------------------------------------------------------------------------
+// eof i2c_lpc2xxx.c