?? i2c-algo-s3c2410.c
字號:
/* ------------------------------------------------------------------------- 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 + -