📄 i2c-imx.c
字号:
/****************************************************************************** * Copyright (C) 2002 Motorola GSG-China * * This program 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 * of the License, or (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * File Name : i2c-imx.c * Description : Implementation of i2c Adapter/Algorithm Driver * Author : Motorola GSG-China * History : 2002/2/7 use msgs[] * 2004/3/3 port to linux kernel v2.6.x for IMX * adding cpu- and bus-usage optimization * (by T.Koschorrek, http://www.synertronixx.de/) * * Additional Description (T. Koschorrek) : * - to enable debug messages just #define DEBUG * - this module supports cpu- and bus-usage-optimization for ad-converter * 'ads1100' and for serial access timekeeper 'm41t00' * - support for other chips is NOT yet implemented (you can implement other * chips by adding two lines in the init-function (i2c_imx_init) * - this module can be accessed via the i2c-dev interface from user-space * - access from kernel-space is not supported (add this if you want) * ******************************************************************************//****************************************************************************** * global stuff ******************************************************************************//*----------------------------------------------------------------------------- * included header files *----------------------------------------------------------------------------*/#include <linux/i2c.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/spinlock.h>#include <asm/arch/irqs.h>#include <asm/arch/hardware.h>#include <asm/leds.h>/*----------------------------------------------------------------------------- * defines *----------------------------------------------------------------------------*/#define DEFAULT_FREQ 0x16#define I2C_IO_CHANGE_FREQ 0xaa#define I2C_IO_GET_STATUS 0xab#define I2C_IMX_TIMEOUT 5000000/* #define DEBUG *//*----------------------------------------------------------------------------- * function declarations *----------------------------------------------------------------------------*/static int i2c_imx_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[ ], int num);static int i2c_imx_smbus(struct i2c_adapter * adapter, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data * data);static int i2c_imx_ioctl(struct i2c_adapter * adapter, unsigned int cmd, unsigned long arg);/*----------------------------------------------------------------------------- * structs for i2c *----------------------------------------------------------------------------*//* algorithm structure for handling with i2c-bus */static struct i2c_algorithm i2c_imx_algorithm = { /* name: */ "IMX I2C algorithm", /* id: */ I2C_ALGO_BIT, /* master_xfer: */ i2c_imx_xfer, /* smbus_xfer: */ i2c_imx_smbus, /* slave_send: */ NULL, /* slave_recv: */ NULL, /* algo_control: */ i2c_imx_ioctl, /* functionality: */ NULL};/* adapter structure to the i2c-bus */static struct i2c_adapter i2c_imx_adapter = { /* name: */ .name = "IMX I2C adapter", /* i2c_algorithm: */ .algo = &i2c_imx_algorithm, /* class: */ .class = I2C_ADAP_CLASS_SMBUS, /* ID: */ .id = I2C_ALGO_BIT | I2C_HW_B_SER,};/* imx-registers for i2c */struct i2c_imx_i2creg { volatile u32 iadr; volatile u32 ifdr; volatile u32 i2cr; volatile u32 i2sr; volatile u32 i2dr;};struct i2c_imx_i2cslp { unsigned long jif; unsigned int slp_time;};/*----------------------------------------------------------------------------- * variables *----------------------------------------------------------------------------*/static struct i2c_imx_i2creg *i2c_imx_reg = (struct i2c_imx_i2creg *)IMX_I2C_BASE;static spinlock_t i2c_imx_irqlock = SPIN_LOCK_UNLOCKED;static int i2c_imx_irq_ok;static int i2c_imx_i2sr;static struct i2c_imx_i2cslp i2c_imx_slp[128];#ifdef DEBUGstatic int i2c_imx_errs_rxack = 0;static int i2c_imx_errs_txcomplete = 0;static int i2c_imx_errs_busbusy = 0;static int i2c_imx_errs_busgrab = 0;#endif/****************************************************************************** * functions (i2c-driver for IMX) ******************************************************************************//*----------------------------------------------------------------------------- * bus grab *----------------------------------------------------------------------------*/static int i2c_imx_bus_grab(void){ int i = 0; /* wait for bus grab */ while(!((i2c_imx_reg->i2sr & (u32)0x30)) ); if (i == I2C_IMX_TIMEOUT) return 4; return 0;}/*----------------------------------------------------------------------------- * bus busy *----------------------------------------------------------------------------*/static int i2c_imx_bus_busy(void){ int i = 0; /* wait for bus not busy */ while((i2c_imx_reg->i2sr & (u32)0x20) && (i < I2C_IMX_TIMEOUT)) i++; if (i == I2C_IMX_TIMEOUT) return 3; return 0;}/*----------------------------------------------------------------------------- * received ack *----------------------------------------------------------------------------*/static int i2c_imx_received_acknowledge(void){ if(i2c_imx_i2sr & 0x01) return 1; return 0;}/*----------------------------------------------------------------------------- * transfer complete *----------------------------------------------------------------------------*/static int i2c_imx_transfer_complete(void){ int i = 0; unsigned long flags; i2c_imx_i2sr = 0x00; /* wait for transfer complete interrupt */ while ((!(i2c_imx_irq_ok)) && (i < I2C_IMX_TIMEOUT)){ spin_lock_irqsave(&i2c_imx_irqlock, flags); i++; spin_unlock_irqrestore(&i2c_imx_irqlock, flags); } i2c_imx_irq_ok = 0; if (i == I2C_IMX_TIMEOUT) return 3; return 0;}/*----------------------------------------------------------------------------- * bus release *----------------------------------------------------------------------------*/static int i2c_imx_bus_release(void){ int i=0, dummy; /* if bus busy reset the module process suggested in reference manual (tahiti) */ if (i2c_imx_bus_busy() && (i < I2C_IMX_TIMEOUT)) { i2c_imx_reg->i2cr = (u32)0x00; i2c_imx_reg->i2cr |= (u32)0x0a; dummy = (u8)i2c_imx_reg->i2dr; i2c_imx_reg->i2sr = (u32)0x00; i2c_imx_reg->i2cr = (u32)0x00; i2c_imx_reg->i2cr &= ~((u32)0x80); i++; } return 0;}/*----------------------------------------------------------------------------- * start *----------------------------------------------------------------------------*/static int i2c_imx_start(void){ int i = 0; /* Set Master Mode */ i2c_imx_reg->i2cr |= 0x20; /* wait while bus grab */ while((i2c_imx_bus_grab()) && (i < I2C_IMX_TIMEOUT)) { i2c_imx_reg->i2cr &= ~((u32)0x20); /* bus stop */ i2c_imx_bus_release(); if (i2c_imx_bus_busy()) return 3; i2c_imx_reg->i2cr |= (u32)0x80; /* [I2CR:IEN] (I2C Enable) */ i2c_imx_reg->i2cr |= (u32)0x08; /* [I2CR:TXAK] dable txack */ i2c_imx_reg->i2cr |= (u32)0x20; /* Set Master Mode */ i++; } if (i == I2C_IMX_TIMEOUT) return 4; return 0;}/*----------------------------------------------------------------------------- * write *----------------------------------------------------------------------------*/static int i2c_imx_write(int i, int *count, struct i2c_msg *msgs){ int err, j; /* select slave */ i2c_imx_reg->i2dr = msgs->addr << 1; if ((err = i2c_imx_transfer_complete())) return err; if ((err = i2c_imx_received_acknowledge())) return err; /* write data */ for ( j = 0; j < msgs->len; j ++ ) { i2c_imx_reg->i2dr = msgs->buf[j]; if ((err = i2c_imx_transfer_complete())) return err; } count += msgs->len; return 0;}/*----------------------------------------------------------------------------- * read *----------------------------------------------------------------------------*/static int i2c_imx_read(int i, int *count, struct i2c_msg *msgs){ int err, j, dummy; /* select slave */ i2c_imx_reg->i2dr = ((u32)msgs->addr << 1) | (u32)0x01; if ((err = i2c_imx_transfer_complete())) return err; if ((err = i2c_imx_received_acknowledge())) return err; /* setup bus to read data */ i2c_imx_reg->i2cr &= ~(u32)0x10; if(msgs->len-1) i2c_imx_reg->i2cr &= ~(u32)0x08; dummy = (u8)i2c_imx_reg->i2dr; /* trigger rec. of next byte */ if ((err = i2c_imx_transfer_complete())) return err; /* read data */ for ( j = 0; j < msgs->len; j ++ ){ if(j== (msgs->len - 2)) i2c_imx_reg->i2cr |= 0x08; msgs->buf[j] = (u8)i2c_imx_reg->i2dr; if ((err = i2c_imx_transfer_complete())) return err; } count += msgs->len; return 0;}/*----------------------------------------------------------------------------- * chip_sleep functions: gets timeout-values for supported chips * you have to add own functions to get optimal timeouts for your own chips * now supported chips: m41t00 at 0x68, ads1100 at 0x48 *----------------------------------------------------------------------------*//* support for ads1100 at address 0x48 */void chip_slp_48(struct i2c_msg *msgs) { switch(msgs->buf[0] & 0x0c) { case 0x00: i2c_imx_slp[0x48-1].slp_time = 1; break; case 0x04: i2c_imx_slp[0x48-1].slp_time = 20; break; case 0x08: i2c_imx_slp[0x48-1].slp_time = 40; break; case 0x0c: i2c_imx_slp[0x48-1].slp_time = 70; break; } return;}/* chip_slp */void chip_slp(struct i2c_msg *msgs) { switch(msgs->addr) { case 0x48: chip_slp_48(msgs); return; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -