xref: /linux/drivers/net/ethernet/mellanox/mlxsw/i2c.c (revision 1f732863)
19948a064SJiri Pirko // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
29948a064SJiri Pirko /* Copyright (c) 2016-2018 Mellanox Technologies. All rights reserved */
36882b0aeSVadim Pasternak 
46882b0aeSVadim Pasternak #include <linux/err.h>
56882b0aeSVadim Pasternak #include <linux/i2c.h>
66882b0aeSVadim Pasternak #include <linux/init.h>
76882b0aeSVadim Pasternak #include <linux/jiffies.h>
86882b0aeSVadim Pasternak #include <linux/kernel.h>
96882b0aeSVadim Pasternak #include <linux/mutex.h>
106882b0aeSVadim Pasternak #include <linux/module.h>
116882b0aeSVadim Pasternak #include <linux/mod_devicetable.h>
1233fa6909SVadim Pasternak #include <linux/platform_data/mlxreg.h>
136882b0aeSVadim Pasternak #include <linux/slab.h>
146882b0aeSVadim Pasternak 
156882b0aeSVadim Pasternak #include "cmd.h"
166882b0aeSVadim Pasternak #include "core.h"
176882b0aeSVadim Pasternak #include "i2c.h"
186a986993SVadim Pasternak #include "resources.h"
196882b0aeSVadim Pasternak 
206882b0aeSVadim Pasternak #define MLXSW_I2C_CIR2_BASE		0x72000
216882b0aeSVadim Pasternak #define MLXSW_I2C_CIR_STATUS_OFF	0x18
226882b0aeSVadim Pasternak #define MLXSW_I2C_CIR2_OFF_STATUS	(MLXSW_I2C_CIR2_BASE + \
236882b0aeSVadim Pasternak 					 MLXSW_I2C_CIR_STATUS_OFF)
246882b0aeSVadim Pasternak #define MLXSW_I2C_OPMOD_SHIFT		12
2595b75cbdSVadim Pasternak #define MLXSW_I2C_EVENT_BIT_SHIFT	22
266882b0aeSVadim Pasternak #define MLXSW_I2C_GO_BIT_SHIFT		23
276882b0aeSVadim Pasternak #define MLXSW_I2C_CIR_CTRL_STATUS_SHIFT	24
2895b75cbdSVadim Pasternak #define MLXSW_I2C_EVENT_BIT		BIT(MLXSW_I2C_EVENT_BIT_SHIFT)
296882b0aeSVadim Pasternak #define MLXSW_I2C_GO_BIT		BIT(MLXSW_I2C_GO_BIT_SHIFT)
306882b0aeSVadim Pasternak #define MLXSW_I2C_GO_OPMODE		BIT(MLXSW_I2C_OPMOD_SHIFT)
316882b0aeSVadim Pasternak #define MLXSW_I2C_SET_IMM_CMD		(MLXSW_I2C_GO_OPMODE | \
326882b0aeSVadim Pasternak 					 MLXSW_CMD_OPCODE_QUERY_FW)
336882b0aeSVadim Pasternak #define MLXSW_I2C_PUSH_IMM_CMD		(MLXSW_I2C_GO_BIT | \
346882b0aeSVadim Pasternak 					 MLXSW_I2C_SET_IMM_CMD)
356882b0aeSVadim Pasternak #define MLXSW_I2C_SET_CMD		(MLXSW_CMD_OPCODE_ACCESS_REG)
366882b0aeSVadim Pasternak #define MLXSW_I2C_PUSH_CMD		(MLXSW_I2C_GO_BIT | MLXSW_I2C_SET_CMD)
376882b0aeSVadim Pasternak #define MLXSW_I2C_TLV_HDR_SIZE		0x10
386882b0aeSVadim Pasternak #define MLXSW_I2C_ADDR_WIDTH		4
396882b0aeSVadim Pasternak #define MLXSW_I2C_PUSH_CMD_SIZE		(MLXSW_I2C_ADDR_WIDTH + 4)
4095b75cbdSVadim Pasternak #define MLXSW_I2C_SET_EVENT_CMD		(MLXSW_I2C_EVENT_BIT)
4195b75cbdSVadim Pasternak #define MLXSW_I2C_PUSH_EVENT_CMD	(MLXSW_I2C_GO_BIT | \
4295b75cbdSVadim Pasternak 					 MLXSW_I2C_SET_EVENT_CMD)
436882b0aeSVadim Pasternak #define MLXSW_I2C_READ_SEMA_SIZE	4
446882b0aeSVadim Pasternak #define MLXSW_I2C_PREP_SIZE		(MLXSW_I2C_ADDR_WIDTH + 28)
456882b0aeSVadim Pasternak #define MLXSW_I2C_MBOX_SIZE		20
466882b0aeSVadim Pasternak #define MLXSW_I2C_MBOX_OUT_PARAM_OFF	12
476882b0aeSVadim Pasternak #define MLXSW_I2C_MBOX_OFFSET_BITS	20
486882b0aeSVadim Pasternak #define MLXSW_I2C_MBOX_SIZE_BITS	12
496882b0aeSVadim Pasternak #define MLXSW_I2C_ADDR_BUF_SIZE		4
503029a693SVadim Pasternak #define MLXSW_I2C_BLK_DEF		32
51d7248f1cSVadim Pasternak #define MLXSW_I2C_BLK_MAX		100
526882b0aeSVadim Pasternak #define MLXSW_I2C_RETRY			5
536882b0aeSVadim Pasternak #define MLXSW_I2C_TIMEOUT_MSECS		5000
5495b75cbdSVadim Pasternak #define MLXSW_I2C_MAX_DATA_SIZE		256
556882b0aeSVadim Pasternak 
5633fa6909SVadim Pasternak /* Driver can be initialized by kernel platform driver or from the user
5733fa6909SVadim Pasternak  * space. In the first case IRQ line number is passed through the platform
5833fa6909SVadim Pasternak  * data, otherwise default IRQ line is to be used. Default IRQ is relevant
5933fa6909SVadim Pasternak  * only for specific I2C slave address, allowing 3.4 MHz I2C path to the chip
6033fa6909SVadim Pasternak  * (special hardware feature for I2C acceleration).
6133fa6909SVadim Pasternak  */
6233fa6909SVadim Pasternak #define MLXSW_I2C_DEFAULT_IRQ		17
6333fa6909SVadim Pasternak #define MLXSW_FAST_I2C_SLAVE		0x37
6433fa6909SVadim Pasternak 
656882b0aeSVadim Pasternak /**
666882b0aeSVadim Pasternak  * struct mlxsw_i2c - device private data:
675769e39cSIdo Schimmel  * @cmd: command attributes;
686882b0aeSVadim Pasternak  * @cmd.mb_size_in: input mailbox size;
696882b0aeSVadim Pasternak  * @cmd.mb_off_in: input mailbox offset in register space;
706882b0aeSVadim Pasternak  * @cmd.mb_size_out: output mailbox size;
716882b0aeSVadim Pasternak  * @cmd.mb_off_out: output mailbox offset in register space;
726882b0aeSVadim Pasternak  * @cmd.lock: command execution lock;
736882b0aeSVadim Pasternak  * @dev: I2C device;
746882b0aeSVadim Pasternak  * @core: switch core pointer;
756882b0aeSVadim Pasternak  * @bus_info: bus info block;
763029a693SVadim Pasternak  * @block_size: maximum block size allowed to pass to under layer;
7733fa6909SVadim Pasternak  * @pdata: device platform data;
7833fa6909SVadim Pasternak  * @irq_work: interrupts work item;
7933fa6909SVadim Pasternak  * @irq: IRQ line number;
806882b0aeSVadim Pasternak  */
816882b0aeSVadim Pasternak struct mlxsw_i2c {
826882b0aeSVadim Pasternak 	struct {
836882b0aeSVadim Pasternak 		u32 mb_size_in;
846882b0aeSVadim Pasternak 		u32 mb_off_in;
856882b0aeSVadim Pasternak 		u32 mb_size_out;
866882b0aeSVadim Pasternak 		u32 mb_off_out;
876882b0aeSVadim Pasternak 		struct mutex lock;
886882b0aeSVadim Pasternak 	} cmd;
896882b0aeSVadim Pasternak 	struct device *dev;
906882b0aeSVadim Pasternak 	struct mlxsw_core *core;
916882b0aeSVadim Pasternak 	struct mlxsw_bus_info bus_info;
923029a693SVadim Pasternak 	u16 block_size;
9333fa6909SVadim Pasternak 	struct mlxreg_core_hotplug_platform_data *pdata;
9433fa6909SVadim Pasternak 	struct work_struct irq_work;
9533fa6909SVadim Pasternak 	int irq;
966882b0aeSVadim Pasternak };
976882b0aeSVadim Pasternak 
986882b0aeSVadim Pasternak #define MLXSW_I2C_READ_MSG(_client, _addr_buf, _buf, _len) {	\
996882b0aeSVadim Pasternak 	{ .addr = (_client)->addr,				\
1006882b0aeSVadim Pasternak 	  .buf = (_addr_buf),					\
1016882b0aeSVadim Pasternak 	  .len = MLXSW_I2C_ADDR_BUF_SIZE,			\
1026882b0aeSVadim Pasternak 	  .flags = 0 },						\
1036882b0aeSVadim Pasternak 	{ .addr = (_client)->addr,				\
1046882b0aeSVadim Pasternak 	  .buf = (_buf),					\
1056882b0aeSVadim Pasternak 	  .len = (_len),					\
1066882b0aeSVadim Pasternak 	  .flags = I2C_M_RD } }
1076882b0aeSVadim Pasternak 
1086882b0aeSVadim Pasternak #define MLXSW_I2C_WRITE_MSG(_client, _buf, _len)		\
1096882b0aeSVadim Pasternak 	{ .addr = (_client)->addr,				\
1106882b0aeSVadim Pasternak 	  .buf = (u8 *)(_buf),					\
1116882b0aeSVadim Pasternak 	  .len = (_len),					\
1126882b0aeSVadim Pasternak 	  .flags = 0 }
1136882b0aeSVadim Pasternak 
1146882b0aeSVadim Pasternak /* Routine converts in and out mail boxes offset and size. */
1156882b0aeSVadim Pasternak static inline void
mlxsw_i2c_convert_mbox(struct mlxsw_i2c * mlxsw_i2c,u8 * buf)1166882b0aeSVadim Pasternak mlxsw_i2c_convert_mbox(struct mlxsw_i2c *mlxsw_i2c, u8 *buf)
1176882b0aeSVadim Pasternak {
1186882b0aeSVadim Pasternak 	u32 tmp;
1196882b0aeSVadim Pasternak 
1206882b0aeSVadim Pasternak 	/* Local in/out mailboxes: 20 bits for offset, 12 for size */
1216882b0aeSVadim Pasternak 	tmp = be32_to_cpup((__be32 *) buf);
1226882b0aeSVadim Pasternak 	mlxsw_i2c->cmd.mb_off_in = tmp &
1236882b0aeSVadim Pasternak 				   GENMASK(MLXSW_I2C_MBOX_OFFSET_BITS - 1, 0);
1246882b0aeSVadim Pasternak 	mlxsw_i2c->cmd.mb_size_in = (tmp & GENMASK(31,
1256882b0aeSVadim Pasternak 					MLXSW_I2C_MBOX_OFFSET_BITS)) >>
1266882b0aeSVadim Pasternak 					MLXSW_I2C_MBOX_OFFSET_BITS;
1276882b0aeSVadim Pasternak 
1286882b0aeSVadim Pasternak 	tmp = be32_to_cpup((__be32 *) (buf + MLXSW_I2C_ADDR_WIDTH));
1296882b0aeSVadim Pasternak 	mlxsw_i2c->cmd.mb_off_out = tmp &
1306882b0aeSVadim Pasternak 				    GENMASK(MLXSW_I2C_MBOX_OFFSET_BITS - 1, 0);
1316882b0aeSVadim Pasternak 	mlxsw_i2c->cmd.mb_size_out = (tmp & GENMASK(31,
1326882b0aeSVadim Pasternak 					MLXSW_I2C_MBOX_OFFSET_BITS)) >>
1336882b0aeSVadim Pasternak 					MLXSW_I2C_MBOX_OFFSET_BITS;
1346882b0aeSVadim Pasternak }
1356882b0aeSVadim Pasternak 
1366882b0aeSVadim Pasternak /* Routine obtains register size from mail box buffer. */
mlxsw_i2c_get_reg_size(u8 * in_mbox)1376882b0aeSVadim Pasternak static inline int mlxsw_i2c_get_reg_size(u8 *in_mbox)
1386882b0aeSVadim Pasternak {
1396882b0aeSVadim Pasternak 	u16  tmp = be16_to_cpup((__be16 *) (in_mbox + MLXSW_I2C_TLV_HDR_SIZE));
1406882b0aeSVadim Pasternak 
1416882b0aeSVadim Pasternak 	return (tmp & 0x7ff) * 4 + MLXSW_I2C_TLV_HDR_SIZE;
1426882b0aeSVadim Pasternak }
1436882b0aeSVadim Pasternak 
1446882b0aeSVadim Pasternak /* Routine sets I2C device internal offset in the transaction buffer. */
mlxsw_i2c_set_slave_addr(u8 * buf,u32 off)1456882b0aeSVadim Pasternak static inline void mlxsw_i2c_set_slave_addr(u8 *buf, u32 off)
1466882b0aeSVadim Pasternak {
1476882b0aeSVadim Pasternak 	__be32 *val = (__be32 *) buf;
1486882b0aeSVadim Pasternak 
1496882b0aeSVadim Pasternak 	*val = htonl(off);
1506882b0aeSVadim Pasternak }
1516882b0aeSVadim Pasternak 
1526882b0aeSVadim Pasternak /* Routine waits until go bit is cleared. */
mlxsw_i2c_wait_go_bit(struct i2c_client * client,struct mlxsw_i2c * mlxsw_i2c,u8 * p_status)1536882b0aeSVadim Pasternak static int mlxsw_i2c_wait_go_bit(struct i2c_client *client,
1546882b0aeSVadim Pasternak 				 struct mlxsw_i2c *mlxsw_i2c, u8 *p_status)
1556882b0aeSVadim Pasternak {
1566882b0aeSVadim Pasternak 	u8 addr_buf[MLXSW_I2C_ADDR_BUF_SIZE];
1576882b0aeSVadim Pasternak 	u8 buf[MLXSW_I2C_READ_SEMA_SIZE];
1586882b0aeSVadim Pasternak 	int len = MLXSW_I2C_READ_SEMA_SIZE;
1596882b0aeSVadim Pasternak 	struct i2c_msg read_sema[] =
1606882b0aeSVadim Pasternak 		MLXSW_I2C_READ_MSG(client, addr_buf, buf, len);
1616882b0aeSVadim Pasternak 	bool wait_done = false;
1626882b0aeSVadim Pasternak 	unsigned long end;
1636882b0aeSVadim Pasternak 	int i = 0, err;
1646882b0aeSVadim Pasternak 
1656882b0aeSVadim Pasternak 	mlxsw_i2c_set_slave_addr(addr_buf, MLXSW_I2C_CIR2_OFF_STATUS);
1666882b0aeSVadim Pasternak 
1676882b0aeSVadim Pasternak 	end = jiffies + msecs_to_jiffies(MLXSW_I2C_TIMEOUT_MSECS);
1686882b0aeSVadim Pasternak 	do {
1696882b0aeSVadim Pasternak 		u32 ctrl;
1706882b0aeSVadim Pasternak 
1716882b0aeSVadim Pasternak 		err = i2c_transfer(client->adapter, read_sema,
1726882b0aeSVadim Pasternak 				   ARRAY_SIZE(read_sema));
1736882b0aeSVadim Pasternak 
1746882b0aeSVadim Pasternak 		ctrl = be32_to_cpu(*(__be32 *) buf);
1756882b0aeSVadim Pasternak 		if (err == ARRAY_SIZE(read_sema)) {
1766882b0aeSVadim Pasternak 			if (!(ctrl & MLXSW_I2C_GO_BIT)) {
1776882b0aeSVadim Pasternak 				wait_done = true;
1786882b0aeSVadim Pasternak 				*p_status = ctrl >>
1796882b0aeSVadim Pasternak 					    MLXSW_I2C_CIR_CTRL_STATUS_SHIFT;
1806882b0aeSVadim Pasternak 				break;
1816882b0aeSVadim Pasternak 			}
1826882b0aeSVadim Pasternak 		}
1836882b0aeSVadim Pasternak 		cond_resched();
1846882b0aeSVadim Pasternak 	} while ((time_before(jiffies, end)) || (i++ < MLXSW_I2C_RETRY));
1856882b0aeSVadim Pasternak 
1866882b0aeSVadim Pasternak 	if (wait_done) {
1876882b0aeSVadim Pasternak 		if (*p_status)
1886882b0aeSVadim Pasternak 			err = -EIO;
1896882b0aeSVadim Pasternak 	} else {
1906882b0aeSVadim Pasternak 		return -ETIMEDOUT;
1916882b0aeSVadim Pasternak 	}
1926882b0aeSVadim Pasternak 
1936882b0aeSVadim Pasternak 	return err > 0 ? 0 : err;
1946882b0aeSVadim Pasternak }
1956882b0aeSVadim Pasternak 
19627758c80SVadim Pasternak /* Routine posts a command to ASIC through mail box. */
mlxsw_i2c_write_cmd(struct i2c_client * client,struct mlxsw_i2c * mlxsw_i2c,int immediate)1976882b0aeSVadim Pasternak static int mlxsw_i2c_write_cmd(struct i2c_client *client,
1986882b0aeSVadim Pasternak 			       struct mlxsw_i2c *mlxsw_i2c,
1996882b0aeSVadim Pasternak 			       int immediate)
2006882b0aeSVadim Pasternak {
2016882b0aeSVadim Pasternak 	__be32 push_cmd_buf[MLXSW_I2C_PUSH_CMD_SIZE / 4] = {
2026882b0aeSVadim Pasternak 		0, cpu_to_be32(MLXSW_I2C_PUSH_IMM_CMD)
2036882b0aeSVadim Pasternak 	};
2046882b0aeSVadim Pasternak 	__be32 prep_cmd_buf[MLXSW_I2C_PREP_SIZE / 4] = {
2056882b0aeSVadim Pasternak 		0, 0, 0, 0, 0, 0,
2066882b0aeSVadim Pasternak 		cpu_to_be32(client->adapter->nr & 0xffff),
2076882b0aeSVadim Pasternak 		cpu_to_be32(MLXSW_I2C_SET_IMM_CMD)
2086882b0aeSVadim Pasternak 	};
2096882b0aeSVadim Pasternak 	struct i2c_msg push_cmd =
2106882b0aeSVadim Pasternak 		MLXSW_I2C_WRITE_MSG(client, push_cmd_buf,
2116882b0aeSVadim Pasternak 				    MLXSW_I2C_PUSH_CMD_SIZE);
2126882b0aeSVadim Pasternak 	struct i2c_msg prep_cmd =
2136882b0aeSVadim Pasternak 		MLXSW_I2C_WRITE_MSG(client, prep_cmd_buf, MLXSW_I2C_PREP_SIZE);
2146882b0aeSVadim Pasternak 	int err;
2156882b0aeSVadim Pasternak 
2166882b0aeSVadim Pasternak 	if (!immediate) {
2176882b0aeSVadim Pasternak 		push_cmd_buf[1] = cpu_to_be32(MLXSW_I2C_PUSH_CMD);
2186882b0aeSVadim Pasternak 		prep_cmd_buf[7] = cpu_to_be32(MLXSW_I2C_SET_CMD);
2196882b0aeSVadim Pasternak 	}
2206882b0aeSVadim Pasternak 	mlxsw_i2c_set_slave_addr((u8 *)prep_cmd_buf,
2216882b0aeSVadim Pasternak 				 MLXSW_I2C_CIR2_BASE);
2226882b0aeSVadim Pasternak 	mlxsw_i2c_set_slave_addr((u8 *)push_cmd_buf,
2236882b0aeSVadim Pasternak 				 MLXSW_I2C_CIR2_OFF_STATUS);
2246882b0aeSVadim Pasternak 
2256882b0aeSVadim Pasternak 	/* Prepare Command Interface Register for transaction */
2266882b0aeSVadim Pasternak 	err = i2c_transfer(client->adapter, &prep_cmd, 1);
2276882b0aeSVadim Pasternak 	if (err < 0)
2286882b0aeSVadim Pasternak 		return err;
2296882b0aeSVadim Pasternak 	else if (err != 1)
2306882b0aeSVadim Pasternak 		return -EIO;
2316882b0aeSVadim Pasternak 
2326882b0aeSVadim Pasternak 	/* Write out Command Interface Register GO bit to push transaction */
2336882b0aeSVadim Pasternak 	err = i2c_transfer(client->adapter, &push_cmd, 1);
2346882b0aeSVadim Pasternak 	if (err < 0)
2356882b0aeSVadim Pasternak 		return err;
2366882b0aeSVadim Pasternak 	else if (err != 1)
2376882b0aeSVadim Pasternak 		return -EIO;
2386882b0aeSVadim Pasternak 
2396882b0aeSVadim Pasternak 	return 0;
2406882b0aeSVadim Pasternak }
2416882b0aeSVadim Pasternak 
24295b75cbdSVadim Pasternak /* Routine posts initialization command to ASIC through mail box. */
24395b75cbdSVadim Pasternak static int
mlxsw_i2c_write_init_cmd(struct i2c_client * client,struct mlxsw_i2c * mlxsw_i2c,u16 opcode,u32 in_mod)24495b75cbdSVadim Pasternak mlxsw_i2c_write_init_cmd(struct i2c_client *client,
24595b75cbdSVadim Pasternak 			 struct mlxsw_i2c *mlxsw_i2c, u16 opcode, u32 in_mod)
24695b75cbdSVadim Pasternak {
24795b75cbdSVadim Pasternak 	__be32 push_cmd_buf[MLXSW_I2C_PUSH_CMD_SIZE / 4] = {
24895b75cbdSVadim Pasternak 		0, cpu_to_be32(MLXSW_I2C_PUSH_EVENT_CMD)
24995b75cbdSVadim Pasternak 	};
25095b75cbdSVadim Pasternak 	__be32 prep_cmd_buf[MLXSW_I2C_PREP_SIZE / 4] = {
25195b75cbdSVadim Pasternak 		0, 0, 0, 0, 0, 0,
25295b75cbdSVadim Pasternak 		cpu_to_be32(client->adapter->nr & 0xffff),
25395b75cbdSVadim Pasternak 		cpu_to_be32(MLXSW_I2C_SET_EVENT_CMD)
25495b75cbdSVadim Pasternak 	};
25595b75cbdSVadim Pasternak 	struct i2c_msg push_cmd =
25695b75cbdSVadim Pasternak 		MLXSW_I2C_WRITE_MSG(client, push_cmd_buf,
25795b75cbdSVadim Pasternak 				    MLXSW_I2C_PUSH_CMD_SIZE);
25895b75cbdSVadim Pasternak 	struct i2c_msg prep_cmd =
25995b75cbdSVadim Pasternak 		MLXSW_I2C_WRITE_MSG(client, prep_cmd_buf, MLXSW_I2C_PREP_SIZE);
26095b75cbdSVadim Pasternak 	u8 status;
26195b75cbdSVadim Pasternak 	int err;
26295b75cbdSVadim Pasternak 
26395b75cbdSVadim Pasternak 	push_cmd_buf[1] = cpu_to_be32(MLXSW_I2C_PUSH_EVENT_CMD | opcode);
26495b75cbdSVadim Pasternak 	prep_cmd_buf[3] = cpu_to_be32(in_mod);
26595b75cbdSVadim Pasternak 	prep_cmd_buf[7] = cpu_to_be32(MLXSW_I2C_GO_BIT | opcode);
26695b75cbdSVadim Pasternak 	mlxsw_i2c_set_slave_addr((u8 *)prep_cmd_buf,
26795b75cbdSVadim Pasternak 				 MLXSW_I2C_CIR2_BASE);
26895b75cbdSVadim Pasternak 	mlxsw_i2c_set_slave_addr((u8 *)push_cmd_buf,
26995b75cbdSVadim Pasternak 				 MLXSW_I2C_CIR2_OFF_STATUS);
27095b75cbdSVadim Pasternak 
27195b75cbdSVadim Pasternak 	/* Prepare Command Interface Register for transaction */
27295b75cbdSVadim Pasternak 	err = i2c_transfer(client->adapter, &prep_cmd, 1);
27395b75cbdSVadim Pasternak 	if (err < 0)
27495b75cbdSVadim Pasternak 		return err;
27595b75cbdSVadim Pasternak 	else if (err != 1)
27695b75cbdSVadim Pasternak 		return -EIO;
27795b75cbdSVadim Pasternak 
27895b75cbdSVadim Pasternak 	/* Write out Command Interface Register GO bit to push transaction */
27995b75cbdSVadim Pasternak 	err = i2c_transfer(client->adapter, &push_cmd, 1);
28095b75cbdSVadim Pasternak 	if (err < 0)
28195b75cbdSVadim Pasternak 		return err;
28295b75cbdSVadim Pasternak 	else if (err != 1)
28395b75cbdSVadim Pasternak 		return -EIO;
28495b75cbdSVadim Pasternak 
28595b75cbdSVadim Pasternak 	/* Wait until go bit is cleared. */
28695b75cbdSVadim Pasternak 	err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, &status);
28795b75cbdSVadim Pasternak 	if (err) {
28895b75cbdSVadim Pasternak 		dev_err(&client->dev, "HW semaphore is not released");
28995b75cbdSVadim Pasternak 		return err;
29095b75cbdSVadim Pasternak 	}
29195b75cbdSVadim Pasternak 
29295b75cbdSVadim Pasternak 	/* Validate transaction completion status. */
29395b75cbdSVadim Pasternak 	if (status) {
29495b75cbdSVadim Pasternak 		dev_err(&client->dev, "Bad transaction completion status %x\n",
29595b75cbdSVadim Pasternak 			status);
29695b75cbdSVadim Pasternak 		return -EIO;
29795b75cbdSVadim Pasternak 	}
29895b75cbdSVadim Pasternak 
29995b75cbdSVadim Pasternak 	return 0;
30095b75cbdSVadim Pasternak }
30195b75cbdSVadim Pasternak 
3026882b0aeSVadim Pasternak /* Routine obtains mail box offsets from ASIC register space. */
mlxsw_i2c_get_mbox(struct i2c_client * client,struct mlxsw_i2c * mlxsw_i2c)3036882b0aeSVadim Pasternak static int mlxsw_i2c_get_mbox(struct i2c_client *client,
3046882b0aeSVadim Pasternak 			      struct mlxsw_i2c *mlxsw_i2c)
3056882b0aeSVadim Pasternak {
3066882b0aeSVadim Pasternak 	u8 addr_buf[MLXSW_I2C_ADDR_BUF_SIZE];
3076882b0aeSVadim Pasternak 	u8 buf[MLXSW_I2C_MBOX_SIZE];
3086882b0aeSVadim Pasternak 	struct i2c_msg mbox_cmd[] =
3096882b0aeSVadim Pasternak 		MLXSW_I2C_READ_MSG(client, addr_buf, buf, MLXSW_I2C_MBOX_SIZE);
3106882b0aeSVadim Pasternak 	int err;
3116882b0aeSVadim Pasternak 
3126882b0aeSVadim Pasternak 	/* Read mail boxes offsets. */
3136882b0aeSVadim Pasternak 	mlxsw_i2c_set_slave_addr(addr_buf, MLXSW_I2C_CIR2_BASE);
3146882b0aeSVadim Pasternak 	err = i2c_transfer(client->adapter, mbox_cmd, 2);
3156882b0aeSVadim Pasternak 	if (err != 2) {
3166882b0aeSVadim Pasternak 		dev_err(&client->dev, "Could not obtain mail boxes\n");
3176882b0aeSVadim Pasternak 		if (!err)
3186882b0aeSVadim Pasternak 			return -EIO;
3196882b0aeSVadim Pasternak 		else
3206882b0aeSVadim Pasternak 			return err;
3216882b0aeSVadim Pasternak 	}
3226882b0aeSVadim Pasternak 
3236882b0aeSVadim Pasternak 	/* Convert mail boxes. */
3246882b0aeSVadim Pasternak 	mlxsw_i2c_convert_mbox(mlxsw_i2c, &buf[MLXSW_I2C_MBOX_OUT_PARAM_OFF]);
3256882b0aeSVadim Pasternak 
3266882b0aeSVadim Pasternak 	return err;
3276882b0aeSVadim Pasternak }
3286882b0aeSVadim Pasternak 
3296882b0aeSVadim Pasternak /* Routine sends I2C write transaction to ASIC device. */
3306882b0aeSVadim Pasternak static int
mlxsw_i2c_write(struct device * dev,size_t in_mbox_size,u8 * in_mbox,int num,u8 * p_status)3316882b0aeSVadim Pasternak mlxsw_i2c_write(struct device *dev, size_t in_mbox_size, u8 *in_mbox, int num,
3326882b0aeSVadim Pasternak 		u8 *p_status)
3336882b0aeSVadim Pasternak {
3346882b0aeSVadim Pasternak 	struct i2c_client *client = to_i2c_client(dev);
3356882b0aeSVadim Pasternak 	struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client);
3366882b0aeSVadim Pasternak 	unsigned long timeout = msecs_to_jiffies(MLXSW_I2C_TIMEOUT_MSECS);
3376882b0aeSVadim Pasternak 	int off = mlxsw_i2c->cmd.mb_off_in, chunk_size, i, j;
3386882b0aeSVadim Pasternak 	unsigned long end;
3393029a693SVadim Pasternak 	u8 *tran_buf;
3406882b0aeSVadim Pasternak 	struct i2c_msg write_tran =
3413029a693SVadim Pasternak 		MLXSW_I2C_WRITE_MSG(client, NULL, MLXSW_I2C_PUSH_CMD_SIZE);
3426882b0aeSVadim Pasternak 	int err;
3436882b0aeSVadim Pasternak 
3443029a693SVadim Pasternak 	tran_buf = kmalloc(mlxsw_i2c->block_size + MLXSW_I2C_ADDR_BUF_SIZE,
3453029a693SVadim Pasternak 			   GFP_KERNEL);
3463029a693SVadim Pasternak 	if (!tran_buf)
3473029a693SVadim Pasternak 		return -ENOMEM;
3483029a693SVadim Pasternak 
3493029a693SVadim Pasternak 	write_tran.buf = tran_buf;
3506882b0aeSVadim Pasternak 	for (i = 0; i < num; i++) {
3513029a693SVadim Pasternak 		chunk_size = (in_mbox_size > mlxsw_i2c->block_size) ?
3523029a693SVadim Pasternak 			     mlxsw_i2c->block_size : in_mbox_size;
3536882b0aeSVadim Pasternak 		write_tran.len = MLXSW_I2C_ADDR_WIDTH + chunk_size;
3546882b0aeSVadim Pasternak 		mlxsw_i2c_set_slave_addr(tran_buf, off);
3556882b0aeSVadim Pasternak 		memcpy(&tran_buf[MLXSW_I2C_ADDR_BUF_SIZE], in_mbox +
3563029a693SVadim Pasternak 		       mlxsw_i2c->block_size * i, chunk_size);
3576882b0aeSVadim Pasternak 
3586882b0aeSVadim Pasternak 		j = 0;
3596882b0aeSVadim Pasternak 		end = jiffies + timeout;
3606882b0aeSVadim Pasternak 		do {
3616882b0aeSVadim Pasternak 			err = i2c_transfer(client->adapter, &write_tran, 1);
3626882b0aeSVadim Pasternak 			if (err == 1)
3636882b0aeSVadim Pasternak 				break;
3646882b0aeSVadim Pasternak 
3656882b0aeSVadim Pasternak 			cond_resched();
3666882b0aeSVadim Pasternak 		} while ((time_before(jiffies, end)) ||
3676882b0aeSVadim Pasternak 			 (j++ < MLXSW_I2C_RETRY));
3686882b0aeSVadim Pasternak 
3696882b0aeSVadim Pasternak 		if (err != 1) {
3703029a693SVadim Pasternak 			if (!err) {
3716882b0aeSVadim Pasternak 				err = -EIO;
3723029a693SVadim Pasternak 				goto mlxsw_i2c_write_exit;
3733029a693SVadim Pasternak 			}
3746882b0aeSVadim Pasternak 		}
3756882b0aeSVadim Pasternak 
3766882b0aeSVadim Pasternak 		off += chunk_size;
3776882b0aeSVadim Pasternak 		in_mbox_size -= chunk_size;
3786882b0aeSVadim Pasternak 	}
3796882b0aeSVadim Pasternak 
3806882b0aeSVadim Pasternak 	/* Prepare and write out Command Interface Register for transaction. */
3816882b0aeSVadim Pasternak 	err = mlxsw_i2c_write_cmd(client, mlxsw_i2c, 0);
3826882b0aeSVadim Pasternak 	if (err) {
3836882b0aeSVadim Pasternak 		dev_err(&client->dev, "Could not start transaction");
3843029a693SVadim Pasternak 		err = -EIO;
3853029a693SVadim Pasternak 		goto mlxsw_i2c_write_exit;
3866882b0aeSVadim Pasternak 	}
3876882b0aeSVadim Pasternak 
3886882b0aeSVadim Pasternak 	/* Wait until go bit is cleared. */
3896882b0aeSVadim Pasternak 	err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, p_status);
3906882b0aeSVadim Pasternak 	if (err) {
3916882b0aeSVadim Pasternak 		dev_err(&client->dev, "HW semaphore is not released");
3923029a693SVadim Pasternak 		goto mlxsw_i2c_write_exit;
3936882b0aeSVadim Pasternak 	}
3946882b0aeSVadim Pasternak 
3956882b0aeSVadim Pasternak 	/* Validate transaction completion status. */
3966882b0aeSVadim Pasternak 	if (*p_status) {
3976882b0aeSVadim Pasternak 		dev_err(&client->dev, "Bad transaction completion status %x\n",
3986882b0aeSVadim Pasternak 			*p_status);
3993029a693SVadim Pasternak 		err = -EIO;
4006882b0aeSVadim Pasternak 	}
4016882b0aeSVadim Pasternak 
4023029a693SVadim Pasternak mlxsw_i2c_write_exit:
4033029a693SVadim Pasternak 	kfree(tran_buf);
4043029a693SVadim Pasternak 	return err;
4056882b0aeSVadim Pasternak }
4066882b0aeSVadim Pasternak 
4076882b0aeSVadim Pasternak /* Routine executes I2C command. */
4086882b0aeSVadim Pasternak static int
mlxsw_i2c_cmd(struct device * dev,u16 opcode,u32 in_mod,size_t in_mbox_size,u8 * in_mbox,size_t out_mbox_size,u8 * out_mbox,u8 * status)40995b75cbdSVadim Pasternak mlxsw_i2c_cmd(struct device *dev, u16 opcode, u32 in_mod, size_t in_mbox_size,
41095b75cbdSVadim Pasternak 	      u8 *in_mbox, size_t out_mbox_size, u8 *out_mbox, u8 *status)
4116882b0aeSVadim Pasternak {
4126882b0aeSVadim Pasternak 	struct i2c_client *client = to_i2c_client(dev);
4136882b0aeSVadim Pasternak 	struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client);
4146882b0aeSVadim Pasternak 	unsigned long timeout = msecs_to_jiffies(MLXSW_I2C_TIMEOUT_MSECS);
4156882b0aeSVadim Pasternak 	u8 tran_buf[MLXSW_I2C_ADDR_BUF_SIZE];
4166882b0aeSVadim Pasternak 	int num, chunk_size, reg_size, i, j;
4176882b0aeSVadim Pasternak 	int off = mlxsw_i2c->cmd.mb_off_out;
4186882b0aeSVadim Pasternak 	unsigned long end;
4196882b0aeSVadim Pasternak 	struct i2c_msg read_tran[] =
4206882b0aeSVadim Pasternak 		MLXSW_I2C_READ_MSG(client, tran_buf, NULL, 0);
4216882b0aeSVadim Pasternak 	int err;
4226882b0aeSVadim Pasternak 
4236882b0aeSVadim Pasternak 	WARN_ON(in_mbox_size % sizeof(u32) || out_mbox_size % sizeof(u32));
4246882b0aeSVadim Pasternak 
42595b75cbdSVadim Pasternak 	if (in_mbox) {
4266882b0aeSVadim Pasternak 		reg_size = mlxsw_i2c_get_reg_size(in_mbox);
427*1f732863SVadim Pasternak 		num = DIV_ROUND_UP(reg_size, mlxsw_i2c->block_size);
4286882b0aeSVadim Pasternak 
4296882b0aeSVadim Pasternak 		if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) {
4306882b0aeSVadim Pasternak 			dev_err(&client->dev, "Could not acquire lock");
4316882b0aeSVadim Pasternak 			return -EINVAL;
4326882b0aeSVadim Pasternak 		}
4336882b0aeSVadim Pasternak 
4346882b0aeSVadim Pasternak 		err = mlxsw_i2c_write(dev, reg_size, in_mbox, num, status);
4356882b0aeSVadim Pasternak 		if (err)
4366882b0aeSVadim Pasternak 			goto cmd_fail;
4376882b0aeSVadim Pasternak 
4386882b0aeSVadim Pasternak 		/* No out mailbox is case of write transaction. */
4396882b0aeSVadim Pasternak 		if (!out_mbox) {
4406882b0aeSVadim Pasternak 			mutex_unlock(&mlxsw_i2c->cmd.lock);
4416882b0aeSVadim Pasternak 			return 0;
4426882b0aeSVadim Pasternak 		}
44395b75cbdSVadim Pasternak 	} else {
44495b75cbdSVadim Pasternak 		/* No input mailbox is case of initialization query command. */
44595b75cbdSVadim Pasternak 		reg_size = MLXSW_I2C_MAX_DATA_SIZE;
446146c7c33SVadim Pasternak 		num = DIV_ROUND_UP(reg_size, mlxsw_i2c->block_size);
44795b75cbdSVadim Pasternak 
44895b75cbdSVadim Pasternak 		if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) {
44995b75cbdSVadim Pasternak 			dev_err(&client->dev, "Could not acquire lock");
45095b75cbdSVadim Pasternak 			return -EINVAL;
45195b75cbdSVadim Pasternak 		}
45295b75cbdSVadim Pasternak 
45395b75cbdSVadim Pasternak 		err = mlxsw_i2c_write_init_cmd(client, mlxsw_i2c, opcode,
45495b75cbdSVadim Pasternak 					       in_mod);
45595b75cbdSVadim Pasternak 		if (err)
45695b75cbdSVadim Pasternak 			goto cmd_fail;
45795b75cbdSVadim Pasternak 	}
4586882b0aeSVadim Pasternak 
4596882b0aeSVadim Pasternak 	/* Send read transaction to get output mailbox content. */
4606882b0aeSVadim Pasternak 	read_tran[1].buf = out_mbox;
4616882b0aeSVadim Pasternak 	for (i = 0; i < num; i++) {
4623029a693SVadim Pasternak 		chunk_size = (reg_size > mlxsw_i2c->block_size) ?
4633029a693SVadim Pasternak 			     mlxsw_i2c->block_size : reg_size;
4646882b0aeSVadim Pasternak 		read_tran[1].len = chunk_size;
4656882b0aeSVadim Pasternak 		mlxsw_i2c_set_slave_addr(tran_buf, off);
4666882b0aeSVadim Pasternak 
4676882b0aeSVadim Pasternak 		j = 0;
4686882b0aeSVadim Pasternak 		end = jiffies + timeout;
4696882b0aeSVadim Pasternak 		do {
4706882b0aeSVadim Pasternak 			err = i2c_transfer(client->adapter, read_tran,
4716882b0aeSVadim Pasternak 					   ARRAY_SIZE(read_tran));
4726882b0aeSVadim Pasternak 			if (err == ARRAY_SIZE(read_tran))
4736882b0aeSVadim Pasternak 				break;
4746882b0aeSVadim Pasternak 
4756882b0aeSVadim Pasternak 			cond_resched();
4766882b0aeSVadim Pasternak 		} while ((time_before(jiffies, end)) ||
4776882b0aeSVadim Pasternak 			 (j++ < MLXSW_I2C_RETRY));
4786882b0aeSVadim Pasternak 
4796882b0aeSVadim Pasternak 		if (err != ARRAY_SIZE(read_tran)) {
4806882b0aeSVadim Pasternak 			if (!err)
4816882b0aeSVadim Pasternak 				err = -EIO;
4826882b0aeSVadim Pasternak 
4836882b0aeSVadim Pasternak 			goto cmd_fail;
4846882b0aeSVadim Pasternak 		}
4856882b0aeSVadim Pasternak 
4866882b0aeSVadim Pasternak 		off += chunk_size;
4876882b0aeSVadim Pasternak 		reg_size -= chunk_size;
4886882b0aeSVadim Pasternak 		read_tran[1].buf += chunk_size;
4896882b0aeSVadim Pasternak 	}
4906882b0aeSVadim Pasternak 
4916882b0aeSVadim Pasternak 	mutex_unlock(&mlxsw_i2c->cmd.lock);
4926882b0aeSVadim Pasternak 
4936882b0aeSVadim Pasternak 	return 0;
4946882b0aeSVadim Pasternak 
4956882b0aeSVadim Pasternak cmd_fail:
4966882b0aeSVadim Pasternak 	mutex_unlock(&mlxsw_i2c->cmd.lock);
4976882b0aeSVadim Pasternak 	return err;
4986882b0aeSVadim Pasternak }
4996882b0aeSVadim Pasternak 
mlxsw_i2c_cmd_exec(void * bus_priv,u16 opcode,u8 opcode_mod,u32 in_mod,bool out_mbox_direct,char * in_mbox,size_t in_mbox_size,char * out_mbox,size_t out_mbox_size,u8 * status)5006882b0aeSVadim Pasternak static int mlxsw_i2c_cmd_exec(void *bus_priv, u16 opcode, u8 opcode_mod,
5016882b0aeSVadim Pasternak 			      u32 in_mod, bool out_mbox_direct,
5026882b0aeSVadim Pasternak 			      char *in_mbox, size_t in_mbox_size,
5036882b0aeSVadim Pasternak 			      char *out_mbox, size_t out_mbox_size,
5046882b0aeSVadim Pasternak 			      u8 *status)
5056882b0aeSVadim Pasternak {
5066882b0aeSVadim Pasternak 	struct mlxsw_i2c *mlxsw_i2c = bus_priv;
5076882b0aeSVadim Pasternak 
50895b75cbdSVadim Pasternak 	return mlxsw_i2c_cmd(mlxsw_i2c->dev, opcode, in_mod, in_mbox_size,
50995b75cbdSVadim Pasternak 			     in_mbox, out_mbox_size, out_mbox, status);
5106882b0aeSVadim Pasternak }
5116882b0aeSVadim Pasternak 
mlxsw_i2c_skb_transmit_busy(void * bus_priv,const struct mlxsw_tx_info * tx_info)5126882b0aeSVadim Pasternak static bool mlxsw_i2c_skb_transmit_busy(void *bus_priv,
5136882b0aeSVadim Pasternak 					const struct mlxsw_tx_info *tx_info)
5146882b0aeSVadim Pasternak {
5156882b0aeSVadim Pasternak 	return false;
5166882b0aeSVadim Pasternak }
5176882b0aeSVadim Pasternak 
mlxsw_i2c_skb_transmit(void * bus_priv,struct sk_buff * skb,const struct mlxsw_tx_info * tx_info)5186882b0aeSVadim Pasternak static int mlxsw_i2c_skb_transmit(void *bus_priv, struct sk_buff *skb,
5196882b0aeSVadim Pasternak 				  const struct mlxsw_tx_info *tx_info)
5206882b0aeSVadim Pasternak {
5216882b0aeSVadim Pasternak 	return 0;
5226882b0aeSVadim Pasternak }
5236882b0aeSVadim Pasternak 
5246882b0aeSVadim Pasternak static int
mlxsw_i2c_init(void * bus_priv,struct mlxsw_core * mlxsw_core,const struct mlxsw_config_profile * profile,struct mlxsw_res * res)5256882b0aeSVadim Pasternak mlxsw_i2c_init(void *bus_priv, struct mlxsw_core *mlxsw_core,
5266882b0aeSVadim Pasternak 	       const struct mlxsw_config_profile *profile,
527f43d9d9bSVadim Pasternak 	       struct mlxsw_res *res)
5286882b0aeSVadim Pasternak {
5296882b0aeSVadim Pasternak 	struct mlxsw_i2c *mlxsw_i2c = bus_priv;
5306a986993SVadim Pasternak 	char *mbox;
5316a986993SVadim Pasternak 	int err;
5326882b0aeSVadim Pasternak 
5336882b0aeSVadim Pasternak 	mlxsw_i2c->core = mlxsw_core;
5346882b0aeSVadim Pasternak 
5356a986993SVadim Pasternak 	mbox = mlxsw_cmd_mbox_alloc();
5366a986993SVadim Pasternak 	if (!mbox)
5376a986993SVadim Pasternak 		return -ENOMEM;
5386a986993SVadim Pasternak 
5399bbd7efbSVadim Pasternak 	err = mlxsw_cmd_query_fw(mlxsw_core, mbox);
5409bbd7efbSVadim Pasternak 	if (err)
5419bbd7efbSVadim Pasternak 		goto mbox_put;
5429bbd7efbSVadim Pasternak 
5439bbd7efbSVadim Pasternak 	mlxsw_i2c->bus_info.fw_rev.major =
5449bbd7efbSVadim Pasternak 		mlxsw_cmd_mbox_query_fw_fw_rev_major_get(mbox);
5459bbd7efbSVadim Pasternak 	mlxsw_i2c->bus_info.fw_rev.minor =
5469bbd7efbSVadim Pasternak 		mlxsw_cmd_mbox_query_fw_fw_rev_minor_get(mbox);
5479bbd7efbSVadim Pasternak 	mlxsw_i2c->bus_info.fw_rev.subminor =
5489bbd7efbSVadim Pasternak 		mlxsw_cmd_mbox_query_fw_fw_rev_subminor_get(mbox);
5499bbd7efbSVadim Pasternak 
5506a986993SVadim Pasternak 	err = mlxsw_core_resources_query(mlxsw_core, mbox, res);
5516a986993SVadim Pasternak 
5529bbd7efbSVadim Pasternak mbox_put:
5536a986993SVadim Pasternak 	mlxsw_cmd_mbox_free(mbox);
5546a986993SVadim Pasternak 	return err;
5556882b0aeSVadim Pasternak }
5566882b0aeSVadim Pasternak 
mlxsw_i2c_fini(void * bus_priv)5576882b0aeSVadim Pasternak static void mlxsw_i2c_fini(void *bus_priv)
5586882b0aeSVadim Pasternak {
5596882b0aeSVadim Pasternak 	struct mlxsw_i2c *mlxsw_i2c = bus_priv;
5606882b0aeSVadim Pasternak 
5616882b0aeSVadim Pasternak 	mlxsw_i2c->core = NULL;
5626882b0aeSVadim Pasternak }
5636882b0aeSVadim Pasternak 
mlxsw_i2c_work_handler(struct work_struct * work)56433fa6909SVadim Pasternak static void mlxsw_i2c_work_handler(struct work_struct *work)
56533fa6909SVadim Pasternak {
56633fa6909SVadim Pasternak 	struct mlxsw_i2c *mlxsw_i2c;
56733fa6909SVadim Pasternak 
56833fa6909SVadim Pasternak 	mlxsw_i2c = container_of(work, struct mlxsw_i2c, irq_work);
56933fa6909SVadim Pasternak 	mlxsw_core_irq_event_handlers_call(mlxsw_i2c->core);
57033fa6909SVadim Pasternak }
57133fa6909SVadim Pasternak 
mlxsw_i2c_irq_handler(int irq,void * dev)57233fa6909SVadim Pasternak static irqreturn_t mlxsw_i2c_irq_handler(int irq, void *dev)
57333fa6909SVadim Pasternak {
57433fa6909SVadim Pasternak 	struct mlxsw_i2c *mlxsw_i2c = dev;
57533fa6909SVadim Pasternak 
57633fa6909SVadim Pasternak 	mlxsw_core_schedule_work(&mlxsw_i2c->irq_work);
57733fa6909SVadim Pasternak 
57833fa6909SVadim Pasternak 	/* Interrupt handler shares IRQ line with 'main' interrupt handler.
57933fa6909SVadim Pasternak 	 * Return here IRQ_NONE, while main handler will return IRQ_HANDLED.
58033fa6909SVadim Pasternak 	 */
58133fa6909SVadim Pasternak 	return IRQ_NONE;
58233fa6909SVadim Pasternak }
58333fa6909SVadim Pasternak 
mlxsw_i2c_irq_init(struct mlxsw_i2c * mlxsw_i2c,u8 addr)58433fa6909SVadim Pasternak static int mlxsw_i2c_irq_init(struct mlxsw_i2c *mlxsw_i2c, u8 addr)
58533fa6909SVadim Pasternak {
58633fa6909SVadim Pasternak 	int err;
58733fa6909SVadim Pasternak 
58833fa6909SVadim Pasternak 	/* Initialize interrupt handler if system hotplug driver is reachable,
58933fa6909SVadim Pasternak 	 * otherwise interrupt line is not enabled and interrupts will not be
59033fa6909SVadim Pasternak 	 * raised to CPU. Also request_irq() call will be not valid.
59133fa6909SVadim Pasternak 	 */
59233fa6909SVadim Pasternak 	if (!IS_REACHABLE(CONFIG_MLXREG_HOTPLUG))
59333fa6909SVadim Pasternak 		return 0;
59433fa6909SVadim Pasternak 
59533fa6909SVadim Pasternak 	/* Set default interrupt line. */
59633fa6909SVadim Pasternak 	if (mlxsw_i2c->pdata && mlxsw_i2c->pdata->irq)
59733fa6909SVadim Pasternak 		mlxsw_i2c->irq = mlxsw_i2c->pdata->irq;
59833fa6909SVadim Pasternak 	else if (addr == MLXSW_FAST_I2C_SLAVE)
59933fa6909SVadim Pasternak 		mlxsw_i2c->irq = MLXSW_I2C_DEFAULT_IRQ;
60033fa6909SVadim Pasternak 
60133fa6909SVadim Pasternak 	if (!mlxsw_i2c->irq)
60233fa6909SVadim Pasternak 		return 0;
60333fa6909SVadim Pasternak 
60433fa6909SVadim Pasternak 	INIT_WORK(&mlxsw_i2c->irq_work, mlxsw_i2c_work_handler);
60533fa6909SVadim Pasternak 	err = request_irq(mlxsw_i2c->irq, mlxsw_i2c_irq_handler,
60633fa6909SVadim Pasternak 			  IRQF_TRIGGER_FALLING | IRQF_SHARED, "mlxsw-i2c",
60733fa6909SVadim Pasternak 			  mlxsw_i2c);
60833fa6909SVadim Pasternak 	if (err) {
60933fa6909SVadim Pasternak 		dev_err(mlxsw_i2c->bus_info.dev, "Failed to request irq: %d\n",
61033fa6909SVadim Pasternak 			err);
61133fa6909SVadim Pasternak 		return err;
61233fa6909SVadim Pasternak 	}
61333fa6909SVadim Pasternak 
61433fa6909SVadim Pasternak 	return 0;
61533fa6909SVadim Pasternak }
61633fa6909SVadim Pasternak 
mlxsw_i2c_irq_fini(struct mlxsw_i2c * mlxsw_i2c)61733fa6909SVadim Pasternak static void mlxsw_i2c_irq_fini(struct mlxsw_i2c *mlxsw_i2c)
61833fa6909SVadim Pasternak {
61933fa6909SVadim Pasternak 	if (!IS_REACHABLE(CONFIG_MLXREG_HOTPLUG) || !mlxsw_i2c->irq)
62033fa6909SVadim Pasternak 		return;
62133fa6909SVadim Pasternak 	cancel_work_sync(&mlxsw_i2c->irq_work);
62233fa6909SVadim Pasternak 	free_irq(mlxsw_i2c->irq, mlxsw_i2c);
62333fa6909SVadim Pasternak }
62433fa6909SVadim Pasternak 
6256882b0aeSVadim Pasternak static const struct mlxsw_bus mlxsw_i2c_bus = {
6266882b0aeSVadim Pasternak 	.kind			= "i2c",
6276882b0aeSVadim Pasternak 	.init			= mlxsw_i2c_init,
6286882b0aeSVadim Pasternak 	.fini			= mlxsw_i2c_fini,
6296882b0aeSVadim Pasternak 	.skb_transmit_busy	= mlxsw_i2c_skb_transmit_busy,
6306882b0aeSVadim Pasternak 	.skb_transmit		= mlxsw_i2c_skb_transmit,
6316882b0aeSVadim Pasternak 	.cmd_exec		= mlxsw_i2c_cmd_exec,
6326882b0aeSVadim Pasternak };
6336882b0aeSVadim Pasternak 
mlxsw_i2c_probe(struct i2c_client * client)634cb405c2aSUwe Kleine-König static int mlxsw_i2c_probe(struct i2c_client *client)
6356882b0aeSVadim Pasternak {
636cb405c2aSUwe Kleine-König 	const struct i2c_device_id *id = i2c_client_get_device_id(client);
6373029a693SVadim Pasternak 	const struct i2c_adapter_quirks *quirks = client->adapter->quirks;
6386882b0aeSVadim Pasternak 	struct mlxsw_i2c *mlxsw_i2c;
6396882b0aeSVadim Pasternak 	u8 status;
6406882b0aeSVadim Pasternak 	int err;
6416882b0aeSVadim Pasternak 
6426882b0aeSVadim Pasternak 	mlxsw_i2c = devm_kzalloc(&client->dev, sizeof(*mlxsw_i2c), GFP_KERNEL);
6436882b0aeSVadim Pasternak 	if (!mlxsw_i2c)
6446882b0aeSVadim Pasternak 		return -ENOMEM;
6456882b0aeSVadim Pasternak 
6463029a693SVadim Pasternak 	if (quirks) {
6473029a693SVadim Pasternak 		if ((quirks->max_read_len &&
6483029a693SVadim Pasternak 		     quirks->max_read_len < MLXSW_I2C_BLK_DEF) ||
6493029a693SVadim Pasternak 		    (quirks->max_write_len &&
6503029a693SVadim Pasternak 		     quirks->max_write_len < MLXSW_I2C_BLK_DEF)) {
6513029a693SVadim Pasternak 			dev_err(&client->dev, "Insufficient transaction buffer length\n");
6523029a693SVadim Pasternak 			return -EOPNOTSUPP;
6533029a693SVadim Pasternak 		}
6543029a693SVadim Pasternak 
655d7248f1cSVadim Pasternak 		mlxsw_i2c->block_size = min_t(u16, MLXSW_I2C_BLK_MAX,
6563029a693SVadim Pasternak 					      min_t(u16, quirks->max_read_len,
6573029a693SVadim Pasternak 						    quirks->max_write_len));
6583029a693SVadim Pasternak 	} else {
6593029a693SVadim Pasternak 		mlxsw_i2c->block_size = MLXSW_I2C_BLK_DEF;
6603029a693SVadim Pasternak 	}
6613029a693SVadim Pasternak 
6626882b0aeSVadim Pasternak 	i2c_set_clientdata(client, mlxsw_i2c);
6636882b0aeSVadim Pasternak 	mutex_init(&mlxsw_i2c->cmd.lock);
6646882b0aeSVadim Pasternak 
6656882b0aeSVadim Pasternak 	/* In order to use mailboxes through the i2c, special area is reserved
6666882b0aeSVadim Pasternak 	 * on the i2c address space that can be used for input and output
6676882b0aeSVadim Pasternak 	 * mailboxes. Such mailboxes are called local mailboxes. When using a
6686882b0aeSVadim Pasternak 	 * local mailbox, software should specify 0 as the Input/Output
6696882b0aeSVadim Pasternak 	 * parameters. The location of the Local Mailbox addresses on the i2c
6706882b0aeSVadim Pasternak 	 * space can be retrieved through the QUERY_FW command.
6716882b0aeSVadim Pasternak 	 * For this purpose QUERY_FW is to be issued with opcode modifier equal
6726882b0aeSVadim Pasternak 	 * 0x01. For such command the output parameter is an immediate value.
6736882b0aeSVadim Pasternak 	 * Here QUERY_FW command is invoked for ASIC probing and for getting
6746882b0aeSVadim Pasternak 	 * local mailboxes addresses from immedate output parameters.
6756882b0aeSVadim Pasternak 	 */
6766882b0aeSVadim Pasternak 
6776882b0aeSVadim Pasternak 	/* Prepare and write out Command Interface Register for transaction */
6786882b0aeSVadim Pasternak 	err = mlxsw_i2c_write_cmd(client, mlxsw_i2c, 1);
6796882b0aeSVadim Pasternak 	if (err) {
6806882b0aeSVadim Pasternak 		dev_err(&client->dev, "Could not start transaction");
6816882b0aeSVadim Pasternak 		goto errout;
6826882b0aeSVadim Pasternak 	}
6836882b0aeSVadim Pasternak 
6846882b0aeSVadim Pasternak 	/* Wait until go bit is cleared. */
6856882b0aeSVadim Pasternak 	err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, &status);
6866882b0aeSVadim Pasternak 	if (err) {
6876882b0aeSVadim Pasternak 		dev_err(&client->dev, "HW semaphore is not released");
6886882b0aeSVadim Pasternak 		goto errout;
6896882b0aeSVadim Pasternak 	}
6906882b0aeSVadim Pasternak 
6916882b0aeSVadim Pasternak 	/* Validate transaction completion status. */
6926882b0aeSVadim Pasternak 	if (status) {
6936882b0aeSVadim Pasternak 		dev_err(&client->dev, "Bad transaction completion status %x\n",
6946882b0aeSVadim Pasternak 			status);
6956882b0aeSVadim Pasternak 		err = -EIO;
6966882b0aeSVadim Pasternak 		goto errout;
6976882b0aeSVadim Pasternak 	}
6986882b0aeSVadim Pasternak 
6996882b0aeSVadim Pasternak 	/* Get mailbox offsets. */
7006882b0aeSVadim Pasternak 	err = mlxsw_i2c_get_mbox(client, mlxsw_i2c);
7016882b0aeSVadim Pasternak 	if (err < 0) {
7026882b0aeSVadim Pasternak 		dev_err(&client->dev, "Fail to get mailboxes\n");
7036882b0aeSVadim Pasternak 		goto errout;
7046882b0aeSVadim Pasternak 	}
7056882b0aeSVadim Pasternak 
7066882b0aeSVadim Pasternak 	dev_info(&client->dev, "%s mb size=%x off=0x%08x out mb size=%x off=0x%08x\n",
7076882b0aeSVadim Pasternak 		 id->name, mlxsw_i2c->cmd.mb_size_in,
7086882b0aeSVadim Pasternak 		 mlxsw_i2c->cmd.mb_off_in, mlxsw_i2c->cmd.mb_size_out,
7096882b0aeSVadim Pasternak 		 mlxsw_i2c->cmd.mb_off_out);
7106882b0aeSVadim Pasternak 
7116882b0aeSVadim Pasternak 	/* Register device bus. */
7126882b0aeSVadim Pasternak 	mlxsw_i2c->bus_info.device_kind = id->name;
7136882b0aeSVadim Pasternak 	mlxsw_i2c->bus_info.device_name = client->name;
7146882b0aeSVadim Pasternak 	mlxsw_i2c->bus_info.dev = &client->dev;
7153dcfe179SVadim Pasternak 	mlxsw_i2c->bus_info.low_frequency = true;
7166882b0aeSVadim Pasternak 	mlxsw_i2c->dev = &client->dev;
71733fa6909SVadim Pasternak 	mlxsw_i2c->pdata = client->dev.platform_data;
71833fa6909SVadim Pasternak 
71933fa6909SVadim Pasternak 	err = mlxsw_i2c_irq_init(mlxsw_i2c, client->addr);
72033fa6909SVadim Pasternak 	if (err)
72133fa6909SVadim Pasternak 		goto errout;
7226882b0aeSVadim Pasternak 
7236882b0aeSVadim Pasternak 	err = mlxsw_core_bus_device_register(&mlxsw_i2c->bus_info,
72424cc68adSArkadi Sharshevsky 					     &mlxsw_i2c_bus, mlxsw_i2c, false,
7255bcfb6a4SJiri Pirko 					     NULL, NULL);
7266882b0aeSVadim Pasternak 	if (err) {
7276882b0aeSVadim Pasternak 		dev_err(&client->dev, "Fail to register core bus\n");
72833fa6909SVadim Pasternak 		goto err_bus_device_register;
7296882b0aeSVadim Pasternak 	}
7306882b0aeSVadim Pasternak 
7316882b0aeSVadim Pasternak 	return 0;
7326882b0aeSVadim Pasternak 
73333fa6909SVadim Pasternak err_bus_device_register:
73433fa6909SVadim Pasternak 	mlxsw_i2c_irq_fini(mlxsw_i2c);
7356882b0aeSVadim Pasternak errout:
736d452088cSVadim Pasternak 	mutex_destroy(&mlxsw_i2c->cmd.lock);
7376882b0aeSVadim Pasternak 	i2c_set_clientdata(client, NULL);
7386882b0aeSVadim Pasternak 
7396882b0aeSVadim Pasternak 	return err;
7406882b0aeSVadim Pasternak }
7416882b0aeSVadim Pasternak 
mlxsw_i2c_remove(struct i2c_client * client)742ed5c2f5fSUwe Kleine-König static void mlxsw_i2c_remove(struct i2c_client *client)
7436882b0aeSVadim Pasternak {
7446882b0aeSVadim Pasternak 	struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client);
7456882b0aeSVadim Pasternak 
74624cc68adSArkadi Sharshevsky 	mlxsw_core_bus_device_unregister(mlxsw_i2c->core, false);
74733fa6909SVadim Pasternak 	mlxsw_i2c_irq_fini(mlxsw_i2c);
7486882b0aeSVadim Pasternak 	mutex_destroy(&mlxsw_i2c->cmd.lock);
7496882b0aeSVadim Pasternak }
7506882b0aeSVadim Pasternak 
mlxsw_i2c_driver_register(struct i2c_driver * i2c_driver)7516882b0aeSVadim Pasternak int mlxsw_i2c_driver_register(struct i2c_driver *i2c_driver)
7526882b0aeSVadim Pasternak {
7533a2cb45cSUwe Kleine-König 	i2c_driver->probe = mlxsw_i2c_probe;
7546882b0aeSVadim Pasternak 	i2c_driver->remove = mlxsw_i2c_remove;
7556882b0aeSVadim Pasternak 	return i2c_add_driver(i2c_driver);
7566882b0aeSVadim Pasternak }
7576882b0aeSVadim Pasternak EXPORT_SYMBOL(mlxsw_i2c_driver_register);
7586882b0aeSVadim Pasternak 
mlxsw_i2c_driver_unregister(struct i2c_driver * i2c_driver)7596882b0aeSVadim Pasternak void mlxsw_i2c_driver_unregister(struct i2c_driver *i2c_driver)
7606882b0aeSVadim Pasternak {
7616882b0aeSVadim Pasternak 	i2c_del_driver(i2c_driver);
7626882b0aeSVadim Pasternak }
7636882b0aeSVadim Pasternak EXPORT_SYMBOL(mlxsw_i2c_driver_unregister);
7646882b0aeSVadim Pasternak 
7656882b0aeSVadim Pasternak MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
7666882b0aeSVadim Pasternak MODULE_DESCRIPTION("Mellanox switch I2C interface driver");
7676882b0aeSVadim Pasternak MODULE_LICENSE("Dual BSD/GPL");
768