xref: /linux/drivers/platform/x86/mlx-platform.c (revision e3b9f1e81de2083f359bacd2a94bf1c024f2ede0)
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 /* Start channel numbers */
89 #define MLXPLAT_CPLD_CH1			2
90 #define MLXPLAT_CPLD_CH2			10
91 
92 /* Number of LPC attached MUX platform devices */
93 #define MLXPLAT_CPLD_LPC_MUX_DEVS		2
94 
95 /* Hotplug devices adapter numbers */
96 #define MLXPLAT_CPLD_NR_NONE			-1
97 #define MLXPLAT_CPLD_PSU_DEFAULT_NR		10
98 #define MLXPLAT_CPLD_PSU_MSNXXXX_NR		4
99 #define MLXPLAT_CPLD_FAN1_DEFAULT_NR		11
100 #define MLXPLAT_CPLD_FAN2_DEFAULT_NR		12
101 #define MLXPLAT_CPLD_FAN3_DEFAULT_NR		13
102 #define MLXPLAT_CPLD_FAN4_DEFAULT_NR		14
103 
104 /* mlxplat_priv - platform private data
105  * @pdev_i2c - i2c controller platform device
106  * @pdev_mux - array of mux platform devices
107  * @pdev_hotplug - hotplug platform devices
108  */
109 struct mlxplat_priv {
110 	struct platform_device *pdev_i2c;
111 	struct platform_device *pdev_mux[MLXPLAT_CPLD_LPC_MUX_DEVS];
112 	struct platform_device *pdev_hotplug;
113 };
114 
115 /* Regions for LPC I2C controller and LPC base register space */
116 static const struct resource mlxplat_lpc_resources[] = {
117 	[0] = DEFINE_RES_NAMED(MLXPLAT_CPLD_LPC_I2C_BASE_ADRR,
118 			       MLXPLAT_CPLD_LPC_IO_RANGE,
119 			       "mlxplat_cpld_lpc_i2c_ctrl", IORESOURCE_IO),
120 	[1] = DEFINE_RES_NAMED(MLXPLAT_CPLD_LPC_REG_BASE_ADRR,
121 			       MLXPLAT_CPLD_LPC_IO_RANGE,
122 			       "mlxplat_cpld_lpc_regs",
123 			       IORESOURCE_IO),
124 };
125 
126 /* Platform default channels */
127 static const int mlxplat_default_channels[][8] = {
128 	{
129 		MLXPLAT_CPLD_CH1, MLXPLAT_CPLD_CH1 + 1, MLXPLAT_CPLD_CH1 + 2,
130 		MLXPLAT_CPLD_CH1 + 3, MLXPLAT_CPLD_CH1 + 4, MLXPLAT_CPLD_CH1 +
131 		5, MLXPLAT_CPLD_CH1 + 6, MLXPLAT_CPLD_CH1 + 7
132 	},
133 	{
134 		MLXPLAT_CPLD_CH2, MLXPLAT_CPLD_CH2 + 1, MLXPLAT_CPLD_CH2 + 2,
135 		MLXPLAT_CPLD_CH2 + 3, MLXPLAT_CPLD_CH2 + 4, MLXPLAT_CPLD_CH2 +
136 		5, MLXPLAT_CPLD_CH2 + 6, MLXPLAT_CPLD_CH2 + 7
137 	},
138 };
139 
140 /* Platform channels for MSN21xx system family */
141 static const int mlxplat_msn21xx_channels[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
142 
143 /* Platform mux data */
144 static struct i2c_mux_reg_platform_data mlxplat_mux_data[] = {
145 	{
146 		.parent = 1,
147 		.base_nr = MLXPLAT_CPLD_CH1,
148 		.write_only = 1,
149 		.reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG1,
150 		.reg_size = 1,
151 		.idle_in_use = 1,
152 	},
153 	{
154 		.parent = 1,
155 		.base_nr = MLXPLAT_CPLD_CH2,
156 		.write_only = 1,
157 		.reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG2,
158 		.reg_size = 1,
159 		.idle_in_use = 1,
160 	},
161 
162 };
163 
164 /* Platform hotplug devices */
165 static struct i2c_board_info mlxplat_mlxcpld_psu[] = {
166 	{
167 		I2C_BOARD_INFO("24c02", 0x51),
168 	},
169 	{
170 		I2C_BOARD_INFO("24c02", 0x50),
171 	},
172 };
173 
174 static struct i2c_board_info mlxplat_mlxcpld_ng_psu[] = {
175 	{
176 		I2C_BOARD_INFO("24c32", 0x51),
177 	},
178 	{
179 		I2C_BOARD_INFO("24c32", 0x50),
180 	},
181 };
182 
183 static struct i2c_board_info mlxplat_mlxcpld_pwr[] = {
184 	{
185 		I2C_BOARD_INFO("dps460", 0x59),
186 	},
187 	{
188 		I2C_BOARD_INFO("dps460", 0x58),
189 	},
190 };
191 
192 static struct i2c_board_info mlxplat_mlxcpld_fan[] = {
193 	{
194 		I2C_BOARD_INFO("24c32", 0x50),
195 	},
196 	{
197 		I2C_BOARD_INFO("24c32", 0x50),
198 	},
199 	{
200 		I2C_BOARD_INFO("24c32", 0x50),
201 	},
202 	{
203 		I2C_BOARD_INFO("24c32", 0x50),
204 	},
205 };
206 
207 /* Platform hotplug default data */
208 static struct mlxreg_core_data mlxplat_mlxcpld_default_psu_items_data[] = {
209 	{
210 		.label = "psu1",
211 		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
212 		.mask = BIT(0),
213 		.hpdev.brdinfo = &mlxplat_mlxcpld_psu[0],
214 		.hpdev.nr = MLXPLAT_CPLD_PSU_DEFAULT_NR,
215 	},
216 	{
217 		.label = "psu2",
218 		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
219 		.mask = BIT(1),
220 		.hpdev.brdinfo = &mlxplat_mlxcpld_psu[1],
221 		.hpdev.nr = MLXPLAT_CPLD_PSU_DEFAULT_NR,
222 	},
223 };
224 
225 static struct mlxreg_core_data mlxplat_mlxcpld_default_pwr_items_data[] = {
226 	{
227 		.label = "pwr1",
228 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
229 		.mask = BIT(0),
230 		.hpdev.brdinfo = &mlxplat_mlxcpld_pwr[0],
231 		.hpdev.nr = MLXPLAT_CPLD_PSU_DEFAULT_NR,
232 	},
233 	{
234 		.label = "pwr2",
235 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
236 		.mask = BIT(1),
237 		.hpdev.brdinfo = &mlxplat_mlxcpld_pwr[1],
238 		.hpdev.nr = MLXPLAT_CPLD_PSU_DEFAULT_NR,
239 	},
240 };
241 
242 static struct mlxreg_core_data mlxplat_mlxcpld_default_fan_items_data[] = {
243 	{
244 		.label = "fan1",
245 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
246 		.mask = BIT(0),
247 		.hpdev.brdinfo = &mlxplat_mlxcpld_fan[0],
248 		.hpdev.nr = MLXPLAT_CPLD_FAN1_DEFAULT_NR,
249 	},
250 	{
251 		.label = "fan2",
252 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
253 		.mask = BIT(1),
254 		.hpdev.brdinfo = &mlxplat_mlxcpld_fan[1],
255 		.hpdev.nr = MLXPLAT_CPLD_FAN2_DEFAULT_NR,
256 	},
257 	{
258 		.label = "fan3",
259 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
260 		.mask = BIT(2),
261 		.hpdev.brdinfo = &mlxplat_mlxcpld_fan[2],
262 		.hpdev.nr = MLXPLAT_CPLD_FAN3_DEFAULT_NR,
263 	},
264 	{
265 		.label = "fan4",
266 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
267 		.mask = BIT(3),
268 		.hpdev.brdinfo = &mlxplat_mlxcpld_fan[3],
269 		.hpdev.nr = MLXPLAT_CPLD_FAN4_DEFAULT_NR,
270 	},
271 };
272 
273 static struct mlxreg_core_item mlxplat_mlxcpld_default_items[] = {
274 	{
275 		.data = mlxplat_mlxcpld_default_psu_items_data,
276 		.aggr_mask = MLXPLAT_CPLD_AGGR_PSU_MASK_DEF,
277 		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
278 		.mask = MLXPLAT_CPLD_PSU_MASK,
279 		.count = ARRAY_SIZE(mlxplat_mlxcpld_psu),
280 		.inversed = 1,
281 		.health = false,
282 	},
283 	{
284 		.data = mlxplat_mlxcpld_default_pwr_items_data,
285 		.aggr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF,
286 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
287 		.mask = MLXPLAT_CPLD_PWR_MASK,
288 		.count = ARRAY_SIZE(mlxplat_mlxcpld_pwr),
289 		.inversed = 0,
290 		.health = false,
291 	},
292 	{
293 		.data = mlxplat_mlxcpld_default_fan_items_data,
294 		.aggr_mask = MLXPLAT_CPLD_AGGR_FAN_MASK_DEF,
295 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
296 		.mask = MLXPLAT_CPLD_FAN_MASK,
297 		.count = ARRAY_SIZE(mlxplat_mlxcpld_fan),
298 		.inversed = 1,
299 		.health = false,
300 	},
301 };
302 
303 static
304 struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_default_data = {
305 	.items = mlxplat_mlxcpld_default_items,
306 	.counter = ARRAY_SIZE(mlxplat_mlxcpld_default_items),
307 	.cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
308 	.mask = MLXPLAT_CPLD_AGGR_MASK_DEF,
309 };
310 
311 static struct mlxreg_core_data mlxplat_mlxcpld_msn21xx_pwr_items_data[] = {
312 	{
313 		.label = "pwr1",
314 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
315 		.mask = BIT(0),
316 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
317 	},
318 	{
319 		.label = "pwr2",
320 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
321 		.mask = BIT(1),
322 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
323 	},
324 };
325 
326 /* Platform hotplug MSN21xx system family data */
327 static struct mlxreg_core_item mlxplat_mlxcpld_msn21xx_items[] = {
328 	{
329 		.data = mlxplat_mlxcpld_msn21xx_pwr_items_data,
330 		.aggr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF,
331 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
332 		.mask = MLXPLAT_CPLD_PWR_MASK,
333 		.count = ARRAY_SIZE(mlxplat_mlxcpld_msn21xx_pwr_items_data),
334 		.inversed = 0,
335 		.health = false,
336 	},
337 };
338 
339 static
340 struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_msn21xx_data = {
341 	.items = mlxplat_mlxcpld_msn21xx_items,
342 	.counter = ARRAY_SIZE(mlxplat_mlxcpld_msn21xx_items),
343 	.cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
344 	.mask = MLXPLAT_CPLD_AGGR_MASK_DEF,
345 	.cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET,
346 	.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW,
347 };
348 
349 /* Platform hotplug msn274x system family data */
350 static struct mlxreg_core_data mlxplat_mlxcpld_msn274x_psu_items_data[] = {
351 	{
352 		.label = "psu1",
353 		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
354 		.mask = BIT(0),
355 		.hpdev.brdinfo = &mlxplat_mlxcpld_psu[0],
356 		.hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR,
357 	},
358 	{
359 		.label = "psu2",
360 		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
361 		.mask = BIT(1),
362 		.hpdev.brdinfo = &mlxplat_mlxcpld_psu[1],
363 		.hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR,
364 	},
365 };
366 
367 static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_pwr_items_data[] = {
368 	{
369 		.label = "pwr1",
370 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
371 		.mask = BIT(0),
372 		.hpdev.brdinfo = &mlxplat_mlxcpld_pwr[0],
373 		.hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR,
374 	},
375 	{
376 		.label = "pwr2",
377 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
378 		.mask = BIT(1),
379 		.hpdev.brdinfo = &mlxplat_mlxcpld_pwr[1],
380 		.hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR,
381 	},
382 };
383 
384 static struct mlxreg_core_data mlxplat_mlxcpld_msn274x_fan_items_data[] = {
385 	{
386 		.label = "fan1",
387 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
388 		.mask = BIT(0),
389 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
390 	},
391 	{
392 		.label = "fan2",
393 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
394 		.mask = BIT(1),
395 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
396 	},
397 	{
398 		.label = "fan3",
399 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
400 		.mask = BIT(2),
401 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
402 	},
403 	{
404 		.label = "fan4",
405 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
406 		.mask = BIT(3),
407 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
408 	},
409 };
410 
411 static struct mlxreg_core_item mlxplat_mlxcpld_msn274x_items[] = {
412 	{
413 		.data = mlxplat_mlxcpld_msn274x_psu_items_data,
414 		.aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
415 		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
416 		.mask = MLXPLAT_CPLD_PSU_MASK,
417 		.count = ARRAY_SIZE(mlxplat_mlxcpld_msn274x_psu_items_data),
418 		.inversed = 1,
419 		.health = false,
420 	},
421 	{
422 		.data = mlxplat_mlxcpld_default_ng_pwr_items_data,
423 		.aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
424 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
425 		.mask = MLXPLAT_CPLD_PWR_MASK,
426 		.count = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_pwr_items_data),
427 		.inversed = 0,
428 		.health = false,
429 	},
430 	{
431 		.data = mlxplat_mlxcpld_msn274x_fan_items_data,
432 		.aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
433 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
434 		.mask = MLXPLAT_CPLD_FAN_MASK,
435 		.count = ARRAY_SIZE(mlxplat_mlxcpld_msn274x_fan_items_data),
436 		.inversed = 1,
437 		.health = false,
438 	},
439 };
440 
441 static
442 struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_msn274x_data = {
443 	.items = mlxplat_mlxcpld_msn274x_items,
444 	.counter = ARRAY_SIZE(mlxplat_mlxcpld_msn274x_items),
445 	.cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
446 	.mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
447 	.cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET,
448 	.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW,
449 };
450 
451 /* Platform hotplug MSN201x system family data */
452 static struct mlxreg_core_data mlxplat_mlxcpld_msn201x_pwr_items_data[] = {
453 	{
454 		.label = "pwr1",
455 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
456 		.mask = BIT(0),
457 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
458 	},
459 	{
460 		.label = "pwr2",
461 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
462 		.mask = BIT(1),
463 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
464 	},
465 };
466 
467 static struct mlxreg_core_item mlxplat_mlxcpld_msn201x_items[] = {
468 	{
469 		.data = mlxplat_mlxcpld_msn201x_pwr_items_data,
470 		.aggr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF,
471 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
472 		.mask = MLXPLAT_CPLD_PWR_MASK,
473 		.count = ARRAY_SIZE(mlxplat_mlxcpld_msn201x_pwr_items_data),
474 		.inversed = 0,
475 		.health = false,
476 	},
477 };
478 
479 static
480 struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_msn201x_data = {
481 	.items = mlxplat_mlxcpld_msn21xx_items,
482 	.counter = ARRAY_SIZE(mlxplat_mlxcpld_msn201x_items),
483 	.cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
484 	.mask = MLXPLAT_CPLD_AGGR_MASK_DEF,
485 	.cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET,
486 	.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW,
487 };
488 
489 /* Platform hotplug next generation system family data */
490 static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_psu_items_data[] = {
491 	{
492 		.label = "psu1",
493 		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
494 		.mask = BIT(0),
495 		.hpdev.brdinfo = &mlxplat_mlxcpld_ng_psu[0],
496 		.hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR,
497 	},
498 	{
499 		.label = "psu2",
500 		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
501 		.mask = BIT(1),
502 		.hpdev.brdinfo = &mlxplat_mlxcpld_ng_psu[1],
503 		.hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR,
504 	},
505 };
506 
507 static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_fan_items_data[] = {
508 	{
509 		.label = "fan1",
510 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
511 		.mask = BIT(0),
512 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
513 	},
514 	{
515 		.label = "fan2",
516 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
517 		.mask = BIT(1),
518 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
519 	},
520 	{
521 		.label = "fan3",
522 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
523 		.mask = BIT(2),
524 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
525 	},
526 	{
527 		.label = "fan4",
528 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
529 		.mask = BIT(3),
530 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
531 	},
532 	{
533 		.label = "fan5",
534 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
535 		.mask = BIT(4),
536 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
537 	},
538 	{
539 		.label = "fan6",
540 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
541 		.mask = BIT(5),
542 		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
543 	},
544 };
545 
546 static struct mlxreg_core_item mlxplat_mlxcpld_default_ng_items[] = {
547 	{
548 		.data = mlxplat_mlxcpld_default_ng_psu_items_data,
549 		.aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
550 		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
551 		.mask = MLXPLAT_CPLD_PSU_MASK,
552 		.count = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_psu_items_data),
553 		.inversed = 1,
554 		.health = false,
555 	},
556 	{
557 		.data = mlxplat_mlxcpld_default_ng_pwr_items_data,
558 		.aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
559 		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
560 		.mask = MLXPLAT_CPLD_PWR_MASK,
561 		.count = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_pwr_items_data),
562 		.inversed = 0,
563 		.health = false,
564 	},
565 	{
566 		.data = mlxplat_mlxcpld_default_ng_fan_items_data,
567 		.aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
568 		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
569 		.mask = MLXPLAT_CPLD_FAN_NG_MASK,
570 		.count = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_fan_items_data),
571 		.inversed = 1,
572 		.health = false,
573 	},
574 };
575 
576 static
577 struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_default_ng_data = {
578 	.items = mlxplat_mlxcpld_default_ng_items,
579 	.counter = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_items),
580 	.cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
581 	.mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
582 	.cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET,
583 	.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW,
584 };
585 
586 static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg)
587 {
588 	switch (reg) {
589 	case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
590 	case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
591 	case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
592 	case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET:
593 	case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFFSET:
594 	case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFFSET:
595 	case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET:
596 	case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET:
597 		return true;
598 	}
599 	return false;
600 }
601 
602 static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg)
603 {
604 	switch (reg) {
605 	case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET:
606 	case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
607 	case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET:
608 	case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
609 	case MLXPLAT_CPLD_LPC_REG_PSU_OFFSET:
610 	case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
611 	case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET:
612 	case MLXPLAT_CPLD_LPC_REG_PWR_OFFSET:
613 	case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFFSET:
614 	case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFFSET:
615 	case MLXPLAT_CPLD_LPC_REG_FAN_OFFSET:
616 	case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET:
617 	case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET:
618 		return true;
619 	}
620 	return false;
621 }
622 
623 static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg)
624 {
625 	switch (reg) {
626 	case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET:
627 	case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
628 	case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET:
629 	case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
630 	case MLXPLAT_CPLD_LPC_REG_PSU_OFFSET:
631 	case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
632 	case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET:
633 	case MLXPLAT_CPLD_LPC_REG_PWR_OFFSET:
634 	case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFFSET:
635 	case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFFSET:
636 	case MLXPLAT_CPLD_LPC_REG_FAN_OFFSET:
637 	case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET:
638 	case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET:
639 		return true;
640 	}
641 	return false;
642 }
643 
644 struct mlxplat_mlxcpld_regmap_context {
645 	void __iomem *base;
646 };
647 
648 static struct mlxplat_mlxcpld_regmap_context mlxplat_mlxcpld_regmap_ctx;
649 
650 static int
651 mlxplat_mlxcpld_reg_read(void *context, unsigned int reg, unsigned int *val)
652 {
653 	struct mlxplat_mlxcpld_regmap_context *ctx = context;
654 
655 	*val = ioread8(ctx->base + reg);
656 	return 0;
657 }
658 
659 static int
660 mlxplat_mlxcpld_reg_write(void *context, unsigned int reg, unsigned int val)
661 {
662 	struct mlxplat_mlxcpld_regmap_context *ctx = context;
663 
664 	iowrite8(val, ctx->base + reg);
665 	return 0;
666 }
667 
668 static const struct regmap_config mlxplat_mlxcpld_regmap_config = {
669 	.reg_bits = 8,
670 	.val_bits = 8,
671 	.max_register = 255,
672 	.cache_type = REGCACHE_FLAT,
673 	.writeable_reg = mlxplat_mlxcpld_writeable_reg,
674 	.readable_reg = mlxplat_mlxcpld_readable_reg,
675 	.volatile_reg = mlxplat_mlxcpld_volatile_reg,
676 	.reg_read = mlxplat_mlxcpld_reg_read,
677 	.reg_write = mlxplat_mlxcpld_reg_write,
678 };
679 
680 static struct resource mlxplat_mlxcpld_resources[] = {
681 	[0] = DEFINE_RES_IRQ_NAMED(17, "mlxreg-hotplug"),
682 };
683 
684 static struct platform_device *mlxplat_dev;
685 static struct mlxreg_core_hotplug_platform_data *mlxplat_hotplug;
686 
687 static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
688 {
689 	int i;
690 
691 	for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
692 		mlxplat_mux_data[i].values = mlxplat_default_channels[i];
693 		mlxplat_mux_data[i].n_values =
694 				ARRAY_SIZE(mlxplat_default_channels[i]);
695 	}
696 	mlxplat_hotplug = &mlxplat_mlxcpld_default_data;
697 
698 	return 1;
699 };
700 
701 static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi)
702 {
703 	int i;
704 
705 	for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
706 		mlxplat_mux_data[i].values = mlxplat_msn21xx_channels;
707 		mlxplat_mux_data[i].n_values =
708 				ARRAY_SIZE(mlxplat_msn21xx_channels);
709 	}
710 	mlxplat_hotplug = &mlxplat_mlxcpld_msn21xx_data;
711 
712 	return 1;
713 };
714 
715 static int __init mlxplat_dmi_msn274x_matched(const struct dmi_system_id *dmi)
716 {
717 	int i;
718 
719 	for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
720 		mlxplat_mux_data[i].values = mlxplat_msn21xx_channels;
721 		mlxplat_mux_data[i].n_values =
722 				ARRAY_SIZE(mlxplat_msn21xx_channels);
723 	}
724 	mlxplat_hotplug = &mlxplat_mlxcpld_msn274x_data;
725 
726 	return 1;
727 };
728 
729 static int __init mlxplat_dmi_msn201x_matched(const struct dmi_system_id *dmi)
730 {
731 	int i;
732 
733 	for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
734 		mlxplat_mux_data[i].values = mlxplat_msn21xx_channels;
735 		mlxplat_mux_data[i].n_values =
736 				ARRAY_SIZE(mlxplat_msn21xx_channels);
737 	}
738 	mlxplat_hotplug = &mlxplat_mlxcpld_msn201x_data;
739 
740 	return 1;
741 };
742 
743 static int __init mlxplat_dmi_qmb7xx_matched(const struct dmi_system_id *dmi)
744 {
745 	int i;
746 
747 	for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
748 		mlxplat_mux_data[i].values = mlxplat_msn21xx_channels;
749 		mlxplat_mux_data[i].n_values =
750 				ARRAY_SIZE(mlxplat_msn21xx_channels);
751 	}
752 	mlxplat_hotplug = &mlxplat_mlxcpld_default_ng_data;
753 
754 	return 1;
755 };
756 
757 static const struct dmi_system_id mlxplat_dmi_table[] __initconst = {
758 	{
759 		.callback = mlxplat_dmi_msn274x_matched,
760 		.matches = {
761 			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
762 			DMI_MATCH(DMI_PRODUCT_NAME, "MSN274"),
763 		},
764 	},
765 	{
766 		.callback = mlxplat_dmi_default_matched,
767 		.matches = {
768 			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
769 			DMI_MATCH(DMI_PRODUCT_NAME, "MSN24"),
770 		},
771 	},
772 	{
773 		.callback = mlxplat_dmi_default_matched,
774 		.matches = {
775 			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
776 			DMI_MATCH(DMI_PRODUCT_NAME, "MSN27"),
777 		},
778 	},
779 	{
780 		.callback = mlxplat_dmi_default_matched,
781 		.matches = {
782 			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
783 			DMI_MATCH(DMI_PRODUCT_NAME, "MSB"),
784 		},
785 	},
786 	{
787 		.callback = mlxplat_dmi_default_matched,
788 		.matches = {
789 			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
790 			DMI_MATCH(DMI_PRODUCT_NAME, "MSX"),
791 		},
792 	},
793 	{
794 		.callback = mlxplat_dmi_msn21xx_matched,
795 		.matches = {
796 			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
797 			DMI_MATCH(DMI_PRODUCT_NAME, "MSN21"),
798 		},
799 	},
800 	{
801 		.callback = mlxplat_dmi_msn201x_matched,
802 		.matches = {
803 			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
804 			DMI_MATCH(DMI_PRODUCT_NAME, "MSN201"),
805 		},
806 	},
807 	{
808 		.callback = mlxplat_dmi_qmb7xx_matched,
809 		.matches = {
810 			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
811 			DMI_MATCH(DMI_PRODUCT_NAME, "QMB7"),
812 		},
813 	},
814 	{
815 		.callback = mlxplat_dmi_qmb7xx_matched,
816 		.matches = {
817 			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
818 			DMI_MATCH(DMI_PRODUCT_NAME, "SN37"),
819 		},
820 	},
821 	{
822 		.callback = mlxplat_dmi_qmb7xx_matched,
823 		.matches = {
824 			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
825 			DMI_MATCH(DMI_PRODUCT_NAME, "SN34"),
826 		},
827 	},
828 	{ }
829 };
830 
831 MODULE_DEVICE_TABLE(dmi, mlxplat_dmi_table);
832 
833 static int __init mlxplat_init(void)
834 {
835 	struct mlxplat_priv *priv;
836 	int i, err;
837 
838 	if (!dmi_check_system(mlxplat_dmi_table))
839 		return -ENODEV;
840 
841 	mlxplat_dev = platform_device_register_simple(MLX_PLAT_DEVICE_NAME, -1,
842 					mlxplat_lpc_resources,
843 					ARRAY_SIZE(mlxplat_lpc_resources));
844 
845 	if (IS_ERR(mlxplat_dev))
846 		return PTR_ERR(mlxplat_dev);
847 
848 	priv = devm_kzalloc(&mlxplat_dev->dev, sizeof(struct mlxplat_priv),
849 			    GFP_KERNEL);
850 	if (!priv) {
851 		err = -ENOMEM;
852 		goto fail_alloc;
853 	}
854 	platform_set_drvdata(mlxplat_dev, priv);
855 
856 	priv->pdev_i2c = platform_device_register_simple("i2c_mlxcpld", -1,
857 							 NULL, 0);
858 	if (IS_ERR(priv->pdev_i2c)) {
859 		err = PTR_ERR(priv->pdev_i2c);
860 		goto fail_alloc;
861 	}
862 
863 	for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
864 		priv->pdev_mux[i] = platform_device_register_resndata(
865 						&mlxplat_dev->dev,
866 						"i2c-mux-reg", i, NULL,
867 						0, &mlxplat_mux_data[i],
868 						sizeof(mlxplat_mux_data[i]));
869 		if (IS_ERR(priv->pdev_mux[i])) {
870 			err = PTR_ERR(priv->pdev_mux[i]);
871 			goto fail_platform_mux_register;
872 		}
873 	}
874 
875 	mlxplat_mlxcpld_regmap_ctx.base = devm_ioport_map(&mlxplat_dev->dev,
876 			       mlxplat_lpc_resources[1].start, 1);
877 	if (!mlxplat_mlxcpld_regmap_ctx.base) {
878 		err = -ENOMEM;
879 		goto fail_platform_mux_register;
880 	}
881 
882 	mlxplat_hotplug->regmap = devm_regmap_init(&mlxplat_dev->dev, NULL,
883 					&mlxplat_mlxcpld_regmap_ctx,
884 					&mlxplat_mlxcpld_regmap_config);
885 	if (IS_ERR(mlxplat_hotplug->regmap)) {
886 		err = PTR_ERR(mlxplat_hotplug->regmap);
887 		goto fail_platform_mux_register;
888 	}
889 
890 	priv->pdev_hotplug = platform_device_register_resndata(
891 				&mlxplat_dev->dev, "mlxreg-hotplug",
892 				PLATFORM_DEVID_NONE,
893 				mlxplat_mlxcpld_resources,
894 				ARRAY_SIZE(mlxplat_mlxcpld_resources),
895 				mlxplat_hotplug, sizeof(*mlxplat_hotplug));
896 	if (IS_ERR(priv->pdev_hotplug)) {
897 		err = PTR_ERR(priv->pdev_hotplug);
898 		goto fail_platform_mux_register;
899 	}
900 
901 	/* Sync registers with hardware. */
902 	regcache_mark_dirty(mlxplat_hotplug->regmap);
903 	err = regcache_sync(mlxplat_hotplug->regmap);
904 	if (err)
905 		goto fail_platform_hotplug_register;
906 
907 	return 0;
908 
909 fail_platform_hotplug_register:
910 	platform_device_unregister(priv->pdev_hotplug);
911 fail_platform_mux_register:
912 	while (--i >= 0)
913 		platform_device_unregister(priv->pdev_mux[i]);
914 	platform_device_unregister(priv->pdev_i2c);
915 fail_alloc:
916 	platform_device_unregister(mlxplat_dev);
917 
918 	return err;
919 }
920 module_init(mlxplat_init);
921 
922 static void __exit mlxplat_exit(void)
923 {
924 	struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev);
925 	int i;
926 
927 	platform_device_unregister(priv->pdev_hotplug);
928 
929 	for (i = ARRAY_SIZE(mlxplat_mux_data) - 1; i >= 0 ; i--)
930 		platform_device_unregister(priv->pdev_mux[i]);
931 
932 	platform_device_unregister(priv->pdev_i2c);
933 	platform_device_unregister(mlxplat_dev);
934 }
935 module_exit(mlxplat_exit);
936 
937 MODULE_AUTHOR("Vadim Pasternak (vadimp@mellanox.com)");
938 MODULE_DESCRIPTION("Mellanox platform driver");
939 MODULE_LICENSE("Dual BSD/GPL");
940