.. | .. |
---|
14 | 14 | #include "cmd.h" |
---|
15 | 15 | #include "core.h" |
---|
16 | 16 | #include "i2c.h" |
---|
| 17 | +#include "resources.h" |
---|
17 | 18 | |
---|
18 | 19 | #define MLXSW_I2C_CIR2_BASE 0x72000 |
---|
19 | 20 | #define MLXSW_I2C_CIR_STATUS_OFF 0x18 |
---|
20 | 21 | #define MLXSW_I2C_CIR2_OFF_STATUS (MLXSW_I2C_CIR2_BASE + \ |
---|
21 | 22 | MLXSW_I2C_CIR_STATUS_OFF) |
---|
22 | 23 | #define MLXSW_I2C_OPMOD_SHIFT 12 |
---|
| 24 | +#define MLXSW_I2C_EVENT_BIT_SHIFT 22 |
---|
23 | 25 | #define MLXSW_I2C_GO_BIT_SHIFT 23 |
---|
24 | 26 | #define MLXSW_I2C_CIR_CTRL_STATUS_SHIFT 24 |
---|
| 27 | +#define MLXSW_I2C_EVENT_BIT BIT(MLXSW_I2C_EVENT_BIT_SHIFT) |
---|
25 | 28 | #define MLXSW_I2C_GO_BIT BIT(MLXSW_I2C_GO_BIT_SHIFT) |
---|
26 | 29 | #define MLXSW_I2C_GO_OPMODE BIT(MLXSW_I2C_OPMOD_SHIFT) |
---|
27 | 30 | #define MLXSW_I2C_SET_IMM_CMD (MLXSW_I2C_GO_OPMODE | \ |
---|
.. | .. |
---|
33 | 36 | #define MLXSW_I2C_TLV_HDR_SIZE 0x10 |
---|
34 | 37 | #define MLXSW_I2C_ADDR_WIDTH 4 |
---|
35 | 38 | #define MLXSW_I2C_PUSH_CMD_SIZE (MLXSW_I2C_ADDR_WIDTH + 4) |
---|
| 39 | +#define MLXSW_I2C_SET_EVENT_CMD (MLXSW_I2C_EVENT_BIT) |
---|
| 40 | +#define MLXSW_I2C_PUSH_EVENT_CMD (MLXSW_I2C_GO_BIT | \ |
---|
| 41 | + MLXSW_I2C_SET_EVENT_CMD) |
---|
36 | 42 | #define MLXSW_I2C_READ_SEMA_SIZE 4 |
---|
37 | 43 | #define MLXSW_I2C_PREP_SIZE (MLXSW_I2C_ADDR_WIDTH + 28) |
---|
38 | 44 | #define MLXSW_I2C_MBOX_SIZE 20 |
---|
39 | 45 | #define MLXSW_I2C_MBOX_OUT_PARAM_OFF 12 |
---|
40 | | -#define MLXSW_I2C_MAX_BUFF_SIZE 32 |
---|
41 | 46 | #define MLXSW_I2C_MBOX_OFFSET_BITS 20 |
---|
42 | 47 | #define MLXSW_I2C_MBOX_SIZE_BITS 12 |
---|
43 | 48 | #define MLXSW_I2C_ADDR_BUF_SIZE 4 |
---|
44 | | -#define MLXSW_I2C_BLK_MAX 32 |
---|
| 49 | +#define MLXSW_I2C_BLK_DEF 32 |
---|
| 50 | +#define MLXSW_I2C_BLK_MAX 100 |
---|
45 | 51 | #define MLXSW_I2C_RETRY 5 |
---|
46 | 52 | #define MLXSW_I2C_TIMEOUT_MSECS 5000 |
---|
| 53 | +#define MLXSW_I2C_MAX_DATA_SIZE 256 |
---|
47 | 54 | |
---|
48 | 55 | /** |
---|
49 | 56 | * struct mlxsw_i2c - device private data: |
---|
| 57 | + * @cmd: command attributes; |
---|
50 | 58 | * @cmd.mb_size_in: input mailbox size; |
---|
51 | 59 | * @cmd.mb_off_in: input mailbox offset in register space; |
---|
52 | 60 | * @cmd.mb_size_out: output mailbox size; |
---|
.. | .. |
---|
55 | 63 | * @dev: I2C device; |
---|
56 | 64 | * @core: switch core pointer; |
---|
57 | 65 | * @bus_info: bus info block; |
---|
| 66 | + * @block_size: maximum block size allowed to pass to under layer; |
---|
58 | 67 | */ |
---|
59 | 68 | struct mlxsw_i2c { |
---|
60 | 69 | struct { |
---|
.. | .. |
---|
67 | 76 | struct device *dev; |
---|
68 | 77 | struct mlxsw_core *core; |
---|
69 | 78 | struct mlxsw_bus_info bus_info; |
---|
| 79 | + u16 block_size; |
---|
70 | 80 | }; |
---|
71 | 81 | |
---|
72 | 82 | #define MLXSW_I2C_READ_MSG(_client, _addr_buf, _buf, _len) { \ |
---|
.. | .. |
---|
167 | 177 | return err > 0 ? 0 : err; |
---|
168 | 178 | } |
---|
169 | 179 | |
---|
170 | | -/* Routine posts a command to ASIC though mail box. */ |
---|
| 180 | +/* Routine posts a command to ASIC through mail box. */ |
---|
171 | 181 | static int mlxsw_i2c_write_cmd(struct i2c_client *client, |
---|
172 | 182 | struct mlxsw_i2c *mlxsw_i2c, |
---|
173 | 183 | int immediate) |
---|
.. | .. |
---|
213 | 223 | return 0; |
---|
214 | 224 | } |
---|
215 | 225 | |
---|
| 226 | +/* Routine posts initialization command to ASIC through mail box. */ |
---|
| 227 | +static int |
---|
| 228 | +mlxsw_i2c_write_init_cmd(struct i2c_client *client, |
---|
| 229 | + struct mlxsw_i2c *mlxsw_i2c, u16 opcode, u32 in_mod) |
---|
| 230 | +{ |
---|
| 231 | + __be32 push_cmd_buf[MLXSW_I2C_PUSH_CMD_SIZE / 4] = { |
---|
| 232 | + 0, cpu_to_be32(MLXSW_I2C_PUSH_EVENT_CMD) |
---|
| 233 | + }; |
---|
| 234 | + __be32 prep_cmd_buf[MLXSW_I2C_PREP_SIZE / 4] = { |
---|
| 235 | + 0, 0, 0, 0, 0, 0, |
---|
| 236 | + cpu_to_be32(client->adapter->nr & 0xffff), |
---|
| 237 | + cpu_to_be32(MLXSW_I2C_SET_EVENT_CMD) |
---|
| 238 | + }; |
---|
| 239 | + struct i2c_msg push_cmd = |
---|
| 240 | + MLXSW_I2C_WRITE_MSG(client, push_cmd_buf, |
---|
| 241 | + MLXSW_I2C_PUSH_CMD_SIZE); |
---|
| 242 | + struct i2c_msg prep_cmd = |
---|
| 243 | + MLXSW_I2C_WRITE_MSG(client, prep_cmd_buf, MLXSW_I2C_PREP_SIZE); |
---|
| 244 | + u8 status; |
---|
| 245 | + int err; |
---|
| 246 | + |
---|
| 247 | + push_cmd_buf[1] = cpu_to_be32(MLXSW_I2C_PUSH_EVENT_CMD | opcode); |
---|
| 248 | + prep_cmd_buf[3] = cpu_to_be32(in_mod); |
---|
| 249 | + prep_cmd_buf[7] = cpu_to_be32(MLXSW_I2C_GO_BIT | opcode); |
---|
| 250 | + mlxsw_i2c_set_slave_addr((u8 *)prep_cmd_buf, |
---|
| 251 | + MLXSW_I2C_CIR2_BASE); |
---|
| 252 | + mlxsw_i2c_set_slave_addr((u8 *)push_cmd_buf, |
---|
| 253 | + MLXSW_I2C_CIR2_OFF_STATUS); |
---|
| 254 | + |
---|
| 255 | + /* Prepare Command Interface Register for transaction */ |
---|
| 256 | + err = i2c_transfer(client->adapter, &prep_cmd, 1); |
---|
| 257 | + if (err < 0) |
---|
| 258 | + return err; |
---|
| 259 | + else if (err != 1) |
---|
| 260 | + return -EIO; |
---|
| 261 | + |
---|
| 262 | + /* Write out Command Interface Register GO bit to push transaction */ |
---|
| 263 | + err = i2c_transfer(client->adapter, &push_cmd, 1); |
---|
| 264 | + if (err < 0) |
---|
| 265 | + return err; |
---|
| 266 | + else if (err != 1) |
---|
| 267 | + return -EIO; |
---|
| 268 | + |
---|
| 269 | + /* Wait until go bit is cleared. */ |
---|
| 270 | + err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, &status); |
---|
| 271 | + if (err) { |
---|
| 272 | + dev_err(&client->dev, "HW semaphore is not released"); |
---|
| 273 | + return err; |
---|
| 274 | + } |
---|
| 275 | + |
---|
| 276 | + /* Validate transaction completion status. */ |
---|
| 277 | + if (status) { |
---|
| 278 | + dev_err(&client->dev, "Bad transaction completion status %x\n", |
---|
| 279 | + status); |
---|
| 280 | + return -EIO; |
---|
| 281 | + } |
---|
| 282 | + |
---|
| 283 | + return 0; |
---|
| 284 | +} |
---|
| 285 | + |
---|
216 | 286 | /* Routine obtains mail box offsets from ASIC register space. */ |
---|
217 | 287 | static int mlxsw_i2c_get_mbox(struct i2c_client *client, |
---|
218 | 288 | struct mlxsw_i2c *mlxsw_i2c) |
---|
.. | .. |
---|
248 | 318 | struct i2c_client *client = to_i2c_client(dev); |
---|
249 | 319 | struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client); |
---|
250 | 320 | unsigned long timeout = msecs_to_jiffies(MLXSW_I2C_TIMEOUT_MSECS); |
---|
251 | | - u8 tran_buf[MLXSW_I2C_MAX_BUFF_SIZE + MLXSW_I2C_ADDR_BUF_SIZE]; |
---|
252 | 321 | int off = mlxsw_i2c->cmd.mb_off_in, chunk_size, i, j; |
---|
253 | 322 | unsigned long end; |
---|
| 323 | + u8 *tran_buf; |
---|
254 | 324 | struct i2c_msg write_tran = |
---|
255 | | - MLXSW_I2C_WRITE_MSG(client, tran_buf, MLXSW_I2C_PUSH_CMD_SIZE); |
---|
| 325 | + MLXSW_I2C_WRITE_MSG(client, NULL, MLXSW_I2C_PUSH_CMD_SIZE); |
---|
256 | 326 | int err; |
---|
257 | 327 | |
---|
| 328 | + tran_buf = kmalloc(mlxsw_i2c->block_size + MLXSW_I2C_ADDR_BUF_SIZE, |
---|
| 329 | + GFP_KERNEL); |
---|
| 330 | + if (!tran_buf) |
---|
| 331 | + return -ENOMEM; |
---|
| 332 | + |
---|
| 333 | + write_tran.buf = tran_buf; |
---|
258 | 334 | for (i = 0; i < num; i++) { |
---|
259 | | - chunk_size = (in_mbox_size > MLXSW_I2C_BLK_MAX) ? |
---|
260 | | - MLXSW_I2C_BLK_MAX : in_mbox_size; |
---|
| 335 | + chunk_size = (in_mbox_size > mlxsw_i2c->block_size) ? |
---|
| 336 | + mlxsw_i2c->block_size : in_mbox_size; |
---|
261 | 337 | write_tran.len = MLXSW_I2C_ADDR_WIDTH + chunk_size; |
---|
262 | 338 | mlxsw_i2c_set_slave_addr(tran_buf, off); |
---|
263 | 339 | memcpy(&tran_buf[MLXSW_I2C_ADDR_BUF_SIZE], in_mbox + |
---|
264 | | - MLXSW_I2C_BLK_MAX * i, chunk_size); |
---|
| 340 | + mlxsw_i2c->block_size * i, chunk_size); |
---|
265 | 341 | |
---|
266 | 342 | j = 0; |
---|
267 | 343 | end = jiffies + timeout; |
---|
.. | .. |
---|
275 | 351 | (j++ < MLXSW_I2C_RETRY)); |
---|
276 | 352 | |
---|
277 | 353 | if (err != 1) { |
---|
278 | | - if (!err) |
---|
| 354 | + if (!err) { |
---|
279 | 355 | err = -EIO; |
---|
280 | | - return err; |
---|
| 356 | + goto mlxsw_i2c_write_exit; |
---|
| 357 | + } |
---|
281 | 358 | } |
---|
282 | 359 | |
---|
283 | 360 | off += chunk_size; |
---|
.. | .. |
---|
288 | 365 | err = mlxsw_i2c_write_cmd(client, mlxsw_i2c, 0); |
---|
289 | 366 | if (err) { |
---|
290 | 367 | dev_err(&client->dev, "Could not start transaction"); |
---|
291 | | - return -EIO; |
---|
| 368 | + err = -EIO; |
---|
| 369 | + goto mlxsw_i2c_write_exit; |
---|
292 | 370 | } |
---|
293 | 371 | |
---|
294 | 372 | /* Wait until go bit is cleared. */ |
---|
295 | 373 | err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, p_status); |
---|
296 | 374 | if (err) { |
---|
297 | 375 | dev_err(&client->dev, "HW semaphore is not released"); |
---|
298 | | - return err; |
---|
| 376 | + goto mlxsw_i2c_write_exit; |
---|
299 | 377 | } |
---|
300 | 378 | |
---|
301 | 379 | /* Validate transaction completion status. */ |
---|
302 | 380 | if (*p_status) { |
---|
303 | 381 | dev_err(&client->dev, "Bad transaction completion status %x\n", |
---|
304 | 382 | *p_status); |
---|
305 | | - return -EIO; |
---|
| 383 | + err = -EIO; |
---|
306 | 384 | } |
---|
307 | 385 | |
---|
308 | | - return 0; |
---|
| 386 | +mlxsw_i2c_write_exit: |
---|
| 387 | + kfree(tran_buf); |
---|
| 388 | + return err; |
---|
309 | 389 | } |
---|
310 | 390 | |
---|
311 | 391 | /* Routine executes I2C command. */ |
---|
312 | 392 | static int |
---|
313 | | -mlxsw_i2c_cmd(struct device *dev, size_t in_mbox_size, u8 *in_mbox, |
---|
314 | | - size_t out_mbox_size, u8 *out_mbox, u8 *status) |
---|
| 393 | +mlxsw_i2c_cmd(struct device *dev, u16 opcode, u32 in_mod, size_t in_mbox_size, |
---|
| 394 | + u8 *in_mbox, size_t out_mbox_size, u8 *out_mbox, u8 *status) |
---|
315 | 395 | { |
---|
316 | 396 | struct i2c_client *client = to_i2c_client(dev); |
---|
317 | 397 | struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client); |
---|
.. | .. |
---|
326 | 406 | |
---|
327 | 407 | WARN_ON(in_mbox_size % sizeof(u32) || out_mbox_size % sizeof(u32)); |
---|
328 | 408 | |
---|
329 | | - reg_size = mlxsw_i2c_get_reg_size(in_mbox); |
---|
330 | | - num = reg_size / MLXSW_I2C_BLK_MAX; |
---|
331 | | - if (reg_size % MLXSW_I2C_BLK_MAX) |
---|
332 | | - num++; |
---|
| 409 | + if (in_mbox) { |
---|
| 410 | + reg_size = mlxsw_i2c_get_reg_size(in_mbox); |
---|
| 411 | + num = reg_size / mlxsw_i2c->block_size; |
---|
| 412 | + if (reg_size % mlxsw_i2c->block_size) |
---|
| 413 | + num++; |
---|
333 | 414 | |
---|
334 | | - if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) { |
---|
335 | | - dev_err(&client->dev, "Could not acquire lock"); |
---|
336 | | - return -EINVAL; |
---|
337 | | - } |
---|
| 415 | + if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) { |
---|
| 416 | + dev_err(&client->dev, "Could not acquire lock"); |
---|
| 417 | + return -EINVAL; |
---|
| 418 | + } |
---|
338 | 419 | |
---|
339 | | - err = mlxsw_i2c_write(dev, reg_size, in_mbox, num, status); |
---|
340 | | - if (err) |
---|
341 | | - goto cmd_fail; |
---|
| 420 | + err = mlxsw_i2c_write(dev, reg_size, in_mbox, num, status); |
---|
| 421 | + if (err) |
---|
| 422 | + goto cmd_fail; |
---|
342 | 423 | |
---|
343 | | - /* No out mailbox is case of write transaction. */ |
---|
344 | | - if (!out_mbox) { |
---|
345 | | - mutex_unlock(&mlxsw_i2c->cmd.lock); |
---|
346 | | - return 0; |
---|
| 424 | + /* No out mailbox is case of write transaction. */ |
---|
| 425 | + if (!out_mbox) { |
---|
| 426 | + mutex_unlock(&mlxsw_i2c->cmd.lock); |
---|
| 427 | + return 0; |
---|
| 428 | + } |
---|
| 429 | + } else { |
---|
| 430 | + /* No input mailbox is case of initialization query command. */ |
---|
| 431 | + reg_size = MLXSW_I2C_MAX_DATA_SIZE; |
---|
| 432 | + num = DIV_ROUND_UP(reg_size, mlxsw_i2c->block_size); |
---|
| 433 | + |
---|
| 434 | + if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) { |
---|
| 435 | + dev_err(&client->dev, "Could not acquire lock"); |
---|
| 436 | + return -EINVAL; |
---|
| 437 | + } |
---|
| 438 | + |
---|
| 439 | + err = mlxsw_i2c_write_init_cmd(client, mlxsw_i2c, opcode, |
---|
| 440 | + in_mod); |
---|
| 441 | + if (err) |
---|
| 442 | + goto cmd_fail; |
---|
347 | 443 | } |
---|
348 | 444 | |
---|
349 | 445 | /* Send read transaction to get output mailbox content. */ |
---|
350 | 446 | read_tran[1].buf = out_mbox; |
---|
351 | 447 | for (i = 0; i < num; i++) { |
---|
352 | | - chunk_size = (reg_size > MLXSW_I2C_BLK_MAX) ? |
---|
353 | | - MLXSW_I2C_BLK_MAX : reg_size; |
---|
| 448 | + chunk_size = (reg_size > mlxsw_i2c->block_size) ? |
---|
| 449 | + mlxsw_i2c->block_size : reg_size; |
---|
354 | 450 | read_tran[1].len = chunk_size; |
---|
355 | 451 | mlxsw_i2c_set_slave_addr(tran_buf, off); |
---|
356 | 452 | |
---|
.. | .. |
---|
395 | 491 | { |
---|
396 | 492 | struct mlxsw_i2c *mlxsw_i2c = bus_priv; |
---|
397 | 493 | |
---|
398 | | - return mlxsw_i2c_cmd(mlxsw_i2c->dev, in_mbox_size, in_mbox, |
---|
399 | | - out_mbox_size, out_mbox, status); |
---|
| 494 | + return mlxsw_i2c_cmd(mlxsw_i2c->dev, opcode, in_mod, in_mbox_size, |
---|
| 495 | + in_mbox, out_mbox_size, out_mbox, status); |
---|
400 | 496 | } |
---|
401 | 497 | |
---|
402 | 498 | static bool mlxsw_i2c_skb_transmit_busy(void *bus_priv, |
---|
.. | .. |
---|
414 | 510 | static int |
---|
415 | 511 | mlxsw_i2c_init(void *bus_priv, struct mlxsw_core *mlxsw_core, |
---|
416 | 512 | const struct mlxsw_config_profile *profile, |
---|
417 | | - struct mlxsw_res *resources) |
---|
| 513 | + struct mlxsw_res *res) |
---|
418 | 514 | { |
---|
419 | 515 | struct mlxsw_i2c *mlxsw_i2c = bus_priv; |
---|
| 516 | + char *mbox; |
---|
| 517 | + int err; |
---|
420 | 518 | |
---|
421 | 519 | mlxsw_i2c->core = mlxsw_core; |
---|
422 | 520 | |
---|
423 | | - return 0; |
---|
| 521 | + mbox = mlxsw_cmd_mbox_alloc(); |
---|
| 522 | + if (!mbox) |
---|
| 523 | + return -ENOMEM; |
---|
| 524 | + |
---|
| 525 | + err = mlxsw_cmd_query_fw(mlxsw_core, mbox); |
---|
| 526 | + if (err) |
---|
| 527 | + goto mbox_put; |
---|
| 528 | + |
---|
| 529 | + mlxsw_i2c->bus_info.fw_rev.major = |
---|
| 530 | + mlxsw_cmd_mbox_query_fw_fw_rev_major_get(mbox); |
---|
| 531 | + mlxsw_i2c->bus_info.fw_rev.minor = |
---|
| 532 | + mlxsw_cmd_mbox_query_fw_fw_rev_minor_get(mbox); |
---|
| 533 | + mlxsw_i2c->bus_info.fw_rev.subminor = |
---|
| 534 | + mlxsw_cmd_mbox_query_fw_fw_rev_subminor_get(mbox); |
---|
| 535 | + |
---|
| 536 | + err = mlxsw_core_resources_query(mlxsw_core, mbox, res); |
---|
| 537 | + |
---|
| 538 | +mbox_put: |
---|
| 539 | + mlxsw_cmd_mbox_free(mbox); |
---|
| 540 | + return err; |
---|
424 | 541 | } |
---|
425 | 542 | |
---|
426 | 543 | static void mlxsw_i2c_fini(void *bus_priv) |
---|
.. | .. |
---|
442 | 559 | static int mlxsw_i2c_probe(struct i2c_client *client, |
---|
443 | 560 | const struct i2c_device_id *id) |
---|
444 | 561 | { |
---|
| 562 | + const struct i2c_adapter_quirks *quirks = client->adapter->quirks; |
---|
445 | 563 | struct mlxsw_i2c *mlxsw_i2c; |
---|
446 | 564 | u8 status; |
---|
447 | 565 | int err; |
---|
.. | .. |
---|
449 | 567 | mlxsw_i2c = devm_kzalloc(&client->dev, sizeof(*mlxsw_i2c), GFP_KERNEL); |
---|
450 | 568 | if (!mlxsw_i2c) |
---|
451 | 569 | return -ENOMEM; |
---|
| 570 | + |
---|
| 571 | + if (quirks) { |
---|
| 572 | + if ((quirks->max_read_len && |
---|
| 573 | + quirks->max_read_len < MLXSW_I2C_BLK_DEF) || |
---|
| 574 | + (quirks->max_write_len && |
---|
| 575 | + quirks->max_write_len < MLXSW_I2C_BLK_DEF)) { |
---|
| 576 | + dev_err(&client->dev, "Insufficient transaction buffer length\n"); |
---|
| 577 | + return -EOPNOTSUPP; |
---|
| 578 | + } |
---|
| 579 | + |
---|
| 580 | + mlxsw_i2c->block_size = min_t(u16, MLXSW_I2C_BLK_MAX, |
---|
| 581 | + min_t(u16, quirks->max_read_len, |
---|
| 582 | + quirks->max_write_len)); |
---|
| 583 | + } else { |
---|
| 584 | + mlxsw_i2c->block_size = MLXSW_I2C_BLK_DEF; |
---|
| 585 | + } |
---|
452 | 586 | |
---|
453 | 587 | i2c_set_clientdata(client, mlxsw_i2c); |
---|
454 | 588 | mutex_init(&mlxsw_i2c->cmd.lock); |
---|
.. | .. |
---|
503 | 637 | mlxsw_i2c->bus_info.device_kind = id->name; |
---|
504 | 638 | mlxsw_i2c->bus_info.device_name = client->name; |
---|
505 | 639 | mlxsw_i2c->bus_info.dev = &client->dev; |
---|
| 640 | + mlxsw_i2c->bus_info.low_frequency = true; |
---|
506 | 641 | mlxsw_i2c->dev = &client->dev; |
---|
507 | 642 | |
---|
508 | 643 | err = mlxsw_core_bus_device_register(&mlxsw_i2c->bus_info, |
---|
509 | 644 | &mlxsw_i2c_bus, mlxsw_i2c, false, |
---|
510 | | - NULL); |
---|
| 645 | + NULL, NULL); |
---|
511 | 646 | if (err) { |
---|
512 | 647 | dev_err(&client->dev, "Fail to register core bus\n"); |
---|
513 | 648 | return err; |
---|
.. | .. |
---|
516 | 651 | return 0; |
---|
517 | 652 | |
---|
518 | 653 | errout: |
---|
| 654 | + mutex_destroy(&mlxsw_i2c->cmd.lock); |
---|
519 | 655 | i2c_set_clientdata(client, NULL); |
---|
520 | 656 | |
---|
521 | 657 | return err; |
---|