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 &mlxplat_dev->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*:"); 378