* 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).