Mercurial > pt1
view driver/pt1_i2c.c @ 39:b03685d25fee
recpt1 now shows error explanation when write() fails.
author | Yoshiki Yazawa <yaz@honeyplanet.jp> |
---|---|
date | Fri, 17 Apr 2009 22:52:35 +0900 |
parents | 67e8eca28a80 |
children | 517e61637f7b |
line wrap: on
line source
/***************************************************************************/ /* I2C情報作成 */ /***************************************************************************/ #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/pci.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/mutex.h> #include <asm/system.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/uaccess.h> #include "pt1_com.h" #include "pt1_i2c.h" #include "pt1_pci.h" #define PROGRAM_ADDRESS 1024 static int state = STATE_STOP ; static int i2c_lock(void __iomem *, __u32, __u32, __u32); static int i2c_lock_one(void __iomem *, __u32, __u32); static int i2c_unlock(void __iomem *, int); static void writebits(void __iomem *, __u32 *, __u32, __u32); static void begin_i2c(void __iomem *, __u32 *, __u32 *); static void start_i2c(void __iomem *, __u32 *, __u32 *, __u32); static void stop_i2c(void __iomem *, __u32 *, __u32 *, __u32, __u32); // PCIに書き込むI2Cデータ生成 void makei2c(void __iomem *regs, __u32 base_addr, __u32 i2caddr, __u32 writemode, __u32 data_en, __u32 clock, __u32 busy) { __u32 val ; val = ((base_addr << I2C_DATA) | (writemode << I2C_WRIET_MODE) | ( data_en << I2C_DATA_EN) | (clock << I2C_CLOCK) | (busy << I2C_BUSY) | i2caddr) ; writel(val, regs + FIFO_ADDR); } int xc3s_init(void __iomem *regs) { __u32 val ; int lp ; int rc ; /* val = (1 << 19) | (1 << 27) | (1 << 16) | (1 << 24) | (1 << 17) | (1 << 25); writel(WRITE_PULSE, regs); BIT 19, 19+8 ON BIT 16, 16+8 ON BIT 17, 17+8 ON */ // XC3S初期化 for(lp = 0 ; lp < PROGRAM_ADDRESS ; lp++){ makei2c(regs, lp, 0, READ_EN, DATA_DIS, CLOCK_DIS, BUSY_DIS); } // XC3S 初期化待ち (512 PCI Clocks) for(lp = 0 ; lp < XC3S_PCI_CLOCK ; lp++){ makei2c(regs, 0, 0, READ_EN, DATA_DIS, CLOCK_DIS, BUSY_DIS); } // プロテクト解除 // これは何を意図しているんだろう? // 元コードが良く判らない for(lp = 0 ; lp < 57 ; lp++){ val = readl(regs); if(val & I2C_READ_SYNC){ break ; } writel(WRITE_PULSE, regs); } for(lp = 0 ; lp < 57 ; lp++){ val = readl(regs); if(val & READ_DATA){ break ; } writel(WRITE_PULSE, regs); } // UNLOCK rc = i2c_unlock(regs, READ_UNLOCK); if(rc < 0){ return rc ; } // Enable PCI rc =i2c_lock(regs, (WRITE_PCI_RESET | WRITE_PCI_RESET_), WRITE_PCI_RESET_, PCI_LOCKED); if(rc < 0){ return -EIO ; } // Enable RAM rc =i2c_lock(regs, (WRITE_RAM_RESET | WRITE_RAM_RESET_), WRITE_RAM_RESET_, RAM_LOCKED); if(rc){ return -EIO ; } for(lp = 0 ; lp < XC3S_PCI_CLOCK ; lp++){ rc = i2c_lock_one(regs, WRITE_RAM_ENABLE, RAM_SHIFT); if(rc < 0){ printk(KERN_ERR "PT1:LOCK FALUT\n"); return rc ; } } // ストリームごとの転送制御(OFF) for(lp = 0 ; lp < MAX_CHANNEL ; lp++){ SetStream(regs, lp, 0); SetStream(regs, lp, 0); } return 0 ; } // // //BIT 0. 1 : Tuner番号 (Enable/Disable) //BIT 8. 9 : Tuner番号 // // void SetStream(void __iomem *regs, __u32 channel, __u32 enable) { __u32 val ; val = (1 << (8 + channel)); if(enable){ val |= (1 << channel); } writel(val, regs + TS_TEST_ENABLE_ADDR); } static int i2c_lock(void __iomem *regs, __u32 firstval, __u32 secondval, __u32 lockval) { __u32 val ; int lp ; writel(firstval, regs); writel(secondval, regs); // RAMがロックされた? for(lp = 0 ; lp < XC3S_PCI_CLOCK ; lp++){ val = readl(regs); if((val & lockval)){ return 0 ; } schedule_timeout_interruptible(msecs_to_jiffies(1)); } return -EIO ; } static int i2c_lock_one(void __iomem *regs, __u32 firstval, __u32 lockval) { __u32 val ; __u32 val2 ; int lp ; val = (readl(regs) & lockval); writel(firstval, regs); // RAMがロックされた? for(lp = 0 ; lp < 10 ; lp++){ for(lp = 0 ; lp < 1024 ; lp++){ val2 = readl(regs); // 最初に取得したデータと逆になればOK if(((val2 & lockval) != val)){ return 0 ; } } schedule_timeout_interruptible(msecs_to_jiffies(1)); } printk(KERN_INFO "PT1:Lock Fault(%x:%x)\n", val, val2); return -EIO ; } static int i2c_unlock(void __iomem *regs, int lockval) { int lp ; __u32 val ; writel(WRITE_PULSE, regs); for(lp = 0 ; lp < 3 ; lp++){ val = readl(regs); if((val &lockval)){ return 0 ; } schedule_timeout_interruptible(msecs_to_jiffies(1)); } return -EIO ; } void blockwrite(void __iomem *regs, WBLOCK *wblock) { int lp ; int bitpos ; __u32 bits ; __u32 old_bits = 1 ; __u32 address = 0; __u32 clock = 0; begin_i2c(regs, &address, &clock); if(state == STATE_STOP){ start_i2c(regs, &address, &clock, old_bits); old_bits = 0 ; stop_i2c(regs, &address, &clock, old_bits, FALSE); state = STATE_START ; } old_bits = 1 ; start_i2c(regs, &address, &clock, old_bits); old_bits = 0 ; // まずアドレスを書く for(bitpos = 0 ; bitpos < 7 ; bitpos++){ bits = ((wblock->addr >> (6 - bitpos)) & 1); writebits(regs, &address, old_bits, bits); old_bits = bits ; } // タイプ:WRT writebits(regs, &address, old_bits, 0); // ACK/NACK用(必ず1) writebits(regs, &address, 0, 1); old_bits = 1 ; // 実際のデータを書く for (lp = 0 ; lp < wblock->count ; lp++){ for(bitpos = 0 ; bitpos < 8 ; bitpos++){ bits = ((wblock->value[lp] >> (7 - bitpos)) & 1); writebits(regs, &address, old_bits, bits); old_bits = bits ; } // ACK/NACK用(必ず1) writebits(regs, &address, old_bits, 1); old_bits = 1 ; } // Clock negedge makei2c(regs, address, address + 1, 0, (old_bits ^ 1), 1, 1); clock = TRUE ; address += 1 ; stop_i2c(regs, &address, &clock, old_bits, TRUE); } void blockread(void __iomem *regs, WBLOCK *wblock, int count) { int lp ; int bitpos ; __u32 bits ; __u32 old_bits = 1 ; __u32 address = 0; __u32 clock = 0; begin_i2c(regs, &address, &clock); if(state == STATE_STOP){ start_i2c(regs, &address, &clock, old_bits); old_bits = 0 ; stop_i2c(regs, &address, &clock, old_bits, FALSE); state = STATE_START ; } old_bits = 1 ; start_i2c(regs, &address, &clock, old_bits); old_bits = 0 ; // まずアドレスを書く for(bitpos = 0 ; bitpos < 7 ; bitpos++){ bits = ((wblock->addr >> (6 - bitpos)) & 1); writebits(regs, &address, old_bits, bits); old_bits = bits ; } // タイプ:WRT writebits(regs, &address, old_bits, 0); // ACK/NACK用(必ず1) writebits(regs, &address, 0, 1); old_bits = 1 ; // 実際のデータを書く for (lp = 0 ; lp < wblock->count ; lp++){ for(bitpos = 0 ; bitpos < 8 ; bitpos++){ bits = ((wblock->value[lp] >> (7 - bitpos)) & 1); writebits(regs, &address, old_bits, bits); old_bits = bits ; } // ACK/NACK用(必ず1) writebits(regs, &address, old_bits, 1); old_bits = 1 ; } // Clock negedge makei2c(regs, address, address + 1, 0, (old_bits ^ 1), 1, 1); clock = TRUE ; address += 1 ; // ここから Read start_i2c(regs, &address, &clock, old_bits); old_bits = 0 ; // まずアドレスを書く for(bitpos = 0 ; bitpos < 7 ; bitpos++){ bits = ((wblock->addr >> (6 - bitpos)) & 1); writebits(regs, &address, old_bits, bits); old_bits = bits ; } // タイプ:RD writebits(regs, &address, old_bits, 1); // ACK/NACK用(必ず1) writebits(regs, &address, 1, 1); old_bits = 1 ; // 実際のデータを書く for (lp = 0 ; lp < count ; lp++){ for(bitpos = 0 ; bitpos < 8 ; bitpos++){ writebits(regs, &address, old_bits, 1); // Read Mode Set makei2c(regs, address, address + 1, 1, 0, 0, 1); address += 1 ; old_bits = 1 ; } if(lp >= (count - 1)){ // ACK/NACK用(必ず1) writebits(regs, &address, old_bits, 1); old_bits = 0 ; }else{ // ACK/NACK用(必ず1) writebits(regs, &address, old_bits, 0); old_bits = 1 ; } } // Clock negedge makei2c(regs, address, address + 1, 0, 0, 1, 1); clock = TRUE ; address += 1 ; old_bits = 1 ; stop_i2c(regs, &address, &clock, old_bits, TRUE); } static void writebits(void __iomem *regs, __u32 *address, __u32 old_bits, __u32 bits) { // CLOCK UP makei2c(regs, *address, *address + 1, 0, (old_bits ^ 1), 1, 1); *address += 1 ; // CLOCK UP makei2c(regs, *address, *address + 1, 0, (bits ^ 1), 1, 1); *address += 1 ; // CLOCK DOWN makei2c(regs, *address, *address + 1, 0, (bits ^ 1), 0, 1); *address += 1 ; } static void begin_i2c(void __iomem *regs, __u32 *address, __u32 *clock) { // bus FREE makei2c(regs, *address, *address, 0, 0, 0, 0); *address += 1 ; // bus busy makei2c(regs, *address, *address + 1, 0, 0, 0, 1); *address += 1 ; *clock = FALSE ; } static void start_i2c(void __iomem *regs, __u32 *address, __u32 *clock, __u32 data) { // データが残っていなければデータを下げる if(!data){ // CLOCKがあればCLOCKを下げる if(*clock != TRUE){ *clock = TRUE ; makei2c(regs, *address, *address + 1, 0, 1, 1, 1); *address += 1 ; } makei2c(regs, *address, *address + 1, 0, 0, 1, 1); *address += 1 ; } if(*clock != FALSE){ *clock = FALSE ; makei2c(regs, *address, *address + 1, 0, 0, 0, 1); *address += 1; } makei2c(regs, *address, *address + 1, 0, 1, 0, 1); *address += 1; *clock = FALSE ; } static void stop_i2c(void __iomem *regs, __u32 *address, __u32 *clock, __u32 data, __u32 end) { // データが残っていて if(data){ // クロックがあれば if(*clock != TRUE){ *clock = TRUE ; makei2c(regs, *address, *address + 1, 0, 0, 1, 1); *address += 1; } makei2c(regs, *address, *address + 1, 0, 1, 1, 1); *address += 1 ; } // クロックが落ちていれば if(*clock){ *clock = FALSE ; makei2c(regs, *address, *address + 1, 0, 1, 0, 1); *address += 1 ; } if(end){ makei2c(regs, *address, 0, 0, 0, 0, 1); }else{ makei2c(regs, *address, *address + 1, 0, 0, 0, 1); *address += 1 ; } } void i2c_write(void __iomem *regs, struct mutex *lock, WBLOCK *wblock) { int lp; __u32 val ; // ロックする mutex_lock(lock); #if 0 printk(KERN_INFO "Addr=%x(%d)\n", wblock->addr, wblock->count); for(lp = 0 ; lp < wblock->count ; lp++){ printk(KERN_INFO "%x\n", wblock->value[lp]); } printk(KERN_INFO "\n"); #endif blockwrite(regs, wblock); writel(FIFO_GO, regs + FIFO_GO_ADDR); //とりあえずロックしないように。 for(lp = 0 ; lp < 100 ; lp++){ val = readl(regs + FIFO_RESULT_ADDR); if(!(val & FIFO_DONE)){ break ; } schedule_timeout_interruptible(msecs_to_jiffies(1)); } mutex_unlock(lock); } __u32 i2c_read(void __iomem *regs, struct mutex *lock, WBLOCK *wblock, int size) { int lp; __u32 val ; // ロックする mutex_lock(lock); #if 0 printk(KERN_INFO "Addr=%x:%d:%d\n", wblock->addr, wblock->count, size); for(lp = 0 ; lp < wblock->count ; lp++){ printk(KERN_INFO "%x\n", wblock->value[lp]); } printk(KERN_INFO "\n"); #endif blockread(regs, wblock, size); writel(FIFO_GO, regs + FIFO_GO_ADDR); for(lp = 0 ; lp < 100 ; lp++){ schedule_timeout_interruptible(msecs_to_jiffies(1)); val = readl(regs + FIFO_RESULT_ADDR); if(!(val & FIFO_DONE)){ break ; } } val = readl(regs + I2C_RESULT_ADDR); mutex_unlock(lock); return val ; }