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