i2c_mcf52xx.c

来自「开放源码实时操作系统源码.」· C语言 代码 · 共 452 行 · 第 1/2 页

C
452
字号
//==========================================================================
//
//      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 {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?