18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * driver for the i2c-tiny-usb adapter - 1.0 48c2ecf20Sopenharmony_ci * http://www.harbaum.org/till/i2c_tiny_usb 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2006-2007 Till Harbaum (Till@Harbaum.org) 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/errno.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/types.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/* include interfaces to usb layer */ 168c2ecf20Sopenharmony_ci#include <linux/usb.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* include interface to i2c layer */ 198c2ecf20Sopenharmony_ci#include <linux/i2c.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* commands via USB, must match command ids in the firmware */ 228c2ecf20Sopenharmony_ci#define CMD_ECHO 0 238c2ecf20Sopenharmony_ci#define CMD_GET_FUNC 1 248c2ecf20Sopenharmony_ci#define CMD_SET_DELAY 2 258c2ecf20Sopenharmony_ci#define CMD_GET_STATUS 3 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define CMD_I2C_IO 4 288c2ecf20Sopenharmony_ci#define CMD_I2C_IO_BEGIN (1<<0) 298c2ecf20Sopenharmony_ci#define CMD_I2C_IO_END (1<<1) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* i2c bit delay, default is 10us -> 100kHz max 328c2ecf20Sopenharmony_ci (in practice, due to additional delays in the i2c bitbanging 338c2ecf20Sopenharmony_ci code this results in a i2c clock of about 50kHz) */ 348c2ecf20Sopenharmony_cistatic unsigned short delay = 10; 358c2ecf20Sopenharmony_cimodule_param(delay, ushort, 0); 368c2ecf20Sopenharmony_ciMODULE_PARM_DESC(delay, "bit delay in microseconds " 378c2ecf20Sopenharmony_ci "(default is 10us for 100kHz max)"); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int usb_read(struct i2c_adapter *adapter, int cmd, 408c2ecf20Sopenharmony_ci int value, int index, void *data, int len); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int usb_write(struct i2c_adapter *adapter, int cmd, 438c2ecf20Sopenharmony_ci int value, int index, void *data, int len); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* ----- begin of i2c layer ---------------------------------------------- */ 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define STATUS_IDLE 0 488c2ecf20Sopenharmony_ci#define STATUS_ADDRESS_ACK 1 498c2ecf20Sopenharmony_ci#define STATUS_ADDRESS_NAK 2 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic int usb_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci unsigned char *pstatus; 548c2ecf20Sopenharmony_ci struct i2c_msg *pmsg; 558c2ecf20Sopenharmony_ci int i, ret; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci dev_dbg(&adapter->dev, "master xfer %d messages:\n", num); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci pstatus = kmalloc(sizeof(*pstatus), GFP_KERNEL); 608c2ecf20Sopenharmony_ci if (!pstatus) 618c2ecf20Sopenharmony_ci return -ENOMEM; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci for (i = 0 ; i < num ; i++) { 648c2ecf20Sopenharmony_ci int cmd = CMD_I2C_IO; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (i == 0) 678c2ecf20Sopenharmony_ci cmd |= CMD_I2C_IO_BEGIN; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (i == num-1) 708c2ecf20Sopenharmony_ci cmd |= CMD_I2C_IO_END; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci pmsg = &msgs[i]; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci dev_dbg(&adapter->dev, 758c2ecf20Sopenharmony_ci " %d: %s (flags %d) %d bytes to 0x%02x\n", 768c2ecf20Sopenharmony_ci i, pmsg->flags & I2C_M_RD ? "read" : "write", 778c2ecf20Sopenharmony_ci pmsg->flags, pmsg->len, pmsg->addr); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* and directly send the message */ 808c2ecf20Sopenharmony_ci if (pmsg->flags & I2C_M_RD) { 818c2ecf20Sopenharmony_ci /* read data */ 828c2ecf20Sopenharmony_ci if (usb_read(adapter, cmd, 838c2ecf20Sopenharmony_ci pmsg->flags, pmsg->addr, 848c2ecf20Sopenharmony_ci pmsg->buf, pmsg->len) != pmsg->len) { 858c2ecf20Sopenharmony_ci dev_err(&adapter->dev, 868c2ecf20Sopenharmony_ci "failure reading data\n"); 878c2ecf20Sopenharmony_ci ret = -EIO; 888c2ecf20Sopenharmony_ci goto out; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci } else { 918c2ecf20Sopenharmony_ci /* write data */ 928c2ecf20Sopenharmony_ci if (usb_write(adapter, cmd, 938c2ecf20Sopenharmony_ci pmsg->flags, pmsg->addr, 948c2ecf20Sopenharmony_ci pmsg->buf, pmsg->len) != pmsg->len) { 958c2ecf20Sopenharmony_ci dev_err(&adapter->dev, 968c2ecf20Sopenharmony_ci "failure writing data\n"); 978c2ecf20Sopenharmony_ci ret = -EIO; 988c2ecf20Sopenharmony_ci goto out; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* read status */ 1038c2ecf20Sopenharmony_ci if (usb_read(adapter, CMD_GET_STATUS, 0, 0, pstatus, 1) != 1) { 1048c2ecf20Sopenharmony_ci dev_err(&adapter->dev, "failure reading status\n"); 1058c2ecf20Sopenharmony_ci ret = -EIO; 1068c2ecf20Sopenharmony_ci goto out; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci dev_dbg(&adapter->dev, " status = %d\n", *pstatus); 1108c2ecf20Sopenharmony_ci if (*pstatus == STATUS_ADDRESS_NAK) { 1118c2ecf20Sopenharmony_ci ret = -ENXIO; 1128c2ecf20Sopenharmony_ci goto out; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci ret = i; 1178c2ecf20Sopenharmony_ciout: 1188c2ecf20Sopenharmony_ci kfree(pstatus); 1198c2ecf20Sopenharmony_ci return ret; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic u32 usb_func(struct i2c_adapter *adapter) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci __le32 *pfunc; 1258c2ecf20Sopenharmony_ci u32 ret; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci pfunc = kmalloc(sizeof(*pfunc), GFP_KERNEL); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* get functionality from adapter */ 1308c2ecf20Sopenharmony_ci if (!pfunc || usb_read(adapter, CMD_GET_FUNC, 0, 0, pfunc, 1318c2ecf20Sopenharmony_ci sizeof(*pfunc)) != sizeof(*pfunc)) { 1328c2ecf20Sopenharmony_ci dev_err(&adapter->dev, "failure reading functionality\n"); 1338c2ecf20Sopenharmony_ci ret = 0; 1348c2ecf20Sopenharmony_ci goto out; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci ret = le32_to_cpup(pfunc); 1388c2ecf20Sopenharmony_ciout: 1398c2ecf20Sopenharmony_ci kfree(pfunc); 1408c2ecf20Sopenharmony_ci return ret; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* This is the actual algorithm we define */ 1448c2ecf20Sopenharmony_cistatic const struct i2c_algorithm usb_algorithm = { 1458c2ecf20Sopenharmony_ci .master_xfer = usb_xfer, 1468c2ecf20Sopenharmony_ci .functionality = usb_func, 1478c2ecf20Sopenharmony_ci}; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/* ----- end of i2c layer ------------------------------------------------ */ 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* ----- begin of usb layer ---------------------------------------------- */ 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* 1548c2ecf20Sopenharmony_ci * Initially the usb i2c interface uses a vid/pid pair donated by 1558c2ecf20Sopenharmony_ci * Future Technology Devices International Ltd., later a pair was 1568c2ecf20Sopenharmony_ci * bought from EZPrototypes 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_cistatic const struct usb_device_id i2c_tiny_usb_table[] = { 1598c2ecf20Sopenharmony_ci { USB_DEVICE(0x0403, 0xc631) }, /* FTDI */ 1608c2ecf20Sopenharmony_ci { USB_DEVICE(0x1c40, 0x0534) }, /* EZPrototypes */ 1618c2ecf20Sopenharmony_ci { } /* Terminating entry */ 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, i2c_tiny_usb_table); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci/* Structure to hold all of our device specific stuff */ 1678c2ecf20Sopenharmony_cistruct i2c_tiny_usb { 1688c2ecf20Sopenharmony_ci struct usb_device *usb_dev; /* the usb device for this device */ 1698c2ecf20Sopenharmony_ci struct usb_interface *interface; /* the interface for this device */ 1708c2ecf20Sopenharmony_ci struct i2c_adapter adapter; /* i2c related things */ 1718c2ecf20Sopenharmony_ci}; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int usb_read(struct i2c_adapter *adapter, int cmd, 1748c2ecf20Sopenharmony_ci int value, int index, void *data, int len) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct i2c_tiny_usb *dev = (struct i2c_tiny_usb *)adapter->algo_data; 1778c2ecf20Sopenharmony_ci void *dmadata = kmalloc(len, GFP_KERNEL); 1788c2ecf20Sopenharmony_ci int ret; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (!dmadata) 1818c2ecf20Sopenharmony_ci return -ENOMEM; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* do control transfer */ 1848c2ecf20Sopenharmony_ci ret = usb_control_msg(dev->usb_dev, usb_rcvctrlpipe(dev->usb_dev, 0), 1858c2ecf20Sopenharmony_ci cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | 1868c2ecf20Sopenharmony_ci USB_DIR_IN, value, index, dmadata, len, 2000); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci memcpy(data, dmadata, len); 1898c2ecf20Sopenharmony_ci kfree(dmadata); 1908c2ecf20Sopenharmony_ci return ret; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int usb_write(struct i2c_adapter *adapter, int cmd, 1948c2ecf20Sopenharmony_ci int value, int index, void *data, int len) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci struct i2c_tiny_usb *dev = (struct i2c_tiny_usb *)adapter->algo_data; 1978c2ecf20Sopenharmony_ci void *dmadata = kmemdup(data, len, GFP_KERNEL); 1988c2ecf20Sopenharmony_ci int ret; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (!dmadata) 2018c2ecf20Sopenharmony_ci return -ENOMEM; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* do control transfer */ 2048c2ecf20Sopenharmony_ci ret = usb_control_msg(dev->usb_dev, usb_sndctrlpipe(dev->usb_dev, 0), 2058c2ecf20Sopenharmony_ci cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 2068c2ecf20Sopenharmony_ci value, index, dmadata, len, 2000); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci kfree(dmadata); 2098c2ecf20Sopenharmony_ci return ret; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic void i2c_tiny_usb_free(struct i2c_tiny_usb *dev) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci usb_put_dev(dev->usb_dev); 2158c2ecf20Sopenharmony_ci kfree(dev); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int i2c_tiny_usb_probe(struct usb_interface *interface, 2198c2ecf20Sopenharmony_ci const struct usb_device_id *id) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct i2c_tiny_usb *dev; 2228c2ecf20Sopenharmony_ci int retval = -ENOMEM; 2238c2ecf20Sopenharmony_ci u16 version; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci dev_dbg(&interface->dev, "probing usb device\n"); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* allocate memory for our device state and initialize it */ 2288c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 2298c2ecf20Sopenharmony_ci if (!dev) 2308c2ecf20Sopenharmony_ci goto error; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci dev->usb_dev = usb_get_dev(interface_to_usbdev(interface)); 2338c2ecf20Sopenharmony_ci dev->interface = interface; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* save our data pointer in this interface device */ 2368c2ecf20Sopenharmony_ci usb_set_intfdata(interface, dev); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci version = le16_to_cpu(dev->usb_dev->descriptor.bcdDevice); 2398c2ecf20Sopenharmony_ci dev_info(&interface->dev, 2408c2ecf20Sopenharmony_ci "version %x.%02x found at bus %03d address %03d\n", 2418c2ecf20Sopenharmony_ci version >> 8, version & 0xff, 2428c2ecf20Sopenharmony_ci dev->usb_dev->bus->busnum, dev->usb_dev->devnum); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* setup i2c adapter description */ 2458c2ecf20Sopenharmony_ci dev->adapter.owner = THIS_MODULE; 2468c2ecf20Sopenharmony_ci dev->adapter.class = I2C_CLASS_HWMON; 2478c2ecf20Sopenharmony_ci dev->adapter.algo = &usb_algorithm; 2488c2ecf20Sopenharmony_ci dev->adapter.algo_data = dev; 2498c2ecf20Sopenharmony_ci snprintf(dev->adapter.name, sizeof(dev->adapter.name), 2508c2ecf20Sopenharmony_ci "i2c-tiny-usb at bus %03d device %03d", 2518c2ecf20Sopenharmony_ci dev->usb_dev->bus->busnum, dev->usb_dev->devnum); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (usb_write(&dev->adapter, CMD_SET_DELAY, delay, 0, NULL, 0) != 0) { 2548c2ecf20Sopenharmony_ci dev_err(&dev->adapter.dev, 2558c2ecf20Sopenharmony_ci "failure setting delay to %dus\n", delay); 2568c2ecf20Sopenharmony_ci retval = -EIO; 2578c2ecf20Sopenharmony_ci goto error; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci dev->adapter.dev.parent = &dev->interface->dev; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* and finally attach to i2c layer */ 2638c2ecf20Sopenharmony_ci i2c_add_adapter(&dev->adapter); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* inform user about successful attachment to i2c layer */ 2668c2ecf20Sopenharmony_ci dev_info(&dev->adapter.dev, "connected i2c-tiny-usb device\n"); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci error: 2718c2ecf20Sopenharmony_ci if (dev) 2728c2ecf20Sopenharmony_ci i2c_tiny_usb_free(dev); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return retval; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic void i2c_tiny_usb_disconnect(struct usb_interface *interface) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct i2c_tiny_usb *dev = usb_get_intfdata(interface); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci i2c_del_adapter(&dev->adapter); 2828c2ecf20Sopenharmony_ci usb_set_intfdata(interface, NULL); 2838c2ecf20Sopenharmony_ci i2c_tiny_usb_free(dev); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci dev_dbg(&interface->dev, "disconnected\n"); 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic struct usb_driver i2c_tiny_usb_driver = { 2898c2ecf20Sopenharmony_ci .name = "i2c-tiny-usb", 2908c2ecf20Sopenharmony_ci .probe = i2c_tiny_usb_probe, 2918c2ecf20Sopenharmony_ci .disconnect = i2c_tiny_usb_disconnect, 2928c2ecf20Sopenharmony_ci .id_table = i2c_tiny_usb_table, 2938c2ecf20Sopenharmony_ci}; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cimodule_usb_driver(i2c_tiny_usb_driver); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci/* ----- end of usb layer ------------------------------------------------ */ 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ciMODULE_AUTHOR("Till Harbaum <Till@Harbaum.org>"); 3008c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("i2c-tiny-usb driver v1.0"); 3018c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 302