// SPDX-License-Identifier: GPL-2.0-only
/*
* MCP2221A - Microchip USB to I2C Host Protocol Bridge
*
* Copyright (c) 2020, Rishi Gupta <gupt21@gmail.com>
*
* Datasheet: https://ww1.microchip.com/downloads/en/DeviceDoc/20005565B.pdf
*/
#include <linux/module.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/bitfield.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/hid.h>
#include <linux/hidraw.h>
#include <linux/i2c.h>
#include <linux/gpio/driver.h>
#include <linux/iio/iio.h>
#include "hid-ids.h"
/* Commands codes in a raw output report */
enum {
MCP2221_I2C_WR_DATA = 0x90,
MCP2221_I2C_WR_NO_STOP = 0x94,
MCP2221_I2C_RD_DATA = 0x91,
MCP2221_I2C_RD_RPT_START = 0x93,
MCP2221_I2C_GET_DATA = 0x40,
MCP2221_I2C_PARAM_OR_STATUS = 0x10,
MCP2221_I2C_SET_SPEED = 0x20,
MCP2221_I2C_CANCEL = 0x10,
MCP2221_GPIO_SET = 0x50,
MCP2221_GPIO_GET = 0x51,
MCP2221_SET_SRAM_SETTINGS = 0x60,
MCP2221_GET_SRAM_SETTINGS = 0x61,
MCP2221_READ_FLASH_DATA = 0xb0,
};
/* Response codes in a raw input report */
enum {
MCP2221_SUCCESS = 0x00,
MCP2221_I2C_ENG_BUSY = 0x01,
MCP2221_I2C_START_TOUT = 0x12,
MCP2221_I2C_STOP_TOUT = 0x62,
MCP2221_I2C_WRADDRL_TOUT = 0x23,
MCP2221_I2C_WRDATA_TOUT = 0x44,
MCP2221_I2C_WRADDRL_NACK = 0x25,
MCP2221_I2C_MASK_ADDR_NACK = 0x40,
MCP2221_I2C_WRADDRL_SEND = 0x21,
MCP2221_I2C_ADDR_NACK = 0x25,
MCP2221_I2C_READ_PARTIAL = 0x54,
MCP2221_I2C_READ_COMPL = 0x55,
MCP2221_ALT_F_NOT_GPIOV = 0xEE,
MCP2221_ALT_F_NOT_GPIOD = 0xEF,
};
/* MCP GPIO direction encoding */
enum {
MCP2221_DIR_OUT = 0x00,
MCP2221_DIR_IN = 0x01,
};
#define MCP_NGPIO 4
/* MCP GPIO set command layout */
struct mcp_set_gpio {
u8 cmd;
u8 dummy;
struct {
u8 change_value;
u8 value;
u8 change_direction;
u8 direction;
} gpio[MCP_NGPIO];
} __packed;
/* MCP GPIO get command layout */
struct mcp_get_gpio {
u8 cmd;
u8 dummy;
struct {
u8 value;
u8 direction;
} gpio[MCP_NGPIO];
} __packed;
/*
* There is no way to distinguish responses. Therefore next command
* is sent only after response to previous has been received. Mutex
* lock is used for this purpose mainly.
*/
struct mcp2221 {
struct hid_device *hdev;
struct i2c_adapter adapter;
struct mutex lock;
struct completion wait_in_report;
struct delayed_work init_work;
u8 *rxbuf;
u8 txbuf[64];
int rxbuf_idx;
int status;
u8 cur_i2c_clk_div;
struct gpio_chip *gc;
u8 gp_idx;
u8 gpio_dir;
u8 mode[4];
#if IS_REACHABLE(CONFIG_IIO)
struct iio_chan_spec iio_channels[3];
u16 adc_values[3];
u8 adc_scale;
u8 dac_value;
u16 dac_scale;
#endif
};
struct mcp2221_iio {
struct mcp2221 *mcp;
};
/*
* Default i2c bus clock frequency 400 kHz. Modify this if you
* want to set some other frequency (min 50 kHz - max 400 kHz).
*/
static uint i2c_clk_freq = 400;
/* Synchronously send output report to the device */
static int mcp_send_report(struct mcp2221 *mcp,
u8 *out_report, size_t len)
{
u8 *buf;
int ret;
buf = kmemdup(out_report, len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
/* mcp2221 uses interrupt endpoint for out reports */
ret = hid_hw_output_report(mcp->hdev, buf, len);
kfree(buf);
if (ret < 0)
return ret;
return 0;
}
/*
* Send o/p report to the device and wait for i/p report to be
* received from the device. If the device does not respond,
* we timeout.
*/
static int mcp_send_data_req_status(struct mcp2221 *mcp,
u8 *out_report, int len<