Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
   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/io.h>
  39#include <linux/module.h>
  40#include <linux/platform_device.h>
  41#include <linux/platform_data/i2c-mux-reg.h>
  42#include <linux/platform_data/mlxreg.h>
  43#include <linux/regmap.h>
  44
  45#define MLX_PLAT_DEVICE_NAME		"mlxplat"
  46
  47/* LPC bus IO offsets */
  48#define MLXPLAT_CPLD_LPC_I2C_BASE_ADRR		0x2000
  49#define MLXPLAT_CPLD_LPC_REG_BASE_ADRR		0x2500
  50#define MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET	0x3a
  51#define MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET	0x3b
  52#define MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET	0x40
  53#define MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET	0x41
  54#define MLXPLAT_CPLD_LPC_REG_PSU_OFFSET		0x58
  55#define MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET	0x59
  56#define MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET	0x5a
  57#define MLXPLAT_CPLD_LPC_REG_PWR_OFFSET		0x64
  58#define MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFFSET	0x65
  59#define MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFFSET	0x66
  60#define MLXPLAT_CPLD_LPC_REG_FAN_OFFSET		0x88
  61#define MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET	0x89
  62#define MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET	0x8a
  63#define MLXPLAT_CPLD_LPC_IO_RANGE		0x100
  64#define MLXPLAT_CPLD_LPC_I2C_CH1_OFF		0xdb
  65#define MLXPLAT_CPLD_LPC_I2C_CH2_OFF		0xda
  66#define MLXPLAT_CPLD_LPC_PIO_OFFSET		0x10000UL
  67#define MLXPLAT_CPLD_LPC_REG1	((MLXPLAT_CPLD_LPC_REG_BASE_ADRR + \
  68				  MLXPLAT_CPLD_LPC_I2C_CH1_OFF) | \
  69				  MLXPLAT_CPLD_LPC_PIO_OFFSET)
  70#define MLXPLAT_CPLD_LPC_REG2	((MLXPLAT_CPLD_LPC_REG_BASE_ADRR + \
  71				  MLXPLAT_CPLD_LPC_I2C_CH2_OFF) | \
  72				  MLXPLAT_CPLD_LPC_PIO_OFFSET)
  73
  74/* Masks for aggregation, psu, pwr and fan event in CPLD related registers. */
  75#define MLXPLAT_CPLD_AGGR_PSU_MASK_DEF	0x08
  76#define MLXPLAT_CPLD_AGGR_PWR_MASK_DEF	0x08
  77#define MLXPLAT_CPLD_AGGR_FAN_MASK_DEF	0x40
  78#define MLXPLAT_CPLD_AGGR_MASK_DEF	(MLXPLAT_CPLD_AGGR_PSU_MASK_DEF | \
  79					 MLXPLAT_CPLD_AGGR_FAN_MASK_DEF)
  80#define MLXPLAT_CPLD_AGGR_MASK_NG_DEF	0x04
  81#define MLXPLAT_CPLD_LOW_AGGR_MASK_LOW	0xc0
  82#define MLXPLAT_CPLD_AGGR_MASK_MSN21XX	0x04
  83#define MLXPLAT_CPLD_PSU_MASK		GENMASK(1, 0)
  84#define MLXPLAT_CPLD_PWR_MASK		GENMASK(1, 0)
  85#define MLXPLAT_CPLD_FAN_MASK		GENMASK(3, 0)
  86#define MLXPLAT_CPLD_FAN_NG_MASK	GENMASK(5, 0)
  87
  88/* Default I2C parent bus number */
  89#define MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR	1
  90
  91/* Maximum number of possible physical buses equipped on system */
  92#define MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM	16
  93
  94/* Number of channels in group */
  95#define MLXPLAT_CPLD_GRP_CHNL_NUM		8
  96
  97/* Start channel numbers */
  98#define MLXPLAT_CPLD_CH1			2
  99#define MLXPLAT_CPLD_CH2			10
 100
 101/* Number of LPC attached MUX platform devices */
 102#define MLXPLAT_CPLD_LPC_MUX_DEVS		2
 103
 104/* Hotplug devices adapter numbers */
 105#define MLXPLAT_CPLD_NR_NONE			-1
 106#define MLXPLAT_CPLD_PSU_DEFAULT_NR		10
 107#define MLXPLAT_CPLD_PSU_MSNXXXX_NR		4
 108#define MLXPLAT_CPLD_FAN1_DEFAULT_NR		11
 109#define MLXPLAT_CPLD_FAN2_DEFAULT_NR		12
 110#define MLXPLAT_CPLD_FAN3_DEFAULT_NR		13
 111#define MLXPLAT_CPLD_FAN4_DEFAULT_NR		14
 112
 113/* mlxplat_priv - platform private data
 114 * @pdev_i2c - i2c controller platform device
 115 * @pdev_mux - array of mux platform devices
 116 * @pdev_hotplug - hotplug platform devices
 117 */
 118struct mlxplat_priv {
 119	struct platform_device *pdev_i2c;
 120	struct platform_device *pdev_mux[MLXPLAT_CPLD_LPC_MUX_DEVS];
 121	struct platform_device *pdev_hotplug;
 122};
 123
 124/* Regions for LPC I2C controller and LPC base register space */
 125static const struct resource mlxplat_lpc_resources[] = {
 126	[0] = DEFINE_RES_NAMED(MLXPLAT_CPLD_LPC_I2C_BASE_ADRR,
 127			       MLXPLAT_CPLD_LPC_IO_RANGE,
 128			       "mlxplat_cpld_lpc_i2c_ctrl", IORESOURCE_IO),
 129	[1] = DEFINE_RES_NAMED(MLXPLAT_CPLD_LPC_REG_BASE_ADRR,
 130			       MLXPLAT_CPLD_LPC_IO_RANGE,
 131			       "mlxplat_cpld_lpc_regs",
 132			       IORESOURCE_IO),
 133};
 134
 135/* Platform default channels */
 136static const int mlxplat_default_channels[][MLXPLAT_CPLD_GRP_CHNL_NUM] = {
 137	{
 138		MLXPLAT_CPLD_CH1, MLXPLAT_CPLD_CH1 + 1, MLXPLAT_CPLD_CH1 + 2,
 139		MLXPLAT_CPLD_CH1 + 3, MLXPLAT_CPLD_CH1 + 4, MLXPLAT_CPLD_CH1 +
 140		5, MLXPLAT_CPLD_CH1 + 6, MLXPLAT_CPLD_CH1 + 7
 141	},
 142	{
 143		MLXPLAT_CPLD_CH2, MLXPLAT_CPLD_CH2 + 1, MLXPLAT_CPLD_CH2 + 2,
 144		MLXPLAT_CPLD_CH2 + 3, MLXPLAT_CPLD_CH2 + 4, MLXPLAT_CPLD_CH2 +
 145		5, MLXPLAT_CPLD_CH2 + 6, MLXPLAT_CPLD_CH2 + 7
 146	},
 147};
 148
 149/* Platform channels for MSN21xx system family */
 150static const int mlxplat_msn21xx_channels[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
 151
 152/* Platform mux data */
 153static struct i2c_mux_reg_platform_data mlxplat_mux_data[] = {
 154	{
 155		.parent = 1,
 156		.base_nr = MLXPLAT_CPLD_CH1,
 157		.write_only = 1,
 158		.reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG1,
 159		.reg_size = 1,
 160		.idle_in_use = 1,
 161	},
 162	{
 163		.parent = 1,
 164		.base_nr = MLXPLAT_CPLD_CH2,
 165		.write_only = 1,
 166		.reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG2,
 167		.reg_size = 1,
 168		.idle_in_use = 1,
 169	},
 170
 171};
 172
 173/* Platform hotplug devices */
 174static struct i2c_board_info mlxplat_mlxcpld_psu[] = {
 175	{
 176		I2C_BOARD_INFO("24c02", 0x51),
 177	},
 178	{
 179		I2C_BOARD_INFO("24c02", 0x50),
 180	},
 181};
 182
 183static struct i2c_board_info mlxplat_mlxcpld_ng_psu[] = {
 184	{
 185		I2C_BOARD_INFO("24c32", 0x51),
 186	},
 187	{
 188		I2C_BOARD_INFO("24c32", 0x50),
 189	},
 190};
 191
 192static struct i2c_board_info mlxplat_mlxcpld_pwr[] = {
 193	{
 194		I2C_BOARD_INFO("dps460", 0x59),
 195	},
 196	{
 197		I2C_BOARD_INFO("dps460", 0x58),
 198	},
 199};
 200
 201static struct i2c_board_info mlxplat_mlxcpld_fan[] = {
 202	{
 203		I2C_BOARD_INFO("24c32", 0x50),
 204	},
 205	{
 206		I2C_BOARD_INFO("24c32", 0x50),
 207	},
 208	{
 209		I2C_BOARD_INFO("24c32", 0x50),
 210	},
 211	{
 212		I2C_BOARD_INFO("24c32", 0x50),
 213	},
 214};
 215
 216/* Platform hotplug default data */
 217static struct mlxreg_core_data mlxplat_mlxcpld_default_psu_items_data[] = {
 218	{
 219		.label = "psu1",
 220		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
 221		.mask = BIT(0),
 222		.hpdev.brdinfo = &mlxplat_mlxcpld_psu[0],
 223		.hpdev.nr = MLXPLAT_CPLD_PSU_DEFAULT_NR,
 224	},
 225	{
 226		.label = "psu2",
 227		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
 228		.mask = BIT(1),
 229		.hpdev.brdinfo = &mlxplat_mlxcpld_psu[1],
 230		.hpdev.nr = MLXPLAT_CPLD_PSU_DEFAULT_NR,
 231	},
 232};
 233
 234static struct mlxreg_core_data mlxplat_mlxcpld_default_pwr_items_data[] = {
 235	{
 236		.label = "pwr1",
 237		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
 238		.mask = BIT(0),
 239		.hpdev.brdinfo = &mlxplat_mlxcpld_pwr[0],
 240		.hpdev.nr = MLXPLAT_CPLD_PSU_DEFAULT_NR,
 241	},
 242	{
 243		.label = "pwr2",
 244		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
 245		.mask = BIT(1),
 246		.hpdev.brdinfo = &mlxplat_mlxcpld_pwr[1],
 247		.hpdev.nr = MLXPLAT_CPLD_PSU_DEFAULT_NR,
 248	},
 249};
 250
 251static struct mlxreg_core_data mlxplat_mlxcpld_default_fan_items_data[] = {
 252	{
 253		.label = "fan1",
 254		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
 255		.mask = BIT(0),
 256		.hpdev.brdinfo = &mlxplat_mlxcpld_fan[0],
 257		.hpdev.nr = MLXPLAT_CPLD_FAN1_DEFAULT_NR,
 258	},
 259	{
 260		.label = "fan2",
 261		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
 262		.mask = BIT(1),
 263		.hpdev.brdinfo = &mlxplat_mlxcpld_fan[1],
 264		.hpdev.nr = MLXPLAT_CPLD_FAN2_DEFAULT_NR,
 265	},
 266	{
 267		.label = "fan3",
 268		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
 269		.mask = BIT(2),
 270		.hpdev.brdinfo = &mlxplat_mlxcpld_fan[2],
 271		.hpdev.nr = MLXPLAT_CPLD_FAN3_DEFAULT_NR,
 272	},
 273	{
 274		.label = "fan4",
 275		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
 276		.mask = BIT(3),
 277		.hpdev.brdinfo = &mlxplat_mlxcpld_fan[3],
 278		.hpdev.nr = MLXPLAT_CPLD_FAN4_DEFAULT_NR,
 279	},
 280};
 281
 282static struct mlxreg_core_item mlxplat_mlxcpld_default_items[] = {
 283	{
 284		.data = mlxplat_mlxcpld_default_psu_items_data,
 285		.aggr_mask = MLXPLAT_CPLD_AGGR_PSU_MASK_DEF,
 286		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
 287		.mask = MLXPLAT_CPLD_PSU_MASK,
 288		.count = ARRAY_SIZE(mlxplat_mlxcpld_psu),
 289		.inversed = 1,
 290		.health = false,
 291	},
 292	{
 293		.data = mlxplat_mlxcpld_default_pwr_items_data,
 294		.aggr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF,
 295		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
 296		.mask = MLXPLAT_CPLD_PWR_MASK,
 297		.count = ARRAY_SIZE(mlxplat_mlxcpld_pwr),
 298		.inversed = 0,
 299		.health = false,
 300	},
 301	{
 302		.data = mlxplat_mlxcpld_default_fan_items_data,
 303		.aggr_mask = MLXPLAT_CPLD_AGGR_FAN_MASK_DEF,
 304		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
 305		.mask = MLXPLAT_CPLD_FAN_MASK,
 306		.count = ARRAY_SIZE(mlxplat_mlxcpld_fan),
 307		.inversed = 1,
 308		.health = false,
 309	},
 310};
 311
 312static
 313struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_default_data = {
 314	.items = mlxplat_mlxcpld_default_items,
 315	.counter = ARRAY_SIZE(mlxplat_mlxcpld_default_items),
 316	.cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
 317	.mask = MLXPLAT_CPLD_AGGR_MASK_DEF,
 318};
 319
 320static struct mlxreg_core_data mlxplat_mlxcpld_msn21xx_pwr_items_data[] = {
 321	{
 322		.label = "pwr1",
 323		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
 324		.mask = BIT(0),
 325		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
 326	},
 327	{
 328		.label = "pwr2",
 329		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
 330		.mask = BIT(1),
 331		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
 332	},
 333};
 334
 335/* Platform hotplug MSN21xx system family data */
 336static struct mlxreg_core_item mlxplat_mlxcpld_msn21xx_items[] = {
 337	{
 338		.data = mlxplat_mlxcpld_msn21xx_pwr_items_data,
 339		.aggr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF,
 340		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
 341		.mask = MLXPLAT_CPLD_PWR_MASK,
 342		.count = ARRAY_SIZE(mlxplat_mlxcpld_msn21xx_pwr_items_data),
 343		.inversed = 0,
 344		.health = false,
 345	},
 346};
 347
 348static
 349struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_msn21xx_data = {
 350	.items = mlxplat_mlxcpld_msn21xx_items,
 351	.counter = ARRAY_SIZE(mlxplat_mlxcpld_msn21xx_items),
 352	.cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
 353	.mask = MLXPLAT_CPLD_AGGR_MASK_DEF,
 354	.cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET,
 355	.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW,
 356};
 357
 358/* Platform hotplug msn274x system family data */
 359static struct mlxreg_core_data mlxplat_mlxcpld_msn274x_psu_items_data[] = {
 360	{
 361		.label = "psu1",
 362		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
 363		.mask = BIT(0),
 364		.hpdev.brdinfo = &mlxplat_mlxcpld_psu[0],
 365		.hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR,
 366	},
 367	{
 368		.label = "psu2",
 369		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
 370		.mask = BIT(1),
 371		.hpdev.brdinfo = &mlxplat_mlxcpld_psu[1],
 372		.hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR,
 373	},
 374};
 375
 376static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_pwr_items_data[] = {
 377	{
 378		.label = "pwr1",
 379		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
 380		.mask = BIT(0),
 381		.hpdev.brdinfo = &mlxplat_mlxcpld_pwr[0],
 382		.hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR,
 383	},
 384	{
 385		.label = "pwr2",
 386		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
 387		.mask = BIT(1),
 388		.hpdev.brdinfo = &mlxplat_mlxcpld_pwr[1],
 389		.hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR,
 390	},
 391};
 392
 393static struct mlxreg_core_data mlxplat_mlxcpld_msn274x_fan_items_data[] = {
 394	{
 395		.label = "fan1",
 396		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
 397		.mask = BIT(0),
 398		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
 399	},
 400	{
 401		.label = "fan2",
 402		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
 403		.mask = BIT(1),
 404		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
 405	},
 406	{
 407		.label = "fan3",
 408		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
 409		.mask = BIT(2),
 410		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
 411	},
 412	{
 413		.label = "fan4",
 414		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
 415		.mask = BIT(3),
 416		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
 417	},
 418};
 419
 420static struct mlxreg_core_item mlxplat_mlxcpld_msn274x_items[] = {
 421	{
 422		.data = mlxplat_mlxcpld_msn274x_psu_items_data,
 423		.aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
 424		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
 425		.mask = MLXPLAT_CPLD_PSU_MASK,
 426		.count = ARRAY_SIZE(mlxplat_mlxcpld_msn274x_psu_items_data),
 427		.inversed = 1,
 428		.health = false,
 429	},
 430	{
 431		.data = mlxplat_mlxcpld_default_ng_pwr_items_data,
 432		.aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
 433		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
 434		.mask = MLXPLAT_CPLD_PWR_MASK,
 435		.count = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_pwr_items_data),
 436		.inversed = 0,
 437		.health = false,
 438	},
 439	{
 440		.data = mlxplat_mlxcpld_msn274x_fan_items_data,
 441		.aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
 442		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
 443		.mask = MLXPLAT_CPLD_FAN_MASK,
 444		.count = ARRAY_SIZE(mlxplat_mlxcpld_msn274x_fan_items_data),
 445		.inversed = 1,
 446		.health = false,
 447	},
 448};
 449
 450static
 451struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_msn274x_data = {
 452	.items = mlxplat_mlxcpld_msn274x_items,
 453	.counter = ARRAY_SIZE(mlxplat_mlxcpld_msn274x_items),
 454	.cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
 455	.mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
 456	.cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET,
 457	.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW,
 458};
 459
 460/* Platform hotplug MSN201x system family data */
 461static struct mlxreg_core_data mlxplat_mlxcpld_msn201x_pwr_items_data[] = {
 462	{
 463		.label = "pwr1",
 464		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
 465		.mask = BIT(0),
 466		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
 467	},
 468	{
 469		.label = "pwr2",
 470		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
 471		.mask = BIT(1),
 472		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
 473	},
 474};
 475
 476static struct mlxreg_core_item mlxplat_mlxcpld_msn201x_items[] = {
 477	{
 478		.data = mlxplat_mlxcpld_msn201x_pwr_items_data,
 479		.aggr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF,
 480		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
 481		.mask = MLXPLAT_CPLD_PWR_MASK,
 482		.count = ARRAY_SIZE(mlxplat_mlxcpld_msn201x_pwr_items_data),
 483		.inversed = 0,
 484		.health = false,
 485	},
 486};
 487
 488static
 489struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_msn201x_data = {
 490	.items = mlxplat_mlxcpld_msn21xx_items,
 491	.counter = ARRAY_SIZE(mlxplat_mlxcpld_msn201x_items),
 492	.cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
 493	.mask = MLXPLAT_CPLD_AGGR_MASK_DEF,
 494	.cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET,
 495	.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW,
 496};
 497
 498/* Platform hotplug next generation system family data */
 499static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_psu_items_data[] = {
 500	{
 501		.label = "psu1",
 502		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
 503		.mask = BIT(0),
 504		.hpdev.brdinfo = &mlxplat_mlxcpld_ng_psu[0],
 505		.hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR,
 506	},
 507	{
 508		.label = "psu2",
 509		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
 510		.mask = BIT(1),
 511		.hpdev.brdinfo = &mlxplat_mlxcpld_ng_psu[1],
 512		.hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR,
 513	},
 514};
 515
 516static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_fan_items_data[] = {
 517	{
 518		.label = "fan1",
 519		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
 520		.mask = BIT(0),
 521		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
 522	},
 523	{
 524		.label = "fan2",
 525		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
 526		.mask = BIT(1),
 527		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
 528	},
 529	{
 530		.label = "fan3",
 531		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
 532		.mask = BIT(2),
 533		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
 534	},
 535	{
 536		.label = "fan4",
 537		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
 538		.mask = BIT(3),
 539		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
 540	},
 541	{
 542		.label = "fan5",
 543		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
 544		.mask = BIT(4),
 545		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
 546	},
 547	{
 548		.label = "fan6",
 549		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
 550		.mask = BIT(5),
 551		.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
 552	},
 553};
 554
 555static struct mlxreg_core_item mlxplat_mlxcpld_default_ng_items[] = {
 556	{
 557		.data = mlxplat_mlxcpld_default_ng_psu_items_data,
 558		.aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
 559		.reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
 560		.mask = MLXPLAT_CPLD_PSU_MASK,
 561		.count = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_psu_items_data),
 562		.inversed = 1,
 563		.health = false,
 564	},
 565	{
 566		.data = mlxplat_mlxcpld_default_ng_pwr_items_data,
 567		.aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
 568		.reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
 569		.mask = MLXPLAT_CPLD_PWR_MASK,
 570		.count = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_pwr_items_data),
 571		.inversed = 0,
 572		.health = false,
 573	},
 574	{
 575		.data = mlxplat_mlxcpld_default_ng_fan_items_data,
 576		.aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
 577		.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
 578		.mask = MLXPLAT_CPLD_FAN_NG_MASK,
 579		.count = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_fan_items_data),
 580		.inversed = 1,
 581		.health = false,
 582	},
 583};
 584
 585static
 586struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_default_ng_data = {
 587	.items = mlxplat_mlxcpld_default_ng_items,
 588	.counter = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_items),
 589	.cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
 590	.mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
 591	.cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET,
 592	.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW,
 593};
 594
 595static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg)
 596{
 597	switch (reg) {
 598	case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
 599	case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
 600	case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
 601	case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET:
 602	case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFFSET:
 603	case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFFSET:
 604	case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET:
 605	case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET:
 606		return true;
 607	}
 608	return false;
 609}
 610
 611static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg)
 612{
 613	switch (reg) {
 614	case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET:
 615	case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
 616	case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET:
 617	case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
 618	case MLXPLAT_CPLD_LPC_REG_PSU_OFFSET:
 619	case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
 620	case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET:
 621	case MLXPLAT_CPLD_LPC_REG_PWR_OFFSET:
 622	case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFFSET:
 623	case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFFSET:
 624	case MLXPLAT_CPLD_LPC_REG_FAN_OFFSET:
 625	case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET:
 626	case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET:
 627		return true;
 628	}
 629	return false;
 630}
 631
 632static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg)
 633{
 634	switch (reg) {
 635	case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET:
 636	case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
 637	case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET:
 638	case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
 639	case MLXPLAT_CPLD_LPC_REG_PSU_OFFSET:
 640	case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
 641	case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET:
 642	case MLXPLAT_CPLD_LPC_REG_PWR_OFFSET:
 643	case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFFSET:
 644	case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFFSET:
 645	case MLXPLAT_CPLD_LPC_REG_FAN_OFFSET:
 646	case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET:
 647	case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET:
 648		return true;
 649	}
 650	return false;
 651}
 652
 653struct mlxplat_mlxcpld_regmap_context {
 654	void __iomem *base;
 655};
 656
 657static struct mlxplat_mlxcpld_regmap_context mlxplat_mlxcpld_regmap_ctx;
 658
 659static int
 660mlxplat_mlxcpld_reg_read(void *context, unsigned int reg, unsigned int *val)
 661{
 662	struct mlxplat_mlxcpld_regmap_context *ctx = context;
 663
 664	*val = ioread8(ctx->base + reg);
 665	return 0;
 666}
 667
 668static int
 669mlxplat_mlxcpld_reg_write(void *context, unsigned int reg, unsigned int val)
 670{
 671	struct mlxplat_mlxcpld_regmap_context *ctx = context;
 672
 673	iowrite8(val, ctx->base + reg);
 674	return 0;
 675}
 676
 677static const struct regmap_config mlxplat_mlxcpld_regmap_config = {
 678	.reg_bits = 8,
 679	.val_bits = 8,
 680	.max_register = 255,
 681	.cache_type = REGCACHE_FLAT,
 682	.writeable_reg = mlxplat_mlxcpld_writeable_reg,
 683	.readable_reg = mlxplat_mlxcpld_readable_reg,
 684	.volatile_reg = mlxplat_mlxcpld_volatile_reg,
 685	.reg_read = mlxplat_mlxcpld_reg_read,
 686	.reg_write = mlxplat_mlxcpld_reg_write,
 687};
 688
 689static struct resource mlxplat_mlxcpld_resources[] = {
 690	[0] = DEFINE_RES_IRQ_NAMED(17, "mlxreg-hotplug"),
 691};
 692
 693static struct platform_device *mlxplat_dev;
 694static struct mlxreg_core_hotplug_platform_data *mlxplat_hotplug;
 695
 696static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
 697{
 698	int i;
 699
 700	for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
 701		mlxplat_mux_data[i].values = mlxplat_default_channels[i];
 702		mlxplat_mux_data[i].n_values =
 703				ARRAY_SIZE(mlxplat_default_channels[i]);
 704	}
 705	mlxplat_hotplug = &mlxplat_mlxcpld_default_data;
 706	mlxplat_hotplug->deferred_nr =
 707		mlxplat_default_channels[i - 1][MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
 708
 709	return 1;
 710};
 711
 712static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi)
 713{
 714	int i;
 715
 716	for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
 717		mlxplat_mux_data[i].values = mlxplat_msn21xx_channels;
 718		mlxplat_mux_data[i].n_values =
 719				ARRAY_SIZE(mlxplat_msn21xx_channels);
 720	}
 721	mlxplat_hotplug = &mlxplat_mlxcpld_msn21xx_data;
 722	mlxplat_hotplug->deferred_nr =
 723		mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
 724
 725	return 1;
 726};
 727
 728static int __init mlxplat_dmi_msn274x_matched(const struct dmi_system_id *dmi)
 729{
 730	int i;
 731
 732	for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
 733		mlxplat_mux_data[i].values = mlxplat_msn21xx_channels;
 734		mlxplat_mux_data[i].n_values =
 735				ARRAY_SIZE(mlxplat_msn21xx_channels);
 736	}
 737	mlxplat_hotplug = &mlxplat_mlxcpld_msn274x_data;
 738	mlxplat_hotplug->deferred_nr =
 739		mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
 740
 741	return 1;
 742};
 743
 744static int __init mlxplat_dmi_msn201x_matched(const struct dmi_system_id *dmi)
 745{
 746	int i;
 747
 748	for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
 749		mlxplat_mux_data[i].values = mlxplat_msn21xx_channels;
 750		mlxplat_mux_data[i].n_values =
 751				ARRAY_SIZE(mlxplat_msn21xx_channels);
 752	}
 753	mlxplat_hotplug = &mlxplat_mlxcpld_msn201x_data;
 754	mlxplat_hotplug->deferred_nr =
 755		mlxplat_default_channels[i - 1][MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
 756
 757	return 1;
 758};
 759
 760static int __init mlxplat_dmi_qmb7xx_matched(const struct dmi_system_id *dmi)
 761{
 762	int i;
 763
 764	for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
 765		mlxplat_mux_data[i].values = mlxplat_msn21xx_channels;
 766		mlxplat_mux_data[i].n_values =
 767				ARRAY_SIZE(mlxplat_msn21xx_channels);
 768	}
 769	mlxplat_hotplug = &mlxplat_mlxcpld_default_ng_data;
 770	mlxplat_hotplug->deferred_nr =
 771		mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
 772
 773	return 1;
 774};
 775
 776static const struct dmi_system_id mlxplat_dmi_table[] __initconst = {
 777	{
 778		.callback = mlxplat_dmi_msn274x_matched,
 779		.matches = {
 780			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
 781			DMI_MATCH(DMI_PRODUCT_NAME, "MSN274"),
 782		},
 783	},
 784	{
 785		.callback = mlxplat_dmi_default_matched,
 786		.matches = {
 787			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
 788			DMI_MATCH(DMI_PRODUCT_NAME, "MSN24"),
 789		},
 790	},
 791	{
 792		.callback = mlxplat_dmi_default_matched,
 793		.matches = {
 794			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
 795			DMI_MATCH(DMI_PRODUCT_NAME, "MSN27"),
 796		},
 797	},
 798	{
 799		.callback = mlxplat_dmi_default_matched,
 800		.matches = {
 801			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
 802			DMI_MATCH(DMI_PRODUCT_NAME, "MSB"),
 803		},
 804	},
 805	{
 806		.callback = mlxplat_dmi_default_matched,
 807		.matches = {
 808			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
 809			DMI_MATCH(DMI_PRODUCT_NAME, "MSX"),
 810		},
 811	},
 812	{
 813		.callback = mlxplat_dmi_msn21xx_matched,
 814		.matches = {
 815			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
 816			DMI_MATCH(DMI_PRODUCT_NAME, "MSN21"),
 817		},
 818	},
 819	{
 820		.callback = mlxplat_dmi_msn201x_matched,
 821		.matches = {
 822			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
 823			DMI_MATCH(DMI_PRODUCT_NAME, "MSN201"),
 824		},
 825	},
 826	{
 827		.callback = mlxplat_dmi_qmb7xx_matched,
 828		.matches = {
 829			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
 830			DMI_MATCH(DMI_PRODUCT_NAME, "QMB7"),
 831		},
 832	},
 833	{
 834		.callback = mlxplat_dmi_qmb7xx_matched,
 835		.matches = {
 836			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
 837			DMI_MATCH(DMI_PRODUCT_NAME, "SN37"),
 838		},
 839	},
 840	{
 841		.callback = mlxplat_dmi_qmb7xx_matched,
 842		.matches = {
 843			DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
 844			DMI_MATCH(DMI_PRODUCT_NAME, "SN34"),
 845		},
 846	},
 847	{ }
 848};
 849
 850MODULE_DEVICE_TABLE(dmi, mlxplat_dmi_table);
 851
 852static int mlxplat_mlxcpld_verify_bus_topology(int *nr)
 853{
 854	struct i2c_adapter *search_adap;
 855	int shift, i;
 856
 857	/* Scan adapters from expected id to verify it is free. */
 858	*nr = MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR;
 859	for (i = MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR; i <
 860	     MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM; i++) {
 861		search_adap = i2c_get_adapter(i);
 862		if (search_adap) {
 863			i2c_put_adapter(search_adap);
 864			continue;
 865		}
 866
 867		/* Return if expected parent adapter is free. */
 868		if (i == MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR)
 869			return 0;
 870		break;
 871	}
 872
 873	/* Return with error if free id for adapter is not found. */
 874	if (i == MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM)
 875		return -ENODEV;
 876
 877	/* Shift adapter ids, since expected parent adapter is not free. */
 878	*nr = i;
 879	for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
 880		shift = *nr - mlxplat_mux_data[i].parent;
 881		mlxplat_mux_data[i].parent = *nr;
 882		mlxplat_mux_data[i].base_nr += shift;
 883		if (shift > 0)
 884			mlxplat_hotplug->shift_nr = shift;
 885	}
 886
 887	return 0;
 888}
 889
 890static int __init mlxplat_init(void)
 891{
 892	struct mlxplat_priv *priv;
 893	int i, nr, err;
 894
 895	if (!dmi_check_system(mlxplat_dmi_table))
 896		return -ENODEV;
 897
 898	mlxplat_dev = platform_device_register_simple(MLX_PLAT_DEVICE_NAME, -1,
 899					mlxplat_lpc_resources,
 900					ARRAY_SIZE(mlxplat_lpc_resources));
 901
 902	if (IS_ERR(mlxplat_dev))
 903		return PTR_ERR(mlxplat_dev);
 904
 905	priv = devm_kzalloc(&mlxplat_dev->dev, sizeof(struct mlxplat_priv),
 906			    GFP_KERNEL);
 907	if (!priv) {
 908		err = -ENOMEM;
 909		goto fail_alloc;
 910	}
 911	platform_set_drvdata(mlxplat_dev, priv);
 912
 913	err = mlxplat_mlxcpld_verify_bus_topology(&nr);
 914	if (nr < 0)
 915		goto fail_alloc;
 916
 917	nr = (nr == MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM) ? -1 : nr;
 918	priv->pdev_i2c = platform_device_register_simple("i2c_mlxcpld", nr,
 919							 NULL, 0);
 920	if (IS_ERR(priv->pdev_i2c)) {
 921		err = PTR_ERR(priv->pdev_i2c);
 922		goto fail_alloc;
 923	}
 924
 925	for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
 926		priv->pdev_mux[i] = platform_device_register_resndata(
 927						&mlxplat_dev->dev,
 928						"i2c-mux-reg", i, NULL,
 929						0, &mlxplat_mux_data[i],
 930						sizeof(mlxplat_mux_data[i]));
 931		if (IS_ERR(priv->pdev_mux[i])) {
 932			err = PTR_ERR(priv->pdev_mux[i]);
 933			goto fail_platform_mux_register;
 934		}
 935	}
 936
 937	mlxplat_mlxcpld_regmap_ctx.base = devm_ioport_map(&mlxplat_dev->dev,
 938			       mlxplat_lpc_resources[1].start, 1);
 939	if (!mlxplat_mlxcpld_regmap_ctx.base) {
 940		err = -ENOMEM;
 941		goto fail_platform_mux_register;
 942	}
 943
 944	mlxplat_hotplug->regmap = devm_regmap_init(&mlxplat_dev->dev, NULL,
 945					&mlxplat_mlxcpld_regmap_ctx,
 946					&mlxplat_mlxcpld_regmap_config);
 947	if (IS_ERR(mlxplat_hotplug->regmap)) {
 948		err = PTR_ERR(mlxplat_hotplug->regmap);
 949		goto fail_platform_mux_register;
 950	}
 951
 952	priv->pdev_hotplug = platform_device_register_resndata(
 953				&mlxplat_dev->dev, "mlxreg-hotplug",
 954				PLATFORM_DEVID_NONE,
 955				mlxplat_mlxcpld_resources,
 956				ARRAY_SIZE(mlxplat_mlxcpld_resources),
 957				mlxplat_hotplug, sizeof(*mlxplat_hotplug));
 958	if (IS_ERR(priv->pdev_hotplug)) {
 959		err = PTR_ERR(priv->pdev_hotplug);
 960		goto fail_platform_mux_register;
 961	}
 962
 963	/* Sync registers with hardware. */
 964	regcache_mark_dirty(mlxplat_hotplug->regmap);
 965	err = regcache_sync(mlxplat_hotplug->regmap);
 966	if (err)
 967		goto fail_platform_hotplug_register;
 968
 969	return 0;
 970
 971fail_platform_hotplug_register:
 972	platform_device_unregister(priv->pdev_hotplug);
 973fail_platform_mux_register:
 974	while (--i >= 0)
 975		platform_device_unregister(priv->pdev_mux[i]);
 976	platform_device_unregister(priv->pdev_i2c);
 977fail_alloc:
 978	platform_device_unregister(mlxplat_dev);
 979
 980	return err;
 981}
 982module_init(mlxplat_init);
 983
 984static void __exit mlxplat_exit(void)
 985{
 986	struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev);
 987	int i;
 988
 989	platform_device_unregister(priv->pdev_hotplug);
 990
 991	for (i = ARRAY_SIZE(mlxplat_mux_data) - 1; i >= 0 ; i--)
 992		platform_device_unregister(priv->pdev_mux[i]);
 993
 994	platform_device_unregister(priv->pdev_i2c);
 995	platform_device_unregister(mlxplat_dev);
 996}
 997module_exit(mlxplat_exit);
 998
 999MODULE_AUTHOR("Vadim Pasternak (vadimp@mellanox.com)");
1000MODULE_DESCRIPTION("Mellanox platform driver");
1001MODULE_LICENSE("Dual BSD/GPL");