From e7e230faf965ca024b4c09d80132cc2bbe748419 Mon Sep 17 00:00:00 2001 From: MellanoxBSP <32340777+MellanoxBSP@users.noreply.github.com> Date: Fri, 1 Dec 2017 22:34:29 +0200 Subject: [PATCH] Mellanox system drivers backport to kernel 4.9 (#1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Mellanox system drivers backport to kernel 4.9 This series of patches introduces support for Mellanox systems. Most of the patches is a backporting from the kernels 4.10 – 4.14 for Mellanox system drivers that have been applied upstream and a few patches contain current Mellanox upstream work – patches which are in review process for acceptances for kernels 4.16 – 4.17. This series introduces Mellanox i2c controller, mux, platform, hotplug, led, asic drivers, TI voltage controller driver and the extension of Mellanox kernel config. In series file all the patches are listed and all old patches, relevant for old 3.16 kernel are commented out. Signed-off-by: Vadim Pasternak * Mellanox system drivers backport to kernel 4.9 This series of patches introduces support for Mellanox systems. Most of the patches is a backporting from the kernels 4.10 - 4.14 for Mellanox system drivers that have been applied upstream and a few patches contain current Mellanox upstream work patches which are in review process for acceptances for kernels 4.16 - 4.17. This series introduces Mellanox i2c controller, mux, platform, hotplug, led, asic drivers, TI voltage controller driver and the extension of Mellanox kernel config. The patchset includes the bellow commits. And additional we have a code from two work in progress patchsets, which are supposed to be available in 4.17, in review in: http://git.infradead.org/users/dvhart/linux-platform-drivers-x86.git http://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds.git This is to make Mellanox drivers architecture independent and also extend the supported system types, since we have a number of new systems). commit ae3923a284cc696311294022e9b1a49058f07387 Author: Bhumika Goyal Date: Mon Aug 21 17:42:04 2017 +0530 i2c: busses: make i2c_adapter_quirks const commit 26b1083b4545ec08a7dcfe759323a2e142d60d75 Author: Wolfram Sang Date: Mon Aug 14 10:23:26 2017 +0200 i2c: mux: mlxcpld: move header file out of I2C realm commit 1da87267006246137a96682404bc0716a10c85a7 Author: Wei Yongjun Date: Thu Jan 12 14:29:04 2017 +0000 i2c: mux: mlxcpld: remove unused including commit 649ac63a9ae5e08b7123f2fa98c2bf42f033bdb9 Author: Peter Rosin Date: Sat Dec 17 21:29:11 2016 +0100 i2c: mux: mlxcpld: fix i2c mux selection caching commit c02b7bf532f7e46f1f9a0e9c3c27ca3f6f134e8d Author: Vadim Pasternak Date: Thu Nov 10 21:26:23 2016 +0000 i2c: mux: mellanox: add driver commit dca897e28f364724e42f65cf0262fa367512498d Author: Vadim Pasternak Date: Thu Nov 3 19:17:19 2016 +0000 leds: verify vendor and change license in mlxcpld driver commit be4fdf99fa4dc95aa01144b207caf2cc9fa074d8 Author: Vadim Pasternak Date: Thu Sep 8 07:25:53 2016 +0000 leds: add driver for Mellanox systems LEDs commit d70eaa386bf9ecc1e5b3002f64eb59172fcec4fd Author: Vadim Pasternak Date: Wed, Nov 1 13:10:42 2017 +0200 mlxsw: i2c: Fix buffer increment counter for write transaction commit 7ca36994a3479e6d9d81baba34a426c47691ea08 Author: Arkadi Sharshevsky Date: Wed Jun 14 09:27:39 2017 +0200 mlxsw: reg: Add MCIA register for cable info access commit 36ca68bf9919299c95ff80e7cf3c217f4c250cbe Author: Elad Raz Date: Mon Jan 9 11:25:44 2017 +0100 mlxsw: Fix mlxsw_i2c_write return value commit d556e929164fdc6c94848374fc98c40852f97adb Author: Vadim Pasternak Date: Wed Nov 16 15:20:46 2016 +0100 mlxsw: minimal: Add I2C support for Mellanox ASICs commit 6882b0aee180f2797b8803bdf699aa45c2e5f2d6 Author: Vadim Pasternak Date: Wed Nov 16 15:20:44 2016 +0100 mlxsw: Introduce support for I2C bus commit ba20de060094c64a981a4fec8fecc587d076383d Author: Vadim Pasternak Date: Wed Oct 18 07:26:55 2017 +0000 platform/x86: move Mellanox hardware platform hotplug driver to platform/mellanox commit 6faadbbb7f9da70ce484f98f72223c20125a1009 Author: Christoph Hellwig Date: Thu Sep 14 11:59:30 2017 +0200 dmi: Mark all struct dmi_system_id instances const commit 9a38b67c9877a7371a250454bbbbf521d02fded5 Author: Vadim Pasternak Date: Wed Dec 14 12:05:15 2016 +0000 platform/x86: mlx-platform: mlxcpld-hotplug driver style fixes commit 63d762b88cb5510f2bfdb5112ced18cde867ae61 Author: Dan Carpenter Date: Sat Jan 7 09:33:34 2017 +0300 platform/x86: mlx-platform: free first dev on error commit afc4715901f0dce3206837a7051af05abf5a1e06 Author: Vadim Pasternak Date: Thu Oct 27 19:55:54 2016 +0000 platform/x86: mlx-platform: Add mlxcpld-hotplug driver registration commit c3886c9d6076555697551da9b921cf6a6e9cc2b5 Author: kbuild test robot Date: Fri Oct 28 01:26:50 2016 +0800 platform/x86: mlx-platform: Fix semicolon.cocci warnings commit 6613d18e90385db5cdbe32fe47567a3c11575b2d Author: Vadim Pasternak Date: Mon Oct 31 07:22:33 2016 +0000 platform/x86: mlx-platform: Move module from arch/x86 commit 304887041d953b6692c0d4a9f8fafb252d32e9a0 Author: Vadim Pasternak Date: Thu Oct 20 16:28:01 2016 +0000 platform/x86: Introduce support for Mellanox hotplug driver commit d4977c083aeb28cf72c1b019e3f9df13608126dd Author: Vadim Pasternak Date: Tue Aug 29 20:06:21 2017 +0000 hwmon: (pmbus) Add support for Intel VID protocol VR13 commit 610526527a13e4c91e64ec3dfb4626c5043291c9 Author: Vadim Pasternak Date: Wed Aug 30 22:02:14 2017 +0000 hwmon: (pmbus) Add support for Texas Instruments tps53679 device In series file all the patches are listed and all old patches, relevant for old 3.16 kernel are commented out. Signed-off-by: Vadim Pasternak * Makefile: uncomment line for applying kernel patches and config This patch uncomments lines in Makefile related to applying patches and configuration changes Signed-off-by: Vadim Pasternak * config: mellanox kernel configuration It adds explicit records for new configuration attributes which are not set in kernel config. Signed-off-by: Vadim Pasternak --- Makefile | 12 +- ...d-master-driver-for-Mellanox-systems.patch | 544 +++++ ...cpld-add-driver-for-Mellanox-systems.patch | 317 +++ ...x-Introduce-Mellanox-hardware-platfo.patch | 1463 ++++++++++++ ...roduce-support-for-Mellanox-hotplug-.patch | 905 +++++++ ...for-support-Mellanox-regmap-LEDs-for.patch | 398 ++++ ...0006-Mellanox-switch-drivers-changes.patch | 2118 +++++++++++++++++ ...-support-for-Intel-VID-protocol-VR13.patch | 30 + ...support-for-Texas-Instruments-tps536.patch | 151 ++ ...x-introduce-mlxreg-io-driver-and-add.patch | 685 ++++++ .../0010-config-mellanox-configuration.patch | 142 ++ patch/series | 68 +- 12 files changed, 6799 insertions(+), 34 deletions(-) create mode 100644 patch/0001-i2c-mlxcpld-add-master-driver-for-Mellanox-systems.patch create mode 100644 patch/0002-i2c-mux-mlxcpld-add-driver-for-Mellanox-systems.patch create mode 100644 patch/0003-platform-mellanox-Introduce-Mellanox-hardware-platfo.patch create mode 100644 patch/0004-platform-x86-Introduce-support-for-Mellanox-hotplug-.patch create mode 100644 patch/0005-leds-add-driver-for-support-Mellanox-regmap-LEDs-for.patch create mode 100644 patch/0006-Mellanox-switch-drivers-changes.patch create mode 100644 patch/0007-hwmon-pmbus-Add-support-for-Intel-VID-protocol-VR13.patch create mode 100644 patch/0008-hwmon-pmbus-Add-support-for-Texas-Instruments-tps536.patch create mode 100644 patch/0009-platform-mellonox-introduce-mlxreg-io-driver-and-add.patch create mode 100644 patch/0010-config-mellanox-configuration.patch diff --git a/Makefile b/Makefile index 627f4908c..86cf03519 100644 --- a/Makefile +++ b/Makefile @@ -41,12 +41,12 @@ $(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% : fakeroot make -f debian/rules.gen setup_amd64_none_amd64 # Applying patches and configuration changes - # git init - # git add -f * - # git add debian/build/build_amd64_none_amd64/.config -f - # git commit -m "unmodified debian source" - # stg init - # stg import -s ../patch/series + git init + git add -f * + git add debian/build/build_amd64_none_amd64/.config -f + git commit -m "unmodified debian source" + stg init + stg import -s ../patch/series # Building a custom kernel from Debian kernel source DO_DOCS=False fakeroot make -f debian/rules -j $(shell nproc) binary-indep diff --git a/patch/0001-i2c-mlxcpld-add-master-driver-for-Mellanox-systems.patch b/patch/0001-i2c-mlxcpld-add-master-driver-for-Mellanox-systems.patch new file mode 100644 index 000000000..54431bcc5 --- /dev/null +++ b/patch/0001-i2c-mlxcpld-add-master-driver-for-Mellanox-systems.patch @@ -0,0 +1,544 @@ +Linux backport patch. Includes following commits: +899e11216e1c215b97f2f8f92c7b010a4e88f38e + + +diff -Nur a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig +--- a/drivers/i2c/busses/Kconfig 2017-11-12 08:08:32.136039784 +0000 ++++ b/drivers/i2c/busses/Kconfig 2017-11-12 08:08:40.776039899 +0000 +@@ -1150,6 +1150,17 @@ + This support is also available as a module. If so, the module + will be called i2c-elektor. + ++config I2C_MLXCPLD ++ tristate "Mellanox I2C driver" ++ depends on X86_64 ++ help ++ This exposes the Mellanox platform I2C busses to the linux I2C layer ++ for X86 based systems. ++ Controller is implemented as CPLD logic. ++ ++ This driver can also be built as a module. If so, the module will be ++ called as i2c-mlxcpld. ++ + config I2C_PCA_ISA + tristate "PCA9564/PCA9665 on an ISA bus" + depends on ISA +diff -Nur a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile +--- a/drivers/i2c/busses/Makefile 2017-11-12 08:08:32.140039784 +0000 ++++ b/drivers/i2c/busses/Makefile 2017-11-12 08:08:40.780039899 +0000 +@@ -116,6 +116,7 @@ + obj-$(CONFIG_I2C_BRCMSTB) += i2c-brcmstb.o + obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += i2c-cros-ec-tunnel.o + obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o ++obj-$(CONFIG_I2C_MLXCPLD) += i2c-mlxcpld.o + obj-$(CONFIG_I2C_OPAL) += i2c-opal.o + obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o + obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o +diff -Nur a/drivers/i2c/busses/i2c-mlxcpld.c b/drivers/i2c/busses/i2c-mlxcpld.c +--- a/drivers/i2c/busses/i2c-mlxcpld.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/i2c/busses/i2c-mlxcpld.c 2017-11-12 08:08:40.780039899 +0000 +@@ -0,0 +1,504 @@ ++/* ++ * Copyright (c) 2016 Mellanox Technologies. All rights reserved. ++ * Copyright (c) 2016 Michael Shych ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* General defines */ ++#define MLXPLAT_CPLD_LPC_I2C_BASE_ADDR 0x2000 ++#define MLXCPLD_I2C_DEVICE_NAME "i2c_mlxcpld" ++#define MLXCPLD_I2C_VALID_FLAG (I2C_M_RECV_LEN | I2C_M_RD) ++#define MLXCPLD_I2C_BUS_NUM 1 ++#define MLXCPLD_I2C_DATA_REG_SZ 36 ++#define MLXCPLD_I2C_MAX_ADDR_LEN 4 ++#define MLXCPLD_I2C_RETR_NUM 2 ++#define MLXCPLD_I2C_XFER_TO 500000 /* usec */ ++#define MLXCPLD_I2C_POLL_TIME 2000 /* usec */ ++ ++/* LPC I2C registers */ ++#define MLXCPLD_LPCI2C_LPF_REG 0x0 ++#define MLXCPLD_LPCI2C_CTRL_REG 0x1 ++#define MLXCPLD_LPCI2C_HALF_CYC_REG 0x4 ++#define MLXCPLD_LPCI2C_I2C_HOLD_REG 0x5 ++#define MLXCPLD_LPCI2C_CMD_REG 0x6 ++#define MLXCPLD_LPCI2C_NUM_DAT_REG 0x7 ++#define MLXCPLD_LPCI2C_NUM_ADDR_REG 0x8 ++#define MLXCPLD_LPCI2C_STATUS_REG 0x9 ++#define MLXCPLD_LPCI2C_DATA_REG 0xa ++ ++/* LPC I2C masks and parametres */ ++#define MLXCPLD_LPCI2C_RST_SEL_MASK 0x1 ++#define MLXCPLD_LPCI2C_TRANS_END 0x1 ++#define MLXCPLD_LPCI2C_STATUS_NACK 0x10 ++#define MLXCPLD_LPCI2C_NO_IND 0 ++#define MLXCPLD_LPCI2C_ACK_IND 1 ++#define MLXCPLD_LPCI2C_NACK_IND 2 ++ ++struct mlxcpld_i2c_curr_xfer { ++ u8 cmd; ++ u8 addr_width; ++ u8 data_len; ++ u8 msg_num; ++ struct i2c_msg *msg; ++}; ++ ++struct mlxcpld_i2c_priv { ++ struct i2c_adapter adap; ++ u32 base_addr; ++ struct mutex lock; ++ struct mlxcpld_i2c_curr_xfer xfer; ++ struct device *dev; ++}; ++ ++static void mlxcpld_i2c_lpc_write_buf(u8 *data, u8 len, u32 addr) ++{ ++ int i; ++ ++ for (i = 0; i < len - len % 4; i += 4) ++ outl(*(u32 *)(data + i), addr + i); ++ for (; i < len; ++i) ++ outb(*(data + i), addr + i); ++} ++ ++static void mlxcpld_i2c_lpc_read_buf(u8 *data, u8 len, u32 addr) ++{ ++ int i; ++ ++ for (i = 0; i < len - len % 4; i += 4) ++ *(u32 *)(data + i) = inl(addr + i); ++ for (; i < len; ++i) ++ *(data + i) = inb(addr + i); ++} ++ ++static void mlxcpld_i2c_read_comm(struct mlxcpld_i2c_priv *priv, u8 offs, ++ u8 *data, u8 datalen) ++{ ++ u32 addr = priv->base_addr + offs; ++ ++ switch (datalen) { ++ case 1: ++ *(data) = inb(addr); ++ break; ++ case 2: ++ *((u16 *)data) = inw(addr); ++ break; ++ case 3: ++ *((u16 *)data) = inw(addr); ++ *(data + 2) = inb(addr + 2); ++ break; ++ case 4: ++ *((u32 *)data) = inl(addr); ++ break; ++ default: ++ mlxcpld_i2c_lpc_read_buf(data, datalen, addr); ++ break; ++ } ++} ++ ++static void mlxcpld_i2c_write_comm(struct mlxcpld_i2c_priv *priv, u8 offs, ++ u8 *data, u8 datalen) ++{ ++ u32 addr = priv->base_addr + offs; ++ ++ switch (datalen) { ++ case 1: ++ outb(*(data), addr); ++ break; ++ case 2: ++ outw(*((u16 *)data), addr); ++ break; ++ case 3: ++ outw(*((u16 *)data), addr); ++ outb(*(data + 2), addr + 2); ++ break; ++ case 4: ++ outl(*((u32 *)data), addr); ++ break; ++ default: ++ mlxcpld_i2c_lpc_write_buf(data, datalen, addr); ++ break; ++ } ++} ++ ++/* ++ * Check validity of received i2c messages parameters. ++ * Returns 0 if OK, other - in case of invalid parameters. ++ */ ++static int mlxcpld_i2c_check_msg_params(struct mlxcpld_i2c_priv *priv, ++ struct i2c_msg *msgs, int num) ++{ ++ int i; ++ ++ if (!num) { ++ dev_err(priv->dev, "Incorrect 0 num of messages\n"); ++ return -EINVAL; ++ } ++ ++ if (unlikely(msgs[0].addr > 0x7f)) { ++ dev_err(priv->dev, "Invalid address 0x%03x\n", ++ msgs[0].addr); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < num; ++i) { ++ if (unlikely(!msgs[i].buf)) { ++ dev_err(priv->dev, "Invalid buf in msg[%d]\n", ++ i); ++ return -EINVAL; ++ } ++ if (unlikely(msgs[0].addr != msgs[i].addr)) { ++ dev_err(priv->dev, "Invalid addr in msg[%d]\n", ++ i); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ * Check if transfer is completed and status of operation. ++ * Returns 0 - transfer completed (both ACK or NACK), ++ * negative - transfer isn't finished. ++ */ ++static int mlxcpld_i2c_check_status(struct mlxcpld_i2c_priv *priv, int *status) ++{ ++ u8 val; ++ ++ mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_STATUS_REG, &val, 1); ++ ++ if (val & MLXCPLD_LPCI2C_TRANS_END) { ++ if (val & MLXCPLD_LPCI2C_STATUS_NACK) ++ /* ++ * The slave is unable to accept the data. No such ++ * slave, command not understood, or unable to accept ++ * any more data. ++ */ ++ *status = MLXCPLD_LPCI2C_NACK_IND; ++ else ++ *status = MLXCPLD_LPCI2C_ACK_IND; ++ return 0; ++ } ++ *status = MLXCPLD_LPCI2C_NO_IND; ++ ++ return -EIO; ++} ++ ++static void mlxcpld_i2c_set_transf_data(struct mlxcpld_i2c_priv *priv, ++ struct i2c_msg *msgs, int num, ++ u8 comm_len) ++{ ++ priv->xfer.msg = msgs; ++ priv->xfer.msg_num = num; ++ ++ /* ++ * All upper layers currently are never use transfer with more than ++ * 2 messages. Actually, it's also not so relevant in Mellanox systems ++ * because of HW limitation. Max size of transfer is not more than 32 ++ * bytes in the current x86 LPCI2C bridge. ++ */ ++ priv->xfer.cmd = msgs[num - 1].flags & I2C_M_RD; ++ ++ if (priv->xfer.cmd == I2C_M_RD && comm_len != msgs[0].len) { ++ priv->xfer.addr_width = msgs[0].len; ++ priv->xfer.data_len = comm_len - priv->xfer.addr_width; ++ } else { ++ priv->xfer.addr_width = 0; ++ priv->xfer.data_len = comm_len; ++ } ++} ++ ++/* Reset CPLD LPCI2C block */ ++static void mlxcpld_i2c_reset(struct mlxcpld_i2c_priv *priv) ++{ ++ u8 val; ++ ++ mutex_lock(&priv->lock); ++ ++ mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_CTRL_REG, &val, 1); ++ val &= ~MLXCPLD_LPCI2C_RST_SEL_MASK; ++ mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_CTRL_REG, &val, 1); ++ ++ mutex_unlock(&priv->lock); ++} ++ ++/* Make sure the CPLD is ready to start transmitting. */ ++static int mlxcpld_i2c_check_busy(struct mlxcpld_i2c_priv *priv) ++{ ++ u8 val; ++ ++ mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_STATUS_REG, &val, 1); ++ ++ if (val & MLXCPLD_LPCI2C_TRANS_END) ++ return 0; ++ ++ return -EIO; ++} ++ ++static int mlxcpld_i2c_wait_for_free(struct mlxcpld_i2c_priv *priv) ++{ ++ int timeout = 0; ++ ++ do { ++ if (!mlxcpld_i2c_check_busy(priv)) ++ break; ++ usleep_range(MLXCPLD_I2C_POLL_TIME / 2, MLXCPLD_I2C_POLL_TIME); ++ timeout += MLXCPLD_I2C_POLL_TIME; ++ } while (timeout <= MLXCPLD_I2C_XFER_TO); ++ ++ if (timeout > MLXCPLD_I2C_XFER_TO) ++ return -ETIMEDOUT; ++ ++ return 0; ++} ++ ++/* ++ * Wait for master transfer to complete. ++ * It puts current process to sleep until we get interrupt or timeout expires. ++ * Returns the number of transferred or read bytes or error (<0). ++ */ ++static int mlxcpld_i2c_wait_for_tc(struct mlxcpld_i2c_priv *priv) ++{ ++ int status, i, timeout = 0; ++ u8 datalen; ++ ++ do { ++ usleep_range(MLXCPLD_I2C_POLL_TIME / 2, MLXCPLD_I2C_POLL_TIME); ++ if (!mlxcpld_i2c_check_status(priv, &status)) ++ break; ++ timeout += MLXCPLD_I2C_POLL_TIME; ++ } while (status == 0 && timeout < MLXCPLD_I2C_XFER_TO); ++ ++ switch (status) { ++ case MLXCPLD_LPCI2C_NO_IND: ++ return -ETIMEDOUT; ++ ++ case MLXCPLD_LPCI2C_ACK_IND: ++ if (priv->xfer.cmd != I2C_M_RD) ++ return (priv->xfer.addr_width + priv->xfer.data_len); ++ ++ if (priv->xfer.msg_num == 1) ++ i = 0; ++ else ++ i = 1; ++ ++ if (!priv->xfer.msg[i].buf) ++ return -EINVAL; ++ ++ /* ++ * Actual read data len will be always the same as ++ * requested len. 0xff (line pull-up) will be returned ++ * if slave has no data to return. Thus don't read ++ * MLXCPLD_LPCI2C_NUM_DAT_REG reg from CPLD. ++ */ ++ datalen = priv->xfer.data_len; ++ ++ mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_DATA_REG, ++ priv->xfer.msg[i].buf, datalen); ++ ++ return datalen; ++ ++ case MLXCPLD_LPCI2C_NACK_IND: ++ return -ENXIO; ++ ++ default: ++ return -EINVAL; ++ } ++} ++ ++static void mlxcpld_i2c_xfer_msg(struct mlxcpld_i2c_priv *priv) ++{ ++ int i, len = 0; ++ u8 cmd; ++ ++ mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_NUM_DAT_REG, ++ &priv->xfer.data_len, 1); ++ mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_NUM_ADDR_REG, ++ &priv->xfer.addr_width, 1); ++ ++ for (i = 0; i < priv->xfer.msg_num; i++) { ++ if ((priv->xfer.msg[i].flags & I2C_M_RD) != I2C_M_RD) { ++ /* Don't write to CPLD buffer in read transaction */ ++ mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_DATA_REG + ++ len, priv->xfer.msg[i].buf, ++ priv->xfer.msg[i].len); ++ len += priv->xfer.msg[i].len; ++ } ++ } ++ ++ /* ++ * Set target slave address with command for master transfer. ++ * It should be latest executed function before CPLD transaction. ++ */ ++ cmd = (priv->xfer.msg[0].addr << 1) | priv->xfer.cmd; ++ mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_CMD_REG, &cmd, 1); ++} ++ ++/* ++ * Generic lpc-i2c transfer. ++ * Returns the number of processed messages or error (<0). ++ */ ++static int mlxcpld_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, ++ int num) ++{ ++ struct mlxcpld_i2c_priv *priv = i2c_get_adapdata(adap); ++ u8 comm_len = 0; ++ int i, err; ++ ++ err = mlxcpld_i2c_check_msg_params(priv, msgs, num); ++ if (err) { ++ dev_err(priv->dev, "Incorrect message\n"); ++ return err; ++ } ++ ++ for (i = 0; i < num; ++i) ++ comm_len += msgs[i].len; ++ ++ /* Check bus state */ ++ if (mlxcpld_i2c_wait_for_free(priv)) { ++ dev_err(priv->dev, "LPCI2C bridge is busy\n"); ++ ++ /* ++ * Usually it means something serious has happened. ++ * We can not have unfinished previous transfer ++ * so it doesn't make any sense to try to stop it. ++ * Probably we were not able to recover from the ++ * previous error. ++ * The only reasonable thing - is soft reset. ++ */ ++ mlxcpld_i2c_reset(priv); ++ if (mlxcpld_i2c_check_busy(priv)) { ++ dev_err(priv->dev, "LPCI2C bridge is busy after reset\n"); ++ return -EIO; ++ } ++ } ++ ++ mlxcpld_i2c_set_transf_data(priv, msgs, num, comm_len); ++ ++ mutex_lock(&priv->lock); ++ ++ /* Do real transfer. Can't fail */ ++ mlxcpld_i2c_xfer_msg(priv); ++ ++ /* Wait for transaction complete */ ++ err = mlxcpld_i2c_wait_for_tc(priv); ++ ++ mutex_unlock(&priv->lock); ++ ++ return err < 0 ? err : num; ++} ++ ++static u32 mlxcpld_i2c_func(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA; ++} ++ ++static const struct i2c_algorithm mlxcpld_i2c_algo = { ++ .master_xfer = mlxcpld_i2c_xfer, ++ .functionality = mlxcpld_i2c_func ++}; ++ ++static struct i2c_adapter_quirks mlxcpld_i2c_quirks = { ++ .flags = I2C_AQ_COMB_WRITE_THEN_READ, ++ .max_read_len = MLXCPLD_I2C_DATA_REG_SZ - MLXCPLD_I2C_MAX_ADDR_LEN, ++ .max_write_len = MLXCPLD_I2C_DATA_REG_SZ, ++ .max_comb_1st_msg_len = 4, ++}; ++ ++static struct i2c_adapter mlxcpld_i2c_adapter = { ++ .owner = THIS_MODULE, ++ .name = "i2c-mlxcpld", ++ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, ++ .algo = &mlxcpld_i2c_algo, ++ .quirks = &mlxcpld_i2c_quirks, ++ .retries = MLXCPLD_I2C_RETR_NUM, ++ .nr = MLXCPLD_I2C_BUS_NUM, ++}; ++ ++static int mlxcpld_i2c_probe(struct platform_device *pdev) ++{ ++ struct mlxcpld_i2c_priv *priv; ++ int err; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ mutex_init(&priv->lock); ++ platform_set_drvdata(pdev, priv); ++ ++ priv->dev = &pdev->dev; ++ ++ /* Register with i2c layer */ ++ mlxcpld_i2c_adapter.timeout = usecs_to_jiffies(MLXCPLD_I2C_XFER_TO); ++ priv->adap = mlxcpld_i2c_adapter; ++ priv->adap.dev.parent = &pdev->dev; ++ priv->base_addr = MLXPLAT_CPLD_LPC_I2C_BASE_ADDR; ++ i2c_set_adapdata(&priv->adap, priv); ++ ++ err = i2c_add_numbered_adapter(&priv->adap); ++ if (err) ++ mutex_destroy(&priv->lock); ++ ++ return err; ++} ++ ++static int mlxcpld_i2c_remove(struct platform_device *pdev) ++{ ++ struct mlxcpld_i2c_priv *priv = platform_get_drvdata(pdev); ++ ++ i2c_del_adapter(&priv->adap); ++ mutex_destroy(&priv->lock); ++ ++ return 0; ++} ++ ++static struct platform_driver mlxcpld_i2c_driver = { ++ .probe = mlxcpld_i2c_probe, ++ .remove = mlxcpld_i2c_remove, ++ .driver = { ++ .name = MLXCPLD_I2C_DEVICE_NAME, ++ }, ++}; ++ ++module_platform_driver(mlxcpld_i2c_driver); ++ ++MODULE_AUTHOR("Michael Shych "); ++MODULE_DESCRIPTION("Mellanox I2C-CPLD controller driver"); ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_ALIAS("platform:i2c-mlxcpld"); diff --git a/patch/0002-i2c-mux-mlxcpld-add-driver-for-Mellanox-systems.patch b/patch/0002-i2c-mux-mlxcpld-add-driver-for-Mellanox-systems.patch new file mode 100644 index 000000000..4ce713858 --- /dev/null +++ b/patch/0002-i2c-mux-mlxcpld-add-driver-for-Mellanox-systems.patch @@ -0,0 +1,317 @@ +Linux backport patch. Includes following commits: +e3448e71adb1fdd7f403c568ef5c2ed5adf2b197 +c3bb77620da428884807fb2f6f3485644e146f84 +db5f807ee3dcc779b78f59982cc3e89863069e9c + + +diff -Nur a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig +--- a/drivers/i2c/muxes/Kconfig 2017-11-12 08:13:59.176044126 +0000 ++++ b/drivers/i2c/muxes/Kconfig 2017-11-12 08:14:27.992044509 +0000 +@@ -82,4 +82,15 @@ + demultiplexer that uses the pinctrl subsystem. This is useful if you + want to change the I2C master at run-time depending on features. + ++config I2C_MUX_MLXCPLD ++ tristate "Mellanox CPLD based I2C multiplexer" ++ help ++ If you say yes to this option, support will be included for a ++ CPLD based I2C multiplexer. This driver provides access to ++ I2C busses connected through a MUX, which is controlled ++ by a CPLD register. ++ ++ This driver can also be built as a module. If so, the module ++ will be called i2c-mux-mlxcpld. ++ + endmenu +diff -Nur a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile +--- a/drivers/i2c/muxes/Makefile 2017-11-12 08:13:59.176044126 +0000 ++++ b/drivers/i2c/muxes/Makefile 2017-11-12 08:14:27.992044509 +0000 +@@ -6,6 +6,7 @@ + obj-$(CONFIG_I2C_DEMUX_PINCTRL) += i2c-demux-pinctrl.o + + obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o ++obj-$(CONFIG_I2C_MUX_MLXCPLD) += i2c-mux-mlxcpld.o + obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o + obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o + obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o +diff -Nur a/drivers/i2c/muxes/i2c-mux-mlxcpld.c b/drivers/i2c/muxes/i2c-mux-mlxcpld.c +--- a/drivers/i2c/muxes/i2c-mux-mlxcpld.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/i2c/muxes/i2c-mux-mlxcpld.c 2017-11-12 08:14:27.992044509 +0000 +@@ -0,0 +1,221 @@ ++/* ++ * drivers/i2c/muxes/i2c-mux-mlxcpld.c ++ * Copyright (c) 2016 Mellanox Technologies. All rights reserved. ++ * Copyright (c) 2016 Michael Shych ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define CPLD_MUX_MAX_NCHANS 8 ++ ++/* mlxcpld_mux - mux control structure: ++ * @last_chan - last register value ++ * @client - I2C device client ++ */ ++struct mlxcpld_mux { ++ u8 last_chan; ++ struct i2c_client *client; ++}; ++ ++/* MUX logic description. ++ * Driver can support different mux control logic, according to CPLD ++ * implementation. ++ * ++ * Connectivity schema. ++ * ++ * i2c-mlxcpld Digital Analog ++ * driver ++ * *--------* * -> mux1 (virt bus2) -> mux -> | ++ * | I2CLPC | i2c physical * -> mux2 (virt bus3) -> mux -> | ++ * | bridge | bus 1 *---------* | ++ * | logic |---------------------> * mux reg * | ++ * | in CPLD| *---------* | ++ * *--------* i2c-mux-mlxpcld ^ * -> muxn (virt busn) -> mux -> | ++ * | driver | | ++ * | *---------------* | Devices ++ * | * CPLD (i2c bus)* select | ++ * | * registers for *--------* ++ * | * mux selection * deselect ++ * | *---------------* ++ * | | ++ * <--------> <-----------> ++ * i2c cntrl Board cntrl reg ++ * reg space space (mux select, ++ * IO, LED, WD, info) ++ * ++ */ ++ ++static const struct i2c_device_id mlxcpld_mux_id[] = { ++ { "mlxcpld_mux_module", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, mlxcpld_mux_id); ++ ++/* Write to mux register. Don't use i2c_transfer() and i2c_smbus_xfer() ++ * for this as they will try to lock adapter a second time. ++ */ ++static int mlxcpld_mux_reg_write(struct i2c_adapter *adap, ++ struct i2c_client *client, u8 val) ++{ ++ struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&client->dev); ++ int ret = -ENODEV; ++ ++ if (adap->algo->master_xfer) { ++ struct i2c_msg msg; ++ u8 msgbuf[] = {pdata->sel_reg_addr, val}; ++ ++ msg.addr = client->addr; ++ msg.flags = 0; ++ msg.len = 2; ++ msg.buf = msgbuf; ++ ret = __i2c_transfer(adap, &msg, 1); ++ ++ if (ret >= 0 && ret != 1) ++ ret = -EREMOTEIO; ++ } else if (adap->algo->smbus_xfer) { ++ union i2c_smbus_data data; ++ ++ data.byte = val; ++ ret = adap->algo->smbus_xfer(adap, client->addr, ++ client->flags, I2C_SMBUS_WRITE, ++ pdata->sel_reg_addr, ++ I2C_SMBUS_BYTE_DATA, &data); ++ } ++ ++ return ret; ++} ++ ++static int mlxcpld_mux_select_chan(struct i2c_mux_core *muxc, u32 chan) ++{ ++ struct mlxcpld_mux *data = i2c_mux_priv(muxc); ++ struct i2c_client *client = data->client; ++ u8 regval = chan + 1; ++ int err = 0; ++ ++ /* Only select the channel if its different from the last channel */ ++ if (data->last_chan != regval) { ++ err = mlxcpld_mux_reg_write(muxc->parent, client, regval); ++ data->last_chan = err < 0 ? 0 : regval; ++ } ++ ++ return err; ++} ++ ++static int mlxcpld_mux_deselect(struct i2c_mux_core *muxc, u32 chan) ++{ ++ struct mlxcpld_mux *data = i2c_mux_priv(muxc); ++ struct i2c_client *client = data->client; ++ ++ /* Deselect active channel */ ++ data->last_chan = 0; ++ ++ return mlxcpld_mux_reg_write(muxc->parent, client, data->last_chan); ++} ++ ++/* Probe/reomove functions */ ++static int mlxcpld_mux_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); ++ struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&client->dev); ++ struct i2c_mux_core *muxc; ++ int num, force; ++ struct mlxcpld_mux *data; ++ int err; ++ ++ if (!pdata) ++ return -EINVAL; ++ ++ if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) ++ return -ENODEV; ++ ++ muxc = i2c_mux_alloc(adap, &client->dev, CPLD_MUX_MAX_NCHANS, ++ sizeof(*data), 0, mlxcpld_mux_select_chan, ++ mlxcpld_mux_deselect); ++ if (!muxc) ++ return -ENOMEM; ++ ++ data = i2c_mux_priv(muxc); ++ i2c_set_clientdata(client, muxc); ++ data->client = client; ++ data->last_chan = 0; /* force the first selection */ ++ ++ /* Create an adapter for each channel. */ ++ for (num = 0; num < CPLD_MUX_MAX_NCHANS; num++) { ++ if (num >= pdata->num_adaps) ++ /* discard unconfigured channels */ ++ break; ++ ++ force = pdata->adap_ids[num]; ++ ++ err = i2c_mux_add_adapter(muxc, force, num, 0); ++ if (err) ++ goto virt_reg_failed; ++ } ++ ++ return 0; ++ ++virt_reg_failed: ++ i2c_mux_del_adapters(muxc); ++ return err; ++} ++ ++static int mlxcpld_mux_remove(struct i2c_client *client) ++{ ++ struct i2c_mux_core *muxc = i2c_get_clientdata(client); ++ ++ i2c_mux_del_adapters(muxc); ++ return 0; ++} ++ ++static struct i2c_driver mlxcpld_mux_driver = { ++ .driver = { ++ .name = "mlxcpld-mux", ++ }, ++ .probe = mlxcpld_mux_probe, ++ .remove = mlxcpld_mux_remove, ++ .id_table = mlxcpld_mux_id, ++}; ++ ++module_i2c_driver(mlxcpld_mux_driver); ++ ++MODULE_AUTHOR("Michael Shych (michaels@mellanox.com)"); ++MODULE_DESCRIPTION("Mellanox I2C-CPLD-MUX driver"); ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_ALIAS("platform:i2c-mux-mlxcpld"); +diff -Nur a/include/linux/i2c/mlxcpld.h b/include/linux/i2c/mlxcpld.h +--- a/include/linux/i2c/mlxcpld.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/include/linux/i2c/mlxcpld.h 2017-11-12 08:17:03.032046568 +0000 +@@ -0,0 +1,52 @@ ++/* ++ * mlxcpld.h - Mellanox I2C multiplexer support in CPLD ++ * ++ * Copyright (c) 2016 Mellanox Technologies. All rights reserved. ++ * Copyright (c) 2016 Michael Shych ++ * ++ * 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. ++ */ ++ ++#ifndef _LINUX_I2C_MLXCPLD_H ++#define _LINUX_I2C_MLXCPLD_H ++ ++/* Platform data for the CPLD I2C multiplexers */ ++ ++/* mlxcpld_mux_plat_data - per mux data, used with i2c_register_board_info ++ * @adap_ids - adapter array ++ * @num_adaps - number of adapters ++ * @sel_reg_addr - mux select register offset in CPLD space ++ */ ++struct mlxcpld_mux_plat_data { ++ int *adap_ids; ++ int num_adaps; ++ int sel_reg_addr; ++}; ++ ++#endif /* _LINUX_I2C_MLXCPLD_H */ diff --git a/patch/0003-platform-mellanox-Introduce-Mellanox-hardware-platfo.patch b/patch/0003-platform-mellanox-Introduce-Mellanox-hardware-platfo.patch new file mode 100644 index 000000000..a2b6a9f45 --- /dev/null +++ b/patch/0003-platform-mellanox-Introduce-Mellanox-hardware-platfo.patch @@ -0,0 +1,1463 @@ +Linux backport patch. Includes following commits: +2926024b5081fc8d4b086677bafa1ac55ea0b911 +eab85698a91e0191e1a00b547ebded411dd9061c +91973760712f350048a0fa8e0363e260bf874313 +ae4c6f185a791890dcf9cab7bc7764ba44e2e696 +90fcad9b077a4660896dbdc815509cee3440886b +290e505b287fe664549cb72b26507d8ffb4a6d39 +d53bc5dc941653f0ed93b11a647bd6ff40f40ef2 +daf155fe70c9d69c28bba632b6a758ac8feab6e7 +c7c1fe5b608cd11668834761404f9d03bf616bdd + + +diff -Nur a/arch/x86/Kconfig b/arch/x86/Kconfig +--- a/arch/x86/Kconfig 2017-11-12 10:26:01.808149323 +0000 ++++ b/arch/x86/Kconfig 2017-11-12 10:27:38.576150608 +0000 +@@ -550,18 +550,6 @@ + Say Y here if you have a Quark based system such as the Arduino + compatible Intel Galileo. + +-config MLX_PLATFORM +- tristate "Mellanox Technologies platform support" +- depends on X86_64 +- depends on X86_EXTENDED_PLATFORM +- ---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 X86_INTEL_LPSS + bool "Intel Low Power Subsystem Support" + depends on X86 && ACPI +diff -Nur a/arch/x86/platform/Makefile b/arch/x86/platform/Makefile +--- a/arch/x86/platform/Makefile 2017-11-12 10:25:48.640149148 +0000 ++++ b/arch/x86/platform/Makefile 2017-11-12 10:27:24.924150426 +0000 +@@ -8,7 +8,6 @@ + obj-y += intel/ + obj-y += intel-mid/ + obj-y += intel-quark/ +-obj-y += mellanox/ + obj-y += olpc/ + obj-y += scx200/ + obj-y += sfi/ +diff -Nur a/arch/x86/platform/mellanox/Makefile b/arch/x86/platform/mellanox/Makefile +--- a/arch/x86/platform/mellanox/Makefile 2017-11-12 10:25:26.704148857 +0000 ++++ b/arch/x86/platform/mellanox/Makefile 1970-01-01 00:00:00.000000000 +0000 +@@ -1 +0,0 @@ +-obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o +diff -Nur a/arch/x86/platform/mellanox/mlx-platform.c b/arch/x86/platform/mellanox/mlx-platform.c +--- a/arch/x86/platform/mellanox/mlx-platform.c 2017-11-12 10:25:26.704148857 +0000 ++++ b/arch/x86/platform/mellanox/mlx-platform.c 1970-01-01 00:00:00.000000000 +0000 +@@ -1,266 +0,0 @@ +-/* +- * arch/x86/platform/mellanox/mlx-platform.c +- * Copyright (c) 2016 Mellanox Technologies. All rights reserved. +- * Copyright (c) 2016 Vadim Pasternak +- * +- * 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 +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define MLX_PLAT_DEVICE_NAME "mlxplat" +- +-/* LPC bus IO offsets */ +-#define MLXPLAT_CPLD_LPC_I2C_BASE_ADRR 0x2000 +-#define MLXPLAT_CPLD_LPC_REG_BASE_ADRR 0x2500 +-#define MLXPLAT_CPLD_LPC_IO_RANGE 0x100 +-#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) +- +-/* Start channel numbers */ +-#define MLXPLAT_CPLD_CH1 2 +-#define MLXPLAT_CPLD_CH2 10 +- +-/* Number of LPC attached MUX platform devices */ +-#define MLXPLAT_CPLD_LPC_MUX_DEVS 2 +- +-/* mlxplat_priv - platform private data +- * @pdev_i2c - i2c controller platform device +- * @pdev_mux - array of mux platform devices +- */ +-struct mlxplat_priv { +- struct platform_device *pdev_i2c; +- struct platform_device *pdev_mux[MLXPLAT_CPLD_LPC_MUX_DEVS]; +-}; +- +-/* Regions for LPC I2C controller and LPC base register space */ +-static const 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), +-}; +- +-/* Platform default channels */ +-static const int mlxplat_default_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 channels for MSN21xx system family */ +-static const int mlxplat_msn21xx_channels[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; +- +-/* Platform mux data */ +-static struct i2c_mux_reg_platform_data mlxplat_mux_data[] = { +- { +- .parent = 1, +- .base_nr = MLXPLAT_CPLD_CH1, +- .write_only = 1, +- .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG1, +- .reg_size = 1, +- .idle_in_use = 1, +- }, +- { +- .parent = 1, +- .base_nr = MLXPLAT_CPLD_CH2, +- .write_only = 1, +- .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG2, +- .reg_size = 1, +- .idle_in_use = 1, +- }, +- +-}; +- +-static struct platform_device *mlxplat_dev; +- +-static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) +-{ +- int i; +- +- for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) { +- mlxplat_mux_data[i].values = mlxplat_default_channels[i]; +- mlxplat_mux_data[i].n_values = +- ARRAY_SIZE(mlxplat_default_channels[i]); +- } +- +- return 1; +-}; +- +-static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi) +-{ +- int i; +- +- for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) { +- mlxplat_mux_data[i].values = mlxplat_msn21xx_channels; +- mlxplat_mux_data[i].n_values = +- ARRAY_SIZE(mlxplat_msn21xx_channels); +- } +- +- return 1; +-}; +- +-static struct dmi_system_id mlxplat_dmi_table[] __initdata = { +- { +- .callback = mlxplat_dmi_default_matched, +- .matches = { +- DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"), +- DMI_MATCH(DMI_PRODUCT_NAME, "MSN24"), +- }, +- }, +- { +- .callback = mlxplat_dmi_default_matched, +- .matches = { +- DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"), +- DMI_MATCH(DMI_PRODUCT_NAME, "MSN27"), +- }, +- }, +- { +- .callback = mlxplat_dmi_default_matched, +- .matches = { +- DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"), +- DMI_MATCH(DMI_PRODUCT_NAME, "MSB"), +- }, +- }, +- { +- .callback = mlxplat_dmi_default_matched, +- .matches = { +- DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"), +- DMI_MATCH(DMI_PRODUCT_NAME, "MSX"), +- }, +- }, +- { +- .callback = mlxplat_dmi_msn21xx_matched, +- .matches = { +- DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"), +- DMI_MATCH(DMI_PRODUCT_NAME, "MSN21"), +- }, +- }, +- { } +-}; +- +-static int __init mlxplat_init(void) +-{ +- struct mlxplat_priv *priv; +- int i, err; +- +- if (!dmi_check_system(mlxplat_dmi_table)) +- return -ENODEV; +- +- mlxplat_dev = platform_device_register_simple(MLX_PLAT_DEVICE_NAME, -1, +- mlxplat_lpc_resources, +- ARRAY_SIZE(mlxplat_lpc_resources)); +- +- if (IS_ERR(mlxplat_dev)) +- return PTR_ERR(mlxplat_dev); +- +- priv = devm_kzalloc(&mlxplat_dev->dev, sizeof(struct mlxplat_priv), +- GFP_KERNEL); +- if (!priv) { +- err = -ENOMEM; +- goto fail_alloc; +- } +- platform_set_drvdata(mlxplat_dev, priv); +- +- 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++) { +- priv->pdev_mux[i] = platform_device_register_resndata( +- &mlxplat_dev->dev, +- "i2c-mux-reg", i, NULL, +- 0, &mlxplat_mux_data[i], +- sizeof(mlxplat_mux_data[i])); +- if (IS_ERR(priv->pdev_mux[i])) { +- err = PTR_ERR(priv->pdev_mux[i]); +- goto fail_platform_mux_register; +- } +- } +- +- return 0; +- +-fail_platform_mux_register: +- while (--i >= 0) +- platform_device_unregister(priv->pdev_mux[i]); +- platform_device_unregister(priv->pdev_i2c); +-fail_alloc: +- platform_device_unregister(mlxplat_dev); +- +- return err; +-} +-module_init(mlxplat_init); +- +-static void __exit mlxplat_exit(void) +-{ +- struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev); +- int i; +- +- for (i = ARRAY_SIZE(mlxplat_mux_data) - 1; i >= 0 ; i--) +- platform_device_unregister(priv->pdev_mux[i]); +- +- platform_device_unregister(priv->pdev_i2c); +- platform_device_unregister(mlxplat_dev); +-} +-module_exit(mlxplat_exit); +- +-MODULE_AUTHOR("Vadim Pasternak (vadimp@mellanox.com)"); +-MODULE_DESCRIPTION("Mellanox platform driver"); +-MODULE_LICENSE("Dual BSD/GPL"); +-MODULE_ALIAS("dmi:*:*Mellanox*:MSN24*:"); +-MODULE_ALIAS("dmi:*:*Mellanox*:MSN27*:"); +-MODULE_ALIAS("dmi:*:*Mellanox*:MSB*:"); +-MODULE_ALIAS("dmi:*:*Mellanox*:MSX*:"); +-MODULE_ALIAS("dmi:*:*Mellanox*:MSN21*:"); +diff -Nur a/drivers/platform/Kconfig b/drivers/platform/Kconfig +--- a/drivers/platform/Kconfig 2017-11-12 11:01:23.768177498 +0000 ++++ b/drivers/platform/Kconfig 2017-11-12 11:01:47.784177817 +0000 +@@ -8,3 +8,5 @@ + source "drivers/platform/goldfish/Kconfig" + + source "drivers/platform/chrome/Kconfig" ++ ++source "drivers/platform/mellanox/Kconfig" +diff -Nur a/drivers/platform/Makefile b/drivers/platform/Makefile +--- a/drivers/platform/Makefile 2017-11-12 11:01:23.768177498 +0000 ++++ b/drivers/platform/Makefile 2017-11-12 11:01:47.784177817 +0000 +@@ -3,6 +3,7 @@ + # + + obj-$(CONFIG_X86) += x86/ ++obj-$(CONFIG_MELLANOX_PLATFORM) += mellanox/ + obj-$(CONFIG_MIPS) += mips/ + obj-$(CONFIG_OLPC) += olpc/ + obj-$(CONFIG_GOLDFISH) += goldfish/ +diff -Nur a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig +--- a/drivers/platform/x86/Kconfig 2017-11-12 11:03:10.600178917 +0000 ++++ b/drivers/platform/x86/Kconfig 2017-11-12 11:03:56.776179530 +0000 +@@ -1027,4 +1027,17 @@ + used to get various SoC events and parameters + directly via debugfs files. Various tools may use + this interface for SoC state monitoring. ++ ++config MLX_PLATFORM ++ tristate "Mellanox Technologies platform support" ++ depends on X86_64 ++ ---help--- ++ This option enables system support for the Mellanox Technologies ++ platform. The Mellanox systems provide data center networking ++ solutions based on Virtual Protocol Interconnect (VPI) technology ++ enable seamless connectivity to 56/100Gb/s InfiniBand or 10/40/56GbE ++ connection. ++ ++ If you have a Mellanox system, say Y or M here. ++ + endif # X86_PLATFORM_DEVICES +diff -Nur a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile +--- a/drivers/platform/x86/Makefile 2017-11-12 11:03:10.600178917 +0000 ++++ b/drivers/platform/x86/Makefile 2017-11-12 11:03:56.776179530 +0000 +@@ -71,3 +71,4 @@ + intel_telemetry_pltdrv.o \ + intel_telemetry_debugfs.o + obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o ++obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o +diff -Nur a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c +--- a/drivers/platform/x86/mlx-platform.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/platform/x86/mlx-platform.c 2017-11-12 11:03:56.776179530 +0000 +@@ -0,0 +1,1090 @@ ++/* ++ * Copyright (c) 2016 Mellanox Technologies. All rights reserved. ++ * Copyright (c) 2016 Vadim Pasternak ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MLX_PLAT_DEVICE_NAME "mlxplat" ++ ++/* LPC bus IO offsets */ ++#define MLXPLAT_CPLD_LPC_I2C_BASE_ADRR 0x2000 ++#define MLXPLAT_CPLD_LPC_REG_BASE_ADRR 0x2500 ++#define MLXPLAT_CPLD_LPC_REG_LED1_OFF 0x20 ++#define MLXPLAT_CPLD_LPC_REG_LED2_OFF 0x21 ++#define MLXPLAT_CPLD_LPC_REG_LED3_OFF 0x22 ++#define MLXPLAT_CPLD_LPC_REG_LED4_OFF 0x23 ++#define MLXPLAT_CPLD_LPC_REG_LED5_OFF 0x24 ++#define MLXPLAT_CPLD_LPC_REG_AGGR_OFF 0x3a ++#define MLXPLAT_CPLD_LPC_REG_AGGR_LOW_OFF 0x40 ++#define MLXPLAT_CPLD_LPC_REG_PSU_OFF 0x58 ++#define MLXPLAT_CPLD_LPC_REG_PWR_OFF 0x64 ++#define MLXPLAT_CPLD_LPC_REG_FAN_OFF 0x88 ++#define MLXPLAT_CPLD_LPC_IO_RANGE 0x100 ++#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) ++ ++/* Masks for aggregation, psu, pwr and fan event in CPLD related registers. */ ++#define MLXPLAT_CPLD_AGGR_PSU_MASK_DEF 0x08 ++#define MLXPLAT_CPLD_AGGR_PWR_MASK_DEF 0x08 ++#define MLXPLAT_CPLD_AGGR_FAN_MASK_DEF 0x40 ++#define MLXPLAT_CPLD_AGGR_MASK_DEF (MLXPLAT_CPLD_AGGR_PSU_MASK_DEF | \ ++ MLXPLAT_CPLD_AGGR_FAN_MASK_DEF) ++#define MLXPLAT_CPLD_AGGR_MASK_NG_DEF 0x04 ++#define MLXPLAT_CPLD_LOW_AGGR_MASK_LOW 0xc0 ++#define MLXPLAT_CPLD_AGGR_MASK_MSN21XX 0x04 ++#define MLXPLAT_CPLD_PSU_MASK GENMASK(1, 0) ++#define MLXPLAT_CPLD_PWR_MASK GENMASK(1, 0) ++#define MLXPLAT_CPLD_FAN_MASK GENMASK(3, 0) ++#define MLXPLAT_CPLD_FAN_NG_MASK GENMASK(5, 0) ++#define MLXPLAT_CPLD_LED_LO_NIBBLE_MASK GENMASK(7, 4) ++#define MLXPLAT_CPLD_LED_HI_NIBBLE_MASK GENMASK(3, 0) ++ ++/* Start channel numbers */ ++#define MLXPLAT_CPLD_CH1 2 ++#define MLXPLAT_CPLD_CH2 10 ++ ++/* Number of LPC attached MUX platform devices */ ++#define MLXPLAT_CPLD_LPC_MUX_DEVS 2 ++ ++/* PSU adapter numbers */ ++#define MLXPLAT_CPLD_PSU_DEFAULT_NR 10 ++#define MLXPLAT_CPLD_PSU_MSNXXXX_NR 4 ++ ++/* mlxplat_priv - platform private data ++ * @pdev_i2c - i2c controller platform device ++ * @pdev_mux - array of mux platform devices ++ * @pdev_hotplug - hotplug platform devices ++ * @pdev_led - led platform devices ++ */ ++struct mlxplat_priv { ++ struct platform_device *pdev_i2c; ++ struct platform_device *pdev_mux[MLXPLAT_CPLD_LPC_MUX_DEVS]; ++ struct platform_device *pdev_hotplug; ++ struct platform_device *pdev_led; ++}; ++ ++/* Regions for LPC I2C controller and LPC base register space */ ++static const 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), ++}; ++ ++/* Platform default channels */ ++static const int mlxplat_default_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 channels for MSN21xx system family */ ++static const int mlxplat_msn21xx_channels[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; ++ ++/* Platform mux data */ ++static struct i2c_mux_reg_platform_data mlxplat_mux_data[] = { ++ { ++ .parent = 1, ++ .base_nr = MLXPLAT_CPLD_CH1, ++ .write_only = 1, ++ .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG1, ++ .reg_size = 1, ++ .idle_in_use = 1, ++ }, ++ { ++ .parent = 1, ++ .base_nr = MLXPLAT_CPLD_CH2, ++ .write_only = 1, ++ .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG2, ++ .reg_size = 1, ++ .idle_in_use = 1, ++ }, ++ ++}; ++ ++/* Platform hotplug devices */ ++static struct i2c_board_info mlxplat_mlxcpld_psu[] = { ++ { ++ I2C_BOARD_INFO("24c02", 0x51), ++ }, ++ { ++ I2C_BOARD_INFO("24c02", 0x50), ++ }, ++}; ++ ++static struct i2c_board_info mlxplat_mlxcpld_pwr[] = { ++ { ++ I2C_BOARD_INFO("dps460", 0x59), ++ }, ++ { ++ I2C_BOARD_INFO("dps460", 0x58), ++ }, ++}; ++ ++static struct i2c_board_info mlxplat_mlxcpld_fan[] = { ++ { ++ I2C_BOARD_INFO("24c32", 0x50), ++ }, ++ { ++ I2C_BOARD_INFO("24c32", 0x50), ++ }, ++ { ++ I2C_BOARD_INFO("24c32", 0x50), ++ }, ++ { ++ I2C_BOARD_INFO("24c32", 0x50), ++ }, ++}; ++ ++/* Platform hotplug default data */ ++static struct mlxreg_core_data mlxplat_mlxcpld_default_psu_items_data[] = { ++ { ++ .label = "psu1", ++ .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFF, ++ .mask = BIT(0), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_psu[0], ++ .hpdev.nr = MLXPLAT_CPLD_PSU_DEFAULT_NR, ++ }, ++ { ++ .label = "psu2", ++ .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFF, ++ .mask = BIT(1), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_psu[1], ++ .hpdev.nr = MLXPLAT_CPLD_PSU_DEFAULT_NR, ++ }, ++}; ++ ++static struct mlxreg_core_data mlxplat_mlxcpld_default_pwr_items_data[] = { ++ { ++ .label = "pwr1", ++ .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFF, ++ .mask = BIT(0), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_pwr[0], ++ .hpdev.nr = MLXPLAT_CPLD_PSU_DEFAULT_NR, ++ }, ++ { ++ .label = "pwr2", ++ .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFF, ++ .mask = BIT(1), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_pwr[1], ++ .hpdev.nr = MLXPLAT_CPLD_PSU_DEFAULT_NR, ++ }, ++}; ++ ++static struct mlxreg_core_data mlxplat_mlxcpld_default_fan_items_data[] = { ++ { ++ .label = "fan1", ++ .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFF, ++ .mask = BIT(0), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_fan[0], ++ .hpdev.nr = 11, ++ }, ++ { ++ .label = "fan2", ++ .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFF, ++ .mask = BIT(1), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_fan[1], ++ .hpdev.nr = 12, ++ }, ++ { ++ .label = "fan3", ++ .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFF, ++ .mask = BIT(2), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_fan[2], ++ .hpdev.nr = 13, ++ }, ++ { ++ .label = "fan4", ++ .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFF, ++ .mask = BIT(3), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_fan[3], ++ .hpdev.nr = 14, ++ }, ++}; ++ ++static struct mlxreg_core_item mlxplat_mlxcpld_default_items[] = { ++ { ++ .data = mlxplat_mlxcpld_default_psu_items_data, ++ .aggr_mask = MLXPLAT_CPLD_AGGR_PSU_MASK_DEF, ++ .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFF, ++ .mask = MLXPLAT_CPLD_PSU_MASK, ++ .count = ARRAY_SIZE(mlxplat_mlxcpld_psu), ++ .inversed = 1, ++ .health = false, ++ }, ++ { ++ .data = mlxplat_mlxcpld_default_pwr_items_data, ++ .aggr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF, ++ .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFF, ++ .mask = MLXPLAT_CPLD_PWR_MASK, ++ .count = ARRAY_SIZE(mlxplat_mlxcpld_pwr), ++ .inversed = 0, ++ .health = false, ++ }, ++ { ++ .data = mlxplat_mlxcpld_default_fan_items_data, ++ .aggr_mask = MLXPLAT_CPLD_AGGR_FAN_MASK_DEF, ++ .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFF, ++ .mask = MLXPLAT_CPLD_FAN_MASK, ++ .count = ARRAY_SIZE(mlxplat_mlxcpld_fan), ++ .inversed = 1, ++ .health = false, ++ }, ++}; ++ ++static ++struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_default_data = { ++ .items = mlxplat_mlxcpld_default_items, ++ .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_items), ++ .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFF, ++ .mask = MLXPLAT_CPLD_AGGR_MASK_DEF, ++}; ++ ++/* Platform hotplug MSN21xx system family data */ ++static struct i2c_board_info mlxplat_mlxcpld_msn21xx_pwr[] = { ++ { ++ I2C_BOARD_INFO("holder", 0x59), ++ }, ++ { ++ I2C_BOARD_INFO("holder", 0x58), ++ }, ++}; ++ ++static struct mlxreg_core_data mlxplat_mlxcpld_msn21xx_pwr_items_data[] = { ++ { ++ .label = "pwr1", ++ .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFF, ++ .mask = BIT(0), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_msn21xx_pwr[0], ++ .hpdev.nr = MLXPLAT_CPLD_PSU_DEFAULT_NR, ++ }, ++ { ++ .label = "pwr2", ++ .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFF, ++ .mask = BIT(1), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_msn21xx_pwr[1], ++ .hpdev.nr = MLXPLAT_CPLD_PSU_DEFAULT_NR, ++ }, ++}; ++ ++static struct mlxreg_core_item mlxplat_mlxcpld_msn21xx_items[] = { ++ { ++ .data = mlxplat_mlxcpld_msn21xx_pwr_items_data, ++ .aggr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF, ++ .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFF, ++ .mask = MLXPLAT_CPLD_PWR_MASK, ++ .count = ARRAY_SIZE(mlxplat_mlxcpld_msn21xx_pwr_items_data), ++ .inversed = 0, ++ .health = false, ++ }, ++}; ++ ++static ++struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_msn21xx_data = { ++ .items = mlxplat_mlxcpld_msn21xx_items, ++ .counter = ARRAY_SIZE(mlxplat_mlxcpld_msn21xx_items), ++ .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFF, ++ .mask = MLXPLAT_CPLD_AGGR_MASK_DEF, ++ .cell_low = MLXPLAT_CPLD_LPC_REG_AGGR_LOW_OFF, ++ .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW, ++}; ++ ++/* Platform hotplug MSN201x system family data */ ++static struct mlxreg_core_data mlxplat_mlxcpld_msn201x_pwr_items_data[] = { ++ { ++ .label = "pwr1", ++ .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFF, ++ .mask = BIT(0), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_msn21xx_pwr[0], ++ .hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR, ++ }, ++ { ++ .label = "pwr2", ++ .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFF, ++ .mask = BIT(1), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_msn21xx_pwr[1], ++ .hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR, ++ }, ++}; ++ ++static struct mlxreg_core_item mlxplat_mlxcpld_msn201x_items[] = { ++ { ++ .data = mlxplat_mlxcpld_msn201x_pwr_items_data, ++ .aggr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF, ++ .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFF, ++ .mask = MLXPLAT_CPLD_PWR_MASK, ++ .count = ARRAY_SIZE(mlxplat_mlxcpld_msn201x_pwr_items_data), ++ .inversed = 0, ++ .health = false, ++ }, ++}; ++ ++static ++struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_msn201x_data = { ++ .items = mlxplat_mlxcpld_msn21xx_items, ++ .counter = ARRAY_SIZE(mlxplat_mlxcpld_msn201x_items), ++ .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFF, ++ .mask = MLXPLAT_CPLD_AGGR_MASK_DEF, ++ .cell_low = MLXPLAT_CPLD_LPC_REG_AGGR_LOW_OFF, ++ .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW, ++}; ++ ++/* Platform hotplug next generation system family data */ ++static struct i2c_board_info mlxplat_mlxcpld_ng_fan = { ++ I2C_BOARD_INFO("holder", 0x50), ++}; ++ ++static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_psu_items_data[] = { ++ { ++ .label = "psu1", ++ .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFF, ++ .mask = BIT(0), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_psu[0], ++ .hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR, ++ }, ++ { ++ .label = "psu2", ++ .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFF, ++ .mask = BIT(1), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_psu[1], ++ .hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR, ++ }, ++}; ++ ++static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_pwr_items_data[] = { ++ { ++ .label = "pwr1", ++ .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFF, ++ .mask = BIT(0), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_pwr[0], ++ .hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR, ++ }, ++ { ++ .label = "pwr2", ++ .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFF, ++ .mask = BIT(1), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_pwr[1], ++ .hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR, ++ }, ++}; ++ ++static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_fan_items_data[] = { ++ { ++ .label = "fan1", ++ .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFF, ++ .mask = BIT(0), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_ng_fan, ++ .hpdev.nr = 11, ++ }, ++ { ++ .label = "fan2", ++ .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFF, ++ .mask = BIT(1), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_ng_fan, ++ .hpdev.nr = 12, ++ }, ++ { ++ .label = "fan3", ++ .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFF, ++ .mask = BIT(2), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_ng_fan, ++ .hpdev.nr = 13, ++ }, ++ { ++ .label = "fan4", ++ .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFF, ++ .mask = BIT(3), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_ng_fan, ++ .hpdev.nr = 14, ++ }, ++ { ++ .label = "fan5", ++ .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFF, ++ .mask = BIT(3), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_ng_fan, ++ .hpdev.nr = 15, ++ }, ++ { ++ .label = "fan6", ++ .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFF, ++ .mask = BIT(3), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_ng_fan, ++ .hpdev.nr = 16, ++ }, ++}; ++ ++static struct mlxreg_core_item mlxplat_mlxcpld_default_ng_items[] = { ++ { ++ .data = mlxplat_mlxcpld_default_ng_psu_items_data, ++ .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, ++ .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFF, ++ .mask = MLXPLAT_CPLD_PSU_MASK, ++ .count = ARRAY_SIZE(mlxplat_mlxcpld_psu), ++ .inversed = 1, ++ .health = false, ++ }, ++ { ++ .data = mlxplat_mlxcpld_default_ng_pwr_items_data, ++ .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, ++ .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFF, ++ .mask = MLXPLAT_CPLD_PWR_MASK, ++ .count = ARRAY_SIZE(mlxplat_mlxcpld_pwr), ++ .inversed = 0, ++ .health = false, ++ }, ++ { ++ .data = mlxplat_mlxcpld_default_ng_fan_items_data, ++ .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, ++ .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFF, ++ .mask = MLXPLAT_CPLD_FAN_MASK, ++ .count = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_fan_items_data), ++ .inversed = 1, ++ .health = false, ++ }, ++}; ++ ++static ++struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_default_ng_data = { ++ .items = mlxplat_mlxcpld_default_ng_items, ++ .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_items), ++ .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFF, ++ .mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, ++ .cell_low = MLXPLAT_CPLD_LPC_REG_AGGR_LOW_OFF, ++ .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW, ++}; ++ ++static struct mlxreg_core_data mlxplat_mlxcpld_msn274x_fan_items_data[] = { ++ { ++ .label = "fan1", ++ .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFF, ++ .mask = BIT(0), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_ng_fan, ++ .hpdev.nr = 11, ++ }, ++ { ++ .label = "fan2", ++ .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFF, ++ .mask = BIT(1), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_ng_fan, ++ .hpdev.nr = 12, ++ }, ++ { ++ .label = "fan3", ++ .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFF, ++ .mask = BIT(2), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_ng_fan, ++ .hpdev.nr = 13, ++ }, ++ { ++ .label = "fan4", ++ .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFF, ++ .mask = BIT(3), ++ .hpdev.brdinfo = &mlxplat_mlxcpld_ng_fan, ++ .hpdev.nr = 14, ++ }, ++}; ++ ++static struct mlxreg_core_item mlxplat_mlxcpld_msn274x_items[] = { ++ { ++ .data = mlxplat_mlxcpld_default_ng_psu_items_data, ++ .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, ++ .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFF, ++ .mask = MLXPLAT_CPLD_PSU_MASK, ++ .count = ARRAY_SIZE(mlxplat_mlxcpld_psu), ++ .inversed = 1, ++ .health = false, ++ }, ++ { ++ .data = mlxplat_mlxcpld_default_ng_pwr_items_data, ++ .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, ++ .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFF, ++ .mask = MLXPLAT_CPLD_PWR_MASK, ++ .count = ARRAY_SIZE(mlxplat_mlxcpld_pwr), ++ .inversed = 0, ++ .health = false, ++ }, ++ { ++ .data = mlxplat_mlxcpld_msn274x_fan_items_data, ++ .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, ++ .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFF, ++ .mask = MLXPLAT_CPLD_FAN_MASK, ++ .count = ARRAY_SIZE(mlxplat_mlxcpld_msn274x_fan_items_data), ++ .inversed = 1, ++ .health = false, ++ }, ++}; ++ ++static ++struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_msn274x_data = { ++ .items = mlxplat_mlxcpld_msn274x_items, ++ .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_items), ++ .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFF, ++ .mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, ++ .cell_low = MLXPLAT_CPLD_LPC_REG_AGGR_LOW_OFF, ++ .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW, ++}; ++ ++/* Platform led default data */ ++static struct mlxreg_core_data mlxplat_mlxcpld_default_led_data[] = { ++ { ++ .label = "status:green", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFF, ++ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, ++ }, ++ { ++ .label = "status:red", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFF, ++ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK ++ }, ++ { ++ .label = "psu:green", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFF, ++ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, ++ }, ++ { ++ .label = "psu:red", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFF, ++ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, ++ }, ++ { ++ .label = "fan1:green", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFF, ++ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, ++ }, ++ { ++ .label = "fan1:red", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFF, ++ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, ++ }, ++ { ++ .label = "fan2:green", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFF, ++ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, ++ }, ++ { ++ .label = "fan2:red", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFF, ++ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, ++ }, ++ { ++ .label = "fan3:green", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFF, ++ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, ++ }, ++ { ++ .label = "fan3:red", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFF, ++ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, ++ }, ++ { ++ .label = "fan4:green", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFF, ++ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, ++ }, ++ { ++ .label = "fan4:red", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFF, ++ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, ++ }, ++}; ++ ++static struct mlxreg_core_led_platform_data mlxplat_default_led_data = { ++ .data = mlxplat_mlxcpld_default_led_data, ++ .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_led_data), ++}; ++ ++/* Platform led MSN21xx system family data */ ++static struct mlxreg_core_data mlxplat_mlxcpld_msn21xx_led_data[] = { ++ { ++ .label = "status:green", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFF, ++ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, ++ }, ++ { ++ .label = "status:red", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFF, ++ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK ++ }, ++ { ++ .label = "fan:green", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFF, ++ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, ++ }, ++ { ++ .label = "fan:red", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFF, ++ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, ++ }, ++ { ++ .label = "psu1:green", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFF, ++ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, ++ }, ++ { ++ .label = "psu1:red", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFF, ++ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, ++ }, ++ { ++ .label = "psu2:green", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFF, ++ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, ++ }, ++ { ++ .label = "psu2:red", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFF, ++ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, ++ }, ++ { ++ .label = "uid:blue", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED5_OFF, ++ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, ++ }, ++}; ++ ++static struct mlxreg_core_led_platform_data mlxplat_msn21xx_led_data = { ++ .data = mlxplat_mlxcpld_msn21xx_led_data, ++ .counter = ARRAY_SIZE(mlxplat_mlxcpld_msn21xx_led_data), ++}; ++ ++/* Platform led for default data for 200GbE systems */ ++static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_led_data[] = { ++ { ++ .label = "status:green", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFF, ++ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, ++ }, ++ { ++ .label = "status:orange", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFF, ++ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK ++ }, ++ { ++ .label = "psu:green", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFF, ++ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, ++ }, ++ { ++ .label = "psu:orange", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFF, ++ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, ++ }, ++ { ++ .label = "fan1:green", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFF, ++ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, ++ }, ++ { ++ .label = "fan1:orange", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFF, ++ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, ++ }, ++ { ++ .label = "fan2:green", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFF, ++ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, ++ }, ++ { ++ .label = "fan2:orange", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFF, ++ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, ++ }, ++ { ++ .label = "fan3:green", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFF, ++ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, ++ }, ++ { ++ .label = "fan3:orange", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFF, ++ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, ++ }, ++ { ++ .label = "fan4:green", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFF, ++ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, ++ }, ++ { ++ .label = "fan4:orange", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFF, ++ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, ++ }, ++ { ++ .label = "fan5:green", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFF, ++ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, ++ }, ++ { ++ .label = "fan5:orange", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFF, ++ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, ++ }, ++ { ++ .label = "fan6:green", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFF, ++ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, ++ }, ++ { ++ .label = "fan6:orange", ++ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFF, ++ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, ++ }, ++}; ++ ++static struct mlxreg_core_led_platform_data mlxplat_default_ng_led_data = { ++ .data = mlxplat_mlxcpld_default_ng_led_data, ++ .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_led_data), ++}; ++ ++static int ++mlxplat_mlxcpld_reg_read(void *context, unsigned int reg, unsigned int *val) ++{ ++ *val = ioread8(context + reg); ++ return 0; ++} ++ ++static int ++mlxplat_mlxcpld_reg_write(void *context, unsigned int reg, unsigned int val) ++{ ++ iowrite8(val, context + reg); ++ return 0; ++} ++ ++const struct regmap_config mlxplat_mlxcpld_regmap_config = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .max_register = 255, ++ .reg_read = mlxplat_mlxcpld_reg_read, ++ .reg_write = mlxplat_mlxcpld_reg_write, ++}; ++ ++static struct resource mlxplat_mlxcpld_resources[] = { ++ [0] = DEFINE_RES_IRQ_NAMED(17, "mlxreg-hotplug"), ++}; ++ ++struct platform_device *mlxplat_dev; ++struct mlxreg_core_hotplug_platform_data *mlxplat_hotplug; ++struct mlxreg_core_led_platform_data *mlxplat_led; ++ ++static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) { ++ mlxplat_mux_data[i].values = mlxplat_default_channels[i]; ++ mlxplat_mux_data[i].n_values = ++ ARRAY_SIZE(mlxplat_default_channels[i]); ++ } ++ mlxplat_hotplug = &mlxplat_mlxcpld_default_data; ++ mlxplat_led = &mlxplat_default_led_data; ++ ++ return 1; ++}; ++ ++static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) { ++ mlxplat_mux_data[i].values = mlxplat_msn21xx_channels; ++ mlxplat_mux_data[i].n_values = ++ ARRAY_SIZE(mlxplat_msn21xx_channels); ++ } ++ mlxplat_hotplug = &mlxplat_mlxcpld_msn21xx_data; ++ mlxplat_led = &mlxplat_msn21xx_led_data; ++ ++ return 1; ++}; ++ ++static int __init mlxplat_dmi_msn274x_matched(const struct dmi_system_id *dmi) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) { ++ mlxplat_mux_data[i].values = mlxplat_msn21xx_channels; ++ mlxplat_mux_data[i].n_values = ++ ARRAY_SIZE(mlxplat_msn21xx_channels); ++ } ++ mlxplat_hotplug = &mlxplat_mlxcpld_msn274x_data; ++ mlxplat_led = &mlxplat_default_led_data; ++ ++ return 1; ++}; ++ ++static int __init mlxplat_dmi_qmb7xx_matched(const struct dmi_system_id *dmi) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) { ++ mlxplat_mux_data[i].values = mlxplat_msn21xx_channels; ++ mlxplat_mux_data[i].n_values = ++ ARRAY_SIZE(mlxplat_msn21xx_channels); ++ } ++ mlxplat_hotplug = &mlxplat_mlxcpld_default_ng_data; ++ mlxplat_led = &mlxplat_default_ng_led_data; ++ ++ return 1; ++}; ++ ++static int __init mlxplat_dmi_msn201x_matched(const struct dmi_system_id *dmi) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) { ++ mlxplat_mux_data[i].values = mlxplat_msn21xx_channels; ++ mlxplat_mux_data[i].n_values = ++ ARRAY_SIZE(mlxplat_msn21xx_channels); ++ } ++ mlxplat_hotplug = &mlxplat_mlxcpld_msn201x_data; ++ mlxplat_led = &mlxplat_msn21xx_led_data; ++ ++ return 1; ++}; ++ ++static struct dmi_system_id mlxplat_dmi_table[] __initdata = { ++ { ++ .callback = mlxplat_dmi_msn274x_matched, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "MSN274"), ++ }, ++ }, ++ { ++ .callback = mlxplat_dmi_default_matched, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "MSN24"), ++ }, ++ }, ++ { ++ .callback = mlxplat_dmi_default_matched, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "MSN27"), ++ }, ++ }, ++ { ++ .callback = mlxplat_dmi_default_matched, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "MSB"), ++ }, ++ }, ++ { ++ .callback = mlxplat_dmi_default_matched, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "MSX"), ++ }, ++ }, ++ { ++ .callback = mlxplat_dmi_msn21xx_matched, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "MSN21"), ++ }, ++ }, ++ { ++ .callback = mlxplat_dmi_msn201x_matched, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "MSN201"), ++ }, ++ }, ++ { ++ .callback = mlxplat_dmi_qmb7xx_matched, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "QMB7"), ++ }, ++ }, ++ { ++ .callback = mlxplat_dmi_qmb7xx_matched, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "SN37"), ++ }, ++ }, ++ { ++ .callback = mlxplat_dmi_qmb7xx_matched, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "SN34"), ++ }, ++ }, ++ { } ++}; ++ ++static int __init mlxplat_init(void) ++{ ++ struct mlxplat_priv *priv; ++ void __iomem *base; ++ int i, err = 0; ++ ++ if (!dmi_check_system(mlxplat_dmi_table)) ++ return -ENODEV; ++ ++ mlxplat_dev = platform_device_register_simple(MLX_PLAT_DEVICE_NAME, -1, ++ mlxplat_lpc_resources, ++ ARRAY_SIZE(mlxplat_lpc_resources)); ++ ++ if (IS_ERR(mlxplat_dev)) ++ return PTR_ERR(mlxplat_dev); ++ ++ priv = devm_kzalloc(&mlxplat_dev->dev, sizeof(struct mlxplat_priv), ++ GFP_KERNEL); ++ if (!priv) { ++ err = -ENOMEM; ++ goto fail_alloc; ++ } ++ platform_set_drvdata(mlxplat_dev, priv); ++ ++ 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++) { ++ priv->pdev_mux[i] = platform_device_register_resndata( ++ &mlxplat_dev->dev, ++ "i2c-mux-reg", i, NULL, ++ 0, &mlxplat_mux_data[i], ++ sizeof(mlxplat_mux_data[i])); ++ if (IS_ERR(priv->pdev_mux[i])) { ++ err = PTR_ERR(priv->pdev_mux[i]); ++ goto fail_platform_mux_register; ++ } ++ } ++ ++ base = devm_ioport_map(&mlxplat_dev->dev, ++ mlxplat_lpc_resources[1].start, 1); ++ if (IS_ERR(base)) ++ goto fail_platform_mux_register; ++ ++ mlxplat_hotplug->regmap = devm_regmap_init(&mlxplat_dev->dev, NULL, ++ base, &mlxplat_mlxcpld_regmap_config); ++ if (IS_ERR(mlxplat_hotplug->regmap)) ++ goto fail_platform_mux_register; ++ ++ priv->pdev_hotplug = platform_device_register_resndata( ++ &mlxplat_dev->dev, "mlxreg-hotplug", ++ PLATFORM_DEVID_NONE, ++ mlxplat_mlxcpld_resources, ++ ARRAY_SIZE(mlxplat_mlxcpld_resources), ++ mlxplat_hotplug, sizeof(*mlxplat_hotplug)); ++ if (IS_ERR(priv->pdev_hotplug)) { ++ err = PTR_ERR(priv->pdev_hotplug); ++ goto fail_platform_mux_register; ++ } ++ ++ mlxplat_led->regmap = mlxplat_hotplug->regmap; ++ priv->pdev_led = platform_device_register_resndata( ++ &mlxplat_dev->dev, "leds-mlxreg", ++ PLATFORM_DEVID_NONE, NULL, 0, ++ mlxplat_led, sizeof(*mlxplat_led)); ++ if (IS_ERR(priv->pdev_led)) { ++ err = PTR_ERR(priv->pdev_led); ++ goto fail_platform_hotplug_register; ++ } ++ ++ return 0; ++ ++fail_platform_hotplug_register: ++ platform_device_unregister(priv->pdev_hotplug); ++fail_platform_mux_register: ++ while (--i >= 0) ++ platform_device_unregister(priv->pdev_mux[i]); ++ platform_device_unregister(priv->pdev_i2c); ++fail_alloc: ++ platform_device_unregister(mlxplat_dev); ++ ++ return err; ++} ++module_init(mlxplat_init); ++ ++static void __exit mlxplat_exit(void) ++{ ++ struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev); ++ int i; ++ ++ platform_device_unregister(priv->pdev_led); ++ platform_device_unregister(priv->pdev_hotplug); ++ ++ for (i = ARRAY_SIZE(mlxplat_mux_data) - 1; i >= 0 ; i--) ++ platform_device_unregister(priv->pdev_mux[i]); ++ ++ platform_device_unregister(priv->pdev_i2c); ++ platform_device_unregister(mlxplat_dev); ++} ++module_exit(mlxplat_exit); ++ ++MODULE_AUTHOR("Vadim Pasternak (vadimp@mellanox.com)"); ++MODULE_DESCRIPTION("Mellanox platform driver"); ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_ALIAS("dmi:*:*Mellanox*:MSN24*:"); ++MODULE_ALIAS("dmi:*:*Mellanox*:MSN27*:"); ++MODULE_ALIAS("dmi:*:*Mellanox*:MSB*:"); ++MODULE_ALIAS("dmi:*:*Mellanox*:MSX*:"); ++MODULE_ALIAS("dmi:*:*Mellanox*:MSN21*:"); ++MODULE_ALIAS("dmi:*:*Mellanox*MSN274*:"); ++MODULE_ALIAS("dmi:*:*Mellanox*MSN201*:"); ++MODULE_ALIAS("dmi:*:*Mellanox*QMB7*:"); ++MODULE_ALIAS("dmi:*:*Mellanox*SN37*:"); ++MODULE_ALIAS("dmi:*:*Mellanox*QM34*:"); diff --git a/patch/0004-platform-x86-Introduce-support-for-Mellanox-hotplug-.patch b/patch/0004-platform-x86-Introduce-support-for-Mellanox-hotplug-.patch new file mode 100644 index 000000000..58973010c --- /dev/null +++ b/patch/0004-platform-x86-Introduce-support-for-Mellanox-hotplug-.patch @@ -0,0 +1,905 @@ +Linux backport patch. Includes following commits: +2926024b5081fc8d4b086677bafa1ac55ea0b911 +6124fdf76488681713f278f3fdf2ba2dfe760211 +c84002d15210ca130263e23911cc399202124eb4 +07b89c2b2a5e8ce30166b96f87b324c6b419f108 +91973760712f350048a0fa8e0363e260bf874313 +c2e714e56360e34f88e0a75ee74e467d8b82de75 +af4779be0f2cec63f4cb15d3db78c5de3523756a +d53bc5dc941653f0ed93b11a647bd6ff40f40ef2 + + +diff -Nur a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig +--- a/drivers/platform/mellanox/Kconfig 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/platform/mellanox/Kconfig 2017-11-12 08:54:58.200076777 +0000 +@@ -0,0 +1,25 @@ ++# ++# Platform support for Mellanox hardware ++# ++ ++menuconfig MELLANOX_PLATFORM ++ bool "Platform support for Mellanox hardware" ++ depends on X86 || ARM || COMPILE_TEST ++ ---help--- ++ Say Y here to get to see options for platform support for ++ Mellanox systems. This option alone does not add any kernel code. ++ ++ If you say N, all options in this submenu will be skipped and disabled. ++ ++if MELLANOX_PLATFORM ++ ++config MLXREG_HOTPLUG ++ tristate "Mellanox platform hotplug driver support" ++ depends on REGMAP ++ depends on HWMON ++ depends on I2C ++ ---help--- ++ This driver handles hot-plug events for the power suppliers, power ++ cables and fans on the wide range Mellanox IB and Ethernet systems. ++ ++endif # MELLANOX_PLATFORM +diff -Nur a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile +--- a/drivers/platform/mellanox/Makefile 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/platform/mellanox/Makefile 2017-11-12 08:54:58.200076777 +0000 +@@ -0,0 +1 @@ ++obj-$(CONFIG_MLXREG_HOTPLUG) += mlxreg-hotplug.o +diff -Nur a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c +--- a/drivers/platform/mellanox/mlxreg-hotplug.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/platform/mellanox/mlxreg-hotplug.c 2017-11-12 08:54:58.200076777 +0000 +@@ -0,0 +1,710 @@ ++/* ++ * Copyright (c) 2017 Mellanox Technologies. All rights reserved. ++ * Copyright (c) 2017 Vadim Pasternak ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Offset of event and mask registers from status register. */ ++#define MLXREG_HOTPLUG_EVENT_OFF 1 ++#define MLXREG_HOTPLUG_MASK_OFF 2 ++#define MLXREG_HOTPLUG_AGGR_MASK_OFF 1 ++ ++/* ASIC health parameters. */ ++#define MLXREG_HOTPLUG_HEALTH_MASK 0x02 ++#define MLXREG_HOTPLUG_RST_CNTR 3 ++ ++#define MLXREG_HOTPLUG_PROP_OKAY "okay" ++#define MLXREG_HOTPLUG_PROP_DISABLED "disabled" ++#define MLXREG_HOTPLUG_PROP_STATUS "status" ++ ++#define MLXREG_HOTPLUG_ATTRS_MAX 24 ++ ++/** ++ * struct mlxreg_hotplug_priv_data - platform private data: ++ * @irq: platform device interrupt number; ++ * @pdev: platform device; ++ * @plat: platform data; ++ * @dwork: delayed work template; ++ * @lock: spin lock; ++ * @hwmon: hwmon device; ++ * @mlxreg_hotplug_attr: sysfs attributes array; ++ * @mlxreg_hotplug_dev_attr: sysfs sensor device attribute array; ++ * @group: sysfs attribute group; ++ * @groups: list of sysfs attribute group for hwmon registration; ++ * @cell: location of top aggregation interrupt register; ++ * @mask: top aggregation interrupt common mask; ++ * @aggr_cache: last value of aggregation register status; ++ */ ++struct mlxreg_hotplug_priv_data { ++ int irq; ++ struct device *dev; ++ struct platform_device *pdev; ++ struct mlxreg_hotplug_platform_data *plat; ++ struct regmap *regmap; ++ struct delayed_work dwork_irq; ++ struct delayed_work dwork; ++ spinlock_t lock; /* sync with interrupt */ ++ struct device *hwmon; ++ struct attribute *mlxreg_hotplug_attr[MLXREG_HOTPLUG_ATTRS_MAX + 1]; ++ struct sensor_device_attribute_2 ++ mlxreg_hotplug_dev_attr[MLXREG_HOTPLUG_ATTRS_MAX]; ++ struct attribute_group group; ++ const struct attribute_group *groups[2]; ++ u32 cell; ++ u32 mask; ++ u32 aggr_cache; ++ bool after_probe; ++}; ++ ++#if defined(CONFIG_OF_DYNAMIC) ++/** ++ * struct mlxreg_hotplug_device_en - Open Firmware property for enabling device ++ * ++ * @name - property name; ++ * @value - property value string; ++ * @length - length of proprty value string; ++ * ++ * The structure is used for the devices, which require some dynamic ++ * selection operation allowing access to them. ++ */ ++static struct property mlxreg_hotplug_device_en = { ++ .name = MLXREG_HOTPLUG_PROP_STATUS, ++ .value = MLXREG_HOTPLUG_PROP_OKAY, ++ .length = sizeof(MLXREG_HOTPLUG_PROP_OKAY), ++}; ++ ++/** ++ * struct mlxreg_hotplug_device_dis - Open Firmware property for disabling ++ * device ++ * ++ * @name - property name; ++ * @value - property value string; ++ * @length - length of proprty value string; ++ * ++ * The structure is used for the devices, which require some dynamic ++ * selection operation disallowing access to them. ++ */ ++static struct property mlxreg_hotplug_device_dis = { ++ .name = MLXREG_HOTPLUG_PROP_STATUS, ++ .value = MLXREG_HOTPLUG_PROP_DISABLED, ++ .length = sizeof(MLXREG_HOTPLUG_PROP_DISABLED), ++}; ++ ++static int mlxreg_hotplug_of_device_create(struct mlxreg_core_data *data) ++{ ++ return of_update_property(data->np, &mlxreg_hotplug_device_en); ++} ++ ++static void mlxreg_hotplug_of_device_destroy(struct mlxreg_core_data *data) ++{ ++ of_update_property(data->np, &mlxreg_hotplug_device_dis); ++ of_node_clear_flag(data->np, OF_POPULATED); ++} ++#else ++static int mlxreg_hotplug_of_device_create(struct mlxreg_core_data *data) ++{ ++ return 0; ++} ++ ++static void mlxreg_hotplug_of_device_destroy(struct mlxreg_core_data *data) ++{ ++} ++#endif ++ ++static int mlxreg_hotplug_device_create(struct mlxreg_core_data *data) ++{ ++ data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr); ++ if (!data->hpdev.adapter) ++ return -EFAULT; ++ ++ data->hpdev.client = i2c_new_device(data->hpdev.adapter, ++ data->hpdev.brdinfo); ++ if (!data->hpdev.client) { ++ i2c_put_adapter(data->hpdev.adapter); ++ data->hpdev.adapter = NULL; ++ return -EFAULT; ++ } ++ ++ return 0; ++} ++ ++static void mlxreg_hotplug_device_destroy(struct mlxreg_core_data *data) ++{ ++ if (data->hpdev.client) { ++ i2c_unregister_device(data->hpdev.client); ++ data->hpdev.client = NULL; ++ } ++ ++ if (data->hpdev.adapter) { ++ i2c_put_adapter(data->hpdev.adapter); ++ data->hpdev.adapter = NULL; ++ } ++} ++ ++static int mlxreg_hotplug_dev_enable(struct mlxreg_core_data *data) ++{ ++ int err; ++ ++ /* Enable and create device. */ ++ if (data->np) ++ err = mlxreg_hotplug_of_device_create(data); ++ else ++ err = mlxreg_hotplug_device_create(data); ++ ++ return err; ++} ++ ++static void mlxreg_hotplug_dev_disable(struct mlxreg_core_data *data) ++{ ++ /* Disable and unregister platform device. */ ++ if (data->np) ++ mlxreg_hotplug_of_device_destroy(data); ++ else ++ mlxreg_hotplug_device_destroy(data); ++} ++ ++static ssize_t mlxreg_hotplug_attr_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct mlxreg_hotplug_priv_data *priv = dev_get_drvdata(dev); ++ struct mlxreg_core_hotplug_platform_data *pdata; ++ int index = to_sensor_dev_attr_2(attr)->index; ++ int nr = to_sensor_dev_attr_2(attr)->nr; ++ struct mlxreg_core_item *item; ++ struct mlxreg_core_data *data; ++ u32 regval; ++ int ret; ++ ++ pdata = dev_get_platdata(&priv->pdev->dev); ++ item = pdata->items + nr; ++ data = item->data + index; ++ ++ ret = regmap_read(priv->regmap, data->reg, ®val); ++ if (ret) ++ return ret; ++ ++ if (item->health) { ++ regval &= data->mask; ++ } else { ++ /* Bit = 0 : functional if item->inversed is true. */ ++ if (item->inversed) ++ regval = !(regval & data->mask); ++ else ++ regval = !!(regval & data->mask); ++ } ++ ++ return sprintf(buf, "%u\n", regval); ++} ++ ++#define PRIV_ATTR(i) priv->mlxreg_hotplug_attr[i] ++#define PRIV_DEV_ATTR(i) priv->mlxreg_hotplug_dev_attr[i] ++ ++static int mlxreg_hotplug_attr_init(struct mlxreg_hotplug_priv_data *priv) ++{ ++ struct mlxreg_core_hotplug_platform_data *pdata; ++ struct mlxreg_core_item *item; ++ struct mlxreg_core_data *data; ++ int num_attrs = 0, id = 0, i, j; ++ ++ pdata = dev_get_platdata(&priv->pdev->dev); ++ item = pdata->items; ++ ++ /* Go over all kinds of items - psu, pwr, fan. */ ++ for (i = 0; i < pdata->counter; i++, item++) { ++ num_attrs += item->count; ++ data = item->data; ++ /* Go over all units within the item. */ ++ for (j = 0; j < item->count; j++, data++, id++) { ++ PRIV_ATTR(id) = &PRIV_DEV_ATTR(id).dev_attr.attr; ++ PRIV_ATTR(id)->name = devm_kasprintf(&priv->pdev->dev, ++ GFP_KERNEL, ++ data->label); ++ ++ if (!PRIV_ATTR(id)->name) { ++ dev_err(priv->dev, "Memory allocation failed for attr %d.\n", ++ id); ++ return -ENOMEM; ++ } ++ ++ PRIV_DEV_ATTR(id).dev_attr.attr.name = ++ PRIV_ATTR(id)->name; ++ PRIV_DEV_ATTR(id).dev_attr.attr.mode = 0444; ++ PRIV_DEV_ATTR(id).dev_attr.show = ++ mlxreg_hotplug_attr_show; ++ PRIV_DEV_ATTR(id).nr = i; ++ PRIV_DEV_ATTR(id).index = j; ++ sysfs_attr_init(&PRIV_DEV_ATTR(id).dev_attr.attr); ++ } ++ } ++ ++ priv->group.attrs = devm_kzalloc(&priv->pdev->dev, num_attrs * ++ sizeof(struct attribute *), ++ GFP_KERNEL); ++ if (!priv->group.attrs) ++ return -ENOMEM; ++ ++ priv->group.attrs = priv->mlxreg_hotplug_attr; ++ priv->groups[0] = &priv->group; ++ priv->groups[1] = NULL; ++ ++ return 0; ++} ++ ++static void ++mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data *priv, ++ struct mlxreg_core_item *item) ++{ ++ struct mlxreg_core_data *data; ++ u32 asserted, regval, bit; ++ int ret; ++ ++ /* ++ * Validate if item related to received signal type is valid. ++ * It should never happen, excepted the situation when some ++ * piece of hardware is broken. In such situation just produce ++ * error message and return. Caller must continue to handle the ++ * signals from other devices if any. ++ */ ++ if (unlikely(!item)) { ++ dev_err(priv->dev, "False signal: at offset:mask 0x%02x:0x%02x.\n", ++ item->reg, item->mask); ++ ++ return; ++ } ++ ++ /* Mask event. */ ++ ret = regmap_write(priv->regmap, item->reg + MLXREG_HOTPLUG_MASK_OFF, ++ 0); ++ if (ret) ++ goto access_error; ++ ++ /* Read status. */ ++ ret = regmap_read(priv->regmap, item->reg, ®val); ++ if (ret) ++ goto access_error; ++ ++ /* Set asserted bits and save last status. */ ++ regval &= item->mask; ++ asserted = item->cache ^ regval; ++ item->cache = regval; ++ ++ for_each_set_bit(bit, (unsigned long *)&asserted, 8) { ++ data = item->data + bit; ++ if (regval & BIT(bit)) { ++ if (item->inversed) ++ mlxreg_hotplug_dev_disable(data); ++ else ++ mlxreg_hotplug_dev_enable(data); ++ } else { ++ if (item->inversed) ++ mlxreg_hotplug_dev_enable(data); ++ else ++ mlxreg_hotplug_dev_disable(data); ++ } ++ } ++ ++ /* Acknowledge event. */ ++ ret = regmap_write(priv->regmap, item->reg + MLXREG_HOTPLUG_EVENT_OFF, ++ 0); ++ if (ret) ++ goto access_error; ++ ++ /* Unmask event. */ ++ ret = regmap_write(priv->regmap, item->reg + MLXREG_HOTPLUG_MASK_OFF, ++ item->mask); ++ if (ret) ++ goto access_error; ++ ++ return; ++ ++access_error: ++ dev_err(priv->dev, "Failed to complete workqueue.\n"); ++} ++ ++static void ++mlxreg_hotplug_health_work_helper(struct mlxreg_hotplug_priv_data *priv, ++ struct mlxreg_core_item *item) ++{ ++ struct mlxreg_core_data *data = item->data; ++ u32 regval; ++ int i, ret; ++ ++ for (i = 0; i < item->count; i++, data++) { ++ /* Mask event. */ ++ ret = regmap_write(priv->regmap, data->reg + ++ MLXREG_HOTPLUG_MASK_OFF, 0); ++ if (ret) ++ goto access_error; ++ ++ /* Read status. */ ++ ret = regmap_read(priv->regmap, data->reg, ®val); ++ if (ret) ++ goto access_error; ++ ++ regval &= data->mask; ++ item->cache = regval; ++ if (regval == MLXREG_HOTPLUG_HEALTH_MASK) { ++ if ((data->health_cntr++ == MLXREG_HOTPLUG_RST_CNTR) || ++ !priv->after_probe) { ++ mlxreg_hotplug_dev_enable(data); ++ data->attached = true; ++ } ++ } else { ++ if (data->attached) { ++ mlxreg_hotplug_dev_disable(data); ++ data->attached = false; ++ data->health_cntr = 0; ++ } ++ } ++ ++ /* Acknowledge event. */ ++ ret = regmap_write(priv->regmap, data->reg + ++ MLXREG_HOTPLUG_EVENT_OFF, 0); ++ if (ret) ++ goto access_error; ++ ++ /* Unmask event. */ ++ ret = regmap_write(priv->regmap, data->reg + ++ MLXREG_HOTPLUG_MASK_OFF, data->mask); ++ if (ret) ++ goto access_error; ++ } ++ ++ return; ++ ++access_error: ++ dev_err(priv->dev, "Failed to complete workqueue.\n"); ++} ++ ++/* ++ * mlxreg_hotplug_work_handler - performs traversing of device interrupt ++ * registers according to the below hierarchy schema: ++ * ++ * Aggregation registers (status/mask) ++ * PSU registers: *---* ++ * *-----------------* | | ++ * |status/event/mask|-----> | * | ++ * *-----------------* | | ++ * Power registers: | | ++ * *-----------------* | | ++ * |status/event/mask|-----> | * | ++ * *-----------------* | | ++ * FAN registers: | |--> CPU ++ * *-----------------* | | ++ * |status/event/mask|-----> | * | ++ * *-----------------* | | ++ * ASIC registers: | | ++ * *-----------------* | | ++ * |status/event/mask|-----> | * | ++ * *-----------------* | | ++ * *---* ++ * ++ * In case some system changed are detected: FAN in/out, PSU in/out, power ++ * cable attached/detached, ASIC helath good/bad, relevant device is created ++ * or destroyed. ++ */ ++static void mlxreg_hotplug_work_handler(struct work_struct *work) ++{ ++ struct mlxreg_hotplug_priv_data *priv = container_of(work, ++ struct mlxreg_hotplug_priv_data, dwork_irq.work); ++ struct mlxreg_core_hotplug_platform_data *pdata; ++ struct mlxreg_core_item *item; ++ unsigned long flags; ++ u32 regval, aggr_asserted; ++ int i; ++ int ret; ++ ++ pdata = dev_get_platdata(&priv->pdev->dev); ++ item = pdata->items; ++ /* Mask aggregation event. */ ++ ret = regmap_write(priv->regmap, pdata->cell + ++ MLXREG_HOTPLUG_AGGR_MASK_OFF, 0); ++ if (ret < 0) ++ goto access_error; ++ ++ /* Read aggregation status. */ ++ ret = regmap_read(priv->regmap, pdata->cell, ®val); ++ if (ret) ++ goto access_error; ++ ++ regval &= pdata->mask; ++ aggr_asserted = priv->aggr_cache ^ regval; ++ priv->aggr_cache = regval; ++ ++ /* Handle topology and health configuration changes. */ ++ for (i = 0; i < pdata->counter; i++, item++) { ++ if (aggr_asserted & item->aggr_mask) { ++ if (item->health) ++ mlxreg_hotplug_health_work_helper(priv, item); ++ else ++ mlxreg_hotplug_work_helper(priv, item); ++ } ++ } ++ ++ if (aggr_asserted) { ++ spin_lock_irqsave(&priv->lock, flags); ++ ++ /* ++ * It is possible, that some signals have been inserted, while ++ * interrupt has been masked by mlxreg_hotplug_work_handler. ++ * In this case such signals will be missed. In order to handle ++ * these signals delayed work is canceled and work task ++ * re-scheduled for immediate execution. It allows to handle ++ * missed signals, if any. In other case work handler just ++ * validates that no new signals have been received during ++ * masking. ++ */ ++ cancel_delayed_work(&priv->dwork_irq); ++ schedule_delayed_work(&priv->dwork_irq, 0); ++ ++ spin_unlock_irqrestore(&priv->lock, flags); ++ ++ return; ++ } ++ ++ /* Unmask aggregation event (no need acknowledge). */ ++ ret = regmap_write(priv->regmap, pdata->cell + ++ MLXREG_HOTPLUG_AGGR_MASK_OFF, pdata->mask); ++ if (ret) ++ goto access_error; ++ ++ return; ++ ++access_error: ++ dev_err(priv->dev, "Failed to complete workqueue.\n"); ++} ++ ++static int mlxreg_hotplug_set_irq(struct mlxreg_hotplug_priv_data *priv) ++{ ++ struct mlxreg_core_hotplug_platform_data *pdata; ++ struct mlxreg_core_item *item; ++ int i; ++ int ret; ++ ++ pdata = dev_get_platdata(&priv->pdev->dev); ++ item = pdata->items; ++ ++ for (i = 0; i < pdata->counter; i++, item++) { ++ /* Clear group presense event. */ ++ ret = regmap_write(priv->regmap, item->reg + ++ MLXREG_HOTPLUG_EVENT_OFF, 0); ++ if (ret) ++ goto access_error; ++ ++ /* Set group initial status as mask and unmask group event. */ ++ if (item->inversed) { ++ item->cache = item->mask; ++ ret = regmap_write(priv->regmap, item->reg + ++ MLXREG_HOTPLUG_MASK_OFF, ++ item->mask); ++ if (ret) ++ goto access_error; ++ } ++ } ++ ++ /* Keep aggregation initial status as zero and unmask events. */ ++ ret = regmap_write(priv->regmap, pdata->cell + ++ MLXREG_HOTPLUG_AGGR_MASK_OFF, pdata->mask); ++ if (ret) ++ goto access_error; ++ ++ /* Keep low aggregation initial status as zero and unmask events. */ ++ ret = regmap_write(priv->regmap, pdata->cell_low + ++ MLXREG_HOTPLUG_AGGR_MASK_OFF, pdata->mask_low); ++ if (ret) ++ goto access_error; ++ ++ /* Invoke work handler for initializing hot plug devices setting. */ ++ mlxreg_hotplug_work_handler(&priv->dwork_irq.work); ++ ++ enable_irq(priv->irq); ++ ++ return 0; ++ ++access_error: ++ dev_err(priv->dev, "Failed to set interrupts.\n"); ++ ++ enable_irq(priv->irq); ++ ++ return ret; ++} ++ ++static void mlxreg_hotplug_unset_irq(struct mlxreg_hotplug_priv_data *priv) ++{ ++ struct mlxreg_core_hotplug_platform_data *pdata; ++ struct mlxreg_core_item *item; ++ struct mlxreg_core_data *data; ++ int count, i, j; ++ ++ pdata = dev_get_platdata(&priv->pdev->dev); ++ item = pdata->items; ++ disable_irq(priv->irq); ++ cancel_delayed_work_sync(&priv->dwork_irq); ++ ++ /* Mask low aggregation event. */ ++ regmap_write(priv->regmap, pdata->cell_low + ++ MLXREG_HOTPLUG_AGGR_MASK_OFF, 0); ++ ++ /* Mask aggregation event. */ ++ regmap_write(priv->regmap, pdata->cell + MLXREG_HOTPLUG_AGGR_MASK_OFF, ++ 0); ++ ++ /* Clear topology configurations. */ ++ for (i = 0; i < pdata->counter; i++, item++) { ++ data = item->data; ++ /* Mask group presense event. */ ++ regmap_write(priv->regmap, data->reg + MLXREG_HOTPLUG_MASK_OFF, ++ 0); ++ /* Clear group presense event. */ ++ regmap_write(priv->regmap, data->reg + ++ MLXREG_HOTPLUG_EVENT_OFF, 0); ++ ++ /* Remove all the attached devices in group. */ ++ count = item->count; ++ for (j = 0; j < count; j++, data++) ++ mlxreg_hotplug_dev_disable(data); ++ } ++} ++ ++static irqreturn_t mlxreg_hotplug_irq_handler(int irq, void *dev) ++{ ++ struct mlxreg_hotplug_priv_data *priv = ++ (struct mlxreg_hotplug_priv_data *)dev; ++ ++ /* Schedule work task for immediate execution.*/ ++ schedule_delayed_work(&priv->dwork_irq, 0); ++ ++ return IRQ_HANDLED; ++} ++ ++static int mlxreg_hotplug_probe(struct platform_device *pdev) ++{ ++ struct mlxreg_core_hotplug_platform_data *pdata; ++ struct mlxreg_hotplug_priv_data *priv; ++ int err; ++ ++ pdata = dev_get_platdata(&pdev->dev); ++ if (!pdata) { ++ dev_err(&pdev->dev, "Failed to get platform data.\n"); ++ return -EINVAL; ++ } ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ if (pdata->irq) { ++ priv->irq = pdata->irq; ++ } else { ++ priv->irq = platform_get_irq(pdev, 0); ++ if (priv->irq < 0) { ++ dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ++ priv->irq); ++ return priv->irq; ++ } ++ } ++ ++ priv->regmap = pdata->regmap; ++ priv->dev = pdev->dev.parent; ++ priv->pdev = pdev; ++ ++ err = devm_request_irq(&pdev->dev, priv->irq, ++ mlxreg_hotplug_irq_handler, IRQF_TRIGGER_FALLING ++ | IRQF_SHARED, "mlxreg-hotplug", priv); ++ if (err) { ++ dev_err(&pdev->dev, "Failed to request irq: %d\n", err); ++ return err; ++ } ++ ++ disable_irq(priv->irq); ++ spin_lock_init(&priv->lock); ++ INIT_DELAYED_WORK(&priv->dwork_irq, mlxreg_hotplug_work_handler); ++ /* Perform initial interrupts setup. */ ++ mlxreg_hotplug_set_irq(priv); ++ ++ priv->after_probe = true; ++ dev_set_drvdata(&pdev->dev, priv); ++ ++ err = mlxreg_hotplug_attr_init(priv); ++ if (err) { ++ dev_err(&pdev->dev, "Failed to allocate attributes: %d\n", ++ err); ++ return err; ++ } ++ ++ priv->hwmon = devm_hwmon_device_register_with_groups(&pdev->dev, ++ "mlxreg_hotplug", priv, priv->groups); ++ if (IS_ERR(priv->hwmon)) { ++ dev_err(&pdev->dev, "Failed to register hwmon device %ld\n", ++ PTR_ERR(priv->hwmon)); ++ return PTR_ERR(priv->hwmon); ++ } ++ ++ return 0; ++} ++ ++static int mlxreg_hotplug_remove(struct platform_device *pdev) ++{ ++ struct mlxreg_hotplug_priv_data *priv = dev_get_drvdata(&pdev->dev); ++ ++ /* Clean interrupts setup. */ ++ mlxreg_hotplug_unset_irq(priv); ++ ++ return 0; ++} ++ ++static struct platform_driver mlxreg_hotplug_driver = { ++ .driver = { ++ .name = "mlxreg-hotplug", ++ }, ++ .probe = mlxreg_hotplug_probe, ++ .remove = mlxreg_hotplug_remove, ++}; ++ ++module_platform_driver(mlxreg_hotplug_driver); ++ ++MODULE_AUTHOR("Vadim Pasternak "); ++MODULE_DESCRIPTION("Mellanox regmap hotplug platform driver"); ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_ALIAS("platform:mlxreg-hotplug"); +diff -Nur a/include/linux/platform_data/mlxreg.h b/include/linux/platform_data/mlxreg.h +--- a/include/linux/platform_data/mlxreg.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/include/linux/platform_data/mlxreg.h 2017-11-12 09:04:09.796084101 +0000 +@@ -0,0 +1,142 @@ ++/* ++ * Copyright (c) 2017 Mellanox Technologies. All rights reserved. ++ * Copyright (c) 2017 Vadim Pasternak ++ * ++ * 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. ++ */ ++ ++#ifndef __LINUX_PLATFORM_DATA_MLXREG_H ++#define __LINUX_PLATFORM_DATA_MLXREG_H ++ ++#define MLXREG_CORE_LABEL_MAX_SIZE 32 ++ ++/** ++ * struct mlxreg_hotplug_device - I2C device data: ++ * ++ * @adapter: I2C device adapter; ++ * @client: I2C device client; ++ * @brdinfo: device board information; ++ * @nr: I2C device adapter number, to which device is to be attached; ++ * ++ * Structure represents I2C hotplug device static data (board topology) and ++ * dynamic data (related kernel objects handles). ++ */ ++struct mlxreg_hotplug_device { ++ struct i2c_adapter *adapter; ++ struct i2c_client *client; ++ struct i2c_board_info *brdinfo; ++ int nr; ++}; ++ ++/** ++ * struct mlxreg_core_data - attributes control data: ++ * ++ * @label: attribute label; ++ * @label: attribute register offset; ++ * @reg: attribute register; ++ * @mask: attribute access mask; ++ * @bit: attribute effective bit; ++ * @np - pointer to node platform associated with attribute; ++ * @hpdev - hotplug device data; ++ * @health_cntr: dynamic device health indication counter; ++ * @attached: true if device has been attached after good helath indication; ++ */ ++struct mlxreg_core_data { ++ char label[MLXREG_CORE_LABEL_MAX_SIZE]; ++ u32 reg; ++ u32 mask; ++ u32 bit; ++ struct device_node *np; ++ struct mlxreg_hotplug_device hpdev; ++ u8 health_cntr; ++ bool attached; ++}; ++ ++/** ++ * struct mlxreg_core_item - same type components controlled by the driver: ++ * ++ * @data: component data; ++ * @aggr_mask: group aggregation mask; ++ * @reg: group interrupt status register; ++ * @mask: group interrupt mask; ++ * @cache: last status value for elements fro the same group; ++ * @count: number of available elements in the group; ++ * @ind: element's index inside the group; ++ * @inversed: if 0: 0 for signal status is OK, if 1 - 1 is OK; ++ * @health: true if device has health indication, false in other case; ++ */ ++struct mlxreg_core_item { ++ struct mlxreg_core_data *data; ++ u32 aggr_mask; ++ u32 reg; ++ u32 mask; ++ u32 cache; ++ u8 count; ++ u8 ind; ++ u8 inversed; ++ u8 health; ++}; ++ ++/** ++ * struct mlxreg_core_led_platform_data - led platform data: ++ * ++ * @led_data: led private data; ++ * @regmap: register map of parent device; ++ * @counter: number of led instances; ++ */ ++struct mlxreg_core_led_platform_data { ++ struct mlxreg_core_data *data; ++ void *regmap; ++ int counter; ++}; ++ ++/** ++ * struct mlxreg_core_hotplug_platform_data - hotplug platform data: ++ * ++ * @items: same type components with the hotplug capability; ++ * @irq: platform interrupt number; ++ * @regmap: register map of parent device; ++ * @counter: number of the components with the hotplug capability; ++ * @cell: location of top aggregation interrupt register; ++ * @mask: top aggregation interrupt common mask; ++ * @cell_low: location of low aggregation interrupt register; ++ * @mask_low: low aggregation interrupt common mask; ++ */ ++struct mlxreg_core_hotplug_platform_data { ++ struct mlxreg_core_item *items; ++ int irq; ++ void *regmap; ++ int counter; ++ u32 cell; ++ u32 mask; ++ u32 cell_low; ++ u32 mask_low; ++}; ++ ++#endif /* __LINUX_PLATFORM_DATA_MLXREG_H */ diff --git a/patch/0005-leds-add-driver-for-support-Mellanox-regmap-LEDs-for.patch b/patch/0005-leds-add-driver-for-support-Mellanox-regmap-LEDs-for.patch new file mode 100644 index 000000000..558cbbc4e --- /dev/null +++ b/patch/0005-leds-add-driver-for-support-Mellanox-regmap-LEDs-for.patch @@ -0,0 +1,398 @@ +Linux backport patch. Includes following commits: +7dc37aeb560416771cbdc286357157c7565dc1fe +9244ef4cb79a8411656cb8fc2366f32f2294a0c9 +daf155fe70c9d69c28bba632b6a758ac8feab6e7 + + +diff -Nur a/drivers/leds/Kconfig b/drivers/leds/Kconfig +--- a/drivers/leds/Kconfig 2017-11-12 09:08:40.740087699 +0000 ++++ b/drivers/leds/Kconfig 2017-11-12 09:06:54.580086289 +0000 +@@ -659,6 +659,35 @@ + This option enabled support for the LEDs on the Mellanox + boards. Say Y to enabled these. + ++config LEDS_MLXREG ++ tristate "LED support for the Mellanox BMC cards" ++ depends on LEDS_CLASS ++ help ++ This option enabled support for the LEDs on the Mellanox BMC cards. ++ The driver can be activated from the device tree or by the direct ++ platform device add call. Say Y to enabled these. To compile this ++ driver as a module, choose 'M' here: the module will be called ++ leds-mlxreg. ++ ++config LEDS_USER ++ tristate "Userspace LED support" ++ depends on LEDS_CLASS ++ help ++ This option enables support for userspace LEDs. Say 'y' to enable this ++ support in kernel. To compile this driver as a module, choose 'm' here: ++ the module will be called uleds. ++ ++config LEDS_NIC78BX ++ tristate "LED support for NI PXI NIC78bx devices" ++ depends on LEDS_CLASS ++ depends on X86 && ACPI ++ help ++ This option enables support for the User1 and User2 LEDs on NI ++ PXI NIC78bx devices. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called leds-nic78bx. ++ + comment "LED Triggers" + source "drivers/leds/trigger/Kconfig" + +diff -Nur a/drivers/leds/Makefile b/drivers/leds/Makefile +--- a/drivers/leds/Makefile 2017-11-12 09:08:40.740087699 +0000 ++++ b/drivers/leds/Makefile 2017-11-12 09:06:54.580086289 +0000 +@@ -71,6 +71,7 @@ + obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o + obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o + obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o ++obj-$(CONFIG_LEDS_MLXREG) += leds-mlxreg.o + + # LED SPI Drivers + obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o +diff -Nur a/drivers/leds/leds-mlxcpld.c b/drivers/leds/leds-mlxcpld.c +--- a/drivers/leds/leds-mlxcpld.c 2017-11-12 09:08:40.740087699 +0000 ++++ b/drivers/leds/leds-mlxcpld.c 2017-11-12 09:08:05.620087233 +0000 +@@ -400,6 +400,9 @@ + struct platform_device *pdev; + int err; + ++ if (!dmi_match(DMI_CHASSIS_VENDOR, "Mellanox Technologies Ltd.")) ++ return -ENODEV; ++ + pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0); + if (IS_ERR(pdev)) { + pr_err("Device allocation failed\n"); +@@ -426,5 +429,5 @@ + + MODULE_AUTHOR("Vadim Pasternak "); + MODULE_DESCRIPTION("Mellanox board LED driver"); +-MODULE_LICENSE("GPL v2"); ++MODULE_LICENSE("Dual BSD/GPL"); + MODULE_ALIAS("platform:leds_mlxcpld"); +diff -Nur a/drivers/leds/leds-mlxreg.c b/drivers/leds/leds-mlxreg.c +--- a/drivers/leds/leds-mlxreg.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/leds/leds-mlxreg.c 2017-11-12 09:06:54.580086289 +0000 +@@ -0,0 +1,318 @@ ++/* ++ * Copyright (c) 2017 Mellanox Technologies. All rights reserved. ++ * Copyright (c) 2017 Vadim Pasternak ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Codes for LEDs. */ ++#define MLXREG_LED_OFFSET_BLINK_3HZ 0x01 /* Offset from solid: 3Hz blink */ ++#define MLXREG_LED_OFFSET_BLINK_6HZ 0x02 /* Offset from solid: 6Hz blink */ ++#define MLXREG_LED_IS_OFF 0x00 /* Off */ ++#define MLXREG_LED_RED_SOLID 0x05 /* Solid red */ ++#define MLXREG_LED_GREEN_SOLID 0x0D /* Solid green */ ++#define MLXREG_LED_AMBER_SOLID 0x09 /* Solid amber */ ++#define MLXREG_LED_BLINK_3HZ 167 /* ~167 msec off/on - HW support */ ++#define MLXREG_LED_BLINK_6HZ 83 /* ~83 msec off/on - HW support */ ++ ++/** ++ * struct mlxreg_led_data - led control data: ++ * ++ * @data: led configuration data; ++ * @led_classdev: led class data; ++ * @base_color: base led color (other colors have constant offset from base); ++ * @led_data: led data; ++ * @data_parent: pointer to private device control data of parent; ++ */ ++struct mlxreg_led_data { ++ struct mlxreg_core_data *data; ++ struct led_classdev led_cdev; ++ u8 base_color; ++ void *data_parent; ++ char led_cdev_name[MLXREG_CORE_LABEL_MAX_SIZE]; ++}; ++ ++#define cdev_to_priv(c) container_of(c, struct mlxreg_led_data, led_cdev) ++ ++/** ++ * struct mlxreg_led_priv_data - platform private data: ++ * ++ * @pdev: platform device; ++ * @pdata: platform data; ++ * @access_lock: mutex for attribute IO access; ++ */ ++struct mlxreg_led_priv_data { ++ struct platform_device *pdev; ++ struct mlxreg_core_led_platform_data *pdata; ++ struct mutex access_lock; /* protect IO operations */ ++}; ++ ++static int ++mlxreg_led_store_hw(struct mlxreg_led_data *led_data, u8 vset) ++{ ++ struct mlxreg_led_priv_data *priv = led_data->data_parent; ++ struct mlxreg_core_led_platform_data *led_pdata = priv->pdata; ++ struct mlxreg_core_data *data = led_data->data; ++ u32 regval; ++ u32 nib; ++ int ret; ++ ++ /* ++ * Each LED is controlled through low or high nibble of the relevant ++ * register byte. Register offset is specified by off parameter. ++ * Parameter vset provides color code: 0x0 for off, 0x5 for solid red, ++ * 0x6 for 3Hz blink red, 0xd for solid green, 0xe for 3Hz blink ++ * green. ++ * Parameter mask specifies which nibble is used for specific LED: mask ++ * 0xf0 - lower nibble is to be used (bits from 0 to 3), mask 0x0f - ++ * higher nibble (bits from 4 to 7). ++ */ ++ mutex_lock(&priv->access_lock); ++ ++ ret = regmap_read(led_pdata->regmap, data->reg, ®val); ++ if (ret) ++ goto access_error; ++ ++ nib = (ror32(data->mask, data->bit) == 0xf0) ? rol32(vset, data->bit) : ++ rol32(vset, data->bit + 4); ++ regval = (regval & data->mask) | nib; ++ ++ ret = regmap_write(led_pdata->regmap, data->reg, regval); ++ ++access_error: ++ mutex_unlock(&priv->access_lock); ++ ++ return ret; ++} ++ ++static enum led_brightness ++mlxreg_led_get_hw(struct mlxreg_led_data *led_data) ++{ ++ struct mlxreg_led_priv_data *priv = led_data->data_parent; ++ struct mlxreg_core_led_platform_data *led_pdata = priv->pdata; ++ struct mlxreg_core_data *data = led_data->data; ++ u32 regval; ++ int ret; ++ ++ /* ++ * Each LED is controlled through low or high nibble of the relevant ++ * register byte. Register offset is specified by off parameter. ++ * Parameter vset provides color code: 0x0 for off, 0x5 for solid red, ++ * 0x6 for 3Hz blink red, 0xd for solid green, 0xe for 3Hz blink ++ * green. ++ * Parameter mask specifies which nibble is used for specific LED: mask ++ * 0xf0 - lower nibble is to be used (bits from 0 to 3), mask 0x0f - ++ * higher nibble (bits from 4 to 7). ++ */ ++ ret = regmap_read(led_pdata->regmap, data->reg, ®val); ++ if (ret < 0) { ++ dev_warn(led_data->led_cdev.dev, "Failed to get current brightness, error: %d\n", ++ ret); ++ /* Assume the LED is OFF */ ++ return LED_OFF; ++ } ++ ++ regval = regval & ~data->mask; ++ regval = (ror32(data->mask, data->bit) == 0xf0) ? ror32(regval, ++ data->bit) : ror32(regval, data->bit + 4); ++ if (regval >= led_data->base_color && ++ regval <= (led_data->base_color + MLXREG_LED_OFFSET_BLINK_6HZ)) ++ ret = LED_FULL; ++ else ++ ret = LED_OFF; ++ ++ return ret; ++} ++ ++static int ++mlxreg_led_brightness_set(struct led_classdev *cled, enum led_brightness value) ++{ ++ struct mlxreg_led_data *led_data = cdev_to_priv(cled); ++ ++ if (value) ++ return mlxreg_led_store_hw(led_data, led_data->base_color); ++ else ++ return mlxreg_led_store_hw(led_data, MLXREG_LED_IS_OFF); ++} ++ ++static enum led_brightness ++mlxreg_led_brightness_get(struct led_classdev *cled) ++{ ++ struct mlxreg_led_data *led_data = cdev_to_priv(cled); ++ ++ return mlxreg_led_get_hw(led_data); ++} ++ ++static int ++mlxreg_led_blink_set(struct led_classdev *cled, unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ struct mlxreg_led_data *led_data = cdev_to_priv(cled); ++ int err; ++ ++ /* ++ * HW supports two types of blinking: full (6Hz) and half (3Hz). ++ * For delay on/off zero LED is setting to solid color. For others ++ * combination blinking is to be controlled by the software timer. ++ */ ++ if (!(*delay_on == 0 && *delay_off == 0) && ++ !(*delay_on == MLXREG_LED_BLINK_3HZ && ++ *delay_off == MLXREG_LED_BLINK_3HZ) && ++ !(*delay_on == MLXREG_LED_BLINK_6HZ && ++ *delay_off == MLXREG_LED_BLINK_6HZ)) ++ return -EINVAL; ++ ++ if (*delay_on == MLXREG_LED_BLINK_6HZ) ++ err = mlxreg_led_store_hw(led_data, led_data->base_color + ++ MLXREG_LED_OFFSET_BLINK_6HZ); ++ else if (*delay_on == MLXREG_LED_BLINK_3HZ) ++ err = mlxreg_led_store_hw(led_data, led_data->base_color + ++ MLXREG_LED_OFFSET_BLINK_3HZ); ++ else ++ err = mlxreg_led_store_hw(led_data, led_data->base_color); ++ ++ return err; ++} ++ ++static int mlxreg_led_config(struct mlxreg_led_priv_data *priv) ++{ ++ struct mlxreg_core_led_platform_data *led_pdata = priv->pdata; ++ struct mlxreg_core_data *data = led_pdata->data; ++ struct mlxreg_led_data *led_data; ++ struct led_classdev *led_cdev; ++ int brightness; ++ int i; ++ int err; ++ ++ for (i = 0; i < led_pdata->counter; i++, data++) { ++ led_data = devm_kzalloc(&priv->pdev->dev, sizeof(*led_data), ++ GFP_KERNEL); ++ if (!led_data) ++ return -ENOMEM; ++ ++ led_cdev = &led_data->led_cdev; ++ led_data->data_parent = priv; ++ if (strstr(data->label, "red") || ++ strstr(data->label, "orange")) { ++ brightness = LED_OFF; ++ led_data->base_color = MLXREG_LED_RED_SOLID; ++ } else if (strstr(data->label, "amber")) { ++ brightness = LED_OFF; ++ led_data->base_color = MLXREG_LED_AMBER_SOLID; ++ } else { ++ brightness = LED_OFF; ++ led_data->base_color = MLXREG_LED_GREEN_SOLID; ++ } ++ sprintf(led_data->led_cdev_name, "%s:%s", "mlxreg", ++ data->label); ++ led_cdev->name = led_data->led_cdev_name; ++ led_cdev->brightness = brightness; ++ led_cdev->max_brightness = 1; ++ led_cdev->brightness_set_blocking = ++ mlxreg_led_brightness_set; ++ led_cdev->brightness_get = mlxreg_led_brightness_get; ++ led_cdev->blink_set = mlxreg_led_blink_set; ++ led_cdev->flags = LED_CORE_SUSPENDRESUME; ++ led_data->data = data; ++ err = devm_led_classdev_register(&priv->pdev->dev, led_cdev); ++ if (err) ++ return err; ++ ++ if (led_cdev->brightness) ++ mlxreg_led_brightness_set(led_cdev, ++ led_cdev->brightness); ++ dev_info(led_cdev->dev, "label: %s, mask: 0x%02x, offset:0x%02x\n", ++ data->label, data->mask, data->reg); ++ } ++ ++ return 0; ++} ++ ++static int mlxreg_led_probe(struct platform_device *pdev) ++{ ++ struct mlxreg_core_led_platform_data *led_pdata; ++ struct mlxreg_led_priv_data *priv; ++ ++ led_pdata = dev_get_platdata(&pdev->dev); ++ if (!led_pdata) { ++ dev_err(&pdev->dev, "Failed to get platform data.\n"); ++ return -EINVAL; ++ } ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ mutex_init(&priv->access_lock); ++ priv->pdev = pdev; ++ priv->pdata = led_pdata; ++ ++ return mlxreg_led_config(priv); ++} ++ ++static int mlxreg_led_remove(struct platform_device *pdev) ++{ ++ struct mlxreg_led_priv_data *priv = dev_get_drvdata(&pdev->dev); ++ ++ mutex_destroy(&priv->access_lock); ++ ++ return 0; ++} ++ ++static const struct of_device_id mlxreg_led_dt_match[] = { ++ { .compatible = "mellanox,leds-mlxreg" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, mlxreg_led_dt_match); ++ ++static struct platform_driver mlxreg_led_driver = { ++ .driver = { ++ .name = "leds-mlxreg", ++ .of_match_table = of_match_ptr(mlxreg_led_dt_match), ++ }, ++ .probe = mlxreg_led_probe, ++ .remove = mlxreg_led_remove, ++}; ++ ++module_platform_driver(mlxreg_led_driver); ++ ++MODULE_AUTHOR("Vadim Pasternak "); ++MODULE_DESCRIPTION("Mellanox LED regmap driver"); ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_ALIAS("platform:leds-mlxreg"); diff --git a/patch/0006-Mellanox-switch-drivers-changes.patch b/patch/0006-Mellanox-switch-drivers-changes.patch new file mode 100644 index 000000000..c820bbdc0 --- /dev/null +++ b/patch/0006-Mellanox-switch-drivers-changes.patch @@ -0,0 +1,2118 @@ +Linux backport patch. Includes following commits: +02f1d19ecd08f7da83bf17d556ba147b16ed9dab +50deb9064015956274a989d035c0a101188c5bf2 +515dc42e5f57aa4b2dbb18fbe6b3200224d051a7 +63b01357d3f002ebed8e532b503a84dde6f45060 +7c2ed7426f0835d5408a2c97fc6ebcc67c5feea0 +65f178307a4274f1ab52f4e729e1eabe00ee2d96 +81ce6e3fefba4ed1578db80609b54ccc3ae624cb +3f65860a8b01652fbda978b991d13c02848c8ee2 +05cdb2439ba8bb00a1746ec68e27cec62ea1e142 +021697a48b00b51636d88e5056015ad65b6da821 +f6410966453b7671a0c4032652db36b2e67ba43c +acf30a9f0714a734531078b7a6d85ab7762c3589 +f334341a185bad33bbc3cf0a3b7d7189d8803bc0 +aec592f5c0d44b3ac4038dc539859fa247738f6e +589428b6233c6a9bffbf8c8bca86f62838f35021 + + +diff -Nur a/drivers/net/ethernet/mellanox/mlxsw/Kconfig b/drivers/net/ethernet/mellanox/mlxsw/Kconfig +--- a/drivers/net/ethernet/mellanox/mlxsw/Kconfig 2017-05-25 13:45:05.000000000 +0000 ++++ b/drivers/net/ethernet/mellanox/mlxsw/Kconfig 2017-11-09 12:40:31.940814834 +0000 +@@ -19,6 +19,24 @@ + ---help--- + Say Y here if you want to expose HWMON interface on mlxsw devices. + ++config MLXSW_CORE_THERMAL ++ bool "Thermal zone support for Mellanox Technologies Switch ASICs" ++ depends on MLXSW_CORE && THERMAL ++ depends on !(MLXSW_CORE=y && THERMAL=m) ++ default y ++ ---help--- ++ Say Y here if you want to automatically control fans speed according ++ ambient temperature reported by ASIC. ++ ++config MLXSW_CORE_QSFP ++ bool "QSFP support for Mellanox Technologies Switch ASICs" ++ depends on MLXSW_CORE && HWMON ++ depends on !(MLXSW_CORE=y && HWMON=m) ++ default y ++ ---help--- ++ Say Y here if you want to expose sysfs QSFP interface on mlxsw ++ devices. ++ + config MLXSW_PCI + tristate "PCI bus implementation for Mellanox Technologies Switch ASICs" + depends on PCI && HAS_DMA && HAS_IOMEM && MLXSW_CORE +@@ -29,6 +47,27 @@ + To compile this driver as a module, choose M here: the + module will be called mlxsw_pci. + ++config MLXSW_I2C ++ tristate "I2C bus implementation for Mellanox Technologies Switch ASICs" ++ depends on I2C && MLXSW_CORE ++ default m ++ ---help--- ++ This is I2C bus implementation for Mellanox Technologies Switch ASICs. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called mlxsw_i2c. ++ ++config MLXSW_SWITCHIB ++ tristate "Mellanox Technologies SwitchIB and SwitchIB-2 support" ++ depends on MLXSW_CORE && NET_SWITCHDEV ++ default m ++ ---help--- ++ This driver supports Mellanox Technologies SwitchIB and SwitchIB-2 ++ Infiniband Switch ASICs. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called mlxsw_switchib. ++ + config MLXSW_SWITCHX2 + tristate "Mellanox Technologies SwitchX-2 support" + depends on MLXSW_CORE && NET_SWITCHDEV +@@ -58,3 +97,14 @@ + ---help--- + Say Y here if you want to use Data Center Bridging (DCB) in the + driver. ++ ++config MLXSW_MINIMAL ++ tristate "Mellanox Technologies minimal I2C support" ++ depends on MLXSW_CORE && MLXSW_I2C ++ default m ++ ---help--- ++ This driver supports I2C access for Mellanox Technologies Switch ++ ASICs. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called mlxsw_minimal. +diff -Nur a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile +--- a/drivers/net/ethernet/mellanox/mlxsw/Makefile 2017-05-25 13:45:05.000000000 +0000 ++++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile 2017-11-09 12:40:31.940814834 +0000 +@@ -1,8 +1,14 @@ + obj-$(CONFIG_MLXSW_CORE) += mlxsw_core.o + mlxsw_core-objs := core.o + mlxsw_core-$(CONFIG_MLXSW_CORE_HWMON) += core_hwmon.o ++mlxsw_core-$(CONFIG_MLXSW_CORE_THERMAL) += core_thermal.o ++mlxsw_core-$(CONFIG_MLXSW_CORE_QSFP) += qsfp_sysfs.o + obj-$(CONFIG_MLXSW_PCI) += mlxsw_pci.o + mlxsw_pci-objs := pci.o ++obj-$(CONFIG_MLXSW_I2C) += mlxsw_i2c.o ++mlxsw_i2c-objs := i2c.o ++obj-$(CONFIG_MLXSW_SWITCHIB) += mlxsw_switchib.o ++mlxsw_switchib-objs := switchib.o + obj-$(CONFIG_MLXSW_SWITCHX2) += mlxsw_switchx2.o + mlxsw_switchx2-objs := switchx2.o + obj-$(CONFIG_MLXSW_SPECTRUM) += mlxsw_spectrum.o +@@ -10,3 +16,5 @@ + spectrum_switchdev.o spectrum_router.o \ + spectrum_kvdl.o + mlxsw_spectrum-$(CONFIG_MLXSW_SPECTRUM_DCB) += spectrum_dcb.o ++obj-$(CONFIG_MLXSW_MINIMAL) += mlxsw_minimal.o ++mlxsw_minimal-objs := minimal.o +diff -Nur a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c +--- a/drivers/net/ethernet/mellanox/mlxsw/core.c 2017-05-25 13:45:05.000000000 +0000 ++++ b/drivers/net/ethernet/mellanox/mlxsw/core.c 2017-11-09 13:03:45.824833341 +0000 +@@ -113,6 +113,9 @@ + } lag; + struct mlxsw_resources resources; + struct mlxsw_hwmon *hwmon; ++ struct mlxsw_thermal *thermal; ++struct mlxsw_qsfp *qsfp; ++ struct mlxsw_core_port ports[MLXSW_PORT_MAX_PORTS]; + unsigned long driver_priv[0]; + /* driver_priv has to be always the last item */ + }; +@@ -579,6 +582,9 @@ + u64 tid; + int err; + ++ if (!(mlxsw_core->bus->features & MLXSW_BUS_F_TXRX)) ++ return 0; ++ + /* Set the upper 32 bits of the transaction ID field to a random + * number. This allows us to discard EMADs addressed to other + * devices. +@@ -615,6 +621,9 @@ + { + char hpkt_pl[MLXSW_REG_HPKT_LEN]; + ++ if (!(mlxsw_core->bus->features & MLXSW_BUS_F_TXRX)) ++ return; ++ + mlxsw_core->emad.use_emad = false; + mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_DISCARD, + MLXSW_TRAP_ID_ETHEMAD); +@@ -1128,9 +1137,21 @@ + if (err) + goto err_hwmon_init; + +- err = mlxsw_driver->init(mlxsw_core, mlxsw_bus_info); ++ err = mlxsw_thermal_init(mlxsw_core, mlxsw_bus_info, ++ &mlxsw_core->thermal); + if (err) +- goto err_driver_init; ++ goto err_thermal_init; ++ ++ err = mlxsw_qsfp_init(mlxsw_core, mlxsw_bus_info, ++ &mlxsw_core->qsfp); ++ if (err) ++ goto err_qsfp_init; ++ ++ if (mlxsw_driver->init) { ++ err = mlxsw_driver->init(mlxsw_core, mlxsw_bus_info); ++ if (err) ++ goto err_driver_init; ++ } + + err = mlxsw_core_debugfs_init(mlxsw_core); + if (err) +@@ -1141,6 +1162,10 @@ + err_debugfs_init: + mlxsw_core->driver->fini(mlxsw_core); + err_driver_init: ++ mlxsw_qsfp_fini(mlxsw_core->qsfp); ++err_qsfp_init: ++ mlxsw_thermal_fini(mlxsw_core->thermal); ++err_thermal_init: + err_hwmon_init: + devlink_unregister(devlink); + err_devlink_register: +@@ -1165,7 +1190,10 @@ + struct devlink *devlink = priv_to_devlink(mlxsw_core); + + mlxsw_core_debugfs_fini(mlxsw_core); +- mlxsw_core->driver->fini(mlxsw_core); ++ if (mlxsw_core->driver->fini) ++ mlxsw_core->driver->fini(mlxsw_core); ++ mlxsw_qsfp_fini(mlxsw_core->qsfp); ++ mlxsw_thermal_fini(mlxsw_core->thermal); + devlink_unregister(devlink); + mlxsw_emad_fini(mlxsw_core); + mlxsw_core->bus->fini(mlxsw_core->bus_priv); +diff -Nur a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h +--- a/drivers/net/ethernet/mellanox/mlxsw/core.h 2017-05-25 13:45:05.000000000 +0000 ++++ b/drivers/net/ethernet/mellanox/mlxsw/core.h 2017-11-09 13:03:45.824833341 +0000 +@@ -300,6 +300,8 @@ + + struct mlxsw_resources *mlxsw_core_resources_get(struct mlxsw_core *mlxsw_core); + ++#define MLXSW_BUS_F_TXRX BIT(0) ++ + struct mlxsw_bus { + const char *kind; + int (*init)(void *bus_priv, struct mlxsw_core *mlxsw_core, +@@ -315,6 +317,7 @@ + char *in_mbox, size_t in_mbox_size, + char *out_mbox, size_t out_mbox_size, + u8 *p_status); ++ u8 features; + }; + + struct mlxsw_bus_info { +@@ -349,5 +352,53 @@ + } + + #endif ++ ++struct mlxsw_thermal; ++ ++#ifdef CONFIG_MLXSW_CORE_THERMAL ++ ++int mlxsw_thermal_init(struct mlxsw_core *mlxsw_core, ++ const struct mlxsw_bus_info *mlxsw_bus_info, ++ struct mlxsw_thermal **p_thermal); ++void mlxsw_thermal_fini(struct mlxsw_thermal *thermal); ++ ++#else ++ ++static inline int mlxsw_thermal_init(struct mlxsw_core *mlxsw_core, ++ const struct mlxsw_bus_info *mlxsw_bus_info, ++ struct mlxsw_thermal **p_thermal) ++{ ++ return 0; ++} ++ ++static inline void mlxsw_thermal_fini(struct mlxsw_thermal *thermal) ++{ ++} ++ ++#endif ++ ++struct mlxsw_qsfp; ++ ++#ifdef CONFIG_MLXSW_CORE_QSFP ++ ++int mlxsw_qsfp_init(struct mlxsw_core *mlxsw_core, ++ const struct mlxsw_bus_info *mlxsw_bus_info, ++ struct mlxsw_qsfp **p_qsfp); ++void mlxsw_qsfp_fini(struct mlxsw_qsfp *qsfp); ++ ++#else ++ ++static inline int mlxsw_qsfp_init(struct mlxsw_core *mlxsw_core, ++ const struct mlxsw_bus_info *mlxsw_bus_info, ++ struct mlxsw_qsfp **p_qsfp) ++{ ++ return 0; ++} ++ ++static inline void mlxsw_qsfp_fini(struct mlxsw_qsfp *qsfp) ++{ ++} ++ ++#endif + + #endif +diff -Nur a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c +--- a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c 2017-05-25 13:45:05.000000000 +0000 ++++ b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c 2017-11-09 13:04:29.120833916 +0000 +@@ -262,7 +262,7 @@ + + static int mlxsw_hwmon_temp_init(struct mlxsw_hwmon *mlxsw_hwmon) + { +- char mtcap_pl[MLXSW_REG_MTCAP_LEN]; ++ char mtcap_pl[MLXSW_REG_MTCAP_LEN] = {0}; + char mtmp_pl[MLXSW_REG_MTMP_LEN]; + u8 sensor_count; + int i; +@@ -295,7 +295,7 @@ + + static int mlxsw_hwmon_fans_init(struct mlxsw_hwmon *mlxsw_hwmon) + { +- char mfcr_pl[MLXSW_REG_MFCR_LEN]; ++ char mfcr_pl[MLXSW_REG_MFCR_LEN] = {0}; + enum mlxsw_reg_mfcr_pwm_frequency freq; + unsigned int type_index; + unsigned int num; +diff -Nur a/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c b/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c +--- a/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c 2017-11-09 13:04:52.192834223 +0000 +@@ -0,0 +1,436 @@ ++/* ++ * drivers/net/ethernet/mellanox/mlxsw/core_thermal.c ++ * Copyright (c) 2016 Ivan Vecera ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++#include "core.h" ++ ++#define MLXSW_THERMAL_POLL_INT 1000 /* ms */ ++#define MLXSW_THERMAL_MAX_TEMP 110000 /* 110C */ ++#define MLXSW_THERMAL_MAX_STATE 10 ++#define MLXSW_THERMAL_MAX_DUTY 255 ++ ++struct mlxsw_thermal_trip { ++ int type; ++ int temp; ++ int min_state; ++ int max_state; ++}; ++ ++static const struct mlxsw_thermal_trip default_thermal_trips[] = { ++ { /* Above normal - 60%-100% PWM */ ++ .type = THERMAL_TRIP_ACTIVE, ++ .temp = 75000, ++ .min_state = (6 * MLXSW_THERMAL_MAX_STATE) / 10, ++ .max_state = MLXSW_THERMAL_MAX_STATE, ++ }, ++ { ++ /* Very high - 100% PWM */ ++ .type = THERMAL_TRIP_ACTIVE, ++ .temp = 85000, ++ .min_state = MLXSW_THERMAL_MAX_STATE, ++ .max_state = MLXSW_THERMAL_MAX_STATE, ++ }, ++ { /* Warning */ ++ .type = THERMAL_TRIP_HOT, ++ .temp = 105000, ++ .min_state = MLXSW_THERMAL_MAX_STATE, ++ .max_state = MLXSW_THERMAL_MAX_STATE, ++ }, ++ { /* Critical - soft poweroff */ ++ .type = THERMAL_TRIP_CRITICAL, ++ .temp = MLXSW_THERMAL_MAX_TEMP, ++ .min_state = MLXSW_THERMAL_MAX_STATE, ++ .max_state = MLXSW_THERMAL_MAX_STATE, ++ } ++}; ++ ++#define MLXSW_THERMAL_NUM_TRIPS ARRAY_SIZE(default_thermal_trips) ++ ++/* Make sure all trips are writable */ ++#define MLXSW_THERMAL_TRIP_MASK (BIT(MLXSW_THERMAL_NUM_TRIPS) - 1) ++ ++struct mlxsw_thermal { ++ struct mlxsw_core *core; ++ const struct mlxsw_bus_info *bus_info; ++ struct thermal_zone_device *tzdev; ++ struct thermal_cooling_device *cdevs[MLXSW_MFCR_PWMS_MAX]; ++ struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS]; ++ enum thermal_device_mode mode; ++}; ++ ++static inline u8 mlxsw_state_to_duty(int state) ++{ ++ return DIV_ROUND_CLOSEST(state * MLXSW_THERMAL_MAX_DUTY, ++ MLXSW_THERMAL_MAX_STATE); ++} ++ ++static inline int mlxsw_duty_to_state(u8 duty) ++{ ++ return DIV_ROUND_CLOSEST(duty * MLXSW_THERMAL_MAX_STATE, ++ MLXSW_THERMAL_MAX_DUTY); ++} ++ ++static int mlxsw_get_cooling_device_idx(struct mlxsw_thermal *thermal, ++ struct thermal_cooling_device *cdev) ++{ ++ int i; ++ ++ for (i = 0; i < MLXSW_MFCR_PWMS_MAX; i++) ++ if (thermal->cdevs[i] == cdev) ++ return i; ++ ++ return -ENODEV; ++} ++ ++static int mlxsw_thermal_bind(struct thermal_zone_device *tzdev, ++ struct thermal_cooling_device *cdev) ++{ ++ struct mlxsw_thermal *thermal = tzdev->devdata; ++ struct device *dev = thermal->bus_info->dev; ++ int i, err; ++ ++ /* If the cooling device is one of ours bind it */ ++ if (mlxsw_get_cooling_device_idx(thermal, cdev) < 0) ++ return 0; ++ ++ for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) { ++ const struct mlxsw_thermal_trip *trip = &thermal->trips[i]; ++ ++ err = thermal_zone_bind_cooling_device(tzdev, i, cdev, ++ trip->max_state, ++ trip->min_state, ++ THERMAL_WEIGHT_DEFAULT); ++ if (err < 0) { ++ dev_err(dev, "Failed to bind cooling device to trip %d\n", i); ++ return err; ++ } ++ } ++ return 0; ++} ++ ++static int mlxsw_thermal_unbind(struct thermal_zone_device *tzdev, ++ struct thermal_cooling_device *cdev) ++{ ++ struct mlxsw_thermal *thermal = tzdev->devdata; ++ struct device *dev = thermal->bus_info->dev; ++ int i; ++ int err; ++ ++ /* If the cooling device is our one unbind it */ ++ if (mlxsw_get_cooling_device_idx(thermal, cdev) < 0) ++ return 0; ++ ++ for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) { ++ err = thermal_zone_unbind_cooling_device(tzdev, i, cdev); ++ if (err < 0) { ++ dev_err(dev, "Failed to unbind cooling device\n"); ++ return err; ++ } ++ } ++ return 0; ++} ++ ++static int mlxsw_thermal_get_mode(struct thermal_zone_device *tzdev, ++ enum thermal_device_mode *mode) ++{ ++ struct mlxsw_thermal *thermal = tzdev->devdata; ++ ++ *mode = thermal->mode; ++ ++ return 0; ++} ++ ++static int mlxsw_thermal_set_mode(struct thermal_zone_device *tzdev, ++ enum thermal_device_mode mode) ++{ ++ struct mlxsw_thermal *thermal = tzdev->devdata; ++ ++ mutex_lock(&tzdev->lock); ++ ++ if (mode == THERMAL_DEVICE_ENABLED) ++ tzdev->polling_delay = MLXSW_THERMAL_POLL_INT; ++ else ++ tzdev->polling_delay = 0; ++ ++ mutex_unlock(&tzdev->lock); ++ ++ thermal->mode = mode; ++ thermal_zone_device_update(tzdev, THERMAL_EVENT_UNSPECIFIED); ++ ++ return 0; ++} ++ ++static int mlxsw_thermal_get_temp(struct thermal_zone_device *tzdev, ++ int *p_temp) ++{ ++ struct mlxsw_thermal *thermal = tzdev->devdata; ++ struct device *dev = thermal->bus_info->dev; ++ char mtmp_pl[MLXSW_REG_MTMP_LEN]; ++ unsigned int temp; ++ int err; ++ ++ mlxsw_reg_mtmp_pack(mtmp_pl, 0, false, false); ++ ++ err = mlxsw_reg_query(thermal->core, MLXSW_REG(mtmp), mtmp_pl); ++ if (err) { ++ dev_err(dev, "Failed to query temp sensor\n"); ++ return err; ++ } ++ mlxsw_reg_mtmp_unpack(mtmp_pl, &temp, NULL, NULL); ++ ++ *p_temp = (int) temp; ++ return 0; ++} ++ ++static int mlxsw_thermal_get_trip_type(struct thermal_zone_device *tzdev, ++ int trip, ++ enum thermal_trip_type *p_type) ++{ ++ struct mlxsw_thermal *thermal = tzdev->devdata; ++ ++ if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS) ++ return -EINVAL; ++ ++ *p_type = thermal->trips[trip].type; ++ return 0; ++} ++ ++static int mlxsw_thermal_get_trip_temp(struct thermal_zone_device *tzdev, ++ int trip, int *p_temp) ++{ ++ struct mlxsw_thermal *thermal = tzdev->devdata; ++ ++ if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS) ++ return -EINVAL; ++ ++ *p_temp = thermal->trips[trip].temp; ++ return 0; ++} ++ ++static int mlxsw_thermal_set_trip_temp(struct thermal_zone_device *tzdev, ++ int trip, int temp) ++{ ++ struct mlxsw_thermal *thermal = tzdev->devdata; ++ ++ if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS || ++ temp > MLXSW_THERMAL_MAX_TEMP) ++ return -EINVAL; ++ ++ thermal->trips[trip].temp = temp; ++ return 0; ++} ++ ++static struct thermal_zone_device_ops mlxsw_thermal_ops = { ++ .bind = mlxsw_thermal_bind, ++ .unbind = mlxsw_thermal_unbind, ++ .get_mode = mlxsw_thermal_get_mode, ++ .set_mode = mlxsw_thermal_set_mode, ++ .get_temp = mlxsw_thermal_get_temp, ++ .get_trip_type = mlxsw_thermal_get_trip_type, ++ .get_trip_temp = mlxsw_thermal_get_trip_temp, ++ .set_trip_temp = mlxsw_thermal_set_trip_temp, ++}; ++ ++static int mlxsw_thermal_get_max_state(struct thermal_cooling_device *cdev, ++ unsigned long *p_state) ++{ ++ *p_state = MLXSW_THERMAL_MAX_STATE; ++ return 0; ++} ++ ++static int mlxsw_thermal_get_cur_state(struct thermal_cooling_device *cdev, ++ unsigned long *p_state) ++ ++{ ++ struct mlxsw_thermal *thermal = cdev->devdata; ++ struct device *dev = thermal->bus_info->dev; ++ char mfsc_pl[MLXSW_REG_MFSC_LEN]; ++ int err, idx; ++ u8 duty; ++ ++ idx = mlxsw_get_cooling_device_idx(thermal, cdev); ++ if (idx < 0) ++ return idx; ++ ++ mlxsw_reg_mfsc_pack(mfsc_pl, idx, 0); ++ err = mlxsw_reg_query(thermal->core, MLXSW_REG(mfsc), mfsc_pl); ++ if (err) { ++ dev_err(dev, "Failed to query PWM duty\n"); ++ return err; ++ } ++ ++ duty = mlxsw_reg_mfsc_pwm_duty_cycle_get(mfsc_pl); ++ *p_state = mlxsw_duty_to_state(duty); ++ return 0; ++} ++ ++static int mlxsw_thermal_set_cur_state(struct thermal_cooling_device *cdev, ++ unsigned long state) ++ ++{ ++ struct mlxsw_thermal *thermal = cdev->devdata; ++ struct device *dev = thermal->bus_info->dev; ++ char mfsc_pl[MLXSW_REG_MFSC_LEN]; ++ int err, idx; ++ ++ idx = mlxsw_get_cooling_device_idx(thermal, cdev); ++ if (idx < 0) ++ return idx; ++ ++ mlxsw_reg_mfsc_pack(mfsc_pl, idx, mlxsw_state_to_duty(state)); ++ err = mlxsw_reg_write(thermal->core, MLXSW_REG(mfsc), mfsc_pl); ++ if (err) { ++ dev_err(dev, "Failed to write PWM duty\n"); ++ return err; ++ } ++ return 0; ++} ++ ++static const struct thermal_cooling_device_ops mlxsw_cooling_ops = { ++ .get_max_state = mlxsw_thermal_get_max_state, ++ .get_cur_state = mlxsw_thermal_get_cur_state, ++ .set_cur_state = mlxsw_thermal_set_cur_state, ++}; ++ ++int mlxsw_thermal_init(struct mlxsw_core *core, ++ const struct mlxsw_bus_info *bus_info, ++ struct mlxsw_thermal **p_thermal) ++{ ++ char mfcr_pl[MLXSW_REG_MFCR_LEN] = { 0 }; ++ enum mlxsw_reg_mfcr_pwm_frequency freq; ++ struct device *dev = bus_info->dev; ++ struct mlxsw_thermal *thermal; ++ u16 tacho_active; ++ u8 pwm_active; ++ int err, i; ++ ++ thermal = devm_kzalloc(dev, sizeof(*thermal), ++ GFP_KERNEL); ++ if (!thermal) ++ return -ENOMEM; ++ ++ thermal->core = core; ++ thermal->bus_info = bus_info; ++ memcpy(thermal->trips, default_thermal_trips, sizeof(thermal->trips)); ++ ++ err = mlxsw_reg_query(thermal->core, MLXSW_REG(mfcr), mfcr_pl); ++ if (err) { ++ dev_err(dev, "Failed to probe PWMs\n"); ++ goto err_free_thermal; ++ } ++ mlxsw_reg_mfcr_unpack(mfcr_pl, &freq, &tacho_active, &pwm_active); ++ ++ for (i = 0; i < MLXSW_MFCR_TACHOS_MAX; i++) { ++ if (tacho_active & BIT(i)) { ++ char mfsl_pl[MLXSW_REG_MFSL_LEN]; ++ ++ mlxsw_reg_mfsl_pack(mfsl_pl, i, 0, 0); ++ ++ /* We need to query the register to preserve maximum */ ++ err = mlxsw_reg_query(thermal->core, MLXSW_REG(mfsl), ++ mfsl_pl); ++ if (err) ++ goto err_free_thermal; ++ ++ /* set the minimal RPMs to 0 */ ++ mlxsw_reg_mfsl_tach_min_set(mfsl_pl, 0); ++ err = mlxsw_reg_write(thermal->core, MLXSW_REG(mfsl), ++ mfsl_pl); ++ if (err) ++ goto err_free_thermal; ++ } ++ } ++ for (i = 0; i < MLXSW_MFCR_PWMS_MAX; i++) { ++ if (pwm_active & BIT(i)) { ++ struct thermal_cooling_device *cdev; ++ ++ cdev = thermal_cooling_device_register("Fan", thermal, ++ &mlxsw_cooling_ops); ++ if (IS_ERR(cdev)) { ++ err = PTR_ERR(cdev); ++ dev_err(dev, "Failed to register cooling device\n"); ++ goto err_unreg_cdevs; ++ } ++ thermal->cdevs[i] = cdev; ++ } ++ } ++ ++ thermal->tzdev = thermal_zone_device_register("mlxsw", ++ MLXSW_THERMAL_NUM_TRIPS, ++ MLXSW_THERMAL_TRIP_MASK, ++ thermal, ++ &mlxsw_thermal_ops, ++ NULL, 0, ++ MLXSW_THERMAL_POLL_INT); ++ if (IS_ERR(thermal->tzdev)) { ++ err = PTR_ERR(thermal->tzdev); ++ dev_err(dev, "Failed to register thermal zone\n"); ++ goto err_unreg_cdevs; ++ } ++ ++ thermal->mode = THERMAL_DEVICE_ENABLED; ++ *p_thermal = thermal; ++ return 0; ++err_unreg_cdevs: ++ for (i = 0; i < MLXSW_MFCR_PWMS_MAX; i++) ++ if (thermal->cdevs[i]) ++ thermal_cooling_device_unregister(thermal->cdevs[i]); ++err_free_thermal: ++ devm_kfree(dev, thermal); ++ return err; ++} ++ ++void mlxsw_thermal_fini(struct mlxsw_thermal *thermal) ++{ ++ int i; ++ ++ if (thermal->tzdev) { ++ thermal_zone_device_unregister(thermal->tzdev); ++ thermal->tzdev = NULL; ++ } ++ ++ for (i = 0; i < MLXSW_MFCR_PWMS_MAX; i++) { ++ if (thermal->cdevs[i]) { ++ thermal_cooling_device_unregister(thermal->cdevs[i]); ++ thermal->cdevs[i] = NULL; ++ } ++ } ++ ++ devm_kfree(thermal->bus_info->dev, thermal); ++} +diff -Nur a/drivers/net/ethernet/mellanox/mlxsw/i2c.c b/drivers/net/ethernet/mellanox/mlxsw/i2c.c +--- a/drivers/net/ethernet/mellanox/mlxsw/i2c.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/net/ethernet/mellanox/mlxsw/i2c.c 2017-11-09 13:04:29.120833916 +0000 +@@ -0,0 +1,582 @@ ++/* ++ * drivers/net/ethernet/mellanox/mlxsw/i2c.c ++ * Copyright (c) 2016 Mellanox Technologies. All rights reserved. ++ * Copyright (c) 2016 Vadim Pasternak ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cmd.h" ++#include "core.h" ++#include "i2c.h" ++ ++static const char mlxsw_i2c_driver_name[] = "mlxsw_i2c"; ++ ++#define MLXSW_I2C_CIR2_BASE 0x72000 ++#define MLXSW_I2C_CIR_STATUS_OFF 0x18 ++#define MLXSW_I2C_CIR2_OFF_STATUS (MLXSW_I2C_CIR2_BASE + \ ++ MLXSW_I2C_CIR_STATUS_OFF) ++#define MLXSW_I2C_OPMOD_SHIFT 12 ++#define MLXSW_I2C_GO_BIT_SHIFT 23 ++#define MLXSW_I2C_CIR_CTRL_STATUS_SHIFT 24 ++#define MLXSW_I2C_GO_BIT BIT(MLXSW_I2C_GO_BIT_SHIFT) ++#define MLXSW_I2C_GO_OPMODE BIT(MLXSW_I2C_OPMOD_SHIFT) ++#define MLXSW_I2C_SET_IMM_CMD (MLXSW_I2C_GO_OPMODE | \ ++ MLXSW_CMD_OPCODE_QUERY_FW) ++#define MLXSW_I2C_PUSH_IMM_CMD (MLXSW_I2C_GO_BIT | \ ++ MLXSW_I2C_SET_IMM_CMD) ++#define MLXSW_I2C_SET_CMD (MLXSW_CMD_OPCODE_ACCESS_REG) ++#define MLXSW_I2C_PUSH_CMD (MLXSW_I2C_GO_BIT | MLXSW_I2C_SET_CMD) ++#define MLXSW_I2C_TLV_HDR_SIZE 0x10 ++#define MLXSW_I2C_ADDR_WIDTH 4 ++#define MLXSW_I2C_PUSH_CMD_SIZE (MLXSW_I2C_ADDR_WIDTH + 4) ++#define MLXSW_I2C_READ_SEMA_SIZE 4 ++#define MLXSW_I2C_PREP_SIZE (MLXSW_I2C_ADDR_WIDTH + 28) ++#define MLXSW_I2C_MBOX_SIZE 20 ++#define MLXSW_I2C_MBOX_OUT_PARAM_OFF 12 ++#define MLXSW_I2C_MAX_BUFF_SIZE 32 ++#define MLXSW_I2C_MBOX_OFFSET_BITS 20 ++#define MLXSW_I2C_MBOX_SIZE_BITS 12 ++#define MLXSW_I2C_ADDR_BUF_SIZE 4 ++#define MLXSW_I2C_BLK_MAX 32 ++#define MLXSW_I2C_RETRY 5 ++#define MLXSW_I2C_TIMEOUT_MSECS 5000 ++ ++/** ++ * struct mlxsw_i2c - device private data: ++ * @cmd.mb_size_in: input mailbox size; ++ * @cmd.mb_off_in: input mailbox offset in register space; ++ * @cmd.mb_size_out: output mailbox size; ++ * @cmd.mb_off_out: output mailbox offset in register space; ++ * @cmd.lock: command execution lock; ++ * @dev: I2C device; ++ * @core: switch core pointer; ++ * @bus_info: bus info block; ++ */ ++struct mlxsw_i2c { ++ struct { ++ u32 mb_size_in; ++ u32 mb_off_in; ++ u32 mb_size_out; ++ u32 mb_off_out; ++ struct mutex lock; ++ } cmd; ++ struct device *dev; ++ struct mlxsw_core *core; ++ struct mlxsw_bus_info bus_info; ++}; ++ ++#define MLXSW_I2C_READ_MSG(_client, _addr_buf, _buf, _len) { \ ++ { .addr = (_client)->addr, \ ++ .buf = (_addr_buf), \ ++ .len = MLXSW_I2C_ADDR_BUF_SIZE, \ ++ .flags = 0 }, \ ++ { .addr = (_client)->addr, \ ++ .buf = (_buf), \ ++ .len = (_len), \ ++ .flags = I2C_M_RD } } ++ ++#define MLXSW_I2C_WRITE_MSG(_client, _buf, _len) \ ++ { .addr = (_client)->addr, \ ++ .buf = (u8 *)(_buf), \ ++ .len = (_len), \ ++ .flags = 0 } ++ ++/* Routine converts in and out mail boxes offset and size. */ ++static inline void ++mlxsw_i2c_convert_mbox(struct mlxsw_i2c *mlxsw_i2c, u8 *buf) ++{ ++ u32 tmp; ++ ++ /* Local in/out mailboxes: 20 bits for offset, 12 for size */ ++ tmp = be32_to_cpup((__be32 *) buf); ++ mlxsw_i2c->cmd.mb_off_in = tmp & ++ GENMASK(MLXSW_I2C_MBOX_OFFSET_BITS - 1, 0); ++ mlxsw_i2c->cmd.mb_size_in = (tmp & GENMASK(31, ++ MLXSW_I2C_MBOX_OFFSET_BITS)) >> ++ MLXSW_I2C_MBOX_OFFSET_BITS; ++ ++ tmp = be32_to_cpup((__be32 *) (buf + MLXSW_I2C_ADDR_WIDTH)); ++ mlxsw_i2c->cmd.mb_off_out = tmp & ++ GENMASK(MLXSW_I2C_MBOX_OFFSET_BITS - 1, 0); ++ mlxsw_i2c->cmd.mb_size_out = (tmp & GENMASK(31, ++ MLXSW_I2C_MBOX_OFFSET_BITS)) >> ++ MLXSW_I2C_MBOX_OFFSET_BITS; ++} ++ ++/* Routine obtains register size from mail box buffer. */ ++static inline int mlxsw_i2c_get_reg_size(u8 *in_mbox) ++{ ++ u16 tmp = be16_to_cpup((__be16 *) (in_mbox + MLXSW_I2C_TLV_HDR_SIZE)); ++ ++ return (tmp & 0x7ff) * 4 + MLXSW_I2C_TLV_HDR_SIZE; ++} ++ ++/* Routine sets I2C device internal offset in the transaction buffer. */ ++static inline void mlxsw_i2c_set_slave_addr(u8 *buf, u32 off) ++{ ++ __be32 *val = (__be32 *) buf; ++ ++ *val = htonl(off); ++} ++ ++/* Routine waits until go bit is cleared. */ ++static int mlxsw_i2c_wait_go_bit(struct i2c_client *client, ++ struct mlxsw_i2c *mlxsw_i2c, u8 *p_status) ++{ ++ u8 addr_buf[MLXSW_I2C_ADDR_BUF_SIZE]; ++ u8 buf[MLXSW_I2C_READ_SEMA_SIZE]; ++ int len = MLXSW_I2C_READ_SEMA_SIZE; ++ struct i2c_msg read_sema[] = ++ MLXSW_I2C_READ_MSG(client, addr_buf, buf, len); ++ bool wait_done = false; ++ unsigned long end; ++ int i = 0, err; ++ ++ mlxsw_i2c_set_slave_addr(addr_buf, MLXSW_I2C_CIR2_OFF_STATUS); ++ ++ end = jiffies + msecs_to_jiffies(MLXSW_I2C_TIMEOUT_MSECS); ++ do { ++ u32 ctrl; ++ ++ err = i2c_transfer(client->adapter, read_sema, ++ ARRAY_SIZE(read_sema)); ++ ++ ctrl = be32_to_cpu(*(__be32 *) buf); ++ if (err == ARRAY_SIZE(read_sema)) { ++ if (!(ctrl & MLXSW_I2C_GO_BIT)) { ++ wait_done = true; ++ *p_status = ctrl >> ++ MLXSW_I2C_CIR_CTRL_STATUS_SHIFT; ++ break; ++ } ++ } ++ cond_resched(); ++ } while ((time_before(jiffies, end)) || (i++ < MLXSW_I2C_RETRY)); ++ ++ if (wait_done) { ++ if (*p_status) ++ err = -EIO; ++ } else { ++ return -ETIMEDOUT; ++ } ++ ++ return err > 0 ? 0 : err; ++} ++ ++/* Routine posts a command to ASIC though mail box. */ ++static int mlxsw_i2c_write_cmd(struct i2c_client *client, ++ struct mlxsw_i2c *mlxsw_i2c, ++ int immediate) ++{ ++ __be32 push_cmd_buf[MLXSW_I2C_PUSH_CMD_SIZE / 4] = { ++ 0, cpu_to_be32(MLXSW_I2C_PUSH_IMM_CMD) ++ }; ++ __be32 prep_cmd_buf[MLXSW_I2C_PREP_SIZE / 4] = { ++ 0, 0, 0, 0, 0, 0, ++ cpu_to_be32(client->adapter->nr & 0xffff), ++ cpu_to_be32(MLXSW_I2C_SET_IMM_CMD) ++ }; ++ struct i2c_msg push_cmd = ++ MLXSW_I2C_WRITE_MSG(client, push_cmd_buf, ++ MLXSW_I2C_PUSH_CMD_SIZE); ++ struct i2c_msg prep_cmd = ++ MLXSW_I2C_WRITE_MSG(client, prep_cmd_buf, MLXSW_I2C_PREP_SIZE); ++ int err; ++ ++ if (!immediate) { ++ push_cmd_buf[1] = cpu_to_be32(MLXSW_I2C_PUSH_CMD); ++ prep_cmd_buf[7] = cpu_to_be32(MLXSW_I2C_SET_CMD); ++ } ++ mlxsw_i2c_set_slave_addr((u8 *)prep_cmd_buf, ++ MLXSW_I2C_CIR2_BASE); ++ mlxsw_i2c_set_slave_addr((u8 *)push_cmd_buf, ++ MLXSW_I2C_CIR2_OFF_STATUS); ++ ++ /* Prepare Command Interface Register for transaction */ ++ err = i2c_transfer(client->adapter, &prep_cmd, 1); ++ if (err < 0) ++ return err; ++ else if (err != 1) ++ return -EIO; ++ ++ /* Write out Command Interface Register GO bit to push transaction */ ++ err = i2c_transfer(client->adapter, &push_cmd, 1); ++ if (err < 0) ++ return err; ++ else if (err != 1) ++ return -EIO; ++ ++ return 0; ++} ++ ++/* Routine obtains mail box offsets from ASIC register space. */ ++static int mlxsw_i2c_get_mbox(struct i2c_client *client, ++ struct mlxsw_i2c *mlxsw_i2c) ++{ ++ u8 addr_buf[MLXSW_I2C_ADDR_BUF_SIZE]; ++ u8 buf[MLXSW_I2C_MBOX_SIZE]; ++ struct i2c_msg mbox_cmd[] = ++ MLXSW_I2C_READ_MSG(client, addr_buf, buf, MLXSW_I2C_MBOX_SIZE); ++ int err; ++ ++ /* Read mail boxes offsets. */ ++ mlxsw_i2c_set_slave_addr(addr_buf, MLXSW_I2C_CIR2_BASE); ++ err = i2c_transfer(client->adapter, mbox_cmd, 2); ++ if (err != 2) { ++ dev_err(&client->dev, "Could not obtain mail boxes\n"); ++ if (!err) ++ return -EIO; ++ else ++ return err; ++ } ++ ++ /* Convert mail boxes. */ ++ mlxsw_i2c_convert_mbox(mlxsw_i2c, &buf[MLXSW_I2C_MBOX_OUT_PARAM_OFF]); ++ ++ return err; ++} ++ ++/* Routine sends I2C write transaction to ASIC device. */ ++static int ++mlxsw_i2c_write(struct device *dev, size_t in_mbox_size, u8 *in_mbox, int num, ++ u8 *p_status) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client); ++ unsigned long timeout = msecs_to_jiffies(MLXSW_I2C_TIMEOUT_MSECS); ++ u8 tran_buf[MLXSW_I2C_MAX_BUFF_SIZE + MLXSW_I2C_ADDR_BUF_SIZE]; ++ int off = mlxsw_i2c->cmd.mb_off_in, chunk_size, i, j; ++ unsigned long end; ++ struct i2c_msg write_tran = ++ MLXSW_I2C_WRITE_MSG(client, tran_buf, MLXSW_I2C_PUSH_CMD_SIZE); ++ int err; ++ ++ for (i = 0; i < num; i++) { ++ chunk_size = (in_mbox_size > MLXSW_I2C_BLK_MAX) ? ++ MLXSW_I2C_BLK_MAX : in_mbox_size; ++ write_tran.len = MLXSW_I2C_ADDR_WIDTH + chunk_size; ++ mlxsw_i2c_set_slave_addr(tran_buf, off); ++ memcpy(&tran_buf[MLXSW_I2C_ADDR_BUF_SIZE], in_mbox + ++ MLXSW_I2C_BLK_MAX * i, chunk_size); ++ ++ j = 0; ++ end = jiffies + timeout; ++ do { ++ err = i2c_transfer(client->adapter, &write_tran, 1); ++ if (err == 1) ++ break; ++ ++ cond_resched(); ++ } while ((time_before(jiffies, end)) || ++ (j++ < MLXSW_I2C_RETRY)); ++ ++ if (err != 1) { ++ if (!err) ++ err = -EIO; ++ return err; ++ } ++ ++ off += chunk_size; ++ in_mbox_size -= chunk_size; ++ } ++ ++ /* Prepare and write out Command Interface Register for transaction. */ ++ err = mlxsw_i2c_write_cmd(client, mlxsw_i2c, 0); ++ if (err) { ++ dev_err(&client->dev, "Could not start transaction"); ++ return -EIO; ++ } ++ ++ /* Wait until go bit is cleared. */ ++ err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, p_status); ++ if (err) { ++ dev_err(&client->dev, "HW semaphore is not released"); ++ return err; ++ } ++ ++ /* Validate transaction completion status. */ ++ if (*p_status) { ++ dev_err(&client->dev, "Bad transaction completion status %x\n", ++ *p_status); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++/* Routine executes I2C command. */ ++static int ++mlxsw_i2c_cmd(struct device *dev, size_t in_mbox_size, u8 *in_mbox, ++ size_t out_mbox_size, u8 *out_mbox, u8 *status) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client); ++ unsigned long timeout = msecs_to_jiffies(MLXSW_I2C_TIMEOUT_MSECS); ++ u8 tran_buf[MLXSW_I2C_ADDR_BUF_SIZE]; ++ int num, chunk_size, reg_size, i, j; ++ int off = mlxsw_i2c->cmd.mb_off_out; ++ unsigned long end; ++ struct i2c_msg read_tran[] = ++ MLXSW_I2C_READ_MSG(client, tran_buf, NULL, 0); ++ int err; ++ ++ WARN_ON(in_mbox_size % sizeof(u32) || out_mbox_size % sizeof(u32)); ++ ++ reg_size = mlxsw_i2c_get_reg_size(in_mbox); ++ num = reg_size / MLXSW_I2C_BLK_MAX; ++ if (reg_size % MLXSW_I2C_BLK_MAX) ++ num++; ++ ++ if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) { ++ dev_err(&client->dev, "Could not acquire lock"); ++ return -EINVAL; ++ } ++ ++ err = mlxsw_i2c_write(dev, reg_size, in_mbox, num, status); ++ if (err) ++ goto cmd_fail; ++ ++ /* No out mailbox is case of write transaction. */ ++ if (!out_mbox) { ++ mutex_unlock(&mlxsw_i2c->cmd.lock); ++ return 0; ++ } ++ ++ /* Send read transaction to get output mailbox content. */ ++ read_tran[1].buf = out_mbox; ++ for (i = 0; i < num; i++) { ++ chunk_size = (reg_size > MLXSW_I2C_BLK_MAX) ? ++ MLXSW_I2C_BLK_MAX : reg_size; ++ read_tran[1].len = chunk_size; ++ mlxsw_i2c_set_slave_addr(tran_buf, off); ++ ++ j = 0; ++ end = jiffies + timeout; ++ do { ++ err = i2c_transfer(client->adapter, read_tran, ++ ARRAY_SIZE(read_tran)); ++ if (err == ARRAY_SIZE(read_tran)) ++ break; ++ ++ cond_resched(); ++ } while ((time_before(jiffies, end)) || ++ (j++ < MLXSW_I2C_RETRY)); ++ ++ if (err != ARRAY_SIZE(read_tran)) { ++ if (!err) ++ err = -EIO; ++ ++ goto cmd_fail; ++ } ++ ++ off += chunk_size; ++ reg_size -= chunk_size; ++ read_tran[1].buf += chunk_size; ++ } ++ ++ mutex_unlock(&mlxsw_i2c->cmd.lock); ++ ++ return 0; ++ ++cmd_fail: ++ mutex_unlock(&mlxsw_i2c->cmd.lock); ++ return err; ++} ++ ++static int mlxsw_i2c_cmd_exec(void *bus_priv, u16 opcode, u8 opcode_mod, ++ u32 in_mod, bool out_mbox_direct, ++ char *in_mbox, size_t in_mbox_size, ++ char *out_mbox, size_t out_mbox_size, ++ u8 *status) ++{ ++ struct mlxsw_i2c *mlxsw_i2c = bus_priv; ++ ++ return mlxsw_i2c_cmd(mlxsw_i2c->dev, in_mbox_size, in_mbox, ++ out_mbox_size, out_mbox, status); ++} ++ ++static bool mlxsw_i2c_skb_transmit_busy(void *bus_priv, ++ const struct mlxsw_tx_info *tx_info) ++{ ++ return false; ++} ++ ++static int mlxsw_i2c_skb_transmit(void *bus_priv, struct sk_buff *skb, ++ const struct mlxsw_tx_info *tx_info) ++{ ++ return 0; ++} ++ ++static int ++mlxsw_i2c_init(void *bus_priv, struct mlxsw_core *mlxsw_core, ++ const struct mlxsw_config_profile *profile, ++ struct mlxsw_resources *resources) ++{ ++ struct mlxsw_i2c *mlxsw_i2c = bus_priv; ++ ++ mlxsw_i2c->core = mlxsw_core; ++ ++ return 0; ++} ++ ++static void mlxsw_i2c_fini(void *bus_priv) ++{ ++ struct mlxsw_i2c *mlxsw_i2c = bus_priv; ++ ++ mlxsw_i2c->core = NULL; ++} ++ ++static const struct mlxsw_bus mlxsw_i2c_bus = { ++ .kind = "i2c", ++ .init = mlxsw_i2c_init, ++ .fini = mlxsw_i2c_fini, ++ .skb_transmit_busy = mlxsw_i2c_skb_transmit_busy, ++ .skb_transmit = mlxsw_i2c_skb_transmit, ++ .cmd_exec = mlxsw_i2c_cmd_exec, ++}; ++ ++static int mlxsw_i2c_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct mlxsw_i2c *mlxsw_i2c; ++ u8 status; ++ int err; ++ ++ mlxsw_i2c = devm_kzalloc(&client->dev, sizeof(*mlxsw_i2c), GFP_KERNEL); ++ if (!mlxsw_i2c) ++ return -ENOMEM; ++ ++ i2c_set_clientdata(client, mlxsw_i2c); ++ mutex_init(&mlxsw_i2c->cmd.lock); ++ ++ /* In order to use mailboxes through the i2c, special area is reserved ++ * on the i2c address space that can be used for input and output ++ * mailboxes. Such mailboxes are called local mailboxes. When using a ++ * local mailbox, software should specify 0 as the Input/Output ++ * parameters. The location of the Local Mailbox addresses on the i2c ++ * space can be retrieved through the QUERY_FW command. ++ * For this purpose QUERY_FW is to be issued with opcode modifier equal ++ * 0x01. For such command the output parameter is an immediate value. ++ * Here QUERY_FW command is invoked for ASIC probing and for getting ++ * local mailboxes addresses from immedate output parameters. ++ */ ++ ++ /* Prepare and write out Command Interface Register for transaction */ ++ err = mlxsw_i2c_write_cmd(client, mlxsw_i2c, 1); ++ if (err) { ++ dev_err(&client->dev, "Could not start transaction"); ++ goto errout; ++ } ++ ++ /* Wait until go bit is cleared. */ ++ err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, &status); ++ if (err) { ++ dev_err(&client->dev, "HW semaphore is not released"); ++ goto errout; ++ } ++ ++ /* Validate transaction completion status. */ ++ if (status) { ++ dev_err(&client->dev, "Bad transaction completion status %x\n", ++ status); ++ err = -EIO; ++ goto errout; ++ } ++ ++ /* Get mailbox offsets. */ ++ err = mlxsw_i2c_get_mbox(client, mlxsw_i2c); ++ if (err < 0) { ++ dev_err(&client->dev, "Fail to get mailboxes\n"); ++ goto errout; ++ } ++ ++ dev_info(&client->dev, "%s mb size=%x off=0x%08x out mb size=%x off=0x%08x\n", ++ id->name, mlxsw_i2c->cmd.mb_size_in, ++ mlxsw_i2c->cmd.mb_off_in, mlxsw_i2c->cmd.mb_size_out, ++ mlxsw_i2c->cmd.mb_off_out); ++ ++ /* Register device bus. */ ++ mlxsw_i2c->bus_info.device_kind = id->name; ++ mlxsw_i2c->bus_info.device_name = client->name; ++ mlxsw_i2c->bus_info.dev = &client->dev; ++ mlxsw_i2c->dev = &client->dev; ++ ++ err = mlxsw_core_bus_device_register(&mlxsw_i2c->bus_info, ++ &mlxsw_i2c_bus, mlxsw_i2c); ++ if (err) { ++ dev_err(&client->dev, "Fail to register core bus\n"); ++ return err; ++ } ++ ++ return 0; ++ ++errout: ++ i2c_set_clientdata(client, NULL); ++ ++ return err; ++} ++ ++static int mlxsw_i2c_remove(struct i2c_client *client) ++{ ++ struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client); ++ ++ mlxsw_core_bus_device_unregister(mlxsw_i2c->core); ++ mutex_destroy(&mlxsw_i2c->cmd.lock); ++ ++ return 0; ++} ++ ++int mlxsw_i2c_driver_register(struct i2c_driver *i2c_driver) ++{ ++ i2c_driver->probe = mlxsw_i2c_probe; ++ i2c_driver->remove = mlxsw_i2c_remove; ++ return i2c_add_driver(i2c_driver); ++} ++EXPORT_SYMBOL(mlxsw_i2c_driver_register); ++ ++void mlxsw_i2c_driver_unregister(struct i2c_driver *i2c_driver) ++{ ++ i2c_del_driver(i2c_driver); ++} ++EXPORT_SYMBOL(mlxsw_i2c_driver_unregister); ++ ++MODULE_AUTHOR("Vadim Pasternak "); ++MODULE_DESCRIPTION("Mellanox switch I2C interface driver"); ++MODULE_LICENSE("Dual BSD/GPL"); +diff -Nur a/drivers/net/ethernet/mellanox/mlxsw/i2c.h b/drivers/net/ethernet/mellanox/mlxsw/i2c.h +--- a/drivers/net/ethernet/mellanox/mlxsw/i2c.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/net/ethernet/mellanox/mlxsw/i2c.h 2017-11-09 12:03:09.944785064 +0000 +@@ -0,0 +1,60 @@ ++/* ++ * drivers/net/ethernet/mellanox/mlxsw/i2c.h ++ * Copyright (c) 2016 Mellanox Technologies. All rights reserved. ++ * Copyright (c) 2016 Vadim Pasternak ++ * ++ * 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. ++ */ ++ ++#ifndef _MLXSW_I2C_H ++#define _MLXSW_I2C_H ++ ++#include ++ ++#if IS_ENABLED(CONFIG_MLXSW_I2C) ++ ++int mlxsw_i2c_driver_register(struct i2c_driver *i2c_driver); ++void mlxsw_i2c_driver_unregister(struct i2c_driver *i2c_driver); ++ ++#else ++ ++static inline int ++mlxsw_i2c_driver_register(struct i2c_driver *i2c_driver) ++{ ++ return -ENODEV; ++} ++ ++static inline void ++mlxsw_i2c_driver_unregister(struct i2c_driver *i2c_driver) ++{ ++} ++ ++#endif ++ ++#endif +diff -Nur a/drivers/net/ethernet/mellanox/mlxsw/minimal.c b/drivers/net/ethernet/mellanox/mlxsw/minimal.c +--- a/drivers/net/ethernet/mellanox/mlxsw/minimal.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/net/ethernet/mellanox/mlxsw/minimal.c 2017-11-09 12:03:23.332785242 +0000 +@@ -0,0 +1,97 @@ ++/* ++ * drivers/net/ethernet/mellanox/mlxsw/minimal.c ++ * Copyright (c) 2016 Mellanox Technologies. All rights reserved. ++ * Copyright (c) 2016 Vadim Pasternak ++ * ++ * 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 ++#include ++#include ++#include ++#include ++ ++#include "core.h" ++#include "i2c.h" ++ ++static const char mlxsw_minimal_driver_name[] = "mlxsw_minimal"; ++ ++static const struct mlxsw_config_profile mlxsw_minimal_config_profile; ++ ++static struct mlxsw_driver mlxsw_minimal_driver = { ++ .kind = mlxsw_minimal_driver_name, ++ .priv_size = 1, ++ .profile = &mlxsw_minimal_config_profile, ++}; ++ ++static const struct i2c_device_id mlxsw_minimal_i2c_id[] = { ++ { "mlxsw_minimal", 0}, ++ { }, ++}; ++ ++static struct i2c_driver mlxsw_minimal_i2c_driver = { ++ .driver.name = "mlxsw_minimal", ++ .class = I2C_CLASS_HWMON, ++ .id_table = mlxsw_minimal_i2c_id, ++}; ++ ++static int __init mlxsw_minimal_module_init(void) ++{ ++ int err; ++ ++ err = mlxsw_core_driver_register(&mlxsw_minimal_driver); ++ if (err) ++ return err; ++ ++ err = mlxsw_i2c_driver_register(&mlxsw_minimal_i2c_driver); ++ if (err) ++ goto err_i2c_driver_register; ++ ++ return 0; ++ ++err_i2c_driver_register: ++ mlxsw_core_driver_unregister(&mlxsw_minimal_driver); ++ ++ return err; ++} ++ ++static void __exit mlxsw_minimal_module_exit(void) ++{ ++ mlxsw_i2c_driver_unregister(&mlxsw_minimal_i2c_driver); ++ mlxsw_core_driver_unregister(&mlxsw_minimal_driver); ++} ++ ++module_init(mlxsw_minimal_module_init); ++module_exit(mlxsw_minimal_module_exit); ++ ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_AUTHOR("Vadim Pasternak "); ++MODULE_DESCRIPTION("Mellanox minimal driver"); ++MODULE_DEVICE_TABLE(i2c, mlxsw_minimal_i2c_id); +diff -Nur a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c +--- a/drivers/net/ethernet/mellanox/mlxsw/pci.c 2017-05-25 13:45:05.000000000 +0000 ++++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c 2017-11-09 13:03:45.824833341 +0000 +@@ -1836,6 +1836,7 @@ + .skb_transmit_busy = mlxsw_pci_skb_transmit_busy, + .skb_transmit = mlxsw_pci_skb_transmit, + .cmd_exec = mlxsw_pci_cmd_exec, ++ .features = MLXSW_BUS_F_TXRX, + }; + + static int mlxsw_pci_sw_reset(struct mlxsw_pci *mlxsw_pci, +diff -Nur a/drivers/net/ethernet/mellanox/mlxsw/qsfp_sysfs.c b/drivers/net/ethernet/mellanox/mlxsw/qsfp_sysfs.c +--- a/drivers/net/ethernet/mellanox/mlxsw/qsfp_sysfs.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/net/ethernet/mellanox/mlxsw/qsfp_sysfs.c 2017-11-09 13:04:52.192834223 +0000 +@@ -0,0 +1,379 @@ ++/* ++ * drivers/net/ethernet/mellanox/mlxsw/qsfp_sysfs.c ++ * Copyright (c) 2017 Mellanox Technologies. All rights reserved. ++ * Copyright (c) 2017 Vadim Pasternak ++ * ++ * 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 ++#include ++#include ++#include ++#include ++ ++#include "core.h" ++ ++#define MLXSW_QSFP_I2C_ADDR 0x50 ++#define MLXSW_QSFP_PAGE_NUM 5 ++#define MLXSW_QSFP_PAGE_SIZE 128 ++#define MLXSW_QSFP_SUB_PAGE_NUM 3 ++#define MLXSW_QSFP_SUB_PAGE_SIZE 48 ++#define MLXSW_QSFP_LAST_SUB_PAGE_SIZE 32 ++#define MLXSW_QSFP_MAX_NUM 64 ++#define MLXSW_QSFP_MIN_REQ_LEN 4 ++#define MLXSW_QSFP_STATUS_VALID_TIME (120 * HZ) ++#define MLXSW_QSFP_MAX_CPLD_NUM 1 ++ ++static const u8 mlxsw_qsfp_page_number[] = { 0xa0, 0x00, 0x01, 0x02, 0x03 }; ++static const u16 mlxsw_qsfp_page_shift[] = { 0x00, 0x80, 0x80, 0x80, 0x80 }; ++ ++/** ++ * Mellanox device Management Cable Info Access Register buffer for reading ++ * QSFP EEPROM info is limited by 48 bytes. In case full page is to be read ++ * (128 bytes), such request will be implemented by three transactions of size ++ * 48, 48, 32. ++ */ ++static const u16 mlxsw_qsfp_sub_page_size[] = { ++ MLXSW_QSFP_SUB_PAGE_SIZE, ++ MLXSW_QSFP_SUB_PAGE_SIZE, ++ MLXSW_QSFP_LAST_SUB_PAGE_SIZE ++}; ++ ++struct mlxsw_qsfp_module { ++ unsigned long last_updated; ++ u8 cache_status; ++}; ++ ++struct mlxsw_qsfp { ++ struct mlxsw_core *core; ++ const struct mlxsw_bus_info *bus_info; ++ struct attribute *attrs[MLXSW_QSFP_MAX_NUM + 1]; ++ struct device_attribute *dev_attrs; ++ struct bin_attribute *eeprom; ++ struct bin_attribute **eeprom_attr_list; ++ struct mlxsw_qsfp_module modules[MLXSW_QSFP_MAX_NUM]; ++ u8 module_ind[MLXSW_QSFP_MAX_NUM]; ++ u8 module_count; ++ struct attribute *cpld_attrs[MLXSW_QSFP_MAX_CPLD_NUM + 1]; ++ struct device_attribute *cpld_dev_attrs; ++}; ++ ++static int ++mlxsw_qsfp_query_module_eeprom(struct mlxsw_qsfp *mlxsw_qsfp, u8 index, ++ loff_t off, size_t count, int page, char *buf) ++{ ++ char eeprom_tmp[MLXSW_QSFP_PAGE_SIZE]; ++ char mcia_pl[MLXSW_REG_MCIA_LEN]; ++ int status; ++ int err; ++ ++ mlxsw_reg_mcia_pack(mcia_pl, index, 0, page, off, count, ++ MLXSW_QSFP_I2C_ADDR); ++ ++ err = mlxsw_reg_query(mlxsw_qsfp->core, MLXSW_REG(mcia), mcia_pl); ++ if (err) ++ return err; ++ ++ status = mlxsw_reg_mcia_status_get(mcia_pl); ++ if (status) ++ return -EIO; ++ ++ mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp); ++ memcpy(buf, eeprom_tmp, count); ++ ++ return 0; ++} ++ ++static int ++mlxsw_qsfp_get_module_eeprom(struct mlxsw_qsfp *mlxsw_qsfp, u8 index, ++ char *buf, loff_t off, size_t count) ++{ ++ int page_ind, page, page_off, subpage, offset, size, res = 0; ++ int err; ++ ++ if (!count) ++ return -EINVAL; ++ ++ memset(buf, 0, count); ++ size = count; ++ while (res < count) { ++ page_ind = off / MLXSW_QSFP_PAGE_SIZE; ++ page_off = off % MLXSW_QSFP_PAGE_SIZE; ++ page = mlxsw_qsfp_page_number[page_ind]; ++ offset = mlxsw_qsfp_page_shift[page_ind] + page_off; ++ subpage = page_off / MLXSW_QSFP_SUB_PAGE_SIZE; ++ size = min_t(u16, size, mlxsw_qsfp_sub_page_size[subpage]); ++ err = mlxsw_qsfp_query_module_eeprom(mlxsw_qsfp, index, offset, ++ size, page, buf + res); ++ if (err) { ++ dev_err(mlxsw_qsfp->bus_info->dev, "Eeprom query failed\n"); ++ return err; ++ } ++ off += size; ++ res += size; ++ size = count - size; ++ } ++ ++ return res; ++} ++ ++static ssize_t mlxsw_qsfp_bin_read(struct file *filp, struct kobject *kobj, ++ struct bin_attribute *attr, char *buf, ++ loff_t off, size_t count) ++{ ++ struct mlxsw_qsfp *mlxsw_qsfp = dev_get_platdata(container_of(kobj, ++ struct device, kobj)); ++ u8 *module_ind = attr->private; ++ size_t size; ++ ++ size = mlxsw_qsfp->eeprom[*module_ind].size; ++ ++ if (off > size) ++ return -ESPIPE; ++ else if (off == size) ++ return 0; ++ else if ((off + count) > size) ++ count = size - off; ++ ++ return mlxsw_qsfp_get_module_eeprom(mlxsw_qsfp, *module_ind, buf, off, ++ count); ++} ++ ++static ssize_t ++mlxsw_qsfp_status_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct mlxsw_qsfp *mlxsw_qsfp = dev_get_platdata(dev); ++ char mcia_pl[MLXSW_REG_MCIA_LEN]; ++ int status; ++ u32 i; ++ int err; ++ ++ for (i = 0; i < mlxsw_qsfp->module_count; i++) { ++ if ((mlxsw_qsfp->dev_attrs + i) == attr) ++ break; ++ } ++ if (i == mlxsw_qsfp->module_count) ++ return -EINVAL; ++ ++ if (time_before(jiffies, mlxsw_qsfp->modules[i].last_updated + ++ MLXSW_QSFP_STATUS_VALID_TIME)) ++ return sprintf(buf, "%u\n", ++ mlxsw_qsfp->modules[i].cache_status); ++ ++ mlxsw_reg_mcia_pack(mcia_pl, i, 0, 0, 0, MLXSW_QSFP_MIN_REQ_LEN, ++ MLXSW_QSFP_I2C_ADDR); ++ err = mlxsw_reg_query(mlxsw_qsfp->core, MLXSW_REG(mcia), mcia_pl); ++ if (err) ++ return err; ++ ++ status = mlxsw_reg_mcia_status_get(mcia_pl); ++ mlxsw_qsfp->modules[i].cache_status = !status; ++ mlxsw_qsfp->modules[i].last_updated = jiffies; ++ ++ return sprintf(buf, "%u\n", !status); ++} ++ ++static ssize_t ++mlxsw_qsfp_cpld_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct mlxsw_qsfp *mlxsw_qsfp = dev_get_platdata(dev); ++ char msci_pl[MLXSW_REG_MSCI_LEN]; ++ u32 version, i; ++ int err; ++ ++ for (i = 0; i < MLXSW_QSFP_MAX_CPLD_NUM; i++) { ++ if ((mlxsw_qsfp->cpld_dev_attrs + i) == attr) ++ break; ++ } ++ if (i == MLXSW_QSFP_MAX_CPLD_NUM) ++ return -EINVAL; ++ ++ mlxsw_reg_msci_pack(msci_pl, i); ++ err = mlxsw_reg_query(mlxsw_qsfp->core, MLXSW_REG(msci), msci_pl); ++ if (err) ++ return err; ++ ++ version = mlxsw_reg_msci_version_get(msci_pl); ++ ++ return sprintf(buf, "%u\n", version); ++} ++ ++int mlxsw_qsfp_init(struct mlxsw_core *mlxsw_core, ++ const struct mlxsw_bus_info *mlxsw_bus_info, ++ struct mlxsw_qsfp **p_qsfp) ++{ ++ struct device_attribute *dev_attr, *cpld_dev_attr; ++ char pmlp_pl[MLXSW_REG_PMLP_LEN]; ++ struct mlxsw_qsfp *mlxsw_qsfp; ++ struct bin_attribute *eeprom; ++ int i, count; ++ u8 width; ++ int err; ++ ++ if (!strcmp(mlxsw_bus_info->device_kind, "i2c")) ++ return 0; ++ ++ mlxsw_qsfp = devm_kzalloc(mlxsw_bus_info->dev, sizeof(*mlxsw_qsfp), ++ GFP_KERNEL); ++ if (!mlxsw_qsfp) ++ return -ENOMEM; ++ ++ mlxsw_qsfp->core = mlxsw_core; ++ mlxsw_qsfp->bus_info = mlxsw_bus_info; ++ mlxsw_bus_info->dev->platform_data = mlxsw_qsfp; ++ ++ for (i = 1; i <= MLXSW_QSFP_MAX_NUM; i++) { ++ mlxsw_reg_pmlp_pack(pmlp_pl, i); ++ err = mlxsw_reg_query(mlxsw_qsfp->core, MLXSW_REG(pmlp), ++ pmlp_pl); ++ if (err) ++ return err; ++ width = mlxsw_reg_pmlp_width_get(pmlp_pl); ++ if (!width) ++ continue; ++ mlxsw_qsfp->module_count++; ++ } ++ ++ count = mlxsw_qsfp->module_count + 1; ++ mlxsw_qsfp->eeprom = devm_kzalloc(mlxsw_bus_info->dev, ++ mlxsw_qsfp->module_count * ++ sizeof(*mlxsw_qsfp->eeprom), ++ GFP_KERNEL); ++ if (!mlxsw_qsfp->eeprom) ++ return -ENOMEM; ++ ++ mlxsw_qsfp->eeprom_attr_list = devm_kzalloc(mlxsw_bus_info->dev, ++ count * ++ sizeof(mlxsw_qsfp->eeprom), ++ GFP_KERNEL); ++ if (!mlxsw_qsfp->eeprom_attr_list) ++ return -ENOMEM; ++ ++ mlxsw_qsfp->dev_attrs = devm_kzalloc(mlxsw_bus_info->dev, count * ++ sizeof(*mlxsw_qsfp->dev_attrs), ++ GFP_KERNEL); ++ if (!mlxsw_qsfp->dev_attrs) ++ return -ENOMEM; ++ ++ mlxsw_qsfp->cpld_dev_attrs = devm_kzalloc(mlxsw_bus_info->dev, ++ MLXSW_QSFP_MAX_CPLD_NUM * ++ sizeof(*mlxsw_qsfp->cpld_dev_attrs), ++ GFP_KERNEL); ++ if (!mlxsw_qsfp->cpld_dev_attrs) ++ return -ENOMEM; ++ ++ eeprom = mlxsw_qsfp->eeprom; ++ dev_attr = mlxsw_qsfp->dev_attrs; ++ for (i = 0; i < mlxsw_qsfp->module_count; i++, eeprom++, dev_attr++) { ++ dev_attr->show = mlxsw_qsfp_status_show; ++ dev_attr->attr.mode = 0444; ++ dev_attr->attr.name = devm_kasprintf(mlxsw_bus_info->dev, ++ GFP_KERNEL, ++ "qsfp%d_status", i + 1); ++ mlxsw_qsfp->attrs[i] = &dev_attr->attr; ++ sysfs_attr_init(&dev_attr->attr); ++ err = sysfs_create_file(&mlxsw_bus_info->dev->kobj, ++ mlxsw_qsfp->attrs[i]); ++ if (err) ++ goto err_create_file; ++ ++ sysfs_bin_attr_init(eeprom); ++ eeprom->attr.name = devm_kasprintf(mlxsw_bus_info->dev, ++ GFP_KERNEL, "qsfp%d", ++ i + 1); ++ eeprom->attr.mode = 0444; ++ eeprom->read = mlxsw_qsfp_bin_read; ++ eeprom->size = MLXSW_QSFP_PAGE_NUM * MLXSW_QSFP_PAGE_SIZE; ++ mlxsw_qsfp->module_ind[i] = i; ++ eeprom->private = &mlxsw_qsfp->module_ind[i]; ++ mlxsw_qsfp->eeprom_attr_list[i] = eeprom; ++ err = sysfs_create_bin_file(&mlxsw_bus_info->dev->kobj, ++ eeprom); ++ if (err) ++ goto err_create_bin_file; ++ } ++ ++ cpld_dev_attr = mlxsw_qsfp->cpld_dev_attrs; ++ for (i = 0; i < MLXSW_QSFP_MAX_CPLD_NUM; i++, cpld_dev_attr++) { ++ cpld_dev_attr->show = mlxsw_qsfp_cpld_show; ++ cpld_dev_attr->attr.mode = 0444; ++ cpld_dev_attr->attr.name = devm_kasprintf(mlxsw_bus_info->dev, ++ GFP_KERNEL, ++ "cpld%d_version", i + 1); ++ mlxsw_qsfp->cpld_attrs[i] = &cpld_dev_attr->attr; ++ sysfs_attr_init(&cpld_dev_attr->attr); ++ err = sysfs_create_file(&mlxsw_bus_info->dev->kobj, ++ mlxsw_qsfp->cpld_attrs[i]); ++ if (err) ++ goto err_create_cpld_file; ++ } ++ ++ *p_qsfp = mlxsw_qsfp; ++ ++ return 0; ++ ++err_create_cpld_file: ++ sysfs_remove_file(&mlxsw_bus_info->dev->kobj, ++ mlxsw_qsfp->cpld_attrs[i--]); ++ i = mlxsw_qsfp->module_count; ++err_create_bin_file: ++ sysfs_remove_file(&mlxsw_bus_info->dev->kobj, ++ mlxsw_qsfp->attrs[i--]); ++err_create_file: ++ while (--i > 0) { ++ sysfs_remove_bin_file(&mlxsw_bus_info->dev->kobj, ++ mlxsw_qsfp->eeprom_attr_list[i]); ++ sysfs_remove_file(&mlxsw_bus_info->dev->kobj, ++ mlxsw_qsfp->attrs[i]); ++ } ++ ++ return err; ++} ++ ++void mlxsw_qsfp_fini(struct mlxsw_qsfp *mlxsw_qsfp) ++{ ++ int i; ++ ++ if (!strcmp(mlxsw_qsfp->bus_info->device_kind, "i2c")) ++ return; ++ ++ for (i = mlxsw_qsfp->module_count - 1; i >= 0; i--) { ++ sysfs_remove_bin_file(&mlxsw_qsfp->bus_info->dev->kobj, ++ mlxsw_qsfp->eeprom_attr_list[i]); ++ sysfs_remove_file(&mlxsw_qsfp->bus_info->dev->kobj, ++ mlxsw_qsfp->attrs[i]); ++ } ++} ++ ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_AUTHOR("Vadim Pasternak "); ++MODULE_DESCRIPTION("Mellanox switch QSFP sysfs driver"); +diff -Nur a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h +--- a/drivers/net/ethernet/mellanox/mlxsw/reg.h 2017-05-25 13:45:05.000000000 +0000 ++++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h 2017-11-09 13:05:10.576834467 +0000 +@@ -50,6 +50,12 @@ + u16 len; /* In u8 */ + }; + ++#define MLXSW_REG_DEFINE(_name, _id, _len) \ ++static const struct mlxsw_reg_info mlxsw_reg_##_name = { \ ++ .id = _id, \ ++ .len = _len, \ ++} ++ + #define MLXSW_REG(type) (&mlxsw_reg_##type) + #define MLXSW_REG_LEN(type) MLXSW_REG(type)->len + #define MLXSW_REG_ZERO(type, payload) memset(payload, 0, MLXSW_REG(type)->len) +@@ -4466,7 +4472,7 @@ + */ + MLXSW_ITEM32(reg, mfcr, pwm_frequency, 0x00, 0, 6); + +-#define MLXSW_MFCR_TACHOS_MAX 10 ++#define MLXSW_MFCR_TACHOS_MAX 12 + + /* reg_mfcr_tacho_active + * Indicates which of the tachometer is active (bit per tachometer). +@@ -4564,6 +4570,54 @@ + mlxsw_reg_mfsm_tacho_set(payload, tacho); + } + ++/* MFSL - Management Fan Speed Limit Register ++ * ------------------------------------------ ++ * The Fan Speed Limit register is used to configure the fan speed ++ * event / interrupt notification mechanism. Fan speed threshold are ++ * defined for both under-speed and over-speed. ++ */ ++#define MLXSW_REG_MFSL_ID 0x9004 ++#define MLXSW_REG_MFSL_LEN 0x0C ++ ++MLXSW_REG_DEFINE(mfsl, MLXSW_REG_MFSL_ID, MLXSW_REG_MFSL_LEN); ++ ++/* reg_mfsl_tacho ++ * Fan tachometer index. ++ * Access: Index ++ */ ++MLXSW_ITEM32(reg, mfsl, tacho, 0x00, 24, 4); ++ ++/* reg_mfsl_tach_min ++ * Tachometer minimum value (minimum RPM). ++ * Access: RW ++ */ ++MLXSW_ITEM32(reg, mfsl, tach_min, 0x04, 0, 16); ++ ++/* reg_mfsl_tach_max ++ * Tachometer maximum value (maximum RPM). ++ * Access: RW ++ */ ++MLXSW_ITEM32(reg, mfsl, tach_max, 0x08, 0, 16); ++ ++static inline void mlxsw_reg_mfsl_pack(char *payload, u8 tacho, ++ u16 tach_min, u16 tach_max) ++{ ++ MLXSW_REG_ZERO(mfsl, payload); ++ mlxsw_reg_mfsl_tacho_set(payload, tacho); ++ mlxsw_reg_mfsl_tach_min_set(payload, tach_min); ++ mlxsw_reg_mfsl_tach_max_set(payload, tach_max); ++} ++ ++static inline void mlxsw_reg_mfsl_unpack(char *payload, u8 tacho, ++ u16 *p_tach_min, u16 *p_tach_max) ++{ ++ if (p_tach_min) ++ *p_tach_min = mlxsw_reg_mfsl_tach_min_get(payload); ++ ++ if (p_tach_max) ++ *p_tach_max = mlxsw_reg_mfsl_tach_max_get(payload); ++} ++ + /* MTCAP - Management Temperature Capabilities + * ------------------------------------------- + * This register exposes the capabilities of the device and +@@ -4635,6 +4689,29 @@ + */ + MLXSW_ITEM32(reg, mtmp, max_temperature, 0x08, 0, 16); + ++/* reg_mtmp_tee ++ * Temperature Event Enable. ++ * 0 - Do not generate event ++ * 1 - Generate event ++ * 2 - Generate single event ++ * Access: RW ++ */ ++MLXSW_ITEM32(reg, mtmp, tee, 0x0C, 30, 2); ++ ++#define MLXSW_REG_MTMP_THRESH_HI 0x348 /* 105 Celsius */ ++ ++/* reg_mtmp_temperature_threshold_hi ++ * High threshold for Temperature Warning Event. In 0.125 Celsius. ++ * Access: RW ++ */ ++MLXSW_ITEM32(reg, mtmp, temperature_threshold_hi, 0x0C, 0, 16); ++ ++/* reg_mtmp_temperature_threshold_lo ++ * Low threshold for Temperature Warning Event. In 0.125 Celsius. ++ * Access: RW ++ */ ++MLXSW_ITEM32(reg, mtmp, temperature_threshold_lo, 0x10, 0, 16); ++ + #define MLXSW_REG_MTMP_SENSOR_NAME_SIZE 8 + + /* reg_mtmp_sensor_name +@@ -4651,6 +4728,8 @@ + mlxsw_reg_mtmp_sensor_index_set(payload, sensor_index); + mlxsw_reg_mtmp_mte_set(payload, max_temp_enable); + mlxsw_reg_mtmp_mtr_set(payload, max_temp_reset); ++ mlxsw_reg_mtmp_temperature_threshold_hi_set(payload, ++ MLXSW_REG_MTMP_THRESH_HI); + } + + static inline void mlxsw_reg_mtmp_unpack(char *payload, unsigned int *p_temp, +@@ -4671,6 +4750,81 @@ + mlxsw_reg_mtmp_sensor_name_memcpy_from(payload, sensor_name); + } + ++/* MCIA - Management Cable Info Access ++ * ----------------------------------- ++ * MCIA register is used to access the SFP+ and QSFP connector's EPROM. ++ */ ++ ++#define MLXSW_REG_MCIA_ID 0x9014 ++#define MLXSW_REG_MCIA_LEN 0x40 ++ ++MLXSW_REG_DEFINE(mcia, MLXSW_REG_MCIA_ID, MLXSW_REG_MCIA_LEN); ++ ++/* reg_mcia_l ++ * Lock bit. Setting this bit will lock the access to the specific ++ * cable. Used for updating a full page in a cable EPROM. Any access ++ * other then subsequence writes will fail while the port is locked. ++ * Access: RW ++ */ ++MLXSW_ITEM32(reg, mcia, l, 0x00, 31, 1); ++ ++/* reg_mcia_module ++ * Module number. ++ * Access: Index ++ */ ++MLXSW_ITEM32(reg, mcia, module, 0x00, 16, 8); ++ ++/* reg_mcia_status ++ * Module status. ++ * Access: RO ++ */ ++MLXSW_ITEM32(reg, mcia, status, 0x00, 0, 8); ++ ++/* reg_mcia_i2c_device_address ++ * I2C device address. ++ * Access: RW ++ */ ++MLXSW_ITEM32(reg, mcia, i2c_device_address, 0x04, 24, 8); ++ ++/* reg_mcia_page_number ++ * Page number. ++ * Access: RW ++ */ ++MLXSW_ITEM32(reg, mcia, page_number, 0x04, 16, 8); ++ ++/* reg_mcia_device_address ++ * Device address. ++ * Access: RW ++ */ ++MLXSW_ITEM32(reg, mcia, device_address, 0x04, 0, 16); ++ ++/* reg_mcia_size ++ * Number of bytes to read/write (up to 48 bytes). ++ * Access: RW ++ */ ++MLXSW_ITEM32(reg, mcia, size, 0x08, 0, 16); ++ ++#define MLXSW_SP_REG_MCIA_EEPROM_SIZE 48 ++ ++/* reg_mcia_eeprom ++ * Bytes to read/write. ++ * Access: RW ++ */ ++MLXSW_ITEM_BUF(reg, mcia, eeprom, 0x10, MLXSW_SP_REG_MCIA_EEPROM_SIZE); ++ ++static inline void mlxsw_reg_mcia_pack(char *payload, u8 module, u8 lock, ++ u8 page_number, u16 device_addr, ++ u8 size, u8 i2c_device_addr) ++{ ++ MLXSW_REG_ZERO(mcia, payload); ++ mlxsw_reg_mcia_module_set(payload, module); ++ mlxsw_reg_mcia_l_set(payload, lock); ++ mlxsw_reg_mcia_page_number_set(payload, page_number); ++ mlxsw_reg_mcia_device_address_set(payload, device_addr); ++ mlxsw_reg_mcia_size_set(payload, size); ++ mlxsw_reg_mcia_i2c_device_address_set(payload, i2c_device_addr); ++} ++ + /* MPAT - Monitoring Port Analyzer Table + * ------------------------------------- + * MPAT Register is used to query and configure the Switch PortAnalyzer Table. +@@ -4788,6 +4942,43 @@ + mlxsw_reg_mpar_pa_id_set(payload, pa_id); + } + ++/* MSCI - Management System CPLD Information Register ++ * --------------------------------------------------- ++ * This register allows querying for the System CPLD(s) information. ++ */ ++#define MLXSW_REG_MSCI_ID 0x902A ++#define MLXSW_REG_MSCI_LEN 0x10 ++ ++static const struct mlxsw_reg_info mlxsw_reg_msci = { ++ .id = MLXSW_REG_MSCI_ID, ++ .len = MLXSW_REG_MSCI_LEN, ++}; ++ ++/* reg_msci_index ++ * Index to access. ++ * Access: Index ++ */ ++MLXSW_ITEM32(reg, msci, index, 0x00, 0, 4); ++ ++/* reg_msci_version ++ * CPLD version. ++ * Access: R0 ++ */ ++MLXSW_ITEM32(reg, msci, version, 0x04, 0, 32); ++ ++static inline void ++mlxsw_reg_msci_pack(char *payload, u8 index) ++{ ++ MLXSW_REG_ZERO(msci, payload); ++ mlxsw_reg_msci_index_set(payload, index); ++} ++ ++static inline void ++mlxsw_reg_msci_unpack(char *payload, u16 *p_version) ++{ ++ *p_version = mlxsw_reg_msci_version_get(payload); ++} ++ + /* MLCR - Management LED Control Register + * -------------------------------------- + * Controls the system LEDs. diff --git a/patch/0007-hwmon-pmbus-Add-support-for-Intel-VID-protocol-VR13.patch b/patch/0007-hwmon-pmbus-Add-support-for-Intel-VID-protocol-VR13.patch new file mode 100644 index 000000000..f0a9bde81 --- /dev/null +++ b/patch/0007-hwmon-pmbus-Add-support-for-Intel-VID-protocol-VR13.patch @@ -0,0 +1,30 @@ +Linux backport patch. Includes following commits: +a4dffccb72a7fa46bb0d7f29e607375387e09956 + + +diff -Nur a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h +--- a/drivers/hwmon/pmbus/pmbus.h 2017-11-09 16:25:22.760993964 +0000 ++++ b/drivers/hwmon/pmbus/pmbus.h 2017-11-09 16:26:02.568994492 +0000 +@@ -341,7 +341,7 @@ + #define PMBUS_HAVE_STATUS_VMON BIT(19) + + enum pmbus_data_format { linear = 0, direct, vid }; +-enum vrm_version { vr11 = 0, vr12 }; ++enum vrm_version { vr11 = 0, vr12, vr13 }; + + struct pmbus_driver_info { + int pages; /* Total number of pages */ +diff -Nur a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c +--- a/drivers/hwmon/pmbus/pmbus_core.c 2017-11-09 16:25:22.760993964 +0000 ++++ b/drivers/hwmon/pmbus/pmbus_core.c 2017-11-09 16:26:02.568994492 +0000 +@@ -531,6 +531,10 @@ + if (val >= 0x01) + rv = 250 + (val - 1) * 5; + break; ++ case vr13: ++ if (val >= 0x01) ++ rv = 500 + (val - 1) * 10; ++ break; + } + return rv; + } diff --git a/patch/0008-hwmon-pmbus-Add-support-for-Texas-Instruments-tps536.patch b/patch/0008-hwmon-pmbus-Add-support-for-Texas-Instruments-tps536.patch new file mode 100644 index 000000000..f95e27695 --- /dev/null +++ b/patch/0008-hwmon-pmbus-Add-support-for-Texas-Instruments-tps536.patch @@ -0,0 +1,151 @@ +Linux backport patch. Includes following commits: +f7caf758e26ab84b2b9def9ec68235c85d645597 + + +diff -Nur a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig +--- a/drivers/hwmon/pmbus/Kconfig 2017-11-09 16:34:05.269000902 +0000 ++++ b/drivers/hwmon/pmbus/Kconfig 2017-11-09 16:35:49.701002288 +0000 +@@ -125,6 +125,15 @@ + This driver can also be built as a module. If so, the module will + be called tps40422. + ++config SENSORS_TPS53679 ++ tristate "TI TPS53679" ++ help ++ If you say yes here you get hardware monitoring support for TI ++ TPS53679. ++ ++ This driver can also be built as a module. If so, the module will ++ be called tps53679. ++ + config SENSORS_UCD9000 + tristate "TI UCD90120, UCD90124, UCD90160, UCD9090, UCD90910" + default n +diff -Nur a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile +--- a/drivers/hwmon/pmbus/Makefile 2017-11-09 16:34:05.269000902 +0000 ++++ b/drivers/hwmon/pmbus/Makefile 2017-11-09 16:35:49.701002288 +0000 +@@ -13,6 +13,7 @@ + obj-$(CONFIG_SENSORS_MAX34440) += max34440.o + obj-$(CONFIG_SENSORS_MAX8688) += max8688.o + obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o ++obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o + obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o + obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o + obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o +diff -Nur a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c +--- a/drivers/hwmon/pmbus/tps53679.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/hwmon/pmbus/tps53679.c 2017-11-09 16:35:49.701002288 +0000 +@@ -0,0 +1,113 @@ ++/* ++ * Hardware monitoring driver for Texas Instruments TPS53679 ++ * ++ * Copyright (c) 2017 Mellanox Technologies. All rights reserved. ++ * Copyright (c) 2017 Vadim Pasternak ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "pmbus.h" ++ ++#define TPS53679_PROT_VR12_5MV 0x01 /* VR12.0 mode, 5-mV DAC */ ++#define TPS53679_PROT_VR12_5_10MV 0x02 /* VR12.5 mode, 10-mV DAC */ ++#define TPS53679_PROT_VR13_10MV 0x04 /* VR13.0 mode, 10-mV DAC */ ++#define TPS53679_PROT_IMVP8_5MV 0x05 /* IMVP8 mode, 5-mV DAC */ ++#define TPS53679_PROT_VR13_5MV 0x07 /* VR13.0 mode, 5-mV DAC */ ++#define TPS53679_PAGE_NUM 2 ++ ++static int tps53679_identify(struct i2c_client *client, ++ struct pmbus_driver_info *info) ++{ ++ u8 vout_params; ++ int ret; ++ ++ /* Read the register with VOUT scaling value.*/ ++ ret = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); ++ if (ret < 0) ++ return ret; ++ ++ vout_params = ret & GENMASK(4, 0); ++ ++ switch (vout_params) { ++ case TPS53679_PROT_VR13_10MV: ++ case TPS53679_PROT_VR12_5_10MV: ++ info->vrm_version = vr13; ++ break; ++ case TPS53679_PROT_VR13_5MV: ++ case TPS53679_PROT_VR12_5MV: ++ case TPS53679_PROT_IMVP8_5MV: ++ info->vrm_version = vr12; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static struct pmbus_driver_info tps53679_info = { ++ .pages = TPS53679_PAGE_NUM, ++ .format[PSC_VOLTAGE_IN] = linear, ++ .format[PSC_VOLTAGE_OUT] = vid, ++ .format[PSC_TEMPERATURE] = linear, ++ .format[PSC_CURRENT_OUT] = linear, ++ .format[PSC_POWER] = linear, ++ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | ++ PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | ++ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | ++ PMBUS_HAVE_POUT, ++ .func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | ++ PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | ++ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | ++ PMBUS_HAVE_POUT, ++ .identify = tps53679_identify, ++}; ++ ++static int tps53679_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ return pmbus_do_probe(client, id, &tps53679_info); ++} ++ ++static const struct i2c_device_id tps53679_id[] = { ++ {"tps53679", 0}, ++ {} ++}; ++ ++MODULE_DEVICE_TABLE(i2c, tps53679_id); ++ ++static const struct of_device_id tps53679_of_match[] = { ++ {.compatible = "ti,tps53679"}, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, tps53679_of_match); ++ ++static struct i2c_driver tps53679_driver = { ++ .driver = { ++ .name = "tps53679", ++ .of_match_table = of_match_ptr(tps53679_of_match), ++ }, ++ .probe = tps53679_probe, ++ .remove = pmbus_do_remove, ++ .id_table = tps53679_id, ++}; ++ ++module_i2c_driver(tps53679_driver); ++ ++MODULE_AUTHOR("Vadim Pasternak "); ++MODULE_DESCRIPTION("PMBus driver for Texas Instruments TPS53679"); ++MODULE_LICENSE("GPL"); diff --git a/patch/0009-platform-mellonox-introduce-mlxreg-io-driver-and-add.patch b/patch/0009-platform-mellonox-introduce-mlxreg-io-driver-and-add.patch new file mode 100644 index 000000000..db22b149b --- /dev/null +++ b/patch/0009-platform-mellonox-introduce-mlxreg-io-driver-and-add.patch @@ -0,0 +1,685 @@ +From 2c7476ab57dd42d8cba6c417ff32a77252964858 Mon Sep 17 00:00:00 2001 +From: Vadim Pasternak +Date: Thu, 16 Nov 2017 17:22:56 +0000 +Subject: [v4.9 backport 38/38] platform: mellonox: introduce mlxreg-io driver + and add driver activation to mlx-platform + +Patch introduces new module mlxreg-io, which exposes the registers of the +programmable devices, equipped on Melanox systems to sysfs. These are the +registers, which are used for system resets operation, system reset causes +monitoring, select operation and version info. + +Signed-off-by: Vadim Pasternak +--- + drivers/leds/leds-mlxreg.c | 10 +- + drivers/platform/mellanox/Kconfig | 11 ++ + drivers/platform/mellanox/Makefile | 1 + + drivers/platform/mellanox/mlxreg-io.c | 211 ++++++++++++++++++++++++++++++++++ + drivers/platform/x86/mlx-platform.c | 193 +++++++++++++++++++++++++++++-- + include/linux/platform_data/mlxreg.h | 6 +- + 6 files changed, 418 insertions(+), 14 deletions(-) + create mode 100644 drivers/platform/mellanox/mlxreg-io.c + +diff --git a/drivers/leds/leds-mlxreg.c b/drivers/leds/leds-mlxreg.c +index a932f20..036c214 100644 +--- a/drivers/leds/leds-mlxreg.c ++++ b/drivers/leds/leds-mlxreg.c +@@ -79,7 +79,7 @@ struct mlxreg_led_data { + */ + struct mlxreg_led_priv_data { + struct platform_device *pdev; +- struct mlxreg_core_led_platform_data *pdata; ++ struct mlxreg_core_platform_data *pdata; + struct mutex access_lock; /* protect IO operations */ + }; + +@@ -87,7 +87,7 @@ static int + mlxreg_led_store_hw(struct mlxreg_led_data *led_data, u8 vset) + { + struct mlxreg_led_priv_data *priv = led_data->data_parent; +- struct mlxreg_core_led_platform_data *led_pdata = priv->pdata; ++ struct mlxreg_core_platform_data *led_pdata = priv->pdata; + struct mlxreg_core_data *data = led_data->data; + u32 regval; + u32 nib; +@@ -125,7 +125,7 @@ static enum led_brightness + mlxreg_led_get_hw(struct mlxreg_led_data *led_data) + { + struct mlxreg_led_priv_data *priv = led_data->data_parent; +- struct mlxreg_core_led_platform_data *led_pdata = priv->pdata; ++ struct mlxreg_core_platform_data *led_pdata = priv->pdata; + struct mlxreg_core_data *data = led_data->data; + u32 regval; + int ret; +@@ -212,7 +212,7 @@ mlxreg_led_blink_set(struct led_classdev *cled, unsigned long *delay_on, + + static int mlxreg_led_config(struct mlxreg_led_priv_data *priv) + { +- struct mlxreg_core_led_platform_data *led_pdata = priv->pdata; ++ struct mlxreg_core_platform_data *led_pdata = priv->pdata; + struct mlxreg_core_data *data = led_pdata->data; + struct mlxreg_led_data *led_data; + struct led_classdev *led_cdev; +@@ -266,7 +266,7 @@ static int mlxreg_led_config(struct mlxreg_led_priv_data *priv) + + static int mlxreg_led_probe(struct platform_device *pdev) + { +- struct mlxreg_core_led_platform_data *led_pdata; ++ struct mlxreg_core_platform_data *led_pdata; + struct mlxreg_led_priv_data *priv; + + led_pdata = dev_get_platdata(&pdev->dev); +diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig +index b197cc1..5c6dc29 100644 +--- a/drivers/platform/mellanox/Kconfig ++++ b/drivers/platform/mellanox/Kconfig +@@ -22,4 +22,15 @@ config MLXREG_HOTPLUG + This driver handles hot-plug events for the power suppliers, power + cables and fans on the wide range Mellanox IB and Ethernet systems. + ++config MLXREG_IO ++ tristate "Mellanox platform register driver support" ++ depends on REGMAP ++ depends on HWMON ++ ---help--- ++ This driver allows access to Mellanox programmable device register ++ space trough sysfs interface. The set of registers for sysfs access ++ are defined per system type bases and includes the registers related ++ to system resets operation, system reset causes monitoring and some ++ kinds of mux selection. ++ + endif # MELLANOX_PLATFORM +diff --git a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile +index f58d089..b9a2692 100644 +--- a/drivers/platform/mellanox/Makefile ++++ b/drivers/platform/mellanox/Makefile +@@ -1 +1,2 @@ + obj-$(CONFIG_MLXREG_HOTPLUG) += mlxreg-hotplug.o ++obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o +diff --git a/drivers/platform/mellanox/mlxreg-io.c b/drivers/platform/mellanox/mlxreg-io.c +new file mode 100644 +index 0000000..f7434ca +--- /dev/null ++++ b/drivers/platform/mellanox/mlxreg-io.c +@@ -0,0 +1,211 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Attribute parameters. */ ++#define MLXREG_IO_ATT_SIZE 10 ++#define MLXREG_IO_ATT_NUM 48 ++ ++/** ++ * struct mlxreg_io_priv_data - driver's private data: ++ * ++ * @pdev: platform device; ++ * @pdata: platform data; ++ * @hwmon: hwmon device; ++ * @mlxreg_io_attr: sysfs attributes array; ++ * @mlxreg_io_dev_attr: sysfs sensor device attribute array; ++ * @group: sysfs attribute group; ++ * @groups: list of sysfs attribute group for hwmon registration; ++ */ ++struct mlxreg_io_priv_data { ++ struct platform_device *pdev; ++ struct mlxreg_core_platform_data *pdata; ++ struct device *hwmon; ++ struct attribute *mlxreg_io_attr[MLXREG_IO_ATT_NUM + 1]; ++ struct sensor_device_attribute mlxreg_io_dev_attr[MLXREG_IO_ATT_NUM]; ++ struct attribute_group group; ++ const struct attribute_group *groups[2]; ++}; ++ ++static ssize_t ++mlxreg_io_attr_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct mlxreg_io_priv_data *priv = dev_get_drvdata(dev); ++ int index = to_sensor_dev_attr(attr)->index; ++ struct mlxreg_core_data *data = priv->pdata->data + index; ++ u32 regval = 0; ++ int ret; ++ ++ ret = regmap_read(priv->pdata->regmap, data->reg, ®val); ++ if (ret) ++ goto access_error; ++ ++ if (!data->bit) ++ regval = !!(regval & ~data->mask); ++ ++ return sprintf(buf, "%u\n", regval); ++ ++access_error: ++ return ret; ++} ++ ++static ssize_t ++mlxreg_io_attr_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ struct mlxreg_io_priv_data *priv = dev_get_drvdata(dev); ++ int index = to_sensor_dev_attr(attr)->index; ++ struct mlxreg_core_data *data = priv->pdata->data + index; ++ u32 val, regval; ++ int ret; ++ ++ ret = kstrtou32(buf, MLXREG_IO_ATT_SIZE, &val); ++ if (ret) ++ return ret; ++ ++ ret = regmap_read(priv->pdata->regmap, data->reg, ®val); ++ if (ret) ++ goto access_error; ++ ++ regval &= data->mask; ++ ++ val = !!val; ++ if (val) ++ regval |= ~data->mask; ++ else ++ regval &= data->mask; ++ ++ ret = regmap_write(priv->pdata->regmap, data->reg, regval); ++ if (ret) ++ goto access_error; ++ ++ return len; ++ ++access_error: ++ dev_err(&priv->pdev->dev, "Bus access error\n"); ++ return ret; ++} ++ ++static int mlxreg_io_attr_init(struct mlxreg_io_priv_data *priv) ++{ ++ int i; ++ ++ priv->group.attrs = devm_kzalloc(&priv->pdev->dev, ++ priv->pdata->counter * ++ sizeof(struct attribute *), ++ GFP_KERNEL); ++ if (!priv->group.attrs) ++ return -ENOMEM; ++ ++ for (i = 0; i < priv->pdata->counter; i++) { ++ priv->mlxreg_io_attr[i] = ++ &priv->mlxreg_io_dev_attr[i].dev_attr.attr; ++ ++ /* Set attribute name as a label. */ ++ priv->mlxreg_io_attr[i]->name = ++ devm_kasprintf(&priv->pdev->dev, GFP_KERNEL, ++ priv->pdata->data[i].label); ++ ++ if (!priv->mlxreg_io_attr[i]->name) { ++ dev_err(&priv->pdev->dev, "Memory allocation failed for sysfs attribute %d.\n", ++ i + 1); ++ return -ENOMEM; ++ } ++ ++ priv->mlxreg_io_dev_attr[i].dev_attr.attr.mode = ++ priv->pdata->data[i].mode; ++ switch (priv->pdata->data[i].mode) { ++ case 0200: ++ priv->mlxreg_io_dev_attr[i].dev_attr.store = ++ mlxreg_io_attr_store; ++ break; ++ ++ case 0444: ++ priv->mlxreg_io_dev_attr[i].dev_attr.show = ++ mlxreg_io_attr_show; ++ break; ++ ++ case 0644: ++ priv->mlxreg_io_dev_attr[i].dev_attr.show = ++ mlxreg_io_attr_show; ++ priv->mlxreg_io_dev_attr[i].dev_attr.store = ++ mlxreg_io_attr_store; ++ break; ++ ++ default: ++ dev_err(&priv->pdev->dev, "Bad access mode %u for attribute %s.\n", ++ priv->pdata->data[i].mode, ++ priv->mlxreg_io_attr[i]->name); ++ return -EINVAL; ++ } ++ ++ priv->mlxreg_io_dev_attr[i].dev_attr.attr.name = ++ priv->mlxreg_io_attr[i]->name; ++ priv->mlxreg_io_dev_attr[i].index = i; ++ sysfs_attr_init(&priv->mlxreg_io_dev_attr[i].dev_attr.attr); ++ } ++ ++ priv->group.attrs = priv->mlxreg_io_attr; ++ priv->groups[0] = &priv->group; ++ priv->groups[1] = NULL; ++ ++ return 0; ++} ++ ++static int mlxreg_io_probe(struct platform_device *pdev) ++{ ++ struct mlxreg_io_priv_data *priv; ++ int err; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->pdata = dev_get_platdata(&pdev->dev); ++ if (!priv->pdata) { ++ dev_err(&pdev->dev, "Failed to get platform data.\n"); ++ return -EINVAL; ++ } ++ ++ priv->pdev = pdev; ++ ++ err = mlxreg_io_attr_init(priv); ++ if (err) { ++ dev_err(&priv->pdev->dev, "Failed to allocate attributes: %d\n", ++ err); ++ return err; ++ } ++ ++ priv->hwmon = devm_hwmon_device_register_with_groups(&pdev->dev, ++ "mlxreg_io", priv, priv->groups); ++ if (IS_ERR(priv->hwmon)) { ++ dev_err(&pdev->dev, "Failed to register hwmon device %ld\n", ++ PTR_ERR(priv->hwmon)); ++ return PTR_ERR(priv->hwmon); ++ } ++ ++ dev_set_drvdata(&pdev->dev, priv); ++ ++ return 0; ++} ++ ++static struct platform_driver mlxreg_io_driver = { ++ .driver = { ++ .name = "mlxreg-io", ++ }, ++ .probe = mlxreg_io_probe, ++}; ++ ++module_platform_driver(mlxreg_io_driver); ++ ++MODULE_AUTHOR("Vadim Pasternak "); ++MODULE_DESCRIPTION("Mellanox regmap I/O access driver"); ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_ALIAS("platform:mlxreg-io"); +diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c +index 49721c2..61cbe35 100644 +--- a/drivers/platform/x86/mlx-platform.c ++++ b/drivers/platform/x86/mlx-platform.c +@@ -47,16 +47,31 @@ + /* LPC bus IO offsets */ + #define MLXPLAT_CPLD_LPC_I2C_BASE_ADRR 0x2000 + #define MLXPLAT_CPLD_LPC_REG_BASE_ADRR 0x2500 ++#define MLXPLAT_CPLD_LPC_REG_CPLD1_VER_OFF 0x00 ++#define MLXPLAT_CPLD_LPC_REG_CPLD2_VER_OFF 0x01 ++#define MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF 0x1d + #define MLXPLAT_CPLD_LPC_REG_LED1_OFF 0x20 + #define MLXPLAT_CPLD_LPC_REG_LED2_OFF 0x21 + #define MLXPLAT_CPLD_LPC_REG_LED3_OFF 0x22 + #define MLXPLAT_CPLD_LPC_REG_LED4_OFF 0x23 + #define MLXPLAT_CPLD_LPC_REG_LED5_OFF 0x24 ++#define MLXPLAT_CPLD_LPC_REG_GP1_OFF 0x30 ++#define MLXPLAT_CPLD_LPC_REG_WP1_OFF 0x31 ++#define MLXPLAT_CPLD_LPC_REG_GP2_OFF 0x32 ++#define MLXPLAT_CPLD_LPC_REG_WP2_OFF 0x33 + #define MLXPLAT_CPLD_LPC_REG_AGGR_OFF 0x3a ++#define MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFF 0x3b + #define MLXPLAT_CPLD_LPC_REG_AGGR_LOW_OFF 0x40 ++#define MLXPLAT_CPLD_LPC_REG_AGGR_LOW_MASK_OFF 0x41 + #define MLXPLAT_CPLD_LPC_REG_PSU_OFF 0x58 ++#define MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFF 0x59 ++#define MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFF 0x5a + #define MLXPLAT_CPLD_LPC_REG_PWR_OFF 0x64 ++#define MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFF 0x65 ++#define MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFF 0x66 + #define MLXPLAT_CPLD_LPC_REG_FAN_OFF 0x88 ++#define MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFF 0x89 ++#define MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFF 0x8a + #define MLXPLAT_CPLD_LPC_IO_RANGE 0x100 + #define MLXPLAT_CPLD_LPC_I2C_CH1_OFF 0xdb + #define MLXPLAT_CPLD_LPC_I2C_CH2_OFF 0xda +@@ -100,12 +115,14 @@ + * @pdev_mux - array of mux platform devices + * @pdev_hotplug - hotplug platform devices + * @pdev_led - led platform devices ++ * @pdev_io_regs - register access platform devices + */ + struct mlxplat_priv { + struct platform_device *pdev_i2c; + struct platform_device *pdev_mux[MLXPLAT_CPLD_LPC_MUX_DEVS]; + struct platform_device *pdev_hotplug; + struct platform_device *pdev_led; ++ struct platform_device *pdev_io_regs; + }; + + /* Regions for LPC I2C controller and LPC base register space */ +@@ -643,7 +660,7 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_led_data[] = { + }, + }; + +-static struct mlxreg_core_led_platform_data mlxplat_default_led_data = { ++static struct mlxreg_core_platform_data mlxplat_default_led_data = { + .data = mlxplat_mlxcpld_default_led_data, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_led_data), + }; +@@ -697,7 +714,7 @@ static struct mlxreg_core_data mlxplat_mlxcpld_msn21xx_led_data[] = { + }, + }; + +-static struct mlxreg_core_led_platform_data mlxplat_msn21xx_led_data = { ++static struct mlxreg_core_platform_data mlxplat_msn21xx_led_data = { + .data = mlxplat_mlxcpld_msn21xx_led_data, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_msn21xx_led_data), + }; +@@ -786,11 +803,105 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_led_data[] = { + }, + }; + +-static struct mlxreg_core_led_platform_data mlxplat_default_ng_led_data = { ++static struct mlxreg_core_platform_data mlxplat_default_ng_led_data = { + .data = mlxplat_mlxcpld_default_ng_led_data, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_led_data), + }; + ++static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case MLXPLAT_CPLD_LPC_REG_LED1_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED2_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED3_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED4_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED5_OFF: ++ case MLXPLAT_CPLD_LPC_REG_GP1_OFF: ++ case MLXPLAT_CPLD_LPC_REG_WP1_OFF: ++ case MLXPLAT_CPLD_LPC_REG_GP2_OFF: ++ case MLXPLAT_CPLD_LPC_REG_WP2_OFF: ++ case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_AGGR_LOW_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFF: ++ case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFF: ++ return true; ++ } ++ return false; ++} ++ ++static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case MLXPLAT_CPLD_LPC_REG_CPLD1_VER_OFF: ++ case MLXPLAT_CPLD_LPC_REG_CPLD2_VER_OFF: ++ case MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED1_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED2_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED3_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED4_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED5_OFF: ++ case MLXPLAT_CPLD_LPC_REG_GP1_OFF: ++ case MLXPLAT_CPLD_LPC_REG_WP1_OFF: ++ case MLXPLAT_CPLD_LPC_REG_GP2_OFF: ++ case MLXPLAT_CPLD_LPC_REG_WP2_OFF: ++ case MLXPLAT_CPLD_LPC_REG_AGGR_OFF: ++ case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_AGGR_LOW_OFF: ++ case MLXPLAT_CPLD_LPC_REG_AGGR_LOW_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PSU_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PWR_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_FAN_OFF: ++ case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFF: ++ case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFF: ++ return true; ++ } ++ return false; ++} ++ ++static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case MLXPLAT_CPLD_LPC_REG_CPLD1_VER_OFF: ++ case MLXPLAT_CPLD_LPC_REG_CPLD2_VER_OFF: ++ case MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED1_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED2_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED3_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED4_OFF: ++ case MLXPLAT_CPLD_LPC_REG_LED5_OFF: ++ case MLXPLAT_CPLD_LPC_REG_GP1_OFF: ++ case MLXPLAT_CPLD_LPC_REG_GP2_OFF: ++ case MLXPLAT_CPLD_LPC_REG_AGGR_OFF: ++ case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_AGGR_LOW_OFF: ++ case MLXPLAT_CPLD_LPC_REG_AGGR_LOW_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PSU_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PWR_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFF: ++ case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFF: ++ case MLXPLAT_CPLD_LPC_REG_FAN_OFF: ++ case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFF: ++ case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFF: ++ return true; ++ } ++ return false; ++} ++ ++static const struct reg_default mlxplat_mlxcpld_regmap_default[] = { ++ { MLXPLAT_CPLD_LPC_REG_WP1_OFF, 0x00 }, ++ { MLXPLAT_CPLD_LPC_REG_WP2_OFF, 0x00 }, ++}; ++ + static int + mlxplat_mlxcpld_reg_read(void *context, unsigned int reg, unsigned int *val) + { +@@ -809,6 +920,12 @@ const struct regmap_config mlxplat_mlxcpld_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 255, ++ .cache_type = REGCACHE_FLAT, ++ .writeable_reg = mlxplat_mlxcpld_writeable_reg, ++ .readable_reg = mlxplat_mlxcpld_readable_reg, ++ .volatile_reg = mlxplat_mlxcpld_volatile_reg, ++ .reg_defaults = mlxplat_mlxcpld_regmap_default, ++ .num_reg_defaults = ARRAY_SIZE(mlxplat_mlxcpld_regmap_default), + .reg_read = mlxplat_mlxcpld_reg_read, + .reg_write = mlxplat_mlxcpld_reg_write, + }; +@@ -817,9 +934,38 @@ static struct resource mlxplat_mlxcpld_resources[] = { + [0] = DEFINE_RES_IRQ_NAMED(17, "mlxreg-hotplug"), + }; + +-struct platform_device *mlxplat_dev; +-struct mlxreg_core_hotplug_platform_data *mlxplat_hotplug; +-struct mlxreg_core_led_platform_data *mlxplat_led; ++static struct mlxreg_core_data mlxplat_mlxcpld_default_regs_io_data[] = { ++ { "cpld1_version", MLXPLAT_CPLD_LPC_REG_CPLD1_VER_OFF, 0x00, ++ GENMASK(7, 0), 0444 }, ++ { "cpld2_version", MLXPLAT_CPLD_LPC_REG_CPLD2_VER_OFF, 0x00, ++ GENMASK(7, 0), 0444 }, ++ { "cause_long_pb", MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF, ++ GENMASK(7, 0) & ~BIT(0), 0x00, 0444 }, ++ { "cause_short_pb", MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF, ++ GENMASK(7, 0) & ~BIT(1), 0x00, 0444 }, ++ { "cause_pwr_aux", MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF, ++ GENMASK(7, 0) & ~BIT(2), 0x00, 0444 }, ++ { "cause_pwr_fail", MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFF, ++ GENMASK(7, 0) & ~BIT(3), 0x00, 0444 }, ++ { "psu1_on", MLXPLAT_CPLD_LPC_REG_GP1_OFF, GENMASK(7, 0) & ~BIT(0), ++ 0x00, 0200 }, ++ { "psu2_on", MLXPLAT_CPLD_LPC_REG_GP1_OFF, GENMASK(7, 0) & ~BIT(1), ++ 0x00, 0200 }, ++ { "pwr_cycle", MLXPLAT_CPLD_LPC_REG_GP1_OFF, GENMASK(7, 0) & ~BIT(2), ++ 0x00, 0200 }, ++ { "select_iio", MLXPLAT_CPLD_LPC_REG_GP2_OFF, GENMASK(7, 0) & ~BIT(6), ++ 0x00, 0644 }, ++}; ++ ++static struct mlxreg_core_platform_data mlxplat_default_regs_io_data = { ++ .data = mlxplat_mlxcpld_default_regs_io_data, ++ .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_regs_io_data), ++}; ++ ++static struct platform_device *mlxplat_dev; ++static struct mlxreg_core_hotplug_platform_data *mlxplat_hotplug; ++static struct mlxreg_core_platform_data *mlxplat_led; ++static struct mlxreg_core_platform_data *mlxplat_regs_io; + + static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) + { +@@ -832,6 +978,7 @@ static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) + } + mlxplat_hotplug = &mlxplat_mlxcpld_default_data; + mlxplat_led = &mlxplat_default_led_data; ++ mlxplat_regs_io = &mlxplat_default_regs_io_data; + + return 1; + }; +@@ -847,6 +994,7 @@ static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi) + } + mlxplat_hotplug = &mlxplat_mlxcpld_msn21xx_data; + mlxplat_led = &mlxplat_msn21xx_led_data; ++ mlxplat_regs_io = &mlxplat_default_regs_io_data; + + return 1; + }; +@@ -862,6 +1010,7 @@ static int __init mlxplat_dmi_msn274x_matched(const struct dmi_system_id *dmi) + } + mlxplat_hotplug = &mlxplat_mlxcpld_msn274x_data; + mlxplat_led = &mlxplat_default_led_data; ++ mlxplat_regs_io = &mlxplat_default_regs_io_data; + + return 1; + }; +@@ -877,6 +1026,7 @@ static int __init mlxplat_dmi_qmb7xx_matched(const struct dmi_system_id *dmi) + } + mlxplat_hotplug = &mlxplat_mlxcpld_default_ng_data; + mlxplat_led = &mlxplat_default_ng_led_data; ++ mlxplat_regs_io = &mlxplat_default_regs_io_data; + + return 1; + }; +@@ -892,6 +1042,7 @@ static int __init mlxplat_dmi_msn201x_matched(const struct dmi_system_id *dmi) + } + mlxplat_hotplug = &mlxplat_mlxcpld_msn201x_data; + mlxplat_led = &mlxplat_msn21xx_led_data; ++ mlxplat_regs_io = &mlxplat_default_regs_io_data; + + return 1; + }; +@@ -974,7 +1125,7 @@ static int __init mlxplat_init(void) + { + struct mlxplat_priv *priv; + void __iomem *base; +- int i, err = 0; ++ int i, j, err = 0; + + if (!dmi_check_system(mlxplat_dmi_table)) + return -ENODEV; +@@ -1023,6 +1174,15 @@ static int __init mlxplat_init(void) + if (IS_ERR(mlxplat_hotplug->regmap)) + goto fail_platform_mux_register; + ++ /* Set default registers. */ ++ for (j = 0; j < mlxplat_mlxcpld_regmap_config.num_reg_defaults; j++) { ++ err = regmap_write(mlxplat_hotplug->regmap, ++ mlxplat_mlxcpld_regmap_default[j].reg, ++ mlxplat_mlxcpld_regmap_default[j].def); ++ if (err) ++ goto fail_platform_mux_register; ++ } ++ + priv->pdev_hotplug = platform_device_register_resndata( + &mlxplat_dev->dev, "mlxreg-hotplug", + PLATFORM_DEVID_NONE, +@@ -1044,8 +1204,26 @@ static int __init mlxplat_init(void) + goto fail_platform_hotplug_register; + } + ++ mlxplat_regs_io->regmap = mlxplat_hotplug->regmap; ++ priv->pdev_io_regs = platform_device_register_resndata( ++ &mlxplat_dev->dev, "mlxreg-io", ++ PLATFORM_DEVID_NONE, NULL, 0, ++ mlxplat_regs_io, sizeof(*mlxplat_regs_io)); ++ if (IS_ERR(priv->pdev_io_regs)) { ++ err = PTR_ERR(priv->pdev_io_regs); ++ goto fail_platform_led_register; ++ } ++ ++ /* Sync registers with hardware. */ ++ regcache_mark_dirty(mlxplat_hotplug->regmap); ++ err = regcache_sync(mlxplat_hotplug->regmap); ++ if (err) ++ goto fail_platform_led_register; ++ + return 0; + ++fail_platform_led_register: ++ platform_device_unregister(priv->pdev_led); + fail_platform_hotplug_register: + platform_device_unregister(priv->pdev_hotplug); + fail_platform_mux_register: +@@ -1064,6 +1242,7 @@ static void __exit mlxplat_exit(void) + struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev); + int i; + ++ platform_device_unregister(priv->pdev_io_regs); + platform_device_unregister(priv->pdev_led); + platform_device_unregister(priv->pdev_hotplug); + +diff --git a/include/linux/platform_data/mlxreg.h b/include/linux/platform_data/mlxreg.h +index dd471c5..c25623b 100644 +--- a/include/linux/platform_data/mlxreg.h ++++ b/include/linux/platform_data/mlxreg.h +@@ -61,6 +61,7 @@ struct mlxreg_hotplug_device { + * @label: attribute register offset; + * @reg: attribute register; + * @mask: attribute access mask; ++ * @mode: access mode; + * @bit: attribute effective bit; + * @np - pointer to node platform associated with attribute; + * @hpdev - hotplug device data; +@@ -72,6 +73,7 @@ struct mlxreg_core_data { + u32 reg; + u32 mask; + u32 bit; ++ umode_t mode; + struct device_node *np; + struct mlxreg_hotplug_device hpdev; + u8 health_cntr; +@@ -104,13 +106,13 @@ struct mlxreg_core_item { + }; + + /** +- * struct mlxreg_core_led_platform_data - led platform data: ++ * struct mlxreg_core_platform_data - platform data: + * + * @led_data: led private data; + * @regmap: register map of parent device; + * @counter: number of led instances; + */ +-struct mlxreg_core_led_platform_data { ++struct mlxreg_core_platform_data { + struct mlxreg_core_data *data; + void *regmap; + int counter; +-- +2.1.4 + diff --git a/patch/0010-config-mellanox-configuration.patch b/patch/0010-config-mellanox-configuration.patch new file mode 100644 index 000000000..da5aac81f --- /dev/null +++ b/patch/0010-config-mellanox-configuration.patch @@ -0,0 +1,142 @@ +From c951dd6c826de2dbcb1d239743448bb56753ef96 Mon Sep 17 00:00:00 2001 +From: root +Date: Mon, 27 Nov 2017 15:53:20 +0200 +Subject: [v4.9 backport 10/10] config: mellanox configuration + +It adds the next configuration flags, used by Mellanox systems: + +CONFIG_MLXSW_CORE=m +CONFIG_MLXSW_CORE_HWMON=y +CONFIG_MLXSW_CORE_THERMAL=y +CONFIG_MLXSW_CORE_QSFP=y +CONFIG_MLXSW_PCI=m +CONFIG_MLXSW_I2C=m +CONFIG_MLXSW_MINIMAL=m +CONFIG_I2C_MUX_REG=y +CONFIG_I2C_MUX_MLXCPLD=m +CONFIG_I2C_MLXCPLD=m +CONFIG_SENSORS_IIO_HWMON=y +CONFIG_PMBUS=y +CONFIG_SENSORS_PMBUS=y +CONFIG_SENSORS_LM25066=m +CONFIG_SENSORS_TPS53679=m +CONFIG_SENSORS_UCD9000=m +CONFIG_SENSORS_UCD9200=m +CONFIG_LEDS_MLXREG=m +CONFIG_MLX_PLATFORM=m +CONFIG_MELLANOX_PLATFORM=y +CONFIG_MLXREG_HOTPLUG=m +CONFIG_MLXREG_IO=m +CONFIG_MAX1363=y + +Signed-off-by: Vadim Pastrenak +--- + debian/build/build_amd64_none_amd64/.config | 28 +++++++++++++++++++++++----- + 1 file changed, 23 insertions(+), 5 deletions(-) + +diff --git a/debian/build/build_amd64_none_amd64/.config b/debian/build/build_amd64_none_amd64/.config +index 88a71ef..f1930a1 100644 +--- a/debian/build/build_amd64_none_amd64/.config ++++ b/debian/build/build_amd64_none_amd64/.config +@@ -2632,7 +2632,13 @@ CONFIG_MLX4_DEBUG=y + CONFIG_MLX5_CORE=m + CONFIG_MLX5_CORE_EN=y + CONFIG_MLX5_CORE_EN_DCB=y +-# CONFIG_MLXSW_CORE is not set ++CONFIG_MLXSW_CORE=m ++CONFIG_MLXSW_CORE_HWMON=y ++CONFIG_MLXSW_CORE_THERMAL=y ++CONFIG_MLXSW_CORE_QSFP=y ++CONFIG_MLXSW_PCI=m ++CONFIG_MLXSW_I2C=m ++CONFIG_MLXSW_MINIMAL=m + CONFIG_NET_VENDOR_MICREL=y + # CONFIG_KS8842 is not set + # CONFIG_KS8851 is not set +@@ -3565,7 +3571,8 @@ CONFIG_I2C_MUX=m + # CONFIG_I2C_MUX_PCA9541 is not set + # CONFIG_I2C_MUX_PCA954x is not set + # CONFIG_I2C_MUX_PINCTRL is not set +-# CONFIG_I2C_MUX_REG is not set ++CONFIG_I2C_MUX_REG=y ++CONFIG_I2C_MUX_MLXCPLD=m + CONFIG_I2C_HELPER_AUTO=y + CONFIG_I2C_SMBUS=m + CONFIG_I2C_ALGOBIT=m +@@ -3632,6 +3639,7 @@ CONFIG_I2C_VIPERBOARD=m + # + # Other I2C/SMBus bus drivers + # ++CONFIG_I2C_MLXCPLD=m + CONFIG_I2C_STUB=m + # CONFIG_I2C_SLAVE is not set + # CONFIG_I2C_DEBUG_CORE is not set +@@ -3879,7 +3887,7 @@ CONFIG_SENSORS_G760A=m + # CONFIG_SENSORS_HIH6130 is not set + CONFIG_SENSORS_IBMAEM=m + CONFIG_SENSORS_IBMPEX=m +-# CONFIG_SENSORS_IIO_HWMON is not set ++CONFIG_SENSORS_IIO_HWMON=y + CONFIG_SENSORS_I5500=m + CONFIG_SENSORS_CORETEMP=m + CONFIG_SENSORS_IT87=m +@@ -3932,7 +3949,21 @@ CONFIG_SENSORS_NCT6775=m + # CONFIG_SENSORS_NCT7802 is not set + # CONFIG_SENSORS_NCT7904 is not set + CONFIG_SENSORS_PCF8591=m +-# CONFIG_PMBUS is not set ++CONFIG_PMBUS=m ++CONFIG_SENSORS_PMBUS=m ++CONFIG_SENSORS_LM25066=m ++CONFIG_SENSORS_TPS53679=m ++CONFIG_SENSORS_UCD9000=m ++CONFIG_SENSORS_UCD9200=m ++# CONFIG_SENSORS_ADM1275 is not set ++# CONFIG_SENSORS_LTC2978 is not set ++# CONFIG_SENSORS_LTC3815 is not set ++# CONFIG_SENSORS_MAX16064 is not set ++# CONFIG_SENSORS_MAX20751 is not set ++# CONFIG_SENSORS_MAX34440 is not set ++# CONFIG_SENSORS_MAX8688 is not set ++# CONFIG_SENSORS_TPS40422 is not set ++# CONFIG_SENSORS_ZL6100 is not set + # CONFIG_SENSORS_SHT15 is not set + CONFIG_SENSORS_SHT21=m + # CONFIG_SENSORS_SHT3x is not set +@@ -5904,6 +5919,9 @@ CONFIG_LEDS_MENF21BMC=m + # + # CONFIG_LEDS_BLINKM is not set + # CONFIG_LEDS_MLXCPLD is not set ++CONFIG_LEDS_MLXREG=m ++# CONFIG_LEDS_USER is not set ++# CONFIG_LEDS_NIC78BX is not set + + # + # LED Triggers +@@ -6483,10 +6497,14 @@ CONFIG_PVPANIC=m + CONFIG_INTEL_PMC_IPC=m + CONFIG_SURFACE_PRO3_BUTTON=m + # CONFIG_INTEL_PUNIT_IPC is not set ++CONFIG_MLX_PLATFORM=m + CONFIG_CHROME_PLATFORMS=y + CONFIG_CHROMEOS_LAPTOP=m + CONFIG_CHROMEOS_PSTORE=m + CONFIG_CROS_KBD_LED_BACKLIGHT=m ++CONFIG_MELLANOX_PLATFORM=y ++CONFIG_MLXREG_HOTPLUG=m ++CONFIG_MLXREG_IO=m + CONFIG_CLKDEV_LOOKUP=y + CONFIG_HAVE_CLK_PREPARE=y + CONFIG_COMMON_CLK=y +@@ -6621,7 +6639,7 @@ CONFIG_MMA9553=m + # CONFIG_INA2XX_ADC is not set + # CONFIG_LTC2485 is not set + # CONFIG_MAX1027 is not set +-# CONFIG_MAX1363 is not set ++CONFIG_MAX1363=y + # CONFIG_MCP320X is not set + # CONFIG_MCP3422 is not set + # CONFIG_NAU7802 is not set +-- +2.11.0 + diff --git a/patch/series b/patch/series index d8e332989..91afaecf1 100644 --- a/patch/series +++ b/patch/series @@ -1,29 +1,41 @@ +# This series applies on GIT commit 18c5597832fcf6988111b05a9a1607ae148723c +0001-i2c-mlxcpld-add-master-driver-for-Mellanox-systems.patch +0002-i2c-mux-mlxcpld-add-driver-for-Mellanox-systems.patch +0003-platform-mellanox-Introduce-Mellanox-hardware-platfo.patch +0004-platform-x86-Introduce-support-for-Mellanox-hotplug-.patch +0005-leds-add-driver-for-support-Mellanox-regmap-LEDs-for.patch +0006-Mellanox-switch-drivers-changes.patch +0007-hwmon-pmbus-Add-support-for-Intel-VID-protocol-VR13.patch +0008-hwmon-pmbus-Add-support-for-Texas-Instruments-tps536.patch +0009-platform-mellonox-introduce-mlxreg-io-driver-and-add.patch +0010-config-mellanox-configuration.patch +# # This series applies on GIT commit 1451b36b2b0d62178e42f648d8a18131af18f7d8 -kernel-sched-core-fix-cgroup-fork-race.patch -config-dell-s6000.patch -config-mlnx-sn2700.patch -config-dell-z9100.patch -config-ingrasys-s9100.patch -driver-at24-fix-odd-length-two-byte-access.patch -driver-hwmon-max6620.patch -driver-hwmon-max6620-fix-rpm-calc.patch -driver-hwmon-max6620-update.patch -driver-hwmon-lm75b-update.patch -driver-hwmon-pmbus-dni_dps460.patch -driver-hwmon-pmbus-dni_dps460-update-pmbus-core.patch -driver-i2c-bus-intel-ismt-add-delay-param.patch -driver-support-sff-8436-eeprom.patch -driver-support-sff-8436-eeprom-update.patch -driver-hwmon-pmbus-add-dps460-support.patch -driver-hwmon-pmbus-ucd9200-mlnx.patch -driver-arista-piix4-mux-patch.patch -driver-arista-net-tg3-dma-mask-4g-sb800.patch -driver-arista-net-tg3-access-regs-indirectly.patch -driver-arista-net-tg3-disallow-broadcom-default-mac.patch -driver-pca954x-i2c-mux-force-deselect-on-exit-flag.patch -linux-3.19-mmc-sdhci-Add-a-quirk-for-AMD-SDHC-transfer-mode-reg.patch -linux-3.19-mmc-sdhci-pci-enable-the-clear-transfer-mode-registe.patch -linux-3.19-mmc-sdhci-pci-enable-sdhci-doesn-t-support-hs200-qui.patch -rtnetlink-catch-EOPNOTSUPP-errors.patch -bridge-per-port-multicast-broadcast-flood-flags.patch -driver-support-tun-config-carrier-enable.patch \ No newline at end of file +# Tkernel-sched-core-fix-cgroup-fork-race.patch +# Tconfig-dell-s6000.patch +# Tconfig-mlnx-sn2700.patch +# Tconfig-dell-z9100.patch +# Tconfig-ingrasys-s9100.patch +# Tdriver-at24-fix-odd-length-two-byte-access.patch +# Tdriver-hwmon-max6620.patch +# Tdriver-hwmon-max6620-fix-rpm-calc.patch +# Tdriver-hwmon-max6620-update.patch +# Tdriver-hwmon-lm75b-update.patch +# Tdriver-hwmon-pmbus-dni_dps460.patch +# Tdriver-hwmon-pmbus-dni_dps460-update-pmbus-core.patch +# Tdriver-i2c-bus-intel-ismt-add-delay-param.patch +# Tdriver-support-sff-8436-eeprom.patch +# Tdriver-support-sff-8436-eeprom-update.patch +# Tdriver-hwmon-pmbus-add-dps460-support.patch +# Tdriver-hwmon-pmbus-ucd9200-mlnx.patch +# Tdriver-arista-piix4-mux-patch.patch +# Tdriver-arista-net-tg3-dma-mask-4g-sb800.patch +# Tdriver-arista-net-tg3-access-regs-indirectly.patch +# Tdriver-arista-net-tg3-disallow-broadcom-default-mac.patch +# Tdriver-pca954x-i2c-mux-force-deselect-on-exit-flag.patch +# Tlinux-3.19-mmc-sdhci-Add-a-quirk-for-AMD-SDHC-transfer-mode-reg.patch +# Tlinux-3.19-mmc-sdhci-pci-enable-the-clear-transfer-mode-registe.patch +# Tlinux-3.19-mmc-sdhci-pci-enable-sdhci-doesn-t-support-hs200-qui.patch +# Trtnetlink-catch-EOPNOTSUPP-errors.patch +# Tbridge-per-port-multicast-broadcast-flood-flags.patch +# Tdriver-support-tun-config-carrier-enable.patch