GNU Linux-libre 4.14.290-gnu1
[releases.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/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>
42
43 #define MLX_PLAT_DEVICE_NAME            "mlxplat"
44
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)
62
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)
73
74 /* Start channel numbers */
75 #define MLXPLAT_CPLD_CH1                        2
76 #define MLXPLAT_CPLD_CH2                        10
77
78 /* Number of LPC attached MUX platform devices */
79 #define MLXPLAT_CPLD_LPC_MUX_DEVS               2
80
81 /* mlxplat_priv - platform private data
82  * @pdev_i2c - i2c controller platform device
83  * @pdev_mux - array of mux platform devices
84  */
85 struct mlxplat_priv {
86         struct platform_device *pdev_i2c;
87         struct platform_device *pdev_mux[MLXPLAT_CPLD_LPC_MUX_DEVS];
88         struct platform_device *pdev_hotplug;
89 };
90
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",
99                                IORESOURCE_IO),
100 };
101
102 /* Platform default channels */
103 static const int mlxplat_default_channels[][8] = {
104         {
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
108         },
109         {
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
113         },
114 };
115
116 /* Platform channels for MSN21xx system family */
117 static const int mlxplat_msn21xx_channels[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
118
119 /* Platform mux data */
120 static struct i2c_mux_reg_platform_data mlxplat_mux_data[] = {
121         {
122                 .parent = 1,
123                 .base_nr = MLXPLAT_CPLD_CH1,
124                 .write_only = 1,
125                 .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG1,
126                 .reg_size = 1,
127                 .idle_in_use = 1,
128         },
129         {
130                 .parent = 1,
131                 .base_nr = MLXPLAT_CPLD_CH2,
132                 .write_only = 1,
133                 .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG2,
134                 .reg_size = 1,
135                 .idle_in_use = 1,
136         },
137
138 };
139
140 /* Platform hotplug devices */
141 static struct mlxcpld_hotplug_device mlxplat_mlxcpld_psu[] = {
142         {
143                 .brdinfo = { I2C_BOARD_INFO("24c02", 0x51) },
144                 .bus = 10,
145         },
146         {
147                 .brdinfo = { I2C_BOARD_INFO("24c02", 0x50) },
148                 .bus = 10,
149         },
150 };
151
152 static struct mlxcpld_hotplug_device mlxplat_mlxcpld_pwr[] = {
153         {
154                 .brdinfo = { I2C_BOARD_INFO("dps460", 0x59) },
155                 .bus = 10,
156         },
157         {
158                 .brdinfo = { I2C_BOARD_INFO("dps460", 0x58) },
159                 .bus = 10,
160         },
161 };
162
163 static struct mlxcpld_hotplug_device mlxplat_mlxcpld_fan[] = {
164         {
165                 .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) },
166                 .bus = 11,
167         },
168         {
169                 .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) },
170                 .bus = 12,
171         },
172         {
173                 .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) },
174                 .bus = 13,
175         },
176         {
177                 .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) },
178                 .bus = 14,
179         },
180 };
181
182 /* Platform hotplug default data */
183 static
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,
202 };
203
204 /* Platform hotplug MSN21xx system family data */
205 static
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),
213 };
214
215 static struct resource mlxplat_mlxcpld_resources[] = {
216         [0] = DEFINE_RES_IRQ_NAMED(17, "mlxcpld-hotplug"),
217 };
218
219 struct platform_device *mlxplat_dev;
220 struct mlxcpld_hotplug_platform_data *mlxplat_hotplug;
221
222 static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
223 {
224         int i;
225
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]);
230         }
231         mlxplat_hotplug = &mlxplat_mlxcpld_default_data;
232
233         return 1;
234 };
235
236 static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi)
237 {
238         int i;
239
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);
244         }
245         mlxplat_hotplug = &mlxplat_mlxcpld_msn21xx_data;
246
247         return 1;
248 };
249
250 static const struct dmi_system_id mlxplat_dmi_table[] __initconst = {
251         {
252                 .callback = mlxplat_dmi_default_matched,
253                 .matches = {
254                         DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
255                         DMI_MATCH(DMI_PRODUCT_NAME, "MSN24"),
256                 },
257         },
258         {
259                 .callback = mlxplat_dmi_default_matched,
260                 .matches = {
261                         DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
262                         DMI_MATCH(DMI_PRODUCT_NAME, "MSN27"),
263                 },
264         },
265         {
266                 .callback = mlxplat_dmi_default_matched,
267                 .matches = {
268                         DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
269                         DMI_MATCH(DMI_PRODUCT_NAME, "MSB"),
270                 },
271         },
272         {
273                 .callback = mlxplat_dmi_default_matched,
274                 .matches = {
275                         DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
276                         DMI_MATCH(DMI_PRODUCT_NAME, "MSX"),
277                 },
278         },
279         {
280                 .callback = mlxplat_dmi_msn21xx_matched,
281                 .matches = {
282                         DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
283                         DMI_MATCH(DMI_PRODUCT_NAME, "MSN21"),
284                 },
285         },
286         { }
287 };
288
289 static int __init mlxplat_init(void)
290 {
291         struct mlxplat_priv *priv;
292         int i, err;
293
294         if (!dmi_check_system(mlxplat_dmi_table))
295                 return -ENODEV;
296
297         mlxplat_dev = platform_device_register_simple(MLX_PLAT_DEVICE_NAME, -1,
298                                         mlxplat_lpc_resources,
299                                         ARRAY_SIZE(mlxplat_lpc_resources));
300
301         if (IS_ERR(mlxplat_dev))
302                 return PTR_ERR(mlxplat_dev);
303
304         priv = devm_kzalloc(&mlxplat_dev->dev, sizeof(struct mlxplat_priv),
305                             GFP_KERNEL);
306         if (!priv) {
307                 err = -ENOMEM;
308                 goto fail_alloc;
309         }
310         platform_set_drvdata(mlxplat_dev, priv);
311
312         priv->pdev_i2c = platform_device_register_simple("i2c_mlxcpld", -1,
313                                                          NULL, 0);
314         if (IS_ERR(priv->pdev_i2c)) {
315                 err = PTR_ERR(priv->pdev_i2c);
316                 goto fail_alloc;
317         }
318
319         for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
320                 priv->pdev_mux[i] = platform_device_register_resndata(
321                                                 &priv->pdev_i2c->dev,
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;
328                 }
329         }
330
331         priv->pdev_hotplug = platform_device_register_resndata(
332                                 &mlxplat_dev->dev, "mlxcpld-hotplug",
333                                 PLATFORM_DEVID_NONE,
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;
340         }
341
342         return 0;
343
344 fail_platform_mux_register:
345         while (--i >= 0)
346                 platform_device_unregister(priv->pdev_mux[i]);
347         platform_device_unregister(priv->pdev_i2c);
348 fail_alloc:
349         platform_device_unregister(mlxplat_dev);
350
351         return err;
352 }
353 module_init(mlxplat_init);
354
355 static void __exit mlxplat_exit(void)
356 {
357         struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev);
358         int i;
359
360         platform_device_unregister(priv->pdev_hotplug);
361
362         for (i = ARRAY_SIZE(mlxplat_mux_data) - 1; i >= 0 ; i--)
363                 platform_device_unregister(priv->pdev_mux[i]);
364
365         platform_device_unregister(priv->pdev_i2c);
366         platform_device_unregister(mlxplat_dev);
367 }
368 module_exit(mlxplat_exit);
369
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*:");