diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-robotfuzz-osif.c')
| -rw-r--r-- | drivers/i2c/busses/i2c-robotfuzz-osif.c | 202 | 
1 files changed, 202 insertions, 0 deletions
diff --git a/drivers/i2c/busses/i2c-robotfuzz-osif.c b/drivers/i2c/busses/i2c-robotfuzz-osif.c new file mode 100644 index 00000000000..ced9c6a308d --- /dev/null +++ b/drivers/i2c/busses/i2c-robotfuzz-osif.c @@ -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");  | 
