xref: /linux/drivers/i2c/busses/i2c-robotfuzz-osif.c (revision ddb7a62af2e766eabb4ab7080e6ed8d6b8915302)
1a10e763bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
283e53a8fSAndrew Lunn /*
383e53a8fSAndrew Lunn  * Driver for RobotFuzz OSIF
483e53a8fSAndrew Lunn  *
583e53a8fSAndrew Lunn  * Copyright (c) 2013 Andrew Lunn <andrew@lunn.ch>
683e53a8fSAndrew Lunn  * Copyright (c) 2007 Barry Carter <Barry.Carter@robotfuzz.com>
783e53a8fSAndrew Lunn  *
883e53a8fSAndrew Lunn  * Based on the i2c-tiny-usb by
983e53a8fSAndrew Lunn  *
1083e53a8fSAndrew Lunn  * Copyright (C) 2006 Til Harbaum (Till@Harbaum.org)
1183e53a8fSAndrew Lunn  */
1283e53a8fSAndrew Lunn 
1383e53a8fSAndrew Lunn #include <linux/kernel.h>
1483e53a8fSAndrew Lunn #include <linux/module.h>
1583e53a8fSAndrew Lunn #include <linux/errno.h>
1683e53a8fSAndrew Lunn #include <linux/i2c.h>
1783e53a8fSAndrew Lunn #include <linux/slab.h>
1883e53a8fSAndrew Lunn #include <linux/usb.h>
1983e53a8fSAndrew Lunn 
2083e53a8fSAndrew Lunn #define OSIFI2C_READ		20
2183e53a8fSAndrew Lunn #define OSIFI2C_WRITE		21
2283e53a8fSAndrew Lunn #define OSIFI2C_STOP		22
2383e53a8fSAndrew Lunn #define OSIFI2C_STATUS		23
2483e53a8fSAndrew Lunn #define OSIFI2C_SET_BIT_RATE	24
2583e53a8fSAndrew Lunn 
2683e53a8fSAndrew Lunn #define STATUS_ADDRESS_ACK	0
2783e53a8fSAndrew Lunn #define STATUS_ADDRESS_NAK	2
2883e53a8fSAndrew Lunn 
2983e53a8fSAndrew Lunn struct osif_priv {
3083e53a8fSAndrew Lunn 	struct usb_device *usb_dev;
3183e53a8fSAndrew Lunn 	struct usb_interface *interface;
3283e53a8fSAndrew Lunn 	struct i2c_adapter adapter;
3383e53a8fSAndrew Lunn 	unsigned char status;
3483e53a8fSAndrew Lunn };
3583e53a8fSAndrew Lunn 
osif_usb_read(struct i2c_adapter * adapter,int cmd,int value,int index,void * data,int len)3683e53a8fSAndrew Lunn static int osif_usb_read(struct i2c_adapter *adapter, int cmd,
3783e53a8fSAndrew Lunn 			 int value, int index, void *data, int len)
3883e53a8fSAndrew Lunn {
3983e53a8fSAndrew Lunn 	struct osif_priv *priv = adapter->algo_data;
4083e53a8fSAndrew Lunn 
4183e53a8fSAndrew Lunn 	return usb_control_msg(priv->usb_dev, usb_rcvctrlpipe(priv->usb_dev, 0),
4283e53a8fSAndrew Lunn 			       cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE |
4383e53a8fSAndrew Lunn 			       USB_DIR_IN, value, index, data, len, 2000);
4483e53a8fSAndrew Lunn }
4583e53a8fSAndrew Lunn 
osif_usb_write(struct i2c_adapter * adapter,int cmd,int value,int index,void * data,int len)4683e53a8fSAndrew Lunn static int osif_usb_write(struct i2c_adapter *adapter, int cmd,
4783e53a8fSAndrew Lunn 			  int value, int index, void *data, int len)
4883e53a8fSAndrew Lunn {
4983e53a8fSAndrew Lunn 
5083e53a8fSAndrew Lunn 	struct osif_priv *priv = adapter->algo_data;
5183e53a8fSAndrew Lunn 
5283e53a8fSAndrew Lunn 	return usb_control_msg(priv->usb_dev, usb_sndctrlpipe(priv->usb_dev, 0),
5383e53a8fSAndrew Lunn 			       cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
5483e53a8fSAndrew Lunn 			       value, index, data, len, 2000);
5583e53a8fSAndrew Lunn }
5683e53a8fSAndrew Lunn 
osif_xfer(struct i2c_adapter * adapter,struct i2c_msg * msgs,int num)5783e53a8fSAndrew Lunn static int osif_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
5883e53a8fSAndrew Lunn 			 int num)
5983e53a8fSAndrew Lunn {
6083e53a8fSAndrew Lunn 	struct osif_priv *priv = adapter->algo_data;
6183e53a8fSAndrew Lunn 	struct i2c_msg *pmsg;
626a0c0d0dSPeter Rosin 	int ret;
637fb29b95SPeter Rosin 	int i;
6483e53a8fSAndrew Lunn 
656a0c0d0dSPeter Rosin 	for (i = 0; i < num; i++) {
6683e53a8fSAndrew Lunn 		pmsg = &msgs[i];
6783e53a8fSAndrew Lunn 
6883e53a8fSAndrew Lunn 		if (pmsg->flags & I2C_M_RD) {
697fb29b95SPeter Rosin 			ret = osif_usb_read(adapter, OSIFI2C_READ,
707fb29b95SPeter Rosin 					    pmsg->flags, pmsg->addr,
717fb29b95SPeter Rosin 					    pmsg->buf, pmsg->len);
7283e53a8fSAndrew Lunn 			if (ret != pmsg->len) {
7383e53a8fSAndrew Lunn 				dev_err(&adapter->dev, "failure reading data\n");
7483e53a8fSAndrew Lunn 				return -EREMOTEIO;
7583e53a8fSAndrew Lunn 			}
7683e53a8fSAndrew Lunn 		} else {
777fb29b95SPeter Rosin 			ret = osif_usb_write(adapter, OSIFI2C_WRITE,
787fb29b95SPeter Rosin 					     pmsg->flags, pmsg->addr,
797fb29b95SPeter Rosin 					     pmsg->buf, pmsg->len);
8083e53a8fSAndrew Lunn 			if (ret != pmsg->len) {
8183e53a8fSAndrew Lunn 				dev_err(&adapter->dev, "failure writing data\n");
8283e53a8fSAndrew Lunn 				return -EREMOTEIO;
8383e53a8fSAndrew Lunn 			}
8483e53a8fSAndrew Lunn 		}
8583e53a8fSAndrew Lunn 
864ca070efSJohan Hovold 		ret = osif_usb_write(adapter, OSIFI2C_STOP, 0, 0, NULL, 0);
8783e53a8fSAndrew Lunn 		if (ret) {
8883e53a8fSAndrew Lunn 			dev_err(&adapter->dev, "failure sending STOP\n");
8983e53a8fSAndrew Lunn 			return -EREMOTEIO;
9083e53a8fSAndrew Lunn 		}
9183e53a8fSAndrew Lunn 
9283e53a8fSAndrew Lunn 		/* read status */
9383e53a8fSAndrew Lunn 		ret = osif_usb_read(adapter, OSIFI2C_STATUS, 0, 0,
9483e53a8fSAndrew Lunn 				    &priv->status, 1);
9583e53a8fSAndrew Lunn 		if (ret != 1) {
9683e53a8fSAndrew Lunn 			dev_err(&adapter->dev, "failure reading status\n");
9783e53a8fSAndrew Lunn 			return -EREMOTEIO;
9883e53a8fSAndrew Lunn 		}
9983e53a8fSAndrew Lunn 
10083e53a8fSAndrew Lunn 		if (priv->status != STATUS_ADDRESS_ACK) {
10183e53a8fSAndrew Lunn 			dev_dbg(&adapter->dev, "status = %d\n", priv->status);
10283e53a8fSAndrew Lunn 			return -EREMOTEIO;
10383e53a8fSAndrew Lunn 		}
10483e53a8fSAndrew Lunn 	}
10583e53a8fSAndrew Lunn 
10683e53a8fSAndrew Lunn 	return i;
10783e53a8fSAndrew Lunn }
10883e53a8fSAndrew Lunn 
osif_func(struct i2c_adapter * adapter)10983e53a8fSAndrew Lunn static u32 osif_func(struct i2c_adapter *adapter)
11083e53a8fSAndrew Lunn {
11183e53a8fSAndrew Lunn 	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
11283e53a8fSAndrew Lunn }
11383e53a8fSAndrew Lunn 
114*56ad91c1SWolfram Sang /* prevent invalid 0-length usb_control_msg */
115*56ad91c1SWolfram Sang static const struct i2c_adapter_quirks osif_quirks = {
116*56ad91c1SWolfram Sang 	.flags = I2C_AQ_NO_ZERO_LEN_READ,
117*56ad91c1SWolfram Sang };
118*56ad91c1SWolfram Sang 
11992d9d0dfSBhumika Goyal static const struct i2c_algorithm osif_algorithm = {
120fd4b7e03SWolfram Sang 	.xfer = osif_xfer,
12183e53a8fSAndrew Lunn 	.functionality = osif_func,
12283e53a8fSAndrew Lunn };
12383e53a8fSAndrew Lunn 
12483e53a8fSAndrew Lunn #define USB_OSIF_VENDOR_ID	0x1964
12583e53a8fSAndrew Lunn #define USB_OSIF_PRODUCT_ID	0x0001
12683e53a8fSAndrew Lunn 
12733c77abcSAxel Lin static const struct usb_device_id osif_table[] = {
12883e53a8fSAndrew Lunn 	{ USB_DEVICE(USB_OSIF_VENDOR_ID, USB_OSIF_PRODUCT_ID) },
12983e53a8fSAndrew Lunn 	{ }
13083e53a8fSAndrew Lunn };
13183e53a8fSAndrew Lunn MODULE_DEVICE_TABLE(usb, osif_table);
13283e53a8fSAndrew Lunn 
osif_probe(struct usb_interface * interface,const struct usb_device_id * id)13383e53a8fSAndrew Lunn static int osif_probe(struct usb_interface *interface,
13483e53a8fSAndrew Lunn 			     const struct usb_device_id *id)
13583e53a8fSAndrew Lunn {
13683e53a8fSAndrew Lunn 	int ret;
13783e53a8fSAndrew Lunn 	struct osif_priv *priv;
13883e53a8fSAndrew Lunn 	u16 version;
13983e53a8fSAndrew Lunn 
14083e53a8fSAndrew Lunn 	priv = devm_kzalloc(&interface->dev, sizeof(*priv), GFP_KERNEL);
14183e53a8fSAndrew Lunn 	if (!priv)
14283e53a8fSAndrew Lunn 		return -ENOMEM;
14383e53a8fSAndrew Lunn 
14483e53a8fSAndrew Lunn 	priv->usb_dev = usb_get_dev(interface_to_usbdev(interface));
14583e53a8fSAndrew Lunn 	priv->interface = interface;
14683e53a8fSAndrew Lunn 
14783e53a8fSAndrew Lunn 	usb_set_intfdata(interface, priv);
14883e53a8fSAndrew Lunn 
14983e53a8fSAndrew Lunn 	priv->adapter.owner = THIS_MODULE;
15083e53a8fSAndrew Lunn 	priv->adapter.class = I2C_CLASS_HWMON;
151*56ad91c1SWolfram Sang 	priv->adapter.quirks = &osif_quirks;
15283e53a8fSAndrew Lunn 	priv->adapter.algo = &osif_algorithm;
15383e53a8fSAndrew Lunn 	priv->adapter.algo_data = priv;
15483e53a8fSAndrew Lunn 	snprintf(priv->adapter.name, sizeof(priv->adapter.name),
15583e53a8fSAndrew Lunn 		 "OSIF at bus %03d device %03d",
15683e53a8fSAndrew Lunn 		 priv->usb_dev->bus->busnum, priv->usb_dev->devnum);
15783e53a8fSAndrew Lunn 
15883e53a8fSAndrew Lunn 	/*
15983e53a8fSAndrew Lunn 	 * Set bus frequency. The frequency is:
16083e53a8fSAndrew Lunn 	 * 120,000,000 / ( 16 + 2 * div * 4^prescale).
16183e53a8fSAndrew Lunn 	 * Using dev = 52, prescale = 0 give 100KHz */
1624ca070efSJohan Hovold 	ret = osif_usb_write(&priv->adapter, OSIFI2C_SET_BIT_RATE, 52, 0,
16383e53a8fSAndrew Lunn 			    NULL, 0);
16483e53a8fSAndrew Lunn 	if (ret) {
16583e53a8fSAndrew Lunn 		dev_err(&interface->dev, "failure sending bit rate");
16683e53a8fSAndrew Lunn 		usb_put_dev(priv->usb_dev);
16783e53a8fSAndrew Lunn 		return ret;
16883e53a8fSAndrew Lunn 	}
16983e53a8fSAndrew Lunn 
17083e53a8fSAndrew Lunn 	i2c_add_adapter(&(priv->adapter));
17183e53a8fSAndrew Lunn 
17283e53a8fSAndrew Lunn 	version = le16_to_cpu(priv->usb_dev->descriptor.bcdDevice);
17383e53a8fSAndrew Lunn 	dev_info(&interface->dev,
17483e53a8fSAndrew Lunn 		 "version %x.%02x found at bus %03d address %03d",
17583e53a8fSAndrew Lunn 		 version >> 8, version & 0xff,
17683e53a8fSAndrew Lunn 		 priv->usb_dev->bus->busnum, priv->usb_dev->devnum);
17783e53a8fSAndrew Lunn 
17883e53a8fSAndrew Lunn 	return 0;
17983e53a8fSAndrew Lunn }
18083e53a8fSAndrew Lunn 
osif_disconnect(struct usb_interface * interface)18183e53a8fSAndrew Lunn static void osif_disconnect(struct usb_interface *interface)
18283e53a8fSAndrew Lunn {
18383e53a8fSAndrew Lunn 	struct osif_priv *priv = usb_get_intfdata(interface);
18483e53a8fSAndrew Lunn 
18583e53a8fSAndrew Lunn 	i2c_del_adapter(&(priv->adapter));
18683e53a8fSAndrew Lunn 	usb_set_intfdata(interface, NULL);
18783e53a8fSAndrew Lunn 	usb_put_dev(priv->usb_dev);
18883e53a8fSAndrew Lunn }
18983e53a8fSAndrew Lunn 
19083e53a8fSAndrew Lunn static struct usb_driver osif_driver = {
19183e53a8fSAndrew Lunn 	.name		= "RobotFuzz Open Source InterFace, OSIF",
19283e53a8fSAndrew Lunn 	.probe		= osif_probe,
19383e53a8fSAndrew Lunn 	.disconnect	= osif_disconnect,
19483e53a8fSAndrew Lunn 	.id_table	= osif_table,
19583e53a8fSAndrew Lunn };
19683e53a8fSAndrew Lunn 
19783e53a8fSAndrew Lunn module_usb_driver(osif_driver);
19883e53a8fSAndrew Lunn 
19983e53a8fSAndrew Lunn MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
20083e53a8fSAndrew Lunn MODULE_AUTHOR("Barry Carter <barry.carter@robotfuzz.com>");
20183e53a8fSAndrew Lunn MODULE_DESCRIPTION("RobotFuzz OSIF driver");
20283e53a8fSAndrew Lunn MODULE_LICENSE("GPL v2");
203