Merge tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rdma/rdma
[linux-2.6-microblaze.git] / drivers / i2c / muxes / i2c-mux-mlxcpld.c
1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /*
3  * Mellanox i2c mux driver
4  *
5  * Copyright (C) 2016-2020 Mellanox Technologies
6  */
7
8 #include <linux/device.h>
9 #include <linux/i2c.h>
10 #include <linux/i2c-mux.h>
11 #include <linux/io.h>
12 #include <linux/init.h>
13 #include <linux/module.h>
14 #include <linux/platform_data/mlxcpld.h>
15 #include <linux/platform_device.h>
16 #include <linux/slab.h>
17
18 /* mlxcpld_mux - mux control structure:
19  * @last_val - last selected register value or -1 if mux deselected
20  * @client - I2C device client
21  * @pdata: platform data
22  */
23 struct mlxcpld_mux {
24         int last_val;
25         struct i2c_client *client;
26         struct mlxcpld_mux_plat_data pdata;
27 };
28
29 /* MUX logic description.
30  * Driver can support different mux control logic, according to CPLD
31  * implementation.
32  *
33  * Connectivity schema.
34  *
35  * i2c-mlxcpld                                 Digital               Analog
36  * driver
37  * *--------*                                 * -> mux1 (virt bus2) -> mux -> |
38  * | I2CLPC | i2c physical                    * -> mux2 (virt bus3) -> mux -> |
39  * | bridge | bus 1                 *---------*                               |
40  * | logic  |---------------------> * mux reg *                               |
41  * | in CPLD|                       *---------*                               |
42  * *--------*   i2c-mux-mlxpcld          ^    * -> muxn (virt busn) -> mux -> |
43  *     |        driver                   |                                    |
44  *     |        *---------------*        |                              Devices
45  *     |        * CPLD (i2c bus)* select |
46  *     |        * registers for *--------*
47  *     |        * mux selection * deselect
48  *     |        *---------------*
49  *     |                 |
50  * <-------->     <----------->
51  * i2c cntrl      Board cntrl reg
52  * reg space      space (mux select,
53  *                IO, LED, WD, info)
54  *
55  */
56
57 /* Write to mux register. Don't use i2c_transfer() and i2c_smbus_xfer()
58  * for this as they will try to lock adapter a second time.
59  */
60 static int mlxcpld_mux_reg_write(struct i2c_adapter *adap,
61                                  struct mlxcpld_mux *mux, u32 val)
62 {
63         struct i2c_client *client = mux->client;
64         union i2c_smbus_data data;
65         struct i2c_msg msg;
66         u8 buf[3];
67
68         switch (mux->pdata.reg_size) {
69         case 1:
70                 data.byte = val;
71                 return __i2c_smbus_xfer(adap, client->addr, client->flags,
72                                         I2C_SMBUS_WRITE, mux->pdata.sel_reg_addr,
73                                         I2C_SMBUS_BYTE_DATA, &data);
74         case 2:
75                 buf[0] = mux->pdata.sel_reg_addr >> 8;
76                 buf[1] = mux->pdata.sel_reg_addr;
77                 buf[2] = val;
78                 msg.addr = client->addr;
79                 msg.buf = buf;
80                 msg.len = mux->pdata.reg_size + 1;
81                 msg.flags = 0;
82                 return __i2c_transfer(adap, &msg, 1);
83         default:
84                 return -EINVAL;
85         }
86 }
87
88 static int mlxcpld_mux_select_chan(struct i2c_mux_core *muxc, u32 chan)
89 {
90         struct mlxcpld_mux *mux = i2c_mux_priv(muxc);
91         u32 regval = chan;
92         int err = 0;
93
94         if (mux->pdata.reg_size == 1)
95                 regval += 1;
96
97         /* Only select the channel if its different from the last channel */
98         if (mux->last_val != regval) {
99                 err = mlxcpld_mux_reg_write(muxc->parent, mux, regval);
100                 mux->last_val = err < 0 ? -1 : regval;
101         }
102
103         return err;
104 }
105
106 static int mlxcpld_mux_deselect(struct i2c_mux_core *muxc, u32 chan)
107 {
108         struct mlxcpld_mux *mux = i2c_mux_priv(muxc);
109
110         /* Deselect active channel */
111         mux->last_val = -1;
112
113         return mlxcpld_mux_reg_write(muxc->parent, mux, 0);
114 }
115
116 /* Probe/reomove functions */
117 static int mlxcpld_mux_probe(struct platform_device *pdev)
118 {
119         struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&pdev->dev);
120         struct i2c_client *client = to_i2c_client(pdev->dev.parent);
121         struct i2c_mux_core *muxc;
122         struct mlxcpld_mux *data;
123         int num, err;
124         u32 func;
125
126         if (!pdata)
127                 return -EINVAL;
128
129         switch (pdata->reg_size) {
130         case 1:
131                 func = I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
132                 break;
133         case 2:
134                 func = I2C_FUNC_I2C;
135                 break;
136         default:
137                 return -EINVAL;
138         }
139
140         if (!i2c_check_functionality(client->adapter, func))
141                 return -ENODEV;
142
143         muxc = i2c_mux_alloc(client->adapter, &pdev->dev, pdata->num_adaps,
144                              sizeof(*data), 0, mlxcpld_mux_select_chan,
145                              mlxcpld_mux_deselect);
146         if (!muxc)
147                 return -ENOMEM;
148
149         platform_set_drvdata(pdev, muxc);
150         data = i2c_mux_priv(muxc);
151         data->client = client;
152         memcpy(&data->pdata, pdata, sizeof(*pdata));
153         data->last_val = -1; /* force the first selection */
154
155         /* Create an adapter for each channel. */
156         for (num = 0; num < pdata->num_adaps; num++) {
157                 err = i2c_mux_add_adapter(muxc, 0, pdata->chan_ids[num], 0);
158                 if (err)
159                         goto virt_reg_failed;
160         }
161
162         /* Notify caller when all channels' adapters are created. */
163         if (pdata->completion_notify)
164                 pdata->completion_notify(pdata->handle, muxc->parent, muxc->adapter);
165
166         return 0;
167
168 virt_reg_failed:
169         i2c_mux_del_adapters(muxc);
170         return err;
171 }
172
173 static int mlxcpld_mux_remove(struct platform_device *pdev)
174 {
175         struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
176
177         i2c_mux_del_adapters(muxc);
178         return 0;
179 }
180
181 static struct platform_driver mlxcpld_mux_driver = {
182         .driver = {
183                 .name = "i2c-mux-mlxcpld",
184         },
185         .probe = mlxcpld_mux_probe,
186         .remove = mlxcpld_mux_remove,
187 };
188
189 module_platform_driver(mlxcpld_mux_driver);
190
191 MODULE_AUTHOR("Michael Shych (michaels@mellanox.com)");
192 MODULE_DESCRIPTION("Mellanox I2C-CPLD-MUX driver");
193 MODULE_LICENSE("Dual BSD/GPL");
194 MODULE_ALIAS("platform:i2c-mux-mlxcpld");