vfc_i2c.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:7k
- /*
- * drivers/sbus/char/vfc_i2c.c
- *
- * Driver for the Videopix Frame Grabber.
- *
- * Functions that support the Phillips i2c(I squared C) bus on the vfc
- * Documentation for the Phillips I2C bus can be found on the
- * phillips home page
- *
- * Copyright (C) 1996 Manish Vachharajani (mvachhar@noc.rutgers.edu)
- *
- */
- /* NOTE: It seems to me that the documentation regarding the
- pcd8584t/pcf8584 does not show the correct way to address the i2c bus.
- Based on the information on the I2C bus itself and the remainder of
- the Phillips docs the following algorithims apper to be correct. I am
- fairly certain that the flowcharts in the phillips docs are wrong. */
- #include <linux/kernel.h>
- #include <linux/string.h>
- #include <linux/slab.h>
- #include <linux/errno.h>
- #include <linux/sched.h>
- #include <linux/wait.h>
- #include <linux/delay.h>
- #include <asm/openprom.h>
- #include <asm/oplib.h>
- #include <asm/io.h>
- #include <asm/system.h>
- #include <asm/sbus.h>
- #if 0
- #define VFC_I2C_DEBUG
- #endif
- #include "vfc.h"
- #include "vfc_i2c.h"
- #define WRITE_S1(__val)
- sbus_writel(__val, &dev->regs->i2c_s1)
- #define WRITE_REG(__val)
- sbus_writel(__val, &dev->regs->i2c_reg)
- #define VFC_I2C_READ (0x1)
- #define VFC_I2C_WRITE (0x0)
-
- /******
- The i2c bus controller chip on the VFC is a pcd8584t, but
- phillips claims it doesn't exist. As far as I can tell it is
- identical to the PCF8584 so I treat it like it is the pcf8584.
-
- NOTE: The pcf8584 only cares
- about the msb of the word you feed it
- *****/
- int vfc_pcf8584_init(struct vfc_dev *dev)
- {
- /* This will also choose register S0_OWN so we can set it. */
- WRITE_S1(RESET);
- /* The pcf8584 shifts this value left one bit and uses
- * it as its i2c bus address.
- */
- WRITE_REG(0x55000000);
- /* This will set the i2c bus at the same speed sun uses,
- * and set another magic bit.
- */
- WRITE_S1(SELECT(S2));
- WRITE_REG(0x14000000);
-
- /* Enable the serial port, idle the i2c bus and set
- * the data reg to s0.
- */
- WRITE_S1(CLEAR_I2C_BUS);
- udelay(100);
- return 0;
- }
- void vfc_i2c_delay_wakeup(struct vfc_dev *dev)
- {
- /* Used to profile code and eliminate too many delays */
- VFC_I2C_DEBUG_PRINTK(("vfc%d: Delayingn", dev->instance));
- wake_up(&dev->poll_wait);
- }
- void vfc_i2c_delay_no_busy(struct vfc_dev *dev, unsigned long usecs)
- {
- init_timer(&dev->poll_timer);
- dev->poll_timer.expires = jiffies +
- ((unsigned long)usecs*(HZ))/1000000;
- dev->poll_timer.data=(unsigned long)dev;
- dev->poll_timer.function=(void *)(unsigned long)vfc_i2c_delay_wakeup;
- add_timer(&dev->poll_timer);
- sleep_on(&dev->poll_wait);
- del_timer(&dev->poll_timer);
- }
- void inline vfc_i2c_delay(struct vfc_dev *dev)
- {
- vfc_i2c_delay_no_busy(dev, 100);
- }
- int vfc_init_i2c_bus(struct vfc_dev *dev)
- {
- WRITE_S1(ENABLE_SERIAL | SELECT(S0) | ACK);
- vfc_i2c_reset_bus(dev);
- return 0;
- }
- int vfc_i2c_reset_bus(struct vfc_dev *dev)
- {
- VFC_I2C_DEBUG_PRINTK((KERN_DEBUG "vfc%d: Resetting the i2c busn",
- dev->instance));
- if(dev == NULL)
- return -EINVAL;
- if(dev->regs == NULL)
- return -EINVAL;
- WRITE_S1(SEND_I2C_STOP);
- WRITE_S1(SEND_I2C_STOP | ACK);
- vfc_i2c_delay(dev);
- WRITE_S1(CLEAR_I2C_BUS);
- VFC_I2C_DEBUG_PRINTK((KERN_DEBUG "vfc%d: I2C status %xn",
- dev->instance,
- sbus_readl(&dev->regs->i2c_s1)));
- return 0;
- }
- int vfc_i2c_wait_for_bus(struct vfc_dev *dev)
- {
- int timeout = 1000;
- while(!(sbus_readl(&dev->regs->i2c_s1) & BB)) {
- if(!(timeout--))
- return -ETIMEDOUT;
- vfc_i2c_delay(dev);
- }
- return 0;
- }
- int vfc_i2c_wait_for_pin(struct vfc_dev *dev, int ack)
- {
- int timeout = 1000;
- int s1;
- while ((s1 = sbus_readl(&dev->regs->i2c_s1)) & PIN) {
- if (!(timeout--))
- return -ETIMEDOUT;
- vfc_i2c_delay(dev);
- }
- if (ack == VFC_I2C_ACK_CHECK) {
- if(s1 & LRB)
- return -EIO;
- }
- return 0;
- }
- #define SHIFT(a) ((a) << 24)
- int vfc_i2c_xmit_addr(struct vfc_dev *dev, unsigned char addr, char mode)
- {
- int ret, raddr;
- #if 1
- WRITE_S1(SEND_I2C_STOP | ACK);
- WRITE_S1(SELECT(S0) | ENABLE_SERIAL);
- vfc_i2c_delay(dev);
- #endif
- switch(mode) {
- case VFC_I2C_READ:
- raddr = SHIFT(((unsigned int)addr | 0x1));
- WRITE_REG(raddr);
- VFC_I2C_DEBUG_PRINTK(("vfc%d: receiving from i2c addr 0x%xn",
- dev->instance, addr | 0x1));
- break;
- case VFC_I2C_WRITE:
- raddr = SHIFT((unsigned int)addr & ~0x1);
- WRITE_REG(raddr);
- VFC_I2C_DEBUG_PRINTK(("vfc%d: sending to i2c addr 0x%xn",
- dev->instance, addr & ~0x1));
- break;
- default:
- return -EINVAL;
- };
- WRITE_S1(SEND_I2C_START);
- vfc_i2c_delay(dev);
- ret = vfc_i2c_wait_for_pin(dev,VFC_I2C_ACK_CHECK); /* We wait
- for the
- i2c send
- to finish
- here but
- Sun
- doesn't,
- hmm */
- if (ret) {
- printk(KERN_ERR "vfc%d: VFC xmit addr timed out or no ackn",
- dev->instance);
- return ret;
- } else if (mode == VFC_I2C_READ) {
- if ((ret = sbus_readl(&dev->regs->i2c_reg) & 0xff000000) != raddr) {
- printk(KERN_WARNING
- "vfc%d: returned slave address "
- "mismatch(%x,%x)n",
- dev->instance, raddr, ret);
- }
- }
- return 0;
- }
- int vfc_i2c_xmit_byte(struct vfc_dev *dev,unsigned char *byte)
- {
- int ret;
- u32 val = SHIFT((unsigned int)*byte);
- WRITE_REG(val);
- ret = vfc_i2c_wait_for_pin(dev, VFC_I2C_ACK_CHECK);
- switch(ret) {
- case -ETIMEDOUT:
- printk(KERN_ERR "vfc%d: VFC xmit byte timed out or no ackn",
- dev->instance);
- break;
- case -EIO:
- ret = XMIT_LAST_BYTE;
- break;
- default:
- break;
- };
- return ret;
- }
- int vfc_i2c_recv_byte(struct vfc_dev *dev, unsigned char *byte, int last)
- {
- int ret;
- if (last) {
- WRITE_REG(NEGATIVE_ACK);
- VFC_I2C_DEBUG_PRINTK(("vfc%d: sending negative ackn",
- dev->instance));
- } else {
- WRITE_S1(ACK);
- }
- ret = vfc_i2c_wait_for_pin(dev, VFC_I2C_NO_ACK_CHECK);
- if(ret) {
- printk(KERN_ERR "vfc%d: "
- "VFC recv byte timed outn",
- dev->instance);
- }
- *byte = (sbus_readl(&dev->regs->i2c_reg)) >> 24;
- return ret;
- }
- int vfc_i2c_recvbuf(struct vfc_dev *dev, unsigned char addr,
- char *buf, int count)
- {
- int ret, last;
- if(!(count && buf && dev && dev->regs) )
- return -EINVAL;
- if ((ret = vfc_i2c_wait_for_bus(dev))) {
- printk(KERN_ERR "vfc%d: VFC I2C bus busyn", dev->instance);
- return ret;
- }
- if ((ret = vfc_i2c_xmit_addr(dev, addr, VFC_I2C_READ))) {
- WRITE_S1(SEND_I2C_STOP);
- vfc_i2c_delay(dev);
- return ret;
- }
-
- last = 0;
- while (count--) {
- if (!count)
- last = 1;
- if ((ret = vfc_i2c_recv_byte(dev, buf, last))) {
- printk(KERN_ERR "vfc%d: "
- "VFC error while receiving byten",
- dev->instance);
- WRITE_S1(SEND_I2C_STOP);
- ret = -EINVAL;
- }
- buf++;
- }
- WRITE_S1(SEND_I2C_STOP | ACK);
- vfc_i2c_delay(dev);
- return ret;
- }
- int vfc_i2c_sendbuf(struct vfc_dev *dev, unsigned char addr,
- char *buf, int count)
- {
- int ret;
-
- if (!(buf && dev && dev->regs))
- return -EINVAL;
-
- if ((ret = vfc_i2c_wait_for_bus(dev))) {
- printk(KERN_ERR "vfc%d: VFC I2C bus busyn", dev->instance);
- return ret;
- }
-
- if ((ret = vfc_i2c_xmit_addr(dev, addr, VFC_I2C_WRITE))) {
- WRITE_S1(SEND_I2C_STOP);
- vfc_i2c_delay(dev);
- return ret;
- }
-
- while(count--) {
- ret = vfc_i2c_xmit_byte(dev, buf);
- switch(ret) {
- case XMIT_LAST_BYTE:
- VFC_I2C_DEBUG_PRINTK(("vfc%d: "
- "Receiver ended transmission with "
- " %d bytes remainingn",
- dev->instance, count));
- ret = 0;
- goto done;
- break;
- case 0:
- break;
- default:
- printk(KERN_ERR "vfc%d: "
- "VFC error while sending byten", dev->instance);
- break;
- };
- buf++;
- }
- done:
- WRITE_S1(SEND_I2C_STOP | ACK);
- vfc_i2c_delay(dev);
- return ret;
- }