5dfe4c964a
Many struct file_operations in the kernel can be "const". Marking them const moves these to the .rodata section, which avoids false sharing with potential dirty data. In addition it'll catch accidental writes at compile time to these shared resources. [akpm@osdl.org: sparc64 fix] Signed-off-by: Arjan van de Ven <arjan@linux.intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
611 lines
12 KiB
C
611 lines
12 KiB
C
/*!***************************************************************************
|
|
*!
|
|
*! FILE NAME : i2c.c
|
|
*!
|
|
*! DESCRIPTION: implements an interface for IIC/I2C, both directly from other
|
|
*! kernel modules (i2c_writereg/readreg) and from userspace using
|
|
*! ioctl()'s
|
|
*!
|
|
*! Nov 30 1998 Torbjorn Eliasson Initial version.
|
|
*! Bjorn Wesen Elinux kernel version.
|
|
*! Jan 14 2000 Johan Adolfsson Fixed PB shadow register stuff -
|
|
*! don't use PB_I2C if DS1302 uses same bits,
|
|
*! use PB.
|
|
*| June 23 2003 Pieter Grimmerink Added 'i2c_sendnack'. i2c_readreg now
|
|
*| generates nack on last received byte,
|
|
*| instead of ack.
|
|
*| i2c_getack changed data level while clock
|
|
*| was high, causing DS75 to see a stop condition
|
|
*!
|
|
*! ---------------------------------------------------------------------------
|
|
*!
|
|
*! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN
|
|
*!
|
|
*!***************************************************************************/
|
|
/* $Id: i2c.c,v 1.2 2005/05/09 15:29:49 starvik Exp $ */
|
|
/****************** INCLUDE FILES SECTION ***********************************/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/string.h>
|
|
#include <linux/init.h>
|
|
|
|
#include <asm/etraxi2c.h>
|
|
|
|
#include <asm/system.h>
|
|
#include <asm/io.h>
|
|
#include <asm/delay.h>
|
|
|
|
#include "i2c.h"
|
|
|
|
/****************** I2C DEFINITION SECTION *************************/
|
|
|
|
#define D(x)
|
|
|
|
#define I2C_MAJOR 123 /* LOCAL/EXPERIMENTAL */
|
|
static const char i2c_name[] = "i2c";
|
|
|
|
#define CLOCK_LOW_TIME 8
|
|
#define CLOCK_HIGH_TIME 8
|
|
#define START_CONDITION_HOLD_TIME 8
|
|
#define STOP_CONDITION_HOLD_TIME 8
|
|
#define ENABLE_OUTPUT 0x01
|
|
#define ENABLE_INPUT 0x00
|
|
#define I2C_CLOCK_HIGH 1
|
|
#define I2C_CLOCK_LOW 0
|
|
#define I2C_DATA_HIGH 1
|
|
#define I2C_DATA_LOW 0
|
|
|
|
#define i2c_enable()
|
|
#define i2c_disable()
|
|
|
|
/* enable or disable output-enable, to select output or input on the i2c bus */
|
|
|
|
#define i2c_dir_out() crisv32_io_set_dir(&cris_i2c_data, crisv32_io_dir_out)
|
|
#define i2c_dir_in() crisv32_io_set_dir(&cris_i2c_data, crisv32_io_dir_in)
|
|
|
|
/* control the i2c clock and data signals */
|
|
|
|
#define i2c_clk(x) crisv32_io_set(&cris_i2c_clk, x)
|
|
#define i2c_data(x) crisv32_io_set(&cris_i2c_data, x)
|
|
|
|
/* read a bit from the i2c interface */
|
|
|
|
#define i2c_getbit() crisv32_io_rd(&cris_i2c_data)
|
|
|
|
#define i2c_delay(usecs) udelay(usecs)
|
|
|
|
/****************** VARIABLE SECTION ************************************/
|
|
|
|
static struct crisv32_iopin cris_i2c_clk;
|
|
static struct crisv32_iopin cris_i2c_data;
|
|
|
|
/****************** FUNCTION DEFINITION SECTION *************************/
|
|
|
|
|
|
/* generate i2c start condition */
|
|
|
|
void
|
|
i2c_start(void)
|
|
{
|
|
/*
|
|
* SCL=1 SDA=1
|
|
*/
|
|
i2c_dir_out();
|
|
i2c_delay(CLOCK_HIGH_TIME/6);
|
|
i2c_data(I2C_DATA_HIGH);
|
|
i2c_clk(I2C_CLOCK_HIGH);
|
|
i2c_delay(CLOCK_HIGH_TIME);
|
|
/*
|
|
* SCL=1 SDA=0
|
|
*/
|
|
i2c_data(I2C_DATA_LOW);
|
|
i2c_delay(START_CONDITION_HOLD_TIME);
|
|
/*
|
|
* SCL=0 SDA=0
|
|
*/
|
|
i2c_clk(I2C_CLOCK_LOW);
|
|
i2c_delay(CLOCK_LOW_TIME);
|
|
}
|
|
|
|
/* generate i2c stop condition */
|
|
|
|
void
|
|
i2c_stop(void)
|
|
{
|
|
i2c_dir_out();
|
|
|
|
/*
|
|
* SCL=0 SDA=0
|
|
*/
|
|
i2c_clk(I2C_CLOCK_LOW);
|
|
i2c_data(I2C_DATA_LOW);
|
|
i2c_delay(CLOCK_LOW_TIME*2);
|
|
/*
|
|
* SCL=1 SDA=0
|
|
*/
|
|
i2c_clk(I2C_CLOCK_HIGH);
|
|
i2c_delay(CLOCK_HIGH_TIME*2);
|
|
/*
|
|
* SCL=1 SDA=1
|
|
*/
|
|
i2c_data(I2C_DATA_HIGH);
|
|
i2c_delay(STOP_CONDITION_HOLD_TIME);
|
|
|
|
i2c_dir_in();
|
|
}
|
|
|
|
/* write a byte to the i2c interface */
|
|
|
|
void
|
|
i2c_outbyte(unsigned char x)
|
|
{
|
|
int i;
|
|
|
|
i2c_dir_out();
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
if (x & 0x80) {
|
|
i2c_data(I2C_DATA_HIGH);
|
|
} else {
|
|
i2c_data(I2C_DATA_LOW);
|
|
}
|
|
|
|
i2c_delay(CLOCK_LOW_TIME/2);
|
|
i2c_clk(I2C_CLOCK_HIGH);
|
|
i2c_delay(CLOCK_HIGH_TIME);
|
|
i2c_clk(I2C_CLOCK_LOW);
|
|
i2c_delay(CLOCK_LOW_TIME/2);
|
|
x <<= 1;
|
|
}
|
|
i2c_data(I2C_DATA_LOW);
|
|
i2c_delay(CLOCK_LOW_TIME/2);
|
|
|
|
/*
|
|
* enable input
|
|
*/
|
|
i2c_dir_in();
|
|
}
|
|
|
|
/* read a byte from the i2c interface */
|
|
|
|
unsigned char
|
|
i2c_inbyte(void)
|
|
{
|
|
unsigned char aBitByte = 0;
|
|
int i;
|
|
|
|
/* Switch off I2C to get bit */
|
|
i2c_disable();
|
|
i2c_dir_in();
|
|
i2c_delay(CLOCK_HIGH_TIME/2);
|
|
|
|
/* Get bit */
|
|
aBitByte |= i2c_getbit();
|
|
|
|
/* Enable I2C */
|
|
i2c_enable();
|
|
i2c_delay(CLOCK_LOW_TIME/2);
|
|
|
|
for (i = 1; i < 8; i++) {
|
|
aBitByte <<= 1;
|
|
/* Clock pulse */
|
|
i2c_clk(I2C_CLOCK_HIGH);
|
|
i2c_delay(CLOCK_HIGH_TIME);
|
|
i2c_clk(I2C_CLOCK_LOW);
|
|
i2c_delay(CLOCK_LOW_TIME);
|
|
|
|
/* Switch off I2C to get bit */
|
|
i2c_disable();
|
|
i2c_dir_in();
|
|
i2c_delay(CLOCK_HIGH_TIME/2);
|
|
|
|
/* Get bit */
|
|
aBitByte |= i2c_getbit();
|
|
|
|
/* Enable I2C */
|
|
i2c_enable();
|
|
i2c_delay(CLOCK_LOW_TIME/2);
|
|
}
|
|
i2c_clk(I2C_CLOCK_HIGH);
|
|
i2c_delay(CLOCK_HIGH_TIME);
|
|
|
|
/*
|
|
* we leave the clock low, getbyte is usually followed
|
|
* by sendack/nack, they assume the clock to be low
|
|
*/
|
|
i2c_clk(I2C_CLOCK_LOW);
|
|
return aBitByte;
|
|
}
|
|
|
|
/*#---------------------------------------------------------------------------
|
|
*#
|
|
*# FUNCTION NAME: i2c_getack
|
|
*#
|
|
*# DESCRIPTION : checks if ack was received from ic2
|
|
*#
|
|
*#--------------------------------------------------------------------------*/
|
|
|
|
int
|
|
i2c_getack(void)
|
|
{
|
|
int ack = 1;
|
|
/*
|
|
* enable output
|
|
*/
|
|
i2c_dir_out();
|
|
/*
|
|
* Release data bus by setting
|
|
* data high
|
|
*/
|
|
i2c_data(I2C_DATA_HIGH);
|
|
/*
|
|
* enable input
|
|
*/
|
|
i2c_dir_in();
|
|
i2c_delay(CLOCK_HIGH_TIME/4);
|
|
/*
|
|
* generate ACK clock pulse
|
|
*/
|
|
i2c_clk(I2C_CLOCK_HIGH);
|
|
/*
|
|
* Use PORT PB instead of I2C
|
|
* for input. (I2C not working)
|
|
*/
|
|
i2c_clk(1);
|
|
i2c_data(1);
|
|
/*
|
|
* switch off I2C
|
|
*/
|
|
i2c_data(1);
|
|
i2c_disable();
|
|
i2c_dir_in();
|
|
/*
|
|
* now wait for ack
|
|
*/
|
|
i2c_delay(CLOCK_HIGH_TIME/2);
|
|
/*
|
|
* check for ack
|
|
*/
|
|
if(i2c_getbit())
|
|
ack = 0;
|
|
i2c_delay(CLOCK_HIGH_TIME/2);
|
|
if(!ack){
|
|
if(!i2c_getbit()) /* receiver pulld SDA low */
|
|
ack = 1;
|
|
i2c_delay(CLOCK_HIGH_TIME/2);
|
|
}
|
|
|
|
/*
|
|
* our clock is high now, make sure data is low
|
|
* before we enable our output. If we keep data high
|
|
* and enable output, we would generate a stop condition.
|
|
*/
|
|
i2c_data(I2C_DATA_LOW);
|
|
|
|
/*
|
|
* end clock pulse
|
|
*/
|
|
i2c_enable();
|
|
i2c_dir_out();
|
|
i2c_clk(I2C_CLOCK_LOW);
|
|
i2c_delay(CLOCK_HIGH_TIME/4);
|
|
/*
|
|
* enable output
|
|
*/
|
|
i2c_dir_out();
|
|
/*
|
|
* remove ACK clock pulse
|
|
*/
|
|
i2c_data(I2C_DATA_HIGH);
|
|
i2c_delay(CLOCK_LOW_TIME/2);
|
|
return ack;
|
|
}
|
|
|
|
/*#---------------------------------------------------------------------------
|
|
*#
|
|
*# FUNCTION NAME: I2C::sendAck
|
|
*#
|
|
*# DESCRIPTION : Send ACK on received data
|
|
*#
|
|
*#--------------------------------------------------------------------------*/
|
|
void
|
|
i2c_sendack(void)
|
|
{
|
|
/*
|
|
* enable output
|
|
*/
|
|
i2c_delay(CLOCK_LOW_TIME);
|
|
i2c_dir_out();
|
|
/*
|
|
* set ack pulse high
|
|
*/
|
|
i2c_data(I2C_DATA_LOW);
|
|
/*
|
|
* generate clock pulse
|
|
*/
|
|
i2c_delay(CLOCK_HIGH_TIME/6);
|
|
i2c_clk(I2C_CLOCK_HIGH);
|
|
i2c_delay(CLOCK_HIGH_TIME);
|
|
i2c_clk(I2C_CLOCK_LOW);
|
|
i2c_delay(CLOCK_LOW_TIME/6);
|
|
/*
|
|
* reset data out
|
|
*/
|
|
i2c_data(I2C_DATA_HIGH);
|
|
i2c_delay(CLOCK_LOW_TIME);
|
|
|
|
i2c_dir_in();
|
|
}
|
|
|
|
/*#---------------------------------------------------------------------------
|
|
*#
|
|
*# FUNCTION NAME: i2c_sendnack
|
|
*#
|
|
*# DESCRIPTION : Sends NACK on received data
|
|
*#
|
|
*#--------------------------------------------------------------------------*/
|
|
void
|
|
i2c_sendnack(void)
|
|
{
|
|
/*
|
|
* enable output
|
|
*/
|
|
i2c_delay(CLOCK_LOW_TIME);
|
|
i2c_dir_out();
|
|
/*
|
|
* set data high
|
|
*/
|
|
i2c_data(I2C_DATA_HIGH);
|
|
/*
|
|
* generate clock pulse
|
|
*/
|
|
i2c_delay(CLOCK_HIGH_TIME/6);
|
|
i2c_clk(I2C_CLOCK_HIGH);
|
|
i2c_delay(CLOCK_HIGH_TIME);
|
|
i2c_clk(I2C_CLOCK_LOW);
|
|
i2c_delay(CLOCK_LOW_TIME);
|
|
|
|
i2c_dir_in();
|
|
}
|
|
|
|
/*#---------------------------------------------------------------------------
|
|
*#
|
|
*# FUNCTION NAME: i2c_writereg
|
|
*#
|
|
*# DESCRIPTION : Writes a value to an I2C device
|
|
*#
|
|
*#--------------------------------------------------------------------------*/
|
|
int
|
|
i2c_writereg(unsigned char theSlave, unsigned char theReg,
|
|
unsigned char theValue)
|
|
{
|
|
int error, cntr = 3;
|
|
unsigned long flags;
|
|
|
|
do {
|
|
error = 0;
|
|
/*
|
|
* we don't like to be interrupted
|
|
*/
|
|
local_irq_save(flags);
|
|
|
|
i2c_start();
|
|
/*
|
|
* send slave address
|
|
*/
|
|
i2c_outbyte((theSlave & 0xfe));
|
|
/*
|
|
* wait for ack
|
|
*/
|
|
if(!i2c_getack())
|
|
error = 1;
|
|
/*
|
|
* now select register
|
|
*/
|
|
i2c_dir_out();
|
|
i2c_outbyte(theReg);
|
|
/*
|
|
* now it's time to wait for ack
|
|
*/
|
|
if(!i2c_getack())
|
|
error |= 2;
|
|
/*
|
|
* send register register data
|
|
*/
|
|
i2c_outbyte(theValue);
|
|
/*
|
|
* now it's time to wait for ack
|
|
*/
|
|
if(!i2c_getack())
|
|
error |= 4;
|
|
/*
|
|
* end byte stream
|
|
*/
|
|
i2c_stop();
|
|
/*
|
|
* enable interrupt again
|
|
*/
|
|
local_irq_restore(flags);
|
|
|
|
} while(error && cntr--);
|
|
|
|
i2c_delay(CLOCK_LOW_TIME);
|
|
|
|
return -error;
|
|
}
|
|
|
|
/*#---------------------------------------------------------------------------
|
|
*#
|
|
*# FUNCTION NAME: i2c_readreg
|
|
*#
|
|
*# DESCRIPTION : Reads a value from the decoder registers.
|
|
*#
|
|
*#--------------------------------------------------------------------------*/
|
|
unsigned char
|
|
i2c_readreg(unsigned char theSlave, unsigned char theReg)
|
|
{
|
|
unsigned char b = 0;
|
|
int error, cntr = 3;
|
|
unsigned long flags;
|
|
|
|
do {
|
|
error = 0;
|
|
/*
|
|
* we don't like to be interrupted
|
|
*/
|
|
local_irq_save(flags);
|
|
/*
|
|
* generate start condition
|
|
*/
|
|
i2c_start();
|
|
|
|
/*
|
|
* send slave address
|
|
*/
|
|
i2c_outbyte((theSlave & 0xfe));
|
|
/*
|
|
* wait for ack
|
|
*/
|
|
if(!i2c_getack())
|
|
error = 1;
|
|
/*
|
|
* now select register
|
|
*/
|
|
i2c_dir_out();
|
|
i2c_outbyte(theReg);
|
|
/*
|
|
* now it's time to wait for ack
|
|
*/
|
|
if(!i2c_getack())
|
|
error = 1;
|
|
/*
|
|
* repeat start condition
|
|
*/
|
|
i2c_delay(CLOCK_LOW_TIME);
|
|
i2c_start();
|
|
/*
|
|
* send slave address
|
|
*/
|
|
i2c_outbyte(theSlave | 0x01);
|
|
/*
|
|
* wait for ack
|
|
*/
|
|
if(!i2c_getack())
|
|
error = 1;
|
|
/*
|
|
* fetch register
|
|
*/
|
|
b = i2c_inbyte();
|
|
/*
|
|
* last received byte needs to be nacked
|
|
* instead of acked
|
|
*/
|
|
i2c_sendnack();
|
|
/*
|
|
* end sequence
|
|
*/
|
|
i2c_stop();
|
|
/*
|
|
* enable interrupt again
|
|
*/
|
|
local_irq_restore(flags);
|
|
|
|
} while(error && cntr--);
|
|
|
|
return b;
|
|
}
|
|
|
|
static int
|
|
i2c_open(struct inode *inode, struct file *filp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
i2c_release(struct inode *inode, struct file *filp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* Main device API. ioctl's to write or read to/from i2c registers.
|
|
*/
|
|
|
|
static int
|
|
i2c_ioctl(struct inode *inode, struct file *file,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
if(_IOC_TYPE(cmd) != ETRAXI2C_IOCTYPE) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (_IOC_NR(cmd)) {
|
|
case I2C_WRITEREG:
|
|
/* write to an i2c slave */
|
|
D(printk("i2cw %d %d %d\n",
|
|
I2C_ARGSLAVE(arg),
|
|
I2C_ARGREG(arg),
|
|
I2C_ARGVALUE(arg)));
|
|
|
|
return i2c_writereg(I2C_ARGSLAVE(arg),
|
|
I2C_ARGREG(arg),
|
|
I2C_ARGVALUE(arg));
|
|
case I2C_READREG:
|
|
{
|
|
unsigned char val;
|
|
/* read from an i2c slave */
|
|
D(printk("i2cr %d %d ",
|
|
I2C_ARGSLAVE(arg),
|
|
I2C_ARGREG(arg)));
|
|
val = i2c_readreg(I2C_ARGSLAVE(arg), I2C_ARGREG(arg));
|
|
D(printk("= %d\n", val));
|
|
return val;
|
|
}
|
|
default:
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct file_operations i2c_fops = {
|
|
owner: THIS_MODULE,
|
|
ioctl: i2c_ioctl,
|
|
open: i2c_open,
|
|
release: i2c_release,
|
|
};
|
|
|
|
int __init
|
|
i2c_init(void)
|
|
{
|
|
int res;
|
|
|
|
/* Setup and enable the Port B I2C interface */
|
|
|
|
crisv32_io_get_name(&cris_i2c_data, CONFIG_ETRAX_I2C_DATA_PORT);
|
|
crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_I2C_CLK_PORT);
|
|
|
|
/* register char device */
|
|
|
|
res = register_chrdev(I2C_MAJOR, i2c_name, &i2c_fops);
|
|
if(res < 0) {
|
|
printk(KERN_ERR "i2c: couldn't get a major number.\n");
|
|
return res;
|
|
}
|
|
|
|
printk(KERN_INFO "I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* this makes sure that i2c_init is called during boot */
|
|
|
|
module_init(i2c_init);
|
|
|
|
/****************** END OF FILE i2c.c ********************************/
|