📄 i2c-iop3xx.c
字号:
/* ------------------------------------------------------------------------- *//* i2c-iop3xx.c i2c driver algorithms for Intel XScale IOP3xx *//* ------------------------------------------------------------------------- *//* Copyright (C) 2003 Peter Milne, D-TACQ Solutions Ltd * <Peter dot Milne at D hyphen TACQ dot com> 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. 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., 675 Mass Ave, Cambridge, MA 02139, USA. *//* ------------------------------------------------------------------------- *//* With acknowledgements to i2c-algo-ibm_ocp.c by Ian DaSilva, MontaVista Software, Inc. idasilva@mvista.com And i2c-algo-pcf.c, which was created by Simon G. Vogl and Hans Berglund: Copyright (C) 1995-1997 Simon G. Vogl, 1998-2000 Hans Berglund And which acknowledged Ky鰏ti M鋖kki <kmalkki@cc.hut.fi>, Frodo Looijaard <frodol@dds.nl>, Martin Bailey<mbailey@littlefeet-inc.com> ---------------------------------------------------------------------------*/#include <linux/interrupt.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/delay.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/sched.h>#include <linux/i2c.h>#include <asm/arch-iop3xx/iop321.h>#include <asm/arch-iop3xx/iop321-irqs.h>#include "i2c-iop3xx.h"/* ----- global defines ----------------------------------------------- */#define PASSERT(x) do { if (!(x) ) \ printk(KERN_CRIT "PASSERT %s in %s:%d\n", #x, __FILE__, __LINE__ );\ } while (0)#define DEB(x) if (i2c_debug>=1) x#define DEB2(x) if (i2c_debug>=2) x#define DEB3(x) if (i2c_debug>=3) x /* print several statistical values*/#define DEBPROTO(x) if (i2c_debug>=9) x; /* debug the protocol by showing transferred bits *//* ----- global variables --------------------------------------------- *//* module parameters: */static int i2c_debug = 1;static inline unsigned char iic_cook_addr(struct i2c_msg *msg) { unsigned char addr; addr = ( msg->addr << 1 ); if (msg->flags & I2C_M_RD ) addr |= 1;/* PGM: what is M_REV_DIR_ADDR - do we need it ?? */ if (msg->flags & I2C_M_REV_DIR_ADDR ) addr ^= 1; return addr; }static inline void iop3xx_adap_reset(struct i2c_algo_iop3xx_data *iop3xx_adap){ // Follows devman 9.3 *iop3xx_adap->biu->CR = IOP321_ICR_UNIT_RESET; *iop3xx_adap->biu->SR = IOP321_ISR_CLEARBITS; *iop3xx_adap->biu->CR = 0;} static inline void iop3xx_adap_set_slave_addr( struct i2c_algo_iop3xx_data *iop3xx_adap ){ *iop3xx_adap->biu->SAR = MYSAR;}static inline void iop3xx_adap_enable( struct i2c_algo_iop3xx_data *iop3xx_adap ){ u32 cr = IOP321_ICR_GCD|IOP321_ICR_SCLEN|IOP321_ICR_UE;/* NB SR bits not same position as CR IE bits :-( */ iop3xx_adap->biu->SR_enabled = IOP321_ISR_ALD|IOP321_ISR_BERRD| IOP321_ISR_RXFULL|IOP321_ISR_TXEMPTY; cr |= IOP321_ICR_ALDIE|IOP321_ICR_BERRIE| IOP321_ICR_RXFULLIE|IOP321_ICR_TXEMPTYIE; *iop3xx_adap->biu->CR = cr;}static void iop3xx_adap_transaction_cleanup( struct i2c_algo_iop3xx_data *iop3xx_adap ){ unsigned cr = *iop3xx_adap->biu->CR; cr &= ~(IOP321_ICR_MSTART | IOP321_ICR_TBYTE | IOP321_ICR_MSTOP | IOP321_ICR_SCLEN ); *iop3xx_adap->biu->CR = cr;}static void iop3xx_adap_final_cleanup( struct i2c_algo_iop3xx_data *iop3xx_adap ){ unsigned cr = *iop3xx_adap->biu->CR; cr &= ~(IOP321_ICR_ALDIE|IOP321_ICR_BERRIE| IOP321_ICR_RXFULLIE|IOP321_ICR_TXEMPTYIE); iop3xx_adap->biu->SR_enabled = 0; *iop3xx_adap->biu->CR = cr;}static void iop3xx_i2c_handler( int this_irq, void *dev_id, struct pt_regs *regs) /* * NB: the handler has to clear the source of the interrupt! * Then it passes the SR flags of interest to BH via adap data */{#define DFN KERN_DEBUG "iop3xx_i2c_handler() " struct i2c_algo_iop3xx_data *iop3xx_adap = dev_id; u32 sr = *iop3xx_adap->biu->SR; DEB2(printk(DFN" SR 0x%08x jiffies %10ld\n", sr, jiffies )); if ( (sr &= iop3xx_adap->biu->SR_enabled) ){ *iop3xx_adap->biu->SR = sr; iop3xx_adap->biu->SR_received |= sr; DEB2(printk(DFN" wake client for 0x%08x\n", iop3xx_adap->biu->SR_received )); wake_up_interruptible(&iop3xx_adap->waitq); }else{ DEB(printk(DFN"Interrupt with no status 0x%08x\n", sr ) ); }#undef DFN}static int iop3xx_adap_error( u32 sr )// check all error conditions, clear them , report most important{ int rc = 0; if ( (sr&IOP321_ISR_BERRD) ) { if ( !rc ) rc = -I2C_ERR_BERR; } if ( (sr&IOP321_ISR_ALD) ){ if ( !rc ) rc = -I2C_ERR_ALD; } return rc; }static inline u32 get_srstat( struct i2c_algo_iop3xx_data *iop3xx_adap ){#define DFN KERN_DEBUG "get_srstat() " unsigned long flags; u32 sr; DEB2(printk(DFN" enter lock\n" )); spin_lock_irqsave( &iop3xx_adap->lock, flags ); sr = iop3xx_adap->biu->SR_received; iop3xx_adap->biu->SR_received = 0; spin_unlock_irqrestore( &iop3xx_adap->lock, flags ); DEB2(printk(DFN" leave lock\n" )); return sr;#undef DFN}/* * sleep until interrupted, then recover and analyse the SR * saved by handler */typedef int (* CompareFunc)( unsigned test, unsigned mask );/* returns 1 on correct comparison */static int iop3xx_adap_wait_event( struct i2c_algo_iop3xx_data *iop3xx_adap, unsigned flags, unsigned* status, CompareFunc compare ){#define DFN KERN_DEBUG "adap_wait_event() " unsigned sr = 0; int interrupted; int done; int rc; long jiffies1 = jiffies; do { interrupted = wait_event_interruptible_timeout ( iop3xx_adap->waitq, (done = compare( sr = get_srstat(iop3xx_adap),flags )), iop3xx_adap->timeout ); if ( (rc = iop3xx_adap_error( sr )) < 0 ){ *status = sr; return rc; }else if ( !interrupted ){ *status = sr; DEB(printk(DFN "TIMEOUT %ld\n", jiffies )); return rc = -ETIMEDOUT; } } while( !done ); *status = sr; DEB3(printk( DFN"%ld %ld\n", jiffies1, jiffies ) ); return rc = 0;#undef DFN}/* * Concrete CompareFuncs */static int all_bits_clear( unsigned test, unsigned mask ) { return (test&mask) == 0;}static int any_bits_set( unsigned test, unsigned mask ) { return (test&mask) != 0;}static int iop3xx_adap_wait_tx_done( struct i2c_algo_iop3xx_data *iop3xx_adap, int *status){ return iop3xx_adap_wait_event( iop3xx_adap, IOP321_ISR_TXEMPTY|IOP321_ISR_ALD|IOP321_ISR_BERRD, status, any_bits_set );}static int iop3xx_adap_wait_rx_done( struct i2c_algo_iop3xx_data *iop3xx_adap, int *status){ return iop3xx_adap_wait_event( iop3xx_adap, IOP321_ISR_RXFULL|IOP321_ISR_ALD|IOP321_ISR_BERRD, status, any_bits_set );}static int iop3xx_adap_wait_idle( struct i2c_algo_iop3xx_data *iop3xx_adap, int *status){ return iop3xx_adap_wait_event( iop3xx_adap, IOP321_ISR_UNITBUSY, status, all_bits_clear );}//// Description: This performs the IOP3xx initialization sequence// Valid for IOP321. Maybe valid for IOP310?.//static int iop3xx_adap_init (struct i2c_algo_iop3xx_data *iop3xx_adap){ *IOP321_GPOD &= ~(iop3xx_adap->channel==0? IOP321_GPOD_I2C0: IOP321_GPOD_I2C1); iop3xx_adap_reset( iop3xx_adap ); iop3xx_adap_set_slave_addr( iop3xx_adap ); iop3xx_adap_enable( iop3xx_adap ); DEB2(printk(KERN_DEBUG "iic_init: Initialized IIC on IOP3xx\n")); return 0;}static int iop3xx_adap_send_target_slave_addr( struct i2c_algo_iop3xx_data *iop3xx_adap, struct i2c_msg* msg ){#define DFN KERN_DEBUG "iop3xx_adap_send_target_slave_addr() " unsigned cr = *iop3xx_adap->biu->CR; int status; int rc; *iop3xx_adap->biu->DBR = iic_cook_addr( msg ); cr &= ~(IOP321_ICR_MSTOP|IOP321_ICR_NACK); cr |= IOP321_ICR_MSTART | IOP321_ICR_TBYTE; *iop3xx_adap->biu->CR = cr; rc = iop3xx_adap_wait_tx_done( iop3xx_adap, &status );/* this assert fires every time, contrary to IOP manual PASSERT( (status&IOP321_ISR_UNITBUSY)!=0 );*/ PASSERT( (status&IOP321_ISR_RXREAD)==0 ); if ( rc < 0 ){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -