|  | /* | 
|  | * 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 algorithms appear 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_no_busy(struct vfc_dev *dev, unsigned long usecs) | 
|  | { | 
|  | schedule_timeout_uninterruptible(usecs_to_jiffies(usecs)); | 
|  | } | 
|  |  | 
|  | 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 bus\n", | 
|  | 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 %x\n", | 
|  | 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%x\n", | 
|  | 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%x\n", | 
|  | 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 ack\n", | 
|  | 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 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) { | 
|  | WRITE_REG(NEGATIVE_ACK); | 
|  | VFC_I2C_DEBUG_PRINTK(("vfc%d: sending negative ack\n", | 
|  | 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 out\n", | 
|  | 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 busy\n", 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 byte\n", | 
|  | 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 busy\n", 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 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: | 
|  | WRITE_S1(SEND_I2C_STOP | ACK); | 
|  | vfc_i2c_delay(dev); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  |