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 + -
显示快捷键?