18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for the Diolan u2c-12 USB-I2C adapter 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2010-2011 Ericsson AB 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Derived from: 88c2ecf20Sopenharmony_ci * i2c-tiny-usb.c 98c2ecf20Sopenharmony_ci * Copyright (C) 2006-2007 Till Harbaum (Till@Harbaum.org) 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/errno.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/types.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/usb.h> 188c2ecf20Sopenharmony_ci#include <linux/i2c.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define DRIVER_NAME "i2c-diolan-u2c" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define USB_VENDOR_ID_DIOLAN 0x0abf 238c2ecf20Sopenharmony_ci#define USB_DEVICE_ID_DIOLAN_U2C 0x3370 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* commands via USB, must match command ids in the firmware */ 278c2ecf20Sopenharmony_ci#define CMD_I2C_READ 0x01 288c2ecf20Sopenharmony_ci#define CMD_I2C_WRITE 0x02 298c2ecf20Sopenharmony_ci#define CMD_I2C_SCAN 0x03 /* Returns list of detected devices */ 308c2ecf20Sopenharmony_ci#define CMD_I2C_RELEASE_SDA 0x04 318c2ecf20Sopenharmony_ci#define CMD_I2C_RELEASE_SCL 0x05 328c2ecf20Sopenharmony_ci#define CMD_I2C_DROP_SDA 0x06 338c2ecf20Sopenharmony_ci#define CMD_I2C_DROP_SCL 0x07 348c2ecf20Sopenharmony_ci#define CMD_I2C_READ_SDA 0x08 358c2ecf20Sopenharmony_ci#define CMD_I2C_READ_SCL 0x09 368c2ecf20Sopenharmony_ci#define CMD_GET_FW_VERSION 0x0a 378c2ecf20Sopenharmony_ci#define CMD_GET_SERIAL 0x0b 388c2ecf20Sopenharmony_ci#define CMD_I2C_START 0x0c 398c2ecf20Sopenharmony_ci#define CMD_I2C_STOP 0x0d 408c2ecf20Sopenharmony_ci#define CMD_I2C_REPEATED_START 0x0e 418c2ecf20Sopenharmony_ci#define CMD_I2C_PUT_BYTE 0x0f 428c2ecf20Sopenharmony_ci#define CMD_I2C_GET_BYTE 0x10 438c2ecf20Sopenharmony_ci#define CMD_I2C_PUT_ACK 0x11 448c2ecf20Sopenharmony_ci#define CMD_I2C_GET_ACK 0x12 458c2ecf20Sopenharmony_ci#define CMD_I2C_PUT_BYTE_ACK 0x13 468c2ecf20Sopenharmony_ci#define CMD_I2C_GET_BYTE_ACK 0x14 478c2ecf20Sopenharmony_ci#define CMD_I2C_SET_SPEED 0x1b 488c2ecf20Sopenharmony_ci#define CMD_I2C_GET_SPEED 0x1c 498c2ecf20Sopenharmony_ci#define CMD_I2C_SET_CLK_SYNC 0x24 508c2ecf20Sopenharmony_ci#define CMD_I2C_GET_CLK_SYNC 0x25 518c2ecf20Sopenharmony_ci#define CMD_I2C_SET_CLK_SYNC_TO 0x26 528c2ecf20Sopenharmony_ci#define CMD_I2C_GET_CLK_SYNC_TO 0x27 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define RESP_OK 0x00 558c2ecf20Sopenharmony_ci#define RESP_FAILED 0x01 568c2ecf20Sopenharmony_ci#define RESP_BAD_MEMADDR 0x04 578c2ecf20Sopenharmony_ci#define RESP_DATA_ERR 0x05 588c2ecf20Sopenharmony_ci#define RESP_NOT_IMPLEMENTED 0x06 598c2ecf20Sopenharmony_ci#define RESP_NACK 0x07 608c2ecf20Sopenharmony_ci#define RESP_TIMEOUT 0x09 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define U2C_I2C_SPEED_FAST 0 /* 400 kHz */ 638c2ecf20Sopenharmony_ci#define U2C_I2C_SPEED_STD 1 /* 100 kHz */ 648c2ecf20Sopenharmony_ci#define U2C_I2C_SPEED_2KHZ 242 /* 2 kHz, minimum speed */ 658c2ecf20Sopenharmony_ci#define U2C_I2C_SPEED(f) ((DIV_ROUND_UP(1000000, (f)) - 10) / 2 + 1) 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define U2C_I2C_FREQ(s) (1000000 / (2 * (s - 1) + 10)) 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define DIOLAN_USB_TIMEOUT 100 /* in ms */ 708c2ecf20Sopenharmony_ci#define DIOLAN_SYNC_TIMEOUT 20 /* in ms */ 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define DIOLAN_OUTBUF_LEN 128 738c2ecf20Sopenharmony_ci#define DIOLAN_FLUSH_LEN (DIOLAN_OUTBUF_LEN - 4) 748c2ecf20Sopenharmony_ci#define DIOLAN_INBUF_LEN 256 /* Maximum supported receive length */ 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* Structure to hold all of our device specific stuff */ 778c2ecf20Sopenharmony_cistruct i2c_diolan_u2c { 788c2ecf20Sopenharmony_ci u8 obuffer[DIOLAN_OUTBUF_LEN]; /* output buffer */ 798c2ecf20Sopenharmony_ci u8 ibuffer[DIOLAN_INBUF_LEN]; /* input buffer */ 808c2ecf20Sopenharmony_ci int ep_in, ep_out; /* Endpoints */ 818c2ecf20Sopenharmony_ci struct usb_device *usb_dev; /* the usb device for this device */ 828c2ecf20Sopenharmony_ci struct usb_interface *interface;/* the interface for this device */ 838c2ecf20Sopenharmony_ci struct i2c_adapter adapter; /* i2c related things */ 848c2ecf20Sopenharmony_ci int olen; /* Output buffer length */ 858c2ecf20Sopenharmony_ci int ocount; /* Number of enqueued messages */ 868c2ecf20Sopenharmony_ci}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic uint frequency = I2C_MAX_STANDARD_MODE_FREQ; /* I2C clock frequency in Hz */ 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cimodule_param(frequency, uint, S_IRUGO | S_IWUSR); 918c2ecf20Sopenharmony_ciMODULE_PARM_DESC(frequency, "I2C clock frequency in hertz"); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* usb layer */ 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* Send command to device, and get response. */ 968c2ecf20Sopenharmony_cistatic int diolan_usb_transfer(struct i2c_diolan_u2c *dev) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci int ret = 0; 998c2ecf20Sopenharmony_ci int actual; 1008c2ecf20Sopenharmony_ci int i; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (!dev->olen || !dev->ocount) 1038c2ecf20Sopenharmony_ci return -EINVAL; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci ret = usb_bulk_msg(dev->usb_dev, 1068c2ecf20Sopenharmony_ci usb_sndbulkpipe(dev->usb_dev, dev->ep_out), 1078c2ecf20Sopenharmony_ci dev->obuffer, dev->olen, &actual, 1088c2ecf20Sopenharmony_ci DIOLAN_USB_TIMEOUT); 1098c2ecf20Sopenharmony_ci if (!ret) { 1108c2ecf20Sopenharmony_ci for (i = 0; i < dev->ocount; i++) { 1118c2ecf20Sopenharmony_ci int tmpret; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci tmpret = usb_bulk_msg(dev->usb_dev, 1148c2ecf20Sopenharmony_ci usb_rcvbulkpipe(dev->usb_dev, 1158c2ecf20Sopenharmony_ci dev->ep_in), 1168c2ecf20Sopenharmony_ci dev->ibuffer, 1178c2ecf20Sopenharmony_ci sizeof(dev->ibuffer), &actual, 1188c2ecf20Sopenharmony_ci DIOLAN_USB_TIMEOUT); 1198c2ecf20Sopenharmony_ci /* 1208c2ecf20Sopenharmony_ci * Stop command processing if a previous command 1218c2ecf20Sopenharmony_ci * returned an error. 1228c2ecf20Sopenharmony_ci * Note that we still need to retrieve all messages. 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci if (ret < 0) 1258c2ecf20Sopenharmony_ci continue; 1268c2ecf20Sopenharmony_ci ret = tmpret; 1278c2ecf20Sopenharmony_ci if (ret == 0 && actual > 0) { 1288c2ecf20Sopenharmony_ci switch (dev->ibuffer[actual - 1]) { 1298c2ecf20Sopenharmony_ci case RESP_NACK: 1308c2ecf20Sopenharmony_ci /* 1318c2ecf20Sopenharmony_ci * Return ENXIO if NACK was received as 1328c2ecf20Sopenharmony_ci * response to the address phase, 1338c2ecf20Sopenharmony_ci * EIO otherwise 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_ci ret = i == 1 ? -ENXIO : -EIO; 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci case RESP_TIMEOUT: 1388c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci case RESP_OK: 1418c2ecf20Sopenharmony_ci /* strip off return code */ 1428c2ecf20Sopenharmony_ci ret = actual - 1; 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci default: 1458c2ecf20Sopenharmony_ci ret = -EIO; 1468c2ecf20Sopenharmony_ci break; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci dev->olen = 0; 1528c2ecf20Sopenharmony_ci dev->ocount = 0; 1538c2ecf20Sopenharmony_ci return ret; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic int diolan_write_cmd(struct i2c_diolan_u2c *dev, bool flush) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci if (flush || dev->olen >= DIOLAN_FLUSH_LEN) 1598c2ecf20Sopenharmony_ci return diolan_usb_transfer(dev); 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci/* Send command (no data) */ 1648c2ecf20Sopenharmony_cistatic int diolan_usb_cmd(struct i2c_diolan_u2c *dev, u8 command, bool flush) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci dev->obuffer[dev->olen++] = command; 1678c2ecf20Sopenharmony_ci dev->ocount++; 1688c2ecf20Sopenharmony_ci return diolan_write_cmd(dev, flush); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci/* Send command with one byte of data */ 1728c2ecf20Sopenharmony_cistatic int diolan_usb_cmd_data(struct i2c_diolan_u2c *dev, u8 command, u8 data, 1738c2ecf20Sopenharmony_ci bool flush) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci dev->obuffer[dev->olen++] = command; 1768c2ecf20Sopenharmony_ci dev->obuffer[dev->olen++] = data; 1778c2ecf20Sopenharmony_ci dev->ocount++; 1788c2ecf20Sopenharmony_ci return diolan_write_cmd(dev, flush); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* Send command with two bytes of data */ 1828c2ecf20Sopenharmony_cistatic int diolan_usb_cmd_data2(struct i2c_diolan_u2c *dev, u8 command, u8 d1, 1838c2ecf20Sopenharmony_ci u8 d2, bool flush) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci dev->obuffer[dev->olen++] = command; 1868c2ecf20Sopenharmony_ci dev->obuffer[dev->olen++] = d1; 1878c2ecf20Sopenharmony_ci dev->obuffer[dev->olen++] = d2; 1888c2ecf20Sopenharmony_ci dev->ocount++; 1898c2ecf20Sopenharmony_ci return diolan_write_cmd(dev, flush); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci/* 1938c2ecf20Sopenharmony_ci * Flush input queue. 1948c2ecf20Sopenharmony_ci * If we don't do this at startup and the controller has queued up 1958c2ecf20Sopenharmony_ci * messages which were not retrieved, it will stop responding 1968c2ecf20Sopenharmony_ci * at some point. 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_cistatic void diolan_flush_input(struct i2c_diolan_u2c *dev) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci int i; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 2038c2ecf20Sopenharmony_ci int actual = 0; 2048c2ecf20Sopenharmony_ci int ret; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci ret = usb_bulk_msg(dev->usb_dev, 2078c2ecf20Sopenharmony_ci usb_rcvbulkpipe(dev->usb_dev, dev->ep_in), 2088c2ecf20Sopenharmony_ci dev->ibuffer, sizeof(dev->ibuffer), &actual, 2098c2ecf20Sopenharmony_ci DIOLAN_USB_TIMEOUT); 2108c2ecf20Sopenharmony_ci if (ret < 0 || actual == 0) 2118c2ecf20Sopenharmony_ci break; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci if (i == 10) 2148c2ecf20Sopenharmony_ci dev_err(&dev->interface->dev, "Failed to flush input buffer\n"); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int diolan_i2c_start(struct i2c_diolan_u2c *dev) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci return diolan_usb_cmd(dev, CMD_I2C_START, false); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic int diolan_i2c_repeated_start(struct i2c_diolan_u2c *dev) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci return diolan_usb_cmd(dev, CMD_I2C_REPEATED_START, false); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic int diolan_i2c_stop(struct i2c_diolan_u2c *dev) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci return diolan_usb_cmd(dev, CMD_I2C_STOP, true); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic int diolan_i2c_get_byte_ack(struct i2c_diolan_u2c *dev, bool ack, 2338c2ecf20Sopenharmony_ci u8 *byte) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci int ret; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci ret = diolan_usb_cmd_data(dev, CMD_I2C_GET_BYTE_ACK, ack, true); 2388c2ecf20Sopenharmony_ci if (ret > 0) 2398c2ecf20Sopenharmony_ci *byte = dev->ibuffer[0]; 2408c2ecf20Sopenharmony_ci else if (ret == 0) 2418c2ecf20Sopenharmony_ci ret = -EIO; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci return ret; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic int diolan_i2c_put_byte_ack(struct i2c_diolan_u2c *dev, u8 byte) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci return diolan_usb_cmd_data(dev, CMD_I2C_PUT_BYTE_ACK, byte, false); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic int diolan_set_speed(struct i2c_diolan_u2c *dev, u8 speed) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci return diolan_usb_cmd_data(dev, CMD_I2C_SET_SPEED, speed, true); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci/* Enable or disable clock synchronization (stretching) */ 2578c2ecf20Sopenharmony_cistatic int diolan_set_clock_synch(struct i2c_diolan_u2c *dev, bool enable) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci return diolan_usb_cmd_data(dev, CMD_I2C_SET_CLK_SYNC, enable, true); 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci/* Set clock synchronization timeout in ms */ 2638c2ecf20Sopenharmony_cistatic int diolan_set_clock_synch_timeout(struct i2c_diolan_u2c *dev, int ms) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci int to_val = ms * 10; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return diolan_usb_cmd_data2(dev, CMD_I2C_SET_CLK_SYNC_TO, 2688c2ecf20Sopenharmony_ci to_val & 0xff, (to_val >> 8) & 0xff, true); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic void diolan_fw_version(struct i2c_diolan_u2c *dev) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci int ret; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci ret = diolan_usb_cmd(dev, CMD_GET_FW_VERSION, true); 2768c2ecf20Sopenharmony_ci if (ret >= 2) 2778c2ecf20Sopenharmony_ci dev_info(&dev->interface->dev, 2788c2ecf20Sopenharmony_ci "Diolan U2C firmware version %u.%u\n", 2798c2ecf20Sopenharmony_ci (unsigned int)dev->ibuffer[0], 2808c2ecf20Sopenharmony_ci (unsigned int)dev->ibuffer[1]); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic void diolan_get_serial(struct i2c_diolan_u2c *dev) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci int ret; 2868c2ecf20Sopenharmony_ci u32 serial; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ret = diolan_usb_cmd(dev, CMD_GET_SERIAL, true); 2898c2ecf20Sopenharmony_ci if (ret >= 4) { 2908c2ecf20Sopenharmony_ci serial = le32_to_cpu(*(u32 *)dev->ibuffer); 2918c2ecf20Sopenharmony_ci dev_info(&dev->interface->dev, 2928c2ecf20Sopenharmony_ci "Diolan U2C serial number %u\n", serial); 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic int diolan_init(struct i2c_diolan_u2c *dev) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci int speed, ret; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (frequency >= 2 * I2C_MAX_STANDARD_MODE_FREQ) { 3018c2ecf20Sopenharmony_ci speed = U2C_I2C_SPEED_FAST; 3028c2ecf20Sopenharmony_ci frequency = I2C_MAX_FAST_MODE_FREQ; 3038c2ecf20Sopenharmony_ci } else if (frequency >= I2C_MAX_STANDARD_MODE_FREQ || frequency == 0) { 3048c2ecf20Sopenharmony_ci speed = U2C_I2C_SPEED_STD; 3058c2ecf20Sopenharmony_ci frequency = I2C_MAX_STANDARD_MODE_FREQ; 3068c2ecf20Sopenharmony_ci } else { 3078c2ecf20Sopenharmony_ci speed = U2C_I2C_SPEED(frequency); 3088c2ecf20Sopenharmony_ci if (speed > U2C_I2C_SPEED_2KHZ) 3098c2ecf20Sopenharmony_ci speed = U2C_I2C_SPEED_2KHZ; 3108c2ecf20Sopenharmony_ci frequency = U2C_I2C_FREQ(speed); 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci dev_info(&dev->interface->dev, 3148c2ecf20Sopenharmony_ci "Diolan U2C at USB bus %03d address %03d speed %d Hz\n", 3158c2ecf20Sopenharmony_ci dev->usb_dev->bus->busnum, dev->usb_dev->devnum, frequency); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci diolan_flush_input(dev); 3188c2ecf20Sopenharmony_ci diolan_fw_version(dev); 3198c2ecf20Sopenharmony_ci diolan_get_serial(dev); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* Set I2C speed */ 3228c2ecf20Sopenharmony_ci ret = diolan_set_speed(dev, speed); 3238c2ecf20Sopenharmony_ci if (ret < 0) 3248c2ecf20Sopenharmony_ci return ret; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* Configure I2C clock synchronization */ 3278c2ecf20Sopenharmony_ci ret = diolan_set_clock_synch(dev, speed != U2C_I2C_SPEED_FAST); 3288c2ecf20Sopenharmony_ci if (ret < 0) 3298c2ecf20Sopenharmony_ci return ret; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (speed != U2C_I2C_SPEED_FAST) 3328c2ecf20Sopenharmony_ci ret = diolan_set_clock_synch_timeout(dev, DIOLAN_SYNC_TIMEOUT); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci return ret; 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci/* i2c layer */ 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic int diolan_usb_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, 3408c2ecf20Sopenharmony_ci int num) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci struct i2c_diolan_u2c *dev = i2c_get_adapdata(adapter); 3438c2ecf20Sopenharmony_ci struct i2c_msg *pmsg; 3448c2ecf20Sopenharmony_ci int i, j; 3458c2ecf20Sopenharmony_ci int ret, sret; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci ret = diolan_i2c_start(dev); 3488c2ecf20Sopenharmony_ci if (ret < 0) 3498c2ecf20Sopenharmony_ci return ret; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 3528c2ecf20Sopenharmony_ci pmsg = &msgs[i]; 3538c2ecf20Sopenharmony_ci if (i) { 3548c2ecf20Sopenharmony_ci ret = diolan_i2c_repeated_start(dev); 3558c2ecf20Sopenharmony_ci if (ret < 0) 3568c2ecf20Sopenharmony_ci goto abort; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci ret = diolan_i2c_put_byte_ack(dev, 3598c2ecf20Sopenharmony_ci i2c_8bit_addr_from_msg(pmsg)); 3608c2ecf20Sopenharmony_ci if (ret < 0) 3618c2ecf20Sopenharmony_ci goto abort; 3628c2ecf20Sopenharmony_ci if (pmsg->flags & I2C_M_RD) { 3638c2ecf20Sopenharmony_ci for (j = 0; j < pmsg->len; j++) { 3648c2ecf20Sopenharmony_ci u8 byte; 3658c2ecf20Sopenharmony_ci bool ack = j < pmsg->len - 1; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* 3688c2ecf20Sopenharmony_ci * Don't send NACK if this is the first byte 3698c2ecf20Sopenharmony_ci * of a SMBUS_BLOCK message. 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_ci if (j == 0 && (pmsg->flags & I2C_M_RECV_LEN)) 3728c2ecf20Sopenharmony_ci ack = true; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci ret = diolan_i2c_get_byte_ack(dev, ack, &byte); 3758c2ecf20Sopenharmony_ci if (ret < 0) 3768c2ecf20Sopenharmony_ci goto abort; 3778c2ecf20Sopenharmony_ci /* 3788c2ecf20Sopenharmony_ci * Adjust count if first received byte is length 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_ci if (j == 0 && (pmsg->flags & I2C_M_RECV_LEN)) { 3818c2ecf20Sopenharmony_ci if (byte == 0 3828c2ecf20Sopenharmony_ci || byte > I2C_SMBUS_BLOCK_MAX) { 3838c2ecf20Sopenharmony_ci ret = -EPROTO; 3848c2ecf20Sopenharmony_ci goto abort; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci pmsg->len += byte; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci pmsg->buf[j] = byte; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci } else { 3918c2ecf20Sopenharmony_ci for (j = 0; j < pmsg->len; j++) { 3928c2ecf20Sopenharmony_ci ret = diolan_i2c_put_byte_ack(dev, 3938c2ecf20Sopenharmony_ci pmsg->buf[j]); 3948c2ecf20Sopenharmony_ci if (ret < 0) 3958c2ecf20Sopenharmony_ci goto abort; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci ret = num; 4008c2ecf20Sopenharmony_ciabort: 4018c2ecf20Sopenharmony_ci sret = diolan_i2c_stop(dev); 4028c2ecf20Sopenharmony_ci if (sret < 0 && ret >= 0) 4038c2ecf20Sopenharmony_ci ret = sret; 4048c2ecf20Sopenharmony_ci return ret; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci/* 4088c2ecf20Sopenharmony_ci * Return list of supported functionality. 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_cistatic u32 diolan_usb_func(struct i2c_adapter *a) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | 4138c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL; 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic const struct i2c_algorithm diolan_usb_algorithm = { 4178c2ecf20Sopenharmony_ci .master_xfer = diolan_usb_xfer, 4188c2ecf20Sopenharmony_ci .functionality = diolan_usb_func, 4198c2ecf20Sopenharmony_ci}; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci/* device layer */ 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic const struct usb_device_id diolan_u2c_table[] = { 4248c2ecf20Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_DIOLAN, USB_DEVICE_ID_DIOLAN_U2C) }, 4258c2ecf20Sopenharmony_ci { } 4268c2ecf20Sopenharmony_ci}; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, diolan_u2c_table); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic void diolan_u2c_free(struct i2c_diolan_u2c *dev) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci usb_put_dev(dev->usb_dev); 4338c2ecf20Sopenharmony_ci kfree(dev); 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic int diolan_u2c_probe(struct usb_interface *interface, 4378c2ecf20Sopenharmony_ci const struct usb_device_id *id) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci struct usb_host_interface *hostif = interface->cur_altsetting; 4408c2ecf20Sopenharmony_ci struct i2c_diolan_u2c *dev; 4418c2ecf20Sopenharmony_ci int ret; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (hostif->desc.bInterfaceNumber != 0 4448c2ecf20Sopenharmony_ci || hostif->desc.bNumEndpoints < 2) 4458c2ecf20Sopenharmony_ci return -ENODEV; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci /* allocate memory for our device state and initialize it */ 4488c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 4498c2ecf20Sopenharmony_ci if (dev == NULL) { 4508c2ecf20Sopenharmony_ci ret = -ENOMEM; 4518c2ecf20Sopenharmony_ci goto error; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci dev->ep_out = hostif->endpoint[0].desc.bEndpointAddress; 4548c2ecf20Sopenharmony_ci dev->ep_in = hostif->endpoint[1].desc.bEndpointAddress; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci dev->usb_dev = usb_get_dev(interface_to_usbdev(interface)); 4578c2ecf20Sopenharmony_ci dev->interface = interface; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* save our data pointer in this interface device */ 4608c2ecf20Sopenharmony_ci usb_set_intfdata(interface, dev); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* setup i2c adapter description */ 4638c2ecf20Sopenharmony_ci dev->adapter.owner = THIS_MODULE; 4648c2ecf20Sopenharmony_ci dev->adapter.class = I2C_CLASS_HWMON; 4658c2ecf20Sopenharmony_ci dev->adapter.algo = &diolan_usb_algorithm; 4668c2ecf20Sopenharmony_ci i2c_set_adapdata(&dev->adapter, dev); 4678c2ecf20Sopenharmony_ci snprintf(dev->adapter.name, sizeof(dev->adapter.name), 4688c2ecf20Sopenharmony_ci DRIVER_NAME " at bus %03d device %03d", 4698c2ecf20Sopenharmony_ci dev->usb_dev->bus->busnum, dev->usb_dev->devnum); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci dev->adapter.dev.parent = &dev->interface->dev; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* initialize diolan i2c interface */ 4748c2ecf20Sopenharmony_ci ret = diolan_init(dev); 4758c2ecf20Sopenharmony_ci if (ret < 0) { 4768c2ecf20Sopenharmony_ci dev_err(&interface->dev, "failed to initialize adapter\n"); 4778c2ecf20Sopenharmony_ci goto error_free; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci /* and finally attach to i2c layer */ 4818c2ecf20Sopenharmony_ci ret = i2c_add_adapter(&dev->adapter); 4828c2ecf20Sopenharmony_ci if (ret < 0) 4838c2ecf20Sopenharmony_ci goto error_free; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci dev_dbg(&interface->dev, "connected " DRIVER_NAME "\n"); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci return 0; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cierror_free: 4908c2ecf20Sopenharmony_ci usb_set_intfdata(interface, NULL); 4918c2ecf20Sopenharmony_ci diolan_u2c_free(dev); 4928c2ecf20Sopenharmony_cierror: 4938c2ecf20Sopenharmony_ci return ret; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic void diolan_u2c_disconnect(struct usb_interface *interface) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct i2c_diolan_u2c *dev = usb_get_intfdata(interface); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci i2c_del_adapter(&dev->adapter); 5018c2ecf20Sopenharmony_ci usb_set_intfdata(interface, NULL); 5028c2ecf20Sopenharmony_ci diolan_u2c_free(dev); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci dev_dbg(&interface->dev, "disconnected\n"); 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic struct usb_driver diolan_u2c_driver = { 5088c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 5098c2ecf20Sopenharmony_ci .probe = diolan_u2c_probe, 5108c2ecf20Sopenharmony_ci .disconnect = diolan_u2c_disconnect, 5118c2ecf20Sopenharmony_ci .id_table = diolan_u2c_table, 5128c2ecf20Sopenharmony_ci}; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cimodule_usb_driver(diolan_u2c_driver); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); 5178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_NAME " driver"); 5188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 519