mlx-platform.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  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. #include <linux/device.h>
  34. #include <linux/dmi.h>
  35. #include <linux/i2c.h>
  36. #include <linux/i2c-mux.h>
  37. #include <linux/io.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/mlxreg.h>
  42. #include <linux/regmap.h>
  43. #define MLX_PLAT_DEVICE_NAME "mlxplat"
  44. /* LPC bus IO offsets */
  45. #define MLXPLAT_CPLD_LPC_I2C_BASE_ADRR 0x2000
  46. #define MLXPLAT_CPLD_LPC_REG_BASE_ADRR 0x2500
  47. #define MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET 0x3a
  48. #define MLXPLAT_CPLD_LPC_REG_PSU_OFFSET 0x58
  49. #define MLXPLAT_CPLD_LPC_REG_PWR_OFFSET 0x64
  50. #define MLXPLAT_CPLD_LPC_REG_FAN_OFFSET 0x88
  51. #define MLXPLAT_CPLD_LPC_IO_RANGE 0x100
  52. #define MLXPLAT_CPLD_LPC_I2C_CH1_OFF 0xdb
  53. #define MLXPLAT_CPLD_LPC_I2C_CH2_OFF 0xda
  54. #define MLXPLAT_CPLD_LPC_PIO_OFFSET 0x10000UL
  55. #define MLXPLAT_CPLD_LPC_REG1 ((MLXPLAT_CPLD_LPC_REG_BASE_ADRR + \
  56. MLXPLAT_CPLD_LPC_I2C_CH1_OFF) | \
  57. MLXPLAT_CPLD_LPC_PIO_OFFSET)
  58. #define MLXPLAT_CPLD_LPC_REG2 ((MLXPLAT_CPLD_LPC_REG_BASE_ADRR + \
  59. MLXPLAT_CPLD_LPC_I2C_CH2_OFF) | \
  60. MLXPLAT_CPLD_LPC_PIO_OFFSET)
  61. /* Masks for aggregation, psu, pwr and fan event in CPLD related registers. */
  62. #define MLXPLAT_CPLD_AGGR_PSU_MASK_DEF 0x08
  63. #define MLXPLAT_CPLD_AGGR_PWR_MASK_DEF 0x08
  64. #define MLXPLAT_CPLD_AGGR_FAN_MASK_DEF 0x40
  65. #define MLXPLAT_CPLD_AGGR_MASK_DEF (MLXPLAT_CPLD_AGGR_PSU_MASK_DEF | \
  66. MLXPLAT_CPLD_AGGR_FAN_MASK_DEF)
  67. #define MLXPLAT_CPLD_AGGR_MASK_MSN21XX 0x04
  68. #define MLXPLAT_CPLD_PSU_MASK GENMASK(1, 0)
  69. #define MLXPLAT_CPLD_PWR_MASK GENMASK(1, 0)
  70. #define MLXPLAT_CPLD_FAN_MASK GENMASK(3, 0)
  71. /* Start channel numbers */
  72. #define MLXPLAT_CPLD_CH1 2
  73. #define MLXPLAT_CPLD_CH2 10
  74. /* Number of LPC attached MUX platform devices */
  75. #define MLXPLAT_CPLD_LPC_MUX_DEVS 2
  76. /* mlxplat_priv - platform private data
  77. * @pdev_i2c - i2c controller platform device
  78. * @pdev_mux - array of mux platform devices
  79. */
  80. struct mlxplat_priv {
  81. struct platform_device *pdev_i2c;
  82. struct platform_device *pdev_mux[MLXPLAT_CPLD_LPC_MUX_DEVS];
  83. struct platform_device *pdev_hotplug;
  84. };
  85. /* Regions for LPC I2C controller and LPC base register space */
  86. static const struct resource mlxplat_lpc_resources[] = {
  87. [0] = DEFINE_RES_NAMED(MLXPLAT_CPLD_LPC_I2C_BASE_ADRR,
  88. MLXPLAT_CPLD_LPC_IO_RANGE,
  89. "mlxplat_cpld_lpc_i2c_ctrl", IORESOURCE_IO),
  90. [1] = DEFINE_RES_NAMED(MLXPLAT_CPLD_LPC_REG_BASE_ADRR,
  91. MLXPLAT_CPLD_LPC_IO_RANGE,
  92. "mlxplat_cpld_lpc_regs",
  93. IORESOURCE_IO),
  94. };
  95. /* Platform default channels */
  96. static const int mlxplat_default_channels[][8] = {
  97. {
  98. MLXPLAT_CPLD_CH1, MLXPLAT_CPLD_CH1 + 1, MLXPLAT_CPLD_CH1 + 2,
  99. MLXPLAT_CPLD_CH1 + 3, MLXPLAT_CPLD_CH1 + 4, MLXPLAT_CPLD_CH1 +
  100. 5, MLXPLAT_CPLD_CH1 + 6, MLXPLAT_CPLD_CH1 + 7
  101. },
  102. {
  103. MLXPLAT_CPLD_CH2, MLXPLAT_CPLD_CH2 + 1, MLXPLAT_CPLD_CH2 + 2,
  104. MLXPLAT_CPLD_CH2 + 3, MLXPLAT_CPLD_CH2 + 4, MLXPLAT_CPLD_CH2 +
  105. 5, MLXPLAT_CPLD_CH2 + 6, MLXPLAT_CPLD_CH2 + 7
  106. },
  107. };
  108. /* Platform channels for MSN21xx system family */
  109. static const int mlxplat_msn21xx_channels[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
  110. /* Platform mux data */
  111. static struct i2c_mux_reg_platform_data mlxplat_mux_data[] = {
  112. {
  113. .parent = 1,
  114. .base_nr = MLXPLAT_CPLD_CH1,
  115. .write_only = 1,
  116. .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG1,
  117. .reg_size = 1,
  118. .idle_in_use = 1,
  119. },
  120. {
  121. .parent = 1,
  122. .base_nr = MLXPLAT_CPLD_CH2,
  123. .write_only = 1,
  124. .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG2,
  125. .reg_size = 1,
  126. .idle_in_use = 1,
  127. },
  128. };
  129. /* Platform hotplug devices */
  130. static struct i2c_board_info mlxplat_mlxcpld_psu[] = {
  131. {
  132. I2C_BOARD_INFO("24c02", 0x51),
  133. },
  134. {
  135. I2C_BOARD_INFO("24c02", 0x50),
  136. },
  137. };
  138. static struct i2c_board_info mlxplat_mlxcpld_pwr[] = {
  139. {
  140. I2C_BOARD_INFO("dps460", 0x59),
  141. },
  142. {
  143. I2C_BOARD_INFO("dps460", 0x58),
  144. },
  145. };
  146. static struct i2c_board_info mlxplat_mlxcpld_fan[] = {
  147. {
  148. I2C_BOARD_INFO("24c32", 0x50),
  149. },
  150. {
  151. I2C_BOARD_INFO("24c32", 0x50),
  152. },
  153. {
  154. I2C_BOARD_INFO("24c32", 0x50),
  155. },
  156. {
  157. I2C_BOARD_INFO("24c32", 0x50),
  158. },
  159. };
  160. /* Platform hotplug default data */
  161. static struct mlxreg_core_data mlxplat_mlxcpld_default_psu_items_data[] = {
  162. {
  163. .label = "psu1",
  164. .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
  165. .mask = BIT(0),
  166. .hpdev.brdinfo = &mlxplat_mlxcpld_psu[0],
  167. .hpdev.nr = 10,
  168. },
  169. {
  170. .label = "psu2",
  171. .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
  172. .mask = BIT(1),
  173. .hpdev.brdinfo = &mlxplat_mlxcpld_psu[1],
  174. .hpdev.nr = 10,
  175. },
  176. };
  177. static struct mlxreg_core_data mlxplat_mlxcpld_default_pwr_items_data[] = {
  178. {
  179. .label = "pwr1",
  180. .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
  181. .mask = BIT(0),
  182. .hpdev.brdinfo = &mlxplat_mlxcpld_pwr[0],
  183. .hpdev.nr = 10,
  184. },
  185. {
  186. .label = "pwr2",
  187. .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
  188. .mask = BIT(1),
  189. .hpdev.brdinfo = &mlxplat_mlxcpld_pwr[1],
  190. .hpdev.nr = 10,
  191. },
  192. };
  193. static struct mlxreg_core_data mlxplat_mlxcpld_default_fan_items_data[] = {
  194. {
  195. .label = "fan1",
  196. .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
  197. .mask = BIT(0),
  198. .hpdev.brdinfo = &mlxplat_mlxcpld_fan[0],
  199. .hpdev.nr = 11,
  200. },
  201. {
  202. .label = "fan2",
  203. .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
  204. .mask = BIT(1),
  205. .hpdev.brdinfo = &mlxplat_mlxcpld_fan[1],
  206. .hpdev.nr = 12,
  207. },
  208. {
  209. .label = "fan3",
  210. .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
  211. .mask = BIT(2),
  212. .hpdev.brdinfo = &mlxplat_mlxcpld_fan[2],
  213. .hpdev.nr = 13,
  214. },
  215. {
  216. .label = "fan4",
  217. .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
  218. .mask = BIT(3),
  219. .hpdev.brdinfo = &mlxplat_mlxcpld_fan[3],
  220. .hpdev.nr = 14,
  221. },
  222. };
  223. static struct mlxreg_core_item mlxplat_mlxcpld_default_items[] = {
  224. {
  225. .data = mlxplat_mlxcpld_default_psu_items_data,
  226. .aggr_mask = MLXPLAT_CPLD_AGGR_PSU_MASK_DEF,
  227. .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
  228. .mask = MLXPLAT_CPLD_PSU_MASK,
  229. .count = ARRAY_SIZE(mlxplat_mlxcpld_psu),
  230. .inversed = 1,
  231. .health = false,
  232. },
  233. {
  234. .data = mlxplat_mlxcpld_default_pwr_items_data,
  235. .aggr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF,
  236. .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
  237. .mask = MLXPLAT_CPLD_PWR_MASK,
  238. .count = ARRAY_SIZE(mlxplat_mlxcpld_pwr),
  239. .inversed = 0,
  240. .health = false,
  241. },
  242. {
  243. .data = mlxplat_mlxcpld_default_fan_items_data,
  244. .aggr_mask = MLXPLAT_CPLD_AGGR_FAN_MASK_DEF,
  245. .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
  246. .mask = MLXPLAT_CPLD_FAN_MASK,
  247. .count = ARRAY_SIZE(mlxplat_mlxcpld_fan),
  248. .inversed = 1,
  249. .health = false,
  250. },
  251. };
  252. static
  253. struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_default_data = {
  254. .items = mlxplat_mlxcpld_default_items,
  255. .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_items),
  256. .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
  257. .mask = MLXPLAT_CPLD_AGGR_MASK_DEF,
  258. };
  259. /* Platform hotplug MSN21xx system family data */
  260. static struct mlxreg_core_item mlxplat_mlxcpld_msn21xx_items[] = {
  261. {
  262. .data = mlxplat_mlxcpld_default_pwr_items_data,
  263. .aggr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF,
  264. .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
  265. .mask = MLXPLAT_CPLD_PWR_MASK,
  266. .count = ARRAY_SIZE(mlxplat_mlxcpld_pwr),
  267. .inversed = 0,
  268. .health = false,
  269. },
  270. };
  271. static
  272. struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_msn21xx_data = {
  273. .items = mlxplat_mlxcpld_msn21xx_items,
  274. .counter = ARRAY_SIZE(mlxplat_mlxcpld_msn21xx_items),
  275. .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
  276. .mask = MLXPLAT_CPLD_AGGR_MASK_DEF,
  277. };
  278. struct mlxplat_mlxcpld_regmap_context {
  279. void __iomem *base;
  280. };
  281. static struct mlxplat_mlxcpld_regmap_context mlxplat_mlxcpld_regmap_ctx;
  282. static int
  283. mlxplat_mlxcpld_reg_read(void *context, unsigned int reg, unsigned int *val)
  284. {
  285. struct mlxplat_mlxcpld_regmap_context *ctx = context;
  286. *val = ioread8(ctx->base + reg);
  287. return 0;
  288. }
  289. static int
  290. mlxplat_mlxcpld_reg_write(void *context, unsigned int reg, unsigned int val)
  291. {
  292. struct mlxplat_mlxcpld_regmap_context *ctx = context;
  293. iowrite8(val, ctx->base + reg);
  294. return 0;
  295. }
  296. static const struct regmap_config mlxplat_mlxcpld_regmap_config = {
  297. .reg_bits = 8,
  298. .val_bits = 8,
  299. .max_register = 255,
  300. .reg_read = mlxplat_mlxcpld_reg_read,
  301. .reg_write = mlxplat_mlxcpld_reg_write,
  302. };
  303. static struct resource mlxplat_mlxcpld_resources[] = {
  304. [0] = DEFINE_RES_IRQ_NAMED(17, "mlxreg-hotplug"),
  305. };
  306. static struct platform_device *mlxplat_dev;
  307. static struct mlxreg_core_hotplug_platform_data *mlxplat_hotplug;
  308. static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
  309. {
  310. int i;
  311. for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
  312. mlxplat_mux_data[i].values = mlxplat_default_channels[i];
  313. mlxplat_mux_data[i].n_values =
  314. ARRAY_SIZE(mlxplat_default_channels[i]);
  315. }
  316. mlxplat_hotplug = &mlxplat_mlxcpld_default_data;
  317. return 1;
  318. };
  319. static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi)
  320. {
  321. int i;
  322. for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
  323. mlxplat_mux_data[i].values = mlxplat_msn21xx_channels;
  324. mlxplat_mux_data[i].n_values =
  325. ARRAY_SIZE(mlxplat_msn21xx_channels);
  326. }
  327. mlxplat_hotplug = &mlxplat_mlxcpld_msn21xx_data;
  328. return 1;
  329. };
  330. static const struct dmi_system_id mlxplat_dmi_table[] __initconst = {
  331. {
  332. .callback = mlxplat_dmi_default_matched,
  333. .matches = {
  334. DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
  335. DMI_MATCH(DMI_PRODUCT_NAME, "MSN24"),
  336. },
  337. },
  338. {
  339. .callback = mlxplat_dmi_default_matched,
  340. .matches = {
  341. DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
  342. DMI_MATCH(DMI_PRODUCT_NAME, "MSN27"),
  343. },
  344. },
  345. {
  346. .callback = mlxplat_dmi_default_matched,
  347. .matches = {
  348. DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
  349. DMI_MATCH(DMI_PRODUCT_NAME, "MSB"),
  350. },
  351. },
  352. {
  353. .callback = mlxplat_dmi_default_matched,
  354. .matches = {
  355. DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
  356. DMI_MATCH(DMI_PRODUCT_NAME, "MSX"),
  357. },
  358. },
  359. {
  360. .callback = mlxplat_dmi_msn21xx_matched,
  361. .matches = {
  362. DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
  363. DMI_MATCH(DMI_PRODUCT_NAME, "MSN21"),
  364. },
  365. },
  366. { }
  367. };
  368. static int __init mlxplat_init(void)
  369. {
  370. struct mlxplat_priv *priv;
  371. int i, err;
  372. if (!dmi_check_system(mlxplat_dmi_table))
  373. return -ENODEV;
  374. mlxplat_dev = platform_device_register_simple(MLX_PLAT_DEVICE_NAME, -1,
  375. mlxplat_lpc_resources,
  376. ARRAY_SIZE(mlxplat_lpc_resources));
  377. if (IS_ERR(mlxplat_dev))
  378. return PTR_ERR(mlxplat_dev);
  379. priv = devm_kzalloc(&mlxplat_dev->dev, sizeof(struct mlxplat_priv),
  380. GFP_KERNEL);
  381. if (!priv) {
  382. err = -ENOMEM;
  383. goto fail_alloc;
  384. }
  385. platform_set_drvdata(mlxplat_dev, priv);
  386. priv->pdev_i2c = platform_device_register_simple("i2c_mlxcpld", -1,
  387. NULL, 0);
  388. if (IS_ERR(priv->pdev_i2c)) {
  389. err = PTR_ERR(priv->pdev_i2c);
  390. goto fail_alloc;
  391. }
  392. for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
  393. priv->pdev_mux[i] = platform_device_register_resndata(
  394. &mlxplat_dev->dev,
  395. "i2c-mux-reg", i, NULL,
  396. 0, &mlxplat_mux_data[i],
  397. sizeof(mlxplat_mux_data[i]));
  398. if (IS_ERR(priv->pdev_mux[i])) {
  399. err = PTR_ERR(priv->pdev_mux[i]);
  400. goto fail_platform_mux_register;
  401. }
  402. }
  403. mlxplat_mlxcpld_regmap_ctx.base = devm_ioport_map(&mlxplat_dev->dev,
  404. mlxplat_lpc_resources[1].start, 1);
  405. if (IS_ERR(mlxplat_mlxcpld_regmap_ctx.base)) {
  406. err = PTR_ERR(mlxplat_mlxcpld_regmap_ctx.base);
  407. goto fail_platform_mux_register;
  408. }
  409. mlxplat_hotplug->regmap = devm_regmap_init(&mlxplat_dev->dev, NULL,
  410. &mlxplat_mlxcpld_regmap_ctx,
  411. &mlxplat_mlxcpld_regmap_config);
  412. if (IS_ERR(mlxplat_hotplug->regmap)) {
  413. err = PTR_ERR(mlxplat_hotplug->regmap);
  414. goto fail_platform_mux_register;
  415. }
  416. priv->pdev_hotplug = platform_device_register_resndata(
  417. &mlxplat_dev->dev, "mlxreg-hotplug",
  418. PLATFORM_DEVID_NONE,
  419. mlxplat_mlxcpld_resources,
  420. ARRAY_SIZE(mlxplat_mlxcpld_resources),
  421. mlxplat_hotplug, sizeof(*mlxplat_hotplug));
  422. if (IS_ERR(priv->pdev_hotplug)) {
  423. err = PTR_ERR(priv->pdev_hotplug);
  424. goto fail_platform_mux_register;
  425. }
  426. return 0;
  427. fail_platform_mux_register:
  428. while (--i >= 0)
  429. platform_device_unregister(priv->pdev_mux[i]);
  430. platform_device_unregister(priv->pdev_i2c);
  431. fail_alloc:
  432. platform_device_unregister(mlxplat_dev);
  433. return err;
  434. }
  435. module_init(mlxplat_init);
  436. static void __exit mlxplat_exit(void)
  437. {
  438. struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev);
  439. int i;
  440. platform_device_unregister(priv->pdev_hotplug);
  441. for (i = ARRAY_SIZE(mlxplat_mux_data) - 1; i >= 0 ; i--)
  442. platform_device_unregister(priv->pdev_mux[i]);
  443. platform_device_unregister(priv->pdev_i2c);
  444. platform_device_unregister(mlxplat_dev);
  445. }
  446. module_exit(mlxplat_exit);
  447. MODULE_AUTHOR("Vadim Pasternak (vadimp@mellanox.com)");
  448. MODULE_DESCRIPTION("Mellanox platform driver");
  449. MODULE_LICENSE("Dual BSD/GPL");
  450. MODULE_ALIAS("dmi:*:*Mellanox*:MSN24*:");
  451. MODULE_ALIAS("dmi:*:*Mellanox*:MSN27*:");
  452. MODULE_ALIAS("dmi:*:*Mellanox*:MSB*:");
  453. MODULE_ALIAS("dmi:*:*Mellanox*:MSX*:");
  454. MODULE_ALIAS("dmi:*:*Mellanox*:MSN21*:");