Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
This commit is contained in:
347
drivers/sbus/char/vfc_i2c.c
Normal file
347
drivers/sbus/char/vfc_i2c.c
Normal file
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* 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: Delaying\n", 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 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user