162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for RobotFuzz OSIF 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2013 Andrew Lunn <andrew@lunn.ch> 662306a36Sopenharmony_ci * Copyright (c) 2007 Barry Carter <Barry.Carter@robotfuzz.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Based on the i2c-tiny-usb by 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Copyright (C) 2006 Til Harbaum (Till@Harbaum.org) 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/errno.h> 1662306a36Sopenharmony_ci#include <linux/i2c.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/usb.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define OSIFI2C_READ 20 2162306a36Sopenharmony_ci#define OSIFI2C_WRITE 21 2262306a36Sopenharmony_ci#define OSIFI2C_STOP 22 2362306a36Sopenharmony_ci#define OSIFI2C_STATUS 23 2462306a36Sopenharmony_ci#define OSIFI2C_SET_BIT_RATE 24 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define STATUS_ADDRESS_ACK 0 2762306a36Sopenharmony_ci#define STATUS_ADDRESS_NAK 2 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistruct osif_priv { 3062306a36Sopenharmony_ci struct usb_device *usb_dev; 3162306a36Sopenharmony_ci struct usb_interface *interface; 3262306a36Sopenharmony_ci struct i2c_adapter adapter; 3362306a36Sopenharmony_ci unsigned char status; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic int osif_usb_read(struct i2c_adapter *adapter, int cmd, 3762306a36Sopenharmony_ci int value, int index, void *data, int len) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci struct osif_priv *priv = adapter->algo_data; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci return usb_control_msg(priv->usb_dev, usb_rcvctrlpipe(priv->usb_dev, 0), 4262306a36Sopenharmony_ci cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | 4362306a36Sopenharmony_ci USB_DIR_IN, value, index, data, len, 2000); 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic int osif_usb_write(struct i2c_adapter *adapter, int cmd, 4762306a36Sopenharmony_ci int value, int index, void *data, int len) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci struct osif_priv *priv = adapter->algo_data; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci return usb_control_msg(priv->usb_dev, usb_sndctrlpipe(priv->usb_dev, 0), 5362306a36Sopenharmony_ci cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 5462306a36Sopenharmony_ci value, index, data, len, 2000); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int osif_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, 5862306a36Sopenharmony_ci int num) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct osif_priv *priv = adapter->algo_data; 6162306a36Sopenharmony_ci struct i2c_msg *pmsg; 6262306a36Sopenharmony_ci int ret; 6362306a36Sopenharmony_ci int i; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci for (i = 0; i < num; i++) { 6662306a36Sopenharmony_ci pmsg = &msgs[i]; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (pmsg->flags & I2C_M_RD) { 6962306a36Sopenharmony_ci ret = osif_usb_read(adapter, OSIFI2C_READ, 7062306a36Sopenharmony_ci pmsg->flags, pmsg->addr, 7162306a36Sopenharmony_ci pmsg->buf, pmsg->len); 7262306a36Sopenharmony_ci if (ret != pmsg->len) { 7362306a36Sopenharmony_ci dev_err(&adapter->dev, "failure reading data\n"); 7462306a36Sopenharmony_ci return -EREMOTEIO; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci } else { 7762306a36Sopenharmony_ci ret = osif_usb_write(adapter, OSIFI2C_WRITE, 7862306a36Sopenharmony_ci pmsg->flags, pmsg->addr, 7962306a36Sopenharmony_ci pmsg->buf, pmsg->len); 8062306a36Sopenharmony_ci if (ret != pmsg->len) { 8162306a36Sopenharmony_ci dev_err(&adapter->dev, "failure writing data\n"); 8262306a36Sopenharmony_ci return -EREMOTEIO; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci ret = osif_usb_write(adapter, OSIFI2C_STOP, 0, 0, NULL, 0); 8762306a36Sopenharmony_ci if (ret) { 8862306a36Sopenharmony_ci dev_err(&adapter->dev, "failure sending STOP\n"); 8962306a36Sopenharmony_ci return -EREMOTEIO; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* read status */ 9362306a36Sopenharmony_ci ret = osif_usb_read(adapter, OSIFI2C_STATUS, 0, 0, 9462306a36Sopenharmony_ci &priv->status, 1); 9562306a36Sopenharmony_ci if (ret != 1) { 9662306a36Sopenharmony_ci dev_err(&adapter->dev, "failure reading status\n"); 9762306a36Sopenharmony_ci return -EREMOTEIO; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (priv->status != STATUS_ADDRESS_ACK) { 10162306a36Sopenharmony_ci dev_dbg(&adapter->dev, "status = %d\n", priv->status); 10262306a36Sopenharmony_ci return -EREMOTEIO; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return i; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic u32 osif_func(struct i2c_adapter *adapter) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic const struct i2c_algorithm osif_algorithm = { 11562306a36Sopenharmony_ci .master_xfer = osif_xfer, 11662306a36Sopenharmony_ci .functionality = osif_func, 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#define USB_OSIF_VENDOR_ID 0x1964 12062306a36Sopenharmony_ci#define USB_OSIF_PRODUCT_ID 0x0001 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic const struct usb_device_id osif_table[] = { 12362306a36Sopenharmony_ci { USB_DEVICE(USB_OSIF_VENDOR_ID, USB_OSIF_PRODUCT_ID) }, 12462306a36Sopenharmony_ci { } 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, osif_table); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic int osif_probe(struct usb_interface *interface, 12962306a36Sopenharmony_ci const struct usb_device_id *id) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci int ret; 13262306a36Sopenharmony_ci struct osif_priv *priv; 13362306a36Sopenharmony_ci u16 version; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci priv = devm_kzalloc(&interface->dev, sizeof(*priv), GFP_KERNEL); 13662306a36Sopenharmony_ci if (!priv) 13762306a36Sopenharmony_ci return -ENOMEM; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci priv->usb_dev = usb_get_dev(interface_to_usbdev(interface)); 14062306a36Sopenharmony_ci priv->interface = interface; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci usb_set_intfdata(interface, priv); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci priv->adapter.owner = THIS_MODULE; 14562306a36Sopenharmony_ci priv->adapter.class = I2C_CLASS_HWMON; 14662306a36Sopenharmony_ci priv->adapter.algo = &osif_algorithm; 14762306a36Sopenharmony_ci priv->adapter.algo_data = priv; 14862306a36Sopenharmony_ci snprintf(priv->adapter.name, sizeof(priv->adapter.name), 14962306a36Sopenharmony_ci "OSIF at bus %03d device %03d", 15062306a36Sopenharmony_ci priv->usb_dev->bus->busnum, priv->usb_dev->devnum); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* 15362306a36Sopenharmony_ci * Set bus frequency. The frequency is: 15462306a36Sopenharmony_ci * 120,000,000 / ( 16 + 2 * div * 4^prescale). 15562306a36Sopenharmony_ci * Using dev = 52, prescale = 0 give 100KHz */ 15662306a36Sopenharmony_ci ret = osif_usb_write(&priv->adapter, OSIFI2C_SET_BIT_RATE, 52, 0, 15762306a36Sopenharmony_ci NULL, 0); 15862306a36Sopenharmony_ci if (ret) { 15962306a36Sopenharmony_ci dev_err(&interface->dev, "failure sending bit rate"); 16062306a36Sopenharmony_ci usb_put_dev(priv->usb_dev); 16162306a36Sopenharmony_ci return ret; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci i2c_add_adapter(&(priv->adapter)); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci version = le16_to_cpu(priv->usb_dev->descriptor.bcdDevice); 16762306a36Sopenharmony_ci dev_info(&interface->dev, 16862306a36Sopenharmony_ci "version %x.%02x found at bus %03d address %03d", 16962306a36Sopenharmony_ci version >> 8, version & 0xff, 17062306a36Sopenharmony_ci priv->usb_dev->bus->busnum, priv->usb_dev->devnum); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return 0; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic void osif_disconnect(struct usb_interface *interface) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct osif_priv *priv = usb_get_intfdata(interface); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci i2c_del_adapter(&(priv->adapter)); 18062306a36Sopenharmony_ci usb_set_intfdata(interface, NULL); 18162306a36Sopenharmony_ci usb_put_dev(priv->usb_dev); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic struct usb_driver osif_driver = { 18562306a36Sopenharmony_ci .name = "RobotFuzz Open Source InterFace, OSIF", 18662306a36Sopenharmony_ci .probe = osif_probe, 18762306a36Sopenharmony_ci .disconnect = osif_disconnect, 18862306a36Sopenharmony_ci .id_table = osif_table, 18962306a36Sopenharmony_ci}; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cimodule_usb_driver(osif_driver); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ciMODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>"); 19462306a36Sopenharmony_ciMODULE_AUTHOR("Barry Carter <barry.carter@robotfuzz.com>"); 19562306a36Sopenharmony_ciMODULE_DESCRIPTION("RobotFuzz OSIF driver"); 19662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 197