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*:");
362