platform/x86: mlx-platform: Fix an ERR_PTR vs NULL issue
[muen/linux.git] / drivers / platform / x86 / mlx-platform.c
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_MSN21XX  0x04
81 #define MLXPLAT_CPLD_PSU_MASK           GENMASK(1, 0)
82 #define MLXPLAT_CPLD_PWR_MASK           GENMASK(1, 0)
83 #define MLXPLAT_CPLD_FAN_MASK           GENMASK(3, 0)
84
85 /* Start channel numbers */
86 #define MLXPLAT_CPLD_CH1                        2
87 #define MLXPLAT_CPLD_CH2                        10
88
89 /* Number of LPC attached MUX platform devices */
90 #define MLXPLAT_CPLD_LPC_MUX_DEVS               2
91
92 /* mlxplat_priv - platform private data
93  * @pdev_i2c - i2c controller platform device
94  * @pdev_mux - array of mux platform devices
95  * @pdev_hotplug - hotplug platform devices
96  */
97 struct mlxplat_priv {
98         struct platform_device *pdev_i2c;
99         struct platform_device *pdev_mux[MLXPLAT_CPLD_LPC_MUX_DEVS];
100         struct platform_device *pdev_hotplug;
101 };
102
103 /* Regions for LPC I2C controller and LPC base register space */
104 static const struct resource mlxplat_lpc_resources[] = {
105         [0] = DEFINE_RES_NAMED(MLXPLAT_CPLD_LPC_I2C_BASE_ADRR,
106                                MLXPLAT_CPLD_LPC_IO_RANGE,
107                                "mlxplat_cpld_lpc_i2c_ctrl", IORESOURCE_IO),
108         [1] = DEFINE_RES_NAMED(MLXPLAT_CPLD_LPC_REG_BASE_ADRR,
109                                MLXPLAT_CPLD_LPC_IO_RANGE,
110                                "mlxplat_cpld_lpc_regs",
111                                IORESOURCE_IO),
112 };
113
114 /* Platform default channels */
115 static const int mlxplat_default_channels[][8] = {
116         {
117                 MLXPLAT_CPLD_CH1, MLXPLAT_CPLD_CH1 + 1, MLXPLAT_CPLD_CH1 + 2,
118                 MLXPLAT_CPLD_CH1 + 3, MLXPLAT_CPLD_CH1 + 4, MLXPLAT_CPLD_CH1 +
119                 5, MLXPLAT_CPLD_CH1 + 6, MLXPLAT_CPLD_CH1 + 7
120         },
121         {
122                 MLXPLAT_CPLD_CH2, MLXPLAT_CPLD_CH2 + 1, MLXPLAT_CPLD_CH2 + 2,
123                 MLXPLAT_CPLD_CH2 + 3, MLXPLAT_CPLD_CH2 + 4, MLXPLAT_CPLD_CH2 +
124                 5, MLXPLAT_CPLD_CH2 + 6, MLXPLAT_CPLD_CH2 + 7
125         },
126 };
127
128 /* Platform channels for MSN21xx system family */
129 static const int mlxplat_msn21xx_channels[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
130
131 /* Platform mux data */
132 static struct i2c_mux_reg_platform_data mlxplat_mux_data[] = {
133         {
134                 .parent = 1,
135                 .base_nr = MLXPLAT_CPLD_CH1,
136                 .write_only = 1,
137                 .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG1,
138                 .reg_size = 1,
139                 .idle_in_use = 1,
140         },
141         {
142                 .parent = 1,
143                 .base_nr = MLXPLAT_CPLD_CH2,
144                 .write_only = 1,
145                 .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG2,
146                 .reg_size = 1,
147                 .idle_in_use = 1,
148         },
149
150 };
151
152 /* Platform hotplug devices */
153 static struct i2c_board_info mlxplat_mlxcpld_psu[] = {
154         {
155                 I2C_BOARD_INFO("24c02", 0x51),
156         },
157         {
158                 I2C_BOARD_INFO("24c02", 0x50),
159         },
160 };
161
162 static struct i2c_board_info mlxplat_mlxcpld_pwr[] = {
163         {
164                 I2C_BOARD_INFO("dps460", 0x59),
165         },
166         {
167                 I2C_BOARD_INFO("dps460", 0x58),
168         },
169 };
170
171 static struct i2c_board_info mlxplat_mlxcpld_fan[] = {
172         {
173                 I2C_BOARD_INFO("24c32", 0x50),
174         },
175         {
176                 I2C_BOARD_INFO("24c32", 0x50),
177         },
178         {
179                 I2C_BOARD_INFO("24c32", 0x50),
180         },
181         {
182                 I2C_BOARD_INFO("24c32", 0x50),
183         },
184 };
185
186 /* Platform hotplug default data */
187 static struct mlxreg_core_data mlxplat_mlxcpld_default_psu_items_data[] = {
188         {
189                 .label = "psu1",
190                 .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
191                 .mask = BIT(0),
192                 .hpdev.brdinfo = &mlxplat_mlxcpld_psu[0],
193                 .hpdev.nr = 10,
194         },
195         {
196                 .label = "psu2",
197                 .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
198                 .mask = BIT(1),
199                 .hpdev.brdinfo = &mlxplat_mlxcpld_psu[1],
200                 .hpdev.nr = 10,
201         },
202 };
203
204 static struct mlxreg_core_data mlxplat_mlxcpld_default_pwr_items_data[] = {
205         {
206                 .label = "pwr1",
207                 .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
208                 .mask = BIT(0),
209                 .hpdev.brdinfo = &mlxplat_mlxcpld_pwr[0],
210                 .hpdev.nr = 10,
211         },
212         {
213                 .label = "pwr2",
214                 .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
215                 .mask = BIT(1),
216                 .hpdev.brdinfo = &mlxplat_mlxcpld_pwr[1],
217                 .hpdev.nr = 10,
218         },
219 };
220
221 static struct mlxreg_core_data mlxplat_mlxcpld_default_fan_items_data[] = {
222         {
223                 .label = "fan1",
224                 .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
225                 .mask = BIT(0),
226                 .hpdev.brdinfo = &mlxplat_mlxcpld_fan[0],
227                 .hpdev.nr = 11,
228         },
229         {
230                 .label = "fan2",
231                 .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
232                 .mask = BIT(1),
233                 .hpdev.brdinfo = &mlxplat_mlxcpld_fan[1],
234                 .hpdev.nr = 12,
235         },
236         {
237                 .label = "fan3",
238                 .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
239                 .mask = BIT(2),
240                 .hpdev.brdinfo = &mlxplat_mlxcpld_fan[2],
241                 .hpdev.nr = 13,
242         },
243         {
244                 .label = "fan4",
245                 .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
246                 .mask = BIT(3),
247                 .hpdev.brdinfo = &mlxplat_mlxcpld_fan[3],
248                 .hpdev.nr = 14,
249         },
250 };
251
252 static struct mlxreg_core_item mlxplat_mlxcpld_default_items[] = {
253         {
254                 .data = mlxplat_mlxcpld_default_psu_items_data,
255                 .aggr_mask = MLXPLAT_CPLD_AGGR_PSU_MASK_DEF,
256                 .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
257                 .mask = MLXPLAT_CPLD_PSU_MASK,
258                 .count = ARRAY_SIZE(mlxplat_mlxcpld_psu),
259                 .inversed = 1,
260                 .health = false,
261         },
262         {
263                 .data = mlxplat_mlxcpld_default_pwr_items_data,
264                 .aggr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF,
265                 .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
266                 .mask = MLXPLAT_CPLD_PWR_MASK,
267                 .count = ARRAY_SIZE(mlxplat_mlxcpld_pwr),
268                 .inversed = 0,
269                 .health = false,
270         },
271         {
272                 .data = mlxplat_mlxcpld_default_fan_items_data,
273                 .aggr_mask = MLXPLAT_CPLD_AGGR_FAN_MASK_DEF,
274                 .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
275                 .mask = MLXPLAT_CPLD_FAN_MASK,
276                 .count = ARRAY_SIZE(mlxplat_mlxcpld_fan),
277                 .inversed = 1,
278                 .health = false,
279         },
280 };
281
282 static
283 struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_default_data = {
284         .items = mlxplat_mlxcpld_default_items,
285         .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_items),
286         .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
287         .mask = MLXPLAT_CPLD_AGGR_MASK_DEF,
288 };
289
290 /* Platform hotplug MSN21xx system family data */
291 static struct mlxreg_core_item mlxplat_mlxcpld_msn21xx_items[] = {
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
303 static
304 struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_msn21xx_data = {
305         .items = mlxplat_mlxcpld_msn21xx_items,
306         .counter = ARRAY_SIZE(mlxplat_mlxcpld_msn21xx_items),
307         .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
308         .mask = MLXPLAT_CPLD_AGGR_MASK_DEF,
309 };
310
311 static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg)
312 {
313         switch (reg) {
314         case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
315         case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
316         case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
317         case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET:
318         case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFFSET:
319         case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFFSET:
320         case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET:
321         case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET:
322                 return true;
323         }
324         return false;
325 }
326
327 static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg)
328 {
329         switch (reg) {
330         case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET:
331         case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
332         case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET:
333         case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
334         case MLXPLAT_CPLD_LPC_REG_PSU_OFFSET:
335         case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
336         case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET:
337         case MLXPLAT_CPLD_LPC_REG_PWR_OFFSET:
338         case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFFSET:
339         case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFFSET:
340         case MLXPLAT_CPLD_LPC_REG_FAN_OFFSET:
341         case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET:
342         case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET:
343                 return true;
344         }
345         return false;
346 }
347
348 static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg)
349 {
350         switch (reg) {
351         case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET:
352         case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
353         case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET:
354         case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
355         case MLXPLAT_CPLD_LPC_REG_PSU_OFFSET:
356         case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
357         case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET:
358         case MLXPLAT_CPLD_LPC_REG_PWR_OFFSET:
359         case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFFSET:
360         case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFFSET:
361         case MLXPLAT_CPLD_LPC_REG_FAN_OFFSET:
362         case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET:
363         case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET:
364                 return true;
365         }
366         return false;
367 }
368
369 struct mlxplat_mlxcpld_regmap_context {
370         void __iomem *base;
371 };
372
373 static struct mlxplat_mlxcpld_regmap_context mlxplat_mlxcpld_regmap_ctx;
374
375 static int
376 mlxplat_mlxcpld_reg_read(void *context, unsigned int reg, unsigned int *val)
377 {
378         struct mlxplat_mlxcpld_regmap_context *ctx = context;
379
380         *val = ioread8(ctx->base + reg);
381         return 0;
382 }
383
384 static int
385 mlxplat_mlxcpld_reg_write(void *context, unsigned int reg, unsigned int val)
386 {
387         struct mlxplat_mlxcpld_regmap_context *ctx = context;
388
389         iowrite8(val, ctx->base + reg);
390         return 0;
391 }
392
393 static const struct regmap_config mlxplat_mlxcpld_regmap_config = {
394         .reg_bits = 8,
395         .val_bits = 8,
396         .max_register = 255,
397         .cache_type = REGCACHE_FLAT,
398         .writeable_reg = mlxplat_mlxcpld_writeable_reg,
399         .readable_reg = mlxplat_mlxcpld_readable_reg,
400         .volatile_reg = mlxplat_mlxcpld_volatile_reg,
401         .reg_read = mlxplat_mlxcpld_reg_read,
402         .reg_write = mlxplat_mlxcpld_reg_write,
403 };
404
405 static struct resource mlxplat_mlxcpld_resources[] = {
406         [0] = DEFINE_RES_IRQ_NAMED(17, "mlxreg-hotplug"),
407 };
408
409 static struct platform_device *mlxplat_dev;
410 static struct mlxreg_core_hotplug_platform_data *mlxplat_hotplug;
411
412 static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
413 {
414         int i;
415
416         for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
417                 mlxplat_mux_data[i].values = mlxplat_default_channels[i];
418                 mlxplat_mux_data[i].n_values =
419                                 ARRAY_SIZE(mlxplat_default_channels[i]);
420         }
421         mlxplat_hotplug = &mlxplat_mlxcpld_default_data;
422
423         return 1;
424 };
425
426 static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi)
427 {
428         int i;
429
430         for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
431                 mlxplat_mux_data[i].values = mlxplat_msn21xx_channels;
432                 mlxplat_mux_data[i].n_values =
433                                 ARRAY_SIZE(mlxplat_msn21xx_channels);
434         }
435         mlxplat_hotplug = &mlxplat_mlxcpld_msn21xx_data;
436
437         return 1;
438 };
439
440 static const struct dmi_system_id mlxplat_dmi_table[] __initconst = {
441         {
442                 .callback = mlxplat_dmi_default_matched,
443                 .matches = {
444                         DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
445                         DMI_MATCH(DMI_PRODUCT_NAME, "MSN24"),
446                 },
447         },
448         {
449                 .callback = mlxplat_dmi_default_matched,
450                 .matches = {
451                         DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
452                         DMI_MATCH(DMI_PRODUCT_NAME, "MSN27"),
453                 },
454         },
455         {
456                 .callback = mlxplat_dmi_default_matched,
457                 .matches = {
458                         DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
459                         DMI_MATCH(DMI_PRODUCT_NAME, "MSB"),
460                 },
461         },
462         {
463                 .callback = mlxplat_dmi_default_matched,
464                 .matches = {
465                         DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
466                         DMI_MATCH(DMI_PRODUCT_NAME, "MSX"),
467                 },
468         },
469         {
470                 .callback = mlxplat_dmi_msn21xx_matched,
471                 .matches = {
472                         DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
473                         DMI_MATCH(DMI_PRODUCT_NAME, "MSN21"),
474                 },
475         },
476         { }
477 };
478
479 MODULE_DEVICE_TABLE(dmi, mlxplat_dmi_table);
480
481 static int __init mlxplat_init(void)
482 {
483         struct mlxplat_priv *priv;
484         int i, err;
485
486         if (!dmi_check_system(mlxplat_dmi_table))
487                 return -ENODEV;
488
489         mlxplat_dev = platform_device_register_simple(MLX_PLAT_DEVICE_NAME, -1,
490                                         mlxplat_lpc_resources,
491                                         ARRAY_SIZE(mlxplat_lpc_resources));
492
493         if (IS_ERR(mlxplat_dev))
494                 return PTR_ERR(mlxplat_dev);
495
496         priv = devm_kzalloc(&mlxplat_dev->dev, sizeof(struct mlxplat_priv),
497                             GFP_KERNEL);
498         if (!priv) {
499                 err = -ENOMEM;
500                 goto fail_alloc;
501         }
502         platform_set_drvdata(mlxplat_dev, priv);
503
504         priv->pdev_i2c = platform_device_register_simple("i2c_mlxcpld", -1,
505                                                          NULL, 0);
506         if (IS_ERR(priv->pdev_i2c)) {
507                 err = PTR_ERR(priv->pdev_i2c);
508                 goto fail_alloc;
509         }
510
511         for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
512                 priv->pdev_mux[i] = platform_device_register_resndata(
513                                                 &mlxplat_dev->dev,
514                                                 "i2c-mux-reg", i, NULL,
515                                                 0, &mlxplat_mux_data[i],
516                                                 sizeof(mlxplat_mux_data[i]));
517                 if (IS_ERR(priv->pdev_mux[i])) {
518                         err = PTR_ERR(priv->pdev_mux[i]);
519                         goto fail_platform_mux_register;
520                 }
521         }
522
523         mlxplat_mlxcpld_regmap_ctx.base = devm_ioport_map(&mlxplat_dev->dev,
524                                mlxplat_lpc_resources[1].start, 1);
525         if (!mlxplat_mlxcpld_regmap_ctx.base) {
526                 err = -ENOMEM;
527                 goto fail_platform_mux_register;
528         }
529
530         mlxplat_hotplug->regmap = devm_regmap_init(&mlxplat_dev->dev, NULL,
531                                         &mlxplat_mlxcpld_regmap_ctx,
532                                         &mlxplat_mlxcpld_regmap_config);
533         if (IS_ERR(mlxplat_hotplug->regmap)) {
534                 err = PTR_ERR(mlxplat_hotplug->regmap);
535                 goto fail_platform_mux_register;
536         }
537
538         priv->pdev_hotplug = platform_device_register_resndata(
539                                 &mlxplat_dev->dev, "mlxreg-hotplug",
540                                 PLATFORM_DEVID_NONE,
541                                 mlxplat_mlxcpld_resources,
542                                 ARRAY_SIZE(mlxplat_mlxcpld_resources),
543                                 mlxplat_hotplug, sizeof(*mlxplat_hotplug));
544         if (IS_ERR(priv->pdev_hotplug)) {
545                 err = PTR_ERR(priv->pdev_hotplug);
546                 goto fail_platform_mux_register;
547         }
548
549         /* Sync registers with hardware. */
550         regcache_mark_dirty(mlxplat_hotplug->regmap);
551         err = regcache_sync(mlxplat_hotplug->regmap);
552         if (err)
553                 goto fail_platform_hotplug_register;
554
555         return 0;
556
557 fail_platform_hotplug_register:
558         platform_device_unregister(priv->pdev_hotplug);
559 fail_platform_mux_register:
560         while (--i >= 0)
561                 platform_device_unregister(priv->pdev_mux[i]);
562         platform_device_unregister(priv->pdev_i2c);
563 fail_alloc:
564         platform_device_unregister(mlxplat_dev);
565
566         return err;
567 }
568 module_init(mlxplat_init);
569
570 static void __exit mlxplat_exit(void)
571 {
572         struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev);
573         int i;
574
575         platform_device_unregister(priv->pdev_hotplug);
576
577         for (i = ARRAY_SIZE(mlxplat_mux_data) - 1; i >= 0 ; i--)
578                 platform_device_unregister(priv->pdev_mux[i]);
579
580         platform_device_unregister(priv->pdev_i2c);
581         platform_device_unregister(mlxplat_dev);
582 }
583 module_exit(mlxplat_exit);
584
585 MODULE_AUTHOR("Vadim Pasternak (vadimp@mellanox.com)");
586 MODULE_DESCRIPTION("Mellanox platform driver");
587 MODULE_LICENSE("Dual BSD/GPL");