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