📄 lx_acb.c
字号:
#include <linux/kernel.h>#include <linux/module.h>#include <linux/config.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/sched.h>#include <linux/delay.h>#include <linux/i2c.h>#include <linux/smp_lock.h>#include <linux/pci.h>#include <asm/io.h>#include "build_num.h"MODULE_AUTHOR("William Morrow (william.morrow@amd.com)");MODULE_DESCRIPTION("AMD LX ACCESS.bus Driver");MODULE_LICENSE("GPL");#define NAME "lx_acb"#define LENGTH(n) (sizeof(n)/sizeof(n[0]))#define ACBSDA (dev->io+0)#define ACBST (dev->io+1)#define ACBST_SDAST 0x40 /* SDA Status */#define ACBST_BER 0x20 #define ACBST_NEGACK 0x10 /* Negative Acknowledge */#define ACBST_STASTR 0x08 /* Stall After Start */#define ACBST_MASTER 0x02#define ACBST_XMIT 0x01#define ACBCST (dev->io+2)#define ACBCST_BB 0x02#define ACBCST_TSDA 0x10#define ACBCST_TGSCL 0x20#define ACBCTL1 (dev->io+3)#define ACBCTL1_STASTRE 0x80#define ACBCTL1_NMINTE 0x40#define ACBCTL1_ACK 0x10#define ACBCTL1_INTEN 0x04#define ACBCTL1_STOP 0x02#define ACBCTL1_START 0x01#define ACBADDR (dev->io+4)#define ACBCTL2 (dev->io+5)#define ACBCTL2_CLK 0x70#define ACBCTL2_ENABLE 0x01#define MAX_DEVICES 4static int debug = 0;MODULE_PARM(debug,"i");#define DEBUG(n,s...) do { if( debug >= (n) ) printk(KERN_INFO NAME ":" s); } while(0)#define POLLED_MODE 1#define POLL_TIMEOUT (HZ)enum amd_acb_state { state_idle, state_address, state_read, state_write, state_next, state_stop, state_error,};#define STATE_ACTIVE ((1<<state_address)|(1<<state_read)|(1<<state_write)|(1<<state_stop))struct acb_dev { unsigned long io; int index; int retries; struct semaphore sem; struct i2c_algorithm algo; struct i2c_adapter adp; enum amd_acb_state state; unsigned short flags; unsigned char addr; unsigned char last; int count; int msgn; int result; int len; unsigned char *bp;};struct amd_acb_driver { struct pci_dev *pci; spinlock_t lock; int model; int n_dev; struct acb_dev dev[MAX_DEVICES];} *s;static unsigned charinz(unsigned short p){ unsigned char ch = inb(p); udelay(5); return ch;}static voidoutz(unsigned char ch,unsigned short p){ outb(ch,p); udelay(5);}static voidacb_dump(struct acb_dev *dev){ DEBUG(3,"ACB%d DUMP ST=%02x CST=%02x CTL1=%02x CTL2=%02x\n", dev->index, inz(ACBST), inz(ACBCST), inz(ACBCTL1), inz(ACBCTL2));}static voidacb_start(struct acb_dev *dev){ DEBUG(3,"ACB%d START\n",dev->index); outz( (inz(ACBCTL1) | ACBCTL1_START), ACBCTL1 );}static voidacb_stop(struct acb_dev *dev){ DEBUG(3,"ACB%d STOP\n",dev->index); outz( (inz(ACBCTL1) | ACBCTL1_STOP), ACBCTL1 );}static voidacb_snak(struct acb_dev *dev){ DEBUG(3,"ACB%d SNAK\n",dev->index); outz( inz(ACBCTL1) | ACBCTL1_ACK, ACBCTL1 );}static voidacb_bbusy(struct acb_dev *dev){ int retries = 100; while( (inz(ACBCST)&ACBCST_BB) != 0 && --retries >= 0 ) { if( (inz(ACBST) & ACBST_MASTER) != 0 ) outz( ACBCTL1_STOP, ACBCTL1 ); else if( (inz(ACBCST) & (ACBCST_TSDA|ACBCST_TGSCL)) == 0 ) outz( ACBCST_TGSCL, ACBCST ); udelay(1000); } if( retries < 0 ) { DEBUG(0,"ACB%d BUS BUSY\n",dev->index); acb_dump(dev); }}static voidacb_reenable(struct acb_dev *dev){ DEBUG(3,"ACB%d REENABLE\n",dev->index); outz( (ACBCTL2_CLK & ~ACBCTL2_ENABLE), ACBCTL2 ); // Configure no interrupt mode (polling) and // Disable global call address outz( 0x00, ACBCTL1 ); // Disable slave address outz( 0x00, ACBADDR ); // Enable the ACCESS.bus device outz( inz(ACBCTL2) | ACBCTL2_ENABLE, ACBCTL2);}static voidacb_reset(struct acb_dev *dev){ DEBUG(3,"ACB%d RESET\n",dev->index); // Clear NEGACK, STASTR and BER bits outz( ACBST_BER|ACBST_NEGACK|ACBST_STASTR, ACBST ); outz( 0, ACBST ); // Status Reg bug workaround // Clear BB (BUS BUSY) bit acb_bbusy(dev); // toggle enable ACB acb_reenable(dev);}static intacb_probe(struct acb_dev *dev){ DEBUG(0,"%s\n",AMD_VERSION); DEBUG(3,"ACB%d PROBE\n",dev->index); acb_reset(dev); //probe with reset outz( (ACBCTL2_CLK & ~ACBCTL2_ENABLE), ACBCTL2 ); if( inz(ACBCTL2) != (ACBCTL2_CLK & ~ACBCTL2_ENABLE)) { DEBUG(2,"ACB%d readback failed\n",dev->index); return -ENXIO; } outz( 0x00, ACBCTL1 ); outz( 0x00, ACBADDR ); outz( inz(ACBCTL2) | ACBCTL2_ENABLE, ACBCTL2 ); if( inz(ACBCTL1) ) { DEBUG(2,"ACB%d disabled, but ACBCTL1!=0\n",dev->index); return -ENXIO; } outz( ACBST_BER|ACBST_NEGACK|ACBST_STASTR, ACBST ); outz( 0, ACBST ); // Status Reg bug workaround acb_bbusy(dev); return 0;}static intamd_acb_machine(struct acb_dev *dev,int status){ unsigned char addr, ch; unsigned short flags; if( (status&ACBST_MASTER) == 0 ) { DEBUG(1,"ACB%d NOT MASTER\n",dev->index); dev->state = state_error; } if( (status&ACBST_BER) != 0 ) { DEBUG(1,"ACB%d BUS ERROR\n",dev->index); outz( ACBST_BER, ACBST ); outz( 0, ACBST ); // Status Reg bug workaround dev->state = state_error; } if( (status&ACBST_STASTR) != 0 ) { DEBUG(5,"ACB%d STALLED\n",dev->index); outz( ACBST_STASTR, ACBST ); outz( 0, ACBST ); // Status Reg bug workaround return 0; } switch( dev->state ) { case state_error: break; case state_address: addr = dev->addr << 1; flags = dev->flags; if( dev->len > 0 ) { if( (flags&I2C_M_RD) != 0 ) { addr |= 1; dev->state = state_read; if( dev->len == 1 ) acb_snak(dev); } else dev->state = state_write; } else dev->state = state_next; if( (flags&I2C_M_REV_DIR_ADDR) != 0 ) addr ^= 1; DEBUG(5,"ACB%d SEND ADDR %02x\n",dev->index,addr); outz( addr, ACBSDA ); break; case state_read: if( (status&ACBST_XMIT) != 0 ) { acb_dump(dev); return 0; } if( --dev->len <= 0 ) { if( dev->last == 0 ) { acb_start(dev); dev->state = state_next; } else { acb_stop(dev); dev->state = state_idle; } } else if( dev->len == 1 ) acb_snak(dev); ch = inz(ACBSDA); DEBUG(5,"ACB%d RECV DATA %02x %d\n",dev->index,ch,dev->len); *dev->bp++ = ch; break; case state_write: if( (status&ACBST_XMIT) == 0 ) { acb_dump(dev); return 0; } if( (status&ACBST_NEGACK) != 0 ) { DEBUG(5,"ACB%d NAK\n",dev->index); if( dev->last == 0 ) acb_start(dev); outz( ACBST_NEGACK, ACBST ); outz( 0, ACBST ); // Status Reg bug workaround dev->state = state_next; break; } if( dev->len <= 0 ) { dev->state = state_next; break; } ch = *dev->bp++; DEBUG(5,"ACB%d SEND DATA %02x %d\n",dev->index,ch,dev->len); outz( ch, ACBSDA ); if( --dev->len <= 0 ) { if( dev->last == 0 ) acb_start(dev); } break; case state_stop: acb_stop(dev); dev->state = state_idle; break; default: DEBUG(0,"ACB%d, Interrupt in state %d\n",dev->index,dev->state); return 0; } if( dev->state == state_next && dev->last != 0 ) dev->state = state_stop; return 1;}#ifdef POLLED_MODEstatic void
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -