* 8xx: i2c-algo-8xx - fixed timeout detection and transmission errors
@ 2005-08-10 7:27 cajus.hahn
2005-08-10 13:58 ` Marcelo Tosatti
0 siblings, 1 reply; 13+ messages in thread
From: cajus.hahn @ 2005-08-10 7:27 UTC (permalink / raw)
To: linuxppc-embedded
Hi all,
I had some problems on my MPC855M based board when I tried to access th=
e
i2c bus.
The cause were some disturbances on the i2c bus. If a slave device or
another master device hold the SDA or SCL line low, the CPM will get in=
to a
state, where no more transmissions are made. The only way I found, to =
get
the CPM back to work, was a complete re-initialisation. -> force_reinit=
()
In this case the busy-wait for transmissions with count < 16 will lock =
the
complete system (CPU load 99.9%)
I added the define I2C_BUSY_WAIT to switch between faster access or saf=
er
system.
I found out that the i2c-algo-8xx.c has a bug in cpm_iic_read() and
cpm_iic_write(): the timeout detection will not work in the case of cou=
nt <
16.
I also added the define I2C_INTERRUPTIBLE_SLEEP: the old driver reporte=
d
IO-error (-EIO) if a timeout occured (which was not working, see above)=
or
if a signal was pending. This caused some problems if the process recei=
ves
user-signals. The driver will report IO-error, which is not correct. Wi=
th
the busy-wait this effect might not be seen, because there will be no
process scheduling -> no signals might be send.
My modified file is appended. The defines for I2C_BUSY_WAIT and
I2C_INTERRUPTIBLE_SLEEP are active, which let the driver act like the o=
ld
one.
Maybe this is helpful for others too and some of the modifications find=
it=B4s way into the official kernel tree.
Cajus Hahn
/*
* i2c-algo-8xx.c i2x driver algorithms for MPC8XX CPM
* Copyright (c) 1999 Dan Malek (dmalek@jlc.net).
*
This program is free software; you can redistribute it and/or modif=
y
it under the terms of the GNU General Public License as published b=
y
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.
*
* moved into proper i2c interface; separated out platform specific
* parts into i2c-rpx.c
* Brad Parker (brad@heeltoe.com)
*
* added define for BUSY_WAIT and INTERRUPTIBLE_SLEEP, added
I2C_ALGO_8XX_DATE + ..VERSION
* fixed bug in cpm_iic_read and cpm_iic_write (timeout never detected =
if
count < 16)
* added force_reinit(): in certain cases (disturbances on the I2C bus)=
a
timeout will
* occur. After this a complete re-initialisation will be necessary,
otherwise all
* following transmissions will have a timeout.
* Cajus Hahn, 09.08.2005
*/
// XXX todo
// timeout sleep?
/* $Id: i2c-algo-8xx.c,v 1.1.1.1 2004/12/10 08:44:35 cajus Exp $ */
#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 <asm/mpc8xx.h>
#include <asm/commproc.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-8xx.h>
#define I2C_ALGO_8XX_DATE "20050809"
#define I2C_ALGO_8XX_VERSION "2.6.2"
#define CPM_MAX_READ 513
/* #define I2C_CHIP_ERRATA */ /* Try uncomment this if you have an olde=
r
CPU(earlier than rev D4) */
#define I2C_BUSY_WAIT /* Uncomment this if you want to do a busy wait =
in
cpm_iic_read and cpm_iic_write */
#define I2C_INTERRUPTIBLE_SLEEP /* Uncomment this if you want the waiti=
ng
in cpm_iic_read and
cpm_iic_write beeing interrupt=
able
by signals */
static wait_queue_head_t iic_wait;
static ushort r_tbase, r_rbase;
int cpm_scan =3D 0;
int cpm_debug =3D 0;
static void
cpm_iic_interrupt(void *dev_id, struct pt_regs *regs)
{
volatile i2c8xx_t *i2c =3D (i2c8xx_t *)dev_id;
if (cpm_debug > 1)
printk(KERN_DEBUG "cpm_iic_interrupt(dev_id=3D%p)\n", dev_i=
d);
#ifdef I2C_CHIP_ERRATA
/* Chip errata, clear enable.
* This seems to not be needed on rev D4 or newer CPUs.
* Someone with an older CPU needs to verify this.
*/
i2c->i2c_i2mod &=3D ~1;
#endif
/* Clear interrupt.
*/
i2c->i2c_i2cer =3D 0xff;
/* Get 'me going again.
*/
#ifdef I2C_INTERRUPTIBLE_SLEEP
wake_up_interruptible(&iic_wait);
#else
wake_up(&iic_wait);
#endif
}
static void
cpm_iic_init(struct i2c_algo_8xx_data *cpm_adap)
{
volatile iic_t *iip =3D cpm_adap->iip;
volatile i2c8xx_t *i2c =3D cpm_adap->i2c;
unsigned char brg;
bd_t *bd =3D (bd_t *)__res;
if (cpm_debug) printk(KERN_DEBUG "cpm_iic_init() - iip=3D%p\n",ii=
p);
/* Initialize the parameter ram.
* We need to make sure many things are initialized to zero,
* especially in the case of a microcode patch.
*/
iip->iic_rstate =3D 0;
iip->iic_rdp =3D 0;
iip->iic_rbptr =3D 0;
iip->iic_rbc =3D 0;
iip->iic_rxtmp =3D 0;
iip->iic_tstate =3D 0;
iip->iic_tdp =3D 0;
iip->iic_tbptr =3D 0;
iip->iic_tbc =3D 0;
iip->iic_txtmp =3D 0;
/* Set up the IIC parameters in the parameter ram.
*/
iip->iic_tbase =3D r_tbase =3D cpm_adap->dp_addr;
iip->iic_rbase =3D r_rbase =3D cpm_adap->dp_addr + sizeof(cbd_t)*=
2;
iip->iic_tfcr =3D SMC_EB;
iip->iic_rfcr =3D SMC_EB;
/* Set maximum receive size.
*/
iip->iic_mrblr =3D CPM_MAX_READ;
/* Initialize Tx/Rx parameters.
*/
if (cpm_adap->reloc =3D=3D 0) {
volatile cpm8xx_t *cp =3D cpm_adap->cp;
cp->cp_cpcr =3D
mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX) | CPM_CR_FL=
G;
while (cp->cp_cpcr & CPM_CR_FLG);
} else {
iip->iic_rbptr =3D iip->iic_rbase;
iip->iic_tbptr =3D iip->iic_tbase;
iip->iic_rstate =3D 0;
iip->iic_tstate =3D 0;
}
/* Select an arbitrary address. Just make sure it is unique.
*/
i2c->i2c_i2add =3D 0xfe;
/* Make clock run at 60 KHz.
*/
brg =3D (unsigned char) (bd->bi_intfreq/(32*2*60000) -3);
i2c->i2c_i2brg =3D brg;
i2c->i2c_i2mod =3D 0x00;
i2c->i2c_i2com =3D 0x01; /* Master mode */
/* Disable interrupts.
*/
i2c->i2c_i2cmr =3D 0;
i2c->i2c_i2cer =3D 0xff;
init_waitqueue_head(&iic_wait);
/* Install interrupt handler.
*/
if (cpm_debug) {
printk (KERN_DEBUG "%s[%d] Install ISR for IRQ %d\n",
__func__,__LINE__, CPMVEC_I2C);
}
cpm_install_handler(CPMVEC_I2C, cpm_iic_interrupt, (void *)i2c);
}
static int
cpm_iic_shutdown(struct i2c_algo_8xx_data *cpm_adap)
{
volatile i2c8xx_t *i2c =3D cpm_adap->i2c;
/* Shut down IIC.
*/
i2c->i2c_i2mod &=3D ~1;
i2c->i2c_i2cmr =3D 0;
i2c->i2c_i2cer =3D 0xff;
return(0);
}
static void
cpm_reset_iic_params(volatile iic_t *iip)
{
iip->iic_tbase =3D r_tbase;
iip->iic_rbase =3D r_rbase;
iip->iic_tfcr =3D SMC_EB;
iip->iic_rfcr =3D SMC_EB;
iip->iic_mrblr =3D CPM_MAX_READ;
iip->iic_rstate =3D 0;
iip->iic_rdp =3D 0;
iip->iic_rbptr =3D iip->iic_rbase;
iip->iic_rbc =3D 0;
iip->iic_rxtmp =3D 0;
iip->iic_tstate =3D 0;
iip->iic_tdp =3D 0;
iip->iic_tbptr =3D iip->iic_tbase;
iip->iic_tbc =3D 0;
iip->iic_txtmp =3D 0;
}
#define BD_SC_NAK ((ushort)0x0004) /* NAK - did not respond */
#define BD_SC_OV ((ushort)0x0002) /* OV - receive overrun */
#define CPM_CR_CLOSE_RXBD ((ushort)0x0007)
static void force_close(struct i2c_algo_8xx_data *cpm)
{
volatile i2c8xx_t *i2c =3D cpm->i2c;
if (cpm_debug)
printk(KERN_DEBUG "force_close()");
if (cpm->reloc =3D=3D 0) { /* micro code disabled */
volatile cpm8xx_t *cp =3D cpm->cp;
cp->cp_cpcr =3D
mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_CLOSE_RXBD) |
CPM_CR_FLG;
while (cp->cp_cpcr & CPM_CR_FLG);
}
i2c->i2c_i2cmr =3D 0x00; /* Disable all interrupts */
i2c->i2c_i2cer =3D 0xff;
}
static void force_reinit(struct i2c_algo_8xx_data *cpm)
{
volatile iic_t *iip =3D cpm->iip;
volatile i2c8xx_t *i2c =3D cpm->i2c;
volatile cpm8xx_t *cp =3D cpm->cp;
unsigned char brg;
bd_t *bd =3D (bd_t *)__res;
// Disable interrupts.
i2c->i2c_i2cmr =3D 0;
i2c->i2c_i2cer =3D 0xff;
// Clear enable
i2c->i2c_i2mod &=3D ~1;
// Initialize the parameter ram.
iip->iic_rstate =3D 0;
iip->iic_rdp =3D 0;
iip->iic_rbptr =3D 0;
iip->iic_rbc =3D 0;
iip->iic_rxtmp =3D 0;
iip->iic_tstate =3D 0;
iip->iic_tdp =3D 0;
iip->iic_tbptr =3D 0;
iip->iic_tbc =3D 0;
iip->iic_txtmp =3D 0;
iip->iic_tbase =3D r_tbase;
iip->iic_rbase =3D r_rbase;
iip->iic_tfcr =3D SMC_EB;
iip->iic_rfcr =3D SMC_EB;
iip->iic_mrblr =3D CPM_MAX_READ;
if (cpm->reloc =3D=3D 0)
{
cp->cp_cpcr =3D mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX) |=
CPM_CR_FLG;
while (cp->cp_cpcr & CPM_CR_FLG);
}
else
{
iip->iic_rbptr =3D iip->iic_rbase;
iip->iic_tbptr =3D iip->iic_tbase;
iip->iic_rstate =3D 0;
iip->iic_tstate =3D 0;
}
// Select an arbitrary address. Just make sure it is unique.
i2c->i2c_i2add =3D 0xfe;
// Make clock run at 60 KHz.
brg =3D (unsigned char) (bd->bi_intfreq/(32*2*60000) -3);
i2c->i2c_i2brg =3D brg;
i2c->i2c_i2mod =3D 0x00;
i2c->i2c_i2com =3D 0x01; /* Master mode */
}
/* Read from IIC...
* abyte =3D address byte, with r/w flag already set
*/
static int
cpm_iic_read(struct i2c_algo_8xx_data *cpm, u_char abyte, char *buf, in=
t
count)
{
volatile iic_t *iip =3D cpm->iip;
volatile i2c8xx_t *i2c =3D cpm->i2c;
volatile cpm8xx_t *cp =3D cpm->cp;
volatile cbd_t *tbdf, *rbdf;
u_char *tb;
unsigned long flags, tmo, timedout;
if (count >=3D CPM_MAX_READ)
return -EINVAL;
/* check for and use a microcode relocation patch */
if (cpm->reloc) {
cpm_reset_iic_params(iip);
}
tbdf =3D (cbd_t *)&cp->cp_dpmem[iip->iic_tbase];
rbdf =3D (cbd_t *)&cp->cp_dpmem[iip->iic_rbase];
/* To read, we need an empty buffer of the proper length.
* All that is used is the first byte for address, the remainder
* is just used for timing (and doesn't really have to exist).
*/
tb =3D cpm->temp;
tb =3D (u_char *)(((uint)tb + 15) & ~15);
tb[0] =3D abyte; /* Device address byte w/rw flag */
flush_dcache_range((unsigned long) tb, (unsigned long) (tb+1));
if (cpm_debug) printk(KERN_DEBUG "cpm_iic_read(abyte=3D0x%x)\n",
abyte);
tbdf->cbd_bufaddr =3D __pa(tb);
tbdf->cbd_datlen =3D count + 1;
tbdf->cbd_sc =3D
BD_SC_READY | BD_SC_LAST |
BD_SC_WRAP | BD_IIC_START;
iip->iic_mrblr =3D count + 1; /* prevent excessive read, +1
is needed otherwise will the
RXB interrupt come too early */
/* flush will invalidate too. */
flush_dcache_range((unsigned long) buf, (unsigned long) (buf+coun=
t));
rbdf->cbd_datlen =3D 0;
rbdf->cbd_bufaddr =3D __pa(buf);
rbdf->cbd_sc =3D BD_SC_EMPTY | BD_SC_WRAP| BD_SC_INTRPT;
timedout =3D 0;
#ifdef I2C_BUSY_WAIT
if(count > 16){
#endif
/* Chip bug, set enable here */
local_irq_save(flags);
i2c->i2c_i2cmr =3D 0x13; /* Enable some interupts */
i2c->i2c_i2cer =3D 0xff;
i2c->i2c_i2mod |=3D 1; /* Enable */
i2c->i2c_i2com |=3D 0x80; /* Begin transmission */
/* Wait for IIC transfer */
#ifdef I2C_INTERRUPTIBLE_SLEEP
tmo =3D interruptible_sleep_on_timeout(&iic_wait,1*HZ);
#else
tmo =3D sleep_on_timeout(&iic_wait,1*HZ);
#endif
if(tmo =3D=3D 0) timedout=3D1;
local_irq_restore(flags);
#ifdef I2C_BUSY_WAIT
} else { /* busy wait for small transfers, its faster */
i2c->i2c_i2cmr =3D 0x00; /* Disable I2C interupts */
i2c->i2c_i2cer =3D 0xff;
i2c->i2c_i2mod |=3D 1; /* Enable */
i2c->i2c_i2com |=3D 0x80; /* Begin transmission */
tmo =3D jiffies + 1*HZ;
while(!(i2c->i2c_i2cer & 0x11 || (timedout =3D
time_after(jiffies, tmo)))); /* Busy wait, with a timeout */
}
#endif
if(timedout)
{
printk(KERN_DEBUG "cpm_iic_read: timeout!\n");
force_reinit(cpm);
return -EIO;
}
#ifdef I2C_INTERRUPTIBLE_SLEEP
if (signal_pending(current))
{
force_close(cpm);
if (cpm_debug)
printk(KERN_DEBUG "cpm_iic_read: signal_pending! \n");
return -EINTR;
}
#endif
#ifdef I2C_CHIP_ERRATA
/* Chip errata, clear enable. This is not needed on rev D4 CPUs.
Disabling I2C too early may cause too short stop condition */
udelay(4);
i2c->i2c_i2mod &=3D ~1;
#endif
if (cpm_debug) {
printk(KERN_DEBUG "tx sc %04x, rx sc %04x\n",
tbdf->cbd_sc, rbdf->cbd_sc);
}
if (tbdf->cbd_sc & BD_SC_READY) {
printk(KERN_INFO "IIC read; complete but tbuf ready\n");
force_close(cpm);
printk(KERN_INFO "tx sc %04x, rx sc %04x\n",
tbdf->cbd_sc, rbdf->cbd_sc);
}
if (tbdf->cbd_sc & BD_SC_NAK) {
if (cpm_debug)
printk(KERN_DEBUG "IIC read; no ack\n");
return -EREMOTEIO;
}
if (rbdf->cbd_sc & BD_SC_EMPTY) {
/* force_close(cpm); */
if (cpm_debug){
printk(KERN_DEBUG "IIC read; complete but rbuf empty\=
n");
printk(KERN_DEBUG "tx sc %04x, rx sc %04x\n",
tbdf->cbd_sc, rbdf->cbd_sc);
}
return -EREMOTEIO;
}
if (rbdf->cbd_sc & BD_SC_OV) {
if (cpm_debug)
printk(KERN_DEBUG "IIC read; Overrun\n");
return -EREMOTEIO;;
}
if (cpm_debug) printk(KERN_DEBUG "read %d bytes\n",
rbdf->cbd_datlen);
if (rbdf->cbd_datlen < count) {
if (cpm_debug)
printk(KERN_DEBUG "IIC read; short, wanted %d got %d\=
n",
count, rbdf->cbd_datlen);
return 0;
}
return count;
}
/* Write to IIC...
* addr =3D address byte, with r/w flag already set
*/
static int
cpm_iic_write(struct i2c_algo_8xx_data *cpm, u_char abyte, char *buf,in=
t
count)
{
volatile iic_t *iip =3D cpm->iip;
volatile i2c8xx_t *i2c =3D cpm->i2c;
volatile cpm8xx_t *cp =3D cpm->cp;
volatile cbd_t *tbdf;
u_char *tb;
unsigned long flags, tmo, timedout;
/* check for and use a microcode relocation patch */
if (cpm->reloc) {
cpm_reset_iic_params(iip);
}
tb =3D cpm->temp;
tb =3D (u_char *)(((uint)tb + 15) & ~15);
*tb =3D abyte; /* Device address byte w/rw flag */
flush_dcache_range((unsigned long) tb, (unsigned long) (tb+1));
flush_dcache_range((unsigned long) buf, (unsigned long) (buf+coun=
t));
if (cpm_debug) printk(KERN_DEBUG "cpm_iic_write(abyte=3D0x%x)\n",=
abyte);
/* set up 2 descriptors */
tbdf =3D (cbd_t *)&cp->cp_dpmem[iip->iic_tbase];
tbdf[0].cbd_bufaddr =3D __pa(tb);
tbdf[0].cbd_datlen =3D 1;
tbdf[0].cbd_sc =3D BD_SC_READY | BD_IIC_START;
tbdf[1].cbd_bufaddr =3D __pa(buf);
tbdf[1].cbd_datlen =3D count;
tbdf[1].cbd_sc =3D BD_SC_READY | BD_SC_INTRPT | BD_SC_LAST |
BD_SC_WRAP;
timedout =3D 0;
#ifdef I2C_BUSY_WAIT
if(count > 16){
#endif
/* Chip bug, set enable here */
local_irq_save(flags);
i2c->i2c_i2cmr =3D 0x13; /* Enable some interupts */
i2c->i2c_i2cer =3D 0xff;
i2c->i2c_i2mod |=3D 1; /* Enable */
i2c->i2c_i2com |=3D 0x80; /* Begin transmission */
/* Wait for IIC transfer */
#ifdef I2C_INTERRUPTIBLE_SLEEP
tmo =3D interruptible_sleep_on_timeout(&iic_wait,1*HZ);
#else
tmo =3D sleep_on_timeout(&iic_wait,1*HZ);
#endif
if(tmo =3D=3D 0) timedout=3D1;
local_irq_restore(flags);
#ifdef I2C_BUSY_WAIT
} else { /* busy wait for small transfers, its faster */
i2c->i2c_i2cmr =3D 0x00; /* Disable I2C interupts */
i2c->i2c_i2cer =3D 0xff;
i2c->i2c_i2mod |=3D 1; /* Enable */
i2c->i2c_i2com |=3D 0x80; /* Begin transmission */
tmo =3D jiffies + 1*HZ;
while(!(i2c->i2c_i2cer & 0x12 || (timedout =3D
time_after(jiffies, tmo)))); /* Busy wait, with a timeout */
}
#endif
if(timedout)
{
printk(KERN_DEBUG "cpm_iic_write: timeout!\n");
force_reinit(cpm);
return -EIO;
}
#ifdef I2C_INTERRUPTIBLE_SLEEP
if (signal_pending(current))
{
force_close(cpm);
if (cpm_debug)
printk(KERN_DEBUG "cpm_iic_write: signal_pending! \n");=
return -EINTR;
}
#endif
#if I2C_CHIP_ERRATA
/* Chip errata, clear enable. This is not needed on rev D4 CPUs.
Disabling I2C too early may cause too short stop condition */
udelay(4);
i2c->i2c_i2mod &=3D ~1;
#endif
if (cpm_debug) {
printk(KERN_DEBUG "tx0 sc %04x, tx1 sc %04x\n",
tbdf[0].cbd_sc, tbdf[1].cbd_sc);
}
if ((tbdf[0].cbd_sc | tbdf[1].cbd_sc) & BD_SC_NAK) {
if (cpm_debug)
printk(KERN_DEBUG "IIC write; no ack\n");
return 0;
}
if ((tbdf[0].cbd_sc | tbdf[1].cbd_sc) & BD_SC_READY) {
if (cpm_debug)
printk(KERN_DEBUG "IIC write; complete but tbuf
ready\n");
return 0;
}
return count;
}
/* See if an IIC address exists..
* addr =3D 7 bit address, unshifted
*/
static int
cpm_iic_tryaddress(struct i2c_algo_8xx_data *cpm, int addr)
{
volatile iic_t *iip =3D cpm->iip;
volatile i2c8xx_t *i2c =3D cpm->i2c;
volatile cpm8xx_t *cp =3D cpm->cp;
volatile cbd_t *tbdf, *rbdf;
u_char *tb;
unsigned long flags, len, tmo;
if (cpm_debug > 1)
printk(KERN_DEBUG "cpm_iic_tryaddress(cpm=3D%p,addr=3D%d)\n=
", cpm,
addr);
/* check for and use a microcode relocation patch */
if (cpm->reloc) {
cpm_reset_iic_params(iip);
}
if (cpm_debug && addr =3D=3D 0) {
printk(KERN_DEBUG "iip %p, dp_addr 0x%x\n", cpm->iip,
cpm->dp_addr);
printk(KERN_DEBUG "iic_tbase %d, r_tbase %d\n", iip->iic_tb=
ase,
r_tbase);
}
tbdf =3D (cbd_t *)&cp->cp_dpmem[iip->iic_tbase];
rbdf =3D (cbd_t *)&cp->cp_dpmem[iip->iic_rbase];
tb =3D cpm->temp;
tb =3D (u_char *)(((uint)tb + 15) & ~15);
/* do a simple read */
tb[0] =3D (addr << 1) | 1; /* device address (+ read) */
len =3D 2;
flush_dcache_range((unsigned long) tb, (unsigned long) (tb+1));
tbdf->cbd_bufaddr =3D __pa(tb);
tbdf->cbd_datlen =3D len;
tbdf->cbd_sc =3D
BD_SC_READY | BD_SC_LAST |
BD_SC_WRAP | BD_IIC_START;
rbdf->cbd_datlen =3D 0;
rbdf->cbd_bufaddr =3D __pa(tb+2);
rbdf->cbd_sc =3D BD_SC_EMPTY | BD_SC_WRAP | BD_SC_INTRPT;
local_irq_save(flags);
i2c->i2c_i2cmr =3D 0x13; /* Enable some interupts */
i2c->i2c_i2cer =3D 0xff;
i2c->i2c_i2mod |=3D 1; /* Enable */
i2c->i2c_i2com |=3D 0x80; /* Begin transmission */
if (cpm_debug > 1) printk(KERN_DEBUG "about to sleep\n");
/* wait for IIC transfer */
#ifdef I2C_INTERRUPTIBLE_SLEEP
tmo =3D interruptible_sleep_on_timeout(&iic_wait,1*HZ);
#else
tmo =3D sleep_on_timeout(&iic_wait,1*HZ);
#endif
local_irq_restore(flags);
#ifdef I2C_CHIP_ERRATA
/* Chip errata, clear enable. This is not needed on rev D4 CPUs.
Disabling I2C too early may cause too short stop condition */
udelay(4);
i2c->i2c_i2mod &=3D ~1;
#endif
if (signal_pending(current) || !tmo){
force_close(cpm);
if(cpm_debug && !tmo)
printk(KERN_DEBUG "IIC tryaddress: timeout!\n");
return -EIO;
}
if (cpm_debug > 1) printk(KERN_DEBUG "back from sleep\n");
if (tbdf->cbd_sc & BD_SC_NAK) {
if (cpm_debug > 1) printk(KERN_DEBUG "IIC try; no ack\n");
return 0;
}
if (tbdf->cbd_sc & BD_SC_READY) {
printk(KERN_INFO "IIC try; complete but tbuf ready\n");
}
return 1;
}
static int cpm_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg msgs[],
int num)
{
struct i2c_algo_8xx_data *adap =3D i2c_adap->algo_data;
struct i2c_msg *pmsg;
int i, ret;
u_char addr;
for (i =3D 0; i < num; i++) {
pmsg =3D &msgs[i];
if (cpm_debug)
printk(KERN_DEBUG "i2c-algo-8xx.o: "
"#%d addr=3D0x%x flags=3D0x%x len=3D%d\n buf=3D=
%lx\n",
i, pmsg->addr, pmsg->flags, pmsg->len, (unsign=
ed
long)pmsg->buf);
addr =3D pmsg->addr << 1;
if (pmsg->flags & I2C_M_RD )
addr |=3D 1;
if (pmsg->flags & I2C_M_REV_DIR_ADDR )
addr ^=3D 1;
if (!(pmsg->flags & I2C_M_NOSTART)) {
}
if (pmsg->flags & I2C_M_RD ) {
/* read bytes into buffer*/
ret =3D cpm_iic_read(adap, addr, pmsg->buf, pmsg->len=
);
if (cpm_debug)
printk(KERN_DEBUG "i2c-algo-8xx.o: read %d
bytes\n", ret);
if (ret < pmsg->len ) {
return (ret<0)? ret : -EREMOTEIO;
}
} else {
/* write bytes from buffer */
ret =3D cpm_iic_write(adap, addr, pmsg->buf, pmsg->le=
n);
if (cpm_debug)
printk(KERN_DEBUG "i2c-algo-8xx.o: wrote %d\n",=
ret);
if (ret < pmsg->len ) {
return (ret<0) ? ret : -EREMOTEIO;
}
}
}
return (num);
}
static int algo_control(struct i2c_adapter *adapter,
unsigned int cmd, unsigned long arg)
{
return 0;
}
static u32 cpm_func(struct i2c_adapter *adap)
{
return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
I2C_FUNC_PROTOCOL_MANGLING;
}
/* -----exported algorithm data: ------------------------------------- =
*/
static struct i2c_algorithm cpm_algo =3D {
"MPC8xx CPM algorithm",
I2C_ALGO_MPC8XX,
cpm_xfer,
NULL,
NULL, /* slave_xmit */
NULL, /* slave_recv */
algo_control, /* ioctl */
cpm_func, /* functionality */
};
/*
* registering functions to load algorithms at runtime
*/
int i2c_8xx_add_bus(struct i2c_adapter *adap)
{
int i;
struct i2c_algo_8xx_data *cpm_adap =3D adap->algo_data;
if (cpm_debug)
printk(KERN_DEBUG "i2c-algo-8xx.o: hw routines for %s
registered.\n",
adap->name);
/* register new adapter to i2c module... */
adap->id |=3D cpm_algo.id;
adap->algo =3D &cpm_algo;
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
i2c_add_adapter(adap);
cpm_iic_init(cpm_adap);
/* scan bus */
if (cpm_scan) {
printk(KERN_INFO " i2c-algo-8xx.o: scanning bus %s...\n",
adap->name);
for (i =3D 0; i < 128; i++) {
if (cpm_iic_tryaddress(cpm_adap, i)) {
printk("(%02x)",i<<1);
}
}
printk("\n");
}
return 0;
}
int i2c_8xx_del_bus(struct i2c_adapter *adap)
{
int res;
struct i2c_algo_8xx_data *cpm_adap =3D adap->algo_data;
cpm_iic_shutdown(cpm_adap);
if ((res =3D i2c_del_adapter(adap)) < 0)
return res;
printk(KERN_INFO "i2c-algo-8xx.o: adapter unregistered:
%s\n",adap->name);
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
return 0;
}
EXPORT_SYMBOL(i2c_8xx_add_bus);
EXPORT_SYMBOL(i2c_8xx_del_bus);
int __init i2c_algo_8xx_init (void)
{
printk(KERN_INFO "i2c-algo-8xx.o: i2c mpc8xx algorithm module ver=
sion
%s (%s)\n", I2C_ALGO_8XX_VERSION, I2C_ALGO_8XX_DATE);
return 0;
}
#ifdef MODULE
MODULE_AUTHOR("Brad Parker <brad@heeltoe.com>");
MODULE_DESCRIPTION("I2C-Bus MPC8XX algorithm");
#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
#endif
int init_module(void)
{
return i2c_algo_8xx_init();
}
void cleanup_module(void)
{
}
#endif=
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: 8xx: i2c-algo-8xx - fixed timeout detection and transmission errors
@ 2005-08-10 9:37 Debora Liu
2005-08-10 9:48 ` Wolfgang Denk
0 siblings, 1 reply; 13+ messages in thread
From: Debora Liu @ 2005-08-10 9:37 UTC (permalink / raw)
To: cajus.hahn@de.abb.com; +Cc: Linuxppc-embedded
Hello, cajus.hahn
In message <2005-08-10 15:27:57 cajus.hahn@de.abb.com> you wrote:
>Hi all,
>
>I had some problems on my MPC855M based board when I tried to access the
>i2c bus.
Try update i2c-algo-8xx.c
= = = = = = = = = = = = = = = = = = = =
Debora Liu
deboralh@fel.com.cn
2005-08-10
begin 600 i2c_algo_8xx-port_to_2_6.patch
M.'AX.B!P;W)T(&DR8RUA;&=O7SAX>"!T;R`R+C8-"@T*0F%S960@;VX@5&]M
M(%)I;FDG<R!A;F0@2F]A:VEM(%1J97)N;'5N9"=S('=O<FL-"@T*4VEG;F5D
M+6]F9BUB>3H@07)I<W1E=2!397)G:6\@4F]Z86YS:VD@1FEL:&\@/&%R:7-`
M8V%T:&5D<F%L;&%B<RYO<F<^#0H-"DEN9&5X.B`R+C8M.'AX+V1R:79E<G,O
M:3)C+V%L9V]S+VDR8RUA;&=O+3AX>"YC#0H]/3T]/3T]/3T]/3T]/3T]/3T]
M/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]
M/3T]#0HM+2T@+V1E=B]N=6QL"3$Y-S`M,#$M,#$@,#`Z,#`Z,#`N,#`P,#`P
M,#`P("LP,#`P#0HK*RL@,BXV+3AX>"]D<FEV97)S+VDR8R]A;&=O<R]I,F,M
M86QG;RTX>'@N8PDR,#`U+3`X+3`X(#`Y.C4U.C,P+C`P,#`P,#`P,"`M,#,P
M,`T*0$`@+3`L,"`K,2PV,3<@0$`-"BLO*@T**R`J(&DR8RUA;&=O+3AX>"YC
M(&DR>"!D<FEV97(@86QG;W)I=&AM<R!F;W(@35!#.%A8($-030T**R`J($-O
M<'ER:6=H="`H8RD@,3DY.2!$86X@36%L96L@*&1M86QE:T!J;&,N;F5T*2X-
M"BL@*@T**R`@("!4:&ES('!R;V=R86T@:7,@9G)E92!S;V9T=V%R93L@>6]U
M(&-A;B!R961I<W1R:6)U=&4@:70@86YD+V]R(&UO9&EF>0T**R`@("!I="!U
M;F1E<B!T:&4@=&5R;7,@;V8@=&AE($=.52!'96YE<F%L(%!U8FQI8R!,:6-E
M;G-E(&%S('!U8FQI<VAE9"!B>0T**R`@("!T:&4@1G)E92!3;V9T=V%R92!&
M;W5N9&%T:6]N.R!E:71H97(@=F5R<VEO;B`R(&]F('1H92!,:6-E;G-E+"!O
M<@T**R`@("`H870@>6]U<B!O<'1I;VXI(&%N>2!L871E<B!V97)S:6]N+@T*
M*PT**R`@("!4:&ES('!R;V=R86T@:7,@9&ES=')I8G5T960@:6X@=&AE(&AO
M<&4@=&AA="!I="!W:6QL(&)E('5S969U;"P-"BL@("`@8G5T(%=)5$A/550@
M04Y9(%=!4E)!3E19.R!W:71H;W5T(&5V96X@=&AE(&EM<&QI960@=V%R<F%N
M='D@;V8-"BL@("`@34520TA!3E1!0DE,2519(&]R($9)5$Y%4U,@1D]2($$@
M4$%25$E#54Q!4B!055)03U-%+B`@4V5E('1H90T**R`@("!'3E4@1V5N97)A
M;"!0=6)L:6,@3&EC96YS92!F;W(@;6]R92!D971A:6QS+@T**PT**R`@("!9
M;W4@<VAO=6QD(&AA=F4@<F5C96EV960@82!C;W!Y(&]F('1H92!'3E4@1V5N
M97)A;"!0=6)L:6,@3&EC96YS90T**R`@("!A;&]N9R!W:71H('1H:7,@<')O
M9W)A;3L@:68@;F]T+"!W<FET92!T;R!T:&4@1G)E92!3;V9T=V%R90T**R`@
M("!&;W5N9&%T:6]N+"!);F,N+"`V-S4@36%S<R!!=F4L($-A;6)R:61G92P@
M34$@,#(Q,SDL(%5302X-"BL@*@T**R`J(&UO=F5D(&EN=&\@<')O<&5R(&DR
M8R!I;G1E<F9A8V4[('-E<&%R871E9"!O=70@<&QA=&9O<FT@<W!E8VEF:6,-
M"BL@*B!P87)T<R!I;G1O(&DR8RUR<'@N8PT**R`J($)R860@4&%R:V5R("AB
M<F%D0&AE96QT;V4N8V]M*0T**R`J+PT**PT**R\O(%A86"!T;V1O#0HK+R\@
M=&EM96]U="!S;&5E<#\-"BL-"BLO*B`D260Z(&DR8RUA;&=O+3AX>"YC+'8@
M,2XW(#(P,#(O,#@O,#,@,C(Z-#@Z,3@@86,Y-#$P($5X<"`D("HO#0HK#0HK
M(VEN8VQU9&4@/&QI;G5X+VME<FYE;"YH/@T**R-I;F-L=61E(#QL:6YU>"]M
M;V1U;&4N:#X-"BLC:6YC;'5D92`\;&EN=7@O9&5L87DN:#X-"BLC:6YC;'5D
M92`\;&EN=7@O<VQA8BYH/@T**R-I;F-L=61E(#QL:6YU>"]V97)S:6]N+F@^
M#0HK(VEN8VQU9&4@/&QI;G5X+VEN:70N:#X-"BLC:6YC;'5D92`\87-M+W5A
M8V-E<W,N:#X-"BLC:6YC;'5D92`\;&EN=7@O:6]P;W)T+F@^#0HK(VEN8VQU
M9&4@/&QI;G5X+V5R<FYO+F@^#0HK(VEN8VQU9&4@/&QI;G5X+W-C:&5D+F@^
M#0HK#0HK(VEN8VQU9&4@/&%S;2]M<&,X>'@N:#X-"BLC:6YC;'5D92`\87-M
M+V-O;6UP<F]C+F@^#0HK#0HK(VEN8VQU9&4@/&QI;G5X+VDR8RYH/@T**R-I
M;F-L=61E(#QL:6YU>"]I,F,M86QG;RTX>'@N:#X-"BL-"BLC9&5F:6YE($-0
M35]-05A?4D5!1`DU,3,-"BLO*B!4<GD@=6YC;VUM96YT('1H:7,@:68@>6]U
M(&AA=F4@86X@;VQD97(@0U!5*&5A<FQI97(@=&AA;B!R978@1#0I("HO#0HK
M+RH@(V1E9FEN92!),D-?0TA)4%]%4E)!5$$@*B\-"BL-"BMS=&%T:6,@8VAA
M<B`J;6]D=6QE7VYA;64@/2`B:3)C7V%L9V]?.'AX(CL-"BLC9&5F:6YE($1%
M0E5'4"AL979E;"P@>"P@>2XN+BD@9&\@>R!<#0HK"0D)"6EF("AC<&U?9&5B
M=6<@/CT@;&5V96PI(%P-"BL)"0D)"7!R:6YT:RA+15).7T1%0E5'("(E<SH@
M(B!X+"!<#0HK"0D)"0D@("`@("`@;6]D=6QE7VYA;64L(",C('DI.R!<#0HK
M"0D)"7T@=VAI;&4H,"D-"BL-"BMS=&%T:6,@=V%I=%]Q=65U95]H96%D7W0@
M:6EC7W=A:70[#0HK<W1A=&EC('5S:&]R="!R7W1B87-E+"!R7W)B87-E.PT*
M*PT**VEN="!C<&U?<V-A;B`](#`[#0HK:6YT(&-P;5]D96)U9R`](#`[#0HK
M#0HK<W1A=&EC("!V;VED#0HK8W!M7VEI8U]I;G1E<G)U<'0H=F]I9"`J9&5V
M7VED+"!S=')U8W0@<'1?<F5G<R`J<F5G<RD-"BM[#0HK"79O;&%T:6QE(&DR
M8SAX>%]T("II,F,@/2`H:3)C.'AX7W0@*BED979?:60[#0HK#0HK"41%0E5'
M4"@R+"`B8W!M7VEI8U]I;G1E<G)U<'0H9&5V7VED/25P*5QN(BP@9&5V7VED
M*3L-"BL-"BLC:69D968@23)#7T-(25!?15)2051!#0HK"2\J($-H:7`@97)R
M871A+"!C;&5A<B!E;F%B;&4N#0HK"2`J(%1H:7,@<V5E;7,@=&\@;F]T(&)E
M(&YE961E9"!O;B!R978@1#0@;W(@;F5W97(@0U!5<RX-"BL)("H@4V]M96]N
M92!W:71H(&%N(&]L9&5R($-052!N965D<R!T;R!V97)I9GD@=&AI<RX-"BL)
M("HO#0HK"6DR8RT^:3)C7VDR;6]D("8]('XQ.PT**R-E;F1I9@T**PT**PDO
M*B!#;&5A<B!I;G1E<G)U<'0N#0HK"2HO#0HK"6DR8RT^:3)C7VDR8V5R(#T@
M,'AF9CL-"BL-"BL)+RH@1V5T("=M92!G;VEN9R!A9V%I;BX-"BL)*B\-"BL)
M=V%K95]U<%]I;G1E<G)U<'1I8FQE*"9I:6-?=V%I="D[#0HK?0T**PT**W-T
M871I8R!V;VED#0HK8W!M7VEI8U]I;FET*'-T<G5C="!I,F-?86QG;U\X>'A?
M9&%T82`J8W!M7V%D87`I#0HK>PT**PEV;VQA=&EL92!I:6-?=`D)*FEI<"`]
M(&-P;5]A9&%P+3YI:7`[#0HK"79O;&%T:6QE(&DR8SAX>%]T"2II,F,@/2!C
M<&U?861A<"T^:3)C.PT**PEU;G-I9VYE9"!C:&%R(&)R9SL-"BL)8F1?="`J
M8F0@/2`H8F1?="`J*5]?<F5S.PT**PT**PE$14)51U`H,2P@(F-P;5]I:6-?
M:6YI="@I("T@:6EP/25P7&XB+"!I:7`I.PT**PT**PDO*B!);FET:6%L:7IE
M('1H92!P87)A;65T97(@<F%M+@T**PD@*B!792!N965D('1O(&UA:V4@<W5R
M92!M86YY('1H:6YG<R!A<F4@:6YI=&EA;&EZ960@=&\@>F5R;RP-"BL)("H@
M97-P96-I86QL>2!I;B!T:&4@8V%S92!O9B!A(&UI8W)O8V]D92!P871C:"X-
M"BL)("HO#0HK"6EI<"T^:6EC7W)S=&%T92`](#`[#0HK"6EI<"T^:6EC7W)D
M<"`](#`[#0HK"6EI<"T^:6EC7W)B<'1R(#T@,#L-"BL):6EP+3YI:6-?<F)C
M(#T@,#L-"BL):6EP+3YI:6-?<GAT;7`@/2`P.PT**PEI:7`M/FEI8U]T<W1A
M=&4@/2`P.PT**PEI:7`M/FEI8U]T9'`@/2`P.PT**PEI:7`M/FEI8U]T8G!T
M<B`](#`[#0HK"6EI<"T^:6EC7W1B8R`](#`[#0HK"6EI<"T^:6EC7W1X=&UP
M(#T@,#L-"BL-"BL)+RH@4V5T('5P('1H92!)24,@<&%R86UE=&5R<R!I;B!T
M:&4@<&%R86UE=&5R(')A;2X-"BL)*B\-"BL):6EP+3YI:6-?=&)A<V4@/2!R
M7W1B87-E(#T@8W!M7V%D87`M/F1P7V%D9'([#0HK"6EI<"T^:6EC7W)B87-E
M(#T@<E]R8F%S92`](&-P;5]A9&%P+3YD<%]A9&1R("L@<VEZ96]F*&-B9%]T
M*2`J(#([#0HK#0HK"6EI<"T^:6EC7W1F8W(@/2!334-?14([#0HK"6EI<"T^
M:6EC7W)F8W(@/2!334-?14([#0HK#0HK"2\J(%-E="!M87AI;75M(')E8V5I
M=F4@<VEZ92X-"BL)*B\-"BL):6EP+3YI:6-?;7)B;'(@/2!#4$U?34%87U)%
M040[#0HK#0HK"2\J($EN:71I86QI>F4@5'@O4G@@<&%R86UE=&5R<RX-"BL)
M*B\-"BL):68@*&-P;5]A9&%P+3YR96QO8R`]/2`P*2![#0HK"0EV;VQA=&EL
M92!C<&TX>'A?="`J8W`@/2!C<&U?861A<"T^8W`[#0HK#0HK"0EC<"T^8W!?
M8W!C<B`]#0HK"0D);6M?8W)?8VUD*$-035]#4E]#2%]),D,L($-035]#4E])
M3DE47U126"D@?"!#4$U?0U)?1DQ'.PT**PD)=VAI;&4@*&-P+3YC<%]C<&-R
M("8@0U!-7T-27T9,1RD[#0HK"7T@96QS92![#0HK"0EI:7`M/FEI8U]R8G!T
M<B`](&EI<"T^:6EC7W)B87-E.PT**PD):6EP+3YI:6-?=&)P='(@/2!I:7`M
M/FEI8U]T8F%S93L-"BL)"6EI<"T^:6EC7W)S=&%T90D](#`[#0HK"0EI:7`M
M/FEI8U]T<W1A=&4)/2`P.PT**PE]#0HK#0HK"2\J(%-E;&5C="!A;B!A<F)I
M=')A<GD@861D<F5S<RX@($IU<W0@;6%K92!S=7)E(&ET(&ES('5N:7%U92X-
M"BL)*B\-"BL):3)C+3YI,F-?:3)A9&0@/2`P>&9E.PT**PT**PDO*B!-86ME
M(&-L;V-K(')U;B!A="`V,"!+2'HN#0HK"2HO#0HK"6)R9R`]("AU;G-I9VYE
M9"!C:&%R*2`H8F0M/F)I7VEN=&9R97$O*#,R*C(J-C`P,#`I("TS*3L-"BL)
M:3)C+3YI,F-?:3)B<F<@/2!B<F<[#0HK#0HK"6DR8RT^:3)C7VDR;6]D(#T@
M,'@P,#L-"BL):3)C+3YI,F-?:3)C;VT@/2`P>#`Q.R`O*B!-87-T97(@;6]D
M92`J+PT**PT**PDO*B!$:7-A8FQE(&EN=&5R<G5P=',N#0HK"2HO#0HK"6DR
M8RT^:3)C7VDR8VUR(#T@,#L-"BL):3)C+3YI,F-?:3)C97(@/2`P>&9F.PT*
M*PT**PEI;FET7W=A:71Q=65U95]H96%D*"9I:6-?=V%I="D[#0HK#0HK"2\J
M($EN<W1A;&P@:6YT97)R=7!T(&AA;F1L97(N#0HK"2HO#0HK"41%0E5'4"@Q
M+"`B26YS=&%L;"!)4U(@9F]R($E242`E9%QN(BP@0U!-5D5#7TDR0RD[#0HK
M"6-P;5]I;G-T86QL7VAA;F1L97(H0U!-5D5#7TDR0RP@8W!M7VEI8U]I;G1E
M<G)U<'0L("AV;VED("HI:3)C*3L-"BM]#0HK#0HK#0HK<W1A=&EC(&EN=`T*
M*V-P;5]I:6-?<VAU=&1O=VXH<W1R=6-T(&DR8U]A;&=O7SAX>%]D871A("IC
M<&U?861A<"D-"BM[#0HK"79O;&%T:6QE(&DR8SAX>%]T("II,F,@/2!C<&U?
M861A<"T^:3)C.PT**PT**PDO*B!3:'5T(&1O=VX@24E#+@T**PDJ+PT**PEI
M,F,M/FDR8U]I,FUO9"`F/2!^,3L-"BL):3)C+3YI,F-?:3)C;7(@/2`P.PT*
M*PEI,F,M/FDR8U]I,F-E<B`](#!X9F8[#0HK#0HK"7)E='5R;B`P.PT**WT-
M"BL-"BMS=&%T:6,@=F]I9`T**V-P;5]R97-E=%]I:6-?<&%R86US*'9O;&%T
M:6QE(&EI8U]T("II:7`I#0HK>PT**PEI:7`M/FEI8U]T8F%S92`](')?=&)A
M<V4[#0HK"6EI<"T^:6EC7W)B87-E(#T@<E]R8F%S93L-"BL-"BL):6EP+3YI
M:6-?=&9C<B`](%--0U]%0CL-"BL):6EP+3YI:6-?<F9C<B`](%--0U]%0CL-
M"BL-"BL):6EP+3YI:6-?;7)B;'(@/2!#4$U?34%87U)%040[#0HK#0HK"6EI
M<"T^:6EC7W)S=&%T92`](#`[#0HK"6EI<"T^:6EC7W)D<"`](#`[#0HK"6EI
M<"T^:6EC7W)B<'1R(#T@:6EP+3YI:6-?<F)A<V4[#0HK"6EI<"T^:6EC7W)B
M8R`](#`[#0HK"6EI<"T^:6EC7W)X=&UP(#T@,#L-"BL):6EP+3YI:6-?='-T
M871E(#T@,#L-"BL):6EP+3YI:6-?=&1P(#T@,#L-"BL):6EP+3YI:6-?=&)P
M='(@/2!I:7`M/FEI8U]T8F%S93L-"BL):6EP+3YI:6-?=&)C(#T@,#L-"BL)
M:6EP+3YI:6-?='AT;7`@/2`P.PT**WT-"BL-"BLC9&5F:6YE($)$7U-#7TY!
M2PD)*"AU<VAO<G0I,'@P,#`T*2`O*B!.04L@+2!D:60@;F]T(')E<W!O;F0@
M*B\-"BLC9&5F:6YE($)$7U-#7T]6"0DH*'5S:&]R="DP>#`P,#(I("\J($]6
M("T@<F5C96EV92!O=F5R<G5N("HO#0HK(V1E9FEN92!#4$U?0U)?0TQ/4T5?
M4EA"1`DH*'5S:&]R="DP>#`P,#<I#0HK#0HK<W1A=&EC('9O:60@9F]R8V5?
M8VQO<V4H<W1R=6-T(&DR8U]A;&=O7SAX>%]D871A("IC<&TI#0HK>PT**PEV
M;VQA=&EL92!I,F,X>'A?="`J:3)C(#T@8W!M+3YI,F,[#0HK"6EF("AC<&TM
M/G)E;&]C(#T](#`I('L@+RH@;6EC<F\@8V]D92!D:7-A8FQE9"`J+PT**PD)
M=F]L871I;&4@8W!M.'AX7W0@*F-P(#T@8W!M+3YC<#L-"BL-"BL)"41%0E5'
M4"@Q+"`B9F]R8V5?8VQO<V4H*5QN(BD[#0HK"0EC<"T^8W!?8W!C<B`]#0HK
M"0D);6M?8W)?8VUD*$-035]#4E]#2%]),D,L($-035]#4E]#3$]315]26$)$
M*2!\#0HK"0D)0U!-7T-27T9,1SL-"BL-"BL)"7=H:6QE("AC<"T^8W!?8W!C
M<B`F($-035]#4E]&3$<I.PT**PE]#0HK"6DR8RT^:3)C7VDR8VUR(#T@,'@P
M,#L)+RH@1&ES86)L92!A;&P@:6YT97)R=7!T<R`J+PT**PEI,F,M/FDR8U]I
M,F-E<B`](#!X9F8[#0HK?0T**PT**PT**R\J(%)E860@9G)O;2!)24,N+BX-
M"BL@*B!A8GET92`](&%D9')E<W,@8GET92P@=VET:"!R+W<@9FQA9R!A;')E
M861Y('-E=`T**R`J+PT**W-T871I8R!I;G0-"BMC<&U?:6EC7W)E860H<W1R
M=6-T(&DR8U]A;&=O7SAX>%]D871A("IC<&TL('5?8VAA<B!A8GET92P@8VAA
M<B`J8G5F+"!I;G0@8V]U;G0I#0HK>PT**PEV;VQA=&EL92!I:6-?="`J:6EP
M(#T@8W!M+3YI:7`[#0HK"79O;&%T:6QE(&DR8SAX>%]T("II,F,@/2!C<&TM
M/FDR8SL-"BL)=F]L871I;&4@8W!M.'AX7W0@*F-P(#T@8W!M+3YC<#L-"BL)
M=F]L871I;&4@8V)D7W0)*G1B9&8L("IR8F1F.PT**PEU7V-H87(@*G1B.PT*
M*PEU;G-I9VYE9"!L;VYG(&9L86=S+"!T;6\[#0HK#0HK"6EF("AC;W5N="`^
M/2!#4$U?34%87U)%040I#0HK"0ER971U<FX@+45)3E9!3#L-"BL-"BL)+RH@
M8VAE8VL@9F]R(&%N9"!U<V4@82!M:6-R;V-O9&4@<F5L;V-A=&EO;B!P871C
M:"`J+PT**PEI9B`H8W!M+3YR96QO8RD-"BL)"6-P;5]R97-E=%]I:6-?<&%R
M86US*&EI<"D[#0HK#0HK"71B9&8@/2`H8V)D7W0@*BDF8W`M/F-P7V1P;65M
M6VEI<"T^:6EC7W1B87-E73L-"BL)<F)D9B`]("AC8F1?="`J*29C<"T^8W!?
M9'!M96U;:6EP+3YI:6-?<F)A<V5=.PT**PT**PDO*B!4;R!R96%D+"!W92!N
M965D(&%N(&5M<'1Y(&)U9F9E<B!O9B!T:&4@<')O<&5R(&QE;F=T:"X-"BL)
M("H@06QL('1H870@:7,@=7-E9"!I<R!T:&4@9FER<W0@8GET92!F;W(@861D
M<F5S<RP@=&AE(')E;6%I;F1E<@T**PD@*B!I<R!J=7-T('5S960@9F]R('1I
M;6EN9R`H86YD(&1O97-N)W0@<F5A;&QY(&AA=F4@=&\@97AI<W0I+@T**PD@
M*B\-"BL)=&(@/2!C<&TM/G1E;7`[#0HK"71B(#T@*'5?8VAA<B`J*2@H*'5I
M;G0I=&(@*R`Q-2D@)B!^,34I.PT**PET8ELP72`](&%B>71E.PD)+RH@1&5V
M:6-E(&%D9')E<W,@8GET92!W+W)W(&9L86<@*B\-"BL-"BL)9FQU<VA?9&-A
M8VAE7W)A;F=E*"AU;G-I9VYE9"!L;VYG*2!T8BP@*'5N<VEG;F5D(&QO;F<I
M("AT8B`K(#$I*3L-"BL-"BL)1$5"54=0*#$L(")C<&U?:6EC7W)E860H86)Y
M=&4],'@E>"E<;B(L(&%B>71E*3L-"BL-"BL)=&)D9BT^8V)D7V)U9F%D9'(@
M/2!?7W!A*'1B*3L-"BL)=&)D9BT^8V)D7V1A=&QE;B`](&-O=6YT("L@,3L-
M"BL)=&)D9BT^8V)D7W-C(#T-"BL)"4)$7U-#7U)%0419('P@0D1?4T-?3$%3
M5"!\#0HK"0E"1%]30U]74D%0('P@0D1?24E#7U-405)4.PT**PT**PEI:7`M
M/FEI8U]M<F)L<B`](&-O=6YT("L@,3L@+RH@<')E=F5N="!E>&-E<W-I=F4@
M<F5A9"P@*S$-"BL)"0D)("`@("`@:7,@;F5E9&5D(&]T:&5R=VES92!W:6QL
M('1H90T**PD)"0D@("`@("!26$(@:6YT97)R=7!T(&-O;64@=&]O(&5A<FQY
M("HO#0HK#0HK"2\J(&9L=7-H('=I;&P@:6YV86QI9&%T92!T;V\N("HO#0HK
M"69L=7-H7V1C86-H95]R86YG92@H=6YS:6=N960@;&]N9RD@8G5F+"`H=6YS
M:6=N960@;&]N9RD@*&)U9BMC;W5N="DI.PT**PT**PER8F1F+3YC8F1?9&%T
M;&5N(#T@,#L-"BL)<F)D9BT^8V)D7V)U9F%D9'(@/2!?7W!A*&)U9BD[#0HK
M#0HK"7)B9&8M/F-B9%]S8R`]($)$7U-#7T5-4%19('P@0D1?4T-?5U)!4'P@
M0D1?4T-?24Y44E!4.PT**PT**PDO*B!#:&EP(&)U9RP@<V5T(&5N86)L92!H
M97)E("HO#0HK"6QO8V%L7VER<5]S879E*&9L86=S*3L-"BL):3)C+3YI,F-?
M:3)C;7(@/2`P>#$S.PDO*B!%;F%B;&4@<V]M92!I;G1E<G5P=',@*B\-"BL)
M:3)C+3YI,F-?:3)C97(@/2`P>&9F.PT**PEI,F,M/FDR8U]I,FUO9"!\/2`Q
M.PDO*B!%;F%B;&4@*B\-"BL)+RH-"BL)("H@0F5G:6X@=')A;G-M:7-S:6]N
M(&%N9"!F;W)C92!M87-T97(N#0HK"2`J(%-O;64@97)R;W)S*$-,*2!C;&5A
M<G,@=&AE($TO4R!B:70-"BL)("HO#0HK"6DR8RT^:3)C7VDR8V]M('P](#!X
M.#`@?"`P>#`Q.PT**PT**PDO*B!786ET(&9O<B!)24,@=')A;G-F97(@*B\-
M"BL)=&UO(#T@:6YT97)R=7!T:6)L95]S;&5E<%]O;E]T:6UE;W5T*"9I:6-?
M=V%I="PQ*DA:*3L-"BL);&]C86Q?:7)Q7W)E<W1O<F4H9FQA9W,I.PT**PT*
M*PEI9B`H<VEG;F%L7W!E;F1I;F<H8W5R<F5N="D@?'P@(71M;RE[#0HK"0EF
M;W)C95]C;&]S92AC<&TI.PT**PD)1$5"54=0*#$L("))24,@<F5A9#H@=&EM
M96]U="%<;B(I.PT**PD)<F5T=7)N("U%24\[#0HK"7T-"BLC:69D968@23)#
M7T-(25!?15)2051!#0HK"2\J($-H:7`@97)R871A+"!C;&5A<B!E;F%B;&4N
M(%1H:7,@:7,@;F]T(&YE961E9"!O;B!R978@1#0@0U!5<RX-"BL)($1I<V%B
M;&EN9R!),D,@=&]O(&5A<FQY(&UA>2!C875S92!T;V\@<VAO<G0@<W1O<"!C
M;VYD:71I;VX@*B\-"BL)=61E;&%Y*#0I.PT**PEI,F,M/FDR8U]I,FUO9"`F
M/2!^,3L-"BLC96YD:68-"BL-"BL)1$5"54=0*#$L(")T>"!S8R`E,#1X+"!R
M>"!S8R`E,#1X7&XB+"!T8F1F+3YC8F1?<V,L(')B9&8M/F-B9%]S8RD[#0HK
M#0HK"6EF("AT8F1F+3YC8F1?<V,@)B!"1%]30U]214%$62D@>PT**PD)<')I
M;G1K*$M%4DY?24Y&3R`B24E#(')E860[(&-O;7!L971E(&)U="!T8G5F(')E
M861Y7&XB*3L-"BL)"69O<F-E7V-L;W-E*&-P;2D[#0HK"0EP<FEN=&LH2T52
M3E])3D9/(")T>"!S8R`E,#1X+"!R>"!S8R`E,#1X7&XB+`T**PD)("`@("`@
M('1B9&8M/F-B9%]S8RP@<F)D9BT^8V)D7W-C*3L-"BL)?0T**PT**PEI9B`H
M=&)D9BT^8V)D7W-C("8@0D1?4T-?3D%+*2![#0HK"0E$14)51U`H,2P@(DE)
M0R!R96%D.R!N;R!A8VM<;B(I.PT**PD)<F5T=7)N("U%4D5-3U1%24\[#0HK
M"7T-"BL-"BL):68@*')B9&8M/F-B9%]S8R`F($)$7U-#7T5-4%19*2![#0HK
M"0DO*B!F;W)C95]C;&]S92AC<&TI.R`J+PT**PD)1$5"54=0*#$L("))24,@
M<F5A9#L@8V]M<&QE=&4@8G5T(')B=68@96UP='E<;B(I.PT**PD)1$5"54=0
M*#$L(")T>"!S8R`E,#1X+"!R>"!S8R`E,#1X7&XB+"!T8F1F+3YC8F1?<V,L
M(')B9&8M/F-B9%]S8RD[#0HK"0ER971U<FX@+45214U/5$5)3SL-"BL)?0T*
M*PT**PEI9B`H<F)D9BT^8V)D7W-C("8@0D1?4T-?3U8I('L-"BL)"41%0E5'
M4"@Q+"`B24E#(')E860[($]V97)R=6Y<;B(I.PT**PD)<F5T=7)N("U%4D5-
M3U1%24\[.PT**PE]#0HK#0HK"41%0E5'4"@Q+"`B<F5A9"`E9"!B>71E<UQN
M(BP@<F)D9BT^8V)D7V1A=&QE;BD[#0HK#0HK"6EF("AR8F1F+3YC8F1?9&%T
M;&5N(#P@8V]U;G0I('L-"BL)"41%0E5'4"@Q+"`B24E#(')E860[('-H;W)T
M+"!W86YT960@)60@9V]T("5D7&XB+"!C;W5N="P-"BL)"2`@("`@("!R8F1F
M+3YC8F1?9&%T;&5N*3L-"BL)"7)E='5R;B`P.PT**PE]#0HK#0HK"7)E='5R
M;B!C;W5N=#L-"BM]#0HK#0HK+RH@5W)I=&4@=&\@24E#+BXN#0HK("H@861D
M<B`](&%D9')E<W,@8GET92P@=VET:"!R+W<@9FQA9R!A;')E861Y('-E=`T*
M*R`J+PT**W-T871I8R!I;G0-"BMC<&U?:6EC7W=R:71E*'-T<G5C="!I,F-?
M86QG;U\X>'A?9&%T82`J8W!M+"!U7V-H87(@86)Y=&4L(&-H87(@*F)U9BQI
M;G0@8V]U;G0I#0HK>PT**PEV;VQA=&EL92!I:6-?="`J:6EP(#T@8W!M+3YI
M:7`[#0HK"79O;&%T:6QE(&DR8SAX>%]T("II,F,@/2!C<&TM/FDR8SL-"BL)
M=F]L871I;&4@8W!M.'AX7W0@*F-P(#T@8W!M+3YC<#L-"BL)=F]L871I;&4@
M8V)D7W0)*G1B9&8[#0HK"75?8VAA<B`J=&([#0HK"75N<VEG;F5D(&QO;F<@
M9FQA9W,L('1M;SL-"BL-"BL)+RH@8VAE8VL@9F]R(&%N9"!U<V4@82!M:6-R
M;V-O9&4@<F5L;V-A=&EO;B!P871C:"`J+PT**PEI9B`H8W!M+3YR96QO8RD-
M"BL)"6-P;5]R97-E=%]I:6-?<&%R86US*&EI<"D[#0HK"71B(#T@8W!M+3YT
M96UP.PT**PET8B`]("AU7V-H87(@*BDH*"AU:6YT*71B("L@,34I("8@?C$U
M*3L-"BL)*G1B(#T@86)Y=&4["0DO*B!$979I8V4@861D<F5S<R!B>71E('<O
M<G<@9FQA9R`J+PT**PT**PEF;'5S:%]D8V%C:&5?<F%N9V4H*'5N<VEG;F5D
M(&QO;F<I('1B+"`H=6YS:6=N960@;&]N9RD@*'1B*S$I*3L-"BL)9FQU<VA?
M9&-A8VAE7W)A;F=E*"AU;G-I9VYE9"!L;VYG*2!B=68L("AU;G-I9VYE9"!L
M;VYG*2`H8G5F*V-O=6YT*2D[#0HK#0HK"41%0E5'4"@Q+"`B8W!M7VEI8U]W
M<FET92AA8GET93TP>"5X*5QN(BP@86)Y=&4I.PT**PT**PDO*B!S970@=7`@
M,B!D97-C<FEP=&]R<R`J+PT**PET8F1F(#T@*&-B9%]T("HI)F-P+3YC<%]D
M<&UE;5MI:7`M/FEI8U]T8F%S95T[#0HK#0HK"71B9&9;,%TN8V)D7V)U9F%D
M9'(@/2!?7W!A*'1B*3L-"BL)=&)D9ELP72YC8F1?9&%T;&5N(#T@,3L-"BL)
M=&)D9ELP72YC8F1?<V,@/2!"1%]30U]214%$62!\($)$7TE)0U]35$%25#L-
M"BL-"BL)=&)D9ELQ72YC8F1?8G5F861D<B`](%]?<&$H8G5F*3L-"BL)=&)D
M9ELQ72YC8F1?9&%T;&5N(#T@8V]U;G0[#0HK"71B9&9;,5TN8V)D7W-C(#T@
M0D1?4T-?4D5!1%D@?"!"1%]30U])3E124%0@?"!"1%]30U],05-4('P@0D1?
M4T-?5U)!4#L-"BL-"BL)+RH@0VAI<"!B=6<L('-E="!E;F%B;&4@:&5R92`J
M+PT**PEL;V-A;%]I<G%?<V%V92AF;&%G<RD[#0HK"6DR8RT^:3)C7VDR8VUR
M(#T@,'@Q,SL)+RH@16YA8FQE('-O;64@:6YT97)U<'1S("HO#0HK"6DR8RT^
M:3)C7VDR8V5R(#T@,'AF9CL-"BL):3)C+3YI,F-?:3)M;V0@?#T@,3L)+RH@
M16YA8FQE("HO#0HK"2\J#0HK"2`J($)E9VEN('1R86YS;6ES<VEO;B!A;F0@
M9F]R8V4@;6%S=&5R+@T**PD@*B!3;VUE(&5R<F]R<RA#3"D@8VQE87)S('1H
M92!-+U,@8FET#0HK"2`J+PT**PEI,F,M/FDR8U]I,F-O;2!\/2`P>#@P('P@
M,'@P,3L-"BL-"BL)+RH@5V%I="!F;W(@24E#('1R86YS9F5R("HO#0HK"71M
M;R`](&EN=&5R<G5P=&EB;&5?<VQE97!?;VY?=&EM96]U="@F:6EC7W=A:70L
M,2I(6BD[#0HK"6QO8V%L7VER<5]R97-T;W)E*&9L86=S*3L-"BL-"BL):68@
M*'-I9VYA;%]P96YD:6YG*&-U<G)E;G0I('Q\("%T;6\I>PT**PD)9F]R8V5?
M8VQO<V4H8W!M*3L-"BL)"6EF("@A=&UO*0T**PD)"41%0E5'4"@Q+"`B24E#
M('=R:71E.B!T:6UE;W5T(5QN(BD[#0HK"0ER971U<FX@+45)3SL-"BL)?0T*
M*PT**R-I9B!),D-?0TA)4%]%4E)!5$$-"BL)+RH@0VAI<"!E<G)A=&$L(&-L
M96%R(&5N86)L92X@5&AI<R!I<R!N;W0@;F5E9&5D(&]N(')E=B!$-"!#4%5S
M+@T**PD@1&ES86)L:6YG($DR0R!T;V\@96%R;'D@;6%Y(&-A=7-E('1O;R!S
M:&]R="!S=&]P(&-O;F1I=&EO;B`J+PT**PEU9&5L87DH-"D[#0HK"6DR8RT^
M:3)C7VDR;6]D("8]('XQ.PT**R-E;F1I9@T**PE$14)51U`H,2P@(G1X,"!S
M8R`E,#1X+"!T>#$@<V,@)3`T>%QN(BP@=&)D9ELP72YC8F1?<V,L#0HK"2`@
M("`@("!T8F1F6S%=+F-B9%]S8RD[#0HK#0HK"6EF("@H=&)D9ELP72YC8F1?
M<V,@?"!T8F1F6S%=+F-B9%]S8RD@)B!"1%]30U].04LI('L-"BL)"41%0E5'
M4"@Q+"`B24E#('=R:71E.R!N;R!A8VM<;B(I.PT**PD)<F5T=7)N(#`[#0HK
M"7T-"BL-"BL):68@*"AT8F1F6S!=+F-B9%]S8R!\('1B9&9;,5TN8V)D7W-C
M*2`F($)$7U-#7U)%0419*2![#0HK"0E$14)51U`H,2P@(DE)0R!W<FET93L@
M8V]M<&QE=&4@8G5T('1B=68@<F5A9'E<;B(I.PT**PD)<F5T=7)N(#`[#0HK
M"7T-"BL-"BL)<F5T=7)N(&-O=6YT.PT**WT-"BL-"BLO*B!3964@:68@86X@
M24E#(&%D9')E<W,@97AI<W1S+BX-"BL@*B!A9&1R(#T@-R!B:70@861D<F5S
M<RP@=6YS:&EF=&5D#0HK("HO#0HK<W1A=&EC(&EN=`T**V-P;5]I:6-?=')Y
M861D<F5S<RAS=')U8W0@:3)C7V%L9V]?.'AX7V1A=&$@*F-P;2P@:6YT(&%D
M9'(I#0HK>PT**PEV;VQA=&EL92!I:6-?="`J:6EP(#T@8W!M+3YI:7`[#0HK
M"79O;&%T:6QE(&DR8SAX>%]T("II,F,@/2!C<&TM/FDR8SL-"BL)=F]L871I
M;&4@8W!M.'AX7W0@*F-P(#T@8W!M+3YC<#L-"BL)=F]L871I;&4@8V)D7W0@
M*G1B9&8L("IR8F1F.PT**PEU7V-H87(@*G1B.PT**PEU;G-I9VYE9"!L;VYG
M(&9L86=S+"!L96XL('1M;SL-"BL-"BL)1$5"54=0*#(L(")C<&U?:6EC7W1R
M>6%D9')E<W,H8W!M/25P+&%D9'(])60I7&XB+"!C<&TL(&%D9'(I.PT**PT*
M*PDO*B!C:&5C:R!F;W(@86YD('5S92!A(&UI8W)O8V]D92!R96QO8V%T:6]N
M('!A=&-H("HO#0HK"6EF("AC<&TM/G)E;&]C*0T**PD)8W!M7W)E<V5T7VEI
M8U]P87)A;7,H:6EP*3L-"BL-"BL):68@*&%D9'(@/3T@,"D@>PT**PD)1$5"
M54=0*#$L(")I:7`@)7`L(&1P7V%D9'(@,'@E>%QN(BP@8W!M+3YI:7`L(&-P
M;2T^9'!?861D<BD[#0HK"0E$14)51U`H,2P@(FEI8U]T8F%S92`E9"P@<E]T
M8F%S92`E9%QN(BP@:6EP+3YI:6-?=&)A<V4L#0HK"0D@("`@("`@<E]T8F%S
M92D[#0HK"7T-"BL-"BL)=&)D9B`]("AC8F1?="`J*29C<"T^8W!?9'!M96U;
M:6EP+3YI:6-?=&)A<V5=.PT**PER8F1F(#T@*&-B9%]T("HI)F-P+3YC<%]D
M<&UE;5MI:7`M/FEI8U]R8F%S95T[#0HK#0HK"71B(#T@8W!M+3YT96UP.PT*
M*PET8B`]("AU7V-H87(@*BDH*"AU:6YT*71B("L@,34I("8@?C$U*3L-"BL-
M"BL)+RH@9&\@82!S:6UP;&4@<F5A9"`J+PT**PET8ELP72`]("AA9&1R(#P\
M(#$I('P@,3L)+RH@9&5V:6-E(&%D9')E<W,@*"L@<F5A9"D@*B\-"BL);&5N
M(#T@,CL-"BL-"BL)9FQU<VA?9&-A8VAE7W)A;F=E*"AU;G-I9VYE9"!L;VYG
M*2!T8BP@*'5N<VEG;F5D(&QO;F<I("AT8BLQ*2D[#0HK#0HK"71B9&8M/F-B
M9%]B=69A9&1R(#T@7U]P82AT8BD[#0HK"71B9&8M/F-B9%]D871L96X@/2!L
M96X[#0HK"71B9&8M/F-B9%]S8R`]#0HK"0E"1%]30U]214%$62!\($)$7U-#
M7TQ!4U0@?`T**PD)0D1?4T-?5U)!4"!\($)$7TE)0U]35$%25#L-"BL-"BL)
M<F)D9BT^8V)D7V1A=&QE;B`](#`[#0HK"7)B9&8M/F-B9%]B=69A9&1R(#T@
M7U]P82AT8BLR*3L-"BL)<F)D9BT^8V)D7W-C(#T@0D1?4T-?14U05%D@?"!"
M1%]30U]74D%0('P@0D1?4T-?24Y44E!4.PT**PT**PDO*B!#:&EP(&)U9RP@
M<V5T(&5N86)L92!H97)E("HO#0HK"6QO8V%L7VER<5]S879E*&9L86=S*3L-
M"BL):3)C+3YI,F-?:3)C;7(@/2`P>#$S.PDO*B!%;F%B;&4@<V]M92!I;G1E
M<G5P=',@*B\-"BL):3)C+3YI,F-?:3)C97(@/2`P>&9F.PT**PEI,F,M/FDR
M8U]I,FUO9"!\/2`Q.PDO*B!%;F%B;&4@*B\-"BL)+RH-"BL)("H@0F5G:6X@
M=')A;G-M:7-S:6]N(&%N9"!F;W)C92!M87-T97(N#0HK"2`J(%-O;64@97)R
M;W)S*$-,*2!C;&5A<G,@=&AE($TO4R!B:70-"BL)("HO#0HK"6DR8RT^:3)C
M7VDR8V]M('P](#!X.#`@?"`P>#`Q.PT**PT**PDO*B!786ET(&9O<B!)24,@
M=')A;G-F97(@*B\-"BL)=&UO(#T@:6YT97)R=7!T:6)L95]S;&5E<%]O;E]T
M:6UE;W5T*"9I:6-?=V%I="PQ*DA:*3L-"BL);&]C86Q?:7)Q7W)E<W1O<F4H
M9FQA9W,I.PT**PT**R-I9F1E9B!),D-?0TA)4%]%4E)!5$$-"BL)+RH@0VAI
M<"!E<G)A=&$L(&-L96%R(&5N86)L92X@5&AI<R!I<R!N;W0@;F5E9&5D(&]N
M(')E=B!$-"!#4%5S+@T**PD@1&ES86)L:6YG($DR0R!T;V\@96%R;'D@;6%Y
M(&-A=7-E('1O;R!S:&]R="!S=&]P(&-O;F1I=&EO;B`J+PT**PEU9&5L87DH
M-"D[#0HK"6DR8RT^:3)C7VDR;6]D("8]('XQ.PT**R-E;F1I9@T**PT**PEI
M9B`H<VEG;F%L7W!E;F1I;F<H8W5R<F5N="D@?'P@(71M;RE[#0HK"0EF;W)C
M95]C;&]S92AC<&TI.PT**PD):68@*"%T;6\I#0HK"0D)1$5"54=0*#$L("))
M24,@=')Y861D<F5S<SH@=&EM96]U="%<;B(I.PT**PD)<F5T=7)N("U%24\[
M#0HK"7T-"BL-"BL)1$5"54=0*#(L(")B86-K(&9R;VT@<VQE97!<;B(I.PT*
M*PT**PEI9B`H=&)D9BT^8V)D7W-C("8@0D1?4T-?3D%+*2![#0HK"0E$14)5
M1U`H,BP@(DE)0R!T<GD[(&YO(&%C:UQN(BD[#0HK"0ER971U<FX@,#L-"BL)
M?0T**PT**PEI9B`H=&)D9BT^8V)D7W-C("8@0D1?4T-?4D5!1%DI#0HK"0EP
M<FEN=&LH2T523E])3D9/("))24,@=')Y.R!C;VUP;&5T92!B=70@=&)U9B!R
M96%D>5QN(BD[#0HK#0HK"7)E='5R;B`Q.PT**WT-"BL-"BMS=&%T:6,@:6YT
M(&-P;5]X9F5R*'-T<G5C="!I,F-?861A<'1E<B`J:3)C7V%D87`L#0HK"0D@
M("`@<W1R=6-T(&DR8U]M<V<@;7-G<UM=+`T**PD)("`@(&EN="!N=6TI#0HK
M>PT**PES=')U8W0@:3)C7V%L9V]?.'AX7V1A=&$@*F%D87`@/2!I,F-?861A
M<"T^86QG;U]D871A.PT**PES=')U8W0@:3)C7VUS9R`J<&US9SL-"BL):6YT
M(&DL(')E=#L-"BL)=5]C:&%R(&%D9'([#0HK#0HK"69O<B`H:2`](#`[(&D@
M/"!N=6T[(&DK*RD@>PT**PD)<&US9R`]("9M<V=S6VE=.PT**PT**PD)1$5"
M54=0*#$L("(C)60@861D<CTP>"5X(&9L86=S/3!X)7@@;&5N/25D7&X@8G5F
M/25P7&XB+`T**PD)("`@("`@(&DL('!M<V<M/F%D9'(L('!M<V<M/F9L86=S
M+"!P;7-G+3YL96XL('!M<V<M/F)U9BD[#0HK#0HK"0EA9&1R(#T@<&US9RT^
M861D<B`\/"`Q.PT**PD):68@*'!M<V<M/F9L86=S("8@23)#7TU?4D0I#0HK
M"0D)861D<B!\/2`Q.PT**PD):68@*'!M<V<M/F9L86=S("8@23)#7TU?4D56
M7T1)4E]!1$12*0T**PD)"6%D9'(@7CT@,3L-"BL-"BL)"6EF("AP;7-G+3YF
M;&%G<R`F($DR0U]-7U)$*2![#0HK"0D)+RH@<F5A9"!B>71E<R!I;G1O(&)U
M9F9E<BHO#0HK"0D)<F5T(#T@8W!M7VEI8U]R96%D*&%D87`L(&%D9'(L('!M
M<V<M/F)U9BP@<&US9RT^;&5N*3L-"BL)"0E$14)51U`H,2P@(G)E860@)60@
M8GET97-<;B(L(')E="D[#0HK"0D):68@*')E="`\('!M<V<M/FQE;BD-"BL)
M"0D)<F5T=7)N("AR970@/"`P*3\@<F5T.BU%4D5-3U1%24\[#0HK"0E](&5L
M<V4@>PT**PD)"2\J('=R:71E(&)Y=&5S(&9R;VT@8G5F9F5R("HO#0HK"0D)
M<F5T(#T@8W!M7VEI8U]W<FET92AA9&%P+"!A9&1R+"!P;7-G+3YB=68L('!M
M<V<M/FQE;BD[#0HK"0D)1$5"54=0*#$L(")W<F]T92`E9%QN(BP@<F5T*3L-
M"BL)"0EI9B`H<F5T(#P@<&US9RT^;&5N*0T**PD)"0ER971U<FX@*')E="`\
M(#`I/R!R970Z+45214U/5$5)3SL-"BL)"7T-"BL)?0T**PER971U<FX@;G5M
M.PT**WT-"BL-"BMS=&%T:6,@=3,R(&-P;5]F=6YC*'-T<G5C="!I,F-?861A
M<'1E<B`J861A<"D-"BM[#0HK"7)E='5R;B!),D-?1E5.0U]334)54U]%355,
M('P@23)#7T953D-?,3!"251?041$4B!\#0HK"2`@("`@("!),D-?1E5.0U]0
M4D]43T-/3%]-04Y'3$E.1SL-"BM]#0HK#0HK<W1A=&EC('-T<G5C="!I,F-?
M86QG;W)I=&AM(&DR8U]A;&=O7SAX>"`]('L-"BL)+FYA;64@/2`B35!#.'AX
M($-032!A;&=O<FET:&TB+`T**PDN:60@/2!),D-?04Q'3U]-4$,X6%@L#0HK
M"2YM87-T97)?>&9E<B`](&-P;5]X9F5R+`T**PDN9G5N8W1I;VYA;&ET>2`]
M(&-P;5]F=6YC+`T**WT[#0HK#0HK+RH-"BL@*B!R96=I<W1E<FEN9R!F=6YC
M=&EO;G,@=&\@;&]A9"!A;&=O<FET:&US(&%T(')U;G1I;64-"BL@*B\-"BMI
M;G0@:3)C7SAX>%]A9&1?8G5S*'-T<G5C="!I,F-?861A<'1E<B`J861A<"D-
M"BM[#0HK"6EN="!I.PT**PES=')U8W0@:3)C7V%L9V]?.'AX7V1A=&$@*F-P
M;5]A9&%P(#T@861A<"T^86QG;U]D871A.PT**PT**PE$14)51U`H,2P@(FAW
M(')O=71I;F5S(&9O<B`E<R!R96=I<W1E<F5D+EQN(BP@861A<"T^;F%M92D[
M#0HK#0HK"2\J(')E9VES=&5R(&YE=R!A9&%P=&5R('1O(&DR8R!M;V1U;&4N
M+BX@*B\-"BL-"BL)861A<"T^:60@?#T@:3)C7V%L9V]?.'AX+FED.PT**PEA
M9&%P+3YA;&=O(#T@)FDR8U]A;&=O7SAX>#L-"BL-"BL):3)C7V%D9%]A9&%P
M=&5R*&%D87`I.PT**PEC<&U?:6EC7VEN:70H8W!M7V%D87`I.PT**PT**PDO
M*B!S8V%N(&)U<R`J+PT**PEI9B`H8W!M7W-C86XI('L-"BL)"7!R:6YT:RA+
M15).7TE.1D\@(B5S.B!S8V%N;FEN9R!B=7,@)7,N+BY<;B(L(&UO9'5L95]N
M86UE+`T**PD)("`@("`@(&%D87`M/FYA;64I.PT**PD)9F]R("AI(#T@,#L@
M:2`\(#$R.#L@:2LK*0T**PD)"6EF("AC<&U?:6EC7W1R>6%D9')E<W,H8W!M
M7V%D87`L(&DI*2![#0HK"0D)"7!R:6YT:R@B*"4P,G@I(BP@:2`\/"`Q*3L-
M"BL)"0E]#0HK"0EP<FEN=&LH(EQN(BD[#0HK"7T-"BL)<F5T=7)N(#`[#0HK
M?0T**PT**VEN="!I,F-?.'AX7V1E;%]B=7,H<W1R=6-T(&DR8U]A9&%P=&5R
M("IA9&%P*0T**WL-"BL):6YT(')E<SL-"BL)<W1R=6-T(&DR8U]A;&=O7SAX
M>%]D871A("IC<&U?861A<"`](&%D87`M/F%L9V]?9&%T83L-"BL-"BL)8W!M
M7VEI8U]S:'5T9&]W;BAC<&U?861A<"D[#0HK#0HK"6EF("@H<F5S(#T@:3)C
M7V1E;%]A9&%P=&5R*&%D87`I*2`\(#`I#0HK"0ER971U<FX@<F5S.PT**PT*
M*PEP<FEN=&LH2T523E])3D9/("(E<SH@861A<'1E<B!U;G)E9VES=&5R960Z
M("5S7&XB+"!M;V1U;&5?;F%M92P-"BL)("`@("`@(&%D87`M/FYA;64I.PT*
M*PT**PER971U<FX@,#L-"BM]#0HK#0HK;6]D=6QE7W!A<F%M*&-P;5]D96)U
M9RP@:6YT+"!37TE254=/('P@4U])5U534BD[#0HK34]$54Q%7U!!4DU?1$53
M0RAC<&U?9&5B=6<L(")3971S('1H92!D96)U9R!L979E;"X@*#`@/2!N;VYE
M+"`Q(#T@;F]R;6%L+"`B#0HK"0D@(CXQ(#T@<&QE;G1Y(BD[#0HK34]$54Q%
M7T%55$A/4B@B0G)A9"!087)K97(@/&)R861`:&5E;'1O92YC;VT^(BD[#0HK
M34]$54Q%7T1%4T-225!424].*")),D,M0G5S($U00SA86"!A;&=O<FET:&TB
M*3L-"BM-3T153$5?3$E#14Y312@B1U!,(BD[#0HK#0HK15A03U)47U-934)/
M3"AI,F-?.'AX7V%D9%]B=7,I.PT**T584$]25%]364U"3TPH:3)C7SAX>%]D
M96Q?8G5S*3L-"BL-"DEN9&5X.B`R+C8M.'AX+V1R:79E<G,O:3)C+V%L9V]S
M+TUA:V5F:6QE#0H]/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]
M/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]#0HM+2T@,BXV+3AX
M>"YO<FEG+V1R:79E<G,O:3)C+V%L9V]S+TUA:V5F:6QE"3(P,#4M,#@M,#@@
M,#DZ-3`Z-3<N,#`P,#`P,#`P("TP,S`P#0HK*RL@,BXV+3AX>"]D<FEV97)S
M+VDR8R]A;&=O<R]-86ME9FEL90DR,#`U+3`X+3`X(#`Y.C4P.C4Y+C`P,#`P
M,#`P,"`M,#,P,`T*0$`@+3@L-B`K."PW($!`#0H@;V)J+20H0T].1DE'7TDR
M0U]!3$=/251%*0DK/2!I,F,M86QG;RUI=&4N;PT*(&]B:BTD*$-/3D9)1U])
M,D-?04Q'3U]324)95$4I"2L](&DR8RUA;&=O+7-I8GET92YO#0H@;V)J+20H
M0T].1DE'7TDR0U]!3$=/7U-'22D)*ST@:3)C+6%L9V\M<V=I+F\-"BMO8FHM
M)"A#3TY&24=?23)#7T%,1T\X6%@I"2L](&DR8RUA;&=O+3AX>"YO#0H@#0H@
M:69E<2`H)"A#3TY&24=?23)#7T1%0E5'7T%,1T\I+'DI#0H@15A44D%?0T9,
M04=3("L]("U$1$5"54<-"DEN9&5X.B`R+C8M.'AX+VEN8VQU9&4O;&EN=7@O
M:3)C+6%L9V\M.'AX+F@-"CT]/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]
M/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T-"BTM+2`O
M9&5V+VYU;&P),3DW,"TP,2TP,2`P,#HP,#HP,"XP,#`P,#`P,#`@*S`P,#`-
M"BLK*R`R+C8M.'AX+VEN8VQU9&4O;&EN=7@O:3)C+6%L9V\M.'AX+F@),C`P
M-2TP."TP."`P.3HU,#HU.2XP,#`P,#`P,#`@+3`S,#`-"D!`("TP+#`@*S$L
M,C@@0$`-"BLO*B`M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM
M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM("HO#0HK
M+RH@:3)C+6%L9V\M.'AX+F@@:3)C(&1R:79E<B!A;&=O<FET:&US(&9O<B!-
M4%@X6%@@0U!-"0D)("`@("`J+PT**R\J("TM+2TM+2TM+2TM+2TM+2TM+2TM
M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM
M+2TM+2TM+2T@*B\-"BL-"BLO*B`D260D("HO#0HK#0HK(VEF;F1E9B!),D-?
M04Q'3U\X6%A?2`T**R-D969I;F4@23)#7T%,1T]?.%A87T@-"BL-"BLC:6YC
M;'5D92`\;&EN=7@O:3)C+F@^#0HK(VEN8VQU9&4@/&%S;2\X>'A?:6UM87`N
M:#X-"BLC:6YC;'5D92`\87-M+V-O;6UP<F]C+F@^#0HK#0HK<W1R=6-T(&DR
M8U]A;&=O7SAX>%]D871A('L-"BL)=6EN="!D<%]A9&1R.PT**PEI;G0@<F5L
M;V,[#0HK"79O;&%T:6QE(&DR8SAX>%]T("II,F,[#0HK"79O;&%T:6QE(&EI
M8U]T"2II:7`[#0HK"79O;&%T:6QE(&-P;3AX>%]T("IC<#L-"BL-"BL)=5]C
M:&%R"71E;7!;-3$S73L-"BM].PT**PT**VEN="!I,F-?.'AX7V%D9%]B=7,H
M<W1R=6-T(&DR8U]A9&%P=&5R("HI.PT**VEN="!I,F-?.'AX7V1E;%]B=7,H
M<W1R=6-T(&DR8U]A9&%P=&5R("HI.PT**PT**R-E;F1I9B`O*B!),D-?04Q'
13U\X6%A?2"`J+PT**PT*#0I
`
end
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: 8xx: i2c-algo-8xx - fixed timeout detection and transmission errors
2005-08-10 9:37 Debora Liu
@ 2005-08-10 9:48 ` Wolfgang Denk
0 siblings, 0 replies; 13+ messages in thread
From: Wolfgang Denk @ 2005-08-10 9:48 UTC (permalink / raw)
To: Debora Liu; +Cc: Linuxppc-embedded
Dear Debora,
in message <20050810093254.3E98667EF5@ozlabs.org> you wrote:
>
> >I had some problems on my MPC855M based board when I tried to access the
> >i2c bus.
>
> Try update i2c-algo-8xx.c
I guess this will not solve Cajus' problems. Please take the time and
read his message again and you will see that his problems will still
be present with your driver version.
Best regards,
Wolfgang Denk
--
Software Engineering: Embedded and Realtime Systems, Embedded Linux
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
panic: can't find /
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: 8xx: i2c-algo-8xx - fixed timeout detection and transmission errors
2005-08-10 7:27 8xx: i2c-algo-8xx - fixed timeout detection and transmission errors cajus.hahn
@ 2005-08-10 13:58 ` Marcelo Tosatti
0 siblings, 0 replies; 13+ messages in thread
From: Marcelo Tosatti @ 2005-08-10 13:58 UTC (permalink / raw)
To: cajus.hahn; +Cc: linuxppc-embedded
On Wed, Aug 10, 2005 at 09:27:57AM +0200, cajus.hahn@de.abb.com wrote:
>=20
>=20
>=20
>=20
> Hi all,
>=20
> I had some problems on my MPC855M based board when I tried to access th=
e
> i2c bus.
> The cause were some disturbances on the i2c bus. If a slave device or
> another master device hold the SDA or SCL line low, the CPM will get in=
to a
> state, where no more transmissions are made. The only way I found, to =
get
> the CPM back to work, was a complete re-initialisation. -> force_reinit=
()
> In this case the busy-wait for transmissions with count < 16 will lock =
the
> complete system (CPU load 99.9%)
> I added the define I2C_BUSY_WAIT to switch between faster access or saf=
er
> system.
> I found out that the i2c-algo-8xx.c has a bug in cpm_iic_read() and
> cpm_iic_write(): the timeout detection will not work in the case of cou=
nt <
> 16.
> I also added the define I2C_INTERRUPTIBLE_SLEEP: the old driver reporte=
d
> IO-error (-EIO) if a timeout occured (which was not working, see above)=
or
> if a signal was pending. This caused some problems if the process recei=
ves
> user-signals. The driver will report IO-error, which is not correct. Wi=
th
> the busy-wait this effect might not be seen, because there will be no
> process scheduling -> no signals might be send.
> My modified file is appended. The defines for I2C_BUSY_WAIT and
> I2C_INTERRUPTIBLE_SLEEP are active, which let the driver act like the o=
ld
> one.
> Maybe this is helpful for others too and some of the modifications find
> it=B4s way into the official kernel tree.
Cajus,
Can you please prepare a diff with "diff -u" against the old version and =
your
modified version of the driver? That way it becomes easier for everyone t=
o=20
stop the changes you have made.
Thanks in advance.
>=20
> Cajus Hahn
>=20
> /*
> * i2c-algo-8xx.c i2x driver algorithms for MPC8XX CPM
> * Copyright (c) 1999 Dan Malek (dmalek@jlc.net).
> *
> This program is free software; you can redistribute it and/or modif=
y
> it under the terms of the GNU General Public License as published b=
y
> the Free Software Foundation; either version 2 of the License, or
> (at your option) any later version.
>=20
> 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.
>=20
> 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.
> *
> * moved into proper i2c interface; separated out platform specific
> * parts into i2c-rpx.c
> * Brad Parker (brad@heeltoe.com)
> *
> * added define for BUSY_WAIT and INTERRUPTIBLE_SLEEP, added
> I2C_ALGO_8XX_DATE + ..VERSION
> * fixed bug in cpm_iic_read and cpm_iic_write (timeout never detected =
if
> count < 16)
> * added force_reinit(): in certain cases (disturbances on the I2C bus)=
a
> timeout will
> * occur. After this a complete re-initialisation will be necessary,
> otherwise all
> * following transmissions will have a timeout.
> * Cajus Hahn, 09.08.2005
> */
>=20
> // XXX todo
> // timeout sleep?
>=20
> /* $Id: i2c-algo-8xx.c,v 1.1.1.1 2004/12/10 08:44:35 cajus Exp $ */
>=20
> #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>
>=20
> #include <asm/mpc8xx.h>
> #include <asm/commproc.h>
>=20
> #include <linux/i2c.h>
> #include <linux/i2c-algo-8xx.h>
>=20
> #define I2C_ALGO_8XX_DATE "20050809"
> #define I2C_ALGO_8XX_VERSION "2.6.2"
>=20
> #define CPM_MAX_READ 513
> /* #define I2C_CHIP_ERRATA */ /* Try uncomment this if you have an olde=
r
> CPU(earlier than rev D4) */
> #define I2C_BUSY_WAIT /* Uncomment this if you want to do a busy wait =
in
> cpm_iic_read and cpm_iic_write */
> #define I2C_INTERRUPTIBLE_SLEEP /* Uncomment this if you want the waiti=
ng
> in cpm_iic_read and
> cpm_iic_write beeing interrupt=
able
> by signals */
>=20
> static wait_queue_head_t iic_wait;
> static ushort r_tbase, r_rbase;
>=20
> int cpm_scan =3D 0;
> int cpm_debug =3D 0;
>=20
> static void
> cpm_iic_interrupt(void *dev_id, struct pt_regs *regs)
> {
> volatile i2c8xx_t *i2c =3D (i2c8xx_t *)dev_id;
>=20
> if (cpm_debug > 1)
> printk(KERN_DEBUG "cpm_iic_interrupt(dev_id=3D%p)\n", dev_i=
d);
>=20
> #ifdef I2C_CHIP_ERRATA
> /* Chip errata, clear enable.
> * This seems to not be needed on rev D4 or newer CPUs.
> * Someone with an older CPU needs to verify this.
> */
> i2c->i2c_i2mod &=3D ~1;
> #endif
>=20
> /* Clear interrupt.
> */
> i2c->i2c_i2cer =3D 0xff;
>=20
> /* Get 'me going again.
> */
> #ifdef I2C_INTERRUPTIBLE_SLEEP
> wake_up_interruptible(&iic_wait);
> #else
> wake_up(&iic_wait);
> #endif
> }
>=20
> static void
> cpm_iic_init(struct i2c_algo_8xx_data *cpm_adap)
> {
> volatile iic_t *iip =3D cpm_adap->iip;
> volatile i2c8xx_t *i2c =3D cpm_adap->i2c;
> unsigned char brg;
> bd_t *bd =3D (bd_t *)__res;
>=20
> if (cpm_debug) printk(KERN_DEBUG "cpm_iic_init() - iip=3D%p\n",ii=
p);
>=20
> /* Initialize the parameter ram.
> * We need to make sure many things are initialized to zero,
> * especially in the case of a microcode patch.
> */
> iip->iic_rstate =3D 0;
> iip->iic_rdp =3D 0;
> iip->iic_rbptr =3D 0;
> iip->iic_rbc =3D 0;
> iip->iic_rxtmp =3D 0;
> iip->iic_tstate =3D 0;
> iip->iic_tdp =3D 0;
> iip->iic_tbptr =3D 0;
> iip->iic_tbc =3D 0;
> iip->iic_txtmp =3D 0;
>=20
> /* Set up the IIC parameters in the parameter ram.
> */
> iip->iic_tbase =3D r_tbase =3D cpm_adap->dp_addr;
> iip->iic_rbase =3D r_rbase =3D cpm_adap->dp_addr + sizeof(cbd_t)*=
2;
>=20
> iip->iic_tfcr =3D SMC_EB;
> iip->iic_rfcr =3D SMC_EB;
>=20
> /* Set maximum receive size.
> */
> iip->iic_mrblr =3D CPM_MAX_READ;
>=20
> /* Initialize Tx/Rx parameters.
> */
> if (cpm_adap->reloc =3D=3D 0) {
> volatile cpm8xx_t *cp =3D cpm_adap->cp;
>=20
> cp->cp_cpcr =3D
> mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX) | CPM_CR_FL=
G;
> while (cp->cp_cpcr & CPM_CR_FLG);
> } else {
> iip->iic_rbptr =3D iip->iic_rbase;
> iip->iic_tbptr =3D iip->iic_tbase;
> iip->iic_rstate =3D 0;
> iip->iic_tstate =3D 0;
> }
>=20
> /* Select an arbitrary address. Just make sure it is unique.
> */
> i2c->i2c_i2add =3D 0xfe;
>=20
> /* Make clock run at 60 KHz.
> */
> brg =3D (unsigned char) (bd->bi_intfreq/(32*2*60000) -3);
> i2c->i2c_i2brg =3D brg;
>=20
> i2c->i2c_i2mod =3D 0x00;
> i2c->i2c_i2com =3D 0x01; /* Master mode */
>=20
> /* Disable interrupts.
> */
> i2c->i2c_i2cmr =3D 0;
> i2c->i2c_i2cer =3D 0xff;
>=20
> init_waitqueue_head(&iic_wait);
>=20
> /* Install interrupt handler.
> */
> if (cpm_debug) {
> printk (KERN_DEBUG "%s[%d] Install ISR for IRQ %d\n",
> __func__,__LINE__, CPMVEC_I2C);
> }
> cpm_install_handler(CPMVEC_I2C, cpm_iic_interrupt, (void *)i2c);
> }
>=20
>=20
> static int
> cpm_iic_shutdown(struct i2c_algo_8xx_data *cpm_adap)
> {
> volatile i2c8xx_t *i2c =3D cpm_adap->i2c;
>=20
> /* Shut down IIC.
> */
> i2c->i2c_i2mod &=3D ~1;
> i2c->i2c_i2cmr =3D 0;
> i2c->i2c_i2cer =3D 0xff;
>=20
> return(0);
> }
>=20
> static void
> cpm_reset_iic_params(volatile iic_t *iip)
> {
> iip->iic_tbase =3D r_tbase;
> iip->iic_rbase =3D r_rbase;
>=20
> iip->iic_tfcr =3D SMC_EB;
> iip->iic_rfcr =3D SMC_EB;
>=20
> iip->iic_mrblr =3D CPM_MAX_READ;
>=20
> iip->iic_rstate =3D 0;
> iip->iic_rdp =3D 0;
> iip->iic_rbptr =3D iip->iic_rbase;
> iip->iic_rbc =3D 0;
> iip->iic_rxtmp =3D 0;
> iip->iic_tstate =3D 0;
> iip->iic_tdp =3D 0;
> iip->iic_tbptr =3D iip->iic_tbase;
> iip->iic_tbc =3D 0;
> iip->iic_txtmp =3D 0;
> }
>=20
> #define BD_SC_NAK ((ushort)0x0004) /* NAK - did not respond */
> #define BD_SC_OV ((ushort)0x0002) /* OV - receive overrun */
> #define CPM_CR_CLOSE_RXBD ((ushort)0x0007)
>=20
> static void force_close(struct i2c_algo_8xx_data *cpm)
> {
> volatile i2c8xx_t *i2c =3D cpm->i2c;
>=20
> if (cpm_debug)
> printk(KERN_DEBUG "force_close()");
>=20
> if (cpm->reloc =3D=3D 0) { /* micro code disabled */
> volatile cpm8xx_t *cp =3D cpm->cp;
> cp->cp_cpcr =3D
> mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_CLOSE_RXBD) |
> CPM_CR_FLG;
>=20
> while (cp->cp_cpcr & CPM_CR_FLG);
> }
>=20
> i2c->i2c_i2cmr =3D 0x00; /* Disable all interrupts */
> i2c->i2c_i2cer =3D 0xff;
> }
>=20
> static void force_reinit(struct i2c_algo_8xx_data *cpm)
> {
> volatile iic_t *iip =3D cpm->iip;
> volatile i2c8xx_t *i2c =3D cpm->i2c;
> volatile cpm8xx_t *cp =3D cpm->cp;
> unsigned char brg;
> bd_t *bd =3D (bd_t *)__res;
>=20
> // Disable interrupts.
> i2c->i2c_i2cmr =3D 0;
> i2c->i2c_i2cer =3D 0xff;
> // Clear enable
> i2c->i2c_i2mod &=3D ~1;
>=20
> // Initialize the parameter ram.
> iip->iic_rstate =3D 0;
> iip->iic_rdp =3D 0;
> iip->iic_rbptr =3D 0;
> iip->iic_rbc =3D 0;
> iip->iic_rxtmp =3D 0;
> iip->iic_tstate =3D 0;
> iip->iic_tdp =3D 0;
> iip->iic_tbptr =3D 0;
> iip->iic_tbc =3D 0;
> iip->iic_txtmp =3D 0;
> iip->iic_tbase =3D r_tbase;
> iip->iic_rbase =3D r_rbase;
> iip->iic_tfcr =3D SMC_EB;
> iip->iic_rfcr =3D SMC_EB;
> iip->iic_mrblr =3D CPM_MAX_READ;
>=20
> if (cpm->reloc =3D=3D 0)
> {
> cp->cp_cpcr =3D mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX) |
> CPM_CR_FLG;
> while (cp->cp_cpcr & CPM_CR_FLG);
> }
> else
> {
> iip->iic_rbptr =3D iip->iic_rbase;
> iip->iic_tbptr =3D iip->iic_tbase;
> iip->iic_rstate =3D 0;
> iip->iic_tstate =3D 0;
> }
>=20
> // Select an arbitrary address. Just make sure it is unique.
> i2c->i2c_i2add =3D 0xfe;
>=20
> // Make clock run at 60 KHz.
> brg =3D (unsigned char) (bd->bi_intfreq/(32*2*60000) -3);
> i2c->i2c_i2brg =3D brg;
>=20
> i2c->i2c_i2mod =3D 0x00;
> i2c->i2c_i2com =3D 0x01; /* Master mode */
> }
>=20
> /* Read from IIC...
> * abyte =3D address byte, with r/w flag already set
> */
> static int
> cpm_iic_read(struct i2c_algo_8xx_data *cpm, u_char abyte, char *buf, in=
t
> count)
> {
> volatile iic_t *iip =3D cpm->iip;
> volatile i2c8xx_t *i2c =3D cpm->i2c;
> volatile cpm8xx_t *cp =3D cpm->cp;
> volatile cbd_t *tbdf, *rbdf;
> u_char *tb;
> unsigned long flags, tmo, timedout;
>=20
> if (count >=3D CPM_MAX_READ)
> return -EINVAL;
>=20
> /* check for and use a microcode relocation patch */
> if (cpm->reloc) {
> cpm_reset_iic_params(iip);
> }
>=20
> tbdf =3D (cbd_t *)&cp->cp_dpmem[iip->iic_tbase];
> rbdf =3D (cbd_t *)&cp->cp_dpmem[iip->iic_rbase];
>=20
> /* To read, we need an empty buffer of the proper length.
> * All that is used is the first byte for address, the remainder
> * is just used for timing (and doesn't really have to exist).
> */
> tb =3D cpm->temp;
> tb =3D (u_char *)(((uint)tb + 15) & ~15);
> tb[0] =3D abyte; /* Device address byte w/rw flag */
>=20
> flush_dcache_range((unsigned long) tb, (unsigned long) (tb+1));
>=20
> if (cpm_debug) printk(KERN_DEBUG "cpm_iic_read(abyte=3D0x%x)\n",
> abyte);
>=20
> tbdf->cbd_bufaddr =3D __pa(tb);
> tbdf->cbd_datlen =3D count + 1;
> tbdf->cbd_sc =3D
> BD_SC_READY | BD_SC_LAST |
> BD_SC_WRAP | BD_IIC_START;
>=20
> iip->iic_mrblr =3D count + 1; /* prevent excessive read, +1
> is needed otherwise will the
> RXB interrupt come too early */
>=20
> /* flush will invalidate too. */
> flush_dcache_range((unsigned long) buf, (unsigned long) (buf+coun=
t));
>=20
> rbdf->cbd_datlen =3D 0;
> rbdf->cbd_bufaddr =3D __pa(buf);
>=20
> rbdf->cbd_sc =3D BD_SC_EMPTY | BD_SC_WRAP| BD_SC_INTRPT;
> timedout =3D 0;
> #ifdef I2C_BUSY_WAIT
> if(count > 16){
> #endif
> /* Chip bug, set enable here */
> local_irq_save(flags);
> i2c->i2c_i2cmr =3D 0x13; /* Enable some interupts */
> i2c->i2c_i2cer =3D 0xff;
> i2c->i2c_i2mod |=3D 1; /* Enable */
> i2c->i2c_i2com |=3D 0x80; /* Begin transmission */
>=20
> /* Wait for IIC transfer */
> #ifdef I2C_INTERRUPTIBLE_SLEEP
> tmo =3D interruptible_sleep_on_timeout(&iic_wait,1*HZ);
> #else
> tmo =3D sleep_on_timeout(&iic_wait,1*HZ);
> #endif
> if(tmo =3D=3D 0) timedout=3D1;
> local_irq_restore(flags);
> #ifdef I2C_BUSY_WAIT
> } else { /* busy wait for small transfers, its faster */
> i2c->i2c_i2cmr =3D 0x00; /* Disable I2C interupts */
> i2c->i2c_i2cer =3D 0xff;
> i2c->i2c_i2mod |=3D 1; /* Enable */
> i2c->i2c_i2com |=3D 0x80; /* Begin transmission */
> tmo =3D jiffies + 1*HZ;
> while(!(i2c->i2c_i2cer & 0x11 || (timedout =3D
> time_after(jiffies, tmo)))); /* Busy wait, with a timeout */
> }
> #endif
> if(timedout)
> {
> printk(KERN_DEBUG "cpm_iic_read: timeout!\n");
> force_reinit(cpm);
> return -EIO;
> }
>=20
> #ifdef I2C_INTERRUPTIBLE_SLEEP
> if (signal_pending(current))
> {
> force_close(cpm);
> if (cpm_debug)
> printk(KERN_DEBUG "cpm_iic_read: signal_pending! \n");
> return -EINTR;
> }
> #endif
>=20
> #ifdef I2C_CHIP_ERRATA
> /* Chip errata, clear enable. This is not needed on rev D4 CPUs.
> Disabling I2C too early may cause too short stop condition */
> udelay(4);
> i2c->i2c_i2mod &=3D ~1;
> #endif
>=20
> if (cpm_debug) {
> printk(KERN_DEBUG "tx sc %04x, rx sc %04x\n",
> tbdf->cbd_sc, rbdf->cbd_sc);
> }
>=20
> if (tbdf->cbd_sc & BD_SC_READY) {
> printk(KERN_INFO "IIC read; complete but tbuf ready\n");
> force_close(cpm);
> printk(KERN_INFO "tx sc %04x, rx sc %04x\n",
> tbdf->cbd_sc, rbdf->cbd_sc);
> }
>=20
> if (tbdf->cbd_sc & BD_SC_NAK) {
> if (cpm_debug)
> printk(KERN_DEBUG "IIC read; no ack\n");
> return -EREMOTEIO;
> }
>=20
> if (rbdf->cbd_sc & BD_SC_EMPTY) {
> /* force_close(cpm); */
> if (cpm_debug){
> printk(KERN_DEBUG "IIC read; complete but rbuf empty\=
n");
> printk(KERN_DEBUG "tx sc %04x, rx sc %04x\n",
> tbdf->cbd_sc, rbdf->cbd_sc);
> }
> return -EREMOTEIO;
> }
>=20
> if (rbdf->cbd_sc & BD_SC_OV) {
> if (cpm_debug)
> printk(KERN_DEBUG "IIC read; Overrun\n");
> return -EREMOTEIO;;
> }
>=20
> if (cpm_debug) printk(KERN_DEBUG "read %d bytes\n",
> rbdf->cbd_datlen);
>=20
> if (rbdf->cbd_datlen < count) {
> if (cpm_debug)
> printk(KERN_DEBUG "IIC read; short, wanted %d got %d\=
n",
> count, rbdf->cbd_datlen);
> return 0;
> }
>=20
> return count;
> }
>=20
> /* Write to IIC...
> * addr =3D address byte, with r/w flag already set
> */
> static int
> cpm_iic_write(struct i2c_algo_8xx_data *cpm, u_char abyte, char *buf,in=
t
> count)
> {
> volatile iic_t *iip =3D cpm->iip;
> volatile i2c8xx_t *i2c =3D cpm->i2c;
> volatile cpm8xx_t *cp =3D cpm->cp;
> volatile cbd_t *tbdf;
> u_char *tb;
> unsigned long flags, tmo, timedout;
>=20
> /* check for and use a microcode relocation patch */
> if (cpm->reloc) {
> cpm_reset_iic_params(iip);
> }
> tb =3D cpm->temp;
> tb =3D (u_char *)(((uint)tb + 15) & ~15);
> *tb =3D abyte; /* Device address byte w/rw flag */
>=20
> flush_dcache_range((unsigned long) tb, (unsigned long) (tb+1));
> flush_dcache_range((unsigned long) buf, (unsigned long) (buf+coun=
t));
>=20
> if (cpm_debug) printk(KERN_DEBUG "cpm_iic_write(abyte=3D0x%x)\n",
> abyte);
>=20
> /* set up 2 descriptors */
> tbdf =3D (cbd_t *)&cp->cp_dpmem[iip->iic_tbase];
>=20
> tbdf[0].cbd_bufaddr =3D __pa(tb);
> tbdf[0].cbd_datlen =3D 1;
> tbdf[0].cbd_sc =3D BD_SC_READY | BD_IIC_START;
>=20
> tbdf[1].cbd_bufaddr =3D __pa(buf);
> tbdf[1].cbd_datlen =3D count;
> tbdf[1].cbd_sc =3D BD_SC_READY | BD_SC_INTRPT | BD_SC_LAST |
> BD_SC_WRAP;
>=20
> timedout =3D 0;
> #ifdef I2C_BUSY_WAIT
> if(count > 16){
> #endif
> /* Chip bug, set enable here */
> local_irq_save(flags);
> i2c->i2c_i2cmr =3D 0x13; /* Enable some interupts */
> i2c->i2c_i2cer =3D 0xff;
> i2c->i2c_i2mod |=3D 1; /* Enable */
> i2c->i2c_i2com |=3D 0x80; /* Begin transmission */
>=20
> /* Wait for IIC transfer */
> #ifdef I2C_INTERRUPTIBLE_SLEEP
> tmo =3D interruptible_sleep_on_timeout(&iic_wait,1*HZ);
> #else
> tmo =3D sleep_on_timeout(&iic_wait,1*HZ);
> #endif
> if(tmo =3D=3D 0) timedout=3D1;
> local_irq_restore(flags);
> #ifdef I2C_BUSY_WAIT
> } else { /* busy wait for small transfers, its faster */
> i2c->i2c_i2cmr =3D 0x00; /* Disable I2C interupts */
> i2c->i2c_i2cer =3D 0xff;
> i2c->i2c_i2mod |=3D 1; /* Enable */
> i2c->i2c_i2com |=3D 0x80; /* Begin transmission */
> tmo =3D jiffies + 1*HZ;
> while(!(i2c->i2c_i2cer & 0x12 || (timedout =3D
> time_after(jiffies, tmo)))); /* Busy wait, with a timeout */
> }
> #endif
> if(timedout)
> {
> printk(KERN_DEBUG "cpm_iic_write: timeout!\n");
> force_reinit(cpm);
> return -EIO;
> }
>=20
> #ifdef I2C_INTERRUPTIBLE_SLEEP
> if (signal_pending(current))
> {
> force_close(cpm);
> if (cpm_debug)
> printk(KERN_DEBUG "cpm_iic_write: signal_pending! \n");
> return -EINTR;
> }
> #endif
>=20
> #if I2C_CHIP_ERRATA
> /* Chip errata, clear enable. This is not needed on rev D4 CPUs.
> Disabling I2C too early may cause too short stop condition */
> udelay(4);
> i2c->i2c_i2mod &=3D ~1;
> #endif
> if (cpm_debug) {
> printk(KERN_DEBUG "tx0 sc %04x, tx1 sc %04x\n",
> tbdf[0].cbd_sc, tbdf[1].cbd_sc);
> }
>=20
> if ((tbdf[0].cbd_sc | tbdf[1].cbd_sc) & BD_SC_NAK) {
> if (cpm_debug)
> printk(KERN_DEBUG "IIC write; no ack\n");
> return 0;
> }
>=20
> if ((tbdf[0].cbd_sc | tbdf[1].cbd_sc) & BD_SC_READY) {
> if (cpm_debug)
> printk(KERN_DEBUG "IIC write; complete but tbuf
> ready\n");
> return 0;
> }
>=20
> return count;
> }
>=20
> /* See if an IIC address exists..
> * addr =3D 7 bit address, unshifted
> */
> static int
> cpm_iic_tryaddress(struct i2c_algo_8xx_data *cpm, int addr)
> {
> volatile iic_t *iip =3D cpm->iip;
> volatile i2c8xx_t *i2c =3D cpm->i2c;
> volatile cpm8xx_t *cp =3D cpm->cp;
> volatile cbd_t *tbdf, *rbdf;
> u_char *tb;
> unsigned long flags, len, tmo;
>=20
> if (cpm_debug > 1)
> printk(KERN_DEBUG "cpm_iic_tryaddress(cpm=3D%p,addr=3D%d)\n=
", cpm,
> addr);
>=20
> /* check for and use a microcode relocation patch */
> if (cpm->reloc) {
> cpm_reset_iic_params(iip);
> }
>=20
> if (cpm_debug && addr =3D=3D 0) {
> printk(KERN_DEBUG "iip %p, dp_addr 0x%x\n", cpm->iip,
> cpm->dp_addr);
> printk(KERN_DEBUG "iic_tbase %d, r_tbase %d\n", iip->iic_tb=
ase,
> r_tbase);
> }
>=20
> tbdf =3D (cbd_t *)&cp->cp_dpmem[iip->iic_tbase];
> rbdf =3D (cbd_t *)&cp->cp_dpmem[iip->iic_rbase];
>=20
> tb =3D cpm->temp;
> tb =3D (u_char *)(((uint)tb + 15) & ~15);
>=20
> /* do a simple read */
> tb[0] =3D (addr << 1) | 1; /* device address (+ read) */
> len =3D 2;
>=20
> flush_dcache_range((unsigned long) tb, (unsigned long) (tb+1));
>=20
> tbdf->cbd_bufaddr =3D __pa(tb);
> tbdf->cbd_datlen =3D len;
> tbdf->cbd_sc =3D
> BD_SC_READY | BD_SC_LAST |
> BD_SC_WRAP | BD_IIC_START;
>=20
> rbdf->cbd_datlen =3D 0;
> rbdf->cbd_bufaddr =3D __pa(tb+2);
> rbdf->cbd_sc =3D BD_SC_EMPTY | BD_SC_WRAP | BD_SC_INTRPT;
>=20
> local_irq_save(flags);
> i2c->i2c_i2cmr =3D 0x13; /* Enable some interupts */
> i2c->i2c_i2cer =3D 0xff;
> i2c->i2c_i2mod |=3D 1; /* Enable */
> i2c->i2c_i2com |=3D 0x80; /* Begin transmission */
>=20
> if (cpm_debug > 1) printk(KERN_DEBUG "about to sleep\n");
>=20
> /* wait for IIC transfer */
> #ifdef I2C_INTERRUPTIBLE_SLEEP
> tmo =3D interruptible_sleep_on_timeout(&iic_wait,1*HZ);
> #else
> tmo =3D sleep_on_timeout(&iic_wait,1*HZ);
> #endif
> local_irq_restore(flags);
>=20
> #ifdef I2C_CHIP_ERRATA
> /* Chip errata, clear enable. This is not needed on rev D4 CPUs.
> Disabling I2C too early may cause too short stop condition */
> udelay(4);
> i2c->i2c_i2mod &=3D ~1;
> #endif
>=20
> if (signal_pending(current) || !tmo){
> force_close(cpm);
> if(cpm_debug && !tmo)
> printk(KERN_DEBUG "IIC tryaddress: timeout!\n");
> return -EIO;
> }
>=20
> if (cpm_debug > 1) printk(KERN_DEBUG "back from sleep\n");
>=20
> if (tbdf->cbd_sc & BD_SC_NAK) {
> if (cpm_debug > 1) printk(KERN_DEBUG "IIC try; no ack\n");
> return 0;
> }
>=20
> if (tbdf->cbd_sc & BD_SC_READY) {
> printk(KERN_INFO "IIC try; complete but tbuf ready\n");
> }
>=20
> return 1;
> }
>=20
> static int cpm_xfer(struct i2c_adapter *i2c_adap,
> struct i2c_msg msgs[],
> int num)
> {
> struct i2c_algo_8xx_data *adap =3D i2c_adap->algo_data;
> struct i2c_msg *pmsg;
> int i, ret;
> u_char addr;
>=20
> for (i =3D 0; i < num; i++) {
> pmsg =3D &msgs[i];
>=20
> if (cpm_debug)
> printk(KERN_DEBUG "i2c-algo-8xx.o: "
> "#%d addr=3D0x%x flags=3D0x%x len=3D%d\n buf=3D=
%lx\n",
> i, pmsg->addr, pmsg->flags, pmsg->len, (unsign=
ed
> long)pmsg->buf);
>=20
> addr =3D pmsg->addr << 1;
> if (pmsg->flags & I2C_M_RD )
> addr |=3D 1;
> if (pmsg->flags & I2C_M_REV_DIR_ADDR )
> addr ^=3D 1;
>=20
> if (!(pmsg->flags & I2C_M_NOSTART)) {
> }
> if (pmsg->flags & I2C_M_RD ) {
> /* read bytes into buffer*/
> ret =3D cpm_iic_read(adap, addr, pmsg->buf, pmsg->len=
);
> if (cpm_debug)
> printk(KERN_DEBUG "i2c-algo-8xx.o: read %d
> bytes\n", ret);
> if (ret < pmsg->len ) {
> return (ret<0)? ret : -EREMOTEIO;
> }
> } else {
> /* write bytes from buffer */
> ret =3D cpm_iic_write(adap, addr, pmsg->buf, pmsg->le=
n);
> if (cpm_debug)
> printk(KERN_DEBUG "i2c-algo-8xx.o: wrote %d\n",
> ret);
> if (ret < pmsg->len ) {
> return (ret<0) ? ret : -EREMOTEIO;
> }
> }
> }
> return (num);
> }
>=20
> static int algo_control(struct i2c_adapter *adapter,
> unsigned int cmd, unsigned long arg)
> {
> return 0;
> }
>=20
> static u32 cpm_func(struct i2c_adapter *adap)
> {
> return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
> I2C_FUNC_PROTOCOL_MANGLING;
> }
>=20
> /* -----exported algorithm data: ------------------------------------- =
*/
>=20
> static struct i2c_algorithm cpm_algo =3D {
> "MPC8xx CPM algorithm",
> I2C_ALGO_MPC8XX,
> cpm_xfer,
> NULL,
> NULL, /* slave_xmit */
> NULL, /* slave_recv */
> algo_control, /* ioctl */
> cpm_func, /* functionality */
> };
>=20
> /*
> * registering functions to load algorithms at runtime
> */
> int i2c_8xx_add_bus(struct i2c_adapter *adap)
> {
> int i;
> struct i2c_algo_8xx_data *cpm_adap =3D adap->algo_data;
>=20
> if (cpm_debug)
> printk(KERN_DEBUG "i2c-algo-8xx.o: hw routines for %s
> registered.\n",
> adap->name);
>=20
> /* register new adapter to i2c module... */
>=20
> adap->id |=3D cpm_algo.id;
> adap->algo =3D &cpm_algo;
>=20
> #ifdef MODULE
> MOD_INC_USE_COUNT;
> #endif
>=20
> i2c_add_adapter(adap);
> cpm_iic_init(cpm_adap);
>=20
> /* scan bus */
> if (cpm_scan) {
> printk(KERN_INFO " i2c-algo-8xx.o: scanning bus %s...\n",
> adap->name);
> for (i =3D 0; i < 128; i++) {
> if (cpm_iic_tryaddress(cpm_adap, i)) {
> printk("(%02x)",i<<1);
> }
> }
> printk("\n");
> }
> return 0;
> }
>=20
> int i2c_8xx_del_bus(struct i2c_adapter *adap)
> {
> int res;
> struct i2c_algo_8xx_data *cpm_adap =3D adap->algo_data;
>=20
> cpm_iic_shutdown(cpm_adap);
>=20
> if ((res =3D i2c_del_adapter(adap)) < 0)
> return res;
>=20
> printk(KERN_INFO "i2c-algo-8xx.o: adapter unregistered:
> %s\n",adap->name);
>=20
> #ifdef MODULE
> MOD_DEC_USE_COUNT;
> #endif
> return 0;
> }
>=20
> EXPORT_SYMBOL(i2c_8xx_add_bus);
> EXPORT_SYMBOL(i2c_8xx_del_bus);
>=20
> int __init i2c_algo_8xx_init (void)
> {
> printk(KERN_INFO "i2c-algo-8xx.o: i2c mpc8xx algorithm module ver=
sion
> %s (%s)\n", I2C_ALGO_8XX_VERSION, I2C_ALGO_8XX_DATE);
> return 0;
> }
>=20
>=20
> #ifdef MODULE
> MODULE_AUTHOR("Brad Parker <brad@heeltoe.com>");
> MODULE_DESCRIPTION("I2C-Bus MPC8XX algorithm");
> #ifdef MODULE_LICENSE
> MODULE_LICENSE("GPL");
> #endif
>=20
> int init_module(void)
> {
> return i2c_algo_8xx_init();
> }
>=20
> void cleanup_module(void)
> {
> }
> #endif
>=20
> _______________________________________________
> Linuxppc-embedded mailing list
> Linuxppc-embedded@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-embedded
^ permalink raw reply [flat|nested] 13+ messages in thread
* 8xx: i2c-algo-8xx - fixed timeout detection and transmission errors
@ 2005-08-11 7:42 Cajus Hahn
0 siblings, 0 replies; 13+ messages in thread
From: Cajus Hahn @ 2005-08-11 7:42 UTC (permalink / raw)
To: linuxppc-embedded, cajus.hahn
Marcello,
my "old" version is from the linuxppc_2_4_devel CVS archive on www.denx.de:/cvsroot.
The i2c-algo-8xx.c there has the revision number 1.5.
Regards
Cajus
--- ../../i2c-algo-8xx.c 2005-08-10 16:19:09.000000000 +0200
+++ drivers/i2c/i2c-algo-8xx.c 2005-08-10 08:03:46.000000000 +0200
@@ -19,12 +19,19 @@
* moved into proper i2c interface; separated out platform specific
* parts into i2c-rpx.c
* Brad Parker (brad@heeltoe.com)
+ *
+ * added define for BUSY_WAIT and INTERRUPTIBLE_SLEEP, added I2C_ALGO_8XX_DATE + ..VERSION
+ * fixed bug in cpm_iic_read and cpm_iic_write (timeout never detected if count < 16)
+ * added force_reinit(): in certain cases (disturbances on the I2C bus) a timeout will
+ * occur. After this a complete re-initialisation will be necessary, otherwise all
+ * following transmissions will have a timeout.
+ * Cajus Hahn, 09.08.2005
*/
// XXX todo
// timeout sleep?
-/* $Id: i2c-algo-8xx.c,v 1.7 2002/08/03 22:48:18 ac9410 Exp $ */
+/* $Id: i2c-algo-8xx.c,v 1.2 2005/08/10 06:03:46 cajus Exp $ */
#include <linux/kernel.h>
#include <linux/module.h>
@@ -43,8 +50,16 @@
#include <linux/i2c.h>
#include <linux/i2c-algo-8xx.h>
+#define I2C_ALGO_8XX_DATE "20050809"
+#define I2C_ALGO_8XX_VERSION "2.6.2"
+
#define CPM_MAX_READ 513
/* #define I2C_CHIP_ERRATA */ /* Try uncomment this if you have an older CPU(earlier than rev D4) */
+#define I2C_BUSY_WAIT /* Uncomment this if you want to do a busy wait in cpm_iic_read and
+ cpm_iic_write. In a timeout case the CPU load will be 99.9% ! */
+#define I2C_INTERRUPTIBLE_SLEEP /* Uncomment this if you want the waiting in cpm_iic_read and
+ cpm_iic_write being interruptable by signals */
+
static wait_queue_head_t iic_wait;
static ushort r_tbase, r_rbase;
@@ -73,7 +88,11 @@
/* Get 'me going again.
*/
+#ifdef I2C_INTERRUPTIBLE_SLEEP
wake_up_interruptible(&iic_wait);
+#else
+ wake_up(&iic_wait);
+#endif
}
static void
@@ -201,20 +220,77 @@
static void force_close(struct i2c_algo_8xx_data *cpm)
{
volatile i2c8xx_t *i2c = cpm->i2c;
+
+ if (cpm_debug)
+ printk(KERN_DEBUG "force_close()");
+
if (cpm->reloc == 0) { /* micro code disabled */
volatile cpm8xx_t *cp = cpm->cp;
-
- if (cpm_debug) printk(KERN_DEBUG "force_close()\n");
cp->cp_cpcr =
mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_CLOSE_RXBD) |
CPM_CR_FLG;
while (cp->cp_cpcr & CPM_CR_FLG);
}
+
i2c->i2c_i2cmr = 0x00; /* Disable all interrupts */
i2c->i2c_i2cer = 0xff;
}
+static void force_reinit(struct i2c_algo_8xx_data *cpm)
+{
+ volatile iic_t *iip = cpm->iip;
+ volatile i2c8xx_t *i2c = cpm->i2c;
+ volatile cpm8xx_t *cp = cpm->cp;
+ unsigned char brg;
+ bd_t *bd = (bd_t *)__res;
+
+ // Disable interrupts.
+ i2c->i2c_i2cmr = 0;
+ i2c->i2c_i2cer = 0xff;
+ // Clear enable
+ i2c->i2c_i2mod &= ~1;
+
+ // Initialize the parameter ram.
+ iip->iic_rstate = 0;
+ iip->iic_rdp = 0;
+ iip->iic_rbptr = 0;
+ iip->iic_rbc = 0;
+ iip->iic_rxtmp = 0;
+ iip->iic_tstate = 0;
+ iip->iic_tdp = 0;
+ iip->iic_tbptr = 0;
+ iip->iic_tbc = 0;
+ iip->iic_txtmp = 0;
+ iip->iic_tbase = r_tbase;
+ iip->iic_rbase = r_rbase;
+ iip->iic_tfcr = SMC_EB;
+ iip->iic_rfcr = SMC_EB;
+ iip->iic_mrblr = CPM_MAX_READ;
+
+ if (cpm->reloc == 0)
+ {
+ cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+ while (cp->cp_cpcr & CPM_CR_FLG);
+ }
+ else
+ {
+ iip->iic_rbptr = iip->iic_rbase;
+ iip->iic_tbptr = iip->iic_tbase;
+ iip->iic_rstate = 0;
+ iip->iic_tstate = 0;
+ }
+
+ // Select an arbitrary address. Just make sure it is unique.
+ i2c->i2c_i2add = 0xfe;
+
+ // Make clock run at 60 KHz.
+ brg = (unsigned char) (bd->bi_intfreq/(32*2*60000) -3);
+ i2c->i2c_i2brg = brg;
+
+ i2c->i2c_i2mod = 0x00;
+ i2c->i2c_i2com = 0x01; /* Master mode */
+}
/* Read from IIC...
* abyte = address byte, with r/w flag already set
@@ -227,7 +303,7 @@
volatile cpm8xx_t *cp = cpm->cp;
volatile cbd_t *tbdf, *rbdf;
u_char *tb;
- unsigned long flags, tmo;
+ unsigned long flags, tmo, timedout;
if (count >= CPM_MAX_READ)
return -EINVAL;
@@ -269,7 +345,10 @@
rbdf->cbd_bufaddr = __pa(buf);
rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP| BD_SC_INTRPT;
+ timedout = 0;
+#ifdef I2C_BUSY_WAIT
if(count > 16){
+#endif
/* Chip bug, set enable here */
local_irq_save(flags);
i2c->i2c_i2cmr = 0x13; /* Enable some interupts */
@@ -278,23 +357,40 @@
i2c->i2c_i2com |= 0x80; /* Begin transmission */
/* Wait for IIC transfer */
+#ifdef I2C_INTERRUPTIBLE_SLEEP
tmo = interruptible_sleep_on_timeout(&iic_wait,1*HZ);
+#else
+ tmo = sleep_on_timeout(&iic_wait,1*HZ);
+#endif
+ if(tmo == 0) timedout=1;
local_irq_restore(flags);
+#ifdef I2C_BUSY_WAIT
} else { /* busy wait for small transfers, its faster */
i2c->i2c_i2cmr = 0x00; /* Disable I2C interupts */
i2c->i2c_i2cer = 0xff;
i2c->i2c_i2mod |= 1; /* Enable */
i2c->i2c_i2com |= 0x80; /* Begin transmission */
tmo = jiffies + 1*HZ;
- while(!(i2c->i2c_i2cer & 0x11 || time_after(jiffies, tmo))); /* Busy wait, with a timeout */
+ while(!(i2c->i2c_i2cer & 0x11 || (timedout = time_after(jiffies, tmo)))); /* Busy wait, with a timeout */
}
+#endif
+ if(timedout)
+ {
+ printk(KERN_DEBUG "cpm_iic_read: timeout!\n");
+ force_reinit(cpm);
+ return -EIO;
+ }
+
+#ifdef I2C_INTERRUPTIBLE_SLEEP
+ if (signal_pending(current))
+ {
+ force_close(cpm);
+ if (cpm_debug)
+ printk(KERN_DEBUG "cpm_iic_read: signal_pending! \n");
+ return -EINTR;
+ }
+#endif
- if (signal_pending(current) || !tmo){
- force_close(cpm);
- if(cpm_debug)
- printk(KERN_DEBUG "IIC read: timeout!\n");
- return -EIO;
- }
#ifdef I2C_CHIP_ERRATA
/* Chip errata, clear enable. This is not needed on rev D4 CPUs.
Disabling I2C too early may cause too short stop condition */
@@ -359,7 +455,7 @@
volatile cpm8xx_t *cp = cpm->cp;
volatile cbd_t *tbdf;
u_char *tb;
- unsigned long flags, tmo;
+ unsigned long flags, tmo, timedout;
/* check for and use a microcode relocation patch */
if (cpm->reloc) {
@@ -385,7 +481,10 @@
tbdf[1].cbd_datlen = count;
tbdf[1].cbd_sc = BD_SC_READY | BD_SC_INTRPT | BD_SC_LAST | BD_SC_WRAP;
+ timedout = 0;
+#ifdef I2C_BUSY_WAIT
if(count > 16){
+#endif
/* Chip bug, set enable here */
local_irq_save(flags);
i2c->i2c_i2cmr = 0x13; /* Enable some interupts */
@@ -394,23 +493,39 @@
i2c->i2c_i2com |= 0x80; /* Begin transmission */
/* Wait for IIC transfer */
+#ifdef I2C_INTERRUPTIBLE_SLEEP
tmo = interruptible_sleep_on_timeout(&iic_wait,1*HZ);
+#else
+ tmo = sleep_on_timeout(&iic_wait,1*HZ);
+#endif
+ if(tmo == 0) timedout=1;
local_irq_restore(flags);
+#ifdef I2C_BUSY_WAIT
} else { /* busy wait for small transfers, its faster */
i2c->i2c_i2cmr = 0x00; /* Disable I2C interupts */
i2c->i2c_i2cer = 0xff;
i2c->i2c_i2mod |= 1; /* Enable */
i2c->i2c_i2com |= 0x80; /* Begin transmission */
tmo = jiffies + 1*HZ;
- while(!(i2c->i2c_i2cer & 0x12 || time_after(jiffies, tmo))); /* Busy wait, with a timeout */
- }
-
- if (signal_pending(current) || !tmo){
- force_close(cpm);
- if(cpm_debug && !tmo)
- printk(KERN_DEBUG "IIC write: timeout!\n");
- return -EIO;
+ while(!(i2c->i2c_i2cer & 0x12 || (timedout = time_after(jiffies, tmo)))); /* Busy wait, with a timeout */
}
+#endif
+ if(timedout)
+ {
+ printk(KERN_DEBUG "cpm_iic_write: timeout!\n");
+ force_reinit(cpm);
+ return -EIO;
+ }
+
+#ifdef I2C_INTERRUPTIBLE_SLEEP
+ if (signal_pending(current))
+ {
+ force_close(cpm);
+ if (cpm_debug)
+ printk(KERN_DEBUG "cpm_iic_write: signal_pending! \n");
+ return -EINTR;
+ }
+#endif
#if I2C_CHIP_ERRATA
/* Chip errata, clear enable. This is not needed on rev D4 CPUs.
@@ -495,7 +610,11 @@
if (cpm_debug > 1) printk(KERN_DEBUG "about to sleep\n");
/* wait for IIC transfer */
+#ifdef I2C_INTERRUPTIBLE_SLEEP
tmo = interruptible_sleep_on_timeout(&iic_wait,1*HZ);
+#else
+ tmo = sleep_on_timeout(&iic_wait,1*HZ);
+#endif
local_irq_restore(flags);
#ifdef I2C_CHIP_ERRATA
@@ -658,7 +777,7 @@
int __init i2c_algo_8xx_init (void)
{
- printk(KERN_INFO "i2c-algo-8xx.o: i2c mpc8xx algorithm module version %s (%s)\n", I2C_VERSION, I2C_DATE);
+ printk(KERN_INFO "i2c-algo-8xx.o: i2c mpc8xx algorithm module version %s (%s)\n", I2C_ALGO_8XX_VERSION, I2C_ALGO_8XX_DATE);
return 0;
}
^ permalink raw reply [flat|nested] 13+ messages in thread
* 8xx: i2c-algo-8xx - fixed timeout detection and transmission errors
@ 2005-08-11 17:50 Tjernlund
0 siblings, 0 replies; 13+ messages in thread
From: Tjernlund @ 2005-08-11 17:50 UTC (permalink / raw)
To: linuxppc-embedded
Try changing all
i2c->i2c_i2com |= 0x80; /* Begin transmission */
to
i2c->i2c_i2com |= 0x80 | 0x01; /* Begin transmission */
That should remove the need to do force_reinit(cpm) I hope.
See http://ozlabs.org/pipermail/linuxppc-embedded/2005-August/019600.html for a litte more info.
Also, I think you should remove the busy wait code. Its not needed IMHO.
Jocke
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: 8xx: i2c-algo-8xx - fixed timeout detection and transmission errors
@ 2005-08-12 5:11 Debora Liu
0 siblings, 0 replies; 13+ messages in thread
From: Debora Liu @ 2005-08-12 5:11 UTC (permalink / raw)
To: Tjernlund; +Cc: Linuxppc-embedded
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 628 bytes --]
Hello, Tjernlund
In message <2005-08-12 01:50:45 tjernlund@tjernlund.se> you wrote:
>Try changing all
> i2c->i2c_i2com |= 0x80; /* Begin transmission */
>to
> i2c->i2c_i2com |= 0x80 | 0x01; /* Begin transmission */
>
>That should remove the need to do force_reinit(cpm) I hope.
>See http://ozlabs.org/pipermail/linuxppc-embedded/2005-August/019600.html for a litte more info.
>
>Also, I think you should remove the busy wait code. Its not needed IMHO.
update i2c_algo_8xx.c as same
= = = = = = = = = = = = = = = = = = = =
¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡Debora Liu
¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡deboralh@fel.com.cn
¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡2005-08-12
^ permalink raw reply [flat|nested] 13+ messages in thread
* 8xx: i2c-algo-8xx - fixed timeout detection and transmission errors
@ 2005-08-12 7:49 cajus.hahn
2005-08-12 8:01 ` Wolfgang Denk
0 siblings, 1 reply; 13+ messages in thread
From: cajus.hahn @ 2005-08-12 7:49 UTC (permalink / raw)
To: linuxppc-embedded
Hello Tjernlund, Hello Debora
>Try changing all
> i2c->i2c_i2com |= 0x80; /* Begin transmission */
>to
> i2c->i2c_i2com |= 0x80 | 0x01; /* Begin transmission */
>
>That should remove the need to do force_reinit(cpm) I hope.
This is NOT working in every case!
It will work, on short bus-disturbances on the SCL line.
I suggest adding the "| 0x01" even this will not help on every transmission
problem.
If you have longer disturbances on SCL or SDA, something in the CPM will
prevent the I2C bus to continue working after the disturbances are gone.
Perhaps there is a problem with the buffer allocation.
I changed the i2c-algo-8xx.c to work with your patch and without the
force_reinit() code.
Then I set the SCL line to ground to simulate a longer bus disturbance.
After some timeouts my SPI bus, yes the SPI bus, reportet transmission
errors. Both bus-interfaces use the CPM. It looks like the SPI buffer gets
garbaged by the I2C interface.
With the force_reinit() this problem did not occur.
Cajus
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: 8xx: i2c-algo-8xx - fixed timeout detection and transmission errors
2005-08-12 7:49 cajus.hahn
@ 2005-08-12 8:01 ` Wolfgang Denk
2005-08-12 8:21 ` cajus.hahn
0 siblings, 1 reply; 13+ messages in thread
From: Wolfgang Denk @ 2005-08-12 8:01 UTC (permalink / raw)
To: cajus.hahn; +Cc: linuxppc-embedded
In message <OF29EF0D8D.F0A40A5C-ONC125705B.0028FA70-C125705B.002AF212@de.abb.com> you wrote:
>
> Then I set the SCL line to ground to simulate a longer bus disturbance.
> After some timeouts my SPI bus, yes the SPI bus, reportet transmission
> errors. Both bus-interfaces use the CPM. It looks like the SPI buffer gets
> garbaged by the I2C interface.
This is "normal". We see similar problems even with perfectly legal
operations on the CPM. The SPI is running at lowest priority on the
CPM, and anything that causes higher CPM load will starve SPI.
Obviously your operation causes the CPM to go into some strange
state.
Best regards,
Wolfgang Denk
--
Software Engineering: Embedded and Realtime Systems, Embedded Linux
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
There are certain things men must do to remain men.
-- Kirk, "The Ultimate Computer", stardate 4929.4
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: 8xx: i2c-algo-8xx - fixed timeout detection and transmission errors
2005-08-12 8:01 ` Wolfgang Denk
@ 2005-08-12 8:21 ` cajus.hahn
0 siblings, 0 replies; 13+ messages in thread
From: cajus.hahn @ 2005-08-12 8:21 UTC (permalink / raw)
To: Wolfgang Denk; +Cc: linuxppc-embedded
My description seems to be a little misleading.
WITHOUT the force_reinit() my SPI driver reports transmission errors if the
I2C bus has longer disturbances.
WITH the force_reinit() my SPI driver works fine.
This sounds like the I2C allocates a new buffer for every timed-out
transmision. If all I2C buffers are full, the SPI buffers get overwritten
!?
I did not really trace back the problem, this is only my suspicion.
Because the force_reinit() resets all the buffer pointer to their init
values the problem with the SPI does not occur.
Cajus
Message from Wolfgang Denk <wd@denx.de>@denx.de received on 12.08.2005
10:01
12.08.2005 Wolfgang Denk <wd@denx.de>@denx.de
10:01
Sent by wd@denx.de
To: Cajus Hahn/DEAPR/ABB@ABB_DE01
cc: linuxppc-embedded@ozlabs.org
Subject: Re: 8xx: i2c-algo-8xx - fixed timeout detection and transmission errors
In message
<OF29EF0D8D.F0A40A5C-ONC125705B.0028FA70-C125705B.002AF212@de.abb.com> you
wrote:
>
> Then I set the SCL line to ground to simulate a longer bus disturbance.
> After some timeouts my SPI bus, yes the SPI bus, reportet transmission
> errors. Both bus-interfaces use the CPM. It looks like the SPI buffer
gets
> garbaged by the I2C interface.
This is "normal". We see similar problems even with perfectly legal
operations on the CPM. The SPI is running at lowest priority on the
CPM, and anything that causes higher CPM load will starve SPI.
Obviously your operation causes the CPM to go into some strange
state.
Best regards,
Wolfgang Denk
--
Software Engineering: Embedded and Realtime Systems, Embedded Linux
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
There are certain things men must do to remain men.
-- Kirk, "The Ultimate Computer", stardate 4929.4
^ permalink raw reply [flat|nested] 13+ messages in thread
* 8xx: i2c-algo-8xx - fixed timeout detection and transmission errors
@ 2005-08-13 15:13 Joakim Tjernlund
0 siblings, 0 replies; 13+ messages in thread
From: Joakim Tjernlund @ 2005-08-13 15:13 UTC (permalink / raw)
To: linuxppc-embedded
> My description seems to be a little misleading.
> WITHOUT the force_reinit() my SPI driver reports transmission errors if the
> I2C bus has longer disturbances.
> WITH the force_reinit() my SPI driver works fine.
> This sounds like the I2C allocates a new buffer for every timed-out
> transmision. If all I2C buffers are full, the SPI buffers get overwritten
> !?
> I did not really trace back the problem, this is only my suspicion.
> Because the force_reinit() resets all the buffer pointer to their init
> values the problem with the SPI does not occur.
>
> Cajus
I think the problem is that the CPM is still waiting on the SCL long after the timeout has happen.
A complete reinit will reset the I2C part of the CPM.
I think you should be able to get away with less, maybe it will enough to disable
I2C (i2c->i2c_i2mod &= ~1) and/or clear internal state (iip->iic_rstate = 0; iip->iic_tstate = 0;)
after a timeout?
Jocke
^ permalink raw reply [flat|nested] 13+ messages in thread
* 8xx: i2c-algo-8xx - fixed timeout detection and transmission errors
@ 2005-08-15 5:59 cajus.hahn
2005-08-15 8:08 ` Joakim Tjernlund
0 siblings, 1 reply; 13+ messages in thread
From: cajus.hahn @ 2005-08-15 5:59 UTC (permalink / raw)
To: linuxppc-embedded
Hi Jocke,
I changed the force_reinit() to
// Disable interrupts.
i2c->i2c_i2cmr = 0;
i2c->i2c_i2cer = 0xff;
// Clear enable
i2c->i2c_i2mod &= ~1;
// Reset internal state
iip->iic_rstate = 0;
iip->iic_tstate = 0;
This seems to work and is less code than my old force_reinit().
On the other hand: This kind of CPM timeout will only occur on heavy I2C
bus conflicts. They should not appear at all. The code will not be executed
if a slave device does not answer! The original driver is from the year
2001 and nobody has mentioned problems of this kind before. Without
detailed knowledge of the CPM internals I feel much safer using my old
force_reinit(), which does a complete re-init.
Cajus
^ permalink raw reply [flat|nested] 13+ messages in thread
* RE: 8xx: i2c-algo-8xx - fixed timeout detection and transmission errors
2005-08-15 5:59 cajus.hahn
@ 2005-08-15 8:08 ` Joakim Tjernlund
0 siblings, 0 replies; 13+ messages in thread
From: Joakim Tjernlund @ 2005-08-15 8:08 UTC (permalink / raw)
To: cajus.hahn, linuxppc-embedded
>
> Hi Jocke,
>
> I changed the force_reinit() to
>
> // Disable interrupts.
> i2c->i2c_i2cmr = 0;
> i2c->i2c_i2cer = 0xff;
> // Clear enable
> i2c->i2c_i2mod &= ~1;
> // Reset internal state
> iip->iic_rstate = 0;
> iip->iic_tstate = 0;
>
> This seems to work and is less code than my old force_reinit().
> On the other hand: This kind of CPM timeout will only occur on heavy I2C
> bus conflicts. They should not appear at all. The code will not be executed
> if a slave device does not answer! The original driver is from the year
> 2001 and nobody has mentioned problems of this kind before. Without
> detailed knowledge of the CPM internals I feel much safer using my old
> force_reinit(), which does a complete re-init.
Good, however I think we should try to it simple, the code that is, and avoid
unneded bloat. I would like to see the shorter version in the kernel.
Jocke
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2005-08-15 8:08 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-08-10 7:27 8xx: i2c-algo-8xx - fixed timeout detection and transmission errors cajus.hahn
2005-08-10 13:58 ` Marcelo Tosatti
-- strict thread matches above, loose matches on Subject: below --
2005-08-10 9:37 Debora Liu
2005-08-10 9:48 ` Wolfgang Denk
2005-08-11 7:42 Cajus Hahn
2005-08-11 17:50 Tjernlund
2005-08-12 5:11 Debora Liu
2005-08-12 7:49 cajus.hahn
2005-08-12 8:01 ` Wolfgang Denk
2005-08-12 8:21 ` cajus.hahn
2005-08-13 15:13 Joakim Tjernlund
2005-08-15 5:59 cajus.hahn
2005-08-15 8:08 ` Joakim Tjernlund
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).