1 /*
2  * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
3  * Copyright (c) 2016 Vadim Pasternak <vadimp@mellanox.com>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the names of the copyright holders nor the names of its
14  *    contributors may be used to endorse or promote products derived from
15  *    this software without specific prior written permission.
16  *
17  * Alternatively, this software may be distributed under the terms of the
18  * GNU General Public License ("GPL") version 2 as published by the Free
19  * Software Foundation.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <linux/device.h>
35 #include <linux/dmi.h>
36 #include <linux/i2c.h>
37 #include <linux/i2c-mux.h>
38 #include <linux/io.h>
39 #include <linux/module.h>
40 #include <linux/platform_device.h>
41 #include <linux/platform_data/i2c-mux-reg.h>
42 #include <linux/platform_data/mlxreg.h>
43 #include <linux/regmap.h>
44 
45 #define MLX_PLAT_DEVICE_NAME		"mlxplat"
46 
47 /* LPC bus IO offsets */
48 #define MLXPLAT_CPLD_LPC_I2C_BASE_ADRR		0x2000
49 #define MLXPLAT_CPLD_LPC_REG_BASE_ADRR		0x2500
50 #define MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET	0x3a
51 #define MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET	0x3b
52 #define MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET	0x40
53 #define MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET	0x41
54 #define MLXPLAT_CPLD_LPC_REG_PSU_OFFSET		0x58
55 #define MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET	0x59
56 #define MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET	0x5a
57 #define MLXPLAT_CPLD_LPC_REG_PWR_OFFSET		0x64
58 #define MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFFSET	0x65
59 #define MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFFSET	0x66
60 #define MLXPLAT_CPLD_LPC_REG_FAN_OFFSET		0x88
61 #define MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET	0x89
62 #define MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET	0x8a
63 #define MLXPLAT_CPLD_LPC_IO_RANGE		0x100
64 #define MLXPLAT_CPLD_LPC_I2C_CH1_OFF		0xdb
65 #define MLXPLAT_CPLD_LPC_I2C_CH2_OFF		0xda
66 #define MLXPLAT_CPLD_LPC_PIO_OFFSET		0x10000UL
67 #define MLXPLAT_CPLD_LPC_REG1	((MLXPLAT_CPLD_LPC_REG_BASE_ADRR + \
68 				  MLXPLAT_CPLD_LPC_I2C_CH1_OFF) | \
69 				  MLXPLAT_CPLD_LPC_PIO_OFFSET)
70 #define MLXPLAT_CPLD_LPC_REG2	((MLXPLAT_CPLD_LPC_REG_BASE_ADRR + \
71 				  MLXPLAT_CPLD_LPC_I2C_CH2_OFF) | \
72 				  MLXPLAT_CPLD_LPC_PIO_OFFSET)
73 
74 /* Masks for aggregation, psu, pwr and fan event in CPLD related registers. */
75 #define MLXPLAT_CPLD_AGGR_PSU_MASK_DEF	0x08
76 #define MLXPLAT_CPLD_AGGR_PWR_MASK_DEF	0x08
77 #define MLXPLAT_CPLD_AGGR_FAN_MASK_DEF	0x40
78 #define MLXPLAT_CPLD_AGGR_MASK_DEF	(MLXPLAT_CPLD_AGGR_PSU_MASK_DEF | \
79 					 MLXPLAT_CPLD_AGGR_FAN_MASK_DEF)
80 #define MLXPLAT_CPLD_AGGR_MASK_NG_DEF	0x04
81 #define MLXPLAT_CPLD_LOW_AGGR_MASK_LOW	0xc0
82 #define MLXPLAT_CPLD_AGGR_MASK_MSN21XX	0x04
83 #define MLXPLAT_CPLD_PSU_MASK		GENMASK(1, 0)
84 #define MLXPLAT_CPLD_PWR_MASK		GENMASK(1, 0)
85 #define MLXPLAT_CPLD_FAN_MASK		GENMASK(3, 0)
86 #define MLXPLAT_CPLD_FAN_NG_MASK	GENMASK(5, 0)
87 
88 /* Default I2C parent bus number */
89 #define MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR	1
90 
91 /* Maximum number of possible physical buses equipped on system */
92 #define MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM	16
93 
94 /* Number of channels in group */
95 #define MLXPLAT_CPLD_GRP_CHNL_NUM		8
96 
97 /* Start channel numbers */
98 #define MLXPLAT_CPLD_CH1			2
99 #define MLXPLAT_CPLD_CH2			10
100 
101 /* Number of LPC attached MUX platform devices */
102 #define MLXPLAT_CPLD_LPC_MUX_DEVS		2
103 
104 /* Hotplug devices adapter numbers */
105 #define MLXPLAT_CPLD_NR_NONE			-1
106 #define MLXPLAT_CPLD_PSU_DEFAULT_NR		10
107 #define MLXPLAT_CPLD_PSU_MSNXXXX_NR		4
108 #define MLXPLAT_CPLD_FAN1_DEFAULT_NR		11
109 #define MLXPLAT_CPLD_FAN2_DEFAULT_NR		12
110 #define MLXPLAT_CPLD_FAN3_DEFAULT_NR		13
111 #define MLXPLAT_CPLD_FAN4_DEFAULT_NR		14
112 
113 /* mlxplat_priv - platform private data
114  * @pdev_i2c - i2c controller platform device
115  * @pdev_mux - array of mux platform devices
116  * @pdev_hotplug - hotplug platform devices
117  */
118 struct mlxplat_priv {
119 	struct platform_device *pdev_i2c;
120 	struct platform_device *pdev_mux[MLXPLAT_CPLD_LPC_MUX_DEVS];
121 	struct platform_device *pdev_hotplug;
122 };
123 
124 /* Regions for LPC I2C controller and LPC base register space */
125 static const struct resource mlxplat_lpc_resources[] = {
126 	[0] = DEFINE_RES_NAMED(MLXPLAT_CPLD_LPC_I2C_BASE_ADRR,
127 			       MLXPLAT_CPLD_LPC_IO_RANGE,
128 			       "mlxplat_cpld_lpc_i2c_ctrl", IORESOURCE_IO),
129 	[1] = DEFINE_RES_NAMED(MLXPLAT_CPLD_LPC_REG_BASE_ADRR,
130 			       MLXPLAT_CPLD_LPC_IO_RANGE,
131 			       "mlxplat_cpld_lpc_regs",
132 			       IORESOURCE_IO),
133 };
134 
135 /* Platform default channels */
136 static const int mlxplat_default_channels[][MLXPLAT_CPLD_GRP_CHNL_NUM] = {
137 	{
138 		MLXPLAT_CPLD_CH1, MLXPLAT_CPLD_CH1 + 1, MLXPLAT_CPLD_CH1 + 2,
139 		MLXPLAT_CPLD_CH1 + 3, MLXPLAT_CPLD_CH1 + 4, MLXPLAT_CPLD_CH1 +
140 		5, MLXPLAT_CPLD_CH1 + 6, MLXPLAT_CPLD_CH1 + 7
141 	},
142 	{
143 		MLXPLAT_CPLD_CH2, MLXPLAT_CPLD_CH2 + 1, MLXPLAT_CPLD_CH2 + 2,
144 		MLXPLAT_CPLD_CH2 + 3, MLXPLAT_CPLD_CH2 + 4, MLXPLAT_CPLD_CH2 +
145 		5, MLXPLAT_CPLD_CH2 + 6, MLXPLAT_CPLD_CH2 + 7
146 	},
147 };
148 
149 /* Platform channels for MSN21xx system family */
150 static const int mlxplat_msn21xx_channels[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
151 
152 /* Platform mux data */
153 static struct i2c_mux_reg_platform_data mlxplat_mux_data[] = {
154 	{
155 		.parent = 1,
156 		.base_nr = MLXPLAT_CPLD_CH1,
157 		.write_only = 1,
158 		.reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG1,
159 		.reg_size = 1,
160 		.idle_in_use = 1,
161 	},
162 	{
163 		.parent = 1,
164 		.base_nr = MLXPLAT_CPLD_CH2,
165 		.write_only = 1,
166 		.reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG2,
167 		.reg_size = 1,
168 		.idle_in_use = 1,
169 	},
170 
171 };
172 
173 /* Platform hotplug devices */
174 static struct i2c_board_info mlxplat_mlxcpld_psu[] = {
175 	{
176 		I2C_BOARD_INFO("24c02", 0x51),
177 	},
178 	{
179 		I2C_BOARD_INFO("24c02", 0x50),
180 	},
181 };
182 
183 static struct i2c_board_info mlxplat_mlxcpld_ng_psu[] = {
184 	{
185 		I2C_BOARD_INFO("24c32", 0x51),
186 	},
187 	{
188 		I2C_BOARD_INFO("24c32", 0x50),
189 	},
190 };
191 
192 static struct i2c_board_info mlxplat_mlxcpld_pwr[] = {
193 	{
194 		I2C_BOARD_INFO("dps460", 0x59),
195 	},
196 	{
197 		I2C_BOARD_INFO("dps460", 0x58),
198 	},
199 };
200 
201 static struct i2c_board_info mlxplat_mlxcpld_fan[] = {
202 	{
203 		I2C_BOARD_INFO("24c32", 0x50),
204 	},
205 	{
206 		I2C_BOARD_INFO("24c32", 0x50),
207 	},
208 	{
209 		I2C_BOARD_INFO("24c32", 0x50),
210 	},
211 	{
212 		I2C_BOARD_INFO("24c32", 0x50),
213 	},
214 };
215 
216 /* Platform hotplug default data */
217 static struct mlxreg_core_data mlxplat_mlxcpld_default_psu_items_data[] = {
218 	{
219 		.label = "psu1",
220 		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
221 		.mask = BIT(0),
222 		.hpdev.brdinfo = &mlxplat_mlxcpld_psu[0],
223 		.hpdev.nr = MLXPLAT_CPLD_PSU_DEFAULT_NR,
224 	},
225 	{
226 		.label = "psu2",
227 		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
228 		.mask = BIT(1),
229 		.hpdev.brdinfo = &mlxplat_mlxcpld_psu[1],
230 		.hpdev.nr = MLXPLAT_CPLD_PSU_DEFAULT_NR,
231 	},
232 };
233 
234 static struct mlxreg_core_data mlxplat_mlxcpld_default_pwr_items_data[] = {
235 	{
236 		.label = "pwr1",
237 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
238 		.mask = BIT(0),
239 		.hpdev.brdinfo = &mlxplat_mlxcpld_pwr[0],
240 		.hpdev.nr = MLXPLAT_CPLD_PSU_DEFAULT_NR,
241 	},
242 	{
243 		.label = "pwr2",
244 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
245 		.mask = BIT(1),
246 		.hpdev.brdinfo = &mlxplat_mlxcpld_pwr[1],
247 		.hpdev.nr = MLXPLAT_CPLD_PSU_DEFAULT_NR,
248 	},
249 };
250 
251 static struct mlxreg_core_data mlxplat_mlxcpld_default_fan_items_data[] = {
252 	{
253 		.label = "fan1",
254 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
255 		.mask = BIT(0),
256 		.hpdev.brdinfo = &mlxplat_mlxcpld_fan[0],
257 		.hpdev.nr = MLXPLAT_CPLD_FAN1_DEFAULT_NR,
258 	},
259 	{
260 		.label = "fan2",
261 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
262 		.mask = BIT(1),
263 		.hpdev.brdinfo = &mlxplat_mlxcpld_fan[1],
264 		.hpdev.nr = MLXPLAT_CPLD_FAN2_DEFAULT_NR,
265 	},
266 	{
267 		.label = "fan3",
268 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
269 		.mask = BIT(2),
270 		.hpdev.brdinfo = &mlxplat_mlxcpld_fan[2],
271 		.hpdev.nr = MLXPLAT_CPLD_FAN3_DEFAULT_NR,
272 	},
273 	{
274 		.label = "fan4",
275 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
276 		.mask = BIT(3),
277 		.hpdev.brdinfo = &mlxplat_mlxcpld_fan[3],
278 		.hpdev.nr = MLXPLAT_CPLD_FAN4_DEFAULT_NR,
279 	},
280 };
281 
282 static struct mlxreg_core_item mlxplat_mlxcpld_default_items[] = {
283 	{
284 		.data = mlxplat_mlxcpld_default_psu_items_data,
285 		.aggr_mask = MLXPLAT_CPLD_AGGR_PSU_MASK_DEF,
286 		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
287 		.mask = MLXPLAT_CPLD_PSU_MASK,
288 		.count = ARRAY_SIZE(mlxplat_mlxcpld_psu),
289 		.inversed = 1,
290 		.health = false,
291 	},
292 	{
293 		.data = mlxplat_mlxcpld_default_pwr_items_data,
294 		.aggr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF,
295 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
296 		.mask = MLXPLAT_CPLD_PWR_MASK,
297 		.count = ARRAY_SIZE(mlxplat_mlxcpld_pwr),
298 		.inversed = 0,
299 		.health = false,
300 	},
301 	{
302 		.data = mlxplat_mlxcpld_default_fan_items_data,
303 		.aggr_mask = MLXPLAT_CPLD_AGGR_FAN_MASK_DEF,
304 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
305 		.mask = MLXPLAT_CPLD_FAN_MASK,
306 		.count = ARRAY_SIZE(mlxplat_mlxcpld_fan),
307 		.inversed = 1,
308 		.health = false,
309 	},
310 };
311 
312 static
313 struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_default_data = {
314 	.items = mlxplat_mlxcpld_default_items,
315 	.counter = ARRAY_SIZE(mlxplat_mlxcpld_default_items),
316 	.cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
317 	.mask = MLXPLAT_CPLD_AGGR_MASK_DEF,
318 };
319 
320 static struct mlxreg_core_data mlxplat_mlxcpld_msn21xx_pwr_items_data[] = {
321 	{
322 		.label = "pwr1",
323 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
324 		.mask = BIT(0),
325 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
326 	},
327 	{
328 		.label = "pwr2",
329 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
330 		.mask = BIT(1),
331 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
332 	},
333 };
334 
335 /* Platform hotplug MSN21xx system family data */
336 static struct mlxreg_core_item mlxplat_mlxcpld_msn21xx_items[] = {
337 	{
338 		.data = mlxplat_mlxcpld_msn21xx_pwr_items_data,
339 		.aggr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF,
340 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
341 		.mask = MLXPLAT_CPLD_PWR_MASK,
342 		.count = ARRAY_SIZE(mlxplat_mlxcpld_msn21xx_pwr_items_data),
343 		.inversed = 0,
344 		.health = false,
345 	},
346 };
347 
348 static
349 struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_msn21xx_data = {
350 	.items = mlxplat_mlxcpld_msn21xx_items,
351 	.counter = ARRAY_SIZE(mlxplat_mlxcpld_msn21xx_items),
352 	.cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
353 	.mask = MLXPLAT_CPLD_AGGR_MASK_DEF,
354 	.cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET,
355 	.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW,
356 };
357 
358 /* Platform hotplug msn274x system family data */
359 static struct mlxreg_core_data mlxplat_mlxcpld_msn274x_psu_items_data[] = {
360 	{
361 		.label = "psu1",
362 		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
363 		.mask = BIT(0),
364 		.hpdev.brdinfo = &mlxplat_mlxcpld_psu[0],
365 		.hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR,
366 	},
367 	{
368 		.label = "psu2",
369 		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
370 		.mask = BIT(1),
371 		.hpdev.brdinfo = &mlxplat_mlxcpld_psu[1],
372 		.hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR,
373 	},
374 };
375 
376 static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_pwr_items_data[] = {
377 	{
378 		.label = "pwr1",
379 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
380 		.mask = BIT(0),
381 		.hpdev.brdinfo = &mlxplat_mlxcpld_pwr[0],
382 		.hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR,
383 	},
384 	{
385 		.label = "pwr2",
386 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
387 		.mask = BIT(1),
388 		.hpdev.brdinfo = &mlxplat_mlxcpld_pwr[1],
389 		.hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR,
390 	},
391 };
392 
393 static struct mlxreg_core_data mlxplat_mlxcpld_msn274x_fan_items_data[] = {
394 	{
395 		.label = "fan1",
396 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
397 		.mask = BIT(0),
398 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
399 	},
400 	{
401 		.label = "fan2",
402 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
403 		.mask = BIT(1),
404 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
405 	},
406 	{
407 		.label = "fan3",
408 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
409 		.mask = BIT(2),
410 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
411 	},
412 	{
413 		.label = "fan4",
414 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
415 		.mask = BIT(3),
416 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
417 	},
418 };
419 
420 static struct mlxreg_core_item mlxplat_mlxcpld_msn274x_items[] = {
421 	{
422 		.data = mlxplat_mlxcpld_msn274x_psu_items_data,
423 		.aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
424 		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
425 		.mask = MLXPLAT_CPLD_PSU_MASK,
426 		.count = ARRAY_SIZE(mlxplat_mlxcpld_msn274x_psu_items_data),
427 		.inversed = 1,
428 		.health = false,
429 	},
430 	{
431 		.data = mlxplat_mlxcpld_default_ng_pwr_items_data,
432 		.aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
433 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
434 		.mask = MLXPLAT_CPLD_PWR_MASK,
435 		.count = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_pwr_items_data),
436 		.inversed = 0,
437 		.health = false,
438 	},
439 	{
440 		.data = mlxplat_mlxcpld_msn274x_fan_items_data,
441 		.aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
442 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
443 		.mask = MLXPLAT_CPLD_FAN_MASK,
444 		.count = ARRAY_SIZE(mlxplat_mlxcpld_msn274x_fan_items_data),
445 		.inversed = 1,
446 		.health = false,
447 	},
448 };
449 
450 static
451 struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_msn274x_data = {
452 	.items = mlxplat_mlxcpld_msn274x_items,
453 	.counter = ARRAY_SIZE(mlxplat_mlxcpld_msn274x_items),
454 	.cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
455 	.mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
456 	.cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET,
457 	.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW,
458 };
459 
460 /* Platform hotplug MSN201x system family data */
461 static struct mlxreg_core_data mlxplat_mlxcpld_msn201x_pwr_items_data[] = {
462 	{
463 		.label = "pwr1",
464 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
465 		.mask = BIT(0),
466 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
467 	},
468 	{
469 		.label = "pwr2",
470 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
471 		.mask = BIT(1),
472 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
473 	},
474 };
475 
476 static struct mlxreg_core_item mlxplat_mlxcpld_msn201x_items[] = {
477 	{
478 		.data = mlxplat_mlxcpld_msn201x_pwr_items_data,
479 		.aggr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF,
480 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
481 		.mask = MLXPLAT_CPLD_PWR_MASK,
482 		.count = ARRAY_SIZE(mlxplat_mlxcpld_msn201x_pwr_items_data),
483 		.inversed = 0,
484 		.health = false,
485 	},
486 };
487 
488 static
489 struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_msn201x_data = {
490 	.items = mlxplat_mlxcpld_msn21xx_items,
491 	.counter = ARRAY_SIZE(mlxplat_mlxcpld_msn201x_items),
492 	.cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
493 	.mask = MLXPLAT_CPLD_AGGR_MASK_DEF,
494 	.cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET,
495 	.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW,
496 };
497 
498 /* Platform hotplug next generation system family data */
499 static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_psu_items_data[] = {
500 	{
501 		.label = "psu1",
502 		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
503 		.mask = BIT(0),
504 		.hpdev.brdinfo = &mlxplat_mlxcpld_ng_psu[0],
505 		.hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR,
506 	},
507 	{
508 		.label = "psu2",
509 		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
510 		.mask = BIT(1),
511 		.hpdev.brdinfo = &mlxplat_mlxcpld_ng_psu[1],
512 		.hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR,
513 	},
514 };
515 
516 static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_fan_items_data[] = {
517 	{
518 		.label = "fan1",
519 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
520 		.mask = BIT(0),
521 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
522 	},
523 	{
524 		.label = "fan2",
525 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
526 		.mask = BIT(1),
527 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
528 	},
529 	{
530 		.label = "fan3",
531 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
532 		.mask = BIT(2),
533 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
534 	},
535 	{
536 		.label = "fan4",
537 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
538 		.mask = BIT(3),
539 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
540 	},
541 	{
542 		.label = "fan5",
543 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
544 		.mask = BIT(4),
545 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
546 	},
547 	{
548 		.label = "fan6",
549 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
550 		.mask = BIT(5),
551 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
552 	},
553 };
554 
555 static struct mlxreg_core_item mlxplat_mlxcpld_default_ng_items[] = {
556 	{
557 		.data = mlxplat_mlxcpld_default_ng_psu_items_data,
558 		.aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
559 		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
560 		.mask = MLXPLAT_CPLD_PSU_MASK,
561 		.count = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_psu_items_data),
562 		.inversed = 1,
563 		.health = false,
564 	},
565 	{
566 		.data = mlxplat_mlxcpld_default_ng_pwr_items_data,
567 		.aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
568 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
569 		.mask = MLXPLAT_CPLD_PWR_MASK,
570 		.count = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_pwr_items_data),
571 		.inversed = 0,
572 		.health = false,
573 	},
574 	{
575 		.data = mlxplat_mlxcpld_default_ng_fan_items_data,
576 		.aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
577 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
578 		.mask = MLXPLAT_CPLD_FAN_NG_MASK,
579 		.count = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_fan_items_data),
580 		.inversed = 1,
581 		.health = false,
582 	},
583 };
584 
585 static
586 struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_default_ng_data = {
587 	.items = mlxplat_mlxcpld_default_ng_items,
588 	.counter = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_items),
589 	.cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
590 	.mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
591 	.cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET,
592 	.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW,
593 };
594 
595 static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg)
596 {
597 	switch (reg) {
598 	case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
599 	case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
600 	case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
601 	case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET:
602 	case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFFSET:
603 	case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFFSET:
604 	case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET:
605 	case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET:
606 		return true;
607 	}
608 	return false;
609 }
610 
611 static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg)
612 {
613 	switch (reg) {
614 	case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET:
615 	case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
616 	case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET:
617 	case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
618 	case MLXPLAT_CPLD_LPC_REG_PSU_OFFSET:
619 	case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
620 	case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET:
621 	case MLXPLAT_CPLD_LPC_REG_PWR_OFFSET:
622 	case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFFSET:
623 	case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFFSET:
624 	case MLXPLAT_CPLD_LPC_REG_FAN_OFFSET:
625 	case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET:
626 	case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET:
627 		return true;
628 	}
629 	return false;
630 }
631 
632 static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg)
633 {
634 	switch (reg) {
635 	case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET:
636 	case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
637 	case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET:
638 	case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
639 	case MLXPLAT_CPLD_LPC_REG_PSU_OFFSET:
640 	case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
641 	case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET:
642 	case MLXPLAT_CPLD_LPC_REG_PWR_OFFSET:
643 	case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFFSET:
644 	case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFFSET:
645 	case MLXPLAT_CPLD_LPC_REG_FAN_OFFSET:
646 	case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET:
647 	case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET:
648 		return true;
649 	}
650 	return false;
651 }
652 
653 struct mlxplat_mlxcpld_regmap_context {
654 	void __iomem *base;
655 };
656 
657 static struct mlxplat_mlxcpld_regmap_context mlxplat_mlxcpld_regmap_ctx;
658 
659 static int
660 mlxplat_mlxcpld_reg_read(void *context, unsigned int reg, unsigned int *val)
661 {
662 	struct mlxplat_mlxcpld_regmap_context *ctx = context;
663 
664 	*val = ioread8(ctx->base + reg);
665 	return 0;
666 }
667 
668 static int
669 mlxplat_mlxcpld_reg_write(void *context, unsigned int reg, unsigned int val)
670 {
671 	struct mlxplat_mlxcpld_regmap_context *ctx = context;
672 
673 	iowrite8(val, ctx->base + reg);
674 	return 0;
675 }
676 
677 static const struct regmap_config mlxplat_mlxcpld_regmap_config = {
678 	.reg_bits = 8,
679 	.val_bits = 8,
680 	.max_register = 255,
681 	.cache_type = REGCACHE_FLAT,
682 	.writeable_reg = mlxplat_mlxcpld_writeable_reg,
683 	.readable_reg = mlxplat_mlxcpld_readable_reg,
684 	.volatile_reg = mlxplat_mlxcpld_volatile_reg,
685 	.reg_read = mlxplat_mlxcpld_reg_read,
686 	.reg_write = mlxplat_mlxcpld_reg_write,
687 };
688 
689 static struct resource mlxplat_mlxcpld_resources[] = {
690 	[0] = DEFINE_RES_IRQ_NAMED(17, "mlxreg-hotplug"),
691 };
692 
693 static struct platform_device *mlxplat_dev;
694 static struct mlxreg_core_hotplug_platform_data *mlxplat_hotplug;
695 
696 static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
697 {
698 	int i;
699 
700 	for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
701 		mlxplat_mux_data[i].values = mlxplat_default_channels[i];
702 		mlxplat_mux_data[i].n_values =
703 				ARRAY_SIZE(mlxplat_default_channels[i]);
704 	}
705 	mlxplat_hotplug = &mlxplat_mlxcpld_default_data;
706 	mlxplat_hotplug->deferred_nr =
707 		mlxplat_default_channels[i - 1][MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
708 
709 	return 1;
710 };
711 
712 static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi)
713 {
714 	int i;
715 
716 	for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
717 		mlxplat_mux_data[i].values = mlxplat_msn21xx_channels;
718 		mlxplat_mux_data[i].n_values =
719 				ARRAY_SIZE(mlxplat_msn21xx_channels);
720 	}
721 	mlxplat_hotplug = &mlxplat_mlxcpld_msn21xx_data;
722 	mlxplat_hotplug->deferred_nr =
723 		mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
724 
725 	return 1;
726 };
727 
728 static int __init mlxplat_dmi_msn274x_matched(const struct dmi_system_id *dmi)
729 {
730 	int i;
731 
732 	for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
733 		mlxplat_mux_data[i].values = mlxplat_msn21xx_channels;
734 		mlxplat_mux_data[i].n_values =
735 				ARRAY_SIZE(mlxplat_msn21xx_channels);
736 	}
737 	mlxplat_hotplug = &mlxplat_mlxcpld_msn274x_data;
738 	mlxplat_hotplug->deferred_nr =
739 		mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
740 
741 	return 1;
742 };
743 
744 static int __init mlxplat_dmi_msn201x_matched(const struct dmi_system_id *dmi)
745 {
746 	int i;
747 
748 	for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
749 		mlxplat_mux_data[i].values = mlxplat_msn21xx_channels;
750 		mlxplat_mux_data[i].n_values =
751 				ARRAY_SIZE(mlxplat_msn21xx_channels);
752 	}
753 	mlxplat_hotplug = &mlxplat_mlxcpld_msn201x_data;
754 	mlxplat_hotplug->deferred_nr =
755 		mlxplat_default_channels[i - 1][MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
756 
757 	return 1;
758 };
759 
760 static int __init mlxplat_dmi_qmb7xx_matched(const struct dmi_system_id *dmi)
761 {
762 	int i;
763 
764 	for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
765 		mlxplat_mux_data[i].values = mlxplat_msn21xx_channels;
766 		mlxplat_mux_data[i].n_values =
767 				ARRAY_SIZE(mlxplat_msn21xx_channels);
768 	}
769 	mlxplat_hotplug = &mlxplat_mlxcpld_default_ng_data;
770 	mlxplat_hotplug->deferred_nr =
771 		mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
772 
773 	return 1;
774 };
775 
776 static const struct dmi_system_id mlxplat_dmi_table[] __initconst = {
777 	{
778 		.callback = mlxplat_dmi_msn274x_matched,
779 		.matches = {
780 			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
781 			DMI_MATCH(DMI_PRODUCT_NAME, "MSN274"),
782 		},
783 	},
784 	{
785 		.callback = mlxplat_dmi_default_matched,
786 		.matches = {
787 			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
788 			DMI_MATCH(DMI_PRODUCT_NAME, "MSN24"),
789 		},
790 	},
791 	{
792 		.callback = mlxplat_dmi_default_matched,
793 		.matches = {
794 			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
795 			DMI_MATCH(DMI_PRODUCT_NAME, "MSN27"),
796 		},
797 	},
798 	{
799 		.callback = mlxplat_dmi_default_matched,
800 		.matches = {
801 			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
802 			DMI_MATCH(DMI_PRODUCT_NAME, "MSB"),
803 		},
804 	},
805 	{
806 		.callback = mlxplat_dmi_default_matched,
807 		.matches = {
808 			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
809 			DMI_MATCH(DMI_PRODUCT_NAME, "MSX"),
810 		},
811 	},
812 	{
813 		.callback = mlxplat_dmi_msn21xx_matched,
814 		.matches = {
815 			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
816 			DMI_MATCH(DMI_PRODUCT_NAME, "MSN21"),
817 		},
818 	},
819 	{
820 		.callback = mlxplat_dmi_msn201x_matched,
821 		.matches = {
822 			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
823 			DMI_MATCH(DMI_PRODUCT_NAME, "MSN201"),
824 		},
825 	},
826 	{
827 		.callback = mlxplat_dmi_qmb7xx_matched,
828 		.matches = {
829 			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
830 			DMI_MATCH(DMI_PRODUCT_NAME, "QMB7"),
831 		},
832 	},
833 	{
834 		.callback = mlxplat_dmi_qmb7xx_matched,
835 		.matches = {
836 			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
837 			DMI_MATCH(DMI_PRODUCT_NAME, "SN37"),
838 		},
839 	},
840 	{
841 		.callback = mlxplat_dmi_qmb7xx_matched,
842 		.matches = {
843 			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
844 			DMI_MATCH(DMI_PRODUCT_NAME, "SN34"),
845 		},
846 	},
847 	{ }
848 };
849 
850 MODULE_DEVICE_TABLE(dmi, mlxplat_dmi_table);
851 
852 static int mlxplat_mlxcpld_verify_bus_topology(int *nr)
853 {
854 	struct i2c_adapter *search_adap;
855 	int shift, i;
856 
857 	/* Scan adapters from expected id to verify it is free. */
858 	*nr = MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR;
859 	for (i = MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR; i <
860 	     MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM; i++) {
861 		search_adap = i2c_get_adapter(i);
862 		if (search_adap) {
863 			i2c_put_adapter(search_adap);
864 			continue;
865 		}
866 
867 		/* Return if expected parent adapter is free. */
868 		if (i == MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR)
869 			return 0;
870 		break;
871 	}
872 
873 	/* Return with error if free id for adapter is not found. */
874 	if (i == MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM)
875 		return -ENODEV;
876 
877 	/* Shift adapter ids, since expected parent adapter is not free. */
878 	*nr = i;
879 	for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
880 		shift = *nr - mlxplat_mux_data[i].parent;
881 		mlxplat_mux_data[i].parent = *nr;
882 		mlxplat_mux_data[i].base_nr += shift;
883 		if (shift > 0)
884 			mlxplat_hotplug->shift_nr = shift;
885 	}
886 
887 	return 0;
888 }
889 
890 static int __init mlxplat_init(void)
891 {
892 	struct mlxplat_priv *priv;
893 	int i, nr, err;
894 
895 	if (!dmi_check_system(mlxplat_dmi_table))
896 		return -ENODEV;
897 
898 	mlxplat_dev = platform_device_register_simple(MLX_PLAT_DEVICE_NAME, -1,
899 					mlxplat_lpc_resources,
900 					ARRAY_SIZE(mlxplat_lpc_resources));
901 
902 	if (IS_ERR(mlxplat_dev))
903 		return PTR_ERR(mlxplat_dev);
904 
905 	priv = devm_kzalloc(&mlxplat_dev->dev, sizeof(struct mlxplat_priv),
906 			    GFP_KERNEL);
907 	if (!priv) {
908 		err = -ENOMEM;
909 		goto fail_alloc;
910 	}
911 	platform_set_drvdata(mlxplat_dev, priv);
912 
913 	err = mlxplat_mlxcpld_verify_bus_topology(&nr);
914 	if (nr < 0)
915 		goto fail_alloc;
916 
917 	nr = (nr == MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM) ? -1 : nr;
918 	priv->pdev_i2c = platform_device_register_simple("i2c_mlxcpld", nr,
919 							 NULL, 0);
920 	if (IS_ERR(priv->pdev_i2c)) {
921 		err = PTR_ERR(priv->pdev_i2c);
922 		goto fail_alloc;
923 	}
924 
925 	for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
926 		priv->pdev_mux[i] = platform_device_register_resndata(
927 						&mlxplat_dev->dev,
928 						"i2c-mux-reg", i, NULL,
929 						0, &mlxplat_mux_data[i],
930 						sizeof(mlxplat_mux_data[i]));
931 		if (IS_ERR(priv->pdev_mux[i])) {
932 			err = PTR_ERR(priv->pdev_mux[i]);
933 			goto fail_platform_mux_register;
934 		}
935 	}
936 
937 	mlxplat_mlxcpld_regmap_ctx.base = devm_ioport_map(&mlxplat_dev->dev,
938 			       mlxplat_lpc_resources[1].start, 1);
939 	if (!mlxplat_mlxcpld_regmap_ctx.base) {
940 		err = -ENOMEM;
941 		goto fail_platform_mux_register;
942 	}
943 
944 	mlxplat_hotplug->regmap = devm_regmap_init(&mlxplat_dev->dev, NULL,
945 					&mlxplat_mlxcpld_regmap_ctx,
946 					&mlxplat_mlxcpld_regmap_config);
947 	if (IS_ERR(mlxplat_hotplug->regmap)) {
948 		err = PTR_ERR(mlxplat_hotplug->regmap);
949 		goto fail_platform_mux_register;
950 	}
951 
952 	priv->pdev_hotplug = platform_device_register_resndata(
953 				&mlxplat_dev->dev, "mlxreg-hotplug",
954 				PLATFORM_DEVID_NONE,
955 				mlxplat_mlxcpld_resources,
956 				ARRAY_SIZE(mlxplat_mlxcpld_resources),
957 				mlxplat_hotplug, sizeof(*mlxplat_hotplug));
958 	if (IS_ERR(priv->pdev_hotplug)) {
959 		err = PTR_ERR(priv->pdev_hotplug);
960 		goto fail_platform_mux_register;
961 	}
962 
963 	/* Sync registers with hardware. */
964 	regcache_mark_dirty(mlxplat_hotplug->regmap);
965 	err = regcache_sync(mlxplat_hotplug->regmap);
966 	if (err)
967 		goto fail_platform_hotplug_register;
968 
969 	return 0;
970 
971 fail_platform_hotplug_register:
972 	platform_device_unregister(priv->pdev_hotplug);
973 fail_platform_mux_register:
974 	while (--i >= 0)
975 		platform_device_unregister(priv->pdev_mux[i]);
976 	platform_device_unregister(priv->pdev_i2c);
977 fail_alloc:
978 	platform_device_unregister(mlxplat_dev);
979 
980 	return err;
981 }
982 module_init(mlxplat_init);
983 
984 static void __exit mlxplat_exit(void)
985 {
986 	struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev);
987 	int i;
988 
989 	platform_device_unregister(priv->pdev_hotplug);
990 
991 	for (i = ARRAY_SIZE(mlxplat_mux_data) - 1; i >= 0 ; i--)
992 		platform_device_unregister(priv->pdev_mux[i]);
993 
994 	platform_device_unregister(priv->pdev_i2c);
995 	platform_device_unregister(mlxplat_dev);
996 }
997 module_exit(mlxplat_exit);
998 
999 MODULE_AUTHOR("Vadim Pasternak (vadimp@mellanox.com)");
1000 MODULE_DESCRIPTION("Mellanox platform driver");
1001 MODULE_LICENSE("Dual BSD/GPL");
1002