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..58be56e
--- /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)
+{
+ 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..8a6a588
--- /dev/null
+++ b/ixr7220h6-64/modules/embd_ctrl.c
@@ -0,0 +1,196 @@
+// * 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)
+{
+ 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..55660be
--- /dev/null
+++ b/ixr7220h6-64/modules/h6_fan_cpld.c
@@ -0,0 +1,606 @@
+// 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)
+{
+ 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..db30557
--- /dev/null
+++ b/ixr7220h6-64/modules/i2c-ocores.c
@@ -0,0 +1,882 @@
+// 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 void 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);
+
+}
+
+#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..5f4d92e
--- /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)
+{
+ 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..01d512b
--- /dev/null
+++ b/ixr7220h6-64/modules/port_cpld0.c
@@ -0,0 +1,1076 @@
+// * 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)
+{
+ 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..bc7b98a
--- /dev/null
+++ b/ixr7220h6-64/modules/port_cpld1.c
@@ -0,0 +1,846 @@
+// * 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)
+{
+ 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..39ba024
--- /dev/null
+++ b/ixr7220h6-64/modules/sys_cpld.c
@@ -0,0 +1,421 @@
+// * 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)
+{
+ 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..bc6e9a6
--- /dev/null
+++ b/ixr7220h6-64/modules/sys_fpga.c
@@ -0,0 +1,256 @@
+// * 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)
+{
+ 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..b24c494
--- /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 = 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;
+ 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);
+ 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)