summaryrefslogtreecommitdiffstats
path: root/drivers/sbus/char/vfc_i2c.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
committer <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
commit19c9bba94152148523ba0f7ef7cffe3d45656b11 (patch)
tree40b1cb534496a7f1ca0f5c314a523c69f1fee464 /drivers/sbus/char/vfc_i2c.c
parent7206675c40394c78a90e74812bbdbf8cf3cca1be (diff)
Import of Linux/MIPS 2.1.36
Diffstat (limited to 'drivers/sbus/char/vfc_i2c.c')
-rw-r--r--drivers/sbus/char/vfc_i2c.c319
1 files changed, 319 insertions, 0 deletions
diff --git a/drivers/sbus/char/vfc_i2c.c b/drivers/sbus/char/vfc_i2c.c
new file mode 100644
index 000000000..952eb9875
--- /dev/null
+++ b/drivers/sbus/char/vfc_i2c.c
@@ -0,0 +1,319 @@
+/*
+ * 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/malloc.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_DEBUG
+#endif
+
+#include "vfc.h"
+#include "vfc_i2c.h"
+
+#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)
+{
+ dev->regs->i2c_s1=RESET; /* This will also choose
+ register S0_OWN so we can set it*/
+
+ dev->regs->i2c_reg=0x55000000; /* the pcf8584 shifts this
+ value left one bit and uses
+ it as its i2c bus address */
+ dev->regs->i2c_s1=SELECT(S2);
+ dev->regs->i2c_reg=0x14000000; /* this will set the i2c bus at
+ the same speed sun uses,
+ and set another magic bit */
+
+ dev->regs->i2c_s1=CLEAR_I2C_BUS; /* enable the serial port,
+ idle the i2c bus and set
+ the data reg to s0 */
+ udelay(100);
+ return 0;
+}
+
+void vfc_i2c_delay_wakeup(struct vfc_dev *dev)
+{
+ wake_up(&dev->poll_wait);
+}
+
+void vfc_i2c_delay_no_busy(struct vfc_dev *dev,unsigned long usecs)
+{
+ dev->poll_timer.next = NULL;
+ dev->poll_timer.prev = NULL;
+ 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)
+{
+ dev->regs->i2c_s1= ENABLE_SERIAL | ACK;
+ vfc_i2c_reset_bus(dev);
+ return 0;
+}
+
+int vfc_i2c_reset_bus(struct vfc_dev *dev)
+{
+ VFC_DEBUG_PRINTK((KERN_DEBUG "vfc%d: Resetting the i2c bus\n",
+ dev->instance));
+ if(!dev) return -EINVAL;
+ if(!dev->regs) return -EINVAL;
+ dev->regs->i2c_s1=SEND_I2C_STOP;
+ dev->regs->i2c_s1=SEND_I2C_STOP | ACK;
+ vfc_i2c_delay(dev);
+ dev->regs->i2c_s1=CLEAR_I2C_BUS;
+ VFC_DEBUG_PRINTK((KERN_DEBUG "vfc%d: I2C status %x\n",
+ dev->instance, dev->regs->i2c_s1));
+ return 0;
+}
+
+int vfc_i2c_wait_for_bus(struct vfc_dev *dev)
+{
+ int timeout=1000;
+
+ while(!(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=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
+ dev->regs->i2c_s1=SEND_I2C_STOP;
+ dev->regs->i2c_s1=SELECT(S0) | ENABLE_SERIAL;
+ vfc_i2c_delay(dev);
+#endif
+
+ switch(mode) {
+ case VFC_I2C_READ:
+ dev->regs->i2c_reg=raddr=SHIFT((unsigned int)addr | 0x1);
+ VFC_DEBUG_PRINTK(("vfc%d: recieving from i2c addr 0x%x\n",
+ dev->instance,addr | 0x1));
+ break;
+ case VFC_I2C_WRITE:
+ dev->regs->i2c_reg=raddr=SHIFT((unsigned int)addr & ~0x1);
+ VFC_DEBUG_PRINTK(("vfc%d: sending to i2c addr 0x%x\n",
+ dev->instance,addr & ~0x1));
+ break;
+ default:
+ return -EINVAL;
+ }
+ dev->regs->i2c_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 ack\n",
+ dev->instance);
+ return ret;
+ } else if(mode == VFC_I2C_READ) {
+ if((ret=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;
+ dev->regs->i2c_reg=SHIFT((unsigned int)*byte);
+
+ 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 ack\n",
+ 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) {
+ dev->regs->i2c_reg=NEGATIVE_ACK;
+ VFC_DEBUG_PRINTK((KERN_DEBUG "vfc%d: sending negative ack\n",
+ dev->instance));
+ } else {
+ dev->regs->i2c_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 out\n",dev->instance);
+ }
+ *byte=(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 busy\n",dev->instance);
+ return ret;
+ }
+
+ if((ret=vfc_i2c_xmit_addr(dev,addr,VFC_I2C_READ))) {
+ dev->regs->i2c_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 recieving byte\n",
+ dev->instance);
+ }
+ buf++;
+ }
+
+ dev->regs->i2c_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 busy\n",dev->instance);
+ return ret;
+ }
+
+ if((ret=vfc_i2c_xmit_addr(dev,addr,VFC_I2C_WRITE))) {
+ dev->regs->i2c_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_DEBUG_PRINTK(("vfc%d: "
+ "Reciever ended transmission with "
+ " %d bytes remaining\n",
+ dev->instance,count));
+ ret=0;
+ goto done;
+ break;
+ case 0:
+ break;
+ default:
+ printk(KERN_ERR "vfc%d: "
+ "VFC error while sending byte\n",dev->instance);
+ break;
+ }
+ buf++;
+ }
+done:
+ dev->regs->i2c_s1=SEND_I2C_STOP | ACK;
+
+ vfc_i2c_delay(dev);
+ return ret;
+}
+
+
+
+
+
+
+
+
+