2 * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
3 * Copyright (c) 2016 Vadim Pasternak <vadimp@mellanox.com>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
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.
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.
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.
34 #include <linux/device.h>
35 #include <linux/dmi.h>
36 #include <linux/i2c.h>
37 #include <linux/i2c-mux.h>
38 #include <linux/module.h>
39 #include <linux/platform_device.h>
40 #include <linux/platform_data/i2c-mux-reg.h>
41 #include <linux/platform_data/mlxcpld-hotplug.h>
43 #define MLX_PLAT_DEVICE_NAME "mlxplat"
45 /* LPC bus IO offsets */
46 #define MLXPLAT_CPLD_LPC_I2C_BASE_ADRR 0x2000
47 #define MLXPLAT_CPLD_LPC_REG_BASE_ADRR 0x2500
48 #define MLXPLAT_CPLD_LPC_REG_AGGR_ADRR 0x253a
49 #define MLXPLAT_CPLD_LPC_REG_PSU_ADRR 0x2558
50 #define MLXPLAT_CPLD_LPC_REG_PWR_ADRR 0x2564
51 #define MLXPLAT_CPLD_LPC_REG_FAN_ADRR 0x2588
52 #define MLXPLAT_CPLD_LPC_IO_RANGE 0x100
53 #define MLXPLAT_CPLD_LPC_I2C_CH1_OFF 0xdb
54 #define MLXPLAT_CPLD_LPC_I2C_CH2_OFF 0xda
55 #define MLXPLAT_CPLD_LPC_PIO_OFFSET 0x10000UL
56 #define MLXPLAT_CPLD_LPC_REG1 ((MLXPLAT_CPLD_LPC_REG_BASE_ADRR + \
57 MLXPLAT_CPLD_LPC_I2C_CH1_OFF) | \
58 MLXPLAT_CPLD_LPC_PIO_OFFSET)
59 #define MLXPLAT_CPLD_LPC_REG2 ((MLXPLAT_CPLD_LPC_REG_BASE_ADRR + \
60 MLXPLAT_CPLD_LPC_I2C_CH2_OFF) | \
61 MLXPLAT_CPLD_LPC_PIO_OFFSET)
63 /* Masks for aggregation, psu, pwr and fan event in CPLD related registers. */
64 #define MLXPLAT_CPLD_AGGR_PSU_MASK_DEF 0x08
65 #define MLXPLAT_CPLD_AGGR_PWR_MASK_DEF 0x08
66 #define MLXPLAT_CPLD_AGGR_FAN_MASK_DEF 0x40
67 #define MLXPLAT_CPLD_AGGR_MASK_DEF (MLXPLAT_CPLD_AGGR_PSU_MASK_DEF | \
68 MLXPLAT_CPLD_AGGR_FAN_MASK_DEF)
69 #define MLXPLAT_CPLD_AGGR_MASK_MSN21XX 0x04
70 #define MLXPLAT_CPLD_PSU_MASK GENMASK(1, 0)
71 #define MLXPLAT_CPLD_PWR_MASK GENMASK(1, 0)
72 #define MLXPLAT_CPLD_FAN_MASK GENMASK(3, 0)
74 /* Start channel numbers */
75 #define MLXPLAT_CPLD_CH1 2
76 #define MLXPLAT_CPLD_CH2 10
78 /* Number of LPC attached MUX platform devices */
79 #define MLXPLAT_CPLD_LPC_MUX_DEVS 2
81 /* mlxplat_priv - platform private data
82 * @pdev_i2c - i2c controller platform device
83 * @pdev_mux - array of mux platform devices
86 struct platform_device
*pdev_i2c
;
87 struct platform_device
*pdev_mux
[MLXPLAT_CPLD_LPC_MUX_DEVS
];
88 struct platform_device
*pdev_hotplug
;
91 /* Regions for LPC I2C controller and LPC base register space */
92 static const struct resource mlxplat_lpc_resources
[] = {
93 [0] = DEFINE_RES_NAMED(MLXPLAT_CPLD_LPC_I2C_BASE_ADRR
,
94 MLXPLAT_CPLD_LPC_IO_RANGE
,
95 "mlxplat_cpld_lpc_i2c_ctrl", IORESOURCE_IO
),
96 [1] = DEFINE_RES_NAMED(MLXPLAT_CPLD_LPC_REG_BASE_ADRR
,
97 MLXPLAT_CPLD_LPC_IO_RANGE
,
98 "mlxplat_cpld_lpc_regs",
102 /* Platform default channels */
103 static const int mlxplat_default_channels
[][8] = {
105 MLXPLAT_CPLD_CH1
, MLXPLAT_CPLD_CH1
+ 1, MLXPLAT_CPLD_CH1
+ 2,
106 MLXPLAT_CPLD_CH1
+ 3, MLXPLAT_CPLD_CH1
+ 4, MLXPLAT_CPLD_CH1
+
107 5, MLXPLAT_CPLD_CH1
+ 6, MLXPLAT_CPLD_CH1
+ 7
110 MLXPLAT_CPLD_CH2
, MLXPLAT_CPLD_CH2
+ 1, MLXPLAT_CPLD_CH2
+ 2,
111 MLXPLAT_CPLD_CH2
+ 3, MLXPLAT_CPLD_CH2
+ 4, MLXPLAT_CPLD_CH2
+
112 5, MLXPLAT_CPLD_CH2
+ 6, MLXPLAT_CPLD_CH2
+ 7
116 /* Platform channels for MSN21xx system family */
117 static const int mlxplat_msn21xx_channels
[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
119 /* Platform mux data */
120 static struct i2c_mux_reg_platform_data mlxplat_mux_data
[] = {
123 .base_nr
= MLXPLAT_CPLD_CH1
,
125 .reg
= (void __iomem
*)MLXPLAT_CPLD_LPC_REG1
,
131 .base_nr
= MLXPLAT_CPLD_CH2
,
133 .reg
= (void __iomem
*)MLXPLAT_CPLD_LPC_REG2
,
140 /* Platform hotplug devices */
141 static struct mlxcpld_hotplug_device mlxplat_mlxcpld_psu
[] = {
143 .brdinfo
= { I2C_BOARD_INFO("24c02", 0x51) },
147 .brdinfo
= { I2C_BOARD_INFO("24c02", 0x50) },
152 static struct mlxcpld_hotplug_device mlxplat_mlxcpld_pwr
[] = {
154 .brdinfo
= { I2C_BOARD_INFO("dps460", 0x59) },
158 .brdinfo
= { I2C_BOARD_INFO("dps460", 0x58) },
163 static struct mlxcpld_hotplug_device mlxplat_mlxcpld_fan
[] = {
165 .brdinfo
= { I2C_BOARD_INFO("24c32", 0x50) },
169 .brdinfo
= { I2C_BOARD_INFO("24c32", 0x50) },
173 .brdinfo
= { I2C_BOARD_INFO("24c32", 0x50) },
177 .brdinfo
= { I2C_BOARD_INFO("24c32", 0x50) },
182 /* Platform hotplug default data */
184 struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_default_data
= {
185 .top_aggr_offset
= MLXPLAT_CPLD_LPC_REG_AGGR_ADRR
,
186 .top_aggr_mask
= MLXPLAT_CPLD_AGGR_MASK_DEF
,
187 .top_aggr_psu_mask
= MLXPLAT_CPLD_AGGR_PSU_MASK_DEF
,
188 .psu_reg_offset
= MLXPLAT_CPLD_LPC_REG_PSU_ADRR
,
189 .psu_mask
= MLXPLAT_CPLD_PSU_MASK
,
190 .psu_count
= ARRAY_SIZE(mlxplat_mlxcpld_psu
),
191 .psu
= mlxplat_mlxcpld_psu
,
192 .top_aggr_pwr_mask
= MLXPLAT_CPLD_AGGR_PWR_MASK_DEF
,
193 .pwr_reg_offset
= MLXPLAT_CPLD_LPC_REG_PWR_ADRR
,
194 .pwr_mask
= MLXPLAT_CPLD_PWR_MASK
,
195 .pwr_count
= ARRAY_SIZE(mlxplat_mlxcpld_pwr
),
196 .pwr
= mlxplat_mlxcpld_pwr
,
197 .top_aggr_fan_mask
= MLXPLAT_CPLD_AGGR_FAN_MASK_DEF
,
198 .fan_reg_offset
= MLXPLAT_CPLD_LPC_REG_FAN_ADRR
,
199 .fan_mask
= MLXPLAT_CPLD_FAN_MASK
,
200 .fan_count
= ARRAY_SIZE(mlxplat_mlxcpld_fan
),
201 .fan
= mlxplat_mlxcpld_fan
,
204 /* Platform hotplug MSN21xx system family data */
206 struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_msn21xx_data
= {
207 .top_aggr_offset
= MLXPLAT_CPLD_LPC_REG_AGGR_ADRR
,
208 .top_aggr_mask
= MLXPLAT_CPLD_AGGR_MASK_MSN21XX
,
209 .top_aggr_pwr_mask
= MLXPLAT_CPLD_AGGR_MASK_MSN21XX
,
210 .pwr_reg_offset
= MLXPLAT_CPLD_LPC_REG_PWR_ADRR
,
211 .pwr_mask
= MLXPLAT_CPLD_PWR_MASK
,
212 .pwr_count
= ARRAY_SIZE(mlxplat_mlxcpld_pwr
),
215 static struct resource mlxplat_mlxcpld_resources
[] = {
216 [0] = DEFINE_RES_IRQ_NAMED(17, "mlxcpld-hotplug"),
219 struct platform_device
*mlxplat_dev
;
220 struct mlxcpld_hotplug_platform_data
*mlxplat_hotplug
;
222 static int __init
mlxplat_dmi_default_matched(const struct dmi_system_id
*dmi
)
226 for (i
= 0; i
< ARRAY_SIZE(mlxplat_mux_data
); i
++) {
227 mlxplat_mux_data
[i
].values
= mlxplat_default_channels
[i
];
228 mlxplat_mux_data
[i
].n_values
=
229 ARRAY_SIZE(mlxplat_default_channels
[i
]);
231 mlxplat_hotplug
= &mlxplat_mlxcpld_default_data
;
236 static int __init
mlxplat_dmi_msn21xx_matched(const struct dmi_system_id
*dmi
)
240 for (i
= 0; i
< ARRAY_SIZE(mlxplat_mux_data
); i
++) {
241 mlxplat_mux_data
[i
].values
= mlxplat_msn21xx_channels
;
242 mlxplat_mux_data
[i
].n_values
=
243 ARRAY_SIZE(mlxplat_msn21xx_channels
);
245 mlxplat_hotplug
= &mlxplat_mlxcpld_msn21xx_data
;
250 static struct dmi_system_id mlxplat_dmi_table
[] __initdata
= {
252 .callback
= mlxplat_dmi_default_matched
,
254 DMI_MATCH(DMI_BOARD_VENDOR
, "Mellanox Technologies"),
255 DMI_MATCH(DMI_PRODUCT_NAME
, "MSN24"),
259 .callback
= mlxplat_dmi_default_matched
,
261 DMI_MATCH(DMI_BOARD_VENDOR
, "Mellanox Technologies"),
262 DMI_MATCH(DMI_PRODUCT_NAME
, "MSN27"),
266 .callback
= mlxplat_dmi_default_matched
,
268 DMI_MATCH(DMI_BOARD_VENDOR
, "Mellanox Technologies"),
269 DMI_MATCH(DMI_PRODUCT_NAME
, "MSB"),
273 .callback
= mlxplat_dmi_default_matched
,
275 DMI_MATCH(DMI_BOARD_VENDOR
, "Mellanox Technologies"),
276 DMI_MATCH(DMI_PRODUCT_NAME
, "MSX"),
280 .callback
= mlxplat_dmi_msn21xx_matched
,
282 DMI_MATCH(DMI_BOARD_VENDOR
, "Mellanox Technologies"),
283 DMI_MATCH(DMI_PRODUCT_NAME
, "MSN21"),
289 static int __init
mlxplat_init(void)
291 struct mlxplat_priv
*priv
;
294 if (!dmi_check_system(mlxplat_dmi_table
))
297 mlxplat_dev
= platform_device_register_simple(MLX_PLAT_DEVICE_NAME
, -1,
298 mlxplat_lpc_resources
,
299 ARRAY_SIZE(mlxplat_lpc_resources
));
301 if (IS_ERR(mlxplat_dev
))
302 return PTR_ERR(mlxplat_dev
);
304 priv
= devm_kzalloc(&mlxplat_dev
->dev
, sizeof(struct mlxplat_priv
),
310 platform_set_drvdata(mlxplat_dev
, priv
);
312 priv
->pdev_i2c
= platform_device_register_simple("i2c_mlxcpld", -1,
314 if (IS_ERR(priv
->pdev_i2c
)) {
315 err
= PTR_ERR(priv
->pdev_i2c
);
319 for (i
= 0; i
< ARRAY_SIZE(mlxplat_mux_data
); i
++) {
320 priv
->pdev_mux
[i
] = platform_device_register_resndata(
322 "i2c-mux-reg", i
, NULL
,
323 0, &mlxplat_mux_data
[i
],
324 sizeof(mlxplat_mux_data
[i
]));
325 if (IS_ERR(priv
->pdev_mux
[i
])) {
326 err
= PTR_ERR(priv
->pdev_mux
[i
]);
327 goto fail_platform_mux_register
;
331 priv
->pdev_hotplug
= platform_device_register_resndata(
332 &mlxplat_dev
->dev
, "mlxcpld-hotplug",
334 mlxplat_mlxcpld_resources
,
335 ARRAY_SIZE(mlxplat_mlxcpld_resources
),
336 mlxplat_hotplug
, sizeof(*mlxplat_hotplug
));
337 if (IS_ERR(priv
->pdev_hotplug
)) {
338 err
= PTR_ERR(priv
->pdev_hotplug
);
339 goto fail_platform_mux_register
;
344 fail_platform_mux_register
:
346 platform_device_unregister(priv
->pdev_mux
[i
]);
347 platform_device_unregister(priv
->pdev_i2c
);
349 platform_device_unregister(mlxplat_dev
);
353 module_init(mlxplat_init
);
355 static void __exit
mlxplat_exit(void)
357 struct mlxplat_priv
*priv
= platform_get_drvdata(mlxplat_dev
);
360 platform_device_unregister(priv
->pdev_hotplug
);
362 for (i
= ARRAY_SIZE(mlxplat_mux_data
) - 1; i
>= 0 ; i
--)
363 platform_device_unregister(priv
->pdev_mux
[i
]);
365 platform_device_unregister(priv
->pdev_i2c
);
366 platform_device_unregister(mlxplat_dev
);
368 module_exit(mlxplat_exit
);
370 MODULE_AUTHOR("Vadim Pasternak (vadimp@mellanox.com)");
371 MODULE_DESCRIPTION("Mellanox platform driver");
372 MODULE_LICENSE("Dual BSD/GPL");
373 MODULE_ALIAS("dmi:*:*Mellanox*:MSN24*:");
374 MODULE_ALIAS("dmi:*:*Mellanox*:MSN27*:");
375 MODULE_ALIAS("dmi:*:*Mellanox*:MSB*:");
376 MODULE_ALIAS("dmi:*:*Mellanox*:MSX*:");
377 MODULE_ALIAS("dmi:*:*Mellanox*:MSN21*:");