| 1 | // SPDX-License-Identifier: GPL-2.0-only |
| 2 | /* |
| 3 | * Intel La Jolla Cove Adapter USB-I2C driver |
| 4 | * |
| 5 | * Copyright (c) 2023, Intel Corporation. |
| 6 | */ |
| 7 | |
| 8 | #include <linux/acpi.h> |
| 9 | #include <linux/auxiliary_bus.h> |
| 10 | #include <linux/bitfield.h> |
| 11 | #include <linux/bits.h> |
| 12 | #include <linux/dev_printk.h> |
| 13 | #include <linux/i2c.h> |
| 14 | #include <linux/module.h> |
| 15 | #include <linux/usb/ljca.h> |
| 16 | |
| 17 | /* I2C init flags */ |
| 18 | #define LJCA_I2C_INIT_FLAG_MODE BIT(0) |
| 19 | #define LJCA_I2C_INIT_FLAG_MODE_POLLING FIELD_PREP(LJCA_I2C_INIT_FLAG_MODE, 0) |
| 20 | #define LJCA_I2C_INIT_FLAG_MODE_INTERRUPT FIELD_PREP(LJCA_I2C_INIT_FLAG_MODE, 1) |
| 21 | |
| 22 | #define LJCA_I2C_INIT_FLAG_ADDR_16BIT BIT(0) |
| 23 | |
| 24 | #define LJCA_I2C_INIT_FLAG_FREQ GENMASK(2, 1) |
| 25 | #define LJCA_I2C_INIT_FLAG_FREQ_100K FIELD_PREP(LJCA_I2C_INIT_FLAG_FREQ, 0) |
| 26 | #define LJCA_I2C_INIT_FLAG_FREQ_400K FIELD_PREP(LJCA_I2C_INIT_FLAG_FREQ, 1) |
| 27 | #define LJCA_I2C_INIT_FLAG_FREQ_1M FIELD_PREP(LJCA_I2C_INIT_FLAG_FREQ, 2) |
| 28 | |
| 29 | #define LJCA_I2C_BUF_SIZE 60u |
| 30 | #define LJCA_I2C_MAX_XFER_SIZE (LJCA_I2C_BUF_SIZE - sizeof(struct ljca_i2c_rw_packet)) |
| 31 | |
| 32 | /* I2C commands */ |
| 33 | enum ljca_i2c_cmd { |
| 34 | LJCA_I2C_INIT = 1, |
| 35 | LJCA_I2C_XFER, |
| 36 | LJCA_I2C_START, |
| 37 | LJCA_I2C_STOP, |
| 38 | LJCA_I2C_READ, |
| 39 | LJCA_I2C_WRITE, |
| 40 | }; |
| 41 | |
| 42 | enum ljca_xfer_type { |
| 43 | LJCA_I2C_WRITE_XFER_TYPE, |
| 44 | LJCA_I2C_READ_XFER_TYPE, |
| 45 | }; |
| 46 | |
| 47 | /* I2C raw commands: Init/Start/Read/Write/Stop */ |
| 48 | struct ljca_i2c_rw_packet { |
| 49 | u8 id; |
| 50 | __le16 len; |
| 51 | u8 data[] __counted_by(len); |
| 52 | } __packed; |
| 53 | |
| 54 | struct ljca_i2c_dev { |
| 55 | struct ljca_client *ljca; |
| 56 | struct ljca_i2c_info *i2c_info; |
| 57 | struct i2c_adapter adap; |
| 58 | |
| 59 | u8 obuf[LJCA_I2C_BUF_SIZE]; |
| 60 | u8 ibuf[LJCA_I2C_BUF_SIZE]; |
| 61 | }; |
| 62 | |
| 63 | static int ljca_i2c_init(struct ljca_i2c_dev *ljca_i2c, u8 id) |
| 64 | { |
| 65 | struct ljca_i2c_rw_packet *w_packet = |
| 66 | (struct ljca_i2c_rw_packet *)ljca_i2c->obuf; |
| 67 | int ret; |
| 68 | |
| 69 | w_packet->id = id; |
| 70 | w_packet->len = cpu_to_le16(sizeof(*w_packet->data)); |
| 71 | w_packet->data[0] = LJCA_I2C_INIT_FLAG_FREQ_400K; |
| 72 | |
| 73 | ret = ljca_transfer(client: ljca_i2c->ljca, cmd: LJCA_I2C_INIT, obuf: (u8 *)w_packet, |
| 74 | struct_size(w_packet, data, 1), NULL, ibuf_len: 0); |
| 75 | |
| 76 | return ret < 0 ? ret : 0; |
| 77 | } |
| 78 | |
| 79 | static int ljca_i2c_start(struct ljca_i2c_dev *ljca_i2c, u8 target_addr, |
| 80 | enum ljca_xfer_type type) |
| 81 | { |
| 82 | struct ljca_i2c_rw_packet *w_packet = |
| 83 | (struct ljca_i2c_rw_packet *)ljca_i2c->obuf; |
| 84 | struct ljca_i2c_rw_packet *r_packet = |
| 85 | (struct ljca_i2c_rw_packet *)ljca_i2c->ibuf; |
| 86 | s16 rp_len; |
| 87 | int ret; |
| 88 | |
| 89 | w_packet->id = ljca_i2c->i2c_info->id; |
| 90 | w_packet->len = cpu_to_le16(sizeof(*w_packet->data)); |
| 91 | w_packet->data[0] = (target_addr << 1) | type; |
| 92 | |
| 93 | ret = ljca_transfer(client: ljca_i2c->ljca, cmd: LJCA_I2C_START, obuf: (u8 *)w_packet, |
| 94 | struct_size(w_packet, data, 1), ibuf: (u8 *)r_packet, |
| 95 | LJCA_I2C_BUF_SIZE); |
| 96 | if (ret < 0 || ret < sizeof(*r_packet)) |
| 97 | return ret < 0 ? ret : -EIO; |
| 98 | |
| 99 | rp_len = le16_to_cpu(r_packet->len); |
| 100 | if (rp_len < 0 || r_packet->id != w_packet->id) { |
| 101 | dev_dbg(&ljca_i2c->adap.dev, |
| 102 | "i2c start failed len: %d id: %d %d\n" , |
| 103 | rp_len, r_packet->id, w_packet->id); |
| 104 | return -EIO; |
| 105 | } |
| 106 | |
| 107 | return 0; |
| 108 | } |
| 109 | |
| 110 | static void ljca_i2c_stop(struct ljca_i2c_dev *ljca_i2c) |
| 111 | { |
| 112 | struct ljca_i2c_rw_packet *w_packet = |
| 113 | (struct ljca_i2c_rw_packet *)ljca_i2c->obuf; |
| 114 | struct ljca_i2c_rw_packet *r_packet = |
| 115 | (struct ljca_i2c_rw_packet *)ljca_i2c->ibuf; |
| 116 | s16 rp_len; |
| 117 | int ret; |
| 118 | |
| 119 | w_packet->id = ljca_i2c->i2c_info->id; |
| 120 | w_packet->len = cpu_to_le16(sizeof(*w_packet->data)); |
| 121 | w_packet->data[0] = 0; |
| 122 | |
| 123 | ret = ljca_transfer(client: ljca_i2c->ljca, cmd: LJCA_I2C_STOP, obuf: (u8 *)w_packet, |
| 124 | struct_size(w_packet, data, 1), ibuf: (u8 *)r_packet, |
| 125 | LJCA_I2C_BUF_SIZE); |
| 126 | if (ret < 0 || ret < sizeof(*r_packet)) { |
| 127 | dev_dbg(&ljca_i2c->adap.dev, |
| 128 | "i2c stop failed ret: %d id: %d\n" , |
| 129 | ret, w_packet->id); |
| 130 | return; |
| 131 | } |
| 132 | |
| 133 | rp_len = le16_to_cpu(r_packet->len); |
| 134 | if (rp_len < 0 || r_packet->id != w_packet->id) |
| 135 | dev_dbg(&ljca_i2c->adap.dev, |
| 136 | "i2c stop failed len: %d id: %d %d\n" , |
| 137 | rp_len, r_packet->id, w_packet->id); |
| 138 | } |
| 139 | |
| 140 | static int ljca_i2c_pure_read(struct ljca_i2c_dev *ljca_i2c, u8 *data, u8 len) |
| 141 | { |
| 142 | struct ljca_i2c_rw_packet *w_packet = |
| 143 | (struct ljca_i2c_rw_packet *)ljca_i2c->obuf; |
| 144 | struct ljca_i2c_rw_packet *r_packet = |
| 145 | (struct ljca_i2c_rw_packet *)ljca_i2c->ibuf; |
| 146 | s16 rp_len; |
| 147 | int ret; |
| 148 | |
| 149 | w_packet->id = ljca_i2c->i2c_info->id; |
| 150 | w_packet->len = cpu_to_le16(len); |
| 151 | w_packet->data[0] = 0; |
| 152 | |
| 153 | ret = ljca_transfer(client: ljca_i2c->ljca, cmd: LJCA_I2C_READ, obuf: (u8 *)w_packet, |
| 154 | struct_size(w_packet, data, 1), ibuf: (u8 *)r_packet, |
| 155 | LJCA_I2C_BUF_SIZE); |
| 156 | if (ret < 0 || ret < sizeof(*r_packet)) |
| 157 | return ret < 0 ? ret : -EIO; |
| 158 | |
| 159 | rp_len = le16_to_cpu(r_packet->len); |
| 160 | if (rp_len != len || r_packet->id != w_packet->id) { |
| 161 | dev_dbg(&ljca_i2c->adap.dev, |
| 162 | "i2c raw read failed len: %d id: %d %d\n" , |
| 163 | rp_len, r_packet->id, w_packet->id); |
| 164 | return -EIO; |
| 165 | } |
| 166 | |
| 167 | memcpy(data, r_packet->data, len); |
| 168 | |
| 169 | return 0; |
| 170 | } |
| 171 | |
| 172 | static int ljca_i2c_read(struct ljca_i2c_dev *ljca_i2c, u8 target_addr, u8 *data, |
| 173 | u8 len) |
| 174 | { |
| 175 | int ret; |
| 176 | |
| 177 | ret = ljca_i2c_start(ljca_i2c, target_addr, type: LJCA_I2C_READ_XFER_TYPE); |
| 178 | if (!ret) |
| 179 | ret = ljca_i2c_pure_read(ljca_i2c, data, len); |
| 180 | |
| 181 | ljca_i2c_stop(ljca_i2c); |
| 182 | |
| 183 | return ret; |
| 184 | } |
| 185 | |
| 186 | static int ljca_i2c_pure_write(struct ljca_i2c_dev *ljca_i2c, u8 *data, u8 len) |
| 187 | { |
| 188 | struct ljca_i2c_rw_packet *w_packet = |
| 189 | (struct ljca_i2c_rw_packet *)ljca_i2c->obuf; |
| 190 | struct ljca_i2c_rw_packet *r_packet = |
| 191 | (struct ljca_i2c_rw_packet *)ljca_i2c->ibuf; |
| 192 | s16 rplen; |
| 193 | int ret; |
| 194 | |
| 195 | w_packet->id = ljca_i2c->i2c_info->id; |
| 196 | w_packet->len = cpu_to_le16(len); |
| 197 | memcpy(w_packet->data, data, len); |
| 198 | |
| 199 | ret = ljca_transfer(client: ljca_i2c->ljca, cmd: LJCA_I2C_WRITE, obuf: (u8 *)w_packet, |
| 200 | struct_size(w_packet, data, len), ibuf: (u8 *)r_packet, |
| 201 | LJCA_I2C_BUF_SIZE); |
| 202 | if (ret < 0 || ret < sizeof(*r_packet)) |
| 203 | return ret < 0 ? ret : -EIO; |
| 204 | |
| 205 | rplen = le16_to_cpu(r_packet->len); |
| 206 | if (rplen != len || r_packet->id != w_packet->id) { |
| 207 | dev_dbg(&ljca_i2c->adap.dev, |
| 208 | "i2c write failed len: %d id: %d/%d\n" , |
| 209 | rplen, r_packet->id, w_packet->id); |
| 210 | return -EIO; |
| 211 | } |
| 212 | |
| 213 | return 0; |
| 214 | } |
| 215 | |
| 216 | static int ljca_i2c_write(struct ljca_i2c_dev *ljca_i2c, u8 target_addr, |
| 217 | u8 *data, u8 len) |
| 218 | { |
| 219 | int ret; |
| 220 | |
| 221 | ret = ljca_i2c_start(ljca_i2c, target_addr, type: LJCA_I2C_WRITE_XFER_TYPE); |
| 222 | if (!ret) |
| 223 | ret = ljca_i2c_pure_write(ljca_i2c, data, len); |
| 224 | |
| 225 | ljca_i2c_stop(ljca_i2c); |
| 226 | |
| 227 | return ret; |
| 228 | } |
| 229 | |
| 230 | static int ljca_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msg, |
| 231 | int num) |
| 232 | { |
| 233 | struct ljca_i2c_dev *ljca_i2c; |
| 234 | struct i2c_msg *cur_msg; |
| 235 | int i, ret; |
| 236 | |
| 237 | ljca_i2c = i2c_get_adapdata(adap: adapter); |
| 238 | if (!ljca_i2c) |
| 239 | return -EINVAL; |
| 240 | |
| 241 | for (i = 0; i < num; i++) { |
| 242 | cur_msg = &msg[i]; |
| 243 | if (cur_msg->flags & I2C_M_RD) |
| 244 | ret = ljca_i2c_read(ljca_i2c, target_addr: cur_msg->addr, |
| 245 | data: cur_msg->buf, len: cur_msg->len); |
| 246 | else |
| 247 | ret = ljca_i2c_write(ljca_i2c, target_addr: cur_msg->addr, |
| 248 | data: cur_msg->buf, len: cur_msg->len); |
| 249 | |
| 250 | if (ret) |
| 251 | return ret; |
| 252 | } |
| 253 | |
| 254 | return num; |
| 255 | } |
| 256 | |
| 257 | static u32 ljca_i2c_func(struct i2c_adapter *adap) |
| 258 | { |
| 259 | return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); |
| 260 | } |
| 261 | |
| 262 | static const struct i2c_adapter_quirks ljca_i2c_quirks = { |
| 263 | .flags = I2C_AQ_NO_ZERO_LEN, |
| 264 | .max_read_len = LJCA_I2C_MAX_XFER_SIZE, |
| 265 | .max_write_len = LJCA_I2C_MAX_XFER_SIZE, |
| 266 | }; |
| 267 | |
| 268 | static const struct i2c_algorithm ljca_i2c_algo = { |
| 269 | .xfer = ljca_i2c_xfer, |
| 270 | .functionality = ljca_i2c_func, |
| 271 | }; |
| 272 | |
| 273 | static int ljca_i2c_probe(struct auxiliary_device *auxdev, |
| 274 | const struct auxiliary_device_id *aux_dev_id) |
| 275 | { |
| 276 | struct ljca_client *ljca = auxiliary_dev_to_ljca_client(auxdev); |
| 277 | struct ljca_i2c_dev *ljca_i2c; |
| 278 | int ret; |
| 279 | |
| 280 | ljca_i2c = devm_kzalloc(dev: &auxdev->dev, size: sizeof(*ljca_i2c), GFP_KERNEL); |
| 281 | if (!ljca_i2c) |
| 282 | return -ENOMEM; |
| 283 | |
| 284 | ljca_i2c->ljca = ljca; |
| 285 | ljca_i2c->i2c_info = dev_get_platdata(dev: &auxdev->dev); |
| 286 | |
| 287 | ljca_i2c->adap.owner = THIS_MODULE; |
| 288 | ljca_i2c->adap.class = I2C_CLASS_HWMON; |
| 289 | ljca_i2c->adap.algo = &ljca_i2c_algo; |
| 290 | ljca_i2c->adap.quirks = &ljca_i2c_quirks; |
| 291 | ljca_i2c->adap.dev.parent = &auxdev->dev; |
| 292 | |
| 293 | snprintf(buf: ljca_i2c->adap.name, size: sizeof(ljca_i2c->adap.name), fmt: "%s-%s-%d" , |
| 294 | dev_name(dev: &auxdev->dev), dev_name(dev: auxdev->dev.parent), |
| 295 | ljca_i2c->i2c_info->id); |
| 296 | |
| 297 | device_set_node(dev: &ljca_i2c->adap.dev, dev_fwnode(&auxdev->dev)); |
| 298 | |
| 299 | i2c_set_adapdata(adap: &ljca_i2c->adap, data: ljca_i2c); |
| 300 | auxiliary_set_drvdata(auxdev, data: ljca_i2c); |
| 301 | |
| 302 | ret = ljca_i2c_init(ljca_i2c, id: ljca_i2c->i2c_info->id); |
| 303 | if (ret) |
| 304 | return dev_err_probe(dev: &auxdev->dev, err: -EIO, |
| 305 | fmt: "i2c init failed id: %d\n" , |
| 306 | ljca_i2c->i2c_info->id); |
| 307 | |
| 308 | ret = devm_i2c_add_adapter(dev: &auxdev->dev, adapter: &ljca_i2c->adap); |
| 309 | if (ret) |
| 310 | return ret; |
| 311 | |
| 312 | if (has_acpi_companion(dev: &ljca_i2c->adap.dev)) |
| 313 | acpi_dev_clear_dependencies(ACPI_COMPANION(&ljca_i2c->adap.dev)); |
| 314 | |
| 315 | return 0; |
| 316 | } |
| 317 | |
| 318 | static void ljca_i2c_remove(struct auxiliary_device *auxdev) |
| 319 | { |
| 320 | struct ljca_i2c_dev *ljca_i2c = auxiliary_get_drvdata(auxdev); |
| 321 | |
| 322 | i2c_del_adapter(adap: &ljca_i2c->adap); |
| 323 | } |
| 324 | |
| 325 | static const struct auxiliary_device_id ljca_i2c_id_table[] = { |
| 326 | { "usb_ljca.ljca-i2c" , 0 }, |
| 327 | { /* sentinel */ } |
| 328 | }; |
| 329 | MODULE_DEVICE_TABLE(auxiliary, ljca_i2c_id_table); |
| 330 | |
| 331 | static struct auxiliary_driver ljca_i2c_driver = { |
| 332 | .probe = ljca_i2c_probe, |
| 333 | .remove = ljca_i2c_remove, |
| 334 | .id_table = ljca_i2c_id_table, |
| 335 | }; |
| 336 | module_auxiliary_driver(ljca_i2c_driver); |
| 337 | |
| 338 | MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>" ); |
| 339 | MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>" ); |
| 340 | MODULE_AUTHOR("Lixu Zhang <lixu.zhang@intel.com>" ); |
| 341 | MODULE_DESCRIPTION("Intel La Jolla Cove Adapter USB-I2C driver" ); |
| 342 | MODULE_LICENSE("GPL" ); |
| 343 | MODULE_IMPORT_NS("LJCA" ); |
| 344 | |