GNU Linux-libre 4.9.297-gnu1
[releases.git] / arch / x86 / platform / mellanox / mlx-platform.c
1 /*
2  * arch/x86/platform/mellanox/mlx-platform.c
3  * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
4  * Copyright (c) 2016 Vadim Pasternak <vadimp@mellanox.com>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the names of the copyright holders nor the names of its
15  *    contributors may be used to endorse or promote products derived from
16  *    this software without specific prior written permission.
17  *
18  * Alternatively, this software may be distributed under the terms of the
19  * GNU General Public License ("GPL") version 2 as published by the Free
20  * Software Foundation.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34
35 #include <linux/device.h>
36 #include <linux/dmi.h>
37 #include <linux/i2c.h>
38 #include <linux/i2c-mux.h>
39 #include <linux/module.h>
40 #include <linux/platform_device.h>
41 #include <linux/platform_data/i2c-mux-reg.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 };
74
75 /* Regions for LPC I2C controller and LPC base register space */
76 static const struct resource mlxplat_lpc_resources[] = {
77         [0] = DEFINE_RES_NAMED(MLXPLAT_CPLD_LPC_I2C_BASE_ADRR,
78                                MLXPLAT_CPLD_LPC_IO_RANGE,
79                                "mlxplat_cpld_lpc_i2c_ctrl", IORESOURCE_IO),
80         [1] = DEFINE_RES_NAMED(MLXPLAT_CPLD_LPC_REG_BASE_ADRR,
81                                MLXPLAT_CPLD_LPC_IO_RANGE,
82                                "mlxplat_cpld_lpc_regs",
83                                IORESOURCE_IO),
84 };
85
86 /* Platform default channels */
87 static const int mlxplat_default_channels[][8] = {
88         {
89                 MLXPLAT_CPLD_CH1, MLXPLAT_CPLD_CH1 + 1, MLXPLAT_CPLD_CH1 + 2,
90                 MLXPLAT_CPLD_CH1 + 3, MLXPLAT_CPLD_CH1 + 4, MLXPLAT_CPLD_CH1 +
91                 5, MLXPLAT_CPLD_CH1 + 6, MLXPLAT_CPLD_CH1 + 7
92         },
93         {
94                 MLXPLAT_CPLD_CH2, MLXPLAT_CPLD_CH2 + 1, MLXPLAT_CPLD_CH2 + 2,
95                 MLXPLAT_CPLD_CH2 + 3, MLXPLAT_CPLD_CH2 + 4, MLXPLAT_CPLD_CH2 +
96                 5, MLXPLAT_CPLD_CH2 + 6, MLXPLAT_CPLD_CH2 + 7
97         },
98 };
99
100 /* Platform channels for MSN21xx system family */
101 static const int mlxplat_msn21xx_channels[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
102
103 /* Platform mux data */
104 static struct i2c_mux_reg_platform_data mlxplat_mux_data[] = {
105         {
106                 .parent = 1,
107                 .base_nr = MLXPLAT_CPLD_CH1,
108                 .write_only = 1,
109                 .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG1,
110                 .reg_size = 1,
111                 .idle_in_use = 1,
112         },
113         {
114                 .parent = 1,
115                 .base_nr = MLXPLAT_CPLD_CH2,
116                 .write_only = 1,
117                 .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG2,
118                 .reg_size = 1,
119                 .idle_in_use = 1,
120         },
121
122 };
123
124 static struct platform_device *mlxplat_dev;
125
126 static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
127 {
128         int i;
129
130         for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
131                 mlxplat_mux_data[i].values = mlxplat_default_channels[i];
132                 mlxplat_mux_data[i].n_values =
133                                 ARRAY_SIZE(mlxplat_default_channels[i]);
134         }
135
136         return 1;
137 };
138
139 static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi)
140 {
141         int i;
142
143         for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
144                 mlxplat_mux_data[i].values = mlxplat_msn21xx_channels;
145                 mlxplat_mux_data[i].n_values =
146                                 ARRAY_SIZE(mlxplat_msn21xx_channels);
147         }
148
149         return 1;
150 };
151
152 static struct dmi_system_id mlxplat_dmi_table[] __initdata = {
153         {
154                 .callback = mlxplat_dmi_default_matched,
155                 .matches = {
156                         DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
157                         DMI_MATCH(DMI_PRODUCT_NAME, "MSN24"),
158                 },
159         },
160         {
161                 .callback = mlxplat_dmi_default_matched,
162                 .matches = {
163                         DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
164                         DMI_MATCH(DMI_PRODUCT_NAME, "MSN27"),
165                 },
166         },
167         {
168                 .callback = mlxplat_dmi_default_matched,
169                 .matches = {
170                         DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
171                         DMI_MATCH(DMI_PRODUCT_NAME, "MSB"),
172                 },
173         },
174         {
175                 .callback = mlxplat_dmi_default_matched,
176                 .matches = {
177                         DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
178                         DMI_MATCH(DMI_PRODUCT_NAME, "MSX"),
179                 },
180         },
181         {
182                 .callback = mlxplat_dmi_msn21xx_matched,
183                 .matches = {
184                         DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
185                         DMI_MATCH(DMI_PRODUCT_NAME, "MSN21"),
186                 },
187         },
188         { }
189 };
190
191 static int __init mlxplat_init(void)
192 {
193         struct mlxplat_priv *priv;
194         int i, err;
195
196         if (!dmi_check_system(mlxplat_dmi_table))
197                 return -ENODEV;
198
199         mlxplat_dev = platform_device_register_simple(MLX_PLAT_DEVICE_NAME, -1,
200                                         mlxplat_lpc_resources,
201                                         ARRAY_SIZE(mlxplat_lpc_resources));
202
203         if (IS_ERR(mlxplat_dev))
204                 return PTR_ERR(mlxplat_dev);
205
206         priv = devm_kzalloc(&mlxplat_dev->dev, sizeof(struct mlxplat_priv),
207                             GFP_KERNEL);
208         if (!priv) {
209                 err = -ENOMEM;
210                 goto fail_alloc;
211         }
212         platform_set_drvdata(mlxplat_dev, priv);
213
214         priv->pdev_i2c = platform_device_register_simple("i2c_mlxcpld", -1,
215                                                          NULL, 0);
216         if (IS_ERR(priv->pdev_i2c)) {
217                 err = PTR_ERR(priv->pdev_i2c);
218                 goto fail_alloc;
219         };
220
221         for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
222                 priv->pdev_mux[i] = platform_device_register_resndata(
223                                                 &mlxplat_dev->dev,
224                                                 "i2c-mux-reg", i, NULL,
225                                                 0, &mlxplat_mux_data[i],
226                                                 sizeof(mlxplat_mux_data[i]));
227                 if (IS_ERR(priv->pdev_mux[i])) {
228                         err = PTR_ERR(priv->pdev_mux[i]);
229                         goto fail_platform_mux_register;
230                 }
231         }
232
233         return 0;
234
235 fail_platform_mux_register:
236         while (--i >= 0)
237                 platform_device_unregister(priv->pdev_mux[i]);
238         platform_device_unregister(priv->pdev_i2c);
239 fail_alloc:
240         platform_device_unregister(mlxplat_dev);
241
242         return err;
243 }
244 module_init(mlxplat_init);
245
246 static void __exit mlxplat_exit(void)
247 {
248         struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev);
249         int i;
250
251         for (i = ARRAY_SIZE(mlxplat_mux_data) - 1; i >= 0 ; i--)
252                 platform_device_unregister(priv->pdev_mux[i]);
253
254         platform_device_unregister(priv->pdev_i2c);
255         platform_device_unregister(mlxplat_dev);
256 }
257 module_exit(mlxplat_exit);
258
259 MODULE_AUTHOR("Vadim Pasternak (vadimp@mellanox.com)");
260 MODULE_DESCRIPTION("Mellanox platform driver");
261 MODULE_LICENSE("Dual BSD/GPL");
262 MODULE_ALIAS("dmi:*:*Mellanox*:MSN24*:");
263 MODULE_ALIAS("dmi:*:*Mellanox*:MSN27*:");
264 MODULE_ALIAS("dmi:*:*Mellanox*:MSB*:");
265 MODULE_ALIAS("dmi:*:*Mellanox*:MSX*:");
266 MODULE_ALIAS("dmi:*:*Mellanox*:MSN21*:");