Mercurial > pt1
diff driver/pt1_i2c.c @ 0:67e8eca28a80
initial import
author | Yoshiki Yazawa <yaz@honeyplanet.jp> |
---|---|
date | Mon, 16 Feb 2009 15:41:49 +0900 |
parents | |
children | 517e61637f7b |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/driver/pt1_i2c.c Mon Feb 16 15:41:49 2009 +0900 @@ -0,0 +1,475 @@ +/***************************************************************************/ +/* 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 ; +}