public inbox for ecos-patches@sourceware.org
 help / color / mirror / Atom feed
* LPC2xxx I2C patch
@ 2008-09-16  9:57 uwe.kindler
  0 siblings, 0 replies; only message in thread
From: uwe.kindler @ 2008-09-16  9:57 UTC (permalink / raw)
  To: ecos-patches

[-- Attachment #1: Type: text/plain, Size: 463 bytes --]

Hello,

the following patch adds support for multiple I2C busses to the LPC2xxx I2C device driver. Newer LPC2xxx devices like LPC2468 support up to three on-chip I2C modules.

The modified driver was tested with Embedded Artists LPC2468 HAL package.

Regards, Uwe


Dipl. Inf. (FH)
Uwe Kindler
Software Engineering

--

cetoni GmbH
Am Wiesenring 6
D-07554 Korbussen

Tel.: +49 (0) 36602 338 28
Fax:  +49 (0) 36602 338 11
uwe.kindler@cetoni.de
http://www.cetoni.de

[-- Attachment #2: i2c_lpc2xxx.patch --]
[-- Type: application/octet-stream, Size: 37581 bytes --]

diff -ruN ecos_web_cvs/ecos/packages/devs/i2c/arm/lpc2xxx/current/ChangeLog ecos/ecos/packages/devs/i2c/arm/lpc2xxx/current/ChangeLog
--- ecos_web_cvs/ecos/packages/devs/i2c/arm/lpc2xxx/current/ChangeLog	2008-07-12 17:55:31.000000000 +0200
+++ ecos/ecos/packages/devs/i2c/arm/lpc2xxx/current/ChangeLog	2008-09-16 11:36:33.000000000 +0200
@@ -1,3 +1,15 @@
+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 into 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
diff -ruN ecos_web_cvs/ecos/packages/devs/i2c/arm/lpc2xxx/current/cdl/i2c_lpc2xxx.cdl ecos/ecos/packages/devs/i2c/arm/lpc2xxx/current/cdl/i2c_lpc2xxx.cdl
--- ecos_web_cvs/ecos/packages/devs/i2c/arm/lpc2xxx/current/cdl/i2c_lpc2xxx.cdl	2008-07-12 17:55:31.000000000 +0200
+++ ecos/ecos/packages/devs/i2c/arm/lpc2xxx/current/cdl/i2c_lpc2xxx.cdl	2008-09-16 11:39:45.000000000 +0200
@@ -2,13 +2,13 @@
 #
 #      i2c_lpc2xxx.cdl
 #
-#      I2C driver for LPC2xxx
+#      eCos LPC2xxx I2C configuration data
 #
 # ====================================================================
 #####ECOSGPLCOPYRIGHTBEGIN####
 ## -------------------------------------------
 ## This file is part of eCos, the Embedded Configurable Operating System.
-## Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+## 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
@@ -38,7 +38,7 @@
 ######DESCRIPTIONBEGIN####
 #
 # Author(s):      Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
-# Contributors:   
+# Contributors:   Uwe Kindler <uwe_kindler@web.de>
 # Date:           2007-07-12
 #
 #####DESCRIPTIONEND####
@@ -50,10 +50,26 @@
     
     parent      CYGPKG_IO_I2C
     active_if   CYGPKG_IO_I2C
-    active_if   CYGPKG_HAL_ARM_LPC2XXX
-
-    description "This package provides a driver for the I2C module found in
-                 Philips LPC2xxx controllers."
+    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
-}
\ No newline at end of file
+    
+    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."
+    }
+}
diff -ruN ecos_web_cvs/ecos/packages/devs/i2c/arm/lpc2xxx/current/include/i2c_lpc2xxx.h ecos/ecos/packages/devs/i2c/arm/lpc2xxx/current/include/i2c_lpc2xxx.h
--- ecos_web_cvs/ecos/packages/devs/i2c/arm/lpc2xxx/current/include/i2c_lpc2xxx.h	1970-01-01 01:00:00.000000000 +0100
+++ ecos/ecos/packages/devs/i2c/arm/lpc2xxx/current/include/i2c_lpc2xxx.h	2008-09-12 12:44:52.000000000 +0200
@@ -0,0 +1,138 @@
+#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.,
+// 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):     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 -ruN ecos_web_cvs/ecos/packages/devs/i2c/arm/lpc2xxx/current/src/i2c_lpc2xxx.c ecos/ecos/packages/devs/i2c/arm/lpc2xxx/current/src/i2c_lpc2xxx.c
--- ecos_web_cvs/ecos/packages/devs/i2c/arm/lpc2xxx/current/src/i2c_lpc2xxx.c	2008-07-12 17:55:31.000000000 +0200
+++ ecos/ecos/packages/devs/i2c/arm/lpc2xxx/current/src/i2c_lpc2xxx.c	2008-09-16 11:49:17.000000000 +0200
@@ -8,7 +8,7 @@
 //####ECOSGPLCOPYRIGHTBEGIN####
 // -------------------------------------------
 // This file is part of eCos, the Embedded Configurable Operating System.
-// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+// 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
@@ -41,7 +41,7 @@
 //#####DESCRIPTIONBEGIN####
 //
 // Author(s):    Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
-// Contributors: 
+// Contributors: Uwe Kindler <uwe_kindler@web.de>
 // Date:         2007-07-12
 // Purpose:      
 // Description:  
@@ -50,365 +50,431 @@
 //
 //==========================================================================
 
+
+//==========================================================================
+//                                 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.
- */
-
-#define I2C_XFER        8
-#define I2C_INTR        CYGNUM_HAL_INTERRUPT_I2C
-#define I2C_FREQ        (CYGNUM_HAL_ARM_LPC2XXX_CLOCK_SPEED / \
-                         CYGNUM_HAL_ARM_LPC2XXX_VPBDIV)
-#define I2C_BASE        CYGARC_HAL_LPC2XXX_REG_I2_BASE
-
-#define I2C_CONSET      CYGARC_HAL_LPC2XXX_REG_I2CONSET
-#define I2C_CONCLR      CYGARC_HAL_LPC2XXX_REG_I2CONCLR
-#define I2C_CON         I2C_CONSET
-#define I2C_STAT        CYGARC_HAL_LPC2XXX_REG_I2STAT
-#define I2C_DAT         CYGARC_HAL_LPC2XXX_REG_I2DAT
-#define I2C_ADR         CYGARC_HAL_LPC2XXX_REG_I2ADR
-#define I2C_SCLH        CYGARC_HAL_LPC2XXX_REG_I2SCLH
-#define I2C_SCLL        CYGARC_HAL_LPC2XXX_REG_I2SCLL
-
-#define I2C_R8(r, x)    HAL_READ_UINT8  (I2C_BASE + (r), (x))
-#define I2C_W8(r, x)    HAL_WRITE_UINT8 (I2C_BASE + (r), (x))
-#define I2C_R16(r, x)   HAL_READ_UINT16 (I2C_BASE + (r), (x))
-#define I2C_W16(r, x)   HAL_WRITE_UINT16(I2C_BASE + (r), (x))
-
-/* Special case for setting/clearing bits in I2C_CON */
-#define SET_CON(x)      I2C_W8(I2C_CONSET, (x))
-#define CLR_CON(x)      I2C_W8(I2C_CONCLR, (x))
-
-#define CON_EN          CYGARC_HAL_LPC2XXX_REG_I2CONSET_I2EN
-#define CON_STA         CYGARC_HAL_LPC2XXX_REG_I2CONSET_STA
-#define CON_STO         CYGARC_HAL_LPC2XXX_REG_I2CONSET_STO
-#define CON_SI          CYGARC_HAL_LPC2XXX_REG_I2CONSET_SI
-#define CON_AA          CYGARC_HAL_LPC2XXX_REG_I2CONSET_AA
-
-static cyg_uint8        i2c_addr;
-static cyg_uint32       i2c_count = 0;
-static const cyg_uint8* i2c_txbuf = NULL;
-static cyg_uint8*       i2c_rxbuf = NULL;
-static cyg_bool         i2c_rxnak;
-
-volatile
-static cyg_uint32       i2c_flag = 0;
-static cyg_uint32       i2c_delay;
-
-static cyg_drv_mutex_t  i2c_lock;
-static cyg_drv_cond_t   i2c_wait;
-static cyg_handle_t     i2c_hand;
-static cyg_interrupt    i2c_data;
-
-#define I2C_FLAG_FINISH 1       /* transfer finished                        */
-#define I2C_FLAG_ACT    2       /* bus still active, no STOP condition sent */
-#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                     */
-
-/*
- * set up I2C bus timing
- * I2C clock period is in PCLK ticks
- */
-static void
-i2c_lpc2xxx_delay(cyg_uint32 delay)
-{
-  cyg_uint32 period;
-  
-  if(delay == i2c_delay)
-    return;
-  
-  period = I2C_FREQ / (1000000000 / delay);
-  period /= 2;
-  
-  I2C_W16(I2C_SCLL, period);
-  I2C_W16(I2C_SCLH, period);
-  i2c_delay = delay;
-}
+//
+// 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
 
-/*
- * 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
-i2c_lpc2xxx_isr(cyg_vector_t vec, cyg_addrword_t data)
+ 
+//==========================================================================
+// 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_uint8 status;
-  I2C_R8(I2C_STAT, status);
-  
-  switch(status) {
-    case 0x08: /* START sent, send Addr+R/W   */
-    case 0x10: /* ReSTART sent, send Addr+R/W */
-      CLR_CON(CON_STA);
-      I2C_W8(I2C_DAT, i2c_addr);
-      break;
+    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       */
-    case 0x28: /* Data ACKed, send more       */
-      if(i2c_count == 0) {
-        i2c_flag = I2C_FLAG_FINISH;
-        cyg_drv_interrupt_mask_intunsafe(vec);
-        cyg_drv_interrupt_acknowledge(vec);
-        return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
-      }
+    case 0x18: // Addr ACKed, send data
+    case 0x28: // Data ACKed, send more
+         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;
+         }
       
-      if(i2c_txbuf == NULL) {
-        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;
-      }
+         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, *i2c_txbuf);
-      i2c_txbuf++;
-      i2c_count--;
-      break;
+         I2C_W8(I2C_DAT(extra), *extra->i2c_txbuf);
+         extra->i2c_txbuf++;
+         extra->i2c_count--;
+         break;
       
-    case 0x50: /* Data ACKed, receive more    */
-    case 0x58: /* Data not ACKed, end reception */
-      if(i2c_rxbuf == NULL) {
-        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;
-      }
+    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, *i2c_rxbuf);
-      i2c_rxbuf++;
-      i2c_count--;
-    case 0x40: /* Addr ACKed, receive data    */
-      if(status == 0x58 || i2c_count == 0) {
-        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_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(i2c_count == 1 && i2c_rxnak)
-        CLR_CON(CON_AA);
-      else    
-        SET_CON(CON_AA);
-      break;
+         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:
-      i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_ADDR;
-      cyg_drv_interrupt_mask_intunsafe(vec);
-      cyg_drv_interrupt_acknowledge(vec);
-      return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
-      break;
-    case 0x30: /* Data not ACKed */
-      i2c_count++;
-      i2c_txbuf--;
-      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 */
-      i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_LOST;
-      break;
-    default: /* lots of unused states... */
-      i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_UNK;
-      break;
-  }
-  
-  CLR_CON(CON_SI);
-  cyg_drv_interrupt_acknowledge(vec);
-  return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
+    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
-i2c_lpc2xxx_dsr(cyg_vector_t vec, cyg_ucount32 count, cyg_addrword_t data)
+lpc2xxx_i2c_dsr(cyg_vector_t vec, cyg_ucount32 count, cyg_addrword_t data)
 {
-  if(i2c_flag)
-    cyg_drv_cond_signal(&i2c_wait);
+    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
- */
-static void
-i2c_lpc2xxx_init(struct cyg_i2c_bus *bus)
+
+//==========================================================================
+// Initialize driver & hardware state
+//==========================================================================
+void cyg_lpc2xxx_i2c_init(struct cyg_i2c_bus *bus)
 {
-  cyg_uint32 addr, tmp;
+    cyg_lpc2xxx_i2c_extra* extra = (cyg_lpc2xxx_i2c_extra*)bus->i2c_extra;
+    cyg_uint16             duty_cycle;
   
-  /* enable I2C pins */
-  addr = CYGARC_HAL_LPC2XXX_REG_PIN_BASE + CYGARC_HAL_LPC2XXX_REG_PINSEL0;
-  HAL_READ_UINT32(addr, tmp);
-  tmp |= 0x50;
-  HAL_WRITE_UINT32(addr, tmp);
-  
-  cyg_drv_mutex_init(&i2c_lock);
-  cyg_drv_cond_init(&i2c_wait, &i2c_lock);
-  cyg_drv_interrupt_create(I2C_INTR, 0, (cyg_addrword_t) 0, &i2c_lpc2xxx_isr,
-                           &i2c_lpc2xxx_dsr, &i2c_hand, &i2c_data);
-  cyg_drv_interrupt_attach(i2c_hand);
-  
-  CLR_CON(CON_EN | CON_STA | CON_SI | CON_AA);
-  I2C_W8(I2C_ADR, 0);
-  SET_CON(CON_EN);
+    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
- */
-static cyg_uint32
-i2c_lpc2xxx_tx(const cyg_i2c_device *dev, 
-               cyg_bool send_start, 
-               const cyg_uint8 *tx_data, 
-               cyg_uint32 count, 
-               cyg_bool send_stop)
+
+//==========================================================================
+// 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)
 {
-  i2c_lpc2xxx_delay(dev->i2c_delay);
-  
-  i2c_addr  = dev->i2c_address << 1;
-  i2c_count = count;
-  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 && i2c_flag == I2C_FLAG_ACT) {
-    SET_CON(CON_STA);
-    CLR_CON(CON_SI);
-  } else if(send_start) {
-    SET_CON(CON_STA);
-  } else {
-    I2C_W8(I2C_DAT, *i2c_txbuf);
-    i2c_txbuf++;
-    i2c_count--;
-    CLR_CON(CON_SI);
-  }
-  
-  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(&i2c_lock);
-  cyg_drv_dsr_lock();
-  cyg_drv_interrupt_unmask(I2C_INTR);
-  while(!(i2c_flag & (I2C_FLAG_FINISH|I2C_FLAG_ERROR)))
-    cyg_drv_cond_wait(&i2c_wait);
-  cyg_drv_interrupt_mask(I2C_INTR);
-  cyg_drv_dsr_unlock();
-  cyg_drv_mutex_unlock(&i2c_lock);
-  
-  /* too bad we have no way to tell the caller */
-  if(i2c_flag & I2C_FLAG_ERROR)
-    diag_printf("I2C TX error flag: %x\n", i2c_flag);
-  
-  if(send_stop) {
-    SET_CON(CON_STO);
-    CLR_CON(CON_SI | CON_STA);
-  } else  i2c_flag = I2C_FLAG_ACT;
-  
-  count -= i2c_count;
-  
-  i2c_addr  = 0;
-  i2c_count = 0;
-  i2c_txbuf = NULL;
+    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)
+    {
+        diag_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;
+    return count;
 }
 
-/*
- * receive into a buffer from a device
- */
-static cyg_uint32
-i2c_lpc2xxx_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)
+ 
+//==========================================================================
+// 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)
 {
-  i2c_lpc2xxx_delay(dev->i2c_delay);
-  
-  i2c_addr  = dev->i2c_address << 1 | 1;
-  i2c_count = count;
-  i2c_rxbuf = rx_data;
-  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 && i2c_flag == I2C_FLAG_ACT) {
-    SET_CON(CON_STA);
-    CLR_CON(CON_SI);
-  } else if(send_start)
-    SET_CON(CON_STA);
-  
-  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(&i2c_lock);
-  cyg_drv_dsr_lock();
-  cyg_drv_interrupt_unmask(I2C_INTR);
-  while(!(i2c_flag & (I2C_FLAG_FINISH|I2C_FLAG_ERROR)))
-    cyg_drv_cond_wait(&i2c_wait);
-  cyg_drv_interrupt_mask(I2C_INTR);
-  cyg_drv_dsr_unlock();
-  cyg_drv_mutex_unlock(&i2c_lock);
-  
-  /* too bad we have no way to tell the caller */
-  if(i2c_flag & I2C_FLAG_ERROR)
-    diag_printf("I2C RX error flag: %x\n", i2c_flag);
-  
-  if(send_stop) {
-    SET_CON(CON_STO);
-    CLR_CON(CON_SI | CON_STA);
-  } else  i2c_flag = I2C_FLAG_ACT;
-  
-  count -= i2c_count;
-  
-  i2c_addr  = 0;
-  i2c_count = 0;
-  i2c_rxbuf = NULL;
-  
-  return count;
+    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 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)
+    {
+        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
- */
-static void
-i2c_lpc2xxx_stop(const cyg_i2c_device *dev)
+//==========================================================================
+//  generate a STOP
+//==========================================================================
+void cyg_lpc2xxx_i2c_stop(const cyg_i2c_device *dev)
 {
-  SET_CON(CON_STO);
+    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;
 }
 
-CYG_I2C_BUS(cyg_i2c_lpc2xxx_bus,
-            &i2c_lpc2xxx_init,
-            &i2c_lpc2xxx_tx,
-            &i2c_lpc2xxx_rx,
-            &i2c_lpc2xxx_stop,
-            (void *) 0);
+//---------------------------------------------------------------------------
+// eof i2c_lpc2xxx.c

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2008-09-16  9:57 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-09-16  9:57 LPC2xxx I2C patch uwe.kindler

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).