⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 i2c-algo-s3c2410.c

📁 s3c2410 arm-linux-2.4.18 i2c master driver
💻 C
📖 第 1 页 / 共 2 页
字号:
/*   -------------------------------------------------------------------------   i2c-algo-s3c2410.c i2c driver algorithms for the Samsung S3C2410X   processor and SMDK2410 reference board.   Steve Hein <ssh@sgi.com>   Copyright 2002 SGI, Inc.   -------------------------------------------------------------------------   This file was highly leveraged from i2c-algo-ppc405.c:      Ian DaSilva, MontaVista Software, Inc.   idasilva@mvista.com or source@mvista.com   Copyright 2000 MontaVista Software Inc.   Changes made to support the IIC peripheral on the IBM PPC 405   ---------------------------------------------------------------------------   This file was highly leveraged from 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   With some changes from Ky鰏ti M鋖kki <kmalkki@cc.hut.fi> and    Frodo Looijaard <frodol@dds.nl> ,and also from Martin Bailey   <mbailey@littlefeet-inc.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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.   ---------------------------------------------------------------------------*/#include <linux/kernel.h>#include <linux/module.h>#include <linux/delay.h>#include <linux/slab.h>#include <linux/version.h>#include <linux/init.h>#include <asm/uaccess.h>#include <linux/ioport.h>#include <linux/errno.h>#include <linux/sched.h>#include <linux/i2c.h>#include <linux/i2c-algo-s3c2410.h>#include "i2c-s3c2410.h"#undef KERN_DEBUG#define KERN_DEBUG#ifdef MODULE_LICENSEMODULE_LICENSE("GPL");#endif/* ----- global defines ----------------------------------------------- */#define DEB(x) if (s3c2410_i2c_debug>=1) x#define DEB2(x) if (s3c2410_i2c_debug>=2) x#define DEB3(x) if (s3c2410_i2c_debug>=3) x /* print several statistical values*/#define DEBPROTO(x) if (s3c2410_i2c_debug>=9) x; 	/* debug the protocol by showing transferred bits */#define DEF_TIMEOUT 2/* debugging - slow down transfer to have a look at the data .. 	*//* I use this with two leds&resistors, each one connected to sda,scl 	*//* respectively. This makes sure that the algorithm works. Some chips   *//* might not like this, as they have an internal timeout of some mils	*//*#define SLO_IO      jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\                        if (need_resched) schedule();*//* ----- global variables ---------------------------------------------	*/#ifdef SLO_IO	int jif;#endif/* module parameters: */int s3c2410_i2c_debug=0;static int i2c_clkdiv=1;/* --- setting states on the bus with the right timing: ---------------	*/#define s3c2410_outb(adap, reg, val) adap->setiic(adap->data, reg, val)#define s3c2410_inb(adap, reg) adap->getiic(adap->data, reg)#define IIC_SINGLE_XFER		0#define IIC_COMBINED_XFER	1/* --- other auxiliary functions --------------------------------------	*///// Description: returns the current speed of the I2C clock in kHz//static int s3c2410_clkspeed(void){	unsigned long pdiv = ((i2c_clkdiv / 100) != 0) ? 16 : 512;	unsigned long div = i2c_clkdiv % 100;	return (PCLK/pdiv)/(div+1)/1024;}//// Description: Puts this process to sleep for a period equal to timeout //static inline void s3c2410_sleep(unsigned long timeout){	schedule_timeout( timeout * HZ);}//// Description: This performs the Samsung S3C2410X IIC initialization sequence// as described in the S3C2410X data book.//static int s3c2410_init(struct i2c_algo_s3c2410_data *adap){	u8 conval = 0;	// initialize control/status regs to 0	s3c2410_outb(adap, S3C2410_IICCON, 0);	s3c2410_outb(adap, S3C2410_IICSTAT, 0);	// set up a dummy IIC slave addr (even though we never use it!)	s3c2410_outb(adap, S3C2410_IICADD, 0x10);		// set up clock frequency for IIC-bus	if (i2c_clkdiv/100) {	   /* IICCLK=PCLK/16 */	   conval |= (i2c_clkdiv%100);            /* Tx clk = IICCLK/(n+1) */	} else {	   conval |= S3C2410_IICCON_TCLK_PCLK512;  /* IICCLK=PCLK/512 */	   conval |= i2c_clkdiv;                   /* Tx clk = IICCLK/(n+1) */	}	// enable interrupts	conval |= S3C2410_IICCON_INT_EN;	// enable ACK generation	conval |= S3C2410_IICCON_ACK_EN;	// write out the control reg. value	s3c2410_outb(adap, S3C2410_IICCON, conval);	// enable I2C bus data output and set master transmit mode	// to get to a sane state (also generates a STOP condition)	s3c2410_outb(adap, S3C2410_IICSTAT, S3C2410_IICSTAT_MTX_MODE | S3C2410_IICSTAT_OUT_EN);	        DEB2(printk(KERN_DEBUG "s3c2410_init: Initialized IIC on S3C2410X, %dkHz clock\n", s3c2410_clkspeed()));	mdelay(20);        return 0;}//// Description: Attempts to reset the I2C controller/bus back to a sane state.//static int s3c2410_reset (struct i2c_algo_s3c2410_data *adap){	int ret;	int count = 0;	//	// re-initialize	//	s3c2410_init(adap);	//	// Assume all is OK if the bus is not busy....	//	while((ret = s3c2410_inb(adap, S3C2410_IICSTAT)) & S3C2410_IICSTAT_BUSY) {	  //	  // Generate stop condition	  //	  DEB2(printk(KERN_DEBUG "s3c2410_reset: Generating STOP condition\n"));	  s3c2410_outb(adap, S3C2410_IICSTAT, S3C2410_IICSTAT_MTX_ENABLE & ~S3C2410_IICSTAT_BUSY);	  //	  // Clear status register and enable ACK generation	  //	  DEB2(printk(KERN_DEBUG "s3c2410_reset: Clearing status register\n"));	  ret = s3c2410_inb(adap, S3C2410_IICCON);          ret = (ret & ~S3C2410_IICCON_INT_PEND) | S3C2410_IICCON_ACK_EN;	  s3c2410_outb(adap, S3C2410_IICCON, ret);          //	  // reset I2C again          //          s3c2410_init(adap);#if 0	  //	  // If still busy do a dummy read to reset the active slave device	  //	  ret = s3c2410_inb(adap, S3C2410_IICSTAT);	  if (ret & S3C2410_IICSTAT_BUSY) {	    DEB2(printk(KERN_DEBUG "s3c2410_reset: Clearing status register before dummy read\n"));	    ret = s3c2410_inb(adap, S3C2410_IICCON);            ret = ret & ~(S3C2410_IICCON_INT_PEND | S3C2410_IICCON_ACK_EN);	    s3c2410_outb(adap, S3C2410_IICCON, ret);	    DEB2(printk(KERN_DEBUG "s3c2410_reset: Dummy read\n"));	    s3c2410_outb(adap, S3C2410_IICSTAT, S3C2410_IICSTAT_MRX_ENABLE);	    s3c2410_inb(adap, S3C2410_IICDS);   // dummy read	    DEB2(printk(KERN_DEBUG "s3c2410_reset: Clearing status register after dummy read\n"));	    ret = s3c2410_inb(adap, S3C2410_IICCON);            ret = (ret & ~(S3C2410_IICCON_INT_PEND)) | S3C2410_IICCON_ACK_EN;	    s3c2410_outb(adap, S3C2410_IICCON, ret);	  }#endif	  //	  // Bail out after a more than reasonable number of attempts	  //	  if (count++ > 50) {	    printk(KERN_DEBUG "s3c2410_reset: I2C bus stuck BUSY!\n");	    return -EIO;	  }	}	return 0;}//// Description: After we issue a transaction on the IIC bus, this function// is called.  It puts this process to sleep until we get an interrupt from// from the controller telling us that the transaction we requested in complete.//static int wait_for_pin(struct i2c_algo_s3c2410_data *adap, int *status) {	int timeout = DEF_TIMEOUT+2;	//int retval;		*status = s3c2410_inb(adap, S3C2410_IICCON);	//printk("wait_for_pin: status = %x\n", *status);	while (timeout-- && !(*status & S3C2410_IICCON_INT_PEND)) {	   //printk("wait_for_pin: timeout=%d, status=%x\n", timeout, *status);	   //printk("wait_for_pin: calling waitforpin\n");	   adap->waitforpin();           //printk("wait_for_pin: returning from waitforpin\n");	   *status = s3c2410_inb(adap, S3C2410_IICCON);	   s3c2410_inb(adap, S3C2410_IICSTAT);	}	//printk("wait_for_pin: returning from wait_for_pin\n");	if (timeout <= 0) {	   // reset I2C	   s3c2410_reset(adap);#if 0	   /* Issue stop signal on the bus */           retval = s3c2410_inb(adap, S3C2410_IICSTAT);           s3c2410_outb(adap, S3C2410_IICSTAT, retval & ~S3C2410_IICSTAT_BUSY);	   /* Clear pending interrupt bit */           retval = s3c2410_inb(adap, S3C2410_IICCON);           s3c2410_outb(adap, S3C2410_IICCON, retval & ~S3C2410_IICCON_INT_PEND);	   // wait for the busy condition to clear	   udelay(adap->udelay);	   // Check the status of the controller.  Does it still see a	   // pending transfer, even though we've tried to stop any	   // ongoing transaction?           retval = s3c2410_inb(adap, S3C2410_IICSTAT);           if(retval & S3C2410_IICSTAT_BUSY) {	      // The iic controller didn't stop when we told it to....	      // The iic controller is hosed.              s3c2410_init(adap);	      /* Is the pending transfer bit in the sts reg finally cleared? */              retval = s3c2410_inb(adap, S3C2410_IICSTAT);              if(retval  & S3C2410_IICSTAT_BUSY) {                 printk("The IIC Controller is hosed.  A processor reset is required\n");              }           }#endif	   return(-ETIMEDOUT);	}	return(0);}//------------------------------------// Utility functions////// Description: This function tries to verify that the device we want to// talk to on the IIC bus really exists. //#if 0static inline int try_address(struct i2c_algo_s3c2410_data *adap,		       unsigned char addr, int retries){	int i, status, ret = -1;	for (i=0;i<retries;i++) {		i2c_outb(adap, addr);		i2c_start(adap);		status = s3c2410_inb(adap, 1);		if (wait_for_pin(adap, &status) >= 0) {			if ((status & I2C_PCF_LRB) == 0) { 				i2c_stop(adap);				break;	/* success! */			}		}		i2c_stop(adap);		udelay(adap->udelay);	}	DEB2(if (i) printk("i2c-algo-s3c2410.o: needed %d retries for %d\n",i,	                   addr));	return ret;}#endif//// Description: Whenever we initiate a transaction, the first byte clocked// onto the bus after the start condition is the address of the// device we want to talk to.  This function manipulates the address specified// so that it makes sense to the hardware when written to the IIC peripheral.//static inline unsigned char iic_addr(struct i2c_algo_s3c2410_data *adap,                                        struct i2c_msg *msg) {	unsigned short flags = msg->flags;	unsigned char addr;	addr = ( msg->addr << 1 );	if (flags & I2C_M_RD )		addr |= 1;	if (flags & I2C_M_REV_DIR_ADDR )		addr ^= 1;	return addr;}//// Description: This function is waits for an interrupt and checks for// timeouts and transmit/receive errors.//static int s3c2410_wait(struct i2c_adapter *i2c_adap, int check_ack){	struct i2c_algo_s3c2410_data *adap = i2c_adap->algo_data;	int ret, timeout, status;	u32 errbits = S3C2410_IICSTAT_ARB_FAILED | ((check_ack) ? S3C2410_IICSTAT_NACK : 0);	// Wait for transmission to complete.	DEB2(printk(KERN_DEBUG "s3c2410_wait: Waiting for interrupt\n"));	timeout = wait_for_pin(adap, &status);	if(timeout < 0) {	   // Error handling           //printk(KERN_ERR "Error: timeout\n");           return -ETIMEDOUT;	}	DEB2(printk(KERN_DEBUG "s3c2410_wait: Got interrupt\n"));	// Check transfer status	ret = s3c2410_inb(adap, S3C2410_IICSTAT);	if (ret & errbits) {	   if (ret & S3C2410_IICSTAT_ARB_FAILED) {	      //printk(KERN_ERR "Lost arbitration\n");	      ret = -EPROTO;	   }	   else if (ret & S3C2410_IICSTAT_NACK) {	      //printk(KERN_ERR "Master transfer aborted by a NACK during the transfer of the address byte\n");	      ret = -ENODEV;	   }	   s3c2410_reset(adap);	   return ret;	}	return 0;}//// Description: This function is called by the upper layers to do the// grunt work for a master send transaction//static int s3c2410_sendbytes(struct i2c_adapter *i2c_adap,                             struct i2c_msg *pmsg, int xfer_flag){	struct i2c_algo_s3c2410_data *adap = i2c_adap->algo_data;	int i = 0, count, ret;	char *buf;        u8 addr;	int rval;   	buf = pmsg->buf;	count = pmsg->len;	DEB(printk(KERN_DEBUG "I2C WRITE s3c2410_sendbytes: len=%d   addr=0x%04x   flags=0x%04x\n", pmsg->len, pmsg->addr, pmsg->flags));

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -