Re: [patch v1] x86/platform/mellanox: introduce support for Mellanox systems platform

From: H. Peter Anvin
Date: Mon Sep 12 2016 - 00:43:07 EST


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.