i2c: Add bus driver for for OSIF USB i2c device.

OSIF, Open Source InterFace, is a USB based i2c bus master.  The
origional design was based on i2c-tiny-usb, but more modern versions
of the firmware running on the MegaAVR microcontroller use a different
protocol over the USB. This code is based on Barry Carter
<barry.carter@gmail.com> driver.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
This commit is contained in:
Andrew Lunn 2014-01-11 00:23:59 +01:00 committed by Wolfram Sang
parent e5c6e7f296
commit 83e53a8f12
3 changed files with 213 additions and 0 deletions

View File

@ -874,6 +874,16 @@ config I2C_PARPORT_LIGHT
This support is also available as a module. If so, the module
will be called i2c-parport-light.
config I2C_ROBOTFUZZ_OSIF
tristate "RobotFuzz Open Source InterFace USB adapter"
depends on USB
help
If you say yes to this option, support will be included for the
RobotFuzz Open Source InterFace USB to I2C interface.
This driver can also be built as a module. If so, the module
will be called i2c-osif.
config I2C_TAOS_EVM
tristate "TAOS evaluation module"
depends on TTY

View File

@ -84,6 +84,7 @@ obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF) += i2c-robotfuzz-osif.o
obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o
obj-$(CONFIG_I2C_TINY_USB) += i2c-tiny-usb.o
obj-$(CONFIG_I2C_VIPERBOARD) += i2c-viperboard.o

View File

@ -0,0 +1,202 @@
/*
* Driver for RobotFuzz OSIF
*
* Copyright (c) 2013 Andrew Lunn <andrew@lunn.ch>
* Copyright (c) 2007 Barry Carter <Barry.Carter@robotfuzz.com>
*
* Based on the i2c-tiny-usb by
*
* Copyright (C) 2006 Til Harbaum (Till@Harbaum.org)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/usb.h>
#define OSIFI2C_READ 20
#define OSIFI2C_WRITE 21
#define OSIFI2C_STOP 22
#define OSIFI2C_STATUS 23
#define OSIFI2C_SET_BIT_RATE 24
#define STATUS_ADDRESS_ACK 0
#define STATUS_ADDRESS_NAK 2
struct osif_priv {
struct usb_device *usb_dev;
struct usb_interface *interface;
struct i2c_adapter adapter;
unsigned char status;
};
static int osif_usb_read(struct i2c_adapter *adapter, int cmd,
int value, int index, void *data, int len)
{
struct osif_priv *priv = adapter->algo_data;
return usb_control_msg(priv->usb_dev, usb_rcvctrlpipe(priv->usb_dev, 0),
cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE |
USB_DIR_IN, value, index, data, len, 2000);
}
static int osif_usb_write(struct i2c_adapter *adapter, int cmd,
int value, int index, void *data, int len)
{
struct osif_priv *priv = adapter->algo_data;
return usb_control_msg(priv->usb_dev, usb_sndctrlpipe(priv->usb_dev, 0),
cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
value, index, data, len, 2000);
}
static int osif_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
int num)
{
struct osif_priv *priv = adapter->algo_data;
struct i2c_msg *pmsg;
int ret = 0;
int i, cmd;
for (i = 0; ret >= 0 && i < num; i++) {
pmsg = &msgs[i];
if (pmsg->flags & I2C_M_RD) {
cmd = OSIFI2C_READ;
ret = osif_usb_read(adapter, cmd, pmsg->flags,
pmsg->addr, pmsg->buf,
pmsg->len);
if (ret != pmsg->len) {
dev_err(&adapter->dev, "failure reading data\n");
return -EREMOTEIO;
}
} else {
cmd = OSIFI2C_WRITE;
ret = osif_usb_write(adapter, cmd, pmsg->flags,
pmsg->addr, pmsg->buf, pmsg->len);
if (ret != pmsg->len) {
dev_err(&adapter->dev, "failure writing data\n");
return -EREMOTEIO;
}
}
ret = osif_usb_read(adapter, OSIFI2C_STOP, 0, 0, NULL, 0);
if (ret) {
dev_err(&adapter->dev, "failure sending STOP\n");
return -EREMOTEIO;
}
/* read status */
ret = osif_usb_read(adapter, OSIFI2C_STATUS, 0, 0,
&priv->status, 1);
if (ret != 1) {
dev_err(&adapter->dev, "failure reading status\n");
return -EREMOTEIO;
}
if (priv->status != STATUS_ADDRESS_ACK) {
dev_dbg(&adapter->dev, "status = %d\n", priv->status);
return -EREMOTEIO;
}
}
return i;
}
static u32 osif_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static struct i2c_algorithm osif_algorithm = {
.master_xfer = osif_xfer,
.functionality = osif_func,
};
#define USB_OSIF_VENDOR_ID 0x1964
#define USB_OSIF_PRODUCT_ID 0x0001
static struct usb_device_id osif_table[] = {
{ USB_DEVICE(USB_OSIF_VENDOR_ID, USB_OSIF_PRODUCT_ID) },
{ }
};
MODULE_DEVICE_TABLE(usb, osif_table);
static int osif_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
int ret;
struct osif_priv *priv;
u16 version;
priv = devm_kzalloc(&interface->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->usb_dev = usb_get_dev(interface_to_usbdev(interface));
priv->interface = interface;
usb_set_intfdata(interface, priv);
priv->adapter.owner = THIS_MODULE;
priv->adapter.class = I2C_CLASS_HWMON;
priv->adapter.algo = &osif_algorithm;
priv->adapter.algo_data = priv;
snprintf(priv->adapter.name, sizeof(priv->adapter.name),
"OSIF at bus %03d device %03d",
priv->usb_dev->bus->busnum, priv->usb_dev->devnum);
/*
* Set bus frequency. The frequency is:
* 120,000,000 / ( 16 + 2 * div * 4^prescale).
* Using dev = 52, prescale = 0 give 100KHz */
ret = osif_usb_read(&priv->adapter, OSIFI2C_SET_BIT_RATE, 52, 0,
NULL, 0);
if (ret) {
dev_err(&interface->dev, "failure sending bit rate");
usb_put_dev(priv->usb_dev);
return ret;
}
i2c_add_adapter(&(priv->adapter));
version = le16_to_cpu(priv->usb_dev->descriptor.bcdDevice);
dev_info(&interface->dev,
"version %x.%02x found at bus %03d address %03d",
version >> 8, version & 0xff,
priv->usb_dev->bus->busnum, priv->usb_dev->devnum);
return 0;
}
static void osif_disconnect(struct usb_interface *interface)
{
struct osif_priv *priv = usb_get_intfdata(interface);
i2c_del_adapter(&(priv->adapter));
usb_set_intfdata(interface, NULL);
usb_put_dev(priv->usb_dev);
}
static struct usb_driver osif_driver = {
.name = "RobotFuzz Open Source InterFace, OSIF",
.probe = osif_probe,
.disconnect = osif_disconnect,
.id_table = osif_table,
};
module_usb_driver(osif_driver);
MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
MODULE_AUTHOR("Barry Carter <barry.carter@robotfuzz.com>");
MODULE_DESCRIPTION("RobotFuzz OSIF driver");
MODULE_LICENSE("GPL v2");