📄 lh79524_i2c_driver.c
字号:
/***********************************************************************
* $Workfile: lh79524_i2c_driver.c $
* $Revision: 1.6 $
* $Author: NambiarA $
* $Date: Dec 30 2004 13:33:22 $
*
* Project: LH79524 CSP library
*
* Description:
* This file contains the implementation of the LH79524 I2C driver
*
* Revision History:
* $Log:: //smaicnt2/pvcs/VM/CHIPS/archives/software/csps/lh79524/sou$
*
* Rev 1.6 Dec 30 2004 13:33:22 NambiarA
* Added the check for TX and RX aborts
*
* Rev 1.5 Oct 14 2004 17:51:24 TilburyC
* Changes to IOCTLS and other code to support the notion that the config register cannot be set up using IOCTLS
*
* Rev 1.4 Oct 13 2004 16:59:38 TilburyC
* Complete I2C device driver
*
* Rev 1.2 Oct 06 2004 17:34:18 TilburyC
* Working i2c driver
*
* Rev 1.1 Oct 01 2004 11:02:16 TilburyC
* many changes to implement i2c driver
*
* Rev 1.0 Sep 14 2004 15:35:42 TilburyC
* Initial revision.
*
*
***********************************************************************
*
* Copyright (c) 2004 Sharp Microelectronics of the Americas
*
* All rights reserved
*
* SHARP MICROELECTRONICS OF THE AMERICAS MAKES NO REPRESENTATION
* OR WARRANTIES WITH RESPECT TO THE PERFORMANCE OF THIS SOFTWARE,
* AND SPECIFICALLY DISCLAIMS ANY RESPONSIBILITY FOR ANY DAMAGES,
* SPECIAL OR CONSEQUENTIAL, CONNECTED WITH THE USE OF THIS SOFTWARE.
*
* SHARP MICROELECTRONICS OF THE AMERICAS PROVIDES THIS SOFTWARE SOLELY
* FOR THE PURPOSE OF SOFTWARE DEVELOPMENT INCORPORATING THE USE OF A
* SHARP MICROCONTROLLER OR SYSTEM-ON-CHIP PRODUCT. USE OF THIS SOURCE
* FILE IMPLIES ACCEPTANCE OF THESE CONDITIONS.
*
**********************************************************************/
#include "lh79524_iocon.h"
#include "lh79524_rcpc.h"
#include "lh79524_vic.h"
#include "lh79524_i2c_driver.h"
/* Defines for the ip block base address and driver control object */
#define REGS_T I2C_REGS_T
#define I2C_WAIT_TIME 10000
#define I2C_STATE_BUSY 0x0001
#define I2C_STATE_OPEN 0x0002
#define I2C_STATE_POLL 0x0004
#define I2C_STATE_INT 0x0008
/* This macro is used by the read and write funcitons to set the
configuration for each transfer. It allows the current mode bits
to be preserved while clearing mode bits that are not specified.
-- See usage in i2c_write() below -- */
#define I2C_MODE_BITS(v) ((v) & I2C_ICCON_MODE | \
(v) & I2C_ICCON_EN | \
(v) & I2C_ICCON_SPEED)
/* i2c state information */
typedef struct
{
I2C_CONFIG_T conf; /* current configuration data strucutre */
I2C_REGS_T *regs; /* pointer to the i2c registers */
volatile UNS_32 state; /* non-zero if the i2c module has been opened */
volatile UNS_32 interrupts; /* number of interrupts */
volatile UNS_32 tx_aborts; /* number of tx aborts */
volatile UNS_32 rx_aborts; /* number of rx aborts */
volatile UNS_32 num_reads; /* number of reads */
volatile UNS_32 num_writes; /* number of writes */
} I2C_STATE_T;
/* main global control data structure */
static I2C_STATE_T i2c_state;
/* Forward declarations of private methods */
static INT_32 i2c_wait_int(void);
static INT_32 i2c_wait_idle(void);
static INT_32 i2c_wait_busy(void);
static void i2c_isr(void);
/* Global status counters that can be returned via IOCTL */
/*
Driver is very simple. There are so many permutations of operating
scenarios that there is no easy way to support the use of a generic
I2C driver. Instead, this driver is intended to support the development
of other drivers for specific peripherals, such as EEPROM or CODEC.
The read and write functions only intend to act on one byte. Since
all transactions on the I2C interface involve more than one byte, it
makes no sense to only read or write one byte. However, this is a
requirement because transactions for different devices have different
formats. A read or a write transaction for a device such as an EEPROM
will vary from one device to the next. That is beyond the scope of
this driver.
In addition, there is no attempt to support simultaneous multiple
opening of the I2C interface by different drivers. All attempts to
open the I2C interface where it is already open will fail. This
functionality should be handled at a higher level.
+---------+----------+---------+
| CODEC | EEPROM | OTHER |
+---------+----------+---------+
| Multi-driver interface |
| (left for customer to do) |
+------------------------------+
| I2C |
| hardware driver |
+------------------------------+
open()
Enables the device and establishes reasonable defaults. Device
defaults to being a 7 bit master at 100kHz as slow as possible. The
open function can accept a pointer to a I2C config data structure
containing all of the information that is required to do a complete
initialization or a NULL pointer, which sets the defaults. There is
no polling mode. All I2C functionality is interrupt driven.
close()
Disables the device. The assumption is made that the device will
be opened and configured again, so no attempt is made to
"deconfigure" the I2C interface. If there is a byte ready to be
read from the device, it is read and then discarded.
read()
This function attempts to read one byte from the interface. It
is assumed by this function that a transaction is in progress.
write()
This function writes one byte to the I2C interface. It is assumed
by this function that a transaction is in progress. Accepts a
parameter that allows the things like the STOP bit to be set. This
is because the START and STOP bits are required to be set for the
last byte of the transaction.
ioctl()
This function can set or read all aspects of the I2C configuration
registers.
Return the current configuration in the config data structure.
I2C_IOCTL_GET_CONFIG
Return the current status in the status data strucutre.
I2C_IOCTL_GET_STATUS
Set the configuration using the config data structure.
I2C_IOCTL_SET_CONFIG_EX
Turn on or off the STOP bit in the config word.
I2C_IOCTL_SET_STOP_STATE
Turn on or off the START bit in the config word.
I2C_IOCTL_SET_START_STATE
Turn on or off the SABT bit in the config word.
I2C_IOCTL_SET_SABT_STATE
Turn the RWC bit off or on in the config word.
I2C_IOCTL_SET_RWC_STATE
Set the speed bit and the timer registers using the i2c speed
data structure.
I2C_IOCTL_SET_SPEED
Return the speed data strucutre in the buffer provided.
I2C_IOCTL_GET_SPEED
Set the slave address of the device using the slave address
data strucutre.
I2C_IOCTL_SET_SLAVE_ADDR
Retutrn the lave address in the buffer provided.
I2C_IOCTL_GET_SLAVE_ADDR
Write the byte specified into the data register.
I2C_IOCTL_WRITE_DATA
Read the data register.
I2C_IOCTL_READ_DATA
Wait (spin) until the i2c interface is idle
I2C_IOCTL_WAIT_IDLE
Return the number of transmit aborts that have taken place since the
driver was opened.
I2C_IOCTL_GET_TXABORTS
Return the number of recieve aborts that have taken place since the
driver was opened.
I2C_IOCTL_GET_RXABORTS
Return the number of reads that have taken place since the
driver was opened.
I2C_IOCTL_GET_READS
Return the number of writes that have taken place since the
driver was opened.
I2C_IOCTL_GET_WRITES
Switch between using polling and interrupts. Default is
polling.
I2C_IOCTL_SET_POLLING
I2C_IOCTL_SET_INTERRUPT
Set the high/low counters
I2C_IOCTL_SET_COUNTER,
Get the high/low counters
I2C_IOCTL_GET_COUNTER,
*/
/**********************************************************************
*
* Function: i2c_open
*
* Purpose:
* Initialize the I2C port with the paramters specified.
*
* Processing:
* Sets up the device control structure for I2C.
*
* Parameters:
* ipbase - the device to open
* arg - contains a pointer to a configuration information
* data structure. Can be NULL for defaults.
* Outputs:
* None
*
* Returns:
* device id or 0. If the device is already open, then return
* SMA_IN_USE. The device ID that is returned is always 1.
*
* Notes:
* None
*
*********************************************************************/
INT_32 i2c_open (INT_32 ipbase, INT_32 arg)
{
// I2C_TIMER_T i2c_timer;
I2C_CONFIG_T *conf;
/* make sure it is the correct device */
if(I2C_BASE != ipbase)
{ /* wrong device */
return 0;
}
/* must not be previously opened */
if(i2c_state.state & I2C_STATE_OPEN)
{ /* has already been initialized */
return 0;
}
else
{ /* init from the data structure */
/* mark it as initialized and reset other modes. polling mode
is default. user must set up interrupts externally to
use them */
i2c_state.state = I2C_STATE_OPEN | I2C_STATE_POLL;
i2c_state.regs = (REGS_T *)I2C_BASE;
/* read the status register to clear interrupts, if any */
i2c_state.interrupts = i2c_state.regs->icstat;
/* clear the tracking vars */
i2c_state.interrupts = 0;
i2c_state.tx_aborts = 0;
i2c_state.rx_aborts = 0;
i2c_state.num_reads = 0;
i2c_state.num_writes = 0;
/* check if there is configuration information in the arg */
if(arg != 0)
{ /* use settings from data structure */
conf = (I2C_CONFIG_T *)arg;
switch(conf->mode)
{
case I2C_MODE_7BIT_SLAVE:
case I2C_MODE_10BIT_SLAVE:
case I2C_MODE_7BIT_MASTER:
case I2C_MODE_10BIT_MASTER:
/* implicitly set low speed */
i2c_state.regs->iccon = conf->mode | I2C_ICCON_EN;
break;
default:
/* invalid mode */
return 0;
}
/* switch to high speed */
if(conf->speed)
{
i2c_state.regs->iccon |= I2C_ICCON_SPEED;
i2c_state.regs->ichcnt = I2C_FAST_HIGH_COUNTER;
i2c_state.regs->iclcnt = I2C_FAST_LOW_COUNTER;
}
else
{
i2c_state.regs->ichcnt = I2C_SLOW_HIGH_COUNTER;
i2c_state.regs->iclcnt = I2C_SLOW_LOW_COUNTER;
}
i2c_state.regs->icsar = conf->low_slave_addr;
i2c_state.regs->icusar = conf->high_slave_addr;
}
else
{ /* using default settings */
/* set the speed to 100kHz, 7 bit master, and enable interface */
i2c_state.regs->iccon = I2C_MODE_7BIT_MASTER | I2C_ICCON_EN;
/* no slave address in master mode */
i2c_state.regs->icsar = 0;
i2c_state.regs->icusar = 0;
/* bus speed default to low */
i2c_state.regs->ichcnt = I2C_SLOW_HIGH_COUNTER;
i2c_state.regs->iclcnt = I2C_SLOW_LOW_COUNTER;
}
}
/* return device id */
return ((INT_32)I2C_BASE);
}
/***********************************************************************
*
* Function: i2c_close
*
* Purpose:
* Turn the driver off
*
* Processing:
* Disable the device
*
* Parameters:
* devid - device id
*
* Outputs:
* None
*
* Returns:
* _NO_ERROR if device is closed successfully.
* _ERROR otherwise.
*
* Notes: None
*
**********************************************************************/
STATUS i2c_close (INT_32 devid)
{
INT_32 status = _NO_ERROR;
/* verify that we are talking to the correct device */
if(I2C_BASE != devid)
{
return SMA_BAD_HANDLE;
}
/* verify that the device is opened */
if(!(i2c_state.state & I2C_STATE_OPEN))
{
return SMA_NOT_OPEN;
}
/* disable the device */
i2c_state.regs->iccon = 0;
i2c_state.state = 0;
/* Done */
return (status);
}
/***********************************************************************
*
* Function: i2c_read
*
* Purpose:
* Read the i2c data.
*
* Processing:
* Read exactly one byte from the I2C device.
*
* Parameters:
* devid - Device id
* byte - Copy the byte that was read here
* attrib - Instruct the I2C to do something special with the byte
* transfer. Valid values are I2C_LAST_BYTE or I2C_SABT.
*
* Outputs:
* None
*
* Returns:
* Success: 1 if there was a byte to be read.
* Failure: SMA_BAD_HANDLE - Handle pased to the function was bad
* SMA_NOT_OPEN - The device is not currently open
* Notes:
* None
*
**********************************************************************/
INT_32 i2c_read (INT_32 devid, CHAR *byte, UNS_32 attrib)
{
INT_32 old_abort = i2c_state.rx_aborts;
/* verify that we are talking to the correct device */
if(I2C_BASE != devid)
{
return SMA_BAD_HANDLE;
}
/* verify that the device is opened */
if(!(i2c_state.state & I2C_STATE_OPEN))
{
return SMA_NOT_OPEN;
}
/* make sure we are not already expecting a byte */
i2c_state.state |= I2C_STATE_BUSY;
/* send the byte with the flags specified by the user */
*byte = (CHAR)i2c_state.regs->icdata;
i2c_state.regs->iccon = I2C_MODE_BITS(i2c_state.regs->iccon) |
I2C_ICCON_START | attrib;
if(i2c_state.state & I2C_STATE_POLL)
{ /* wait for the interrupt bit to be set */
if(i2c_wait_int())
{
return _ERROR;
}
}
else
{ /* default is to wait for the interrupt */
if(i2c_wait_busy())
{
return _ERROR;
}
}
i2c_state.num_reads++;
/*check if the read was aborted*/
if(old_abort != i2c_state.rx_aborts)
{
return _ERROR;
}
/* return */
return 1;
}
/***********************************************************************
*
* Function: i2c_write
*
* Purpose:
* Write data to i2c.
*
* Processing:
* Write exactly one byte to the I2C interface.
*
*
* Parameters:
* devid - device id
* byte - byte to write
* attrib - instruct the I2C to do something special with the byte
* transfer. Valid values are I2C_LAST_BYTE or I2C_SABT.
*
* Outputs:
* None
*
* Returns:
* Success: return 1
* Failure: SMA_BAD_HANDLE - Handle pased to the function was bad
* SMA_NOT_OPEN - The device is not currently open
*
* Notes:
* In master mode, the destination address and the read/write bit
* are expected to be pre-set in the buffer that is passed to this
* funciton.
*
* The caller must supply all of the attributes that are required
* for each byte read or write. Attributes not supplied are
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -