linux/drivers/i2c/busses/i2c-cp2615.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * i2c support for Silicon Labs' CP2615 Digital Audio Bridge
   4 *
   5 * (c) 2021, Bence Csókás <bence98@sch.bme.hu>
   6 */
   7
   8#include <linux/errno.h>
   9#include <linux/i2c.h>
  10#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/string.h>
  13#include <linux/usb.h>
  14
  15/** CP2615 I/O Protocol implementation */
  16
  17#define CP2615_VID 0x10c4
  18#define CP2615_PID 0xeac1
  19
  20#define IOP_EP_IN  0x82
  21#define IOP_EP_OUT 0x02
  22#define IOP_IFN 1
  23#define IOP_ALTSETTING 2
  24
  25#define MAX_IOP_SIZE 64
  26#define MAX_IOP_PAYLOAD_SIZE (MAX_IOP_SIZE - 6)
  27#define MAX_I2C_SIZE (MAX_IOP_PAYLOAD_SIZE - 4)
  28
  29enum cp2615_iop_msg_type {
  30        iop_GetAccessoryInfo = 0xD100,
  31        iop_AccessoryInfo = 0xA100,
  32        iop_GetPortConfiguration = 0xD203,
  33        iop_PortConfiguration = 0xA203,
  34        iop_DoI2cTransfer = 0xD400,
  35        iop_I2cTransferResult = 0xA400,
  36        iop_GetSerialState = 0xD501,
  37        iop_SerialState = 0xA501
  38};
  39
  40struct __packed cp2615_iop_msg {
  41        __be16 preamble, length, msg;
  42        u8 data[MAX_IOP_PAYLOAD_SIZE];
  43};
  44
  45#define PART_ID_A01 0x1400
  46#define PART_ID_A02 0x1500
  47
  48struct __packed cp2615_iop_accessory_info {
  49        __be16 part_id, option_id, proto_ver;
  50};
  51
  52struct __packed cp2615_i2c_transfer {
  53        u8 tag, i2caddr, read_len, write_len;
  54        u8 data[MAX_I2C_SIZE];
  55};
  56
  57/* Possible values for struct cp2615_i2c_transfer_result.status */
  58enum cp2615_i2c_status {
  59        /* Writing to the internal EEPROM failed, because it is locked */
  60        CP2615_CFG_LOCKED = -6,
  61        /* read_len or write_len out of range */
  62        CP2615_INVALID_PARAM = -4,
  63        /* I2C slave did not ACK in time */
  64        CP2615_TIMEOUT,
  65        /* I2C bus busy */
  66        CP2615_BUS_BUSY,
  67        /* I2C bus error (ie. device NAK'd the request) */
  68        CP2615_BUS_ERROR,
  69        CP2615_SUCCESS
  70};
  71
  72struct __packed cp2615_i2c_transfer_result {
  73        u8 tag, i2caddr;
  74        s8 status;
  75        u8 read_len;
  76        u8 data[MAX_I2C_SIZE];
  77};
  78
  79static int cp2615_init_iop_msg(struct cp2615_iop_msg *ret, enum cp2615_iop_msg_type msg,
  80                        const void *data, size_t data_len)
  81{
  82        if (data_len > MAX_IOP_PAYLOAD_SIZE)
  83                return -EFBIG;
  84
  85        if (!ret)
  86                return -EINVAL;
  87
  88        ret->preamble = 0x2A2A;
  89        ret->length = htons(data_len + 6);
  90        ret->msg = htons(msg);
  91        if (data && data_len)
  92                memcpy(&ret->data, data, data_len);
  93        return 0;
  94}
  95
  96static int cp2615_init_i2c_msg(struct cp2615_iop_msg *ret, const struct cp2615_i2c_transfer *data)
  97{
  98        return cp2615_init_iop_msg(ret, iop_DoI2cTransfer, data, 4 + data->write_len);
  99}
 100
 101/* Translates status codes to Linux errno's */
 102static int cp2615_check_status(enum cp2615_i2c_status status)
 103{
 104        switch (status) {
 105        case CP2615_SUCCESS:
 106                        return 0;
 107        case CP2615_BUS_ERROR:
 108                return -ENXIO;
 109        case CP2615_BUS_BUSY:
 110                return -EAGAIN;
 111        case CP2615_TIMEOUT:
 112                return -ETIMEDOUT;
 113        case CP2615_INVALID_PARAM:
 114                return -EINVAL;
 115        case CP2615_CFG_LOCKED:
 116                return -EPERM;
 117        }
 118        /* Unknown error code */
 119        return -EPROTO;
 120}
 121
 122/** Driver code */
 123
 124static int
 125cp2615_i2c_send(struct usb_interface *usbif, struct cp2615_i2c_transfer *i2c_w)
 126{
 127        struct cp2615_iop_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
 128        struct usb_device *usbdev = interface_to_usbdev(usbif);
 129        int res = cp2615_init_i2c_msg(msg, i2c_w);
 130
 131        if (!res)
 132                res = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, IOP_EP_OUT),
 133                                   msg, ntohs(msg->length), NULL, 0);
 134        kfree(msg);
 135        return res;
 136}
 137
 138static int
 139cp2615_i2c_recv(struct usb_interface *usbif, unsigned char tag, void *buf)
 140{
 141        struct usb_device *usbdev = interface_to_usbdev(usbif);
 142        struct cp2615_iop_msg *msg;
 143        struct cp2615_i2c_transfer_result *i2c_r;
 144        int res;
 145
 146        msg = kzalloc(sizeof(*msg), GFP_KERNEL);
 147        if (!msg)
 148                return -ENOMEM;
 149
 150        res = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, IOP_EP_IN), msg,
 151                           sizeof(struct cp2615_iop_msg), NULL, 0);
 152        if (res < 0) {
 153                kfree(msg);
 154                return res;
 155        }
 156
 157        i2c_r = (struct cp2615_i2c_transfer_result *)&msg->data;
 158        if (msg->msg != htons(iop_I2cTransferResult) || i2c_r->tag != tag) {
 159                kfree(msg);
 160                return -EIO;
 161        }
 162
 163        res = cp2615_check_status(i2c_r->status);
 164        if (!res)
 165                memcpy(buf, &i2c_r->data, i2c_r->read_len);
 166
 167        kfree(msg);
 168        return res;
 169}
 170
 171/* Checks if the IOP is functional by querying the part's ID */
 172static int cp2615_check_iop(struct usb_interface *usbif)
 173{
 174        struct cp2615_iop_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
 175        struct cp2615_iop_accessory_info *info = (struct cp2615_iop_accessory_info *)&msg->data;
 176        struct usb_device *usbdev = interface_to_usbdev(usbif);
 177        int res = cp2615_init_iop_msg(msg, iop_GetAccessoryInfo, NULL, 0);
 178
 179        if (res)
 180                goto out;
 181
 182        res = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, IOP_EP_OUT),
 183                                   msg, ntohs(msg->length), NULL, 0);
 184        if (res)
 185                goto out;
 186
 187        res = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, IOP_EP_IN),
 188                               msg, sizeof(struct cp2615_iop_msg), NULL, 0);
 189        if (res)
 190                goto out;
 191
 192        if (msg->msg != htons(iop_AccessoryInfo)) {
 193                res = -EIO;
 194                goto out;
 195        }
 196
 197        switch (ntohs(info->part_id)) {
 198        case PART_ID_A01:
 199                dev_dbg(&usbif->dev, "Found A01 part. (WARNING: errata exists!)\n");
 200                break;
 201        case PART_ID_A02:
 202                dev_dbg(&usbif->dev, "Found good A02 part.\n");
 203                break;
 204        default:
 205                dev_warn(&usbif->dev, "Unknown part ID %04X\n", ntohs(info->part_id));
 206        }
 207
 208out:
 209        kfree(msg);
 210        return res;
 211}
 212
 213static int
 214cp2615_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 215{
 216        struct usb_interface *usbif = adap->algo_data;
 217        int i = 0, ret = 0;
 218        struct i2c_msg *msg;
 219        struct cp2615_i2c_transfer i2c_w = {0};
 220
 221        dev_dbg(&usbif->dev, "Doing %d I2C transactions\n", num);
 222
 223        for (; !ret && i < num; i++) {
 224                msg = &msgs[i];
 225
 226                i2c_w.tag = 0xdd;
 227                i2c_w.i2caddr = i2c_8bit_addr_from_msg(msg);
 228                if (msg->flags & I2C_M_RD) {
 229                        i2c_w.read_len = msg->len;
 230                        i2c_w.write_len = 0;
 231                } else {
 232                        i2c_w.read_len = 0;
 233                        i2c_w.write_len = msg->len;
 234                        memcpy(&i2c_w.data, msg->buf, i2c_w.write_len);
 235                }
 236                ret = cp2615_i2c_send(usbif, &i2c_w);
 237                if (ret)
 238                        break;
 239                ret = cp2615_i2c_recv(usbif, i2c_w.tag, msg->buf);
 240        }
 241        if (ret < 0)
 242                return ret;
 243        return i;
 244}
 245
 246static u32
 247cp2615_i2c_func(struct i2c_adapter *adap)
 248{
 249        return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
 250}
 251
 252static const struct i2c_algorithm cp2615_i2c_algo = {
 253        .master_xfer    = cp2615_i2c_master_xfer,
 254        .functionality  = cp2615_i2c_func,
 255};
 256
 257/*
 258 * This chip has some limitations: one is that the USB endpoint
 259 * can only receive 64 bytes/transfer, that leaves 54 bytes for
 260 * the I2C transfer. On top of that, EITHER read_len OR write_len
 261 * may be zero, but not both. If both are non-zero, the adapter
 262 * issues a write followed by a read. And the chip does not
 263 * support repeated START between the write and read phases.
 264 */
 265static struct i2c_adapter_quirks cp2615_i2c_quirks = {
 266        .max_write_len = MAX_I2C_SIZE,
 267        .max_read_len = MAX_I2C_SIZE,
 268        .flags = I2C_AQ_COMB_WRITE_THEN_READ | I2C_AQ_NO_ZERO_LEN | I2C_AQ_NO_REP_START,
 269        .max_comb_1st_msg_len = MAX_I2C_SIZE,
 270        .max_comb_2nd_msg_len = MAX_I2C_SIZE
 271};
 272
 273static void
 274cp2615_i2c_remove(struct usb_interface *usbif)
 275{
 276        struct i2c_adapter *adap = usb_get_intfdata(usbif);
 277
 278        usb_set_intfdata(usbif, NULL);
 279        i2c_del_adapter(adap);
 280}
 281
 282static int
 283cp2615_i2c_probe(struct usb_interface *usbif, const struct usb_device_id *id)
 284{
 285        int ret = 0;
 286        struct i2c_adapter *adap;
 287        struct usb_device *usbdev = interface_to_usbdev(usbif);
 288
 289        ret = usb_set_interface(usbdev, IOP_IFN, IOP_ALTSETTING);
 290        if (ret)
 291                return ret;
 292
 293        ret = cp2615_check_iop(usbif);
 294        if (ret)
 295                return ret;
 296
 297        adap = devm_kzalloc(&usbif->dev, sizeof(struct i2c_adapter), GFP_KERNEL);
 298        if (!adap)
 299                return -ENOMEM;
 300
 301        strncpy(adap->name, usbdev->serial, sizeof(adap->name) - 1);
 302        adap->owner = THIS_MODULE;
 303        adap->dev.parent = &usbif->dev;
 304        adap->dev.of_node = usbif->dev.of_node;
 305        adap->timeout = HZ;
 306        adap->algo = &cp2615_i2c_algo;
 307        adap->quirks = &cp2615_i2c_quirks;
 308        adap->algo_data = usbif;
 309
 310        ret = i2c_add_adapter(adap);
 311        if (ret)
 312                return ret;
 313
 314        usb_set_intfdata(usbif, adap);
 315        return 0;
 316}
 317
 318static const struct usb_device_id id_table[] = {
 319        { USB_DEVICE_INTERFACE_NUMBER(CP2615_VID, CP2615_PID, IOP_IFN) },
 320        { }
 321};
 322
 323MODULE_DEVICE_TABLE(usb, id_table);
 324
 325static struct usb_driver cp2615_i2c_driver = {
 326        .name = "i2c-cp2615",
 327        .probe = cp2615_i2c_probe,
 328        .disconnect = cp2615_i2c_remove,
 329        .id_table = id_table,
 330};
 331
 332module_usb_driver(cp2615_i2c_driver);
 333
 334MODULE_AUTHOR("Bence Csókás <bence98@sch.bme.hu>");
 335MODULE_DESCRIPTION("CP2615 I2C bus driver");
 336MODULE_LICENSE("GPL");
 337