On September 11, 2016 11:29:58 PM PDT, vadimp@xxxxxxxxxxxx wrote: >From: Vadim Pasternak <vadimp@xxxxxxxxxxxx> > >Enable system support for the Mellanox Technologies platform, which >provides support for the next Mellanox basic systems: "msx6710", >"msx6720", "msb7700", "msn2700", "msx1410", "msn2410", "msb7800", >"msn2740", "msn2100" and also various number of derivative systems from >the above basic types. > >The Kconfig currently controlling compilation of this code is: >arch/x86/platform:config MLX_PLATFORM >arch/x86/platform: tristate "Mellanox Technologies platform >support" > >Signed-off-by: Vadim Pasternak <vadimp@xxxxxxxxxxxx> >--- > MAINTAINERS | 7 + > arch/x86/Kconfig | 23 ++ > arch/x86/platform/Makefile | 1 + > arch/x86/platform/mellanox/Makefile | 1 + >arch/x86/platform/mellanox/mlx-platform.c | 501 >++++++++++++++++++++++++++++++ > 5 files changed, 533 insertions(+) > create mode 100644 arch/x86/platform/mellanox/Makefile > create mode 100644 arch/x86/platform/mellanox/mlx-platform.c > >diff --git a/MAINTAINERS b/MAINTAINERS >index 4705c94..8a675de 100644 >--- a/MAINTAINERS >+++ b/MAINTAINERS >@@ -7664,6 +7664,13 @@ W: http://www.mellanox.com > Q: http://patchwork.ozlabs.org/project/netdev/list/ > F: drivers/net/ethernet/mellanox/mlxsw/ > >+MELLANOX PLATFORM DRIVER >+M: Vadim Pasternak <vadimp@xxxxxxxxxxxx> >+L: platform-driver-x86@xxxxxxxxxxxxxxx >+S: Supported >+W: http://www.mellanox.com >+F: arch/x86/platform/mellanox/mlx-platform.c >+ > SOFT-ROCE DRIVER (rxe) > M: Moni Shoua <monis@xxxxxxxxxxxx> > L: linux-rdma@xxxxxxxxxxxxxxx >diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig >index c580d8c..cc7efdd9 100644 >--- a/arch/x86/Kconfig >+++ b/arch/x86/Kconfig >@@ -2631,6 +2631,29 @@ config TS5500 > > endif # X86_32 > >+config MLX_PLATFORM >+ tristate "Mellanox Technologies platform support" >+ depends on X86_64 >+ depends on PCI >+ depends on DMI >+ depends on I2C_MLXCPLD >+ depends on I2C_MUX_REG >+ select HWMON >+ select PMBUS >+ select LM75 >+ select NEW_LEDS >+ select LEDS_CLASS >+ select LEDS_TRIGGERS >+ select LEDS_TRIGGER_TIMER >+ select LEDS_MLXCPLD >+ ---help--- >+ This option enables system support for the Mellanox Technologies >+ platform. >+ >+ Say Y here if you are building a kernel for Mellanox system. >+ >+ Otherwise, say N. >+ > config AMD_NB > def_bool y > depends on CPU_SUP_AMD && PCI >diff --git a/arch/x86/platform/Makefile b/arch/x86/platform/Makefile >index 184842e..3c3c19e 100644 >--- a/arch/x86/platform/Makefile >+++ b/arch/x86/platform/Makefile >@@ -8,6 +8,7 @@ obj-y += iris/ > obj-y += intel/ > obj-y += intel-mid/ > obj-y += intel-quark/ >+obj-y += mellanox/ > obj-y += olpc/ > obj-y += scx200/ > obj-y += sfi/ >diff --git a/arch/x86/platform/mellanox/Makefile >b/arch/x86/platform/mellanox/Makefile >new file mode 100644 >index 0000000..f43c931 >--- /dev/null >+++ b/arch/x86/platform/mellanox/Makefile >@@ -0,0 +1 @@ >+obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o >diff --git a/arch/x86/platform/mellanox/mlx-platform.c >b/arch/x86/platform/mellanox/mlx-platform.c >new file mode 100644 >index 0000000..02afa89 >--- /dev/null >+++ b/arch/x86/platform/mellanox/mlx-platform.c >@@ -0,0 +1,501 @@ >+/* >+ * arch/x86/platform/mellanox/mlx-platform.c >+ * Copyright (c) 2016 Mellanox Technologies. All rights reserved. >+ * Copyright (c) 2016 Vadim Pasternak <vadimp@xxxxxxxxxxxx> >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above >copyright >+ * notice, this list of conditions and the following disclaimer in >the >+ * documentation and/or other materials provided with the >distribution. >+ * 3. Neither the names of the copyright holders nor the names of its >+ * contributors may be used to endorse or promote products derived >from >+ * this software without specific prior written permission. >+ * >+ * Alternatively, this software may be distributed under the terms of >the >+ * GNU General Public License ("GPL") version 2 as published by the >Free >+ * Software Foundation. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS >"AS IS" >+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >PURPOSE >+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR >CONTRIBUTORS BE >+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR >+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT >OF >+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR >BUSINESS >+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, >WHETHER IN >+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR >OTHERWISE) >+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED >OF THE >+ * POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include <linux/acpi.h> >+#include <linux/device.h> >+#include <linux/i2c.h> >+#include <linux/i2c-mux.h> >+#include <linux/module.h> >+#include <linux/of_platform.h> >+#include <linux/platform_device.h> >+#include <linux/platform_data/i2c-mux-reg.h> >+#include <linux/pci.h> >+#include <linux/slab.h> >+#include <linux/version.h> >+ >+#define MLX_PLAT_DEVICE_NAME "mlxplat" >+ >+/* LPC IFC in PCH defines */ >+#define MLXPLAT_CPLD_LPC_I2C_BASE_ADRR 0x2000 >+#define MLXPLAT_CPLD_LPC_REG_BASE_ADRR 0x2500 >+#define MLXPLAT_CPLD_LPC_CTRL_IFC_BUS_ID 0 >+#define MLXPLAT_CPLD_LPC_CTRL_IFC_SLOT_ID 31 >+#define MLXPLAT_CPLD_LPC_CTRL_IFC_FUNC_ID 0 >+#define MLXPLAT_CPLD_LPC_QM67_DEV_ID 0x1c4f >+#define MLXPLAT_CPLD_LPC_QM77_DEV_ID 0x1e55 >+#define MLXPLAT_CPLD_LPC_RNG_DEV_ID 0x1f38 >+#define MLXPLAT_CPLD_LPC_I2C_CH1_OFF 0xdb >+#define MLXPLAT_CPLD_LPC_I2C_CH2_OFF 0xda >+#define MLXPLAT_CPLD_LPC_PIO_OFFSET 0x10000UL >+#define MLXPLAT_CPLD_LPC_REG1 ((MLXPLAT_CPLD_LPC_REG_BASE_ADRR + \ >+ MLXPLAT_CPLD_LPC_I2C_CH1_OFF) | \ >+ MLXPLAT_CPLD_LPC_PIO_OFFSET) >+#define MLXPLAT_CPLD_LPC_REG2 ((MLXPLAT_CPLD_LPC_REG_BASE_ADRR + \ >+ MLXPLAT_CPLD_LPC_I2C_CH2_OFF) | \ >+ MLXPLAT_CPLD_LPC_PIO_OFFSET) >+ >+/* Use generic decode range 4 for CPLD LPC */ >+#define MLXPLAT_CPLD_LPC_PCH_GEN_DEC_RANGE4 0x90 >+#define MLXPLAT_CPLD_LPC_PCH_GEN_DEC_BASE 0x84 >+#define MLXPLAT_CPLD_LPC_RNG_CTRL 0x84 >+#define MLXPLAT_CPLD_LPC_PCH_GEN_DEC_RANGES 4 >+#define MLXPLAT_CPLD_LPC_I2C_RANGE 2 >+#define MLXPLAT_CPLD_LPC_RANGE 3 >+#define MLXPLAT_CPLD_LPC_CLKS_EN 0 >+#define MLXPLAT_CPLD_LPC_IO_RANGE 0x100 >+ >+/* Start channel numbers */ >+#define MLXPLAT_CPLD_CH1 2 >+#define MLXPLAT_CPLD_CH2 10 >+ >+/* mlxplat_priv - board private data >+ * @lpc_reg - register space >+ * @dev_id - platform device id >+ * @lpc_i2c_res- lpc cpld i2c resource space >+ * @lpc_cpld_res - lpc cpld register resource space >+ * @pdev - platform device >+ */ >+struct mlxplat_priv { >+ u32 lpc_reg[MLXPLAT_CPLD_LPC_PCH_GEN_DEC_RANGES]; >+ u16 dev_id; >+ struct resource *lpc_i2c_res; >+ struct resource *lpc_cpld_res; >+ struct platform_device *pdev; >+ struct platform_device *pdev_i2c; >+}; >+ >+/* Regions for LPC I2C controller and LPC base register space */ >+static struct resource mlxplat_lpc_resources[] = { >+ [0] = DEFINE_RES_NAMED(MLXPLAT_CPLD_LPC_I2C_BASE_ADRR, >+ MLXPLAT_CPLD_LPC_IO_RANGE, >+ "mlxplat_cpld_lpc_i2c_ctrl", IORESOURCE_IO), >+ [1] = DEFINE_RES_NAMED(MLXPLAT_CPLD_LPC_REG_BASE_ADRR, >+ MLXPLAT_CPLD_LPC_IO_RANGE, >+ "mlxplat_cpld_lpc_regs", >+ IORESOURCE_IO), >+}; >+ >+/* Platfform channels */ >+static int mlxplat_channels[][8] = { >+ { >+ MLXPLAT_CPLD_CH1, MLXPLAT_CPLD_CH1 + 1, MLXPLAT_CPLD_CH1 + 2, >+ MLXPLAT_CPLD_CH1 + 3, MLXPLAT_CPLD_CH1 + 4, MLXPLAT_CPLD_CH1 + >+ 5, MLXPLAT_CPLD_CH1 + 6, MLXPLAT_CPLD_CH1 + 7 >+ }, >+ { >+ MLXPLAT_CPLD_CH2, MLXPLAT_CPLD_CH2 + 1, MLXPLAT_CPLD_CH2 + 2, >+ MLXPLAT_CPLD_CH2 + 3, MLXPLAT_CPLD_CH2 + 4, MLXPLAT_CPLD_CH2 + >+ + 5, MLXPLAT_CPLD_CH2 + 6, MLXPLAT_CPLD_CH2 + 7 >+ }, >+}; >+ >+/* Platform mux data */ >+struct i2c_mux_reg_platform_data mlxplat_mux_data[] = { >+ { >+ .parent = 1, >+ .base_nr = MLXPLAT_CPLD_CH1, >+ .write_only = 1, >+ .values = mlxplat_channels[0], >+ .n_values = ARRAY_SIZE(mlxplat_channels[0]), >+ .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG1, >+ .reg_size = 1, >+ .idle_in_use = 1, >+ }, >+ { >+ .parent = 1, >+ .base_nr = MLXPLAT_CPLD_CH2, >+ .write_only = 1, >+ .values = mlxplat_channels[1], >+ .n_values = ARRAY_SIZE(mlxplat_channels[1]), >+ .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG2, >+ .reg_size = 1, >+ .idle_in_use = 1, >+ }, >+ >+}; >+ >+/* mlxplat_topology - platform entry data: >+ * @pdev - platform device >+ * @name - platform device name >+ */ >+struct mlxplat_topology { >+ struct platform_device *pdev; >+ const char *name; >+}; >+ >+/* Platform topology */ >+struct mlxplat_topology mlxplat_topo[] = { >+ { >+ .name = "i2c-mux-reg", >+ }, >+ { >+ .name = "i2c-mux-reg", >+ }, >+}; >+ >+struct platform_device *mlxplat_dev; >+ >+static int mlxplat_lpc_i2c_dec_range_config(struct mlxplat_priv *priv, >+ struct pci_dev *pdev, u8 range, >+ u16 base_addr) >+{ >+ u16 rng_reg; >+ u32 val; >+ int err; >+ >+ if (range >= MLXPLAT_CPLD_LPC_PCH_GEN_DEC_RANGES) { >+ dev_err(&priv->pdev->dev, "Incorrect LPC decode range %d > %d\n", >+ range, MLXPLAT_CPLD_LPC_PCH_GEN_DEC_RANGES); >+ return -ERANGE; >+ } >+ >+ rng_reg = MLXPLAT_CPLD_LPC_PCH_GEN_DEC_BASE + 4 * range; >+ err = pci_read_config_dword(pdev, rng_reg, &val); >+ if (err) { >+ dev_err(&priv->pdev->dev, "Access to LPC_PCH config failed, err >%d\n", >+ err); >+ return -EFAULT; >+ } >+ priv->lpc_reg[range] = val; >+ >+ /* Clean all bits excepted reserved (reserved: 2, 16, 17 , 24 - 31). >*/ >+ val &= 0xff030002; >+ /* Set bits 18 - 23 to allow decode range address mask, set bit 1 to >+ * enable decode range, clear bit 1,2 in base address. >+ */ >+ val |= 0xfc0001 | (base_addr & 0xfff3); >+ err = pci_write_config_dword(pdev, rng_reg, val); >+ if (err) >+ dev_err(&priv->pdev->dev, "Config of LPC_PCH Generic Decode Range %d >failed, err %d\n", >+ range, err); >+ >+ return err; >+} >+ >+static void mlxplat_lpc_dec_rng_config_clean(struct pci_dev *pdev, u32 >val, >+ u8 range) >+{ >+ /* Restore old value */ >+ if (pci_write_config_dword(pdev, (MLXPLAT_CPLD_LPC_PCH_GEN_DEC_BASE + >+ range * 4), val)) >+ dev_err(&pdev->dev, "Deconfig of LPC_PCH Generic Decode Range %x >failed\n", >+ range); >+ >+} >+ >+static int mlxplat_lpc_request_region(struct mlxplat_priv *priv, >+ struct resource *res) >+{ >+ resource_size_t size = resource_size(res); >+ >+ if (!devm_request_region(&priv->pdev->dev, res->start, size, >+ res->name)) { >+ devm_release_region(&priv->pdev->dev, res->start, size); >+ >+ if (!devm_request_region(&priv->pdev->dev, res->start, size, >+ res->name)) { >+ dev_err(&priv->pdev->dev, "Request ioregion 0x%llx len 0x%llx for >%s fail\n", >+ res->start, size, res->name); >+ return -EIO; >+ } >+ } >+ >+ return 0; >+} >+ >+static int mlxplat_lpc_request_regions(struct mlxplat_priv *priv) >+{ >+ int i; >+ int err; >+ >+ for (i = 0; i < ARRAY_SIZE(mlxplat_lpc_resources); i++) { >+ err = mlxplat_lpc_request_region(priv, >+ &mlxplat_lpc_resources[i]); >+ if (err) >+ return err; >+ } >+ >+ priv->lpc_i2c_res = &mlxplat_lpc_resources[0]; >+ priv->lpc_cpld_res = &mlxplat_lpc_resources[1]; >+ >+ return 0; >+} >+ >+static int mlxplat_lpc_ivb_config(struct mlxplat_priv *priv, >+ struct pci_dev *pdev) >+{ >+ int err; >+ >+ err = mlxplat_lpc_i2c_dec_range_config(priv, pdev, >+ MLXPLAT_CPLD_LPC_I2C_RANGE, >+ MLXPLAT_CPLD_LPC_I2C_BASE_ADRR); >+ if (err) { >+ dev_err(&priv->pdev->dev, "LPC decode range %d config failed, err >%d\n", >+ MLXPLAT_CPLD_LPC_I2C_RANGE, err); >+ pci_dev_put(pdev); >+ return -EFAULT; >+ } >+ >+ err = mlxplat_lpc_i2c_dec_range_config(priv, pdev, >+ MLXPLAT_CPLD_LPC_RANGE, >+ MLXPLAT_CPLD_LPC_REG_BASE_ADRR); >+ if (err) { >+ dev_err(&priv->pdev->dev, "LPC decode range %d config failed, err >%d\n", >+ MLXPLAT_CPLD_LPC_I2C_RANGE, err); >+ return -EFAULT; >+ } >+ >+ return err; >+} >+ >+static void mlxplat_lpc_ivb_config_clean(struct mlxplat_priv *priv, >+ struct pci_dev *pdev) >+{ >+ mlxplat_lpc_dec_rng_config_clean(pdev, >+ priv->lpc_reg[MLXPLAT_CPLD_LPC_RANGE], >+ MLXPLAT_CPLD_LPC_RANGE); >+ mlxplat_lpc_dec_rng_config_clean(pdev, >+ priv->lpc_reg[MLXPLAT_CPLD_LPC_I2C_RANGE], >+ MLXPLAT_CPLD_LPC_I2C_RANGE); >+ >+} >+ >+static int mlxplat_lpc_range_config(struct mlxplat_priv *priv, >+ struct pci_dev *pdev) >+{ >+ u32 val, lpc_clks; >+ int err; >+ >+ err = pci_read_config_dword(pdev, MLXPLAT_CPLD_LPC_RNG_CTRL, &val); >+ if (err) { >+ dev_err(&priv->pdev->dev, "Access to LPC Ctrl reg failed, err %d\n", >+ err); >+ return -EFAULT; >+ } >+ lpc_clks = val & 0x3; >+ if (lpc_clks != MLXPLAT_CPLD_LPC_CLKS_EN) { >+ val &= 0xFFFFFFFC; >+ err = pci_write_config_dword(pdev, MLXPLAT_CPLD_LPC_RNG_CTRL, >+ val); >+ if (err) { >+ dev_err(&priv->pdev->dev, "Config LPC CLKS CTRL failed, err %d\n", >+ err); >+ return -EFAULT; >+ } >+ } >+ >+ return err; >+} >+ >+static int mlxplat_lpc_config(struct mlxplat_priv *priv) >+{ >+ struct pci_dev *pdev = NULL; >+ u16 dev_id; >+ int err; >+ >+ pdev = pci_get_bus_and_slot(MLXPLAT_CPLD_LPC_CTRL_IFC_BUS_ID, >+ PCI_DEVFN(MLXPLAT_CPLD_LPC_CTRL_IFC_SLOT_ID, >+ MLXPLAT_CPLD_LPC_CTRL_IFC_FUNC_ID)); >+ >+ if (!pdev) { >+ dev_err(&priv->pdev->dev, "LPC controller bus:%d slot:%d func:%d not >found\n", >+ MLXPLAT_CPLD_LPC_CTRL_IFC_BUS_ID, >+ MLXPLAT_CPLD_LPC_CTRL_IFC_SLOT_ID, >+ MLXPLAT_CPLD_LPC_CTRL_IFC_FUNC_ID); >+ return -EFAULT; >+ } >+ >+ err = pci_read_config_word(pdev, 2, &dev_id); >+ if (err) { >+ dev_err(&priv->pdev->dev, "Access PCIe LPC interface failed, err >%d\n", >+ err); >+ goto failure; >+ } >+ >+ switch (dev_id) { >+ case MLXPLAT_CPLD_LPC_QM67_DEV_ID: >+ case MLXPLAT_CPLD_LPC_QM77_DEV_ID: >+ err = mlxplat_lpc_ivb_config(priv, pdev); >+ break; >+ case MLXPLAT_CPLD_LPC_RNG_DEV_ID: >+ err = mlxplat_lpc_range_config(priv, pdev); >+ break; >+ default: >+ err = -ENXIO; >+ dev_err(&priv->pdev->dev, "Unsupported DevId 0x%x bus:%d slot:%d >func:%d\n", >+ dev_id, MLXPLAT_CPLD_LPC_CTRL_IFC_BUS_ID, >+ MLXPLAT_CPLD_LPC_CTRL_IFC_SLOT_ID, >+ MLXPLAT_CPLD_LPC_CTRL_IFC_FUNC_ID); >+ goto failure; >+ } >+ priv->dev_id = dev_id; >+ >+failure: >+ pci_dev_put(pdev); >+ return err; >+} >+ >+static int mlxplat_lpc_config_clean(struct mlxplat_priv *priv) >+{ >+ struct pci_dev *pdev = NULL; >+ int err = 0; >+ >+ pdev = pci_get_bus_and_slot(MLXPLAT_CPLD_LPC_CTRL_IFC_BUS_ID, >+ PCI_DEVFN(MLXPLAT_CPLD_LPC_CTRL_IFC_SLOT_ID, >+ MLXPLAT_CPLD_LPC_CTRL_IFC_FUNC_ID)); >+ if (!pdev) { >+ dev_err(&priv->pdev->dev, "LPC controller bus:%d slot:%d func:%d not >found\n", >+ MLXPLAT_CPLD_LPC_CTRL_IFC_BUS_ID, >+ MLXPLAT_CPLD_LPC_CTRL_IFC_SLOT_ID, >+ MLXPLAT_CPLD_LPC_CTRL_IFC_FUNC_ID); >+ return -EFAULT; >+ } >+ >+ switch (priv->dev_id) { >+ case MLXPLAT_CPLD_LPC_QM67_DEV_ID: >+ case MLXPLAT_CPLD_LPC_QM77_DEV_ID: >+ mlxplat_lpc_ivb_config_clean(priv, pdev); >+ break; >+ case MLXPLAT_CPLD_LPC_RNG_DEV_ID: >+ break; >+ default: >+ err = -ENXIO; >+ dev_err(&priv->pdev->dev, "Unsupported DevId 0x%x bus:%d slot:%d >func:%d\n", >+ priv->dev_id, MLXPLAT_CPLD_LPC_CTRL_IFC_BUS_ID, >+ MLXPLAT_CPLD_LPC_CTRL_IFC_SLOT_ID, >+ MLXPLAT_CPLD_LPC_CTRL_IFC_FUNC_ID); >+ break; >+ } >+ >+ pci_dev_put(pdev); >+ >+ return err; >+} >+ >+static int __init mlxplat_init(void) >+{ >+ struct mlxplat_priv *priv; >+ struct device *dev; >+ int i, j; >+ int err; >+ >+ mlxplat_dev = platform_device_alloc(MLX_PLAT_DEVICE_NAME, -1); >+ if (!mlxplat_dev) { >+ pr_err("Alloc %s platform device failed\n", >+ MLX_PLAT_DEVICE_NAME); >+ return -ENOMEM; >+ } >+ >+ err = platform_device_add(mlxplat_dev); >+ if (err) { >+ pr_err("Add %s platform device failed (%d)\n", >+ MLX_PLAT_DEVICE_NAME, err); >+ goto fail_platform_device_add; >+ } >+ dev = &mlxplat_dev->dev; >+ >+ priv = devm_kzalloc(dev, sizeof(struct mlxplat_priv), GFP_KERNEL); >+ if (!priv) { >+ err = -ENOMEM; >+ dev_err(dev, "Failed to allocate mlxplat_priv\n"); >+ goto fail_alloc; >+ } >+ platform_set_drvdata(mlxplat_dev, priv); >+ priv->pdev = mlxplat_dev; >+ >+ err = mlxplat_lpc_config(priv); >+ if (err) { >+ dev_err(dev, "Failed to configure LPC interface\n"); >+ goto fail_alloc; >+ } >+ >+ err = mlxplat_lpc_request_regions(priv); >+ if (err) { >+ dev_err(dev, "Request ioregion failed (%d)\n", err); >+ goto fail_alloc; >+ } >+ >+ priv->pdev_i2c = platform_device_register_simple("i2c_mlxcpld", -1, >+ NULL, 0); >+ if (IS_ERR(priv->pdev_i2c)) { >+ err = PTR_ERR(priv->pdev_i2c); >+ goto fail_alloc; >+ }; >+ >+ for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) { >+ mlxplat_topo[i].pdev = platform_device_register_resndata(dev, >+ mlxplat_topo[i].name, i, NULL, >+ 0, &mlxplat_mux_data[i], >+ sizeof(mlxplat_mux_data[i])); >+ if (IS_ERR(mlxplat_topo[i].pdev)) { >+ err = PTR_ERR(mlxplat_topo[i].pdev); >+ goto fail_platform_mux_register; >+ } >+ } >+ >+ return err; >+ >+fail_platform_mux_register: >+ for (j = i; j > 0 ; j--) >+ platform_device_unregister(mlxplat_topo[j].pdev); >+ platform_device_unregister(priv->pdev_i2c); >+fail_alloc: >+ platform_device_del(mlxplat_dev); >+fail_platform_device_add: >+ platform_device_put(mlxplat_dev); >+ >+ return err; >+} >+ >+static void __exit mlxplat_exit(void) >+{ >+ int i; >+ struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev); >+ >+ for (i = ARRAY_SIZE(mlxplat_mux_data) - 1; i >= 0 ; i--) >+ platform_device_unregister(mlxplat_topo[i].pdev); >+ >+ platform_device_unregister(priv->pdev_i2c); >+ mlxplat_lpc_config_clean(priv); >+ platform_device_del(mlxplat_dev); >+ platform_device_put(mlxplat_dev); >+} >+ >+module_init(mlxplat_init); >+module_exit(mlxplat_exit); >+ >+MODULE_AUTHOR("Vadim Pasternak (vadimp@xxxxxxxxxxxx)"); >+MODULE_DESCRIPTION("Mellanox platform driver"); >+MODULE_LICENSE("GPL v2"); >+MODULE_ALIAS("platform:mlx-platform"); This is a horrifically uninformative piece of code. What does this driver do? Why is it needed? What makes it a "platform" in the sense of the way the term is used in the Linux kernel? How does it affect the overall system, and in what sense does it diverge from a standard x86? -- Sent from my Android device with K-9 Mail. Please excuse brevity and formatting. -- To unsubscribe from this list: send the line "unsubscribe platform-driver-x86" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html