162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2016-2018 Mellanox Technologies. All rights reserved */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/err.h> 562306a36Sopenharmony_ci#include <linux/i2c.h> 662306a36Sopenharmony_ci#include <linux/init.h> 762306a36Sopenharmony_ci#include <linux/jiffies.h> 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/mutex.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 1262306a36Sopenharmony_ci#include <linux/platform_data/mlxreg.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "cmd.h" 1662306a36Sopenharmony_ci#include "core.h" 1762306a36Sopenharmony_ci#include "i2c.h" 1862306a36Sopenharmony_ci#include "resources.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define MLXSW_I2C_CIR2_BASE 0x72000 2162306a36Sopenharmony_ci#define MLXSW_I2C_CIR_STATUS_OFF 0x18 2262306a36Sopenharmony_ci#define MLXSW_I2C_CIR2_OFF_STATUS (MLXSW_I2C_CIR2_BASE + \ 2362306a36Sopenharmony_ci MLXSW_I2C_CIR_STATUS_OFF) 2462306a36Sopenharmony_ci#define MLXSW_I2C_OPMOD_SHIFT 12 2562306a36Sopenharmony_ci#define MLXSW_I2C_EVENT_BIT_SHIFT 22 2662306a36Sopenharmony_ci#define MLXSW_I2C_GO_BIT_SHIFT 23 2762306a36Sopenharmony_ci#define MLXSW_I2C_CIR_CTRL_STATUS_SHIFT 24 2862306a36Sopenharmony_ci#define MLXSW_I2C_EVENT_BIT BIT(MLXSW_I2C_EVENT_BIT_SHIFT) 2962306a36Sopenharmony_ci#define MLXSW_I2C_GO_BIT BIT(MLXSW_I2C_GO_BIT_SHIFT) 3062306a36Sopenharmony_ci#define MLXSW_I2C_GO_OPMODE BIT(MLXSW_I2C_OPMOD_SHIFT) 3162306a36Sopenharmony_ci#define MLXSW_I2C_SET_IMM_CMD (MLXSW_I2C_GO_OPMODE | \ 3262306a36Sopenharmony_ci MLXSW_CMD_OPCODE_QUERY_FW) 3362306a36Sopenharmony_ci#define MLXSW_I2C_PUSH_IMM_CMD (MLXSW_I2C_GO_BIT | \ 3462306a36Sopenharmony_ci MLXSW_I2C_SET_IMM_CMD) 3562306a36Sopenharmony_ci#define MLXSW_I2C_SET_CMD (MLXSW_CMD_OPCODE_ACCESS_REG) 3662306a36Sopenharmony_ci#define MLXSW_I2C_PUSH_CMD (MLXSW_I2C_GO_BIT | MLXSW_I2C_SET_CMD) 3762306a36Sopenharmony_ci#define MLXSW_I2C_TLV_HDR_SIZE 0x10 3862306a36Sopenharmony_ci#define MLXSW_I2C_ADDR_WIDTH 4 3962306a36Sopenharmony_ci#define MLXSW_I2C_PUSH_CMD_SIZE (MLXSW_I2C_ADDR_WIDTH + 4) 4062306a36Sopenharmony_ci#define MLXSW_I2C_SET_EVENT_CMD (MLXSW_I2C_EVENT_BIT) 4162306a36Sopenharmony_ci#define MLXSW_I2C_PUSH_EVENT_CMD (MLXSW_I2C_GO_BIT | \ 4262306a36Sopenharmony_ci MLXSW_I2C_SET_EVENT_CMD) 4362306a36Sopenharmony_ci#define MLXSW_I2C_READ_SEMA_SIZE 4 4462306a36Sopenharmony_ci#define MLXSW_I2C_PREP_SIZE (MLXSW_I2C_ADDR_WIDTH + 28) 4562306a36Sopenharmony_ci#define MLXSW_I2C_MBOX_SIZE 20 4662306a36Sopenharmony_ci#define MLXSW_I2C_MBOX_OUT_PARAM_OFF 12 4762306a36Sopenharmony_ci#define MLXSW_I2C_MBOX_OFFSET_BITS 20 4862306a36Sopenharmony_ci#define MLXSW_I2C_MBOX_SIZE_BITS 12 4962306a36Sopenharmony_ci#define MLXSW_I2C_ADDR_BUF_SIZE 4 5062306a36Sopenharmony_ci#define MLXSW_I2C_BLK_DEF 32 5162306a36Sopenharmony_ci#define MLXSW_I2C_BLK_MAX 100 5262306a36Sopenharmony_ci#define MLXSW_I2C_RETRY 5 5362306a36Sopenharmony_ci#define MLXSW_I2C_TIMEOUT_MSECS 5000 5462306a36Sopenharmony_ci#define MLXSW_I2C_MAX_DATA_SIZE 256 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* Driver can be initialized by kernel platform driver or from the user 5762306a36Sopenharmony_ci * space. In the first case IRQ line number is passed through the platform 5862306a36Sopenharmony_ci * data, otherwise default IRQ line is to be used. Default IRQ is relevant 5962306a36Sopenharmony_ci * only for specific I2C slave address, allowing 3.4 MHz I2C path to the chip 6062306a36Sopenharmony_ci * (special hardware feature for I2C acceleration). 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci#define MLXSW_I2C_DEFAULT_IRQ 17 6362306a36Sopenharmony_ci#define MLXSW_FAST_I2C_SLAVE 0x37 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/** 6662306a36Sopenharmony_ci * struct mlxsw_i2c - device private data: 6762306a36Sopenharmony_ci * @cmd: command attributes; 6862306a36Sopenharmony_ci * @cmd.mb_size_in: input mailbox size; 6962306a36Sopenharmony_ci * @cmd.mb_off_in: input mailbox offset in register space; 7062306a36Sopenharmony_ci * @cmd.mb_size_out: output mailbox size; 7162306a36Sopenharmony_ci * @cmd.mb_off_out: output mailbox offset in register space; 7262306a36Sopenharmony_ci * @cmd.lock: command execution lock; 7362306a36Sopenharmony_ci * @dev: I2C device; 7462306a36Sopenharmony_ci * @core: switch core pointer; 7562306a36Sopenharmony_ci * @bus_info: bus info block; 7662306a36Sopenharmony_ci * @block_size: maximum block size allowed to pass to under layer; 7762306a36Sopenharmony_ci * @pdata: device platform data; 7862306a36Sopenharmony_ci * @irq_work: interrupts work item; 7962306a36Sopenharmony_ci * @irq: IRQ line number; 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_cistruct mlxsw_i2c { 8262306a36Sopenharmony_ci struct { 8362306a36Sopenharmony_ci u32 mb_size_in; 8462306a36Sopenharmony_ci u32 mb_off_in; 8562306a36Sopenharmony_ci u32 mb_size_out; 8662306a36Sopenharmony_ci u32 mb_off_out; 8762306a36Sopenharmony_ci struct mutex lock; 8862306a36Sopenharmony_ci } cmd; 8962306a36Sopenharmony_ci struct device *dev; 9062306a36Sopenharmony_ci struct mlxsw_core *core; 9162306a36Sopenharmony_ci struct mlxsw_bus_info bus_info; 9262306a36Sopenharmony_ci u16 block_size; 9362306a36Sopenharmony_ci struct mlxreg_core_hotplug_platform_data *pdata; 9462306a36Sopenharmony_ci struct work_struct irq_work; 9562306a36Sopenharmony_ci int irq; 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define MLXSW_I2C_READ_MSG(_client, _addr_buf, _buf, _len) { \ 9962306a36Sopenharmony_ci { .addr = (_client)->addr, \ 10062306a36Sopenharmony_ci .buf = (_addr_buf), \ 10162306a36Sopenharmony_ci .len = MLXSW_I2C_ADDR_BUF_SIZE, \ 10262306a36Sopenharmony_ci .flags = 0 }, \ 10362306a36Sopenharmony_ci { .addr = (_client)->addr, \ 10462306a36Sopenharmony_ci .buf = (_buf), \ 10562306a36Sopenharmony_ci .len = (_len), \ 10662306a36Sopenharmony_ci .flags = I2C_M_RD } } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#define MLXSW_I2C_WRITE_MSG(_client, _buf, _len) \ 10962306a36Sopenharmony_ci { .addr = (_client)->addr, \ 11062306a36Sopenharmony_ci .buf = (u8 *)(_buf), \ 11162306a36Sopenharmony_ci .len = (_len), \ 11262306a36Sopenharmony_ci .flags = 0 } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* Routine converts in and out mail boxes offset and size. */ 11562306a36Sopenharmony_cistatic inline void 11662306a36Sopenharmony_cimlxsw_i2c_convert_mbox(struct mlxsw_i2c *mlxsw_i2c, u8 *buf) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci u32 tmp; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* Local in/out mailboxes: 20 bits for offset, 12 for size */ 12162306a36Sopenharmony_ci tmp = be32_to_cpup((__be32 *) buf); 12262306a36Sopenharmony_ci mlxsw_i2c->cmd.mb_off_in = tmp & 12362306a36Sopenharmony_ci GENMASK(MLXSW_I2C_MBOX_OFFSET_BITS - 1, 0); 12462306a36Sopenharmony_ci mlxsw_i2c->cmd.mb_size_in = (tmp & GENMASK(31, 12562306a36Sopenharmony_ci MLXSW_I2C_MBOX_OFFSET_BITS)) >> 12662306a36Sopenharmony_ci MLXSW_I2C_MBOX_OFFSET_BITS; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci tmp = be32_to_cpup((__be32 *) (buf + MLXSW_I2C_ADDR_WIDTH)); 12962306a36Sopenharmony_ci mlxsw_i2c->cmd.mb_off_out = tmp & 13062306a36Sopenharmony_ci GENMASK(MLXSW_I2C_MBOX_OFFSET_BITS - 1, 0); 13162306a36Sopenharmony_ci mlxsw_i2c->cmd.mb_size_out = (tmp & GENMASK(31, 13262306a36Sopenharmony_ci MLXSW_I2C_MBOX_OFFSET_BITS)) >> 13362306a36Sopenharmony_ci MLXSW_I2C_MBOX_OFFSET_BITS; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/* Routine obtains register size from mail box buffer. */ 13762306a36Sopenharmony_cistatic inline int mlxsw_i2c_get_reg_size(u8 *in_mbox) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci u16 tmp = be16_to_cpup((__be16 *) (in_mbox + MLXSW_I2C_TLV_HDR_SIZE)); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return (tmp & 0x7ff) * 4 + MLXSW_I2C_TLV_HDR_SIZE; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* Routine sets I2C device internal offset in the transaction buffer. */ 14562306a36Sopenharmony_cistatic inline void mlxsw_i2c_set_slave_addr(u8 *buf, u32 off) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci __be32 *val = (__be32 *) buf; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci *val = htonl(off); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci/* Routine waits until go bit is cleared. */ 15362306a36Sopenharmony_cistatic int mlxsw_i2c_wait_go_bit(struct i2c_client *client, 15462306a36Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c, u8 *p_status) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci u8 addr_buf[MLXSW_I2C_ADDR_BUF_SIZE]; 15762306a36Sopenharmony_ci u8 buf[MLXSW_I2C_READ_SEMA_SIZE]; 15862306a36Sopenharmony_ci int len = MLXSW_I2C_READ_SEMA_SIZE; 15962306a36Sopenharmony_ci struct i2c_msg read_sema[] = 16062306a36Sopenharmony_ci MLXSW_I2C_READ_MSG(client, addr_buf, buf, len); 16162306a36Sopenharmony_ci bool wait_done = false; 16262306a36Sopenharmony_ci unsigned long end; 16362306a36Sopenharmony_ci int i = 0, err; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci mlxsw_i2c_set_slave_addr(addr_buf, MLXSW_I2C_CIR2_OFF_STATUS); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci end = jiffies + msecs_to_jiffies(MLXSW_I2C_TIMEOUT_MSECS); 16862306a36Sopenharmony_ci do { 16962306a36Sopenharmony_ci u32 ctrl; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci err = i2c_transfer(client->adapter, read_sema, 17262306a36Sopenharmony_ci ARRAY_SIZE(read_sema)); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci ctrl = be32_to_cpu(*(__be32 *) buf); 17562306a36Sopenharmony_ci if (err == ARRAY_SIZE(read_sema)) { 17662306a36Sopenharmony_ci if (!(ctrl & MLXSW_I2C_GO_BIT)) { 17762306a36Sopenharmony_ci wait_done = true; 17862306a36Sopenharmony_ci *p_status = ctrl >> 17962306a36Sopenharmony_ci MLXSW_I2C_CIR_CTRL_STATUS_SHIFT; 18062306a36Sopenharmony_ci break; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci cond_resched(); 18462306a36Sopenharmony_ci } while ((time_before(jiffies, end)) || (i++ < MLXSW_I2C_RETRY)); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (wait_done) { 18762306a36Sopenharmony_ci if (*p_status) 18862306a36Sopenharmony_ci err = -EIO; 18962306a36Sopenharmony_ci } else { 19062306a36Sopenharmony_ci return -ETIMEDOUT; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return err > 0 ? 0 : err; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/* Routine posts a command to ASIC through mail box. */ 19762306a36Sopenharmony_cistatic int mlxsw_i2c_write_cmd(struct i2c_client *client, 19862306a36Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c, 19962306a36Sopenharmony_ci int immediate) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci __be32 push_cmd_buf[MLXSW_I2C_PUSH_CMD_SIZE / 4] = { 20262306a36Sopenharmony_ci 0, cpu_to_be32(MLXSW_I2C_PUSH_IMM_CMD) 20362306a36Sopenharmony_ci }; 20462306a36Sopenharmony_ci __be32 prep_cmd_buf[MLXSW_I2C_PREP_SIZE / 4] = { 20562306a36Sopenharmony_ci 0, 0, 0, 0, 0, 0, 20662306a36Sopenharmony_ci cpu_to_be32(client->adapter->nr & 0xffff), 20762306a36Sopenharmony_ci cpu_to_be32(MLXSW_I2C_SET_IMM_CMD) 20862306a36Sopenharmony_ci }; 20962306a36Sopenharmony_ci struct i2c_msg push_cmd = 21062306a36Sopenharmony_ci MLXSW_I2C_WRITE_MSG(client, push_cmd_buf, 21162306a36Sopenharmony_ci MLXSW_I2C_PUSH_CMD_SIZE); 21262306a36Sopenharmony_ci struct i2c_msg prep_cmd = 21362306a36Sopenharmony_ci MLXSW_I2C_WRITE_MSG(client, prep_cmd_buf, MLXSW_I2C_PREP_SIZE); 21462306a36Sopenharmony_ci int err; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (!immediate) { 21762306a36Sopenharmony_ci push_cmd_buf[1] = cpu_to_be32(MLXSW_I2C_PUSH_CMD); 21862306a36Sopenharmony_ci prep_cmd_buf[7] = cpu_to_be32(MLXSW_I2C_SET_CMD); 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci mlxsw_i2c_set_slave_addr((u8 *)prep_cmd_buf, 22162306a36Sopenharmony_ci MLXSW_I2C_CIR2_BASE); 22262306a36Sopenharmony_ci mlxsw_i2c_set_slave_addr((u8 *)push_cmd_buf, 22362306a36Sopenharmony_ci MLXSW_I2C_CIR2_OFF_STATUS); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* Prepare Command Interface Register for transaction */ 22662306a36Sopenharmony_ci err = i2c_transfer(client->adapter, &prep_cmd, 1); 22762306a36Sopenharmony_ci if (err < 0) 22862306a36Sopenharmony_ci return err; 22962306a36Sopenharmony_ci else if (err != 1) 23062306a36Sopenharmony_ci return -EIO; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci /* Write out Command Interface Register GO bit to push transaction */ 23362306a36Sopenharmony_ci err = i2c_transfer(client->adapter, &push_cmd, 1); 23462306a36Sopenharmony_ci if (err < 0) 23562306a36Sopenharmony_ci return err; 23662306a36Sopenharmony_ci else if (err != 1) 23762306a36Sopenharmony_ci return -EIO; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return 0; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci/* Routine posts initialization command to ASIC through mail box. */ 24362306a36Sopenharmony_cistatic int 24462306a36Sopenharmony_cimlxsw_i2c_write_init_cmd(struct i2c_client *client, 24562306a36Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c, u16 opcode, u32 in_mod) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci __be32 push_cmd_buf[MLXSW_I2C_PUSH_CMD_SIZE / 4] = { 24862306a36Sopenharmony_ci 0, cpu_to_be32(MLXSW_I2C_PUSH_EVENT_CMD) 24962306a36Sopenharmony_ci }; 25062306a36Sopenharmony_ci __be32 prep_cmd_buf[MLXSW_I2C_PREP_SIZE / 4] = { 25162306a36Sopenharmony_ci 0, 0, 0, 0, 0, 0, 25262306a36Sopenharmony_ci cpu_to_be32(client->adapter->nr & 0xffff), 25362306a36Sopenharmony_ci cpu_to_be32(MLXSW_I2C_SET_EVENT_CMD) 25462306a36Sopenharmony_ci }; 25562306a36Sopenharmony_ci struct i2c_msg push_cmd = 25662306a36Sopenharmony_ci MLXSW_I2C_WRITE_MSG(client, push_cmd_buf, 25762306a36Sopenharmony_ci MLXSW_I2C_PUSH_CMD_SIZE); 25862306a36Sopenharmony_ci struct i2c_msg prep_cmd = 25962306a36Sopenharmony_ci MLXSW_I2C_WRITE_MSG(client, prep_cmd_buf, MLXSW_I2C_PREP_SIZE); 26062306a36Sopenharmony_ci u8 status; 26162306a36Sopenharmony_ci int err; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci push_cmd_buf[1] = cpu_to_be32(MLXSW_I2C_PUSH_EVENT_CMD | opcode); 26462306a36Sopenharmony_ci prep_cmd_buf[3] = cpu_to_be32(in_mod); 26562306a36Sopenharmony_ci prep_cmd_buf[7] = cpu_to_be32(MLXSW_I2C_GO_BIT | opcode); 26662306a36Sopenharmony_ci mlxsw_i2c_set_slave_addr((u8 *)prep_cmd_buf, 26762306a36Sopenharmony_ci MLXSW_I2C_CIR2_BASE); 26862306a36Sopenharmony_ci mlxsw_i2c_set_slave_addr((u8 *)push_cmd_buf, 26962306a36Sopenharmony_ci MLXSW_I2C_CIR2_OFF_STATUS); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* Prepare Command Interface Register for transaction */ 27262306a36Sopenharmony_ci err = i2c_transfer(client->adapter, &prep_cmd, 1); 27362306a36Sopenharmony_ci if (err < 0) 27462306a36Sopenharmony_ci return err; 27562306a36Sopenharmony_ci else if (err != 1) 27662306a36Sopenharmony_ci return -EIO; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* Write out Command Interface Register GO bit to push transaction */ 27962306a36Sopenharmony_ci err = i2c_transfer(client->adapter, &push_cmd, 1); 28062306a36Sopenharmony_ci if (err < 0) 28162306a36Sopenharmony_ci return err; 28262306a36Sopenharmony_ci else if (err != 1) 28362306a36Sopenharmony_ci return -EIO; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* Wait until go bit is cleared. */ 28662306a36Sopenharmony_ci err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, &status); 28762306a36Sopenharmony_ci if (err) { 28862306a36Sopenharmony_ci dev_err(&client->dev, "HW semaphore is not released"); 28962306a36Sopenharmony_ci return err; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* Validate transaction completion status. */ 29362306a36Sopenharmony_ci if (status) { 29462306a36Sopenharmony_ci dev_err(&client->dev, "Bad transaction completion status %x\n", 29562306a36Sopenharmony_ci status); 29662306a36Sopenharmony_ci return -EIO; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return 0; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci/* Routine obtains mail box offsets from ASIC register space. */ 30362306a36Sopenharmony_cistatic int mlxsw_i2c_get_mbox(struct i2c_client *client, 30462306a36Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci u8 addr_buf[MLXSW_I2C_ADDR_BUF_SIZE]; 30762306a36Sopenharmony_ci u8 buf[MLXSW_I2C_MBOX_SIZE]; 30862306a36Sopenharmony_ci struct i2c_msg mbox_cmd[] = 30962306a36Sopenharmony_ci MLXSW_I2C_READ_MSG(client, addr_buf, buf, MLXSW_I2C_MBOX_SIZE); 31062306a36Sopenharmony_ci int err; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* Read mail boxes offsets. */ 31362306a36Sopenharmony_ci mlxsw_i2c_set_slave_addr(addr_buf, MLXSW_I2C_CIR2_BASE); 31462306a36Sopenharmony_ci err = i2c_transfer(client->adapter, mbox_cmd, 2); 31562306a36Sopenharmony_ci if (err != 2) { 31662306a36Sopenharmony_ci dev_err(&client->dev, "Could not obtain mail boxes\n"); 31762306a36Sopenharmony_ci if (!err) 31862306a36Sopenharmony_ci return -EIO; 31962306a36Sopenharmony_ci else 32062306a36Sopenharmony_ci return err; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* Convert mail boxes. */ 32462306a36Sopenharmony_ci mlxsw_i2c_convert_mbox(mlxsw_i2c, &buf[MLXSW_I2C_MBOX_OUT_PARAM_OFF]); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci return err; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci/* Routine sends I2C write transaction to ASIC device. */ 33062306a36Sopenharmony_cistatic int 33162306a36Sopenharmony_cimlxsw_i2c_write(struct device *dev, size_t in_mbox_size, u8 *in_mbox, int num, 33262306a36Sopenharmony_ci u8 *p_status) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 33562306a36Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client); 33662306a36Sopenharmony_ci unsigned long timeout = msecs_to_jiffies(MLXSW_I2C_TIMEOUT_MSECS); 33762306a36Sopenharmony_ci int off = mlxsw_i2c->cmd.mb_off_in, chunk_size, i, j; 33862306a36Sopenharmony_ci unsigned long end; 33962306a36Sopenharmony_ci u8 *tran_buf; 34062306a36Sopenharmony_ci struct i2c_msg write_tran = 34162306a36Sopenharmony_ci MLXSW_I2C_WRITE_MSG(client, NULL, MLXSW_I2C_PUSH_CMD_SIZE); 34262306a36Sopenharmony_ci int err; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci tran_buf = kmalloc(mlxsw_i2c->block_size + MLXSW_I2C_ADDR_BUF_SIZE, 34562306a36Sopenharmony_ci GFP_KERNEL); 34662306a36Sopenharmony_ci if (!tran_buf) 34762306a36Sopenharmony_ci return -ENOMEM; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci write_tran.buf = tran_buf; 35062306a36Sopenharmony_ci for (i = 0; i < num; i++) { 35162306a36Sopenharmony_ci chunk_size = (in_mbox_size > mlxsw_i2c->block_size) ? 35262306a36Sopenharmony_ci mlxsw_i2c->block_size : in_mbox_size; 35362306a36Sopenharmony_ci write_tran.len = MLXSW_I2C_ADDR_WIDTH + chunk_size; 35462306a36Sopenharmony_ci mlxsw_i2c_set_slave_addr(tran_buf, off); 35562306a36Sopenharmony_ci memcpy(&tran_buf[MLXSW_I2C_ADDR_BUF_SIZE], in_mbox + 35662306a36Sopenharmony_ci mlxsw_i2c->block_size * i, chunk_size); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci j = 0; 35962306a36Sopenharmony_ci end = jiffies + timeout; 36062306a36Sopenharmony_ci do { 36162306a36Sopenharmony_ci err = i2c_transfer(client->adapter, &write_tran, 1); 36262306a36Sopenharmony_ci if (err == 1) 36362306a36Sopenharmony_ci break; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci cond_resched(); 36662306a36Sopenharmony_ci } while ((time_before(jiffies, end)) || 36762306a36Sopenharmony_ci (j++ < MLXSW_I2C_RETRY)); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (err != 1) { 37062306a36Sopenharmony_ci if (!err) { 37162306a36Sopenharmony_ci err = -EIO; 37262306a36Sopenharmony_ci goto mlxsw_i2c_write_exit; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci off += chunk_size; 37762306a36Sopenharmony_ci in_mbox_size -= chunk_size; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* Prepare and write out Command Interface Register for transaction. */ 38162306a36Sopenharmony_ci err = mlxsw_i2c_write_cmd(client, mlxsw_i2c, 0); 38262306a36Sopenharmony_ci if (err) { 38362306a36Sopenharmony_ci dev_err(&client->dev, "Could not start transaction"); 38462306a36Sopenharmony_ci err = -EIO; 38562306a36Sopenharmony_ci goto mlxsw_i2c_write_exit; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* Wait until go bit is cleared. */ 38962306a36Sopenharmony_ci err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, p_status); 39062306a36Sopenharmony_ci if (err) { 39162306a36Sopenharmony_ci dev_err(&client->dev, "HW semaphore is not released"); 39262306a36Sopenharmony_ci goto mlxsw_i2c_write_exit; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* Validate transaction completion status. */ 39662306a36Sopenharmony_ci if (*p_status) { 39762306a36Sopenharmony_ci dev_err(&client->dev, "Bad transaction completion status %x\n", 39862306a36Sopenharmony_ci *p_status); 39962306a36Sopenharmony_ci err = -EIO; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cimlxsw_i2c_write_exit: 40362306a36Sopenharmony_ci kfree(tran_buf); 40462306a36Sopenharmony_ci return err; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci/* Routine executes I2C command. */ 40862306a36Sopenharmony_cistatic int 40962306a36Sopenharmony_cimlxsw_i2c_cmd(struct device *dev, u16 opcode, u32 in_mod, size_t in_mbox_size, 41062306a36Sopenharmony_ci u8 *in_mbox, size_t out_mbox_size, u8 *out_mbox, u8 *status) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 41362306a36Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client); 41462306a36Sopenharmony_ci unsigned long timeout = msecs_to_jiffies(MLXSW_I2C_TIMEOUT_MSECS); 41562306a36Sopenharmony_ci u8 tran_buf[MLXSW_I2C_ADDR_BUF_SIZE]; 41662306a36Sopenharmony_ci int num, chunk_size, reg_size, i, j; 41762306a36Sopenharmony_ci int off = mlxsw_i2c->cmd.mb_off_out; 41862306a36Sopenharmony_ci unsigned long end; 41962306a36Sopenharmony_ci struct i2c_msg read_tran[] = 42062306a36Sopenharmony_ci MLXSW_I2C_READ_MSG(client, tran_buf, NULL, 0); 42162306a36Sopenharmony_ci int err; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci WARN_ON(in_mbox_size % sizeof(u32) || out_mbox_size % sizeof(u32)); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (in_mbox) { 42662306a36Sopenharmony_ci reg_size = mlxsw_i2c_get_reg_size(in_mbox); 42762306a36Sopenharmony_ci num = reg_size / mlxsw_i2c->block_size; 42862306a36Sopenharmony_ci if (reg_size % mlxsw_i2c->block_size) 42962306a36Sopenharmony_ci num++; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) { 43262306a36Sopenharmony_ci dev_err(&client->dev, "Could not acquire lock"); 43362306a36Sopenharmony_ci return -EINVAL; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci err = mlxsw_i2c_write(dev, reg_size, in_mbox, num, status); 43762306a36Sopenharmony_ci if (err) 43862306a36Sopenharmony_ci goto cmd_fail; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* No out mailbox is case of write transaction. */ 44162306a36Sopenharmony_ci if (!out_mbox) { 44262306a36Sopenharmony_ci mutex_unlock(&mlxsw_i2c->cmd.lock); 44362306a36Sopenharmony_ci return 0; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci } else { 44662306a36Sopenharmony_ci /* No input mailbox is case of initialization query command. */ 44762306a36Sopenharmony_ci reg_size = MLXSW_I2C_MAX_DATA_SIZE; 44862306a36Sopenharmony_ci num = DIV_ROUND_UP(reg_size, mlxsw_i2c->block_size); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) { 45162306a36Sopenharmony_ci dev_err(&client->dev, "Could not acquire lock"); 45262306a36Sopenharmony_ci return -EINVAL; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci err = mlxsw_i2c_write_init_cmd(client, mlxsw_i2c, opcode, 45662306a36Sopenharmony_ci in_mod); 45762306a36Sopenharmony_ci if (err) 45862306a36Sopenharmony_ci goto cmd_fail; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* Send read transaction to get output mailbox content. */ 46262306a36Sopenharmony_ci read_tran[1].buf = out_mbox; 46362306a36Sopenharmony_ci for (i = 0; i < num; i++) { 46462306a36Sopenharmony_ci chunk_size = (reg_size > mlxsw_i2c->block_size) ? 46562306a36Sopenharmony_ci mlxsw_i2c->block_size : reg_size; 46662306a36Sopenharmony_ci read_tran[1].len = chunk_size; 46762306a36Sopenharmony_ci mlxsw_i2c_set_slave_addr(tran_buf, off); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci j = 0; 47062306a36Sopenharmony_ci end = jiffies + timeout; 47162306a36Sopenharmony_ci do { 47262306a36Sopenharmony_ci err = i2c_transfer(client->adapter, read_tran, 47362306a36Sopenharmony_ci ARRAY_SIZE(read_tran)); 47462306a36Sopenharmony_ci if (err == ARRAY_SIZE(read_tran)) 47562306a36Sopenharmony_ci break; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci cond_resched(); 47862306a36Sopenharmony_ci } while ((time_before(jiffies, end)) || 47962306a36Sopenharmony_ci (j++ < MLXSW_I2C_RETRY)); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (err != ARRAY_SIZE(read_tran)) { 48262306a36Sopenharmony_ci if (!err) 48362306a36Sopenharmony_ci err = -EIO; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci goto cmd_fail; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci off += chunk_size; 48962306a36Sopenharmony_ci reg_size -= chunk_size; 49062306a36Sopenharmony_ci read_tran[1].buf += chunk_size; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci mutex_unlock(&mlxsw_i2c->cmd.lock); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci return 0; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cicmd_fail: 49862306a36Sopenharmony_ci mutex_unlock(&mlxsw_i2c->cmd.lock); 49962306a36Sopenharmony_ci return err; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic int mlxsw_i2c_cmd_exec(void *bus_priv, u16 opcode, u8 opcode_mod, 50362306a36Sopenharmony_ci u32 in_mod, bool out_mbox_direct, 50462306a36Sopenharmony_ci char *in_mbox, size_t in_mbox_size, 50562306a36Sopenharmony_ci char *out_mbox, size_t out_mbox_size, 50662306a36Sopenharmony_ci u8 *status) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c = bus_priv; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci return mlxsw_i2c_cmd(mlxsw_i2c->dev, opcode, in_mod, in_mbox_size, 51162306a36Sopenharmony_ci in_mbox, out_mbox_size, out_mbox, status); 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic bool mlxsw_i2c_skb_transmit_busy(void *bus_priv, 51562306a36Sopenharmony_ci const struct mlxsw_tx_info *tx_info) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci return false; 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic int mlxsw_i2c_skb_transmit(void *bus_priv, struct sk_buff *skb, 52162306a36Sopenharmony_ci const struct mlxsw_tx_info *tx_info) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci return 0; 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic int 52762306a36Sopenharmony_cimlxsw_i2c_init(void *bus_priv, struct mlxsw_core *mlxsw_core, 52862306a36Sopenharmony_ci const struct mlxsw_config_profile *profile, 52962306a36Sopenharmony_ci struct mlxsw_res *res) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c = bus_priv; 53262306a36Sopenharmony_ci char *mbox; 53362306a36Sopenharmony_ci int err; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci mlxsw_i2c->core = mlxsw_core; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci mbox = mlxsw_cmd_mbox_alloc(); 53862306a36Sopenharmony_ci if (!mbox) 53962306a36Sopenharmony_ci return -ENOMEM; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci err = mlxsw_cmd_query_fw(mlxsw_core, mbox); 54262306a36Sopenharmony_ci if (err) 54362306a36Sopenharmony_ci goto mbox_put; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci mlxsw_i2c->bus_info.fw_rev.major = 54662306a36Sopenharmony_ci mlxsw_cmd_mbox_query_fw_fw_rev_major_get(mbox); 54762306a36Sopenharmony_ci mlxsw_i2c->bus_info.fw_rev.minor = 54862306a36Sopenharmony_ci mlxsw_cmd_mbox_query_fw_fw_rev_minor_get(mbox); 54962306a36Sopenharmony_ci mlxsw_i2c->bus_info.fw_rev.subminor = 55062306a36Sopenharmony_ci mlxsw_cmd_mbox_query_fw_fw_rev_subminor_get(mbox); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci err = mlxsw_core_resources_query(mlxsw_core, mbox, res); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cimbox_put: 55562306a36Sopenharmony_ci mlxsw_cmd_mbox_free(mbox); 55662306a36Sopenharmony_ci return err; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic void mlxsw_i2c_fini(void *bus_priv) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c = bus_priv; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci mlxsw_i2c->core = NULL; 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic void mlxsw_i2c_work_handler(struct work_struct *work) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci mlxsw_i2c = container_of(work, struct mlxsw_i2c, irq_work); 57162306a36Sopenharmony_ci mlxsw_core_irq_event_handlers_call(mlxsw_i2c->core); 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic irqreturn_t mlxsw_i2c_irq_handler(int irq, void *dev) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c = dev; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci mlxsw_core_schedule_work(&mlxsw_i2c->irq_work); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* Interrupt handler shares IRQ line with 'main' interrupt handler. 58162306a36Sopenharmony_ci * Return here IRQ_NONE, while main handler will return IRQ_HANDLED. 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_ci return IRQ_NONE; 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_cistatic int mlxsw_i2c_irq_init(struct mlxsw_i2c *mlxsw_i2c, u8 addr) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci int err; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci /* Initialize interrupt handler if system hotplug driver is reachable, 59162306a36Sopenharmony_ci * otherwise interrupt line is not enabled and interrupts will not be 59262306a36Sopenharmony_ci * raised to CPU. Also request_irq() call will be not valid. 59362306a36Sopenharmony_ci */ 59462306a36Sopenharmony_ci if (!IS_REACHABLE(CONFIG_MLXREG_HOTPLUG)) 59562306a36Sopenharmony_ci return 0; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* Set default interrupt line. */ 59862306a36Sopenharmony_ci if (mlxsw_i2c->pdata && mlxsw_i2c->pdata->irq) 59962306a36Sopenharmony_ci mlxsw_i2c->irq = mlxsw_i2c->pdata->irq; 60062306a36Sopenharmony_ci else if (addr == MLXSW_FAST_I2C_SLAVE) 60162306a36Sopenharmony_ci mlxsw_i2c->irq = MLXSW_I2C_DEFAULT_IRQ; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci if (!mlxsw_i2c->irq) 60462306a36Sopenharmony_ci return 0; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci INIT_WORK(&mlxsw_i2c->irq_work, mlxsw_i2c_work_handler); 60762306a36Sopenharmony_ci err = request_irq(mlxsw_i2c->irq, mlxsw_i2c_irq_handler, 60862306a36Sopenharmony_ci IRQF_TRIGGER_FALLING | IRQF_SHARED, "mlxsw-i2c", 60962306a36Sopenharmony_ci mlxsw_i2c); 61062306a36Sopenharmony_ci if (err) { 61162306a36Sopenharmony_ci dev_err(mlxsw_i2c->bus_info.dev, "Failed to request irq: %d\n", 61262306a36Sopenharmony_ci err); 61362306a36Sopenharmony_ci return err; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci return 0; 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic void mlxsw_i2c_irq_fini(struct mlxsw_i2c *mlxsw_i2c) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci if (!IS_REACHABLE(CONFIG_MLXREG_HOTPLUG) || !mlxsw_i2c->irq) 62262306a36Sopenharmony_ci return; 62362306a36Sopenharmony_ci cancel_work_sync(&mlxsw_i2c->irq_work); 62462306a36Sopenharmony_ci free_irq(mlxsw_i2c->irq, mlxsw_i2c); 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic const struct mlxsw_bus mlxsw_i2c_bus = { 62862306a36Sopenharmony_ci .kind = "i2c", 62962306a36Sopenharmony_ci .init = mlxsw_i2c_init, 63062306a36Sopenharmony_ci .fini = mlxsw_i2c_fini, 63162306a36Sopenharmony_ci .skb_transmit_busy = mlxsw_i2c_skb_transmit_busy, 63262306a36Sopenharmony_ci .skb_transmit = mlxsw_i2c_skb_transmit, 63362306a36Sopenharmony_ci .cmd_exec = mlxsw_i2c_cmd_exec, 63462306a36Sopenharmony_ci}; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic int mlxsw_i2c_probe(struct i2c_client *client) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci const struct i2c_device_id *id = i2c_client_get_device_id(client); 63962306a36Sopenharmony_ci const struct i2c_adapter_quirks *quirks = client->adapter->quirks; 64062306a36Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c; 64162306a36Sopenharmony_ci u8 status; 64262306a36Sopenharmony_ci int err; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci mlxsw_i2c = devm_kzalloc(&client->dev, sizeof(*mlxsw_i2c), GFP_KERNEL); 64562306a36Sopenharmony_ci if (!mlxsw_i2c) 64662306a36Sopenharmony_ci return -ENOMEM; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (quirks) { 64962306a36Sopenharmony_ci if ((quirks->max_read_len && 65062306a36Sopenharmony_ci quirks->max_read_len < MLXSW_I2C_BLK_DEF) || 65162306a36Sopenharmony_ci (quirks->max_write_len && 65262306a36Sopenharmony_ci quirks->max_write_len < MLXSW_I2C_BLK_DEF)) { 65362306a36Sopenharmony_ci dev_err(&client->dev, "Insufficient transaction buffer length\n"); 65462306a36Sopenharmony_ci return -EOPNOTSUPP; 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci mlxsw_i2c->block_size = min_t(u16, MLXSW_I2C_BLK_MAX, 65862306a36Sopenharmony_ci min_t(u16, quirks->max_read_len, 65962306a36Sopenharmony_ci quirks->max_write_len)); 66062306a36Sopenharmony_ci } else { 66162306a36Sopenharmony_ci mlxsw_i2c->block_size = MLXSW_I2C_BLK_DEF; 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci i2c_set_clientdata(client, mlxsw_i2c); 66562306a36Sopenharmony_ci mutex_init(&mlxsw_i2c->cmd.lock); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci /* In order to use mailboxes through the i2c, special area is reserved 66862306a36Sopenharmony_ci * on the i2c address space that can be used for input and output 66962306a36Sopenharmony_ci * mailboxes. Such mailboxes are called local mailboxes. When using a 67062306a36Sopenharmony_ci * local mailbox, software should specify 0 as the Input/Output 67162306a36Sopenharmony_ci * parameters. The location of the Local Mailbox addresses on the i2c 67262306a36Sopenharmony_ci * space can be retrieved through the QUERY_FW command. 67362306a36Sopenharmony_ci * For this purpose QUERY_FW is to be issued with opcode modifier equal 67462306a36Sopenharmony_ci * 0x01. For such command the output parameter is an immediate value. 67562306a36Sopenharmony_ci * Here QUERY_FW command is invoked for ASIC probing and for getting 67662306a36Sopenharmony_ci * local mailboxes addresses from immedate output parameters. 67762306a36Sopenharmony_ci */ 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci /* Prepare and write out Command Interface Register for transaction */ 68062306a36Sopenharmony_ci err = mlxsw_i2c_write_cmd(client, mlxsw_i2c, 1); 68162306a36Sopenharmony_ci if (err) { 68262306a36Sopenharmony_ci dev_err(&client->dev, "Could not start transaction"); 68362306a36Sopenharmony_ci goto errout; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* Wait until go bit is cleared. */ 68762306a36Sopenharmony_ci err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, &status); 68862306a36Sopenharmony_ci if (err) { 68962306a36Sopenharmony_ci dev_err(&client->dev, "HW semaphore is not released"); 69062306a36Sopenharmony_ci goto errout; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* Validate transaction completion status. */ 69462306a36Sopenharmony_ci if (status) { 69562306a36Sopenharmony_ci dev_err(&client->dev, "Bad transaction completion status %x\n", 69662306a36Sopenharmony_ci status); 69762306a36Sopenharmony_ci err = -EIO; 69862306a36Sopenharmony_ci goto errout; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci /* Get mailbox offsets. */ 70262306a36Sopenharmony_ci err = mlxsw_i2c_get_mbox(client, mlxsw_i2c); 70362306a36Sopenharmony_ci if (err < 0) { 70462306a36Sopenharmony_ci dev_err(&client->dev, "Fail to get mailboxes\n"); 70562306a36Sopenharmony_ci goto errout; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci dev_info(&client->dev, "%s mb size=%x off=0x%08x out mb size=%x off=0x%08x\n", 70962306a36Sopenharmony_ci id->name, mlxsw_i2c->cmd.mb_size_in, 71062306a36Sopenharmony_ci mlxsw_i2c->cmd.mb_off_in, mlxsw_i2c->cmd.mb_size_out, 71162306a36Sopenharmony_ci mlxsw_i2c->cmd.mb_off_out); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci /* Register device bus. */ 71462306a36Sopenharmony_ci mlxsw_i2c->bus_info.device_kind = id->name; 71562306a36Sopenharmony_ci mlxsw_i2c->bus_info.device_name = client->name; 71662306a36Sopenharmony_ci mlxsw_i2c->bus_info.dev = &client->dev; 71762306a36Sopenharmony_ci mlxsw_i2c->bus_info.low_frequency = true; 71862306a36Sopenharmony_ci mlxsw_i2c->dev = &client->dev; 71962306a36Sopenharmony_ci mlxsw_i2c->pdata = client->dev.platform_data; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci err = mlxsw_i2c_irq_init(mlxsw_i2c, client->addr); 72262306a36Sopenharmony_ci if (err) 72362306a36Sopenharmony_ci goto errout; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci err = mlxsw_core_bus_device_register(&mlxsw_i2c->bus_info, 72662306a36Sopenharmony_ci &mlxsw_i2c_bus, mlxsw_i2c, false, 72762306a36Sopenharmony_ci NULL, NULL); 72862306a36Sopenharmony_ci if (err) { 72962306a36Sopenharmony_ci dev_err(&client->dev, "Fail to register core bus\n"); 73062306a36Sopenharmony_ci goto err_bus_device_register; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci return 0; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cierr_bus_device_register: 73662306a36Sopenharmony_ci mlxsw_i2c_irq_fini(mlxsw_i2c); 73762306a36Sopenharmony_cierrout: 73862306a36Sopenharmony_ci mutex_destroy(&mlxsw_i2c->cmd.lock); 73962306a36Sopenharmony_ci i2c_set_clientdata(client, NULL); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci return err; 74262306a36Sopenharmony_ci} 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic void mlxsw_i2c_remove(struct i2c_client *client) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci mlxsw_core_bus_device_unregister(mlxsw_i2c->core, false); 74962306a36Sopenharmony_ci mlxsw_i2c_irq_fini(mlxsw_i2c); 75062306a36Sopenharmony_ci mutex_destroy(&mlxsw_i2c->cmd.lock); 75162306a36Sopenharmony_ci} 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ciint mlxsw_i2c_driver_register(struct i2c_driver *i2c_driver) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci i2c_driver->probe = mlxsw_i2c_probe; 75662306a36Sopenharmony_ci i2c_driver->remove = mlxsw_i2c_remove; 75762306a36Sopenharmony_ci return i2c_add_driver(i2c_driver); 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ciEXPORT_SYMBOL(mlxsw_i2c_driver_register); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_civoid mlxsw_i2c_driver_unregister(struct i2c_driver *i2c_driver) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci i2c_del_driver(i2c_driver); 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ciEXPORT_SYMBOL(mlxsw_i2c_driver_unregister); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ciMODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>"); 76862306a36Sopenharmony_ciMODULE_DESCRIPTION("Mellanox switch I2C interface driver"); 76962306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 770