18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (c) 2016-2018 Mellanox Technologies. All rights reserved */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/err.h> 58c2ecf20Sopenharmony_ci#include <linux/i2c.h> 68c2ecf20Sopenharmony_ci#include <linux/init.h> 78c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/mutex.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "cmd.h" 158c2ecf20Sopenharmony_ci#include "core.h" 168c2ecf20Sopenharmony_ci#include "i2c.h" 178c2ecf20Sopenharmony_ci#include "resources.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define MLXSW_I2C_CIR2_BASE 0x72000 208c2ecf20Sopenharmony_ci#define MLXSW_I2C_CIR_STATUS_OFF 0x18 218c2ecf20Sopenharmony_ci#define MLXSW_I2C_CIR2_OFF_STATUS (MLXSW_I2C_CIR2_BASE + \ 228c2ecf20Sopenharmony_ci MLXSW_I2C_CIR_STATUS_OFF) 238c2ecf20Sopenharmony_ci#define MLXSW_I2C_OPMOD_SHIFT 12 248c2ecf20Sopenharmony_ci#define MLXSW_I2C_EVENT_BIT_SHIFT 22 258c2ecf20Sopenharmony_ci#define MLXSW_I2C_GO_BIT_SHIFT 23 268c2ecf20Sopenharmony_ci#define MLXSW_I2C_CIR_CTRL_STATUS_SHIFT 24 278c2ecf20Sopenharmony_ci#define MLXSW_I2C_EVENT_BIT BIT(MLXSW_I2C_EVENT_BIT_SHIFT) 288c2ecf20Sopenharmony_ci#define MLXSW_I2C_GO_BIT BIT(MLXSW_I2C_GO_BIT_SHIFT) 298c2ecf20Sopenharmony_ci#define MLXSW_I2C_GO_OPMODE BIT(MLXSW_I2C_OPMOD_SHIFT) 308c2ecf20Sopenharmony_ci#define MLXSW_I2C_SET_IMM_CMD (MLXSW_I2C_GO_OPMODE | \ 318c2ecf20Sopenharmony_ci MLXSW_CMD_OPCODE_QUERY_FW) 328c2ecf20Sopenharmony_ci#define MLXSW_I2C_PUSH_IMM_CMD (MLXSW_I2C_GO_BIT | \ 338c2ecf20Sopenharmony_ci MLXSW_I2C_SET_IMM_CMD) 348c2ecf20Sopenharmony_ci#define MLXSW_I2C_SET_CMD (MLXSW_CMD_OPCODE_ACCESS_REG) 358c2ecf20Sopenharmony_ci#define MLXSW_I2C_PUSH_CMD (MLXSW_I2C_GO_BIT | MLXSW_I2C_SET_CMD) 368c2ecf20Sopenharmony_ci#define MLXSW_I2C_TLV_HDR_SIZE 0x10 378c2ecf20Sopenharmony_ci#define MLXSW_I2C_ADDR_WIDTH 4 388c2ecf20Sopenharmony_ci#define MLXSW_I2C_PUSH_CMD_SIZE (MLXSW_I2C_ADDR_WIDTH + 4) 398c2ecf20Sopenharmony_ci#define MLXSW_I2C_SET_EVENT_CMD (MLXSW_I2C_EVENT_BIT) 408c2ecf20Sopenharmony_ci#define MLXSW_I2C_PUSH_EVENT_CMD (MLXSW_I2C_GO_BIT | \ 418c2ecf20Sopenharmony_ci MLXSW_I2C_SET_EVENT_CMD) 428c2ecf20Sopenharmony_ci#define MLXSW_I2C_READ_SEMA_SIZE 4 438c2ecf20Sopenharmony_ci#define MLXSW_I2C_PREP_SIZE (MLXSW_I2C_ADDR_WIDTH + 28) 448c2ecf20Sopenharmony_ci#define MLXSW_I2C_MBOX_SIZE 20 458c2ecf20Sopenharmony_ci#define MLXSW_I2C_MBOX_OUT_PARAM_OFF 12 468c2ecf20Sopenharmony_ci#define MLXSW_I2C_MBOX_OFFSET_BITS 20 478c2ecf20Sopenharmony_ci#define MLXSW_I2C_MBOX_SIZE_BITS 12 488c2ecf20Sopenharmony_ci#define MLXSW_I2C_ADDR_BUF_SIZE 4 498c2ecf20Sopenharmony_ci#define MLXSW_I2C_BLK_DEF 32 508c2ecf20Sopenharmony_ci#define MLXSW_I2C_BLK_MAX 100 518c2ecf20Sopenharmony_ci#define MLXSW_I2C_RETRY 5 528c2ecf20Sopenharmony_ci#define MLXSW_I2C_TIMEOUT_MSECS 5000 538c2ecf20Sopenharmony_ci#define MLXSW_I2C_MAX_DATA_SIZE 256 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/** 568c2ecf20Sopenharmony_ci * struct mlxsw_i2c - device private data: 578c2ecf20Sopenharmony_ci * @cmd: command attributes; 588c2ecf20Sopenharmony_ci * @cmd.mb_size_in: input mailbox size; 598c2ecf20Sopenharmony_ci * @cmd.mb_off_in: input mailbox offset in register space; 608c2ecf20Sopenharmony_ci * @cmd.mb_size_out: output mailbox size; 618c2ecf20Sopenharmony_ci * @cmd.mb_off_out: output mailbox offset in register space; 628c2ecf20Sopenharmony_ci * @cmd.lock: command execution lock; 638c2ecf20Sopenharmony_ci * @dev: I2C device; 648c2ecf20Sopenharmony_ci * @core: switch core pointer; 658c2ecf20Sopenharmony_ci * @bus_info: bus info block; 668c2ecf20Sopenharmony_ci * @block_size: maximum block size allowed to pass to under layer; 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_cistruct mlxsw_i2c { 698c2ecf20Sopenharmony_ci struct { 708c2ecf20Sopenharmony_ci u32 mb_size_in; 718c2ecf20Sopenharmony_ci u32 mb_off_in; 728c2ecf20Sopenharmony_ci u32 mb_size_out; 738c2ecf20Sopenharmony_ci u32 mb_off_out; 748c2ecf20Sopenharmony_ci struct mutex lock; 758c2ecf20Sopenharmony_ci } cmd; 768c2ecf20Sopenharmony_ci struct device *dev; 778c2ecf20Sopenharmony_ci struct mlxsw_core *core; 788c2ecf20Sopenharmony_ci struct mlxsw_bus_info bus_info; 798c2ecf20Sopenharmony_ci u16 block_size; 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#define MLXSW_I2C_READ_MSG(_client, _addr_buf, _buf, _len) { \ 838c2ecf20Sopenharmony_ci { .addr = (_client)->addr, \ 848c2ecf20Sopenharmony_ci .buf = (_addr_buf), \ 858c2ecf20Sopenharmony_ci .len = MLXSW_I2C_ADDR_BUF_SIZE, \ 868c2ecf20Sopenharmony_ci .flags = 0 }, \ 878c2ecf20Sopenharmony_ci { .addr = (_client)->addr, \ 888c2ecf20Sopenharmony_ci .buf = (_buf), \ 898c2ecf20Sopenharmony_ci .len = (_len), \ 908c2ecf20Sopenharmony_ci .flags = I2C_M_RD } } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#define MLXSW_I2C_WRITE_MSG(_client, _buf, _len) \ 938c2ecf20Sopenharmony_ci { .addr = (_client)->addr, \ 948c2ecf20Sopenharmony_ci .buf = (u8 *)(_buf), \ 958c2ecf20Sopenharmony_ci .len = (_len), \ 968c2ecf20Sopenharmony_ci .flags = 0 } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* Routine converts in and out mail boxes offset and size. */ 998c2ecf20Sopenharmony_cistatic inline void 1008c2ecf20Sopenharmony_cimlxsw_i2c_convert_mbox(struct mlxsw_i2c *mlxsw_i2c, u8 *buf) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci u32 tmp; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* Local in/out mailboxes: 20 bits for offset, 12 for size */ 1058c2ecf20Sopenharmony_ci tmp = be32_to_cpup((__be32 *) buf); 1068c2ecf20Sopenharmony_ci mlxsw_i2c->cmd.mb_off_in = tmp & 1078c2ecf20Sopenharmony_ci GENMASK(MLXSW_I2C_MBOX_OFFSET_BITS - 1, 0); 1088c2ecf20Sopenharmony_ci mlxsw_i2c->cmd.mb_size_in = (tmp & GENMASK(31, 1098c2ecf20Sopenharmony_ci MLXSW_I2C_MBOX_OFFSET_BITS)) >> 1108c2ecf20Sopenharmony_ci MLXSW_I2C_MBOX_OFFSET_BITS; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci tmp = be32_to_cpup((__be32 *) (buf + MLXSW_I2C_ADDR_WIDTH)); 1138c2ecf20Sopenharmony_ci mlxsw_i2c->cmd.mb_off_out = tmp & 1148c2ecf20Sopenharmony_ci GENMASK(MLXSW_I2C_MBOX_OFFSET_BITS - 1, 0); 1158c2ecf20Sopenharmony_ci mlxsw_i2c->cmd.mb_size_out = (tmp & GENMASK(31, 1168c2ecf20Sopenharmony_ci MLXSW_I2C_MBOX_OFFSET_BITS)) >> 1178c2ecf20Sopenharmony_ci MLXSW_I2C_MBOX_OFFSET_BITS; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/* Routine obtains register size from mail box buffer. */ 1218c2ecf20Sopenharmony_cistatic inline int mlxsw_i2c_get_reg_size(u8 *in_mbox) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci u16 tmp = be16_to_cpup((__be16 *) (in_mbox + MLXSW_I2C_TLV_HDR_SIZE)); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return (tmp & 0x7ff) * 4 + MLXSW_I2C_TLV_HDR_SIZE; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* Routine sets I2C device internal offset in the transaction buffer. */ 1298c2ecf20Sopenharmony_cistatic inline void mlxsw_i2c_set_slave_addr(u8 *buf, u32 off) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci __be32 *val = (__be32 *) buf; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci *val = htonl(off); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/* Routine waits until go bit is cleared. */ 1378c2ecf20Sopenharmony_cistatic int mlxsw_i2c_wait_go_bit(struct i2c_client *client, 1388c2ecf20Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c, u8 *p_status) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci u8 addr_buf[MLXSW_I2C_ADDR_BUF_SIZE]; 1418c2ecf20Sopenharmony_ci u8 buf[MLXSW_I2C_READ_SEMA_SIZE]; 1428c2ecf20Sopenharmony_ci int len = MLXSW_I2C_READ_SEMA_SIZE; 1438c2ecf20Sopenharmony_ci struct i2c_msg read_sema[] = 1448c2ecf20Sopenharmony_ci MLXSW_I2C_READ_MSG(client, addr_buf, buf, len); 1458c2ecf20Sopenharmony_ci bool wait_done = false; 1468c2ecf20Sopenharmony_ci unsigned long end; 1478c2ecf20Sopenharmony_ci int i = 0, err; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci mlxsw_i2c_set_slave_addr(addr_buf, MLXSW_I2C_CIR2_OFF_STATUS); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci end = jiffies + msecs_to_jiffies(MLXSW_I2C_TIMEOUT_MSECS); 1528c2ecf20Sopenharmony_ci do { 1538c2ecf20Sopenharmony_ci u32 ctrl; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci err = i2c_transfer(client->adapter, read_sema, 1568c2ecf20Sopenharmony_ci ARRAY_SIZE(read_sema)); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci ctrl = be32_to_cpu(*(__be32 *) buf); 1598c2ecf20Sopenharmony_ci if (err == ARRAY_SIZE(read_sema)) { 1608c2ecf20Sopenharmony_ci if (!(ctrl & MLXSW_I2C_GO_BIT)) { 1618c2ecf20Sopenharmony_ci wait_done = true; 1628c2ecf20Sopenharmony_ci *p_status = ctrl >> 1638c2ecf20Sopenharmony_ci MLXSW_I2C_CIR_CTRL_STATUS_SHIFT; 1648c2ecf20Sopenharmony_ci break; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci cond_resched(); 1688c2ecf20Sopenharmony_ci } while ((time_before(jiffies, end)) || (i++ < MLXSW_I2C_RETRY)); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (wait_done) { 1718c2ecf20Sopenharmony_ci if (*p_status) 1728c2ecf20Sopenharmony_ci err = -EIO; 1738c2ecf20Sopenharmony_ci } else { 1748c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return err > 0 ? 0 : err; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/* Routine posts a command to ASIC through mail box. */ 1818c2ecf20Sopenharmony_cistatic int mlxsw_i2c_write_cmd(struct i2c_client *client, 1828c2ecf20Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c, 1838c2ecf20Sopenharmony_ci int immediate) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci __be32 push_cmd_buf[MLXSW_I2C_PUSH_CMD_SIZE / 4] = { 1868c2ecf20Sopenharmony_ci 0, cpu_to_be32(MLXSW_I2C_PUSH_IMM_CMD) 1878c2ecf20Sopenharmony_ci }; 1888c2ecf20Sopenharmony_ci __be32 prep_cmd_buf[MLXSW_I2C_PREP_SIZE / 4] = { 1898c2ecf20Sopenharmony_ci 0, 0, 0, 0, 0, 0, 1908c2ecf20Sopenharmony_ci cpu_to_be32(client->adapter->nr & 0xffff), 1918c2ecf20Sopenharmony_ci cpu_to_be32(MLXSW_I2C_SET_IMM_CMD) 1928c2ecf20Sopenharmony_ci }; 1938c2ecf20Sopenharmony_ci struct i2c_msg push_cmd = 1948c2ecf20Sopenharmony_ci MLXSW_I2C_WRITE_MSG(client, push_cmd_buf, 1958c2ecf20Sopenharmony_ci MLXSW_I2C_PUSH_CMD_SIZE); 1968c2ecf20Sopenharmony_ci struct i2c_msg prep_cmd = 1978c2ecf20Sopenharmony_ci MLXSW_I2C_WRITE_MSG(client, prep_cmd_buf, MLXSW_I2C_PREP_SIZE); 1988c2ecf20Sopenharmony_ci int err; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (!immediate) { 2018c2ecf20Sopenharmony_ci push_cmd_buf[1] = cpu_to_be32(MLXSW_I2C_PUSH_CMD); 2028c2ecf20Sopenharmony_ci prep_cmd_buf[7] = cpu_to_be32(MLXSW_I2C_SET_CMD); 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci mlxsw_i2c_set_slave_addr((u8 *)prep_cmd_buf, 2058c2ecf20Sopenharmony_ci MLXSW_I2C_CIR2_BASE); 2068c2ecf20Sopenharmony_ci mlxsw_i2c_set_slave_addr((u8 *)push_cmd_buf, 2078c2ecf20Sopenharmony_ci MLXSW_I2C_CIR2_OFF_STATUS); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* Prepare Command Interface Register for transaction */ 2108c2ecf20Sopenharmony_ci err = i2c_transfer(client->adapter, &prep_cmd, 1); 2118c2ecf20Sopenharmony_ci if (err < 0) 2128c2ecf20Sopenharmony_ci return err; 2138c2ecf20Sopenharmony_ci else if (err != 1) 2148c2ecf20Sopenharmony_ci return -EIO; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* Write out Command Interface Register GO bit to push transaction */ 2178c2ecf20Sopenharmony_ci err = i2c_transfer(client->adapter, &push_cmd, 1); 2188c2ecf20Sopenharmony_ci if (err < 0) 2198c2ecf20Sopenharmony_ci return err; 2208c2ecf20Sopenharmony_ci else if (err != 1) 2218c2ecf20Sopenharmony_ci return -EIO; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci/* Routine posts initialization command to ASIC through mail box. */ 2278c2ecf20Sopenharmony_cistatic int 2288c2ecf20Sopenharmony_cimlxsw_i2c_write_init_cmd(struct i2c_client *client, 2298c2ecf20Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c, u16 opcode, u32 in_mod) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci __be32 push_cmd_buf[MLXSW_I2C_PUSH_CMD_SIZE / 4] = { 2328c2ecf20Sopenharmony_ci 0, cpu_to_be32(MLXSW_I2C_PUSH_EVENT_CMD) 2338c2ecf20Sopenharmony_ci }; 2348c2ecf20Sopenharmony_ci __be32 prep_cmd_buf[MLXSW_I2C_PREP_SIZE / 4] = { 2358c2ecf20Sopenharmony_ci 0, 0, 0, 0, 0, 0, 2368c2ecf20Sopenharmony_ci cpu_to_be32(client->adapter->nr & 0xffff), 2378c2ecf20Sopenharmony_ci cpu_to_be32(MLXSW_I2C_SET_EVENT_CMD) 2388c2ecf20Sopenharmony_ci }; 2398c2ecf20Sopenharmony_ci struct i2c_msg push_cmd = 2408c2ecf20Sopenharmony_ci MLXSW_I2C_WRITE_MSG(client, push_cmd_buf, 2418c2ecf20Sopenharmony_ci MLXSW_I2C_PUSH_CMD_SIZE); 2428c2ecf20Sopenharmony_ci struct i2c_msg prep_cmd = 2438c2ecf20Sopenharmony_ci MLXSW_I2C_WRITE_MSG(client, prep_cmd_buf, MLXSW_I2C_PREP_SIZE); 2448c2ecf20Sopenharmony_ci u8 status; 2458c2ecf20Sopenharmony_ci int err; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci push_cmd_buf[1] = cpu_to_be32(MLXSW_I2C_PUSH_EVENT_CMD | opcode); 2488c2ecf20Sopenharmony_ci prep_cmd_buf[3] = cpu_to_be32(in_mod); 2498c2ecf20Sopenharmony_ci prep_cmd_buf[7] = cpu_to_be32(MLXSW_I2C_GO_BIT | opcode); 2508c2ecf20Sopenharmony_ci mlxsw_i2c_set_slave_addr((u8 *)prep_cmd_buf, 2518c2ecf20Sopenharmony_ci MLXSW_I2C_CIR2_BASE); 2528c2ecf20Sopenharmony_ci mlxsw_i2c_set_slave_addr((u8 *)push_cmd_buf, 2538c2ecf20Sopenharmony_ci MLXSW_I2C_CIR2_OFF_STATUS); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* Prepare Command Interface Register for transaction */ 2568c2ecf20Sopenharmony_ci err = i2c_transfer(client->adapter, &prep_cmd, 1); 2578c2ecf20Sopenharmony_ci if (err < 0) 2588c2ecf20Sopenharmony_ci return err; 2598c2ecf20Sopenharmony_ci else if (err != 1) 2608c2ecf20Sopenharmony_ci return -EIO; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* Write out Command Interface Register GO bit to push transaction */ 2638c2ecf20Sopenharmony_ci err = i2c_transfer(client->adapter, &push_cmd, 1); 2648c2ecf20Sopenharmony_ci if (err < 0) 2658c2ecf20Sopenharmony_ci return err; 2668c2ecf20Sopenharmony_ci else if (err != 1) 2678c2ecf20Sopenharmony_ci return -EIO; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* Wait until go bit is cleared. */ 2708c2ecf20Sopenharmony_ci err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, &status); 2718c2ecf20Sopenharmony_ci if (err) { 2728c2ecf20Sopenharmony_ci dev_err(&client->dev, "HW semaphore is not released"); 2738c2ecf20Sopenharmony_ci return err; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* Validate transaction completion status. */ 2778c2ecf20Sopenharmony_ci if (status) { 2788c2ecf20Sopenharmony_ci dev_err(&client->dev, "Bad transaction completion status %x\n", 2798c2ecf20Sopenharmony_ci status); 2808c2ecf20Sopenharmony_ci return -EIO; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci/* Routine obtains mail box offsets from ASIC register space. */ 2878c2ecf20Sopenharmony_cistatic int mlxsw_i2c_get_mbox(struct i2c_client *client, 2888c2ecf20Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci u8 addr_buf[MLXSW_I2C_ADDR_BUF_SIZE]; 2918c2ecf20Sopenharmony_ci u8 buf[MLXSW_I2C_MBOX_SIZE]; 2928c2ecf20Sopenharmony_ci struct i2c_msg mbox_cmd[] = 2938c2ecf20Sopenharmony_ci MLXSW_I2C_READ_MSG(client, addr_buf, buf, MLXSW_I2C_MBOX_SIZE); 2948c2ecf20Sopenharmony_ci int err; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* Read mail boxes offsets. */ 2978c2ecf20Sopenharmony_ci mlxsw_i2c_set_slave_addr(addr_buf, MLXSW_I2C_CIR2_BASE); 2988c2ecf20Sopenharmony_ci err = i2c_transfer(client->adapter, mbox_cmd, 2); 2998c2ecf20Sopenharmony_ci if (err != 2) { 3008c2ecf20Sopenharmony_ci dev_err(&client->dev, "Could not obtain mail boxes\n"); 3018c2ecf20Sopenharmony_ci if (!err) 3028c2ecf20Sopenharmony_ci return -EIO; 3038c2ecf20Sopenharmony_ci else 3048c2ecf20Sopenharmony_ci return err; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* Convert mail boxes. */ 3088c2ecf20Sopenharmony_ci mlxsw_i2c_convert_mbox(mlxsw_i2c, &buf[MLXSW_I2C_MBOX_OUT_PARAM_OFF]); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci return err; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci/* Routine sends I2C write transaction to ASIC device. */ 3148c2ecf20Sopenharmony_cistatic int 3158c2ecf20Sopenharmony_cimlxsw_i2c_write(struct device *dev, size_t in_mbox_size, u8 *in_mbox, int num, 3168c2ecf20Sopenharmony_ci u8 *p_status) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 3198c2ecf20Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client); 3208c2ecf20Sopenharmony_ci unsigned long timeout = msecs_to_jiffies(MLXSW_I2C_TIMEOUT_MSECS); 3218c2ecf20Sopenharmony_ci int off = mlxsw_i2c->cmd.mb_off_in, chunk_size, i, j; 3228c2ecf20Sopenharmony_ci unsigned long end; 3238c2ecf20Sopenharmony_ci u8 *tran_buf; 3248c2ecf20Sopenharmony_ci struct i2c_msg write_tran = 3258c2ecf20Sopenharmony_ci MLXSW_I2C_WRITE_MSG(client, NULL, MLXSW_I2C_PUSH_CMD_SIZE); 3268c2ecf20Sopenharmony_ci int err; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci tran_buf = kmalloc(mlxsw_i2c->block_size + MLXSW_I2C_ADDR_BUF_SIZE, 3298c2ecf20Sopenharmony_ci GFP_KERNEL); 3308c2ecf20Sopenharmony_ci if (!tran_buf) 3318c2ecf20Sopenharmony_ci return -ENOMEM; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci write_tran.buf = tran_buf; 3348c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 3358c2ecf20Sopenharmony_ci chunk_size = (in_mbox_size > mlxsw_i2c->block_size) ? 3368c2ecf20Sopenharmony_ci mlxsw_i2c->block_size : in_mbox_size; 3378c2ecf20Sopenharmony_ci write_tran.len = MLXSW_I2C_ADDR_WIDTH + chunk_size; 3388c2ecf20Sopenharmony_ci mlxsw_i2c_set_slave_addr(tran_buf, off); 3398c2ecf20Sopenharmony_ci memcpy(&tran_buf[MLXSW_I2C_ADDR_BUF_SIZE], in_mbox + 3408c2ecf20Sopenharmony_ci mlxsw_i2c->block_size * i, chunk_size); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci j = 0; 3438c2ecf20Sopenharmony_ci end = jiffies + timeout; 3448c2ecf20Sopenharmony_ci do { 3458c2ecf20Sopenharmony_ci err = i2c_transfer(client->adapter, &write_tran, 1); 3468c2ecf20Sopenharmony_ci if (err == 1) 3478c2ecf20Sopenharmony_ci break; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci cond_resched(); 3508c2ecf20Sopenharmony_ci } while ((time_before(jiffies, end)) || 3518c2ecf20Sopenharmony_ci (j++ < MLXSW_I2C_RETRY)); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (err != 1) { 3548c2ecf20Sopenharmony_ci if (!err) { 3558c2ecf20Sopenharmony_ci err = -EIO; 3568c2ecf20Sopenharmony_ci goto mlxsw_i2c_write_exit; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci off += chunk_size; 3618c2ecf20Sopenharmony_ci in_mbox_size -= chunk_size; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* Prepare and write out Command Interface Register for transaction. */ 3658c2ecf20Sopenharmony_ci err = mlxsw_i2c_write_cmd(client, mlxsw_i2c, 0); 3668c2ecf20Sopenharmony_ci if (err) { 3678c2ecf20Sopenharmony_ci dev_err(&client->dev, "Could not start transaction"); 3688c2ecf20Sopenharmony_ci err = -EIO; 3698c2ecf20Sopenharmony_ci goto mlxsw_i2c_write_exit; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* Wait until go bit is cleared. */ 3738c2ecf20Sopenharmony_ci err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, p_status); 3748c2ecf20Sopenharmony_ci if (err) { 3758c2ecf20Sopenharmony_ci dev_err(&client->dev, "HW semaphore is not released"); 3768c2ecf20Sopenharmony_ci goto mlxsw_i2c_write_exit; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* Validate transaction completion status. */ 3808c2ecf20Sopenharmony_ci if (*p_status) { 3818c2ecf20Sopenharmony_ci dev_err(&client->dev, "Bad transaction completion status %x\n", 3828c2ecf20Sopenharmony_ci *p_status); 3838c2ecf20Sopenharmony_ci err = -EIO; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cimlxsw_i2c_write_exit: 3878c2ecf20Sopenharmony_ci kfree(tran_buf); 3888c2ecf20Sopenharmony_ci return err; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci/* Routine executes I2C command. */ 3928c2ecf20Sopenharmony_cistatic int 3938c2ecf20Sopenharmony_cimlxsw_i2c_cmd(struct device *dev, u16 opcode, u32 in_mod, size_t in_mbox_size, 3948c2ecf20Sopenharmony_ci u8 *in_mbox, size_t out_mbox_size, u8 *out_mbox, u8 *status) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 3978c2ecf20Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client); 3988c2ecf20Sopenharmony_ci unsigned long timeout = msecs_to_jiffies(MLXSW_I2C_TIMEOUT_MSECS); 3998c2ecf20Sopenharmony_ci u8 tran_buf[MLXSW_I2C_ADDR_BUF_SIZE]; 4008c2ecf20Sopenharmony_ci int num, chunk_size, reg_size, i, j; 4018c2ecf20Sopenharmony_ci int off = mlxsw_i2c->cmd.mb_off_out; 4028c2ecf20Sopenharmony_ci unsigned long end; 4038c2ecf20Sopenharmony_ci struct i2c_msg read_tran[] = 4048c2ecf20Sopenharmony_ci MLXSW_I2C_READ_MSG(client, tran_buf, NULL, 0); 4058c2ecf20Sopenharmony_ci int err; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci WARN_ON(in_mbox_size % sizeof(u32) || out_mbox_size % sizeof(u32)); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (in_mbox) { 4108c2ecf20Sopenharmony_ci reg_size = mlxsw_i2c_get_reg_size(in_mbox); 4118c2ecf20Sopenharmony_ci num = reg_size / mlxsw_i2c->block_size; 4128c2ecf20Sopenharmony_ci if (reg_size % mlxsw_i2c->block_size) 4138c2ecf20Sopenharmony_ci num++; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) { 4168c2ecf20Sopenharmony_ci dev_err(&client->dev, "Could not acquire lock"); 4178c2ecf20Sopenharmony_ci return -EINVAL; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci err = mlxsw_i2c_write(dev, reg_size, in_mbox, num, status); 4218c2ecf20Sopenharmony_ci if (err) 4228c2ecf20Sopenharmony_ci goto cmd_fail; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* No out mailbox is case of write transaction. */ 4258c2ecf20Sopenharmony_ci if (!out_mbox) { 4268c2ecf20Sopenharmony_ci mutex_unlock(&mlxsw_i2c->cmd.lock); 4278c2ecf20Sopenharmony_ci return 0; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci } else { 4308c2ecf20Sopenharmony_ci /* No input mailbox is case of initialization query command. */ 4318c2ecf20Sopenharmony_ci reg_size = MLXSW_I2C_MAX_DATA_SIZE; 4328c2ecf20Sopenharmony_ci num = DIV_ROUND_UP(reg_size, mlxsw_i2c->block_size); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) { 4358c2ecf20Sopenharmony_ci dev_err(&client->dev, "Could not acquire lock"); 4368c2ecf20Sopenharmony_ci return -EINVAL; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci err = mlxsw_i2c_write_init_cmd(client, mlxsw_i2c, opcode, 4408c2ecf20Sopenharmony_ci in_mod); 4418c2ecf20Sopenharmony_ci if (err) 4428c2ecf20Sopenharmony_ci goto cmd_fail; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* Send read transaction to get output mailbox content. */ 4468c2ecf20Sopenharmony_ci read_tran[1].buf = out_mbox; 4478c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 4488c2ecf20Sopenharmony_ci chunk_size = (reg_size > mlxsw_i2c->block_size) ? 4498c2ecf20Sopenharmony_ci mlxsw_i2c->block_size : reg_size; 4508c2ecf20Sopenharmony_ci read_tran[1].len = chunk_size; 4518c2ecf20Sopenharmony_ci mlxsw_i2c_set_slave_addr(tran_buf, off); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci j = 0; 4548c2ecf20Sopenharmony_ci end = jiffies + timeout; 4558c2ecf20Sopenharmony_ci do { 4568c2ecf20Sopenharmony_ci err = i2c_transfer(client->adapter, read_tran, 4578c2ecf20Sopenharmony_ci ARRAY_SIZE(read_tran)); 4588c2ecf20Sopenharmony_ci if (err == ARRAY_SIZE(read_tran)) 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci cond_resched(); 4628c2ecf20Sopenharmony_ci } while ((time_before(jiffies, end)) || 4638c2ecf20Sopenharmony_ci (j++ < MLXSW_I2C_RETRY)); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (err != ARRAY_SIZE(read_tran)) { 4668c2ecf20Sopenharmony_ci if (!err) 4678c2ecf20Sopenharmony_ci err = -EIO; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci goto cmd_fail; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci off += chunk_size; 4738c2ecf20Sopenharmony_ci reg_size -= chunk_size; 4748c2ecf20Sopenharmony_ci read_tran[1].buf += chunk_size; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci mutex_unlock(&mlxsw_i2c->cmd.lock); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci return 0; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cicmd_fail: 4828c2ecf20Sopenharmony_ci mutex_unlock(&mlxsw_i2c->cmd.lock); 4838c2ecf20Sopenharmony_ci return err; 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic int mlxsw_i2c_cmd_exec(void *bus_priv, u16 opcode, u8 opcode_mod, 4878c2ecf20Sopenharmony_ci u32 in_mod, bool out_mbox_direct, 4888c2ecf20Sopenharmony_ci char *in_mbox, size_t in_mbox_size, 4898c2ecf20Sopenharmony_ci char *out_mbox, size_t out_mbox_size, 4908c2ecf20Sopenharmony_ci u8 *status) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c = bus_priv; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci return mlxsw_i2c_cmd(mlxsw_i2c->dev, opcode, in_mod, in_mbox_size, 4958c2ecf20Sopenharmony_ci in_mbox, out_mbox_size, out_mbox, status); 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic bool mlxsw_i2c_skb_transmit_busy(void *bus_priv, 4998c2ecf20Sopenharmony_ci const struct mlxsw_tx_info *tx_info) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci return false; 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic int mlxsw_i2c_skb_transmit(void *bus_priv, struct sk_buff *skb, 5058c2ecf20Sopenharmony_ci const struct mlxsw_tx_info *tx_info) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci return 0; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic int 5118c2ecf20Sopenharmony_cimlxsw_i2c_init(void *bus_priv, struct mlxsw_core *mlxsw_core, 5128c2ecf20Sopenharmony_ci const struct mlxsw_config_profile *profile, 5138c2ecf20Sopenharmony_ci struct mlxsw_res *res) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c = bus_priv; 5168c2ecf20Sopenharmony_ci char *mbox; 5178c2ecf20Sopenharmony_ci int err; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci mlxsw_i2c->core = mlxsw_core; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci mbox = mlxsw_cmd_mbox_alloc(); 5228c2ecf20Sopenharmony_ci if (!mbox) 5238c2ecf20Sopenharmony_ci return -ENOMEM; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci err = mlxsw_cmd_query_fw(mlxsw_core, mbox); 5268c2ecf20Sopenharmony_ci if (err) 5278c2ecf20Sopenharmony_ci goto mbox_put; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci mlxsw_i2c->bus_info.fw_rev.major = 5308c2ecf20Sopenharmony_ci mlxsw_cmd_mbox_query_fw_fw_rev_major_get(mbox); 5318c2ecf20Sopenharmony_ci mlxsw_i2c->bus_info.fw_rev.minor = 5328c2ecf20Sopenharmony_ci mlxsw_cmd_mbox_query_fw_fw_rev_minor_get(mbox); 5338c2ecf20Sopenharmony_ci mlxsw_i2c->bus_info.fw_rev.subminor = 5348c2ecf20Sopenharmony_ci mlxsw_cmd_mbox_query_fw_fw_rev_subminor_get(mbox); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci err = mlxsw_core_resources_query(mlxsw_core, mbox, res); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cimbox_put: 5398c2ecf20Sopenharmony_ci mlxsw_cmd_mbox_free(mbox); 5408c2ecf20Sopenharmony_ci return err; 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic void mlxsw_i2c_fini(void *bus_priv) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c = bus_priv; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci mlxsw_i2c->core = NULL; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic const struct mlxsw_bus mlxsw_i2c_bus = { 5518c2ecf20Sopenharmony_ci .kind = "i2c", 5528c2ecf20Sopenharmony_ci .init = mlxsw_i2c_init, 5538c2ecf20Sopenharmony_ci .fini = mlxsw_i2c_fini, 5548c2ecf20Sopenharmony_ci .skb_transmit_busy = mlxsw_i2c_skb_transmit_busy, 5558c2ecf20Sopenharmony_ci .skb_transmit = mlxsw_i2c_skb_transmit, 5568c2ecf20Sopenharmony_ci .cmd_exec = mlxsw_i2c_cmd_exec, 5578c2ecf20Sopenharmony_ci}; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic int mlxsw_i2c_probe(struct i2c_client *client, 5608c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci const struct i2c_adapter_quirks *quirks = client->adapter->quirks; 5638c2ecf20Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c; 5648c2ecf20Sopenharmony_ci u8 status; 5658c2ecf20Sopenharmony_ci int err; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci mlxsw_i2c = devm_kzalloc(&client->dev, sizeof(*mlxsw_i2c), GFP_KERNEL); 5688c2ecf20Sopenharmony_ci if (!mlxsw_i2c) 5698c2ecf20Sopenharmony_ci return -ENOMEM; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci if (quirks) { 5728c2ecf20Sopenharmony_ci if ((quirks->max_read_len && 5738c2ecf20Sopenharmony_ci quirks->max_read_len < MLXSW_I2C_BLK_DEF) || 5748c2ecf20Sopenharmony_ci (quirks->max_write_len && 5758c2ecf20Sopenharmony_ci quirks->max_write_len < MLXSW_I2C_BLK_DEF)) { 5768c2ecf20Sopenharmony_ci dev_err(&client->dev, "Insufficient transaction buffer length\n"); 5778c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci mlxsw_i2c->block_size = min_t(u16, MLXSW_I2C_BLK_MAX, 5818c2ecf20Sopenharmony_ci min_t(u16, quirks->max_read_len, 5828c2ecf20Sopenharmony_ci quirks->max_write_len)); 5838c2ecf20Sopenharmony_ci } else { 5848c2ecf20Sopenharmony_ci mlxsw_i2c->block_size = MLXSW_I2C_BLK_DEF; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci i2c_set_clientdata(client, mlxsw_i2c); 5888c2ecf20Sopenharmony_ci mutex_init(&mlxsw_i2c->cmd.lock); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci /* In order to use mailboxes through the i2c, special area is reserved 5918c2ecf20Sopenharmony_ci * on the i2c address space that can be used for input and output 5928c2ecf20Sopenharmony_ci * mailboxes. Such mailboxes are called local mailboxes. When using a 5938c2ecf20Sopenharmony_ci * local mailbox, software should specify 0 as the Input/Output 5948c2ecf20Sopenharmony_ci * parameters. The location of the Local Mailbox addresses on the i2c 5958c2ecf20Sopenharmony_ci * space can be retrieved through the QUERY_FW command. 5968c2ecf20Sopenharmony_ci * For this purpose QUERY_FW is to be issued with opcode modifier equal 5978c2ecf20Sopenharmony_ci * 0x01. For such command the output parameter is an immediate value. 5988c2ecf20Sopenharmony_ci * Here QUERY_FW command is invoked for ASIC probing and for getting 5998c2ecf20Sopenharmony_ci * local mailboxes addresses from immedate output parameters. 6008c2ecf20Sopenharmony_ci */ 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* Prepare and write out Command Interface Register for transaction */ 6038c2ecf20Sopenharmony_ci err = mlxsw_i2c_write_cmd(client, mlxsw_i2c, 1); 6048c2ecf20Sopenharmony_ci if (err) { 6058c2ecf20Sopenharmony_ci dev_err(&client->dev, "Could not start transaction"); 6068c2ecf20Sopenharmony_ci goto errout; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci /* Wait until go bit is cleared. */ 6108c2ecf20Sopenharmony_ci err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, &status); 6118c2ecf20Sopenharmony_ci if (err) { 6128c2ecf20Sopenharmony_ci dev_err(&client->dev, "HW semaphore is not released"); 6138c2ecf20Sopenharmony_ci goto errout; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci /* Validate transaction completion status. */ 6178c2ecf20Sopenharmony_ci if (status) { 6188c2ecf20Sopenharmony_ci dev_err(&client->dev, "Bad transaction completion status %x\n", 6198c2ecf20Sopenharmony_ci status); 6208c2ecf20Sopenharmony_ci err = -EIO; 6218c2ecf20Sopenharmony_ci goto errout; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci /* Get mailbox offsets. */ 6258c2ecf20Sopenharmony_ci err = mlxsw_i2c_get_mbox(client, mlxsw_i2c); 6268c2ecf20Sopenharmony_ci if (err < 0) { 6278c2ecf20Sopenharmony_ci dev_err(&client->dev, "Fail to get mailboxes\n"); 6288c2ecf20Sopenharmony_ci goto errout; 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci dev_info(&client->dev, "%s mb size=%x off=0x%08x out mb size=%x off=0x%08x\n", 6328c2ecf20Sopenharmony_ci id->name, mlxsw_i2c->cmd.mb_size_in, 6338c2ecf20Sopenharmony_ci mlxsw_i2c->cmd.mb_off_in, mlxsw_i2c->cmd.mb_size_out, 6348c2ecf20Sopenharmony_ci mlxsw_i2c->cmd.mb_off_out); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* Register device bus. */ 6378c2ecf20Sopenharmony_ci mlxsw_i2c->bus_info.device_kind = id->name; 6388c2ecf20Sopenharmony_ci mlxsw_i2c->bus_info.device_name = client->name; 6398c2ecf20Sopenharmony_ci mlxsw_i2c->bus_info.dev = &client->dev; 6408c2ecf20Sopenharmony_ci mlxsw_i2c->bus_info.low_frequency = true; 6418c2ecf20Sopenharmony_ci mlxsw_i2c->dev = &client->dev; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci err = mlxsw_core_bus_device_register(&mlxsw_i2c->bus_info, 6448c2ecf20Sopenharmony_ci &mlxsw_i2c_bus, mlxsw_i2c, false, 6458c2ecf20Sopenharmony_ci NULL, NULL); 6468c2ecf20Sopenharmony_ci if (err) { 6478c2ecf20Sopenharmony_ci dev_err(&client->dev, "Fail to register core bus\n"); 6488c2ecf20Sopenharmony_ci return err; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci return 0; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cierrout: 6548c2ecf20Sopenharmony_ci mutex_destroy(&mlxsw_i2c->cmd.lock); 6558c2ecf20Sopenharmony_ci i2c_set_clientdata(client, NULL); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci return err; 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_cistatic int mlxsw_i2c_remove(struct i2c_client *client) 6618c2ecf20Sopenharmony_ci{ 6628c2ecf20Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci mlxsw_core_bus_device_unregister(mlxsw_i2c->core, false); 6658c2ecf20Sopenharmony_ci mutex_destroy(&mlxsw_i2c->cmd.lock); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci return 0; 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ciint mlxsw_i2c_driver_register(struct i2c_driver *i2c_driver) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci i2c_driver->probe = mlxsw_i2c_probe; 6738c2ecf20Sopenharmony_ci i2c_driver->remove = mlxsw_i2c_remove; 6748c2ecf20Sopenharmony_ci return i2c_add_driver(i2c_driver); 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mlxsw_i2c_driver_register); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_civoid mlxsw_i2c_driver_unregister(struct i2c_driver *i2c_driver) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci i2c_del_driver(i2c_driver); 6818c2ecf20Sopenharmony_ci} 6828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mlxsw_i2c_driver_unregister); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>"); 6858c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Mellanox switch I2C interface driver"); 6868c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 687