📄 i2c-algo-ibm_ocp.c
字号:
/* ------------------------------------------------------------------------- i2c-algo-ibm_ocp.c i2c driver algorithms for IBM PPC 405 adapters ------------------------------------------------------------------------- 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. --------------------------------------------------------------------------- History: 01/20/12 - Armin akuster@mvista.com ported up to 2.4.16+ Version 02/03/25 - Armin converted to ocp format removed commented out or #if 0 code added G閞ard Basler's fix to iic_combined_transaction() such that it returns the number of successfully completed transfers .*/#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 "i2c.h"#include "i2c-algo-ibm_ocp.h"#include <asm/ocp.h>/* ----- global defines ----------------------------------------------- */#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 */#define DEF_TIMEOUT 5/* ----- global variables --------------------------------------------- *//* module parameters: */static int i2c_debug=0;/* --- setting states on the bus with the right timing: --------------- */#define iic_outb(adap, reg, val) adap->setiic(adap->data, (int) &(reg), val)#define iic_inb(adap, reg) adap->getiic(adap->data, (int) &(reg))#define IICO_I2C_SDAHIGH 0x0780#define IICO_I2C_SDALOW 0x0781#define IICO_I2C_SCLHIGH 0x0782#define IICO_I2C_SCLLOW 0x0783#define IICO_I2C_LINEREAD 0x0784#define IIC_SINGLE_XFER 0#define IIC_COMBINED_XFER 1#define IIC_ERR_LOST_ARB -2#define IIC_ERR_INCOMPLETE_XFR -3#define IIC_ERR_NACK -1/* --- other auxiliary functions -------------------------------------- *///// Description: Puts this process to sleep for a period equal to timeout //static inline void iic_sleep(unsigned long timeout){ schedule_timeout( timeout * HZ);}//// Description: This performs the IBM PPC 405 IIC initialization sequence// as described in the PPC405GP data book.//static int iic_init (struct i2c_algo_iic_data *adap){ struct iic_regs *iic; struct iic_ibm *adap_priv_data = adap->data; unsigned short retval; iic = (struct iic_regs *) adap_priv_data->iic_base; /* Clear master low master address */ iic_outb(adap,iic->lmadr, 0); /* Clear high master address */ iic_outb(adap,iic->hmadr, 0); /* Clear low slave address */ iic_outb(adap,iic->lsadr, 0); /* Clear high slave address */ iic_outb(adap,iic->hsadr, 0); /* Clear status */ iic_outb(adap,iic->sts, 0x0a); /* Clear extended status */ iic_outb(adap,iic->extsts, 0x8f); /* Set clock division */ iic_outb(adap,iic->clkdiv, 0x04); retval = iic_inb(adap, iic->clkdiv); DEB(printk("iic_init: CLKDIV register = %x\n", retval)); /* Enable interrupts on Requested Master Transfer Complete */ iic_outb(adap,iic->intmsk, 0x01); /* Clear transfer count */ iic_outb(adap,iic->xfrcnt, 0x0); /* Clear extended control and status */ iic_outb(adap,iic->xtcntlss, 0xf0); /* Set mode control (flush master data buf, enable hold SCL, exit */ /* unknown state. */ iic_outb(adap,iic->mdcntl, 0x47); /* Clear control register */ iic_outb(adap,iic->cntl, 0x0); DEB2(printk(KERN_DEBUG "iic_init: Initialized IIC on PPC 405\n")); 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_iic_data *adap, int *status) { int timeout = DEF_TIMEOUT; int retval; struct iic_regs *iic; struct iic_ibm *adap_priv_data = adap->data; iic = (struct iic_regs *) adap_priv_data->iic_base; *status = iic_inb(adap, iic->sts);#ifndef STUB_I2C while (timeout-- && (*status & 0x01)) { adap->waitforpin(adap->data); *status = iic_inb(adap, iic->sts); }#endif if (timeout <= 0) { /* Issue stop signal on the bus, and force an interrupt */ retval = iic_inb(adap, iic->cntl); iic_outb(adap, iic->cntl, retval | 0x80); /* Clear status register */ iic_outb(adap, iic->sts, 0x0a); /* Exit unknown bus state */ retval = iic_inb(adap, iic->mdcntl); iic_outb(adap, iic->mdcntl, (retval | 0x02)); // Check the status of the controller. Does it still see a // pending transfer, even though we've tried to stop any // ongoing transaction? retval = iic_inb(adap, iic->sts); retval = retval & 0x01; if(retval) { // The iic controller is hosed. It is not responding to any // of our commands. We have already tried to force it into // a known state, but it has not worked. Our only choice now // is a soft reset, which will clear all registers, and force // us to re-initialize the controller. /* Soft reset */ iic_outb(adap, iic->xtcntlss, 0x01); udelay(500); iic_init(adap); /* Is the pending transfer bit in the sts reg finally cleared? */ retval = iic_inb(adap, iic->sts); retval = retval & 0x01; if(retval) { printk(KERN_CRIT "The IIC Controller is hosed. A processor reset is required\n"); } // For some reason, even though the interrupt bit in this // register was set during iic_init, it didn't take. We // need to set it again. Don't ask me why....this is just what // I saw when testing timeouts. iic_outb(adap, iic->intmsk, 0x01); } return(-1); } else return(0);}//------------------------------------// Utility functions////// Description: Look at the status register to see if there was an error// in the requested transaction. If there is, look at the extended status// register and determine the exact cause.//int analyze_status(struct i2c_algo_iic_data *adap, int *error_code){ int ret; struct iic_regs *iic; struct iic_ibm *adap_priv_data = adap->data; iic = (struct iic_regs *) adap_priv_data->iic_base; ret = iic_inb(adap, iic->sts); if(ret & 0x04) { // Error occurred ret = iic_inb(adap, iic->extsts); if(ret & 0x04) { // Lost arbitration *error_code = IIC_ERR_LOST_ARB; } if(ret & 0x02) { // Incomplete transfer *error_code = IIC_ERR_INCOMPLETE_XFR; } if(ret & 0x01) { // Master transfer aborted by a NACK during the transfer of the // address byte *error_code = IIC_ERR_NACK; } return -1; } return 0;}//// Description: This function is called by the upper layers to do the// grunt work for a master send transaction//static int iic_sendbytes(struct i2c_adapter *i2c_adap,const char *buf, int count, int xfer_flag){ struct iic_regs *iic; struct i2c_algo_iic_data *adap = i2c_adap->algo_data; struct iic_ibm *adap_priv_data = adap->data; int wrcount, status, timeout; int loops, remainder, i, j; int ret, error_code; iic = (struct iic_regs *) adap_priv_data->iic_base; if( count == 0 ) return 0; wrcount = 0; loops = count / 4; remainder = count % 4; if((loops > 1) && (remainder == 0)) { for(i=0; i<(loops-1); i++) { // // Write four bytes to master data buffer // for(j=0; j<4; j++) { iic_outb(adap, iic->mdbuf, buf[wrcount++]); } // // Issue command to IICO device to begin transmission // iic_outb(adap, iic->cntl, 0x35); // // Wait for transmission to complete. When it does, //loop to the top of the for statement and write the // next four bytes. // timeout = wait_for_pin(adap, &status); if(timeout < 0) { // // Error handling // //printk(KERN_ERR "Error: write timeout\n"); return wrcount; } ret = analyze_status(adap, &error_code); if(ret < 0) { if(error_code == IIC_ERR_INCOMPLETE_XFR) { // Return the number of bytes transferred ret = iic_inb(adap, iic->xfrcnt); ret = ret & 0x07; return (wrcount-4+ret); } else return error_code; } } } else if((loops >= 1) && (remainder > 0)){ //printk(KERN_DEBUG "iic_sendbytes: (loops >= 1)\n"); for(i=0; i<loops; i++) { // // Write four bytes to master data buffer // for(j=0; j<4; j++) { iic_outb(adap, iic->mdbuf, buf[wrcount++]); } // // Issue command to IICO device to begin transmission // iic_outb(adap, iic->cntl, 0x35); // // Wait for transmission to complete. When it does, //loop to the top of the for statement and write the // next four bytes. // timeout = wait_for_pin(adap, &status); if(timeout < 0) { // // Error handling // //printk(KERN_ERR "Error: write timeout\n"); return wrcount; } ret = analyze_status(adap, &error_code); if(ret < 0) { if(error_code == IIC_ERR_INCOMPLETE_XFR) { // Return the number of bytes transferred ret = iic_inb(adap, iic->xfrcnt); ret = ret & 0x07; return (wrcount-4+ret); } else return error_code; } } } //printk(KERN_DEBUG "iic_sendbytes: expedite write\n"); if(remainder == 0) remainder = 4; // remainder = remainder - 1; // // Write the remaining bytes (less than or equal to 4) // for(i=0; i<remainder; i++) { iic_outb(adap, iic->mdbuf, buf[wrcount++]); //printk(KERN_DEBUG "iic_sendbytes: data transferred = %x, wrcount = %d\n", buf[wrcount-1], (wrcount-1)); } //printk(KERN_DEBUG "iic_sendbytes: Issuing write\n"); if(xfer_flag == IIC_COMBINED_XFER) { iic_outb(adap, iic->cntl, (0x09 | ((remainder-1) << 4))); } else { iic_outb(adap, iic->cntl, (0x01 | ((remainder-1) << 4))); } DEB2(printk(KERN_DEBUG "iic_sendbytes: Waiting for interrupt\n")); timeout = wait_for_pin(adap, &status); if(timeout < 0) { // // Error handling // //printk(KERN_ERR "Error: write timeout\n"); return wrcount; } ret = analyze_status(adap, &error_code); if(ret < 0) { if(error_code == IIC_ERR_INCOMPLETE_XFR) { // Return the number of bytes transferred ret = iic_inb(adap, iic->xfrcnt); ret = ret & 0x07; return (wrcount-4+ret); } else return error_code; } DEB2(printk(KERN_DEBUG "iic_sendbytes: Got interrupt\n")); return wrcount;}//// Description: Called by the upper layers to do the grunt work for// a master read transaction.//static int iic_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count, int xfer_type){ struct iic_regs *iic; int rdcount=0, i, status, timeout; struct i2c_algo_iic_data *adap = i2c_adap->algo_data; struct iic_ibm *adap_priv_data = adap->data; int loops, remainder, j; int ret, error_code; iic = (struct iic_regs *) adap_priv_data->iic_base; if(count == 0) return 0; loops = count / 4; remainder = count % 4; //printk(KERN_DEBUG "iic_readbytes: loops = %d, remainder = %d\n", loops, remainder); if((loops > 1) && (remainder == 0)) { //printk(KERN_DEBUG "iic_readbytes: (loops > 1) && (remainder == 0)\n"); for(i=0; i<(loops-1); i++) { // // Issue command to begin master read (4 bytes maximum) // //printk(KERN_DEBUG "--->Issued read command\n"); iic_outb(adap, iic->cntl, 0x37); // // Wait for transmission to complete. When it does, // loop to the top of the for statement and write the // next four bytes. // //printk(KERN_DEBUG "--->Waiting for interrupt\n"); timeout = wait_for_pin(adap, &status);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -