]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/platform/x86/mlx-platform.c
platform/x86: mlx-platform: free first dev on error
[karo-tx-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/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_IO_RANGE               0x100
49 #define MLXPLAT_CPLD_LPC_I2C_CH1_OFF            0xdb
50 #define MLXPLAT_CPLD_LPC_I2C_CH2_OFF            0xda
51 #define MLXPLAT_CPLD_LPC_PIO_OFFSET             0x10000UL
52 #define MLXPLAT_CPLD_LPC_REG1   ((MLXPLAT_CPLD_LPC_REG_BASE_ADRR + \
53                                   MLXPLAT_CPLD_LPC_I2C_CH1_OFF) | \
54                                   MLXPLAT_CPLD_LPC_PIO_OFFSET)
55 #define MLXPLAT_CPLD_LPC_REG2   ((MLXPLAT_CPLD_LPC_REG_BASE_ADRR + \
56                                   MLXPLAT_CPLD_LPC_I2C_CH2_OFF) | \
57                                   MLXPLAT_CPLD_LPC_PIO_OFFSET)
58
59 /* Start channel numbers */
60 #define MLXPLAT_CPLD_CH1                        2
61 #define MLXPLAT_CPLD_CH2                        10
62
63 /* Number of LPC attached MUX platform devices */
64 #define MLXPLAT_CPLD_LPC_MUX_DEVS               2
65
66 /* mlxplat_priv - platform private data
67  * @pdev_i2c - i2c controller platform device
68  * @pdev_mux - array of mux platform devices
69  */
70 struct mlxplat_priv {
71         struct platform_device *pdev_i2c;
72         struct platform_device *pdev_mux[MLXPLAT_CPLD_LPC_MUX_DEVS];
73         struct platform_device *pdev_hotplug;
74 };
75
76 /* Regions for LPC I2C controller and LPC base register space */
77 static const struct resource mlxplat_lpc_resources[] = {
78         [0] = DEFINE_RES_NAMED(MLXPLAT_CPLD_LPC_I2C_BASE_ADRR,
79                                MLXPLAT_CPLD_LPC_IO_RANGE,
80                                "mlxplat_cpld_lpc_i2c_ctrl", IORESOURCE_IO),
81         [1] = DEFINE_RES_NAMED(MLXPLAT_CPLD_LPC_REG_BASE_ADRR,
82                                MLXPLAT_CPLD_LPC_IO_RANGE,
83                                "mlxplat_cpld_lpc_regs",
84                                IORESOURCE_IO),
85 };
86
87 /* Platform default channels */
88 static const int mlxplat_default_channels[][8] = {
89         {
90                 MLXPLAT_CPLD_CH1, MLXPLAT_CPLD_CH1 + 1, MLXPLAT_CPLD_CH1 + 2,
91                 MLXPLAT_CPLD_CH1 + 3, MLXPLAT_CPLD_CH1 + 4, MLXPLAT_CPLD_CH1 +
92                 5, MLXPLAT_CPLD_CH1 + 6, MLXPLAT_CPLD_CH1 + 7
93         },
94         {
95                 MLXPLAT_CPLD_CH2, MLXPLAT_CPLD_CH2 + 1, MLXPLAT_CPLD_CH2 + 2,
96                 MLXPLAT_CPLD_CH2 + 3, MLXPLAT_CPLD_CH2 + 4, MLXPLAT_CPLD_CH2 +
97                 5, MLXPLAT_CPLD_CH2 + 6, MLXPLAT_CPLD_CH2 + 7
98         },
99 };
100
101 /* Platform channels for MSN21xx system family */
102 static const int mlxplat_msn21xx_channels[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
103
104 /* Platform mux data */
105 static struct i2c_mux_reg_platform_data mlxplat_mux_data[] = {
106         {
107                 .parent = 1,
108                 .base_nr = MLXPLAT_CPLD_CH1,
109                 .write_only = 1,
110                 .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG1,
111                 .reg_size = 1,
112                 .idle_in_use = 1,
113         },
114         {
115                 .parent = 1,
116                 .base_nr = MLXPLAT_CPLD_CH2,
117                 .write_only = 1,
118                 .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG2,
119                 .reg_size = 1,
120                 .idle_in_use = 1,
121         },
122
123 };
124
125 /* Platform hotplug devices */
126 static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_psu[] = {
127         {
128                 .brdinfo = { I2C_BOARD_INFO("24c02", 0x51) },
129                 .bus = 10,
130         },
131         {
132                 .brdinfo = { I2C_BOARD_INFO("24c02", 0x50) },
133                 .bus = 10,
134         },
135 };
136
137 static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_pwr[] = {
138         {
139                 .brdinfo = { I2C_BOARD_INFO("dps460", 0x59) },
140                 .bus = 10,
141         },
142         {
143                 .brdinfo = { I2C_BOARD_INFO("dps460", 0x58) },
144                 .bus = 10,
145         },
146 };
147
148 static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_fan[] = {
149         {
150                 .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) },
151                 .bus = 11,
152         },
153         {
154                 .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) },
155                 .bus = 12,
156         },
157         {
158                 .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) },
159                 .bus = 13,
160         },
161         {
162                 .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) },
163                 .bus = 14,
164         },
165 };
166
167 /* Platform hotplug default data */
168 static
169 struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_hotplug_default_data = {
170         .top_aggr_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x3a),
171         .top_aggr_mask = 0x48,
172         .top_aggr_psu_mask = 0x08,
173         .psu_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x58),
174         .psu_mask = 0x03,
175         .psu_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_psu),
176         .psu = mlxplat_mlxcpld_hotplug_psu,
177         .top_aggr_pwr_mask = 0x08,
178         .pwr_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x64),
179         .pwr_mask = 0x03,
180         .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_pwr),
181         .pwr = mlxplat_mlxcpld_hotplug_pwr,
182         .top_aggr_fan_mask = 0x40,
183         .fan_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x88),
184         .fan_mask = 0x0f,
185         .fan_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_fan),
186         .fan = mlxplat_mlxcpld_hotplug_fan,
187 };
188
189 /* Platform hotplug MSN21xx system family data */
190 static
191 struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_hotplug_msn21xx_data = {
192         .top_aggr_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x3a),
193         .top_aggr_mask = 0x04,
194         .top_aggr_pwr_mask = 0x04,
195         .pwr_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x64),
196         .pwr_mask = 0x03,
197         .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_pwr),
198 };
199
200 static struct resource mlxplat_mlxcpld_hotplug_resources[] = {
201         [0] = DEFINE_RES_IRQ_NAMED(17, "mlxcpld-hotplug"),
202 };
203
204 struct platform_device *mlxplat_dev;
205 struct mlxcpld_hotplug_platform_data *mlxplat_hotplug;
206
207 static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
208 {
209         int i;
210
211         for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
212                 mlxplat_mux_data[i].values = mlxplat_default_channels[i];
213                 mlxplat_mux_data[i].n_values =
214                                 ARRAY_SIZE(mlxplat_default_channels[i]);
215         }
216         mlxplat_hotplug = &mlxplat_mlxcpld_hotplug_default_data;
217
218         return 1;
219 };
220
221 static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi)
222 {
223         int i;
224
225         for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
226                 mlxplat_mux_data[i].values = mlxplat_msn21xx_channels;
227                 mlxplat_mux_data[i].n_values =
228                                 ARRAY_SIZE(mlxplat_msn21xx_channels);
229         }
230         mlxplat_hotplug = &mlxplat_mlxcpld_hotplug_msn21xx_data;
231
232         return 1;
233 };
234
235 static struct dmi_system_id mlxplat_dmi_table[] __initdata = {
236         {
237                 .callback = mlxplat_dmi_default_matched,
238                 .matches = {
239                         DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
240                         DMI_MATCH(DMI_PRODUCT_NAME, "MSN24"),
241                 },
242         },
243         {
244                 .callback = mlxplat_dmi_default_matched,
245                 .matches = {
246                         DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
247                         DMI_MATCH(DMI_PRODUCT_NAME, "MSN27"),
248                 },
249         },
250         {
251                 .callback = mlxplat_dmi_default_matched,
252                 .matches = {
253                         DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
254                         DMI_MATCH(DMI_PRODUCT_NAME, "MSB"),
255                 },
256         },
257         {
258                 .callback = mlxplat_dmi_default_matched,
259                 .matches = {
260                         DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
261                         DMI_MATCH(DMI_PRODUCT_NAME, "MSX"),
262                 },
263         },
264         {
265                 .callback = mlxplat_dmi_msn21xx_matched,
266                 .matches = {
267                         DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
268                         DMI_MATCH(DMI_PRODUCT_NAME, "MSN21"),
269                 },
270         },
271         { }
272 };
273
274 static int __init mlxplat_init(void)
275 {
276         struct mlxplat_priv *priv;
277         int i, err;
278
279         if (!dmi_check_system(mlxplat_dmi_table))
280                 return -ENODEV;
281
282         mlxplat_dev = platform_device_register_simple(MLX_PLAT_DEVICE_NAME, -1,
283                                         mlxplat_lpc_resources,
284                                         ARRAY_SIZE(mlxplat_lpc_resources));
285
286         if (IS_ERR(mlxplat_dev))
287                 return PTR_ERR(mlxplat_dev);
288
289         priv = devm_kzalloc(&mlxplat_dev->dev, sizeof(struct mlxplat_priv),
290                             GFP_KERNEL);
291         if (!priv) {
292                 err = -ENOMEM;
293                 goto fail_alloc;
294         }
295         platform_set_drvdata(mlxplat_dev, priv);
296
297         priv->pdev_i2c = platform_device_register_simple("i2c_mlxcpld", -1,
298                                                          NULL, 0);
299         if (IS_ERR(priv->pdev_i2c)) {
300                 err = PTR_ERR(priv->pdev_i2c);
301                 goto fail_alloc;
302         }
303
304         for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
305                 priv->pdev_mux[i] = platform_device_register_resndata(
306                                                 &mlxplat_dev->dev,
307                                                 "i2c-mux-reg", i, NULL,
308                                                 0, &mlxplat_mux_data[i],
309                                                 sizeof(mlxplat_mux_data[i]));
310                 if (IS_ERR(priv->pdev_mux[i])) {
311                         err = PTR_ERR(priv->pdev_mux[i]);
312                         goto fail_platform_mux_register;
313                 }
314         }
315
316         priv->pdev_hotplug = platform_device_register_resndata(
317                                 &mlxplat_dev->dev, "mlxcpld-hotplug", -1,
318                                 mlxplat_mlxcpld_hotplug_resources,
319                                 ARRAY_SIZE(mlxplat_mlxcpld_hotplug_resources),
320                                 mlxplat_hotplug, sizeof(*mlxplat_hotplug));
321         if (IS_ERR(priv->pdev_hotplug)) {
322                 err = PTR_ERR(priv->pdev_hotplug);
323                 goto fail_platform_mux_register;
324         }
325
326         return 0;
327
328 fail_platform_mux_register:
329         while (--i >= 0)
330                 platform_device_unregister(priv->pdev_mux[i]);
331         platform_device_unregister(priv->pdev_i2c);
332 fail_alloc:
333         platform_device_unregister(mlxplat_dev);
334
335         return err;
336 }
337 module_init(mlxplat_init);
338
339 static void __exit mlxplat_exit(void)
340 {
341         struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev);
342         int i;
343
344         platform_device_unregister(priv->pdev_hotplug);
345
346         for (i = ARRAY_SIZE(mlxplat_mux_data) - 1; i >= 0 ; i--)
347                 platform_device_unregister(priv->pdev_mux[i]);
348
349         platform_device_unregister(priv->pdev_i2c);
350         platform_device_unregister(mlxplat_dev);
351 }
352 module_exit(mlxplat_exit);
353
354 MODULE_AUTHOR("Vadim Pasternak (vadimp@mellanox.com)");
355 MODULE_DESCRIPTION("Mellanox platform driver");
356 MODULE_LICENSE("Dual BSD/GPL");
357 MODULE_ALIAS("dmi:*:*Mellanox*:MSN24*:");
358 MODULE_ALIAS("dmi:*:*Mellanox*:MSN27*:");
359 MODULE_ALIAS("dmi:*:*Mellanox*:MSB*:");
360 MODULE_ALIAS("dmi:*:*Mellanox*:MSX*:");
361 MODULE_ALIAS("dmi:*:*Mellanox*:MSN21*:");