Mercurial > ecos
changeset 2263:b79bf6f1fe6b
Add M68K/MCF52xx coldfire I2C device driver
| author | bartv |
|---|---|
| date | Sun, 06 Aug 2006 22:34:05 +0000 |
| parents | c393118a9636 |
| children | af00278f385c |
| files | packages/ChangeLog packages/devs/i2c/m68k/mcf52xx/current/ChangeLog packages/devs/i2c/m68k/mcf52xx/current/cdl/i2c_mcf52xx.cdl packages/devs/i2c/m68k/mcf52xx/current/doc/mcf52xx_i2c.sgml packages/devs/i2c/m68k/mcf52xx/current/include/i2c_mcf52xx.h packages/devs/i2c/m68k/mcf52xx/current/src/i2c_mcf52xx.c packages/ecos.db |
| diffstat | 7 files changed, 1007 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/packages/ChangeLog +++ b/packages/ChangeLog @@ -1,3 +1,7 @@ +2006-08-06 Bart Veer <bartv@ecoscentric.com> + + * ecos.db: add M68K/MCF52xx ColdFire I2C driver + 2006-07-18 Jonathan Larmour <jifl@eCosCentric.com> * ecos.db: Add AT-HTTPD package from Anthony Tonizzo.
new file mode 100644 --- /dev/null +++ b/packages/devs/i2c/m68k/mcf52xx/current/ChangeLog @@ -0,0 +1,41 @@ +2006-02-28 Bart Veer <bartv@ecoscentric.com> + + * doc/mcf52xx_i2c.sgml, include/i2c_mcf52xx.h: new files + + * src/i2c_mcf52xx.c, cdl/i2c_mcf52xx.cdl: various clean-ups + +2005-10-23 Uwe Kindler <uwe_kindler@web.de> + + * mcf52xx I2C driver package created + +//=========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2005, 2006 eCosCentric Ltd. +// +// 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#### +//===========================================================================
new file mode 100644 --- /dev/null +++ b/packages/devs/i2c/m68k/mcf52xx/current/cdl/i2c_mcf52xx.cdl @@ -0,0 +1,105 @@ +# ==================================================================== +# +# i2c_mcf52xx.cdl +# +# eCos MCF52xx I2C configuration data +# +# ==================================================================== +#####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 2005, 2006 eCosCentric Limited +## +## 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. +## ------------------------------------------- +#####ECOSGPLCOPYRIGHTEND#### +# ==================================================================== +######DESCRIPTIONBEGIN#### +# +# Author(s): Uwe Kindler +# Contributors: Bart Veer +# Date: 2005-10-23 +# +#####DESCRIPTIONEND#### +# ==================================================================== + + +cdl_package CYGPKG_DEVS_I2C_MCF52xx { + display "I2C driver for coldfire MCF52xx family" + + parent CYGPKG_IO_I2C + active_if CYGPKG_IO_I2C + active_if CYGPKG_HAL_M68K_MCF52xx + + description " + This package provides a generic I2C device driver for the on-chip + I2C modules in MCF52xx ColdFire processors." + + include_dir cyg/io + compile i2c_mcf52xx.c + + cdl_option CYGHWR_DEVS_I2C_MCF52xx_MULTIPLE_BUSES { + display "Target hardware may have multiple MCF52xx I2C buses" + flavor bool + default_value 0 + description " + The MCF52xx I2C driver can support multiple I2C bus devices, but + typically the coldfire processor only provides a single device. 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_component CYGPKG_DEVS_I2C_MCF52xx_OPTIONS { + display "I2C driver build options" + flavor none + active_if { CYGINT_DEVS_I2C_MCF52xx_BUS_DEVICES > 0 } + description " + Package specific build options including control over + compiler flags used only in building the MCF52xx I2C + bus driver." + + cdl_option CYGPKG_DEVS_I2C_MCF52xx_CFLAGS_ADD { + display "Additional compiler flags" + flavor data + no_define + default_value { "" } + description " + This option modifies the set of compiler flags for + building the MCF52xx I2C bus driver. These flags are + used in addition to the set of global flags." + } + + cdl_option CYGPKG_DEVS_I2C_MCF52xx_CFLAGS_REMOVE { + display "Suppressed compiler flags" + flavor data + no_define + default_value { "" } + description " + This option modifies the set of compiler flags for + building the MCF52xx I2C bus driver. These flags are + removed from the set of global flags if present." + } + } +}
new file mode 100644 --- /dev/null +++ b/packages/devs/i2c/m68k/mcf52xx/current/doc/mcf52xx_i2c.sgml @@ -0,0 +1,279 @@ +<!-- DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V3.1//EN" --> + +<!-- {{{ Banner --> + +<!-- =============================================================== --> +<!-- --> +<!-- mcf52xx_i2c.sgml --> +<!-- --> +<!-- Documentation for the mcf52xx I2C bus driver --> +<!-- --> +<!-- =============================================================== --> +<!-- ####COPYRIGHTBEGIN#### --> +<!-- --> +<!-- =============================================================== --> +<!-- Copyright (C) 2006 eCosCentric Limited --> +<!-- This material may be distributed only subject to the terms --> +<!-- and conditions set forth in the Open Publication License, v1.0 --> +<!-- or later (the latest version is presently available at --> +<!-- http://www.opencontent.org/openpub/) --> +<!-- Distribution of the work or derivative of the work in any --> +<!-- standard (paper) book form is prohibited unless prior --> +<!-- permission obtained from the copyright holder --> +<!-- =============================================================== --> +<!-- --> +<!-- ####COPYRIGHTEND#### --> +<!-- =============================================================== --> +<!-- #####DESCRIPTIONBEGIN#### --> +<!-- --> +<!-- Author(s): bartv --> +<!-- Contact(s): bartv --> +<!-- Date: 2006/02/19 --> +<!-- Version: 0.01 --> +<!-- --> +<!-- ####DESCRIPTIONEND#### --> +<!-- =============================================================== --> + +<!-- }}} --> + +<part id="devs-i2c-m68k-mcf52xx-part"><title>Motorola MCF52xx ColdFire I<superscript>2</superscript>C Bus Driver</title> + +<refentry id="devs-i2c-m68k-mcf52xx"> + <refmeta> + <refentrytitle>Motorola MCF52xx Coldfire I<superscript>2</superscript>C Bus Driver</refentrytitle> + </refmeta> + <refnamediv> + <refname><varname>CYGPKG_DEVS_I2C_MCF52xx</varname></refname> + <refpurpose>eCos Support for the Motorola Coldfire I<superscript>2</superscript>C Bus</refpurpose> + </refnamediv> + + <refsect1 id="devs-i2c-m68k-mcf52xx-description"><title>Description</title> + <para> +Several processors in the Motorola ColdFire family come with one or +more on-chip I<superscript>2</superscript>C bus devices. This package +provides an eCos I<superscript>2</superscript>C bus driver. It was +originally developed on an MCF5280 but should work with any ColdFire +processor that uses a compatible bus device. The driver implements the +functionality defined by the generic I<superscript>2</superscript>C +package <varname>CYGPKG_IO_I2C</varname>. + </para> + <caution><para> +The hardware does not support DMA or fifos, so usually a transfer will +involve an interrupt for every byte transferred. Since the +I<superscript>2</superscript>C bus typically runs at 100KHz large +transfers will consume much of the available cpu time. + </para></caution> + <para> +This package does not provide any <structname>cyg_i2c_bus</structname> +structures. The number of I<superscript>2</superscript>C buses varies +between ColdFire processors. If multiple buses are available then +exactly which one(s) are in use on a given hardware platform depends +entirely on that platform. The desired I<superscript>2</superscript>C +bus speed also depends on the platform, and there may be other issues +such as how the processor pins should be set up. Hence it is left to +other code, usually the platform HAL, to instantiate the bus +structure(s). This driver package supplies the necessary functions and +utility macros. Similarly this package does not provide any +<structname>cyg_i2c_device</structname> structures. Which +I<superscript>2</superscript>C devices are hooked up to which +I<superscript>2</superscript>C bus is entirely a characteristic of the +hardware platform, so again it is up to the platform HAL to +instantiate the necessary structures. + </para> + <para> +The driver will operate in interrupt-driven mode if interrupts are +enabled when a transfer is initiated. Otherwise it will operate in +polled mode. This allows the driver to be used in a variety of +configurations including inside RedBoot. + </para> + </refsect1> + + <refsect1 id="devs-i2c-m68k-mcf52xx-config"><title>Configuration Options</title> + <para> +The I<superscript>2</superscript>C bus driver package should be loaded +automatically when selecting a target containing a suitable ColdFire +processor, and it should never be necessary to load the package +explicitly. If the application does not use any of the +I<superscript>2</superscript>C functionality, directly or indirectly, +then all the I<superscript>2</superscript>C code should be removed at +link-time and the application does not suffer any overheads. + </para> + <para> +By default the driver assumes a single I<superscript>2</superscript>C +bus and optimizes for that case. For example options like the ISR +vector and priority are handled by compile-time +<literal>#define</literal>'s in the platform HAL's exported header +files rather than by per-bus structure fields. This helps to reduce +both code and data overheads. If the driver should support multiple +I<superscript>2</superscript>C buses then +<varname>CYGHWR_DEVS_I2C_MCF52xx_MULTIPLE_BUSES</varname> should be +enabled. Typically this will be done by the platform HAL using a CDL +<property>requires</property> property. If bus instantiation happens +outside the platform HAL and hence the HAL's header files do not +provide the appropriate definitions, then this configuration option +should also be defined. + </para> + <para> +The only other configuration options in this package provide control +over the compiler flags used to build the driver code. + </para> + </refsect1> + + <refsect1 id="devs-i2c-m68k-mcf52xx-bus-devices"><title>Defining the Bus and Devices</title> + <para> +For most hardware targets the platform HAL will instantiate the +<structname>cyg_i2c_bus</structname> and +<structname>cyg_i2c_device</structname> structures, and it will also +initialize the hardware so that the +I<superscript>2</superscript>C-related pins are connected +appropriately. Some development boards have no +I<superscript>2</superscript>C devices, but the +I<superscript>2</superscript>C bus signals are accessible via an +expansion connector and I<superscript>2</superscript>C devices can be +put on a daughter board. In such cases it may be necessary for the +application to instantiate both the bus and all the device structures. +Alternatively the platform HAL may provide a configuration option to +enable just the bus, with the devices still left to application code. + </para> + <para> +To facilitate bus instantiation the header file <filename +class="headerfile">cyg/io/i2c_mcf52xx.h</filename> provides a utility +macro <function>CYG_MCF52xx_I2C_BUS</function>. This takes six +parameters: + </para> + <orderedlist> + <listitem><para> +The name of the bus, for example +<varname>hal_dnp5280_i2c_bus</varname>. This name will be used when +instantiating the I<superscript>2</superscript>C devices. + </para></listitem> + <listitem><para> +An initialization function. If no platform-specific initialization is +needed then this can be the <function>cyg_mcf52xx_i2c_init</function> +function exported by this driver. Otherwise it can be a +platform-specific function which, for example, sets up the relevant +pins appropriately and then chains into +<function>cyg_mcf52xx_i2c_init</function>. + </para></listitem> + <listitem><para> +The base address of the I<superscript>2</superscript>C bus. For +example on an MCF5282 with the IPSBAR set to its usual value of +0x40000000, the I<superscript>2</superscript>C bus is at location +0x40000300. + </para></listitem> + <listitem><para> +The interrupt vector, for example +<varname>CYGNUM_HAL_ISR_I2C_IIF</varname> on an MCF5282. + </para></listitem> + <listitem><para> +The interrupt priority. Typically this will be a configurable option +within the platform HAL. + </para></listitem> + <listitem><para> +A value for the I<superscript>2</superscript>C bus's I2FDR register. +That register controls the bus speed. Typical bus speeds are 100KHz +and 400KHz, depending on the capabilities of the attached devices. +There is no simple relationship between the system clock speed, the +desired bus speed, and the FDR register. Although the driver could +determine the FDR setting using a lookup table and appropriate code, +it is better to determine the correct value once during the porting +process and avoid unnecessary run-time overheads. + </para></listitem> + </orderedlist> + <para> +For the common case where only a single I<superscript>2</superscript>C +bus should be supported +(<varname>CYGHWR_DEVS_I2C_MCF52xx_MULTIPLE_BUSES</varname> is +disabled), the last four parameters should be provided by preprocessor +<literal>#define</literal>'s, typically in <filename +class="headerfile">cyg/hal/plf_io.h</filename> which gets +<literal>#include</literal>'d automatically via +<filename>cyg/hal/hal_io.h</filename>. This header can also define the +<varname>HAL_I2C_EXPORTED_DEVICES</varname> macro as per the generic +I<superscript>2</superscript>C package: + </para> + <programlisting width=72> +#include <pkgconf/hal_m68k_dnp5280.h> +… +#ifdef CYGHWR_HAL_M68K_DNP5280_I2C +#define HAL_MCF52xx_I2C_SINGLETON_BASE (HAL_MCF52xx_MBAR+HAL_MCF5282_I2C0_BASE) +#define HAL_MCF52xx_I2C_SINGLETON_ISRVEC CYGNUM_HAL_ISR_I2C_IIF +#define HAL_MCF52xx_I2C_SINGLETON_ISRPRI CYGNUM_HAL_M68K_DNP5280_I2C_ISRPRI +#define HAL_MCF52xx_I2C_SINGLETON_FDR CYGNUM_HAL_M68K_DNP5280_I2C_FDR + +#define HAL_I2C_EXPORTED_DEVICES \ + extern cyg_i2c_bus hal_dnp5280_i2c_bus; +#endif + </programlisting> + <para> +On this particular platform the I<superscript>2</superscript>C bus is +only accessible on an expansion connector so the support is +conditional on a configuration option +<varname>CYGHWR_HAL_M68K_DNP5280_I2C</varname>. The interrupt priority +and I2FDR values are also controlled by configuration options. On +other platforms the I<superscript>2</superscript>C support may not be +conditional and the priority and/or FDR values may be hard-wired. + </para> + <para> +The I<superscript>2</superscript>C bus instantiation should happen in +an ordinary C or C++ file, typically in the platform HAL. The +corresponding object file should go into +<filename>libtarget.a</filename> and the file should only contain +I<superscript>2</superscript>C-related code to get the maximum benefit +of linker garbage collection. + </para> + <programlisting width=72> +#include <cyg/infra/cyg_type.h> +#include <cyg/hal/hal_io.h> +#include <cyg/io/i2c.h> +#include <cyg/io/i2c_mcf52xx.h> + +static void +dnp5280_i2c_init(struct cyg_i2c_bus* bus) +{ + cyg_uint16 paspar; + // Reset GPIO pins PAS0/1 to their alternative SCL/SDA settings + HAL_READ_UINT16(HAL_MCF5282_IPSBAR + HAL_MCF5282_GPIO_PASPAR, paspar); + paspar &= ~(HAL_MCF5282_GPIO_PASPAR_A0_MASK | HAL_MCF5282_GPIO_PASPAR_A1_MASK); + paspar |= (HAL_MCF5282_GPIO_PASPAR_A0_SCL | HAL_MCF5282_GPIO_PASPAR_A1_SDA); + HAL_WRITE_UINT16(HAL_MCF5282_IPSBAR + HAL_MCF5282_GPIO_PASPAR, paspar); + + // And leave the driver to take care of the rest. + cyg_mcf52xx_i2c_init(bus); +} + +CYG_MCF52xx_I2C_BUS(hal_dnp5280_i2c_bus, + &dnp5280_i2c_init, + HAL_MCF52xx_I2C_SINGLETON_BASE, + HAL_MCF52xx_I2C_SINGLETON_ISRVEC, + HAL_MCF52xx_I2C_SINGLETON_ISRPRI, + HAL_MCF52xx_I2C_SINGLETON_FDR); + + </programlisting> + <para> +Obviously if <varname>CYGHWR_DEVS_I2C_MCF52xx_MULTIPLE_BUSES</varname> +is enabled then the singleton macros may not be defined and the +appropriate numbers should be used directly. This example uses a +custom initialization function which sets up the relevant pins and +then chains into the I<superscript>2</superscript>C drivers' +<function>cyg_mcf52xx_i2c_init</function> function. If the platform +HAL has already set up the pins correctly then +<function>cyg_mcf52xx_i2c_init</function> could be used directly in +the bus instantiation, saving a small amount of code for the custom +initialization function. + </para> + <para> +I<superscript>2</superscript>C device structures can be instantiated +in the usual way, for example: + </para> + <programlisting width=72> +CYG_I2C_DEVICE(cyg_i2c_wallclock_ds1307, + &hal_dnp5280_i2c_bus, + 0x68, + 0x00, + CYG_I2C_DEFAULT_DELAY); + </programlisting> + </refsect1> + +</refentry> +</part>
new file mode 100644 --- /dev/null +++ b/packages/devs/i2c/m68k/mcf52xx/current/include/i2c_mcf52xx.h @@ -0,0 +1,117 @@ +//========================================================================== +// +// devs/i2c/m68k/mcf52xx/current/src/i2c_mcf52xx.h +// +// I2C driver for Motorola coldfire processors +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2005, 2006 eCosCentric Ltd. +// +// 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): Bart Veer +// Contributors: +// Date: 2005-11-20 +// Description: I2C driver for motorola coldfire processor +//####DESCRIPTIONEND#### +//========================================================================== + +#include <pkgconf/devs_i2c_mcf52xx.h> +#include <cyg/infra/cyg_type.h> +#include <cyg/hal/drv_api.h> + +typedef enum cyg_mcf52xx_i2c_xfer_mode { + CYG_MCF52xx_I2C_XFER_MODE_INVALID = 0x00, + CYG_MCF52xx_I2C_XFER_MODE_TX = 0x01, + CYG_MCF52xx_I2C_XFER_MODE_RX = 0x02, + CYG_MCF52xx_I2C_XFER_MODE_STARTRX = 0x03 +} cyg_mcf52xx_i2c_xfer_mode; + +typedef struct cyg_mcf52xx_i2c_extra { +#ifdef CYGHWR_DEVS_I2C_MCF52xx_MULTIPLE_BUSES + // Put statically initialized fields first. + cyg_uint8* i2c_base; // Per-bus h/w details + cyg_vector_t i2c_isrvec; + int i2c_isrpri; + int i2c_fdr; +#endif + + cyg_uint8 i2c_owner; // We have bus ownership + cyg_uint8 i2c_lost_arb; // Error condition leading to loss of bus ownership + cyg_uint8 i2c_send_nack; // As per rx send_nack argument + cyg_uint8 i2c_got_nack; // The last tx resulted in a nack + cyg_uint8 i2c_completed; // Set by DSR, checked by thread + + union { + const cyg_uint8* i2c_tx_data; + cyg_uint8* i2c_rx_data; + } i2c_data; // The current buffer for rx or tx + cyg_uint32 i2c_count; // Number of bytes left in buffer + cyg_mcf52xx_i2c_xfer_mode i2c_mode; // TX, RX, ... + + + 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_mcf52xx_i2c_extra; + +externC void cyg_mcf52xx_i2c_init(struct cyg_i2c_bus*); +externC cyg_uint32 cyg_mcf52xx_i2c_tx(const cyg_i2c_device*, cyg_bool, const cyg_uint8*, cyg_uint32, cyg_bool); +externC cyg_uint32 cyg_mcf52xx_i2c_rx(const cyg_i2c_device*, cyg_bool, cyg_uint8*, cyg_uint32, cyg_bool, cyg_bool); +externC void cyg_mcf52xx_i2c_stop(const cyg_i2c_device*); + +#ifdef CYGHWR_DEVS_I2C_MCF52xx_MULTIPLE_BUSES +# define CYG_MCF52xx_I2C_BUS(_name_, _init_fn_, _base_, _isr_vec_, _isr_pri_, _fdr_) \ + static cyg_mcf52xx_i2c_extra _name_ ## _extra = { \ + _base_, \ + _isr_vec_, \ + _isr_pri_, \ + _fdr_ \ + } ; \ + CYG_I2C_BUS(_name_, \ + _init_fn_, \ + &cyg_mcf52xx_i2c_tx, \ + &cyg_mcf52xx_i2c_rx, \ + &cyg_mcf52xx_i2c_stop, \ + (void*) & ( _name_ ## _extra)) ; + +#else +# define CYG_MCF52xx_I2C_BUS(_name_, _init_fn_, _base_, _isr_vec_, _isr_pri_, _fdr_) \ + static cyg_mcf52xx_i2c_extra _name_ ## _extra; \ + CYG_I2C_BUS(_name_, \ + _init_fn_, \ + cyg_mcf52xx_i2c_tx, \ + cyg_mcf52xx_i2c_rx, \ + cyg_mcf52xx_i2c_stop, \ + (void*) & ( _name_ ## _extra)) ; +#endif +
new file mode 100644 --- /dev/null +++ b/packages/devs/i2c/m68k/mcf52xx/current/src/i2c_mcf52xx.c @@ -0,0 +1,451 @@ +//========================================================================== +// +// devs/i2c/m68k/mcf52xx/current/src/i2c_mcf52xx.c +// +// I2C driver for Motorola coldfire processors +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2005, 2006 eCosCentric Ltd. +// +// 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): Uwe Kindler, Bart Veer +// Contributors: +// Date: 2005-10-23 +// Description: I2C driver for motorola coldfire processor +//####DESCRIPTIONEND#### +//========================================================================== + +#include <pkgconf/system.h> +#include <pkgconf/devs_i2c_mcf52xx.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_mcf52xx.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> + +// Optimize for the case of a single bus device, while still allowing +// multiple devices. +#ifndef CYGHWR_DEVS_I2C_MCF52xx_MULTIPLE_BUSES +# define I2C_BASE(_extra_) (cyg_uint8*)HAL_MCF52xx_I2C_SINGLETON_BASE +# define I2C_ISRVEC(_extra_) HAL_MCF52xx_I2C_SINGLETON_ISRVEC +# define I2C_ISRPRI(_extra_) HAL_MCF52xx_I2C_SINGLETON_ISRPRI +# define I2C_FDR(_extra_) HAL_MCF52xx_I2C_SINGLETON_FDR +#else +# define I2C_BASE(_extra_) ((_extra_)->i2c_base) +# define I2C_ISRVEC(_extra_) ((_extra_)->i2c_isrvec) +# define I2C_ISRPRI(_extra_) ((_extra_)->i2c_isrpri) +# define I2C_FDR(_extra_) ((_extra_)->i2c_fdr) +#endif + +// If building for a singleton but the macros are no defined, assume +// the I2C support is conditional on a disabled platform HAL +// configuration option. This handles the common case of an I2C bus +// accessed only via an expansion connector. +#if defined(CYGHWR_DEVS_I2C_MCF52xx_MULTIPLE_BUSES) || defined(HAL_MCF52xx_I2C_SINGLETON_BASE) + +// ---------------------------------------------------------------------------- +// Interrupt handling and polling +// +// The MCF52xx I2C bus device does not have a fifo or any kind of DMA +// capability, so can generate interrupts at a very high rate: ~10K +// interrupts per second if the bus is running at the standard 100KHz, +// or 50K for a high-speed 400KHz bus. To keep the cpu load down to +// something vaguely reasonable as much work as possible has to be +// done in the ISR, with the DSR used only for completion. +static cyg_uint32 +mcf52xx_i2c_isr(cyg_vector_t vec, cyg_addrword_t data) +{ + cyg_mcf52xx_i2c_extra* extra = (cyg_mcf52xx_i2c_extra*)data; + cyg_uint8 sr, dr; + cyg_uint8* base = I2C_BASE(extra); + cyg_uint32 result = CYG_ISR_HANDLED; + + // Read the current status, then clear the interrupt and + // arbitration-lost flags. No later code will look at the + // SR register again. + HAL_READ_UINT8( base + HAL_MCF52xx_I2C_SR_OFF, sr); + HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_SR_OFF, 0x00); + + // What to do next depends on the current transfer mode. + if (CYG_MCF52xx_I2C_XFER_MODE_TX == extra->i2c_mode) { + // We are in a transmit, or sending the address byte just + // before a transmit. + if (sr & HAL_MCF52xx_I2C_SR_IAL) { + // Lost the bus, abort the transfer. count has already been + // decremented. Assume the byte did not actually arrive. + extra->i2c_count += 1; + result = CYG_ISR_HANDLED | CYG_ISR_CALL_DSR; + } else if (sr & HAL_MCF52xx_I2C_SR_RXAK) { + // This byte has been sent but the device cannot accept + // any more. The nack must be remembered. Otherwise if + // we got a nack for the last byte in a tx then the + // calling code will think the entire tx succeeded, + // and there will be problems if the next call is + // another tx without a repeated start. + extra->i2c_got_nack = 1; + result = CYG_ISR_HANDLED | CYG_ISR_CALL_DSR; + } else if (0 == extra->i2c_count) { + // No more bytes to send. + result = CYG_ISR_HANDLED | CYG_ISR_CALL_DSR; + } else { + HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_DR_OFF, *(extra->i2c_data.i2c_tx_data)); + extra->i2c_data.i2c_tx_data += 1; + extra->i2c_count -= 1; + } + } else if (CYG_MCF52xx_I2C_XFER_MODE_RX == extra->i2c_mode) { + if (sr & HAL_MCF52xx_I2C_SR_IAL) { + // Lost the bus? Maybe a spurious stop + result = CYG_ISR_HANDLED | CYG_ISR_CALL_DSR; + } else { + if (extra->i2c_send_nack && (2 == extra->i2c_count)) { + // Received one, one more to go, and that one should be nacked. + HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF, + HAL_MCF52xx_I2C_CR_IEN | + HAL_MCF52xx_I2C_CR_IIEN | + HAL_MCF52xx_I2C_CR_MSTA | + HAL_MCF52xx_I2C_CR_TXAK); + } else if (1 == extra->i2c_count) { + // Received the last byte. The docs say to send a stop, + // but there may be another transaction_rx() call. We + // cannot just read DR again, that would trigger another + // read. So instead switch to transmit mode for now, + // which should cause the h/w to wait until a byte is + // written to DR. + HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF, + HAL_MCF52xx_I2C_CR_IEN | + HAL_MCF52xx_I2C_CR_IIEN | + HAL_MCF52xx_I2C_CR_MSTA | + HAL_MCF52xx_I2C_CR_MTX); + result = CYG_ISR_HANDLED | CYG_ISR_CALL_DSR; + } + + HAL_READ_UINT8(base + HAL_MCF52xx_I2C_DR_OFF, dr); + *(extra->i2c_data.i2c_rx_data) = dr; + extra->i2c_data.i2c_rx_data += 1; + extra->i2c_count -= 1; + } + } else if (CYG_MCF52xx_I2C_XFER_MODE_STARTRX == extra->i2c_mode) { + // Start followed by RX. The address byte has been sent, we + // need to switch to receiving. + if (sr & HAL_MCF52xx_I2C_SR_IAL) { + // Looks like no device acknowledged the address. + result = CYG_ISR_HANDLED | CYG_ISR_CALL_DSR; + } else { + extra->i2c_mode = CYG_MCF52xx_I2C_XFER_MODE_RX; + if (extra->i2c_send_nack && (1 == extra->i2c_count)) { + HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF, + HAL_MCF52xx_I2C_CR_IEN | + HAL_MCF52xx_I2C_CR_IIEN | + HAL_MCF52xx_I2C_CR_MSTA | + HAL_MCF52xx_I2C_CR_TXAK); + } else { + HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF, + HAL_MCF52xx_I2C_CR_IEN | + HAL_MCF52xx_I2C_CR_IIEN | + HAL_MCF52xx_I2C_CR_MSTA); + } + // This dummy read causes the next rx to start + HAL_READ_UINT8(base + HAL_MCF52xx_I2C_DR_OFF, dr); + } + } else { + // Invalid state? Some kind of spurious interrupt? Just ignore + // it. + CYG_FAIL("I2C spurious interrupt"); + } + + // NOTE: this will acknowledge the interrupt even in polled mode. + // Probably harmless. Using I2C_ISRVEC rather than the vec arg + // means a constant number for the singleton case, which may + // allow the HAL to optimize the acknowledge away completely. + HAL_INTERRUPT_ACKNOWLEDGE(I2C_ISRVEC(extra)); + return result; +} + +static void +mcf52xx_i2c_dsr(cyg_vector_t vec, cyg_ucount32 count, cyg_addrword_t data) +{ + cyg_mcf52xx_i2c_extra* extra = (cyg_mcf52xx_i2c_extra*)data; + extra->i2c_completed = 1; + cyg_drv_cond_signal(&(extra->i2c_wait)); +} + +// A transfer has been started. Wait for completion, allowing for both +// polled and interrupt-driven mode. +static inline void +mcf52xx_i2c_doit(cyg_mcf52xx_i2c_extra* extra) +{ + cyg_uint8* base = I2C_BASE(extra); + int ints_state; + int sr; + + HAL_QUERY_INTERRUPTS(ints_state); + if (((ints_state >> 8) & 0x07) > CYGNUM_HAL_INTERRUPT_DEFAULT_IPL_LEVEL) { + // Interrupts are currently disabled. We'll have to poll. + for ( ; ; ) { + HAL_READ_UINT8(base + HAL_MCF52xx_I2C_SR_OFF, sr); + if (sr & HAL_MCF52xx_I2C_SR_IIF) { + if (CYG_ISR_CALL_DSR & mcf52xx_i2c_isr(I2C_ISRVEC(extra), (cyg_addrword_t)extra)) { + break; + } + } + } + } else { + cyg_drv_mutex_lock(&(extra->i2c_lock)); + cyg_drv_dsr_lock(); + while (! extra->i2c_completed) { + cyg_drv_cond_wait(&(extra->i2c_wait)); + } + cyg_drv_dsr_unlock(); + cyg_drv_mutex_unlock(&(extra->i2c_lock)); + } +} + +static cyg_bool +mcf52xx_i2c_send_start(cyg_mcf52xx_i2c_extra* extra, int address) +{ + cyg_uint8* base = I2C_BASE(extra); + cyg_uint8 sr; + + // This may be a repeated start or the beginning of a transaction. + // If the former then we still own the bus. + if (!extra->i2c_owner) { + // The bus is currently in slave mode. See if another master + // currently owns the bus and if so fail immediately. It is up + // to higher level code to decide when to retry. Alternatively + // if the bus has somehow got stuck in busy mode it is again + // up to higher level code to sort things out. + HAL_READ_UINT8(I2C_BASE(extra) + HAL_MCF52xx_I2C_SR_OFF, sr); + if (sr & HAL_MCF52xx_I2C_SR_IBB) { + return 0; + } + + // Now we can put the bus into master mode + HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF, + HAL_MCF52xx_I2C_CR_IEN | + HAL_MCF52xx_I2C_CR_IIEN | + HAL_MCF52xx_I2C_CR_MSTA | // This implicitly generates the start + HAL_MCF52xx_I2C_CR_MTX); // The address byte needs to be transmitted. + extra->i2c_owner = 1; + } else { + HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF, + HAL_MCF52xx_I2C_CR_IEN | + HAL_MCF52xx_I2C_CR_IIEN | + HAL_MCF52xx_I2C_CR_MSTA | // Already set so no start generated by this + HAL_MCF52xx_I2C_CR_MTX | + HAL_MCF52xx_I2C_CR_RSTA); // Repeated start + } + + // Any previous nack is no longer relevant. If the device cannot accept + // more data it will nack the address. + extra->i2c_got_nack = 0; + // Now send the address. The rest of the transfer is handled by the + // interrupt/polling code. + HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_DR_OFF, address); + return 1; +} + +static inline void +mcf52xx_i2c_stopit(cyg_mcf52xx_i2c_extra* extra) +{ + // If we still own the bus this releases it (by clearing MSTA) and + // generating a stop. If we have lost arbitration then this write + // has no effect (other than disabling interrupts). Either way the + // bus should end up in a consistent state. + HAL_WRITE_UINT8(I2C_BASE(extra) + HAL_MCF52xx_I2C_CR_OFF, HAL_MCF52xx_I2C_CR_IEN); + extra->i2c_lost_arb = 0; + extra->i2c_owner = 0; + extra->i2c_mode = CYG_MCF52xx_I2C_XFER_MODE_INVALID; +} + +// ---------------------------------------------------------------------------- +// The functions needed for all I2C devices. + +void +cyg_mcf52xx_i2c_init(struct cyg_i2c_bus* bus) +{ + cyg_mcf52xx_i2c_extra* extra = (cyg_mcf52xx_i2c_extra*)bus->i2c_extra; + cyg_uint8 reg; + cyg_uint8* base = I2C_BASE(extra); + + 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, + &mcf52xx_i2c_isr, + &mcf52xx_i2c_dsr, + &(extra->i2c_interrupt_handle), + &(extra->i2c_interrupt_data)); + cyg_drv_interrupt_attach(extra->i2c_interrupt_handle); + + // Before unmasking the interrupt sort out the hardware. + // + // The bus frequency is set by the platform HAL or user, since + // it depends on what mixture of devices are present on the bus. + HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_FDR_OFF, I2C_FDR(extra)); + // The device will operate in slave mode when idle. If there is + // another bus master then the coldfire might accidentally accept + // requests intended for another device. Address 0 is installed + // as the slave address. This is the General Call address, used + // for broadcasting. It might be better to use another address + // like an Hs-mode one, but conflicts are still possible. + HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_ADR_OFF, 0x0); + // Enable the I2C device but do not start any transfers and + // leave interrupts disabled. + HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF, HAL_MCF52xx_I2C_CR_IEN); + + // As per the documentation, if IBB is set then issue a stop. It + // is not really clear this is the right thing to do in + // multimaster setups, if another master happens to start a + // transfer at this exact time. Presumably it solves more problems + // than it might cause. + HAL_READ_UINT8(base + HAL_MCF52xx_I2C_SR_OFF, reg); + if (reg & HAL_MCF52xx_I2C_SR_IBB) { + HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF, 0x0000); + HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF, 0x00A0); + HAL_READ_UINT8( base + HAL_MCF52xx_I2C_DR_OFF, reg); + HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_SR_OFF, 0x0000); + HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF, 0x0000); + + // Don't forget to reenable the device. + HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF, HAL_MCF52xx_I2C_CR_IEN); + } + + // Clear any pending conditions including interrupts. + HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_SR_OFF, 0); + + // Interrupts can now be safely unmasked + HAL_INTERRUPT_UNMASK(I2C_ISRVEC(extra)); +} + +cyg_uint32 +cyg_mcf52xx_i2c_tx(const cyg_i2c_device* dev, cyg_bool send_start, const cyg_uint8* tx_data, cyg_uint32 count, cyg_bool send_stop) +{ + cyg_mcf52xx_i2c_extra* extra = (cyg_mcf52xx_i2c_extra*)dev->i2c_bus->i2c_extra; + + extra->i2c_count = count; + if (! extra->i2c_lost_arb) { + extra->i2c_completed = 0; + extra->i2c_mode = CYG_MCF52xx_I2C_XFER_MODE_TX; + + if (send_start) { + extra->i2c_data.i2c_tx_data = tx_data; + if (! mcf52xx_i2c_send_start(extra, (dev->i2c_address << 1) | 0x00)) { + diag_printf("send_start failed\n"); + return 0; + } + mcf52xx_i2c_doit(extra); + } else if ( !extra->i2c_got_nack) { + // We are in the middle of a transaction and not + // generating a repeated start, so the device must already + // be set up for writes. + extra->i2c_data.i2c_tx_data = &(tx_data[1]); + extra->i2c_count = count - 1; + HAL_WRITE_UINT8(I2C_BASE(extra) + HAL_MCF52xx_I2C_DR_OFF, *tx_data); + mcf52xx_i2c_doit(extra); + } + } + if (send_stop) { + mcf52xx_i2c_stopit(extra); + } + + // tx() should return the number of bytes actually transmitted. + // ISR() increments extra->count after a failure, which leads to + // an edge condition when send_start and there is no acknowledgment + // of the address byte. + if (extra->i2c_count > count) { + return 0; + } + return count - extra->i2c_count; +} + +cyg_uint32 +cyg_mcf52xx_i2c_rx(const cyg_i2c_device* dev, cyg_bool send_start, cyg_uint8* rx_data, cyg_uint32 count, cyg_bool send_nack, cyg_bool send_stop) +{ + cyg_mcf52xx_i2c_extra* extra = (cyg_mcf52xx_i2c_extra*)dev->i2c_bus->i2c_extra; + cyg_uint8* base = I2C_BASE(extra); + cyg_uint8 discard; + + extra->i2c_count = count; + extra->i2c_send_nack = send_nack; + + if (! extra->i2c_lost_arb) { + extra->i2c_completed = 0; + extra->i2c_data.i2c_rx_data = rx_data; + if (send_start) { + extra->i2c_mode = CYG_MCF52xx_I2C_XFER_MODE_STARTRX; + if (! mcf52xx_i2c_send_start(extra, (dev->i2c_address << 1) | 0x01) ) { + return 0; + } + } else { + // In the middle of a transaction. The previous transfer + // will have left the device in tx mode. + extra->i2c_mode = CYG_MCF52xx_I2C_XFER_MODE_RX; + if (send_nack && (1 == count)) { + HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF, + HAL_MCF52xx_I2C_CR_IEN | + HAL_MCF52xx_I2C_CR_IIEN | + HAL_MCF52xx_I2C_CR_MSTA | + HAL_MCF52xx_I2C_CR_TXAK); + } else { + HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF, + HAL_MCF52xx_I2C_CR_IEN | + HAL_MCF52xx_I2C_CR_IIEN | + HAL_MCF52xx_I2C_CR_MSTA); + } + // So reading the data register here should get the device + // reading the next byte. + HAL_READ_UINT8(base + HAL_MCF52xx_I2C_DR_OFF, discard); + } + mcf52xx_i2c_doit(extra); + } + if (send_stop) { + mcf52xx_i2c_stopit(extra); + } + return count - extra->i2c_count; +} + +void +cyg_mcf52xx_i2c_stop(const cyg_i2c_device* dev) +{ + cyg_mcf52xx_i2c_extra* extra = (cyg_mcf52xx_i2c_extra*)dev->i2c_bus->i2c_extra; + mcf52xx_i2c_stopit(extra); +} + +#endif // defined(CYGHWR_DEVS_I2C_MCF52xx_MULTIPLE_BUSES) || defined(HAL_MCF52xx_I2C_SINGLETON_BASE) +//--------------------------------------------------------------------------- +// EOF i2c_mcf52xx.c
--- a/packages/ecos.db +++ b/packages/ecos.db @@ -2202,6 +2202,16 @@ package CYGPKG_IO_I2C { support for bit-banged I2C buses." } +package CYGPKG_DEVS_I2C_MCF52xx { + alias { "MCF52xx I2C driver" devs_i2c_mcf52xx mcf52xx_i2c_driver } + hardware + directory devs/i2c/m68k/mcf52xx + script i2c_mcf52xx.cdl + description " + This packages provides an I2C driver for the on-chip + device provided by the MCF52xx coldfire processor family." +} + package CYGPKG_KERNEL { alias { "eCos kernel" kernel } directory kernel
