1*4882a593Smuzhiyun // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2*4882a593Smuzhiyun /* Copyright (c) 2016-2018 Mellanox Technologies. All rights reserved */
3*4882a593Smuzhiyun
4*4882a593Smuzhiyun #include <linux/err.h>
5*4882a593Smuzhiyun #include <linux/i2c.h>
6*4882a593Smuzhiyun #include <linux/init.h>
7*4882a593Smuzhiyun #include <linux/jiffies.h>
8*4882a593Smuzhiyun #include <linux/kernel.h>
9*4882a593Smuzhiyun #include <linux/mutex.h>
10*4882a593Smuzhiyun #include <linux/module.h>
11*4882a593Smuzhiyun #include <linux/mod_devicetable.h>
12*4882a593Smuzhiyun #include <linux/slab.h>
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #include "cmd.h"
15*4882a593Smuzhiyun #include "core.h"
16*4882a593Smuzhiyun #include "i2c.h"
17*4882a593Smuzhiyun #include "resources.h"
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun #define MLXSW_I2C_CIR2_BASE 0x72000
20*4882a593Smuzhiyun #define MLXSW_I2C_CIR_STATUS_OFF 0x18
21*4882a593Smuzhiyun #define MLXSW_I2C_CIR2_OFF_STATUS (MLXSW_I2C_CIR2_BASE + \
22*4882a593Smuzhiyun MLXSW_I2C_CIR_STATUS_OFF)
23*4882a593Smuzhiyun #define MLXSW_I2C_OPMOD_SHIFT 12
24*4882a593Smuzhiyun #define MLXSW_I2C_EVENT_BIT_SHIFT 22
25*4882a593Smuzhiyun #define MLXSW_I2C_GO_BIT_SHIFT 23
26*4882a593Smuzhiyun #define MLXSW_I2C_CIR_CTRL_STATUS_SHIFT 24
27*4882a593Smuzhiyun #define MLXSW_I2C_EVENT_BIT BIT(MLXSW_I2C_EVENT_BIT_SHIFT)
28*4882a593Smuzhiyun #define MLXSW_I2C_GO_BIT BIT(MLXSW_I2C_GO_BIT_SHIFT)
29*4882a593Smuzhiyun #define MLXSW_I2C_GO_OPMODE BIT(MLXSW_I2C_OPMOD_SHIFT)
30*4882a593Smuzhiyun #define MLXSW_I2C_SET_IMM_CMD (MLXSW_I2C_GO_OPMODE | \
31*4882a593Smuzhiyun MLXSW_CMD_OPCODE_QUERY_FW)
32*4882a593Smuzhiyun #define MLXSW_I2C_PUSH_IMM_CMD (MLXSW_I2C_GO_BIT | \
33*4882a593Smuzhiyun MLXSW_I2C_SET_IMM_CMD)
34*4882a593Smuzhiyun #define MLXSW_I2C_SET_CMD (MLXSW_CMD_OPCODE_ACCESS_REG)
35*4882a593Smuzhiyun #define MLXSW_I2C_PUSH_CMD (MLXSW_I2C_GO_BIT | MLXSW_I2C_SET_CMD)
36*4882a593Smuzhiyun #define MLXSW_I2C_TLV_HDR_SIZE 0x10
37*4882a593Smuzhiyun #define MLXSW_I2C_ADDR_WIDTH 4
38*4882a593Smuzhiyun #define MLXSW_I2C_PUSH_CMD_SIZE (MLXSW_I2C_ADDR_WIDTH + 4)
39*4882a593Smuzhiyun #define MLXSW_I2C_SET_EVENT_CMD (MLXSW_I2C_EVENT_BIT)
40*4882a593Smuzhiyun #define MLXSW_I2C_PUSH_EVENT_CMD (MLXSW_I2C_GO_BIT | \
41*4882a593Smuzhiyun MLXSW_I2C_SET_EVENT_CMD)
42*4882a593Smuzhiyun #define MLXSW_I2C_READ_SEMA_SIZE 4
43*4882a593Smuzhiyun #define MLXSW_I2C_PREP_SIZE (MLXSW_I2C_ADDR_WIDTH + 28)
44*4882a593Smuzhiyun #define MLXSW_I2C_MBOX_SIZE 20
45*4882a593Smuzhiyun #define MLXSW_I2C_MBOX_OUT_PARAM_OFF 12
46*4882a593Smuzhiyun #define MLXSW_I2C_MBOX_OFFSET_BITS 20
47*4882a593Smuzhiyun #define MLXSW_I2C_MBOX_SIZE_BITS 12
48*4882a593Smuzhiyun #define MLXSW_I2C_ADDR_BUF_SIZE 4
49*4882a593Smuzhiyun #define MLXSW_I2C_BLK_DEF 32
50*4882a593Smuzhiyun #define MLXSW_I2C_RETRY 5
51*4882a593Smuzhiyun #define MLXSW_I2C_TIMEOUT_MSECS 5000
52*4882a593Smuzhiyun #define MLXSW_I2C_MAX_DATA_SIZE 256
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun /**
55*4882a593Smuzhiyun * struct mlxsw_i2c - device private data:
56*4882a593Smuzhiyun * @cmd: command attributes;
57*4882a593Smuzhiyun * @cmd.mb_size_in: input mailbox size;
58*4882a593Smuzhiyun * @cmd.mb_off_in: input mailbox offset in register space;
59*4882a593Smuzhiyun * @cmd.mb_size_out: output mailbox size;
60*4882a593Smuzhiyun * @cmd.mb_off_out: output mailbox offset in register space;
61*4882a593Smuzhiyun * @cmd.lock: command execution lock;
62*4882a593Smuzhiyun * @dev: I2C device;
63*4882a593Smuzhiyun * @core: switch core pointer;
64*4882a593Smuzhiyun * @bus_info: bus info block;
65*4882a593Smuzhiyun * @block_size: maximum block size allowed to pass to under layer;
66*4882a593Smuzhiyun */
67*4882a593Smuzhiyun struct mlxsw_i2c {
68*4882a593Smuzhiyun struct {
69*4882a593Smuzhiyun u32 mb_size_in;
70*4882a593Smuzhiyun u32 mb_off_in;
71*4882a593Smuzhiyun u32 mb_size_out;
72*4882a593Smuzhiyun u32 mb_off_out;
73*4882a593Smuzhiyun struct mutex lock;
74*4882a593Smuzhiyun } cmd;
75*4882a593Smuzhiyun struct device *dev;
76*4882a593Smuzhiyun struct mlxsw_core *core;
77*4882a593Smuzhiyun struct mlxsw_bus_info bus_info;
78*4882a593Smuzhiyun u16 block_size;
79*4882a593Smuzhiyun };
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun #define MLXSW_I2C_READ_MSG(_client, _addr_buf, _buf, _len) { \
82*4882a593Smuzhiyun { .addr = (_client)->addr, \
83*4882a593Smuzhiyun .buf = (_addr_buf), \
84*4882a593Smuzhiyun .len = MLXSW_I2C_ADDR_BUF_SIZE, \
85*4882a593Smuzhiyun .flags = 0 }, \
86*4882a593Smuzhiyun { .addr = (_client)->addr, \
87*4882a593Smuzhiyun .buf = (_buf), \
88*4882a593Smuzhiyun .len = (_len), \
89*4882a593Smuzhiyun .flags = I2C_M_RD } }
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun #define MLXSW_I2C_WRITE_MSG(_client, _buf, _len) \
92*4882a593Smuzhiyun { .addr = (_client)->addr, \
93*4882a593Smuzhiyun .buf = (u8 *)(_buf), \
94*4882a593Smuzhiyun .len = (_len), \
95*4882a593Smuzhiyun .flags = 0 }
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun /* Routine converts in and out mail boxes offset and size. */
98*4882a593Smuzhiyun static inline void
mlxsw_i2c_convert_mbox(struct mlxsw_i2c * mlxsw_i2c,u8 * buf)99*4882a593Smuzhiyun mlxsw_i2c_convert_mbox(struct mlxsw_i2c *mlxsw_i2c, u8 *buf)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun u32 tmp;
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun /* Local in/out mailboxes: 20 bits for offset, 12 for size */
104*4882a593Smuzhiyun tmp = be32_to_cpup((__be32 *) buf);
105*4882a593Smuzhiyun mlxsw_i2c->cmd.mb_off_in = tmp &
106*4882a593Smuzhiyun GENMASK(MLXSW_I2C_MBOX_OFFSET_BITS - 1, 0);
107*4882a593Smuzhiyun mlxsw_i2c->cmd.mb_size_in = (tmp & GENMASK(31,
108*4882a593Smuzhiyun MLXSW_I2C_MBOX_OFFSET_BITS)) >>
109*4882a593Smuzhiyun MLXSW_I2C_MBOX_OFFSET_BITS;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun tmp = be32_to_cpup((__be32 *) (buf + MLXSW_I2C_ADDR_WIDTH));
112*4882a593Smuzhiyun mlxsw_i2c->cmd.mb_off_out = tmp &
113*4882a593Smuzhiyun GENMASK(MLXSW_I2C_MBOX_OFFSET_BITS - 1, 0);
114*4882a593Smuzhiyun mlxsw_i2c->cmd.mb_size_out = (tmp & GENMASK(31,
115*4882a593Smuzhiyun MLXSW_I2C_MBOX_OFFSET_BITS)) >>
116*4882a593Smuzhiyun MLXSW_I2C_MBOX_OFFSET_BITS;
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun /* Routine obtains register size from mail box buffer. */
mlxsw_i2c_get_reg_size(u8 * in_mbox)120*4882a593Smuzhiyun static inline int mlxsw_i2c_get_reg_size(u8 *in_mbox)
121*4882a593Smuzhiyun {
122*4882a593Smuzhiyun u16 tmp = be16_to_cpup((__be16 *) (in_mbox + MLXSW_I2C_TLV_HDR_SIZE));
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun return (tmp & 0x7ff) * 4 + MLXSW_I2C_TLV_HDR_SIZE;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun /* Routine sets I2C device internal offset in the transaction buffer. */
mlxsw_i2c_set_slave_addr(u8 * buf,u32 off)128*4882a593Smuzhiyun static inline void mlxsw_i2c_set_slave_addr(u8 *buf, u32 off)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun __be32 *val = (__be32 *) buf;
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun *val = htonl(off);
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun /* Routine waits until go bit is cleared. */
mlxsw_i2c_wait_go_bit(struct i2c_client * client,struct mlxsw_i2c * mlxsw_i2c,u8 * p_status)136*4882a593Smuzhiyun static int mlxsw_i2c_wait_go_bit(struct i2c_client *client,
137*4882a593Smuzhiyun struct mlxsw_i2c *mlxsw_i2c, u8 *p_status)
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun u8 addr_buf[MLXSW_I2C_ADDR_BUF_SIZE];
140*4882a593Smuzhiyun u8 buf[MLXSW_I2C_READ_SEMA_SIZE];
141*4882a593Smuzhiyun int len = MLXSW_I2C_READ_SEMA_SIZE;
142*4882a593Smuzhiyun struct i2c_msg read_sema[] =
143*4882a593Smuzhiyun MLXSW_I2C_READ_MSG(client, addr_buf, buf, len);
144*4882a593Smuzhiyun bool wait_done = false;
145*4882a593Smuzhiyun unsigned long end;
146*4882a593Smuzhiyun int i = 0, err;
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun mlxsw_i2c_set_slave_addr(addr_buf, MLXSW_I2C_CIR2_OFF_STATUS);
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun end = jiffies + msecs_to_jiffies(MLXSW_I2C_TIMEOUT_MSECS);
151*4882a593Smuzhiyun do {
152*4882a593Smuzhiyun u32 ctrl;
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun err = i2c_transfer(client->adapter, read_sema,
155*4882a593Smuzhiyun ARRAY_SIZE(read_sema));
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun ctrl = be32_to_cpu(*(__be32 *) buf);
158*4882a593Smuzhiyun if (err == ARRAY_SIZE(read_sema)) {
159*4882a593Smuzhiyun if (!(ctrl & MLXSW_I2C_GO_BIT)) {
160*4882a593Smuzhiyun wait_done = true;
161*4882a593Smuzhiyun *p_status = ctrl >>
162*4882a593Smuzhiyun MLXSW_I2C_CIR_CTRL_STATUS_SHIFT;
163*4882a593Smuzhiyun break;
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun }
166*4882a593Smuzhiyun cond_resched();
167*4882a593Smuzhiyun } while ((time_before(jiffies, end)) || (i++ < MLXSW_I2C_RETRY));
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun if (wait_done) {
170*4882a593Smuzhiyun if (*p_status)
171*4882a593Smuzhiyun err = -EIO;
172*4882a593Smuzhiyun } else {
173*4882a593Smuzhiyun return -ETIMEDOUT;
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun return err > 0 ? 0 : err;
177*4882a593Smuzhiyun }
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun /* Routine posts a command to ASIC through mail box. */
mlxsw_i2c_write_cmd(struct i2c_client * client,struct mlxsw_i2c * mlxsw_i2c,int immediate)180*4882a593Smuzhiyun static int mlxsw_i2c_write_cmd(struct i2c_client *client,
181*4882a593Smuzhiyun struct mlxsw_i2c *mlxsw_i2c,
182*4882a593Smuzhiyun int immediate)
183*4882a593Smuzhiyun {
184*4882a593Smuzhiyun __be32 push_cmd_buf[MLXSW_I2C_PUSH_CMD_SIZE / 4] = {
185*4882a593Smuzhiyun 0, cpu_to_be32(MLXSW_I2C_PUSH_IMM_CMD)
186*4882a593Smuzhiyun };
187*4882a593Smuzhiyun __be32 prep_cmd_buf[MLXSW_I2C_PREP_SIZE / 4] = {
188*4882a593Smuzhiyun 0, 0, 0, 0, 0, 0,
189*4882a593Smuzhiyun cpu_to_be32(client->adapter->nr & 0xffff),
190*4882a593Smuzhiyun cpu_to_be32(MLXSW_I2C_SET_IMM_CMD)
191*4882a593Smuzhiyun };
192*4882a593Smuzhiyun struct i2c_msg push_cmd =
193*4882a593Smuzhiyun MLXSW_I2C_WRITE_MSG(client, push_cmd_buf,
194*4882a593Smuzhiyun MLXSW_I2C_PUSH_CMD_SIZE);
195*4882a593Smuzhiyun struct i2c_msg prep_cmd =
196*4882a593Smuzhiyun MLXSW_I2C_WRITE_MSG(client, prep_cmd_buf, MLXSW_I2C_PREP_SIZE);
197*4882a593Smuzhiyun int err;
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun if (!immediate) {
200*4882a593Smuzhiyun push_cmd_buf[1] = cpu_to_be32(MLXSW_I2C_PUSH_CMD);
201*4882a593Smuzhiyun prep_cmd_buf[7] = cpu_to_be32(MLXSW_I2C_SET_CMD);
202*4882a593Smuzhiyun }
203*4882a593Smuzhiyun mlxsw_i2c_set_slave_addr((u8 *)prep_cmd_buf,
204*4882a593Smuzhiyun MLXSW_I2C_CIR2_BASE);
205*4882a593Smuzhiyun mlxsw_i2c_set_slave_addr((u8 *)push_cmd_buf,
206*4882a593Smuzhiyun MLXSW_I2C_CIR2_OFF_STATUS);
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun /* Prepare Command Interface Register for transaction */
209*4882a593Smuzhiyun err = i2c_transfer(client->adapter, &prep_cmd, 1);
210*4882a593Smuzhiyun if (err < 0)
211*4882a593Smuzhiyun return err;
212*4882a593Smuzhiyun else if (err != 1)
213*4882a593Smuzhiyun return -EIO;
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun /* Write out Command Interface Register GO bit to push transaction */
216*4882a593Smuzhiyun err = i2c_transfer(client->adapter, &push_cmd, 1);
217*4882a593Smuzhiyun if (err < 0)
218*4882a593Smuzhiyun return err;
219*4882a593Smuzhiyun else if (err != 1)
220*4882a593Smuzhiyun return -EIO;
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun return 0;
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun /* Routine posts initialization command to ASIC through mail box. */
226*4882a593Smuzhiyun static int
mlxsw_i2c_write_init_cmd(struct i2c_client * client,struct mlxsw_i2c * mlxsw_i2c,u16 opcode,u32 in_mod)227*4882a593Smuzhiyun mlxsw_i2c_write_init_cmd(struct i2c_client *client,
228*4882a593Smuzhiyun struct mlxsw_i2c *mlxsw_i2c, u16 opcode, u32 in_mod)
229*4882a593Smuzhiyun {
230*4882a593Smuzhiyun __be32 push_cmd_buf[MLXSW_I2C_PUSH_CMD_SIZE / 4] = {
231*4882a593Smuzhiyun 0, cpu_to_be32(MLXSW_I2C_PUSH_EVENT_CMD)
232*4882a593Smuzhiyun };
233*4882a593Smuzhiyun __be32 prep_cmd_buf[MLXSW_I2C_PREP_SIZE / 4] = {
234*4882a593Smuzhiyun 0, 0, 0, 0, 0, 0,
235*4882a593Smuzhiyun cpu_to_be32(client->adapter->nr & 0xffff),
236*4882a593Smuzhiyun cpu_to_be32(MLXSW_I2C_SET_EVENT_CMD)
237*4882a593Smuzhiyun };
238*4882a593Smuzhiyun struct i2c_msg push_cmd =
239*4882a593Smuzhiyun MLXSW_I2C_WRITE_MSG(client, push_cmd_buf,
240*4882a593Smuzhiyun MLXSW_I2C_PUSH_CMD_SIZE);
241*4882a593Smuzhiyun struct i2c_msg prep_cmd =
242*4882a593Smuzhiyun MLXSW_I2C_WRITE_MSG(client, prep_cmd_buf, MLXSW_I2C_PREP_SIZE);
243*4882a593Smuzhiyun u8 status;
244*4882a593Smuzhiyun int err;
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun push_cmd_buf[1] = cpu_to_be32(MLXSW_I2C_PUSH_EVENT_CMD | opcode);
247*4882a593Smuzhiyun prep_cmd_buf[3] = cpu_to_be32(in_mod);
248*4882a593Smuzhiyun prep_cmd_buf[7] = cpu_to_be32(MLXSW_I2C_GO_BIT | opcode);
249*4882a593Smuzhiyun mlxsw_i2c_set_slave_addr((u8 *)prep_cmd_buf,
250*4882a593Smuzhiyun MLXSW_I2C_CIR2_BASE);
251*4882a593Smuzhiyun mlxsw_i2c_set_slave_addr((u8 *)push_cmd_buf,
252*4882a593Smuzhiyun MLXSW_I2C_CIR2_OFF_STATUS);
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun /* Prepare Command Interface Register for transaction */
255*4882a593Smuzhiyun err = i2c_transfer(client->adapter, &prep_cmd, 1);
256*4882a593Smuzhiyun if (err < 0)
257*4882a593Smuzhiyun return err;
258*4882a593Smuzhiyun else if (err != 1)
259*4882a593Smuzhiyun return -EIO;
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun /* Write out Command Interface Register GO bit to push transaction */
262*4882a593Smuzhiyun err = i2c_transfer(client->adapter, &push_cmd, 1);
263*4882a593Smuzhiyun if (err < 0)
264*4882a593Smuzhiyun return err;
265*4882a593Smuzhiyun else if (err != 1)
266*4882a593Smuzhiyun return -EIO;
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun /* Wait until go bit is cleared. */
269*4882a593Smuzhiyun err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, &status);
270*4882a593Smuzhiyun if (err) {
271*4882a593Smuzhiyun dev_err(&client->dev, "HW semaphore is not released");
272*4882a593Smuzhiyun return err;
273*4882a593Smuzhiyun }
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun /* Validate transaction completion status. */
276*4882a593Smuzhiyun if (status) {
277*4882a593Smuzhiyun dev_err(&client->dev, "Bad transaction completion status %x\n",
278*4882a593Smuzhiyun status);
279*4882a593Smuzhiyun return -EIO;
280*4882a593Smuzhiyun }
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun return 0;
283*4882a593Smuzhiyun }
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun /* Routine obtains mail box offsets from ASIC register space. */
mlxsw_i2c_get_mbox(struct i2c_client * client,struct mlxsw_i2c * mlxsw_i2c)286*4882a593Smuzhiyun static int mlxsw_i2c_get_mbox(struct i2c_client *client,
287*4882a593Smuzhiyun struct mlxsw_i2c *mlxsw_i2c)
288*4882a593Smuzhiyun {
289*4882a593Smuzhiyun u8 addr_buf[MLXSW_I2C_ADDR_BUF_SIZE];
290*4882a593Smuzhiyun u8 buf[MLXSW_I2C_MBOX_SIZE];
291*4882a593Smuzhiyun struct i2c_msg mbox_cmd[] =
292*4882a593Smuzhiyun MLXSW_I2C_READ_MSG(client, addr_buf, buf, MLXSW_I2C_MBOX_SIZE);
293*4882a593Smuzhiyun int err;
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun /* Read mail boxes offsets. */
296*4882a593Smuzhiyun mlxsw_i2c_set_slave_addr(addr_buf, MLXSW_I2C_CIR2_BASE);
297*4882a593Smuzhiyun err = i2c_transfer(client->adapter, mbox_cmd, 2);
298*4882a593Smuzhiyun if (err != 2) {
299*4882a593Smuzhiyun dev_err(&client->dev, "Could not obtain mail boxes\n");
300*4882a593Smuzhiyun if (!err)
301*4882a593Smuzhiyun return -EIO;
302*4882a593Smuzhiyun else
303*4882a593Smuzhiyun return err;
304*4882a593Smuzhiyun }
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun /* Convert mail boxes. */
307*4882a593Smuzhiyun mlxsw_i2c_convert_mbox(mlxsw_i2c, &buf[MLXSW_I2C_MBOX_OUT_PARAM_OFF]);
308*4882a593Smuzhiyun
309*4882a593Smuzhiyun return err;
310*4882a593Smuzhiyun }
311*4882a593Smuzhiyun
312*4882a593Smuzhiyun /* Routine sends I2C write transaction to ASIC device. */
313*4882a593Smuzhiyun static int
mlxsw_i2c_write(struct device * dev,size_t in_mbox_size,u8 * in_mbox,int num,u8 * p_status)314*4882a593Smuzhiyun mlxsw_i2c_write(struct device *dev, size_t in_mbox_size, u8 *in_mbox, int num,
315*4882a593Smuzhiyun u8 *p_status)
316*4882a593Smuzhiyun {
317*4882a593Smuzhiyun struct i2c_client *client = to_i2c_client(dev);
318*4882a593Smuzhiyun struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client);
319*4882a593Smuzhiyun unsigned long timeout = msecs_to_jiffies(MLXSW_I2C_TIMEOUT_MSECS);
320*4882a593Smuzhiyun int off = mlxsw_i2c->cmd.mb_off_in, chunk_size, i, j;
321*4882a593Smuzhiyun unsigned long end;
322*4882a593Smuzhiyun u8 *tran_buf;
323*4882a593Smuzhiyun struct i2c_msg write_tran =
324*4882a593Smuzhiyun MLXSW_I2C_WRITE_MSG(client, NULL, MLXSW_I2C_PUSH_CMD_SIZE);
325*4882a593Smuzhiyun int err;
326*4882a593Smuzhiyun
327*4882a593Smuzhiyun tran_buf = kmalloc(mlxsw_i2c->block_size + MLXSW_I2C_ADDR_BUF_SIZE,
328*4882a593Smuzhiyun GFP_KERNEL);
329*4882a593Smuzhiyun if (!tran_buf)
330*4882a593Smuzhiyun return -ENOMEM;
331*4882a593Smuzhiyun
332*4882a593Smuzhiyun write_tran.buf = tran_buf;
333*4882a593Smuzhiyun for (i = 0; i < num; i++) {
334*4882a593Smuzhiyun chunk_size = (in_mbox_size > mlxsw_i2c->block_size) ?
335*4882a593Smuzhiyun mlxsw_i2c->block_size : in_mbox_size;
336*4882a593Smuzhiyun write_tran.len = MLXSW_I2C_ADDR_WIDTH + chunk_size;
337*4882a593Smuzhiyun mlxsw_i2c_set_slave_addr(tran_buf, off);
338*4882a593Smuzhiyun memcpy(&tran_buf[MLXSW_I2C_ADDR_BUF_SIZE], in_mbox +
339*4882a593Smuzhiyun mlxsw_i2c->block_size * i, chunk_size);
340*4882a593Smuzhiyun
341*4882a593Smuzhiyun j = 0;
342*4882a593Smuzhiyun end = jiffies + timeout;
343*4882a593Smuzhiyun do {
344*4882a593Smuzhiyun err = i2c_transfer(client->adapter, &write_tran, 1);
345*4882a593Smuzhiyun if (err == 1)
346*4882a593Smuzhiyun break;
347*4882a593Smuzhiyun
348*4882a593Smuzhiyun cond_resched();
349*4882a593Smuzhiyun } while ((time_before(jiffies, end)) ||
350*4882a593Smuzhiyun (j++ < MLXSW_I2C_RETRY));
351*4882a593Smuzhiyun
352*4882a593Smuzhiyun if (err != 1) {
353*4882a593Smuzhiyun if (!err) {
354*4882a593Smuzhiyun err = -EIO;
355*4882a593Smuzhiyun goto mlxsw_i2c_write_exit;
356*4882a593Smuzhiyun }
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun off += chunk_size;
360*4882a593Smuzhiyun in_mbox_size -= chunk_size;
361*4882a593Smuzhiyun }
362*4882a593Smuzhiyun
363*4882a593Smuzhiyun /* Prepare and write out Command Interface Register for transaction. */
364*4882a593Smuzhiyun err = mlxsw_i2c_write_cmd(client, mlxsw_i2c, 0);
365*4882a593Smuzhiyun if (err) {
366*4882a593Smuzhiyun dev_err(&client->dev, "Could not start transaction");
367*4882a593Smuzhiyun err = -EIO;
368*4882a593Smuzhiyun goto mlxsw_i2c_write_exit;
369*4882a593Smuzhiyun }
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun /* Wait until go bit is cleared. */
372*4882a593Smuzhiyun err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, p_status);
373*4882a593Smuzhiyun if (err) {
374*4882a593Smuzhiyun dev_err(&client->dev, "HW semaphore is not released");
375*4882a593Smuzhiyun goto mlxsw_i2c_write_exit;
376*4882a593Smuzhiyun }
377*4882a593Smuzhiyun
378*4882a593Smuzhiyun /* Validate transaction completion status. */
379*4882a593Smuzhiyun if (*p_status) {
380*4882a593Smuzhiyun dev_err(&client->dev, "Bad transaction completion status %x\n",
381*4882a593Smuzhiyun *p_status);
382*4882a593Smuzhiyun err = -EIO;
383*4882a593Smuzhiyun }
384*4882a593Smuzhiyun
385*4882a593Smuzhiyun mlxsw_i2c_write_exit:
386*4882a593Smuzhiyun kfree(tran_buf);
387*4882a593Smuzhiyun return err;
388*4882a593Smuzhiyun }
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun /* Routine executes I2C command. */
391*4882a593Smuzhiyun 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)392*4882a593Smuzhiyun mlxsw_i2c_cmd(struct device *dev, u16 opcode, u32 in_mod, size_t in_mbox_size,
393*4882a593Smuzhiyun u8 *in_mbox, size_t out_mbox_size, u8 *out_mbox, u8 *status)
394*4882a593Smuzhiyun {
395*4882a593Smuzhiyun struct i2c_client *client = to_i2c_client(dev);
396*4882a593Smuzhiyun struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client);
397*4882a593Smuzhiyun unsigned long timeout = msecs_to_jiffies(MLXSW_I2C_TIMEOUT_MSECS);
398*4882a593Smuzhiyun u8 tran_buf[MLXSW_I2C_ADDR_BUF_SIZE];
399*4882a593Smuzhiyun int num, chunk_size, reg_size, i, j;
400*4882a593Smuzhiyun int off = mlxsw_i2c->cmd.mb_off_out;
401*4882a593Smuzhiyun unsigned long end;
402*4882a593Smuzhiyun struct i2c_msg read_tran[] =
403*4882a593Smuzhiyun MLXSW_I2C_READ_MSG(client, tran_buf, NULL, 0);
404*4882a593Smuzhiyun int err;
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun WARN_ON(in_mbox_size % sizeof(u32) || out_mbox_size % sizeof(u32));
407*4882a593Smuzhiyun
408*4882a593Smuzhiyun if (in_mbox) {
409*4882a593Smuzhiyun reg_size = mlxsw_i2c_get_reg_size(in_mbox);
410*4882a593Smuzhiyun num = reg_size / mlxsw_i2c->block_size;
411*4882a593Smuzhiyun if (reg_size % mlxsw_i2c->block_size)
412*4882a593Smuzhiyun num++;
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) {
415*4882a593Smuzhiyun dev_err(&client->dev, "Could not acquire lock");
416*4882a593Smuzhiyun return -EINVAL;
417*4882a593Smuzhiyun }
418*4882a593Smuzhiyun
419*4882a593Smuzhiyun err = mlxsw_i2c_write(dev, reg_size, in_mbox, num, status);
420*4882a593Smuzhiyun if (err)
421*4882a593Smuzhiyun goto cmd_fail;
422*4882a593Smuzhiyun
423*4882a593Smuzhiyun /* No out mailbox is case of write transaction. */
424*4882a593Smuzhiyun if (!out_mbox) {
425*4882a593Smuzhiyun mutex_unlock(&mlxsw_i2c->cmd.lock);
426*4882a593Smuzhiyun return 0;
427*4882a593Smuzhiyun }
428*4882a593Smuzhiyun } else {
429*4882a593Smuzhiyun /* No input mailbox is case of initialization query command. */
430*4882a593Smuzhiyun reg_size = MLXSW_I2C_MAX_DATA_SIZE;
431*4882a593Smuzhiyun num = reg_size / mlxsw_i2c->block_size;
432*4882a593Smuzhiyun
433*4882a593Smuzhiyun if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) {
434*4882a593Smuzhiyun dev_err(&client->dev, "Could not acquire lock");
435*4882a593Smuzhiyun return -EINVAL;
436*4882a593Smuzhiyun }
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun err = mlxsw_i2c_write_init_cmd(client, mlxsw_i2c, opcode,
439*4882a593Smuzhiyun in_mod);
440*4882a593Smuzhiyun if (err)
441*4882a593Smuzhiyun goto cmd_fail;
442*4882a593Smuzhiyun }
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun /* Send read transaction to get output mailbox content. */
445*4882a593Smuzhiyun read_tran[1].buf = out_mbox;
446*4882a593Smuzhiyun for (i = 0; i < num; i++) {
447*4882a593Smuzhiyun chunk_size = (reg_size > mlxsw_i2c->block_size) ?
448*4882a593Smuzhiyun mlxsw_i2c->block_size : reg_size;
449*4882a593Smuzhiyun read_tran[1].len = chunk_size;
450*4882a593Smuzhiyun mlxsw_i2c_set_slave_addr(tran_buf, off);
451*4882a593Smuzhiyun
452*4882a593Smuzhiyun j = 0;
453*4882a593Smuzhiyun end = jiffies + timeout;
454*4882a593Smuzhiyun do {
455*4882a593Smuzhiyun err = i2c_transfer(client->adapter, read_tran,
456*4882a593Smuzhiyun ARRAY_SIZE(read_tran));
457*4882a593Smuzhiyun if (err == ARRAY_SIZE(read_tran))
458*4882a593Smuzhiyun break;
459*4882a593Smuzhiyun
460*4882a593Smuzhiyun cond_resched();
461*4882a593Smuzhiyun } while ((time_before(jiffies, end)) ||
462*4882a593Smuzhiyun (j++ < MLXSW_I2C_RETRY));
463*4882a593Smuzhiyun
464*4882a593Smuzhiyun if (err != ARRAY_SIZE(read_tran)) {
465*4882a593Smuzhiyun if (!err)
466*4882a593Smuzhiyun err = -EIO;
467*4882a593Smuzhiyun
468*4882a593Smuzhiyun goto cmd_fail;
469*4882a593Smuzhiyun }
470*4882a593Smuzhiyun
471*4882a593Smuzhiyun off += chunk_size;
472*4882a593Smuzhiyun reg_size -= chunk_size;
473*4882a593Smuzhiyun read_tran[1].buf += chunk_size;
474*4882a593Smuzhiyun }
475*4882a593Smuzhiyun
476*4882a593Smuzhiyun mutex_unlock(&mlxsw_i2c->cmd.lock);
477*4882a593Smuzhiyun
478*4882a593Smuzhiyun return 0;
479*4882a593Smuzhiyun
480*4882a593Smuzhiyun cmd_fail:
481*4882a593Smuzhiyun mutex_unlock(&mlxsw_i2c->cmd.lock);
482*4882a593Smuzhiyun return err;
483*4882a593Smuzhiyun }
484*4882a593Smuzhiyun
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)485*4882a593Smuzhiyun static int mlxsw_i2c_cmd_exec(void *bus_priv, u16 opcode, u8 opcode_mod,
486*4882a593Smuzhiyun u32 in_mod, bool out_mbox_direct,
487*4882a593Smuzhiyun char *in_mbox, size_t in_mbox_size,
488*4882a593Smuzhiyun char *out_mbox, size_t out_mbox_size,
489*4882a593Smuzhiyun u8 *status)
490*4882a593Smuzhiyun {
491*4882a593Smuzhiyun struct mlxsw_i2c *mlxsw_i2c = bus_priv;
492*4882a593Smuzhiyun
493*4882a593Smuzhiyun return mlxsw_i2c_cmd(mlxsw_i2c->dev, opcode, in_mod, in_mbox_size,
494*4882a593Smuzhiyun in_mbox, out_mbox_size, out_mbox, status);
495*4882a593Smuzhiyun }
496*4882a593Smuzhiyun
mlxsw_i2c_skb_transmit_busy(void * bus_priv,const struct mlxsw_tx_info * tx_info)497*4882a593Smuzhiyun static bool mlxsw_i2c_skb_transmit_busy(void *bus_priv,
498*4882a593Smuzhiyun const struct mlxsw_tx_info *tx_info)
499*4882a593Smuzhiyun {
500*4882a593Smuzhiyun return false;
501*4882a593Smuzhiyun }
502*4882a593Smuzhiyun
mlxsw_i2c_skb_transmit(void * bus_priv,struct sk_buff * skb,const struct mlxsw_tx_info * tx_info)503*4882a593Smuzhiyun static int mlxsw_i2c_skb_transmit(void *bus_priv, struct sk_buff *skb,
504*4882a593Smuzhiyun const struct mlxsw_tx_info *tx_info)
505*4882a593Smuzhiyun {
506*4882a593Smuzhiyun return 0;
507*4882a593Smuzhiyun }
508*4882a593Smuzhiyun
509*4882a593Smuzhiyun static int
mlxsw_i2c_init(void * bus_priv,struct mlxsw_core * mlxsw_core,const struct mlxsw_config_profile * profile,struct mlxsw_res * res)510*4882a593Smuzhiyun mlxsw_i2c_init(void *bus_priv, struct mlxsw_core *mlxsw_core,
511*4882a593Smuzhiyun const struct mlxsw_config_profile *profile,
512*4882a593Smuzhiyun struct mlxsw_res *res)
513*4882a593Smuzhiyun {
514*4882a593Smuzhiyun struct mlxsw_i2c *mlxsw_i2c = bus_priv;
515*4882a593Smuzhiyun char *mbox;
516*4882a593Smuzhiyun int err;
517*4882a593Smuzhiyun
518*4882a593Smuzhiyun mlxsw_i2c->core = mlxsw_core;
519*4882a593Smuzhiyun
520*4882a593Smuzhiyun mbox = mlxsw_cmd_mbox_alloc();
521*4882a593Smuzhiyun if (!mbox)
522*4882a593Smuzhiyun return -ENOMEM;
523*4882a593Smuzhiyun
524*4882a593Smuzhiyun err = mlxsw_cmd_query_fw(mlxsw_core, mbox);
525*4882a593Smuzhiyun if (err)
526*4882a593Smuzhiyun goto mbox_put;
527*4882a593Smuzhiyun
528*4882a593Smuzhiyun mlxsw_i2c->bus_info.fw_rev.major =
529*4882a593Smuzhiyun mlxsw_cmd_mbox_query_fw_fw_rev_major_get(mbox);
530*4882a593Smuzhiyun mlxsw_i2c->bus_info.fw_rev.minor =
531*4882a593Smuzhiyun mlxsw_cmd_mbox_query_fw_fw_rev_minor_get(mbox);
532*4882a593Smuzhiyun mlxsw_i2c->bus_info.fw_rev.subminor =
533*4882a593Smuzhiyun mlxsw_cmd_mbox_query_fw_fw_rev_subminor_get(mbox);
534*4882a593Smuzhiyun
535*4882a593Smuzhiyun err = mlxsw_core_resources_query(mlxsw_core, mbox, res);
536*4882a593Smuzhiyun
537*4882a593Smuzhiyun mbox_put:
538*4882a593Smuzhiyun mlxsw_cmd_mbox_free(mbox);
539*4882a593Smuzhiyun return err;
540*4882a593Smuzhiyun }
541*4882a593Smuzhiyun
mlxsw_i2c_fini(void * bus_priv)542*4882a593Smuzhiyun static void mlxsw_i2c_fini(void *bus_priv)
543*4882a593Smuzhiyun {
544*4882a593Smuzhiyun struct mlxsw_i2c *mlxsw_i2c = bus_priv;
545*4882a593Smuzhiyun
546*4882a593Smuzhiyun mlxsw_i2c->core = NULL;
547*4882a593Smuzhiyun }
548*4882a593Smuzhiyun
549*4882a593Smuzhiyun static const struct mlxsw_bus mlxsw_i2c_bus = {
550*4882a593Smuzhiyun .kind = "i2c",
551*4882a593Smuzhiyun .init = mlxsw_i2c_init,
552*4882a593Smuzhiyun .fini = mlxsw_i2c_fini,
553*4882a593Smuzhiyun .skb_transmit_busy = mlxsw_i2c_skb_transmit_busy,
554*4882a593Smuzhiyun .skb_transmit = mlxsw_i2c_skb_transmit,
555*4882a593Smuzhiyun .cmd_exec = mlxsw_i2c_cmd_exec,
556*4882a593Smuzhiyun };
557*4882a593Smuzhiyun
mlxsw_i2c_probe(struct i2c_client * client,const struct i2c_device_id * id)558*4882a593Smuzhiyun static int mlxsw_i2c_probe(struct i2c_client *client,
559*4882a593Smuzhiyun const struct i2c_device_id *id)
560*4882a593Smuzhiyun {
561*4882a593Smuzhiyun const struct i2c_adapter_quirks *quirks = client->adapter->quirks;
562*4882a593Smuzhiyun struct mlxsw_i2c *mlxsw_i2c;
563*4882a593Smuzhiyun u8 status;
564*4882a593Smuzhiyun int err;
565*4882a593Smuzhiyun
566*4882a593Smuzhiyun mlxsw_i2c = devm_kzalloc(&client->dev, sizeof(*mlxsw_i2c), GFP_KERNEL);
567*4882a593Smuzhiyun if (!mlxsw_i2c)
568*4882a593Smuzhiyun return -ENOMEM;
569*4882a593Smuzhiyun
570*4882a593Smuzhiyun if (quirks) {
571*4882a593Smuzhiyun if ((quirks->max_read_len &&
572*4882a593Smuzhiyun quirks->max_read_len < MLXSW_I2C_BLK_DEF) ||
573*4882a593Smuzhiyun (quirks->max_write_len &&
574*4882a593Smuzhiyun quirks->max_write_len < MLXSW_I2C_BLK_DEF)) {
575*4882a593Smuzhiyun dev_err(&client->dev, "Insufficient transaction buffer length\n");
576*4882a593Smuzhiyun return -EOPNOTSUPP;
577*4882a593Smuzhiyun }
578*4882a593Smuzhiyun
579*4882a593Smuzhiyun mlxsw_i2c->block_size = max_t(u16, MLXSW_I2C_BLK_DEF,
580*4882a593Smuzhiyun min_t(u16, quirks->max_read_len,
581*4882a593Smuzhiyun quirks->max_write_len));
582*4882a593Smuzhiyun } else {
583*4882a593Smuzhiyun mlxsw_i2c->block_size = MLXSW_I2C_BLK_DEF;
584*4882a593Smuzhiyun }
585*4882a593Smuzhiyun
586*4882a593Smuzhiyun i2c_set_clientdata(client, mlxsw_i2c);
587*4882a593Smuzhiyun mutex_init(&mlxsw_i2c->cmd.lock);
588*4882a593Smuzhiyun
589*4882a593Smuzhiyun /* In order to use mailboxes through the i2c, special area is reserved
590*4882a593Smuzhiyun * on the i2c address space that can be used for input and output
591*4882a593Smuzhiyun * mailboxes. Such mailboxes are called local mailboxes. When using a
592*4882a593Smuzhiyun * local mailbox, software should specify 0 as the Input/Output
593*4882a593Smuzhiyun * parameters. The location of the Local Mailbox addresses on the i2c
594*4882a593Smuzhiyun * space can be retrieved through the QUERY_FW command.
595*4882a593Smuzhiyun * For this purpose QUERY_FW is to be issued with opcode modifier equal
596*4882a593Smuzhiyun * 0x01. For such command the output parameter is an immediate value.
597*4882a593Smuzhiyun * Here QUERY_FW command is invoked for ASIC probing and for getting
598*4882a593Smuzhiyun * local mailboxes addresses from immedate output parameters.
599*4882a593Smuzhiyun */
600*4882a593Smuzhiyun
601*4882a593Smuzhiyun /* Prepare and write out Command Interface Register for transaction */
602*4882a593Smuzhiyun err = mlxsw_i2c_write_cmd(client, mlxsw_i2c, 1);
603*4882a593Smuzhiyun if (err) {
604*4882a593Smuzhiyun dev_err(&client->dev, "Could not start transaction");
605*4882a593Smuzhiyun goto errout;
606*4882a593Smuzhiyun }
607*4882a593Smuzhiyun
608*4882a593Smuzhiyun /* Wait until go bit is cleared. */
609*4882a593Smuzhiyun err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, &status);
610*4882a593Smuzhiyun if (err) {
611*4882a593Smuzhiyun dev_err(&client->dev, "HW semaphore is not released");
612*4882a593Smuzhiyun goto errout;
613*4882a593Smuzhiyun }
614*4882a593Smuzhiyun
615*4882a593Smuzhiyun /* Validate transaction completion status. */
616*4882a593Smuzhiyun if (status) {
617*4882a593Smuzhiyun dev_err(&client->dev, "Bad transaction completion status %x\n",
618*4882a593Smuzhiyun status);
619*4882a593Smuzhiyun err = -EIO;
620*4882a593Smuzhiyun goto errout;
621*4882a593Smuzhiyun }
622*4882a593Smuzhiyun
623*4882a593Smuzhiyun /* Get mailbox offsets. */
624*4882a593Smuzhiyun err = mlxsw_i2c_get_mbox(client, mlxsw_i2c);
625*4882a593Smuzhiyun if (err < 0) {
626*4882a593Smuzhiyun dev_err(&client->dev, "Fail to get mailboxes\n");
627*4882a593Smuzhiyun goto errout;
628*4882a593Smuzhiyun }
629*4882a593Smuzhiyun
630*4882a593Smuzhiyun dev_info(&client->dev, "%s mb size=%x off=0x%08x out mb size=%x off=0x%08x\n",
631*4882a593Smuzhiyun id->name, mlxsw_i2c->cmd.mb_size_in,
632*4882a593Smuzhiyun mlxsw_i2c->cmd.mb_off_in, mlxsw_i2c->cmd.mb_size_out,
633*4882a593Smuzhiyun mlxsw_i2c->cmd.mb_off_out);
634*4882a593Smuzhiyun
635*4882a593Smuzhiyun /* Register device bus. */
636*4882a593Smuzhiyun mlxsw_i2c->bus_info.device_kind = id->name;
637*4882a593Smuzhiyun mlxsw_i2c->bus_info.device_name = client->name;
638*4882a593Smuzhiyun mlxsw_i2c->bus_info.dev = &client->dev;
639*4882a593Smuzhiyun mlxsw_i2c->bus_info.low_frequency = true;
640*4882a593Smuzhiyun mlxsw_i2c->dev = &client->dev;
641*4882a593Smuzhiyun
642*4882a593Smuzhiyun err = mlxsw_core_bus_device_register(&mlxsw_i2c->bus_info,
643*4882a593Smuzhiyun &mlxsw_i2c_bus, mlxsw_i2c, false,
644*4882a593Smuzhiyun NULL, NULL);
645*4882a593Smuzhiyun if (err) {
646*4882a593Smuzhiyun dev_err(&client->dev, "Fail to register core bus\n");
647*4882a593Smuzhiyun return err;
648*4882a593Smuzhiyun }
649*4882a593Smuzhiyun
650*4882a593Smuzhiyun return 0;
651*4882a593Smuzhiyun
652*4882a593Smuzhiyun errout:
653*4882a593Smuzhiyun mutex_destroy(&mlxsw_i2c->cmd.lock);
654*4882a593Smuzhiyun i2c_set_clientdata(client, NULL);
655*4882a593Smuzhiyun
656*4882a593Smuzhiyun return err;
657*4882a593Smuzhiyun }
658*4882a593Smuzhiyun
mlxsw_i2c_remove(struct i2c_client * client)659*4882a593Smuzhiyun static int mlxsw_i2c_remove(struct i2c_client *client)
660*4882a593Smuzhiyun {
661*4882a593Smuzhiyun struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client);
662*4882a593Smuzhiyun
663*4882a593Smuzhiyun mlxsw_core_bus_device_unregister(mlxsw_i2c->core, false);
664*4882a593Smuzhiyun mutex_destroy(&mlxsw_i2c->cmd.lock);
665*4882a593Smuzhiyun
666*4882a593Smuzhiyun return 0;
667*4882a593Smuzhiyun }
668*4882a593Smuzhiyun
mlxsw_i2c_driver_register(struct i2c_driver * i2c_driver)669*4882a593Smuzhiyun int mlxsw_i2c_driver_register(struct i2c_driver *i2c_driver)
670*4882a593Smuzhiyun {
671*4882a593Smuzhiyun i2c_driver->probe = mlxsw_i2c_probe;
672*4882a593Smuzhiyun i2c_driver->remove = mlxsw_i2c_remove;
673*4882a593Smuzhiyun return i2c_add_driver(i2c_driver);
674*4882a593Smuzhiyun }
675*4882a593Smuzhiyun EXPORT_SYMBOL(mlxsw_i2c_driver_register);
676*4882a593Smuzhiyun
mlxsw_i2c_driver_unregister(struct i2c_driver * i2c_driver)677*4882a593Smuzhiyun void mlxsw_i2c_driver_unregister(struct i2c_driver *i2c_driver)
678*4882a593Smuzhiyun {
679*4882a593Smuzhiyun i2c_del_driver(i2c_driver);
680*4882a593Smuzhiyun }
681*4882a593Smuzhiyun EXPORT_SYMBOL(mlxsw_i2c_driver_unregister);
682*4882a593Smuzhiyun
683*4882a593Smuzhiyun MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
684*4882a593Smuzhiyun MODULE_DESCRIPTION("Mellanox switch I2C interface driver");
685*4882a593Smuzhiyun MODULE_LICENSE("Dual BSD/GPL");
686