From 186cef994f542d29730817bfc71aec26a17f672b Mon Sep 17 00:00:00 2001 From: Willy Liu Date: Tue, 30 Sep 2025 14:54:27 +0800 Subject: [PATCH] [ACCTON][AS7927-50X] Add platform CPU: Ice Lake-D (D-1713NTE 4C) MAC: Broadcom Q3U (BCM88400), 1 pcs , 1.2 Tb/s integrated packet processor Cooling: 6+1 FAN-tray modules with 7 pcs of 40.9mm x 40.9mm x 92.99mm 31000 R.P.M 12V hot-swappable fans Power Supply: 12V/650W AC PSU F2B/B2F airflow, 1+1 redundant load-sharing, hot-swappable 12V/850W DC PSU F2B/B2F airflow, 1+1 redundant load-sharing, hot-swappable Ethernet Ports: 1x800G QSFP-DD port 1xQSFP-DD port 8xSFP56 ports 40xSFP28 ports BMC: AST2600 Signed-off-by: Willy Liu --- .../buster/common/all-base-packages.yml | 1 + .../src/python/onl/platform/base.py | 3 + .../configs/x86_64-all/x86_64-all.config | 2 +- .../accton/x86-64/as7927-50x/.gitignore | 2 + .../accton/x86-64/as7927-50x/Makefile | 1 + .../accton/x86-64/as7927-50x/modules/Makefile | 1 + .../accton/x86-64/as7927-50x/modules/PKG.yml | 1 + .../as7927-50x/modules/builds/.gitignore | 1 + .../x86-64/as7927-50x/modules/builds/Makefile | 7 + .../as7927-50x/modules/builds/src/Makefile | 9 + .../modules/builds/src/accton_ipmi_intf.c | 232 ++++ .../modules/builds/src/accton_ipmi_intf.h | 72 + .../builds/src/x86-64-accton-as7927-50x-fan.c | 681 ++++++++++ .../src/x86-64-accton-as7927-50x-fpga.c | 1182 +++++++++++++++++ .../src/x86-64-accton-as7927-50x-i2c-ocores.c | 980 ++++++++++++++ .../src/x86-64-accton-as7927-50x-leds.c | 377 ++++++ .../builds/src/x86-64-accton-as7927-50x-psu.c | 979 ++++++++++++++ .../builds/src/x86-64-accton-as7927-50x-sys.c | 410 ++++++ .../src/x86-64-accton-as7927-50x-thermal.c | 259 ++++ .../accton/x86-64/as7927-50x/onlp/Makefile | 1 + .../accton/x86-64/as7927-50x/onlp/PKG.yml | 1 + .../x86-64/as7927-50x/onlp/builds/Makefile | 2 + .../as7927-50x/onlp/builds/lib/Makefile | 2 + .../as7927-50x/onlp/builds/onlpdump/Makefile | 2 + .../x86_64_accton_as7927_50x/.gitignore | 0 .../builds/x86_64_accton_as7927_50x/.module | 1 + .../builds/x86_64_accton_as7927_50x/Makefile | 9 + .../builds/x86_64_accton_as7927_50x/README | 6 + .../module/auto/make.mk | 9 + .../module/auto/x86_64_accton_as7927_50x.yml | 50 + .../x86_64_accton_as7927_50x.x | 14 + .../x86_64_accton_as7927_50x_config.h | 137 ++ .../x86_64_accton_as7927_50x_dox.h | 26 + .../x86_64_accton_as7927_50x_porting.h | 107 ++ .../x86_64_accton_as7927_50x/module/make.mk | 10 + .../module/src/Makefile | 9 + .../module/src/debug.c | 45 + .../module/src/fani.c | 301 +++++ .../module/src/ledi.c | 235 ++++ .../module/src/make.mk | 9 + .../module/src/platform_lib.c | 402 ++++++ .../module/src/platform_lib.h | 143 ++ .../module/src/psui.c | 177 +++ .../module/src/sfpi.c | 326 +++++ .../module/src/sysi.c | 428 ++++++ .../module/src/thermali.c | 208 +++ .../src/x86_64_accton_as7927_50x_config.c | 81 ++ .../src/x86_64_accton_as7927_50x_enums.c | 10 + .../module/src/x86_64_accton_as7927_50x_int.h | 12 + .../module/src/x86_64_accton_as7927_50x_log.c | 17 + .../module/src/x86_64_accton_as7927_50x_log.h | 12 + .../src/x86_64_accton_as7927_50x_module.c | 24 + .../src/x86_64_accton_as7927_50x_ucli.c | 50 + .../as7927-50x/platform-config/Makefile | 1 + .../as7927-50x/platform-config/r0/Makefile | 1 + .../as7927-50x/platform-config/r0/PKG.yml | 1 + .../src/lib/x86-64-accton-as7927-50x-r0.yml | 35 + .../x86_64_accton_as7927_50x_r0/__init__.py | 105 ++ 58 files changed, 8208 insertions(+), 1 deletion(-) create mode 100644 packages/platforms/accton/x86-64/as7927-50x/.gitignore create mode 100644 packages/platforms/accton/x86-64/as7927-50x/Makefile create mode 100644 packages/platforms/accton/x86-64/as7927-50x/modules/Makefile create mode 100644 packages/platforms/accton/x86-64/as7927-50x/modules/PKG.yml create mode 100644 packages/platforms/accton/x86-64/as7927-50x/modules/builds/.gitignore create mode 100644 packages/platforms/accton/x86-64/as7927-50x/modules/builds/Makefile create mode 100644 packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/Makefile create mode 100644 packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/accton_ipmi_intf.c create mode 100644 packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/accton_ipmi_intf.h create mode 100644 packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-fan.c create mode 100644 packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-fpga.c create mode 100644 packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-i2c-ocores.c create mode 100644 packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-leds.c create mode 100644 packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-psu.c create mode 100644 packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-sys.c create mode 100644 packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-thermal.c create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/Makefile create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/PKG.yml create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/Makefile create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/lib/Makefile create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/onlpdump/Makefile create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/.gitignore create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/.module create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/Makefile create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/README create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/auto/make.mk create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/auto/x86_64_accton_as7927_50x.yml create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/inc/x86_64_accton_as7927_50x/x86_64_accton_as7927_50x.x create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/inc/x86_64_accton_as7927_50x/x86_64_accton_as7927_50x_config.h create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/inc/x86_64_accton_as7927_50x/x86_64_accton_as7927_50x_dox.h create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/inc/x86_64_accton_as7927_50x/x86_64_accton_as7927_50x_porting.h create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/make.mk create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/Makefile create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/debug.c create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/fani.c create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/ledi.c create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/make.mk create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/platform_lib.c create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/platform_lib.h create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/psui.c create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/sfpi.c create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/sysi.c create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/thermali.c create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_config.c create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_enums.c create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_int.h create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_log.c create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_log.h create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_module.c create mode 100644 packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_ucli.c create mode 100644 packages/platforms/accton/x86-64/as7927-50x/platform-config/Makefile create mode 100644 packages/platforms/accton/x86-64/as7927-50x/platform-config/r0/Makefile create mode 100644 packages/platforms/accton/x86-64/as7927-50x/platform-config/r0/PKG.yml create mode 100644 packages/platforms/accton/x86-64/as7927-50x/platform-config/r0/src/lib/x86-64-accton-as7927-50x-r0.yml create mode 100644 packages/platforms/accton/x86-64/as7927-50x/platform-config/r0/src/python/x86_64_accton_as7927_50x_r0/__init__.py diff --git a/builds/any/rootfs/buster/common/all-base-packages.yml b/builds/any/rootfs/buster/common/all-base-packages.yml index b8e9df5f3a..36a5d200c9 100644 --- a/builds/any/rootfs/buster/common/all-base-packages.yml +++ b/builds/any/rootfs/buster/common/all-base-packages.yml @@ -85,3 +85,4 @@ - htop - tree - memtester +- ipmitool diff --git a/packages/base/all/vendor-config-onl/src/python/onl/platform/base.py b/packages/base/all/vendor-config-onl/src/python/onl/platform/base.py index 08bdb9839b..0b533affe8 100644 --- a/packages/base/all/vendor-config-onl/src/python/onl/platform/base.py +++ b/packages/base/all/vendor-config-onl/src/python/onl/platform/base.py @@ -650,3 +650,6 @@ class OnlPlatformPortConfig_40x100_13x400_2x10(object): class OnlPlatformPortConfig_64x800_2x25(object): PORT_COUNT=66 PORT_CONFIG="64x800 + 2x25" +class OnlPlatformPortConfig_1x800_1x400_8X50_40X25(object): + PORT_COUNT=50 + PORT_CONFIG="1x800 + 1x400 + 8x50 + 40x25" diff --git a/packages/base/any/kernels/5.4-lts/configs/x86_64-all/x86_64-all.config b/packages/base/any/kernels/5.4-lts/configs/x86_64-all/x86_64-all.config index 08480880c6..f5e2a65d94 100644 --- a/packages/base/any/kernels/5.4-lts/configs/x86_64-all/x86_64-all.config +++ b/packages/base/any/kernels/5.4-lts/configs/x86_64-all/x86_64-all.config @@ -1263,7 +1263,7 @@ CONFIG_HOTPLUG_PCI=y # # end of Cadence PCIe controllers support -# CONFIG_VMD is not set +CONFIG_VMD=y # # DesignWare PCI Core Support diff --git a/packages/platforms/accton/x86-64/as7927-50x/.gitignore b/packages/platforms/accton/x86-64/as7927-50x/.gitignore new file mode 100644 index 0000000000..45b8dfb338 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/.gitignore @@ -0,0 +1,2 @@ +*x86*64*accton*as7927*50x*.mk +onlpdump.mk diff --git a/packages/platforms/accton/x86-64/as7927-50x/Makefile b/packages/platforms/accton/x86-64/as7927-50x/Makefile new file mode 100644 index 0000000000..dc1e7b86f0 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk diff --git a/packages/platforms/accton/x86-64/as7927-50x/modules/Makefile b/packages/platforms/accton/x86-64/as7927-50x/modules/Makefile new file mode 100644 index 0000000000..dc1e7b86f0 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/modules/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk diff --git a/packages/platforms/accton/x86-64/as7927-50x/modules/PKG.yml b/packages/platforms/accton/x86-64/as7927-50x/modules/PKG.yml new file mode 100644 index 0000000000..71fef8fb1d --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/modules/PKG.yml @@ -0,0 +1 @@ +!include $ONL_TEMPLATES/platform-modules.yml VENDOR=accton BASENAME=x86-64-accton-as7927-50x ARCH=amd64 KERNELS="onl-kernel-5.4-lts-x86-64-all:amd64" diff --git a/packages/platforms/accton/x86-64/as7927-50x/modules/builds/.gitignore b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/.gitignore new file mode 100644 index 0000000000..a65b41774a --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/.gitignore @@ -0,0 +1 @@ +lib diff --git a/packages/platforms/accton/x86-64/as7927-50x/modules/builds/Makefile b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/Makefile new file mode 100644 index 0000000000..ece4e0c2ad --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/Makefile @@ -0,0 +1,7 @@ +KERNELS := onl-kernel-5.4-lts-x86-64-all:amd64 +KMODULES := src +VENDOR := accton +BASENAME := x86-64-accton-as7927-50x +ARCH := x86_64 +include $(ONL)/make/kmodule.mk + diff --git a/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/Makefile b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/Makefile new file mode 100644 index 0000000000..f5488dc79d --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/Makefile @@ -0,0 +1,9 @@ +obj-m += accton_ipmi_intf.o +obj-m += x86-64-accton-as7927-50x-i2c-ocores.o +obj-m += x86-64-accton-as7927-50x-fpga.o +obj-m += x86-64-accton-as7927-50x-fan.o +obj-m += x86-64-accton-as7927-50x-thermal.o +obj-m += x86-64-accton-as7927-50x-psu.o +obj-m += x86-64-accton-as7927-50x-sys.o +obj-m += x86-64-accton-as7927-50x-leds.o + diff --git a/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/accton_ipmi_intf.c b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/accton_ipmi_intf.c new file mode 100644 index 0000000000..0bf88f6461 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/accton_ipmi_intf.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright 2024 Accton Technology Corporation. + * + * 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. + * + * + * Description: + * IPMI driver related interface implementation + */ +#include +#include +#include +#include "accton_ipmi_intf.h" + +#define ACCTON_IPMI_NETFN 0x34 +#define IPMI_TIMEOUT (5 * HZ) +#define IPMI_ERR_RETRY_TIMES 1 +#define RAW_CMD_BUF_SIZE 32 + +static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data); + +/* Functions to talk to the IPMI layer */ + +/* Initialize IPMI data structure and create a user interface for communication */ +int init_ipmi_data(struct ipmi_data *ipmi, int iface, struct device *dev) +{ + int err; + + if (!ipmi || !dev) + return -EINVAL; + + init_completion(&ipmi->read_complete); + + // Initialize IPMI address + ipmi->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + ipmi->address.channel = IPMI_BMC_CHANNEL; + ipmi->address.data[0] = 0; + ipmi->interface = iface; + ipmi->dev = dev; // Storing the device for future reference + + // Initialize message buffers + ipmi->tx_msgid = 0; + ipmi->tx_message.netfn = ACCTON_IPMI_NETFN; + + // Assign the message handler + ipmi->ipmi_hndlrs.ipmi_recv_hndl = ipmi_msg_handler; + + // Create IPMI messaging interface user + err = ipmi_create_user(ipmi->interface, &ipmi->ipmi_hndlrs, + ipmi, &ipmi->user); + if (err < 0) { + dev_err(dev, + "Unable to register user with IPMI interface %d, err: %d\n", + ipmi->interface, err); + return err; + } + + return 0; +} +EXPORT_SYMBOL(init_ipmi_data); + +/* Handler function for receiving IPMI messages */ +static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) +{ + unsigned short rx_len; + struct ipmi_data *ipmi = user_msg_data; + + // Check for message ID mismatch + if (msg->msgid != ipmi->tx_msgid) { + dev_err(ipmi->dev, "Mismatch between received msgid " + "(%02x) and transmitted msgid (%02x)!\n", + (int)msg->msgid, (int)ipmi->tx_msgid); + ipmi_free_recv_msg(msg); + return; + } + + // Handle received message type + ipmi->rx_recv_type = msg->recv_type; + + // Parse message data + if (msg->msg.data_len > 0) + ipmi->rx_result = msg->msg.data[0]; + else + ipmi->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE; + + // Copy remaining message data if available + if (msg->msg.data_len > 1) { + rx_len = msg->msg.data_len - 1; + if (ipmi->rx_msg_len < rx_len) + rx_len = ipmi->rx_msg_len; + + ipmi->rx_msg_len = rx_len; + memcpy(ipmi->rx_msg_data, msg->msg.data + 1, ipmi->rx_msg_len); + } else { + ipmi->rx_msg_len = 0; + } + + // Free the received message and signal completion + ipmi_free_recv_msg(msg); + complete(&ipmi->read_complete); +} + +static void _ipmi_log_error(struct ipmi_data *ipmi, unsigned char cmd, + unsigned char *tx_data, unsigned short tx_len, + int status, int retry) +{ + int i, pos; + char *cmdline = NULL; + char raw_cmd[RAW_CMD_BUF_SIZE] = { 0 }; + + // Format the command and data into a raw command string + pos = snprintf(raw_cmd, sizeof(raw_cmd), "0x%02x", cmd); + for (i = 0; i < tx_len && pos < sizeof(raw_cmd); i++) { + pos += snprintf(raw_cmd + pos, sizeof(raw_cmd) - pos, + " 0x%02x", tx_data[i]); + } + + // Log the error message + cmdline = kstrdup_quotable_cmdline(current, GFP_KERNEL); + dev_err(ipmi->dev, + "ipmi_send_message: retry(%d), error(%d), cmd(%s) raw_cmd=[%s]\r\n", + retry, status, cmdline ? cmdline : "", raw_cmd); + + if (cmdline) { + kfree(cmdline); + } +} + +/* Send an IPMI command */ +static int _ipmi_send_message(struct ipmi_data *ipmi, unsigned char cmd, + unsigned char *tx_data, unsigned short tx_len, + unsigned char *rx_data, unsigned short rx_len) +{ + int err; + + // Validate the input parameters + if ((tx_len && !tx_data) || (rx_len && !rx_data)) { + return -EINVAL; + } + + // Initialize IPMI message + ipmi->tx_message.cmd = cmd; + ipmi->tx_message.data = tx_len ? tx_data : NULL; + ipmi->tx_message.data_len = tx_len; + ipmi->rx_msg_data = rx_len ? rx_data : NULL; + ipmi->rx_msg_len = rx_len; + + // Validate the IPMI address + err = ipmi_validate_addr(&ipmi->address, sizeof(ipmi->address)); + if (err) { + dev_err(ipmi->dev, "Invalid IPMI address: %x\n", err); + return err; + } + + // Increment message ID and send the request + ipmi->tx_msgid++; + err = ipmi_request_settime(ipmi->user, &ipmi->address, ipmi->tx_msgid, + &ipmi->tx_message, ipmi, 0, 0, 0); + if (err) { + dev_err(ipmi->dev, "IPMI request_settime failed: %x\n", err); + return err; + } + + // Wait for the message to complete + err = wait_for_completion_timeout(&ipmi->read_complete, IPMI_TIMEOUT); + if (!err) { + dev_err(ipmi->dev, "IPMI command timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + +/* Send an IPMI command to the IPMI device and receive the response */ +int ipmi_send_message(struct ipmi_data *ipmi, unsigned char cmd, + unsigned char *tx_data, unsigned short tx_len, + unsigned char *rx_data, unsigned short rx_len) +{ + int status = 0, retry = 0; + + // Validate the input parameters + if ((tx_len && !tx_data) || (rx_len && !rx_data)) { + return -EINVAL; + } + + for (retry = 0; retry <= IPMI_ERR_RETRY_TIMES; retry++) { + status = _ipmi_send_message(ipmi, cmd, tx_data, tx_len, rx_data, rx_len); + if (unlikely(status != 0)) { + _ipmi_log_error(ipmi, cmd, tx_data, tx_len, status, retry); + continue; + } + + if (unlikely(ipmi->rx_result != 0)) { + _ipmi_log_error(ipmi, cmd, tx_data, tx_len, status, retry); + continue; + } + + // Success, exit the retry loop + break; + } + + return status; +} + +EXPORT_SYMBOL(ipmi_send_message); + +static int __init ipmi_module_init(void) +{ + printk(KERN_INFO "Accton IPMI Module loaded\n"); + return 0; +} + +static void __exit ipmi_module_exit(void) +{ + printk(KERN_INFO "Accton IPMI Module unloaded\n"); +} + +module_init(ipmi_module_init); +module_exit(ipmi_module_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("Accton IPMI messaging module"); +MODULE_LICENSE("GPL"); diff --git a/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/accton_ipmi_intf.h b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/accton_ipmi_intf.h new file mode 100644 index 0000000000..4b2fe59f93 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/accton_ipmi_intf.h @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright 2024 Accton Technology Corporation. + * + * 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. + * + * + * Description: + * IPMI driver related interface declarations + */ + +#ifndef ACCTON_IPMI_INTF_H +#define ACCTON_IPMI_INTF_H + +#include +#include + +/* Structure to hold IPMI (Intelligent Platform Management Interface) data */ +struct ipmi_data { + struct completion read_complete; // Synchronization primitive for signaling message read completion + struct ipmi_addr address; // Structure to store the IPMI system interface address + struct ipmi_user *user; // Pointer to IPMI user created by the kernel + int interface; // Interface identifier for the IPMI system + + struct kernel_ipmi_msg tx_message; // Message structure for sending IPMI commands + long tx_msgid; // Message ID for tracking IPMI message transactions + + void *rx_msg_data; // Pointer to buffer for storing received IPMI message data + unsigned short rx_msg_len; // Length of the received IPMI message + unsigned char rx_result; // Result code from the received IPMI message + int rx_recv_type; // Type of the received message (e.g., system interface, LAN, etc.) + + struct ipmi_user_hndl ipmi_hndlrs; // IPMI handler structure for handling incoming IPMI messages + struct device *dev; // Device structure for logging errors +}; + +/* Function declarations */ + +/* + * Initialize IPMI data structure and create a user interface for communication. + * + * @param ipmi: Pointer to ipmi_data structure to be initialized. + * @param iface: IPMI interface identifier. + * @param dev: Device structure for logging errors. + * @return 0 on success, or an error code on failure. + */ +extern int init_ipmi_data(struct ipmi_data *ipmi, int iface, struct device *dev); + +/* + * Send an IPMI command to the IPMI device and receive the response. + * + * @param ipmi: Pointer to ipmi_data structure containing IPMI communication information. + * @param cmd: IPMI command byte. + * @param tx_data: Pointer to data buffer for the command payload. + * @param tx_len: Length of the command payload data. + * @param rx_data: Pointer to buffer for storing the response data. + * @param rx_len: Length of the response data buffer. + * @return 0 on success, or an error code on failure. + */ +extern int ipmi_send_message(struct ipmi_data *ipmi, unsigned char cmd, + unsigned char *tx_data, unsigned short tx_len, + unsigned char *rx_data, unsigned short rx_len); + +#endif /* ACCTON_IPMI_INTF_H */ diff --git a/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-fan.c b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-fan.c new file mode 100644 index 0000000000..be53d533c6 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-fan.c @@ -0,0 +1,681 @@ +/* + * Copyright (C) Roger Ho + * + * + * Based on: + * pca954x.c from Kumar Gala + * Copyright (C) 2006 + * + * Based on: + * pca954x.c from Ken Harrenstien + * Copyright (C) 2004 Google, Inc. (Ken Harrenstien) + * + * Based on: + * i2c-virtual_cb.c from Brian Kuschak + * and + * pca9540.c from Jean Delvare . + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "accton_ipmi_intf.h" + + +#define DRVNAME "as7927_50x_fan" +#define IPMI_FAN_READ_CMD 0x14 +#define IPMI_FAN_WRITE_CMD 0x15 +#define IPMI_FAN_READ_RPM_CMD 0x20 +#define IPMI_FAN_REG_READ_CMD 0x20 +#define MAX_FAN_F_SPEED_RPM 31000 +#define MAX_FAN_R_SPEED_RPM 28000 + +static ssize_t set_fan(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t show_fan(struct device *dev, struct device_attribute *attr, + char *buf); +static ssize_t show_version(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_dir(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_threshold(struct device *dev, struct device_attribute *da, + char *buf); +static int as7927_50x_fan_probe(struct platform_device *pdev); +static int as7927_50x_fan_remove(struct platform_device *pdev); + +enum fan_id { + FAN_1, + FAN_2, + FAN_3, + FAN_4, + FAN_5, + FAN_6, + FAN_7, + FAN_8, + FAN_9, + FAN_10, + FAN_11, + FAN_12, + FAN_13, + FAN_14, + NUM_OF_FAN, + NUM_OF_FAN_MODULE = NUM_OF_FAN +}; + +enum fan_data_index { + FAN_PRESENT, + FAN_PWM, + FAN_SPEED0, + FAN_SPEED1, + FAN_DATA_COUNT, + + FAN_TARGET_SPEED0 = 0, + FAN_TARGET_SPEED1, + FAN_SPEED_TOLERANCE, + FAN_SPEED_DATA_COUNT +}; + +struct as7927_50x_fan_data { + struct platform_device *pdev; + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + /* 4 bytes for each fan, the last 2 bytes is fan dir */ + unsigned char ipmi_resp[NUM_OF_FAN * FAN_DATA_COUNT + 2]; + unsigned char ipmi_resp_cpld[2]; + unsigned char ipmi_resp_speed[NUM_OF_FAN * FAN_SPEED_DATA_COUNT]; + struct ipmi_data ipmi; + unsigned char ipmi_tx_data[3]; /* 0: FAN id, 1: 0x02, 2: PWM */ +}; + +struct as7927_50x_fan_data *data = NULL; + +static struct platform_driver as7927_50x_fan_driver = { + .probe = as7927_50x_fan_probe, + .remove = as7927_50x_fan_remove, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +#define FAN_PRESENT_ATTR_ID(index) FAN##index##_PRESENT +#define FAN_PWM_ATTR_ID(index) FAN##index##_PWM +#define FAN_RPM_ATTR_ID(index) FAN##index##_INPUT +#define FAN_DIR_ATTR_ID(index) FAN##index##_DIR +#define FAN_FAULT_ATTR_ID(index) FAN##index##_FAULT +#define FAN_RPM_TARGET_ATTR_ID(index) FAN##index##_TARGET +#define FAN_RPM_TOLERANCE_ATTR_ID(index) FAN##index##_TOLERANCE + +#define FAN_ATTR(fan_id) \ + FAN_PRESENT_ATTR_ID(fan_id), \ + FAN_PWM_ATTR_ID(fan_id), \ + FAN_RPM_ATTR_ID(fan_id), \ + FAN_DIR_ATTR_ID(fan_id), \ + FAN_FAULT_ATTR_ID(fan_id) + +#define FAN_RPM_THRESHOLD_ATTR(fan_id) \ + FAN_RPM_TARGET_ATTR_ID(fan_id), \ + FAN_RPM_TOLERANCE_ATTR_ID(fan_id) + +enum as7927_50x_fan_sysfs_attrs { + FAN_ATTR(1), + FAN_ATTR(2), + FAN_ATTR(3), + FAN_ATTR(4), + FAN_ATTR(5), + FAN_ATTR(6), + FAN_ATTR(7), + FAN_ATTR(8), + FAN_ATTR(9), + FAN_ATTR(10), + FAN_ATTR(11), + FAN_ATTR(12), + FAN_ATTR(13), + FAN_ATTR(14), + NUM_OF_FAN_ATTR, + FAN_VERSION, + FAN_R_MAX_RPM, + FAN_F_MAX_RPM, + NUM_OF_PER_FAN_ATTR = (NUM_OF_FAN_ATTR/NUM_OF_FAN), + START_OF_FAN_THRESHOLD_ATTR = -1, + FAN_RPM_THRESHOLD_ATTR(1), + FAN_RPM_THRESHOLD_ATTR(2), + FAN_RPM_THRESHOLD_ATTR(3), + FAN_RPM_THRESHOLD_ATTR(4), + FAN_RPM_THRESHOLD_ATTR(5), + FAN_RPM_THRESHOLD_ATTR(6), + FAN_RPM_THRESHOLD_ATTR(7), + FAN_RPM_THRESHOLD_ATTR(8), + FAN_RPM_THRESHOLD_ATTR(9), + FAN_RPM_THRESHOLD_ATTR(10), + FAN_RPM_THRESHOLD_ATTR(11), + FAN_RPM_THRESHOLD_ATTR(12), + FAN_RPM_THRESHOLD_ATTR(13), + FAN_RPM_THRESHOLD_ATTR(14), + NUM_OF_THRESHOLD_ATTR, + NUM_OF_PER_FAN_THRESHOLD_ATTR = (NUM_OF_THRESHOLD_ATTR/NUM_OF_FAN) +}; + +/* fan attributes */ +#define DECLARE_FAN_VER_SENSOR_DEVICE_ATTR() \ + static SENSOR_DEVICE_ATTR(version, S_IRUGO, show_version, NULL, FAN_VERSION) +#define DECLARE_FAN_VER_ATTR() \ + &sensor_dev_attr_version.dev_attr.attr + +#define DECLARE_FAN_SENSOR_DEVICE_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_present, S_IRUGO, show_fan, NULL, \ + FAN##index##_PRESENT); \ + static SENSOR_DEVICE_ATTR(fan##index##_pwm, S_IWUSR | S_IRUGO, show_fan, \ + set_fan, FAN##index##_PWM); \ + static SENSOR_DEVICE_ATTR(fan##index##_input, S_IRUGO, show_fan, NULL, \ + FAN##index##_INPUT); \ + static SENSOR_DEVICE_ATTR(fan##index##_dir, S_IRUGO, show_dir, NULL, \ + FAN##index##_DIR); \ + static SENSOR_DEVICE_ATTR(fan##index##_fault, S_IRUGO, show_fan, NULL, \ + FAN##index##_FAULT); \ + static SENSOR_DEVICE_ATTR(fan##index##_target, S_IRUGO, show_threshold, \ + NULL, FAN##index##_TARGET); \ + static SENSOR_DEVICE_ATTR(fan##index##_tolerance, S_IRUGO, show_threshold,\ + NULL, FAN##index##_TOLERANCE); \ + +static SENSOR_DEVICE_ATTR(fan_max_r_speed_rpm, S_IRUGO, show_fan, NULL, \ + FAN_R_MAX_RPM); +static SENSOR_DEVICE_ATTR(fan_max_f_speed_rpm, S_IRUGO, show_fan, NULL, \ + FAN_F_MAX_RPM); +#define DECLARE_FAN_MAX_RPM_ATTR(index) \ + &sensor_dev_attr_fan_max_r_speed_rpm.dev_attr.attr,\ + &sensor_dev_attr_fan_max_f_speed_rpm.dev_attr.attr + +#define DECLARE_FAN_ATTR(index) \ + &sensor_dev_attr_fan##index##_present.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_pwm.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_input.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_dir.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_fault.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_target.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_tolerance.dev_attr.attr + +DECLARE_FAN_SENSOR_DEVICE_ATTR(1); +DECLARE_FAN_SENSOR_DEVICE_ATTR(2); +DECLARE_FAN_SENSOR_DEVICE_ATTR(3); +DECLARE_FAN_SENSOR_DEVICE_ATTR(4); +DECLARE_FAN_SENSOR_DEVICE_ATTR(5); +DECLARE_FAN_SENSOR_DEVICE_ATTR(6); +DECLARE_FAN_SENSOR_DEVICE_ATTR(7); +DECLARE_FAN_SENSOR_DEVICE_ATTR(8); +DECLARE_FAN_SENSOR_DEVICE_ATTR(9); +DECLARE_FAN_SENSOR_DEVICE_ATTR(10); +DECLARE_FAN_SENSOR_DEVICE_ATTR(11); +DECLARE_FAN_SENSOR_DEVICE_ATTR(12); +DECLARE_FAN_SENSOR_DEVICE_ATTR(13); +DECLARE_FAN_SENSOR_DEVICE_ATTR(14); +DECLARE_FAN_VER_SENSOR_DEVICE_ATTR(); + +static struct attribute *as7927_50x_fan_attrs[] = { + /* fan attributes */ + DECLARE_FAN_ATTR(1), + DECLARE_FAN_ATTR(2), + DECLARE_FAN_ATTR(3), + DECLARE_FAN_ATTR(4), + DECLARE_FAN_ATTR(5), + DECLARE_FAN_ATTR(6), + DECLARE_FAN_ATTR(7), + DECLARE_FAN_ATTR(8), + DECLARE_FAN_ATTR(9), + DECLARE_FAN_ATTR(10), + DECLARE_FAN_ATTR(11), + DECLARE_FAN_ATTR(12), + DECLARE_FAN_ATTR(13), + DECLARE_FAN_ATTR(14), + DECLARE_FAN_VER_ATTR(), + DECLARE_FAN_MAX_RPM_ATTR(), + NULL +}; +ATTRIBUTE_GROUPS(as7927_50x_fan); + +static struct as7927_50x_fan_data *as7927_50x_fan_update_device(void) +{ + int status = 0; + + if (time_before(jiffies, data->last_updated + HZ * 5) && data->valid) + return data; + + data->valid = 0; + status = ipmi_send_message(&data->ipmi, IPMI_FAN_READ_CMD, NULL, 0, + data->ipmi_resp, sizeof(data->ipmi_resp)); + if (unlikely(status != 0)) + goto exit; + + if (unlikely(data->ipmi.rx_result != 0)) { + status = -EIO; + goto exit; + } + + data->ipmi_tx_data[0] = IPMI_FAN_READ_RPM_CMD; + status = ipmi_send_message(&data->ipmi, IPMI_FAN_READ_CMD, + data->ipmi_tx_data, 1, + data->ipmi_resp_speed, + sizeof(data->ipmi_resp_speed)); + + data->last_updated = jiffies; + data->valid = 1; + +exit: + return data; +} + +static ssize_t show_fan(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + unsigned char fid = attr->index / NUM_OF_PER_FAN_ATTR; + int value = 0; + int index = 0; + int present = 0; + int error = 0; + + switch (attr->index) { + case FAN_R_MAX_RPM: + return sprintf(buf, "%d\n", MAX_FAN_R_SPEED_RPM); + case FAN_F_MAX_RPM: + return sprintf(buf, "%d\n", MAX_FAN_F_SPEED_RPM); + } + + mutex_lock(&data->update_lock); + + data = as7927_50x_fan_update_device(); + if (!data->valid) { + error = -EIO; + goto exit; + } + + index = fid * FAN_DATA_COUNT; /* base index */ + present = !!data->ipmi_resp[index + FAN_PRESENT]; + + switch (attr->index) { + case FAN1_PRESENT: + case FAN2_PRESENT: + case FAN3_PRESENT: + case FAN4_PRESENT: + case FAN5_PRESENT: + case FAN6_PRESENT: + case FAN7_PRESENT: + case FAN8_PRESENT: + case FAN9_PRESENT: + case FAN10_PRESENT: + case FAN11_PRESENT: + case FAN12_PRESENT: + case FAN13_PRESENT: + case FAN14_PRESENT: + value = present; + break; + case FAN1_PWM: + case FAN2_PWM: + case FAN3_PWM: + case FAN4_PWM: + case FAN5_PWM: + case FAN6_PWM: + case FAN7_PWM: + case FAN8_PWM: + case FAN9_PWM: + case FAN10_PWM: + case FAN11_PWM: + case FAN12_PWM: + case FAN13_PWM: + case FAN14_PWM: + index = (fid % NUM_OF_FAN_MODULE) * FAN_DATA_COUNT; + value = DIV_ROUND_CLOSEST(data->ipmi_resp[index + FAN_PWM] * 666, 100); + break; + case FAN1_INPUT: + case FAN2_INPUT: + case FAN3_INPUT: + case FAN4_INPUT: + case FAN5_INPUT: + case FAN6_INPUT: + case FAN7_INPUT: + case FAN8_INPUT: + case FAN9_INPUT: + case FAN10_INPUT: + case FAN11_INPUT: + case FAN12_INPUT: + case FAN13_INPUT: + case FAN14_INPUT: + value = (int)data->ipmi_resp[index + FAN_SPEED0] | + (int)data->ipmi_resp[index + FAN_SPEED1] << 8; + break; + case FAN1_FAULT: + case FAN2_FAULT: + case FAN3_FAULT: + case FAN4_FAULT: + case FAN5_FAULT: + case FAN6_FAULT: + case FAN7_FAULT: + case FAN8_FAULT: + case FAN9_FAULT: + case FAN10_FAULT: + case FAN11_FAULT: + case FAN12_FAULT: + case FAN13_FAULT: + case FAN14_FAULT: + value = (int)data->ipmi_resp[index + FAN_SPEED0] | + (int)data->ipmi_resp[index + FAN_SPEED1] << 8; + value = !value; + break; + default: + error = -EINVAL; + goto exit; + } + + mutex_unlock(&data->update_lock); + return sprintf(buf, "%d\n", value); + +exit: + mutex_unlock(&data->update_lock); + return error; +} + +static ssize_t set_fan(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + long pwm; + int status; + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + unsigned char fid = attr->index / NUM_OF_PER_FAN_ATTR; + + status = kstrtol(buf, 10, &pwm); + if (status) + return status; + + pwm = (pwm * 100) / 666; /* Convert pwm to register value */ + + mutex_lock(&data->update_lock); + + /* + * Send IPMI write command : + * BMC supports a design with seven fan modules, each containing two fans. + * Since NUM_OF_FAN_MODULE is 14 in AS7927, it needs to be divided by 2. + * The result : + * fan1_pwm(Front) : ipmi_tx_data[0] = 1, fan2_pwm(Front) : ipmi_tx_data[0] = 2 + * fan3_pwm(Front) : ipmi_tx_data[0] = 3, fan4_pwm(Front) : ipmi_tx_data[0] = 4 + * ... + * fan8_pwm(Rear) : ipmi_tx_data[0] = 1, fan9_pwm(Rear) : ipmi_tx_data[0] = 2 + * fan10_pwm(Rear) : ipmi_tx_data[0] = 3, fan11_pwm(Rear) : ipmi_tx_data[0] = 4 + * + */ + data->ipmi_tx_data[0] = (fid % (NUM_OF_FAN_MODULE / 2)) + 1; + data->ipmi_tx_data[1] = 0x02; + data->ipmi_tx_data[2] = pwm; + status = ipmi_send_message(&data->ipmi, IPMI_FAN_WRITE_CMD, + data->ipmi_tx_data, sizeof(data->ipmi_tx_data), + NULL, 0); + if (unlikely(status != 0)) + goto exit; + + if (unlikely(data->ipmi.rx_result != 0)) { + status = -EIO; + goto exit; + } + + /* force update */ + data->valid = 0; + status = count; + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static struct as7927_50x_fan_data *as7927_50x_fan_update_cpld_ver(void) +{ + int status = 0; + + data->valid = 0; + data->ipmi_tx_data[0] = 0x33; + status = ipmi_send_message(&data->ipmi, IPMI_FAN_REG_READ_CMD, + data->ipmi_tx_data, 1, + data->ipmi_resp_cpld, + sizeof(data->ipmi_resp_cpld)); + if (unlikely(status != 0)) + goto exit; + + if (unlikely(data->ipmi.rx_result != 0)) { + status = -EIO; + goto exit; + } + + data->last_updated = jiffies; + data->valid = 1; + +exit: + return data; +} + +static ssize_t show_version(struct device *dev, struct device_attribute *da, + char *buf) +{ + unsigned char major; + unsigned char minor; + int error = 0; + + mutex_lock(&data->update_lock); + + data = as7927_50x_fan_update_cpld_ver(); + if (!data->valid) { + error = -EIO; + goto exit; + } + + major = data->ipmi_resp_cpld[0]; + minor = data->ipmi_resp_cpld[1]; + mutex_unlock(&data->update_lock); + return sprintf(buf, "%d.%d\n", major, minor); + +exit: + mutex_unlock(&data->update_lock); + return error; +} + +static ssize_t show_dir(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + unsigned char fid = (attr->index / NUM_OF_PER_FAN_ATTR); + int value = 0; + int index = 0; + int present = 0; + int error = 0; + + mutex_lock(&data->update_lock); + + data = as7927_50x_fan_update_device(); + if (!data->valid) { + error = -EIO; + goto exit; + } + + index = fid * FAN_DATA_COUNT; /* base index */ + present = !!data->ipmi_resp[index + FAN_PRESENT]; + + value = data->ipmi_resp[NUM_OF_FAN * FAN_DATA_COUNT] | + (data->ipmi_resp[NUM_OF_FAN * FAN_DATA_COUNT + 1] << 8); + mutex_unlock(&data->update_lock); + + if (!present) + return sprintf(buf, "0\n"); + else + return sprintf(buf, "%s\n", + (value & BIT(fid % NUM_OF_FAN_MODULE)) ? "B2F" : "F2B"); + +exit: + mutex_unlock(&data->update_lock); + return error; +} + +static ssize_t show_threshold(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + unsigned char fid = (attr->index / NUM_OF_PER_FAN_THRESHOLD_ATTR); + int value = 0; + int index = 0; + int error = 0; + + mutex_lock(&data->update_lock); + + data = as7927_50x_fan_update_device(); + if (!data->valid) { + error = -EIO; + goto exit; + } + + index = fid * FAN_SPEED_DATA_COUNT; /* base index */ + + switch (attr->index) { + case FAN1_TARGET: + case FAN2_TARGET: + case FAN3_TARGET: + case FAN4_TARGET: + case FAN5_TARGET: + case FAN6_TARGET: + case FAN7_TARGET: + case FAN8_TARGET: + case FAN9_TARGET: + case FAN10_TARGET: + case FAN11_TARGET: + case FAN12_TARGET: + case FAN13_TARGET: + case FAN14_TARGET: + value = (int)data->ipmi_resp_speed[index + FAN_TARGET_SPEED0] | + (int)data->ipmi_resp_speed[index + FAN_TARGET_SPEED1] << 8; + break; + case FAN1_TOLERANCE: + case FAN2_TOLERANCE: + case FAN3_TOLERANCE: + case FAN4_TOLERANCE: + case FAN5_TOLERANCE: + case FAN6_TOLERANCE: + case FAN7_TOLERANCE: + case FAN8_TOLERANCE: + case FAN9_TOLERANCE: + case FAN10_TOLERANCE: + case FAN11_TOLERANCE: + case FAN12_TOLERANCE: + case FAN13_TOLERANCE: + case FAN14_TOLERANCE: + value = (int)data->ipmi_resp_speed[index + FAN_SPEED_TOLERANCE]; + break; + default: + error = -EINVAL; + goto exit; + } + + mutex_unlock(&data->update_lock); + return sprintf(buf, "%d\n", value); + +exit: + mutex_unlock(&data->update_lock); + return error; +} + +static int as7927_50x_fan_probe(struct platform_device *pdev) +{ + int status = 0; + struct device *hwmon_dev; + + hwmon_dev = hwmon_device_register_with_info(&pdev->dev, DRVNAME, + NULL, NULL, as7927_50x_fan_groups); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + return status; + } + + mutex_lock(&data->update_lock); + data->hwmon_dev = hwmon_dev; + mutex_unlock(&data->update_lock); + + dev_info(&pdev->dev, "Device Created\n"); + + return status; +} + +static int as7927_50x_fan_remove(struct platform_device *pdev) +{ + mutex_lock(&data->update_lock); + if (data->hwmon_dev) { + hwmon_device_unregister(data->hwmon_dev); + data->hwmon_dev = NULL; + } + mutex_unlock(&data->update_lock); + + return 0; +} + +static int __init as7927_50x_fan_init(void) +{ + int ret; + + data = kzalloc(sizeof(struct as7927_50x_fan_data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto alloc_err; + } + + mutex_init(&data->update_lock); + + ret = platform_driver_register(&as7927_50x_fan_driver); + if (ret < 0) + goto dri_reg_err; + + data->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(data->pdev)) { + ret = PTR_ERR(data->pdev); + goto dev_reg_err; + } + + /* Set up IPMI interface */ + ret = init_ipmi_data(&data->ipmi, 0, &data->pdev->dev); + if (ret) { + goto ipmi_err; + } + + return 0; + +ipmi_err: + platform_device_unregister(data->pdev); +dev_reg_err: + platform_driver_unregister(&as7927_50x_fan_driver); +dri_reg_err: + kfree(data); +alloc_err: + return ret; +} + +static void __exit as7927_50x_fan_exit(void) +{ + if (data) { + ipmi_destroy_user(data->ipmi.user); + platform_device_unregister(data->pdev); + kfree(data); + } + platform_driver_unregister(&as7927_50x_fan_driver); +} + +MODULE_AUTHOR("Roger Ho "); +MODULE_DESCRIPTION("as7927_50x_fan driver"); +MODULE_LICENSE("GPL"); + +module_init(as7927_50x_fan_init); +module_exit(as7927_50x_fan_exit); diff --git a/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-fpga.c b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-fpga.c new file mode 100644 index 0000000000..7b2457cafa --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-fpga.c @@ -0,0 +1,1182 @@ +/* + * Copyright (C) Willy Liu + * + * This module supports the accton fpga via pcie that read/write reg + * mechanism to get OSFP/SFP status ...etc. + * This includes the: + * Accton as7927_50x FPGA + * + * Copyright (C) 2017 Finisar Corp. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include + +/*********************************************** + * variable define + * *********************************************/ +#define DRVNAME "as7927_50x_fpga" +#define OCORES_I2C_DRVNAME "ocores-as7927" +#define PORT_NUM 50 /* 40 SFP28 + 8 SFP56 + 1 400G OSFP-DD + 1 800G QSFP-DD */ +/* + * PCIE BAR address + */ +#define BAR0_NUM 0 + +#define REGION_LEN 0xFF +#define FPGA_PCI_VENDOR_ID 0x10EE +#define FPGA_PCI_DEVICE_ID 0x7021 + +#define SPI_BUSY_MASK_CPLD1 0x01 +#define SPI_BUSY_MASK_CPLD2 0x02 + +/*********************************************** + * FPGA + * *********************************************/ +#define FPGA_PCIE_START_OFFSET 0x0000 +#define FPGA_BOARD_INFO_REG (FPGA_PCIE_START_OFFSET + 0x00) +#define FPGA_MAJOR_VER_REG (FPGA_PCIE_START_OFFSET + 0x01) +#define FPGA_MINOR_VER_REG (FPGA_PCIE_START_OFFSET + 0x02) + +/*********************************************** + * CPLD1 + * *********************************************/ +#define CPLD1_PCIE_START_OFFSET 0x2000 +/* Port CPLD1 VER */ +#define CPLD1_MAJOR_VER_REG (CPLD1_PCIE_START_OFFSET + 0x00) +#define CPLD1_MINOR_VER_REG (CPLD1_PCIE_START_OFFSET + 0x01) +/* SFP P0-24 RXLOS */ +#define XCVR_P7_P0_RXLOS_REG (CPLD1_PCIE_START_OFFSET + 0x21) +#define XCVR_P15_P8_RXLOS_REG (CPLD1_PCIE_START_OFFSET + 0x22) +#define XCVR_P23_P16_RXLOS_REG (CPLD1_PCIE_START_OFFSET + 0x23) +#define XCVR_P24_RXLOS_REG (CPLD1_PCIE_START_OFFSET + 0x24) +/* SFP P0-24 TX FAULT */ +#define XCVR_P7_P0_TXFAULT_REG (CPLD1_PCIE_START_OFFSET + 0x25) +#define XCVR_P15_P8_TXFAULT_REG (CPLD1_PCIE_START_OFFSET + 0x26) +#define XCVR_P23_P16_TXFAULT_REG (CPLD1_PCIE_START_OFFSET + 0x27) +#define XCVR_P24_TXFAULT_REG (CPLD1_PCIE_START_OFFSET + 0x28) +/* SFP P0-24 TX DISABLE */ +#define XCVR_P7_P0_TXDIS_REG (CPLD1_PCIE_START_OFFSET + 0x29) +#define XCVR_P15_P8_TXDIS_REG (CPLD1_PCIE_START_OFFSET + 0x2A) +#define XCVR_P23_P16_TXDIS_REG (CPLD1_PCIE_START_OFFSET + 0x2B) +#define XCVR_P24_TXDIS_REG (CPLD1_PCIE_START_OFFSET + 0x2C) +/* SFP P0-24 EFUSE */ +#define XCVR_P7_P0_EFUSE_REG (CPLD1_PCIE_START_OFFSET + 0x30) +#define XCVR_P15_P8_EFUSE_REG (CPLD1_PCIE_START_OFFSET + 0x31) +#define XCVR_P23_P16_EFUSE_REG (CPLD1_PCIE_START_OFFSET + 0x32) +#define XCVR_P24_EFUSE_REG (CPLD1_PCIE_START_OFFSET + 0x33) +/* SFP P0-24 MOD */ +#define XCVR_P7_P0_PRESENT_REG (CPLD1_PCIE_START_OFFSET + 0x50) +#define XCVR_P15_P8_PRESENT_REG (CPLD1_PCIE_START_OFFSET + 0x51) +#define XCVR_P23_P16_PRESENT_REG (CPLD1_PCIE_START_OFFSET + 0x52) +#define XCVR_P24_PRESENT_REG (CPLD1_PCIE_START_OFFSET + 0x53) + +/*********************************************** + * CPLD2 + * *********************************************/ +#define CPLD2_PCIE_START_OFFSET 0x3000 +/* Port CPLD2 VER */ +#define CPLD2_MAJOR_VER_REG (CPLD2_PCIE_START_OFFSET + 0x00) +#define CPLD2_MINOR_VER_REG (CPLD2_PCIE_START_OFFSET + 0x01) +/* Port 25-47 MOD */ +#define XCVR_P32_P25_PRESENT_REG (CPLD2_PCIE_START_OFFSET + 0x06) +#define XCVR_P40_P33_PRESENT_REG (CPLD2_PCIE_START_OFFSET + 0x07) +#define XCVR_P47_P41_PRESENT_REG (CPLD2_PCIE_START_OFFSET + 0x08) +/* Port 48-49 QSFP-DD PRESENT, LP MODE, POWER GOOD */ +#define XCVR_P49_P48_QSFPDD_REG (CPLD2_PCIE_START_OFFSET + 0x12) +/* Port 48-49 QSFP-DD RST */ +#define XCVR_P49_P48_QSFPDD_RST_REG (CPLD2_PCIE_START_OFFSET + 0x13) +/* Port 25-47 EFUSE */ +#define XCVR_P32_P25_EFUSE_REG (CPLD2_PCIE_START_OFFSET + 0x26) +#define XCVR_P40_P33_EFUSE_REG (CPLD2_PCIE_START_OFFSET + 0x27) +#define XCVR_P47_P41_EFUSE_REG (CPLD2_PCIE_START_OFFSET + 0x28) +/* Port 48-49 QSFP-DD ENABLE */ +#define XCVR_P49_P48_EN_REG (CPLD2_PCIE_START_OFFSET + 0x29) +/* Port 25-47 RXLOS */ +#define XCVR_P32_P25_RXLOS_REG (CPLD2_PCIE_START_OFFSET + 0x30) +#define XCVR_P40_P33_RXLOS_REG (CPLD2_PCIE_START_OFFSET + 0x31) +#define XCVR_P47_P41_RXLOS_REG (CPLD2_PCIE_START_OFFSET + 0x32) +/* Port 25-47 TX FAULT */ +#define XCVR_P32_P25_TXFAULT_REG (CPLD2_PCIE_START_OFFSET + 0x33) +#define XCVR_P40_P33_TXFAULT_REG (CPLD2_PCIE_START_OFFSET + 0x34) +#define XCVR_P47_P41_TXFAULT_REG (CPLD2_PCIE_START_OFFSET + 0x35) +/* Port 25-47 TX DISABLE */ +#define XCVR_P32_P25_TXDIS_REG (CPLD2_PCIE_START_OFFSET + 0x40) +#define XCVR_P40_P33_TXDIS_REG (CPLD2_PCIE_START_OFFSET + 0x41) +#define XCVR_P47_P41_TXDIS_REG (CPLD2_PCIE_START_OFFSET + 0x42) + +#define TRANSCEIVER_PRESENT_ATTR_ID(index) MODULE_PRESENT_##index +#define TRANSCEIVER_LPMODE_ATTR_ID(index) MODULE_LPMODE_##index +#define TRANSCEIVER_RESET_ATTR_ID(index) MODULE_RESET_##index +#define TRANSCEIVER_TX_DISABLE_ATTR_ID(index) MODULE_TX_DISABLE_##index +#define TRANSCEIVER_TX_FAULT_ATTR_ID(index) MODULE_TX_FAULT_##index +#define TRANSCEIVER_RX_LOS_ATTR_ID(index) MODULE_RX_LOS_##index +#define TRANSCEIVER_EFUSE_ATTR_ID(index) MODULE_EFUSE_##index +#define TRANSCEIVER_ENABLE_ATTR_ID(index) MODULE_ENABLE_##index + +/*********************************************** + * macro define + * *********************************************/ +#define pcie_err(fmt, args...) \ + printk(KERN_ERR "["DRVNAME"]: " fmt " ", ##args) + +#define pcie_info(fmt, args...) \ + printk(KERN_ERR "["DRVNAME"]: " fmt " ", ##args) + + +#define LOCK(lock) \ +do { \ + spin_lock(lock); \ +} while (0) + +#define UNLOCK(lock) \ +do { \ + spin_unlock(lock); \ +} while (0) + + +/*********************************************** + * structure & variable declare + * *********************************************/ +typedef struct pci_fpga_device_s { + void __iomem *data_base_addr0; + resource_size_t data_region0; + resource_size_t data_region1; + resource_size_t data_region2; + struct pci_dev *pci_dev; + struct platform_device *fpga_i2c[PORT_NUM]; +} pci_fpga_device_t; + +/*fpga port status*/ +struct as7927_50x_fpga_data { + u8 cpld_reg[2]; + unsigned long last_updated; /* In jiffies */ + pci_fpga_device_t pci_fpga_dev; +}; + +static struct platform_device *pdev = NULL; +extern spinlock_t cpld_access_lock; +extern int wait_spi(u32 mask, unsigned long timeout); + +/*********************************************** + * enum define + * *********************************************/ + enum fpga_sysfs_attributes { + /* transceiver attributes */ + TRANSCEIVER_PRESENT_ATTR_ID(1), + TRANSCEIVER_PRESENT_ATTR_ID(2), + TRANSCEIVER_PRESENT_ATTR_ID(3), + TRANSCEIVER_PRESENT_ATTR_ID(4), + TRANSCEIVER_PRESENT_ATTR_ID(5), + TRANSCEIVER_PRESENT_ATTR_ID(6), + TRANSCEIVER_PRESENT_ATTR_ID(7), + TRANSCEIVER_PRESENT_ATTR_ID(8), + TRANSCEIVER_PRESENT_ATTR_ID(9), + TRANSCEIVER_PRESENT_ATTR_ID(10), + TRANSCEIVER_PRESENT_ATTR_ID(11), + TRANSCEIVER_PRESENT_ATTR_ID(12), + TRANSCEIVER_PRESENT_ATTR_ID(13), + TRANSCEIVER_PRESENT_ATTR_ID(14), + TRANSCEIVER_PRESENT_ATTR_ID(15), + TRANSCEIVER_PRESENT_ATTR_ID(16), + TRANSCEIVER_PRESENT_ATTR_ID(17), + TRANSCEIVER_PRESENT_ATTR_ID(18), + TRANSCEIVER_PRESENT_ATTR_ID(19), + TRANSCEIVER_PRESENT_ATTR_ID(20), + TRANSCEIVER_PRESENT_ATTR_ID(21), + TRANSCEIVER_PRESENT_ATTR_ID(22), + TRANSCEIVER_PRESENT_ATTR_ID(23), + TRANSCEIVER_PRESENT_ATTR_ID(24), + TRANSCEIVER_PRESENT_ATTR_ID(25), + TRANSCEIVER_PRESENT_ATTR_ID(26), + TRANSCEIVER_PRESENT_ATTR_ID(27), + TRANSCEIVER_PRESENT_ATTR_ID(28), + TRANSCEIVER_PRESENT_ATTR_ID(29), + TRANSCEIVER_PRESENT_ATTR_ID(30), + TRANSCEIVER_PRESENT_ATTR_ID(31), + TRANSCEIVER_PRESENT_ATTR_ID(32), + TRANSCEIVER_PRESENT_ATTR_ID(33), + TRANSCEIVER_PRESENT_ATTR_ID(34), + TRANSCEIVER_PRESENT_ATTR_ID(35), + TRANSCEIVER_PRESENT_ATTR_ID(36), + TRANSCEIVER_PRESENT_ATTR_ID(37), + TRANSCEIVER_PRESENT_ATTR_ID(38), + TRANSCEIVER_PRESENT_ATTR_ID(39), + TRANSCEIVER_PRESENT_ATTR_ID(40), + TRANSCEIVER_PRESENT_ATTR_ID(41), + TRANSCEIVER_PRESENT_ATTR_ID(42), + TRANSCEIVER_PRESENT_ATTR_ID(43), + TRANSCEIVER_PRESENT_ATTR_ID(44), + TRANSCEIVER_PRESENT_ATTR_ID(45), + TRANSCEIVER_PRESENT_ATTR_ID(46), + TRANSCEIVER_PRESENT_ATTR_ID(47), + TRANSCEIVER_PRESENT_ATTR_ID(48), + TRANSCEIVER_PRESENT_ATTR_ID(49), + TRANSCEIVER_PRESENT_ATTR_ID(50), + /*TX DISABLE */ + TRANSCEIVER_TX_DISABLE_ATTR_ID(1), + TRANSCEIVER_TX_DISABLE_ATTR_ID(2), + TRANSCEIVER_TX_DISABLE_ATTR_ID(3), + TRANSCEIVER_TX_DISABLE_ATTR_ID(4), + TRANSCEIVER_TX_DISABLE_ATTR_ID(5), + TRANSCEIVER_TX_DISABLE_ATTR_ID(6), + TRANSCEIVER_TX_DISABLE_ATTR_ID(7), + TRANSCEIVER_TX_DISABLE_ATTR_ID(8), + TRANSCEIVER_TX_DISABLE_ATTR_ID(9), + TRANSCEIVER_TX_DISABLE_ATTR_ID(10), + TRANSCEIVER_TX_DISABLE_ATTR_ID(11), + TRANSCEIVER_TX_DISABLE_ATTR_ID(12), + TRANSCEIVER_TX_DISABLE_ATTR_ID(13), + TRANSCEIVER_TX_DISABLE_ATTR_ID(14), + TRANSCEIVER_TX_DISABLE_ATTR_ID(15), + TRANSCEIVER_TX_DISABLE_ATTR_ID(16), + TRANSCEIVER_TX_DISABLE_ATTR_ID(17), + TRANSCEIVER_TX_DISABLE_ATTR_ID(18), + TRANSCEIVER_TX_DISABLE_ATTR_ID(19), + TRANSCEIVER_TX_DISABLE_ATTR_ID(20), + TRANSCEIVER_TX_DISABLE_ATTR_ID(21), + TRANSCEIVER_TX_DISABLE_ATTR_ID(22), + TRANSCEIVER_TX_DISABLE_ATTR_ID(23), + TRANSCEIVER_TX_DISABLE_ATTR_ID(24), + TRANSCEIVER_TX_DISABLE_ATTR_ID(25), + TRANSCEIVER_TX_DISABLE_ATTR_ID(26), + TRANSCEIVER_TX_DISABLE_ATTR_ID(27), + TRANSCEIVER_TX_DISABLE_ATTR_ID(28), + TRANSCEIVER_TX_DISABLE_ATTR_ID(29), + TRANSCEIVER_TX_DISABLE_ATTR_ID(30), + TRANSCEIVER_TX_DISABLE_ATTR_ID(31), + TRANSCEIVER_TX_DISABLE_ATTR_ID(32), + TRANSCEIVER_TX_DISABLE_ATTR_ID(33), + TRANSCEIVER_TX_DISABLE_ATTR_ID(34), + TRANSCEIVER_TX_DISABLE_ATTR_ID(35), + TRANSCEIVER_TX_DISABLE_ATTR_ID(36), + TRANSCEIVER_TX_DISABLE_ATTR_ID(37), + TRANSCEIVER_TX_DISABLE_ATTR_ID(38), + TRANSCEIVER_TX_DISABLE_ATTR_ID(39), + TRANSCEIVER_TX_DISABLE_ATTR_ID(40), + TRANSCEIVER_TX_DISABLE_ATTR_ID(41), + TRANSCEIVER_TX_DISABLE_ATTR_ID(42), + TRANSCEIVER_TX_DISABLE_ATTR_ID(43), + TRANSCEIVER_TX_DISABLE_ATTR_ID(44), + TRANSCEIVER_TX_DISABLE_ATTR_ID(45), + TRANSCEIVER_TX_DISABLE_ATTR_ID(46), + TRANSCEIVER_TX_DISABLE_ATTR_ID(47), + TRANSCEIVER_TX_DISABLE_ATTR_ID(48), + /* TX FAULT */ + TRANSCEIVER_TX_FAULT_ATTR_ID(1), + TRANSCEIVER_TX_FAULT_ATTR_ID(2), + TRANSCEIVER_TX_FAULT_ATTR_ID(3), + TRANSCEIVER_TX_FAULT_ATTR_ID(4), + TRANSCEIVER_TX_FAULT_ATTR_ID(5), + TRANSCEIVER_TX_FAULT_ATTR_ID(6), + TRANSCEIVER_TX_FAULT_ATTR_ID(7), + TRANSCEIVER_TX_FAULT_ATTR_ID(8), + TRANSCEIVER_TX_FAULT_ATTR_ID(9), + TRANSCEIVER_TX_FAULT_ATTR_ID(10), + TRANSCEIVER_TX_FAULT_ATTR_ID(11), + TRANSCEIVER_TX_FAULT_ATTR_ID(12), + TRANSCEIVER_TX_FAULT_ATTR_ID(13), + TRANSCEIVER_TX_FAULT_ATTR_ID(14), + TRANSCEIVER_TX_FAULT_ATTR_ID(15), + TRANSCEIVER_TX_FAULT_ATTR_ID(16), + TRANSCEIVER_TX_FAULT_ATTR_ID(17), + TRANSCEIVER_TX_FAULT_ATTR_ID(18), + TRANSCEIVER_TX_FAULT_ATTR_ID(19), + TRANSCEIVER_TX_FAULT_ATTR_ID(20), + TRANSCEIVER_TX_FAULT_ATTR_ID(21), + TRANSCEIVER_TX_FAULT_ATTR_ID(22), + TRANSCEIVER_TX_FAULT_ATTR_ID(23), + TRANSCEIVER_TX_FAULT_ATTR_ID(24), + TRANSCEIVER_TX_FAULT_ATTR_ID(25), + TRANSCEIVER_TX_FAULT_ATTR_ID(26), + TRANSCEIVER_TX_FAULT_ATTR_ID(27), + TRANSCEIVER_TX_FAULT_ATTR_ID(28), + TRANSCEIVER_TX_FAULT_ATTR_ID(29), + TRANSCEIVER_TX_FAULT_ATTR_ID(30), + TRANSCEIVER_TX_FAULT_ATTR_ID(31), + TRANSCEIVER_TX_FAULT_ATTR_ID(32), + TRANSCEIVER_TX_FAULT_ATTR_ID(33), + TRANSCEIVER_TX_FAULT_ATTR_ID(34), + TRANSCEIVER_TX_FAULT_ATTR_ID(35), + TRANSCEIVER_TX_FAULT_ATTR_ID(36), + TRANSCEIVER_TX_FAULT_ATTR_ID(37), + TRANSCEIVER_TX_FAULT_ATTR_ID(38), + TRANSCEIVER_TX_FAULT_ATTR_ID(39), + TRANSCEIVER_TX_FAULT_ATTR_ID(40), + TRANSCEIVER_TX_FAULT_ATTR_ID(41), + TRANSCEIVER_TX_FAULT_ATTR_ID(42), + TRANSCEIVER_TX_FAULT_ATTR_ID(43), + TRANSCEIVER_TX_FAULT_ATTR_ID(44), + TRANSCEIVER_TX_FAULT_ATTR_ID(45), + TRANSCEIVER_TX_FAULT_ATTR_ID(46), + TRANSCEIVER_TX_FAULT_ATTR_ID(47), + TRANSCEIVER_TX_FAULT_ATTR_ID(48), + /* RX LOS */ + TRANSCEIVER_RX_LOS_ATTR_ID(1), + TRANSCEIVER_RX_LOS_ATTR_ID(2), + TRANSCEIVER_RX_LOS_ATTR_ID(3), + TRANSCEIVER_RX_LOS_ATTR_ID(4), + TRANSCEIVER_RX_LOS_ATTR_ID(5), + TRANSCEIVER_RX_LOS_ATTR_ID(6), + TRANSCEIVER_RX_LOS_ATTR_ID(7), + TRANSCEIVER_RX_LOS_ATTR_ID(8), + TRANSCEIVER_RX_LOS_ATTR_ID(9), + TRANSCEIVER_RX_LOS_ATTR_ID(10), + TRANSCEIVER_RX_LOS_ATTR_ID(11), + TRANSCEIVER_RX_LOS_ATTR_ID(12), + TRANSCEIVER_RX_LOS_ATTR_ID(13), + TRANSCEIVER_RX_LOS_ATTR_ID(14), + TRANSCEIVER_RX_LOS_ATTR_ID(15), + TRANSCEIVER_RX_LOS_ATTR_ID(16), + TRANSCEIVER_RX_LOS_ATTR_ID(17), + TRANSCEIVER_RX_LOS_ATTR_ID(18), + TRANSCEIVER_RX_LOS_ATTR_ID(19), + TRANSCEIVER_RX_LOS_ATTR_ID(20), + TRANSCEIVER_RX_LOS_ATTR_ID(21), + TRANSCEIVER_RX_LOS_ATTR_ID(22), + TRANSCEIVER_RX_LOS_ATTR_ID(23), + TRANSCEIVER_RX_LOS_ATTR_ID(24), + TRANSCEIVER_RX_LOS_ATTR_ID(25), + TRANSCEIVER_RX_LOS_ATTR_ID(26), + TRANSCEIVER_RX_LOS_ATTR_ID(27), + TRANSCEIVER_RX_LOS_ATTR_ID(28), + TRANSCEIVER_RX_LOS_ATTR_ID(29), + TRANSCEIVER_RX_LOS_ATTR_ID(30), + TRANSCEIVER_RX_LOS_ATTR_ID(31), + TRANSCEIVER_RX_LOS_ATTR_ID(32), + TRANSCEIVER_RX_LOS_ATTR_ID(33), + TRANSCEIVER_RX_LOS_ATTR_ID(34), + TRANSCEIVER_RX_LOS_ATTR_ID(35), + TRANSCEIVER_RX_LOS_ATTR_ID(36), + TRANSCEIVER_RX_LOS_ATTR_ID(37), + TRANSCEIVER_RX_LOS_ATTR_ID(38), + TRANSCEIVER_RX_LOS_ATTR_ID(39), + TRANSCEIVER_RX_LOS_ATTR_ID(40), + TRANSCEIVER_RX_LOS_ATTR_ID(41), + TRANSCEIVER_RX_LOS_ATTR_ID(42), + TRANSCEIVER_RX_LOS_ATTR_ID(43), + TRANSCEIVER_RX_LOS_ATTR_ID(44), + TRANSCEIVER_RX_LOS_ATTR_ID(45), + TRANSCEIVER_RX_LOS_ATTR_ID(46), + TRANSCEIVER_RX_LOS_ATTR_ID(47), + TRANSCEIVER_RX_LOS_ATTR_ID(48), + /* EFUSE */ + TRANSCEIVER_EFUSE_ATTR_ID(1), + TRANSCEIVER_EFUSE_ATTR_ID(2), + TRANSCEIVER_EFUSE_ATTR_ID(3), + TRANSCEIVER_EFUSE_ATTR_ID(4), + TRANSCEIVER_EFUSE_ATTR_ID(5), + TRANSCEIVER_EFUSE_ATTR_ID(6), + TRANSCEIVER_EFUSE_ATTR_ID(7), + TRANSCEIVER_EFUSE_ATTR_ID(8), + TRANSCEIVER_EFUSE_ATTR_ID(9), + TRANSCEIVER_EFUSE_ATTR_ID(10), + TRANSCEIVER_EFUSE_ATTR_ID(11), + TRANSCEIVER_EFUSE_ATTR_ID(12), + TRANSCEIVER_EFUSE_ATTR_ID(13), + TRANSCEIVER_EFUSE_ATTR_ID(14), + TRANSCEIVER_EFUSE_ATTR_ID(15), + TRANSCEIVER_EFUSE_ATTR_ID(16), + TRANSCEIVER_EFUSE_ATTR_ID(17), + TRANSCEIVER_EFUSE_ATTR_ID(18), + TRANSCEIVER_EFUSE_ATTR_ID(19), + TRANSCEIVER_EFUSE_ATTR_ID(20), + TRANSCEIVER_EFUSE_ATTR_ID(21), + TRANSCEIVER_EFUSE_ATTR_ID(22), + TRANSCEIVER_EFUSE_ATTR_ID(23), + TRANSCEIVER_EFUSE_ATTR_ID(24), + TRANSCEIVER_EFUSE_ATTR_ID(25), + TRANSCEIVER_EFUSE_ATTR_ID(26), + TRANSCEIVER_EFUSE_ATTR_ID(27), + TRANSCEIVER_EFUSE_ATTR_ID(28), + TRANSCEIVER_EFUSE_ATTR_ID(29), + TRANSCEIVER_EFUSE_ATTR_ID(30), + TRANSCEIVER_EFUSE_ATTR_ID(31), + TRANSCEIVER_EFUSE_ATTR_ID(32), + TRANSCEIVER_EFUSE_ATTR_ID(33), + TRANSCEIVER_EFUSE_ATTR_ID(34), + TRANSCEIVER_EFUSE_ATTR_ID(35), + TRANSCEIVER_EFUSE_ATTR_ID(36), + TRANSCEIVER_EFUSE_ATTR_ID(37), + TRANSCEIVER_EFUSE_ATTR_ID(38), + TRANSCEIVER_EFUSE_ATTR_ID(39), + TRANSCEIVER_EFUSE_ATTR_ID(40), + TRANSCEIVER_EFUSE_ATTR_ID(41), + TRANSCEIVER_EFUSE_ATTR_ID(42), + TRANSCEIVER_EFUSE_ATTR_ID(43), + TRANSCEIVER_EFUSE_ATTR_ID(44), + TRANSCEIVER_EFUSE_ATTR_ID(45), + TRANSCEIVER_EFUSE_ATTR_ID(46), + TRANSCEIVER_EFUSE_ATTR_ID(47), + TRANSCEIVER_EFUSE_ATTR_ID(48), + /* QSFP-DD LP MODE and RESET */ + TRANSCEIVER_ENABLE_ATTR_ID(49), + TRANSCEIVER_ENABLE_ATTR_ID(50), + TRANSCEIVER_RESET_ATTR_ID(49), + TRANSCEIVER_RESET_ATTR_ID(50), + TRANSCEIVER_LPMODE_ATTR_ID(49), + TRANSCEIVER_LPMODE_ATTR_ID(50), + FPGA_VERSION, + CPLD1_VERSION, + CPLD2_VERSION, +}; + +/*********************************************** + * function declare + * *********************************************/ +static ssize_t status_read(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t status_write(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); + +#define DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(index) \ + static SENSOR_DEVICE_ATTR(module_present_##index, S_IRUGO, status_read, NULL, MODULE_PRESENT_##index); \ + static SENSOR_DEVICE_ATTR(module_rx_los_##index, S_IRUGO, status_read, NULL, MODULE_RX_LOS_##index); \ + static SENSOR_DEVICE_ATTR(module_tx_fault_##index, S_IRUGO, status_read, NULL, MODULE_TX_FAULT_##index); \ + static SENSOR_DEVICE_ATTR(module_tx_disable_##index, S_IRUGO|S_IWUSR, status_read, status_write, MODULE_TX_DISABLE_##index); \ + static SENSOR_DEVICE_ATTR(module_efuse_##index, S_IRUGO|S_IWUSR, status_read, status_write, MODULE_EFUSE_##index) +#define DECLARE_TRANSCEIVER_ATTR(index) \ + &sensor_dev_attr_module_present_##index.dev_attr.attr, \ + &sensor_dev_attr_module_rx_los_##index.dev_attr.attr, \ + &sensor_dev_attr_module_tx_fault_##index.dev_attr.attr, \ + &sensor_dev_attr_module_tx_disable_##index.dev_attr.attr, \ + &sensor_dev_attr_module_efuse_##index.dev_attr.attr + +/* sfp 1-48 transceiver attributes */ +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(1); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(2); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(3); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(4); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(5); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(6); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(7); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(8); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(9); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(10); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(11); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(12); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(13); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(14); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(15); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(16); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(17); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(18); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(19); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(20); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(21); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(22); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(23); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(24); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(25); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(26); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(27); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(28); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(29); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(30); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(31); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(32); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(33); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(34); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(35); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(36); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(37); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(38); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(39); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(40); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(41); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(42); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(43); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(44); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(45); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(46); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(47); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(48); +/* QSFP-DD 49, 50 */ +static SENSOR_DEVICE_ATTR(module_enable_49, S_IRUGO|S_IWUSR, status_read, status_write, MODULE_ENABLE_49); +static SENSOR_DEVICE_ATTR(module_enable_50, S_IRUGO|S_IWUSR, status_read, status_write, MODULE_ENABLE_50); +static SENSOR_DEVICE_ATTR(module_present_49, S_IRUGO, status_read, NULL, MODULE_PRESENT_49); +static SENSOR_DEVICE_ATTR(module_present_50, S_IRUGO, status_read, NULL, MODULE_PRESENT_50); +static SENSOR_DEVICE_ATTR(module_reset_49, S_IRUGO|S_IWUSR, status_read, status_write, MODULE_RESET_49); +static SENSOR_DEVICE_ATTR(module_reset_50, S_IRUGO|S_IWUSR, status_read, status_write, MODULE_RESET_50); +static SENSOR_DEVICE_ATTR(module_lpmode_49, S_IRUGO|S_IWUSR, status_read, status_write, MODULE_LPMODE_49); +static SENSOR_DEVICE_ATTR(module_lpmode_50, S_IRUGO|S_IWUSR, status_read, status_write, MODULE_LPMODE_50); +/* VERSION */ +static SENSOR_DEVICE_ATTR(fpga_version, S_IRUGO, status_read, NULL, FPGA_VERSION); +static SENSOR_DEVICE_ATTR(cpld1_version, S_IRUGO, status_read, NULL, CPLD1_VERSION); +static SENSOR_DEVICE_ATTR(cpld2_version, S_IRUGO, status_read, NULL, CPLD2_VERSION); +static struct attribute *fpga_transceiver_attributes[] = { + DECLARE_TRANSCEIVER_ATTR(1), + DECLARE_TRANSCEIVER_ATTR(2), + DECLARE_TRANSCEIVER_ATTR(3), + DECLARE_TRANSCEIVER_ATTR(4), + DECLARE_TRANSCEIVER_ATTR(5), + DECLARE_TRANSCEIVER_ATTR(6), + DECLARE_TRANSCEIVER_ATTR(7), + DECLARE_TRANSCEIVER_ATTR(8), + DECLARE_TRANSCEIVER_ATTR(9), + DECLARE_TRANSCEIVER_ATTR(10), + DECLARE_TRANSCEIVER_ATTR(11), + DECLARE_TRANSCEIVER_ATTR(12), + DECLARE_TRANSCEIVER_ATTR(13), + DECLARE_TRANSCEIVER_ATTR(14), + DECLARE_TRANSCEIVER_ATTR(15), + DECLARE_TRANSCEIVER_ATTR(16), + DECLARE_TRANSCEIVER_ATTR(17), + DECLARE_TRANSCEIVER_ATTR(18), + DECLARE_TRANSCEIVER_ATTR(19), + DECLARE_TRANSCEIVER_ATTR(20), + DECLARE_TRANSCEIVER_ATTR(21), + DECLARE_TRANSCEIVER_ATTR(22), + DECLARE_TRANSCEIVER_ATTR(23), + DECLARE_TRANSCEIVER_ATTR(24), + DECLARE_TRANSCEIVER_ATTR(25), + DECLARE_TRANSCEIVER_ATTR(26), + DECLARE_TRANSCEIVER_ATTR(27), + DECLARE_TRANSCEIVER_ATTR(28), + DECLARE_TRANSCEIVER_ATTR(29), + DECLARE_TRANSCEIVER_ATTR(30), + DECLARE_TRANSCEIVER_ATTR(31), + DECLARE_TRANSCEIVER_ATTR(32), + DECLARE_TRANSCEIVER_ATTR(33), + DECLARE_TRANSCEIVER_ATTR(34), + DECLARE_TRANSCEIVER_ATTR(35), + DECLARE_TRANSCEIVER_ATTR(36), + DECLARE_TRANSCEIVER_ATTR(37), + DECLARE_TRANSCEIVER_ATTR(38), + DECLARE_TRANSCEIVER_ATTR(39), + DECLARE_TRANSCEIVER_ATTR(40), + DECLARE_TRANSCEIVER_ATTR(41), + DECLARE_TRANSCEIVER_ATTR(42), + DECLARE_TRANSCEIVER_ATTR(43), + DECLARE_TRANSCEIVER_ATTR(44), + DECLARE_TRANSCEIVER_ATTR(45), + DECLARE_TRANSCEIVER_ATTR(46), + DECLARE_TRANSCEIVER_ATTR(47), + DECLARE_TRANSCEIVER_ATTR(48), + &sensor_dev_attr_module_enable_49.dev_attr.attr, + &sensor_dev_attr_module_enable_50.dev_attr.attr, + &sensor_dev_attr_module_present_49.dev_attr.attr, + &sensor_dev_attr_module_present_50.dev_attr.attr, + &sensor_dev_attr_module_reset_49.dev_attr.attr, + &sensor_dev_attr_module_reset_50.dev_attr.attr, + &sensor_dev_attr_module_lpmode_49.dev_attr.attr, + &sensor_dev_attr_module_lpmode_50.dev_attr.attr, + &sensor_dev_attr_fpga_version.dev_attr.attr, + &sensor_dev_attr_cpld1_version.dev_attr.attr, + &sensor_dev_attr_cpld2_version.dev_attr.attr, + NULL +}; + +static const struct attribute_group fpga_port_stat_group = { + .attrs = fpga_transceiver_attributes, +}; + +struct attribute_mapping { + u16 attr_base; + u16 reg; + u8 revert; +}; + +/* Define an array of attribute mappings */ +static struct attribute_mapping attribute_mappings[] = { + [MODULE_PRESENT_1 ... MODULE_PRESENT_8] = {MODULE_PRESENT_1, XCVR_P7_P0_PRESENT_REG, 1}, + [MODULE_PRESENT_9 ... MODULE_PRESENT_16] = {MODULE_PRESENT_9, XCVR_P15_P8_PRESENT_REG, 1}, + [MODULE_PRESENT_17 ... MODULE_PRESENT_24] = {MODULE_PRESENT_17, XCVR_P23_P16_PRESENT_REG, 1}, + [MODULE_PRESENT_25] = {MODULE_PRESENT_25, XCVR_P24_PRESENT_REG, 1}, + [MODULE_PRESENT_26 ... MODULE_PRESENT_33] = {MODULE_PRESENT_26, XCVR_P32_P25_PRESENT_REG, 1}, + [MODULE_PRESENT_34 ... MODULE_PRESENT_41] = {MODULE_PRESENT_34, XCVR_P40_P33_PRESENT_REG, 1}, + [MODULE_PRESENT_42 ... MODULE_PRESENT_48] = {MODULE_PRESENT_42, XCVR_P47_P41_PRESENT_REG, 1}, + + [MODULE_RX_LOS_1 ... MODULE_RX_LOS_8] = {MODULE_RX_LOS_1, XCVR_P7_P0_RXLOS_REG, 0}, + [MODULE_RX_LOS_9 ... MODULE_RX_LOS_16] = {MODULE_RX_LOS_9, XCVR_P15_P8_RXLOS_REG, 0}, + [MODULE_RX_LOS_17 ... MODULE_RX_LOS_24] = {MODULE_RX_LOS_17, XCVR_P23_P16_RXLOS_REG, 0}, + [MODULE_RX_LOS_25] = {MODULE_RX_LOS_25, XCVR_P24_RXLOS_REG, 0}, + [MODULE_RX_LOS_26 ... MODULE_RX_LOS_33] = {MODULE_RX_LOS_26, XCVR_P32_P25_RXLOS_REG, 0}, + [MODULE_RX_LOS_34 ... MODULE_RX_LOS_41] = {MODULE_RX_LOS_34, XCVR_P40_P33_RXLOS_REG, 0}, + [MODULE_RX_LOS_42 ... MODULE_RX_LOS_48] = {MODULE_RX_LOS_42, XCVR_P47_P41_RXLOS_REG, 0}, + + [MODULE_TX_FAULT_1 ... MODULE_TX_FAULT_8] = {MODULE_TX_FAULT_1, XCVR_P7_P0_TXFAULT_REG, 0}, + [MODULE_TX_FAULT_9 ... MODULE_TX_FAULT_16] = {MODULE_TX_FAULT_9, XCVR_P15_P8_TXFAULT_REG, 0}, + [MODULE_TX_FAULT_17 ... MODULE_TX_FAULT_24] = {MODULE_TX_FAULT_17, XCVR_P23_P16_TXFAULT_REG, 0}, + [MODULE_TX_FAULT_25] = {MODULE_TX_FAULT_25, XCVR_P24_TXFAULT_REG, 0}, + [MODULE_TX_FAULT_26 ... MODULE_TX_FAULT_33] = {MODULE_TX_FAULT_26, XCVR_P32_P25_TXFAULT_REG, 0}, + [MODULE_TX_FAULT_34 ... MODULE_TX_FAULT_41] = {MODULE_TX_FAULT_34, XCVR_P40_P33_TXFAULT_REG, 0}, + [MODULE_TX_FAULT_42 ... MODULE_TX_FAULT_48] = {MODULE_TX_FAULT_42, XCVR_P47_P41_TXFAULT_REG, 0}, + + [MODULE_TX_DISABLE_1 ... MODULE_TX_DISABLE_8] = {MODULE_TX_DISABLE_1, XCVR_P7_P0_TXDIS_REG, 0}, + [MODULE_TX_DISABLE_9 ... MODULE_TX_DISABLE_16] = {MODULE_TX_DISABLE_9, XCVR_P15_P8_TXDIS_REG, 0}, + [MODULE_TX_DISABLE_17 ... MODULE_TX_DISABLE_24] = {MODULE_TX_DISABLE_17, XCVR_P23_P16_TXDIS_REG, 0}, + [MODULE_TX_DISABLE_25] = {MODULE_TX_DISABLE_25, XCVR_P24_TXDIS_REG, 0}, + [MODULE_TX_DISABLE_26 ... MODULE_TX_DISABLE_33] = {MODULE_TX_DISABLE_26, XCVR_P32_P25_TXDIS_REG, 0}, + [MODULE_TX_DISABLE_34 ... MODULE_TX_DISABLE_41] = {MODULE_TX_DISABLE_34, XCVR_P40_P33_TXDIS_REG, 0}, + [MODULE_TX_DISABLE_42 ... MODULE_TX_DISABLE_48] = {MODULE_TX_DISABLE_42, XCVR_P47_P41_TXDIS_REG, 0}, + + [MODULE_EFUSE_1 ... MODULE_EFUSE_8] = {MODULE_EFUSE_1, XCVR_P7_P0_EFUSE_REG, 1}, + [MODULE_EFUSE_9 ... MODULE_EFUSE_16] = {MODULE_EFUSE_9, XCVR_P15_P8_EFUSE_REG, 1}, + [MODULE_EFUSE_17 ... MODULE_EFUSE_24] = {MODULE_EFUSE_17, XCVR_P23_P16_EFUSE_REG, 1}, + [MODULE_EFUSE_25] = {MODULE_EFUSE_25, XCVR_P24_EFUSE_REG, 1}, + [MODULE_EFUSE_26 ... MODULE_EFUSE_33] = {MODULE_EFUSE_26, XCVR_P32_P25_EFUSE_REG, 1}, + [MODULE_EFUSE_34 ... MODULE_EFUSE_41] = {MODULE_EFUSE_34, XCVR_P40_P33_EFUSE_REG, 1}, + [MODULE_EFUSE_42 ... MODULE_EFUSE_48] = {MODULE_EFUSE_42, XCVR_P47_P41_EFUSE_REG, 1}, + +}; + +static inline unsigned int fpga_read(void __iomem *addr, u32 spi_mask) +{ + wait_spi(spi_mask, usecs_to_jiffies(20)); + return ioread8(addr); +} + +static inline void fpga_write(void __iomem *addr, u8 val, u32 spi_mask) +{ + wait_spi(spi_mask, usecs_to_jiffies(20)); + iowrite8(val, addr); +} + +static ssize_t status_read(struct device *dev, struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as7927_50x_fpga_data *fpga_ctl = dev_get_drvdata(dev); + ssize_t ret = -EINVAL; + u16 reg; + u8 major, minor, reg_val; + u8 bits_shift; + + switch(attr->index) + { + case FPGA_VERSION: + major = ioread8(fpga_ctl->pci_fpga_dev.data_base_addr0 + FPGA_MAJOR_VER_REG); + minor = ioread8(fpga_ctl->pci_fpga_dev.data_base_addr0 + FPGA_MINOR_VER_REG); + ret = sprintf(buf, "%x.%x\n", major, minor); + break; + case CPLD1_VERSION: + LOCK(&cpld_access_lock); + reg = CPLD1_MAJOR_VER_REG; + major = fpga_read(fpga_ctl->pci_fpga_dev.data_base_addr0 + reg, + SPI_BUSY_MASK_CPLD1); + reg = CPLD1_MINOR_VER_REG; + minor = fpga_read(fpga_ctl->pci_fpga_dev.data_base_addr0 + reg, + SPI_BUSY_MASK_CPLD1); + UNLOCK(&cpld_access_lock); + + ret = sprintf(buf, "%x.%X\n", major, minor); + break; + case CPLD2_VERSION: + LOCK(&cpld_access_lock); + reg = CPLD2_MAJOR_VER_REG; + major = fpga_read(fpga_ctl->pci_fpga_dev.data_base_addr0 + reg, + SPI_BUSY_MASK_CPLD2); + reg = CPLD2_MINOR_VER_REG; + minor = fpga_read(fpga_ctl->pci_fpga_dev.data_base_addr0 + reg, + SPI_BUSY_MASK_CPLD2); + UNLOCK(&cpld_access_lock); + + ret = sprintf(buf, "%x.%x\n", major, minor); + break; + case MODULE_PRESENT_1 ... MODULE_PRESENT_48: + case MODULE_RX_LOS_1 ... MODULE_RX_LOS_48: + case MODULE_TX_FAULT_1 ... MODULE_TX_FAULT_48: + case MODULE_TX_DISABLE_1 ... MODULE_TX_DISABLE_48: + case MODULE_EFUSE_1 ... MODULE_EFUSE_48: + reg = attribute_mappings[attr->index].reg; + LOCK(&cpld_access_lock); + if ((reg & 0xF000) == CPLD1_PCIE_START_OFFSET) { + reg_val = fpga_read(fpga_ctl->pci_fpga_dev.data_base_addr0 + reg, + SPI_BUSY_MASK_CPLD1); + } else if ((reg & 0xF000) == CPLD2_PCIE_START_OFFSET) { + reg_val = fpga_read(fpga_ctl->pci_fpga_dev.data_base_addr0 + reg, + SPI_BUSY_MASK_CPLD2); + } + UNLOCK(&cpld_access_lock); + + bits_shift = attr->index - attribute_mappings[attr->index].attr_base; + reg_val = (reg_val >> bits_shift) & 0x01; + if (attribute_mappings[attr->index].revert) { + reg_val = !reg_val; + } + ret = sprintf(buf, "%u\n", reg_val); + break; + case MODULE_PRESENT_49 ... MODULE_PRESENT_50: + LOCK(&cpld_access_lock); + reg_val = fpga_read(fpga_ctl->pci_fpga_dev.data_base_addr0 + XCVR_P49_P48_QSFPDD_REG, SPI_BUSY_MASK_CPLD2); + UNLOCK(&cpld_access_lock); + bits_shift = attr->index - MODULE_PRESENT_49; + reg_val = !((reg_val >> (4 + bits_shift)) & 0x01); + ret = sprintf(buf, "%u\n", reg_val); + break; + case MODULE_LPMODE_49 ... MODULE_LPMODE_50: + LOCK(&cpld_access_lock); + reg_val = fpga_read(fpga_ctl->pci_fpga_dev.data_base_addr0 + XCVR_P49_P48_QSFPDD_REG, SPI_BUSY_MASK_CPLD2); + UNLOCK(&cpld_access_lock); + bits_shift = attr->index - MODULE_LPMODE_49; + reg_val = (reg_val >> (2 + bits_shift)) & 0x01; + ret = sprintf(buf, "%u\n", reg_val); + break; + case MODULE_RESET_49 ... MODULE_RESET_50: + LOCK(&cpld_access_lock); + reg_val = fpga_read(fpga_ctl->pci_fpga_dev.data_base_addr0 + XCVR_P49_P48_QSFPDD_RST_REG, SPI_BUSY_MASK_CPLD2); + UNLOCK(&cpld_access_lock); + bits_shift = attr->index - MODULE_RESET_49; + reg_val = !((reg_val >> (bits_shift)) & 0x01); + ret = sprintf(buf, "%u\n", reg_val); + break; + case MODULE_ENABLE_49 ... MODULE_ENABLE_50: + LOCK(&cpld_access_lock); + reg_val = fpga_read(fpga_ctl->pci_fpga_dev.data_base_addr0 + XCVR_P49_P48_EN_REG, SPI_BUSY_MASK_CPLD2); + UNLOCK(&cpld_access_lock); + bits_shift = attr->index - MODULE_ENABLE_49; + reg_val = (reg_val >> bits_shift) & 0x01; + ret = sprintf(buf, "%u\n", reg_val); + break; + default: + break; + } + + return ret; +} + +static ssize_t status_write(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as7927_50x_fpga_data *fpga_ctl = dev_get_drvdata(dev); + void __iomem *addr; + int status; + u16 reg; + u8 input; + u8 reg_val, bit_mask, should_set_bit; + u32 spi_mask; + + status = kstrtou8(buf, 10, &input); + if (status) { + return status; + } + + addr = fpga_ctl->pci_fpga_dev.data_base_addr0; + switch(attr->index) + { + + case MODULE_EFUSE_1 ... MODULE_EFUSE_48: + reg = attribute_mappings[attr->index].reg; + if ((reg & 0xF000) == CPLD1_PCIE_START_OFFSET) { + spi_mask = SPI_BUSY_MASK_CPLD1; + } else if ((reg & 0xF000) == CPLD2_PCIE_START_OFFSET) { + spi_mask = SPI_BUSY_MASK_CPLD2; + } + + bit_mask = 0x01 << (attr->index - attribute_mappings[attr->index].attr_base); + should_set_bit = attribute_mappings[attr->index].revert ? !input : input; + + LOCK(&cpld_access_lock); + reg_val = fpga_read(addr + reg, spi_mask); + if (should_set_bit) { + reg_val |= bit_mask; + } else { + reg_val &= ~bit_mask; + } + fpga_write(addr + reg, reg_val, spi_mask); + UNLOCK(&cpld_access_lock); + break; + case MODULE_TX_DISABLE_1 ... MODULE_TX_DISABLE_48: + reg = attribute_mappings[attr->index].reg; + if ((reg & 0xF000) == CPLD1_PCIE_START_OFFSET) { + spi_mask = SPI_BUSY_MASK_CPLD1; + } else if ((reg & 0xF000) == CPLD2_PCIE_START_OFFSET) { + spi_mask = SPI_BUSY_MASK_CPLD2; + } + + bit_mask = 0x01 << (attr->index - attribute_mappings[attr->index].attr_base); + should_set_bit = attribute_mappings[attr->index].revert ? !input : input; + + LOCK(&cpld_access_lock); + reg_val = fpga_read(addr + reg, spi_mask); + if (should_set_bit) { + reg_val |= bit_mask; + } else { + reg_val &= ~bit_mask; + } + fpga_write(addr + reg, reg_val, spi_mask); + UNLOCK(&cpld_access_lock); + break; + case MODULE_LPMODE_49 ... MODULE_LPMODE_50: + LOCK(&cpld_access_lock); + reg = XCVR_P49_P48_QSFPDD_REG; + spi_mask = SPI_BUSY_MASK_CPLD2; + reg_val = fpga_read(addr + reg, spi_mask); + bit_mask = 0x01 << (attr->index - MODULE_LPMODE_49 + 2); + should_set_bit = input; + if (should_set_bit) { + reg_val |= bit_mask; + } else { + reg_val &= ~bit_mask; + } + fpga_write(addr + reg, reg_val, spi_mask); + UNLOCK(&cpld_access_lock); + break; + case MODULE_RESET_49 ... MODULE_RESET_50: + LOCK(&cpld_access_lock); + reg = XCVR_P49_P48_QSFPDD_RST_REG; + spi_mask = SPI_BUSY_MASK_CPLD2; + reg_val = fpga_read(addr + reg, spi_mask); + bit_mask = 0x01 << (attr->index - MODULE_RESET_49); + should_set_bit = !input; + if (should_set_bit) { + reg_val |= bit_mask; + } else { + reg_val &= ~bit_mask; + } + fpga_write(addr + reg, reg_val, spi_mask); + UNLOCK(&cpld_access_lock); + break; + case MODULE_ENABLE_49 ... MODULE_ENABLE_50: + LOCK(&cpld_access_lock); + reg = XCVR_P49_P48_EN_REG; + spi_mask = SPI_BUSY_MASK_CPLD2; + reg_val = fpga_read(addr + reg, spi_mask); + bit_mask = 0x01 << (attr->index - MODULE_ENABLE_49); + should_set_bit = input; + if (should_set_bit) { + reg_val |= bit_mask; + } else { + reg_val &= ~bit_mask; + } + fpga_write(addr + reg, reg_val, spi_mask); + UNLOCK(&cpld_access_lock); + break; + default: + break; + } + + return count; +} + +struct _port_data { + u16 offset; + u16 mask; /* SPI Busy mask : 0x01 --> CPLD1, 0x02 --> CPLD2 */ +}; +/* ============PCIe Bar Offset to I2C Master Mapping============== */ +static const struct _port_data port[PORT_NUM]= { + /* CPLD1 I2C SFP28 port 1-24 */ + {0x2100, SPI_BUSY_MASK_CPLD1},/* 0x2100 - 0x2110 CPLD1 I2C Master SFP28 Port0 */ + {0x2120, SPI_BUSY_MASK_CPLD1},/* 0x2120 - 0x2130 CPLD1 I2C Master SFP28 Port1 */ + {0x2140, SPI_BUSY_MASK_CPLD1},/* 0x2140 - 0x2150 CPLD1 I2C Master SFP28 Port2 */ + {0x2160, SPI_BUSY_MASK_CPLD1},/* 0x2160 - 0x2170 CPLD1 I2C Master SFP28 Port3 */ + {0x2180, SPI_BUSY_MASK_CPLD1},/* 0x2180 - 0x2190 CPLD1 I2C Master SFP28 Port4 */ + {0x21A0, SPI_BUSY_MASK_CPLD1},/* 0x21A0 - 0x21B0 CPLD1 I2C Master SFP28 Port5 */ + {0x21C0, SPI_BUSY_MASK_CPLD1},/* 0x21C0 - 0x21D0 CPLD1 I2C Master SFP28 Port6 */ + {0x21E0, SPI_BUSY_MASK_CPLD1},/* 0x21E0 - 0x21F0 CPLD1 I2C Master SFP28 Port7 */ + {0x2200, SPI_BUSY_MASK_CPLD1},/* 0x2200 - 0x2210 CPLD1 I2C Master SFP28 Port8 */ + {0x2220, SPI_BUSY_MASK_CPLD1},/* 0x2220 - 0x2230 CPLD1 I2C Master SFP28 Port9 */ + {0x2240, SPI_BUSY_MASK_CPLD1},/* 0x2240 - 0x2250 CPLD1 I2C Master SFP28 Port10 */ + {0x2260, SPI_BUSY_MASK_CPLD1},/* 0x2260 - 0x2270 CPLD1 I2C Master SFP28 Port11 */ + {0x2280, SPI_BUSY_MASK_CPLD1},/* 0x2280 - 0x2290 CPLD1 I2C Master SFP28 Port12 */ + {0x22A0, SPI_BUSY_MASK_CPLD1},/* 0x22A0 - 0x22B0 CPLD1 I2C Master SFP28 Port13 */ + {0x22C0, SPI_BUSY_MASK_CPLD1},/* 0x22C0 - 0x22D0 CPLD1 I2C Master SFP28 Port14 */ + {0x22E0, SPI_BUSY_MASK_CPLD1},/* 0x22E0 - 0x22F0 CPLD1 I2C Master SFP28 Port15 */ + {0x2300, SPI_BUSY_MASK_CPLD1},/* 0x2300 - 0x2310 CPLD1 I2C Master SFP28 Port16 */ + {0x2320, SPI_BUSY_MASK_CPLD1},/* 0x2320 - 0x2330 CPLD1 I2C Master SFP28 Port17 */ + {0x2340, SPI_BUSY_MASK_CPLD1},/* 0x2340 - 0x2350 CPLD1 I2C Master SFP28 Port18 */ + {0x2360, SPI_BUSY_MASK_CPLD1},/* 0x2360 - 0x2370 CPLD1 I2C Master SFP28 Port19 */ + {0x2380, SPI_BUSY_MASK_CPLD1},/* 0x2380 - 0x2390 CPLD1 I2C Master SFP28 Port20 */ + {0x23A0, SPI_BUSY_MASK_CPLD1},/* 0x23A0 - 0x23B0 CPLD1 I2C Master SFP28 Port21 */ + {0x23C0, SPI_BUSY_MASK_CPLD1},/* 0x23C0 - 0x23D0 CPLD1 I2C Master SFP28 Port22 */ + {0x23E0, SPI_BUSY_MASK_CPLD1},/* 0x23E0 - 0x23F0 CPLD1 I2C Master SFP28 Port23 */ + {0x2400, SPI_BUSY_MASK_CPLD1},/* 0x2400 - 0x2410 CPLD1 I2C Master SFP28 Port24 */ + /* CPLD2 I2C SFP28 Port25-49 */ + {0x3100, SPI_BUSY_MASK_CPLD2},/* 0x3100 - 0x3110 CPLD1 I2C Master SFP28 Port25 */ + {0x3120, SPI_BUSY_MASK_CPLD2},/* 0x3120 - 0x3130 CPLD1 I2C Master SFP28 Port26 */ + {0x3140, SPI_BUSY_MASK_CPLD2},/* 0x3140 - 0x3150 CPLD1 I2C Master SFP28 Port27 */ + {0x3160, SPI_BUSY_MASK_CPLD2},/* 0x3160 - 0x3170 CPLD1 I2C Master SFP28 Port28 */ + {0x3180, SPI_BUSY_MASK_CPLD2},/* 0x3180 - 0x3190 CPLD1 I2C Master SFP28 Port29 */ + {0x31A0, SPI_BUSY_MASK_CPLD2},/* 0x31A0 - 0x31B0 CPLD1 I2C Master SFP28 Port30 */ + {0x31C0, SPI_BUSY_MASK_CPLD2},/* 0x31C0 - 0x31D0 CPLD1 I2C Master SFP28 Port31 */ + {0x31E0, SPI_BUSY_MASK_CPLD2},/* 0x31E0 - 0x31F0 CPLD1 I2C Master SFP28 Port32 */ + {0x3200, SPI_BUSY_MASK_CPLD2},/* 0x3200 - 0x3210 CPLD1 I2C Master SFP28 Port33 */ + {0x3220, SPI_BUSY_MASK_CPLD2},/* 0x3220 - 0x3230 CPLD1 I2C Master SFP28 Port34 */ + {0x3240, SPI_BUSY_MASK_CPLD2},/* 0x3240 - 0x3250 CPLD1 I2C Master SFP28 Port35 */ + {0x3260, SPI_BUSY_MASK_CPLD2},/* 0x3260 - 0x3270 CPLD1 I2C Master SFP28 Port36 */ + {0x3280, SPI_BUSY_MASK_CPLD2},/* 0x3280 - 0x3290 CPLD1 I2C Master SFP28 Port37 */ + {0x32A0, SPI_BUSY_MASK_CPLD2},/* 0x32A0 - 0x32B0 CPLD1 I2C Master SFP28 Port38 */ + {0x32C0, SPI_BUSY_MASK_CPLD2},/* 0x32C0 - 0x32D0 CPLD1 I2C Master SFP28 Port39 */ + {0x32E0, SPI_BUSY_MASK_CPLD2},/* 0x32E0 - 0x32F0 CPLD1 I2C Master SFP28 Port40 */ + {0x3300, SPI_BUSY_MASK_CPLD2},/* 0x3300 - 0x3310 CPLD1 I2C Master SFP28 Port41 */ + {0x3320, SPI_BUSY_MASK_CPLD2},/* 0x3320 - 0x3330 CPLD1 I2C Master SFP28 Port42 */ + {0x3340, SPI_BUSY_MASK_CPLD2},/* 0x3340 - 0x3350 CPLD1 I2C Master SFP28 Port43 */ + {0x3360, SPI_BUSY_MASK_CPLD2},/* 0x3360 - 0x3370 CPLD1 I2C Master SFP28 Port44 */ + {0x3380, SPI_BUSY_MASK_CPLD2},/* 0x3380 - 0x3390 CPLD1 I2C Master SFP28 Port45 */ + {0x33A0, SPI_BUSY_MASK_CPLD2},/* 0x33A0 - 0x33B0 CPLD1 I2C Master SFP28 Port46 */ + {0x33C0, SPI_BUSY_MASK_CPLD2},/* 0x33C0 - 0x33D0 CPLD1 I2C Master SFP28 Port47 */ + {0x3400, SPI_BUSY_MASK_CPLD2},/* 0x3400 - 0x3410 CPLD1 I2C Master SFP28 Port48 */ + {0x3420, SPI_BUSY_MASK_CPLD2},/* 0x3420 - 0x3430 CPLD1 I2C Master SFP28 Port49 */ +}; + +static struct ocores_i2c_platform_data as7927_50x_platform_data = { + .reg_io_width = 1, + .reg_shift = 2, + /* + * PRER_L and PRER_H are calculated based on clock_khz and bus_khz + * in i2c-ocores.c:ocores_init. + */ +#if 1 + /* SCL 400KHZ in FPGA spec. => PRER_L = 0x0B, PRER_H = 0x00 */ + .clock_khz = 24000, + .bus_khz = 400, +#else + /* SCL 100KHZ in FPGA spec. => PRER_L = 0x2F, PRER_H = 0x00 */ + .clock_khz = 24000, + .bus_khz = 100, +#endif +}; + +struct platform_device *ocore_i2c_device_add(unsigned int id, unsigned long bar_base, + unsigned int offset) +{ + struct resource res = DEFINE_RES_MEM(bar_base + offset, 0x20); + struct platform_device *pdev; + int err; + + pdev = platform_device_alloc(OCORES_I2C_DRVNAME, id); + if (!pdev) { + err = -ENOMEM; + pcie_err("Port%u device allocation failed (%d)\n", (id & 0xFF), err); + goto exit; + } + + err = platform_device_add_resources(pdev, &res, 1); + if (err) { + pcie_err("Port%u device resource addition failed (%d)\n", (id & 0xFF), err); + goto exit_device_put; + } + + err = platform_device_add_data(pdev, &as7927_50x_platform_data, + sizeof(struct ocores_i2c_platform_data)); + if (err) { + pcie_err("Port%u platform data allocation failed (%d)\n", (id & 0xFF), err); + goto exit_device_put; + } + + err = platform_device_add(pdev); + if (err) { + pcie_err("Port%u device addition failed (%d)\n", (id & 0xFF), err); + goto exit_device_put; + } + + return pdev; + +exit_device_put: + platform_device_put(pdev); +exit: + return NULL; +} + +static int as7927_50x_pcie_fpga_stat_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct as7927_50x_fpga_data *fpga_ctl; + struct pci_dev *pcidev; + struct resource *ret; + int i; + int status = 0, err = 0; + unsigned long bar_base; + + fpga_ctl = devm_kzalloc(dev, sizeof(struct as7927_50x_fpga_data), GFP_KERNEL); + if (!fpga_ctl) { + return -ENOMEM; + } + platform_set_drvdata(pdev, fpga_ctl); + + pcidev = pci_get_device(FPGA_PCI_VENDOR_ID, FPGA_PCI_DEVICE_ID, NULL); + if (!pcidev) { + dev_err(dev, "Cannot found PCI device(%x:%x)\n", + FPGA_PCI_VENDOR_ID, FPGA_PCI_DEVICE_ID); + return -ENODEV; + } + fpga_ctl->pci_fpga_dev.pci_dev = pcidev; + + err = pci_enable_device(pcidev); + if (err != 0) { + dev_err(dev, "Cannot enable PCI device(%x:%x)\n", + FPGA_PCI_VENDOR_ID, FPGA_PCI_DEVICE_ID); + status = -ENODEV; + goto exit_pci_disable; + } + /* enable PCI bus-mastering */ + pci_set_master(pcidev); + /* + * Cannot use 'pci_request_regions(pcidev, DRVNAME)' + * to request all Region 0 because another + * address will be allocated by the i2c-ocores.ko. + */ + fpga_ctl->pci_fpga_dev.data_base_addr0 = pci_iomap(pcidev, BAR0_NUM, 0); + if (fpga_ctl->pci_fpga_dev.data_base_addr0 == NULL) { + dev_err(dev, "Failed to map BAR0\n"); + status = -EIO; + goto exit_pci_disable; + } + /* FPGA */ + fpga_ctl->pci_fpga_dev.data_region0 = pci_resource_start(pcidev, BAR0_NUM); + ret = request_mem_region(fpga_ctl->pci_fpga_dev.data_region0, REGION_LEN, DRVNAME"_fpga"); + if (ret == NULL) { + dev_err(dev, "[%s] cannot request region\n", DRVNAME"_fpga"); + status = -EIO; + goto exit_pci_iounmap0; + } + dev_info(dev, "(BAR%d resource: Start=0x%lx, Length=0x%x)", BAR0_NUM, + (unsigned long)fpga_ctl->pci_fpga_dev.data_region0, REGION_LEN); + /* CPLD1 */ + fpga_ctl->pci_fpga_dev.data_region1 = pci_resource_start(pcidev, BAR0_NUM) + CPLD1_PCIE_START_OFFSET; + ret = request_mem_region(fpga_ctl->pci_fpga_dev.data_region1, REGION_LEN, DRVNAME"_cpld1"); + if (ret == NULL) { + dev_err(dev, "[%s] cannot request region\n", DRVNAME"_cpld1"); + status = -EIO; + goto exit_pci_iounmap1; + } + dev_info(dev, "(BAR%d resource: Start=0x%lx, Length=0x%x)", BAR0_NUM, + (unsigned long)fpga_ctl->pci_fpga_dev.data_region1, REGION_LEN); + /* CPLD2 */ + fpga_ctl->pci_fpga_dev.data_region2 = pci_resource_start(pcidev, BAR0_NUM) + CPLD2_PCIE_START_OFFSET; + ret = request_mem_region(fpga_ctl->pci_fpga_dev.data_region2, REGION_LEN, DRVNAME"_cpld2"); + if (ret == NULL) { + dev_err(dev, "[%s] cannot request region\n", DRVNAME"_cpld2"); + status = -EIO; + goto exit_pci_iounmap2; + } + dev_info(dev, "(BAR%d resource: Start=0x%lx, Length=0x%x)", BAR0_NUM, + (unsigned long)fpga_ctl->pci_fpga_dev.data_region2, REGION_LEN); + + /* Create I2C ocore devices first, then create the FPGA sysfs. + * To prevent the application from accessing an ocore device + * that has not been fully created due to the port status + * being present. + */ + + /* + * Create ocore_i2c device for OSFP EEPROM + */ + for (i = 0; i < PORT_NUM; i++) { + bar_base = pci_resource_start(pcidev, BAR0_NUM); + fpga_ctl->pci_fpga_dev.fpga_i2c[i] = + ocore_i2c_device_add((i | (port[i].mask << 8)), bar_base, port[i].offset); + if (IS_ERR(fpga_ctl->pci_fpga_dev.fpga_i2c[i])) { + status = PTR_ERR(fpga_ctl->pci_fpga_dev.fpga_i2c[i]); + dev_err(dev, "rc:%d, unload Port%u[0x%ux] device\n", + status, i, port[i].offset); + goto exit_ocores_device; + } + } + + status = sysfs_create_group(&pdev->dev.kobj, &fpga_port_stat_group); + if (status) { + goto exit_ocores_device; + } + + return 0; + +exit_ocores_device: + while (i > 0) { + i--; + platform_device_unregister(fpga_ctl->pci_fpga_dev.fpga_i2c[i]); + } + release_mem_region(fpga_ctl->pci_fpga_dev.data_region2, REGION_LEN); +exit_pci_iounmap2: + release_mem_region(fpga_ctl->pci_fpga_dev.data_region1, REGION_LEN); +exit_pci_iounmap1: + release_mem_region(fpga_ctl->pci_fpga_dev.data_region0, REGION_LEN); +exit_pci_iounmap0: + pci_iounmap(fpga_ctl->pci_fpga_dev.pci_dev, fpga_ctl->pci_fpga_dev.data_base_addr0); +exit_pci_disable: + pci_disable_device(fpga_ctl->pci_fpga_dev.pci_dev); + + return status; +} + +static int as7927_50x_pcie_fpga_stat_remove(struct platform_device *pdev) +{ + struct as7927_50x_fpga_data *fpga_ctl = platform_get_drvdata(pdev); + + if (pci_is_enabled(fpga_ctl->pci_fpga_dev.pci_dev)) { + int i; + sysfs_remove_group(&pdev->dev.kobj, &fpga_port_stat_group); + /* Unregister ocore_i2c device */ + for (i = 0; i < PORT_NUM; i++) { + platform_device_unregister(fpga_ctl->pci_fpga_dev.fpga_i2c[i]); + } + release_mem_region(fpga_ctl->pci_fpga_dev.data_region2, REGION_LEN); + release_mem_region(fpga_ctl->pci_fpga_dev.data_region1, REGION_LEN); + release_mem_region(fpga_ctl->pci_fpga_dev.data_region0, REGION_LEN); + pci_iounmap(fpga_ctl->pci_fpga_dev.pci_dev, fpga_ctl->pci_fpga_dev.data_base_addr0); + pci_disable_device(fpga_ctl->pci_fpga_dev.pci_dev); + } + + return 0; +} + +static struct platform_driver pcie_fpga_port_stat_driver = { + .probe = as7927_50x_pcie_fpga_stat_probe, + .remove = as7927_50x_pcie_fpga_stat_remove, + .driver = { + .owner = THIS_MODULE, + .name = DRVNAME, + }, +}; + +static int __init as7927_50x_pcie_fpga_init(void) +{ + int status = 0; + + /* + * Create FPGA platform driver and device + */ + status = platform_driver_register(&pcie_fpga_port_stat_driver); + if (status < 0) { + return status; + } + + pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(pdev)) { + status = PTR_ERR(pdev); + goto exit_pci; + } + + return status; + +exit_pci: + platform_driver_unregister(&pcie_fpga_port_stat_driver); + + return status; +} + +static void __exit as7927_50x_pcie_fpga_exit(void) +{ + platform_device_unregister(pdev); + platform_driver_unregister(&pcie_fpga_port_stat_driver); +} + + +module_init(as7927_50x_pcie_fpga_init); +module_exit(as7927_50x_pcie_fpga_exit); + +MODULE_AUTHOR("Willy Liu "); +MODULE_DESCRIPTION("AS7927-50X FPGA via PCIE"); +MODULE_LICENSE("GPL"); diff --git a/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-i2c-ocores.c b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-i2c-ocores.c new file mode 100644 index 0000000000..6218448dcb --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-i2c-ocores.c @@ -0,0 +1,980 @@ + +// SPDX-License-Identifier: GPL-2.0 +/* + * i2c-ocores.c: I2C bus driver for OpenCores I2C controller + * (https://opencores.org/project/i2c/overview) + * + * Peter Korsgaard + * + * Support for the GRLIB port of the controller by + * Andreas Larsson + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 'process_lock' exists because ocores_process() and ocores_process_timeout() + * can't run in parallel. + */ +struct ocores_i2c { + void __iomem *base; + int iobase; + u32 reg_shift; + u32 reg_io_width; + unsigned long flags; + wait_queue_head_t wait; + struct i2c_adapter adap; + struct i2c_msg *msg; + int pos; + int nmsgs; + int state; /* see STATE_ */ + spinlock_t process_lock; + struct clk *clk; + int ip_clock_khz; + int bus_clock_khz; + void (*setreg)(struct ocores_i2c *i2c, int reg, u8 value); + u8 (*getreg)(struct ocores_i2c *i2c, int reg); +}; + +/* registers */ +#define OCI2C_PRELOW 0 +#define OCI2C_PREHIGH 1 +#define OCI2C_CONTROL 2 +#define OCI2C_DATA 3 +#define OCI2C_CMD 4 /* write only */ +#define OCI2C_STATUS 4 /* read only, same address as OCI2C_CMD */ + +#define OCI2C_CTRL_IEN 0x40 +#define OCI2C_CTRL_EN 0x80 + +#define OCI2C_CMD_START 0x91 +#define OCI2C_CMD_STOP 0x41 +#define OCI2C_CMD_READ 0x21 +#define OCI2C_CMD_WRITE 0x11 +#define OCI2C_CMD_READ_ACK 0x21 +#define OCI2C_CMD_READ_NACK 0x29 +#define OCI2C_CMD_IACK 0x01 + +#define OCI2C_STAT_IF 0x01 +#define OCI2C_STAT_TIP 0x02 +#define OCI2C_STAT_ARBLOST 0x20 +#define OCI2C_STAT_BUSY 0x40 +#define OCI2C_STAT_NACK 0x80 + +#define STATE_DONE 0 +#define STATE_START 1 +#define STATE_WRITE 2 +#define STATE_READ 3 +#define STATE_ERROR 4 + +#define TYPE_OCORES 0 +#define TYPE_GRLIB 1 +#define TYPE_SIFIVE_REV0 2 + +#define OCORES_FLAG_BROKEN_IRQ BIT(1) /* Broken IRQ for FU540-C000 SoC */ + +static unsigned int timeout = 1; +module_param(timeout , uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(timeout, "Tiemout for ocores_poll_wait, in unit of milliseconds."); + +static unsigned int debug = 1; +module_param(debug , uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(debug, "Enable or disable debug message. 1 -> enable, 0 -> disable"); + +spinlock_t cpld_access_lock; +EXPORT_SYMBOL(cpld_access_lock); + +#define LOCK(lock) \ +do { \ + spin_lock(lock); \ +} while (0) + +#define UNLOCK(lock) \ +do { \ + spin_unlock(lock); \ +} while (0) + + +#define SPI_BUSY_ADDR (0xb3c00000 + 0x33) +#define IOREMAP_SIZE (0x01) +static void __iomem *spi_busy_reg=NULL; +int wait_spi(u32 mask, u8 times) { + u32 data; + u32 spi_busy_flag = 0; + u8 max_times = 0; + max_times = times; + /* pr_info("CPLD %u, Will time-out at jiffie %lu\n", cpld_id,timeout); */ + if (!spi_busy_reg) { + return -EFAULT; + } + + while(max_times) { + data = ioread8(spi_busy_reg); + if (!((data& 0xFF) & mask)) + { + /* spi flag is normal*/ + if (spi_busy_flag) + break; + } + else + { + spi_busy_flag = 1; + } + + usleep_range(10, 11); + max_times--; + + if(max_times == 0) + { + if(!spi_busy_flag) + break; + if (debug) { + pr_warn("spi_busy_flag %d as max_times is 0 \n", spi_busy_flag); + } + return -ETIMEDOUT; + } + } + + return 0; +} +EXPORT_SYMBOL(wait_spi); + +static int wait_cpld(struct ocores_i2c *i2c, u8 times) { + struct platform_device *pdev; + struct device *dev; + + if (!i2c->adap.dev.parent) { + return -EFAULT; + } + + /* Get SPI Busy mask from pdev->id */ + dev = i2c->adap.dev.parent; + pdev = container_of(dev, struct platform_device, dev); + wait_spi((pdev->id & 0xFF00) >> 8, times); + + return 0; +} + +static void oc_setreg_8(struct ocores_i2c *i2c, int reg, u8 value) +{ + iowrite8(value, i2c->base + (reg << i2c->reg_shift)); +} + +static void oc_setreg_16(struct ocores_i2c *i2c, int reg, u8 value) +{ + iowrite16(value, i2c->base + (reg << i2c->reg_shift)); +} + +static void oc_setreg_32(struct ocores_i2c *i2c, int reg, u8 value) +{ + iowrite32(value, i2c->base + (reg << i2c->reg_shift)); +} + +static void oc_setreg_16be(struct ocores_i2c *i2c, int reg, u8 value) +{ + iowrite16be(value, i2c->base + (reg << i2c->reg_shift)); +} + +static void oc_setreg_32be(struct ocores_i2c *i2c, int reg, u8 value) +{ + iowrite32be(value, i2c->base + (reg << i2c->reg_shift)); +} + +static inline u8 oc_getreg_8(struct ocores_i2c *i2c, int reg) +{ + return ioread8(i2c->base + (reg << i2c->reg_shift)); +} + +static inline u8 oc_getreg_16(struct ocores_i2c *i2c, int reg) +{ + return ioread16(i2c->base + (reg << i2c->reg_shift)); +} + +static inline u8 oc_getreg_32(struct ocores_i2c *i2c, int reg) +{ + return ioread32(i2c->base + (reg << i2c->reg_shift)); +} + +static inline u8 oc_getreg_16be(struct ocores_i2c *i2c, int reg) +{ + return ioread16be(i2c->base + (reg << i2c->reg_shift)); +} + +static inline u8 oc_getreg_32be(struct ocores_i2c *i2c, int reg) +{ + return ioread32be(i2c->base + (reg << i2c->reg_shift)); +} + +static void oc_setreg_io_8(struct ocores_i2c *i2c, int reg, u8 value) +{ + outb(value, i2c->iobase + reg); +} + +static inline u8 oc_getreg_io_8(struct ocores_i2c *i2c, int reg) +{ + return inb(i2c->iobase + reg); +} + +static inline void oc_setreg(struct ocores_i2c *i2c, int reg, u8 value) +{ + wait_cpld(i2c, 6); + i2c->setreg(i2c, reg, value); +} + +static inline u8 oc_getreg(struct ocores_i2c *i2c, int reg) +{ + wait_cpld(i2c, 6); + return i2c->getreg(i2c, reg); +} + +static void ocores_process(struct ocores_i2c *i2c, u8 stat) +{ + struct i2c_msg *msg = i2c->msg; + unsigned long flags; + struct device *dev = i2c->adap.dev.parent; + + /* + * If we spin here is because we are in timeout, so we are going + * to be in STATE_ERROR. See ocores_process_timeout() + */ + spin_lock_irqsave(&i2c->process_lock, flags); + if ((i2c->state == STATE_DONE) || (i2c->state == STATE_ERROR)) { + /* stop has been sent */ + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK); + wake_up(&i2c->wait); + goto out; + } + + /* error? */ + if (stat & OCI2C_STAT_ARBLOST) { + i2c->state = STATE_ERROR; + if (debug) { + dev_warn(dev, "I2C %s arbitration lost", i2c->adap.name); + } + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); + goto out; + } + + if ((i2c->state == STATE_START) || (i2c->state == STATE_WRITE)) { + i2c->state = + (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE; + + if (stat & OCI2C_STAT_NACK) { + i2c->state = STATE_ERROR; + if (debug) { + dev_warn(dev, "I2C %s, no ACK from slave 0x%02x", + i2c->adap.name, msg->addr); + } + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); + goto out; + } + } else { + msg->buf[i2c->pos++] = oc_getreg(i2c, OCI2C_DATA); + } + + /* end of msg? */ + if (i2c->pos == msg->len) { + i2c->nmsgs--; + i2c->msg++; + i2c->pos = 0; + msg = i2c->msg; + + if (i2c->nmsgs) { /* end? */ + /* send start? */ + if (!(msg->flags & I2C_M_NOSTART)) { + u8 addr = i2c_8bit_addr_from_msg(msg); + + i2c->state = STATE_START; + + oc_setreg(i2c, OCI2C_DATA, addr); + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); + goto out; + } + i2c->state = (msg->flags & I2C_M_RD) + ? STATE_READ : STATE_WRITE; + } else { + i2c->state = STATE_DONE; + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); + goto out; + } + } + + if (i2c->state == STATE_READ) { + oc_setreg(i2c, OCI2C_CMD, i2c->pos == (msg->len-1) ? + OCI2C_CMD_READ_NACK : OCI2C_CMD_READ_ACK); + } else { + oc_setreg(i2c, OCI2C_DATA, msg->buf[i2c->pos++]); + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_WRITE); + } + +out: + spin_unlock_irqrestore(&i2c->process_lock, flags); + +} + +static irqreturn_t ocores_isr(int irq, void *dev_id) +{ + struct ocores_i2c *i2c = dev_id; + u8 stat = oc_getreg(i2c, OCI2C_STATUS); + + if (i2c->flags & OCORES_FLAG_BROKEN_IRQ) { + if ((stat & OCI2C_STAT_IF) && !(stat & OCI2C_STAT_BUSY)) + return IRQ_NONE; + } else if (!(stat & OCI2C_STAT_IF)) { + return IRQ_NONE; + } + ocores_process(i2c, stat); + + return IRQ_HANDLED; +} + +/** + * Process timeout event + * @i2c: ocores I2C device instance + */ +static void ocores_process_timeout(struct ocores_i2c *i2c) +{ + unsigned long flags; + + spin_lock_irqsave(&i2c->process_lock, flags); + i2c->state = STATE_ERROR; + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); + spin_unlock_irqrestore(&i2c->process_lock, flags); +} + +/** + * Wait until something change in a given register + * @i2c: ocores I2C device instance + * @reg: register to query + * @mask: bitmask to apply on register value + * @val: expected result + * @timeout: timeout in jiffies + * + * Timeout is necessary to avoid to stay here forever when the chip + * does not answer correctly. + * + * Return: 0 on success, -ETIMEDOUT on timeout + */ +static int ocores_wait(struct ocores_i2c *i2c, + int reg, u8 mask, u8 val, + const unsigned long timeout) +{ + unsigned long j; + + j = jiffies + timeout; + while (1) { + u8 status = oc_getreg(i2c, reg); + + if ((status & mask) == val) + break; + + if (time_after(jiffies, j)) + return -ETIMEDOUT; + } + return 0; +} + +/** + * Wait until is possible to process some data + * @i2c: ocores I2C device instance + * + * Used when the device is in polling mode (interrupts disabled). + * + * Return: 0 on success, -ETIMEDOUT on timeout + */ +static int ocores_poll_wait(struct ocores_i2c *i2c) +{ + u8 mask; + int err; + + if (i2c->state == STATE_DONE || i2c->state == STATE_ERROR) { + /* transfer is over */ + mask = OCI2C_STAT_BUSY; + } else { + /* on going transfer */ + mask = OCI2C_STAT_TIP; + /* + * We wait for the data to be transferred (8bit), + * then we start polling on the ACK/NACK bit + */ + udelay((8 * 1000) / i2c->bus_clock_khz); + } + + /* + * once we are here we expect to get the expected result immediately + * so if after 1ms we timeout then something is broken. + */ + err = ocores_wait(i2c, OCI2C_STATUS, mask, 0, msecs_to_jiffies(timeout)); + if (err) { + if (debug) { + dev_warn(i2c->adap.dev.parent, + "%s: STATUS timeout, bit 0x%x did not clear in %ums(msecs_to_jiffies(%u)=%lu)\n", + __func__, mask, timeout, timeout, msecs_to_jiffies(timeout)); + } + } + return err; +} + +/** + * It handles an IRQ-less transfer + * @i2c: ocores I2C device instance + * + * Even if IRQ are disabled, the I2C OpenCore IP behavior is exactly the same + * (only that IRQ are not produced). This means that we can re-use entirely + * ocores_isr(), we just add our polling code around it. + * + * It can run in atomic context + * + * Return: 0 on success, -ETIMEDOUT on timeout + */ +static int ocores_process_polling(struct ocores_i2c *i2c) +{ + irqreturn_t ret; + int err; + + while (1) { + err = ocores_poll_wait(i2c); + if (err) { + break; /* timeout */ + } + + ret = ocores_isr(-1, i2c); + if (ret == IRQ_NONE) + break; /* all messages have been transferred */ + else { + if (i2c->flags & OCORES_FLAG_BROKEN_IRQ) + if (i2c->state == STATE_DONE) + break; + } + } + + return err; +} + +static int ocores_xfer_core(struct ocores_i2c *i2c, + struct i2c_msg *msgs, int num, + bool polling) +{ + int ret = 0; + u8 ctrl; + + LOCK(&cpld_access_lock); + + ctrl = oc_getreg(i2c, OCI2C_CONTROL); + if (polling) + oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~OCI2C_CTRL_IEN); + else + oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_IEN); + + i2c->msg = msgs; + i2c->pos = 0; + i2c->nmsgs = num; + i2c->state = STATE_START; + + oc_setreg(i2c, OCI2C_DATA, i2c_8bit_addr_from_msg(i2c->msg)); + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); + + if (polling) { + ret = ocores_process_polling(i2c); + } else { + if (wait_event_timeout(i2c->wait, + (i2c->state == STATE_ERROR) || + (i2c->state == STATE_DONE), HZ) == 0) + ret = -ETIMEDOUT; + } + if (ret) { + ocores_process_timeout(i2c); + UNLOCK(&cpld_access_lock); + return ret; + } + + UNLOCK(&cpld_access_lock); + return (i2c->state == STATE_DONE) ? num : -EIO; +} + +static int ocores_xfer_polling(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + return ocores_xfer_core(i2c_get_adapdata(adap), msgs, num, true); +} + +static int ocores_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + return ocores_xfer_core(i2c_get_adapdata(adap), msgs, num, false); +} + +static int ocores_init(struct device *dev, struct ocores_i2c *i2c) +{ + struct device *org; + int prescale; + int diff; + u8 ctrl; + + /* Temporary assignment for checking SPI Busy status. */ + org = i2c->adap.dev.parent; + i2c->adap.dev.parent = dev; + + LOCK(&cpld_access_lock); + ctrl = oc_getreg(i2c, OCI2C_CONTROL); + + /* make sure the device is disabled */ + ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN); + oc_setreg(i2c, OCI2C_CONTROL, ctrl); + UNLOCK(&cpld_access_lock); + + prescale = (i2c->ip_clock_khz / (5 * i2c->bus_clock_khz)) - 1; + prescale = clamp(prescale, 0, 0xffff); + + diff = i2c->ip_clock_khz / (5 * (prescale + 1)) - i2c->bus_clock_khz; + if (abs(diff) > i2c->bus_clock_khz / 10) { + i2c->adap.dev.parent = org; + dev_err(dev, + "Unsupported clock settings: core: %d KHz, bus: %d KHz\n", + i2c->ip_clock_khz, i2c->bus_clock_khz); + return -EINVAL; + } + + dev_info(dev, "OCI2C_PRELOW=0x%02x OCI2C_PREHIGH=0x%02x\n", + prescale & 0xff, prescale >> 8); + LOCK(&cpld_access_lock); + oc_setreg(i2c, OCI2C_PRELOW, prescale & 0xff); + oc_setreg(i2c, OCI2C_PREHIGH, prescale >> 8); + + /* Init the device */ + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK); + oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_EN); + UNLOCK(&cpld_access_lock); + + i2c->adap.dev.parent = org; + + return 0; +} + + +static u32 ocores_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm ocores_algorithm = { + .master_xfer = ocores_xfer, + .master_xfer_atomic = ocores_xfer_polling, + .functionality = ocores_func, +}; + +static const struct i2c_adapter ocores_adapter = { + .owner = THIS_MODULE, + .name = "i2c-ocores", + .class = I2C_CLASS_DEPRECATED, + .algo = &ocores_algorithm, +}; + +static const struct of_device_id ocores_i2c_match[] = { + { + .compatible = "opencores,i2c-ocores", + .data = (void *)TYPE_OCORES, + }, + { + .compatible = "aeroflexgaisler,i2cmst", + .data = (void *)TYPE_GRLIB, + }, + { + .compatible = "sifive,fu540-c000-i2c", + .data = (void *)TYPE_SIFIVE_REV0, + }, + { + .compatible = "sifive,i2c0", + .data = (void *)TYPE_SIFIVE_REV0, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ocores_i2c_match); + +#ifdef CONFIG_OF +/* + * Read and write functions for the GRLIB port of the controller. Registers are + * 32-bit big endian and the PRELOW and PREHIGH registers are merged into one + * register. The subsequent registers have their offsets decreased accordingly. + */ +static u8 oc_getreg_grlib(struct ocores_i2c *i2c, int reg) +{ + u32 rd; + int rreg = reg; + + if (reg != OCI2C_PRELOW) + rreg--; + rd = ioread32be(i2c->base + (rreg << i2c->reg_shift)); + if (reg == OCI2C_PREHIGH) + return (u8)(rd >> 8); + else + return (u8)rd; +} + +static void oc_setreg_grlib(struct ocores_i2c *i2c, int reg, u8 value) +{ + u32 curr, wr; + int rreg = reg; + + if (reg != OCI2C_PRELOW) + rreg--; + if (reg == OCI2C_PRELOW || reg == OCI2C_PREHIGH) { + curr = ioread32be(i2c->base + (rreg << i2c->reg_shift)); + if (reg == OCI2C_PRELOW) + wr = (curr & 0xff00) | value; + else + wr = (((u32)value) << 8) | (curr & 0xff); + } else { + wr = value; + } + iowrite32be(wr, i2c->base + (rreg << i2c->reg_shift)); +} + +static int ocores_i2c_of_probe(struct platform_device *pdev, + struct ocores_i2c *i2c) +{ + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match; + u32 val; + u32 clock_frequency; + bool clock_frequency_present; + + if (of_property_read_u32(np, "reg-shift", &i2c->reg_shift)) { + /* no 'reg-shift', check for deprecated 'regstep' */ + if (!of_property_read_u32(np, "regstep", &val)) { + if (!is_power_of_2(val)) { + dev_err(&pdev->dev, "invalid regstep %d\n", + val); + return -EINVAL; + } + i2c->reg_shift = ilog2(val); + dev_warn(&pdev->dev, + "regstep property deprecated, use reg-shift\n"); + } + } + + clock_frequency_present = !of_property_read_u32(np, "clock-frequency", + &clock_frequency); + i2c->bus_clock_khz = 100; + + i2c->clk = devm_clk_get(&pdev->dev, NULL); + + if (!IS_ERR(i2c->clk)) { + int ret = clk_prepare_enable(i2c->clk); + + if (ret) { + dev_err(&pdev->dev, + "clk_prepare_enable failed: %d\n", ret); + return ret; + } + i2c->ip_clock_khz = clk_get_rate(i2c->clk) / 1000; + if (clock_frequency_present) + i2c->bus_clock_khz = clock_frequency / 1000; + } + + if (i2c->ip_clock_khz == 0) { + if (of_property_read_u32(np, "opencores,ip-clock-frequency", + &val)) { + if (!clock_frequency_present) { + dev_err(&pdev->dev, + "Missing required parameter 'opencores,ip-clock-frequency'\n"); + clk_disable_unprepare(i2c->clk); + return -ENODEV; + } + i2c->ip_clock_khz = clock_frequency / 1000; + dev_warn(&pdev->dev, + "Deprecated usage of the 'clock-frequency' property, please update to 'opencores,ip-clock-frequency'\n"); + } else { + i2c->ip_clock_khz = val / 1000; + if (clock_frequency_present) + i2c->bus_clock_khz = clock_frequency / 1000; + } + } + + of_property_read_u32(pdev->dev.of_node, "reg-io-width", + &i2c->reg_io_width); + + match = of_match_node(ocores_i2c_match, pdev->dev.of_node); + if (match && (long)match->data == TYPE_GRLIB) { + dev_dbg(&pdev->dev, "GRLIB variant of i2c-ocores\n"); + i2c->setreg = oc_setreg_grlib; + i2c->getreg = oc_getreg_grlib; + } + + return 0; +} +#else +#define ocores_i2c_of_probe(pdev, i2c) -ENODEV +#endif + +static int ocores_i2c_probe(struct platform_device *pdev) +{ + struct ocores_i2c *i2c; + struct ocores_i2c_platform_data *pdata; + const struct of_device_id *match; + struct resource *res; + int irq; + int ret; + int i; + + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + spin_lock_init(&i2c->process_lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) { + i2c->base = devm_ioremap_resource(&pdev->dev, res); + dev_info(&pdev->dev, "Resouce start:0x%llx, end:0x%llx", res->start, res->end); + if (IS_ERR(i2c->base)) + return PTR_ERR(i2c->base); + } else { + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) + return -EINVAL; + i2c->iobase = res->start; + if (!devm_request_region(&pdev->dev, res->start, + resource_size(res), + pdev->name)) { + dev_err(&pdev->dev, "Can't get I/O resource.\n"); + return -EBUSY; + } + i2c->setreg = oc_setreg_io_8; + i2c->getreg = oc_getreg_io_8; + } + + pdata = dev_get_platdata(&pdev->dev); + if (pdata) { + i2c->reg_shift = pdata->reg_shift; + i2c->reg_io_width = pdata->reg_io_width; + i2c->ip_clock_khz = pdata->clock_khz; + dev_info(&pdev->dev, "Write %d KHz, ioWidth:%d, shift:%d", i2c->ip_clock_khz, pdata->reg_io_width ,pdata->reg_shift); + if (pdata->bus_khz) + i2c->bus_clock_khz = pdata->bus_khz; + else + i2c->bus_clock_khz = 100; + } else { + ret = ocores_i2c_of_probe(pdev, i2c); + if (ret) + return ret; + } + + if (i2c->reg_io_width == 0) + i2c->reg_io_width = 1; /* Set to default value */ + + if (!i2c->setreg || !i2c->getreg) { + bool be = pdata ? pdata->big_endian : + of_device_is_big_endian(pdev->dev.of_node); + + switch (i2c->reg_io_width) { + case 1: + i2c->setreg = oc_setreg_8; + i2c->getreg = oc_getreg_8; + break; + + case 2: + i2c->setreg = be ? oc_setreg_16be : oc_setreg_16; + i2c->getreg = be ? oc_getreg_16be : oc_getreg_16; + break; + + case 4: + i2c->setreg = be ? oc_setreg_32be : oc_setreg_32; + i2c->getreg = be ? oc_getreg_32be : oc_getreg_32; + break; + + default: + dev_err(&pdev->dev, "Unsupported I/O width (%d)\n", + i2c->reg_io_width); + ret = -EINVAL; + goto err_clk; + } + } + + init_waitqueue_head(&i2c->wait); + + irq = platform_get_irq_optional(pdev, 0); + if (irq == -ENXIO) { + ocores_algorithm.master_xfer = ocores_xfer_polling; + + /* + * Set in OCORES_FLAG_BROKEN_IRQ to enable workaround for + * FU540-C000 SoC in polling mode. + */ + match = of_match_node(ocores_i2c_match, pdev->dev.of_node); + if (match && (long)match->data == TYPE_SIFIVE_REV0) + i2c->flags |= OCORES_FLAG_BROKEN_IRQ; + } else { + if (irq < 0) + return irq; + } + + if (ocores_algorithm.master_xfer != ocores_xfer_polling) { + ret = devm_request_any_context_irq(&pdev->dev, irq, + ocores_isr, 0, + pdev->name, i2c); + if (ret) { + dev_err(&pdev->dev, "Cannot claim IRQ\n"); + goto err_clk; + } + } + ret = ocores_init(&pdev->dev, i2c); + if (ret) { + goto err_clk; + } + /* hook up driver to tree */ + platform_set_drvdata(pdev, i2c); + i2c->adap = ocores_adapter; + i2c_set_adapdata(&i2c->adap, i2c); + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.dev.of_node = pdev->dev.of_node; + + /* add i2c adapter to i2c tree */ + ret = i2c_add_adapter(&i2c->adap); + if (ret) { + goto err_clk; + } + /* add in known devices to the bus */ + if (pdata) { + for (i = 0; i < pdata->num_devices; i++){ + i2c_new_client_device(&i2c->adap, pdata->devices + i); + } + } + + return 0; + +err_clk: + clk_disable_unprepare(i2c->clk); + return ret; +} + +static int ocores_i2c_remove(struct platform_device *pdev) +{ + struct ocores_i2c *i2c = platform_get_drvdata(pdev); + u8 ctrl; + + LOCK(&cpld_access_lock); + ctrl = oc_getreg(i2c, OCI2C_CONTROL); + + /* disable i2c logic */ + ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN); + oc_setreg(i2c, OCI2C_CONTROL, ctrl); + UNLOCK(&cpld_access_lock); + + /* remove adapter & data */ + i2c_del_adapter(&i2c->adap); + + if (!IS_ERR(i2c->clk)) + clk_disable_unprepare(i2c->clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int ocores_i2c_suspend(struct device *dev) +{ + struct ocores_i2c *i2c = dev_get_drvdata(dev); + u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL); + + /* make sure the device is disabled */ + ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN); + oc_setreg(i2c, OCI2C_CONTROL, ctrl); + + if (!IS_ERR(i2c->clk)) + clk_disable_unprepare(i2c->clk); + return 0; +} + +static int ocores_i2c_resume(struct device *dev) +{ + struct ocores_i2c *i2c = dev_get_drvdata(dev); + + if (!IS_ERR(i2c->clk)) { + unsigned long rate; + int ret = clk_prepare_enable(i2c->clk); + + if (ret) { + dev_err(dev, + "clk_prepare_enable failed: %d\n", ret); + return ret; + } + rate = clk_get_rate(i2c->clk) / 1000; + if (rate) + i2c->ip_clock_khz = rate; + } + return ocores_init(dev, i2c); +} + +static SIMPLE_DEV_PM_OPS(ocores_i2c_pm, ocores_i2c_suspend, ocores_i2c_resume); +#define OCORES_I2C_PM (&ocores_i2c_pm) +#else +#define OCORES_I2C_PM NULL +#endif + +static struct platform_driver ocores_i2c_driver = { + .probe = ocores_i2c_probe, + .remove = ocores_i2c_remove, + .driver = { + .name = "ocores-as7927", + .of_match_table = ocores_i2c_match, + .pm = OCORES_I2C_PM, + }, +}; + +#if 0 +module_platform_driver(ocores_i2c_driver); +#else +static int __init ocores_i2c_as7927_50x_init(void) +{ + int err; + + err = platform_driver_register(&ocores_i2c_driver); + if (err < 0) { + pr_err("Failed to register ocores_i2c_driver"); + return err; + } + + spin_lock_init(&cpld_access_lock); + + /* + * The register address of SPI Busy is 0x33. + */ + spi_busy_reg = ioremap(SPI_BUSY_ADDR, IOREMAP_SIZE); + if (!spi_busy_reg) { + pr_err("could not remap spi_busy_reg memory\n"); + return -ENOMEM; + } + + return 0; +} +static void __exit ocores_i2c_as7927_50x_exit(void) +{ + platform_driver_unregister(&ocores_i2c_driver); + + iounmap(spi_busy_reg); +} + +module_init(ocores_i2c_as7927_50x_init); +module_exit(ocores_i2c_as7927_50x_exit); +#endif + +MODULE_AUTHOR("Willy Liu "); +MODULE_DESCRIPTION("OpenCores I2C bus driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ocores-as7927"); diff --git a/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-leds.c b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-leds.c new file mode 100644 index 0000000000..b68b76f5b0 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-leds.c @@ -0,0 +1,377 @@ +/* + * Copyright (C) Willy Liu + * + * Based on: + * pca954x.c from Kumar Gala + * Copyright (C) 2006 + * + * Based on: + * pca954x.c from Ken Harrenstien + * Copyright (C) 2004 Google, Inc. (Ken Harrenstien) + * + * Based on: + * i2c-virtual_cb.c from Brian Kuschak + * and + * pca9540.c from Jean Delvare . + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "accton_ipmi_intf.h" + + +#define DRVNAME "as7927_50x_led" +#define IPMI_LED_READ_CMD 0x1A +#define IPMI_LED_WRITE_CMD 0x1B + +static ssize_t set_led(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t show_led(struct device *dev, struct device_attribute *attr, + char *buf); +static int as7927_50x_led_probe(struct platform_device *pdev); +static int as7927_50x_led_remove(struct platform_device *pdev); + +struct as7927_50x_led_data { + struct platform_device *pdev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + unsigned char ipmi_resp[6]; /* 0:Loc 1:Alarm 2:Diag 3:Fan 4:Psu1 5:Psu2 */ + struct ipmi_data ipmi; +}; + +struct as7927_50x_led_data *data = NULL; + +static struct platform_driver as7927_50x_led_driver = { + .probe = as7927_50x_led_probe, + .remove = as7927_50x_led_remove, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +enum ipmi_led_light_mode { + IPMI_LED_MODE_OFF, + IPMI_LED_MODE_RED = 2, + IPMI_LED_MODE_RED_BLINKING = 3, + IPMI_LED_MODE_GREEN = 4, + IPMI_LED_MODE_GREEN_BLINKING = 5, + IPMI_LED_MODE_BLUE = 8, + IPMI_LED_MODE_BLUE_BLINKING = 9, + IPMI_LED_MODE_CYAN = 0xC, + IPMI_LED_MODE_WHITE = 0xE, + IPMI_LED_MODE_AMBER = 0x10, + IPMI_LED_MODE_ORANGE = 0x20, +}; + +enum led_light_mode { + LED_MODE_OFF, + LED_MODE_RED = 10, + LED_MODE_RED_BLINKING = 11, + LED_MODE_ORANGE = 12, + LED_MODE_ORANGE_BLINKING = 13, + LED_MODE_YELLOW = 14, + LED_MODE_YELLOW_BLINKING = 15, + LED_MODE_GREEN = 16, + LED_MODE_GREEN_BLINKING = 17, + LED_MODE_BLUE = 18, + LED_MODE_BLUE_BLINKING = 19, + LED_MODE_PURPLE = 20, + LED_MODE_PURPLE_BLINKING = 21, + LED_MODE_AUTO = 22, + LED_MODE_AUTO_BLINKING = 23, + LED_MODE_WHITE = 24, + LED_MODE_WHITE_BLINKING = 25, + LED_MODE_CYAN = 26, + LED_MODE_CYAN_BLINKING = 27, + LED_MODE_UNKNOWN = 99 +}; + +enum as7927_50x_led_sysfs_attrs { + LED_LOC, + LED_DIAG, + LED_ALARM, + LED_FAN, + LED_PSU1, + LED_PSU2 +}; + +static SENSOR_DEVICE_ATTR(led_loc, S_IWUSR | S_IRUGO, show_led, set_led, + LED_LOC); +static SENSOR_DEVICE_ATTR(led_diag, S_IWUSR | S_IRUGO, show_led, NULL, + LED_DIAG); +static SENSOR_DEVICE_ATTR(led_alarm, S_IWUSR | S_IRUGO, show_led, set_led, + LED_ALARM); +static SENSOR_DEVICE_ATTR(led_fan, S_IWUSR | S_IRUGO, show_led, NULL, + LED_FAN); +static SENSOR_DEVICE_ATTR(led_psu1, S_IWUSR | S_IRUGO, show_led, NULL, + LED_PSU1); +static SENSOR_DEVICE_ATTR(led_psu2, S_IWUSR | S_IRUGO, show_led, NULL, + LED_PSU2); + +static struct attribute *as7927_50x_led_attributes[] = { + &sensor_dev_attr_led_loc.dev_attr.attr, + &sensor_dev_attr_led_diag.dev_attr.attr, + &sensor_dev_attr_led_alarm.dev_attr.attr, + &sensor_dev_attr_led_fan.dev_attr.attr, + &sensor_dev_attr_led_psu1.dev_attr.attr, + &sensor_dev_attr_led_psu2.dev_attr.attr, + NULL +}; + +static const struct attribute_group as7927_50x_led_group = { + .attrs = as7927_50x_led_attributes, +}; + +static struct as7927_50x_led_data *as7927_50x_led_update_device(void) +{ + int status = 0; + + if (time_before(jiffies, data->last_updated + HZ * 5) && data->valid) { + return data; + } + + data->valid = 0; + status = ipmi_send_message(&data->ipmi, IPMI_LED_READ_CMD, NULL, 0, + data->ipmi_resp, sizeof(data->ipmi_resp)); + if (unlikely(status != 0)) { + goto exit; + } + + if (unlikely(data->ipmi.rx_result != 0)) { + status = -EIO; + goto exit; + } + + data->last_updated = jiffies; + data->valid = 1; + +exit: + return data; +} + +static ssize_t show_led(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int value = 0; + int error = 0; + + mutex_lock(&data->update_lock); + + data = as7927_50x_led_update_device(); + if (!data->valid) { + error = -EIO; + goto exit; + } + + switch (data->ipmi_resp[attr->index]) { + case IPMI_LED_MODE_OFF: + value = LED_MODE_OFF; + break; + case IPMI_LED_MODE_RED: + value = LED_MODE_RED; + break; + case IPMI_LED_MODE_RED_BLINKING: + value = LED_MODE_RED_BLINKING; + break; + case IPMI_LED_MODE_GREEN: + value = LED_MODE_GREEN; + break; + case IPMI_LED_MODE_GREEN_BLINKING: + value = LED_MODE_GREEN_BLINKING; + break; + case IPMI_LED_MODE_BLUE: + value = LED_MODE_BLUE; + break; + case IPMI_LED_MODE_BLUE_BLINKING: + value = LED_MODE_BLUE_BLINKING; + break; + case IPMI_LED_MODE_CYAN: + value = LED_MODE_CYAN; + break; + case IPMI_LED_MODE_WHITE: + value = LED_MODE_WHITE; + break; + case IPMI_LED_MODE_AMBER: + value = LED_MODE_YELLOW; + break; + case IPMI_LED_MODE_ORANGE: + value = LED_MODE_ORANGE; + break; + default: + error = -EINVAL; + goto exit; + } + + mutex_unlock(&data->update_lock); + return sprintf(buf, "%d\n", value); + +exit: + mutex_unlock(&data->update_lock); + return error; +} + +static ssize_t set_led(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + long mode; + int status; + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + + status = kstrtol(buf, 10, &mode); + if (status) + return status; + + mutex_lock(&data->update_lock); + + data->ipmi_resp[0] = attr->index + 1; + + switch (mode) { + case LED_MODE_OFF: + data->ipmi_resp[1] = IPMI_LED_MODE_OFF; + break; + case LED_MODE_RED: + data->ipmi_resp[1] = IPMI_LED_MODE_RED; + break; + case LED_MODE_RED_BLINKING: + data->ipmi_resp[1] = IPMI_LED_MODE_RED_BLINKING; + break; + case LED_MODE_GREEN: + data->ipmi_resp[1] = IPMI_LED_MODE_GREEN; + break; + case LED_MODE_GREEN_BLINKING: + data->ipmi_resp[1] = IPMI_LED_MODE_GREEN_BLINKING; + break; + case LED_MODE_BLUE: + data->ipmi_resp[1] = IPMI_LED_MODE_BLUE; + break; + case LED_MODE_BLUE_BLINKING: + data->ipmi_resp[1] = IPMI_LED_MODE_BLUE_BLINKING; + break; + case LED_MODE_CYAN: + data->ipmi_resp[1] = IPMI_LED_MODE_CYAN; + break; + case LED_MODE_WHITE: + data->ipmi_resp[1] = IPMI_LED_MODE_WHITE; + break; + case LED_MODE_YELLOW: + data->ipmi_resp[1] = IPMI_LED_MODE_AMBER; + break; + case LED_MODE_ORANGE: + data->ipmi_resp[1] = IPMI_LED_MODE_ORANGE; + break; + default: + status = -EINVAL; + goto exit; + } + + /* Send IPMI write command */ + status = ipmi_send_message(&data->ipmi, IPMI_LED_WRITE_CMD, + data->ipmi_resp, 2, NULL, 0); + if (unlikely(status != 0)) + goto exit; + + if (unlikely(data->ipmi.rx_result != 0)) { + status = -EIO; + goto exit; + } + + status = count; + data->valid = 0; + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static int as7927_50x_led_probe(struct platform_device *pdev) +{ + int status = -1; + + /* Register sysfs hooks */ + status = sysfs_create_group(&pdev->dev.kobj, &as7927_50x_led_group); + if (status) + goto exit; + + dev_info(&pdev->dev, "device created\n"); + + return 0; + +exit: + return status; +} + +static int as7927_50x_led_remove(struct platform_device *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, &as7927_50x_led_group); + + return 0; +} + +static int __init as7927_50x_led_init(void) +{ + int ret; + + data = kzalloc(sizeof(struct as7927_50x_led_data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto alloc_err; + } + + mutex_init(&data->update_lock); + data->valid = 0; + + ret = platform_driver_register(&as7927_50x_led_driver); + if (ret < 0) + goto dri_reg_err; + + data->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(data->pdev)) { + ret = PTR_ERR(data->pdev); + goto dev_reg_err; + } + + /* Set up IPMI interface */ + ret = init_ipmi_data(&data->ipmi, 0, &data->pdev->dev); + if (ret) + goto ipmi_err; + + return 0; + +ipmi_err: + platform_device_unregister(data->pdev); +dev_reg_err: + platform_driver_unregister(&as7927_50x_led_driver); +dri_reg_err: + kfree(data); +alloc_err: + return ret; +} + +static void __exit as7927_50x_led_exit(void) +{ + ipmi_destroy_user(data->ipmi.user); + platform_device_unregister(data->pdev); + platform_driver_unregister(&as7927_50x_led_driver); + kfree(data); +} + +MODULE_AUTHOR("Willy Liu "); +MODULE_DESCRIPTION("as7927_50x_led driver"); +MODULE_LICENSE("GPL"); + +module_init(as7927_50x_led_init); +module_exit(as7927_50x_led_exit); diff --git a/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-psu.c b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-psu.c new file mode 100644 index 0000000000..9449c0a997 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-psu.c @@ -0,0 +1,979 @@ +/* + * Copyright (C) Roger Ho + * Based on: + * pca954x.c from Kumar Gala + * Copyright (C) 2006 + * + * Based on: + * pca954x.c from Ken Harrenstien + * Copyright (C) 2004 Google, Inc. (Ken Harrenstien) + * + * Based on: + * i2c-virtual_cb.c from Brian Kuschak + * and + * pca9540.c from Jean Delvare . + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "accton_ipmi_intf.h" + + +#define DRVNAME "as7927_50x_psu" +#define IPMI_PSU_READ_CMD 0x16 +#define IPMI_PSU_MODEL_NAME_CMD 0x10 +#define IPMI_PSU_SERIAL_NUM_CMD 0x11 +#define IPMI_PSU_MFR_ID_CMD 0x12 +#define IPMI_PSU_FAN_DIR_CMD 0x13 +#define IPMI_PSU_HW_VERSION_CMD 0x14 +#define IPMI_PSU_INFO_CMD 0x20 +#define IPMI_MODEL_SERIAL_LEN 32 +#define IPMI_FAN_DIR_LEN 3 + +#define MAX_AC_PSU_FAN_SPEED 22000 +#define MAX_DC_PSU_FAN_SPEED 30000 + +static ssize_t show_psu(struct device *dev, struct device_attribute *attr, + char *buf); +static ssize_t show_psu_info(struct device *dev, struct device_attribute *attr, + char *buf); +static ssize_t show_string(struct device *dev, struct device_attribute *attr, + char *buf); +static int as7927_50x_psu_probe(struct platform_device *pdev); +static int as7927_50x_psu_remove(struct platform_device *pdev); + +enum psu_id { + PSU_1, + PSU_2, + NUM_OF_PSU +}; + +enum psu_data_index { + PSU_PRESENT = 0, + PSU_TEMP_FAULT, + PSU_POWER_GOOD_FPGA, + PSU_POWER_GOOD_PMBUS, + PSU_OVER_VOLTAGE, + PSU_OVER_CURRENT, + PSU_POWER_ON, + PSU_VIN0, + PSU_VIN1, + PSU_VIN2, + PSU_VOUT0, + PSU_VOUT1, + PSU_VOUT2, + PSU_IIN0, + PSU_IIN1, + PSU_IIN2, + PSU_IOUT0, + PSU_IOUT1, + PSU_IOUT2, + PSU_PIN0, + PSU_PIN1, + PSU_PIN2, + PSU_PIN3, + PSU_POUT0, + PSU_POUT1, + PSU_POUT2, + PSU_POUT3, + PSU_TEMP1_0, + PSU_TEMP1_1, + PSU_TEMP2_0, + PSU_TEMP2_1, + PSU_TEMP3_0, + PSU_TEMP3_1, + PSU_FAN0, + PSU_FAN1, + PSU_VOUT_MODE, + PSU_STATUS_COUNT, + PSU_MODEL = 0, + PSU_SERIAL = 0, + PSU_TYPE = 1, + PSU_TEMP1_MAX0, + PSU_TEMP1_MAX1, + PSU_TEMP1_MIN0, + PSU_TEMP1_MIN1, + PSU_TEMP2_MAX0, + PSU_TEMP2_MAX1, + PSU_TEMP2_MIN0, + PSU_TEMP2_MIN1, + PSU_TEMP3_MAX0, + PSU_TEMP3_MAX1, + PSU_TEMP3_MIN0, + PSU_TEMP3_MIN1, + PSU_VIN_UPPER_CRIT0, + PSU_VIN_UPPER_CRIT1, + PSU_VIN_UPPER_CRIT2, + PSU_VIN_MAX0, + PSU_VIN_MAX1, + PSU_VIN_MAX2, + PSU_VIN_MIN0, + PSU_VIN_MIN1, + PSU_VIN_MIN2, + PSU_VIN_LOWER_CRIT0, + PSU_VIN_LOWER_CRIT1, + PSU_VIN_LOWER_CRIT2, + PSU_VOUT_MAX0, + PSU_VOUT_MAX1, + PSU_VOUT_MAX2, + PSU_VOUT_MIN0, + PSU_VOUT_MIN1, + PSU_VOUT_MIN2, + PSU_IIN_MAX0, + PSU_IIN_MAX1, + PSU_IIN_MAX2, + PSU_IOUT_MAX0, + PSU_IOUT_MAX1, + PSU_IOUT_MAX2, + PSU_PIN_MAX0, + PSU_PIN_MAX1, + PSU_PIN_MAX2, + PSU_PIN_MAX3, + PSU_POUT_MAX0, + PSU_POUT_MAX1, + PSU_POUT_MAX2, + PSU_POUT_MAX3, + PSU_INFO_COUNT +}; + +struct ipmi_psu_resp_data { + unsigned char status[PSU_STATUS_COUNT]; + unsigned char info[PSU_INFO_COUNT]; + char serial[IPMI_MODEL_SERIAL_LEN+1]; + char model[IPMI_MODEL_SERIAL_LEN+1]; + char hwversion[IPMI_MODEL_SERIAL_LEN+1]; + char mfrid[IPMI_MODEL_SERIAL_LEN+1]; + char fandir[IPMI_FAN_DIR_LEN+1]; +}; + +struct as7927_50x_psu_data { + struct platform_device *pdev; + struct device *hwmon_dev[2]; + struct mutex update_lock; + char valid[2]; /* != 0 if registers are valid, 0: PSU1, 1: PSU2 */ + unsigned long last_updated[2]; /* In jiffies, 0: PSU1, 1: PSU2 */ + struct ipmi_data ipmi; + struct ipmi_psu_resp_data ipmi_resp[2]; /* 0: PSU1, 1: PSU2 */ + unsigned char ipmi_tx_data[2]; +}; + +struct as7927_50x_psu_data *data = NULL; + +static struct platform_driver as7927_50x_psu_driver = { + .probe = as7927_50x_psu_probe, + .remove = as7927_50x_psu_remove, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +#define PSU_PRESENT_ATTR_ID(index) PSU##index##_PRESENT +#define PSU_POWERGOOD_ATTR_ID(index) PSU##index##_POWER_GOOD +#define PSU_VIN_ATTR_ID(index) PSU##index##_VIN +#define PSU_VOUT_ATTR_ID(index) PSU##index##_VOUT +#define PSU_IIN_ATTR_ID(index) PSU##index##_IIN +#define PSU_IOUT_ATTR_ID(index) PSU##index##_IOUT +#define PSU_PIN_ATTR_ID(index) PSU##index##_PIN +#define PSU_POUT_ATTR_ID(index) PSU##index##_POUT +#define PSU_MODEL_ATTR_ID(index) PSU##index##_MODEL +#define PSU_SERIAL_ATTR_ID(index) PSU##index##_SERIAL +#define PSU_HW_VERSION_ATTR_ID(index) PSU##index##_HW_VERSION +#define PSU_MFR_ID_ATTR_ID(index) PSU##index##_MFR_ID +#define PSU_TEMP1_INPUT_ATTR_ID(index) PSU##index##_TEMP1_INPUT +#define PSU_TEMP2_INPUT_ATTR_ID(index) PSU##index##_TEMP2_INPUT +#define PSU_TEMP3_INPUT_ATTR_ID(index) PSU##index##_TEMP3_INPUT +#define PSU_FAN_INPUT_ATTR_ID(index) PSU##index##_FAN_INPUT +#define PSU_FAN_DIR_ATTR_ID(index) PSU##index##_FAN_DIR + +#define PSU_TYPE_ATTR_ID(index) PSU##index##_TYPE +#define PSU_FAN_SPEED_MAX_ATTR_ID(index) PSU##index##_FAN_SPEED_MAX +#define PSU_TEMP1_INPUT_MAX_ATTR_ID(index) PSU##index##_TEMP1_INPUT_MAX +#define PSU_TEMP1_INPUT_MIN_ATTR_ID(index) PSU##index##_TEMP1_INPUT_MIN +#define PSU_TEMP2_INPUT_MAX_ATTR_ID(index) PSU##index##_TEMP2_INPUT_MAX +#define PSU_TEMP2_INPUT_MIN_ATTR_ID(index) PSU##index##_TEMP2_INPUT_MIN +#define PSU_TEMP3_INPUT_MAX_ATTR_ID(index) PSU##index##_TEMP3_INPUT_MAX +#define PSU_TEMP3_INPUT_MIN_ATTR_ID(index) PSU##index##_TEMP3_INPUT_MIN +#define PSU_VIN_MAX_ATTR_ID(index) PSU##index##_VIN_MAX +#define PSU_VIN_MIN_ATTR_ID(index) PSU##index##_VIN_MIN +#define PSU_VIN_UPPER_CRIT_ATTR_ID(index) PSU##index##_VIN_UPPER_CRIT +#define PSU_VIN_LOWER_CRIT_ATTR_ID(index) PSU##index##_VIN_LOWER_CRIT +#define PSU_VOUT_MAX_ATTR_ID(index) PSU##index##_VOUT_MAX +#define PSU_VOUT_MIN_ATTR_ID(index) PSU##index##_VOUT_MIN +#define PSU_IIN_MAX_ATTR_ID(index) PSU##index##_IIN_MAX +#define PSU_IOUT_MAX_ATTR_ID(index) PSU##index##_IOUT_MAX +#define PSU_PIN_MAX_ATTR_ID(index) PSU##index##_PIN_MAX +#define PSU_POUT_MAX_ATTR_ID(index) PSU##index##_POUT_MAX + +#define PSU_ATTR(psu_id) \ + PSU_PRESENT_ATTR_ID(psu_id), \ + PSU_POWERGOOD_ATTR_ID(psu_id), \ + PSU_VIN_ATTR_ID(psu_id), \ + PSU_VOUT_ATTR_ID(psu_id), \ + PSU_IIN_ATTR_ID(psu_id), \ + PSU_IOUT_ATTR_ID(psu_id), \ + PSU_PIN_ATTR_ID(psu_id), \ + PSU_POUT_ATTR_ID(psu_id), \ + PSU_MODEL_ATTR_ID(psu_id), \ + PSU_SERIAL_ATTR_ID(psu_id), \ + PSU_HW_VERSION_ATTR_ID(psu_id), \ + PSU_MFR_ID_ATTR_ID(psu_id), \ + PSU_TEMP1_INPUT_ATTR_ID(psu_id), \ + PSU_TEMP2_INPUT_ATTR_ID(psu_id), \ + PSU_TEMP3_INPUT_ATTR_ID(psu_id), \ + PSU_FAN_INPUT_ATTR_ID(psu_id), \ + PSU_FAN_DIR_ATTR_ID(psu_id), \ + PSU_TYPE_ATTR_ID(psu_id), \ + PSU_FAN_SPEED_MAX_ATTR_ID(psu_id), \ + PSU_TEMP1_INPUT_MAX_ATTR_ID(psu_id), \ + PSU_TEMP1_INPUT_MIN_ATTR_ID(psu_id), \ + PSU_TEMP2_INPUT_MAX_ATTR_ID(psu_id), \ + PSU_TEMP2_INPUT_MIN_ATTR_ID(psu_id), \ + PSU_TEMP3_INPUT_MAX_ATTR_ID(psu_id), \ + PSU_TEMP3_INPUT_MIN_ATTR_ID(psu_id), \ + PSU_VIN_MAX_ATTR_ID(psu_id), \ + PSU_VIN_MIN_ATTR_ID(psu_id), \ + PSU_VIN_UPPER_CRIT_ATTR_ID(psu_id), \ + PSU_VIN_LOWER_CRIT_ATTR_ID(psu_id), \ + PSU_VOUT_MAX_ATTR_ID(psu_id), \ + PSU_VOUT_MIN_ATTR_ID(psu_id), \ + PSU_IIN_MAX_ATTR_ID(psu_id), \ + PSU_IOUT_MAX_ATTR_ID(psu_id), \ + PSU_PIN_MAX_ATTR_ID(psu_id), \ + PSU_POUT_MAX_ATTR_ID(psu_id) + +enum as7927_50x_psu_sysfs_attrs { + /* psu attributes */ + PSU_ATTR(1), + PSU_ATTR(2), + NUM_OF_PSU_ATTR, + NUM_OF_PER_PSU_ATTR = (NUM_OF_PSU_ATTR/NUM_OF_PSU) +}; + +/* psu attributes */ +#define DECLARE_PSU_SENSOR_DEVICE_ATTR(index) \ + static SENSOR_DEVICE_ATTR(psu##index##_present, S_IRUGO, show_psu, NULL, \ + PSU##index##_PRESENT); \ + static SENSOR_DEVICE_ATTR(psu##index##_power_good, S_IRUGO, show_psu, NULL,\ + PSU##index##_POWER_GOOD); \ + static SENSOR_DEVICE_ATTR(psu##index##_vin, S_IRUGO, show_psu, NULL, \ + PSU##index##_VIN); \ + static SENSOR_DEVICE_ATTR(psu##index##_vout, S_IRUGO, show_psu, NULL, \ + PSU##index##_VOUT); \ + static SENSOR_DEVICE_ATTR(psu##index##_iin, S_IRUGO, show_psu, NULL, \ + PSU##index##_IIN); \ + static SENSOR_DEVICE_ATTR(psu##index##_iout, S_IRUGO, show_psu, NULL, \ + PSU##index##_IOUT); \ + static SENSOR_DEVICE_ATTR(psu##index##_pin, S_IRUGO, show_psu, NULL, \ + PSU##index##_PIN); \ + static SENSOR_DEVICE_ATTR(psu##index##_pout, S_IRUGO, show_psu, NULL, \ + PSU##index##_POUT); \ + static SENSOR_DEVICE_ATTR(psu##index##_model, S_IRUGO, show_string, NULL, \ + PSU##index##_MODEL); \ + static SENSOR_DEVICE_ATTR(psu##index##_serial, S_IRUGO, show_string, NULL,\ + PSU##index##_SERIAL);\ + static SENSOR_DEVICE_ATTR(psu##index##_hw_version, S_IRUGO, show_string, NULL,\ + PSU##index##_HW_VERSION);\ + static SENSOR_DEVICE_ATTR(psu##index##_mfr_id, S_IRUGO, show_string, NULL,\ + PSU##index##_MFR_ID);\ + static SENSOR_DEVICE_ATTR(psu##index##_temp1_input, S_IRUGO, show_psu,NULL,\ + PSU##index##_TEMP1_INPUT); \ + static SENSOR_DEVICE_ATTR(psu##index##_temp2_input, S_IRUGO, show_psu,NULL,\ + PSU##index##_TEMP2_INPUT); \ + static SENSOR_DEVICE_ATTR(psu##index##_temp3_input, S_IRUGO, show_psu,NULL,\ + PSU##index##_TEMP3_INPUT); \ + static SENSOR_DEVICE_ATTR(psu##index##_fan1_input, S_IRUGO, show_psu, NULL,\ + PSU##index##_FAN_INPUT); \ + static SENSOR_DEVICE_ATTR(psu##index##_fan_dir, S_IRUGO, show_string, NULL,\ + PSU##index##_FAN_DIR); \ + static SENSOR_DEVICE_ATTR(psu##index##_type, S_IRUGO, \ + show_psu_info, NULL, PSU##index##_TYPE); \ + static SENSOR_DEVICE_ATTR(psu##index##_fan_speed_max, S_IRUGO, \ + show_psu_info, NULL, PSU##index##_FAN_SPEED_MAX); \ + static SENSOR_DEVICE_ATTR(psu##index##_temp1_input_max, S_IRUGO, \ + show_psu_info, NULL, PSU##index##_TEMP1_INPUT_MAX); \ + static SENSOR_DEVICE_ATTR(psu##index##_temp1_input_min, S_IRUGO, \ + show_psu_info, NULL, PSU##index##_TEMP1_INPUT_MIN); \ + static SENSOR_DEVICE_ATTR(psu##index##_temp2_input_max, S_IRUGO, \ + show_psu_info, NULL, PSU##index##_TEMP2_INPUT_MAX); \ + static SENSOR_DEVICE_ATTR(psu##index##_temp2_input_min, S_IRUGO, \ + show_psu_info, NULL, PSU##index##_TEMP2_INPUT_MIN); \ + static SENSOR_DEVICE_ATTR(psu##index##_temp3_input_max, S_IRUGO, \ + show_psu_info, NULL, PSU##index##_TEMP3_INPUT_MAX); \ + static SENSOR_DEVICE_ATTR(psu##index##_temp3_input_min, S_IRUGO, \ + show_psu_info, NULL, PSU##index##_TEMP3_INPUT_MIN); \ + static SENSOR_DEVICE_ATTR(psu##index##_vin_max, S_IRUGO, \ + show_psu_info, NULL, PSU##index##_VIN_MAX); \ + static SENSOR_DEVICE_ATTR(psu##index##_vin_min, S_IRUGO, \ + show_psu_info, NULL, PSU##index##_VIN_MIN); \ + static SENSOR_DEVICE_ATTR(psu##index##_vin_upper_crit, S_IRUGO, \ + show_psu_info, NULL, PSU##index##_VIN_UPPER_CRIT); \ + static SENSOR_DEVICE_ATTR(psu##index##_vin_lower_crit, S_IRUGO, \ + show_psu_info, NULL, PSU##index##_VIN_LOWER_CRIT); \ + static SENSOR_DEVICE_ATTR(psu##index##_vout_max, S_IRUGO, \ + show_psu_info, NULL, PSU##index##_VOUT_MAX); \ + static SENSOR_DEVICE_ATTR(psu##index##_vout_min, S_IRUGO, \ + show_psu_info, NULL, PSU##index##_VOUT_MIN); \ + static SENSOR_DEVICE_ATTR(psu##index##_iin_max, S_IRUGO, \ + show_psu_info, NULL, PSU##index##_IIN_MAX); \ + static SENSOR_DEVICE_ATTR(psu##index##_iout_max, S_IRUGO, \ + show_psu_info, NULL, PSU##index##_IOUT_MAX); \ + static SENSOR_DEVICE_ATTR(psu##index##_pin_max, S_IRUGO, \ + show_psu_info, NULL, PSU##index##_PIN_MAX); \ + static SENSOR_DEVICE_ATTR(psu##index##_pout_max, S_IRUGO, \ + show_psu_info, NULL, PSU##index##_POUT_MAX) + +#define DECLARE_PSU_ATTR(index) \ + &sensor_dev_attr_psu##index##_present.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_power_good.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_vin.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_vout.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_iin.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_iout.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_pin.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_pout.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_model.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_serial.dev_attr.attr,\ + &sensor_dev_attr_psu##index##_hw_version.dev_attr.attr,\ + &sensor_dev_attr_psu##index##_mfr_id.dev_attr.attr,\ + &sensor_dev_attr_psu##index##_temp1_input.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_temp2_input.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_temp3_input.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_fan1_input.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_fan_dir.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_type.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_fan_speed_max.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_temp1_input_max.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_temp1_input_min.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_temp2_input_max.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_temp2_input_min.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_temp3_input_max.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_temp3_input_min.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_vin_max.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_vin_min.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_vin_upper_crit.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_vin_lower_crit.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_vout_max.dev_attr.attr,\ + &sensor_dev_attr_psu##index##_vout_min.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_iin_max.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_iout_max.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_pin_max.dev_attr.attr, \ + &sensor_dev_attr_psu##index##_pout_max.dev_attr.attr + +DECLARE_PSU_SENSOR_DEVICE_ATTR(1); +/*Duplicate nodes for lm-sensors.*/ +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_psu, NULL, PSU1_VOUT); +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, show_psu, NULL, PSU1_IOUT); +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, show_psu, NULL, PSU1_POUT); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_psu, NULL, PSU1_TEMP1_INPUT); +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_psu, NULL, PSU1_FAN_INPUT); + +static struct attribute *as7927_50x_psu1_attrs[] = { + /* psu attributes */ + DECLARE_PSU_ATTR(1), + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_power1_input.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_fan1_input.dev_attr.attr, + NULL +}; +static struct attribute_group as7927_50x_psu1_group = { + .attrs = as7927_50x_psu1_attrs, +}; +/* ATTRIBUTE_GROUPS(as7927_50x_psu1); */ + +DECLARE_PSU_SENSOR_DEVICE_ATTR(2); +/*Duplicate nodes for lm-sensors.*/ +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_psu, NULL, PSU2_VOUT); +static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, show_psu, NULL, PSU2_IOUT); +static SENSOR_DEVICE_ATTR(power2_input, S_IRUGO, show_psu, NULL, PSU2_POUT); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_psu, NULL, PSU2_TEMP1_INPUT); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_psu, NULL, PSU2_FAN_INPUT); +static struct attribute *as7927_50x_psu2_attrs[] = { + /* psu attributes */ + DECLARE_PSU_ATTR(2), + &sensor_dev_attr_curr2_input.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_power2_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + NULL +}; +static struct attribute_group as7927_50x_psu2_group = { + .attrs = as7927_50x_psu2_attrs, +}; +/* ATTRIBUTE_GROUPS(as7927_50x_psu2); */ + +const struct attribute_group *as7927_50x_psu_groups[][2] = { + {&as7927_50x_psu1_group, NULL}, + {&as7927_50x_psu2_group, NULL} +}; + +static struct as7927_50x_psu_data *as7927_50x_psu_update_device(struct device_attribute *da) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + unsigned char pid = attr->index / NUM_OF_PER_PSU_ATTR; + int status = 0; + + if (time_before(jiffies, data->last_updated[pid] + HZ * 5) && data->valid[pid]) + return data; + + data->valid[pid] = 0; + /* To be compatible for older BMC firmware */ + data->ipmi_resp[pid].status[PSU_VOUT_MODE] = 0xff; + + /* Get status from ipmi */ + data->ipmi_tx_data[0] = pid + 1; /* PSU ID base id for ipmi start from 1 */ + status = ipmi_send_message(&data->ipmi, IPMI_PSU_READ_CMD, + data->ipmi_tx_data, 1, + data->ipmi_resp[pid].status, + sizeof(data->ipmi_resp[pid].status)); + if (unlikely(status != 0)) + goto exit; + + if (unlikely(data->ipmi.rx_result != 0)) { + status = -EIO; + goto exit; + } + + /* Get model name from ipmi */ + data->ipmi_tx_data[1] = IPMI_PSU_MODEL_NAME_CMD; + status = ipmi_send_message(&data->ipmi, IPMI_PSU_READ_CMD, + data->ipmi_tx_data, 2, + data->ipmi_resp[pid].model, + sizeof(data->ipmi_resp[pid].model) - 1); + if (unlikely(status != 0)) + goto exit; + + if (unlikely(data->ipmi.rx_result != 0)) { + status = -EIO; + goto exit; + } + + /* Get serial number from ipmi */ + data->ipmi_tx_data[1] = IPMI_PSU_SERIAL_NUM_CMD; + status = ipmi_send_message(&data->ipmi, IPMI_PSU_READ_CMD, + data->ipmi_tx_data, 2, + data->ipmi_resp[pid].serial, + sizeof(data->ipmi_resp[pid].serial) - 1); + if (unlikely(status != 0)) + goto exit; + + if (unlikely(data->ipmi.rx_result != 0)) { + status = -EIO; + goto exit; + } + + /* Get HW version number from ipmi */ + data->ipmi_tx_data[1] = IPMI_PSU_HW_VERSION_CMD; + status = ipmi_send_message(&data->ipmi, IPMI_PSU_READ_CMD, + data->ipmi_tx_data, 2, + data->ipmi_resp[pid].hwversion, + sizeof(data->ipmi_resp[pid].hwversion) - 1); + if (unlikely(status != 0)) + goto exit; + + if (unlikely(data->ipmi.rx_result != 0)) { + status = -EIO; + goto exit; + } + + /* Get manufacturer ID number from ipmi */ + data->ipmi_tx_data[1] = IPMI_PSU_MFR_ID_CMD; + status = ipmi_send_message(&data->ipmi, IPMI_PSU_READ_CMD, + data->ipmi_tx_data, 2, + data->ipmi_resp[pid].mfrid, + sizeof(data->ipmi_resp[pid].mfrid) - 1); + if (unlikely(status != 0)) + goto exit; + + if (unlikely(data->ipmi.rx_result != 0)) { + status = -EIO; + goto exit; + } + + /* Get fan direction from ipmi */ + data->ipmi_tx_data[1] = IPMI_PSU_FAN_DIR_CMD; + status = ipmi_send_message(&data->ipmi, IPMI_PSU_READ_CMD, + data->ipmi_tx_data, 2, + data->ipmi_resp[pid].fandir, + sizeof(data->ipmi_resp[pid].fandir) - 1); + if (unlikely(status != 0)) + goto exit; + + if (unlikely(data->ipmi.rx_result != 0)) { + status = -EIO; + goto exit; + } + + /* Get capability from ipmi */ + data->ipmi_tx_data[1] = IPMI_PSU_INFO_CMD; + status = ipmi_send_message(&data->ipmi, IPMI_PSU_READ_CMD, + data->ipmi_tx_data, 2, + data->ipmi_resp[pid].info, + sizeof(data->ipmi_resp[pid].info)); + if (unlikely(status != 0)) + goto exit; + + if (unlikely(data->ipmi.rx_result != 0)) { + status = -EIO; + goto exit; + } + + data->last_updated[pid] = jiffies; + data->valid[pid] = 1; + +exit: + return data; +} + +#define VALIDATE_PRESENT_RETURN(id) \ +do { \ + if (data->ipmi_resp[id].status[PSU_PRESENT] == 0) { \ + mutex_unlock(&data->update_lock); \ + return -ENXIO; \ + } \ +} while (0) + +static ssize_t show_psu(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + unsigned char pid = attr->index / NUM_OF_PER_PSU_ATTR; + u32 value = 0; + int present = 0; + int error = 0; + int multiplier = 1000; + + mutex_lock(&data->update_lock); + + data = as7927_50x_psu_update_device(da); + if (!data->valid[pid]) { + error = -EIO; + goto exit; + } + + present = !!(data->ipmi_resp[pid].status[PSU_PRESENT]); + + switch (attr->index) { + case PSU1_PRESENT: + case PSU2_PRESENT: + value = present; + break; + case PSU1_POWER_GOOD: + case PSU2_POWER_GOOD: + VALIDATE_PRESENT_RETURN(pid); + value = data->ipmi_resp[pid].status[PSU_POWER_GOOD_PMBUS]; + break; + case PSU1_IIN: + case PSU2_IIN: + VALIDATE_PRESENT_RETURN(pid); + value = ((u32)data->ipmi_resp[pid].status[PSU_IIN0] | + (u32)data->ipmi_resp[pid].status[PSU_IIN1] << 8 | + (u32)data->ipmi_resp[pid].status[PSU_IIN2] << 16); + break; + case PSU1_IOUT: + case PSU2_IOUT: + VALIDATE_PRESENT_RETURN(pid); + value = ((u32)data->ipmi_resp[pid].status[PSU_IOUT0] | + (u32)data->ipmi_resp[pid].status[PSU_IOUT1] << 8 | + (u32)data->ipmi_resp[pid].status[PSU_IOUT2] << 16); + break; + case PSU1_VIN: + case PSU2_VIN: + VALIDATE_PRESENT_RETURN(pid); + value = ((u32)data->ipmi_resp[pid].status[PSU_VIN0] | + (u32)data->ipmi_resp[pid].status[PSU_VIN1] << 8 | + (u32)data->ipmi_resp[pid].status[PSU_VIN2] << 16); + break; + case PSU1_VOUT: + case PSU2_VOUT: + VALIDATE_PRESENT_RETURN(pid); + value = ((u32)data->ipmi_resp[pid].status[PSU_VOUT0] | + (u32)data->ipmi_resp[pid].status[PSU_VOUT1] << 8 | + (u32)data->ipmi_resp[pid].status[PSU_VOUT2] << 16); + break; + case PSU1_PIN: + case PSU2_PIN: + VALIDATE_PRESENT_RETURN(pid); + value = ((u32)data->ipmi_resp[pid].status[PSU_PIN0] | + (u32)data->ipmi_resp[pid].status[PSU_PIN1] << 8 | + (u32)data->ipmi_resp[pid].status[PSU_PIN2] << 16 | + (u32)data->ipmi_resp[pid].status[PSU_PIN3] << 24); + value /= 1000; // Convert to milliwatt + break; + case PSU1_POUT: + case PSU2_POUT: + VALIDATE_PRESENT_RETURN(pid); + value = ((u32)data->ipmi_resp[pid].status[PSU_POUT0] | + (u32)data->ipmi_resp[pid].status[PSU_POUT1] << 8 | + (u32)data->ipmi_resp[pid].status[PSU_POUT2] << 16 | + (u32)data->ipmi_resp[pid].status[PSU_POUT3] << 24); + value /= 1000; // Convert to milliwatt + break; + case PSU1_TEMP1_INPUT: + case PSU2_TEMP1_INPUT: + VALIDATE_PRESENT_RETURN(pid); + value = (s16)((u16)data->ipmi_resp[pid].status[PSU_TEMP1_0] | + (u16)data->ipmi_resp[pid].status[PSU_TEMP1_1] << 8); + value *= 1000; // Convert to millidegree Celsius + break; + case PSU1_TEMP2_INPUT: + case PSU2_TEMP2_INPUT: + VALIDATE_PRESENT_RETURN(pid); + value = (s16)((u16)data->ipmi_resp[pid].status[PSU_TEMP2_0] | + (u16)data->ipmi_resp[pid].status[PSU_TEMP2_1] << 8); + value *= 1000; // Convert to millidegree Celsius + break; + case PSU1_TEMP3_INPUT: + case PSU2_TEMP3_INPUT: + VALIDATE_PRESENT_RETURN(pid); + value = (s16)((u16)data->ipmi_resp[pid].status[PSU_TEMP3_0] | + (u16)data->ipmi_resp[pid].status[PSU_TEMP3_1] << 8); + value *= 1000; // Convert to millidegree Celsius + break; + case PSU1_FAN_INPUT: + case PSU2_FAN_INPUT: + VALIDATE_PRESENT_RETURN(pid); + multiplier = 1; + value = ((u32)data->ipmi_resp[pid].status[PSU_FAN0] | + (u32)data->ipmi_resp[pid].status[PSU_FAN1] << 8); + break; + default: + error = -EINVAL; + goto exit; + } + + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", present ? value : 0); + +exit: + mutex_unlock(&data->update_lock); + return error; +} + +static ssize_t show_psu_info(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + unsigned char pid = attr->index / NUM_OF_PER_PSU_ATTR; + s32 value = 0; + int present = 0; + int error = 0; + + mutex_lock(&data->update_lock); + + data = as7927_50x_psu_update_device(da); + if (!data->valid[pid]) { + error = -EIO; + goto exit; + } + + present = !!(data->ipmi_resp[pid].status[PSU_PRESENT]); + + switch (attr->index) { + case PSU1_TYPE: + case PSU2_TYPE: + VALIDATE_PRESENT_RETURN(pid); + value = (u32)data->ipmi_resp[pid].info[PSU_TYPE]; + break; + case PSU1_FAN_SPEED_MAX: + case PSU2_FAN_SPEED_MAX: + VALIDATE_PRESENT_RETURN(pid); + int psu_type = data->ipmi_resp[pid].info[PSU_TYPE]; + value = (psu_type==1) ? MAX_AC_PSU_FAN_SPEED : MAX_DC_PSU_FAN_SPEED; + break; + case PSU1_TEMP1_INPUT_MAX: + case PSU2_TEMP1_INPUT_MAX: + VALIDATE_PRESENT_RETURN(pid); + value = (s16)((u16)data->ipmi_resp[pid].info[PSU_TEMP1_MAX0] | + (u16)data->ipmi_resp[pid].info[PSU_TEMP1_MAX1] << 8); + value *= 1000; // Convert to millidegree Celsius + break; + case PSU1_TEMP1_INPUT_MIN: + case PSU2_TEMP1_INPUT_MIN: + VALIDATE_PRESENT_RETURN(pid); + value = (s16)((u16)data->ipmi_resp[pid].info[PSU_TEMP1_MIN0] | + (u16)data->ipmi_resp[pid].info[PSU_TEMP1_MIN1] << 8); + value *= 1000; // Convert to millidegree Celsius + break; + case PSU1_TEMP2_INPUT_MAX: + case PSU2_TEMP2_INPUT_MAX: + VALIDATE_PRESENT_RETURN(pid); + value = (s16)((u16)data->ipmi_resp[pid].info[PSU_TEMP2_MAX0] | + (u16)data->ipmi_resp[pid].info[PSU_TEMP2_MAX1] << 8); + value *= 1000; // Convert to millidegree Celsius + break; + case PSU1_TEMP2_INPUT_MIN: + case PSU2_TEMP2_INPUT_MIN: + VALIDATE_PRESENT_RETURN(pid); + value = (s16)((u16)data->ipmi_resp[pid].info[PSU_TEMP2_MIN0] | + (u16)data->ipmi_resp[pid].info[PSU_TEMP2_MIN1] << 8); + value *= 1000; // Convert to millidegree Celsius + break; + case PSU1_TEMP3_INPUT_MAX: + case PSU2_TEMP3_INPUT_MAX: + VALIDATE_PRESENT_RETURN(pid); + value = (s16)((u16)data->ipmi_resp[pid].info[PSU_TEMP3_MAX0] | + (u16)data->ipmi_resp[pid].info[PSU_TEMP3_MAX1] << 8); + value *= 1000; // Convert to millidegree Celsius + break; + case PSU1_TEMP3_INPUT_MIN: + case PSU2_TEMP3_INPUT_MIN: + VALIDATE_PRESENT_RETURN(pid); + value = (s16)((u16)data->ipmi_resp[pid].info[PSU_TEMP3_MIN0] | + (u16)data->ipmi_resp[pid].info[PSU_TEMP3_MIN1] << 8); + value *= 1000; // Convert to millidegree Celsius + break; + case PSU1_VIN_UPPER_CRIT: + case PSU2_VIN_UPPER_CRIT: + VALIDATE_PRESENT_RETURN(pid); + value = ((u32)data->ipmi_resp[pid].info[PSU_VIN_UPPER_CRIT0] | + (u32)data->ipmi_resp[pid].info[PSU_VIN_UPPER_CRIT1] << 8 | + (u32)data->ipmi_resp[pid].info[PSU_VIN_UPPER_CRIT2] << 16); + break; + case PSU1_VIN_LOWER_CRIT: + case PSU2_VIN_LOWER_CRIT: + VALIDATE_PRESENT_RETURN(pid); + value = ((u32)data->ipmi_resp[pid].info[PSU_VIN_LOWER_CRIT0] | + (u32)data->ipmi_resp[pid].info[PSU_VIN_LOWER_CRIT1] << 8 | + (u32)data->ipmi_resp[pid].info[PSU_VIN_LOWER_CRIT2] << 16); + break; + case PSU1_VIN_MAX: + case PSU2_VIN_MAX: + VALIDATE_PRESENT_RETURN(pid); + value = ((u32)data->ipmi_resp[pid].info[PSU_VIN_MAX0] | + (u32)data->ipmi_resp[pid].info[PSU_VIN_MAX1] << 8 | + (u32)data->ipmi_resp[pid].info[PSU_VIN_MAX2] << 16); + break; + case PSU1_VIN_MIN: + case PSU2_VIN_MIN: + VALIDATE_PRESENT_RETURN(pid); + value = ((u32)data->ipmi_resp[pid].info[PSU_VIN_MIN0] | + (u32)data->ipmi_resp[pid].info[PSU_VIN_MIN1] << 8 | + (u32)data->ipmi_resp[pid].info[PSU_VIN_MIN2] << 16); + break; + case PSU1_VOUT_MAX: + case PSU2_VOUT_MAX: + VALIDATE_PRESENT_RETURN(pid); + value = ((u32)data->ipmi_resp[pid].info[PSU_VOUT_MAX0] | + (u32)data->ipmi_resp[pid].info[PSU_VOUT_MAX1] << 8 | + (u32)data->ipmi_resp[pid].info[PSU_VOUT_MAX2] << 16); + break; + case PSU1_VOUT_MIN: + case PSU2_VOUT_MIN: + VALIDATE_PRESENT_RETURN(pid); + value = ((u32)data->ipmi_resp[pid].info[PSU_VOUT_MIN0] | + (u32)data->ipmi_resp[pid].info[PSU_VOUT_MIN1] << 8 | + (u32)data->ipmi_resp[pid].info[PSU_VOUT_MIN2] << 16); + break; + case PSU1_IIN_MAX: + case PSU2_IIN_MAX: + VALIDATE_PRESENT_RETURN(pid); + value = ((u32)data->ipmi_resp[pid].info[PSU_IIN_MAX0] | + (u32)data->ipmi_resp[pid].info[PSU_IIN_MAX1] << 8 | + (u32)data->ipmi_resp[pid].info[PSU_IIN_MAX2] << 16); + break; + case PSU1_IOUT_MAX: + case PSU2_IOUT_MAX: + VALIDATE_PRESENT_RETURN(pid); + value = ((u32)data->ipmi_resp[pid].info[PSU_IOUT_MAX0] | + (u32)data->ipmi_resp[pid].info[PSU_IOUT_MAX1] << 8 | + (u32)data->ipmi_resp[pid].info[PSU_IOUT_MAX2] << 16); + break; + case PSU1_PIN_MAX: + case PSU2_PIN_MAX: + VALIDATE_PRESENT_RETURN(pid); + value = ((u32)data->ipmi_resp[pid].info[PSU_PIN_MAX0] | + (u32)data->ipmi_resp[pid].info[PSU_PIN_MAX1] << 8 | + (u32)data->ipmi_resp[pid].info[PSU_PIN_MAX2] << 16 | + (u32)data->ipmi_resp[pid].info[PSU_PIN_MAX3] << 24); + value /= 1000; // Convert to milliwatt + break; + case PSU1_POUT_MAX: + case PSU2_POUT_MAX: + VALIDATE_PRESENT_RETURN(pid); + value = ((u32)data->ipmi_resp[pid].info[PSU_POUT_MAX0] | + (u32)data->ipmi_resp[pid].info[PSU_POUT_MAX1] << 8 | + (u32)data->ipmi_resp[pid].info[PSU_POUT_MAX2] << 16 | + (u32)data->ipmi_resp[pid].info[PSU_POUT_MAX3] << 24); + value /= 1000; // Convert to milliwatt + break; + default: + error = -EINVAL; + goto exit; + } + + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", present ? value : 0); + +exit: + mutex_unlock(&data->update_lock); + return error; +} + +static ssize_t show_string(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + unsigned char pid = attr->index / NUM_OF_PER_PSU_ATTR; + char *str = NULL; + int error = 0; + + mutex_lock(&data->update_lock); + + data = as7927_50x_psu_update_device(da); + if (!data->valid[pid]) { + error = -EIO; + goto exit; + } + + switch (attr->index) { + case PSU1_MODEL: + case PSU2_MODEL: + VALIDATE_PRESENT_RETURN(pid); + str = data->ipmi_resp[pid].model; + break; + case PSU1_SERIAL: + case PSU2_SERIAL: + VALIDATE_PRESENT_RETURN(pid); + str = data->ipmi_resp[pid].serial; + break; + case PSU1_HW_VERSION: + case PSU2_HW_VERSION: + VALIDATE_PRESENT_RETURN(pid); + str = data->ipmi_resp[pid].hwversion; + break; + case PSU1_MFR_ID: + case PSU2_MFR_ID: + VALIDATE_PRESENT_RETURN(pid); + str = data->ipmi_resp[pid].mfrid; + break; + case PSU1_FAN_DIR: + case PSU2_FAN_DIR: + VALIDATE_PRESENT_RETURN(pid); + str = data->ipmi_resp[pid].fandir; + break; + default: + error = -EINVAL; + goto exit; + } + + mutex_unlock(&data->update_lock); + return sprintf(buf, "%s\n", str); + +exit: + mutex_unlock(&data->update_lock); + return error; +} + +static int as7927_50x_psu_probe(struct platform_device *pdev) +{ + int status = 0; + struct device *hwmon_dev = NULL; + int i = 0; + + for (i = 0; i < 2 ; i++) { + hwmon_dev = hwmon_device_register_with_info(&pdev->dev, DRVNAME, + NULL, NULL, as7927_50x_psu_groups[i]); + if (IS_ERR(hwmon_dev)) { + status = PTR_ERR(hwmon_dev); + return status; + } + + mutex_lock(&data->update_lock); + data->hwmon_dev[i] = hwmon_dev; + mutex_unlock(&data->update_lock); + + dev_info(&pdev->dev, "PSU%d device created\n", i + 1); + } + + return 0; +} + +static int as7927_50x_psu_remove(struct platform_device *pdev) +{ + int i = 0; + + for(i = 0; i < 2 ; i++) { + mutex_lock(&data->update_lock); + if (data->hwmon_dev[i]) { + hwmon_device_unregister(data->hwmon_dev[i]); + data->hwmon_dev[i] = NULL; + } + mutex_unlock(&data->update_lock); + } + + return 0; +} + +static int __init as7927_50x_psu_init(void) +{ + int ret; + + data = kzalloc(sizeof(struct as7927_50x_psu_data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto alloc_err; + } + + mutex_init(&data->update_lock); + + ret = platform_driver_register(&as7927_50x_psu_driver); + if (ret < 0) + goto dri_reg_err; + + + data->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(data->pdev)) { + ret = PTR_ERR(data->pdev); + goto dev_reg_err; + } + + /* Set up IPMI interface */ + ret = init_ipmi_data(&data->ipmi, 0, &data->pdev->dev); + if (ret) { + goto ipmi_err; + } + + return 0; + +ipmi_err: + platform_device_unregister(data->pdev); +dev_reg_err: + platform_driver_unregister(&as7927_50x_psu_driver); +dri_reg_err: + kfree(data); +alloc_err: + return ret; +} + +static void __exit as7927_50x_psu_exit(void) +{ + ipmi_destroy_user(data->ipmi.user); + platform_device_unregister(data->pdev); + platform_driver_unregister(&as7927_50x_psu_driver); + kfree(data); +} + +MODULE_AUTHOR("Roger Ho "); +MODULE_DESCRIPTION("as7927_50x_psu driver"); +MODULE_LICENSE("GPL"); + +module_init(as7927_50x_psu_init); +module_exit(as7927_50x_psu_exit); diff --git a/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-sys.c b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-sys.c new file mode 100644 index 0000000000..26be078c38 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-sys.c @@ -0,0 +1,410 @@ +/* + * Copyright (C) Brandon Chuang + * + * Based on: + * pca954x.c from Kumar Gala + * Copyright (C) 2006 + * + * Based on: + * pca954x.c from Ken Harrenstien + * Copyright (C) 2004 Google, Inc. (Ken Harrenstien) + * + * Based on: + * i2c-virtual_cb.c from Brian Kuschak + * and + * pca9540.c from Jean Delvare . + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "accton_ipmi_intf.h" + + +#define DRVNAME "as7927_50x_sys" + +#define IPMI_SYSEEPROM_READ_CMD 0x18 +#define IPMI_READ_MAX_LEN 128 + +#define EEPROM_NAME "eeprom" +#define EEPROM_SIZE 256 /* 256 byte eeprom */ + +#define IPMI_CPLD_READ_CMD 0x20 +#define IPMI_CPLD_COM_E_CMD 0x21 +#define IPMI_CPLD_FAN_CMD 0x33 +#define IPMI_CPLD_DCSCM_CMD 0x06 // Since addr conflicts with FPGA, replaced by 0x06 +#define IPMI_CPLD_FPGA_CMD 0x60 +#define IPMI_CPLD_SYS_CMD 0x61 +#define IPMI_SEND_THERMAL_DATA_CMD 0x13 + +static int as7927_50x_sys_probe(struct platform_device *pdev); +static int as7927_50x_sys_remove(struct platform_device *pdev); +static ssize_t show_cpld_version(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t set_bmc_thermal_data(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); + +struct as7927_50x_sys_data { + struct platform_device *pdev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + struct ipmi_data ipmi; + unsigned char ipmi_resp_eeprom[EEPROM_SIZE]; + unsigned char ipmi_resp_cpld[4]; + unsigned char ipmi_tx_data[3]; + struct bin_attribute eeprom; /* eeprom data */ +}; + +struct as7927_50x_sys_data *data = NULL; + +static struct platform_driver as7927_50x_sys_driver = { + .probe = as7927_50x_sys_probe, + .remove = as7927_50x_sys_remove, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +enum as5916_54xks_sys_sysfs_attrs { + COM_E_CPLD, + FPGA_CPLD, + FAN_CPLD, + DCSCM_CPLD, + SYS_CPLD, + THERMAL_DATA +}; +/* Functions to talk to the IPMI layer */ +static SENSOR_DEVICE_ATTR(come_e_cpld_ver, S_IRUGO, show_cpld_version, NULL, COM_E_CPLD); +static SENSOR_DEVICE_ATTR(fpga_cpld_ver, S_IRUGO, show_cpld_version, NULL, FPGA_CPLD); +static SENSOR_DEVICE_ATTR(fan_cpld_ver, S_IRUGO, show_cpld_version, NULL, FAN_CPLD); +static SENSOR_DEVICE_ATTR(dcscm_cpld_ver, S_IRUGO, show_cpld_version, NULL, DCSCM_CPLD); +static SENSOR_DEVICE_ATTR(sys_cpld_ver, S_IRUGO, show_cpld_version, NULL, SYS_CPLD); +static SENSOR_DEVICE_ATTR(bmc_thermal_data, S_IWUSR, NULL, set_bmc_thermal_data, THERMAL_DATA); + +static struct attribute *as7927_50x_sys_attributes[] = { + &sensor_dev_attr_come_e_cpld_ver.dev_attr.attr, + &sensor_dev_attr_fpga_cpld_ver.dev_attr.attr, + &sensor_dev_attr_fan_cpld_ver.dev_attr.attr, + &sensor_dev_attr_dcscm_cpld_ver.dev_attr.attr, + &sensor_dev_attr_sys_cpld_ver.dev_attr.attr, + &sensor_dev_attr_bmc_thermal_data.dev_attr.attr, + NULL +}; + +static const struct attribute_group as7927_50x_sys_group = { + .attrs = as7927_50x_sys_attributes, +}; + +static ssize_t sys_eeprom_read(loff_t off, char *buf, size_t count) +{ + int status = 0; + unsigned char length = 0; + + if ((off + count) > EEPROM_SIZE) { + return -EINVAL; + } + + length = (count >= IPMI_READ_MAX_LEN) ? IPMI_READ_MAX_LEN : count; + data->ipmi_tx_data[0] = (off & 0xff); + data->ipmi_tx_data[1] = length; + status = ipmi_send_message(&data->ipmi, IPMI_SYSEEPROM_READ_CMD, + data->ipmi_tx_data, 2, + data->ipmi_resp_eeprom + off, length); + if (unlikely(status != 0)) { + goto exit; + } + + if (unlikely(data->ipmi.rx_result != 0)) { + status = -EIO; + goto exit; + } + + status = length; /* Read length */ + memcpy(buf, data->ipmi_resp_eeprom + off, length); + +exit: + return status; +} + +static ssize_t sysfs_bin_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + ssize_t retval = 0; + + if (unlikely(!count)) { + return count; + } + + /* + * Read data from chip, protecting against concurrent updates + * from this host + */ + mutex_lock(&data->update_lock); + + while (count) { + ssize_t status; + + status = sys_eeprom_read(off, buf, count); + if (status <= 0) { + if (retval == 0) { + retval = status; + } + break; + } + + buf += status; + off += status; + count -= status; + retval += status; + } + + mutex_unlock(&data->update_lock); + return retval; + +} + +static int sysfs_eeprom_init(struct kobject *kobj, struct bin_attribute *eeprom) +{ + sysfs_bin_attr_init(eeprom); + eeprom->attr.name = EEPROM_NAME; + eeprom->attr.mode = S_IRUGO; + eeprom->read = sysfs_bin_read; + eeprom->write = NULL; + eeprom->size = EEPROM_SIZE; + + /* Create eeprom file */ + return sysfs_create_bin_file(kobj, eeprom); +} + +static int sysfs_eeprom_cleanup(struct kobject *kobj, struct bin_attribute *eeprom) +{ + sysfs_remove_bin_file(kobj, eeprom); + return 0; +} + +static struct as7927_50x_sys_data *as7927_50x_sys_update_cpld_ver(unsigned char cpld_addr) +{ + int status = 0; + + data->valid = 0; + data->ipmi_tx_data[0] = cpld_addr; + status = ipmi_send_message(&data->ipmi, IPMI_CPLD_READ_CMD, + data->ipmi_tx_data, 1, + data->ipmi_resp_cpld, + sizeof(data->ipmi_resp_cpld)); + if (unlikely(status != 0)) + goto exit; + + if (unlikely(data->ipmi.rx_result != 0)) { + status = -EIO; + goto exit; + } + + data->last_updated = jiffies; + data->valid = 1; + +exit: + return data; +} + +static ssize_t show_cpld_version(struct device *dev, struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + unsigned char major; + unsigned char minor; + unsigned char cpld_addr = 0; + int error = 0; + + switch (attr->index) { + case COM_E_CPLD: + cpld_addr = IPMI_CPLD_COM_E_CMD; + break; + case FPGA_CPLD: + cpld_addr = IPMI_CPLD_FPGA_CMD; + break; + case FAN_CPLD: + cpld_addr = IPMI_CPLD_FAN_CMD; + break; + case DCSCM_CPLD: + cpld_addr = IPMI_CPLD_DCSCM_CMD; + break; + case SYS_CPLD: + cpld_addr = IPMI_CPLD_SYS_CMD; + break; + default: + return -EINVAL; + } + + mutex_lock(&data->update_lock); + + data = as7927_50x_sys_update_cpld_ver(cpld_addr); + if (!data->valid) { + error = -EIO; + goto exit; + } + + + major = data->ipmi_resp_cpld[0]; + mutex_unlock(&data->update_lock); + if (attr->index == DCSCM_CPLD) + return snprintf(buf, 32, "%x\n", major); + else if (attr->index == COM_E_CPLD){ + major = data->ipmi_resp_cpld[2]; + minor = data->ipmi_resp_cpld[3]; + } + else + minor = data->ipmi_resp_cpld[1]; + return snprintf(buf, 32, "%x.%x\n", major, minor); + +exit: + mutex_unlock(&data->update_lock); + return error; +} + +static ssize_t set_bmc_thermal_data(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int status; + int args; + char *opt, tmp[32] = {0}; + char *tmp_p; + size_t copy_size; + u8 input[3] = {0}; + + copy_size = (count < sizeof(tmp)) ? count : sizeof(tmp) - 1; + #ifdef __STDC_LIB_EXT1__ + memcpy_s(tmp, copy_size, buf, copy_size); + #else + memcpy(tmp, buf, copy_size); + #endif + tmp[copy_size] = '\0'; + + args = 0; + tmp_p = strim(tmp); + while (args < 3 && (opt = strsep(&tmp_p, " ")) != NULL) { + if (kstrtou8(opt, 10, &input[args]) == 0) { + args++; + } + } + if (args != 3) { + return -EINVAL; + } + + mutex_lock(&data->update_lock); + + data->ipmi_tx_data[0] = input[0]; + data->ipmi_tx_data[1] = input[1]; + data->ipmi_tx_data[2] = input[2]; + status = ipmi_send_message(&data->ipmi, IPMI_SEND_THERMAL_DATA_CMD, + data->ipmi_tx_data, 3, + NULL, 0); + if (unlikely(status != 0)) + goto exit; + + if (unlikely(data->ipmi.rx_result != 0)) { + status = -EINVAL; + goto exit; + } + + status = count; + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static int as7927_50x_sys_probe(struct platform_device *pdev) +{ + int status = -1; + + /* Register sysfs hooks */ + status = sysfs_eeprom_init(&pdev->dev.kobj, &data->eeprom); + if (status) { + goto exit; + } + /* Register sysfs hooks */ + status = sysfs_create_group(&pdev->dev.kobj, &as7927_50x_sys_group); + if (status) + goto exit; + dev_info(&pdev->dev, "device created\n"); + + return 0; + +exit: + return status; +} + +static int as7927_50x_sys_remove(struct platform_device *pdev) +{ + sysfs_eeprom_cleanup(&pdev->dev.kobj, &data->eeprom); + sysfs_remove_group(&pdev->dev.kobj, &as7927_50x_sys_group); + + return 0; +} + +static int __init as7927_50x_sys_init(void) +{ + int ret; + + data = kzalloc(sizeof(struct as7927_50x_sys_data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto alloc_err; + } + + mutex_init(&data->update_lock); + + ret = platform_driver_register(&as7927_50x_sys_driver); + if (ret < 0) { + goto dri_reg_err; + } + + data->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(data->pdev)) { + ret = PTR_ERR(data->pdev); + goto dev_reg_err; + } + + /* Set up IPMI interface */ + ret = init_ipmi_data(&data->ipmi, 0, &data->pdev->dev); + if (ret) + goto ipmi_err; + + return 0; + +ipmi_err: + platform_device_unregister(data->pdev); +dev_reg_err: + platform_driver_unregister(&as7927_50x_sys_driver); +dri_reg_err: + kfree(data); +alloc_err: + return ret; +} + +static void __exit as7927_50x_sys_exit(void) +{ + ipmi_destroy_user(data->ipmi.user); + platform_device_unregister(data->pdev); + platform_driver_unregister(&as7927_50x_sys_driver); + kfree(data); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as7927_50x_sys driver"); +MODULE_LICENSE("GPL"); + +module_init(as7927_50x_sys_init); +module_exit(as7927_50x_sys_exit); diff --git a/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-thermal.c b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-thermal.c new file mode 100644 index 0000000000..cf368e1b0b --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/modules/builds/src/x86-64-accton-as7927-50x-thermal.c @@ -0,0 +1,259 @@ +/* + * Copyright (C) Roger Ho + * + * Based on: + * pca954x.c from Kumar Gala + * Copyright (C) 2006 + * + * Based on: + * pca954x.c from Ken Harrenstien + * Copyright (C) 2004 Google, Inc. (Ken Harrenstien) + * + * Based on: + * i2c-virtual_cb.c from Brian Kuschak + * and + * pca9540.c from Jean Delvare . + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "accton_ipmi_intf.h" + +#define DRVNAME "as7927_50x_thermal" +#define IPMI_THERMAL_READ_CMD 0x12 +#define THERMAL_COUNT 12 +#define THERMAL_DATA_LEN 3 +#define THERMAL_DATA_COUNT (THERMAL_COUNT * THERMAL_DATA_LEN) + +static ssize_t show_temp(struct device *dev, struct device_attribute *attr, + char *buf); +static int as7927_50x_thermal_probe(struct platform_device *pdev); +static int as7927_50x_thermal_remove(struct platform_device *pdev); + +enum temp_data_index { + TEMP_ADDR, + TEMP_FAULT, + TEMP_INPUT, + TEMP_DATA_COUNT +}; + +struct as7927_50x_thermal_data { + struct platform_device *pdev; + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + char ipmi_resp[THERMAL_DATA_COUNT]; /* 3 bytes for each thermal */ + struct ipmi_data ipmi; + unsigned char ipmi_tx_data[2]; /* 0: thermal id, 1: temp */ +}; + +struct as7927_50x_thermal_data *data = NULL; + +static struct platform_driver as7927_50x_thermal_driver = { + .probe = as7927_50x_thermal_probe, + .remove = as7927_50x_thermal_remove, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +enum as7927_50x_thermal_sysfs_attrs { + TEMP1_INPUT, // CB_RearLefttemp(U21 0x48) + TEMP2_INPUT, // CB_FrontLeft_temp(U29 0x49) + TEMP3_INPUT, // MB_FrontLeft_temp(U51 0x4A) + TEMP4_INPUT, // I/OB_temp(AU12 0x49) + TEMP5_INPUT, // MB_FrontRight_temp(U53 0x4C) + TEMP6_INPUT, // MB_RearLeft_temp(U22 0x49) + TEMP7_INPUT, // MAC_DiodeCore_temp(U22 0x49) + TEMP8_INPUT, // MAC_DiodeNif100_temp(U22 0x49) + TEMP9_INPUT, // MAC_DiodeNif50_temp(U22 0x49) + TEMP10_INPUT, // MAC_DiodeSch_temp(U22 0x49) + TEMP11_INPUT, // FB_FrontRight_temp(U1 0x4D) + TEMP12_INPUT, // FB_FrontLeft_temp(U2 0x4E) +}; + +#define DECLARE_THERMAL_SENSOR_DEVICE_ATTR(index) \ + static SENSOR_DEVICE_ATTR(temp##index##_input, S_IRUGO, show_temp, \ + NULL, TEMP##index##_INPUT); + +#define DECLARE_THERMAL_ATTR(index) \ + &sensor_dev_attr_temp##index##_input.dev_attr.attr + +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(1); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(2); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(3); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(4); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(5); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(6); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(7); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(8); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(9); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(10); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(11); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(12); + +static struct attribute *as7927_50x_thermal_attrs[] = { + DECLARE_THERMAL_ATTR(1), + DECLARE_THERMAL_ATTR(2), + DECLARE_THERMAL_ATTR(3), + DECLARE_THERMAL_ATTR(4), + DECLARE_THERMAL_ATTR(5), + DECLARE_THERMAL_ATTR(6), + DECLARE_THERMAL_ATTR(7), + DECLARE_THERMAL_ATTR(8), + DECLARE_THERMAL_ATTR(9), + DECLARE_THERMAL_ATTR(10), + DECLARE_THERMAL_ATTR(11), + DECLARE_THERMAL_ATTR(12), + NULL +}; +ATTRIBUTE_GROUPS(as7927_50x_thermal); + +static ssize_t show_temp(struct device *dev, struct device_attribute *da, + char *buf) +{ + int status = 0; + int index = 0; + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ * 5) || !data->valid) { + data->valid = 0; + + status = ipmi_send_message(&data->ipmi, IPMI_THERMAL_READ_CMD, NULL, 0, + data->ipmi_resp, sizeof(data->ipmi_resp)); + if (unlikely(status != 0)) + goto exit; + + if (unlikely(data->ipmi.rx_result != 0)) { + status = -EIO; + goto exit; + } + + data->last_updated = jiffies; + data->valid = 1; + } + + /* Get temp fault status */ + index = attr->index * TEMP_DATA_COUNT + TEMP_FAULT; + if (unlikely(data->ipmi_resp[index] == 0)) { + status = -EIO; + goto exit; + } + + /* Get temperature in degree celsius */ + index = attr->index * TEMP_DATA_COUNT + TEMP_INPUT; + status = ((s8)data->ipmi_resp[index]) * 1000; + + mutex_unlock(&data->update_lock); + return sprintf(buf, "%d\n", status); + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static int as7927_50x_thermal_probe(struct platform_device *pdev) +{ + int status = 0; + struct device *hwmon_dev; + + hwmon_dev = hwmon_device_register_with_info(&pdev->dev, DRVNAME, + NULL, NULL, as7927_50x_thermal_groups); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + return status; + } + + mutex_lock(&data->update_lock); + data->hwmon_dev = hwmon_dev; + mutex_unlock(&data->update_lock); + + dev_info(&pdev->dev, "Device Created\n"); + + return status; +} + +static int as7927_50x_thermal_remove(struct platform_device *pdev) +{ + mutex_lock(&data->update_lock); + if (data->hwmon_dev) { + hwmon_device_unregister(data->hwmon_dev); + data->hwmon_dev = NULL; + } + mutex_unlock(&data->update_lock); + + return 0; +} + +static int __init as7927_50x_thermal_init(void) +{ + int ret; + + data = kzalloc(sizeof(struct as7927_50x_thermal_data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto alloc_err; + } + + mutex_init(&data->update_lock); + + ret = platform_driver_register(&as7927_50x_thermal_driver); + if (ret < 0) + goto dri_reg_err; + + data->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(data->pdev)) { + ret = PTR_ERR(data->pdev); + goto dev_reg_err; + } + + /* Set up IPMI interface */ + ret = init_ipmi_data(&data->ipmi, 0, &data->pdev->dev); + if (ret) { + goto ipmi_err; + } + + return 0; + +ipmi_err: + platform_device_unregister(data->pdev); +dev_reg_err: + platform_driver_unregister(&as7927_50x_thermal_driver); +dri_reg_err: + kfree(data); +alloc_err: + return ret; +} + +static void __exit as7927_50x_thermal_exit(void) +{ + if (data) { + ipmi_destroy_user(data->ipmi.user); + platform_device_unregister(data->pdev); + platform_driver_unregister(&as7927_50x_thermal_driver); + kfree(data); + } +} + +MODULE_AUTHOR("Roger Ho "); +MODULE_DESCRIPTION("as7927_50x_thermal driver"); +MODULE_LICENSE("GPL"); + +module_init(as7927_50x_thermal_init); +module_exit(as7927_50x_thermal_exit); diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/Makefile b/packages/platforms/accton/x86-64/as7927-50x/onlp/Makefile new file mode 100644 index 0000000000..003238cf6d --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk \ No newline at end of file diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/PKG.yml b/packages/platforms/accton/x86-64/as7927-50x/onlp/PKG.yml new file mode 100644 index 0000000000..1f4ae940b5 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/PKG.yml @@ -0,0 +1 @@ +!include $ONL_TEMPLATES/onlp-platform-any.yml PLATFORM=x86-64-accton-as7927-50x ARCH=amd64 TOOLCHAIN=x86_64-linux-gnu diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/Makefile b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/Makefile new file mode 100644 index 0000000000..e7437cb23a --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/Makefile @@ -0,0 +1,2 @@ +FILTER=src +include $(ONL)/make/subdirs.mk diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/lib/Makefile b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/lib/Makefile new file mode 100644 index 0000000000..f7870beefc --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/lib/Makefile @@ -0,0 +1,2 @@ +PLATFORM := x86-64-accton-as7927-50x +include $(ONL)/packages/base/any/onlp/builds/platform/libonlp-platform.mk diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/onlpdump/Makefile b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/onlpdump/Makefile new file mode 100644 index 0000000000..448b89a8a1 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/onlpdump/Makefile @@ -0,0 +1,2 @@ +PLATFORM := x86-64-accton-as7927-50x +include $(ONL)/packages/base/any/onlp/builds/platform/onlps.mk diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/.gitignore b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/.module b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/.module new file mode 100644 index 0000000000..19a739768e --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/.module @@ -0,0 +1 @@ +name: x86_64_accton_as7927_50x diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/Makefile b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/Makefile new file mode 100644 index 0000000000..b2e56d8c97 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/Makefile @@ -0,0 +1,9 @@ +############################################################################### +# +# +# +############################################################################### +include $(ONL)/make/config.mk +MODULE := x86_64_accton_as7927_50x +AUTOMODULE := x86_64_accton_as7927_50x +include $(BUILDER)/definemodule.mk diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/README b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/README new file mode 100644 index 0000000000..b0e05b612f --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/README @@ -0,0 +1,6 @@ +############################################################################### +# +# x86_64_accton_as7927_50x README +# +############################################################################### + diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/auto/make.mk b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/auto/make.mk new file mode 100644 index 0000000000..14be097c65 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/auto/make.mk @@ -0,0 +1,9 @@ +############################################################################### +# +# x86_64_accton_as7927_50x Autogeneration +# +############################################################################### +x86_64_accton_as7927_50x_AUTO_DEFS := module/auto/x86_64_accton_as7927_50x.yml +x86_64_accton_as7927_50x_AUTO_DIRS := module/inc/x86_64_accton_as7927_50x module/src +include $(BUILDER)/auto.mk + diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/auto/x86_64_accton_as7927_50x.yml b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/auto/x86_64_accton_as7927_50x.yml new file mode 100644 index 0000000000..1d011dab40 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/auto/x86_64_accton_as7927_50x.yml @@ -0,0 +1,50 @@ +############################################################################### +# +# x86_64_accton_as7927_50x Autogeneration Definitions. +# +############################################################################### + +cdefs: &cdefs +- X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_LOGGING: + doc: "Include or exclude logging." + default: 1 +- X86_64_ACCTON_AS7927_50X_CONFIG_LOG_OPTIONS_DEFAULT: + doc: "Default enabled log options." + default: AIM_LOG_OPTIONS_DEFAULT +- X86_64_ACCTON_AS7927_50X_CONFIG_LOG_BITS_DEFAULT: + doc: "Default enabled log bits." + default: AIM_LOG_BITS_DEFAULT +- X86_64_ACCTON_AS7927_50X_CONFIG_LOG_CUSTOM_BITS_DEFAULT: + doc: "Default enabled custom log bits." + default: 0 +- X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_STDLIB: + doc: "Default all porting macros to use the C standard libraries." + default: 1 +- X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS: + doc: "Include standard library headers for stdlib porting macros." + default: x86_64_accton_as7927_50x_CONFIG_PORTING_STDLIB +- X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_UCLI: + doc: "Include generic uCli support." + default: 0 +- X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION: + doc: "Assume chassis fan direction is the same as the PSU fan direction." + default: 0 + + +definitions: + cdefs: + x86_64_accton_as7927_50x_CONFIG_HEADER: + defs: *cdefs + basename: x86_64_accton_as7927_50x_config + + portingmacro: + x86_64_accton_as7927_50x: + macros: + - malloc + - free + - memset + - memcpy + - strncpy + - vsnprintf + - snprintf + - strlen diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/inc/x86_64_accton_as7927_50x/x86_64_accton_as7927_50x.x b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/inc/x86_64_accton_as7927_50x/x86_64_accton_as7927_50x.x new file mode 100644 index 0000000000..079b1b8be2 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/inc/x86_64_accton_as7927_50x/x86_64_accton_as7927_50x.x @@ -0,0 +1,14 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +/* <--auto.start.xmacro(ALL).define> */ +/* */ + +/* <--auto.start.xenum(ALL).define> */ +/* */ + + diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/inc/x86_64_accton_as7927_50x/x86_64_accton_as7927_50x_config.h b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/inc/x86_64_accton_as7927_50x/x86_64_accton_as7927_50x_config.h new file mode 100644 index 0000000000..d7389d7059 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/inc/x86_64_accton_as7927_50x/x86_64_accton_as7927_50x_config.h @@ -0,0 +1,137 @@ +/**************************************************************************//** + * + * @file + * @brief x86_64_accton_as7927_50x Configuration Header + * + * @addtogroup x86_64_accton_as7927_50x-config + * @{ + * + *****************************************************************************/ +#ifndef __x86_64_accton_as7927_50x_CONFIG_H__ +#define __x86_64_accton_as7927_50x_CONFIG_H__ + +#ifdef GLOBAL_INCLUDE_CUSTOM_CONFIG +#include +#endif +#ifdef x86_64_accton_as7927_50x_INCLUDE_CUSTOM_CONFIG +#include +#endif + +/* */ +#include +/** + * X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_LOGGING + * + * Include or exclude logging. */ + + +#ifndef X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_LOGGING +#define X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_LOGGING 1 +#endif + +/** + * X86_64_ACCTON_AS7927_50X_CONFIG_LOG_OPTIONS_DEFAULT + * + * Default enabled log options. */ + + +#ifndef X86_64_ACCTON_AS7927_50X_CONFIG_LOG_OPTIONS_DEFAULT +#define X86_64_ACCTON_AS7927_50X_CONFIG_LOG_OPTIONS_DEFAULT AIM_LOG_OPTIONS_DEFAULT +#endif + +/** + * X86_64_ACCTON_AS7927_50X_CONFIG_LOG_BITS_DEFAULT + * + * Default enabled log bits. */ + + +#ifndef X86_64_ACCTON_AS7927_50X_CONFIG_LOG_BITS_DEFAULT +#define X86_64_ACCTON_AS7927_50X_CONFIG_LOG_BITS_DEFAULT AIM_LOG_BITS_DEFAULT +#endif + +/** + * X86_64_ACCTON_AS7927_50X_CONFIG_LOG_CUSTOM_BITS_DEFAULT + * + * Default enabled custom log bits. */ + + +#ifndef X86_64_ACCTON_AS7927_50X_CONFIG_LOG_CUSTOM_BITS_DEFAULT +#define X86_64_ACCTON_AS7927_50X_CONFIG_LOG_CUSTOM_BITS_DEFAULT 0 +#endif + +/** + * X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_STDLIB + * + * Default all porting macros to use the C standard libraries. */ + + +#ifndef X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_STDLIB +#define X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_STDLIB 1 +#endif + +/** + * X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS + * + * Include standard library headers for stdlib porting macros. */ + + +#ifndef X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS +#define X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS x86_64_accton_as7927_50x_CONFIG_PORTING_STDLIB +#endif + +/** + * X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_UCLI + * + * Include generic uCli support. */ + + +#ifndef X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_UCLI +#define X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_UCLI 0 +#endif + +/** + * X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION + * + * Assume chassis fan direction is the same as the PSU fan direction. */ + + +#ifndef X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION +#define X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION 0 +#endif + + + +/** + * All compile time options can be queried or displayed + */ + +/** Configuration settings structure. */ +typedef struct x86_64_accton_as7927_50x_config_settings_s { + /** name */ + const char* name; + /** value */ + const char* value; +} x86_64_accton_as7927_50x_config_settings_t; + +/** Configuration settings table. */ +/** x86_64_accton_as7927_50x_config_settings table. */ +extern x86_64_accton_as7927_50x_config_settings_t x86_64_accton_as7927_50x_config_settings[]; + +/** + * @brief Lookup a configuration setting. + * @param setting The name of the configuration option to lookup. + */ +const char* x86_64_accton_as7927_50x_config_lookup(const char* setting); + +/** + * @brief Show the compile-time configuration. + * @param pvs The output stream. + */ +int x86_64_accton_as7927_50x_config_show(struct aim_pvs_s* pvs); + +/* */ + +#include "x86_64_accton_as7927_50x_porting.h" + +#endif /* __x86_64_accton_as7927_50x_CONFIG_H__ */ +/* @} */ diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/inc/x86_64_accton_as7927_50x/x86_64_accton_as7927_50x_dox.h b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/inc/x86_64_accton_as7927_50x/x86_64_accton_as7927_50x_dox.h new file mode 100644 index 0000000000..8e3e69dd55 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/inc/x86_64_accton_as7927_50x/x86_64_accton_as7927_50x_dox.h @@ -0,0 +1,26 @@ +/**************************************************************************//** + * + * x86_64_accton_as7927_50x Doxygen Header + * + *****************************************************************************/ +#ifndef __x86_64_accton_as7927_50x_DOX_H__ +#define __x86_64_accton_as7927_50x_DOX_H__ + +/** + * @defgroup x86_64_accton_as7927_50x x86_64_accton_as7927_50x - x86_64_accton_as7927_50x Description + * + +The documentation overview for this module should go here. + + * + * @{ + * + * @defgroup x86_64_accton_as7927_50x-x86_64_accton_as7927_50x Public Interface + * @defgroup x86_64_accton_as7927_50x-config Compile Time Configuration + * @defgroup x86_64_accton_as7927_50x-porting Porting Macros + * + * @} + * + */ + +#endif /* __x86_64_accton_as7927_50x_DOX_H__ */ diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/inc/x86_64_accton_as7927_50x/x86_64_accton_as7927_50x_porting.h b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/inc/x86_64_accton_as7927_50x/x86_64_accton_as7927_50x_porting.h new file mode 100644 index 0000000000..09f13c1b74 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/inc/x86_64_accton_as7927_50x/x86_64_accton_as7927_50x_porting.h @@ -0,0 +1,107 @@ +/**************************************************************************//** + * + * @file + * @brief x86_64_accton_as7927_50x Porting Macros. + * + * @addtogroup x86_64_accton_as7927_50x-porting + * @{ + * + *****************************************************************************/ +#ifndef __x86_64_accton_as7927_50x_PORTING_H__ +#define __x86_64_accton_as7927_50x_PORTING_H__ + + +/* */ +#if X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS == 1 +#include +#include +#include +#include +#include +#endif + +#ifndef X86_64_ACCTON_AS7927_50X_MALLOC + #if defined(GLOBAL_MALLOC) + #define X86_64_ACCTON_AS7927_50X_MALLOC GLOBAL_MALLOC + #elif X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS7927_50X_MALLOC malloc + #else + #error The macro X86_64_ACCTON_AS7927_50X_MALLOC is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_AS7927_50X_FREE + #if defined(GLOBAL_FREE) + #define X86_64_ACCTON_AS7927_50X_FREE GLOBAL_FREE + #elif X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS7927_50X_FREE free + #else + #error The macro X86_64_ACCTON_AS7927_50X_FREE is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_AS7927_50X_MEMSET + #if defined(GLOBAL_MEMSET) + #define X86_64_ACCTON_AS7927_50X_MEMSET GLOBAL_MEMSET + #elif X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS7927_50X_MEMSET memset + #else + #error The macro X86_64_ACCTON_AS7927_50X_MEMSET is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_AS7927_50X_MEMCPY + #if defined(GLOBAL_MEMCPY) + #define X86_64_ACCTON_AS7927_50X_MEMCPY GLOBAL_MEMCPY + #elif X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS7927_50X_MEMCPY memcpy + #else + #error The macro X86_64_ACCTON_AS7927_50X_MEMCPY is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_AS7927_50X_STRNCPY + #if defined(GLOBAL_STRNCPY) + #define X86_64_ACCTON_AS7927_50X_STRNCPY GLOBAL_STRNCPY + #elif X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS7927_50X_STRNCPY strncpy + #else + #error The macro X86_64_ACCTON_AS7927_50X_STRNCPY is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_AS7927_50X_VSNPRINTF + #if defined(GLOBAL_VSNPRINTF) + #define X86_64_ACCTON_AS7927_50X_VSNPRINTF GLOBAL_VSNPRINTF + #elif X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS7927_50X_VSNPRINTF vsnprintf + #else + #error The macro X86_64_ACCTON_AS7927_50X_VSNPRINTF is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_AS7927_50X_SNPRINTF + #if defined(GLOBAL_SNPRINTF) + #define X86_64_ACCTON_AS7927_50X_SNPRINTF GLOBAL_SNPRINTF + #elif X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS7927_50X_SNPRINTF snprintf + #else + #error The macro X86_64_ACCTON_AS7927_50X_SNPRINTF is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_AS7927_50X_STRLEN + #if defined(GLOBAL_STRLEN) + #define X86_64_ACCTON_AS7927_50X_STRLEN GLOBAL_STRLEN + #elif X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS7927_50X_STRLEN strlen + #else + #error The macro X86_64_ACCTON_AS7927_50X_STRLEN is required but cannot be defined. + #endif +#endif + +/* */ + + +#endif /* __x86_64_accton_as7927_50x_PORTING_H__ */ +/* @} */ diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/make.mk b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/make.mk new file mode 100644 index 0000000000..8ddb7bc00c --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/make.mk @@ -0,0 +1,10 @@ +############################################################################### +# +# +# +############################################################################### +THIS_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +x86_64_accton_as7927_50x_INCLUDES := -I $(THIS_DIR)inc +x86_64_accton_as7927_50x_INTERNAL_INCLUDES := -I $(THIS_DIR)src +x86_64_accton_as7927_50x_DEPENDMODULE_ENTRIES := init:x86_64_accton_as7927_50x ucli:x86_64_accton_as7927_50x + diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/Makefile b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/Makefile new file mode 100644 index 0000000000..1de8ddadb4 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/Makefile @@ -0,0 +1,9 @@ +############################################################################### +# +# Local source generation targets. +# +############################################################################### + +ucli: + @../../../../tools/uclihandlers.py x86_64_accton_as7927_50x_ucli.c + diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/debug.c b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/debug.c new file mode 100644 index 0000000000..fadeb24efa --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/debug.c @@ -0,0 +1,45 @@ +#include "x86_64_accton_as7927_50x_int.h" + +#if x86_64_accton_as7927_50x_CONFIG_INCLUDE_DEBUG == 1 + +#include + +static char help__[] = + "Usage: debug [options]\n" + " -c CPLD Versions\n" + " -h Help\n" + ; + +int +x86_64_accton_as7927_50x_debug_main(int argc, char* argv[]) +{ + int c = 0; + int help = 0; + int rv = 0; + + while( (c = getopt(argc, argv, "ch")) != -1) { + switch(c) + { + case 'c': c = 1; break; + case 'h': help = 1; rv = 0; break; + default: help = 1; rv = 1; break; + } + + } + + if(help || argc == 1) { + printf("%s", help__); + return rv; + } + + if(c) { + printf("Not implemented.\n"); + } + + + return 0; +} + +#endif + + diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/fani.c b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/fani.c new file mode 100644 index 0000000000..61d6328dce --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/fani.c @@ -0,0 +1,301 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2014 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * Fan Platform Implementation Defaults. + * + ***********************************************************/ +#include +#include +#include "platform_lib.h" + +enum fan_id { + FAN_1_ON_FAN_BOARD = 1, + FAN_2_ON_FAN_BOARD, + FAN_3_ON_FAN_BOARD, + FAN_4_ON_FAN_BOARD, + FAN_5_ON_FAN_BOARD, + FAN_6_ON_FAN_BOARD, + FAN_7_ON_FAN_BOARD, + FAN_8_ON_FAN_BOARD, + FAN_9_ON_FAN_BOARD, + FAN_10_ON_FAN_BOARD, + FAN_11_ON_FAN_BOARD, + FAN_12_ON_FAN_BOARD, + FAN_13_ON_FAN_BOARD, + FAN_14_ON_FAN_BOARD, + FAN_1_ON_PSU_1, + FAN_1_ON_PSU_2 +}; + +#define CHASSIS_FAN_INFO(fid) \ + { \ + { ONLP_FAN_ID_CREATE(FAN_##fid##_ON_FAN_BOARD), "Chassis Fan - "#fid, 0, {0} },\ + 0x0,\ + ONLP_FAN_CAPS_SET_PERCENTAGE | ONLP_FAN_CAPS_GET_RPM | ONLP_FAN_CAPS_GET_PERCENTAGE,\ + 0,\ + 0,\ + ONLP_FAN_MODE_INVALID,\ + } + +#define PSU_FAN_INFO(pid, fid) \ + { \ + { ONLP_FAN_ID_CREATE(FAN_##fid##_ON_PSU_##pid), "PSU "#pid" - Fan "#fid, 0, {0} },\ + 0x0,\ + ONLP_FAN_CAPS_GET_RPM | ONLP_FAN_CAPS_GET_PERCENTAGE,\ + 0,\ + 0,\ + ONLP_FAN_MODE_INVALID,\ + } + +/* Static fan information */ +onlp_fan_info_t finfo[] = { + { }, /* Not used */ + CHASSIS_FAN_INFO(1), + CHASSIS_FAN_INFO(2), + CHASSIS_FAN_INFO(3), + CHASSIS_FAN_INFO(4), + CHASSIS_FAN_INFO(5), + CHASSIS_FAN_INFO(6), + CHASSIS_FAN_INFO(7), + CHASSIS_FAN_INFO(8), + CHASSIS_FAN_INFO(9), + CHASSIS_FAN_INFO(10), + CHASSIS_FAN_INFO(11), + CHASSIS_FAN_INFO(12), + CHASSIS_FAN_INFO(13), + CHASSIS_FAN_INFO(14), + PSU_FAN_INFO(1,1), + PSU_FAN_INFO(2,1) +}; + +#define VALIDATE(_id) \ + do { \ + if(!ONLP_OID_IS_FAN(_id)) { \ + return ONLP_STATUS_E_INVALID; \ + } \ + } while(0) + +static int +_onlp_fani_set_fan_dir_info(int fid, onlp_fan_info_t* info) +{ + enum onlp_fan_dir dir = onlp_get_fan_dir(fid); + + if (FAN_DIR_F2B == dir) { + info->caps |= ONLP_FAN_CAPS_F2B; + info->status |= ONLP_FAN_STATUS_F2B; + } + else { + info->caps |= ONLP_FAN_CAPS_B2F; + info->status |= ONLP_FAN_STATUS_B2F; + } + + return ONLP_STATUS_OK; +} + +static int +_onlp_fani_info_get_fan(int fid, onlp_fan_info_t* info) +{ + int value, ret, pwm; + + /* get fan present status + */ + ret = onlp_file_read_int(&value, FAN_SYSFS_FORMAT"fan%d_present", fid); + if (ret < 0) { + return ONLP_STATUS_E_INTERNAL; + } + + if (value == 0) { + return ONLP_STATUS_OK; + } + info->status |= ONLP_FAN_STATUS_PRESENT; + + /* get fan fault status (turn on when any one fails) + */ + ret = onlp_file_read_int(&value, "%s""fan%d_fault", FAN_SYSFS_FORMAT, fid); + if (ret < 0) { + return ONLP_STATUS_E_INTERNAL; + } + + if (value > 0) { + info->status |= ONLP_FAN_STATUS_FAILED; + return ONLP_STATUS_OK; + } + + /* get fan speed + */ + ret = onlp_file_read_int(&value, "%s""fan%d_input", FAN_SYSFS_FORMAT, fid); + if (ret < 0) { + return ONLP_STATUS_E_INTERNAL; + } + info->rpm = value; + + /* get speed percentage from rpm + */ + pwm = 0; + ret = onlp_file_read_int(&pwm, "%s""fan%d_pwm", FAN_SYSFS_FORMAT, fid); + if (ret < 0) { + return ONLP_STATUS_E_INTERNAL; + } + + value = 0; + ret = onlp_file_read_int(&value, "%s""fan%d_target", FAN_SYSFS_FORMAT, fid); + if (ret < 0 || value == 0) { + return ONLP_STATUS_E_INTERNAL; + } + + info->percentage = (info->rpm*pwm)/value; + if (info->percentage > 100) + info->percentage = 100; + + _onlp_fani_set_fan_dir_info(fid, info); + + return ONLP_STATUS_OK; +} + +static int +_onlp_fani_info_get_fan_on_psu(int pid, onlp_fan_info_t* info) +{ + char *str = NULL; + int len = 0; + int val = 0; + int max_speed = 0; + int ret = 0; + int hwmon_idx; + + info->status |= ONLP_FAN_STATUS_PRESENT; + + /* get fan direction + */ + hwmon_idx = onlp_get_psu_hwmon_idx(pid); + if (hwmon_idx >= 0) { + char file[32]; + snprintf(file, sizeof(file), "psu%d_fan_dir", pid); + + len = onlp_file_read_str(&str, PSU_SYSFS_FORMAT_1, hwmon_idx, file); + if (str && len >= 3) { + if (strncmp(str, "B2F", strlen("B2F")) == 0) { + info->status |= ONLP_FAN_STATUS_B2F; + } + else { + info->status |= ONLP_FAN_STATUS_F2B; + } + } + AIM_FREE_IF_PTR(str); + } + + /* get max fan speed + */ + ret = onlp_file_read_int(&max_speed, PSU_SYSFS_FORMAT, pid, "fan_speed_max"); + if (ret < 0) { + AIM_LOG_ERROR("Unable to read status from PSU(%d)\r\n", pid); + return ONLP_STATUS_E_INTERNAL; + } + + /* get fan speed + */ + ret = onlp_file_read_int(&val, PSU_SYSFS_FORMAT, pid, "fan1_input"); + if (ret < 0) { + AIM_LOG_ERROR("Unable to read status from PSU(%d)\r\n", pid); + return ONLP_STATUS_E_INTERNAL; + } + info->rpm = val; + info->percentage = (info->rpm * 100)/max_speed; + + return ONLP_STATUS_OK; +} + +/* + * This function will be called prior to all of onlp_fani_* functions. + */ +int +onlp_fani_init(void) +{ + return ONLP_STATUS_OK; +} + +int +onlp_fani_info_get(onlp_oid_t id, onlp_fan_info_t* info) +{ + int rc = 0; + int fid; + VALIDATE(id); + + fid = ONLP_OID_ID_GET(id); + *info = finfo[fid]; + + switch (fid) { + case FAN_1_ON_FAN_BOARD: + case FAN_2_ON_FAN_BOARD: + case FAN_3_ON_FAN_BOARD: + case FAN_4_ON_FAN_BOARD: + case FAN_5_ON_FAN_BOARD: + case FAN_6_ON_FAN_BOARD: + case FAN_7_ON_FAN_BOARD: + case FAN_8_ON_FAN_BOARD: + case FAN_9_ON_FAN_BOARD: + case FAN_10_ON_FAN_BOARD: + case FAN_11_ON_FAN_BOARD: + case FAN_12_ON_FAN_BOARD: + case FAN_13_ON_FAN_BOARD: + case FAN_14_ON_FAN_BOARD: + rc = _onlp_fani_info_get_fan(fid, info); + break; + case FAN_1_ON_PSU_1: + case FAN_1_ON_PSU_2: + rc = _onlp_fani_info_get_fan_on_psu(fid-FAN_14_ON_FAN_BOARD, info); + break; + default: + rc = ONLP_STATUS_E_INVALID; + break; + } + + return rc; +} + +/* + * This function sets the fan speed of the given OID as a percentage. + * + * This will only be called if the OID has the PERCENTAGE_SET + * capability. + * + * It is optional if you have no fans at all with this feature. + */ +int +onlp_fani_percentage_set(onlp_oid_t id, int p) +{ + int fid; + + VALIDATE(id); + + fid = ONLP_OID_ID_GET(id); + + if (fid < FAN_1_ON_FAN_BOARD || fid > FAN_14_ON_FAN_BOARD) { + return ONLP_STATUS_E_UNSUPPORTED; + } + + if (onlp_file_write_int(p, "%s""fan%d_pwm", FAN_SYSFS_FORMAT, fid) != 0) { + AIM_LOG_ERROR("Unable to change duty cycle of fan (%d)\r\n", fid); + return ONLP_STATUS_E_INTERNAL; + } + + return ONLP_STATUS_OK; +} diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/ledi.c b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/ledi.c new file mode 100644 index 0000000000..09f2fb7a15 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/ledi.c @@ -0,0 +1,235 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2013 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * + * + ***********************************************************/ +#include +#include +#include "platform_lib.h" + +#define VALIDATE(_id) \ + do { \ + if(!ONLP_OID_IS_LED(_id)) { \ + return ONLP_STATUS_E_INVALID; \ + } \ + } while(0) + +#define LED_FORMAT "/sys/devices/platform/as7927_50x_led/led_%s" + +enum led_light_mode { /*must be the same with the definition @ kernel driver */ + LED_MODE_OFF, + LED_MODE_RED = 10, + LED_MODE_RED_BLINKING = 11, + LED_MODE_ORANGE = 12, + LED_MODE_ORANGE_BLINKING = 13, + LED_MODE_YELLOW = 14, + LED_MODE_YELLOW_BLINKING = 15, + LED_MODE_GREEN = 16, + LED_MODE_GREEN_BLINKING = 17, + LED_MODE_BLUE = 18, + LED_MODE_BLUE_BLINKING = 19, + LED_MODE_PURPLE = 20, + LED_MODE_PURPLE_BLINKING = 21, + LED_MODE_AUTO = 22, + LED_MODE_AUTO_BLINKING = 23, + LED_MODE_WHITE = 24, + LED_MODE_WHITE_BLINKING = 25, + LED_MODE_CYAN = 26, + LED_MODE_CYAN_BLINKING = 27, + LED_MODE_UNKNOWN = 99 +}; + +typedef struct led_light_mode_map { + enum onlp_led_id id; + enum led_light_mode driver_led_mode; + enum onlp_led_mode_e onlp_led_mode; +} led_light_mode_map_t; + +led_light_mode_map_t led_map[] = { + { LED_LOC, LED_MODE_OFF, ONLP_LED_MODE_OFF }, + { LED_LOC, LED_MODE_BLUE_BLINKING, ONLP_LED_MODE_BLUE_BLINKING }, + { LED_DIAG, LED_MODE_GREEN, ONLP_LED_MODE_GREEN }, + { LED_DIAG, LED_MODE_RED, ONLP_LED_MODE_RED }, + { LED_ALARM, LED_MODE_OFF, ONLP_LED_MODE_OFF }, + { LED_ALARM, LED_MODE_RED, ONLP_LED_MODE_RED }, + { LED_FAN, LED_MODE_OFF, ONLP_LED_MODE_OFF }, + { LED_FAN, LED_MODE_GREEN, ONLP_LED_MODE_GREEN }, + { LED_FAN, LED_MODE_RED, ONLP_LED_MODE_RED }, + { LED_PSU1, LED_MODE_OFF, ONLP_LED_MODE_OFF }, + { LED_PSU1, LED_MODE_GREEN, ONLP_LED_MODE_GREEN }, + { LED_PSU1, LED_MODE_RED, ONLP_LED_MODE_RED }, + { LED_PSU2, LED_MODE_OFF, ONLP_LED_MODE_OFF }, + { LED_PSU2, LED_MODE_GREEN, ONLP_LED_MODE_GREEN }, + { LED_PSU2, LED_MODE_RED, ONLP_LED_MODE_RED }, +}; + +static char *leds[] = { /* must map with onlp_led_id */ + NULL, + "loc", + "diag", + "alarm", + "fan", + "psu1", + "psu2", +}; + +/* + * Get the information for the given LED OID. + */ +static onlp_led_info_t linfo[] = +{ + { }, /* Not used */ + { + { ONLP_LED_ID_CREATE(LED_LOC), "Chassis LED 1 (LOC LED)", 0, {0} }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_ON_OFF | ONLP_LED_CAPS_BLUE_BLINKING, + }, + { + { ONLP_LED_ID_CREATE(LED_DIAG), "Chassis LED 2 (DIAG LED)", 0, {0} }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_AUTO, + }, + { + { ONLP_LED_ID_CREATE(LED_ALARM), "Chassis LED 3 (ALARM LED)", 0, {0} }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_ON_OFF | ONLP_LED_CAPS_RED, + }, + { + { ONLP_LED_ID_CREATE(LED_FAN), "Chassis LED 4 (FAN LED)", 0, {0} }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_AUTO, + }, + { + { ONLP_LED_ID_CREATE(LED_PSU1), "Chassis LED 5 (PSU1 LED)", 0, {0} }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_AUTO, + }, + { + { ONLP_LED_ID_CREATE(LED_PSU2), "Chassis LED 6 (PSU2 LED)", 0, {0} }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_AUTO, + }, +}; + +static int driver_to_onlp_led_mode(enum onlp_led_id id, enum led_light_mode driver_led_mode) +{ + int i, nsize = sizeof(led_map)/sizeof(led_map[0]); + + for (i = 0; i < nsize; i++) { + if (id == led_map[i].id && driver_led_mode == led_map[i].driver_led_mode) { + return led_map[i].onlp_led_mode; + } + } + + return 0; +} + +static int onlp_to_driver_led_mode(enum onlp_led_id id, onlp_led_mode_t onlp_led_mode) +{ + int i, nsize = sizeof(led_map)/sizeof(led_map[0]); + + for (i = 0; i < nsize; i++) { + if (id == led_map[i].id && onlp_led_mode == led_map[i].onlp_led_mode) { + return led_map[i].driver_led_mode; + } + } + + return 0; +} + +/* + * This function will be called prior to any other onlp_ledi_* functions. + */ +int +onlp_ledi_init(void) +{ + return ONLP_STATUS_OK; +} + +int +onlp_ledi_info_get(onlp_oid_t id, onlp_led_info_t* info) +{ + int lid, value; + VALIDATE(id); + + lid = ONLP_OID_ID_GET(id); + + /* Set the onlp_oid_hdr_t and capabilities */ + *info = linfo[ONLP_OID_ID_GET(id)]; + + /* Get LED mode */ + if (onlp_file_read_int(&value, LED_FORMAT, leds[lid]) < 0) { + return ONLP_STATUS_E_INTERNAL; + } + + info->mode = driver_to_onlp_led_mode(lid, value); + + /* Set the on/off status */ + if (info->mode != ONLP_LED_MODE_OFF) { + info->status |= ONLP_LED_STATUS_ON; + } + + return ONLP_STATUS_OK; +} + +/* + * Turn an LED on or off. + * + * This function will only be called if the LED OID supports the ONOFF + * capability. + * + * What 'on' means in terms of colors or modes for multimode LEDs is + * up to the platform to decide. This is intended as baseline toggle mechanism. + */ +int +onlp_ledi_set(onlp_oid_t id, int on_or_off) +{ + VALIDATE(id); + + if (!on_or_off) { + return onlp_ledi_mode_set(id, ONLP_LED_MODE_OFF); + } + + return ONLP_STATUS_E_UNSUPPORTED; +} + +/* + * This function puts the LED into the given mode. It is a more functional + * interface for multimode LEDs. + * + * Only modes reported in the LED's capabilities will be attempted. + */ +int +onlp_ledi_mode_set(onlp_oid_t id, onlp_led_mode_t mode) +{ + int lid; + VALIDATE(id); + + lid = ONLP_OID_ID_GET(id); + + if (onlp_file_write_int(onlp_to_driver_led_mode(lid , mode), LED_FORMAT, leds[lid]) != 0) { + return ONLP_STATUS_E_INTERNAL; + } + + return ONLP_STATUS_OK; +} diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/make.mk b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/make.mk new file mode 100644 index 0000000000..35d9a74568 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/make.mk @@ -0,0 +1,9 @@ +############################################################################### +# +# +# +############################################################################### + +LIBRARY := x86_64_accton_as7927_50x +$(LIBRARY)_SUBDIR := $(dir $(lastword $(MAKEFILE_LIST))) +include $(BUILDER)/lib.mk diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/platform_lib.c b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/platform_lib.c new file mode 100644 index 0000000000..5877b0a333 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/platform_lib.c @@ -0,0 +1,402 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2014 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * + * + ***********************************************************/ +#include +#include +#include +#include +#include "platform_lib.h" + +enum onlp_psu_type onlp_get_psu_type(int pid) +{ + int hwmon_idx; + int val; + char file[32]; + enum onlp_psu_type type = PSU_TYPE_DC; + + hwmon_idx = onlp_get_psu_hwmon_idx(pid); + if (hwmon_idx < 0) { + return type; + } + + snprintf(file, sizeof(file), "psu%d_type", pid); + + if (onlp_file_read_int(&val, PSU_SYSFS_FORMAT_1, hwmon_idx, file) == 0) { + if (val >= 0 && val < PSU_TYPE_COUNT) { + type = (enum onlp_psu_type)val; + } + } + + return type; +} + +enum onlp_fan_dir onlp_get_fan_dir(int fid) +{ + int len = 0; + int i = 0; + int hwmon_idx; + char *str = NULL; + char *dirs[FAN_DIR_COUNT] = { "F2B", "B2F" }; + char file[32]; + enum onlp_fan_dir dir = FAN_DIR_F2B; + + hwmon_idx = onlp_get_fan_hwmon_idx(); + if (hwmon_idx >= 0) { + /* Read attribute */ + snprintf(file, sizeof(file), "fan%d_dir", fid); + len = onlp_file_read_str(&str, FAN_SYSFS_FORMAT_1, hwmon_idx, file); + + /* Verify Fan dir string length */ + if (!str || len < 3) { + AIM_FREE_IF_PTR(str); + return dir; + } + + for (i = 0; i < AIM_ARRAYSIZE(dirs); i++) { + if (strncmp(str, dirs[i], strlen(dirs[i])) == 0) { + dir = (enum onlp_fan_dir)i; + break; + } + } + + AIM_FREE_IF_PTR(str); + } + + return dir; +} + +int onlp_get_psu_hwmon_idx(int pid) +{ + /* find hwmon index */ + char* file = NULL; + char path[64]; + int ret, hwmon_idx, max_hwmon_idx = 20; + + for (hwmon_idx = 0; hwmon_idx <= max_hwmon_idx; hwmon_idx++) { + snprintf(path, sizeof(path), "/sys/devices/platform/as7927_50x_psu/hwmon/hwmon%d/", hwmon_idx); + + if (pid == 1) + ret = onlp_file_find(path, "psu1_present", &file); + else if (pid == 2) + ret = onlp_file_find(path, "psu2_present", &file); + else + return -1; + + AIM_FREE_IF_PTR(file); + + if (ONLP_STATUS_OK == ret) + return hwmon_idx; + } + + return -1; +} + +int onlp_get_fan_hwmon_idx(void) +{ + /* find hwmon index */ + char* file = NULL; + char path[64]; + int ret, hwmon_idx, max_hwmon_idx = 20; + + for (hwmon_idx = 0; hwmon_idx <= max_hwmon_idx; hwmon_idx++) { + snprintf(path, sizeof(path), "/sys/devices/platform/as7927_50x_fan/hwmon/hwmon%d/", hwmon_idx); + + ret = onlp_file_find(path, "name", &file); + AIM_FREE_IF_PTR(file); + + if (ONLP_STATUS_OK == ret) + return hwmon_idx; + } + + return -1; +} + +int +get_xcvr_presence(void) +{ + onlp_sfp_bitmap_t bitmap; + onlp_sfp_bitmap_t_init(&bitmap); + onlp_sfp_presence_bitmap_get(&bitmap); + return !(AIM_BITMAP_COUNT(&bitmap) == 0); +} + +// ======================================================= +// SFF-8472 (SFP) Temperature and Alarm Getters +// ======================================================= +int +get_sff8472_temp(int port, int *temp) +{ + int value; + int16_t port_temp; + + /* SFF-8472 DDM Support Check is at Address A0h (0x50), Offset 92 */ + value = onlp_sfpi_dev_readb(port, 0x50, 92); + if (value < 0 || !(value & 0x40)) { + *temp = ONLP_STATUS_E_MISSING; + return ONLP_STATUS_E_MISSING; + } + + /* SFF-8472 Temperature is at Address A2h (0x51), Offset 96-97 */ + value = onlp_sfpi_dev_readb(port, 0x51, 96); + if (value < 0) { + *temp = ONLP_STATUS_E_MISSING; + return ONLP_STATUS_E_MISSING; + } + port_temp = (int16_t)((value & 0xFF) << 8); + + value = onlp_sfpi_dev_readb(port, 0x51, 97); + if (value < 0) { + *temp = ONLP_STATUS_E_MISSING; + return ONLP_STATUS_E_MISSING; + } + port_temp = (port_temp | (int16_t)(value & 0xFF)); + + *temp = (int)port_temp * 1000 / 256; + return ONLP_STATUS_OK; +} + +int +get_sff8472_temp_alarm(int port, int *alarm) +{ + int value; + int16_t port_alarm; + + /* SFF-8472 High Alarm is at Address A2h (0x51), Offset 00-01 */ + value = onlp_sfpi_dev_readb(port, 0x51, 0); + if (value < 0) { + *alarm = ONLP_STATUS_E_MISSING; + return ONLP_STATUS_E_MISSING; + } + port_alarm = (int16_t)((value & 0xFF) << 8); + + value = onlp_sfpi_dev_readb(port, 0x51, 1); + if (value < 0) { + *alarm = ONLP_STATUS_E_MISSING; + return ONLP_STATUS_E_MISSING; + } + port_alarm = (port_alarm | (int16_t)(value & 0xFF)); + + *alarm = (int)port_alarm * 1000 / 256; + return ONLP_STATUS_OK; +} + +// ======================================================= +// SFF-8436 (QSFP+) Temperature and Alarm Getters +// ======================================================= +int +get_sff8436_temp(int port, int *temp) +{ + int value; + int16_t port_temp; + + /* QSFP+ Temperature is at Address A0h (0x50), Page 00h, Offset 22-23 */ + value = onlp_sfpi_dev_readb(port, 0x50, 22); + if (value < 0) { + *temp = ONLP_STATUS_E_MISSING; + return ONLP_STATUS_E_MISSING; + } + port_temp = (int16_t)((value & 0xFF) << 8); + + value = onlp_sfpi_dev_readb(port, 0x50, 23); + if (value < 0) { + *temp = ONLP_STATUS_E_MISSING; + return ONLP_STATUS_E_MISSING; + } + port_temp = (port_temp | (int16_t)(value & 0xFF)); + + *temp = (int)port_temp * 1000 / 256; + return ONLP_STATUS_OK; +} + +int +get_sff8436_temp_alarm(int port, int *alarm) +{ + int value; + int16_t port_alarm; + + /* QSFP+ Memory model check is at Address A0h (0x50), Offset 2 */ + value = onlp_sfpi_dev_readb(port, 0x50, 0x2); + if (value < 0 || (value & 0x04)) { + *alarm = ONLP_STATUS_E_MISSING; + return ONLP_STATUS_E_MISSING; + } + + /* QSFP+ Temp High Alarm is at Page 03h,Offset 128-129 */ + if (onlp_sfpi_dev_writeb(port, 0x50, 127, 0x03) < 0) { + *alarm = ONLP_STATUS_E_MISSING; + return ONLP_STATUS_E_MISSING; + } + + value = onlp_sfpi_dev_readb(port, 0x50, 128); + if (value < 0) { + *alarm = ONLP_STATUS_E_MISSING; + onlp_sfpi_dev_writeb(port, 0x50, 127, 0x00); + return ONLP_STATUS_E_MISSING; + } + port_alarm = (int16_t)((value & 0xFF) << 8); + + value = onlp_sfpi_dev_readb(port, 0x50, 129); + if (value < 0) { + *alarm = ONLP_STATUS_E_MISSING; + onlp_sfpi_dev_writeb(port, 0x50, 127, 0x00); + return ONLP_STATUS_E_MISSING; + } + port_alarm = (port_alarm | (int16_t)(value & 0xFF)); + *alarm = (int)port_alarm * 1000 / 256; + + onlp_sfpi_dev_writeb(port, 0x50, 127, 0x00); + return ONLP_STATUS_OK; +} + +// ======================================================= +// CMIS (QSFP-DD) Temperature and Alarm Getters +// ======================================================= +int get_cmis_temp(int port, int *temp) +{ + int value; + int16_t port_temp; + + /* CMIS Temperature is at Address A0h (0x50), Page 00h, Offset 14-15 */ + value = onlp_sfpi_dev_readb(port, 0x50, 14); + if (value < 0) { + *temp = ONLP_STATUS_E_MISSING; + return ONLP_STATUS_E_MISSING; + } + port_temp = (int16_t)((value & 0xFF) << 8); + + value = onlp_sfpi_dev_readb(port, 0x50, 15); + if (value < 0) { + *temp = ONLP_STATUS_E_MISSING; + return ONLP_STATUS_E_MISSING; + } + port_temp = (port_temp | (int16_t)(value & 0xFF)); + + *temp = (int)port_temp * 1000 / 256; + return ONLP_STATUS_OK; +} + +int get_cmis_temp_alarm(int port, int *alarm) +{ + int value; + int16_t port_alarm; + + /* CMIS Memory model check is at Address A0h (0x50), Offset 2 */ + value = onlp_sfpi_dev_readb(port, 0x50, 0x2); + if (value < 0 || (value & 0x80)) { + *alarm = ONLP_STATUS_E_MISSING; + return ONLP_STATUS_E_MISSING; + } + + /* CMIS Temp High Alarm is at Page 02h,Offset 128-129 */ + if (onlp_sfpi_dev_writeb(port, 0x50, 127, 0x02) < 0) { + *alarm = ONLP_STATUS_E_MISSING; + return ONLP_STATUS_E_MISSING; + } + + value = onlp_sfpi_dev_readb(port, 0x50, 128); + if (value < 0) { + *alarm = ONLP_STATUS_E_MISSING; + onlp_sfpi_dev_writeb(port, 0x50, 127, 0x00); + return ONLP_STATUS_E_MISSING; + } + port_alarm = (int16_t)((value & 0xFF) << 8); + + value = onlp_sfpi_dev_readb(port, 0x50, 129); + if (value < 0) { + *alarm = ONLP_STATUS_E_MISSING; + onlp_sfpi_dev_writeb(port, 0x50, 127, 0x00); + return ONLP_STATUS_E_MISSING; + } + port_alarm = (port_alarm | (int16_t)(value & 0xFF)); + *alarm = (int)port_alarm * 1000 / 256; + + onlp_sfpi_dev_writeb(port, 0x50, 127, 0x00); + return ONLP_STATUS_OK; +} + +int +get_xcvr_temp(temp_reader_data_t *temp) +{ + int ret = ONLP_STATUS_OK; + int value, port; + int port_temp = ONLP_STATUS_E_MISSING, port_alarm = ONLP_STATUS_E_MISSING; + + memset(temp, 0, sizeof(temp_reader_data_t)); + + if (!get_xcvr_presence()) { + return ONLP_STATUS_OK; + } + + for (port = 1; port <= PORT_NUM; port++) { + temp->ports[port].temp = ONLP_STATUS_E_MISSING; + temp->ports[port].high_alarm = ONLP_STATUS_E_MISSING; + temp->ports[port].present = 0; + + port_temp = ONLP_STATUS_E_MISSING; + port_alarm = ONLP_STATUS_E_MISSING; + + if (onlp_sfpi_is_present(port) != 1) { + continue; + } + + temp->ports[port].present = 1; + + value = onlp_sfpi_dev_readb(port, 0x50, 0); + if (value < 0) { + AIM_LOG_ERROR("Unable to get read port(%d) eeprom\r\n", port); + continue; + } + + if (value == 0x18 || value == 0x19 || value == 0x1E) { + ret = get_cmis_temp(port, &port_temp); + if (ret != ONLP_STATUS_OK) { + continue; + } + get_cmis_temp_alarm(port, &port_alarm); + } + else if (value == 0x0C || value == 0x0D || value == 0x11 || value == 0xE1) { + ret = get_sff8436_temp(port, &port_temp); + if (ret != ONLP_STATUS_OK) { + continue; + } + get_sff8436_temp_alarm(port, &port_alarm); + } + else if(value == 0x03 || value == 0x0b) { + ret = get_sff8472_temp(port, &port_temp); + if (ret != ONLP_STATUS_OK) { + continue; + } + get_sff8472_temp_alarm(port, &port_alarm); + } + else { + continue; + } + + temp->ports[port].temp = port_temp; + temp->ports[port].high_alarm = (port_alarm != ONLP_STATUS_E_MISSING) ? port_alarm : 75000; + } + + return ONLP_STATUS_OK; +} diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/platform_lib.h b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/platform_lib.h new file mode 100644 index 0000000000..bff59e6dd2 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/platform_lib.h @@ -0,0 +1,143 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2014 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * + * + ***********************************************************/ +#ifndef __PLATFORM_LIB_H__ +#define __PLATFORM_LIB_H__ + +#include +#include +#include "x86_64_accton_as7927_50x_log.h" + +#define CHASSIS_FAN_COUNT 14 +#define CHASSIS_THERMAL_COUNT 17 +#define CHASSIS_LED_COUNT 6 +#define CHASSIS_PSU_COUNT 2 +#define NUM_OF_THERMAL_PER_PSU 3 + +#define PSU1_ID 1 +#define PSU2_ID 2 + +#define PSU_SYSFS_FORMAT "/sys/devices/platform/as7927_50x_psu*psu%d_%s" +#define PSU_SYSFS_FORMAT_1 "/sys/devices/platform/as7927_50x_psu/hwmon/hwmon%d/%s" +#define FAN_SYSFS_FORMAT "/sys/devices/platform/as7927_50x_fan*" +#define FAN_SYSFS_FORMAT_1 "/sys/devices/platform/as7927_50x_fan/hwmon/hwmon%d/%s" +#define SYS_LED_PATH "/sys/devices/platform/as7927_50x_led/" +#define IDPROM_PATH "/sys/bus/platform/devices/as7927_50x_sys/eeprom" +#define BIOS_VER_PATH "/sys/devices/virtual/dmi/id/bios_version" +#define BMC_VER1_PATH "/sys/devices/platform/ipmi_bmc.0/firmware_revision" +#define BMC_VER2_PATH "/sys/devices/platform/ipmi_bmc.0/aux_firmware_revision" +#define BMC_THERMAL_DATA_PATH "/sys/devices/platform/as7927_50x_sys/bmc_thermal_data" + +enum onlp_thermal_id { + THERMAL_RESERVED = 0, + THERMAL_1_CPU_CORE, + THERMAL_2_CPU_CORE, + THERMAL_3_CPU_CORE, + THERMAL_4_CPU_CORE, + THERMAL_5_CPU_CORE, + THERMAL_1_ON_CARRIER_BROAD, + THERMAL_2_ON_CARRIER_BROAD, + THERMAL_3_ON_MAIN_BROAD, + THERMAL_4_ON_IO_BROAD, + THERMAL_5_ON_MAIN_BROAD, + THERMAL_6_ON_MAIN_BROAD, + THERMAL_7_ON_MAC_BROAD, + THERMAL_8_ON_MAC_BROAD, + THERMAL_9_ON_MAC_BROAD, + THERMAL_10_ON_MAC_BROAD, + THERMAL_1_ON_FAN_BROAD, + THERMAL_2_ON_FAN_BROAD, + THERMAL_1_ON_PSU1, + THERMAL_2_ON_PSU1, + THERMAL_3_ON_PSU1, + THERMAL_1_ON_PSU2, + THERMAL_2_ON_PSU2, + THERMAL_3_ON_PSU2, + THERMAL_COUNT +}; + +enum onlp_led_id { + LED_LOC = 1, + LED_DIAG, + LED_ALARM, + LED_FAN, + LED_PSU1, + LED_PSU2 +}; + +enum onlp_fan_dir { + FAN_DIR_F2B, + FAN_DIR_B2F, + FAN_DIR_COUNT +}; + +enum onlp_psu_type { + PSU_TYPE_DC, + PSU_TYPE_AC, + PSU_TYPE_COUNT +}; + +typedef enum as7927_50x_platform_id { + as7927_50x, + PID_UNKNOWN +} as7927_50x_platform_id_t; + +#define PORT_NUM 50 +#define LAST_OF_SFP_PORT 48 + +typedef struct port_thermal_data { + int present; + int temp; + int high_alarm; +} port_thermal_data_t; + +typedef struct temp_reader_data { + port_thermal_data_t ports[PORT_NUM + 1]; +} temp_reader_data_t; + +enum onlp_fan_dir onlp_get_fan_dir(int fid); +enum onlp_psu_type onlp_get_psu_type(int pid); +int onlp_get_psu_hwmon_idx(int pid); +int onlp_get_fan_hwmon_idx(void); + +int get_xcvr_presence(void); +int get_sff8472_temp(int port, int *temp); +int get_sff8472_temp_alarm(int port, int *alarm); +int get_sff8436_temp(int port, int *temp); +int get_sff8436_temp_alarm(int port, int *alarm); +int get_cmis_temp(int port, int *temp); +int get_cmis_temp_alarm(int port, int *alarm); +int get_xcvr_temp(temp_reader_data_t *temp); + +#define AIM_FREE_IF_PTR(p) \ + do \ + { \ + if (p) { \ + aim_free(p); \ + p = NULL; \ + } \ + } while (0) + +#endif /* __PLATFORM_LIB_H__ */ diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/psui.c b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/psui.c new file mode 100644 index 0000000000..beb8a58b05 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/psui.c @@ -0,0 +1,177 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2014 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * + * + ***********************************************************/ +#include +#include +#include "platform_lib.h" + +#define PSU_STATUS_PRESENT 1 +#define PSU_STATUS_POWER_GOOD 1 + +#define VALIDATE(_id) \ + do { \ + if(!ONLP_OID_IS_PSU(_id)) { \ + return ONLP_STATUS_E_INVALID; \ + } \ + } while(0) + +int +onlp_psui_init(void) +{ + return ONLP_STATUS_OK; +} + +/* + * Get all information about the given PSU oid. + */ +static onlp_psu_info_t pinfo[] = { + { }, /* Not used */ + { + { ONLP_PSU_ID_CREATE(PSU1_ID), "PSU-1", 0, {0} }, + }, + { + { ONLP_PSU_ID_CREATE(PSU2_ID), "PSU-2", 0, {0} }, + } +}; + +int +onlp_psui_info_get(onlp_oid_t id, onlp_psu_info_t* info) +{ + int val = 0; + int ret = ONLP_STATUS_OK; + int pid = ONLP_OID_ID_GET(id); + int thermal_count = 0; + int hwmon_idx; + VALIDATE(id); + + memset(info, 0, sizeof(onlp_psu_info_t)); + *info = pinfo[pid]; /* Set the onlp_oid_hdr_t */ + + /* Get the present state */ + ret = onlp_file_read_int(&val, PSU_SYSFS_FORMAT, pid, "present"); + if (ret < 0) { + info->status &= ~ONLP_PSU_STATUS_PRESENT; + return ONLP_STATUS_E_INTERNAL; + } + + if (val != PSU_STATUS_PRESENT) { + info->status &= ~ONLP_PSU_STATUS_PRESENT; + return ONLP_STATUS_OK; + } + info->status |= ONLP_PSU_STATUS_PRESENT; + + /* Get power good status */ + ret = onlp_file_read_int(&val, PSU_SYSFS_FORMAT, pid, "power_good"); + if (ret < 0) { + info->status |= ONLP_PSU_STATUS_FAILED; + return ONLP_STATUS_E_INTERNAL; + } + + if (val != PSU_STATUS_POWER_GOOD) { + info->status |= ONLP_PSU_STATUS_UNPLUGGED; + } + + /* Set capability + */ + info->caps = ONLP_PSU_CAPS_AC; + + /* Set the associated oid_table */ + thermal_count = CHASSIS_THERMAL_COUNT; + + info->hdr.coids[0] = ONLP_THERMAL_ID_CREATE(thermal_count + (pid-1)*NUM_OF_THERMAL_PER_PSU + 1); + info->hdr.coids[1] = ONLP_THERMAL_ID_CREATE(thermal_count + (pid-1)*NUM_OF_THERMAL_PER_PSU + 2); + info->hdr.coids[2] = ONLP_THERMAL_ID_CREATE(thermal_count + (pid-1)*NUM_OF_THERMAL_PER_PSU + 3); + info->hdr.coids[3] = ONLP_FAN_ID_CREATE(pid + CHASSIS_FAN_COUNT); + + /* Read voltage, current and power */ + val = 0; + ret = onlp_file_read_int(&val, PSU_SYSFS_FORMAT, pid, "vin"); + if (ret == ONLP_STATUS_OK && val) { + info->mvin = val; + info->caps |= ONLP_PSU_CAPS_VIN; + } + + val = 0; + ret = onlp_file_read_int(&val, PSU_SYSFS_FORMAT, pid, "iin"); + if (ret == ONLP_STATUS_OK && val) { + info->miin = val; + info->caps |= ONLP_PSU_CAPS_IIN; + } + + val = 0; + ret = onlp_file_read_int(&val, PSU_SYSFS_FORMAT, pid, "pin"); + if (ret == ONLP_STATUS_OK && val) { + info->mpin = val; + info->caps |= ONLP_PSU_CAPS_PIN; + } + + val = 0; + ret = onlp_file_read_int(&val, PSU_SYSFS_FORMAT, pid, "vout"); + if (ret == ONLP_STATUS_OK && val) { + info->mvout = val; + info->caps |= ONLP_PSU_CAPS_VOUT; + } + + val = 0; + ret = onlp_file_read_int(&val, PSU_SYSFS_FORMAT, pid, "iout"); + if (ret == ONLP_STATUS_OK && val) { + info->miout = val; + info->caps |= ONLP_PSU_CAPS_IOUT; + } + + val = 0; + ret = onlp_file_read_int(&val, PSU_SYSFS_FORMAT, pid, "pout"); + if (ret == ONLP_STATUS_OK && val) { + info->mpout = val; + info->caps |= ONLP_PSU_CAPS_POUT; + } + + hwmon_idx = onlp_get_psu_hwmon_idx(pid); + if (hwmon_idx >= 0) { + char *str = NULL; + int len; + char file[32]; + + /* Read model */ + snprintf(file, sizeof(file), "psu%d_model", pid); + len = onlp_file_read_str(&str, PSU_SYSFS_FORMAT_1, hwmon_idx, file); + if (str && len) { + memcpy(info->model, str, len); + info->model[len] = '\0'; + } + AIM_FREE_IF_PTR(str); + + /* Read serial */ + snprintf(file, sizeof(file), "psu%d_serial", pid); + len = onlp_file_read_str(&str, PSU_SYSFS_FORMAT_1, hwmon_idx, file); + if (str && len) { + memcpy(info->serial, str, len); + info->serial[len] = '\0'; + } + AIM_FREE_IF_PTR(str); + } + + return ret; +} diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/sfpi.c b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/sfpi.c new file mode 100644 index 0000000000..5d1b647b4c --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/sfpi.c @@ -0,0 +1,326 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2013 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * + * + ***********************************************************/ +#include +#include +#include +#include "x86_64_accton_as7927_50x_int.h" +#include "x86_64_accton_as7927_50x_log.h" + +#define VALIDATE_SFP(_port) \ + do { \ + if (_port < 1 || _port > 48) \ + return ONLP_STATUS_E_UNSUPPORTED; \ + } while(0) + +#define VALIDATE_QSFP(_port) \ + do { \ + if (_port < 49 || _port > 50 ) \ + return ONLP_STATUS_E_UNSUPPORTED; \ + } while(0) + +#define MODULE_EEPROM_FORMAT "/sys/bus/i2c/devices/%d-0050/eeprom" +#define MODULE_PRESENT_FORMAT "/sys/bus/platform/devices/as7927_50x_fpga/module_present_%d" +#define MODULE_RXLOS_FORMAT "/sys/bus/platform/devices/as7927_50x_fpga/module_rx_los_%d" +#define MODULE_TXFAULT_FORMAT "//sys/bus/platform/devices/as7927_50x_fpga/module_tx_fault_%d" +#define MODULE_TXDISABLE_FORMAT "/sys/bus/platform/devices/as7927_50x_fpga/module_tx_disable_%d" +#define MODULE_RESET_FORMAT "/sys/bus/platform/devices/as7927_50x_fpga/module_reset_%d" +#define MODULE_LPMODE_FORMAT "/sys/bus/platform/devices/as7927_50x_fpga/module_lpmode_%d" + +#define NUM_OF_PORT 50 +#define NUM_OF_SFP_PORT 48 +static const int port_bus_index[NUM_OF_PORT] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50 +}; + +#define PORT_BUS_INDEX(port) (port_bus_index[port-1]) + +/************************************************************ + * + * SFPI Entry Points + * + ***********************************************************/ +int +onlp_sfpi_init(void) +{ + /* Called at initialization time */ + return ONLP_STATUS_OK; +} + +int +onlp_sfpi_bitmap_get(onlp_sfp_bitmap_t* bmap) +{ + int p; + + for(p = 1; p <= NUM_OF_PORT; p++) { + AIM_BITMAP_SET(bmap, p); + } + + return ONLP_STATUS_OK; +} + +int +onlp_sfpi_is_present(int port) +{ + /* + * Return 1 if present. + * Return 0 if not present. + * Return < 0 if error. + */ + int present; + + if (onlp_file_read_int(&present, MODULE_PRESENT_FORMAT, port) < 0) { + AIM_LOG_ERROR("Unable to read present status from port(%d)\r\n", port); + return ONLP_STATUS_E_INTERNAL; + } + + return present; +} + +int onlp_sfpi_rx_los_bitmap_get(onlp_sfp_bitmap_t* dst) +{ + int i = 1, value = 0, present = 0; + + for (i = 1; i <= NUM_OF_PORT; i++) + { + if (i <= NUM_OF_SFP_PORT) + { + if (onlp_file_read_int(&value, MODULE_RXLOS_FORMAT, i) < 0) { + AIM_LOG_ERROR("Unable to read rx_loss status from port(%d)\r\n", i); + return ONLP_STATUS_E_INTERNAL; + } + + if (onlp_file_read_int(&present, MODULE_PRESENT_FORMAT, i) < 0) { + AIM_LOG_ERROR("Unable to read present status from port(%d)\r\n", i); + return ONLP_STATUS_E_INTERNAL; + } + + if ((value) && (present)) + AIM_BITMAP_MOD(dst, i, 1); + else + AIM_BITMAP_MOD(dst, i, 0); + } + else + { + AIM_BITMAP_MOD(dst, i, 0); + } + } + return ONLP_STATUS_OK; +} + +int +onlp_sfpi_eeprom_read(int port, uint8_t data[256]) +{ + /* + * Read the SFP eeprom into data[] + * + * Return MISSING if SFP is missing. + * Return OK if eeprom is read + */ + int size = 0; + memset(data, 0, 256); + + if(onlp_file_read(data, 256, &size, MODULE_EEPROM_FORMAT, PORT_BUS_INDEX(port)) != ONLP_STATUS_OK) { + AIM_LOG_ERROR("Unable to read eeprom from port(%d)\r\n", port); + return ONLP_STATUS_E_INTERNAL; + } + + if (size != 256) { + AIM_LOG_ERROR("Unable to read eeprom from port(%d), size is different!\r\n", port); + return ONLP_STATUS_E_INTERNAL; + } + + return ONLP_STATUS_OK; +} + +int +onlp_sfpi_dom_read(int port, uint8_t data[256]) +{ + FILE* fp; + char file[64] = {0}; + + sprintf(file, MODULE_EEPROM_FORMAT, PORT_BUS_INDEX(port)); + fp = fopen(file, "r"); + if(fp == NULL) { + AIM_LOG_ERROR("Unable to open the eeprom device file of port(%d)", port); + return ONLP_STATUS_E_INTERNAL; + } + + if (fseek(fp, 256, SEEK_CUR) != 0) { + fclose(fp); + AIM_LOG_ERROR("Unable to set the file position indicator of port(%d)", port); + return ONLP_STATUS_E_INTERNAL; + } + + int ret = fread(data, 1, 256, fp); + fclose(fp); + if (ret != 256) { + AIM_LOG_ERROR("Unable to read the module_eeprom device file of port(%d)", port); + return ONLP_STATUS_E_INTERNAL; + } + + return ONLP_STATUS_OK; +} + +int +onlp_sfpi_dev_readb(int port, uint8_t devaddr, uint8_t addr) +{ + int bus = PORT_BUS_INDEX(port); + return onlp_i2c_readb(bus, devaddr, addr, ONLP_I2C_F_FORCE); +} + +int +onlp_sfpi_dev_writeb(int port, uint8_t devaddr, uint8_t addr, uint8_t value) +{ + int bus = PORT_BUS_INDEX(port); + return onlp_i2c_writeb(bus, devaddr, addr, value, ONLP_I2C_F_FORCE); +} + +int +onlp_sfpi_dev_readw(int port, uint8_t devaddr, uint8_t addr) +{ + int bus = PORT_BUS_INDEX(port); + return onlp_i2c_readw(bus, devaddr, addr, ONLP_I2C_F_FORCE); +} + +int +onlp_sfpi_dev_writew(int port, uint8_t devaddr, uint8_t addr, uint16_t value) +{ + int bus = PORT_BUS_INDEX(port); + return onlp_i2c_writew(bus, devaddr, addr, value, ONLP_I2C_F_FORCE); +} + +int +onlp_sfpi_control_set(int port, onlp_sfp_control_t control, int value) +{ + switch(control) { + case ONLP_SFP_CONTROL_TX_DISABLE: { + VALIDATE_SFP(port); + + if (onlp_file_write_int(value, MODULE_TXDISABLE_FORMAT, port) < 0) { + AIM_LOG_ERROR("Unable to set tx disable status to port(%d)\r\n", port); + return ONLP_STATUS_E_INTERNAL; + } + + return ONLP_STATUS_OK; + } + case ONLP_SFP_CONTROL_RESET_STATE: { + VALIDATE_QSFP(port); + + if (onlp_file_write_int(value, MODULE_RESET_FORMAT, port) < 0) { + AIM_LOG_ERROR("Unable to write reset status to port(%d)\r\n", port); + return ONLP_STATUS_E_INTERNAL; + } + + return ONLP_STATUS_OK; + } + case ONLP_SFP_CONTROL_LP_MODE: { + VALIDATE_QSFP(port); + + if (onlp_file_write_int(value, MODULE_LPMODE_FORMAT, port) < 0) { + AIM_LOG_ERROR("Unable to write lp mode status to port(%d)\r\n", port); + return ONLP_STATUS_E_INTERNAL; + } + + return ONLP_STATUS_OK; + } + default: + break; + } + + return ONLP_STATUS_E_UNSUPPORTED; +} + +int +onlp_sfpi_control_get(int port, onlp_sfp_control_t control, int* value) +{ + switch(control) { + case ONLP_SFP_CONTROL_RX_LOS: { + VALIDATE_SFP(port); + + if (onlp_file_read_int(value, MODULE_RXLOS_FORMAT, port) < 0) { + AIM_LOG_ERROR("Unable to read rx loss status from port(%d)\r\n", port); + return ONLP_STATUS_E_INTERNAL; + } + + return ONLP_STATUS_OK; + } + + case ONLP_SFP_CONTROL_TX_FAULT: { + VALIDATE_SFP(port); + + if (onlp_file_read_int(value, MODULE_TXFAULT_FORMAT, port) < 0) { + AIM_LOG_ERROR("Unable to read tx fault status from port(%d)\r\n", port); + return ONLP_STATUS_E_INTERNAL; + } + + return ONLP_STATUS_OK; + } + + case ONLP_SFP_CONTROL_TX_DISABLE: { + VALIDATE_SFP(port); + + if (onlp_file_read_int(value, MODULE_TXDISABLE_FORMAT, port) < 0) { + AIM_LOG_ERROR("Unable to read tx disabled status from port(%d)\r\n", port); + return ONLP_STATUS_E_INTERNAL; + } + + return ONLP_STATUS_OK; + } + case ONLP_SFP_CONTROL_RESET_STATE: { + VALIDATE_QSFP(port); + + if (onlp_file_read_int(value, MODULE_RESET_FORMAT, port) < 0) { + AIM_LOG_ERROR("Unable to read reset status from port(%d)\r\n", port); + return ONLP_STATUS_E_INTERNAL; + } + + return ONLP_STATUS_OK; + } + case ONLP_SFP_CONTROL_LP_MODE: { + VALIDATE_QSFP(port); + + if (onlp_file_read_int(value, MODULE_LPMODE_FORMAT, port) < 0) { + AIM_LOG_ERROR("Unable to read lp mode status from port(%d)\r\n", port); + return ONLP_STATUS_E_INTERNAL; + } + + return ONLP_STATUS_OK; + } + default: + break; + } + + return ONLP_STATUS_E_UNSUPPORTED; +} + +int +onlp_sfpi_denit(void) +{ + return ONLP_STATUS_OK; +} diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/sysi.c b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/sysi.c new file mode 100644 index 0000000000..ba91c8c4ef --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/sysi.c @@ -0,0 +1,428 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2014 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * + * + ***********************************************************/ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "platform_lib.h" + +#include "x86_64_accton_as7927_50x_int.h" +#include "x86_64_accton_as7927_50x_log.h" + +#define NUM_OF_CPLD_VER 7 +#define BMC_FILE_RETRY_COUNT 3 // Retry count for file read/write operations +#define BMC_FILE_RETRY_DELAY_S 1 // Delay between retries (in seconds, 1s) + +#define MODULE_EFUSE_FORMAT "/sys/bus/platform/devices/as7927_50x_fpga/module_efuse_%d" + +int xcvr_over_temp_protector(int port); + +enum temp_sensors { + TEMP_SENSOR_XCVR, + TEMP_SENSOR_COUNT +}; + +typedef int (*temp_getter_t)(temp_reader_data_t *temp); +typedef int (*ot_protector_t)(int port); + +typedef struct temp_handler { + temp_getter_t temp_readers[TEMP_SENSOR_COUNT]; +} temp_handler_t; + +/* over temp protection */ +typedef struct otp_handler { + ot_protector_t otp_writer; +} otp_handler_t; + +struct thermal_policy_manager { + temp_handler_t temp_hdlr; + otp_handler_t otp_hdlr; /* over temp protector */ +}; + +struct thermal_policy_manager tp_mgr = { + .temp_hdlr = { + .temp_readers = { + [TEMP_SENSOR_XCVR] = get_xcvr_temp + } + }, + .otp_hdlr = { + .otp_writer = xcvr_over_temp_protector + } +}; + +static char* cpld_ver_path[NUM_OF_CPLD_VER] = { + "/sys/bus/platform/devices/as7927_50x_sys/come_e_cpld_ver", /* CPU CPLD */ + "/sys/bus/platform/devices/as7927_50x_sys/sys_cpld_ver", /* System CPLD */ + "/sys/bus/platform/devices/as7927_50x_fpga/cpld1_version", /* Port CPLD1 */ + "/sys/bus/platform/devices/as7927_50x_fpga/cpld2_version", /* Port CPLD2 */ + "/sys/bus/platform/devices/as7927_50x_sys/dcscm_cpld_ver", /* DC-SCM CPLD */ + "/sys/bus/platform/devices/as7927_50x_sys/fan_cpld_ver", /* Fan CPLD */ + "/sys/bus/platform/devices/as7927_50x_sys/fpga_cpld_ver", /* FPGA */ +}; + +const char* +onlp_sysi_platform_get(void) +{ + return "x86-64-accton-as7927-50x-r0"; +} + +int +onlp_sysi_onie_data_get(uint8_t** data, int* size) +{ + uint8_t* rdata = aim_zmalloc(256); + if (onlp_file_read(rdata, 256, size, IDPROM_PATH) == ONLP_STATUS_OK) { + if(*size == 256) { + *data = rdata; + return ONLP_STATUS_OK; + } + } + + aim_free(rdata); + *size = 0; + return ONLP_STATUS_E_INTERNAL; +} + +int +onlp_sysi_oids_get(onlp_oid_t* table, int max) +{ + int i; + onlp_oid_t* e = table; + memset(table, 0, max*sizeof(onlp_oid_t)); + + /* 9 Thermal sensors on the chassis */ + for (i = 1; i <= CHASSIS_THERMAL_COUNT; i++) { + *e++ = ONLP_THERMAL_ID_CREATE(i); + } + + /* 5 LEDs on the chassis */ + for (i = 1; i <= CHASSIS_LED_COUNT; i++) { + *e++ = ONLP_LED_ID_CREATE(i); + } + + /* 2 PSUs on the chassis */ + for (i = 1; i <= CHASSIS_PSU_COUNT; i++) { + *e++ = ONLP_PSU_ID_CREATE(i); + } + + /* 8 Fans on the chassis */ + for (i = 1; i <= CHASSIS_FAN_COUNT; i++) { + *e++ = ONLP_FAN_ID_CREATE(i); + } + + return 0; +} + + +int +onlp_sysi_platform_info_get(onlp_platform_info_t* pi) +{ + int i, len, ret = ONLP_STATUS_OK; + char *v[NUM_OF_CPLD_VER] = {NULL}; + char *bmc_buf = NULL; + char *aux_buf = NULL; + int bmc_major = 0, bmc_minor = 0; + unsigned int bmc_aux[4] = {0}; + char bmc_ver[16] = ""; + onlp_onie_info_t onie; + char *bios_ver = NULL; + + for (i = 0; i < AIM_ARRAYSIZE(cpld_ver_path); i++) { + + len = onlp_file_read_str(&v[i], cpld_ver_path[i]); + + if (v[i] == NULL || len <= 0) + return ONLP_STATUS_E_INTERNAL; + } + + onlp_file_read_str(&bios_ver, BIOS_VER_PATH); + onlp_onie_decode_file(&onie, IDPROM_PATH); + + if ((onlp_file_read_str(&bmc_buf, BMC_VER1_PATH) >= 0) && + (onlp_file_read_str(&aux_buf, BMC_VER2_PATH) >= 0)) + { + bmc_buf[strcspn(bmc_buf, "\n")] = '\0'; + aux_buf[strcspn(aux_buf, "\n")] = '\0'; + + /* + * NOTE: The value in /sys/devices/platform/ipmi_bmc.0/firmware_revision is formatted + * using "%u.%x" in the kernel driver (see ipmi_msghandler.c::firmware_revision_show). + * The second field (after the dot) is output in hexadecimal format and must be parsed + * using "%x" from user-space. + */ + if (sscanf(bmc_buf, "%u.%x", &bmc_major, &bmc_minor) == 2 && + sscanf(aux_buf, "0x%x 0x%x 0x%x 0x%x", &bmc_aux[0], &bmc_aux[1], &bmc_aux[2], &bmc_aux[3]) == 4) + { + snprintf(bmc_ver, sizeof(bmc_ver), "%02X.%02X.%02X", + bmc_major, bmc_minor, bmc_aux[3]); + } + } + + pi->cpld_versions = aim_fstrdup("\r\n\t CPU(0x21):%s" + "\r\n\t Main(0x61):%s" + "\r\n\t Main(0x62):%s" + "\r\n\t Main(0x63):%s" + "\r\n\t Carrier(0x60):%s" + "\r\n\t Fan(0x33):%s" + , v[0], v[1], v[2], v[3], v[4], v[5]); + + pi->other_versions = aim_fstrdup("\r\n\t FPGA(0x60):%s" + "\r\n\t BIOS: %s" + "\r\n\t ONIE: %s" + "\r\n\t BMC: %s", + v[6], bios_ver, onie.onie_version, bmc_ver); + + for (i = 0; i < AIM_ARRAYSIZE(v); i++) { + AIM_FREE_IF_PTR(v[i]); + } + + AIM_FREE_IF_PTR(bmc_buf); + AIM_FREE_IF_PTR(aux_buf); + AIM_FREE_IF_PTR(bios_ver); + onlp_onie_info_free(&onie); + + return ret; +} + +void +onlp_sysi_platform_info_free(onlp_platform_info_t* pi) +{ + aim_free(pi->cpld_versions); + aim_free(pi->other_versions); +} + +int +xcvr_over_temp_protector(int port) +{ + int ret = ONLP_STATUS_E_INTERNAL; + AIM_SYSLOG_CRIT("Temperature critical", "OTP Action", + "Critical temperature detected on port %d; performing OTP protect action!", port); + + // SFP + if (port > 0 && port <= LAST_OF_SFP_PORT){ + ret = onlp_file_write_int(0, MODULE_EFUSE_FORMAT, port); + if (ret != ONLP_STATUS_OK){ + AIM_LOG_ERROR("Unable to write e-fuse status from port(%d)\r\n", port); + } + } + // QSFP + else if (port > LAST_OF_SFP_PORT && port <= PORT_NUM){ + ret = onlp_sfpi_control_set(port, ONLP_SFP_CONTROL_RESET_STATE, 1); + if (ret != ONLP_STATUS_OK){ + AIM_LOG_ERROR("Unable to write reset status to port(%d)\r\n", port); + } + } + + return ret; +} + +/* + * Send thermal data (MAC temp, XCVR temp, port number) to BMC. + * + * This writes to the BMC thermal policy interface, equivalent to: + * ipmitool raw 0x34 0x13 + * + * Temperatures are in millidegree Celsius and converted to degrees Celsius before sending. + * + * @param mac_temp MAC sensor temperature in milli-degrees Celsius + * @param xcvr_temp Transceiver temperature in milli-degrees Celsius + * @param xcvr_num Transceiver port number + * + * @return ONLP_STATUS_OK on success + * ONLP_STATUS_E_INTERNAL if formatting fails + * ONLP_STATUS_E_MISSING if writing to BMC fails + */ +int +send_thermal_data_to_bmc(int mac_temp, int xcvr_temp, int xcvr_num) +{ + char data[64]; + int ret = ONLP_STATUS_E_INTERNAL; + + if (xcvr_temp == 0) { + xcvr_num = 0; + } + + ret = snprintf(data, sizeof(data), "%d %d %d", + (mac_temp / 1000), (xcvr_temp / 1000), xcvr_num); + if (ret < 0 || ret >= (int)sizeof(data)) { + AIM_LOG_WARN("snprintf failed or truncated: mac=%d xcvr=%d port=%d (ret=%d)\n", + mac_temp, xcvr_temp, xcvr_num, ret); + return ONLP_STATUS_E_INTERNAL; + } + + for (int i = 0; i < BMC_FILE_RETRY_COUNT; i++) { + ret = onlp_file_write_str(data, BMC_THERMAL_DATA_PATH); + if (ret == ONLP_STATUS_OK) { + return ONLP_STATUS_OK; + } + sleep(BMC_FILE_RETRY_DELAY_S); + } + + AIM_LOG_ERROR("Failed to write '%s' to %s", data , BMC_THERMAL_DATA_PATH); + return ONLP_STATUS_E_MISSING; +} + +/* + * Control BMC thermal policy by collecting and sending temperature data. + * + *This function performs the following: + * . Reads transceiver temperatures via registered readers. + * . The MAC temperature will Sends 0 because it is not being used. + * . Sends the collected data to the BMC for thermal management. + * + * @return ONLP_STATUS_OK on success, + * ONLP_STATUS_E_MISSING if: + * - sending thermal data fails. + */ +int +control_thermal_policy_via_bmc(void) +{ + int i, p; + int max_temp = ONLP_STATUS_E_MISSING; + int max_port = ONLP_STATUS_E_MISSING; + temp_reader_data_t temp[TEMP_SENSOR_COUNT] = {0}; + static bool port_otp_triggered[PORT_NUM + 1] = {false}; + + for (i = 0; i < AIM_ARRAYSIZE(temp); i++) { + tp_mgr.temp_hdlr.temp_readers[i](&temp[i]); + } + + for (p = 1; p <= PORT_NUM; p++) { + int present = temp[TEMP_SENSOR_XCVR].ports[p].present; + int current_temp = temp[TEMP_SENSOR_XCVR].ports[p].temp; + int high_alarm = temp[TEMP_SENSOR_XCVR].ports[p].high_alarm; + + if (!present || current_temp == ONLP_STATUS_E_MISSING) { + port_otp_triggered[p] = false; + continue; + } + + if (max_temp == ONLP_STATUS_E_MISSING || current_temp > max_temp) { + max_temp = current_temp; + max_port = p; + } + + if (high_alarm != ONLP_STATUS_E_MISSING) { + if (current_temp >= high_alarm) { + if (!port_otp_triggered[p]) { + AIM_LOG_WARN("Port %d temperature (%d mC) exceeded high alarm (%d mC)! Triggering OTP.\n", + p, current_temp, high_alarm); + tp_mgr.otp_hdlr.otp_writer(p); + port_otp_triggered[p] = true; + } + } + else { + port_otp_triggered[p] = false; + } + } + else { + port_otp_triggered[p] = false; + } + } + + if (max_temp == ONLP_STATUS_E_MISSING) { + max_temp = 0; + max_port = 0; + } + int mac_temp = 0; // MAC temperature not required + + return send_thermal_data_to_bmc(mac_temp, max_temp, max_port); +} + +static pthread_mutex_t thermal_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t thermal_cond = PTHREAD_COND_INITIALIZER; +static bool thermal_thread_started = false; +static bool thermal_thread_waiting = false; +static pthread_t thermal_thread; + +/* + * Thermal policy thread loop. + * + * This background thread waits for a condition signal and runs the thermal policy. + * It uses a condition variable to sleep until triggered, and only one instance runs at a time. + * + * Note: Signals are ignored if the thread is busy. No event queueing. + */ +void +*thermal_policy_thread_loop(void *arg) +{ + while (1) { + pthread_mutex_lock(&thermal_lock); + thermal_thread_waiting = true; + pthread_cond_wait(&thermal_cond, &thermal_lock); + thermal_thread_waiting = false; + pthread_mutex_unlock(&thermal_lock); + + control_thermal_policy_via_bmc(); + } + + return NULL; +} + +void +start_thermal_policy_thread_once(void) +{ + pthread_mutex_lock(&thermal_lock); + if (!thermal_thread_started) { + thermal_thread_started = true; + if (pthread_create(&thermal_thread, NULL, thermal_policy_thread_loop, NULL) != 0) { + AIM_LOG_ERROR("Failed to start thermal policy thread."); + thermal_thread_started = false; + } else { + pthread_detach(thermal_thread); + thermal_thread_waiting = true; + AIM_LOG_INFO("Thermal policy thread started."); + } + } + pthread_mutex_unlock(&thermal_lock); +} + +int +onlp_sysi_platform_manage_fans(void) +{ + start_thermal_policy_thread_once(); + + pthread_mutex_lock(&thermal_lock); + if (thermal_thread_waiting) { + pthread_cond_signal(&thermal_cond); + } else { + AIM_LOG_INFO("Thermal policy thread is busy; skipping this trigger."); + } + pthread_mutex_unlock(&thermal_lock); + + return ONLP_STATUS_OK; +} + +int +onlp_sysi_platform_manage_leds(void) +{ + return ONLP_STATUS_E_UNSUPPORTED; +} \ No newline at end of file diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/thermali.c b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/thermali.c new file mode 100644 index 0000000000..9410cc4be3 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/thermali.c @@ -0,0 +1,208 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2014 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * Thermal Sensor Platform Implementation. + * + ***********************************************************/ +#include +#include +#include "platform_lib.h" + +#define VALIDATE(_id) \ + do { \ + if(!ONLP_OID_IS_THERMAL(_id)) { \ + return ONLP_STATUS_E_INVALID; \ + } \ + } while(0) + +static char* devfiles__[] = { /* must map with onlp_thermal_id */ + NULL, + "/sys/devices/platform/coretemp.0*temp1_input", + "/sys/devices/platform/coretemp.0*temp2_input", + "/sys/devices/platform/coretemp.0*temp3_input", + "/sys/devices/platform/coretemp.0*temp4_input", + "/sys/devices/platform/coretemp.0*temp5_input", + "/sys/devices/platform/as7927_50x_thermal*temp1_input", + "/sys/devices/platform/as7927_50x_thermal*temp2_input", + "/sys/devices/platform/as7927_50x_thermal*temp3_input", + "/sys/devices/platform/as7927_50x_thermal*temp4_input", + "/sys/devices/platform/as7927_50x_thermal*temp5_input", + "/sys/devices/platform/as7927_50x_thermal*temp6_input", + "/sys/devices/platform/as7927_50x_thermal*temp7_input", + "/sys/devices/platform/as7927_50x_thermal*temp8_input", + "/sys/devices/platform/as7927_50x_thermal*temp9_input", + "/sys/devices/platform/as7927_50x_thermal*temp10_input", + "/sys/devices/platform/as7927_50x_thermal*temp11_input", + "/sys/devices/platform/as7927_50x_thermal*temp12_input", + "/sys/devices/platform/as7927_50x_psu*psu1_temp1_input", + "/sys/devices/platform/as7927_50x_psu*psu1_temp2_input", + "/sys/devices/platform/as7927_50x_psu*psu1_temp3_input", + "/sys/devices/platform/as7927_50x_psu*psu2_temp1_input", + "/sys/devices/platform/as7927_50x_psu*psu2_temp2_input", + "/sys/devices/platform/as7927_50x_psu*psu2_temp3_input" +}; + +typedef struct { + int warning; + int error; + int shutdown; +} platform_thermal_thresholds_t; + +typedef struct { + platform_thermal_thresholds_t f2b; + platform_thermal_thresholds_t b2f; +} thermal_dir_thresholds_t; + +static thermal_dir_thresholds_t threshold_dict[] = { + [8] = { {45100, 50100, 53100}, {50750, 55750, 58750} }, /* MB_FrontLeft */ + [9] = { {43900, 48900, 51900}, {47130, 52130, 55130} }, /* I/OB */ + [10] = { {45300, 50300, 53300}, {48250, 53250, 56250} }, /* MB_FrontRight */ + [11] = { {47000, 52000, 55000}, {52380, 57380, 60380} }, /* MB_RearLeft */ + [12] = { {78300, 83300, 86300}, {80130, 85130, 88130} }, /* MAC_DiodeCore */ + [13] = { {85600, 90600, 93600}, {88060, 93060, 96060} }, /* MAC_DiodeNif100 */ + [14] = { {83500, 88500, 91500}, {85560, 90560, 93560} }, /* MAC_DiodeNif50 */ + [15] = { {82900, 87900, 90900}, {85310, 90310, 93310} }, /* MAC_DiodeSch */ + [16] = { {46600, 51600, 54600}, {45000, 50000, 53000} }, /* FB_FrontRight */ + [17] = { {43900, 48900, 51900}, {44380, 49380, 52380} } /* FB_FrontLeft */ +}; + +typedef struct { + platform_thermal_thresholds_t dc; + platform_thermal_thresholds_t ac; +} thermal_psu_type_thresholds_t; + +static thermal_psu_type_thresholds_t psu_threshold_dict[] = { + [1] = { { 83000, 86000, 89000}, {100000, 105000, 108000} }, /* PSU_TEMP1 */ + [2] = { {100000, 105000, 108000}, {110000, 115000, 118000} }, /* PSU_TEMP2 */ + [3] = { {110000, 115000, 118000}, { 70000, 75000, 78000} } /* PSU_TEMP3 */ +}; + +static onlp_thermal_info_t tinfo_base[] = { + { }, /* Not used */ + { { ONLP_THERMAL_ID_CREATE(THERMAL_1_CPU_CORE), "CPU DIE", 0, {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, {61000, 66000, 69000} }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_2_CPU_CORE), "CPU Core 1", 0, {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, {61000, 66000, 69000} }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_3_CPU_CORE), "CPU Core 2", 0, {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, {61000, 66000, 69000} }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_4_CPU_CORE), "CPU Core 3", 0, {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, {61000, 66000, 69000} }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_5_CPU_CORE), "CPU Core 4", 0, {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, {61000, 66000, 69000} }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_1_ON_CARRIER_BROAD), "CB_RearLefttemp(0x48)", 0, {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_2_ON_CARRIER_BROAD), "CB_FrontLeft_temp(0x49)", 0, {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_3_ON_MAIN_BROAD), "MB_FrontLeft_temp(0x4A)", 0, {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_4_ON_IO_BROAD), "I/OB_temp(0x49)", 0, {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_5_ON_MAIN_BROAD), "MB_FrontRight_temp(0x4C)", 0, {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_6_ON_MAIN_BROAD), "MB_RearLeft_temp(0x49)", 0, {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_7_ON_MAC_BROAD), "MAC_DiodeCore_temp(0x49)", 0, {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_8_ON_MAC_BROAD), "MAC_DiodeNif100_temp(0x49)", 0, {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_9_ON_MAC_BROAD), "MAC_DiodeNif50_temp(0x49)", 0, {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_10_ON_MAC_BROAD), "MAC_DiodeSch_temp(0x49)", 0, {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_1_ON_FAN_BROAD), "FB_FrontRight_temp(0x4D)", 0, {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_2_ON_FAN_BROAD), "FB_FrontLeft_temp(0x4E)", 0, {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_1_ON_PSU1), "PSU-1 Thermal Sensor 1", ONLP_PSU_ID_CREATE(PSU1_ID), {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_2_ON_PSU1), "PSU-1 Thermal Sensor 2", ONLP_PSU_ID_CREATE(PSU1_ID), {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_3_ON_PSU1), "PSU-1 Thermal Sensor 3", ONLP_PSU_ID_CREATE(PSU1_ID), {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_1_ON_PSU2), "PSU-2 Thermal Sensor 1", ONLP_PSU_ID_CREATE(PSU2_ID), {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_2_ON_PSU2), "PSU-2 Thermal Sensor 2", ONLP_PSU_ID_CREATE(PSU2_ID), {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_3_ON_PSU2), "PSU-2 Thermal Sensor 3", ONLP_PSU_ID_CREATE(PSU2_ID), {0} }, + ONLP_THERMAL_STATUS_PRESENT, ONLP_THERMAL_CAPS_ALL, 0, ONLP_THERMAL_THRESHOLD_INIT_DEFAULTS } +}; + +/* + * This will be called to initialize the thermali subsystem. + */ +int +onlp_thermali_init(void) +{ + return ONLP_STATUS_OK; +} + +/* + * Retrieve the information structure for the given thermal OID. + * + * If the OID is invalid, return ONLP_E_STATUS_INVALID. + * If an unexpected error occurs, return ONLP_E_STATUS_INTERNAL. + * Otherwise, return ONLP_STATUS_OK with the OID's information. + * + * Note -- it is expected that you fill out the information + * structure even if the sensor described by the OID is not present. + */ +int +onlp_thermali_info_get(onlp_oid_t id, onlp_thermal_info_t* info) +{ + int tid; + int psu_id; + int psu_type; + int fan_dir; + int sensor_idx; + platform_thermal_thresholds_t *th; + + VALIDATE(id); + + tid = ONLP_OID_ID_GET(id); + if (tid <= 0 || tid >= THERMAL_COUNT) { + return ONLP_STATUS_E_INVALID; + } + + *info = tinfo_base[tid]; + + if (tid >= 8 && tid <= 17) { + fan_dir = onlp_get_fan_dir(1); + th = (fan_dir == FAN_DIR_B2F) ? &threshold_dict[tid].b2f : &threshold_dict[tid].f2b; + + info->thresholds.warning = th->warning; + info->thresholds.error = th->error; + info->thresholds.shutdown = th->shutdown; + } + else if (tid >= 18 && tid <= 23) { + psu_id = (tid <= 20) ? 1 : 2; + psu_type = onlp_get_psu_type(psu_id); + + sensor_idx = (tid - 18) % 3 + 1; + th = (psu_type == PSU_TYPE_DC) ? &psu_threshold_dict[sensor_idx].dc : &psu_threshold_dict[sensor_idx].ac; + + info->thresholds.warning = th->warning; + info->thresholds.error = th->error; + info->thresholds.shutdown = th->shutdown; + } + + return onlp_file_read_int(&info->mcelsius, devfiles__[tid]); +} \ No newline at end of file diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_config.c b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_config.c new file mode 100644 index 0000000000..7cd2b207a6 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_config.c @@ -0,0 +1,81 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +/* */ +#define __x86_64_accton_as7927_50x_config_STRINGIFY_NAME(_x) #_x +#define __x86_64_accton_as7927_50x_config_STRINGIFY_VALUE(_x) __x86_64_accton_as7927_50x_config_STRINGIFY_NAME(_x) +x86_64_accton_as7927_50x_config_settings_t x86_64_accton_as7927_50x_config_settings[] = +{ +#ifdef X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_LOGGING + { __x86_64_accton_as7927_50x_config_STRINGIFY_NAME(X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_LOGGING), __x86_64_accton_as7927_50x_config_STRINGIFY_VALUE(X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_LOGGING) }, +#else +{ X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_LOGGING(__x86_64_accton_as7927_50x_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_AS7927_50X_CONFIG_LOG_OPTIONS_DEFAULT + { __x86_64_accton_as7927_50x_config_STRINGIFY_NAME(X86_64_ACCTON_AS7927_50X_CONFIG_LOG_OPTIONS_DEFAULT), __x86_64_accton_as7927_50x_config_STRINGIFY_VALUE(X86_64_ACCTON_AS7927_50X_CONFIG_LOG_OPTIONS_DEFAULT) }, +#else +{ X86_64_ACCTON_AS7927_50X_CONFIG_LOG_OPTIONS_DEFAULT(__x86_64_accton_as7927_50x_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_AS7927_50X_CONFIG_LOG_BITS_DEFAULT + { __x86_64_accton_as7927_50x_config_STRINGIFY_NAME(X86_64_ACCTON_AS7927_50X_CONFIG_LOG_BITS_DEFAULT), __x86_64_accton_as7927_50x_config_STRINGIFY_VALUE(X86_64_ACCTON_AS7927_50X_CONFIG_LOG_BITS_DEFAULT) }, +#else +{ X86_64_ACCTON_AS7927_50X_CONFIG_LOG_BITS_DEFAULT(__x86_64_accton_as7927_50x_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_AS7927_50X_CONFIG_LOG_CUSTOM_BITS_DEFAULT + { __x86_64_accton_as7927_50x_config_STRINGIFY_NAME(X86_64_ACCTON_AS7927_50X_CONFIG_LOG_CUSTOM_BITS_DEFAULT), __x86_64_accton_as7927_50x_config_STRINGIFY_VALUE(X86_64_ACCTON_AS7927_50X_CONFIG_LOG_CUSTOM_BITS_DEFAULT) }, +#else +{ X86_64_ACCTON_AS7927_50X_CONFIG_LOG_CUSTOM_BITS_DEFAULT(__x86_64_accton_as7927_50x_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_STDLIB + { __x86_64_accton_as7927_50x_config_STRINGIFY_NAME(X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_STDLIB), __x86_64_accton_as7927_50x_config_STRINGIFY_VALUE(X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_STDLIB) }, +#else +{ X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_STDLIB(__x86_64_accton_as7927_50x_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS + { __x86_64_accton_as7927_50x_config_STRINGIFY_NAME(X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS), __x86_64_accton_as7927_50x_config_STRINGIFY_VALUE(X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS) }, +#else +{ X86_64_ACCTON_AS7927_50X_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS(__x86_64_accton_as7927_50x_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_UCLI + { __x86_64_accton_as7927_50x_config_STRINGIFY_NAME(X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_UCLI), __x86_64_accton_as7927_50x_config_STRINGIFY_VALUE(X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_UCLI) }, +#else +{ X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_UCLI(__x86_64_accton_as7927_50x_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION + { __x86_64_accton_as7927_50x_config_STRINGIFY_NAME(X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION), __x86_64_accton_as7927_50x_config_STRINGIFY_VALUE(X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION) }, +#else +{ X86_64_ACCTON_AS7927_50X_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION(__x86_64_accton_as7927_50x_config_STRINGIFY_NAME), "__undefined__" }, +#endif + { NULL, NULL } +}; +#undef __x86_64_accton_as7927_50x_config_STRINGIFY_VALUE +#undef __x86_64_accton_as7927_50x_config_STRINGIFY_NAME + +const char* +x86_64_accton_as7927_50x_config_lookup(const char* setting) +{ + int i; + for(i = 0; x86_64_accton_as7927_50x_config_settings[i].name; i++) { + if(!strcmp(x86_64_accton_as7927_50x_config_settings[i].name, setting)) { + return x86_64_accton_as7927_50x_config_settings[i].value; + } + } + return NULL; +} + +int +x86_64_accton_as7927_50x_config_show(struct aim_pvs_s* pvs) +{ + int i; + for(i = 0; x86_64_accton_as7927_50x_config_settings[i].name; i++) { + aim_printf(pvs, "%s = %s\n", x86_64_accton_as7927_50x_config_settings[i].name, x86_64_accton_as7927_50x_config_settings[i].value); + } + return i; +} + +/* */ + diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_enums.c b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_enums.c new file mode 100644 index 0000000000..5ca437d741 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_enums.c @@ -0,0 +1,10 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +/* <--auto.start.enum(ALL).source> */ +/* */ + diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_int.h b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_int.h new file mode 100644 index 0000000000..6086f4e1e1 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_int.h @@ -0,0 +1,12 @@ +/**************************************************************************//** + * + * x86_64_accton_as7927_50x Internal Header + * + *****************************************************************************/ +#ifndef __x86_64_accton_as7927_50x_INT_H__ +#define __x86_64_accton_as7927_50x_INT_H__ + +#include + + +#endif /* __x86_64_accton_as7927_50x_INT_H__ */ diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_log.c b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_log.c new file mode 100644 index 0000000000..7b979e0945 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_log.c @@ -0,0 +1,17 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +#include "x86_64_accton_as7927_50x_log.h" +/* + * x86_64_accton_as7927_50x log struct. + */ +AIM_LOG_STRUCT_DEFINE( + X86_64_ACCTON_AS7927_50X_CONFIG_LOG_OPTIONS_DEFAULT, + X86_64_ACCTON_AS7927_50X_CONFIG_LOG_BITS_DEFAULT, + NULL, /* Custom log map */ + X86_64_ACCTON_AS7927_50X_CONFIG_LOG_CUSTOM_BITS_DEFAULT + ); diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_log.h b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_log.h new file mode 100644 index 0000000000..4a47d7e885 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_log.h @@ -0,0 +1,12 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#ifndef __x86_64_accton_as7927_50x_LOG_H__ +#define __x86_64_accton_as7927_50x_LOG_H__ + +#define AIM_LOG_MODULE_NAME x86_64_accton_as7927_50x +#include + +#endif /* __x86_64_accton_as7927_50x_LOG_H__ */ diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_module.c b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_module.c new file mode 100644 index 0000000000..6cc8e0b413 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_module.c @@ -0,0 +1,24 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +#include "x86_64_accton_as7927_50x_log.h" + +static int +datatypes_init__(void) +{ +#define x86_64_accton_as7927_50x_ENUMERATION_ENTRY(_enum_name, _desc) AIM_DATATYPE_MAP_REGISTER(_enum_name, _enum_name##_map, _desc, AIM_LOG_INTERNAL); +#include + return 0; +} + +void __x86_64_accton_as7927_50x_module_init__(void) +{ + AIM_LOG_STRUCT_REGISTER(); + datatypes_init__(); +} + +int __onlp_platform_version__ = 1; diff --git a/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_ucli.c b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_ucli.c new file mode 100644 index 0000000000..507c0e20df --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/onlp/builds/x86_64_accton_as7927_50x/module/src/x86_64_accton_as7927_50x_ucli.c @@ -0,0 +1,50 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +#if x86_64_accton_as7927_50x_CONFIG_INCLUDE_UCLI == 1 + +#include +#include +#include + +static ucli_status_t +x86_64_accton_as7927_50x_ucli_ucli__config__(ucli_context_t* uc) +{ + UCLI_HANDLER_MACRO_MODULE_CONFIG(x86_64_accton_as7927_50x) +} + +/* */ +/* */ + +static ucli_module_t +x86_64_accton_as7927_50x_ucli_module__ = + { + "x86_64_accton_as7927_50x_ucli", + NULL, + x86_64_accton_as7927_50x_ucli_ucli_handlers__, + NULL, + NULL, + }; + +ucli_node_t* +x86_64_accton_as7927_50x_ucli_node_create(void) +{ + ucli_node_t* n; + ucli_module_init(&x86_64_accton_as7927_50x_ucli_module__); + n = ucli_node_create("x86_64_accton_as7927_50x", NULL, &x86_64_accton_as7927_50x_ucli_module__); + ucli_node_subnode_add(n, ucli_module_log_node_create("x86_64_accton_as7927_50x")); + return n; +} + +#else +void* +x86_64_accton_as7927_50x_ucli_node_create(void) +{ + return NULL; +} +#endif + diff --git a/packages/platforms/accton/x86-64/as7927-50x/platform-config/Makefile b/packages/platforms/accton/x86-64/as7927-50x/platform-config/Makefile new file mode 100644 index 0000000000..dc1e7b86f0 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/platform-config/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk diff --git a/packages/platforms/accton/x86-64/as7927-50x/platform-config/r0/Makefile b/packages/platforms/accton/x86-64/as7927-50x/platform-config/r0/Makefile new file mode 100644 index 0000000000..dc1e7b86f0 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/platform-config/r0/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk diff --git a/packages/platforms/accton/x86-64/as7927-50x/platform-config/r0/PKG.yml b/packages/platforms/accton/x86-64/as7927-50x/platform-config/r0/PKG.yml new file mode 100644 index 0000000000..b1cc6f0f37 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/platform-config/r0/PKG.yml @@ -0,0 +1 @@ +!include $ONL_TEMPLATES/platform-config-platform.yml ARCH=amd64 VENDOR=accton BASENAME=x86-64-accton-as7927-50x REVISION=r0 diff --git a/packages/platforms/accton/x86-64/as7927-50x/platform-config/r0/src/lib/x86-64-accton-as7927-50x-r0.yml b/packages/platforms/accton/x86-64/as7927-50x/platform-config/r0/src/lib/x86-64-accton-as7927-50x-r0.yml new file mode 100644 index 0000000000..a44d552aef --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/platform-config/r0/src/lib/x86-64-accton-as7927-50x-r0.yml @@ -0,0 +1,35 @@ +--- + +###################################################################### +# +# platform-config for AS7927 +# +###################################################################### + +x86-64-accton-as7927-50x-r0: + + grub: + + serial: >- + --port=0x3f8 + --speed=115200 + --word=8 + --parity=no + --stop=1 + + kernel: + <<: *kernel-5-4 + + args: >- + nopat + console=ttyS0,115200n8 + intel_iommu=off + pcie_aspm=off + intremap=off + modprobe.blacklist=fpga_device + + ##network + ## interfaces: + ## ma1: + ## name: ~ + ## syspath: pci0000:00/0000:00:14.0 diff --git a/packages/platforms/accton/x86-64/as7927-50x/platform-config/r0/src/python/x86_64_accton_as7927_50x_r0/__init__.py b/packages/platforms/accton/x86-64/as7927-50x/platform-config/r0/src/python/x86_64_accton_as7927_50x_r0/__init__.py new file mode 100644 index 0000000000..c43305aa51 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7927-50x/platform-config/r0/src/python/x86_64_accton_as7927_50x_r0/__init__.py @@ -0,0 +1,105 @@ +import commands +from itertools import chain +from onl.platform.base import * +from onl.platform.accton import * +from time import sleep + + +init_ipmi_dev = [ + 'echo "remove,kcs,i/o,0xca2" > /sys/module/ipmi_si/parameters/hotmod', + 'echo "add,kcs,i/o,0xca2" > /sys/module/ipmi_si/parameters/hotmod'] + +ATTEMPTS = 5 +INTERVAL = 3 + +def init_ipmi_dev_intf(): + attempts = ATTEMPTS + interval = INTERVAL + + while attempts: + if os.path.exists('/dev/ipmi0') or os.path.exists('/dev/ipmidev/0'): + return (True, (ATTEMPTS - attempts) * interval) + + for i in range(0, len(init_ipmi_dev)): + commands.getstatusoutput(init_ipmi_dev[i]) + + attempts -= 1 + sleep(interval) + + return (False, ATTEMPTS * interval) + +def init_ipmi_oem_cmd(): + attempts = ATTEMPTS + interval = INTERVAL + + while attempts: + status, output = commands.getstatusoutput('ipmitool raw 0x34 0x95') + if status: + attempts -= 1 + sleep(interval) + continue + + return (True, (ATTEMPTS - attempts) * interval) + + return (False, ATTEMPTS * interval) + +def init_ipmi(): + attempts = ATTEMPTS + interval = 60 + + while attempts: + attempts -= 1 + + (status, elapsed_dev) = init_ipmi_dev_intf() + if status is not True: + sleep(interval - elapsed_dev) + continue + + (status, elapsed_oem) = init_ipmi_oem_cmd() + if status is not True: + sleep(interval - elapsed_dev - elapsed_oem) + continue + + print('IPMI dev interface is ready.') + return True + + print('Failed to initialize IPMI dev interface') + return False + +class OnlPlatform_x86_64_accton_as7927_50x_r0(OnlPlatformAccton, + OnlPlatformPortConfig_1x800_1x400_8X50_40X25): + PLATFORM='x86-64-accton-as7927-50x-r0' + MODEL="AS7927-50X" + SYS_OBJECT_ID=".7927.50" + + def modprobe(self, module, required=True, params={}): + cmd = "modprobe %s" % module + subprocess.check_call(cmd, shell=True) + + def baseconfig(self): + if init_ipmi() is not True: + return False + + self.modprobe('optoe') + self.modprobe('accton_ipmi_intf') + + for m in [ 'i2c-ocores', 'fpga', 'fan', 'psu', 'thermal', 'sys', 'leds']: + self.insmod("x86-64-accton-as7927-50x-%s" % m) + + # initialize SFP devices + for port in range(1, 49): + subprocess.call('echo 1 > /sys/devices/platform/as7927_50x_fpga/module_efuse_%d' % (port), shell=True) + for port in range(49, 51): + subprocess.call('echo 1 > /sys/devices/platform/as7927_50x_fpga/module_enable_%d' % (port), shell=True) + subprocess.call('echo 0 > /sys/devices/platform/as7927_50x_fpga/module_reset_%d' % (port), shell=True) + #SFP + for port in range(1, 49): + self.new_i2c_device('optoe2', 0x50, port) + #QSFP-DD + for port in range(49, 51): + self.new_i2c_device('optoe3', 0x50, port) + + for port in range(1, 51): + subprocess.call('echo port%d > /sys/bus/i2c/devices/%d-0050/port_name' % (port, port), shell=True) + + return True