// SPDX-License-Identifier: GPL-2.0-only
/*
* hid-ft260.c - FTDI FT260 USB HID to I2C host bridge
*
* Copyright (c) 2021, Michael Zaidman <michaelz@xsightlabs.com>
*
* Data Sheet:
* https://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT260.pdf
*/
#include "hid-ids.h"
#include <linux/hidraw.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/usb.h>
#ifdef DEBUG
static int ft260_debug = 1;
#else
static int ft260_debug;
#endif
module_param_named(debug, ft260_debug, int, 0600);
MODULE_PARM_DESC(debug, "Toggle FT260 debugging messages");
#define ft260_dbg(format, arg...) \
do { \
if (ft260_debug) \
pr_info("%s: " format, __func__, ##arg); \
} while (0)
#define FT260_REPORT_MAX_LENGTH (64)
#define FT260_I2C_DATA_REPORT_ID(len) (FT260_I2C_REPORT_MIN + (len - 1) / 4)
#define FT260_WAKEUP_NEEDED_AFTER_MS (4800) /* 5s minus 200ms margin */
/*
* The ft260 input report format defines 62 bytes for the data payload, but
* when requested 62 bytes, the controller returns 60 and 2 in separate input
* reports. To achieve better performance with the multi-report read data
* transfers, we set the maximum read payload length to a multiple of 60.
* With a 100 kHz I2C clock, one 240 bytes read takes about 1/27 second,
* which is excessive; On the other hand, some higher layer drivers like at24
* or optoe limit the i2c reads to 128 bytes. To not block other drivers out
* of I2C for potentially troublesome amounts of time, we select the maximum
* read payload length to be 180 bytes.
*/
#define FT260_RD_DATA_MAX (180)
#define FT260_WR_DATA_MAX (60)
/*
* Device interface configuration.
* The FT260 has 2 interfaces that are controlled by DCNF0 and DCNF1 pins.
* First implementes USB HID to I2C bridge function and
* second - USB HID to UART bridge function.
*/
enum {
FT260_MODE_ALL = 0x00,
FT260_MODE_I2C = 0x01,
FT260_MODE_UART = 0x02,
FT260_MODE_BOTH = 0x03,
};
/* Control pipe */
enum {
FT260_GET_RQST_TYPE = 0xA1,
FT260_GET_REPORT = 0x01,
FT260_SET_RQST_TYPE = 0x21,
FT260_SET_REPORT = 0x09,
FT260_FEATURE = 0x03,
};
/* Report IDs / Feature In */
enum {
FT260_CHIP_VERSION = 0xA0,
FT260_SYSTEM_SETTINGS = 0xA1,
FT260_I2C_STATUS = 0xC0,
FT260_I2C_READ_REQ = 0xC2,
FT260_I2C_REPORT_MIN = 0xD0,
FT260_I2C_REPORT_MAX = 0xDE,
FT260_GPIO = 0xB0,
FT260_UART_INTERRUPT_STATUS = 0xB1,
FT260_UART_STATUS = 0xE0,
FT260_UART_RI_DCD_STATUS = 0xE1,
FT260_UART_REPORT = 0xF0,
};
/* Feature Out */
enum {
FT260_SET_CLOCK = 0x01,
FT260_SET_I2C_MODE = 0x02,
FT260_SET_UART_MODE = 0x03,
FT260_ENABLE_INTERRUPT = 0x05,
FT260_SELECT_GPIO2_FUNC = 0x06,
FT260_ENABLE_UART_DCD_RI = 0x07,
FT260_SELECT_GPIOA_FUNC = 0x08,
FT260_SELECT_GPIOG_FUNC = 0x09,
FT260_SET_INTERRUPT_TRIGGER = 0x0A,
FT260_SET_SUSPEND_OUT_POLAR = 0x0B,
FT260_ENABLE_UART_RI_WAKEUP = 0x0C,
FT260_SET_UART_RI_WAKEUP_CFG = 0x0D,
FT260_SET_I2C_RESET = 0x20,
FT260_SET_I2C_CLOCK_SPEED = 0x22,
FT260_SET_UART_RESET = 0x40,
FT260_SET_UART_CONFIG = 0x41,
FT260_SET_UART_BAUD_RATE = 0x42,
FT260_SET_UART_DATA_BIT = 0x43,
FT260_SET_UART_PARITY = 0x44,
FT260_SET_UART_STOP_BIT = 0x45,
FT260_SET_UART_BREAKING = 0x46,
FT260_SET_UART_XON_XOFF = 0x49,
};
/* Response codes in I2C status report */
enum {
FT260_I2C_STATUS_SUCCESS = 0x00,
FT260_I2C_STATUS_CTRL_BUSY = 0x01,
FT260_I2C_STATUS_ERROR = 0x02,
FT260_I2C_STATUS_ADDR_NO_ACK = 0x04,
FT260_I2C_STATUS_DATA_NO_ACK = 0x08,
FT260_I2C_STATUS_ARBITR_LOST = 0x10,
FT260_I2C_STATUS_CTRL_IDLE = 0x20,
FT260_I2C_STATUS_BUS_BUSY = 0x40,
};
/* I2C Conditions flags */
enum {
FT260_FLAG_NONE = 0x00,
FT260_FLAG_START = 0x02,
FT260_FLAG_START_REPEATED = 0x03,
FT260_FLAG_STOP = 0x04,
FT260_FLAG_START_STOP = 0x06,
FT260_FLAG_START_STOP_REPEATED = 0x07,
};
#define FT260_SET_REQUEST_VALUE(report_id) ((FT260_FEATURE << 8) | report_id)
/* Feature In reports */
struct ft260_get_chip_version_report {
u8 report; /* FT260_CHIP_VERSION */
u8 chip_code[4]; /* FTDI chip identification code */
u8 reserved[8];
} __packed;
struct ft260_get_system_status_report {
u8 report; /* FT260_SYSTEM_SETTINGS */
u8 chip_mode; /* DCNF0 and DCNF1 status, bits 0-1 */
u8 clock_ctl; /* 0 - 12MHz, 1 - 24MHz, 2 - 48MHz */
u8 suspend_status; /* 0 - not suspended, 1 - suspended */
u8 pwren_status; /* 0 - FT260 is not ready, 1 - ready */
u8 i2c_enable; /* 0 - disabled, 1 - enabled */
u8 uart_mode; /* 0 - OFF; 1 - RTS_CTS, 2