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