From 3f71c4148dbc46e3c04b647bff87e5475872c93f Mon Sep 17 00:00:00 2001 From: jumao Date: Mon, 4 Aug 2025 15:20:33 -0400 Subject: [PATCH 1/2] [H6-64] Add support for Nokia H6-64 --- debian/control | 6 +- debian/rules | 2 +- .../sonic-platform-nokia-ixr7220h6-64.install | 5 + ...sonic-platform-nokia-ixr7220h6-64.postinst | 11 + ixr7220h6-64/modules/Makefile | 11 + ixr7220h6-64/modules/eeprom_fru.c | 524 ++++++++ ixr7220h6-64/modules/embd_ctrl.c | 197 +++ ixr7220h6-64/modules/h6_fan_cpld.c | 607 ++++++++++ ixr7220h6-64/modules/h6_i2c_oc.c | 258 ++++ ixr7220h6-64/modules/i2c-ocores.c | 883 ++++++++++++++ ixr7220h6-64/modules/pmbus_psu.c | 563 +++++++++ ixr7220h6-64/modules/port_cpld0.c | 1077 +++++++++++++++++ ixr7220h6-64/modules/port_cpld1.c | 847 +++++++++++++ ixr7220h6-64/modules/sys_cpld.c | 422 +++++++ ixr7220h6-64/modules/sys_fpga.c | 257 ++++ ixr7220h6-64/modules/sys_mux.c | 201 +++ ixr7220h6-64/scripts/h6_64_platform_init.sh | 132 ++ ixr7220h6-64/scripts/ports_notify.py | 115 ++ .../service/h6_64_platform_init.service | 12 + ixr7220h6-64/service/ports_notify.service | 15 + ixr7220h6-64/setup.py | 21 + ixr7220h6-64/sonic_platform/__init__.py | 2 + ixr7220h6-64/sonic_platform/chassis.py | 398 ++++++ ixr7220h6-64/sonic_platform/component.py | 277 +++++ ixr7220h6-64/sonic_platform/eeprom.py | 220 ++++ ixr7220h6-64/sonic_platform/fan.py | 262 ++++ ixr7220h6-64/sonic_platform/fan_drawer.py | 206 ++++ ixr7220h6-64/sonic_platform/platform.py | 19 + ixr7220h6-64/sonic_platform/psu.py | 305 +++++ ixr7220h6-64/sonic_platform/sfp.py | 218 ++++ ixr7220h6-64/sonic_platform/sfp_event.py | 124 ++ ixr7220h6-64/sonic_platform/sysfs.py | 48 + ixr7220h6-64/sonic_platform/test/README | 1 + .../sonic_platform/test/test-chassis.py | 62 + .../sonic_platform/test/test-component.py | 19 + .../sonic_platform/test/test-eeprom.py | 24 + .../sonic_platform/test/test-fan-drawer.py | 29 + ixr7220h6-64/sonic_platform/test/test-fan.py | 34 + ixr7220h6-64/sonic_platform/test/test-psu.py | 44 + ixr7220h6-64/sonic_platform/test/test-sfp.py | 54 + .../sonic_platform/test/test-thermal.py | 49 + .../sonic_platform/test/test-watchdog.py | 19 + ixr7220h6-64/sonic_platform/thermal.py | 261 ++++ .../sonic_platform/thermal_actions.py | 199 +++ .../sonic_platform/thermal_conditions.py | 70 ++ ixr7220h6-64/sonic_platform/thermal_infos.py | 236 ++++ .../sonic_platform/thermal_manager.py | 58 + ixr7220h6-64/sonic_platform/watchdog.py | 175 +++ 48 files changed, 9577 insertions(+), 2 deletions(-) create mode 100644 debian/sonic-platform-nokia-ixr7220h6-64.install create mode 100644 debian/sonic-platform-nokia-ixr7220h6-64.postinst create mode 100755 ixr7220h6-64/modules/Makefile create mode 100644 ixr7220h6-64/modules/eeprom_fru.c create mode 100644 ixr7220h6-64/modules/embd_ctrl.c create mode 100644 ixr7220h6-64/modules/h6_fan_cpld.c create mode 100644 ixr7220h6-64/modules/h6_i2c_oc.c create mode 100644 ixr7220h6-64/modules/i2c-ocores.c create mode 100644 ixr7220h6-64/modules/pmbus_psu.c create mode 100644 ixr7220h6-64/modules/port_cpld0.c create mode 100644 ixr7220h6-64/modules/port_cpld1.c create mode 100644 ixr7220h6-64/modules/sys_cpld.c create mode 100644 ixr7220h6-64/modules/sys_fpga.c create mode 100644 ixr7220h6-64/modules/sys_mux.c create mode 100755 ixr7220h6-64/scripts/h6_64_platform_init.sh create mode 100644 ixr7220h6-64/scripts/ports_notify.py create mode 100755 ixr7220h6-64/service/h6_64_platform_init.service create mode 100644 ixr7220h6-64/service/ports_notify.service create mode 100755 ixr7220h6-64/setup.py create mode 100755 ixr7220h6-64/sonic_platform/__init__.py create mode 100755 ixr7220h6-64/sonic_platform/chassis.py create mode 100755 ixr7220h6-64/sonic_platform/component.py create mode 100755 ixr7220h6-64/sonic_platform/eeprom.py create mode 100755 ixr7220h6-64/sonic_platform/fan.py create mode 100755 ixr7220h6-64/sonic_platform/fan_drawer.py create mode 100755 ixr7220h6-64/sonic_platform/platform.py create mode 100755 ixr7220h6-64/sonic_platform/psu.py create mode 100755 ixr7220h6-64/sonic_platform/sfp.py create mode 100755 ixr7220h6-64/sonic_platform/sfp_event.py create mode 100755 ixr7220h6-64/sonic_platform/sysfs.py create mode 100755 ixr7220h6-64/sonic_platform/test/README create mode 100755 ixr7220h6-64/sonic_platform/test/test-chassis.py create mode 100755 ixr7220h6-64/sonic_platform/test/test-component.py create mode 100755 ixr7220h6-64/sonic_platform/test/test-eeprom.py create mode 100755 ixr7220h6-64/sonic_platform/test/test-fan-drawer.py create mode 100755 ixr7220h6-64/sonic_platform/test/test-fan.py create mode 100755 ixr7220h6-64/sonic_platform/test/test-psu.py create mode 100755 ixr7220h6-64/sonic_platform/test/test-sfp.py create mode 100755 ixr7220h6-64/sonic_platform/test/test-thermal.py create mode 100755 ixr7220h6-64/sonic_platform/test/test-watchdog.py create mode 100755 ixr7220h6-64/sonic_platform/thermal.py create mode 100755 ixr7220h6-64/sonic_platform/thermal_actions.py create mode 100755 ixr7220h6-64/sonic_platform/thermal_conditions.py create mode 100755 ixr7220h6-64/sonic_platform/thermal_infos.py create mode 100755 ixr7220h6-64/sonic_platform/thermal_manager.py create mode 100755 ixr7220h6-64/sonic_platform/watchdog.py diff --git a/debian/control b/debian/control index ff2595c..980855b 100755 --- a/debian/control +++ b/debian/control @@ -42,7 +42,7 @@ Description: kernel modules for platform devices such as qsfp, fan, psu, led Package: sonic-platform-nokia-ixr7220h5-64o Architecture: amd64 Depends: ${misc:Depends} -Description: kernel modules for platform devices such as qsfp, fan, psu, led +Description: kernel modules for platform devices such as osfp, fan, psu, led Package: sonic-platform-nokia-ixr7250x1b Architecture: amd64 @@ -54,3 +54,7 @@ Architecture: amd64 Depends: ${misc:Depends} Description: kernel modules for platform devices such as fan, led, sfp +Package: sonic-platform-nokia-ixr7220h6-64 +Architecture: amd64 +Depends: ${misc:Depends} +Description: kernel modules for platform devices such as osfp, fan, led, sfp diff --git a/debian/rules b/debian/rules index c588445..37096b7 100755 --- a/debian/rules +++ b/debian/rules @@ -20,7 +20,7 @@ KERNEL_SRC := /lib/modules/$(KVERSION) MOD_SRC_DIR := $(shell pwd) MODULE_DIRS := chassis ixr7220h3 ixr7220h4-32d ixr7220h5-32d ixr7220h5-64d ixr7220h4-64d \ - ixr7220h5-64o ixr7250x1b ixr7250x3b ixr7220d4 + ixr7220h5-64o ixr7250x1b ixr7250x3b ixr7220d4 ixr7220h6-64 BRIDGE_DRIVER_TARGETS := ixr7250x1b ixr7250x3b MODULE_DIR := modules UTILS_DIR := utils diff --git a/debian/sonic-platform-nokia-ixr7220h6-64.install b/debian/sonic-platform-nokia-ixr7220h6-64.install new file mode 100644 index 0000000..10400b5 --- /dev/null +++ b/debian/sonic-platform-nokia-ixr7220h6-64.install @@ -0,0 +1,5 @@ +ixr7220h6-64/scripts/h6_64_platform_init.sh usr/local/bin +ixr7220h6-64/scripts/ports_notify.py usr/local/bin +ixr7220h6-64/service/h6_64_platform_init.service etc/systemd/system +ixr7220h6-64/service/ports_notify.service etc/systemd/system/ +ixr7220h6-64/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/x86_64-nokia_ixr7220_h6_64-r0 diff --git a/debian/sonic-platform-nokia-ixr7220h6-64.postinst b/debian/sonic-platform-nokia-ixr7220h6-64.postinst new file mode 100644 index 0000000..f23e603 --- /dev/null +++ b/debian/sonic-platform-nokia-ixr7220h6-64.postinst @@ -0,0 +1,11 @@ +#!/bin/sh +# postinst script for sonic-platform-nokia-IXR7220-H6-64 +# +# see: dh_installdeb(1) + +chmod a+x /usr/local/bin/h6_64_platform_init.sh +systemctl enable h6_64_platform_init.service +systemctl start h6_64_platform_init.service +chmod a+x /usr/local/bin/ports_notify.py +systemctl enable ports_notify.service +systemctl start --no-block ports_notify.service diff --git a/ixr7220h6-64/modules/Makefile b/ixr7220h6-64/modules/Makefile new file mode 100755 index 0000000..e9671ac --- /dev/null +++ b/ixr7220h6-64/modules/Makefile @@ -0,0 +1,11 @@ +obj-m += i2c-ocores.o +obj-m += h6_i2c_oc.o +obj-m += sys_mux.o +obj-m += sys_cpld.o +obj-m += port_cpld0.o +obj-m += port_cpld1.o +obj-m += eeprom_fru.o +obj-m += h6_fan_cpld.o +obj-m += sys_fpga.o +obj-m += embd_ctrl.o +obj-m += pmbus_psu.o diff --git a/ixr7220h6-64/modules/eeprom_fru.c b/ixr7220h6-64/modules/eeprom_fru.c new file mode 100644 index 0000000..179ec7e --- /dev/null +++ b/ixr7220h6-64/modules/eeprom_fru.c @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * eeprom_fru.c - NOKIA EEPROM FRU Sysfs driver + * + * + * Copyright (C) 2024 Nokia 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 3 of the License, or + * 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. + * see + */ + +#include +#include +#include +#include + +#define EEPROM_NAME "eeprom_fru" +#define FIELD_LEN_MAX 255 +static unsigned int debug = 0; +module_param_named(debug, debug, uint, 0); +MODULE_PARM_DESC(debug, "Debug enable(default to 0)"); + +static unsigned int read_eeprom_max_len = 0xb8; +module_param_named(read_eeprom_max_len, read_eeprom_max_len, uint, 0); +MODULE_PARM_DESC(read_eeprom_max_len, "read_eeprom_max_len(default to 0xb8)"); + +#define FRU_END_OF_FIELDS 0xc1 +#define BUF2STR_MAXIMUM_OUTPUT_SIZE (3*1024 + 1) +struct fru_header { + u8 version; + union { + struct { + u8 internal; + u8 chassis; + u8 board; + u8 product; + u8 multi; + } offset; + u8 offsets[5]; + }; + u8 pad; + u8 checksum; +}; + +struct at24_data { + /* + * Lock protects against activities from other Linux tasks, + * but not from changes by other I2C masters. + */ + struct mutex lock; + struct i2c_client *client; + char part_number[FIELD_LEN_MAX + 1]; + char product_version[FIELD_LEN_MAX + 1]; + char serial_number[FIELD_LEN_MAX + 1]; + char mfg_name[FIELD_LEN_MAX+1]; + char product_name[FIELD_LEN_MAX + 1]; + char clei_code[FIELD_LEN_MAX + 1]; + char deviation[FIELD_LEN_MAX + 1]; + char customer_sn[FIELD_LEN_MAX + 1]; + char fru_ver[FIELD_LEN_MAX + 1]; + char mfg_date[FIELD_LEN_MAX + 1]; +}; + +u8 fru_calc_checksum(void *area, size_t len) +{ + u8 checksum = 0; + u8 * data = area; + size_t i; + + for (i = 0; i < len - 1; i++) + checksum += data[i]; + + return -checksum; +} + +int fru_checksum_is_valid(void *area, size_t len) +{ + u8 * data = area; + /* Checksum is valid when the stored checksum equals calculated */ + return data[len - 1] == fru_calc_checksum(area, len); +} + +const char * buf2str_extended(const u8 *buf, int len, const char *sep) +{ + static char str[BUF2STR_MAXIMUM_OUTPUT_SIZE] = {0}; + char *cur; + int i; + int sz; + int left; + int sep_len; + + if (!buf) { + snprintf(str, sizeof(str), ""); + return (const char *)str; + } + cur = str; + left = sizeof(str); + if (sep) { + sep_len = strlen(sep); + } else { + sep_len = 0; + } + for (i = 0; i < len; i++) { + /* may return more than 2, depending on locale */ + sz = snprintf(cur, left, "%2.2x", buf[i]); + if (sz >= left) { + /* buffer overflow, truncate */ + break; + } + cur += sz; + left -= sz; + /* do not write separator after last byte */ + if (sep && i != (len - 1)) { + if (sep_len >= left) { + break; + } + strncpy(cur, sep, left - sz); + cur += sep_len; + left -= sep_len; + } + } + *cur = '\0'; + + return (const char *)str; +} + +const char * buf2str(const u8 *buf, int len) +{ + return buf2str_extended(buf, len, NULL); +} + +char * get_fru_area_str(struct device *dev, u8 * data, u32 * offset) +{ + static const char bcd_plus[] = "0123456789 -.:,_"; + char * str; + int len, off, size, i, j, k, typecode, char_idx; + union { + u32 bits; + char chars[4]; + } u; + + size = 0; + off = *offset; + + /* bits 6:7 contain format */ + typecode = ((data[off] & 0xC0) >> 6); + + /* bits 0:5 contain length */ + len = data[off++]; + len &= 0x3f; + + switch (typecode) { + case 0: /* 00b: binary/unspecified */ + case 1: /* 01b: BCD plus */ + /* hex dump or BCD -> 2x length */ + size = (len * 2); + break; + case 2: /* 10b: 6-bit ASCII */ + /* 4 chars per group of 1-3 bytes, round up to 4 bytes boundary */ + size = (len / 3 + 1) * 4; + break; + case 3: /* 11b: 8-bit ASCII */ + /* no length adjustment */ + size = len; + break; + } + + if (size < 1) { + *offset = off; + return NULL; + } + str = devm_kzalloc(dev, size+1, GFP_KERNEL); + if (!str) + return NULL; + + if (size == 0) { + str[0] = '\0'; + *offset = off; + return str; + } + + switch (typecode) { + case 0: /* Binary */ + strncpy(str, buf2str(&data[off], len), size); + break; + + case 1: /* BCD plus */ + for (k = 0; k < size; k++) + str[k] = bcd_plus[((data[off + k / 2] >> ((k % 2) ? 0 : 4)) & 0x0f)]; + str[k] = '\0'; + break; + + case 2: /* 6-bit ASCII */ + for (i = j = 0; i < len; i += 3) { + u.bits = 0; + k = ((len - i) < 3 ? (len - i) : 3); + + memcpy((void *)&u.bits, &data[off+i], k); + char_idx = 0; + for (k=0; k<4; k++) { + str[j++] = ((u.chars[char_idx] & 0x3f) + 0x20); + u.bits >>= 6; + } + } + str[j] = '\0'; + break; + + case 3: + memcpy(str, &data[off], size); + str[size] = '\0'; + break; + } + + off += len; + *offset = off; + + return str; +} + +static int decode_fru_product_info_area(struct i2c_client *client, u8 * raw_data, u32 offset) +{ + char * fru_area; + u8 * fru_data; + u32 fru_len, i; + u8 * tmp = raw_data + offset; + struct device *dev = &client->dev; + struct at24_data *at24 = i2c_get_clientdata(client); + fru_len = 0; + + /* read enough to check length field */ + fru_len = 8 * tmp[1]; + + if (fru_len == 0) { + return -EINVAL; + } + fru_data = devm_kzalloc(dev, fru_len, GFP_KERNEL); + + if (!fru_data) + return -ENOMEM; + + memcpy(fru_data, raw_data+offset, fru_len); + + struct fru_product_info_area_field { + char name[64]; + char * p; + }; + + const struct fru_product_info_area_field fru_fields[] = { + {"Product Area Format Version", NULL}, + {"Product Area Length", NULL}, + {"Language Code", NULL}, + {"Manufacturer Name", at24->mfg_name}, + {"Product Name", at24->product_name}, + {"Product Part/Model Number", at24->part_number}, + {"Product Version", at24->product_version}, + {"Product Serial Number", at24->serial_number}, + {"Asset Tag", NULL}, + {"FRU File ID", NULL}, + {"CLEI Code", at24->clei_code}, + {"Deviation", at24->deviation}, + {"Customer SN", at24->customer_sn}, + {"FRU Version", at24->fru_ver}, + {"Manufacture Date", at24->mfg_date} + }; + + /* Check area checksum */ + if(debug && !fru_checksum_is_valid(fru_data, fru_len)) { + dev_warn(dev, "Invalid eeprom checksum.\n"); + return -EINVAL; + } + + i = 3; + int j = 0; + for(; j < ARRAY_SIZE(fru_fields); j++) + { + if(j < 3) { + if(debug) { + dev_info(dev, "%s: %x\n", fru_fields[j].name, fru_data[j]); + } + continue; + } + fru_area = get_fru_area_str(dev, fru_data, &i); + if(fru_area && fru_fields[j].p) { + if (strlen(fru_area) > 0) { + if(debug) { + dev_info(dev, "%s: %s\n", fru_fields[j].name, fru_area); + } + int len = strlen(fru_area); + if( len > FIELD_LEN_MAX) { + len = FIELD_LEN_MAX; + } + strncpy(fru_fields[j].p, fru_area, len); + } + } + } + return 0; +} + +int decode_eeprom(struct i2c_client *client) +{ + u8 * raw_data = kzalloc(read_eeprom_max_len, GFP_KERNEL); + if (!raw_data) { + return -ENOMEM; + } + + for (int i = 0; i < read_eeprom_max_len / 2; i++) { + u16 data = i2c_smbus_read_word_data(client, i * 2); + raw_data[i * 2] = data & 0xff; + raw_data[i * 2 + 1] = (data >> 8) & 0xff; + } + + struct fru_header header; + memset(&header, 0, sizeof(struct fru_header)); + + if(debug) { + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, raw_data, read_eeprom_max_len, true); + } + + /* According to IPMI Platform Management FRU Information Storage Definition v1.0 */ + memcpy(&header, raw_data, 8); + if (header.version != 1) { + struct device *dev = &client->dev; + dev_err(dev, "Unknown FRU header version 0x%02x", header.version); + } + /* + * Only process Product Info Area + */ + if ((header.offset.product*8) >= sizeof(struct fru_header)) + decode_fru_product_info_area(client, raw_data, header.offset.product*8); + + kfree(raw_data); + return 0; +} + +static ssize_t trigger_read_eeprom(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + if(!strncmp(buf,"1", count-1)) { + struct at24_data *data = dev_get_drvdata(dev); + decode_eeprom(data->client); + } + return count; +} + +static ssize_t show_part_number(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct at24_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->part_number); +} + +static ssize_t show_serial_number(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct at24_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->serial_number); +} + +static ssize_t show_product_version(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct at24_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->product_version); +} + +static ssize_t show_mfg_name(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct at24_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->mfg_name); +} + +static ssize_t show_product_name(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct at24_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->product_name); +} + +static ssize_t show_clei_code(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct at24_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->clei_code); +} + +static ssize_t show_deviation(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct at24_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->deviation); +} + +static ssize_t show_customer_sn(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct at24_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->customer_sn); +} + +static ssize_t show_fru_ver(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct at24_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->fru_ver); +} + +static ssize_t show_mfg_date(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct at24_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->mfg_date); +} + +// sysfs attributes +static SENSOR_DEVICE_ATTR(read_eeprom, S_IWUSR, NULL, trigger_read_eeprom, 0); +static SENSOR_DEVICE_ATTR(part_number, S_IRUGO, show_part_number, NULL, 0); +static SENSOR_DEVICE_ATTR(serial_number, S_IRUGO, show_serial_number, NULL, 0); +static SENSOR_DEVICE_ATTR(product_version, S_IRUGO, show_product_version, NULL, 0); +static SENSOR_DEVICE_ATTR(mfg_name, S_IRUGO, show_mfg_name, NULL, 0); +static SENSOR_DEVICE_ATTR(product_name, S_IRUGO, show_product_name, NULL, 0); +static SENSOR_DEVICE_ATTR(clei_code, S_IRUGO, show_clei_code, NULL, 0); +static SENSOR_DEVICE_ATTR(deviation, S_IRUGO, show_deviation, NULL, 0); +static SENSOR_DEVICE_ATTR(customer_sn, S_IRUGO, show_customer_sn, NULL, 0); +static SENSOR_DEVICE_ATTR(fru_ver, S_IRUGO, show_fru_ver, NULL, 0); +static SENSOR_DEVICE_ATTR(mfg_date, S_IRUGO, show_mfg_date, NULL, 0); + +static struct attribute *eeprom_attributes[] = { + &sensor_dev_attr_read_eeprom.dev_attr.attr, + &sensor_dev_attr_part_number.dev_attr.attr, + &sensor_dev_attr_serial_number.dev_attr.attr, + &sensor_dev_attr_product_version.dev_attr.attr, + &sensor_dev_attr_mfg_name.dev_attr.attr, + &sensor_dev_attr_product_name.dev_attr.attr, + &sensor_dev_attr_clei_code.dev_attr.attr, + &sensor_dev_attr_deviation.dev_attr.attr, + &sensor_dev_attr_customer_sn.dev_attr.attr, + &sensor_dev_attr_fru_ver.dev_attr.attr, + &sensor_dev_attr_mfg_date.dev_attr.attr, + NULL +}; + +static const struct attribute_group eeprom_group = { + .attrs = eeprom_attributes, +}; + +static int eeprom_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct at24_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA)) { + dev_info(&client->dev, "i2c_check_functionality failed!\n"); + status = -EIO; + return status; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + return status; + } + + mutex_init(&data->lock); + i2c_set_clientdata(client, data); + + dev_info(&client->dev, "eeprom chip found\n"); + /* Create sysfs entries */ + status = sysfs_create_group(&client->dev.kobj, &eeprom_group); + if (status) { + dev_err(dev, "Cannot create sysfs\n"); + kfree(data); + return status; + } + data->client = client; + decode_eeprom(client); + return status; +} + +static void eeprom_remove(struct i2c_client *client) +{ + struct at24_data *data = i2c_get_clientdata(client); + sysfs_remove_group(&client->dev.kobj, &eeprom_group); + kfree(data); + return; +} + +static const struct i2c_device_id eeprom_id[] = { + { EEPROM_NAME, 0 }, + { EEPROM_NAME, 0 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, eeprom_id); + +/* Address scanned */ +static const unsigned short normal_i2c[] = { 0x50, 0x51, I2C_CLIENT_END }; + +/* This is the driver that will be inserted */ +static struct i2c_driver eeprom_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = EEPROM_NAME, + }, + .probe = eeprom_probe, + .remove = eeprom_remove, + .id_table = eeprom_id, + .address_list = normal_i2c, +}; + +static int __init eeprom_init(void) +{ + return i2c_add_driver(&eeprom_driver); +} + +static void __exit eeprom_exit(void) +{ + i2c_del_driver(&eeprom_driver); +} + +MODULE_DESCRIPTION("NOKIA EEPROM FRU Sysfs driver"); +MODULE_AUTHOR("Nokia"); +MODULE_LICENSE("GPL"); + +module_init(eeprom_init); +module_exit(eeprom_exit); diff --git a/ixr7220h6-64/modules/embd_ctrl.c b/ixr7220h6-64/modules/embd_ctrl.c new file mode 100644 index 0000000..28ff8cf --- /dev/null +++ b/ixr7220h6-64/modules/embd_ctrl.c @@ -0,0 +1,197 @@ +// * Embedded Controller driver for Nokia Router +// * +// * Copyright (C) 2025 Nokia 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 3 of the License, or +// * 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. +// * see + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "embd_ctrl" + +// REGISTERS ADDRESS MAP +#define CPU_TEMP_REG 0x10 +#define MEM0_TEMP_REG 0x12 +#define MEM1_TEMP_REG 0x13 + +static const unsigned short ec_address_list[] = {0x21, I2C_CLIENT_END}; + +struct ec_data { + struct i2c_client *client; + struct mutex update_lock; +}; + +static int ec_i2c_read(struct ec_data *data, u8 reg) +{ + int val = 0; + struct i2c_client *client = data->client; + + val = i2c_smbus_read_byte_data(client, reg); + if (val < 0) { + dev_warn(&client->dev, "EC READ WARN: reg(0x%02x) err %d\n", reg, val); + } + + return val; +} + +#ifdef EC_WRITE +static void ec_i2c_write(struct ec_data *data, u8 reg, u8 value) +{ + int res = 0; + struct i2c_client *client = data->client; + + mutex_lock(&data->update_lock); + res = i2c_smbus_write_byte_data(client, reg, value); + if (res < 0) { + dev_warn(&client->dev, "EC WRITE WARN: reg(0x%02x) err %d\n", reg, res); + } + mutex_unlock(&data->update_lock); +} +#endif + +static ssize_t show_cpu_temperature(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct ec_data *data = dev_get_drvdata(dev); + u8 val = 0; + val = ec_i2c_read(data, CPU_TEMP_REG); + return sprintf(buf, "%d\n", (s8)val * 1000); +} + +static ssize_t show_mem0_temperature(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct ec_data *data = dev_get_drvdata(dev); + u8 val = 0; + val = ec_i2c_read(data, MEM0_TEMP_REG); + return sprintf(buf, "%d\n", (s8)val * 1000); +} + +static ssize_t show_mem1_temperature(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct ec_data *data = dev_get_drvdata(dev); + u8 val = 0; + val = ec_i2c_read(data, MEM1_TEMP_REG); + return sprintf(buf, "%d\n", (s8)val * 1000); +} + +// sysfs attributes +static SENSOR_DEVICE_ATTR(cpu_temperature, S_IRUGO, show_cpu_temperature, NULL, 0); +static SENSOR_DEVICE_ATTR(mem0_temperature, S_IRUGO, show_mem0_temperature, NULL, 0); +static SENSOR_DEVICE_ATTR(mem1_temperature, S_IRUGO, show_mem1_temperature, NULL, 0); + +static struct attribute *embd_ctrl_attributes[] = { + &sensor_dev_attr_cpu_temperature.dev_attr.attr, + &sensor_dev_attr_mem0_temperature.dev_attr.attr, + &sensor_dev_attr_mem1_temperature.dev_attr.attr, + NULL +}; + +static const struct attribute_group embd_ctrl_group = { + .attrs = embd_ctrl_attributes, +}; + +static int embd_ctrl_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int status = 0; + struct ec_data *data = NULL; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_warn(&client->dev, "EC PROBE WARN: i2c_check_functionality failed (0x%x)\n", client->addr); + status = -EIO; + goto exit; + } + + dev_info(&client->dev, "Nokia embeded controller chip found.\n"); + data = kzalloc(sizeof(struct ec_data), GFP_KERNEL); + + if (!data) { + dev_warn(&client->dev, "EC PROBE WARN: Can't allocate memory\n"); + status = -ENOMEM; + goto exit; + } + + data->client = client; + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + status = sysfs_create_group(&client->dev.kobj, &embd_ctrl_group); + if (status) { + dev_warn(&client->dev, "EC INIT WARN: Cannot create sysfs\n"); + goto exit; + } + + return 0; + +exit: + return status; +} + +static void embd_ctrl_remove(struct i2c_client *client) +{ + struct ec_data *data = i2c_get_clientdata(client); + sysfs_remove_group(&client->dev.kobj, &embd_ctrl_group); + kfree(data); +} + +static const struct of_device_id embd_ctrl_of_ids[] = { + { + .compatible = "Nokia,embd_ctrl", + .data = (void *) 0, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, embd_ctrl_of_ids); + +static const struct i2c_device_id embd_ctrl_ids[] = { + { DRIVER_NAME, 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, embd_ctrl_ids); + +static struct i2c_driver embd_ctrl_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(embd_ctrl_of_ids), + }, + .probe = embd_ctrl_probe, + .remove = embd_ctrl_remove, + .id_table = embd_ctrl_ids, + .address_list = ec_address_list, +}; + +static int __init embd_ctrl_init(void) +{ + return i2c_add_driver(&embd_ctrl_driver); +} + +static void __exit embd_ctrl_exit(void) +{ + i2c_del_driver(&embd_ctrl_driver); +} + +MODULE_AUTHOR("Nokia"); +MODULE_DESCRIPTION("NOKIA Embedded Controller driver"); +MODULE_LICENSE("GPL"); + +module_init(embd_ctrl_init); +module_exit(embd_ctrl_exit); diff --git a/ixr7220h6-64/modules/h6_fan_cpld.c b/ixr7220h6-64/modules/h6_fan_cpld.c new file mode 100644 index 0000000..95b433b --- /dev/null +++ b/ixr7220h6-64/modules/h6_fan_cpld.c @@ -0,0 +1,607 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * A hwmon driver for the Accton h6 fan + * + * Copyright (C) 2024 Accton Technology Corporation. + * Roger Ho + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "h6_fan" + +#define I2C_RW_RETRY_COUNT (10) +#define I2C_RW_RETRY_INTERVAL (60) /* ms */ + +static struct h6_fan_data *h6_fan_update_device(struct device + *dev); +static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t set_fan_led(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t reg_read(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t reg_write(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); + +/* fan related data, the index should match sysfs_fan_attributes + */ +static const u8 fan_reg[] = { + 0x00, /* fan pcb information */ + 0x01, /* fan cpld major version */ + 0x02, /* fan cpld minor version */ + 0x08, /* fan 0-3 present status */ + 0x0e, /* fan 0-3 led */ + 0x10, /* front fan 0 pwm */ + 0x11, /* rear fan 0 pwm */ + 0x12, /* front fan 1 pwm */ + 0x13, /* rear fan 1 pwm */ + 0x14, /* front fan 2 pwm */ + 0x15, /* rear fan 2 pwm */ + 0x16, /* front fan 3 pwm */ + 0x17, /* rear fan 3 pwm */ + 0x20, /* front fan 0 speed(rpm) */ + 0x21, /* rear fan 0 speed(rpm) */ + 0x22, /* front fan 1 speed(rpm) */ + 0x23, /* rear fan 1 speed(rpm) */ + 0x24, /* front fan 2 speed(rpm) */ + 0x25, /* rear fan 2 speed(rpm) */ + 0x26, /* front fan 3 speed(rpm) */ + 0x27, /* rear fan 3 speed(rpm) */ +}; + +/* Each client has this additional data */ +struct h6_fan_data { + struct i2c_client *client; + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 reg_val[ARRAY_SIZE(fan_reg)]; /* Register value */ + u8 reg_addr; +}; + +enum fan_id { + FAN1_ID, + FAN2_ID, + FAN3_ID, + FAN4_ID, +}; + +enum sysfs_fan_attributes { + FAN_PCB_REG, + FAN_MAJOR_VERSION_REG, + FAN_MINOR_VERSION_REG, + FAN_PRESENT_REG, + FAN_LED_REG, + FAN1_FRONT_PWM_REG, + FAN1_REAR_PWM_REG, + FAN2_FRONT_PWM_REG, + FAN2_REAR_PWM_REG, + FAN3_FRONT_PWM_REG, + FAN3_REAR_PWM_REG, + FAN4_FRONT_PWM_REG, + FAN4_REAR_PWM_REG, + FAN1_FRONT_SPEED_RPM_REG, + FAN1_REAR_SPEED_RPM_REG, + FAN2_FRONT_SPEED_RPM_REG, + FAN2_REAR_SPEED_RPM_REG, + FAN3_REAR_SPEED_RPM_REG, + FAN3_FRONT_SPEED_RPM_REG, + FAN4_FRONT_SPEED_RPM_REG, + FAN4_REAR_SPEED_RPM_REG, + + FAN1_RPM, + FAN2_RPM, + FAN3_RPM, + FAN4_RPM, + FAN5_RPM, + FAN6_RPM, + FAN7_RPM, + FAN8_RPM, + FAN1_PRESENT, + FAN2_PRESENT, + FAN3_PRESENT, + FAN4_PRESENT, + FAN1_PWM, + FAN2_PWM, + FAN3_PWM, + FAN4_PWM, + FAN5_PWM, + FAN6_PWM, + FAN7_PWM, + FAN8_PWM, + FAN1_LED, + FAN2_LED, + FAN3_LED, + FAN4_LED, + FAN_FW_VERSION, + FAN_PCB_VERSION, + FAN_ACCESS +}; + +enum fan_led_light_mode { + FAN_LED_MODE_OFF, + FAN_LED_MODE_RED = 10, + FAN_LED_MODE_GREEN = 16, + FAN_LED_MODE_UNKNOWN = 99 +}; + + +/* Define attributes + */ +#define DECLARE_FAN_SENSOR_DEVICE_ATTR(index, index2) \ + static SENSOR_DEVICE_ATTR(fan##index##_present, S_IRUGO, fan_show_value, NULL, \ + FAN##index##_PRESENT); \ + static SENSOR_DEVICE_ATTR(fan##index##_pwm, S_IWUSR | S_IRUGO, fan_show_value, \ + set_duty_cycle, FAN##index##_PWM); \ + static SENSOR_DEVICE_ATTR(fan##index2##_pwm, S_IWUSR | S_IRUGO, fan_show_value, \ + set_duty_cycle, FAN##index2##_PWM); \ + static SENSOR_DEVICE_ATTR(fan##index##_input, S_IRUGO, fan_show_value, NULL, \ + FAN##index##_RPM); \ + static SENSOR_DEVICE_ATTR(fan##index2##_input, S_IRUGO, fan_show_value, NULL, \ + FAN##index2##_RPM); \ + static SENSOR_DEVICE_ATTR(fan##index##_led, S_IRUGO, fan_show_value, NULL,\ + FAN##index##_LED); \ + +#define DECLARE_FAN_ATTR(index, index2) \ + &sensor_dev_attr_fan##index##_present.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_pwm.dev_attr.attr, \ + &sensor_dev_attr_fan##index2##_pwm.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_input.dev_attr.attr, \ + &sensor_dev_attr_fan##index2##_input.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_led.dev_attr.attr + +#define DECLARE_FAN_FW_VERSION_SENSOR_DEV_ATTR() \ + static SENSOR_DEVICE_ATTR(version, S_IRUGO, fan_show_value, NULL, FAN_FW_VERSION) + +#define DECLARE_FAN_FW_VERSION_ATTR() &sensor_dev_attr_version.dev_attr.attr + +#define DECLARE_FAN_PCB_VERSION_SENSOR_DEV_ATTR() \ + static SENSOR_DEVICE_ATTR(pcb_version, S_IRUGO, fan_show_value, NULL, FAN_PCB_VERSION) + +#define DECLARE_FAN_PCB_VERSION_ATTR() &sensor_dev_attr_pcb_version.dev_attr.attr + +#define DECLARE_FAN_ACCESS_SENSOR_DEV_ATTR() \ + static SENSOR_DEVICE_ATTR(access, S_IWUSR | S_IRUGO, reg_read, reg_write, FAN_ACCESS) + +#define DECLARE_FAN_ACCESS_ATTR() &sensor_dev_attr_access.dev_attr.attr + +DECLARE_FAN_SENSOR_DEVICE_ATTR(1, 5); +DECLARE_FAN_SENSOR_DEVICE_ATTR(2, 6); +DECLARE_FAN_SENSOR_DEVICE_ATTR(3, 7); +DECLARE_FAN_SENSOR_DEVICE_ATTR(4, 8); +DECLARE_FAN_FW_VERSION_SENSOR_DEV_ATTR(); +DECLARE_FAN_PCB_VERSION_SENSOR_DEV_ATTR(); +DECLARE_FAN_ACCESS_SENSOR_DEV_ATTR(); + +static struct attribute *h6_fan_attributes[] = { + /* fan related attributes */ + DECLARE_FAN_ATTR(1, 5), + DECLARE_FAN_ATTR(2, 6), + DECLARE_FAN_ATTR(3, 7), + DECLARE_FAN_ATTR(4, 8), + DECLARE_FAN_FW_VERSION_ATTR(), + DECLARE_FAN_PCB_VERSION_ATTR(), + DECLARE_FAN_ACCESS_ATTR(), + NULL +}; + +#define FAN_DUTY_CYCLE_REG_MASK 0xF +#define FAN_MAX_DUTY_CYCLE 100 +#define FAN_REG_VAL_TO_SPEED_RPM_STEP 150 + +static int h6_fan_read_value(struct i2c_client *client, u8 reg) +{ + int status = 0, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_read_byte_data(client, reg); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + return status; +} + +static int h6_fan_write_value(struct i2c_client *client, u8 reg, + u8 value) +{ + int status = 0, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_write_byte_data(client, reg, value); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + return status; +} + +static u32 reg_val_to_speed_rpm(u8 reg_val) +{ + return (u32) reg_val *FAN_REG_VAL_TO_SPEED_RPM_STEP; +} + +static u8 reg_val_to_is_present(u8 reg_val, enum fan_id id) +{ + return !((reg_val >> id) & 0x1); +} + +static u8 reg_val_to_color(u8 reg_val, enum fan_id id) +{ + u8 green_mask = (1 << (7 - (id * 2))); + u8 red_mask = (1 << (6 - (id * 2))); + + if (!(reg_val & green_mask)) + return FAN_LED_MODE_GREEN; + else if (!(reg_val & red_mask)) + return FAN_LED_MODE_RED; + else + return FAN_LED_MODE_OFF; +} + +static u8 reg_val_to_led(u8 reg_val, enum fan_id id) +{ + return (reg_val >> (id * 2)) & 0x3; +} + +static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int error, value; + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct h6_fan_data *data = dev_get_drvdata(dev); + u8 reg_val, idx; + + error = kstrtoint(buf, 10, &value); + if (error) { + return error; + } + + if (value < 0 || value > FAN_MAX_DUTY_CYCLE) { + return -EINVAL; + } + + switch (attr->index) { + case FAN1_PWM ... FAN8_PWM: + reg_val = (value * 100) / 666; + idx = (attr->index - FAN1_PWM); + + mutex_lock(&data->update_lock); + /* Front FAN */ + h6_fan_write_value(data->client, + fan_reg[FAN1_FRONT_PWM_REG + idx], + reg_val); + /* force update register */ + data->valid = 0; + mutex_unlock(&data->update_lock); + break; + default: + break; + } + + return count; +} + +static ssize_t set_fan_led(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int error, value; + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct h6_fan_data *data = dev_get_drvdata(dev); + u8 green_mask, red_mask; + u8 reg_val; + + error = kstrtoint(buf, 10, &value); + if (error) { + return error; + } + + if (value < 0) { + return -EINVAL; + } + else if (value != FAN_LED_MODE_GREEN && + value != FAN_LED_MODE_RED && + value != FAN_LED_MODE_OFF) { + return -EINVAL; + } + + switch (attr->index) { + case FAN1_LED ... FAN4_LED: + green_mask = (1 << (7 - ((attr->index - FAN1_LED) * 2))); + red_mask = (1 << (6 - ((attr->index - FAN1_LED) * 2))); + reg_val = h6_fan_read_value(data->client, fan_reg[FAN_LED_REG]); + switch (value) { + case FAN_LED_MODE_RED: + reg_val |= green_mask; + reg_val &= ~(red_mask); + break; + case FAN_LED_MODE_GREEN: + reg_val |= red_mask; + reg_val &= ~(green_mask); + break; + case FAN_LED_MODE_OFF: + reg_val |= (green_mask); + reg_val |= (red_mask); + break; + default: + break; + } + mutex_lock(&data->update_lock); + h6_fan_write_value(data->client, fan_reg[FAN_LED_REG], + reg_val); + /* force update register */ + data->valid = 0; + mutex_unlock(&data->update_lock); + break; + default: + break; + } + + return count; +} + +static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct h6_fan_data *data = h6_fan_update_device(dev); + ssize_t ret = 0; + u8 idx, reg_val; + + if (!data->valid) { + return ret; + } + + switch (attr->index) { + case FAN_PCB_VERSION: + ret = sprintf(buf, "0x%02x\n", data->reg_val[FAN_PCB_REG]); + break; + case FAN_FW_VERSION: + ret = sprintf(buf, "%d.%d\n", + data->reg_val[FAN_MAJOR_VERSION_REG], + data->reg_val[FAN_MINOR_VERSION_REG]); + break; + case FAN1_PWM ... FAN8_PWM: + reg_val = data->reg_val[FAN1_FRONT_PWM_REG + + (attr->index - FAN1_PWM)] & 0x0F; + ret = sprintf(buf, "%u\n", (reg_val * 667) / 100); + break; + case FAN1_RPM ... FAN8_RPM: + idx = FAN1_FRONT_SPEED_RPM_REG + (attr->index - FAN1_RPM); + ret = sprintf(buf, "%u\n", + reg_val_to_speed_rpm(data->reg_val[idx])); + break; + case FAN1_PRESENT ... FAN4_PRESENT: + ret = sprintf(buf, "%d\n", + reg_val_to_is_present(data->reg_val[FAN_PRESENT_REG], + attr->index - FAN1_PRESENT)); + break; + case FAN1_LED ... FAN4_LED: + ret = sprintf(buf, "%d\n", + reg_val_to_led(data->reg_val[FAN_LED_REG], + attr->index - FAN1_LED)); + break; + default: + break; + } + + return ret; +} + +static ssize_t reg_read(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct h6_fan_data *data = dev_get_drvdata(dev); + ssize_t ret = -EINVAL; + u8 reg_val; + + reg_val = h6_fan_read_value(data->client, data->reg_addr); + ret = sprintf(buf, "0x%02x\n", reg_val); + + return ret; +} + +static ssize_t reg_write(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct h6_fan_data *data = dev_get_drvdata(dev); + int args; + char *opt, tmp[32] = { 0 }; + char *tmp_p; + size_t copy_size; + u8 input[2] = { 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 = tmp; + while (args < 2 && (opt = strsep(&tmp_p, " ")) != NULL) { + if (kstrtou8(opt, 16, &input[args]) == 0) { + args++; + } + } + + switch (args) { + case 2: + /* Write value to register */ + mutex_lock(&data->update_lock); + h6_fan_write_value(data->client, input[0], input[1]); + data->valid = 0; + mutex_unlock(&data->update_lock); + break; + case 1: + /* Read value from register */ + data->reg_addr = input[0]; + break; + default: + return -EINVAL; + } + + return count; +} + +static const struct attribute_group h6_fan_group = { + .attrs = h6_fan_attributes, +}; + +__ATTRIBUTE_GROUPS(h6_fan); + +static struct h6_fan_data *h6_fan_update_device(struct device + *dev) +{ + struct h6_fan_data *data = dev_get_drvdata(dev); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || + !data->valid) { + int i; + + dev_dbg(dev, "Starting h6_fan update\n"); + data->valid = 0; + + /* Update fan data */ + for (i = 0; i < ARRAY_SIZE(data->reg_val); i++) { + int status = h6_fan_read_value(data->client, fan_reg[i]); + if (status < 0) { + data->valid = 0; + mutex_unlock(&data->update_lock); + dev_dbg(dev, "reg %d, err %d\n", fan_reg[i], + status); + return data; + } else { + data->reg_val[i] = status; + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + return data; +} + +static int h6_fan_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct h6_fan_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct h6_fan_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + data->valid = 0; + data->client = client; + mutex_init(&data->update_lock); + + dev_info(&client->dev, "chip found\n"); + + data->hwmon_dev = + hwmon_device_register_with_groups(&client->dev, client->name, data, + h6_fan_groups); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_free; + } + + dev_info(&client->dev, "%s: fan '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + + exit_free: + kfree(data); + exit: + return status; +} + +static void h6_fan_remove(struct i2c_client *client) +{ + struct h6_fan_data *data = i2c_get_clientdata(client); + hwmon_device_unregister(data->hwmon_dev); + kfree(data); + +} + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { 0x33, I2C_CLIENT_END }; + +static const struct i2c_device_id h6_fan_id[] = { + {"h6_fan", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, h6_fan_id); + +static struct i2c_driver h6_fan_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = DRVNAME, + }, + .probe = h6_fan_probe, + .remove = h6_fan_remove, + .id_table = h6_fan_id, + .address_list = normal_i2c, +}; + +module_i2c_driver(h6_fan_driver); + +MODULE_AUTHOR("Roger Ho "); +MODULE_DESCRIPTION("FAN Driver"); +MODULE_LICENSE("GPL"); diff --git a/ixr7220h6-64/modules/h6_i2c_oc.c b/ixr7220h6-64/modules/h6_i2c_oc.c new file mode 100644 index 0000000..6cc3d52 --- /dev/null +++ b/ixr7220h6-64/modules/h6_i2c_oc.c @@ -0,0 +1,258 @@ +// Driver for Nokia-7220-IXR-H6-64 Router +/* + * Copyright (C) 2025 Accton Technology Corporation. + * Copyright (C) 2025 Nokia 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 3 of the License, or + * 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. + * see + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PORT_NUM (64 + 2) /*64 OSFPs + 2 SFP28s*/ + +/* + * PCIE BAR0 address + */ +#define BAR0_NUM 0 +#define BAR1_NUM 1 +#define BAR2_NUM 2 +#define REGION_LEN 0xFF +#define FPGA_PCI_VENDOR_ID 0x10ee +#define FPGA_PCI_DEVICE_ID 0x7021 + +/* CPLD 1 */ +#define CPLD1_PCIE_START_OFFSET 0x2000 + +/* CPLD 2 */ +#define CPLD2_PCIE_START_OFFSET 0x3000 + +static uint param_i2c_khz = 400; +module_param(param_i2c_khz, uint, S_IRUGO); +MODULE_PARM_DESC(param_i2c_khz, "Target clock speed of i2c bus, in KHz."); + +static struct pci_dev *g_pcidev = NULL; + +static const uint adapt_offset[PORT_NUM]= { + 0x2100,// CPLD1 I2C Master OSFP Port0 + 0x2120,// CPLD1 I2C Master OSFP Port1 + 0x2140,// CPLD1 I2C Master OSFP Port2 + 0x2160,// CPLD1 I2C Master OSFP Port3 + 0x2180,// CPLD1 I2C Master OSFP Port4 + 0x21A0,// CPLD1 I2C Master OSFP Port5 + 0x21C0,// CPLD1 I2C Master OSFP Port6 + 0x21E0,// CPLD1 I2C Master OSFP Port7 + 0x2200,// CPLD1 I2C Master OSFP Port8 + 0x2220,// CPLD1 I2C Master OSFP Port9 + 0x2240,// CPLD1 I2C Master OSFP Port10 + 0x2260,// CPLD1 I2C Master OSFP Port11 + 0x2280,// CPLD1 I2C Master OSFP Port12 + 0x22A0,// CPLD1 I2C Master OSFP Port13 + 0x22C0,// CPLD1 I2C Master OSFP Port14 + 0x22E0,// CPLD1 I2C Master OSFP Port15 + 0x3100,// CPLD2 I2C Master OSFP Port16 + 0x3120,// CPLD2 I2C Master OSFP Port17 + 0x3140,// CPLD2 I2C Master OSFP Port18 + 0x3160,// CPLD2 I2C Master OSFP Port19 + 0x3180,// CPLD2 I2C Master OSFP Port20 + 0x31A0,// CPLD2 I2C Master OSFP Port21 + 0x31C0,// CPLD2 I2C Master OSFP Port22 + 0x31E0,// CPLD2 I2C Master OSFP Port23 + 0x3200,// CPLD2 I2C Master OSFP Port24 + 0x3220,// CPLD2 I2C Master OSFP Port25 + 0x3240,// CPLD2 I2C Master OSFP Port26 + 0x3260,// CPLD2 I2C Master OSFP Port27 + 0x3280,// CPLD2 I2C Master OSFP Port28 + 0x32A0,// CPLD2 I2C Master OSFP Port29 + 0x32C0,// CPLD2 I2C Master OSFP Port30 + 0x32E0,// CPLD2 I2C Master OSFP Port31 + 0x2300,// CPLD1 I2C Master OSFP Port32 + 0x2320,// CPLD1 I2C Master OSFP Port33 + 0x2340,// CPLD1 I2C Master OSFP Port34 + 0x2360,// CPLD1 I2C Master OSFP Port35 + 0x2380,// CPLD1 I2C Master OSFP Port36 + 0x23A0,// CPLD1 I2C Master OSFP Port37 + 0x23C0,// CPLD1 I2C Master OSFP Port38 + 0x23E0,// CPLD1 I2C Master OSFP Port39 + 0x2400,// CPLD1 I2C Master OSFP Port40 + 0x2420,// CPLD1 I2C Master OSFP Port41 + 0x2440,// CPLD1 I2C Master OSFP Port42 + 0x2460,// CPLD1 I2C Master OSFP Port43 + 0x2480,// CPLD1 I2C Master OSFP Port44 + 0x24A0,// CPLD1 I2C Master OSFP Port45 + 0x24C0,// CPLD1 I2C Master OSFP Port46 + 0x24E0,// CPLD1 I2C Master OSFP Port47 + 0x3300,// CPLD2 I2C Master OSFP Port48 + 0x3320,// CPLD2 I2C Master OSFP Port49 + 0x3340,// CPLD2 I2C Master OSFP Port50 + 0x3360,// CPLD2 I2C Master OSFP Port51 + 0x3380,// CPLD2 I2C Master OSFP Port52 + 0x33A0,// CPLD2 I2C Master OSFP Port53 + 0x33C0,// CPLD2 I2C Master OSFP Port54 + 0x33E0,// CPLD2 I2C Master OSFP Port55 + 0x3400,// CPLD2 I2C Master OSFP Port56 + 0x3420,// CPLD2 I2C Master OSFP Port57 + 0x3440,// CPLD2 I2C Master OSFP Port58 + 0x3460,// CPLD2 I2C Master OSFP Port59 + 0x3480,// CPLD2 I2C Master OSFP Port60 + 0x34A0,// CPLD2 I2C Master OSFP Port61 + 0x34C0,// CPLD2 I2C Master OSFP Port62 + 0x34E0,// CPLD2 I2C Master OSFP Port63 + 0x2500,// CPLD2 I2C Master SFP-28 Port64 + 0x2520,// CPLD2 I2C Master SFP-28 Port65 +}; + +static struct ocores_i2c_platform_data h6_64 = { + .reg_shift = 2, + .clock_khz = 25000, + .bus_khz = 400, + .devices = NULL, //trcv_nvm, + .num_devices = 1 //ARRAY_SIZE(trcv_nvm) +}; + +static void ftdi_release_platform_dev(struct device *dev) +{ + dev->parent = NULL; +} + +static struct platform_device myi2c[PORT_NUM] = {{0}}; +static int __init h6_ocore_i2c_init(void) +{ + int i, err = 0; + static const char *devname = "ocores-i2c"; + static struct resource ocores_resources[PORT_NUM] = {0}; + struct pci_dev *pcidev; + int status = 0; + unsigned long bar_base; + struct resource *res; + struct platform_device *p = NULL; + + pcidev = pci_get_device(FPGA_PCI_VENDOR_ID, FPGA_PCI_DEVICE_ID, NULL); + if (!pcidev) { + pr_err("Cannot found PCI device(%x:%x)\n", + FPGA_PCI_VENDOR_ID, FPGA_PCI_DEVICE_ID); + return -ENODEV; + } + + g_pcidev = pcidev; + + err = pci_enable_device(pcidev); + if (err != 0) { + pr_err("Cannot enable PCI device(%x:%x)\n", + FPGA_PCI_VENDOR_ID, FPGA_PCI_DEVICE_ID); + status = -ENODEV; + goto exit_pci_put; + } + /* enable PCI bus-mastering */ + pci_set_master(pcidev); + + status = pci_enable_msi(pcidev); + if (status < 0) { + pr_err("Failed to allocate IRQ vectors: %d\n", status); + goto exit_pci_disable; + } + + h6_64.bus_khz = clamp_val(param_i2c_khz, 50, 400); + for(i = 0; i < PORT_NUM; i++) { + p = &myi2c[i]; + p->name = devname; + p->id = i; + p->dev.platform_data = &h6_64; + p->dev.release = ftdi_release_platform_dev; + res = &ocores_resources[i]; + switch (i) + { + // Nokia TH6 has three BAR address + case 0 ... 15: + bar_base = pci_resource_start(pcidev, BAR1_NUM); + break; + case 16 ... 31: + bar_base = pci_resource_start(pcidev, BAR2_NUM); + break; + case 32 ... 47: + bar_base = pci_resource_start(pcidev, BAR1_NUM); + break; + case 48 ... 63: + bar_base = pci_resource_start(pcidev, BAR2_NUM); + break; + case 64 ... 65: + bar_base = pci_resource_start(pcidev, BAR1_NUM); + break; + default: + break; + } + res->start = bar_base + adapt_offset[i]; + res->end = res->start + 0x20 - 1; + res->name = NULL; + res->flags =IORESOURCE_MEM; + res->desc = IORES_DESC_NONE; + p->num_resources = 1; + p->resource = res; + err = platform_device_register(p); + if (err) + goto unload; + } + + return 0; + +unload: + { + int j; + pr_err("[ERROR]rc:%d, unload %u register devices\n", err, i); + for(j = 0; j < i; j++) { + platform_device_unregister(&myi2c[j]); + } + } + pci_disable_msi(pcidev); + +exit_pci_disable: + pci_disable_device(pcidev); + +exit_pci_put: + pci_dev_put(pcidev); + g_pcidev = NULL; + + return status ? status : err; +} + +static void __exit h6_ocore_i2c_exit(void) +{ + int i; + for(i = PORT_NUM ; i > 0; i--) { + platform_device_unregister(&myi2c[i-1]); + } + if (g_pcidev) { + pci_disable_msi(g_pcidev); + pci_disable_device(g_pcidev); + pci_dev_put(g_pcidev); + g_pcidev = NULL; + } +} + +module_init(h6_ocore_i2c_init); +module_exit(h6_ocore_i2c_exit); + +MODULE_AUTHOR("Roy Lee "); +MODULE_DESCRIPTION("h6 ocore_i2c platform device driver"); +MODULE_LICENSE("GPL"); diff --git a/ixr7220h6-64/modules/i2c-ocores.c b/ixr7220h6-64/modules/i2c-ocores.c new file mode 100644 index 0000000..00e3e21 --- /dev/null +++ b/ixr7220h6-64/modules/i2c-ocores.c @@ -0,0 +1,883 @@ +// 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 + +enum _print_level {PL_ERR, PL_WARN, PL_INFO, PL_DEBUG}; + +static uint param_verbose = PL_WARN; +module_param(param_verbose, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(param_verbose, "Print more information for debugging. It can be 0,1 2, or 3."); + +/* + * '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 */ + +#define DEBUG(args...) \ + debug_print(__func__, __LINE__, PL_DEBUG, args) +#define INFO(args...) \ + debug_print(__func__, __LINE__, PL_INFO, args) +#define _WARN(args...) \ + debug_print(__func__, __LINE__, PL_WARN, args) +#define ERR(args...) \ + debug_print(__func__, __LINE__, PL_ERR, args) +/*-----------------------------------------------------------------------*/ +static void debug_print(const char *func, int line, u32 level, + const char *fmt, ...) +{ + va_list args; + char buf[256]; + if (param_verbose >= level) { + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + pr_info("[%d]%s#%d: %s\n", level, func, line, buf); + } +} + +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) +{ + i2c->setreg(i2c, reg, value); + DEBUG("Write 0x%02x to 0x%02x\n", value, reg); +} + +static inline u8 oc_getreg(struct ocores_i2c *i2c, int reg) +{ + u8 value; + value = i2c->getreg(i2c, reg); + + DEBUG("Read 0x%02x from 0x%02x\n", value, reg); + return value; +} + +static void ocores_process(struct ocores_i2c *i2c, u8 stat) +{ + struct i2c_msg *msg = i2c->msg; + unsigned long flags; + + /* + * 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; + /*_WARN("I2C %s arbitration lost", i2c->adap.name);*/ + dev_warn(i2c->adap.dev.parent, "arbitration lost, stat:%02x", stat); + 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; + /*DEBUG("I2C %s, no ACK from slave 0x%02x", i2c->adap.name, msg->addr);*/ + dev_warn(i2c->adap.dev.parent, "no ACK from slave 0x%02x", 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(1)); + if (err) + dev_warn(i2c->adap.dev.parent, + "%s: STATUS timeout, bit 0x%x did not clear in 1 ms\n", + __func__, mask); + 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 + */ +static void ocores_process_polling(struct ocores_i2c *i2c) +{ + while (1) { + irqreturn_t ret; + int err; + + err = ocores_poll_wait(i2c); + if (err) { + i2c->state = STATE_ERROR; + 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; + } + } +} + +static int ocores_xfer_core(struct ocores_i2c *i2c, + struct i2c_msg *msgs, int num, + bool polling) +{ + int ret; + u8 ctrl; + + 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) { + ocores_process_polling(i2c); + } else { + ret = wait_event_timeout(i2c->wait, + (i2c->state == STATE_ERROR) || + (i2c->state == STATE_DONE), HZ); + if (ret == 0) { + ocores_process_timeout(i2c); + return -ETIMEDOUT; + } + } + + 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) +{ + int prescale; + int diff; + 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); + + 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) { + dev_err(dev, + "Unsupported clock settings: core: %d KHz, bus: %d KHz\n", + i2c->ip_clock_khz, i2c->bus_clock_khz); + return -EINVAL; + } + + 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); + + 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); + + DEBUG("Write %d KHz => %dKHz", i2c->ip_clock_khz, 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 void __iomem *ocores_devm_ioremap(struct device *dev, struct resource *res) +{ + resource_size_t size; + void __iomem *dest_ptr; + + BUG_ON(!dev); + + if (!res || resource_type(res) != IORESOURCE_MEM) { + dev_err(dev, "invalid resource\n"); + return IOMEM_ERR_PTR(-EINVAL); + } + + size = resource_size(res); + dest_ptr = devm_ioremap(dev, res->start, size); + if (!dest_ptr) { + dev_err(dev, "ioremap failed for resource %pR\n", res); + dest_ptr = IOMEM_ERR_PTR(-ENOMEM); + } + + return dest_ptr; +} + +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 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 = ocores_devm_ioremap(&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; + INFO("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 { + INFO("No specific config"); + 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 == -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; + + DEBUG("Write ocores_init"); + ret = ocores_init(&pdev->dev, i2c); + if (ret) { + ERR("Fail init device id %u", pdev->id); + 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; + DEBUG("pdev:%p dev:%p = %p", pdev, &pdev->dev, i2c->adap.dev.parent); + i2c->adap.dev.of_node = pdev->dev.of_node; + DEBUG("Config adap:%s", i2c->adap.name); + + /* add i2c adapter to i2c tree */ + ret = i2c_add_adapter(&i2c->adap); + if (ret) { + ERR("Fail to add adap:%s", i2c->adap.name); + goto err_clk; + } + + return 0; + +err_clk: + DEBUG("err_ret:%d", ret); + 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 = oc_getreg(i2c, OCI2C_CONTROL); + + /* disable i2c logic */ + ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN); + oc_setreg(i2c, OCI2C_CONTROL, ctrl); + + /* 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-i2c", + .of_match_table = ocores_i2c_match, + .pm = OCORES_I2C_PM, + }, +}; + +static int __init ocores_i2c_as1813_init(void) +{ + int err; + + err = platform_driver_register(&ocores_i2c_driver); + if (err < 0) { + ERR("Failed to register ocores_i2c_driver"); + return err; + } + return 0; +} +static void __exit ocores_i2c_as1813_exit(void) +{ + platform_driver_unregister(&ocores_i2c_driver); + +} + +module_init(ocores_i2c_as1813_init); +module_exit(ocores_i2c_as1813_exit); + +MODULE_AUTHOR("Peter Korsgaard "); +MODULE_DESCRIPTION("OpenCores I2C bus driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ocores-i2c"); \ No newline at end of file diff --git a/ixr7220h6-64/modules/pmbus_psu.c b/ixr7220h6-64/modules/pmbus_psu.c new file mode 100644 index 0000000..bb59365 --- /dev/null +++ b/ixr7220h6-64/modules/pmbus_psu.c @@ -0,0 +1,563 @@ +// Driver for delta PSU +// +// Copyright (C) 2025 Delta Network Technology Corporation +// Copyright (C) 2025 Nokia Corporation. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* PSU parameter */ +#define PSU_REG_OPERATION (0x01) +#define PSU_REG_RW_VOUT_MODE (0x20) +#define PSU_REG_STATUS (0x79) +#define PSU_REG_RO_FAN_STATUS (0x81) +#define PSU_REG_RO_FAN_SPEED (0x90) +#define PSU_REG_RO_VIN (0x88) +#define PSU_REG_RO_VOUT (0x8b) +#define PSU_REG_RO_IIN (0x89) +#define PSU_REG_RO_IOUT (0x8c) +#define PSU_REG_RO_POUT (0x96) +#define PSU_REG_RO_PIN (0x97) +#define PSU_REG_RO_TEMP1 (0x8d) +#define PSU_REG_RO_TEMP2 (0x8e) +#define PSU_REG_RO_TEMP3 (0x8f) +#define PSU_REG_RO_MFR_MODEL (0x9a) +#define PSU_REG_RO_MFR_SERIAL (0x9e) +#define PSU_REG_FW_REV (0xd9) +#define PSU_REG_LED (0xe2) +#define PSU_MFR_MODELNAME_LENGTH (16) +#define PSU_MFR_SERIALNUM_LENGTH (20) +#define PSU_DRIVER_NAME "pmbus_psu" + +/* fan in PSU */ +#define PSU_FAN_NUMBER (1) +#define PSU_FAN1_FAULT_BIT (7) + +/* thermal in PSU */ +#define PSU_THERMAL_NUMBER (3) + +/* Address scanned */ +static const unsigned short normal_i2c[] = { 0x58, 0x59, 0x5a, 0x5b, I2C_CLIENT_END }; + +/* This is additional data */ +struct psu_data +{ + struct mutex update_lock; + char valid; + unsigned long last_updated; /* In jiffies */ + /* Registers value */ + u8 vout_mode; + u16 v_in; + u16 v_out; + u16 i_in; + u16 i_out; + u16 p_in; + u16 p_out; + u16 temp_input[PSU_THERMAL_NUMBER]; + u8 fan_fault; + u16 fan_speed[PSU_FAN_NUMBER]; +}; + +static int two_complement_to_int(u16 data, u8 valid_bit, int mask); +static int calculate_return_value(int value); +static ssize_t for_vin(struct device *dev, struct device_attribute *dev_attr, char *buf); +static ssize_t for_iin(struct device *dev, struct device_attribute *dev_attr, char *buf); +static ssize_t for_iout(struct device *dev, struct device_attribute *dev_attr, char *buf); +static ssize_t for_pin(struct device *dev, struct device_attribute *dev_attr, char *buf); +static ssize_t for_pout(struct device *dev, struct device_attribute *dev_attr, char *buf); +static ssize_t for_temp1(struct device *dev, struct device_attribute *dev_attr, char *buf); +static ssize_t for_temp2(struct device *dev, struct device_attribute *dev_attr, char *buf); +static ssize_t for_temp3(struct device *dev, struct device_attribute *dev_attr, char *buf); +static ssize_t for_fan_speed(struct device *dev, struct device_attribute *dev_attr, char *buf); +static ssize_t for_fan_fault(struct device *dev, struct device_attribute *dev_attr, char *buf); +static ssize_t for_vout_data(struct device *dev, struct device_attribute *dev_attr, char *buf); +static int psu_read_byte(struct i2c_client *client, u8 reg); +static int psu_read_word(struct i2c_client *client, u8 reg); +static int psu_read_block(struct i2c_client *client, u8 command, u8 *data); +static struct psu_data *psu_update_device(struct device *dev, u8 reg); +static ssize_t for_serial(struct device *dev, struct device_attribute *dev_attr, char *buf); +static ssize_t for_model(struct device *dev, struct device_attribute *dev_attr, char *buf); + +enum psu_sysfs_attributes +{ + PSU_V_IN, + PSU_V_OUT, + PSU_I_IN, + PSU_I_OUT, + PSU_P_IN, + PSU_P_OUT, + PSU_TEMP1_INPUT, + PSU_TEMP2_INPUT, + PSU_TEMP3_INPUT, + PSU_FAN1_FAULT, + PSU_FAN1_DUTY_CYCLE, + PSU_FAN1_SPEED, + PSU_MFR_MODEL, + PSU_MFR_SERIAL, +}; + +static int two_complement_to_int(u16 data, u8 valid_bit, int mask) +{ + u16 valid_data = data & mask; + bool is_negative = valid_data >> (valid_bit - 1); + + return is_negative ? (-(((~valid_data) & mask) + 1)) : valid_data; +} + +static int calculate_return_value(int value) +{ + int multiplier = 1000; + int exponent = 0, mantissa = 0; + + exponent = two_complement_to_int(value >> 11, 5, 0x1f); + mantissa = two_complement_to_int(value & 0x7ff, 11, 0x7ff); + + return (exponent >= 0) ? (mantissa << exponent) * multiplier : (mantissa * multiplier) / (1 << -exponent); + +} + +static ssize_t for_vin(struct device *dev, struct device_attribute *dev_attr, char *buf) +{ + struct psu_data *data = psu_update_device(dev, PSU_REG_RO_VIN); + + return sprintf(buf, "%d\n", calculate_return_value(data->v_in)); +} + +static ssize_t for_iin(struct device *dev, struct device_attribute *dev_attr, char *buf) +{ + struct psu_data *data = psu_update_device(dev, PSU_REG_RO_IIN); + + + return sprintf(buf, "%d\n", calculate_return_value(data->i_in)); +} + +static ssize_t for_iout(struct device *dev, struct device_attribute *dev_attr, char *buf) +{ + struct psu_data *data = psu_update_device(dev, PSU_REG_RO_IOUT); + + return sprintf(buf, "%d\n", calculate_return_value(data->i_out)); +} + +static ssize_t for_pin(struct device *dev, struct device_attribute *dev_attr, char *buf) +{ + struct psu_data *data = psu_update_device(dev, PSU_REG_RO_PIN); + + return sprintf(buf, "%d\n", calculate_return_value(data->p_in)); +} + +static ssize_t for_pout(struct device *dev, struct device_attribute *dev_attr, char *buf) +{ + struct psu_data *data = psu_update_device(dev, PSU_REG_RO_POUT); + + return sprintf(buf, "%d\n", calculate_return_value(data->p_out)); +} + +static ssize_t for_temp1(struct device *dev, struct device_attribute *dev_attr, char *buf) +{ + struct psu_data *data = psu_update_device(dev, PSU_REG_RO_TEMP1); + + return sprintf(buf, "%d\n", calculate_return_value(data->temp_input[0])); +} + +static ssize_t for_temp2(struct device *dev, struct device_attribute *dev_attr, char *buf) +{ + struct psu_data *data = psu_update_device(dev, PSU_REG_RO_TEMP2); + + return sprintf(buf, "%d\n", calculate_return_value(data->temp_input[1])); +} + +static ssize_t for_temp3(struct device *dev, struct device_attribute *dev_attr, char *buf) +{ + struct psu_data *data = psu_update_device(dev, PSU_REG_RO_TEMP3); + + return sprintf(buf, "%d\n", calculate_return_value(data->temp_input[2])); +} + +static ssize_t for_fan_speed(struct device *dev, struct device_attribute *dev_attr, char *buf) +{ + struct psu_data *data = psu_update_device(dev, PSU_REG_RO_FAN_SPEED); + + return sprintf(buf, "%d\n", calculate_return_value(data->fan_speed[0])/1000); +} + +static ssize_t for_vout_data(struct device *dev, struct device_attribute *dev_attr, char *buf) +{ + struct psu_data *data = psu_update_device(dev, PSU_REG_RW_VOUT_MODE); + int exponent = 0, mantissa = 0; + int multiplier = 1000; + + data = psu_update_device(dev, PSU_REG_RO_VOUT); + mdelay(30); + exponent = two_complement_to_int(data->vout_mode, 5, 0x1f); + mantissa = data->v_out; + + return (exponent > 0) ? sprintf(buf, "%d\n", \ + mantissa * (1 << exponent)) : \ + sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent)); +} + +static ssize_t for_fan_fault(struct device *dev, struct device_attribute *dev_attr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct psu_data *data = psu_update_device(dev, PSU_REG_RO_FAN_STATUS); + + u8 shift = (attr->index == PSU_FAN1_FAULT) ? PSU_FAN1_FAULT_BIT : (PSU_FAN1_FAULT_BIT - (attr->index - PSU_FAN1_FAULT)); + + return sprintf(buf, "%d\n", data->fan_fault >> shift); +} + +static ssize_t for_model(struct device *dev, struct device_attribute *dev_attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 mfr_model[PSU_MFR_MODELNAME_LENGTH+1]; + int status; + + status = psu_read_block(client, PSU_REG_RO_MFR_MODEL, mfr_model); + if (status < 0) + { + dev_info(&client->dev, "reg %d, err %d\n", PSU_REG_RO_MFR_MODEL, status); + mfr_model[1] = '\0'; + } + else + { + mfr_model[ARRAY_SIZE(mfr_model) - 1] = '\0'; + } + + return sprintf(buf, "%s\n", mfr_model); +} + +static ssize_t for_serial(struct device *dev, struct device_attribute *dev_attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 mfr_serial[PSU_MFR_SERIALNUM_LENGTH+1]; + int status; + + status = psu_read_block(client, PSU_REG_RO_MFR_SERIAL, mfr_serial); + if (status < 0) + { + dev_info(&client->dev, "reg %d, err %d\n", PSU_REG_RO_MFR_SERIAL, status); + mfr_serial[1] = '\0'; + } + else + { + mfr_serial[ARRAY_SIZE(mfr_serial) - 1] = '\0'; + } + + return sprintf(buf, "%s\n", mfr_serial); +} + +static int psu_read_byte(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int psu_read_word(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_word_data(client, reg); +} + +static int psu_write_byte_pec(struct i2c_client *client, u8 reg, \ + u8 value) +{ + union i2c_smbus_data data; + data.byte = value; + return i2c_smbus_xfer(client->adapter, client->addr, + client->flags |= I2C_CLIENT_PEC, + I2C_SMBUS_WRITE, reg, + I2C_SMBUS_BYTE_DATA, &data); +} + +static int psu_read_block(struct i2c_client *client, u8 command, u8 *data) +{ + int result = i2c_smbus_read_block_data(client, command, data); + if (unlikely(result < 0)) + goto abort; + + result = 0; +abort: + return result; +} + +struct reg_data_byte +{ + u8 reg; + u8 *value; +}; + +struct reg_data_word +{ + u8 reg; + u16 *value; +}; + +static struct psu_data *psu_update_device(struct device *dev, u8 reg) +{ + struct i2c_client *client = to_i2c_client(dev); + struct psu_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated)) + { + int i, status; + + struct reg_data_byte regs_byte[] = { + {PSU_REG_RW_VOUT_MODE, &data->vout_mode}, + {PSU_REG_RO_FAN_STATUS, &data->fan_fault} + }; + + struct reg_data_word regs_word[] = { + {PSU_REG_RO_VIN, &data->v_in}, + {PSU_REG_RO_VOUT, &data->v_out}, + {PSU_REG_RO_IIN, &data->i_in}, + {PSU_REG_RO_IOUT, &data->i_out}, + {PSU_REG_RO_POUT, &data->p_out}, + {PSU_REG_RO_PIN, &data->p_in}, + {PSU_REG_RO_TEMP1, &(data->temp_input[0])}, + {PSU_REG_RO_TEMP2, &(data->temp_input[1])}, + {PSU_REG_RO_TEMP3, &(data->temp_input[2])}, + {PSU_REG_RO_FAN_SPEED, &(data->fan_speed[0])}, + }; + + //dev_info(&client->dev, "start data update\n"); + + /* one milliseconds from now */ + data->last_updated = jiffies + HZ / 1000; + + for (i = 0; i < ARRAY_SIZE(regs_byte); i++) + { + if (reg != regs_byte[i].reg) + continue; + + status = psu_read_byte(client, regs_byte[i].reg); + if (status < 0) + { + dev_info(&client->dev, "reg %d, err %d\n", regs_byte[i].reg, status); + *(regs_byte[i].value) = 0; + } + else + { + *(regs_byte[i].value) = status; + } + break; + } + + for (i = 0; i < ARRAY_SIZE(regs_word); i++) + { + if (reg != regs_word[i].reg) + continue; + + status = psu_read_word(client, regs_word[i].reg); + if (status < 0) + { + dev_info(&client->dev, "reg %d, err %d\n", regs_word[i].reg, status); + *(regs_word[i].value) = 0; + } + else + { + *(regs_word[i].value) = status; + } + break; + } + + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + return data; +} + +static ssize_t show_psu_rst(struct device *dev, struct device_attribute *dev_attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 val; + + val = psu_read_byte(client, PSU_REG_OPERATION); + + return sprintf(buf, "0x%02x\n", val); +} + +static ssize_t set_psu_rst(struct device *dev, struct device_attribute *dev_attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + const char *str_in = "Reset\n"; + int res = 0; + + if (strcmp(buf, str_in) == 0) { + dev_warn(&client->dev, "Reg(0x%02x) written to cycle this PSU\n", PSU_REG_OPERATION); + res = psu_write_byte_pec(client, PSU_REG_OPERATION, 0x60); + if (res < 0) { + dev_warn(&client->dev, "%s WRITE ERROR: reg(0x%02x) err %d\n", PSU_DRIVER_NAME, PSU_REG_OPERATION, res); + } + } + else + return -EINVAL; + + return count; +} + +static ssize_t show_psu_ioc(struct device *dev, struct device_attribute *dev_attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + int val; + + val = psu_read_word(client, PSU_REG_STATUS); + + return sprintf(buf, "%d\n", (val>>4) & 0x1 ? 1:0); +} + +static ssize_t show_psu_rev(struct device *dev, struct device_attribute *dev_attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + int status; + u8 rev[4]; + + status = psu_read_block(client, PSU_REG_FW_REV, rev); + + return sprintf(buf, "0x%02x 0x%02x 0x%02x\n", rev[2], rev[1], rev[0]); +} + +static ssize_t show_psu_led(struct device *dev, struct device_attribute *dev_attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 val; + + val = psu_read_byte(client, PSU_REG_LED); + + return sprintf(buf, "%d\n", val); +} + +/* sysfs attributes */ +static SENSOR_DEVICE_ATTR(psu_v_in, S_IRUGO, for_vin, NULL, PSU_V_IN); +static SENSOR_DEVICE_ATTR(psu_v_out, S_IRUGO, for_vout_data, NULL, PSU_V_OUT); +static SENSOR_DEVICE_ATTR(psu_i_in, S_IRUGO, for_iin, NULL, PSU_I_IN); +static SENSOR_DEVICE_ATTR(psu_i_out, S_IRUGO, for_iout, NULL, PSU_I_OUT); +static SENSOR_DEVICE_ATTR(psu_p_in, S_IRUGO, for_pin, NULL, PSU_P_IN); +static SENSOR_DEVICE_ATTR(psu_p_out, S_IRUGO, for_pout, NULL, PSU_P_OUT); +static SENSOR_DEVICE_ATTR(psu_temp1_input, S_IRUGO, for_temp1, NULL, PSU_TEMP1_INPUT); +static SENSOR_DEVICE_ATTR(psu_temp2_input, S_IRUGO, for_temp2, NULL, PSU_TEMP2_INPUT); +static SENSOR_DEVICE_ATTR(psu_temp3_input, S_IRUGO, for_temp3, NULL, PSU_TEMP3_INPUT); +static SENSOR_DEVICE_ATTR(psu_fan1_fault, S_IRUGO, for_fan_fault, NULL, PSU_FAN1_FAULT); +static SENSOR_DEVICE_ATTR(psu_fan1_speed_rpm, S_IRUGO, for_fan_speed, NULL, PSU_FAN1_SPEED); +static SENSOR_DEVICE_ATTR(psu_mfr_model, S_IRUGO, for_model, NULL, PSU_MFR_MODEL); +static SENSOR_DEVICE_ATTR(psu_mfr_serial, S_IRUGO, for_serial, NULL, PSU_MFR_SERIAL); +static SENSOR_DEVICE_ATTR(psu_rst, S_IRUGO | S_IWUSR, show_psu_rst, set_psu_rst, 0); +static SENSOR_DEVICE_ATTR(psu_ioc, S_IRUGO, show_psu_ioc, NULL, 0); +static SENSOR_DEVICE_ATTR(psu_rev, S_IRUGO, show_psu_rev, NULL, 0); +static SENSOR_DEVICE_ATTR(psu_led, S_IRUGO, show_psu_led, NULL, 0); + +static struct attribute *psu_attributes[] = { + &sensor_dev_attr_psu_v_in.dev_attr.attr, + &sensor_dev_attr_psu_v_out.dev_attr.attr, + &sensor_dev_attr_psu_i_in.dev_attr.attr, + &sensor_dev_attr_psu_i_out.dev_attr.attr, + &sensor_dev_attr_psu_p_in.dev_attr.attr, + &sensor_dev_attr_psu_p_out.dev_attr.attr, + &sensor_dev_attr_psu_temp1_input.dev_attr.attr, + &sensor_dev_attr_psu_temp2_input.dev_attr.attr, + &sensor_dev_attr_psu_temp3_input.dev_attr.attr, + &sensor_dev_attr_psu_fan1_fault.dev_attr.attr, + &sensor_dev_attr_psu_fan1_speed_rpm.dev_attr.attr, + &sensor_dev_attr_psu_mfr_model.dev_attr.attr, + &sensor_dev_attr_psu_mfr_serial.dev_attr.attr, + &sensor_dev_attr_psu_rst.dev_attr.attr, + &sensor_dev_attr_psu_ioc.dev_attr.attr, + &sensor_dev_attr_psu_rev.dev_attr.attr, + &sensor_dev_attr_psu_led.dev_attr.attr, + NULL +}; + +static const struct attribute_group psu_group = { + .attrs = psu_attributes, +}; + +static int psu_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct psu_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA)) + { + dev_info(&client->dev, "i2c_check_functionality failed!!!\n"); + status = -EIO; + return status; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + { + status = -ENOMEM; + return status; + } + + i2c_set_clientdata(client, data); + data->valid = 0; + mutex_init(&data->update_lock); + + dev_info(&client->dev, "%s found\n", PSU_DRIVER_NAME); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &psu_group); + if (status) + { + dev_info(&client->dev, "sysfs_create_group failed!!!\n"); + kfree(data); + return status; + } + + return 0; +} + +static void psu_remove(struct i2c_client *client) +{ + struct psu_data *data = i2c_get_clientdata(client); + sysfs_remove_group(&client->dev.kobj, &psu_group); + kfree(data); + + return; +} + +static const struct i2c_device_id psu_id[] = { + { PSU_DRIVER_NAME, 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, psu_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver psu_driver = { + .driver = { + .name = PSU_DRIVER_NAME, + }, + .probe = psu_probe, + .remove = psu_remove, + .id_table = psu_id, + .address_list = normal_i2c, +}; + +static int __init pmbus_psu_init(void) +{ + return i2c_add_driver(&psu_driver); +} + +static void __exit pmbus_psu_exit(void) +{ + i2c_del_driver(&psu_driver); +} + +MODULE_AUTHOR("DNI SW5"); +MODULE_DESCRIPTION("DNI PSU Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.0.3"); + +module_init(pmbus_psu_init); +module_exit(pmbus_psu_exit); diff --git a/ixr7220h6-64/modules/port_cpld0.c b/ixr7220h6-64/modules/port_cpld0.c new file mode 100644 index 0000000..0ebfa8d --- /dev/null +++ b/ixr7220h6-64/modules/port_cpld0.c @@ -0,0 +1,1077 @@ +// * CPLD driver for Nokia-7220-IXR-H6-64 Router +// * +// * Copyright (C) 2025 Nokia 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 3 of the License, or +// * 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. +// * see + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "port_cpld0" + +// REGISTERS ADDRESS MAP +#define VER_MAJOR_REG 0x00 +#define VER_MINOR_REG 0x01 +#define SFP_CTRL_REG 0x03 +#define SCRATCH_REG 0x04 +#define SFP_MISC_REG 0x05 +#define SFP_TXFAULT_REG 0x06 +#define SFP_TXDIS_REG 0x07 +#define SFP_RXLOSS_REG 0x08 +#define SFP_MODPRS_REG 0x09 +#define SFP_EN_LP_REG 0x10 +#define OSFP_LPMODE_REG0 0x70 +#define OSFP_RST_REG0 0x78 +#define OSFP_MODPRS_REG0 0x88 +#define OSFP_PWGOOD_REG0 0x90 +#define OSFP_ENABLE_REG0 0x94 +#define OSFP_LOOPBK_REG0 0x98 + +// REG BIT FIELD POSITION or MASK +#define SFP0 0x0 +#define SFP1 0x1 + +static const unsigned short cpld_address_list[] = {0x64, I2C_CLIENT_END}; + +struct cpld_data { + struct i2c_client *client; + struct mutex update_lock; +}; + +static int cpld_i2c_read(struct cpld_data *data, u8 reg) +{ + int val = 0; + struct i2c_client *client = data->client; + + val = i2c_smbus_read_byte_data(client, reg); + if (val < 0) { + dev_warn(&client->dev, "CPLD READ ERROR: reg(0x%02x) err %d\n", reg, val); + } + + return val; +} + +static void cpld_i2c_write(struct cpld_data *data, u8 reg, u8 value) +{ + int res = 0; + struct i2c_client *client = data->client; + + mutex_lock(&data->update_lock); + res = i2c_smbus_write_byte_data(client, reg, value); + if (res < 0) { + dev_warn(&client->dev, "CPLD WRITE ERROR: reg(0x%02x) err %d\n", reg, res); + } + mutex_unlock(&data->update_lock); +} + +static void dump_reg(struct cpld_data *data) +{ + struct i2c_client *client = data->client; + u8 val0 = 0; + u8 val1 = 0; + u8 val2 = 0; + u8 val3 = 0; + + val0 = cpld_i2c_read(data, OSFP_RST_REG0); + val1 = cpld_i2c_read(data, OSFP_RST_REG0 + 1); + val2 = cpld_i2c_read(data, OSFP_RST_REG0 + 2); + val3 = cpld_i2c_read(data, OSFP_RST_REG0 + 3); + dev_info(&client->dev, "[PORT_CPLD0]OSFP_RESET_REG: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", val0, val1, val2, val3); + + val0 = cpld_i2c_read(data, OSFP_LPMODE_REG0); + val1 = cpld_i2c_read(data, OSFP_LPMODE_REG0 + 1); + val2 = cpld_i2c_read(data, OSFP_LPMODE_REG0 + 2); + val3 = cpld_i2c_read(data, OSFP_LPMODE_REG0 + 3); + dev_info(&client->dev, "[PORT_CPLD0]OSFP_LPMODE_REG: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", val0, val1, val2, val3); + + val0 = cpld_i2c_read(data, OSFP_MODPRS_REG0); + val1 = cpld_i2c_read(data, OSFP_MODPRS_REG0 + 1); + val2 = cpld_i2c_read(data, OSFP_MODPRS_REG0 + 2); + val3 = cpld_i2c_read(data, OSFP_MODPRS_REG0 + 3); + dev_info(&client->dev, "[PORT_CPLD0]OSFP_MODPRES_REG: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", val0, val1, val2, val3); + +} + +static ssize_t show_ver(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + u8 reg_major = 0; + u8 reg_minor = 0; + + reg_major = cpld_i2c_read(data, VER_MAJOR_REG); + reg_minor = cpld_i2c_read(data, VER_MINOR_REG); + + return sprintf(buf, "%d.%d\n", reg_major, reg_minor); +} + +static ssize_t show_scratch(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + u8 val = 0; + + val = cpld_i2c_read(data, SCRATCH_REG); + + return sprintf(buf, "0x%02x\n", val); +} + +static ssize_t set_scratch(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct cpld_data *data = dev_get_drvdata(dev); + u8 usr_val = 0; + + int ret = kstrtou8(buf, 16, &usr_val); + if (ret != 0) { + return ret; + } + if (usr_val > 0xFF) { + return -EINVAL; + } + + cpld_i2c_write(data, SCRATCH_REG, usr_val); + + return count; +} + +static ssize_t show_sfp_ctl(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, SFP_CTRL_REG); + + return sprintf(buf, "%d\n", (val>>sda->index) & 0x1 ? 1:0); +} + +static ssize_t set_sfp_ctl(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 reg_val = 0; + u8 usr_val = 0; + u8 mask; + + int ret = kstrtou8(buf, 10, &usr_val); + if (ret != 0) { + return ret; + } + if (usr_val > 1) { + return -EINVAL; + } + + mask = (~(1 << sda->index)) & 0xFF; + reg_val = cpld_i2c_read(data, SFP_CTRL_REG); + reg_val = reg_val & mask; + usr_val = usr_val << sda->index; + cpld_i2c_write(data, SFP_CTRL_REG, (reg_val | usr_val)); + + return count; +} + +static ssize_t show_sfp_misc(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, SFP_MISC_REG); + + return sprintf(buf, "%d\n", (val>>sda->index) & 0x1 ? 1:0); +} + +static ssize_t set_sfp_misc(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 reg_val = 0; + u8 usr_val = 0; + u8 mask; + + int ret = kstrtou8(buf, 10, &usr_val); + if (ret != 0) { + return ret; + } + if (usr_val > 1) { + return -EINVAL; + } + + mask = (~(1 << sda->index)) & 0xFF; + reg_val = cpld_i2c_read(data, SFP_MISC_REG); + reg_val = reg_val & mask; + usr_val = usr_val << sda->index; + cpld_i2c_write(data, SFP_MISC_REG, (reg_val | usr_val)); + + return count; +} + +static ssize_t show_sfp_tx_fault(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, SFP_TXFAULT_REG); + + return sprintf(buf, "%d\n", (val>>sda->index) & 0x1 ? 1:0); +} + +static ssize_t show_sfp_tx_en(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, SFP_TXDIS_REG); + + return sprintf(buf, "%d\n", (val>>sda->index) & 0x1 ? 1:0); +} + +static ssize_t set_sfp_tx_en(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 reg_val = 0; + u8 usr_val = 0; + u8 mask; + + int ret = kstrtou8(buf, 10, &usr_val); + if (ret != 0) { + return ret; + } + if (usr_val > 1) { + return -EINVAL; + } + + mask = (~(1 << sda->index)) & 0xFF; + reg_val = cpld_i2c_read(data, SFP_TXDIS_REG); + reg_val = reg_val & mask; + usr_val = usr_val << sda->index; + cpld_i2c_write(data, SFP_TXDIS_REG, (reg_val | usr_val)); + + return count; +} + +static ssize_t show_sfp_rx_los(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, SFP_RXLOSS_REG); + + return sprintf(buf, "%d\n", (val>>sda->index) & 0x1 ? 1:0); +} + +static ssize_t show_sfp_prs(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, SFP_MODPRS_REG); + + return sprintf(buf, "%d\n", (val>>sda->index) & 0x1 ? 1:0); +} + +static ssize_t show_sfp_en_lp(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, SFP_EN_LP_REG); + + return sprintf(buf, "%d\n", (val>>sda->index) & 0x1 ? 1:0); +} + +static ssize_t set_sfp_en_lp(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 reg_val = 0; + u8 usr_val = 0; + u8 mask; + + int ret = kstrtou8(buf, 10, &usr_val); + if (ret != 0) { + return ret; + } + if (usr_val > 1) { + return -EINVAL; + } + + mask = (~(1 << sda->index)) & 0xFF; + reg_val = cpld_i2c_read(data, SFP_EN_LP_REG); + reg_val = reg_val & mask; + usr_val = usr_val << sda->index; + cpld_i2c_write(data, SFP_EN_LP_REG, (reg_val | usr_val)); + + return count; +} + +static ssize_t show_osfp_lpmode(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, OSFP_LPMODE_REG0 + (sda->index / 8)); + + return sprintf(buf, "%d\n", (val>>(sda->index % 8)) & 0x1 ? 1:0); +} + +static ssize_t set_osfp_lpmode(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 reg_val = 0; + u8 usr_val = 0; + u8 mask; + + int ret = kstrtou8(buf, 10, &usr_val); + if (ret != 0) { + return ret; + } + if (usr_val > 1) { + return -EINVAL; + } + + mask = (~(1 << (sda->index % 8))) & 0xFF; + reg_val = cpld_i2c_read(data, OSFP_LPMODE_REG0 + (sda->index / 8)); + reg_val = reg_val & mask; + usr_val = usr_val << (sda->index % 8); + cpld_i2c_write(data, OSFP_LPMODE_REG0 + (sda->index / 8), (reg_val | usr_val)); + + return count; +} + +static ssize_t show_osfp_rst(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, OSFP_RST_REG0 + (sda->index / 8)); + + return sprintf(buf, "%d\n", (val>>(sda->index % 8)) & 0x1 ? 1:0); +} + +static ssize_t set_osfp_rst(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 reg_val = 0; + u8 usr_val = 0; + u8 mask; + + int ret = kstrtou8(buf, 10, &usr_val); + if (ret != 0) { + return ret; + } + if (usr_val > 1) { + return -EINVAL; + } + + mask = (~(1 << (sda->index % 8))) & 0xFF; + reg_val = cpld_i2c_read(data, OSFP_RST_REG0 + (sda->index / 8)); + reg_val = reg_val & mask; + usr_val = usr_val << (sda->index % 8); + cpld_i2c_write(data, OSFP_RST_REG0 + (sda->index / 8), (reg_val | usr_val)); + + return count; +} + +static ssize_t show_osfp_prs(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, OSFP_MODPRS_REG0 + (sda->index / 8)); + + return sprintf(buf, "%d\n", (val>>(sda->index % 8)) & 0x1 ? 1:0); +} + +static ssize_t show_modprs_reg(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, OSFP_MODPRS_REG0 + sda->index); + + return sprintf(buf, "0x%02x\n", val); +} + +static ssize_t show_osfp_pwgood(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, OSFP_PWGOOD_REG0 + (sda->index / 8)); + + return sprintf(buf, "%d\n", (val>>(sda->index % 8)) & 0x1 ? 1:0); +} + +static ssize_t show_osfp_en(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, OSFP_ENABLE_REG0 + (sda->index / 8)); + + return sprintf(buf, "%d\n", (val>>(sda->index % 8)) & 0x1 ? 1:0); +} + +static ssize_t set_osfp_en(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 reg_val = 0; + u8 usr_val = 0; + u8 mask; + + int ret = kstrtou8(buf, 10, &usr_val); + if (ret != 0) { + return ret; + } + if (usr_val > 1) { + return -EINVAL; + } + + mask = (~(1 << (sda->index % 8))) & 0xFF; + reg_val = cpld_i2c_read(data, OSFP_ENABLE_REG0 + (sda->index / 8)); + reg_val = reg_val & mask; + usr_val = usr_val << (sda->index % 8); + cpld_i2c_write(data, OSFP_ENABLE_REG0 + (sda->index / 8), (reg_val | usr_val)); + + return count; +} + +static ssize_t show_osfp_loopb(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, OSFP_LOOPBK_REG0 + (sda->index / 8)); + + return sprintf(buf, "%d\n", (val>>(sda->index % 8)) & 0x1 ? 1:0); +} + +static ssize_t set_osfp_loopb(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 reg_val = 0; + u8 usr_val = 0; + u8 mask; + + int ret = kstrtou8(buf, 10, &usr_val); + if (ret != 0) { + return ret; + } + if (usr_val > 1) { + return -EINVAL; + } + + mask = (~(1 << (sda->index % 8))) & 0xFF; + reg_val = cpld_i2c_read(data, OSFP_LOOPBK_REG0 + (sda->index / 8)); + reg_val = reg_val & mask; + usr_val = usr_val << (sda->index % 8); + cpld_i2c_write(data, OSFP_LOOPBK_REG0 + (sda->index / 8), (reg_val | usr_val)); + + return count; +} + +// sysfs attributes +static SENSOR_DEVICE_ATTR(version, S_IRUGO, show_ver, NULL, 0); +static SENSOR_DEVICE_ATTR(scratch, S_IRUGO | S_IWUSR, show_scratch, set_scratch, 0); + +static SENSOR_DEVICE_ATTR(port_65_rx_rate, S_IRUGO | S_IWUSR, show_sfp_ctl, set_sfp_ctl, 0); +static SENSOR_DEVICE_ATTR(port_65_tx_rate, S_IRUGO | S_IWUSR, show_sfp_ctl, set_sfp_ctl, 1); +static SENSOR_DEVICE_ATTR(port_66_rx_rate, S_IRUGO | S_IWUSR, show_sfp_ctl, set_sfp_ctl, 2); +static SENSOR_DEVICE_ATTR(port_66_tx_rate, S_IRUGO | S_IWUSR, show_sfp_ctl, set_sfp_ctl, 3); +static SENSOR_DEVICE_ATTR(port_65_efuse_en, S_IRUGO | S_IWUSR, show_sfp_misc, set_sfp_misc, 0); +static SENSOR_DEVICE_ATTR(port_66_efuse_en, S_IRUGO | S_IWUSR, show_sfp_misc, set_sfp_misc, 1); +static SENSOR_DEVICE_ATTR(port_65_efuse_flag, S_IRUGO | S_IWUSR, show_sfp_misc, set_sfp_misc, 2); +static SENSOR_DEVICE_ATTR(port_66_efuse_flag, S_IRUGO | S_IWUSR, show_sfp_misc, set_sfp_misc, 3); +static SENSOR_DEVICE_ATTR(port_65_tx_fault, S_IRUGO, show_sfp_tx_fault, NULL, SFP0); +static SENSOR_DEVICE_ATTR(port_66_tx_fault, S_IRUGO, show_sfp_tx_fault, NULL, SFP1); +static SENSOR_DEVICE_ATTR(port_65_tx_en, S_IRUGO | S_IWUSR, show_sfp_tx_en, set_sfp_tx_en, SFP0); +static SENSOR_DEVICE_ATTR(port_66_tx_en, S_IRUGO | S_IWUSR, show_sfp_tx_en, set_sfp_tx_en, SFP1); +static SENSOR_DEVICE_ATTR(port_65_rx_los, S_IRUGO, show_sfp_rx_los, NULL, SFP0); +static SENSOR_DEVICE_ATTR(port_66_rx_los, S_IRUGO, show_sfp_rx_los, NULL, SFP1); +static SENSOR_DEVICE_ATTR(port_65_prs, S_IRUGO, show_sfp_prs, NULL, SFP0); +static SENSOR_DEVICE_ATTR(port_66_prs, S_IRUGO, show_sfp_prs, NULL, SFP1); +static SENSOR_DEVICE_ATTR(port_65_en, S_IRUGO | S_IWUSR, show_sfp_en_lp, set_sfp_en_lp, 0); +static SENSOR_DEVICE_ATTR(port_66_en, S_IRUGO | S_IWUSR, show_sfp_en_lp, set_sfp_en_lp, 1); +static SENSOR_DEVICE_ATTR(port_65_loopb, S_IRUGO | S_IWUSR, show_sfp_en_lp, set_sfp_en_lp, 2); +static SENSOR_DEVICE_ATTR(port_66_loopb, S_IRUGO | S_IWUSR, show_sfp_en_lp, set_sfp_en_lp, 3); + +static SENSOR_DEVICE_ATTR(port_1_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 0); +static SENSOR_DEVICE_ATTR(port_2_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 1); +static SENSOR_DEVICE_ATTR(port_3_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 2); +static SENSOR_DEVICE_ATTR(port_4_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 3); +static SENSOR_DEVICE_ATTR(port_5_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 4); +static SENSOR_DEVICE_ATTR(port_6_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 5); +static SENSOR_DEVICE_ATTR(port_7_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 6); +static SENSOR_DEVICE_ATTR(port_8_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 7); +static SENSOR_DEVICE_ATTR(port_9_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 8); +static SENSOR_DEVICE_ATTR(port_10_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 9); +static SENSOR_DEVICE_ATTR(port_11_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 10); +static SENSOR_DEVICE_ATTR(port_12_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 11); +static SENSOR_DEVICE_ATTR(port_13_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 12); +static SENSOR_DEVICE_ATTR(port_14_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 13); +static SENSOR_DEVICE_ATTR(port_15_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 14); +static SENSOR_DEVICE_ATTR(port_16_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 15); +static SENSOR_DEVICE_ATTR(port_33_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 16); +static SENSOR_DEVICE_ATTR(port_34_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 17); +static SENSOR_DEVICE_ATTR(port_35_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 18); +static SENSOR_DEVICE_ATTR(port_36_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 19); +static SENSOR_DEVICE_ATTR(port_37_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 20); +static SENSOR_DEVICE_ATTR(port_38_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 21); +static SENSOR_DEVICE_ATTR(port_39_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 22); +static SENSOR_DEVICE_ATTR(port_40_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 23); +static SENSOR_DEVICE_ATTR(port_41_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 24); +static SENSOR_DEVICE_ATTR(port_42_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 25); +static SENSOR_DEVICE_ATTR(port_43_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 26); +static SENSOR_DEVICE_ATTR(port_44_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 27); +static SENSOR_DEVICE_ATTR(port_45_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 28); +static SENSOR_DEVICE_ATTR(port_46_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 29); +static SENSOR_DEVICE_ATTR(port_47_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 30); +static SENSOR_DEVICE_ATTR(port_48_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 31); + +static SENSOR_DEVICE_ATTR(port_1_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 0); +static SENSOR_DEVICE_ATTR(port_2_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 1); +static SENSOR_DEVICE_ATTR(port_3_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 2); +static SENSOR_DEVICE_ATTR(port_4_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 3); +static SENSOR_DEVICE_ATTR(port_5_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 4); +static SENSOR_DEVICE_ATTR(port_6_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 5); +static SENSOR_DEVICE_ATTR(port_7_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 6); +static SENSOR_DEVICE_ATTR(port_8_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 7); +static SENSOR_DEVICE_ATTR(port_9_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 8); +static SENSOR_DEVICE_ATTR(port_10_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 9); +static SENSOR_DEVICE_ATTR(port_11_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 10); +static SENSOR_DEVICE_ATTR(port_12_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 11); +static SENSOR_DEVICE_ATTR(port_13_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 12); +static SENSOR_DEVICE_ATTR(port_14_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 13); +static SENSOR_DEVICE_ATTR(port_15_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 14); +static SENSOR_DEVICE_ATTR(port_16_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 15); +static SENSOR_DEVICE_ATTR(port_33_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 16); +static SENSOR_DEVICE_ATTR(port_34_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 17); +static SENSOR_DEVICE_ATTR(port_35_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 18); +static SENSOR_DEVICE_ATTR(port_36_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 19); +static SENSOR_DEVICE_ATTR(port_37_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 20); +static SENSOR_DEVICE_ATTR(port_38_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 21); +static SENSOR_DEVICE_ATTR(port_39_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 22); +static SENSOR_DEVICE_ATTR(port_40_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 23); +static SENSOR_DEVICE_ATTR(port_41_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 24); +static SENSOR_DEVICE_ATTR(port_42_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 25); +static SENSOR_DEVICE_ATTR(port_43_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 26); +static SENSOR_DEVICE_ATTR(port_44_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 27); +static SENSOR_DEVICE_ATTR(port_45_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 28); +static SENSOR_DEVICE_ATTR(port_46_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 29); +static SENSOR_DEVICE_ATTR(port_47_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 30); +static SENSOR_DEVICE_ATTR(port_48_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 31); + +static SENSOR_DEVICE_ATTR(port_1_prs, S_IRUGO, show_osfp_prs, NULL, 0); +static SENSOR_DEVICE_ATTR(port_2_prs, S_IRUGO, show_osfp_prs, NULL, 1); +static SENSOR_DEVICE_ATTR(port_3_prs, S_IRUGO, show_osfp_prs, NULL, 2); +static SENSOR_DEVICE_ATTR(port_4_prs, S_IRUGO, show_osfp_prs, NULL, 3); +static SENSOR_DEVICE_ATTR(port_5_prs, S_IRUGO, show_osfp_prs, NULL, 4); +static SENSOR_DEVICE_ATTR(port_6_prs, S_IRUGO, show_osfp_prs, NULL, 5); +static SENSOR_DEVICE_ATTR(port_7_prs, S_IRUGO, show_osfp_prs, NULL, 6); +static SENSOR_DEVICE_ATTR(port_8_prs, S_IRUGO, show_osfp_prs, NULL, 7); +static SENSOR_DEVICE_ATTR(port_9_prs, S_IRUGO, show_osfp_prs, NULL, 8); +static SENSOR_DEVICE_ATTR(port_10_prs, S_IRUGO, show_osfp_prs, NULL, 9); +static SENSOR_DEVICE_ATTR(port_11_prs, S_IRUGO, show_osfp_prs, NULL, 10); +static SENSOR_DEVICE_ATTR(port_12_prs, S_IRUGO, show_osfp_prs, NULL, 11); +static SENSOR_DEVICE_ATTR(port_13_prs, S_IRUGO, show_osfp_prs, NULL, 12); +static SENSOR_DEVICE_ATTR(port_14_prs, S_IRUGO, show_osfp_prs, NULL, 13); +static SENSOR_DEVICE_ATTR(port_15_prs, S_IRUGO, show_osfp_prs, NULL, 14); +static SENSOR_DEVICE_ATTR(port_16_prs, S_IRUGO, show_osfp_prs, NULL, 15); +static SENSOR_DEVICE_ATTR(port_33_prs, S_IRUGO, show_osfp_prs, NULL, 16); +static SENSOR_DEVICE_ATTR(port_34_prs, S_IRUGO, show_osfp_prs, NULL, 17); +static SENSOR_DEVICE_ATTR(port_35_prs, S_IRUGO, show_osfp_prs, NULL, 18); +static SENSOR_DEVICE_ATTR(port_36_prs, S_IRUGO, show_osfp_prs, NULL, 19); +static SENSOR_DEVICE_ATTR(port_37_prs, S_IRUGO, show_osfp_prs, NULL, 20); +static SENSOR_DEVICE_ATTR(port_38_prs, S_IRUGO, show_osfp_prs, NULL, 21); +static SENSOR_DEVICE_ATTR(port_39_prs, S_IRUGO, show_osfp_prs, NULL, 22); +static SENSOR_DEVICE_ATTR(port_40_prs, S_IRUGO, show_osfp_prs, NULL, 23); +static SENSOR_DEVICE_ATTR(port_41_prs, S_IRUGO, show_osfp_prs, NULL, 24); +static SENSOR_DEVICE_ATTR(port_42_prs, S_IRUGO, show_osfp_prs, NULL, 25); +static SENSOR_DEVICE_ATTR(port_43_prs, S_IRUGO, show_osfp_prs, NULL, 26); +static SENSOR_DEVICE_ATTR(port_44_prs, S_IRUGO, show_osfp_prs, NULL, 27); +static SENSOR_DEVICE_ATTR(port_45_prs, S_IRUGO, show_osfp_prs, NULL, 28); +static SENSOR_DEVICE_ATTR(port_46_prs, S_IRUGO, show_osfp_prs, NULL, 29); +static SENSOR_DEVICE_ATTR(port_47_prs, S_IRUGO, show_osfp_prs, NULL, 30); +static SENSOR_DEVICE_ATTR(port_48_prs, S_IRUGO, show_osfp_prs, NULL, 31); + +static SENSOR_DEVICE_ATTR(modprs_reg1, S_IRUGO, show_modprs_reg, NULL, 0); +static SENSOR_DEVICE_ATTR(modprs_reg2, S_IRUGO, show_modprs_reg, NULL, 1); +static SENSOR_DEVICE_ATTR(modprs_reg3, S_IRUGO, show_modprs_reg, NULL, 2); +static SENSOR_DEVICE_ATTR(modprs_reg4, S_IRUGO, show_modprs_reg, NULL, 3); + +static SENSOR_DEVICE_ATTR(port_1_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 0); +static SENSOR_DEVICE_ATTR(port_2_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 1); +static SENSOR_DEVICE_ATTR(port_3_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 2); +static SENSOR_DEVICE_ATTR(port_4_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 3); +static SENSOR_DEVICE_ATTR(port_5_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 4); +static SENSOR_DEVICE_ATTR(port_6_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 5); +static SENSOR_DEVICE_ATTR(port_7_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 6); +static SENSOR_DEVICE_ATTR(port_8_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 7); +static SENSOR_DEVICE_ATTR(port_9_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 8); +static SENSOR_DEVICE_ATTR(port_10_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 9); +static SENSOR_DEVICE_ATTR(port_11_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 10); +static SENSOR_DEVICE_ATTR(port_12_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 11); +static SENSOR_DEVICE_ATTR(port_13_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 12); +static SENSOR_DEVICE_ATTR(port_14_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 13); +static SENSOR_DEVICE_ATTR(port_15_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 14); +static SENSOR_DEVICE_ATTR(port_16_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 15); +static SENSOR_DEVICE_ATTR(port_33_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 16); +static SENSOR_DEVICE_ATTR(port_34_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 17); +static SENSOR_DEVICE_ATTR(port_35_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 18); +static SENSOR_DEVICE_ATTR(port_36_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 19); +static SENSOR_DEVICE_ATTR(port_37_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 20); +static SENSOR_DEVICE_ATTR(port_38_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 21); +static SENSOR_DEVICE_ATTR(port_39_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 22); +static SENSOR_DEVICE_ATTR(port_40_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 23); +static SENSOR_DEVICE_ATTR(port_41_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 24); +static SENSOR_DEVICE_ATTR(port_42_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 25); +static SENSOR_DEVICE_ATTR(port_43_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 26); +static SENSOR_DEVICE_ATTR(port_44_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 27); +static SENSOR_DEVICE_ATTR(port_45_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 28); +static SENSOR_DEVICE_ATTR(port_46_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 29); +static SENSOR_DEVICE_ATTR(port_47_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 30); +static SENSOR_DEVICE_ATTR(port_48_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 31); + +static SENSOR_DEVICE_ATTR(port_1_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 0); +static SENSOR_DEVICE_ATTR(port_2_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 1); +static SENSOR_DEVICE_ATTR(port_3_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 2); +static SENSOR_DEVICE_ATTR(port_4_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 3); +static SENSOR_DEVICE_ATTR(port_5_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 4); +static SENSOR_DEVICE_ATTR(port_6_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 5); +static SENSOR_DEVICE_ATTR(port_7_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 6); +static SENSOR_DEVICE_ATTR(port_8_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 7); +static SENSOR_DEVICE_ATTR(port_9_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 8); +static SENSOR_DEVICE_ATTR(port_10_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 9); +static SENSOR_DEVICE_ATTR(port_11_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 10); +static SENSOR_DEVICE_ATTR(port_12_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 11); +static SENSOR_DEVICE_ATTR(port_13_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 12); +static SENSOR_DEVICE_ATTR(port_14_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 13); +static SENSOR_DEVICE_ATTR(port_15_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 14); +static SENSOR_DEVICE_ATTR(port_16_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 15); +static SENSOR_DEVICE_ATTR(port_33_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 16); +static SENSOR_DEVICE_ATTR(port_34_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 17); +static SENSOR_DEVICE_ATTR(port_35_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 18); +static SENSOR_DEVICE_ATTR(port_36_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 19); +static SENSOR_DEVICE_ATTR(port_37_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 20); +static SENSOR_DEVICE_ATTR(port_38_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 21); +static SENSOR_DEVICE_ATTR(port_39_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 22); +static SENSOR_DEVICE_ATTR(port_40_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 23); +static SENSOR_DEVICE_ATTR(port_41_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 24); +static SENSOR_DEVICE_ATTR(port_42_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 25); +static SENSOR_DEVICE_ATTR(port_43_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 26); +static SENSOR_DEVICE_ATTR(port_44_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 27); +static SENSOR_DEVICE_ATTR(port_45_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 28); +static SENSOR_DEVICE_ATTR(port_46_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 29); +static SENSOR_DEVICE_ATTR(port_47_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 30); +static SENSOR_DEVICE_ATTR(port_48_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 31); + +static SENSOR_DEVICE_ATTR(port_1_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 0); +static SENSOR_DEVICE_ATTR(port_2_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 1); +static SENSOR_DEVICE_ATTR(port_3_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 2); +static SENSOR_DEVICE_ATTR(port_4_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 3); +static SENSOR_DEVICE_ATTR(port_5_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 4); +static SENSOR_DEVICE_ATTR(port_6_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 5); +static SENSOR_DEVICE_ATTR(port_7_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 6); +static SENSOR_DEVICE_ATTR(port_8_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 7); +static SENSOR_DEVICE_ATTR(port_9_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 8); +static SENSOR_DEVICE_ATTR(port_10_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 9); +static SENSOR_DEVICE_ATTR(port_11_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 10); +static SENSOR_DEVICE_ATTR(port_12_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 11); +static SENSOR_DEVICE_ATTR(port_13_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 12); +static SENSOR_DEVICE_ATTR(port_14_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 13); +static SENSOR_DEVICE_ATTR(port_15_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 14); +static SENSOR_DEVICE_ATTR(port_16_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 15); +static SENSOR_DEVICE_ATTR(port_33_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 16); +static SENSOR_DEVICE_ATTR(port_34_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 17); +static SENSOR_DEVICE_ATTR(port_35_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 18); +static SENSOR_DEVICE_ATTR(port_36_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 19); +static SENSOR_DEVICE_ATTR(port_37_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 20); +static SENSOR_DEVICE_ATTR(port_38_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 21); +static SENSOR_DEVICE_ATTR(port_39_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 22); +static SENSOR_DEVICE_ATTR(port_40_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 23); +static SENSOR_DEVICE_ATTR(port_41_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 24); +static SENSOR_DEVICE_ATTR(port_42_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 25); +static SENSOR_DEVICE_ATTR(port_43_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 26); +static SENSOR_DEVICE_ATTR(port_44_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 27); +static SENSOR_DEVICE_ATTR(port_45_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 28); +static SENSOR_DEVICE_ATTR(port_46_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 29); +static SENSOR_DEVICE_ATTR(port_47_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 30); +static SENSOR_DEVICE_ATTR(port_48_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 31); + +static struct attribute *port_cpld0_attributes[] = { + &sensor_dev_attr_version.dev_attr.attr, + &sensor_dev_attr_scratch.dev_attr.attr, + + &sensor_dev_attr_port_65_rx_rate.dev_attr.attr, + &sensor_dev_attr_port_65_tx_rate.dev_attr.attr, + &sensor_dev_attr_port_66_rx_rate.dev_attr.attr, + &sensor_dev_attr_port_66_tx_rate.dev_attr.attr, + &sensor_dev_attr_port_66_efuse_en.dev_attr.attr, + &sensor_dev_attr_port_65_efuse_en.dev_attr.attr, + &sensor_dev_attr_port_65_efuse_flag.dev_attr.attr, + &sensor_dev_attr_port_66_efuse_flag.dev_attr.attr, + &sensor_dev_attr_port_65_tx_fault.dev_attr.attr, + &sensor_dev_attr_port_66_tx_fault.dev_attr.attr, + &sensor_dev_attr_port_65_tx_en.dev_attr.attr, + &sensor_dev_attr_port_66_tx_en.dev_attr.attr, + &sensor_dev_attr_port_65_rx_los.dev_attr.attr, + &sensor_dev_attr_port_66_rx_los.dev_attr.attr, + &sensor_dev_attr_port_65_prs.dev_attr.attr, + &sensor_dev_attr_port_66_prs.dev_attr.attr, + &sensor_dev_attr_port_65_en.dev_attr.attr, + &sensor_dev_attr_port_66_en.dev_attr.attr, + &sensor_dev_attr_port_65_loopb.dev_attr.attr, + &sensor_dev_attr_port_66_loopb.dev_attr.attr, + + &sensor_dev_attr_port_1_lpmod.dev_attr.attr, + &sensor_dev_attr_port_2_lpmod.dev_attr.attr, + &sensor_dev_attr_port_3_lpmod.dev_attr.attr, + &sensor_dev_attr_port_4_lpmod.dev_attr.attr, + &sensor_dev_attr_port_5_lpmod.dev_attr.attr, + &sensor_dev_attr_port_6_lpmod.dev_attr.attr, + &sensor_dev_attr_port_7_lpmod.dev_attr.attr, + &sensor_dev_attr_port_8_lpmod.dev_attr.attr, + &sensor_dev_attr_port_9_lpmod.dev_attr.attr, + &sensor_dev_attr_port_10_lpmod.dev_attr.attr, + &sensor_dev_attr_port_11_lpmod.dev_attr.attr, + &sensor_dev_attr_port_12_lpmod.dev_attr.attr, + &sensor_dev_attr_port_13_lpmod.dev_attr.attr, + &sensor_dev_attr_port_14_lpmod.dev_attr.attr, + &sensor_dev_attr_port_15_lpmod.dev_attr.attr, + &sensor_dev_attr_port_16_lpmod.dev_attr.attr, + &sensor_dev_attr_port_33_lpmod.dev_attr.attr, + &sensor_dev_attr_port_34_lpmod.dev_attr.attr, + &sensor_dev_attr_port_35_lpmod.dev_attr.attr, + &sensor_dev_attr_port_36_lpmod.dev_attr.attr, + &sensor_dev_attr_port_37_lpmod.dev_attr.attr, + &sensor_dev_attr_port_38_lpmod.dev_attr.attr, + &sensor_dev_attr_port_39_lpmod.dev_attr.attr, + &sensor_dev_attr_port_40_lpmod.dev_attr.attr, + &sensor_dev_attr_port_41_lpmod.dev_attr.attr, + &sensor_dev_attr_port_42_lpmod.dev_attr.attr, + &sensor_dev_attr_port_43_lpmod.dev_attr.attr, + &sensor_dev_attr_port_44_lpmod.dev_attr.attr, + &sensor_dev_attr_port_45_lpmod.dev_attr.attr, + &sensor_dev_attr_port_46_lpmod.dev_attr.attr, + &sensor_dev_attr_port_47_lpmod.dev_attr.attr, + &sensor_dev_attr_port_48_lpmod.dev_attr.attr, + + &sensor_dev_attr_port_1_rst.dev_attr.attr, + &sensor_dev_attr_port_2_rst.dev_attr.attr, + &sensor_dev_attr_port_3_rst.dev_attr.attr, + &sensor_dev_attr_port_4_rst.dev_attr.attr, + &sensor_dev_attr_port_5_rst.dev_attr.attr, + &sensor_dev_attr_port_6_rst.dev_attr.attr, + &sensor_dev_attr_port_7_rst.dev_attr.attr, + &sensor_dev_attr_port_8_rst.dev_attr.attr, + &sensor_dev_attr_port_9_rst.dev_attr.attr, + &sensor_dev_attr_port_10_rst.dev_attr.attr, + &sensor_dev_attr_port_11_rst.dev_attr.attr, + &sensor_dev_attr_port_12_rst.dev_attr.attr, + &sensor_dev_attr_port_13_rst.dev_attr.attr, + &sensor_dev_attr_port_14_rst.dev_attr.attr, + &sensor_dev_attr_port_15_rst.dev_attr.attr, + &sensor_dev_attr_port_16_rst.dev_attr.attr, + &sensor_dev_attr_port_33_rst.dev_attr.attr, + &sensor_dev_attr_port_34_rst.dev_attr.attr, + &sensor_dev_attr_port_35_rst.dev_attr.attr, + &sensor_dev_attr_port_36_rst.dev_attr.attr, + &sensor_dev_attr_port_37_rst.dev_attr.attr, + &sensor_dev_attr_port_38_rst.dev_attr.attr, + &sensor_dev_attr_port_39_rst.dev_attr.attr, + &sensor_dev_attr_port_40_rst.dev_attr.attr, + &sensor_dev_attr_port_41_rst.dev_attr.attr, + &sensor_dev_attr_port_42_rst.dev_attr.attr, + &sensor_dev_attr_port_43_rst.dev_attr.attr, + &sensor_dev_attr_port_44_rst.dev_attr.attr, + &sensor_dev_attr_port_45_rst.dev_attr.attr, + &sensor_dev_attr_port_46_rst.dev_attr.attr, + &sensor_dev_attr_port_47_rst.dev_attr.attr, + &sensor_dev_attr_port_48_rst.dev_attr.attr, + + &sensor_dev_attr_port_1_prs.dev_attr.attr, + &sensor_dev_attr_port_2_prs.dev_attr.attr, + &sensor_dev_attr_port_3_prs.dev_attr.attr, + &sensor_dev_attr_port_4_prs.dev_attr.attr, + &sensor_dev_attr_port_5_prs.dev_attr.attr, + &sensor_dev_attr_port_6_prs.dev_attr.attr, + &sensor_dev_attr_port_7_prs.dev_attr.attr, + &sensor_dev_attr_port_8_prs.dev_attr.attr, + &sensor_dev_attr_port_9_prs.dev_attr.attr, + &sensor_dev_attr_port_10_prs.dev_attr.attr, + &sensor_dev_attr_port_11_prs.dev_attr.attr, + &sensor_dev_attr_port_12_prs.dev_attr.attr, + &sensor_dev_attr_port_13_prs.dev_attr.attr, + &sensor_dev_attr_port_14_prs.dev_attr.attr, + &sensor_dev_attr_port_15_prs.dev_attr.attr, + &sensor_dev_attr_port_16_prs.dev_attr.attr, + &sensor_dev_attr_port_33_prs.dev_attr.attr, + &sensor_dev_attr_port_34_prs.dev_attr.attr, + &sensor_dev_attr_port_35_prs.dev_attr.attr, + &sensor_dev_attr_port_36_prs.dev_attr.attr, + &sensor_dev_attr_port_37_prs.dev_attr.attr, + &sensor_dev_attr_port_38_prs.dev_attr.attr, + &sensor_dev_attr_port_39_prs.dev_attr.attr, + &sensor_dev_attr_port_40_prs.dev_attr.attr, + &sensor_dev_attr_port_41_prs.dev_attr.attr, + &sensor_dev_attr_port_42_prs.dev_attr.attr, + &sensor_dev_attr_port_43_prs.dev_attr.attr, + &sensor_dev_attr_port_44_prs.dev_attr.attr, + &sensor_dev_attr_port_45_prs.dev_attr.attr, + &sensor_dev_attr_port_46_prs.dev_attr.attr, + &sensor_dev_attr_port_47_prs.dev_attr.attr, + &sensor_dev_attr_port_48_prs.dev_attr.attr, + + &sensor_dev_attr_modprs_reg1.dev_attr.attr, + &sensor_dev_attr_modprs_reg2.dev_attr.attr, + &sensor_dev_attr_modprs_reg3.dev_attr.attr, + &sensor_dev_attr_modprs_reg4.dev_attr.attr, + + &sensor_dev_attr_port_1_pwgood.dev_attr.attr, + &sensor_dev_attr_port_2_pwgood.dev_attr.attr, + &sensor_dev_attr_port_3_pwgood.dev_attr.attr, + &sensor_dev_attr_port_4_pwgood.dev_attr.attr, + &sensor_dev_attr_port_5_pwgood.dev_attr.attr, + &sensor_dev_attr_port_6_pwgood.dev_attr.attr, + &sensor_dev_attr_port_7_pwgood.dev_attr.attr, + &sensor_dev_attr_port_8_pwgood.dev_attr.attr, + &sensor_dev_attr_port_9_pwgood.dev_attr.attr, + &sensor_dev_attr_port_10_pwgood.dev_attr.attr, + &sensor_dev_attr_port_11_pwgood.dev_attr.attr, + &sensor_dev_attr_port_12_pwgood.dev_attr.attr, + &sensor_dev_attr_port_13_pwgood.dev_attr.attr, + &sensor_dev_attr_port_14_pwgood.dev_attr.attr, + &sensor_dev_attr_port_15_pwgood.dev_attr.attr, + &sensor_dev_attr_port_16_pwgood.dev_attr.attr, + &sensor_dev_attr_port_33_pwgood.dev_attr.attr, + &sensor_dev_attr_port_34_pwgood.dev_attr.attr, + &sensor_dev_attr_port_35_pwgood.dev_attr.attr, + &sensor_dev_attr_port_36_pwgood.dev_attr.attr, + &sensor_dev_attr_port_37_pwgood.dev_attr.attr, + &sensor_dev_attr_port_38_pwgood.dev_attr.attr, + &sensor_dev_attr_port_39_pwgood.dev_attr.attr, + &sensor_dev_attr_port_40_pwgood.dev_attr.attr, + &sensor_dev_attr_port_41_pwgood.dev_attr.attr, + &sensor_dev_attr_port_42_pwgood.dev_attr.attr, + &sensor_dev_attr_port_43_pwgood.dev_attr.attr, + &sensor_dev_attr_port_44_pwgood.dev_attr.attr, + &sensor_dev_attr_port_45_pwgood.dev_attr.attr, + &sensor_dev_attr_port_46_pwgood.dev_attr.attr, + &sensor_dev_attr_port_47_pwgood.dev_attr.attr, + &sensor_dev_attr_port_48_pwgood.dev_attr.attr, + + &sensor_dev_attr_port_1_en.dev_attr.attr, + &sensor_dev_attr_port_2_en.dev_attr.attr, + &sensor_dev_attr_port_3_en.dev_attr.attr, + &sensor_dev_attr_port_4_en.dev_attr.attr, + &sensor_dev_attr_port_5_en.dev_attr.attr, + &sensor_dev_attr_port_6_en.dev_attr.attr, + &sensor_dev_attr_port_7_en.dev_attr.attr, + &sensor_dev_attr_port_8_en.dev_attr.attr, + &sensor_dev_attr_port_9_en.dev_attr.attr, + &sensor_dev_attr_port_10_en.dev_attr.attr, + &sensor_dev_attr_port_11_en.dev_attr.attr, + &sensor_dev_attr_port_12_en.dev_attr.attr, + &sensor_dev_attr_port_13_en.dev_attr.attr, + &sensor_dev_attr_port_14_en.dev_attr.attr, + &sensor_dev_attr_port_15_en.dev_attr.attr, + &sensor_dev_attr_port_16_en.dev_attr.attr, + &sensor_dev_attr_port_33_en.dev_attr.attr, + &sensor_dev_attr_port_34_en.dev_attr.attr, + &sensor_dev_attr_port_35_en.dev_attr.attr, + &sensor_dev_attr_port_36_en.dev_attr.attr, + &sensor_dev_attr_port_37_en.dev_attr.attr, + &sensor_dev_attr_port_38_en.dev_attr.attr, + &sensor_dev_attr_port_39_en.dev_attr.attr, + &sensor_dev_attr_port_40_en.dev_attr.attr, + &sensor_dev_attr_port_41_en.dev_attr.attr, + &sensor_dev_attr_port_42_en.dev_attr.attr, + &sensor_dev_attr_port_43_en.dev_attr.attr, + &sensor_dev_attr_port_44_en.dev_attr.attr, + &sensor_dev_attr_port_45_en.dev_attr.attr, + &sensor_dev_attr_port_46_en.dev_attr.attr, + &sensor_dev_attr_port_47_en.dev_attr.attr, + &sensor_dev_attr_port_48_en.dev_attr.attr, + + &sensor_dev_attr_port_1_loopb.dev_attr.attr, + &sensor_dev_attr_port_2_loopb.dev_attr.attr, + &sensor_dev_attr_port_3_loopb.dev_attr.attr, + &sensor_dev_attr_port_4_loopb.dev_attr.attr, + &sensor_dev_attr_port_5_loopb.dev_attr.attr, + &sensor_dev_attr_port_6_loopb.dev_attr.attr, + &sensor_dev_attr_port_7_loopb.dev_attr.attr, + &sensor_dev_attr_port_8_loopb.dev_attr.attr, + &sensor_dev_attr_port_9_loopb.dev_attr.attr, + &sensor_dev_attr_port_10_loopb.dev_attr.attr, + &sensor_dev_attr_port_11_loopb.dev_attr.attr, + &sensor_dev_attr_port_12_loopb.dev_attr.attr, + &sensor_dev_attr_port_13_loopb.dev_attr.attr, + &sensor_dev_attr_port_14_loopb.dev_attr.attr, + &sensor_dev_attr_port_15_loopb.dev_attr.attr, + &sensor_dev_attr_port_16_loopb.dev_attr.attr, + &sensor_dev_attr_port_33_loopb.dev_attr.attr, + &sensor_dev_attr_port_34_loopb.dev_attr.attr, + &sensor_dev_attr_port_35_loopb.dev_attr.attr, + &sensor_dev_attr_port_36_loopb.dev_attr.attr, + &sensor_dev_attr_port_37_loopb.dev_attr.attr, + &sensor_dev_attr_port_38_loopb.dev_attr.attr, + &sensor_dev_attr_port_39_loopb.dev_attr.attr, + &sensor_dev_attr_port_40_loopb.dev_attr.attr, + &sensor_dev_attr_port_41_loopb.dev_attr.attr, + &sensor_dev_attr_port_42_loopb.dev_attr.attr, + &sensor_dev_attr_port_43_loopb.dev_attr.attr, + &sensor_dev_attr_port_44_loopb.dev_attr.attr, + &sensor_dev_attr_port_45_loopb.dev_attr.attr, + &sensor_dev_attr_port_46_loopb.dev_attr.attr, + &sensor_dev_attr_port_47_loopb.dev_attr.attr, + &sensor_dev_attr_port_48_loopb.dev_attr.attr, + + NULL +}; + +static const struct attribute_group port_cpld0_group = { + .attrs = port_cpld0_attributes, +}; + +static int port_cpld0_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int status; + struct cpld_data *data = NULL; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "CPLD PROBE ERROR: i2c_check_functionality failed (0x%x)\n", client->addr); + status = -EIO; + goto exit; + } + + dev_info(&client->dev, "Nokia PORT_CPLD0 chip found.\n"); + data = kzalloc(sizeof(struct cpld_data), GFP_KERNEL); + + if (!data) { + dev_err(&client->dev, "CPLD PROBE ERROR: Can't allocate memory\n"); + status = -ENOMEM; + goto exit; + } + + data->client = client; + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + status = sysfs_create_group(&client->dev.kobj, &port_cpld0_group); + if (status) { + dev_err(&client->dev, "CPLD INIT ERROR: Cannot create sysfs\n"); + goto exit; + } + + dump_reg(data); + dev_info(&client->dev, "[PORT_CPLD0]Reseting PORTs ...\n"); + cpld_i2c_write(data, OSFP_LPMODE_REG0, 0x0); + cpld_i2c_write(data, OSFP_LPMODE_REG0+1, 0x0); + cpld_i2c_write(data, OSFP_LPMODE_REG0+2, 0x0); + cpld_i2c_write(data, OSFP_LPMODE_REG0+3, 0x0); + cpld_i2c_write(data, OSFP_RST_REG0, 0x0); + cpld_i2c_write(data, OSFP_RST_REG0+1, 0x0); + cpld_i2c_write(data, OSFP_RST_REG0+2, 0x0); + cpld_i2c_write(data, OSFP_RST_REG0+3, 0x0); + msleep(500); + cpld_i2c_write(data, OSFP_RST_REG0, 0xFF); + cpld_i2c_write(data, OSFP_RST_REG0+1, 0xFF); + cpld_i2c_write(data, OSFP_RST_REG0+2, 0xFF); + cpld_i2c_write(data, OSFP_RST_REG0+3, 0xFF); + dev_info(&client->dev, "[PORT_CPLD0]PORTs reset done.\n"); + cpld_i2c_write(data, SFP_MISC_REG, 0xC); + cpld_i2c_write(data, SFP_TXDIS_REG, 0x3); + cpld_i2c_write(data, SFP_EN_LP_REG, 0x0); + dump_reg(data); + + return 0; + +exit: + return status; +} + +static void port_cpld0_remove(struct i2c_client *client) +{ + struct cpld_data *data = i2c_get_clientdata(client); + sysfs_remove_group(&client->dev.kobj, &port_cpld0_group); + kfree(data); +} + +static const struct of_device_id port_cpld0_of_ids[] = { + { + .compatible = "nokia,port_cpld0", + .data = (void *) 0, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, port_cpld0_of_ids); + +static const struct i2c_device_id port_cpld0_ids[] = { + { DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, port_cpld0_ids); + +static struct i2c_driver port_cpld0_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(port_cpld0_of_ids), + }, + .probe = port_cpld0_probe, + .remove = port_cpld0_remove, + .id_table = port_cpld0_ids, + .address_list = cpld_address_list, +}; + +static int __init port_cpld0_init(void) +{ + return i2c_add_driver(&port_cpld0_driver); +} + +static void __exit port_cpld0_exit(void) +{ + i2c_del_driver(&port_cpld0_driver); +} + +MODULE_AUTHOR("Nokia"); +MODULE_DESCRIPTION("NOKIA H6-64 CPLD0 driver"); +MODULE_LICENSE("GPL"); + +module_init(port_cpld0_init); +module_exit(port_cpld0_exit); diff --git a/ixr7220h6-64/modules/port_cpld1.c b/ixr7220h6-64/modules/port_cpld1.c new file mode 100644 index 0000000..c32c04f --- /dev/null +++ b/ixr7220h6-64/modules/port_cpld1.c @@ -0,0 +1,847 @@ +// * CPLD driver for Nokia-7220-IXR-H6-64 Router +// * +// * Copyright (C) 2025 Nokia 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 3 of the License, or +// * 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. +// * see + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "port_cpld1" + +// REGISTERS ADDRESS MAP +#define VER_MAJOR_REG 0x00 +#define VER_MINOR_REG 0x01 +#define SCRATCH_REG 0x04 +#define OSFP_LPMODE_REG0 0x70 +#define OSFP_RST_REG0 0x78 +#define OSFP_MODPRS_REG0 0x88 +#define OSFP_PWGOOD_REG0 0x90 +#define OSFP_ENABLE_REG0 0x94 +#define OSFP_LOOPBK_REG0 0x98 + +// REG BIT FIELD POSITION or MASK + + +static const unsigned short cpld_address_list[] = {0x65, I2C_CLIENT_END}; + +struct cpld_data { + struct i2c_client *client; + struct mutex update_lock; +}; + +static int cpld_i2c_read(struct cpld_data *data, u8 reg) +{ + int val = 0; + struct i2c_client *client = data->client; + + val = i2c_smbus_read_byte_data(client, reg); + if (val < 0) { + dev_warn(&client->dev, "CPLD READ ERROR: reg(0x%02x) err %d\n", reg, val); + } + + return val; +} + +static void cpld_i2c_write(struct cpld_data *data, u8 reg, u8 value) +{ + int res = 0; + struct i2c_client *client = data->client; + + mutex_lock(&data->update_lock); + res = i2c_smbus_write_byte_data(client, reg, value); + if (res < 0) { + dev_warn(&client->dev, "CPLD WRITE ERROR: reg(0x%02x) err %d\n", reg, res); + } + mutex_unlock(&data->update_lock); +} + +static void dump_reg(struct cpld_data *data) +{ + struct i2c_client *client = data->client; + u8 val0 = 0; + u8 val1 = 0; + u8 val2 = 0; + u8 val3 = 0; + + val0 = cpld_i2c_read(data, OSFP_RST_REG0); + val1 = cpld_i2c_read(data, OSFP_RST_REG0 + 1); + val2 = cpld_i2c_read(data, OSFP_RST_REG0 + 2); + val3 = cpld_i2c_read(data, OSFP_RST_REG0 + 3); + dev_info(&client->dev, "[PORT_CPLD1]OSFP_RESET_REG: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", val0, val1, val2, val3); + + val0 = cpld_i2c_read(data, OSFP_LPMODE_REG0); + val1 = cpld_i2c_read(data, OSFP_LPMODE_REG0 + 1); + val2 = cpld_i2c_read(data, OSFP_LPMODE_REG0 + 2); + val3 = cpld_i2c_read(data, OSFP_LPMODE_REG0 + 3); + dev_info(&client->dev, "[PORT_CPLD1]OSFP_LPMODE_REG: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", val0, val1, val2, val3); + + val0 = cpld_i2c_read(data, OSFP_MODPRS_REG0); + val1 = cpld_i2c_read(data, OSFP_MODPRS_REG0 + 1); + val2 = cpld_i2c_read(data, OSFP_MODPRS_REG0 + 2); + val3 = cpld_i2c_read(data, OSFP_MODPRS_REG0 + 3); + dev_info(&client->dev, "[PORT_CPLD1]OSFP_MODPRES_REG: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", val0, val1, val2, val3); + +} + +static ssize_t show_ver(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + u8 reg_major = 0; + u8 reg_minor = 0; + + reg_major = cpld_i2c_read(data, VER_MAJOR_REG); + reg_minor = cpld_i2c_read(data, VER_MINOR_REG); + + return sprintf(buf, "%d.%d\n", reg_major, reg_minor); +} + +static ssize_t show_scratch(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + u8 val = 0; + + val = cpld_i2c_read(data, SCRATCH_REG); + + return sprintf(buf, "0x%02x\n", val); +} + +static ssize_t set_scratch(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct cpld_data *data = dev_get_drvdata(dev); + u8 usr_val = 0; + + int ret = kstrtou8(buf, 16, &usr_val); + if (ret != 0) { + return ret; + } + if (usr_val > 0xFF) { + return -EINVAL; + } + + cpld_i2c_write(data, SCRATCH_REG, usr_val); + + return count; +} + +static ssize_t show_osfp_lpmode(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, OSFP_LPMODE_REG0 + (sda->index / 8)); + + return sprintf(buf, "%d\n", (val>>(sda->index % 8)) & 0x1 ? 1:0); +} + +static ssize_t set_osfp_lpmode(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 reg_val = 0; + u8 usr_val = 0; + u8 mask; + + int ret = kstrtou8(buf, 10, &usr_val); + if (ret != 0) { + return ret; + } + if (usr_val > 1) { + return -EINVAL; + } + + mask = (~(1 << (sda->index % 8))) & 0xFF; + reg_val = cpld_i2c_read(data, OSFP_LPMODE_REG0 + (sda->index / 8)); + reg_val = reg_val & mask; + usr_val = usr_val << (sda->index % 8); + cpld_i2c_write(data, OSFP_LPMODE_REG0 + (sda->index / 8), (reg_val | usr_val)); + + return count; +} + +static ssize_t show_osfp_rst(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, OSFP_RST_REG0 + (sda->index / 8)); + + return sprintf(buf, "%d\n", (val>>(sda->index % 8)) & 0x1 ? 1:0); +} + +static ssize_t set_osfp_rst(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 reg_val = 0; + u8 usr_val = 0; + u8 mask; + + int ret = kstrtou8(buf, 10, &usr_val); + if (ret != 0) { + return ret; + } + if (usr_val > 1) { + return -EINVAL; + } + + mask = (~(1 << (sda->index % 8))) & 0xFF; + reg_val = cpld_i2c_read(data, OSFP_RST_REG0 + (sda->index / 8)); + reg_val = reg_val & mask; + usr_val = usr_val << (sda->index % 8); + cpld_i2c_write(data, OSFP_RST_REG0 + (sda->index / 8), (reg_val | usr_val)); + + return count; +} + +static ssize_t show_osfp_prs(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, OSFP_MODPRS_REG0 + (sda->index / 8)); + + return sprintf(buf, "%d\n", (val>>(sda->index % 8)) & 0x1 ? 1:0); +} + +static ssize_t show_modprs_reg(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, OSFP_MODPRS_REG0 + sda->index); + + return sprintf(buf, "0x%02x\n", val); +} + +static ssize_t show_osfp_pwgood(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, OSFP_PWGOOD_REG0 + (sda->index / 8)); + + return sprintf(buf, "%d\n", (val>>(sda->index % 8)) & 0x1 ? 1:0); +} + +static ssize_t show_osfp_en(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, OSFP_ENABLE_REG0 + (sda->index / 8)); + + return sprintf(buf, "%d\n", (val>>(sda->index % 8)) & 0x1 ? 1:0); +} + +static ssize_t set_osfp_en(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 reg_val = 0; + u8 usr_val = 0; + u8 mask; + + int ret = kstrtou8(buf, 10, &usr_val); + if (ret != 0) { + return ret; + } + if (usr_val > 1) { + return -EINVAL; + } + + mask = (~(1 << (sda->index % 8))) & 0xFF; + reg_val = cpld_i2c_read(data, OSFP_ENABLE_REG0 + (sda->index / 8)); + reg_val = reg_val & mask; + usr_val = usr_val << (sda->index % 8); + cpld_i2c_write(data, OSFP_ENABLE_REG0 + (sda->index / 8), (reg_val | usr_val)); + + return count; +} + +static ssize_t show_osfp_loopb(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, OSFP_LOOPBK_REG0 + (sda->index / 8)); + + return sprintf(buf, "%d\n", (val>>(sda->index % 8)) & 0x1 ? 1:0); +} + +static ssize_t set_osfp_loopb(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 reg_val = 0; + u8 usr_val = 0; + u8 mask; + + int ret = kstrtou8(buf, 10, &usr_val); + if (ret != 0) { + return ret; + } + if (usr_val > 1) { + return -EINVAL; + } + + mask = (~(1 << (sda->index % 8))) & 0xFF; + reg_val = cpld_i2c_read(data, OSFP_LOOPBK_REG0 + (sda->index / 8)); + reg_val = reg_val & mask; + usr_val = usr_val << (sda->index % 8); + cpld_i2c_write(data, OSFP_LOOPBK_REG0 + (sda->index / 8), (reg_val | usr_val)); + + return count; +} + +// sysfs attributes +static SENSOR_DEVICE_ATTR(version, S_IRUGO, show_ver, NULL, 0); +static SENSOR_DEVICE_ATTR(scratch, S_IRUGO | S_IWUSR, show_scratch, set_scratch, 0); + +static SENSOR_DEVICE_ATTR(port_17_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 0); +static SENSOR_DEVICE_ATTR(port_18_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 1); +static SENSOR_DEVICE_ATTR(port_19_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 2); +static SENSOR_DEVICE_ATTR(port_20_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 3); +static SENSOR_DEVICE_ATTR(port_21_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 4); +static SENSOR_DEVICE_ATTR(port_22_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 5); +static SENSOR_DEVICE_ATTR(port_23_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 6); +static SENSOR_DEVICE_ATTR(port_24_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 7); +static SENSOR_DEVICE_ATTR(port_25_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 8); +static SENSOR_DEVICE_ATTR(port_26_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 9); +static SENSOR_DEVICE_ATTR(port_27_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 10); +static SENSOR_DEVICE_ATTR(port_28_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 11); +static SENSOR_DEVICE_ATTR(port_29_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 12); +static SENSOR_DEVICE_ATTR(port_30_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 13); +static SENSOR_DEVICE_ATTR(port_31_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 14); +static SENSOR_DEVICE_ATTR(port_32_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 15); +static SENSOR_DEVICE_ATTR(port_49_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 16); +static SENSOR_DEVICE_ATTR(port_50_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 17); +static SENSOR_DEVICE_ATTR(port_51_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 18); +static SENSOR_DEVICE_ATTR(port_52_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 19); +static SENSOR_DEVICE_ATTR(port_53_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 20); +static SENSOR_DEVICE_ATTR(port_54_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 21); +static SENSOR_DEVICE_ATTR(port_55_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 22); +static SENSOR_DEVICE_ATTR(port_56_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 23); +static SENSOR_DEVICE_ATTR(port_57_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 24); +static SENSOR_DEVICE_ATTR(port_58_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 25); +static SENSOR_DEVICE_ATTR(port_59_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 26); +static SENSOR_DEVICE_ATTR(port_60_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 27); +static SENSOR_DEVICE_ATTR(port_61_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 28); +static SENSOR_DEVICE_ATTR(port_62_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 29); +static SENSOR_DEVICE_ATTR(port_63_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 30); +static SENSOR_DEVICE_ATTR(port_64_lpmod, S_IRUGO | S_IWUSR, show_osfp_lpmode, set_osfp_lpmode, 31); + +static SENSOR_DEVICE_ATTR(port_17_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 0); +static SENSOR_DEVICE_ATTR(port_18_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 1); +static SENSOR_DEVICE_ATTR(port_19_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 2); +static SENSOR_DEVICE_ATTR(port_20_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 3); +static SENSOR_DEVICE_ATTR(port_21_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 4); +static SENSOR_DEVICE_ATTR(port_22_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 5); +static SENSOR_DEVICE_ATTR(port_23_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 6); +static SENSOR_DEVICE_ATTR(port_24_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 7); +static SENSOR_DEVICE_ATTR(port_25_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 8); +static SENSOR_DEVICE_ATTR(port_26_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 9); +static SENSOR_DEVICE_ATTR(port_27_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 10); +static SENSOR_DEVICE_ATTR(port_28_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 11); +static SENSOR_DEVICE_ATTR(port_29_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 12); +static SENSOR_DEVICE_ATTR(port_30_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 13); +static SENSOR_DEVICE_ATTR(port_31_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 14); +static SENSOR_DEVICE_ATTR(port_32_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 15); +static SENSOR_DEVICE_ATTR(port_49_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 16); +static SENSOR_DEVICE_ATTR(port_50_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 17); +static SENSOR_DEVICE_ATTR(port_51_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 18); +static SENSOR_DEVICE_ATTR(port_52_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 19); +static SENSOR_DEVICE_ATTR(port_53_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 20); +static SENSOR_DEVICE_ATTR(port_54_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 21); +static SENSOR_DEVICE_ATTR(port_55_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 22); +static SENSOR_DEVICE_ATTR(port_56_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 23); +static SENSOR_DEVICE_ATTR(port_57_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 24); +static SENSOR_DEVICE_ATTR(port_58_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 25); +static SENSOR_DEVICE_ATTR(port_59_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 26); +static SENSOR_DEVICE_ATTR(port_60_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 27); +static SENSOR_DEVICE_ATTR(port_61_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 28); +static SENSOR_DEVICE_ATTR(port_62_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 29); +static SENSOR_DEVICE_ATTR(port_63_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 30); +static SENSOR_DEVICE_ATTR(port_64_rst, S_IRUGO | S_IWUSR, show_osfp_rst, set_osfp_rst, 31); + +static SENSOR_DEVICE_ATTR(port_17_prs, S_IRUGO, show_osfp_prs, NULL, 0); +static SENSOR_DEVICE_ATTR(port_18_prs, S_IRUGO, show_osfp_prs, NULL, 1); +static SENSOR_DEVICE_ATTR(port_19_prs, S_IRUGO, show_osfp_prs, NULL, 2); +static SENSOR_DEVICE_ATTR(port_20_prs, S_IRUGO, show_osfp_prs, NULL, 3); +static SENSOR_DEVICE_ATTR(port_21_prs, S_IRUGO, show_osfp_prs, NULL, 4); +static SENSOR_DEVICE_ATTR(port_22_prs, S_IRUGO, show_osfp_prs, NULL, 5); +static SENSOR_DEVICE_ATTR(port_23_prs, S_IRUGO, show_osfp_prs, NULL, 6); +static SENSOR_DEVICE_ATTR(port_24_prs, S_IRUGO, show_osfp_prs, NULL, 7); +static SENSOR_DEVICE_ATTR(port_25_prs, S_IRUGO, show_osfp_prs, NULL, 8); +static SENSOR_DEVICE_ATTR(port_26_prs, S_IRUGO, show_osfp_prs, NULL, 9); +static SENSOR_DEVICE_ATTR(port_27_prs, S_IRUGO, show_osfp_prs, NULL, 10); +static SENSOR_DEVICE_ATTR(port_28_prs, S_IRUGO, show_osfp_prs, NULL, 11); +static SENSOR_DEVICE_ATTR(port_29_prs, S_IRUGO, show_osfp_prs, NULL, 12); +static SENSOR_DEVICE_ATTR(port_30_prs, S_IRUGO, show_osfp_prs, NULL, 13); +static SENSOR_DEVICE_ATTR(port_31_prs, S_IRUGO, show_osfp_prs, NULL, 14); +static SENSOR_DEVICE_ATTR(port_32_prs, S_IRUGO, show_osfp_prs, NULL, 15); +static SENSOR_DEVICE_ATTR(port_49_prs, S_IRUGO, show_osfp_prs, NULL, 16); +static SENSOR_DEVICE_ATTR(port_50_prs, S_IRUGO, show_osfp_prs, NULL, 17); +static SENSOR_DEVICE_ATTR(port_51_prs, S_IRUGO, show_osfp_prs, NULL, 18); +static SENSOR_DEVICE_ATTR(port_52_prs, S_IRUGO, show_osfp_prs, NULL, 19); +static SENSOR_DEVICE_ATTR(port_53_prs, S_IRUGO, show_osfp_prs, NULL, 20); +static SENSOR_DEVICE_ATTR(port_54_prs, S_IRUGO, show_osfp_prs, NULL, 21); +static SENSOR_DEVICE_ATTR(port_55_prs, S_IRUGO, show_osfp_prs, NULL, 22); +static SENSOR_DEVICE_ATTR(port_56_prs, S_IRUGO, show_osfp_prs, NULL, 23); +static SENSOR_DEVICE_ATTR(port_57_prs, S_IRUGO, show_osfp_prs, NULL, 24); +static SENSOR_DEVICE_ATTR(port_58_prs, S_IRUGO, show_osfp_prs, NULL, 25); +static SENSOR_DEVICE_ATTR(port_59_prs, S_IRUGO, show_osfp_prs, NULL, 26); +static SENSOR_DEVICE_ATTR(port_60_prs, S_IRUGO, show_osfp_prs, NULL, 27); +static SENSOR_DEVICE_ATTR(port_61_prs, S_IRUGO, show_osfp_prs, NULL, 28); +static SENSOR_DEVICE_ATTR(port_62_prs, S_IRUGO, show_osfp_prs, NULL, 29); +static SENSOR_DEVICE_ATTR(port_63_prs, S_IRUGO, show_osfp_prs, NULL, 30); +static SENSOR_DEVICE_ATTR(port_64_prs, S_IRUGO, show_osfp_prs, NULL, 31); + +static SENSOR_DEVICE_ATTR(modprs_reg1, S_IRUGO, show_modprs_reg, NULL, 0); +static SENSOR_DEVICE_ATTR(modprs_reg2, S_IRUGO, show_modprs_reg, NULL, 1); +static SENSOR_DEVICE_ATTR(modprs_reg3, S_IRUGO, show_modprs_reg, NULL, 2); +static SENSOR_DEVICE_ATTR(modprs_reg4, S_IRUGO, show_modprs_reg, NULL, 3); + +static SENSOR_DEVICE_ATTR(port_17_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 0); +static SENSOR_DEVICE_ATTR(port_18_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 1); +static SENSOR_DEVICE_ATTR(port_19_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 2); +static SENSOR_DEVICE_ATTR(port_20_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 3); +static SENSOR_DEVICE_ATTR(port_21_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 4); +static SENSOR_DEVICE_ATTR(port_22_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 5); +static SENSOR_DEVICE_ATTR(port_23_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 6); +static SENSOR_DEVICE_ATTR(port_24_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 7); +static SENSOR_DEVICE_ATTR(port_25_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 8); +static SENSOR_DEVICE_ATTR(port_26_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 9); +static SENSOR_DEVICE_ATTR(port_27_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 10); +static SENSOR_DEVICE_ATTR(port_28_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 11); +static SENSOR_DEVICE_ATTR(port_29_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 12); +static SENSOR_DEVICE_ATTR(port_30_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 13); +static SENSOR_DEVICE_ATTR(port_31_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 14); +static SENSOR_DEVICE_ATTR(port_32_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 15); +static SENSOR_DEVICE_ATTR(port_49_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 16); +static SENSOR_DEVICE_ATTR(port_50_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 17); +static SENSOR_DEVICE_ATTR(port_51_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 18); +static SENSOR_DEVICE_ATTR(port_52_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 19); +static SENSOR_DEVICE_ATTR(port_53_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 20); +static SENSOR_DEVICE_ATTR(port_54_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 21); +static SENSOR_DEVICE_ATTR(port_55_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 22); +static SENSOR_DEVICE_ATTR(port_56_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 23); +static SENSOR_DEVICE_ATTR(port_57_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 24); +static SENSOR_DEVICE_ATTR(port_58_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 25); +static SENSOR_DEVICE_ATTR(port_59_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 26); +static SENSOR_DEVICE_ATTR(port_60_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 27); +static SENSOR_DEVICE_ATTR(port_61_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 28); +static SENSOR_DEVICE_ATTR(port_62_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 29); +static SENSOR_DEVICE_ATTR(port_63_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 30); +static SENSOR_DEVICE_ATTR(port_64_pwgood, S_IRUGO, show_osfp_pwgood, NULL, 31); + +static SENSOR_DEVICE_ATTR(port_17_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 0); +static SENSOR_DEVICE_ATTR(port_18_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 1); +static SENSOR_DEVICE_ATTR(port_19_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 2); +static SENSOR_DEVICE_ATTR(port_20_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 3); +static SENSOR_DEVICE_ATTR(port_21_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 4); +static SENSOR_DEVICE_ATTR(port_22_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 5); +static SENSOR_DEVICE_ATTR(port_23_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 6); +static SENSOR_DEVICE_ATTR(port_24_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 7); +static SENSOR_DEVICE_ATTR(port_25_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 8); +static SENSOR_DEVICE_ATTR(port_26_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 9); +static SENSOR_DEVICE_ATTR(port_27_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 10); +static SENSOR_DEVICE_ATTR(port_28_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 11); +static SENSOR_DEVICE_ATTR(port_29_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 12); +static SENSOR_DEVICE_ATTR(port_30_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 13); +static SENSOR_DEVICE_ATTR(port_31_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 14); +static SENSOR_DEVICE_ATTR(port_32_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 15); +static SENSOR_DEVICE_ATTR(port_49_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 16); +static SENSOR_DEVICE_ATTR(port_50_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 17); +static SENSOR_DEVICE_ATTR(port_51_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 18); +static SENSOR_DEVICE_ATTR(port_52_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 19); +static SENSOR_DEVICE_ATTR(port_53_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 20); +static SENSOR_DEVICE_ATTR(port_54_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 21); +static SENSOR_DEVICE_ATTR(port_55_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 22); +static SENSOR_DEVICE_ATTR(port_56_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 23); +static SENSOR_DEVICE_ATTR(port_57_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 24); +static SENSOR_DEVICE_ATTR(port_58_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 25); +static SENSOR_DEVICE_ATTR(port_59_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 26); +static SENSOR_DEVICE_ATTR(port_60_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 27); +static SENSOR_DEVICE_ATTR(port_61_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 28); +static SENSOR_DEVICE_ATTR(port_62_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 29); +static SENSOR_DEVICE_ATTR(port_63_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 30); +static SENSOR_DEVICE_ATTR(port_64_en, S_IRUGO | S_IWUSR, show_osfp_en, set_osfp_en, 31); + +static SENSOR_DEVICE_ATTR(port_17_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 0); +static SENSOR_DEVICE_ATTR(port_18_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 1); +static SENSOR_DEVICE_ATTR(port_19_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 2); +static SENSOR_DEVICE_ATTR(port_20_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 3); +static SENSOR_DEVICE_ATTR(port_21_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 4); +static SENSOR_DEVICE_ATTR(port_22_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 5); +static SENSOR_DEVICE_ATTR(port_23_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 6); +static SENSOR_DEVICE_ATTR(port_24_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 7); +static SENSOR_DEVICE_ATTR(port_25_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 8); +static SENSOR_DEVICE_ATTR(port_26_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 9); +static SENSOR_DEVICE_ATTR(port_27_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 10); +static SENSOR_DEVICE_ATTR(port_28_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 11); +static SENSOR_DEVICE_ATTR(port_29_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 12); +static SENSOR_DEVICE_ATTR(port_30_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 13); +static SENSOR_DEVICE_ATTR(port_31_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 14); +static SENSOR_DEVICE_ATTR(port_32_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 15); +static SENSOR_DEVICE_ATTR(port_49_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 16); +static SENSOR_DEVICE_ATTR(port_50_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 17); +static SENSOR_DEVICE_ATTR(port_51_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 18); +static SENSOR_DEVICE_ATTR(port_52_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 19); +static SENSOR_DEVICE_ATTR(port_53_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 20); +static SENSOR_DEVICE_ATTR(port_54_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 21); +static SENSOR_DEVICE_ATTR(port_55_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 22); +static SENSOR_DEVICE_ATTR(port_56_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 23); +static SENSOR_DEVICE_ATTR(port_57_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 24); +static SENSOR_DEVICE_ATTR(port_58_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 25); +static SENSOR_DEVICE_ATTR(port_59_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 26); +static SENSOR_DEVICE_ATTR(port_60_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 27); +static SENSOR_DEVICE_ATTR(port_61_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 28); +static SENSOR_DEVICE_ATTR(port_62_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 29); +static SENSOR_DEVICE_ATTR(port_63_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 30); +static SENSOR_DEVICE_ATTR(port_64_loopb, S_IRUGO | S_IWUSR, show_osfp_loopb, set_osfp_loopb, 31); + +static struct attribute *port_cpld1_attributes[] = { + &sensor_dev_attr_version.dev_attr.attr, + &sensor_dev_attr_scratch.dev_attr.attr, + + &sensor_dev_attr_port_17_lpmod.dev_attr.attr, + &sensor_dev_attr_port_18_lpmod.dev_attr.attr, + &sensor_dev_attr_port_19_lpmod.dev_attr.attr, + &sensor_dev_attr_port_20_lpmod.dev_attr.attr, + &sensor_dev_attr_port_21_lpmod.dev_attr.attr, + &sensor_dev_attr_port_22_lpmod.dev_attr.attr, + &sensor_dev_attr_port_23_lpmod.dev_attr.attr, + &sensor_dev_attr_port_24_lpmod.dev_attr.attr, + &sensor_dev_attr_port_25_lpmod.dev_attr.attr, + &sensor_dev_attr_port_26_lpmod.dev_attr.attr, + &sensor_dev_attr_port_27_lpmod.dev_attr.attr, + &sensor_dev_attr_port_28_lpmod.dev_attr.attr, + &sensor_dev_attr_port_29_lpmod.dev_attr.attr, + &sensor_dev_attr_port_30_lpmod.dev_attr.attr, + &sensor_dev_attr_port_31_lpmod.dev_attr.attr, + &sensor_dev_attr_port_32_lpmod.dev_attr.attr, + &sensor_dev_attr_port_49_lpmod.dev_attr.attr, + &sensor_dev_attr_port_50_lpmod.dev_attr.attr, + &sensor_dev_attr_port_51_lpmod.dev_attr.attr, + &sensor_dev_attr_port_52_lpmod.dev_attr.attr, + &sensor_dev_attr_port_53_lpmod.dev_attr.attr, + &sensor_dev_attr_port_54_lpmod.dev_attr.attr, + &sensor_dev_attr_port_55_lpmod.dev_attr.attr, + &sensor_dev_attr_port_56_lpmod.dev_attr.attr, + &sensor_dev_attr_port_57_lpmod.dev_attr.attr, + &sensor_dev_attr_port_58_lpmod.dev_attr.attr, + &sensor_dev_attr_port_59_lpmod.dev_attr.attr, + &sensor_dev_attr_port_60_lpmod.dev_attr.attr, + &sensor_dev_attr_port_61_lpmod.dev_attr.attr, + &sensor_dev_attr_port_62_lpmod.dev_attr.attr, + &sensor_dev_attr_port_63_lpmod.dev_attr.attr, + &sensor_dev_attr_port_64_lpmod.dev_attr.attr, + + &sensor_dev_attr_port_17_rst.dev_attr.attr, + &sensor_dev_attr_port_18_rst.dev_attr.attr, + &sensor_dev_attr_port_19_rst.dev_attr.attr, + &sensor_dev_attr_port_20_rst.dev_attr.attr, + &sensor_dev_attr_port_21_rst.dev_attr.attr, + &sensor_dev_attr_port_22_rst.dev_attr.attr, + &sensor_dev_attr_port_23_rst.dev_attr.attr, + &sensor_dev_attr_port_24_rst.dev_attr.attr, + &sensor_dev_attr_port_25_rst.dev_attr.attr, + &sensor_dev_attr_port_26_rst.dev_attr.attr, + &sensor_dev_attr_port_27_rst.dev_attr.attr, + &sensor_dev_attr_port_28_rst.dev_attr.attr, + &sensor_dev_attr_port_29_rst.dev_attr.attr, + &sensor_dev_attr_port_30_rst.dev_attr.attr, + &sensor_dev_attr_port_31_rst.dev_attr.attr, + &sensor_dev_attr_port_32_rst.dev_attr.attr, + &sensor_dev_attr_port_49_rst.dev_attr.attr, + &sensor_dev_attr_port_50_rst.dev_attr.attr, + &sensor_dev_attr_port_51_rst.dev_attr.attr, + &sensor_dev_attr_port_52_rst.dev_attr.attr, + &sensor_dev_attr_port_53_rst.dev_attr.attr, + &sensor_dev_attr_port_54_rst.dev_attr.attr, + &sensor_dev_attr_port_55_rst.dev_attr.attr, + &sensor_dev_attr_port_56_rst.dev_attr.attr, + &sensor_dev_attr_port_57_rst.dev_attr.attr, + &sensor_dev_attr_port_58_rst.dev_attr.attr, + &sensor_dev_attr_port_59_rst.dev_attr.attr, + &sensor_dev_attr_port_60_rst.dev_attr.attr, + &sensor_dev_attr_port_61_rst.dev_attr.attr, + &sensor_dev_attr_port_62_rst.dev_attr.attr, + &sensor_dev_attr_port_63_rst.dev_attr.attr, + &sensor_dev_attr_port_64_rst.dev_attr.attr, + + &sensor_dev_attr_port_17_prs.dev_attr.attr, + &sensor_dev_attr_port_18_prs.dev_attr.attr, + &sensor_dev_attr_port_19_prs.dev_attr.attr, + &sensor_dev_attr_port_20_prs.dev_attr.attr, + &sensor_dev_attr_port_21_prs.dev_attr.attr, + &sensor_dev_attr_port_22_prs.dev_attr.attr, + &sensor_dev_attr_port_23_prs.dev_attr.attr, + &sensor_dev_attr_port_24_prs.dev_attr.attr, + &sensor_dev_attr_port_25_prs.dev_attr.attr, + &sensor_dev_attr_port_26_prs.dev_attr.attr, + &sensor_dev_attr_port_27_prs.dev_attr.attr, + &sensor_dev_attr_port_28_prs.dev_attr.attr, + &sensor_dev_attr_port_29_prs.dev_attr.attr, + &sensor_dev_attr_port_30_prs.dev_attr.attr, + &sensor_dev_attr_port_31_prs.dev_attr.attr, + &sensor_dev_attr_port_32_prs.dev_attr.attr, + &sensor_dev_attr_port_49_prs.dev_attr.attr, + &sensor_dev_attr_port_50_prs.dev_attr.attr, + &sensor_dev_attr_port_51_prs.dev_attr.attr, + &sensor_dev_attr_port_52_prs.dev_attr.attr, + &sensor_dev_attr_port_53_prs.dev_attr.attr, + &sensor_dev_attr_port_54_prs.dev_attr.attr, + &sensor_dev_attr_port_55_prs.dev_attr.attr, + &sensor_dev_attr_port_56_prs.dev_attr.attr, + &sensor_dev_attr_port_57_prs.dev_attr.attr, + &sensor_dev_attr_port_58_prs.dev_attr.attr, + &sensor_dev_attr_port_59_prs.dev_attr.attr, + &sensor_dev_attr_port_60_prs.dev_attr.attr, + &sensor_dev_attr_port_61_prs.dev_attr.attr, + &sensor_dev_attr_port_62_prs.dev_attr.attr, + &sensor_dev_attr_port_63_prs.dev_attr.attr, + &sensor_dev_attr_port_64_prs.dev_attr.attr, + + &sensor_dev_attr_modprs_reg1.dev_attr.attr, + &sensor_dev_attr_modprs_reg2.dev_attr.attr, + &sensor_dev_attr_modprs_reg3.dev_attr.attr, + &sensor_dev_attr_modprs_reg4.dev_attr.attr, + + &sensor_dev_attr_port_17_pwgood.dev_attr.attr, + &sensor_dev_attr_port_18_pwgood.dev_attr.attr, + &sensor_dev_attr_port_19_pwgood.dev_attr.attr, + &sensor_dev_attr_port_20_pwgood.dev_attr.attr, + &sensor_dev_attr_port_21_pwgood.dev_attr.attr, + &sensor_dev_attr_port_22_pwgood.dev_attr.attr, + &sensor_dev_attr_port_23_pwgood.dev_attr.attr, + &sensor_dev_attr_port_24_pwgood.dev_attr.attr, + &sensor_dev_attr_port_25_pwgood.dev_attr.attr, + &sensor_dev_attr_port_26_pwgood.dev_attr.attr, + &sensor_dev_attr_port_27_pwgood.dev_attr.attr, + &sensor_dev_attr_port_28_pwgood.dev_attr.attr, + &sensor_dev_attr_port_29_pwgood.dev_attr.attr, + &sensor_dev_attr_port_30_pwgood.dev_attr.attr, + &sensor_dev_attr_port_31_pwgood.dev_attr.attr, + &sensor_dev_attr_port_32_pwgood.dev_attr.attr, + &sensor_dev_attr_port_49_pwgood.dev_attr.attr, + &sensor_dev_attr_port_50_pwgood.dev_attr.attr, + &sensor_dev_attr_port_51_pwgood.dev_attr.attr, + &sensor_dev_attr_port_52_pwgood.dev_attr.attr, + &sensor_dev_attr_port_53_pwgood.dev_attr.attr, + &sensor_dev_attr_port_54_pwgood.dev_attr.attr, + &sensor_dev_attr_port_55_pwgood.dev_attr.attr, + &sensor_dev_attr_port_56_pwgood.dev_attr.attr, + &sensor_dev_attr_port_57_pwgood.dev_attr.attr, + &sensor_dev_attr_port_58_pwgood.dev_attr.attr, + &sensor_dev_attr_port_59_pwgood.dev_attr.attr, + &sensor_dev_attr_port_60_pwgood.dev_attr.attr, + &sensor_dev_attr_port_61_pwgood.dev_attr.attr, + &sensor_dev_attr_port_62_pwgood.dev_attr.attr, + &sensor_dev_attr_port_63_pwgood.dev_attr.attr, + &sensor_dev_attr_port_64_pwgood.dev_attr.attr, + + &sensor_dev_attr_port_17_en.dev_attr.attr, + &sensor_dev_attr_port_18_en.dev_attr.attr, + &sensor_dev_attr_port_19_en.dev_attr.attr, + &sensor_dev_attr_port_20_en.dev_attr.attr, + &sensor_dev_attr_port_21_en.dev_attr.attr, + &sensor_dev_attr_port_22_en.dev_attr.attr, + &sensor_dev_attr_port_23_en.dev_attr.attr, + &sensor_dev_attr_port_24_en.dev_attr.attr, + &sensor_dev_attr_port_25_en.dev_attr.attr, + &sensor_dev_attr_port_26_en.dev_attr.attr, + &sensor_dev_attr_port_27_en.dev_attr.attr, + &sensor_dev_attr_port_28_en.dev_attr.attr, + &sensor_dev_attr_port_29_en.dev_attr.attr, + &sensor_dev_attr_port_30_en.dev_attr.attr, + &sensor_dev_attr_port_31_en.dev_attr.attr, + &sensor_dev_attr_port_32_en.dev_attr.attr, + &sensor_dev_attr_port_49_en.dev_attr.attr, + &sensor_dev_attr_port_50_en.dev_attr.attr, + &sensor_dev_attr_port_51_en.dev_attr.attr, + &sensor_dev_attr_port_52_en.dev_attr.attr, + &sensor_dev_attr_port_53_en.dev_attr.attr, + &sensor_dev_attr_port_54_en.dev_attr.attr, + &sensor_dev_attr_port_55_en.dev_attr.attr, + &sensor_dev_attr_port_56_en.dev_attr.attr, + &sensor_dev_attr_port_57_en.dev_attr.attr, + &sensor_dev_attr_port_58_en.dev_attr.attr, + &sensor_dev_attr_port_59_en.dev_attr.attr, + &sensor_dev_attr_port_60_en.dev_attr.attr, + &sensor_dev_attr_port_61_en.dev_attr.attr, + &sensor_dev_attr_port_62_en.dev_attr.attr, + &sensor_dev_attr_port_63_en.dev_attr.attr, + &sensor_dev_attr_port_64_en.dev_attr.attr, + + &sensor_dev_attr_port_17_loopb.dev_attr.attr, + &sensor_dev_attr_port_18_loopb.dev_attr.attr, + &sensor_dev_attr_port_19_loopb.dev_attr.attr, + &sensor_dev_attr_port_20_loopb.dev_attr.attr, + &sensor_dev_attr_port_21_loopb.dev_attr.attr, + &sensor_dev_attr_port_22_loopb.dev_attr.attr, + &sensor_dev_attr_port_23_loopb.dev_attr.attr, + &sensor_dev_attr_port_24_loopb.dev_attr.attr, + &sensor_dev_attr_port_25_loopb.dev_attr.attr, + &sensor_dev_attr_port_26_loopb.dev_attr.attr, + &sensor_dev_attr_port_27_loopb.dev_attr.attr, + &sensor_dev_attr_port_28_loopb.dev_attr.attr, + &sensor_dev_attr_port_29_loopb.dev_attr.attr, + &sensor_dev_attr_port_30_loopb.dev_attr.attr, + &sensor_dev_attr_port_31_loopb.dev_attr.attr, + &sensor_dev_attr_port_32_loopb.dev_attr.attr, + &sensor_dev_attr_port_49_loopb.dev_attr.attr, + &sensor_dev_attr_port_50_loopb.dev_attr.attr, + &sensor_dev_attr_port_51_loopb.dev_attr.attr, + &sensor_dev_attr_port_52_loopb.dev_attr.attr, + &sensor_dev_attr_port_53_loopb.dev_attr.attr, + &sensor_dev_attr_port_54_loopb.dev_attr.attr, + &sensor_dev_attr_port_55_loopb.dev_attr.attr, + &sensor_dev_attr_port_56_loopb.dev_attr.attr, + &sensor_dev_attr_port_57_loopb.dev_attr.attr, + &sensor_dev_attr_port_58_loopb.dev_attr.attr, + &sensor_dev_attr_port_59_loopb.dev_attr.attr, + &sensor_dev_attr_port_60_loopb.dev_attr.attr, + &sensor_dev_attr_port_61_loopb.dev_attr.attr, + &sensor_dev_attr_port_62_loopb.dev_attr.attr, + &sensor_dev_attr_port_63_loopb.dev_attr.attr, + &sensor_dev_attr_port_64_loopb.dev_attr.attr, + + NULL +}; + +static const struct attribute_group port_cpld1_group = { + .attrs = port_cpld1_attributes, +}; + +static int port_cpld1_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int status; + struct cpld_data *data = NULL; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "CPLD PROBE ERROR: i2c_check_functionality failed (0x%x)\n", client->addr); + status = -EIO; + goto exit; + } + + dev_info(&client->dev, "Nokia PORT_CPLD1 chip found.\n"); + data = kzalloc(sizeof(struct cpld_data), GFP_KERNEL); + + if (!data) { + dev_err(&client->dev, "CPLD PROBE ERROR: Can't allocate memory\n"); + status = -ENOMEM; + goto exit; + } + + data->client = client; + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + status = sysfs_create_group(&client->dev.kobj, &port_cpld1_group); + if (status) { + dev_err(&client->dev, "CPLD INIT ERROR: Cannot create sysfs\n"); + goto exit; + } + + dump_reg(data); + dev_info(&client->dev, "[PORT_CPLD1]Reseting PORTs ...\n"); + cpld_i2c_write(data, OSFP_LPMODE_REG0, 0x0); + cpld_i2c_write(data, OSFP_LPMODE_REG0+1, 0x0); + cpld_i2c_write(data, OSFP_LPMODE_REG0+2, 0x0); + cpld_i2c_write(data, OSFP_LPMODE_REG0+3, 0x0); + cpld_i2c_write(data, OSFP_RST_REG0, 0x0); + cpld_i2c_write(data, OSFP_RST_REG0+1, 0x0); + cpld_i2c_write(data, OSFP_RST_REG0+2, 0x0); + cpld_i2c_write(data, OSFP_RST_REG0+3, 0x0); + msleep(500); + cpld_i2c_write(data, OSFP_RST_REG0, 0xFF); + cpld_i2c_write(data, OSFP_RST_REG0+1, 0xFF); + cpld_i2c_write(data, OSFP_RST_REG0+2, 0xFF); + cpld_i2c_write(data, OSFP_RST_REG0+3, 0xFF); + dev_info(&client->dev, "[PORT_CPLD1]PORTs reset done.\n"); + dump_reg(data); + + return 0; + +exit: + return status; +} + +static void port_cpld1_remove(struct i2c_client *client) +{ + struct cpld_data *data = i2c_get_clientdata(client); + sysfs_remove_group(&client->dev.kobj, &port_cpld1_group); + kfree(data); +} + +static const struct of_device_id port_cpld1_of_ids[] = { + { + .compatible = "nokia,port_cpld1", + .data = (void *) 0, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, port_cpld1_of_ids); + +static const struct i2c_device_id port_cpld1_ids[] = { + { DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, port_cpld1_ids); + +static struct i2c_driver port_cpld1_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(port_cpld1_of_ids), + }, + .probe = port_cpld1_probe, + .remove = port_cpld1_remove, + .id_table = port_cpld1_ids, + .address_list = cpld_address_list, +}; + +static int __init port_cpld1_init(void) +{ + return i2c_add_driver(&port_cpld1_driver); +} + +static void __exit port_cpld1_exit(void) +{ + i2c_del_driver(&port_cpld1_driver); +} + +MODULE_AUTHOR("Nokia"); +MODULE_DESCRIPTION("NOKIA H6-64 CPLD1 driver"); +MODULE_LICENSE("GPL"); + +module_init(port_cpld1_init); +module_exit(port_cpld1_exit); diff --git a/ixr7220h6-64/modules/sys_cpld.c b/ixr7220h6-64/modules/sys_cpld.c new file mode 100644 index 0000000..83cbc80 --- /dev/null +++ b/ixr7220h6-64/modules/sys_cpld.c @@ -0,0 +1,422 @@ +// * CPLD driver for Nokia-7220-IXR-H6-64 Router +// * +// * Copyright (C) 2025 Nokia 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 3 of the License, or +// * 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. +// * see + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "sys_cpld" + +// REGISTERS ADDRESS MAP +#define VER_MAJOR_REG 0x00 +#define VER_MINOR_REG 0x01 +#define SCRATCH_REG 0x04 +#define PSU_GOOD_REG 0x0B +#define PSU_PRES_REG 0x0C +#define RST_GROUP1_REG 0x11 +#define OSFP_EFUSE_REG0 0x70 +#define SYS_LED_REG0 0x80 +#define SYS_LED_REG1 0x81 + +// REG BIT FIELD POSITION or MASK + +static const unsigned short cpld_address_list[] = {0x61, I2C_CLIENT_END}; + +struct cpld_data { + struct i2c_client *client; + struct mutex update_lock; + int osfp_efuse; +}; + +static int cpld_i2c_read(struct cpld_data *data, u8 reg) +{ + int val = 0; + struct i2c_client *client = data->client; + + val = i2c_smbus_read_byte_data(client, reg); + if (val < 0) { + dev_warn(&client->dev, "CPLD READ ERROR: reg(0x%02x) err %d\n", reg, val); + } + + return val; +} + +static void cpld_i2c_write(struct cpld_data *data, u8 reg, u8 value) +{ + int res = 0; + struct i2c_client *client = data->client; + + mutex_lock(&data->update_lock); + res = i2c_smbus_write_byte_data(client, reg, value); + if (res < 0) { + dev_warn(&client->dev, "CPLD WRITE ERROR: reg(0x%02x) err %d\n", reg, res); + } + mutex_unlock(&data->update_lock); +} + +static ssize_t show_ver(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + u8 reg_major = 0; + u8 reg_minor = 0; + + reg_major = cpld_i2c_read(data, VER_MAJOR_REG); + reg_minor = cpld_i2c_read(data, VER_MINOR_REG); + + return sprintf(buf, "%d.%d\n", reg_major, reg_minor); +} + +static ssize_t show_scratch(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + u8 val = 0; + + val = cpld_i2c_read(data, SCRATCH_REG); + + return sprintf(buf, "0x%02x\n", val); +} + +static ssize_t set_scratch(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct cpld_data *data = dev_get_drvdata(dev); + u8 usr_val = 0; + + int ret = kstrtou8(buf, 16, &usr_val); + if (ret != 0) { + return ret; + } + if (usr_val > 0xFF) { + return -EINVAL; + } + + cpld_i2c_write(data, SCRATCH_REG, usr_val); + + return count; +} + +static ssize_t show_psu_ok(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, PSU_GOOD_REG); + + return sprintf(buf, "%d\n", (val>>sda->index) & 0x1 ? 1:0); +} + +static ssize_t show_psu_pres(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, PSU_PRES_REG); + + return sprintf(buf, "%d\n", (val>>sda->index) & 0x1 ? 1:0); +} + +static ssize_t show_led0(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + u8 mask = 0xF; + + val = cpld_i2c_read(data, SYS_LED_REG0); + if (sda->index == 0) mask = 0xF; + else mask = 0x3; + return sprintf(buf, "0x%x\n", (val>>sda->index) & mask); +} + +static ssize_t set_led0(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 reg_val = 0; + u8 usr_val = 0; + u8 reg_mask = 0xFF; + u8 mask = 0xF; + + int ret = kstrtou8(buf, 16, &usr_val); + if (ret != 0) { + return ret; + } + if (sda->index == 0) mask = 0xF; + else mask = 0x3; + if (usr_val > mask) { + return -EINVAL; + } + reg_mask = (~(mask << sda->index)) & 0xFF; + reg_val = cpld_i2c_read(data, SYS_LED_REG0); + reg_val = reg_val & reg_mask; + usr_val = usr_val << sda->index; + cpld_i2c_write(data, SYS_LED_REG0, (reg_val | usr_val)); + + return count; +} + +static ssize_t show_led1(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + u8 mask = 0xF; + + val = cpld_i2c_read(data, SYS_LED_REG1); + if (sda->index == 0) mask = 0xF; + else mask = 0x3; + return sprintf(buf, "0x%x\n", (val>>sda->index) & mask); +} + +static ssize_t set_led1(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 reg_val = 0; + u8 usr_val = 0; + u8 reg_mask = 0xFF; + u8 mask = 0xF; + + int ret = kstrtou8(buf, 16, &usr_val); + if (ret != 0) { + return ret; + } + if (sda->index == 0) mask = 0xF; + else mask = 0x3; + if (usr_val > mask) { + return -EINVAL; + } + reg_mask = (~(mask << sda->index)) & 0xFF; + reg_val = cpld_i2c_read(data, SYS_LED_REG1); + reg_val = reg_val & reg_mask; + usr_val = usr_val << sda->index; + cpld_i2c_write(data, SYS_LED_REG1, (reg_val | usr_val)); + + return count; +} + +static ssize_t show_rst1(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 val = 0; + + val = cpld_i2c_read(data, RST_GROUP1_REG); + + return sprintf(buf, "%d\n", (val>>sda->index) & 0x1 ? 1:0); +} + +static ssize_t set_rst1(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct cpld_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + u8 reg_val = 0; + u8 usr_val = 0; + u8 mask; + + int ret = kstrtou8(buf, 10, &usr_val); + if (ret != 0) { + return ret; + } + if (usr_val > 1) { + return -EINVAL; + } + + mask = (~(1 << sda->index)) & 0xFF; + reg_val = cpld_i2c_read(data, RST_GROUP1_REG); + reg_val = reg_val & mask; + usr_val = usr_val << sda->index; + cpld_i2c_write(data, RST_GROUP1_REG, (reg_val | usr_val)); + + return count; +} + +static ssize_t show_osfp_efuse(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", (data->osfp_efuse) ? "Enabled":"Disabled"); +} + +static ssize_t set_osfp_efuse(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct cpld_data *data = dev_get_drvdata(dev); + int i; + const char *str_en = "Enable\n"; + const char *str_dis = "Disable\n"; + + if (strcmp(buf, str_en) == 0) { + for (i=0;i<8;i++) cpld_i2c_write(data, OSFP_EFUSE_REG0+i, 0xFF); + data->osfp_efuse = 1; + } + else if (strcmp(buf, str_dis) == 0) { + for (i=0;i<8;i++) cpld_i2c_write(data, OSFP_EFUSE_REG0+i, 0x0); + data->osfp_efuse = 0; + } + else + return -EINVAL; + + return count; +} + +// sysfs attributes +static SENSOR_DEVICE_ATTR(version, S_IRUGO, show_ver, NULL, 0); +static SENSOR_DEVICE_ATTR(scratch, S_IRUGO | S_IWUSR, show_scratch, set_scratch, 0); + +static SENSOR_DEVICE_ATTR(psu1_ok, S_IRUGO, show_psu_ok, NULL, 0); +static SENSOR_DEVICE_ATTR(psu2_ok, S_IRUGO, show_psu_ok, NULL, 1); +static SENSOR_DEVICE_ATTR(psu3_ok, S_IRUGO, show_psu_ok, NULL, 2); +static SENSOR_DEVICE_ATTR(psu4_ok, S_IRUGO, show_psu_ok, NULL, 3); +static SENSOR_DEVICE_ATTR(psu1_pres, S_IRUGO, show_psu_pres, NULL, 4); +static SENSOR_DEVICE_ATTR(psu2_pres, S_IRUGO, show_psu_pres, NULL, 5); +static SENSOR_DEVICE_ATTR(psu3_pres, S_IRUGO, show_psu_pres, NULL, 6); +static SENSOR_DEVICE_ATTR(psu4_pres, S_IRUGO, show_psu_pres, NULL, 7); + +static SENSOR_DEVICE_ATTR(led_sys, S_IRUGO | S_IWUSR, show_led0, set_led0, 0); +static SENSOR_DEVICE_ATTR(led_psu, S_IRUGO, show_led0, NULL, 4); +static SENSOR_DEVICE_ATTR(led_loc, S_IRUGO | S_IWUSR, show_led1, set_led1, 0); +static SENSOR_DEVICE_ATTR(led_fan, S_IRUGO | S_IWUSR, show_led1, set_led1, 4); + +static SENSOR_DEVICE_ATTR(mac_pcie_rst, S_IRUGO | S_IWUSR, show_rst1, set_rst1, 3); +static SENSOR_DEVICE_ATTR(osfp_efuse, S_IRUGO | S_IWUSR, show_osfp_efuse, set_osfp_efuse, 0); + +static struct attribute *sys_cpld_attributes[] = { + &sensor_dev_attr_version.dev_attr.attr, + &sensor_dev_attr_scratch.dev_attr.attr, + + &sensor_dev_attr_psu1_ok.dev_attr.attr, + &sensor_dev_attr_psu2_ok.dev_attr.attr, + &sensor_dev_attr_psu3_ok.dev_attr.attr, + &sensor_dev_attr_psu4_ok.dev_attr.attr, + &sensor_dev_attr_psu1_pres.dev_attr.attr, + &sensor_dev_attr_psu2_pres.dev_attr.attr, + &sensor_dev_attr_psu3_pres.dev_attr.attr, + &sensor_dev_attr_psu4_pres.dev_attr.attr, + + &sensor_dev_attr_led_sys.dev_attr.attr, + &sensor_dev_attr_led_psu.dev_attr.attr, + &sensor_dev_attr_led_loc.dev_attr.attr, + &sensor_dev_attr_led_fan.dev_attr.attr, + + &sensor_dev_attr_mac_pcie_rst.dev_attr.attr, + &sensor_dev_attr_osfp_efuse.dev_attr.attr, + + NULL +}; + +static const struct attribute_group sys_cpld_group = { + .attrs = sys_cpld_attributes, +}; + +static int sys_cpld_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int status; + struct cpld_data *data = NULL; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "CPLD PROBE ERROR: i2c_check_functionality failed (0x%x)\n", client->addr); + status = -EIO; + goto exit; + } + + dev_info(&client->dev, "Nokia SYS_CPLD chip found.\n"); + data = kzalloc(sizeof(struct cpld_data), GFP_KERNEL); + + if (!data) { + dev_err(&client->dev, "CPLD PROBE ERROR: Can't allocate memory\n"); + status = -ENOMEM; + goto exit; + } + + data->client = client; + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + status = sysfs_create_group(&client->dev.kobj, &sys_cpld_group); + if (status) { + dev_err(&client->dev, "CPLD INIT ERROR: Cannot create sysfs\n"); + goto exit; + } + + int i; + for (i=0;i<8;i++) cpld_i2c_write(data, OSFP_EFUSE_REG0+i, 0xFF); + data->osfp_efuse = 1; + + return 0; + +exit: + return status; +} + +static void sys_cpld_remove(struct i2c_client *client) +{ + struct cpld_data *data = i2c_get_clientdata(client); + sysfs_remove_group(&client->dev.kobj, &sys_cpld_group); + kfree(data); +} + +static const struct of_device_id sys_cpld_of_ids[] = { + { + .compatible = "nokia,sys_cpld", + .data = (void *) 0, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, sys_cpld_of_ids); + +static const struct i2c_device_id sys_cpld_ids[] = { + { DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sys_cpld_ids); + +static struct i2c_driver sys_cpld_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(sys_cpld_of_ids), + }, + .probe = sys_cpld_probe, + .remove = sys_cpld_remove, + .id_table = sys_cpld_ids, + .address_list = cpld_address_list, +}; + +static int __init sys_cpld_init(void) +{ + return i2c_add_driver(&sys_cpld_driver); +} + +static void __exit sys_cpld_exit(void) +{ + i2c_del_driver(&sys_cpld_driver); +} + +MODULE_AUTHOR("Nokia"); +MODULE_DESCRIPTION("NOKIA H6-64 SYS_CPLD driver"); +MODULE_LICENSE("GPL"); + +module_init(sys_cpld_init); +module_exit(sys_cpld_exit); diff --git a/ixr7220h6-64/modules/sys_fpga.c b/ixr7220h6-64/modules/sys_fpga.c new file mode 100644 index 0000000..0bd912b --- /dev/null +++ b/ixr7220h6-64/modules/sys_fpga.c @@ -0,0 +1,257 @@ +// * CPLD driver for Nokia-7220-IXR-H6-64 Router +// * +// * Copyright (C) 2025 Nokia 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 3 of the License, or +// * 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. +// * see + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "sys_fpga" + +// REGISTERS ADDRESS MAP +#define VER_MAJOR_REG 0x01 +#define VER_MINOR_REG 0x02 +#define SCRATCH_REG 0x04 +#define HITLESS_REG 0x0A +#define MISC_CPLD_REG 0x0B +#define JTAG_MUX_REG 0x36 +#define RESET_REASON_REG 0x3B + +// REG BIT FIELD POSITION or MASK + +static const unsigned short cpld_address_list[] = {0x60, I2C_CLIENT_END}; + +struct cpld_data { + struct i2c_client *client; + struct mutex update_lock; + int reset_cause; +}; + +static int cpld_i2c_read(struct cpld_data *data, u8 reg) +{ + int val = 0; + struct i2c_client *client = data->client; + + val = i2c_smbus_read_byte_data(client, reg); + if (val < 0) { + dev_warn(&client->dev, "CPLD READ ERROR: reg(0x%02x) err %d\n", reg, val); + } + + return val; +} + +static void cpld_i2c_write(struct cpld_data *data, u8 reg, u8 value) +{ + int res = 0; + struct i2c_client *client = data->client; + + mutex_lock(&data->update_lock); + res = i2c_smbus_write_byte_data(client, reg, value); + if (res < 0) { + dev_warn(&client->dev, "CPLD WRITE ERROR: reg(0x%02x) err %d\n", reg, res); + } + mutex_unlock(&data->update_lock); +} + +static ssize_t show_ver(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + u8 reg_major = 0; + u8 reg_minor = 0; + + reg_major = cpld_i2c_read(data, VER_MAJOR_REG); + reg_minor = cpld_i2c_read(data, VER_MINOR_REG); + + return sprintf(buf, "%d.%d\n", reg_major, reg_minor); +} + +static ssize_t show_scratch(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + u8 val = 0; + + val = cpld_i2c_read(data, SCRATCH_REG); + + return sprintf(buf, "0x%02x\n", val); +} + +static ssize_t set_scratch(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct cpld_data *data = dev_get_drvdata(dev); + u8 usr_val = 0; + + int ret = kstrtou8(buf, 16, &usr_val); + if (ret != 0) { + return ret; + } + if (usr_val > 0xFF) { + return -EINVAL; + } + + cpld_i2c_write(data, SCRATCH_REG, usr_val); + + return count; +} + +static ssize_t show_reset_cause(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%02x\n", data->reset_cause); +} + +static ssize_t show_hitless(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct cpld_data *data = dev_get_drvdata(dev); + u8 val = 0; + + val = cpld_i2c_read(data, HITLESS_REG); + + return sprintf(buf, "0x%02x\n", val); +} + +static ssize_t set_hitless(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct cpld_data *data = dev_get_drvdata(dev); + u8 usr_val = 0; + + int ret = kstrtou8(buf, 16, &usr_val); + if (ret != 0) { + return ret; + } + if (usr_val > 0xFF) { + return -EINVAL; + } + + cpld_i2c_write(data, HITLESS_REG, usr_val); + + return count; +} + +// sysfs attributes +static SENSOR_DEVICE_ATTR(version, S_IRUGO, show_ver, NULL, 0); +static SENSOR_DEVICE_ATTR(scratch, S_IRUGO | S_IWUSR, show_scratch, set_scratch, 0); +static SENSOR_DEVICE_ATTR(reset_cause, S_IRUGO, show_reset_cause, NULL, 0); +static SENSOR_DEVICE_ATTR(hitless, S_IRUGO | S_IWUSR, show_hitless, set_hitless, 0); + +static struct attribute *sys_fpga_attributes[] = { + &sensor_dev_attr_version.dev_attr.attr, + &sensor_dev_attr_scratch.dev_attr.attr, + &sensor_dev_attr_reset_cause.dev_attr.attr, + &sensor_dev_attr_hitless.dev_attr.attr, + + NULL +}; + +static const struct attribute_group sys_fpga_group = { + .attrs = sys_fpga_attributes, +}; + +static int sys_fpga_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int status; + struct cpld_data *data = NULL; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "CPLD PROBE ERROR: i2c_check_functionality failed (0x%x)\n", client->addr); + status = -EIO; + goto exit; + } + + dev_info(&client->dev, "Nokia SYS_FPGA chip found.\n"); + data = kzalloc(sizeof(struct cpld_data), GFP_KERNEL); + + if (!data) { + dev_err(&client->dev, "CPLD PROBE ERROR: Can't allocate memory\n"); + status = -ENOMEM; + goto exit; + } + + data->client = client; + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + status = sysfs_create_group(&client->dev.kobj, &sys_fpga_group); + if (status) { + dev_err(&client->dev, "CPLD INIT ERROR: Cannot create sysfs\n"); + goto exit; + } + + data->reset_cause = cpld_i2c_read(data, RESET_REASON_REG); + cpld_i2c_write(data, RESET_REASON_REG, 0xFF); + + return 0; + +exit: + return status; +} + +static void sys_fpga_remove(struct i2c_client *client) +{ + struct cpld_data *data = i2c_get_clientdata(client); + sysfs_remove_group(&client->dev.kobj, &sys_fpga_group); + kfree(data); +} + +static const struct of_device_id sys_fpga_of_ids[] = { + { + .compatible = "nokia,sys_fpga", + .data = (void *) 0, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, sys_fpga_of_ids); + +static const struct i2c_device_id sys_fpga_ids[] = { + { DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sys_fpga_ids); + +static struct i2c_driver sys_fpga_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(sys_fpga_of_ids), + }, + .probe = sys_fpga_probe, + .remove = sys_fpga_remove, + .id_table = sys_fpga_ids, + .address_list = cpld_address_list, +}; + +static int __init sys_fpga_init(void) +{ + return i2c_add_driver(&sys_fpga_driver); +} + +static void __exit sys_fpga_exit(void) +{ + i2c_del_driver(&sys_fpga_driver); +} + +MODULE_AUTHOR("Nokia"); +MODULE_DESCRIPTION("NOKIA H6-64 SYS_FPGA driver"); +MODULE_LICENSE("GPL"); + +module_init(sys_fpga_init); +module_exit(sys_fpga_exit); diff --git a/ixr7220h6-64/modules/sys_mux.c b/ixr7220h6-64/modules/sys_mux.c new file mode 100644 index 0000000..669e63a --- /dev/null +++ b/ixr7220h6-64/modules/sys_mux.c @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright (C) Brandon Chuang + * + * This module supports the accton cpld that hold the channel select + * mechanism for other i2c slave devices, such as SFP. + * This includes the: + * Accton as456x CPLD1/CPLD2/CPLD3 + * + * 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 + +#define DRVNAME "sys_mux" + +#define I2C_RW_RETRY_COUNT 10 +#define I2C_RW_RETRY_INTERVAL 60 /* ms */ + +#define SYS_MUX_NCHANS 8 +#define SYS_MUX_SELECT_REG 0x0 +#define SYS_MUX_DESELECT_VAL 0x0 + +enum mux_type { + sys_mux +}; + +struct chip_desc { + u8 nchans; + u8 select_reg; + u8 deselect_val; +}; + +struct sys_mux_data { + enum mux_type type; + struct mutex update_lock; + struct i2c_client *client; +}; + +/* Provide specs for the as456x CPLD types we know about */ +static const struct chip_desc chips[] = { + [sys_mux] = { + .nchans = SYS_MUX_NCHANS, + .select_reg = SYS_MUX_SELECT_REG, + .deselect_val = SYS_MUX_DESELECT_VAL} +}; + +static const struct i2c_device_id sys_mux_id[] = { + {"sys_mux", sys_mux}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, sys_mux_id); + +static const struct of_device_id sys_mux_of_match[] = { + {.compatible = "sys_mux",.data = &chips[sys_mux]}, + {} +}; + +MODULE_DEVICE_TABLE(of, sys_mux_of_match); + +/* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer() + for this as they will try to lock adapter a second time */ +static int sys_mux_write(struct i2c_adapter *adap, + struct i2c_client *client, u8 reg, u8 val) +{ + union i2c_smbus_data data; + + data.byte = val; + return __i2c_smbus_xfer(adap, client->addr, client->flags, + I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE_DATA, + &data); +} + +static int sys_mux_select_chan(struct i2c_mux_core *muxc, u32 chan) +{ + struct sys_mux_data *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + int ret = 0; + + mutex_lock(&data->update_lock); + switch (data->type) { + case sys_mux: + ret = sys_mux_write(muxc->parent, client, + chips[data->type].select_reg, + 1 << chan); + break; + default: + break; + } + + mutex_unlock(&data->update_lock); + return ret; +} + +static int sys_mux_deselect_mux(struct i2c_mux_core *muxc, u32 chan) +{ + struct sys_mux_data *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + int ret = 0; + + mutex_lock(&data->update_lock); + ret = sys_mux_write(muxc->parent, client, + chips[data->type].select_reg, + chips[data->type].deselect_val); + mutex_unlock(&data->update_lock); + return ret; +} + +static void sys_mux_cleanup(struct i2c_mux_core *muxc) +{ + i2c_mux_del_adapters(muxc); +} + +/* + * I2C init/probing/exit functions + */ +static int sys_mux_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); + struct device *dev = &client->dev; + struct sys_mux_data *data; + struct i2c_mux_core *muxc; + int ret = -ENODEV; + int i = 0; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE)) + return -ENODEV; + + muxc = i2c_mux_alloc(adap, dev, SYS_MUX_NCHANS, sizeof(*data), 0, + sys_mux_select_chan, + sys_mux_deselect_mux); + if (!muxc) + return -ENOMEM; + + data = i2c_mux_priv(muxc); + mutex_init(&data->update_lock); + data->type = id->driver_data; + data->client = client; + i2c_set_clientdata(client, muxc); + + /* Now create an adapter for each channel */ + for (i = 0; i < chips[data->type].nchans; i++) { + ret = i2c_mux_add_adapter(muxc, 0, i, 0); + if (ret) + goto exit_mux; + } + + return 0; + + exit_mux: + sys_mux_cleanup(muxc); + return ret; +} + +static void sys_mux_remove(struct i2c_client *client) +{ + struct i2c_mux_core *muxc = i2c_get_clientdata(client); + sys_mux_cleanup(muxc); +} + +static struct i2c_driver sys_mux_driver = { + .driver = { + .name = "sys_mux", + .owner = THIS_MODULE, + }, + .probe = sys_mux_probe, + .remove = sys_mux_remove, + .id_table = sys_mux_id, +}; + +module_i2c_driver(sys_mux_driver); + +MODULE_AUTHOR("Roger Ho "); +MODULE_DESCRIPTION("sys_mux driver"); +MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/ixr7220h6-64/scripts/h6_64_platform_init.sh b/ixr7220h6-64/scripts/h6_64_platform_init.sh new file mode 100755 index 0000000..750eab5 --- /dev/null +++ b/ixr7220h6-64/scripts/h6_64_platform_init.sh @@ -0,0 +1,132 @@ +#!/bin/bash + +# Load required kernel-mode drivers + +load_kernel_drivers() { + echo "Loading Kernel Drivers" + depmod -a + rmmod amd-xgbe + rmmod igb + rmmod i2c-piix4 + rmmod i2c_designware_platform + modprobe igb + modprobe amd-xgbe + modprobe i2c_designware_platform + modprobe i2c-piix4 + + modprobe i2c-i801 + modprobe i2c-dev + modprobe i2c-mux + modprobe i2c-smbus + modprobe i2c-ismt + modprobe i2c-ocores + modprobe h6_i2c_oc + modprobe sys_mux + modprobe drivetemp +} + +h6-64_profile() +{ + MAC_ADDR=$(ip link show eth0 | grep ether | awk '{print $2}') + sed -i "s/switchMacAddress=.*/switchMacAddress=$MAC_ADDR/g" /usr/share/sonic/device/x86_64-nokia_ixr7220_h6_64-r0/Nokia-IXR7220-H6-64/profile.ini + echo "Nokia-IXR7220-H6-64: Updated switch mac address ${MAC_ADDR}" +} + +file_exists() { + # Wait 10 seconds max till file exists + for((i=0; i<10; i++)); + do + if [ -f $1 ]; then + return 1 + fi + sleep 1 + done + return 0 +} + +# Install kernel drivers required for i2c bus access +load_kernel_drivers + +echo sys_fpga 0x60 > /sys/bus/i2c/devices/i2c-1/new_device + +#Instantiated fpga mux +echo sys_mux 0x70 > /sys/bus/i2c/devices/i2c-1/new_device +echo sys_mux 0x71 > /sys/bus/i2c/devices/i2c-1/new_device +echo sys_mux 0x72 > /sys/bus/i2c/devices/i2c-73/new_device +echo sys_mux 0x73 > /sys/bus/i2c/devices/i2c-73/new_device +echo sys_mux 0x74 > /sys/bus/i2c/devices/i2c-73/new_device +echo sys_mux 0x76 > /sys/bus/i2c/devices/i2c-79/new_device +echo sys_mux 0x76 > /sys/bus/i2c/devices/i2c-80/new_device + +#Instantiated system eeprom +echo 24c04 0x56 > /sys/bus/i2c/devices/i2c-99/new_device + +echo sys_cpld 0x61 > /sys/bus/i2c/devices/i2c-73/new_device +echo port_cpld0 0x64 > /sys/bus/i2c/devices/i2c-89/new_device +echo port_cpld1 0x65 > /sys/bus/i2c/devices/i2c-90/new_device + +# Fan +echo h6_fan 0x33 > /sys/bus/i2c/devices/i2c-79/new_device +echo h6_fan 0x33 > /sys/bus/i2c/devices/i2c-80/new_device + +echo 24c64 0x50 > /sys/bus/i2c/devices/i2c-112/new_device +echo 24c64 0x50 > /sys/bus/i2c/devices/i2c-113/new_device +echo 24c64 0x50 > /sys/bus/i2c/devices/i2c-114/new_device +echo 24c64 0x50 > /sys/bus/i2c/devices/i2c-115/new_device + +echo 24c64 0x50 > /sys/bus/i2c/devices/i2c-120/new_device +echo 24c64 0x50 > /sys/bus/i2c/devices/i2c-121/new_device +echo 24c64 0x50 > /sys/bus/i2c/devices/i2c-122/new_device +echo 24c64 0x50 > /sys/bus/i2c/devices/i2c-123/new_device + +for index in {5..68}; do + echo optoe1 0x50 > /sys/bus/i2c/devices/i2c-${index}/new_device +done +echo optoe2 0x50 > /sys/bus/i2c/devices/i2c-69/new_device +echo optoe2 0x50 > /sys/bus/i2c/devices/i2c-70/new_device + +#PSU +for i in {0..3}; do + pres="/sys/bus/i2c/devices/73-0061/psu$((i+1))_pres" + bus_path="/sys/bus/i2c/devices/i2c-$((94+i))/new_device" # 94~97 + pmbus_addr=$((0x58 + i)) # 0x58~0x5B + eeprom_addr=$((0x50 + i)) # 0x50~0x53 + + if [ "$(cat "$pres")" = "0" ]; then + echo "pmbus_psu $(printf '0x%X' $pmbus_addr)" > "$bus_path" + echo "eeprom_fru $(printf '0x%X' $eeprom_addr)" > "$bus_path" + fi +done + +# thermal +echo embd_ctrl 0x21 > /sys/bus/i2c/devices/i2c-0/new_device +echo jc42 0x18 > /sys/bus/i2c/devices/i2c-0/new_device +echo lm75 0x48 > /sys/bus/i2c/devices/i2c-77/new_device +echo lm75 0x49 > /sys/bus/i2c/devices/i2c-77/new_device +echo tmp464 0x48 > /sys/bus/i2c/devices/i2c-101/new_device +echo lm75 0x48 > /sys/bus/i2c/devices/i2c-98/new_device +echo lm75 0x49 > /sys/bus/i2c/devices/i2c-98/new_device +echo lm75 0x4a > /sys/bus/i2c/devices/i2c-98/new_device +echo lm75 0x4b > /sys/bus/i2c/devices/i2c-98/new_device +echo lm75 0x4c > /sys/bus/i2c/devices/i2c-98/new_device +echo lm75 0x4d > /sys/bus/i2c/devices/i2c-98/new_device +echo lm75 0x4d > /sys/bus/i2c/devices/i2c-111/new_device +echo lm75 0x4d > /sys/bus/i2c/devices/i2c-119/new_device + +for ch in {1..8}; do + echo 60 > /sys/bus/i2c/devices/79-0033/hwmon/hwmon*/fan${ch}_pwm + echo 60 > /sys/bus/i2c/devices/80-0033/hwmon/hwmon*/fan${ch}_pwm +done + +file_exists /sys/bus/i2c/devices/99-0056/eeprom +status=$? +if [ "$status" == "1" ]; then + chmod 644 /sys/bus/i2c/devices/99-0056/eeprom +else + echo "SYSEEPROM file not found" +fi + +h6-64_profile + +exit + diff --git a/ixr7220h6-64/scripts/ports_notify.py b/ixr7220h6-64/scripts/ports_notify.py new file mode 100644 index 0000000..778da09 --- /dev/null +++ b/ixr7220h6-64/scripts/ports_notify.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +""" + port_notify: + notify port status change from Sonic DB +""" + +try: + from swsscommon import swsscommon + from sonic_py_common import daemon_base, logger + from sonic_platform.sysfs import write_sysfs_file +except ImportError as e: + raise ImportError (str(e) + " - required module not found") + +SYSLOG_IDENTIFIER = "ports_notify" + +SELECT_TIMEOUT_MSECS = 1000 + +SWPLD2_DIR = "/sys/bus/i2c/devices/89-0064/" +SWPLD3_DIR = "/sys/bus/i2c/devices/90-0065/" +PORT_NUM = 66 + +# Global logger class instance +sonic_logger = logger.Logger(SYSLOG_IDENTIFIER) + +def wait_for_port_init_done(): + # Connect to APPL_DB and subscribe to PORT table notifications + appl_db = daemon_base.db_connect("APPL_DB") + sel = swsscommon.Select() + sst = swsscommon.SubscriberStateTable(appl_db, swsscommon.APP_PORT_TABLE_NAME) + sel.addSelectable(sst) + + # Make sure this daemon started after all port configured + while True: + (state, c) = sel.select(1000) + if state == swsscommon.Select.TIMEOUT: + continue + if state != swsscommon.Select.OBJECT: + sonic_logger.log_warning("sel.select() did not return swsscommon.Select.OBJECT") + continue + + (key, op, fvp) = sst.pop() + + # Wait until PortInitDone + if key in ["PortInitDone"]: + break + +def subscribe_port_config_change(): + sel = swsscommon.Select() + config_db = daemon_base.db_connect("CONFIG_DB") + port_tbl = swsscommon.SubscriberStateTable(config_db, swsscommon.CFG_PORT_TABLE_NAME) + port_tbl.filter = ['admin_status'] + sel.addSelectable(port_tbl) + return sel, port_tbl + +def handle_port_config_change(sel, port_config, logger): + """Select PORT table changes, once there is a port configuration add/remove, notify observers + """ + try: + (state, _) = sel.select(SELECT_TIMEOUT_MSECS) + except Exception: + return -1 + + if state == swsscommon.Select.TIMEOUT: + return 0 + if state != swsscommon.Select.OBJECT: + return -2 + + while True: + (port_name, op, fvp) = port_config.pop() + if not port_name: + break + + if fvp is not None: + fvp = dict(fvp) + + if 'admin_status' in fvp: + if 'index' in fvp: + port_index = int(fvp['index']) + if port_index in range(1, 17) or port_index in range(33, 49): + file_name = SWPLD2_DIR + f"port_{port_index}_en" + elif port_index in range(17, 33) or port_index in range(49, 65): + file_name = SWPLD3_DIR + f"port_{port_index}_en" + elif port_index in range(65, PORT_NUM+1): + file_name = SWPLD2_DIR + f"port_{port_index}_en" + else: + logger.log_warning(f"Wrong port index {port_index} for port {port_name}") + continue + else: + logger.log_warning(f"Wrong index from port {port_name}: {fvp}") + continue + + if fvp['admin_status'] == 'up': + write_sysfs_file(file_name, '1') + elif fvp['admin_status'] == 'down': + write_sysfs_file(file_name, '0') + + return 0 + +def main(): + + # Wait for PortInitDone + wait_for_port_init_done() + + sonic_logger.log_info("port init done!") + + sel, port_config = subscribe_port_config_change() + + while True: + status = handle_port_config_change(sel, port_config, sonic_logger) + if status < 0: + return -1 + + +if __name__ == '__main__': + main() diff --git a/ixr7220h6-64/service/h6_64_platform_init.service b/ixr7220h6-64/service/h6_64_platform_init.service new file mode 100755 index 0000000..b4cd5dc --- /dev/null +++ b/ixr7220h6-64/service/h6_64_platform_init.service @@ -0,0 +1,12 @@ +[Unit] +Description=Nokia-IXR7220-H6-64 Platform Service +After=sysinit.target +Before=pmon.service determine-reboot-cause.service + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/h6_64_platform_init.sh +StandardOutput=tty + +[Install] +WantedBy=multi-user.target diff --git a/ixr7220h6-64/service/ports_notify.service b/ixr7220h6-64/service/ports_notify.service new file mode 100644 index 0000000..db83b0e --- /dev/null +++ b/ixr7220h6-64/service/ports_notify.service @@ -0,0 +1,15 @@ +[Unit] +Description=ports_notify Service +Requires=swss.service database.service +After=swss.service database.service +BindsTo=swss.service database.service + + +[Service] +ExecStart=/usr/local/bin/ports_notify.py +Restart=always +RestartSec=10s +KillSignal=SIGTERM + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/ixr7220h6-64/setup.py b/ixr7220h6-64/setup.py new file mode 100755 index 0000000..d9398fa --- /dev/null +++ b/ixr7220h6-64/setup.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +import os +import sys +from setuptools import setup +os.listdir + +setup( + name='sonic_platform', + version='1.0', + description='Module to initialize Nokia IXR7220-H6-64 platforms', + + packages=[ + 'sonic_platform', + 'sonic_platform.test' + ], + + package_dir={ + 'sonic_platform': 'sonic_platform' + } +) diff --git a/ixr7220h6-64/sonic_platform/__init__.py b/ixr7220h6-64/sonic_platform/__init__.py new file mode 100755 index 0000000..290d8cd --- /dev/null +++ b/ixr7220h6-64/sonic_platform/__init__.py @@ -0,0 +1,2 @@ +__all__ = ["platform"] +from sonic_platform import * diff --git a/ixr7220h6-64/sonic_platform/chassis.py b/ixr7220h6-64/sonic_platform/chassis.py new file mode 100755 index 0000000..8d1b3bc --- /dev/null +++ b/ixr7220h6-64/sonic_platform/chassis.py @@ -0,0 +1,398 @@ +""" + Module contains an implementation of SONiC Platform Base API and + provides the platform information +""" + +try: + import os + import sys + from sonic_platform_base.chassis_base import ChassisBase + from sonic_platform.sfp import Sfp + from sonic_platform.eeprom import Eeprom + from sonic_platform.fan import Fan + from sonic_platform.sysfs import read_sysfs_file, write_sysfs_file + from .fan_drawer import RealDrawer + from sonic_platform.psu import Psu + from sonic_platform.thermal import Thermal + from sonic_platform.component import Component + from sonic_platform.sfp_event import SfpEvent + from sonic_py_common import logger +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +# Port numbers for SFP List Initialization +PORT_START = 1 +PORT_NUM = 64 +PORT_END = 66 +PORT_I2C_START = 5 +MAX_SELECT_DELAY = 10 + +# Device counts +FAN_DRAWERS = 8 +FANS_PER_DRAWER = 2 +PSU_NUM = 4 +THERMAL_NUM = 17 +COMPONENT_NUM = 7 + +CPLD_DIR = "/sys/bus/i2c/devices/73-0061/" +SYSFPGA_DIR = "/sys/bus/i2c/devices/1-0060/" + +SYSLOG_IDENTIFIER = "chassis" +sonic_logger = logger.Logger(SYSLOG_IDENTIFIER) +sonic_logger.set_min_log_priority_info() + +class Chassis(ChassisBase): + """ + Nokia platform-specific Chassis class + customized for the 7220 H6-64 platform. + """ + def __init__(self): + ChassisBase.__init__(self) + self.system_led_supported_color = ['green', 'blue', 'green_blink', + 'amber', 'off'] + + # Verify optoe driver OSFP eeprom devices were enumerated and exist + # then create the sfp nodes + eeprom_path = "/sys/bus/i2c/devices/{}-0050/eeprom" + for index in range(PORT_START, PORT_START + PORT_END): + port_i2c_map = PORT_I2C_START + index - 1 + port_eeprom_path = eeprom_path.format(port_i2c_map) + if not os.path.exists(port_eeprom_path): + sonic_logger.log_info(f"path {port_eeprom_path} didnt exist") + if index <= PORT_NUM: + sfp_node = Sfp(index, 'OSFP', port_eeprom_path, port_i2c_map) + elif index > PORT_NUM and index <= PORT_END: + sfp_node = Sfp(index, 'SFP28', port_eeprom_path, port_i2c_map) + self._sfp_list.append(sfp_node) + + self.sfp_event_initialized = False + + # Instantiate system eeprom object + self._eeprom = Eeprom(False, 0, False, 0) + + self._watchdog = None + self.sfp_event = None + self.max_select_event_returned = None + + # Construct lists fans, power supplies, thermals & components + for i in range(THERMAL_NUM): + thermal = Thermal(i, self._sfp_list) + self._thermal_list.append(thermal) + + drawer_num = FAN_DRAWERS + fan_num_per_drawer = FANS_PER_DRAWER + drawer_ctor = RealDrawer + for drawer_index in range(drawer_num): + drawer = drawer_ctor(drawer_index) + self._fan_drawer_list.append(drawer) + for fan_index in range(fan_num_per_drawer): + fan = Fan(fan_index, drawer_index) + drawer._fan_list.append(fan) + self._fan_list.append(fan) + + for i in range(PSU_NUM): + psu = Psu(i) + self._psu_list.append(psu) + + for i in range(COMPONENT_NUM): + component = Component(i) + self._component_list.append(component) + + def get_sfp(self, index): + """ + Retrieves sfp represented by (1-based) index + Args: + index: An integer, the index (1-based) of the sfp to retrieve. + The index should be the sequence of physical SFP ports in a + chassis starting from 1. + + Returns: + An object dervied from SfpBase representing the specified sfp + """ + sfp = None + + try: + # The index will start from 1 + sfp = self._sfp_list[index-1] + except IndexError: + sys.stderr.write(f"SFP index {index} out of range (1-{len(self._sfp_list)})\n") + return sfp + + def get_change_event(self, timeout=0): + """ + Returns a nested dictionary containing all devices which have + experienced a change at chassis level + + Args: + timeout: Timeout in milliseconds (optional). If timeout == 0, + this method will block until a change is detected. + + Returns: + (bool, dict): + - True if call successful, False if not; + - A nested dictionary where key is a device type, + value is a dictionary with key:value pairs in the format of + {'device_id':'device_event'}, + where device_id is the device ID for this device and + device_event, + status='1' represents device inserted, + status='0' represents device removed. + Ex. {'fan':{'0':'0', '2':'1'}, 'sfp':{'11':'0'}} + indicates that fan 0 has been removed, fan 2 + has been inserted and sfp 11 has been removed. + """ + # Initialize SFP event first + if not self.sfp_event_initialized: + self.sfp_event = SfpEvent() + self.sfp_event.initialize() + self.max_select_event_returned = PORT_END + self.sfp_event_initialized = True + + wait_for_ever = timeout == 0 + port_dict = {} + if wait_for_ever: + # xrcvd will call this monitor loop in the "SYSTEM_READY" state + # sonic_logger.log_info(" wait_for_ever get_change_event %d" % timeout) + timeout = MAX_SELECT_DELAY + while True: + status = self.sfp_event.check_sfp_status(port_dict, timeout) + if port_dict: + break + else: + # At boot up and in "INIT" state call from xrcvd will have timeout + # value return true without change after timeout and will + # transition to "SYSTEM_READY" + status = self.sfp_event.check_sfp_status(port_dict, timeout) + + if status: + return True, {'sfp': port_dict} + return True, {'sfp': {}} + + def get_num_psus(self): + """ + Retrieves the num of the psus + Returns: + int: The num of the psus + """ + return PSU_NUM + + def get_name(self): + """ + Retrieves the name of the chassis + Returns: + string: The name of the chassis + """ + return self._eeprom.modelstr() + + def get_presence(self): + """ + Retrieves the presence of the chassis + Returns: + bool: True if chassis is present, False if not + """ + return True + + def get_model(self): + """ + Retrieves the model number (or part number) of the chassis + Returns: + string: Model/part number of chassis + """ + return self._eeprom.part_number_str() + + def get_serial(self): + """ + Retrieves the serial number of the chassis + Returns: + string: Serial number of chassis + """ + return self._eeprom.serial_number_str() + + def get_status(self): + """ + Retrieves the operational status of the chassis + Returns: + bool: A boolean value, True if chassis is operating properly + False if not + """ + return True + + def get_base_mac(self): + """ + Retrieves the base MAC address for the chassis + + Returns: + A string containing the MAC address in the format + 'XX:XX:XX:XX:XX:XX' + """ + return self._eeprom.base_mac_addr() + + def get_service_tag(self): + """ + Retrieves the Service Tag of the chassis + Returns: + string: Service Tag of chassis + """ + return self._eeprom.service_tag_str() + + def get_revision(self): + """ + Retrieves the hardware revision of the chassis + + Returns: + string: Revision value of chassis + """ + return str(0) + + def get_system_eeprom_info(self): + """ + Retrieves the full content of system EEPROM information for the + chassis + + Returns: + A dictionary where keys are the type code defined in + OCP ONIE TlvInfo EEPROM format and values are their + corresponding values. + """ + return self._eeprom.system_eeprom_info() + + def get_thermal_manager(self): + """ + Get thermal manager + + Returns: + ThermalManager + """ + from .thermal_manager import ThermalManager + return ThermalManager + + def initizalize_system_led(self): + """ + Initizalize system led + + Returns: + bool: True if it is successful. + """ + return True + + def get_reboot_cause(self): + """ + Retrieves the cause of the previous reboot + Returns: + A tuple (string, string) where the first element is a string + containing the cause of the previous reboot. This string must be + one of the predefined strings in this class. If the first string + is "REBOOT_CAUSE_HARDWARE_OTHER", the second string can be used + to pass a description of the reboot cause. + """ + result = read_sysfs_file(SYSFPGA_DIR + "reset_cause") + + if (int(result, 16) & 0x10) >> 4 == 1: + return (self.REBOOT_CAUSE_WATCHDOG, "CPU_WD") + + if (int(result, 16) & 0x01) == 1: + return (self.REBOOT_CAUSE_WATCHDOG, "EC_WD") + + if (int(result, 16) & 0x02) >> 1 == 1: + return (self.REBOOT_CAUSE_HARDWARE_OTHER, "CPU Over Heat") + + if (int(result, 16) & 0x08) >> 3 == 1: + return (self.REBOOT_CAUSE_HARDWARE_OTHER, "Power Cycle") + + return (self.REBOOT_CAUSE_NON_HARDWARE, None) + + def get_watchdog(self): + """ + Retrieves hardware watchdog device on this chassis + + Returns: + An object derived from WatchdogBase representing the hardware + watchdog device + + Note: + We overload this method to ensure that watchdog is only initialized + when it is referenced. Currently, only one daemon can open the + watchdog. To initialize watchdog in the constructor causes multiple + daemon try opening watchdog when loading and constructing a chassis + object and fail. By doing so we can eliminate that risk. + """ + try: + if self._watchdog is None: + from sonic_platform.watchdog import WatchdogImplBase + watchdog_device_path = "/dev/watchdog0" + self._watchdog = WatchdogImplBase(watchdog_device_path) + except ImportError as e: + sonic_logger.log_warning(f"Fail to load watchdog {repr(e)}") + + return self._watchdog + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. If the agent + cannot determine the parent-relative position + for some reason, or if the associated value of entPhysicalContainedIn is '0', + then the value '-1' is returned + Returns: + integer: The 1-based relative physical position in parent device or -1 if + cannot determine the position + """ + return -1 + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False + + def set_status_led(self, color): + """ + Sets the state of the system LED + + Args: + color: A string representing the color with which to set the + system LED + + Returns: + bool: True if system LED state is set successfully, False if not + """ + color_to_value = { + 'blue': '0x3', + 'green': '0x5', + 'amber': '0x6', + 'off': '0x7', + 'green_blink': '0xf' + } + + if color not in self.system_led_supported_color: + return False + + value = color_to_value.get(color) + if value is None: + return False + + write_sysfs_file(CPLD_DIR + 'led_sys', value) + return True + + def get_status_led(self): + """ + Gets the state of the system LED + + Returns: + A string, one of the valid LED color strings which could be vendor + specified. + """ + result = read_sysfs_file(CPLD_DIR + 'led_sys') + val = int(result, 16) + if (val & 0x8) == 0x8: + return self.system_led_supported_color[2] + if val == 0x3: + return self.system_led_supported_color[1] + if val == 0x5: + return self.system_led_supported_color[0] + if val == 0x6: + return self.system_led_supported_color[3] + if val == 0x7: + return self.system_led_supported_color[4] + return 'N/A' diff --git a/ixr7220h6-64/sonic_platform/component.py b/ixr7220h6-64/sonic_platform/component.py new file mode 100755 index 0000000..f637b7e --- /dev/null +++ b/ixr7220h6-64/sonic_platform/component.py @@ -0,0 +1,277 @@ +""" + NOKIA IXR7220 H6-64 + + Module contains an implementation of SONiC Platform Base API and + provides the Components' (e.g., BIOS, CPLD, FPGA, etc.) available in + the platform +""" + +try: + import os + import subprocess + import ntpath + import time + import glob + from sonic_platform_base.component_base import ComponentBase + from sonic_platform.sysfs import read_sysfs_file, write_sysfs_file +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +SYSFS_DIR = ["/sys/class/dmi/id/", + "/sys/bus/i2c/devices/1-0060/", + "/sys/bus/i2c/devices/73-0061/", + "/sys/bus/i2c/devices/89-0064/", + "/sys/bus/i2c/devices/90-0065/", + "/sys/bus/i2c/devices/79-0033/hwmon/hwmon*/", + "/sys/bus/i2c/devices/80-0033/hwmon/hwmon*/"] + +class Component(ComponentBase): + """Nokia platform-specific Component class""" + + CHASSIS_COMPONENTS = [ + ["BIOS", "Basic Input/Output System"], + ["SYS_FPGA", "Used for managing CPU board"], + ["SYS_CPLD", "Used for managing BCM chip, PSUs and LEDs"], + ["PORT_CPLD0", "Used for managing PORT 1-16, 33-48, SFP28"], + ["PORT_CPLD1", "Used for managing PORT 17-32, 49-64"], + ["FCM0_CPLD", "Used for managing upper fan drawers"], + ["FCM1_CPLD", "Used for managing lower fan drawers"] ] + DEV_NAME = [" ", " ", "MAIN_CPLD", "MAIN_CPLD", "MAIN_CPLD", "FAN0_CPLD", "FAN1_CPLD"] + TFR_NAME = [" ", " ", "h6_64_sys_cpld_tfr.vme", "h6_64_port_cpld0_tfr.vme", + "h6_64_port_cpld1_tfr.vme", "h6_64_fan_cpld_tfr.vme", "h6_64_fan_cpld_tfr.vme"] + + BIOS_UPDATE_COMMAND = ['./afulnx_64', '', '/B', '/P', '/N', '/K'] + FPGA_CHECK_COMMAND = ['./fpga_spi_flash.sh', '-rid'] + FPGA_UPDATE_COMMAND = ['./fpga_spi_flash.sh', '-upd', '', '-all'] + CPLD_UPDATE_COMMAND = ['./cpldupd', '-u', '', ''] + + def __init__(self, component_index): + self.index = component_index + self.name = self.CHASSIS_COMPONENTS[self.index][0] + self.description = self.CHASSIS_COMPONENTS[self.index][1] + if self.name == "FCM0_CPLD" or self.name == "FCM1_CPLD": + hwmon_dir = glob.glob(SYSFS_DIR[self.index]) + self.sysfs_dir = hwmon_dir[0] + else: + self.sysfs_dir = SYSFS_DIR[self.index] + self.dev_name = self.DEV_NAME[self.index] + self.tfr_name = self.TFR_NAME[self.index] + + def _get_command_result(self, cmdline): + try: + proc = subprocess.Popen(cmdline.split(), stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + stdout = proc.communicate()[0] + proc.wait() + result = stdout.rstrip('\n') + except OSError: + result = None + + return result + + def _get_cpld_version(self): + if self.name == "BIOS": + return read_sysfs_file(self.sysfs_dir + "bios_version") + else: + return read_sysfs_file(self.sysfs_dir + "version") + + def get_name(self): + """ + Retrieves the name of the component + + Returns: + A string containing the name of the component + """ + return self.name + + def get_model(self): + """ + Retrieves the part number of the component + Returns: + string: Part number of component + """ + return 'NA' + + def get_serial(self): + """ + Retrieves the serial number of the component + Returns: + string: Serial number of component + """ + return 'NA' + + def get_presence(self): + """ + Retrieves the presence of the component + Returns: + bool: True if present, False if not + """ + return True + + def get_status(self): + """ + Retrieves the operational status of the component + Returns: + bool: True if component is operating properly, False if not + """ + return True + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return -1 + + def is_replaceable(self): + """ + Indicate whether component is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False + + def get_description(self): + """ + Retrieves the description of the component + + Returns: + A string containing the description of the component + """ + return self.description + + def get_firmware_version(self): + """ + Retrieves the firmware version of the component + + Returns: + A string containing the firmware version of the component + """ + return self._get_cpld_version() + + def install_firmware(self, image_path): + """ + Installs firmware to the component + + Args: + image_path: A string, path to firmware image + + Returns: + A boolean, True if install was successful, False if not + """ + image_name = ntpath.basename(image_path) + + # check whether the image file exists + os.chdir("/tmp") + if not os.path.isfile(image_name): + print(f"ERROR: the image {image_name} doesn't exist in /tmp") + return False + + if self.name == "BIOS": + # check whether the BIOS upgrade tool exists + if not os.path.isfile('/tmp/afulnx_64'): + print("ERROR: the BIOS upgrade tool /tmp/afulnx_64 doesn't exist ") + return False + self.BIOS_UPDATE_COMMAND[1] = image_name + try: + subprocess.run(self.BIOS_UPDATE_COMMAND, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + print(f"ERROR: Failed to upgrade BIOS: rc={e.returncode}") + return False + print("\nBIOS update has ended\n") + + elif self.name == "SYS_FPGA": + # check whether the fpga upgrade tool exists + if not os.path.isfile('/tmp/fpga_spi_flash.sh'): + print("ERROR: the fpga upgrade tool /tmp/fpga_spi_flash.sh doesn't exist ") + return False + if not os.path.isfile('/tmp/fpga_upd2'): + print("ERROR: the fpga upgrade tool /tmp/fpga_upd2 doesn't exist ") + return False + try: + result = subprocess.check_output(self.FPGA_CHECK_COMMAND) + result = subprocess.check_output(self.FPGA_CHECK_COMMAND) + text = result.decode('utf-8') + print(text) + except subprocess.CalledProcessError as e: + print(f"ERROR: Failed to check SYS_FPGA RDID: rc={e.returncode}") + last = text.splitlines() + if last[-1].strip() != "RDID: c2 20 18": + print("FPGA RDID check failed!") + return False + self.FPGA_UPDATE_COMMAND[2] = image_name + try: + subprocess.run(self.FPGA_UPDATE_COMMAND, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + print(f"ERROR: Failed to upgrade SYS_FPGA: rc={e.returncode}") + return False + print("\nSYS_FPGA firmware update has ended\n") + print("!!!The system will power cycle in 10 sec!!!") + time.sleep(7) + self._power_cycle() + + else: + # check whether the cpld upgrade tool exists + if not os.path.isfile('/tmp/cpldupd'): + print("ERROR: the cpld upgrade tool /tmp/cpldupd doesn't exist ") + return False + val = [" ", " ", "0x4", "0x2", "0x1", "0x10", "0x8"] + write_sysfs_file("/sys/bus/i2c/devices/1-0060/hitless", val[self.index]) + self.CPLD_UPDATE_COMMAND[2] = self.dev_name + self.CPLD_UPDATE_COMMAND[3] = image_name + try: + subprocess.run(self.CPLD_UPDATE_COMMAND, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + print(f"ERROR: Failed to upgrade {self.name}: rc={e.returncode}") + return False + self.CPLD_UPDATE_COMMAND[3] = self.tfr_name + try: + subprocess.run(self.CPLD_UPDATE_COMMAND, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + print(f"ERROR: Failed to upgrade {self.name}: rc={e.returncode}") + return False + write_sysfs_file("/sys/bus/i2c/devices/1-0060/hitless", "0x0") + print(f"\n{self.name} firmware update has ended\n") + + return True + + def update_firmware(self, image_path): + """ + Updates firmware of the component + + This API performs firmware update: it assumes firmware installation and loading in a single call. + In case platform component requires some extra steps (apart from calling Low Level Utility) + to load the installed firmware (e.g, reboot, power cycle, etc.) - this will be done automatically by API + + Args: + image_path: A string, path to firmware image + + Raises: + RuntimeError: update failed + """ + return False + + def get_available_firmware_version(self, image_path): + """ + Retrieves the available firmware version of the component + + Note: the firmware version will be read from image + + Args: + image_path: A string, path to firmware image + + Returns: + A string containing the available firmware version of the component + """ + return "N/A" + + def _power_cycle(self): + os.system('sync') + os.system('sync') + time.sleep(3) + for i in range(4): + file_path = f"/sys/bus/i2c/devices/{i+94}-00{hex(0x58+i)[2:]}/psu_rst" + if os.path.exists(file_path): + write_sysfs_file(file_path, "Reset\n") \ No newline at end of file diff --git a/ixr7220h6-64/sonic_platform/eeprom.py b/ixr7220h6-64/sonic_platform/eeprom.py new file mode 100755 index 0000000..17aa291 --- /dev/null +++ b/ixr7220h6-64/sonic_platform/eeprom.py @@ -0,0 +1,220 @@ +""" + Nokia IXR7220 H6-64 + + Module contains platform specific implementation of SONiC Platform + Base API and provides the EEPROMs' information. + + The different EEPROMs available are as follows: + - System EEPROM : Contains Serial number, Service tag, Base MA + address, etc. in ONIE TlvInfo EEPROM format. +""" + +try: + import os + from sonic_platform_base.sonic_eeprom.eeprom_tlvinfo import TlvInfoDecoder + from sonic_py_common import logger +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +I2C_FAN_EEPROM = ["112-0050", "120-0050", + "113-0050", "121-0050", + "114-0050", "122-0050", + "115-0050", "123-0050"] + +sonic_logger = logger.Logger('eeprom') + +class Eeprom(TlvInfoDecoder): + """Nokia platform-specific EEPROM class""" + + I2C_DIR = "/sys/bus/i2c/devices/" + def __init__(self, is_psu, psu_index, is_fan, drawer_index): + self.is_psu_eeprom = is_psu + self.is_fan_eeprom = is_fan + self.is_sys_eeprom = not (is_psu | is_fan) + self.service_tag = 'NA' + self.part_number = 'NA' + + if self.is_sys_eeprom: + self.start_offset = 0 + self.eeprom_path = self.I2C_DIR + "99-0056/eeprom" + # System EEPROM is in ONIE TlvInfo EEPROM format + super(Eeprom, self).__init__(self.eeprom_path, self.start_offset, '', True) + self.base_mac = '' + self.serial_number = '' + self.part_number = '' + self.model_str = '' + self.service_tag = '' + self.manuf_date = 'NA' + elif self.is_fan_eeprom: + self.start_offset = 0 + self.eeprom_path = self.I2C_DIR + f"{I2C_FAN_EEPROM[drawer_index]}/eeprom" + # Fan EEPROM is in ONIE TlvInfo EEPROM format + super(Eeprom, self).__init__(self.eeprom_path, self.start_offset, '', True) + self.serial_number = '' + self.part_number = '' + self.model_str = '' + self.service_tag = '' + self.manuf_date = '' + self.base_mac = 'N/A' + else: + self.serial_number = 'N/A' + self.part_number = 'N/A' + self.model_str = 'N/A' + self.service_tag = 'N/A' + self.manuf_date = 'N/A' + + def _load_system_eeprom(self): + """ + Reads the system EEPROM and retrieves the values corresponding + to the codes defined as per ONIE TlvInfo EEPROM format and fills + them in a dictionary. + """ + try: + # Read System EEPROM as per ONIE TlvInfo EEPROM format. + self.eeprom_data = self.read_eeprom() + except Exception as e: + sonic_logger.log_warning("Unable to read system eeprom") + self.base_mac = 'NA' + self.serial_number = 'NA' + self.part_number = 'NA' + self.model_str = 'NA' + self.service_tag = 'NA' + self.manuf_date = 'NA' + self.eeprom_tlv_dict = dict() + else: + eeprom = self.eeprom_data + self.eeprom_tlv_dict = dict() + + if not self.is_valid_tlvinfo_header(eeprom): + sonic_logger.log_warning("Invalid system eeprom TLV header") + self.base_mac = 'NA' + self.serial_number = 'NA' + self.part_number = 'NA' + self.model_str = 'NA' + self.service_tag = 'NA' + self.manuf_date = 'NA' + return + + total_length = (eeprom[9] << 8) | eeprom[10] + tlv_index = self._TLV_INFO_HDR_LEN + tlv_end = self._TLV_INFO_HDR_LEN + total_length + + while (tlv_index + 2) < len(eeprom) and tlv_index < tlv_end: + if not self.is_valid_tlv(eeprom[tlv_index:]): + break + + tlv = eeprom[tlv_index:tlv_index + 2 + + eeprom[tlv_index + 1]] + code = "0x%02X" % (tlv[0]) + + name, value = self.decoder(None, tlv) + + self.eeprom_tlv_dict[code] = value + if eeprom[tlv_index] == self._TLV_CODE_CRC_32: + break + + tlv_index += eeprom[tlv_index+1] + 2 + + self.base_mac = self.eeprom_tlv_dict.get( + "0x%X" % (self._TLV_CODE_MAC_BASE), 'NA') + self.serial_number = self.eeprom_tlv_dict.get( + "0x%X" % (self._TLV_CODE_SERIAL_NUMBER), 'NA') + self.part_number = self.eeprom_tlv_dict.get( + "0x%X" % (self._TLV_CODE_PART_NUMBER), 'NA') + self.model_str = self.eeprom_tlv_dict.get( + "0x%X" % (self._TLV_CODE_PRODUCT_NAME), 'NA') + self.service_tag = self.eeprom_tlv_dict.get( + "0x%X" % (self._TLV_CODE_SERVICE_TAG), 'NA') + self.manuf_date = self.eeprom_tlv_dict.get( + "0x%X" % (self._TLV_CODE_MANUF_DATE), 'NA') + + + def _get_eeprom_field(self, field_name): + """ + For a field name specified in the EEPROM format, returns the + presence of the field and the value for the same. + """ + field_start = 0 + for field in self.format: + field_end = field_start + field[2] + if field[0] == field_name: + return (True, self.eeprom_data[field_start:field_end]) + field_start = field_end + + return (False, None) + + def serial_number_str(self): + """ + Returns the serial number. + """ + if not self.serial_number: + self._load_system_eeprom() + + return self.serial_number + + def part_number_str(self): + """ + Returns the part number. + """ + if not self.part_number: + self._load_system_eeprom() + + return self.part_number + + def airflow_fan_type(self): + """ + Returns the airflow fan type. + """ + if self.is_psu_eeprom: + return int(self.psu_type.encode('hex'), 16) + if self.is_fan_eeprom: + return int(self.fan_type.encode('hex'), 16) + return None + + # System EEPROM specific methods + def base_mac_addr(self): + """ + Returns the base MAC address found in the system EEPROM. + """ + if not self.base_mac: + self._load_system_eeprom() + + return self.base_mac + + def modelstr(self): + """ + Returns the Model name. + """ + if not self.model_str: + self._load_system_eeprom() + + return self.model_str + + def service_tag_str(self): + """ + Returns the servicetag number. + """ + if not self.service_tag: + self._load_system_eeprom() + + return self.service_tag + + def manuf_date_str(self): + """ + Returns the servicetag number. + """ + if not self.manuf_date: + self._load_system_eeprom() + + return self.manuf_date + + def system_eeprom_info(self): + """ + Returns a dictionary, where keys are the type code defined in + ONIE EEPROM format and values are their corresponding values + found in the system EEPROM. + """ + if not self.eeprom_tlv_dict: + self._load_system_eeprom() + + return self.eeprom_tlv_dict diff --git a/ixr7220h6-64/sonic_platform/fan.py b/ixr7220h6-64/sonic_platform/fan.py new file mode 100755 index 0000000..15d313d --- /dev/null +++ b/ixr7220h6-64/sonic_platform/fan.py @@ -0,0 +1,262 @@ +""" + Nokia IXR7220 H6-64 + + Module contains an implementation of SONiC Platform Base API and + provides the Fans' information which are available in the platform +""" + +try: + import glob + from sonic_platform_base.fan_base import FanBase + from sonic_platform.sysfs import read_sysfs_file, write_sysfs_file + from sonic_py_common import logger +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +FANS_PER_DRAWER = 2 +MAX_FAN_F_SPEED = 20500 +MAX_FAN_R_SPEED = 21800 +FAN_TOLERANCE = 50 +WORKING_FAN_SPEED = 2000 + +HWMON_DIR = "/sys/bus/i2c/devices/{}/hwmon/hwmon*/" +I2C_DEV_LIST = ["79-0033", "80-0033"] + +FAN_INDEX_IN_DRAWER = [(1, 2), + (1, 2), + (3, 4), + (3, 4), + (5, 6), + (5, 6), + (7, 8), + (7, 8)] + +sonic_logger = logger.Logger('fan') + +class Fan(FanBase): + """Nokia platform-specific Fan class""" + + def __init__(self, fan_index, drawer_index, psu_fan=False, dependency=None): + self.is_psu_fan = psu_fan + i2c_dev = I2C_DEV_LIST[drawer_index%2] + hwmon_path = glob.glob(HWMON_DIR.format(i2c_dev)) + self.fan_led_color = ['off', 'green', 'amber', 'green_blink'] + + if not self.is_psu_fan: + # Fan is 1-based in Nokia platforms + self.index = drawer_index * FANS_PER_DRAWER + fan_index + 1 + fan_index_dir = FAN_INDEX_IN_DRAWER[drawer_index][fan_index] + self.set_fan_speed_reg = hwmon_path[0] + f"fan{fan_index_dir}_pwm" + self.get_fan_speed_reg = hwmon_path[0] + f"fan{fan_index_dir}_input" + self.get_fan_presence_reg = hwmon_path[0] + f"fan{(drawer_index//2)+1}_present" + self.fan_led_reg = hwmon_path[0] + f"fan{(drawer_index//2)+1}_led" + + if fan_index == 0: + self.max_fan_speed = MAX_FAN_F_SPEED + else: + self.max_fan_speed = MAX_FAN_R_SPEED + + else: + # this is a PSU Fan + self.index = fan_index + self.dependency = dependency + + def get_name(self): + """ + Retrieves the name of the Fan + + Returns: + string: The name of the Fan + """ + if not self.is_psu_fan: + return f"Fan{self.index}" + else: + return f"PSU{self.index}_Fan" + + def get_presence(self): + """ + Retrieves the presence of the Fan Unit + + Returns: + bool: True if Fan is present, False if not + """ + result = read_sysfs_file(self.get_fan_presence_reg) + if result == '1': # present + return True + + return False + + def get_model(self): + """ + Retrieves the model number of the Fan + + Returns: + string: Model number of Fan. Use part number for this. + """ + return 'N/A' + + def get_serial(self): + """ + Retrieves the serial number of the Fan + + Returns: + string: Serial number of Fan + """ + #if self.get_presence(): + # result = read_sysfs_file(self.eeprom_dir + "serial_number") + # return result.strip() + return 'N/A' + + def get_part_number(self): + """ + Retrieves the part number of the Fan + + Returns: + string: Part number of Fan + """ + #if self.get_presence(): + # result = read_sysfs_file(self.eeprom_dir + "part_number") + # return result.strip() + return 'N/A' + + def get_service_tag(self): + """ + Retrieves the service tag of the Fan + + Returns: + string: Service Tag of Fan + """ + return 'N/A' + + def get_status(self): + """ + Retrieves the operational status of the Fan + + Returns: + bool: True if Fan is operating properly, False if not + """ + status = False + + fan_speed = read_sysfs_file(self.get_fan_speed_reg) + if (fan_speed != 'ERR'): + if (int(fan_speed) > WORKING_FAN_SPEED): + status = True + + return status + + def get_direction(self): + """ + Retrieves the fan airflow direction + Possible fan directions (relative to port-side of device) + Returns: + A string, either FAN_DIRECTION_INTAKE or + FAN_DIRECTION_EXHAUST depending on fan direction + """ + return self.FAN_DIRECTION_INTAKE + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device + Returns: + integer: The 1-based relative physical position in parent device + """ + return self.index + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True + + def get_speed(self): + """ + Retrieves the speed of a Front FAN in the tray in revolutions per + minute defined by 1-based index + :param index: An integer, 1-based index of the FAN to query speed + :return: integer, denoting front FAN speed + """ + speed = 0 + + fan_speed = read_sysfs_file(self.get_fan_speed_reg) + if (fan_speed != 'ERR'): + speed_in_rpm = int(fan_speed) + else: + speed_in_rpm = 0 + + speed = round(100*speed_in_rpm/self.max_fan_speed) + + return min(speed, 100) + + def get_speed_tolerance(self): + """ + Retrieves the speed tolerance of the fan + + Returns: + An integer, the percentage of variance from target speed + which is considered tolerable + """ + return FAN_TOLERANCE + + def set_speed(self, speed): + """ + Set fan speed to expected value + Args: + speed: An integer, the percentage of full fan speed to set + fan to, in the range 0 (off) to 100 (full speed) + Returns: + bool: True if set success, False if fail. + """ + if self.is_psu_fan: + return False + + if speed in range(0, 101): + rv = write_sysfs_file(self.set_fan_speed_reg, str(speed)) + if (rv != 'ERR'): + return True + else: + return False + else: + return False + + def set_status_led(self, color): + """ + Set led to expected color + Args: + color: A string representing the color with which to set the + fan module status LED + Returns: + bool: True if set success, False if fail. + """ + return False + + def get_status_led(self): + """ + Gets the state of the fan status LED + + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings. + """ + if not self.get_presence(): + return 'N/A' + + result = read_sysfs_file(self.fan_led_reg) + val = int(result) + if val < len(self.fan_led_color): + return self.fan_led_color[val] + return 'N/A' + + def get_target_speed(self): + """ + Retrieves the target (expected) speed of the fan + + Returns: + An integer, the percentage of full fan speed, in the range 0 + (off) to 100 (full speed) + """ + + fan_duty = read_sysfs_file(self.set_fan_speed_reg) + if fan_duty != 'ERR': + return int(fan_duty) + return 0 diff --git a/ixr7220h6-64/sonic_platform/fan_drawer.py b/ixr7220h6-64/sonic_platform/fan_drawer.py new file mode 100755 index 0000000..4ce3816 --- /dev/null +++ b/ixr7220h6-64/sonic_platform/fan_drawer.py @@ -0,0 +1,206 @@ +""" + Nokia IXR7220 H6-64 + + Module contains an implementation of SONiC Platform Base API and + provides the Fan Drawer status which is available in the platform +""" + +try: + from sonic_platform.sysfs import read_sysfs_file, write_sysfs_file + from sonic_platform.eeprom import Eeprom + from sonic_platform_base.fan_drawer_base import FanDrawerBase + from sonic_py_common import logger + import os + import glob +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +FANS_PER_DRAWER = 2 +HWMON_DIR = "/sys/bus/i2c/devices/{}/hwmon/hwmon*/" +I2C_DEV_LIST = ["80-0033", "79-0033"] +EEPROM_ADDR = ['112', '120', '113', '121', + '114', '122', '115', '123'] +sonic_logger = logger.Logger('fan_drawer') + +class NokiaFanDrawer(FanDrawerBase): + """ + Nokia platform-specific NokiaFanDrawer class + """ + def __init__(self, index): + super().__init__() + self._index = index + 1 + self.fan_led_color = ['off', 'green', 'amber', 'green_blink'] + + self.fan_direction_intake = "intake" + i2c_dev = I2C_DEV_LIST[self._index%2] + hwmon_path = glob.glob(HWMON_DIR.format(i2c_dev)) + self.get_fan_presence_reg = hwmon_path[0] + f"fan{(index//2)+1}_present" + self.fan_led_reg = hwmon_path[0] + f"fan{(index//2)+1}_led" + self.eeprom_inited = False + self.eeprom_dir = f"/sys/bus/i2c/devices/{EEPROM_ADDR[index]}-0050" + self.new_eeprom_cmd = f"echo 24c64 0x50 > /sys/bus/i2c/devices/i2c-{EEPROM_ADDR[index]}/new_device" + self.del_eeprom_cmd = f"echo 0x50 > /sys/bus/i2c/devices/i2c-{EEPROM_ADDR[index]}/delete_device" + + def get_index(self): + """ + Retrieves the index of the Fan Drawer + Returns: + int: the Fan Drawer's index + """ + return self._index + + def get_presence(self): + """ + Retrieves the presence of the Fan Drawer + Returns: + bool: return True if the Fan Drawer is present + """ + result = read_sysfs_file(self.get_fan_presence_reg) + if result == '1': # present + if not self.eeprom_inited: + if not os.path.exists(self.eeprom_dir): + os.system(self.new_eeprom_cmd) + self.eeprom = Eeprom(False, 0, True, self._index-1) + self.eeprom_inited = True + return True + + if os.path.exists(self.eeprom_dir): + os.system(self.del_eeprom_cmd) + self.eeprom_inited = False + return False + + def get_model(self): + """ + Retrieves the model number of the Fan Drawer + Returns: + string: Part number of Fan Drawer + """ + if not self.get_presence(): + return 'N/A' + return self.eeprom.modelstr() + + def get_serial(self): + """ + Retrieves the serial number of the Fan Drawer + Returns: + string: Serial number of Fan + """ + if not self.get_presence(): + return 'N/A' + return self.eeprom.serial_number_str() + + def get_part_number(self): + """ + Retrieves the part number of the Fan Drawer + + Returns: + string: Part number of Fan + """ + if not self.get_presence(): + return 'N/A' + return self.eeprom.part_number_str() + + def get_service_tag(self): + """ + Retrieves the servicetag number of the Fan Drawer + + Returns: + string: servicetag number of Fan + """ + if not self.get_presence(): + return 'N/A' + return self.eeprom.service_tag_str() + + def get_manuf_date(self): + """ + Retrieves the servicetag number of the Fan Drawer + + Returns: + string: servicetag number of Fan + """ + if not self.get_presence(): + return 'N/A' + return self.eeprom.manuf_date_str() + + def get_status(self): + """ + Retrieves the operational status of the Fan Drawer + Returns: + bool: True if Fan is operating properly, False if not + """ + good_fan = 0 + if not self.get_presence(): + return False + for fan in self._fan_list: + if fan.get_status(): + good_fan = good_fan + 1 + + if good_fan == FANS_PER_DRAWER: + return True + return False + + def get_direction(self): + """ + Retrieves the direction of the Fan Drawer + Returns: + string: direction string + """ + return self.fan_direction_intake + + def set_status_led(self, color): + """ + Sets the state of the fan drawer status LED + + Args: + color: A string representing the color with which to set the + fan drawer status LED + + Returns: + bool: True if status LED state is set successfully, False if not + """ + return False + + def get_status_led(self): + """ + Gets the state of the fan drawer LED + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings + """ + if not self.get_presence(): + return 'N/A' + + result = read_sysfs_file(self.fan_led_reg) + val = int(result) + if val < len(self.fan_led_color): + return self.fan_led_color[val] + return 'N/A' + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device + Returns: + integer: The 1-based relative physical position in parent device + """ + return self._index + +class RealDrawer(NokiaFanDrawer): + """ + For Nokia platforms with fan drawer(s) + """ + def __init__(self, index): + super(RealDrawer, self).__init__(index) + self._name = f'drawer{self._index}' + + def get_name(self): + """ + return module name + """ + return self._name diff --git a/ixr7220h6-64/sonic_platform/platform.py b/ixr7220h6-64/sonic_platform/platform.py new file mode 100755 index 0000000..6af8751 --- /dev/null +++ b/ixr7220h6-64/sonic_platform/platform.py @@ -0,0 +1,19 @@ +""" + Module contains an implementation of SONiC Platform Base API and + provides the platform information +""" + +try: + from sonic_platform_base.platform_base import PlatformBase + from sonic_platform.chassis import Chassis +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +class Platform(PlatformBase): + """ + Nokia platform-specific class + """ + + def __init__(self): + PlatformBase.__init__(self) + self._chassis = Chassis() diff --git a/ixr7220h6-64/sonic_platform/psu.py b/ixr7220h6-64/sonic_platform/psu.py new file mode 100755 index 0000000..a8d1791 --- /dev/null +++ b/ixr7220h6-64/sonic_platform/psu.py @@ -0,0 +1,305 @@ +""" + Nokia IXR7220 H6-64 + + Module contains an implementation of SONiC Platform Base API and + provides the PSUs' information which are available in the platform +""" + +try: + from sonic_platform.sysfs import read_sysfs_file + from sonic_platform_base.psu_base import PsuBase + from sonic_py_common import logger + import os +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +PSU_NUM = 4 +REG_DIR = "/sys/bus/i2c/devices/73-0061/" +MAX_VOLTAGE = 264 +MIN_VOLTAGE = 180 +I2C_BUS = [94, 95, 96, 97] +PSU_ADDR = ["58", "59", "5a", "5b"] +EEPROM_ADDR = ['50', '51', '52', '53'] + +sonic_logger = logger.Logger('psu') +sonic_logger.set_min_log_priority_info() + +class Psu(PsuBase): + """Nokia platform-specific PSU class for 7220 H6-64 """ + def __init__(self, psu_index): + PsuBase.__init__(self) + self.psu_led_color = ['off', 'green', 'green_blink', 'amber', + 'amber_blink', 'green_blink_fast'] + # PSU is 1-based in Nokia platforms + self.index = psu_index + 1 + self.psu_dir = f"/sys/bus/i2c/devices/{I2C_BUS[psu_index]}-00{PSU_ADDR[psu_index]}/" + self.eeprom_dir = f"/sys/bus/i2c/devices/{I2C_BUS[psu_index]}-00{EEPROM_ADDR[psu_index]}/" + self.new_psu_cmd = f"echo pmbus_psu 0x{PSU_ADDR[psu_index]} > /sys/bus/i2c/devices/i2c-{I2C_BUS[psu_index]}/new_device" + self.new_eeprom_cmd = f"echo eeprom_fru 0x{EEPROM_ADDR[psu_index]} > /sys/bus/i2c/devices/i2c-{I2C_BUS[psu_index]}/new_device" + self.del_eeprom_cmd = f"echo 0x{EEPROM_ADDR[psu_index]} > /sys/bus/i2c/devices/i2c-{I2C_BUS[psu_index]}/delete_device" + + def _get_active_psus(self): + """ + Retrieves the operational status of the PSU and + calculates number of active PSU's + + Returns: + Integer: Number of active PSU's + """ + active_psus = 0 + for i in range(PSU_NUM): + result = read_sysfs_file(REG_DIR+f"psu{i+1}_ok") + if result == '1': + active_psus = active_psus + 1 + + return active_psus + + def get_name(self): + """ + Retrieves the name of the device + + Returns: + string: The name of the device + """ + return f"PSU{self.index}" + + def get_presence(self): + """ + Retrieves the presence of the Power Supply Unit (PSU) + + Returns: + bool: True if PSU is present, False if not + """ + result = read_sysfs_file(REG_DIR + f"psu{self.index}_pres") + if result == '0': # present + if not os.path.exists(self.psu_dir): + os.system(self.new_psu_cmd) + if not os.path.exists(self.eeprom_dir): + os.system(self.new_eeprom_cmd) + return True + # not present + if os.path.exists(self.eeprom_dir): + os.system(self.del_eeprom_cmd) + return False + + def get_model(self): + """ + Retrieves the part number of the PSU + + Returns: + string: Part number of PSU + """ + if self.get_presence(): + return read_sysfs_file(self.eeprom_dir+"product_name") + + return 'N/A' + + def get_serial(self): + """ + Retrieves the serial number of the PSU + + Returns: + string: Serial number of PSU + """ + if self.get_presence(): + return read_sysfs_file(self.eeprom_dir+"serial_number") + + return 'N/A' + + def get_revision(self): + """ + Retrieves the HW revision of the PSU + + Returns: + string: HW revision of PSU + """ + if self.get_presence(): + return read_sysfs_file(self.eeprom_dir+"product_version") + return 'N/A' + + def get_part_number(self): + """ + Retrieves the part number of the PSU + + Returns: + string: Part number of PSU + """ + if self.get_presence(): + return read_sysfs_file(self.eeprom_dir+"part_number") + + return 'N/A' + + def get_status(self): + """ + Retrieves the operational status of the PSU + + Returns: + bool: True if PSU is operating properly, False if not + """ + return '1' == read_sysfs_file(REG_DIR+f"psu{self.index}_ok") + + def get_voltage(self): + """ + Retrieves current PSU voltage output + + Returns: + A float number, the output voltage in volts, + e.g. 12.1 + """ + if self.get_presence(): + result = read_sysfs_file(self.psu_dir+"psu_v_in") + psu_voltage = (float(result))/1000 + else: + psu_voltage = 0.0 + + return psu_voltage + + def get_current(self): + """ + Retrieves present electric current supplied by PSU + + Returns: + A float number, the electric current in amperes, e.g 15.4 + """ + if self.get_presence(): + result = read_sysfs_file(self.psu_dir+"psu_i_in") + psu_current = (float(result))/1000 + else: + psu_current = 0.0 + + return psu_current + + def get_power(self): + """ + Retrieves current energy supplied by PSU + + Returns: + A float number, the power in watts, e.g. 302.6 + """ + if self.get_presence(): + result = read_sysfs_file(self.psu_dir+"psu_p_in") + psu_power = (float(result))/1000 + else: + psu_power = 0.0 + + return psu_power + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device + Returns: + integer: The 1-based relative physical position in parent device + """ + return self.index + + def get_voltage_high_threshold(self): + """ + Retrieves the high threshold PSU voltage output + + Returns: + A float number, the high threshold output voltage in volts, + e.g. 12.1 + """ + return MAX_VOLTAGE + + def get_voltage_low_threshold(self): + """ + Retrieves the low threshold PSU voltage output + + Returns: + A float number, the low threshold output voltage in volts, + e.g. 12.1 + """ + return MIN_VOLTAGE + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True + + def get_powergood_status(self): + """ + Retrieves the powergood status of PSU + Returns: + A boolean, True if PSU has stablized its output voltages and + passed all its internal self-tests, False if not. + """ + return '1' == read_sysfs_file(REG_DIR+f"psu{self.index}_ok") + + def get_status_led(self): + """ + Gets the state of the PSU status LED + + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings. + """ + if not self.get_presence(): + return 'N/A' + + result = read_sysfs_file(self.psu_dir+"psu_led") + val = int(result) + if val < len(self.psu_led_color): + return self.psu_led_color[val] + return 'N/A' + + def set_status_led(self, _color): + """ + Sets the state of the PSU status LED + Args: + color: A string representing the color with which to set the + PSU status LED + Returns: + bool: True if status LED state is set successfully, False if + not + """ + return False + + def get_status_master_led(self): + """ + Gets the state of the front panel PSU status LED + + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings. + """ + result = read_sysfs_file(REG_DIR+"led_psu") + if result == '1': + return 'green' + else: + return 'amber' + + def set_status_master_led(self, _color): + """ + Sets the state of the front panel PSU status LED + + Returns: + bool: True if status LED state is set successfully, False if + not + """ + return False + + def get_mfg_date(self): + """ + Retrieves the manufacturing date in the PSU EEPROM + + Returns: + string: mfg_date of PSU + """ + if self.get_presence() and os.path.exists(self.eeprom_dir+"mfg_date"): + return read_sysfs_file(self.eeprom_dir+"mfg_date") + + return 'N/A' + + def get_fw_rev(self): + """ + Retrieves the firmware revision of the PSU + + Returns: + string: firmware revision of PSU + """ + if self.get_presence(): + return read_sysfs_file(self.psu_dir+"psu_rev") + + return 'N/A' diff --git a/ixr7220h6-64/sonic_platform/sfp.py b/ixr7220h6-64/sonic_platform/sfp.py new file mode 100755 index 0000000..9196c2c --- /dev/null +++ b/ixr7220h6-64/sonic_platform/sfp.py @@ -0,0 +1,218 @@ +""" + Name: sfp.py, version: 1.0 + + Description: Module contains the definitions of SFP related APIs + for Nokia IXR 7220 H6-64 platform. + + Copyright (c) 2024, Nokia + All rights reserved. +""" +try: + import time + import sys + from sonic_platform_base.sonic_xcvr.sfp_optoe_base import SfpOptoeBase + from sonic_py_common import logger, device_info + from sonic_platform.sysfs import read_sysfs_file, write_sysfs_file +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +PORT_NUM = 64 + +SWPLD2_DIR = "/sys/bus/i2c/devices/89-0064/" +SWPLD3_DIR = "/sys/bus/i2c/devices/90-0065/" + +SYSLOG_IDENTIFIER = "sfp" +sonic_logger = logger.Logger(SYSLOG_IDENTIFIER) +sonic_logger.set_min_log_priority_info() + +class Sfp(SfpOptoeBase): + """ + Nokia IXR-7220 H6-64 Platform-specific Sfp refactor class + """ + instances = [] + + port_to_i2c_mapping = 0 + + def __init__(self, index, sfp_type, eeprom_path, port_i2c_map): + SfpOptoeBase.__init__(self) + + self.index = index + self.port_num = index + self.sfp_type = sfp_type + + self.eeprom_path = eeprom_path + self.port_to_i2c_mapping = port_i2c_map + if index <= PORT_NUM: + self.name = sfp_type + '_' + str(index) + self.port_name = sfp_type + '_' + str(index-1) + else: + self.name = sfp_type + '_' + str(index-PORT_NUM) + self.port_name = sfp_type + '_' + str(index-PORT_NUM-1) + self.port_to_eeprom_mapping = {} + self.port_to_eeprom_mapping[index] = eeprom_path + if (self.index >= 17 and self.index <= 32) or (self.index >= 49 and self.index <= 64): + self.swpld_path = SWPLD3_DIR + else: + self.swpld_path = SWPLD2_DIR + + self._version_info = device_info.get_sonic_version_info() + + Sfp.instances.append(self) + + def get_eeprom_path(self): + """ + Retrieves the eeprom path + Returns: + string: eeprom path + """ + return self.eeprom_path + + def get_presence(self): + """ + Retrieves the presence + Returns: + bool: True if is present, False if not + """ + sfpstatus = read_sysfs_file(self.swpld_path+f"port_{self.index}_prs") + + if sfpstatus == '0': + return True + + return False + + def get_name(self): + """ + Retrieves the name of the device + Returns: + string: The name of the device + """ + return self.name + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent device or + -1 if cannot determine the position + """ + return -1 + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True + + def _get_error_code(self): + """ + Get error code of the SFP module + + Returns: + The error code + """ + return NotImplementedError + + def get_error_description(self): + """ + Get error description + + Args: + error_code: The error code returned by _get_error_code + + Returns: + The error description + """ + if not self.get_presence(): + error_description = self.SFP_STATUS_UNPLUGGED + else: + error_description = self.SFP_STATUS_OK + + return error_description + + def get_reset_status(self): + """ + Retrieves the reset status of SFP + Returns: + A Boolean, True if reset enabled, False if disabled + """ + if self.index <= PORT_NUM: + result = read_sysfs_file(self.swpld_path+f"port_{self.index}_rst") + if result == '0': + return True + return False + return False + + def get_status(self): + """ + Retrieves the operational status of the device + """ + status = False + reset = self.get_reset_status() + if self.get_presence(): + if not reset: + status = True + + return status + + def reset(self): + """ + Reset SFP. + Returns: + A boolean, True if successful, False if not + """ + if not self.get_presence(): + sys.stderr.write(f"Error: Port {self.index} not inserted, could not reset it.\n\n") + return False + sonic_logger.log_info(f"Reseting port #{self.index}.") + + result1 = 'ERR' + result2 = 'ERR' + result1 = write_sysfs_file(self.swpld_path+f"port_{self.index}_lpmod", '0') + result2 = write_sysfs_file(self.swpld_path+f"port_{self.index}_rst", '0') + time.sleep(0.5) + result2 = write_sysfs_file(self.swpld_path+f"port_{self.index}_rst", '1') + + if result1 != 'ERR' and result2 != 'ERR': + return True + + return False + + def set_lpmode(self, lpmode): + """ + Sets the lpmode (low power mode) of SFP + Args: + lpmode: A Boolean, True to enable lpmode, False to disable it + Note : + Returns: + A boolean, True if lpmode is set successfully, False if not + """ + result = 'ERR' + + if self.index <= PORT_NUM: + if lpmode: + result = write_sysfs_file(self.swpld_path+f"port_{self.index}_lpmod", '0') + else: + result = write_sysfs_file(self.swpld_path+f"port_{self.index}_lpmod", '1') + + if result != 'ERR': + return True + + return False + + def get_lpmode(self): + """ + Retrieves the lpmode (low power mode) status of this SFP + Returns: + A Boolean, True if lpmode is enabled, False if disabled + """ + result = 'ERR' + + if self.index <= PORT_NUM: + result = read_sysfs_file(self.swpld_path+f"port_{self.index}_lpmod") + + if result == '0': + return True + + return False diff --git a/ixr7220h6-64/sonic_platform/sfp_event.py b/ixr7220h6-64/sonic_platform/sfp_event.py new file mode 100755 index 0000000..9dd901d --- /dev/null +++ b/ixr7220h6-64/sonic_platform/sfp_event.py @@ -0,0 +1,124 @@ +"""" + listen for the SFP change event and return to chassis. +""" + +try: + import time + from sonic_py_common import logger + from sonic_platform.sysfs import read_sysfs_file, write_sysfs_file +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +# system level event/error +EVENT_ON_ALL_SFP = '-1' +SYSTEM_NOT_READY = 'system_not_ready' +SYSTEM_READY = 'system_become_ready' +SYSTEM_FAIL = 'system_fail' + +# SFP PORT numbers +PORT_START = 1 +PORT_END = 66 + +SWPLD2_DIR = "/sys/bus/i2c/devices/89-0064/" +SWPLD3_DIR = "/sys/bus/i2c/devices/90-0065/" + +SYSLOG_IDENTIFIER = "sfp_event" +sonic_logger = logger.Logger(SYSLOG_IDENTIFIER) + +class SfpEvent: + ''' Listen to plugin/plugout cable events ''' + + def __init__(self): + self.handle = None + self.modprs_list = [] + + def initialize(self): + # Get Transceiver status + time.sleep(5) + self.modprs_list = self._get_transceiver_status() + if self.modprs_list[PORT_END-2]: + write_sysfs_file(SWPLD2_DIR+"port_65_tx_en", '0') + if self.modprs_list[PORT_END-1]: + write_sysfs_file(SWPLD2_DIR+"port_66_tx_en", '0') + + def deinitialize(self): + if self.handle is None: + return + + def _get_transceiver_status(self): + port_status = [] + reg_value = [] + reg_value.append(read_sysfs_file(SWPLD2_DIR + "modprs_reg1")) + reg_value.append(read_sysfs_file(SWPLD2_DIR + "modprs_reg2")) + reg_value.append(read_sysfs_file(SWPLD3_DIR + "modprs_reg1")) + reg_value.append(read_sysfs_file(SWPLD3_DIR + "modprs_reg2")) + reg_value.append(read_sysfs_file(SWPLD2_DIR + "modprs_reg3")) + reg_value.append(read_sysfs_file(SWPLD2_DIR + "modprs_reg4")) + reg_value.append(read_sysfs_file(SWPLD3_DIR + "modprs_reg3")) + reg_value.append(read_sysfs_file(SWPLD3_DIR + "modprs_reg4")) + for i in range(8): + bin_str = f'{int(reg_value[i], 16):08b}' + bin_str = bin_str[::-1] + bool_list = [not bool(int(bit)) for bit in bin_str] + port_status.extend(bool_list) + + for port in range (PORT_END-1, PORT_END+1): + status = read_sysfs_file(SWPLD2_DIR+f"port_{port}_prs") + if status == '0': + port_status.append(True) + else: + port_status.append(False) + + return port_status + + def check_sfp_status(self, port_change, timeout): + """ + check_sfp_status called from get_change_event, this will return correct + status of all SFP ports if there is a change in any of them + """ + start_time = time.time() + forever = False + + if timeout == 0: + forever = True + elif timeout > 0: + timeout = timeout / float(1000) # Convert to secs + else: + return False, {} + end_time = start_time + timeout + + if start_time > end_time: + return False, {} # Time wrap or possibly incorrect timeout + + while (timeout >= 0): + # Check for OIR events and return updated port_change + port_status = self._get_transceiver_status() + if port_status != self.modprs_list: + for i in range(PORT_END): + if port_status[i] != self.modprs_list[i]: + if port_status[i] == True: + port_change[i+1] = '1' + else: + port_change[i+1] = '0' + + if (i == PORT_END -2) or (i == PORT_END -1): + if port_status[i]: + write_sysfs_file(SWPLD2_DIR+f"port_{i+1}_tx_en", '0') + else: + write_sysfs_file(SWPLD2_DIR+f"port_{i+1}_tx_en", '1') + + # Update reg value + self.modprs_list = port_status + return True, port_change + + if forever: + time.sleep(1) + else: + timeout = end_time - time.time() + if timeout >= 1: + time.sleep(1) # We poll at 1 second granularity + else: + if timeout > 0: + time.sleep(timeout) + return True, {} + return False, {} diff --git a/ixr7220h6-64/sonic_platform/sysfs.py b/ixr7220h6-64/sonic_platform/sysfs.py new file mode 100755 index 0000000..b8338be --- /dev/null +++ b/ixr7220h6-64/sonic_platform/sysfs.py @@ -0,0 +1,48 @@ +""" + Nokia IXR7220 sysfs functions + + Module contains an implementation of SONiC Platform Base API and + provides the PSUs' information which are available in the platform +""" + +def read_sysfs_file(sysfs_file): + """ + On successful read, returns the value read from given + reg_name and on failure returns ERR + """ + rv = 'ERR' + + try: + with open(sysfs_file, 'r', encoding='utf-8') as fd: + rv = fd.read() + fd.close() + except FileNotFoundError: + print(f"Error: {sysfs_file} doesn't exist.") + except PermissionError: + print(f"Error: Permission denied when reading file {sysfs_file}.") + except IOError: + print(f"IOError: An error occurred while reading file {sysfs_file}.") + if rv != 'ERR': + rv = rv.rstrip('\r\n') + rv = rv.lstrip(" ") + return rv + +def write_sysfs_file(sysfs_file, value): + """ + On successful write, the value read will be written on + reg_name and on failure returns ERR + """ + rv = 'ERR' + + try: + with open(sysfs_file, 'w', encoding='utf-8') as fd: + rv = fd.write(value) + fd.close() + except FileNotFoundError: + print(f"Error: {sysfs_file} doesn't exist.") + except PermissionError: + print(f"Error: Permission denied when writing file {sysfs_file}.") + except IOError: + print(f"IOError: An error occurred while writing file {sysfs_file}.") + + return rv diff --git a/ixr7220h6-64/sonic_platform/test/README b/ixr7220h6-64/sonic_platform/test/README new file mode 100755 index 0000000..3efc8fa --- /dev/null +++ b/ixr7220h6-64/sonic_platform/test/README @@ -0,0 +1 @@ +This directory contains unit tests of the Platform API 2.0 diff --git a/ixr7220h6-64/sonic_platform/test/test-chassis.py b/ixr7220h6-64/sonic_platform/test/test-chassis.py new file mode 100755 index 0000000..7d065dd --- /dev/null +++ b/ixr7220h6-64/sonic_platform/test/test-chassis.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +try: + import sonic_platform.platform + import sonic_platform.chassis + import unittest +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + + +class Test1(unittest.TestCase): + def test_1(self): + print("-----------------") + print("Chassis Unit Test") + print("-----------------") + + chassis = sonic_platform.platform.Platform().get_chassis() + print(" Chassis name: {}".format(chassis.get_name())) + + print(" Chassis presence: {}".format(chassis.get_presence())) + + print(" Chassis serial: {}".format(chassis.get_serial())) + + print(" Chassis revision: {}".format(chassis.get_revision())) + + print(" Chassis status: {}".format(chassis.get_status())) + + print(" Chassis base_mac: {}".format(chassis.get_base_mac())) + + print(" Chassis reboot cause: {}\n".format(chassis.get_reboot_cause())) + + print(" Chassis watchdog: {}".format(chassis.get_watchdog())) + + print(" Chassis num_components: {}".format(chassis.get_num_components())) + + print(" Chassis all_components: {}\n".format(chassis.get_all_components())) + + print(" Chassis num_modules: {}".format(chassis.get_num_modules())) + + print(" Chassis all_modules: {}\n".format(chassis.get_all_modules())) + + print(" Chassis num_fans: {}".format(chassis.get_num_fans())) + + print(" Chassis all_fans: {}\n".format(chassis.get_all_fans())) + + print(" Chassis num_psus: {}".format(chassis.get_num_psus())) + + print(" Chassis all_psus: {}\n".format(chassis.get_all_psus())) + + print(" Chassis num_thermals: {}".format(chassis.get_num_thermals())) + + print(" Chassis all_thermals: {}\n".format(chassis.get_all_thermals())) + + print(" Chassis num_sfps: {}".format(chassis.get_num_sfps())) + + print(" Chassis all_sfps: {}\n".format(chassis.get_all_sfps())) + + print(" Chassis eeprom: {}".format(chassis.get_eeprom())) + + +if __name__ == '__main__': + unittest.main() diff --git a/ixr7220h6-64/sonic_platform/test/test-component.py b/ixr7220h6-64/sonic_platform/test/test-component.py new file mode 100755 index 0000000..ee29512 --- /dev/null +++ b/ixr7220h6-64/sonic_platform/test/test-component.py @@ -0,0 +1,19 @@ +#!/usr/bin/python + +from sonic_platform.chassis import Chassis + + +def main(): + print("---------------------------") + print("Chassis Component Unit Test") + print("---------------------------") + + chassis = Chassis() + + for component in chassis.get_all_components(): + print(" Name: {}".format(component.get_name())) + print(" Description: {}".format(component.get_description())) + print(" FW version: {}\n".format(component.get_firmware_version())) + +if __name__ == '__main__': + main() diff --git a/ixr7220h6-64/sonic_platform/test/test-eeprom.py b/ixr7220h6-64/sonic_platform/test/test-eeprom.py new file mode 100755 index 0000000..d6f214c --- /dev/null +++ b/ixr7220h6-64/sonic_platform/test/test-eeprom.py @@ -0,0 +1,24 @@ +#!/usr/bin/python + +from sonic_platform.chassis import Chassis + + +def main(): + print("------------------------") + print("Chassis eeprom Unit Test") + print("------------------------") + + chassis = Chassis() + + eeprom = chassis.get_eeprom() + + print(" Model: {}, Service Tag: {}".format(eeprom.modelstr(), + eeprom.service_tag_str())) + print(" Part#: {}, Serial#: {}".format(eeprom.part_number_str(), + eeprom.serial_number_str())) + print(" Base MAC: {}".format(eeprom.base_mac_addr())) + + + +if __name__ == '__main__': + main() diff --git a/ixr7220h6-64/sonic_platform/test/test-fan-drawer.py b/ixr7220h6-64/sonic_platform/test/test-fan-drawer.py new file mode 100755 index 0000000..20860e4 --- /dev/null +++ b/ixr7220h6-64/sonic_platform/test/test-fan-drawer.py @@ -0,0 +1,29 @@ +#!/usr/bin/python +import unittest +from sonic_platform.chassis import Chassis + +class Test1(unittest.TestCase): + def test_1(self): + print("---------------------------") + print("Chassis Fan Drawer Unit Test") + print("---------------------------") + + chassis = Chassis() + + for fan_drawer in chassis.get_all_fan_drawers(): + if not fan_drawer.get_presence(): + print(" Name: {} not present".format(fan_drawer.get_name())) + else: + print(" Name:", fan_drawer.get_name()) + print(" Presence: {}, Status: {}, LED: {}".format(fan_drawer.get_presence(), + fan_drawer.get_status(), + fan_drawer.get_status_led())) + print(" Serial#: {}".format(fan_drawer.get_serial())) + print(" Part#: {}".format(fan_drawer.get_part_number())) + print(" Direction: {}\n".format(fan_drawer.get_direction())) + print(" Replaceable: {}, Index: {}\n".format(fan_drawer.is_replaceable(), fan_drawer.get_position_in_parent())) + + + +if __name__ == '__main__': + unittest.main() diff --git a/ixr7220h6-64/sonic_platform/test/test-fan.py b/ixr7220h6-64/sonic_platform/test/test-fan.py new file mode 100755 index 0000000..ae647aa --- /dev/null +++ b/ixr7220h6-64/sonic_platform/test/test-fan.py @@ -0,0 +1,34 @@ +#!/usr/bin/python + +from sonic_platform.chassis import Chassis + +def main(): + print("---------------------") + print("Chassis Fan Unit Test") + print("---------------------") + + chassis = Chassis() + + for fandraw in chassis.get_all_fan_drawers(): + if not fandraw.get_presence(): + print("\n Name: {} not present".format(fandraw.get_name())) + else: + print(" Name:", fandraw.get_name()) + print(" Presence: {}, Status: {}, Direction: {}, LED: {}".format(fandraw.get_presence(), + fandraw.get_status(), + fandraw.fan_direction_intake, + fandraw.get_status_led())) + print(" Part_number: {}, Serial: {}".format(fandraw.get_part_number(), + fandraw.get_serial())) + print(" Service Tag: {}, mfg_date: {}, ".format(fandraw.get_service_tag(), + fandraw.get_manuf_date())) + for fan in fandraw.get_all_fans(): + fan_status = fan.get_status() + print(" {} Status: {}, Target Speed: {}%, Speed: {}%".format(fan.get_name(), + fan_status, + str(fan.get_target_speed()), + str(fan.get_speed()))) + return + +if __name__ == '__main__': + main() diff --git a/ixr7220h6-64/sonic_platform/test/test-psu.py b/ixr7220h6-64/sonic_platform/test/test-psu.py new file mode 100755 index 0000000..2db5878 --- /dev/null +++ b/ixr7220h6-64/sonic_platform/test/test-psu.py @@ -0,0 +1,44 @@ +#!/usr/bin/python + +from sonic_platform.chassis import Chassis + + +def main(): + print("---------------------") + print("Chassis PSU Unit Test") + print("---------------------") + + chassis = Chassis() + + for psu in chassis.get_all_psus(): + if psu.index == 1: + print(" Active num:", psu._get_active_psus()) + print(" Master LED: {}\n".format(psu.get_status_master_led())) + + if not psu.get_presence(): + print(" Name: {} not present\n".format(psu.get_name())) + else: + print(" Name:", psu.get_name()) + print(" Presence: {}, Status: {}, LED: {}".format(psu.get_presence(), + psu.get_status(), + psu.get_status_led())) + print(" Model: {}, Serial#: {}, Part#: {}".format(psu.get_model(), + psu.get_serial(), + psu.get_part_number())) + print(" Mfg_date: {}, FW_Rev: {}".format(psu.get_mfg_date(), + psu.get_fw_rev())) + try: + current = psu.get_current() + except NotImplementedError: + current = "NA" + try: + power = psu.get_power() + except NotImplementedError: + power = "NA" + + print(" Voltage: {}, Current: {}, Power: {}\n".format(psu.get_voltage(), + current, + power)) + +if __name__ == '__main__': + main() diff --git a/ixr7220h6-64/sonic_platform/test/test-sfp.py b/ixr7220h6-64/sonic_platform/test/test-sfp.py new file mode 100755 index 0000000..f76c2e7 --- /dev/null +++ b/ixr7220h6-64/sonic_platform/test/test-sfp.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python + +try: + from sonic_platform.chassis import Chassis +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +def main(): + print("---------------------") + print("Chassis SFP Unit Test") + print("---------------------") + + chassis = Chassis() + + PORT_START = 1 + PORT_END = 66 + + for physical_port in range(PORT_START, PORT_START + PORT_END): + + print(" ") + print(" SFP transceiver tests PORT = ", physical_port) + name = chassis.get_sfp(physical_port).get_name() + print(" SFP transceiver tests NAME = ", name) + + presence = chassis.get_sfp(physical_port).get_presence() + print("TEST 1 - sfp presence [ True ] ", physical_port, presence) + + status = chassis.get_sfp(physical_port).get_reset_status() + print("TEST 2 - sfp reset status [ False ] ", physical_port, status) + + txdisable = chassis.get_sfp(physical_port).get_tx_disable() + print("TEST 3 - sfp tx_disable [ False ] ", physical_port, txdisable) + + rxlos = chassis.get_sfp(physical_port).get_rx_los() + print("TEST 4 - sfp status rxlos [ False ] ", physical_port, rxlos) + + txfault = chassis.get_sfp(physical_port).get_tx_fault() + print("TEST 5 - sfp status txfault [ False ] ", physical_port, txfault) + + lpmode = chassis.get_sfp(physical_port).get_lpmode() + print("TEST 6 - sfp enable lpmode [ False ] ", physical_port, lpmode) + + trans_info = chassis.get_sfp(physical_port).get_transceiver_info() + print("TEST 7 - sfp transceiver info for port:", physical_port, trans_info) + + dom_real_value = chassis.get_sfp(physical_port).get_transceiver_dom_real_value() + print("TEST 8 - sfp transceiver dom real value for port:", physical_port, dom_real_value) + + threshold = chassis.get_sfp(physical_port).get_transceiver_threshold_info() + print("TEST 9 - transceiver sfp threshold info for port:", physical_port, threshold) + +if __name__ == '__main__': + main() diff --git a/ixr7220h6-64/sonic_platform/test/test-thermal.py b/ixr7220h6-64/sonic_platform/test/test-thermal.py new file mode 100755 index 0000000..ce008de --- /dev/null +++ b/ixr7220h6-64/sonic_platform/test/test-thermal.py @@ -0,0 +1,49 @@ +#!/usr/bin/python + +from sonic_platform.chassis import Chassis + +def main(): + print("-------------------------") + print("Chassis Thermal Unit Test") + print("-------------------------") + + chassis = Chassis() + + for thermal in chassis.get_all_thermals(): + if not thermal.get_presence(): + print(" Name: {} not present".format(thermal.get_name())) + else: + print(" Name:", thermal.get_name()) + print(" Presence: {}, Status: {}".format(thermal.get_presence(), + thermal.get_status())) + print(" Model: {}, Serial#: {}".format(thermal.get_model(), + thermal.get_serial())) + print(" Temperature(C): {}".format(thermal.get_temperature())) + + try: + low_thresh = thermal.get_low_threshold() + except NotImplementedError: + low_thresh = "NA" + try: + high_thresh = thermal.get_high_threshold() + except NotImplementedError: + high_thresh = "NA" + + print(" Low Threshold(C): {}, High Threshold(C): {}".format(low_thresh, + high_thresh)) + + try: + crit_low_thresh = thermal.get_low_critical_threshold() + except NotImplementedError: + crit_low_thresh = "NA" + try: + crit_high_thresh = thermal.get_high_critical_threshold() + except NotImplementedError: + crit_high_thresh = "NA" + + print(" Crit Low Threshold(C): {}, Crit High Threshold(C): {}\n".format(crit_low_thresh, + crit_high_thresh)) + + +if __name__ == '__main__': + main() diff --git a/ixr7220h6-64/sonic_platform/test/test-watchdog.py b/ixr7220h6-64/sonic_platform/test/test-watchdog.py new file mode 100755 index 0000000..135c26f --- /dev/null +++ b/ixr7220h6-64/sonic_platform/test/test-watchdog.py @@ -0,0 +1,19 @@ +#!/usr/bin/python + +from sonic_platform.chassis import Chassis + + +def main(): + print("---------------------") + print("Chassis Watchdog Test") + print("---------------------") + + chassis = Chassis() + + watchdog = chassis.get_watchdog() + + print(" Armed: {}".format(watchdog.is_armed())) + print(" Time Left: {}".format(watchdog.get_remaining_time())) + +if __name__ == '__main__': + main() diff --git a/ixr7220h6-64/sonic_platform/thermal.py b/ixr7220h6-64/sonic_platform/thermal.py new file mode 100755 index 0000000..ffc5391 --- /dev/null +++ b/ixr7220h6-64/sonic_platform/thermal.py @@ -0,0 +1,261 @@ +""" + Nokia IXR7220-H6-64 + Module contains an implementation of SONiC Platform Base API and + provides the Thermals' information which are available in the platform +""" + +try: + import glob + from sonic_platform_base.thermal_base import ThermalBase + from sonic_py_common import logger + from swsscommon.swsscommon import SonicV2Connector + from sonic_platform.sysfs import read_sysfs_file +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +sonic_logger = logger.Logger('thermal') + +THERMAL_NUM = 17 + +class Thermal(ThermalBase): + """Nokia platform-specific Thermal class""" + + HWMON_DIR = "/sys/bus/i2c/devices/{}/hwmon/hwmon*/" + I2C_DEV_LIST = ["0-0018", "77-0049", "77-0048", "98-004d", "98-004b", + "98-004c", "98-004a", "101-0048", "98-0049", "98-0048", + "111-004d", "119-004d", "0-0021","0-0021"] + THERMAL_NAME = ["CPU Board", "CB Left", "CB Right", "SFP Board", "MB Left", + "MB Center 1", "MB MAC", "MB Center 2", "MB Right", "MB Front Right", + "FCM Upper", "FCM Lower", "CPU", "DDR", "Max Port Temp.", + "SSD", "ASIC TH6"] + THRESHHOLD = [65.0, 66.0, 68.0, 62.0, 75.0, + 105.0, 105.0, 105.0, 78.0, 65.0, + 66.0, 66.0, 95.0, 70.0, 85.0, + 70.0, 95.0] + CRITICAL_THRESHHOLD = [70.0, 70.0, 72.0, 67.0, 80.0, + 115.0, 115.0, 115.0, 80.0, 70.0, + 69.0, 69.0, 99.0, 80.0, 90.0, + 80.0, 100.0] + + def __init__(self, thermal_index, sfps): + ThermalBase.__init__(self) + self.index = thermal_index + 1 + self.is_fan_thermal = False + self.dependency = None + self._minimum = None + self._maximum = None + self.thermal_high_threshold_file = None + + self.thermal_high_crit_threshold_file = None + self.thermal_temperature_file = None + + if self.index == THERMAL_NUM - 1: #SSD + self.device_path = glob.glob("/sys/block/sda/device/hwmon/*") + if len(self.device_path) > 0: + self.thermal_temperature_file = self.device_path[0] + "/temp1_input" + elif self.index == THERMAL_NUM - 2: + self.sfps = sfps + elif self.index == THERMAL_NUM - 3: + self.thermal_temperature_file = "/sys/bus/i2c/devices/" + self.I2C_DEV_LIST[self.index - 1] + "/mem1_temperature" + elif self.index == THERMAL_NUM - 4: + self.thermal_temperature_file = "/sys/bus/i2c/devices/" + self.I2C_DEV_LIST[self.index - 1] + "/cpu_temperature" + elif self.index < THERMAL_NUM - 4: + self.device_path = glob.glob(self.HWMON_DIR.format(self.I2C_DEV_LIST[self.index - 1])) + if len(self.device_path) > 0: + self.thermal_temperature_file = self.device_path[0] + "temp1_input" + + def get_name(self): + """ + Retrieves the name of the thermal + + Returns: + string: The name of the thermal + """ + return self.THERMAL_NAME[self.index - 1] + + def get_presence(self): + """ + Retrieves the presence of the thermal + + Returns: + bool: True if thermal is present, False if not + """ + if self.dependency: + return self.dependency.get_presence() + return True + + def get_model(self): + """ + Retrieves the model number (or part number) of the Thermal + + Returns: + string: Model/part number of Thermal + """ + return 'NA' + + def get_serial(self): + """ + Retrieves the serial number of the Thermal + + Returns: + string: Serial number of Thermal + """ + return 'NA' + + def get_status(self): + """ + Retrieves the operational status of the thermal + + Returns: + A boolean value, True if thermal is operating properly, + False if not + """ + if self.dependency: + return self.dependency.get_status() + return True + + def get_temperature(self): + """ + Retrieves current temperature reading from thermal + + Returns: + A float number of current temperature in Celsius up to + nearest thousandth of one degree Celsius, e.g. 30.125 + """ + thermal_temperature = 0.0 + if self.index == THERMAL_NUM: + db = SonicV2Connector() + db.connect(db.STATE_DB) + data_dict = db.get_all(db.STATE_DB, 'ASIC_TEMPERATURE_INFO') + if data_dict: + thermal_temperature = float(data_dict['maximum_temperature']) + elif self.index == THERMAL_NUM - 1: # SSD + temp = read_sysfs_file(self.thermal_temperature_file) + if temp != 'ERR': + thermal_temperature = float(temp) / 1000 + elif self.index == THERMAL_NUM - 2: + for sfp in self.sfps: + try: + if sfp.get_presence(): + temp = sfp.get_temperature() + else: + temp = None + except: + temp = None + if (temp is not None) and (temp > thermal_temperature): + thermal_temperature = temp + elif self.thermal_temperature_file is not None: + temp = read_sysfs_file(self.thermal_temperature_file) + if temp != 'ERR': + thermal_temperature = float(temp) / 1000 + + if self._minimum is None or self._minimum > thermal_temperature: + self._minimum = thermal_temperature + if self._maximum is None or self._maximum < thermal_temperature: + self._maximum = thermal_temperature + + return float(f"{thermal_temperature:.3f}") + + def get_high_threshold(self): + """ + Retrieves the high threshold temperature of thermal + + Returns: + A float number, the high threshold temperature of thermal in + Celsius up to nearest thousandth of one degree Celsius, + e.g. 30.125 + """ + return self.THRESHHOLD[self.index-1] + + def set_high_threshold(self, _temperature): + """ + Sets the high threshold temperature of thermal + + Args : + temperature: A float number up to nearest thousandth of one + degree Celsius, e.g. 30.125 + Returns: + A boolean, True if threshold is set successfully, False if + not + """ + # Thermal threshold values are pre-defined based on HW. + return False + + def get_high_critical_threshold(self): + """ + Retrieves the high critical threshold temperature of thermal + + Returns: + A float number, the high critical threshold temperature of thermal in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + if self.index == THERMAL_NUM: + return 103.0 + return 80.0 + + def set_high_critical_threshold(self): + """ + Sets the high_critical threshold temperature of thermal + + Args : + temperature: A float number up to nearest thousandth of one + degree Celsius, e.g. 30.125 + Returns: + A boolean, True if threshold is set successfully, False if + not + """ + # Thermal threshold values are pre-defined based on HW. + return False + + def get_low_threshold(self): + """ + Retrieves the low threshold temperature of thermal + Returns: + A float number, the low threshold temperature of thermal in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + return 0.0 + + def set_low_threshold(self, _temperature): + """ + Sets the low threshold temperature of thermal + + Args : + temperature: A float number up to nearest thousandth of one + degree Celsius, e.g. 30.125 + Returns: + A boolean, True if threshold is set successfully, False if + not + """ + # Thermal threshold values are pre-defined based on HW. + return False + + def get_minimum_recorded(self): + """ + Retrieves minimum recorded temperature + """ + self.get_temperature() + return self._minimum + + def get_maximum_recorded(self): + """ + Retrieves maxmum recorded temperature + """ + self.get_temperature() + return self._maximum + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device + Returns: + integer: The 1-based relative physical position in parent device + """ + return self.index + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False diff --git a/ixr7220h6-64/sonic_platform/thermal_actions.py b/ixr7220h6-64/sonic_platform/thermal_actions.py new file mode 100755 index 0000000..e71359e --- /dev/null +++ b/ixr7220h6-64/sonic_platform/thermal_actions.py @@ -0,0 +1,199 @@ +try: + from sonic_platform_base.sonic_thermal_control.thermal_action_base import ThermalPolicyActionBase + from sonic_platform_base.sonic_thermal_control.thermal_json_object import thermal_json_object + from sonic_py_common import logger +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +sonic_logger = logger.Logger('thermal_actions') + +class SetFanSpeedAction(ThermalPolicyActionBase): + """ + Base thermal action class to set speed for fans + """ + # JSON field definition + JSON_FIELD_SPEED = 'speed' + JSON_FIELD_DEFAULT_SPEED = 'default_speed' + JSON_FIELD_THRESHOLD1_SPEED = 'threshold1_speed' + JSON_FIELD_HIGHTEMP_SPEED = 'hightemp_speed' + + def __init__(self): + """ + Constructor of SetFanSpeedAction + """ + self.default_speed = 46 + self.threshold1_speed=73 + self.hightemp_speed = 100 + self.speed = self.default_speed + + def load_from_json(self, json_obj): + """ + Construct SetFanSpeedAction via JSON. JSON example: + { + "type": "fan.all.set_speed" + "speed": "100" + } + :param json_obj: A JSON object representing a SetFanSpeedAction action. + :return: + """ + if SetFanSpeedAction.JSON_FIELD_SPEED in json_obj: + speed = float(json_obj[SetFanSpeedAction.JSON_FIELD_SPEED]) + if speed < 0 or speed > 100: + raise ValueError('SetFanSpeedAction invalid speed value {} in JSON policy file, valid value should be [0, 100]'. + format(speed)) + self.speed = float(json_obj[SetFanSpeedAction.JSON_FIELD_SPEED]) + else: + raise ValueError('SetFanSpeedAction missing mandatory field {} in JSON policy file'. + format(SetFanSpeedAction.JSON_FIELD_SPEED)) + + @classmethod + def set_all_fan_speed(cls, thermal_info_dict, speed): + from .thermal_infos import FanInfo + if FanInfo.INFO_NAME in thermal_info_dict and isinstance(thermal_info_dict[FanInfo.INFO_NAME], FanInfo): + fan_info_obj = thermal_info_dict[FanInfo.INFO_NAME] + for fan in fan_info_obj.get_presence_fans(): + fan.set_speed(int(speed)) + +@thermal_json_object('fan.all.set_speed') +class SetAllFanSpeedAction(SetFanSpeedAction): + """ + Action to set speed for all fans + """ + def execute(self, thermal_info_dict): + """ + Set speed for all fans + :param thermal_info_dict: A dictionary stores all thermal information. + :return: + """ + SetAllFanSpeedAction.set_all_fan_speed(thermal_info_dict, self.speed) + +@thermal_json_object('thermal.temp_check_and_set_all_fan_speed') +class ThermalRecoverAction(SetFanSpeedAction): + """ + Action to check thermal sensor temperature change status and set speed for all fans + """ + def load_from_json(self, json_obj): + """ + Construct ThermalRecoverAction via JSON. JSON example: + { + "type": "thermal.temp_check_and_set_all_fan_speed" + "default_speed": "46", + "threshold1_speed": "73", + "hightemp_speed": "100" + } + :param json_obj: A JSON object representing a ThermalRecoverAction action. + :return: + """ + if SetFanSpeedAction.JSON_FIELD_DEFAULT_SPEED in json_obj: + default_speed = float(json_obj[SetFanSpeedAction.JSON_FIELD_DEFAULT_SPEED]) + if default_speed < 0 or default_speed > 100: + raise ValueError('SetFanSpeedAction invalid default speed value {} in JSON policy file, valid value should be [0, 100]'. + format(default_speed)) + self.default_speed = float(json_obj[SetFanSpeedAction.JSON_FIELD_DEFAULT_SPEED]) + else: + raise ValueError('SetFanSpeedAction missing mandatory field {} in JSON policy file'. + format(SetFanSpeedAction.JSON_FIELD_DEFAULT_SPEED)) + + if SetFanSpeedAction.JSON_FIELD_THRESHOLD1_SPEED in json_obj: + threshold1_speed = float(json_obj[SetFanSpeedAction.JSON_FIELD_THRESHOLD1_SPEED]) + if threshold1_speed < 0 or threshold1_speed > 100: + raise ValueError('SetFanSpeedAction invalid default speed value {} in JSON policy file, valid value should be [0, 100]'. + format(threshold1_speed)) + self.threshold1_speed = float(json_obj[SetFanSpeedAction.JSON_FIELD_THRESHOLD1_SPEED]) + else: + raise ValueError('SetFanSpeedAction missing mandatory field {} in JSON policy file'. + format(SetFanSpeedAction.JSON_FIELD_THRESHOLD1_SPEED)) + + if SetFanSpeedAction.JSON_FIELD_HIGHTEMP_SPEED in json_obj: + hightemp_speed = float(json_obj[SetFanSpeedAction.JSON_FIELD_HIGHTEMP_SPEED]) + if hightemp_speed < 0 or hightemp_speed > 100: + raise ValueError('SetFanSpeedAction invalid hightemp speed value {} in JSON policy file, valid value should be [0, 100]'. + format(hightemp_speed)) + self.hightemp_speed = float(json_obj[SetFanSpeedAction.JSON_FIELD_HIGHTEMP_SPEED]) + else: + raise ValueError('SetFanSpeedAction missing mandatory field {} in JSON policy file'. + format(SetFanSpeedAction.JSON_FIELD_HIGHTEMP_SPEED)) + + sonic_logger.log_warning("ThermalRecoverAction: default: {}, threshold1: {}, hightemp: {}".format(self.default_speed, self.threshold1_speed, self.hightemp_speed)) + + def execute(self, thermal_info_dict): + """ + Check check thermal sensor temperature change status and set speed for all fans + :param thermal_info_dict: A dictionary stores all thermal information. + :return: + """ + from .thermal_infos import ThermalInfo + if ThermalInfo.INFO_NAME in thermal_info_dict and \ + isinstance(thermal_info_dict[ThermalInfo.INFO_NAME], ThermalInfo): + + thermal_info_obj = thermal_info_dict[ThermalInfo.INFO_NAME] + if thermal_info_obj.is_set_fan_high_temp_speed(): + ThermalRecoverAction.set_all_fan_speed(thermal_info_dict, self.hightemp_speed) + elif thermal_info_obj.is_set_fan_threshold_one_speed(): + ThermalRecoverAction.set_all_fan_speed(thermal_info_dict, self.threshold1_speed) + elif thermal_info_obj.is_set_fan_default_speed(): + ThermalRecoverAction.set_all_fan_speed(thermal_info_dict, self.default_speed) + +@thermal_json_object('switch.shutdown') +class SwitchPolicyAction(ThermalPolicyActionBase): + """ + Base class for thermal action. Once all thermal conditions in a thermal policy are matched, + all predefined thermal action will be executed. + """ + def execute(self, thermal_info_dict): + """ + Take action when thermal condition matches. For example, adjust speed of fan or shut + down the switch. + :param thermal_info_dict: A dictionary stores all thermal information. + :return: + """ + sonic_logger.log_warning("Alarm for temperature critical is detected, reboot Device") + +@thermal_json_object('thermal_control.control') +class ControlThermalAlgoAction(ThermalPolicyActionBase): + """ + Action to control the thermal control algorithm + """ + # JSON field definition + JSON_FIELD_STATUS = 'status' + + def __init__(self): + self.status = True + + def load_from_json(self, json_obj): + """ + Construct ControlThermalAlgoAction via JSON. JSON example: + { + "type": "thermal_control.control" + "status": "true" + } + :param json_obj: A JSON object representing a ControlThermalAlgoAction action. + :return: + """ + if ControlThermalAlgoAction.JSON_FIELD_STATUS in json_obj: + status_str = json_obj[ControlThermalAlgoAction.JSON_FIELD_STATUS].lower() + if status_str == 'true': + self.status = True + elif status_str == 'false': + self.status = False + else: + raise ValueError(f'Invalid {ControlThermalAlgoAction.JSON_FIELD_STATUS} field value, please specify true of false') + else: + raise ValueError('ControlThermalAlgoAction ' + f'missing mandatory field {ControlThermalAlgoAction.JSON_FIELD_STATUS} in JSON policy file') + + def execute(self, thermal_info_dict): + """ + Disable thermal control algorithm + :param thermal_info_dict: A dictionary stores all thermal information. + :return: + """ + from .thermal_infos import ChassisInfo + if ChassisInfo.INFO_NAME in thermal_info_dict: + chassis_info_obj = thermal_info_dict[ChassisInfo.INFO_NAME] + chassis = chassis_info_obj.get_chassis() + thermal_manager = chassis.get_thermal_manager() + if self.status: + thermal_manager.start_thermal_control_algorithm() + else: + thermal_manager.stop_thermal_control_algorithm() diff --git a/ixr7220h6-64/sonic_platform/thermal_conditions.py b/ixr7220h6-64/sonic_platform/thermal_conditions.py new file mode 100755 index 0000000..d19fd4f --- /dev/null +++ b/ixr7220h6-64/sonic_platform/thermal_conditions.py @@ -0,0 +1,70 @@ +try: + from sonic_platform_base.sonic_thermal_control.thermal_condition_base import ThermalPolicyConditionBase + from sonic_platform_base.sonic_thermal_control.thermal_json_object import thermal_json_object +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +class FanCondition(ThermalPolicyConditionBase): + def get_fan_info(self, thermal_info_dict): + from .thermal_infos import FanInfo + if FanInfo.INFO_NAME in thermal_info_dict and isinstance(thermal_info_dict[FanInfo.INFO_NAME], FanInfo): + return thermal_info_dict[FanInfo.INFO_NAME] + return None + +@thermal_json_object('fan.any.absence') +class AnyFanAbsenceCondition(FanCondition): + def is_match(self, thermal_info_dict): + fan_info_obj = self.get_fan_info(thermal_info_dict) + return len(fan_info_obj.get_absence_fans()) > 0 if fan_info_obj else False + +@thermal_json_object('fan.all.absence') +class AllFanAbsenceCondition(FanCondition): + def is_match(self, thermal_info_dict): + fan_info_obj = self.get_fan_info(thermal_info_dict) + return len(fan_info_obj.get_presence_fans()) == 0 if fan_info_obj else False + +@thermal_json_object('fan.all.presence') +class AllFanPresenceCondition(FanCondition): + def is_match(self, thermal_info_dict): + fan_info_obj = self.get_fan_info(thermal_info_dict) + return len(fan_info_obj.get_absence_fans()) == 0 if fan_info_obj else False + +class ThermalCondition(ThermalPolicyConditionBase): + def get_thermal_info(self, thermal_info_dict): + from .thermal_infos import ThermalInfo + if ThermalInfo.INFO_NAME in thermal_info_dict and isinstance(thermal_info_dict[ThermalInfo.INFO_NAME], ThermalInfo): + return thermal_info_dict[ThermalInfo.INFO_NAME] + return None + +@thermal_json_object('thermal.over.high_critical_threshold') +class ThermalOverHighCriticalCondition(ThermalCondition): + def is_match(self, thermal_info_dict): + thermal_info_obj = self.get_thermal_info(thermal_info_dict) + if thermal_info_obj: + return thermal_info_obj.is_over_high_critical_threshold() + return False + +class PsuCondition(ThermalPolicyConditionBase): + def get_psu_info(self, thermal_info_dict): + from .thermal_infos import PsuInfo + if PsuInfo.INFO_NAME in thermal_info_dict and isinstance(thermal_info_dict[PsuInfo.INFO_NAME], PsuInfo): + return thermal_info_dict[PsuInfo.INFO_NAME] + return None + +@thermal_json_object('psu.any.absence') +class AnyPsuAbsenceCondition(PsuCondition): + def is_match(self, thermal_info_dict): + psu_info_obj = self.get_psu_info(thermal_info_dict) + return len(psu_info_obj.get_absence_psus()) > 0 if psu_info_obj else False + +@thermal_json_object('psu.all.absence') +class AllPsuAbsenceCondition(PsuCondition): + def is_match(self, thermal_info_dict): + psu_info_obj = self.get_psu_info(thermal_info_dict) + return len(psu_info_obj.get_presence_psus()) == 0 if psu_info_obj else False + +@thermal_json_object('psu.all.presence') +class AllPsuPresenceCondition(PsuCondition): + def is_match(self, thermal_info_dict): + psu_info_obj = self.get_psu_info(thermal_info_dict) + return len(psu_info_obj.get_absence_psus()) == 0 if psu_info_obj else False diff --git a/ixr7220h6-64/sonic_platform/thermal_infos.py b/ixr7220h6-64/sonic_platform/thermal_infos.py new file mode 100755 index 0000000..5b60d3b --- /dev/null +++ b/ixr7220h6-64/sonic_platform/thermal_infos.py @@ -0,0 +1,236 @@ +try: + from sonic_platform_base.sonic_thermal_control.thermal_info_base import ThermalPolicyInfoBase + from sonic_platform_base.sonic_thermal_control.thermal_json_object import thermal_json_object + from sonic_py_common.logger import Logger +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +logger = Logger() + +@thermal_json_object('fan_info') +class FanInfo(ThermalPolicyInfoBase): + """ + Fan information needed by thermal policy + """ + # Fan information name + INFO_NAME = 'fan_info' + + def __init__(self): + self._absence_fans = set() + self._presence_fans = set() + self._status_changed = False + + def collect(self, chassis): + """ + Collect absence and presence fans. + :param chassis: The chassis object + :return: + """ + self._status_changed = False + for fan in chassis.get_all_fans(): + if fan.get_presence() and fan not in self._presence_fans: + self._presence_fans.add(fan) + self._status_changed = True + if fan in self._absence_fans: + self._absence_fans.remove(fan) + elif not fan.get_presence() and fan not in self._absence_fans: + self._absence_fans.add(fan) + self._status_changed = True + if fan in self._presence_fans: + self._presence_fans.remove(fan) + + def get_absence_fans(self): + """ + Retrieves absence fans + :return: A set of absence fans + """ + return self._absence_fans + + def get_presence_fans(self): + """ + Retrieves presence fans + :return: A set of presence fans + """ + return self._presence_fans + + def is_status_changed(self): + """ + Retrieves if the status of fan information changed + :return: True if status changed else False + """ + return self._status_changed + +@thermal_json_object('thermal_info') +class ThermalInfo(ThermalPolicyInfoBase): + """ + Thermal information needed by thermal policy + """ + # Fan information name + INFO_NAME = 'thermal_info' + + def __init__(self): + self._old_threshold_level = -1 + self._current_threshold_level = 0 + self._num_fan_levels = 1 + self._level_up_threshold = [[55, 60, 65, 55, 65, 100, 100, 100, 75, 60, 55, 55, 85, 65, 65, 60, 80]] + + self._level_down_threshold = [[45, 36, 45, 45, 50, 68, 68, 68, 60, 36, 45, 45, 68, 40, 45, 35, 55]] + + def collect(self, chassis): + """ + Collect thermal sensor temperature change status + :param chassis: The chassis object + :return: + """ + self._temps = [] + self._over_high_critical_threshold = False + self._set_fan_default_speed = False + self._set_fan_threshold_one_speed = False + self._set_fan_high_temp_speed = False + + # Calculate average temp within the device + num_of_thermals = chassis.get_num_thermals() + for index in range(num_of_thermals): + self._temps.insert(index, chassis.get_thermal(index).get_temperature()) + + # Find current required threshold level + max_level =0 + min_level = [self._num_fan_levels for i in range(num_of_thermals)] + for index in range(num_of_thermals): + for level in range(self._num_fan_levels): + + if self._temps[index]>self._level_up_threshold[level][index]: + if max_levellevel: + min_level[index]=level + + max_of_min_level=max(min_level) + + #compare with running threshold level + if max_of_min_level > self._old_threshold_level: + max_of_min_level=self._old_threshold_level + + self._current_threshold_level = max(max_of_min_level,max_level) + + #set fan to max speed if one fan is down + for fan in chassis.get_all_fans(): + if not fan.get_status() : + self._current_threshold_level = 2 + + # Decide fan speed based on threshold level + if self._current_threshold_level != self._old_threshold_level: + if self._current_threshold_level == 0: + self._set_fan_default_speed = True + elif self._current_threshold_level == 1: + self._set_fan_threshold_one_speed = True + elif self._current_threshold_level == 2: + self._set_fan_high_temp_speed = True + + self._old_threshold_level=self._current_threshold_level + + def is_set_fan_default_speed(self): + """ + Retrieves if the temperature is warm up and over high threshold + :return: True if the temperature is warm up and over high threshold else False + """ + return self._set_fan_default_speed + + def is_set_fan_threshold_one_speed(self): + """ + Retrieves if the temperature is warm up and over high threshold + :return: True if the temperature is warm up and over high threshold else False + """ + return self._set_fan_threshold_one_speed + + def is_set_fan_high_temp_speed(self): + """ + Retrieves if the temperature is warm up and over high threshold + :return: True if the temperature is warm up and over high threshold else False + """ + return self._set_fan_high_temp_speed + + def is_over_high_critical_threshold(self): + """ + Retrieves if the temperature is over high critical threshold + :return: True if the temperature is over high critical threshold else False + """ + return self._over_high_critical_threshold + +@thermal_json_object('psu_info') +class PsuInfo(ThermalPolicyInfoBase): + """ + PSU information needed by thermal policy + """ + INFO_NAME = 'psu_info' + + def __init__(self): + self._absence_psus = set() + self._presence_psus = set() + self._status_changed = False + + def collect(self, chassis): + """ + Collect absence and presence PSUs. + :param chassis: The chassis object + :return: + """ + self._status_changed = False + for psu in chassis.get_all_psus(): + if psu.get_presence() and psu.get_powergood_status() and psu not in self._presence_psus: + self._presence_psus.add(psu) + self._status_changed = True + if psu in self._absence_psus: + self._absence_psus.remove(psu) + elif (not psu.get_presence() or not psu.get_powergood_status()) and psu not in self._absence_psus: + self._absence_psus.add(psu) + self._status_changed = True + if psu in self._presence_psus: + self._presence_psus.remove(psu) + + def get_absence_psus(self): + """ + Retrieves presence PSUs + :return: A set of absence PSUs + """ + return self._absence_psus + + def get_presence_psus(self): + """ + Retrieves presence PSUs + :return: A set of presence fans + """ + return self._presence_psus + + def is_status_changed(self): + """ + Retrieves if the status of PSU information changed + :return: True if status changed else False + """ + return self._status_changed + +@thermal_json_object('chassis_info') +class ChassisInfo(ThermalPolicyInfoBase): + """ + Chassis information needed by thermal policy + """ + INFO_NAME = 'chassis_info' + + def __init__(self): + self._chassis = None + + def collect(self, chassis): + """ + Collect platform chassis. + :param chassis: The chassis object + :return: + """ + self._chassis = chassis + + def get_chassis(self): + """ + Retrieves platform chassis object + :return: A platform chassis object. + """ + return self._chassis diff --git a/ixr7220h6-64/sonic_platform/thermal_manager.py b/ixr7220h6-64/sonic_platform/thermal_manager.py new file mode 100755 index 0000000..7d84fa9 --- /dev/null +++ b/ixr7220h6-64/sonic_platform/thermal_manager.py @@ -0,0 +1,58 @@ +try: + from sonic_platform_base.sonic_thermal_control.thermal_manager_base import ThermalManagerBase + from .thermal_actions import * + from .thermal_conditions import * + from .thermal_infos import * +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +THERMALD_INTERVAL = 10 + +class ThermalManager(ThermalManagerBase): + """Nokia platform-specific Thermal Manager class""" + THERMAL_ALGORITHM_CONTROL_PATH = '/var/run/hw-management/config/suspend' + + @classmethod + def get_interval(cls): + return THERMALD_INTERVAL + + @classmethod + def start_thermal_control_algorithm(cls): + """ + Start thermal control algorithm + + Returns: + bool: True if set success, False if fail. + """ + cls._control_thermal_control_algorithm(False) + + @classmethod + def stop_thermal_control_algorithm(cls): + """ + Stop thermal control algorithm + + Returns: + bool: True if set success, False if fail. + """ + cls._control_thermal_control_algorithm(True) + + @classmethod + def _control_thermal_control_algorithm(cls, suspend): + """ + Control thermal control algorithm + + Args: + suspend: Bool, indicate suspend the algorithm or not + + Returns: + bool: True if set success, False if fail. + """ + status = True + write_value = 1 if suspend else 0 + try: + with open(cls.THERMAL_ALGORITHM_CONTROL_PATH, 'w') as control_file: + control_file.write(str(write_value)) + except (ValueError, IOError): + status = False + + return status diff --git a/ixr7220h6-64/sonic_platform/watchdog.py b/ixr7220h6-64/sonic_platform/watchdog.py new file mode 100755 index 0000000..5bb2e00 --- /dev/null +++ b/ixr7220h6-64/sonic_platform/watchdog.py @@ -0,0 +1,175 @@ +""" + Module contains an implementation of SONiC Platform Base API and + provides access to hardware watchdog +""" +try: + import os + import fcntl + import array + from sonic_platform_base.watchdog_base import WatchdogBase + from sonic_platform.sysfs import read_sysfs_file +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +# ioctl constants +IO_WRITE = 0x40000000 +IO_READ = 0x80000000 +IO_SIZE_INT = 0x00040000 +IO_READ_WRITE = 0xC0000000 +IO_TYPE_WATCHDOG = ord('W') << 8 + +WDR_INT = IO_READ | IO_SIZE_INT | IO_TYPE_WATCHDOG +WDWR_INT = IO_READ_WRITE | IO_SIZE_INT | IO_TYPE_WATCHDOG + +# Watchdog ioctl commands +WDIOC_SETOPTIONS = 4 | WDR_INT +WDIOC_KEEPALIVE = 5 | WDR_INT +WDIOC_SETTIMEOUT = 6 | WDWR_INT +WDIOC_GETTIMEOUT = 7 | WDR_INT +WDIOC_SETPRETIMEOUT = 8 | WDWR_INT +WDIOC_GETPRETIMEOUT = 9 | WDR_INT +WDIOC_GETTIMELEFT = 10 | WDR_INT + +# Watchdog status constants +WDIOS_DISABLECARD = 0x0001 +WDIOS_ENABLECARD = 0x0002 + +# watchdog sysfs +WD_SYSFS_PATH = "/sys/class/watchdog/watchdog0/" + +WD_COMMON_ERROR = -1 + +class WatchdogImplBase(WatchdogBase): + """ + Base class that implements common logic for interacting + with watchdog using ioctl commands + """ + def __init__(self, wd_device_path): + """ + Open a watchdog handle + @param wd_device_path Path to watchdog device + """ + super().__init__() + + self.watchdog="" + self.watchdog_path = wd_device_path + self.wd_state_reg = WD_SYSFS_PATH+"state" + self.wd_timeout_reg = WD_SYSFS_PATH+"timeout" + self.wd_timeleft_reg = WD_SYSFS_PATH+"timeleft" + + self.timeout = self._gettimeout() + + def _disablewatchdog(self): + """ + Turn off the watchdog timer + """ + req = array.array('h', [WDIOS_DISABLECARD]) + fcntl.ioctl(self.watchdog, WDIOC_SETOPTIONS, req, False) + + def _enablewatchdog(self): + """ + Turn on the watchdog timer + """ + req = array.array('h', [WDIOS_ENABLECARD]) + fcntl.ioctl(self.watchdog, WDIOC_SETOPTIONS, req, False) + + def _keepalive(self): + """ + Keep alive watchdog timer + """ + fcntl.ioctl(self.watchdog, WDIOC_KEEPALIVE) + + def _settimeout(self, seconds): + """ + Set watchdog timer timeout + @param seconds - timeout in seconds + @return is the actual set timeout + """ + req = array.array('I', [seconds]) + fcntl.ioctl(self.watchdog, WDIOC_SETTIMEOUT, req, True) + + return int(req[0]) + + def _gettimeout(self): + """ + Get watchdog timeout + @return watchdog timeout + """ + timeout=0 + timeout=read_sysfs_file(self.wd_timeout_reg) + + return timeout + + def _gettimeleft(self): + """ + Get time left before watchdog timer expires + @return time left in seconds + """ + req = array.array('I', [0]) + fcntl.ioctl(self.watchdog, WDIOC_GETTIMELEFT, req, True) + + return int(req[0]) + + def arm(self, seconds): + """ + Implements arm WatchdogBase API + """ + ret = WD_COMMON_ERROR + if (seconds < 0 or seconds > 340 ): + return ret + + if not self.watchdog: + self.watchdog = os.open(self.watchdog_path, os.O_WRONLY) + try: + if self.timeout != seconds: + self.timeout = self._settimeout(seconds) + if self.is_armed(): + self._keepalive() + else: + self._enablewatchdog() + ret = self.timeout + except IOError: + pass + + return ret + + def disarm(self): + """ + Implements disarm WatchdogBase API + + Returns: + A boolean, True if watchdog is disarmed successfully, False + if not + """ + if not self.watchdog: + self.watchdog = os.open(self.watchdog_path, os.O_WRONLY) + try: + self._disablewatchdog() + self.timeout = 0 + except IOError: + return False + + return True + + def is_armed(self): + """ + Implements is_armed WatchdogBase API + """ + status = False + + state = read_sysfs_file(self.wd_state_reg) + if state != 'inactive': + status = True + + return status + + def get_remaining_time(self): + """ + Implements get_remaining_time WatchdogBase API + """ + timeleft = WD_COMMON_ERROR + + if self.is_armed(): + timeleft=read_sysfs_file(self.wd_timeleft_reg) + + return int(timeleft) From ead77d57bb81541679d0d416949bd6e4e8736db3 Mon Sep 17 00:00:00 2001 From: y7zhou Date: Thu, 11 Dec 2025 09:56:22 -0500 Subject: [PATCH 2/2] [H6-64] support kernel 6.12.x --- ixr7220h6-64/modules/eeprom_fru.c | 2 +- ixr7220h6-64/modules/embd_ctrl.c | 3 +-- ixr7220h6-64/modules/h6_fan_cpld.c | 3 +-- ixr7220h6-64/modules/i2c-ocores.c | 3 +-- ixr7220h6-64/modules/pmbus_psu.c | 2 +- ixr7220h6-64/modules/port_cpld0.c | 3 +-- ixr7220h6-64/modules/port_cpld1.c | 3 +-- ixr7220h6-64/modules/sys_cpld.c | 3 +-- ixr7220h6-64/modules/sys_fpga.c | 3 +-- ixr7220h6-64/modules/sys_mux.c | 8 ++++---- 10 files changed, 13 insertions(+), 20 deletions(-) diff --git a/ixr7220h6-64/modules/eeprom_fru.c b/ixr7220h6-64/modules/eeprom_fru.c index 179ec7e..58be56e 100644 --- a/ixr7220h6-64/modules/eeprom_fru.c +++ b/ixr7220h6-64/modules/eeprom_fru.c @@ -441,7 +441,7 @@ static const struct attribute_group eeprom_group = { .attrs = eeprom_attributes, }; -static int eeprom_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int eeprom_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct at24_data *data; diff --git a/ixr7220h6-64/modules/embd_ctrl.c b/ixr7220h6-64/modules/embd_ctrl.c index 28ff8cf..8a6a588 100644 --- a/ixr7220h6-64/modules/embd_ctrl.c +++ b/ixr7220h6-64/modules/embd_ctrl.c @@ -108,8 +108,7 @@ static const struct attribute_group embd_ctrl_group = { .attrs = embd_ctrl_attributes, }; -static int embd_ctrl_probe(struct i2c_client *client, - const struct i2c_device_id *dev_id) +static int embd_ctrl_probe(struct i2c_client *client) { int status = 0; struct ec_data *data = NULL; diff --git a/ixr7220h6-64/modules/h6_fan_cpld.c b/ixr7220h6-64/modules/h6_fan_cpld.c index 95b433b..55660be 100644 --- a/ixr7220h6-64/modules/h6_fan_cpld.c +++ b/ixr7220h6-64/modules/h6_fan_cpld.c @@ -528,8 +528,7 @@ static struct h6_fan_data *h6_fan_update_device(struct device return data; } -static int h6_fan_probe(struct i2c_client *client, - const struct i2c_device_id *dev_id) +static int h6_fan_probe(struct i2c_client *client) { struct h6_fan_data *data; int status; diff --git a/ixr7220h6-64/modules/i2c-ocores.c b/ixr7220h6-64/modules/i2c-ocores.c index 00e3e21..db30557 100644 --- a/ixr7220h6-64/modules/i2c-ocores.c +++ b/ixr7220h6-64/modules/i2c-ocores.c @@ -788,7 +788,7 @@ static int ocores_i2c_probe(struct platform_device *pdev) return ret; } -static int ocores_i2c_remove(struct platform_device *pdev) +static void ocores_i2c_remove(struct platform_device *pdev) { struct ocores_i2c *i2c = platform_get_drvdata(pdev); u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL); @@ -803,7 +803,6 @@ static int ocores_i2c_remove(struct platform_device *pdev) if (!IS_ERR(i2c->clk)) clk_disable_unprepare(i2c->clk); - return 0; } #ifdef CONFIG_PM_SLEEP diff --git a/ixr7220h6-64/modules/pmbus_psu.c b/ixr7220h6-64/modules/pmbus_psu.c index bb59365..5f4d92e 100644 --- a/ixr7220h6-64/modules/pmbus_psu.c +++ b/ixr7220h6-64/modules/pmbus_psu.c @@ -481,7 +481,7 @@ static const struct attribute_group psu_group = { .attrs = psu_attributes, }; -static int psu_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int psu_probe(struct i2c_client *client) { struct psu_data *data; int status; diff --git a/ixr7220h6-64/modules/port_cpld0.c b/ixr7220h6-64/modules/port_cpld0.c index 0ebfa8d..01d512b 100644 --- a/ixr7220h6-64/modules/port_cpld0.c +++ b/ixr7220h6-64/modules/port_cpld0.c @@ -968,8 +968,7 @@ static const struct attribute_group port_cpld0_group = { .attrs = port_cpld0_attributes, }; -static int port_cpld0_probe(struct i2c_client *client, - const struct i2c_device_id *dev_id) +static int port_cpld0_probe(struct i2c_client *client) { int status; struct cpld_data *data = NULL; diff --git a/ixr7220h6-64/modules/port_cpld1.c b/ixr7220h6-64/modules/port_cpld1.c index c32c04f..bc7b98a 100644 --- a/ixr7220h6-64/modules/port_cpld1.c +++ b/ixr7220h6-64/modules/port_cpld1.c @@ -741,8 +741,7 @@ static const struct attribute_group port_cpld1_group = { .attrs = port_cpld1_attributes, }; -static int port_cpld1_probe(struct i2c_client *client, - const struct i2c_device_id *dev_id) +static int port_cpld1_probe(struct i2c_client *client) { int status; struct cpld_data *data = NULL; diff --git a/ixr7220h6-64/modules/sys_cpld.c b/ixr7220h6-64/modules/sys_cpld.c index 83cbc80..39ba024 100644 --- a/ixr7220h6-64/modules/sys_cpld.c +++ b/ixr7220h6-64/modules/sys_cpld.c @@ -330,8 +330,7 @@ static const struct attribute_group sys_cpld_group = { .attrs = sys_cpld_attributes, }; -static int sys_cpld_probe(struct i2c_client *client, - const struct i2c_device_id *dev_id) +static int sys_cpld_probe(struct i2c_client *client) { int status; struct cpld_data *data = NULL; diff --git a/ixr7220h6-64/modules/sys_fpga.c b/ixr7220h6-64/modules/sys_fpga.c index 0bd912b..bc6e9a6 100644 --- a/ixr7220h6-64/modules/sys_fpga.c +++ b/ixr7220h6-64/modules/sys_fpga.c @@ -166,8 +166,7 @@ static const struct attribute_group sys_fpga_group = { .attrs = sys_fpga_attributes, }; -static int sys_fpga_probe(struct i2c_client *client, - const struct i2c_device_id *dev_id) +static int sys_fpga_probe(struct i2c_client *client) { int status; struct cpld_data *data = NULL; diff --git a/ixr7220h6-64/modules/sys_mux.c b/ixr7220h6-64/modules/sys_mux.c index 669e63a..b24c494 100644 --- a/ixr7220h6-64/modules/sys_mux.c +++ b/ixr7220h6-64/modules/sys_mux.c @@ -139,10 +139,10 @@ static void sys_mux_cleanup(struct i2c_mux_core *muxc) /* * I2C init/probing/exit functions */ -static int sys_mux_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int sys_mux_probe(struct i2c_client *client) { - struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); + const struct i2c_device_id *id = i2c_client_get_device_id(client); + struct i2c_adapter *adap = client->adapter; struct device *dev = &client->dev; struct sys_mux_data *data; struct i2c_mux_core *muxc; @@ -166,7 +166,7 @@ static int sys_mux_probe(struct i2c_client *client, /* Now create an adapter for each channel */ for (i = 0; i < chips[data->type].nchans; i++) { - ret = i2c_mux_add_adapter(muxc, 0, i, 0); + ret = i2c_mux_add_adapter(muxc, 0, i); if (ret) goto exit_mux; }