# HG changeset patch # User asl # Date 1222111146 0 # Node ID cec45c19fdcc6ad6507486f7dea473b08e69b563 # Parent 19e4b89dda340a71ccde1d963bc777bf9e89b39f * 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 diff --git a/packages/devs/i2c/arm/lpc2xxx/current/ChangeLog b/packages/devs/i2c/arm/lpc2xxx/current/ChangeLog --- a/packages/devs/i2c/arm/lpc2xxx/current/ChangeLog +++ b/packages/devs/i2c/arm/lpc2xxx/current/ChangeLog @@ -1,3 +1,16 @@ +2008-09-16 Uwe Kindler + + * 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 * lpc2xxx: driver for on-chip I2C unit diff --git a/packages/devs/i2c/arm/lpc2xxx/current/cdl/i2c_lpc2xxx.cdl b/packages/devs/i2c/arm/lpc2xxx/current/cdl/i2c_lpc2xxx.cdl --- a/packages/devs/i2c/arm/lpc2xxx/current/cdl/i2c_lpc2xxx.cdl +++ b/packages/devs/i2c/arm/lpc2xxx/current/cdl/i2c_lpc2xxx.cdl @@ -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 -# Contributors: +# Contributors: Uwe Kindler # Date: 2007-07-12 # #####DESCRIPTIONEND#### @@ -50,10 +50,45 @@ cdl_package CYGPKG_DEVS_I2C_ARM_LPC2XXX parent CYGPKG_IO_I2C active_if CYGPKG_IO_I2C - active_if CYGPKG_HAL_ARM_LPC2XXX + active_if CYGPKG_HAL_ARM_LPC2XXX || CYGPKG_HAL_ARM_LPC24XX - description "This package provides a driver for the I2C module found in - Philips LPC2xxx controllers." - + 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." + } + + 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/packages/devs/i2c/arm/lpc2xxx/current/include/i2c_lpc2xxx.h b/packages/devs/i2c/arm/lpc2xxx/current/include/i2c_lpc2xxx.h new file mode 100644 --- /dev/null +++ b/packages/devs/i2c/arm/lpc2xxx/current/include/i2c_lpc2xxx.h @@ -0,0 +1,141 @@ +#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 +// Contributors: Uwe Kindler +// Date: 2008-09-11 +// Description: I2C driver for LPC2xxx +//####DESCRIPTIONEND#### +//========================================================================== + + +//========================================================================== +// INCLUDES +//========================================================================== +#include +#include +#include + + +//========================================================================== +// 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/packages/devs/i2c/arm/lpc2xxx/current/src/i2c_lpc2xxx.c b/packages/devs/i2c/arm/lpc2xxx/current/src/i2c_lpc2xxx.c --- a/packages/devs/i2c/arm/lpc2xxx/current/src/i2c_lpc2xxx.c +++ b/packages/devs/i2c/arm/lpc2xxx/current/src/i2c_lpc2xxx.c @@ -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 -// Contributors: +// Contributors: Uwe Kindler // Date: 2007-07-12 // Purpose: // Description: @@ -50,365 +50,443 @@ // //========================================================================== + +//========================================================================== +// INCLUDES +//========================================================================== #include #include #include +#include #include #include +#include #include #include #include #include -/* - * 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 +// +// 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_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_XFER 8 -#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)) +#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) -/* 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 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)) -#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 +// 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)) -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; +// 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) -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 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 -#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 */ +#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 -/* - * set up I2C bus timing - * I2C clock period is in PCLK ticks - */ -static void -i2c_lpc2xxx_delay(cyg_uint32 delay) +//========================================================================== +// 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_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; -} - -/* - * 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) -{ - 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; - - 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; - } - - 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; - } - - I2C_W8(I2C_DAT, *i2c_txbuf); - i2c_txbuf++; - 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; - } - - 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; - } - - if(i2c_count == 1 && i2c_rxnak) - CLR_CON(CON_AA); - else - SET_CON(CON_AA); - 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 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; -} - -static void -i2c_lpc2xxx_dsr(cyg_vector_t vec, cyg_ucount32 count, cyg_addrword_t data) -{ - if(i2c_flag) - cyg_drv_cond_signal(&i2c_wait); -} - -/* - * Initialize driver & hardware state - */ -static void -i2c_lpc2xxx_init(struct cyg_i2c_bus *bus) -{ - cyg_uint32 addr, tmp; - - /* 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); -} - -/* - * 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) -{ - 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); - } + 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) - 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; - - 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) -{ - 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; - + 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; + } } -/* - * generate a STOP - */ +//========================================================================== +// DSR signals data +//========================================================================== static void -i2c_lpc2xxx_stop(const cyg_i2c_device *dev) +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) { - SET_CON(CON_STO); + 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); } -CYG_I2C_BUS(cyg_i2c_lpc2xxx_bus, - &i2c_lpc2xxx_init, - &i2c_lpc2xxx_tx, - &i2c_lpc2xxx_rx, - &i2c_lpc2xxx_stop, - (void *) 0); + +//========================================================================== +// 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