diff --git a/debian/control b/debian/control
index b4bd552..1a4a57f 100755
--- a/debian/control
+++ b/debian/control
@@ -63,3 +63,9 @@ Package: sonic-platform-nokia-ixr7220h6-64
Architecture: amd64
Depends: ${misc:Depends}
Description: kernel modules for platform devices such as osfp, fan, led, sfp
+
+Package: sonic-platform-nokia-ixr7220h6-128
+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 37d2661..367be75 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 ixr7250x4 ixr7220d4 ixr7220h6-64
+ ixr7220h5-64o ixr7250x1b ixr7250x3b ixr7250x4 ixr7220d4 ixr7220h6-64 ixr7220h6-128
BRIDGE_DRIVER_TARGETS := ixr7250x1b ixr7250x3b ixr7250x4
MODULE_DIR := modules
UTILS_DIR := utils
diff --git a/debian/sonic-platform-nokia-ixr7220h6-128.install b/debian/sonic-platform-nokia-ixr7220h6-128.install
new file mode 100644
index 0000000..61f96a5
--- /dev/null
+++ b/debian/sonic-platform-nokia-ixr7220h6-128.install
@@ -0,0 +1,6 @@
+ixr7220h6-128/scripts/h6_128_platform_init.sh usr/local/bin
+ixr7220h6-128/scripts/set_ps.py usr/local/bin
+ixr7220h6-128/scripts/ports_notify.py usr/local/bin
+ixr7220h6-128/service/h6_128_platform_init.service etc/systemd/system
+ixr7220h6-128/service/ports_notify.service etc/systemd/system/
+ixr7220h6-128/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/x86_64-nokia_ixr7220_h6_128-r0
diff --git a/debian/sonic-platform-nokia-ixr7220h6-128.postinst b/debian/sonic-platform-nokia-ixr7220h6-128.postinst
new file mode 100644
index 0000000..640ea94
--- /dev/null
+++ b/debian/sonic-platform-nokia-ixr7220h6-128.postinst
@@ -0,0 +1,12 @@
+#!/bin/sh
+# postinst script for sonic-platform-nokia-IXR7220-H6-128
+#
+# see: dh_installdeb(1)
+
+chmod a+x /usr/local/bin/set_ps.py
+chmod a+x /usr/local/bin/h6_128_platform_init.sh
+systemctl enable h6_128_platform_init.service
+systemctl start h6_128_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-128/modules/Makefile b/ixr7220h6-128/modules/Makefile
new file mode 100644
index 0000000..327463c
--- /dev/null
+++ b/ixr7220h6-128/modules/Makefile
@@ -0,0 +1,12 @@
+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 += port_cpld2.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-128/modules/eeprom_fru.c b/ixr7220h6-128/modules/eeprom_fru.c
new file mode 100644
index 0000000..853a60e
--- /dev/null
+++ b/ixr7220h6-128/modules/eeprom_fru.c
@@ -0,0 +1,536 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * eeprom_fru.c - NOKIA EEPROM FRU Sysfs driver
+ *
+ *
+ * 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
+
+#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);
+int fru_checksum_is_valid(void *area, size_t len);
+const char * buf2str_extended(const u8 *buf, int len, const char *sep);
+const char * buf2str(const u8 *buf, int len);
+char * get_fru_area_str(struct device *dev, u8 * data, u32 * offset);
+int decode_eeprom(struct i2c_client *client);
+
+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 = NULL;
+ int len, off, size, i, j, k, typecode, char_idx;
+ union {
+ u32 bits;
+ char chars[4];
+ } u;
+
+ size = 0;
+ off = *offset;
+
+ if(data[off] == FRU_END_OF_FIELDS)
+ return NULL;
+ /* bits 6:7 contain format */
+ typecode = ((data[off] & 0xC0) >> 6);
+
+ /* bits 0:5 contain length */
+ len = data[off++];
+ len &= 0x3f;
+ if(debug) {
+ dev_info(dev, "data[0x%x] = %x, typecode:0x%x len:%d, ", off, data[off], typecode, len);
+ }
+
+ 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 + 8, read_eeprom_max_len - 8, 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-128/modules/embd_ctrl.c b/ixr7220h6-128/modules/embd_ctrl.c
new file mode 100644
index 0000000..8a6a588
--- /dev/null
+++ b/ixr7220h6-128/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-128/modules/h6_fan_cpld.c b/ixr7220h6-128/modules/h6_fan_cpld.c
new file mode 100644
index 0000000..a450116
--- /dev/null
+++ b/ixr7220h6-128/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) 2025 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, "%02x.%02x\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-128/modules/h6_i2c_oc.c b/ixr7220h6-128/modules/h6_i2c_oc.c
new file mode 100644
index 0000000..c742853
--- /dev/null
+++ b/ixr7220h6-128/modules/h6_i2c_oc.c
@@ -0,0 +1,312 @@
+// Driver for Nokia-7220-IXR-H6-128 Router
+/*
+ * Copyright (C) 2026 Accton Technology Corporation.
+ * Copyright (C) 2026 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 (128 + 2) /*128 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,// MESS_TOP_L_CPLD I2C Master OSFP Port1
+ 0x2120,// MESS_TOP_L_CPLD I2C Master OSFP Port2
+ 0x2140,// MESS_TOP_L_CPLD I2C Master OSFP Port3
+ 0x2160,// MESS_TOP_L_CPLD I2C Master OSFP Port4
+ 0x2180,// MESS_TOP_L_CPLD I2C Master OSFP Port5
+ 0x21A0,// MESS_TOP_L_CPLD I2C Master OSFP Port6
+ 0x21C0,// MESS_TOP_L_CPLD I2C Master OSFP Port7
+ 0x21E0,// MESS_TOP_L_CPLD I2C Master OSFP Port8
+ 0x2200,// MESS_TOP_L_CPLD I2C Master OSFP Port9
+ 0x2220,// MESS_TOP_L_CPLD I2C Master OSFP Port10
+ 0x2240,// MESS_TOP_L_CPLD I2C Master OSFP Port11
+ 0x2260,// MESS_TOP_L_CPLD I2C Master OSFP Port12
+ 0x2280,// MESS_TOP_L_CPLD I2C Master OSFP Port13
+ 0x22A0,// MESS_TOP_L_CPLD I2C Master OSFP Port14
+ 0x22C0,// MESS_TOP_L_CPLD I2C Master OSFP Port15
+ 0x22E0,// MESS_TOP_L_CPLD I2C Master OSFP Port16
+ 0x2100,// MESS_TOP_R_CPLD I2C Master OSFP Port17
+ 0x2120,// MESS_TOP_R_CPLD I2C Master OSFP Port18
+ 0x2140,// MESS_TOP_R_CPLD I2C Master OSFP Port19
+ 0x2160,// MESS_TOP_R_CPLD I2C Master OSFP Port20
+ 0x2180,// MESS_TOP_R_CPLD I2C Master OSFP Port21
+ 0x21A0,// MESS_TOP_R_CPLD I2C Master OSFP Port22
+ 0x21C0,// MESS_TOP_R_CPLD I2C Master OSFP Port23
+ 0x21E0,// MESS_TOP_R_CPLD I2C Master OSFP Port24
+ 0x2200,// MESS_TOP_R_CPLD I2C Master OSFP Port25
+ 0x2220,// MESS_TOP_R_CPLD I2C Master OSFP Port26
+ 0x2240,// MESS_TOP_R_CPLD I2C Master OSFP Port27
+ 0x2260,// MESS_TOP_R_CPLD I2C Master OSFP Port28
+ 0x2280,// MESS_TOP_R_CPLD I2C Master OSFP Port29
+ 0x22A0,// MESS_TOP_R_CPLD I2C Master OSFP Port30
+ 0x22C0,// MESS_TOP_R_CPLD I2C Master OSFP Port31
+ 0x22E0,// MESS_TOP_R_CPLD I2C Master OSFP Port32
+//////////////////////////////////////////////////////
+ 0x2100,// PORTCPLD0 I2C Master OSFP Port33
+ 0x2120,// PORTCPLD0 I2C Master OSFP Port34
+ 0x2180,// PORTCPLD0 I2C Master OSFP Port35
+ 0x21A0,// PORTCPLD0 I2C Master OSFP Port36
+ 0x2200,// PORTCPLD0 I2C Master OSFP Port37
+ 0x2220,// PORTCPLD0 I2C Master OSFP Port38
+ 0x2280,// PORTCPLD0 I2C Master OSFP Port39
+ 0x22A0,// PORTCPLD0 I2C Master OSFP Port40
+ 0x2300,// PORTCPLD0 I2C Master OSFP Port41
+ 0x2320,// PORTCPLD0 I2C Master OSFP Port42
+ 0x2380,// PORTCPLD0 I2C Master OSFP Port43
+ 0x23A0,// PORTCPLD0 I2C Master OSFP Port44
+ 0x2400,// PORTCPLD0 I2C Master OSFP Port45
+ 0x2420,// PORTCPLD0 I2C Master OSFP Port46
+ 0x2480,// PORTCPLD0 I2C Master OSFP Port47
+ 0x24A0,// PORTCPLD0 I2C Master OSFP Port48
+ 0x2100,// PORTCPLD1 I2C Master OSFP Port49
+ 0x2120,// PORTCPLD1 I2C Master OSFP Port50
+ 0x2180,// PORTCPLD1 I2C Master OSFP Port51
+ 0x21A0,// PORTCPLD1 I2C Master OSFP Port52
+ 0x2200,// PORTCPLD1 I2C Master OSFP Port53
+ 0x2220,// PORTCPLD1 I2C Master OSFP Port54
+ 0x2280,// PORTCPLD1 I2C Master OSFP Port55
+ 0x22A0,// PORTCPLD1 I2C Master OSFP Port56
+ 0x2300,// PORTCPLD1 I2C Master OSFP Port57
+ 0x2320,// PORTCPLD1 I2C Master OSFP Port58
+ 0x2380,// PORTCPLD1 I2C Master OSFP Port59
+ 0x23A0,// PORTCPLD1 I2C Master OSFP Port60
+ 0x2400,// PORTCPLD1 I2C Master OSFP Port61
+ 0x2420,// PORTCPLD1 I2C Master OSFP Port62
+ 0x2480,// PORTCPLD1 I2C Master OSFP Port63
+ 0x24A0,// PORTCPLD1 I2C Master OSFP Port64
+//////////////////////////////////////////////////////
+ 0x2140,// PORTCPLD0 I2C Master OSFP Port65
+ 0x2160,// PORTCPLD0 I2C Master OSFP Port66
+ 0x21C0,// PORTCPLD0 I2C Master OSFP Port67
+ 0x21E0,// PORTCPLD0 I2C Master OSFP Port68
+ 0x2240,// PORTCPLD0 I2C Master OSFP Port69
+ 0x2260,// PORTCPLD0 I2C Master OSFP Port70
+ 0x22C0,// PORTCPLD0 I2C Master OSFP Port71
+ 0x22E0,// PORTCPLD0 I2C Master OSFP Port72
+ 0x2340,// PORTCPLD0 I2C Master OSFP Port73
+ 0x2360,// PORTCPLD0 I2C Master OSFP Port74
+ 0x23C0,// PORTCPLD0 I2C Master OSFP Port75
+ 0x23E0,// PORTCPLD0 I2C Master OSFP Port76
+ 0x2440,// PORTCPLD0 I2C Master OSFP Port77
+ 0x2460,// PORTCPLD0 I2C Master OSFP Port78
+ 0x24C0,// PORTCPLD0 I2C Master OSFP Port79
+ 0x24E0,// PORTCPLD0 I2C Master OSFP Port80
+ 0x2140,// PORTCPLD1 I2C Master OSFP Port81
+ 0x2160,// PORTCPLD1 I2C Master OSFP Port82
+ 0x21C0,// PORTCPLD1 I2C Master OSFP Port83
+ 0x21E0,// PORTCPLD1 I2C Master OSFP Port84
+ 0x2240,// PORTCPLD1 I2C Master OSFP Port85
+ 0x2260,// PORTCPLD1 I2C Master OSFP Port86
+ 0x22C0,// PORTCPLD1 I2C Master OSFP Port87
+ 0x22E0,// PORTCPLD1 I2C Master OSFP Port88
+ 0x2340,// PORTCPLD1 I2C Master OSFP Port89
+ 0x2360,// PORTCPLD1 I2C Master OSFP Port90
+ 0x23C0,// PORTCPLD1 I2C Master OSFP Port91
+ 0x23E0,// PORTCPLD1 I2C Master OSFP Port92
+ 0x2440,// PORTCPLD1 I2C Master OSFP Port93
+ 0x2460,// PORTCPLD1 I2C Master OSFP Port94
+ 0x24C0,// PORTCPLD1 I2C Master OSFP Port95
+ 0x24E0,// PORTCPLD1 I2C Master OSFP Port96
+//////////////////////////////////////////////////////
+ 0x2100,// MESS_BOT_L_CPLD I2C Master OSFP Port97
+ 0x2120,// MESS_BOT_L_CPLD I2C Master OSFP Port98
+ 0x2140,// MESS_BOT_L_CPLD I2C Master OSFP Port99
+ 0x2160,// MESS_BOT_L_CPLD I2C Master OSFP Port100
+ 0x2180,// MESS_BOT_L_CPLD I2C Master OSFP Port101
+ 0x21A0,// MESS_BOT_L_CPLD I2C Master OSFP Port102
+ 0x21C0,// MESS_BOT_L_CPLD I2C Master OSFP Port103
+ 0x21E0,// MESS_BOT_L_CPLD I2C Master OSFP Port104
+ 0x2200,// MESS_BOT_L_CPLD I2C Master OSFP Port105
+ 0x2220,// MESS_BOT_L_CPLD I2C Master OSFP Port106
+ 0x2240,// MESS_BOT_L_CPLD I2C Master OSFP Port107
+ 0x2260,// MESS_BOT_L_CPLD I2C Master OSFP Port108
+ 0x2280,// MESS_BOT_L_CPLD I2C Master OSFP Port109
+ 0x22A0,// MESS_BOT_L_CPLD I2C Master OSFP Port110
+ 0x22C0,// MESS_BOT_L_CPLD I2C Master OSFP Port111
+ 0x22E0,// MESS_BOT_L_CPLD I2C Master OSFP Port112
+ 0x2100,// MESS_BOT_R_CPLD I2C Master OSFP Port113
+ 0x2120,// MESS_BOT_R_CPLD I2C Master OSFP Port114
+ 0x2140,// MESS_BOT_R_CPLD I2C Master OSFP Port115
+ 0x2160,// MESS_BOT_R_CPLD I2C Master OSFP Port116
+ 0x2180,// MESS_BOT_R_CPLD I2C Master OSFP Port117
+ 0x21A0,// MESS_BOT_R_CPLD I2C Master OSFP Port118
+ 0x21C0,// MESS_BOT_R_CPLD I2C Master OSFP Port119
+ 0x21E0,// MESS_BOT_R_CPLD I2C Master OSFP Port120
+ 0x2200,// MESS_BOT_R_CPLD I2C Master OSFP Port121
+ 0x2220,// MESS_BOT_R_CPLD I2C Master OSFP Port122
+ 0x2240,// MESS_BOT_R_CPLD I2C Master OSFP Port123
+ 0x2260,// MESS_BOT_R_CPLD I2C Master OSFP Port124
+ 0x2280,// MESS_BOT_R_CPLD I2C Master OSFP Port125
+ 0x22A0,// MESS_BOT_R_CPLD I2C Master OSFP Port126
+ 0x22C0,// MESS_BOT_R_CPLD I2C Master OSFP Port127
+ 0x22E0,// MESS_BOT_R_CPLD I2C Master OSFP Port128
+ 0x2500,// MESS_BOT_R_CPLD I2C Master SFP Port1
+ 0x2520,// MESS_BOT_R_CPLD I2C Master SFP Port2
+};
+
+static struct ocores_i2c_platform_data i2c_data = {
+ .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;
+ }
+
+ i2c_data.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 = &i2c_data;
+ p->dev.release = ftdi_release_platform_dev;
+ res = &ocores_resources[i];
+ switch (i)
+ {
+ case 0 ... 129:
+ bar_base = pci_resource_start(pcidev, BAR0_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-128/modules/i2c-ocores.c b/ixr7220h6-128/modules/i2c-ocores.c
new file mode 100644
index 0000000..a6c87b8
--- /dev/null
+++ b/ixr7220h6-128/modules/i2c-ocores.c
@@ -0,0 +1,1123 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * i2c-ocores.c: I2C bus driver for OpenCores I2C controller
+ * (https://opencores.org/project/i2c/overview)
+ *
+ * Peter Korsgaard
+ *
+ * Support for the GRLIB port of the controller by
+ * Andreas Larsson
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define OCORE_DRIVER_VERSION "2.0.0"
+
+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.");
+
+static uint param_timeout = 1;
+module_param(param_timeout, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(param_timeout, "for debugging. 1 ms should be enough");
+
+// spi mux mapping table
+// ex : arr_spi_mux [0] = port 1 = dev_id 0 = spi mux 0x04
+// ex : arr_spi_mux [32] = port 33 = dev_id 32 = spi mux 0x0
+static uint8_t arr_spi_mux[] = {
+ // top part 64 port
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+
+ // bottom part 64 port
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+
+ // SFP
+ 0x01, 0x01
+};
+
+#define PORT_NUM (128 + 2) /*128 OSFPs + 2 SFP28s*/
+static const uint arr_pcie_offset[PORT_NUM]= {
+ 0x2100,// MESS_TOP_L_CPLD I2C Master OSFP Port1
+ 0x2120,// MESS_TOP_L_CPLD I2C Master OSFP Port2
+ 0x2140,// MESS_TOP_L_CPLD I2C Master OSFP Port3
+ 0x2160,// MESS_TOP_L_CPLD I2C Master OSFP Port4
+ 0x2180,// MESS_TOP_L_CPLD I2C Master OSFP Port5
+ 0x21A0,// MESS_TOP_L_CPLD I2C Master OSFP Port6
+ 0x21C0,// MESS_TOP_L_CPLD I2C Master OSFP Port7
+ 0x21E0,// MESS_TOP_L_CPLD I2C Master OSFP Port8
+ 0x2200,// MESS_TOP_L_CPLD I2C Master OSFP Port9
+ 0x2220,// MESS_TOP_L_CPLD I2C Master OSFP Port10
+ 0x2240,// MESS_TOP_L_CPLD I2C Master OSFP Port11
+ 0x2260,// MESS_TOP_L_CPLD I2C Master OSFP Port12
+ 0x2280,// MESS_TOP_L_CPLD I2C Master OSFP Port13
+ 0x22A0,// MESS_TOP_L_CPLD I2C Master OSFP Port14
+ 0x22C0,// MESS_TOP_L_CPLD I2C Master OSFP Port15
+ 0x22E0,// MESS_TOP_L_CPLD I2C Master OSFP Port16
+ 0x2100,// MESS_TOP_R_CPLD I2C Master OSFP Port17
+ 0x2120,// MESS_TOP_R_CPLD I2C Master OSFP Port18
+ 0x2140,// MESS_TOP_R_CPLD I2C Master OSFP Port19
+ 0x2160,// MESS_TOP_R_CPLD I2C Master OSFP Port20
+ 0x2180,// MESS_TOP_R_CPLD I2C Master OSFP Port21
+ 0x21A0,// MESS_TOP_R_CPLD I2C Master OSFP Port22
+ 0x21C0,// MESS_TOP_R_CPLD I2C Master OSFP Port23
+ 0x21E0,// MESS_TOP_R_CPLD I2C Master OSFP Port24
+ 0x2200,// MESS_TOP_R_CPLD I2C Master OSFP Port25
+ 0x2220,// MESS_TOP_R_CPLD I2C Master OSFP Port26
+ 0x2240,// MESS_TOP_R_CPLD I2C Master OSFP Port27
+ 0x2260,// MESS_TOP_R_CPLD I2C Master OSFP Port28
+ 0x2280,// MESS_TOP_R_CPLD I2C Master OSFP Port29
+ 0x22A0,// MESS_TOP_R_CPLD I2C Master OSFP Port30
+ 0x22C0,// MESS_TOP_R_CPLD I2C Master OSFP Port31
+ 0x22E0,// MESS_TOP_R_CPLD I2C Master OSFP Port32
+//////////////////////////////////////////////////////
+ 0x2100,// PORTCPLD0 I2C Master OSFP Port33
+ 0x2120,// PORTCPLD0 I2C Master OSFP Port34
+ 0x2180,// PORTCPLD0 I2C Master OSFP Port35
+ 0x21A0,// PORTCPLD0 I2C Master OSFP Port36
+ 0x2200,// PORTCPLD0 I2C Master OSFP Port37
+ 0x2220,// PORTCPLD0 I2C Master OSFP Port38
+ 0x2280,// PORTCPLD0 I2C Master OSFP Port39
+ 0x22A0,// PORTCPLD0 I2C Master OSFP Port40
+ 0x2300,// PORTCPLD0 I2C Master OSFP Port41
+ 0x2320,// PORTCPLD0 I2C Master OSFP Port42
+ 0x2380,// PORTCPLD0 I2C Master OSFP Port43
+ 0x23A0,// PORTCPLD0 I2C Master OSFP Port44
+ 0x2400,// PORTCPLD0 I2C Master OSFP Port45
+ 0x2420,// PORTCPLD0 I2C Master OSFP Port46
+ 0x2480,// PORTCPLD0 I2C Master OSFP Port47
+ 0x24A0,// PORTCPLD0 I2C Master OSFP Port48
+ 0x2100,// PORTCPLD1 I2C Master OSFP Port49
+ 0x2120,// PORTCPLD1 I2C Master OSFP Port50
+ 0x2180,// PORTCPLD1 I2C Master OSFP Port51
+ 0x21A0,// PORTCPLD1 I2C Master OSFP Port52
+ 0x2200,// PORTCPLD1 I2C Master OSFP Port53
+ 0x2220,// PORTCPLD1 I2C Master OSFP Port54
+ 0x2280,// PORTCPLD1 I2C Master OSFP Port55
+ 0x22A0,// PORTCPLD1 I2C Master OSFP Port56
+ 0x2300,// PORTCPLD1 I2C Master OSFP Port57
+ 0x2320,// PORTCPLD1 I2C Master OSFP Port58
+ 0x2380,// PORTCPLD1 I2C Master OSFP Port59
+ 0x23A0,// PORTCPLD1 I2C Master OSFP Port60
+ 0x2400,// PORTCPLD1 I2C Master OSFP Port61
+ 0x2420,// PORTCPLD1 I2C Master OSFP Port62
+ 0x2480,// PORTCPLD1 I2C Master OSFP Port63
+ 0x24A0,// PORTCPLD1 I2C Master OSFP Port64
+//////////////////////////////////////////////////////
+ 0x2140,// PORTCPLD0 I2C Master OSFP Port65
+ 0x2160,// PORTCPLD0 I2C Master OSFP Port66
+ 0x21C0,// PORTCPLD0 I2C Master OSFP Port67
+ 0x21E0,// PORTCPLD0 I2C Master OSFP Port68
+ 0x2240,// PORTCPLD0 I2C Master OSFP Port69
+ 0x2260,// PORTCPLD0 I2C Master OSFP Port70
+ 0x22C0,// PORTCPLD0 I2C Master OSFP Port71
+ 0x22E0,// PORTCPLD0 I2C Master OSFP Port72
+ 0x2340,// PORTCPLD0 I2C Master OSFP Port73
+ 0x2360,// PORTCPLD0 I2C Master OSFP Port74
+ 0x23C0,// PORTCPLD0 I2C Master OSFP Port75
+ 0x23E0,// PORTCPLD0 I2C Master OSFP Port76
+ 0x2440,// PORTCPLD0 I2C Master OSFP Port77
+ 0x2460,// PORTCPLD0 I2C Master OSFP Port78
+ 0x24C0,// PORTCPLD0 I2C Master OSFP Port79
+ 0x24E0,// PORTCPLD0 I2C Master OSFP Port80
+ 0x2140,// PORTCPLD1 I2C Master OSFP Port81
+ 0x2160,// PORTCPLD1 I2C Master OSFP Port82
+ 0x21C0,// PORTCPLD1 I2C Master OSFP Port83
+ 0x21E0,// PORTCPLD1 I2C Master OSFP Port84
+ 0x2240,// PORTCPLD1 I2C Master OSFP Port85
+ 0x2260,// PORTCPLD1 I2C Master OSFP Port86
+ 0x22C0,// PORTCPLD1 I2C Master OSFP Port87
+ 0x22E0,// PORTCPLD1 I2C Master OSFP Port88
+ 0x2340,// PORTCPLD1 I2C Master OSFP Port89
+ 0x2360,// PORTCPLD1 I2C Master OSFP Port90
+ 0x23C0,// PORTCPLD1 I2C Master OSFP Port91
+ 0x23E0,// PORTCPLD1 I2C Master OSFP Port92
+ 0x2440,// PORTCPLD1 I2C Master OSFP Port93
+ 0x2460,// PORTCPLD1 I2C Master OSFP Port94
+ 0x24C0,// PORTCPLD1 I2C Master OSFP Port95
+ 0x24E0,// PORTCPLD1 I2C Master OSFP Port96
+//////////////////////////////////////////////////////
+ 0x2100,// MESS_BOT_L_CPLD I2C Master OSFP Port97
+ 0x2120,// MESS_BOT_L_CPLD I2C Master OSFP Port98
+ 0x2140,// MESS_BOT_L_CPLD I2C Master OSFP Port99
+ 0x2160,// MESS_BOT_L_CPLD I2C Master OSFP Port100
+ 0x2180,// MESS_BOT_L_CPLD I2C Master OSFP Port101
+ 0x21A0,// MESS_BOT_L_CPLD I2C Master OSFP Port102
+ 0x21C0,// MESS_BOT_L_CPLD I2C Master OSFP Port103
+ 0x21E0,// MESS_BOT_L_CPLD I2C Master OSFP Port104
+ 0x2200,// MESS_BOT_L_CPLD I2C Master OSFP Port105
+ 0x2220,// MESS_BOT_L_CPLD I2C Master OSFP Port106
+ 0x2240,// MESS_BOT_L_CPLD I2C Master OSFP Port107
+ 0x2260,// MESS_BOT_L_CPLD I2C Master OSFP Port108
+ 0x2280,// MESS_BOT_L_CPLD I2C Master OSFP Port109
+ 0x22A0,// MESS_BOT_L_CPLD I2C Master OSFP Port110
+ 0x22C0,// MESS_BOT_L_CPLD I2C Master OSFP Port111
+ 0x22E0,// MESS_BOT_L_CPLD I2C Master OSFP Port112
+ 0x2100,// MESS_BOT_R_CPLD I2C Master OSFP Port113
+ 0x2120,// MESS_BOT_R_CPLD I2C Master OSFP Port114
+ 0x2140,// MESS_BOT_R_CPLD I2C Master OSFP Port115
+ 0x2160,// MESS_BOT_R_CPLD I2C Master OSFP Port116
+ 0x2180,// MESS_BOT_R_CPLD I2C Master OSFP Port117
+ 0x21A0,// MESS_BOT_R_CPLD I2C Master OSFP Port118
+ 0x21C0,// MESS_BOT_R_CPLD I2C Master OSFP Port119
+ 0x21E0,// MESS_BOT_R_CPLD I2C Master OSFP Port120
+ 0x2200,// MESS_BOT_R_CPLD I2C Master OSFP Port121
+ 0x2220,// MESS_BOT_R_CPLD I2C Master OSFP Port122
+ 0x2240,// MESS_BOT_R_CPLD I2C Master OSFP Port123
+ 0x2260,// MESS_BOT_R_CPLD I2C Master OSFP Port124
+ 0x2280,// MESS_BOT_R_CPLD I2C Master OSFP Port125
+ 0x22A0,// MESS_BOT_R_CPLD I2C Master OSFP Port126
+ 0x22C0,// MESS_BOT_R_CPLD I2C Master OSFP Port127
+ 0x22E0,// MESS_BOT_R_CPLD I2C Master OSFP Port128
+ 0x2500,// MESS_BOT_R_CPLD I2C Master SFP Port1
+ 0x2520,// MESS_BOT_R_CPLD I2C Master SFP Port2
+};
+
+#define PCIE_SPI_MUX_OFFSET 0x2f00
+static phys_addr_t pcie_bar0_phys_addr = 0;
+static void __iomem *spi_mux_virt_base= NULL;
+/*
+ * '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);
+ u8 cached_pdev_id;
+};
+
+/* 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(param_timeout));
+ if (err)
+ dev_warn(i2c->adap.dev.parent,
+ "%s: STATUS timeout, bit 0x%x did not clear in %u ms\n",
+ __func__, mask, param_timeout);
+ return err;
+}
+
+/**
+ * It handles an IRQ-less transfer
+ * @i2c: ocores I2C device instance
+ *
+ * Even if IRQ are disabled, the I2C OpenCore IP behavior is exactly the same
+ * (only that IRQ are not produced). This means that we can re-use entirely
+ * ocores_isr(), we just add our polling code around it.
+ *
+ * It can run in atomic context
+ */
+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;
+ }
+ }
+}
+
+// spinlock_t pcie_mux_lock;
+static DEFINE_MUTEX(pcie_mux_lock);
+static volatile int last_id = -1;
+static int ocores_xfer_core(struct ocores_i2c *i2c,
+ struct i2c_msg *msgs, int num,
+ bool polling)
+{
+ int ret;
+ u8 ctrl;
+
+ if (i2c->cached_pdev_id >= ARRAY_SIZE(arr_spi_mux)) {
+ return -EINVAL;
+ }
+
+ mutex_lock(&pcie_mux_lock);
+
+ if (last_id != i2c->cached_pdev_id)
+ {
+ INFO("switch mux to %u i2c->cached_pdev_id =%u \n", arr_spi_mux[i2c->cached_pdev_id], i2c->cached_pdev_id);
+ iowrite8(arr_spi_mux[i2c->cached_pdev_id], spi_mux_virt_base);
+ last_id = i2c->cached_pdev_id;
+ (void)ioread8(spi_mux_virt_base);
+ }
+
+ 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 {
+ INFO("going to wait_event_timeout");
+ ret = wait_event_timeout(i2c->wait,
+ (i2c->state == STATE_ERROR) ||
+ (i2c->state == STATE_DONE), HZ);
+ if (ret == 0) {
+ ocores_process_timeout(i2c);
+ mutex_unlock(&pcie_mux_lock);
+ return -ETIMEDOUT;
+ }
+ }
+ ret = (i2c->state == STATE_DONE) ? num : -EIO;
+
+ mutex_unlock(&pcie_mux_lock);
+
+ return ret;
+}
+
+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;
+
+ 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);
+ dev_info(&pdev->dev,"dev id=%u \n", pdev->id);
+
+ i2c->cached_pdev_id = pdev->id;
+
+ mutex_lock(&pcie_mux_lock);
+
+ if (spi_mux_virt_base == NULL)
+ {
+ if (i2c->cached_pdev_id >= 0 && i2c->cached_pdev_id < ARRAY_SIZE(arr_pcie_offset))
+ {
+ pcie_bar0_phys_addr = res->start - arr_pcie_offset[i2c->cached_pdev_id];
+ spi_mux_virt_base = ioremap(pcie_bar0_phys_addr + PCIE_SPI_MUX_OFFSET, 4);
+ if (!spi_mux_virt_base)
+ {
+ mutex_unlock(&pcie_mux_lock);
+ printk(KERN_ERR "ioremap failed\n");
+ return -ENOMEM;
+ }
+ last_id = -1;
+ pr_info("Device %d: BAR0=0x%08llx, offset=0x%08x, mapped=0x%08llx,spi_mux_virt_base=0x%p\n",
+ i2c->cached_pdev_id,
+ pcie_bar0_phys_addr,
+ arr_pcie_offset[i2c->cached_pdev_id],
+ res->start,
+ spi_mux_virt_base);
+ }
+ }
+
+ if (spi_mux_virt_base != NULL) {
+ iowrite8(arr_spi_mux[i2c->cached_pdev_id], spi_mux_virt_base);
+ last_id = i2c->cached_pdev_id;
+ dev_info(&pdev->dev,"switch mux to %d \n", arr_spi_mux[i2c->cached_pdev_id]);
+ (void)ioread8(spi_mux_virt_base);
+ }
+ mutex_unlock(&pcie_mux_lock);
+
+ 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);
+
+ pr_info("ocores_i2c_resume\n");
+ mutex_lock(&pcie_mux_lock);
+ last_id = -1;
+ mutex_unlock(&pcie_mux_lock);
+
+ 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_init(void)
+{
+ int err;
+ pr_info("ocores init :timeout delay %u\n", param_timeout);
+ 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_exit(void)
+{
+ platform_driver_unregister(&ocores_i2c_driver);
+ if (spi_mux_virt_base) {
+ iounmap(spi_mux_virt_base);
+ spi_mux_virt_base= NULL;
+ }
+}
+
+module_init(ocores_i2c_init);
+module_exit(ocores_i2c_exit);
+
+MODULE_AUTHOR("Peter Korsgaard ");
+MODULE_DESCRIPTION("OpenCores I2C bus driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ocores-i2c");
+MODULE_VERSION(OCORE_DRIVER_VERSION);
diff --git a/ixr7220h6-128/modules/pmbus_psu.c b/ixr7220h6-128/modules/pmbus_psu.c
new file mode 100644
index 0000000..5f4d92e
--- /dev/null
+++ b/ixr7220h6-128/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-128/modules/port_cpld0.c b/ixr7220h6-128/modules/port_cpld0.c
new file mode 100644
index 0000000..2518f1b
--- /dev/null
+++ b/ixr7220h6-128/modules/port_cpld0.c
@@ -0,0 +1,665 @@
+// * CPLD driver for Nokia-7220-IXR-H6-128 Router
+// *
+// * Copyright (C) 2026 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 SCRATCH_REG 0x04
+#define PORT_LPMODE_REG0 0x70
+#define PORT_RST_REG0 0x78
+#define PORT_MODPRS_REG0 0x88
+#define PORT_PWGOOD_REG0 0x90
+#define PORT_ENABLE_REG0 0x98
+
+static const unsigned short cpld_address_list[] = {0x74, 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, PORT_RST_REG0);
+ val1 = cpld_i2c_read(data, PORT_RST_REG0 + 1);
+ val2 = cpld_i2c_read(data, PORT_RST_REG0 + 2);
+ val3 = cpld_i2c_read(data, PORT_RST_REG0 + 3);
+ dev_info(&client->dev, "[PORT_CPLD0]PORT_RESET_REG: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", val0, val1, val2, val3);
+
+ val0 = cpld_i2c_read(data, PORT_LPMODE_REG0);
+ val1 = cpld_i2c_read(data, PORT_LPMODE_REG0 + 1);
+ val2 = cpld_i2c_read(data, PORT_LPMODE_REG0 + 2);
+ val3 = cpld_i2c_read(data, PORT_LPMODE_REG0 + 3);
+ dev_info(&client->dev, "[PORT_CPLD0]PORT_LPMODE_REG: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", val0, val1, val2, val3);
+
+ val0 = cpld_i2c_read(data, PORT_MODPRS_REG0);
+ val1 = cpld_i2c_read(data, PORT_MODPRS_REG0 + 1);
+ val2 = cpld_i2c_read(data, PORT_MODPRS_REG0 + 2);
+ val3 = cpld_i2c_read(data, PORT_MODPRS_REG0 + 3);
+ dev_info(&client->dev, "[PORT_CPLD0]PORT_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, "%02x.%02x\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_port_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, PORT_LPMODE_REG0 + (sda->index / 8));
+
+ return sprintf(buf, "%d\n", (val>>(sda->index % 8)) & 0x1 ? 1:0);
+}
+
+static ssize_t set_port_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, PORT_LPMODE_REG0 + (sda->index / 8));
+ reg_val = reg_val & mask;
+ usr_val = usr_val << (sda->index % 8);
+ cpld_i2c_write(data, PORT_LPMODE_REG0 + (sda->index / 8), (reg_val | usr_val));
+
+ return count;
+}
+
+static ssize_t show_port_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, PORT_RST_REG0 + (sda->index / 8));
+
+ return sprintf(buf, "%d\n", (val>>(sda->index % 8)) & 0x1 ? 1:0);
+}
+
+static ssize_t set_port_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, PORT_RST_REG0 + (sda->index / 8));
+ reg_val = reg_val & mask;
+ usr_val = usr_val << (sda->index % 8);
+ cpld_i2c_write(data, PORT_RST_REG0 + (sda->index / 8), (reg_val | usr_val));
+
+ return count;
+}
+
+static ssize_t show_port_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, PORT_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, PORT_MODPRS_REG0 + sda->index);
+
+ return sprintf(buf, "0x%02x\n", val);
+}
+
+static ssize_t show_port_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;
+
+ return sprintf(buf, "na\n");
+ val = cpld_i2c_read(data, PORT_ENABLE_REG0 + (sda->index / 8));
+
+ return sprintf(buf, "%d\n", (val>>(sda->index % 8)) & 0x1 ? 1:0);
+}
+
+static ssize_t set_port_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;
+
+ return 0;
+ 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, PORT_ENABLE_REG0 + (sda->index / 8));
+ reg_val = reg_val & mask;
+ usr_val = usr_val << (sda->index % 8);
+ cpld_i2c_write(data, PORT_ENABLE_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_1_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 0);
+static SENSOR_DEVICE_ATTR(port_2_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 1);
+static SENSOR_DEVICE_ATTR(port_3_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 2);
+static SENSOR_DEVICE_ATTR(port_4_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 3);
+static SENSOR_DEVICE_ATTR(port_5_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 4);
+static SENSOR_DEVICE_ATTR(port_6_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 5);
+static SENSOR_DEVICE_ATTR(port_7_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 6);
+static SENSOR_DEVICE_ATTR(port_8_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 7);
+static SENSOR_DEVICE_ATTR(port_9_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 8);
+static SENSOR_DEVICE_ATTR(port_10_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 9);
+static SENSOR_DEVICE_ATTR(port_11_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 10);
+static SENSOR_DEVICE_ATTR(port_12_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 11);
+static SENSOR_DEVICE_ATTR(port_13_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 12);
+static SENSOR_DEVICE_ATTR(port_14_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 13);
+static SENSOR_DEVICE_ATTR(port_15_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 14);
+static SENSOR_DEVICE_ATTR(port_16_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 15);
+static SENSOR_DEVICE_ATTR(port_17_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 16);
+static SENSOR_DEVICE_ATTR(port_18_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 17);
+static SENSOR_DEVICE_ATTR(port_19_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 18);
+static SENSOR_DEVICE_ATTR(port_20_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 19);
+static SENSOR_DEVICE_ATTR(port_21_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 20);
+static SENSOR_DEVICE_ATTR(port_22_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 21);
+static SENSOR_DEVICE_ATTR(port_23_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 22);
+static SENSOR_DEVICE_ATTR(port_24_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 23);
+static SENSOR_DEVICE_ATTR(port_25_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 24);
+static SENSOR_DEVICE_ATTR(port_26_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 25);
+static SENSOR_DEVICE_ATTR(port_27_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 26);
+static SENSOR_DEVICE_ATTR(port_28_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 27);
+static SENSOR_DEVICE_ATTR(port_29_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 28);
+static SENSOR_DEVICE_ATTR(port_30_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 29);
+static SENSOR_DEVICE_ATTR(port_31_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 30);
+static SENSOR_DEVICE_ATTR(port_32_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 31);
+
+static SENSOR_DEVICE_ATTR(port_1_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 0);
+static SENSOR_DEVICE_ATTR(port_2_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 1);
+static SENSOR_DEVICE_ATTR(port_3_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 2);
+static SENSOR_DEVICE_ATTR(port_4_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 3);
+static SENSOR_DEVICE_ATTR(port_5_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 4);
+static SENSOR_DEVICE_ATTR(port_6_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 5);
+static SENSOR_DEVICE_ATTR(port_7_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 6);
+static SENSOR_DEVICE_ATTR(port_8_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 7);
+static SENSOR_DEVICE_ATTR(port_9_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 8);
+static SENSOR_DEVICE_ATTR(port_10_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 9);
+static SENSOR_DEVICE_ATTR(port_11_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 10);
+static SENSOR_DEVICE_ATTR(port_12_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 11);
+static SENSOR_DEVICE_ATTR(port_13_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 12);
+static SENSOR_DEVICE_ATTR(port_14_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 13);
+static SENSOR_DEVICE_ATTR(port_15_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 14);
+static SENSOR_DEVICE_ATTR(port_16_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 15);
+static SENSOR_DEVICE_ATTR(port_17_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 16);
+static SENSOR_DEVICE_ATTR(port_18_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 17);
+static SENSOR_DEVICE_ATTR(port_19_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 18);
+static SENSOR_DEVICE_ATTR(port_20_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 19);
+static SENSOR_DEVICE_ATTR(port_21_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 20);
+static SENSOR_DEVICE_ATTR(port_22_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 21);
+static SENSOR_DEVICE_ATTR(port_23_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 22);
+static SENSOR_DEVICE_ATTR(port_24_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 23);
+static SENSOR_DEVICE_ATTR(port_25_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 24);
+static SENSOR_DEVICE_ATTR(port_26_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 25);
+static SENSOR_DEVICE_ATTR(port_27_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 26);
+static SENSOR_DEVICE_ATTR(port_28_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 27);
+static SENSOR_DEVICE_ATTR(port_29_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 28);
+static SENSOR_DEVICE_ATTR(port_30_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 29);
+static SENSOR_DEVICE_ATTR(port_31_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 30);
+static SENSOR_DEVICE_ATTR(port_32_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 31);
+
+static SENSOR_DEVICE_ATTR(port_1_prs, S_IRUGO, show_port_prs, NULL, 0);
+static SENSOR_DEVICE_ATTR(port_2_prs, S_IRUGO, show_port_prs, NULL, 1);
+static SENSOR_DEVICE_ATTR(port_3_prs, S_IRUGO, show_port_prs, NULL, 2);
+static SENSOR_DEVICE_ATTR(port_4_prs, S_IRUGO, show_port_prs, NULL, 3);
+static SENSOR_DEVICE_ATTR(port_5_prs, S_IRUGO, show_port_prs, NULL, 4);
+static SENSOR_DEVICE_ATTR(port_6_prs, S_IRUGO, show_port_prs, NULL, 5);
+static SENSOR_DEVICE_ATTR(port_7_prs, S_IRUGO, show_port_prs, NULL, 6);
+static SENSOR_DEVICE_ATTR(port_8_prs, S_IRUGO, show_port_prs, NULL, 7);
+static SENSOR_DEVICE_ATTR(port_9_prs, S_IRUGO, show_port_prs, NULL, 8);
+static SENSOR_DEVICE_ATTR(port_10_prs, S_IRUGO, show_port_prs, NULL, 9);
+static SENSOR_DEVICE_ATTR(port_11_prs, S_IRUGO, show_port_prs, NULL, 10);
+static SENSOR_DEVICE_ATTR(port_12_prs, S_IRUGO, show_port_prs, NULL, 11);
+static SENSOR_DEVICE_ATTR(port_13_prs, S_IRUGO, show_port_prs, NULL, 12);
+static SENSOR_DEVICE_ATTR(port_14_prs, S_IRUGO, show_port_prs, NULL, 13);
+static SENSOR_DEVICE_ATTR(port_15_prs, S_IRUGO, show_port_prs, NULL, 14);
+static SENSOR_DEVICE_ATTR(port_16_prs, S_IRUGO, show_port_prs, NULL, 15);
+static SENSOR_DEVICE_ATTR(port_17_prs, S_IRUGO, show_port_prs, NULL, 16);
+static SENSOR_DEVICE_ATTR(port_18_prs, S_IRUGO, show_port_prs, NULL, 17);
+static SENSOR_DEVICE_ATTR(port_19_prs, S_IRUGO, show_port_prs, NULL, 18);
+static SENSOR_DEVICE_ATTR(port_20_prs, S_IRUGO, show_port_prs, NULL, 19);
+static SENSOR_DEVICE_ATTR(port_21_prs, S_IRUGO, show_port_prs, NULL, 20);
+static SENSOR_DEVICE_ATTR(port_22_prs, S_IRUGO, show_port_prs, NULL, 21);
+static SENSOR_DEVICE_ATTR(port_23_prs, S_IRUGO, show_port_prs, NULL, 22);
+static SENSOR_DEVICE_ATTR(port_24_prs, S_IRUGO, show_port_prs, NULL, 23);
+static SENSOR_DEVICE_ATTR(port_25_prs, S_IRUGO, show_port_prs, NULL, 24);
+static SENSOR_DEVICE_ATTR(port_26_prs, S_IRUGO, show_port_prs, NULL, 25);
+static SENSOR_DEVICE_ATTR(port_27_prs, S_IRUGO, show_port_prs, NULL, 26);
+static SENSOR_DEVICE_ATTR(port_28_prs, S_IRUGO, show_port_prs, NULL, 27);
+static SENSOR_DEVICE_ATTR(port_29_prs, S_IRUGO, show_port_prs, NULL, 28);
+static SENSOR_DEVICE_ATTR(port_30_prs, S_IRUGO, show_port_prs, NULL, 29);
+static SENSOR_DEVICE_ATTR(port_31_prs, S_IRUGO, show_port_prs, NULL, 30);
+static SENSOR_DEVICE_ATTR(port_32_prs, S_IRUGO, show_port_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_en, S_IRUGO, show_port_en, set_port_en, 0);
+static SENSOR_DEVICE_ATTR(port_2_en, S_IRUGO, show_port_en, set_port_en, 1);
+static SENSOR_DEVICE_ATTR(port_3_en, S_IRUGO, show_port_en, set_port_en, 2);
+static SENSOR_DEVICE_ATTR(port_4_en, S_IRUGO, show_port_en, set_port_en, 3);
+static SENSOR_DEVICE_ATTR(port_5_en, S_IRUGO, show_port_en, set_port_en, 4);
+static SENSOR_DEVICE_ATTR(port_6_en, S_IRUGO, show_port_en, set_port_en, 5);
+static SENSOR_DEVICE_ATTR(port_7_en, S_IRUGO, show_port_en, set_port_en, 6);
+static SENSOR_DEVICE_ATTR(port_8_en, S_IRUGO, show_port_en, set_port_en, 7);
+static SENSOR_DEVICE_ATTR(port_9_en, S_IRUGO, show_port_en, set_port_en, 8);
+static SENSOR_DEVICE_ATTR(port_10_en, S_IRUGO, show_port_en, set_port_en, 9);
+static SENSOR_DEVICE_ATTR(port_11_en, S_IRUGO, show_port_en, set_port_en, 10);
+static SENSOR_DEVICE_ATTR(port_12_en, S_IRUGO, show_port_en, set_port_en, 11);
+static SENSOR_DEVICE_ATTR(port_13_en, S_IRUGO, show_port_en, set_port_en, 12);
+static SENSOR_DEVICE_ATTR(port_14_en, S_IRUGO, show_port_en, set_port_en, 13);
+static SENSOR_DEVICE_ATTR(port_15_en, S_IRUGO, show_port_en, set_port_en, 14);
+static SENSOR_DEVICE_ATTR(port_16_en, S_IRUGO, show_port_en, set_port_en, 15);
+static SENSOR_DEVICE_ATTR(port_17_en, S_IRUGO, show_port_en, set_port_en, 16);
+static SENSOR_DEVICE_ATTR(port_18_en, S_IRUGO, show_port_en, set_port_en, 17);
+static SENSOR_DEVICE_ATTR(port_19_en, S_IRUGO, show_port_en, set_port_en, 18);
+static SENSOR_DEVICE_ATTR(port_20_en, S_IRUGO, show_port_en, set_port_en, 19);
+static SENSOR_DEVICE_ATTR(port_21_en, S_IRUGO, show_port_en, set_port_en, 20);
+static SENSOR_DEVICE_ATTR(port_22_en, S_IRUGO, show_port_en, set_port_en, 21);
+static SENSOR_DEVICE_ATTR(port_23_en, S_IRUGO, show_port_en, set_port_en, 22);
+static SENSOR_DEVICE_ATTR(port_24_en, S_IRUGO, show_port_en, set_port_en, 23);
+static SENSOR_DEVICE_ATTR(port_25_en, S_IRUGO, show_port_en, set_port_en, 24);
+static SENSOR_DEVICE_ATTR(port_26_en, S_IRUGO, show_port_en, set_port_en, 25);
+static SENSOR_DEVICE_ATTR(port_27_en, S_IRUGO, show_port_en, set_port_en, 26);
+static SENSOR_DEVICE_ATTR(port_28_en, S_IRUGO, show_port_en, set_port_en, 27);
+static SENSOR_DEVICE_ATTR(port_29_en, S_IRUGO, show_port_en, set_port_en, 28);
+static SENSOR_DEVICE_ATTR(port_30_en, S_IRUGO, show_port_en, set_port_en, 29);
+static SENSOR_DEVICE_ATTR(port_31_en, S_IRUGO, show_port_en, set_port_en, 30);
+static SENSOR_DEVICE_ATTR(port_32_en, S_IRUGO, show_port_en, set_port_en, 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_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_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_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_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_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_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_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_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_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,
+
+ 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, PORT_LPMODE_REG0, 0x0);
+ cpld_i2c_write(data, PORT_LPMODE_REG0+1, 0x0);
+ cpld_i2c_write(data, PORT_LPMODE_REG0+2, 0x0);
+ cpld_i2c_write(data, PORT_LPMODE_REG0+3, 0x0);
+ cpld_i2c_write(data, PORT_RST_REG0, 0x0);
+ cpld_i2c_write(data, PORT_RST_REG0+1, 0x0);
+ cpld_i2c_write(data, PORT_RST_REG0+2, 0x0);
+ cpld_i2c_write(data, PORT_RST_REG0+3, 0x0);
+ msleep(500);
+ cpld_i2c_write(data, PORT_RST_REG0, 0xFF);
+ cpld_i2c_write(data, PORT_RST_REG0+1, 0xFF);
+ cpld_i2c_write(data, PORT_RST_REG0+2, 0xFF);
+ cpld_i2c_write(data, PORT_RST_REG0+3, 0xFF);
+ dev_info(&client->dev, "[PORT_CPLD0]PORTs reset done.\n");
+ 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 = "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-128 PORT_CPLD0 driver");
+MODULE_LICENSE("GPL");
+
+module_init(port_cpld0_init);
+module_exit(port_cpld0_exit);
diff --git a/ixr7220h6-128/modules/port_cpld1.c b/ixr7220h6-128/modules/port_cpld1.c
new file mode 100644
index 0000000..81b94ff
--- /dev/null
+++ b/ixr7220h6-128/modules/port_cpld1.c
@@ -0,0 +1,758 @@
+// * CPLD driver for Nokia-7220-IXR-H6-128 Router
+// *
+// * Copyright (C) 2026 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 SFP_CTRL_REG 0x03
+#define SCRATCH_REG 0x04
+//#define SFP_MISC_REG 0x05
+#define SFP_TXFAULT_REG 0x10
+#define SFP_TXDIS_REG 0x18
+#define SFP_RXLOSS_REG 0x20
+#define SFP_MODPRS_REG 0x28
+#define PORT_LPMODE_REG0 0x70
+#define PORT_RST_REG0 0x78
+#define PORT_MODPRS_REG0 0x88
+#define PORT_PWGOOD_REG0 0x90
+#define PORT_ENABLE_REG0 0x98
+
+static const unsigned short cpld_address_list[] = {0x75, 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, PORT_RST_REG0);
+ val1 = cpld_i2c_read(data, PORT_RST_REG0 + 1);
+ val2 = cpld_i2c_read(data, PORT_RST_REG0 + 2);
+ val3 = cpld_i2c_read(data, PORT_RST_REG0 + 3);
+ dev_info(&client->dev, "[PORT_CPLD1]PORT_RESET_REG: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", val0, val1, val2, val3);
+
+ val0 = cpld_i2c_read(data, PORT_LPMODE_REG0);
+ val1 = cpld_i2c_read(data, PORT_LPMODE_REG0 + 1);
+ val2 = cpld_i2c_read(data, PORT_LPMODE_REG0 + 2);
+ val3 = cpld_i2c_read(data, PORT_LPMODE_REG0 + 3);
+ dev_info(&client->dev, "[PORT_CPLD1]PORT_LPMODE_REG: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", val0, val1, val2, val3);
+
+ val0 = cpld_i2c_read(data, PORT_MODPRS_REG0);
+ val1 = cpld_i2c_read(data, PORT_MODPRS_REG0 + 1);
+ val2 = cpld_i2c_read(data, PORT_MODPRS_REG0 + 2);
+ val3 = cpld_i2c_read(data, PORT_MODPRS_REG0 + 3);
+ dev_info(&client->dev, "[PORT_CPLD1]PORT_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, "%02x.%02x\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_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_port_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, PORT_LPMODE_REG0 + (sda->index / 8));
+
+ return sprintf(buf, "%d\n", (val>>(sda->index % 8)) & 0x1 ? 1:0);
+}
+
+static ssize_t set_port_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, PORT_LPMODE_REG0 + (sda->index / 8));
+ reg_val = reg_val & mask;
+ usr_val = usr_val << (sda->index % 8);
+ cpld_i2c_write(data, PORT_LPMODE_REG0 + (sda->index / 8), (reg_val | usr_val));
+
+ return count;
+}
+
+static ssize_t show_port_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, PORT_RST_REG0 + (sda->index / 8));
+
+ return sprintf(buf, "%d\n", (val>>(sda->index % 8)) & 0x1 ? 1:0);
+}
+
+static ssize_t set_port_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, PORT_RST_REG0 + (sda->index / 8));
+ reg_val = reg_val & mask;
+ usr_val = usr_val << (sda->index % 8);
+ cpld_i2c_write(data, PORT_RST_REG0 + (sda->index / 8), (reg_val | usr_val));
+
+ return count;
+}
+
+static ssize_t show_port_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, PORT_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, PORT_MODPRS_REG0 + sda->index);
+
+ return sprintf(buf, "0x%02x\n", val);
+}
+
+static ssize_t show_port_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;
+
+ return sprintf(buf, "na\n");
+ val = cpld_i2c_read(data, PORT_ENABLE_REG0 + (sda->index / 8));
+
+ return sprintf(buf, "%d\n", (val>>(sda->index % 8)) & 0x1 ? 1:0);
+}
+
+static ssize_t set_port_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;
+
+ return 0;
+ 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, PORT_ENABLE_REG0 + (sda->index / 8));
+ reg_val = reg_val & mask;
+ usr_val = usr_val << (sda->index % 8);
+ cpld_i2c_write(data, PORT_ENABLE_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_33_tx_fault, S_IRUGO, show_sfp_tx_fault, NULL, 0);
+static SENSOR_DEVICE_ATTR(port_34_tx_fault, S_IRUGO, show_sfp_tx_fault, NULL, 1);
+static SENSOR_DEVICE_ATTR(port_33_tx_en, S_IRUGO | S_IWUSR, show_sfp_tx_en, set_sfp_tx_en, 0);
+static SENSOR_DEVICE_ATTR(port_34_tx_en, S_IRUGO | S_IWUSR, show_sfp_tx_en, set_sfp_tx_en, 1);
+static SENSOR_DEVICE_ATTR(port_33_rx_los, S_IRUGO, show_sfp_rx_los, NULL, 0);
+static SENSOR_DEVICE_ATTR(port_34_rx_los, S_IRUGO, show_sfp_rx_los, NULL, 1);
+static SENSOR_DEVICE_ATTR(port_33_prs, S_IRUGO, show_sfp_prs, NULL, 0);
+static SENSOR_DEVICE_ATTR(port_34_prs, S_IRUGO, show_sfp_prs, NULL, 1);
+
+static SENSOR_DEVICE_ATTR(port_1_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 0);
+static SENSOR_DEVICE_ATTR(port_2_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 1);
+static SENSOR_DEVICE_ATTR(port_3_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 2);
+static SENSOR_DEVICE_ATTR(port_4_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 3);
+static SENSOR_DEVICE_ATTR(port_5_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 4);
+static SENSOR_DEVICE_ATTR(port_6_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 5);
+static SENSOR_DEVICE_ATTR(port_7_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 6);
+static SENSOR_DEVICE_ATTR(port_8_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 7);
+static SENSOR_DEVICE_ATTR(port_9_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 8);
+static SENSOR_DEVICE_ATTR(port_10_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 9);
+static SENSOR_DEVICE_ATTR(port_11_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 10);
+static SENSOR_DEVICE_ATTR(port_12_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 11);
+static SENSOR_DEVICE_ATTR(port_13_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 12);
+static SENSOR_DEVICE_ATTR(port_14_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 13);
+static SENSOR_DEVICE_ATTR(port_15_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 14);
+static SENSOR_DEVICE_ATTR(port_16_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 15);
+static SENSOR_DEVICE_ATTR(port_17_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 16);
+static SENSOR_DEVICE_ATTR(port_18_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 17);
+static SENSOR_DEVICE_ATTR(port_19_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 18);
+static SENSOR_DEVICE_ATTR(port_20_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 19);
+static SENSOR_DEVICE_ATTR(port_21_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 20);
+static SENSOR_DEVICE_ATTR(port_22_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 21);
+static SENSOR_DEVICE_ATTR(port_23_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 22);
+static SENSOR_DEVICE_ATTR(port_24_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 23);
+static SENSOR_DEVICE_ATTR(port_25_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 24);
+static SENSOR_DEVICE_ATTR(port_26_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 25);
+static SENSOR_DEVICE_ATTR(port_27_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 26);
+static SENSOR_DEVICE_ATTR(port_28_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 27);
+static SENSOR_DEVICE_ATTR(port_29_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 28);
+static SENSOR_DEVICE_ATTR(port_30_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 29);
+static SENSOR_DEVICE_ATTR(port_31_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 30);
+static SENSOR_DEVICE_ATTR(port_32_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 31);
+
+static SENSOR_DEVICE_ATTR(port_1_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 0);
+static SENSOR_DEVICE_ATTR(port_2_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 1);
+static SENSOR_DEVICE_ATTR(port_3_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 2);
+static SENSOR_DEVICE_ATTR(port_4_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 3);
+static SENSOR_DEVICE_ATTR(port_5_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 4);
+static SENSOR_DEVICE_ATTR(port_6_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 5);
+static SENSOR_DEVICE_ATTR(port_7_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 6);
+static SENSOR_DEVICE_ATTR(port_8_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 7);
+static SENSOR_DEVICE_ATTR(port_9_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 8);
+static SENSOR_DEVICE_ATTR(port_10_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 9);
+static SENSOR_DEVICE_ATTR(port_11_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 10);
+static SENSOR_DEVICE_ATTR(port_12_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 11);
+static SENSOR_DEVICE_ATTR(port_13_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 12);
+static SENSOR_DEVICE_ATTR(port_14_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 13);
+static SENSOR_DEVICE_ATTR(port_15_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 14);
+static SENSOR_DEVICE_ATTR(port_16_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 15);
+static SENSOR_DEVICE_ATTR(port_17_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 16);
+static SENSOR_DEVICE_ATTR(port_18_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 17);
+static SENSOR_DEVICE_ATTR(port_19_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 18);
+static SENSOR_DEVICE_ATTR(port_20_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 19);
+static SENSOR_DEVICE_ATTR(port_21_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 20);
+static SENSOR_DEVICE_ATTR(port_22_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 21);
+static SENSOR_DEVICE_ATTR(port_23_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 22);
+static SENSOR_DEVICE_ATTR(port_24_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 23);
+static SENSOR_DEVICE_ATTR(port_25_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 24);
+static SENSOR_DEVICE_ATTR(port_26_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 25);
+static SENSOR_DEVICE_ATTR(port_27_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 26);
+static SENSOR_DEVICE_ATTR(port_28_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 27);
+static SENSOR_DEVICE_ATTR(port_29_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 28);
+static SENSOR_DEVICE_ATTR(port_30_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 29);
+static SENSOR_DEVICE_ATTR(port_31_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 30);
+static SENSOR_DEVICE_ATTR(port_32_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 31);
+
+static SENSOR_DEVICE_ATTR(port_1_prs, S_IRUGO, show_port_prs, NULL, 0);
+static SENSOR_DEVICE_ATTR(port_2_prs, S_IRUGO, show_port_prs, NULL, 1);
+static SENSOR_DEVICE_ATTR(port_3_prs, S_IRUGO, show_port_prs, NULL, 2);
+static SENSOR_DEVICE_ATTR(port_4_prs, S_IRUGO, show_port_prs, NULL, 3);
+static SENSOR_DEVICE_ATTR(port_5_prs, S_IRUGO, show_port_prs, NULL, 4);
+static SENSOR_DEVICE_ATTR(port_6_prs, S_IRUGO, show_port_prs, NULL, 5);
+static SENSOR_DEVICE_ATTR(port_7_prs, S_IRUGO, show_port_prs, NULL, 6);
+static SENSOR_DEVICE_ATTR(port_8_prs, S_IRUGO, show_port_prs, NULL, 7);
+static SENSOR_DEVICE_ATTR(port_9_prs, S_IRUGO, show_port_prs, NULL, 8);
+static SENSOR_DEVICE_ATTR(port_10_prs, S_IRUGO, show_port_prs, NULL, 9);
+static SENSOR_DEVICE_ATTR(port_11_prs, S_IRUGO, show_port_prs, NULL, 10);
+static SENSOR_DEVICE_ATTR(port_12_prs, S_IRUGO, show_port_prs, NULL, 11);
+static SENSOR_DEVICE_ATTR(port_13_prs, S_IRUGO, show_port_prs, NULL, 12);
+static SENSOR_DEVICE_ATTR(port_14_prs, S_IRUGO, show_port_prs, NULL, 13);
+static SENSOR_DEVICE_ATTR(port_15_prs, S_IRUGO, show_port_prs, NULL, 14);
+static SENSOR_DEVICE_ATTR(port_16_prs, S_IRUGO, show_port_prs, NULL, 15);
+static SENSOR_DEVICE_ATTR(port_17_prs, S_IRUGO, show_port_prs, NULL, 16);
+static SENSOR_DEVICE_ATTR(port_18_prs, S_IRUGO, show_port_prs, NULL, 17);
+static SENSOR_DEVICE_ATTR(port_19_prs, S_IRUGO, show_port_prs, NULL, 18);
+static SENSOR_DEVICE_ATTR(port_20_prs, S_IRUGO, show_port_prs, NULL, 19);
+static SENSOR_DEVICE_ATTR(port_21_prs, S_IRUGO, show_port_prs, NULL, 20);
+static SENSOR_DEVICE_ATTR(port_22_prs, S_IRUGO, show_port_prs, NULL, 21);
+static SENSOR_DEVICE_ATTR(port_23_prs, S_IRUGO, show_port_prs, NULL, 22);
+static SENSOR_DEVICE_ATTR(port_24_prs, S_IRUGO, show_port_prs, NULL, 23);
+static SENSOR_DEVICE_ATTR(port_25_prs, S_IRUGO, show_port_prs, NULL, 24);
+static SENSOR_DEVICE_ATTR(port_26_prs, S_IRUGO, show_port_prs, NULL, 25);
+static SENSOR_DEVICE_ATTR(port_27_prs, S_IRUGO, show_port_prs, NULL, 26);
+static SENSOR_DEVICE_ATTR(port_28_prs, S_IRUGO, show_port_prs, NULL, 27);
+static SENSOR_DEVICE_ATTR(port_29_prs, S_IRUGO, show_port_prs, NULL, 28);
+static SENSOR_DEVICE_ATTR(port_30_prs, S_IRUGO, show_port_prs, NULL, 29);
+static SENSOR_DEVICE_ATTR(port_31_prs, S_IRUGO, show_port_prs, NULL, 30);
+static SENSOR_DEVICE_ATTR(port_32_prs, S_IRUGO, show_port_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_en, S_IRUGO, show_port_en, set_port_en, 0);
+static SENSOR_DEVICE_ATTR(port_2_en, S_IRUGO, show_port_en, set_port_en, 1);
+static SENSOR_DEVICE_ATTR(port_3_en, S_IRUGO, show_port_en, set_port_en, 2);
+static SENSOR_DEVICE_ATTR(port_4_en, S_IRUGO, show_port_en, set_port_en, 3);
+static SENSOR_DEVICE_ATTR(port_5_en, S_IRUGO, show_port_en, set_port_en, 4);
+static SENSOR_DEVICE_ATTR(port_6_en, S_IRUGO, show_port_en, set_port_en, 5);
+static SENSOR_DEVICE_ATTR(port_7_en, S_IRUGO, show_port_en, set_port_en, 6);
+static SENSOR_DEVICE_ATTR(port_8_en, S_IRUGO, show_port_en, set_port_en, 7);
+static SENSOR_DEVICE_ATTR(port_9_en, S_IRUGO, show_port_en, set_port_en, 8);
+static SENSOR_DEVICE_ATTR(port_10_en, S_IRUGO, show_port_en, set_port_en, 9);
+static SENSOR_DEVICE_ATTR(port_11_en, S_IRUGO, show_port_en, set_port_en, 10);
+static SENSOR_DEVICE_ATTR(port_12_en, S_IRUGO, show_port_en, set_port_en, 11);
+static SENSOR_DEVICE_ATTR(port_13_en, S_IRUGO, show_port_en, set_port_en, 12);
+static SENSOR_DEVICE_ATTR(port_14_en, S_IRUGO, show_port_en, set_port_en, 13);
+static SENSOR_DEVICE_ATTR(port_15_en, S_IRUGO, show_port_en, set_port_en, 14);
+static SENSOR_DEVICE_ATTR(port_16_en, S_IRUGO, show_port_en, set_port_en, 15);
+static SENSOR_DEVICE_ATTR(port_17_en, S_IRUGO, show_port_en, set_port_en, 16);
+static SENSOR_DEVICE_ATTR(port_18_en, S_IRUGO, show_port_en, set_port_en, 17);
+static SENSOR_DEVICE_ATTR(port_19_en, S_IRUGO, show_port_en, set_port_en, 18);
+static SENSOR_DEVICE_ATTR(port_20_en, S_IRUGO, show_port_en, set_port_en, 19);
+static SENSOR_DEVICE_ATTR(port_21_en, S_IRUGO, show_port_en, set_port_en, 20);
+static SENSOR_DEVICE_ATTR(port_22_en, S_IRUGO, show_port_en, set_port_en, 21);
+static SENSOR_DEVICE_ATTR(port_23_en, S_IRUGO, show_port_en, set_port_en, 22);
+static SENSOR_DEVICE_ATTR(port_24_en, S_IRUGO, show_port_en, set_port_en, 23);
+static SENSOR_DEVICE_ATTR(port_25_en, S_IRUGO, show_port_en, set_port_en, 24);
+static SENSOR_DEVICE_ATTR(port_26_en, S_IRUGO, show_port_en, set_port_en, 25);
+static SENSOR_DEVICE_ATTR(port_27_en, S_IRUGO, show_port_en, set_port_en, 26);
+static SENSOR_DEVICE_ATTR(port_28_en, S_IRUGO, show_port_en, set_port_en, 27);
+static SENSOR_DEVICE_ATTR(port_29_en, S_IRUGO, show_port_en, set_port_en, 28);
+static SENSOR_DEVICE_ATTR(port_30_en, S_IRUGO, show_port_en, set_port_en, 29);
+static SENSOR_DEVICE_ATTR(port_31_en, S_IRUGO, show_port_en, set_port_en, 30);
+static SENSOR_DEVICE_ATTR(port_32_en, S_IRUGO, show_port_en, set_port_en, 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_33_tx_fault.dev_attr.attr,
+ &sensor_dev_attr_port_34_tx_fault.dev_attr.attr,
+ &sensor_dev_attr_port_33_tx_en.dev_attr.attr,
+ &sensor_dev_attr_port_34_tx_en.dev_attr.attr,
+ &sensor_dev_attr_port_33_rx_los.dev_attr.attr,
+ &sensor_dev_attr_port_34_rx_los.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_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_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_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_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_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_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_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_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_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,
+
+ 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, PORT_LPMODE_REG0, 0x0);
+ cpld_i2c_write(data, PORT_LPMODE_REG0+1, 0x0);
+ cpld_i2c_write(data, PORT_LPMODE_REG0+2, 0x0);
+ cpld_i2c_write(data, PORT_LPMODE_REG0+3, 0x0);
+ cpld_i2c_write(data, PORT_RST_REG0, 0x0);
+ cpld_i2c_write(data, PORT_RST_REG0+1, 0x0);
+ cpld_i2c_write(data, PORT_RST_REG0+2, 0x0);
+ cpld_i2c_write(data, PORT_RST_REG0+3, 0x0);
+ msleep(500);
+ cpld_i2c_write(data, PORT_RST_REG0, 0xFF);
+ cpld_i2c_write(data, PORT_RST_REG0+1, 0xFF);
+ cpld_i2c_write(data, PORT_RST_REG0+2, 0xFF);
+ cpld_i2c_write(data, PORT_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 = "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-128 PORT_CPLD1 driver");
+MODULE_LICENSE("GPL");
+
+module_init(port_cpld1_init);
+module_exit(port_cpld1_exit);
diff --git a/ixr7220h6-128/modules/port_cpld2.c b/ixr7220h6-128/modules/port_cpld2.c
new file mode 100644
index 0000000..8d1c3ae
--- /dev/null
+++ b/ixr7220h6-128/modules/port_cpld2.c
@@ -0,0 +1,556 @@
+// * CPLD driver for Nokia-7220-IXR-H6-128 Router
+// *
+// * Copyright (C) 2026 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_cpld2"
+
+// REGISTERS ADDRESS MAP
+#define VER_MAJOR_REG 0x00
+#define VER_MINOR_REG 0x01
+#define SCRATCH_REG 0x04
+#define PORT_LPMODE_REG0 0x70
+#define PORT_EFUSE_REG0 0x72
+#define PORT_RST_REG0 0x78
+#define PORT_MODPRS_REG0 0x88
+#define PORT_PWGOOD_REG0 0x90
+#define PORT_ENABLE_REG0 0x98
+
+static const unsigned short cpld_address_list[] = {0x73, 0x76, I2C_CLIENT_END};
+
+struct cpld_data {
+ struct i2c_client *client;
+ struct mutex update_lock;
+ int port_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 void dump_reg(struct cpld_data *data)
+{
+ struct i2c_client *client = data->client;
+ u8 val0 = 0;
+ u8 val1 = 0;
+
+ val0 = cpld_i2c_read(data, PORT_RST_REG0);
+ val1 = cpld_i2c_read(data, PORT_RST_REG0 + 1);
+ dev_info(&client->dev, "[PORT_CPLD2]PORT_RESET_REG: 0x%02x, 0x%02x\n", val0, val1);
+
+ val0 = cpld_i2c_read(data, PORT_LPMODE_REG0);
+ val1 = cpld_i2c_read(data, PORT_LPMODE_REG0 + 1);
+ dev_info(&client->dev, "[PORT_CPLD2]PORT_LPMODE_REG: 0x%02x, 0x%02x\n", val0, val1);
+
+ val0 = cpld_i2c_read(data, PORT_MODPRS_REG0);
+ val1 = cpld_i2c_read(data, PORT_MODPRS_REG0 + 1);
+ dev_info(&client->dev, "[PORT_CPLD2]PORT_MODPRES_REG: 0x%02x, 0x%02x\n", val0, val1);
+
+}
+
+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, "%02x.%02x\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_port_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, PORT_LPMODE_REG0 + (sda->index / 8));
+
+ return sprintf(buf, "%d\n", (val>>(sda->index % 8)) & 0x1 ? 1:0);
+}
+
+static ssize_t set_port_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, PORT_LPMODE_REG0 + (sda->index / 8));
+ reg_val = reg_val & mask;
+ usr_val = usr_val << (sda->index % 8);
+ cpld_i2c_write(data, PORT_LPMODE_REG0 + (sda->index / 8), (reg_val | usr_val));
+
+ return count;
+}
+
+static ssize_t show_port_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, PORT_RST_REG0 + (sda->index / 8));
+
+ return sprintf(buf, "%d\n", (val>>(sda->index % 8)) & 0x1 ? 1:0);
+}
+
+static ssize_t set_port_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, PORT_RST_REG0 + (sda->index / 8));
+ reg_val = reg_val & mask;
+ usr_val = usr_val << (sda->index % 8);
+ cpld_i2c_write(data, PORT_RST_REG0 + (sda->index / 8), (reg_val | usr_val));
+
+ return count;
+}
+
+static ssize_t show_port_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, PORT_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, PORT_MODPRS_REG0 + sda->index);
+
+ return sprintf(buf, "0x%02x\n", val);
+}
+
+static ssize_t show_port_efuse(struct device *dev, struct device_attribute *devattr, char *buf)
+{
+ struct cpld_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", (data->port_efuse) ? "Enabled":"Disabled");
+}
+
+static ssize_t set_port_efuse(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count)
+{
+ struct cpld_data *data = dev_get_drvdata(dev);
+ const char *str_en = "Enable\n";
+ const char *str_dis = "Disable\n";
+
+ if (strcmp(buf, str_en) == 0) {
+ cpld_i2c_write(data, PORT_EFUSE_REG0, 0xFF);
+ cpld_i2c_write(data, PORT_EFUSE_REG0+1, 0xFF);
+ data->port_efuse = 1;
+ }
+ else if (strcmp(buf, str_dis) == 0) {
+ cpld_i2c_write(data, PORT_EFUSE_REG0, 0x0);
+ cpld_i2c_write(data, PORT_EFUSE_REG0+1, 0x0);
+ data->port_efuse = 0;
+ }
+ else
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t show_port_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;
+
+ return sprintf(buf, "na\n");
+ val = cpld_i2c_read(data, PORT_ENABLE_REG0 + (sda->index / 8));
+
+ return sprintf(buf, "%d\n", (val>>(sda->index % 8)) & 0x1 ? 1:0);
+}
+
+static ssize_t set_port_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;
+
+ return 0;
+ 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, PORT_ENABLE_REG0 + (sda->index / 8));
+ reg_val = reg_val & mask;
+ usr_val = usr_val << (sda->index % 8);
+ cpld_i2c_write(data, PORT_ENABLE_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_1_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 0);
+static SENSOR_DEVICE_ATTR(port_2_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 1);
+static SENSOR_DEVICE_ATTR(port_3_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 2);
+static SENSOR_DEVICE_ATTR(port_4_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 3);
+static SENSOR_DEVICE_ATTR(port_5_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 4);
+static SENSOR_DEVICE_ATTR(port_6_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 5);
+static SENSOR_DEVICE_ATTR(port_7_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 6);
+static SENSOR_DEVICE_ATTR(port_8_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 7);
+static SENSOR_DEVICE_ATTR(port_9_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 8);
+static SENSOR_DEVICE_ATTR(port_10_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 9);
+static SENSOR_DEVICE_ATTR(port_11_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 10);
+static SENSOR_DEVICE_ATTR(port_12_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 11);
+static SENSOR_DEVICE_ATTR(port_13_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 12);
+static SENSOR_DEVICE_ATTR(port_14_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 13);
+static SENSOR_DEVICE_ATTR(port_15_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 14);
+static SENSOR_DEVICE_ATTR(port_16_lpmod, S_IRUGO | S_IWUSR, show_port_lpmode, set_port_lpmode, 15);
+
+static SENSOR_DEVICE_ATTR(port_1_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 0);
+static SENSOR_DEVICE_ATTR(port_2_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 1);
+static SENSOR_DEVICE_ATTR(port_3_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 2);
+static SENSOR_DEVICE_ATTR(port_4_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 3);
+static SENSOR_DEVICE_ATTR(port_5_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 4);
+static SENSOR_DEVICE_ATTR(port_6_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 5);
+static SENSOR_DEVICE_ATTR(port_7_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 6);
+static SENSOR_DEVICE_ATTR(port_8_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 7);
+static SENSOR_DEVICE_ATTR(port_9_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 8);
+static SENSOR_DEVICE_ATTR(port_10_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 9);
+static SENSOR_DEVICE_ATTR(port_11_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 10);
+static SENSOR_DEVICE_ATTR(port_12_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 11);
+static SENSOR_DEVICE_ATTR(port_13_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 12);
+static SENSOR_DEVICE_ATTR(port_14_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 13);
+static SENSOR_DEVICE_ATTR(port_15_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 14);
+static SENSOR_DEVICE_ATTR(port_16_rst, S_IRUGO | S_IWUSR, show_port_rst, set_port_rst, 15);
+
+static SENSOR_DEVICE_ATTR(port_1_prs, S_IRUGO, show_port_prs, NULL, 0);
+static SENSOR_DEVICE_ATTR(port_2_prs, S_IRUGO, show_port_prs, NULL, 1);
+static SENSOR_DEVICE_ATTR(port_3_prs, S_IRUGO, show_port_prs, NULL, 2);
+static SENSOR_DEVICE_ATTR(port_4_prs, S_IRUGO, show_port_prs, NULL, 3);
+static SENSOR_DEVICE_ATTR(port_5_prs, S_IRUGO, show_port_prs, NULL, 4);
+static SENSOR_DEVICE_ATTR(port_6_prs, S_IRUGO, show_port_prs, NULL, 5);
+static SENSOR_DEVICE_ATTR(port_7_prs, S_IRUGO, show_port_prs, NULL, 6);
+static SENSOR_DEVICE_ATTR(port_8_prs, S_IRUGO, show_port_prs, NULL, 7);
+static SENSOR_DEVICE_ATTR(port_9_prs, S_IRUGO, show_port_prs, NULL, 8);
+static SENSOR_DEVICE_ATTR(port_10_prs, S_IRUGO, show_port_prs, NULL, 9);
+static SENSOR_DEVICE_ATTR(port_11_prs, S_IRUGO, show_port_prs, NULL, 10);
+static SENSOR_DEVICE_ATTR(port_12_prs, S_IRUGO, show_port_prs, NULL, 11);
+static SENSOR_DEVICE_ATTR(port_13_prs, S_IRUGO, show_port_prs, NULL, 12);
+static SENSOR_DEVICE_ATTR(port_14_prs, S_IRUGO, show_port_prs, NULL, 13);
+static SENSOR_DEVICE_ATTR(port_15_prs, S_IRUGO, show_port_prs, NULL, 14);
+static SENSOR_DEVICE_ATTR(port_16_prs, S_IRUGO, show_port_prs, NULL, 15);
+
+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(port_1_en, S_IRUGO, show_port_en, set_port_en, 0);
+static SENSOR_DEVICE_ATTR(port_2_en, S_IRUGO, show_port_en, set_port_en, 1);
+static SENSOR_DEVICE_ATTR(port_3_en, S_IRUGO, show_port_en, set_port_en, 2);
+static SENSOR_DEVICE_ATTR(port_4_en, S_IRUGO, show_port_en, set_port_en, 3);
+static SENSOR_DEVICE_ATTR(port_5_en, S_IRUGO, show_port_en, set_port_en, 4);
+static SENSOR_DEVICE_ATTR(port_6_en, S_IRUGO, show_port_en, set_port_en, 5);
+static SENSOR_DEVICE_ATTR(port_7_en, S_IRUGO, show_port_en, set_port_en, 6);
+static SENSOR_DEVICE_ATTR(port_8_en, S_IRUGO, show_port_en, set_port_en, 7);
+static SENSOR_DEVICE_ATTR(port_9_en, S_IRUGO, show_port_en, set_port_en, 8);
+static SENSOR_DEVICE_ATTR(port_10_en, S_IRUGO, show_port_en, set_port_en, 9);
+static SENSOR_DEVICE_ATTR(port_11_en, S_IRUGO, show_port_en, set_port_en, 10);
+static SENSOR_DEVICE_ATTR(port_12_en, S_IRUGO, show_port_en, set_port_en, 11);
+static SENSOR_DEVICE_ATTR(port_13_en, S_IRUGO, show_port_en, set_port_en, 12);
+static SENSOR_DEVICE_ATTR(port_14_en, S_IRUGO, show_port_en, set_port_en, 13);
+static SENSOR_DEVICE_ATTR(port_15_en, S_IRUGO, show_port_en, set_port_en, 14);
+static SENSOR_DEVICE_ATTR(port_16_en, S_IRUGO, show_port_en, set_port_en, 15);
+
+static SENSOR_DEVICE_ATTR(port_efuse, S_IRUGO | S_IWUSR, show_port_efuse, set_port_efuse, 0);
+
+static struct attribute *port_cpld2_attributes[] = {
+ &sensor_dev_attr_version.dev_attr.attr,
+ &sensor_dev_attr_scratch.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_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_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_modprs_reg1.dev_attr.attr,
+ &sensor_dev_attr_modprs_reg2.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_efuse.dev_attr.attr,
+
+ NULL
+};
+
+static const struct attribute_group port_cpld2_group = {
+ .attrs = port_cpld2_attributes,
+};
+
+static int port_cpld2_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_CPLD2 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_cpld2_group);
+ if (status) {
+ dev_err(&client->dev, "CPLD INIT ERROR: Cannot create sysfs\n");
+ goto exit;
+ }
+
+ dump_reg(data);
+ cpld_i2c_write(data, PORT_EFUSE_REG0, 0xFF);
+ cpld_i2c_write(data, PORT_EFUSE_REG0+1, 0xFF);
+ dev_info(&client->dev, "[PORT_CPLD2]Reseting PORTs ...\n");
+ cpld_i2c_write(data, PORT_LPMODE_REG0, 0x0);
+ cpld_i2c_write(data, PORT_LPMODE_REG0+1, 0x0);
+ cpld_i2c_write(data, PORT_RST_REG0, 0x0);
+ cpld_i2c_write(data, PORT_RST_REG0+1, 0x0);
+ msleep(500);
+ cpld_i2c_write(data, PORT_RST_REG0, 0xFF);
+ cpld_i2c_write(data, PORT_RST_REG0+1, 0xFF);
+ dev_info(&client->dev, "[PORT_CPLD2]PORTs reset done.\n");
+ dump_reg(data);
+
+ return 0;
+
+exit:
+ return status;
+}
+
+static void port_cpld2_remove(struct i2c_client *client)
+{
+ struct cpld_data *data = i2c_get_clientdata(client);
+ sysfs_remove_group(&client->dev.kobj, &port_cpld2_group);
+ kfree(data);
+}
+
+static const struct of_device_id port_cpld2_of_ids[] = {
+ {
+ .compatible = "port_cpld2",
+ .data = (void *) 0,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, port_cpld2_of_ids);
+
+static const struct i2c_device_id port_cpld2_ids[] = {
+ { DRIVER_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, port_cpld2_ids);
+
+static struct i2c_driver port_cpld2_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(port_cpld2_of_ids),
+ },
+ .probe = port_cpld2_probe,
+ .remove = port_cpld2_remove,
+ .id_table = port_cpld2_ids,
+ .address_list = cpld_address_list,
+};
+
+static int __init port_cpld2_init(void)
+{
+ return i2c_add_driver(&port_cpld2_driver);
+}
+
+static void __exit port_cpld2_exit(void)
+{
+ i2c_del_driver(&port_cpld2_driver);
+}
+
+MODULE_AUTHOR("Nokia");
+MODULE_DESCRIPTION("NOKIA H6-128 PORT_CPLD2 driver");
+MODULE_LICENSE("GPL");
+
+module_init(port_cpld2_init);
+module_exit(port_cpld2_exit);
diff --git a/ixr7220h6-128/modules/sys_cpld.c b/ixr7220h6-128/modules/sys_cpld.c
new file mode 100644
index 0000000..1c28a9f
--- /dev/null
+++ b/ixr7220h6-128/modules/sys_cpld.c
@@ -0,0 +1,338 @@
+// * CPLD driver for Nokia-7220-IXR-H6-128 Router
+// *
+// * Copyright (C) 2026 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 SYS_LED_REG2 0x8
+#define SYS_LED_REG3 0x9
+#define OSFP_EFUSE_REG0 0x10
+
+static const unsigned short cpld_address_list[] = {0x71, 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, "%02x.%02x\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_led2(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_REG2);
+ if (sda->index == 0) mask = 0xF;
+ else mask = 0x3;
+ return sprintf(buf, "0x%x\n", (val>>sda->index) & mask);
+}
+
+static ssize_t set_led2(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_REG2);
+ reg_val = reg_val & reg_mask;
+ usr_val = usr_val << sda->index;
+ cpld_i2c_write(data, SYS_LED_REG2, (reg_val | usr_val));
+
+ return count;
+}
+
+static ssize_t show_led3(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_REG3);
+ if (sda->index == 0) mask = 0xF;
+ else mask = 0x3;
+ return sprintf(buf, "0x%x\n", (val>>sda->index) & mask);
+}
+
+static ssize_t set_led3(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_REG3);
+ reg_val = reg_val & reg_mask;
+ usr_val = usr_val << sda->index;
+ cpld_i2c_write(data, SYS_LED_REG3, (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(led_sys, S_IRUGO | S_IWUSR, show_led0, set_led0, 0);
+static SENSOR_DEVICE_ATTR(led_psu, S_IRUGO, show_led3, NULL, 0);
+//static SENSOR_DEVICE_ATTR(led_loc, S_IRUGO | S_IWUSR, show_led2, set_led2, 0);
+static SENSOR_DEVICE_ATTR(led_fan, S_IRUGO | S_IWUSR, show_led2, set_led2, 4);
+
+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_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_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 = "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-128 SYS_CPLD driver");
+MODULE_LICENSE("GPL");
+
+module_init(sys_cpld_init);
+module_exit(sys_cpld_exit);
diff --git a/ixr7220h6-128/modules/sys_fpga.c b/ixr7220h6-128/modules/sys_fpga.c
new file mode 100644
index 0000000..c39ed95
--- /dev/null
+++ b/ixr7220h6-128/modules/sys_fpga.c
@@ -0,0 +1,241 @@
+// * CPLD driver for Nokia-7220-IXR-H6-128 Router
+// *
+// * Copyright (C) 2026 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 HW_BOARD_VER_REG 0x00
+#define VER_MAJOR_REG 0x01
+#define VER_MINOR_REG 0x02
+#define PSU_PRESENT_REG 0x07
+#define SSD_PRESENT_REG 0x08
+#define PSU_POWERGOOD_REG 0x51
+
+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, "SYS_FPGA 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, "SYS_FPGA WRITE ERROR: reg(0x%02x) err %d\n", reg, res);
+ }
+ mutex_unlock(&data->update_lock);
+}
+
+static ssize_t show_hw_board_ver(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, HW_BOARD_VER_REG);
+ return sprintf(buf, "0x%x\n", val);
+}
+
+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, "%02x.%02x\n", reg_major, reg_minor);
+}
+
+static ssize_t show_psu_present(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_PRESENT_REG);
+ return sprintf(buf, "%d\n", (val>>sda->index) & 0x1 ? 1:0);
+}
+
+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_POWERGOOD_REG);
+ return sprintf(buf, "%d\n", (val>>sda->index) & 0x1 ? 1:0);
+}
+
+static ssize_t show_ssd_present(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, SSD_PRESENT_REG);
+ return sprintf(buf, "%d\n", (val>>sda->index) & 0x1 ? 1:0);
+}
+
+// sysfs attributes
+static SENSOR_DEVICE_ATTR(hw_board_version, S_IRUGO, show_hw_board_ver, NULL, 0);
+static SENSOR_DEVICE_ATTR(version, S_IRUGO, show_ver, NULL, 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_present, NULL, 3);
+static SENSOR_DEVICE_ATTR(psu2_pres, S_IRUGO, show_psu_present, NULL, 2);
+static SENSOR_DEVICE_ATTR(psu3_pres, S_IRUGO, show_psu_present, NULL, 1);
+static SENSOR_DEVICE_ATTR(psu4_pres, S_IRUGO, show_psu_present, NULL, 0);
+static SENSOR_DEVICE_ATTR(ssd1_pres, S_IRUGO, show_ssd_present, NULL, 1);
+static SENSOR_DEVICE_ATTR(ssd2_pres, S_IRUGO, show_ssd_present, NULL, 0);
+
+static struct attribute *sys_fpga_attributes[] = {
+ &sensor_dev_attr_hw_board_version.dev_attr.attr,
+ &sensor_dev_attr_version.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_ssd1_pres.dev_attr.attr,
+ &sensor_dev_attr_ssd2_pres.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;
+ }
+
+ 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 = "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-128 SYS_FPGA driver");
+MODULE_LICENSE("GPL");
+
+module_init(sys_fpga_init);
+module_exit(sys_fpga_exit);
diff --git a/ixr7220h6-128/modules/sys_mux.c b/ixr7220h6-128/modules/sys_mux.c
new file mode 100644
index 0000000..ad0884e
--- /dev/null
+++ b/ixr7220h6-128/modules/sys_mux.c
@@ -0,0 +1,238 @@
+/* NOKIA I2C-bus multiplexer driver
+ * Copyright (C) 2025 Nokia Corporation.
+ * This module supports the FPGA & CPLD that hold the channel select
+ * mechanism for other i2c slave devices
+ *
+ *
+ * Based on:
+ * sys_mux.c Brandon Chuang
+ * Copyright (C) 2025
+ #
+ * 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 16
+#define SYS_MUX_SELECT_REG 0x0
+#define SYS_MUX_DESELECT_VAL 0x0
+
+enum mux_type {
+ mux_fpga,
+ mux_sys_cpld,
+ mux_fcm,
+ mux_mezz
+};
+
+struct chip_desc {
+ u8 nchans;
+ u8 select_reg;
+ u8 deselect_val;
+ enum muxtype {
+ is_mux = 0,
+ isswi
+ } muxtype;
+};
+
+struct sys_mux_data {
+ enum mux_type type;
+ struct mutex update_lock;
+ struct i2c_client *client;
+};
+
+static const struct chip_desc chips[] = {
+ [mux_fpga] = {
+ .nchans = 14,
+ .select_reg = SYS_MUX_SELECT_REG,
+ .deselect_val = SYS_MUX_DESELECT_VAL,
+ .muxtype = is_mux
+ },
+ [mux_sys_cpld] = {
+ .nchans = 15,
+ .select_reg = SYS_MUX_SELECT_REG,
+ .deselect_val = SYS_MUX_DESELECT_VAL,
+ .muxtype = is_mux
+ },
+ [mux_fcm] = {
+ .nchans = 6,
+ .select_reg = SYS_MUX_SELECT_REG,
+ .deselect_val = SYS_MUX_DESELECT_VAL,
+ .muxtype = isswi
+ },
+ [mux_mezz] = {
+ .nchans = 3,
+ .select_reg = SYS_MUX_SELECT_REG,
+ .deselect_val = SYS_MUX_DESELECT_VAL,
+ .muxtype = is_mux
+ }
+};
+
+static const struct i2c_device_id sys_mux_id[] = {
+ {"mux_fpga", mux_fpga},
+ {"mux_sys_cpld", mux_sys_cpld},
+ {"mux_fcm", mux_fcm},
+ {"mux_mezz", mux_mezz},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, sys_mux_id);
+
+static const struct of_device_id sys_mux_of_match[] = {
+ {.compatible = "mux_fpga",.data = &chips[mux_fpga]},
+ {.compatible = "mux_sys_cpld",.data = &chips[mux_sys_cpld]},
+ {.compatible = "mux_fcm",.data = &chips[mux_fcm]},
+ {.compatible = "mux_mezz",.data = &chips[mux_mezz]},
+ {}
+};
+
+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 mux_fpga:
+ case mux_sys_cpld:
+ case mux_mezz:
+ ret = sys_mux_write(muxc->parent, client,
+ chips[data->type].select_reg,
+ chan + 1);
+ break;
+ case mux_fcm:
+ 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);
+}
+
+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 = DRVNAME,
+ .owner = THIS_MODULE,
+ .of_match_table = sys_mux_of_match,
+ },
+ .probe = sys_mux_probe,
+ .remove = sys_mux_remove,
+ .id_table = sys_mux_id,
+};
+
+module_i2c_driver(sys_mux_driver);
+MODULE_AUTHOR("Nokia");
+MODULE_DESCRIPTION("NOKIA I2C-bus multiplexer driver");
+MODULE_LICENSE("GPL");
+
diff --git a/ixr7220h6-128/scripts/h6_128_platform_init.sh b/ixr7220h6-128/scripts/h6_128_platform_init.sh
new file mode 100644
index 0000000..af14aec
--- /dev/null
+++ b/ixr7220h6-128/scripts/h6_128_platform_init.sh
@@ -0,0 +1,153 @@
+#!/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-i801
+ modprobe i2c-dev
+ modprobe i2c-mux
+ modprobe i2c-smbus
+ modprobe i2c-ismt
+ modprobe i2c-ocores
+ modprobe h6_i2c_oc
+}
+
+h6-128_profile()
+{
+ #MAC_ADDR=$(ip link show eth0 | grep ether | awk '{print $2}')
+ MAC_ADDR=$(sudo decode-syseeprom -m)
+ sed -i "s/switchMacAddress=.*/switchMacAddress=$MAC_ADDR/g" /usr/share/sonic/device/x86_64-nokia_ixr7220_h6_128-r0/Nokia-IXR7220-H6-128/profile.ini
+ echo "Nokia-IXR7220-H6-128: 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
+}
+
+load_kernel_drivers
+
+sleep 1
+i2cset -y 1 0x60 0xf 0x1
+sleep 1
+i2cset -y 1 0x77 0x0 0x03
+sleep 2
+i2cset -y 1 0x71 0xc 0x3f
+sleep 1
+
+echo sys_fpga 0x60 > /sys/bus/i2c/devices/i2c-1/new_device
+
+echo mux_fpga 0x77 > /sys/bus/i2c/devices/i2c-1/new_device
+sleep 1
+echo mux_sys_cpld 0x72 > /sys/bus/i2c/devices/i2c-134/new_device
+
+echo sys_cpld 0x71 > /sys/bus/i2c/devices/i2c-134/new_device
+
+echo mux_fcm 0x75 > /sys/bus/i2c/devices/i2c-144/new_device
+echo mux_fcm 0x76 > /sys/bus/i2c/devices/i2c-145/new_device
+sleep 1
+echo mux_mezz 0x75 > /sys/bus/i2c/devices/i2c-150/new_device
+echo mux_mezz 0x75 > /sys/bus/i2c/devices/i2c-151/new_device
+echo mux_mezz 0x75 > /sys/bus/i2c/devices/i2c-152/new_device
+echo mux_mezz 0x75 > /sys/bus/i2c/devices/i2c-153/new_device
+
+echo port_cpld0 0x74 > /sys/bus/i2c/devices/i2c-148/new_device
+echo port_cpld1 0x75 > /sys/bus/i2c/devices/i2c-149/new_device
+echo port_cpld2 0x73 > /sys/bus/i2c/devices/i2c-150/new_device
+echo port_cpld2 0x73 > /sys/bus/i2c/devices/i2c-151/new_device
+echo port_cpld2 0x76 > /sys/bus/i2c/devices/i2c-152/new_device
+echo port_cpld2 0x76 > /sys/bus/i2c/devices/i2c-153/new_device
+
+echo 24c04 0x56 > /sys/bus/i2c/devices/i2c-173/new_device
+echo 24c04 0x56 > /sys/bus/i2c/devices/i2c-176/new_device
+echo 24c04 0x56 > /sys/bus/i2c/devices/i2c-179/new_device
+echo 24c04 0x56 > /sys/bus/i2c/devices/i2c-182/new_device
+
+echo 24c04 0x56 > /sys/bus/i2c/devices/i2c-158/new_device
+
+echo h6_fan 0x32 > /sys/bus/i2c/devices/i2c-144/new_device
+echo h6_fan 0x33 > /sys/bus/i2c/devices/i2c-145/new_device
+
+echo 24c64 0x50 > /sys/bus/i2c/devices/i2c-163/new_device
+echo 24c64 0x50 > /sys/bus/i2c/devices/i2c-164/new_device
+echo 24c64 0x50 > /sys/bus/i2c/devices/i2c-165/new_device
+echo 24c64 0x50 > /sys/bus/i2c/devices/i2c-166/new_device
+echo 24c64 0x50 > /sys/bus/i2c/devices/i2c-169/new_device
+echo 24c64 0x50 > /sys/bus/i2c/devices/i2c-170/new_device
+echo 24c64 0x50 > /sys/bus/i2c/devices/i2c-171/new_device
+echo 24c64 0x50 > /sys/bus/i2c/devices/i2c-172/new_device
+
+for i in {0..3}; do
+ pres="/sys/bus/i2c/devices/1-0060/psu$((i+1))_pres"
+ bus_path="/sys/bus/i2c/devices/i2c-$((136+i))/new_device"
+ pmbus_addr=$((0x58 + i))
+ eeprom_addr=$((0x50 + i))
+
+ 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
+
+echo embd_ctrl 0x21 > /sys/bus/i2c/devices/i2c-0/new_device
+echo lm75 0x48 > /sys/bus/i2c/devices/i2c-143/new_device
+echo lm75 0x48 > /sys/bus/i2c/devices/i2c-154/new_device
+echo lm75 0x49 > /sys/bus/i2c/devices/i2c-154/new_device
+echo lm75 0x4A > /sys/bus/i2c/devices/i2c-154/new_device
+echo lm75 0x4B > /sys/bus/i2c/devices/i2c-154/new_device
+echo lm75 0x4C > /sys/bus/i2c/devices/i2c-154/new_device
+echo lm75 0x4D > /sys/bus/i2c/devices/i2c-154/new_device
+
+echo tmp75 0x4D > /sys/bus/i2c/devices/i2c-161/new_device
+echo tmp75 0x4E > /sys/bus/i2c/devices/i2c-162/new_device
+echo tmp75 0x4D > /sys/bus/i2c/devices/i2c-167/new_device
+echo tmp75 0x4E > /sys/bus/i2c/devices/i2c-168/new_device
+
+echo lm75 0x48 > /sys/bus/i2c/devices/i2c-174/new_device
+echo lm75 0x48 > /sys/bus/i2c/devices/i2c-177/new_device
+echo lm75 0x48 > /sys/bus/i2c/devices/i2c-180/new_device
+echo lm75 0x48 > /sys/bus/i2c/devices/i2c-183/new_device
+
+file_exists /sys/bus/i2c/devices/158-0056/eeprom
+status=$?
+if [ "$status" == "1" ]; then
+ chmod 644 /sys/bus/i2c/devices/158-0056/eeprom
+else
+ echo "SYSEEPROM file not found"
+fi
+
+for index in {2..129}; do
+ echo optoe1 0x50 > /sys/bus/i2c/devices/i2c-${index}/new_device
+done
+echo optoe2 0x50 > /sys/bus/i2c/devices/i2c-130/new_device
+echo optoe2 0x50 > /sys/bus/i2c/devices/i2c-131/new_device
+
+h6-128_profile
+
+/usr/local/bin/set_ps.py
+
+for ch in {1..8}; do
+ echo 60 > /sys/bus/i2c/devices/144-0032/hwmon/hwmon*/fan${ch}_pwm
+ echo 60 > /sys/bus/i2c/devices/145-0033/hwmon/hwmon*/fan${ch}_pwm
+done
+
+exit
+
+
diff --git a/ixr7220h6-128/scripts/ports_notify.py b/ixr7220h6-128/scripts/ports_notify.py
new file mode 100644
index 0000000..d52e490
--- /dev/null
+++ b/ixr7220h6-128/scripts/ports_notify.py
@@ -0,0 +1,121 @@
+#!/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
+
+PORT_NUM = 130
+SYSFS_DIR = "/sys/bus/i2c/devices/{}/"
+PORTPLD_ADDR = ["152-0076", "153-0076", "148-0074", "149-0075", "150-0073", "151-0073"]
+ADDR_IDX = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,3,3]
+PORT_IDX = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
+ 1,2,5,6,9,10,13,14,17,18,21,22,25,26,29,30,1,2,5,6,9,10,13,14,17,18,21,22,25,26,29,30,
+ 3,4,7,8,11,12,15,16,19,20,23,24,27,28,31,32,3,4,7,8,11,12,15,16,19,20,23,24,27,28,31,32,
+ 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,33,34]
+
+# 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, PORT_NUM+1):
+ pld_path = SYSFS_DIR.format(PORTPLD_ADDR[ADDR_IDX[port_index-1]])
+ pld_port_idx = PORT_IDX[port_index-1]
+ file_name = pld_path + f"port_{pld_port_idx}_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-128/scripts/set_ps.py b/ixr7220h6-128/scripts/set_ps.py
new file mode 100644
index 0000000..a5abb07
--- /dev/null
+++ b/ixr7220h6-128/scripts/set_ps.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+
+import struct
+from os import *
+from mmap import *
+
+RESOURCE = "/sys/bus/pci/devices/0000:03:00.0/resource0"
+REG1 = [0x2100, 0x2120, 0x2140, 0x2160, 0x2180, 0x21A0, 0x21C0, 0x21E0, 0x2200, 0x2220, 0x2240, 0x2260, 0x2280, 0x22A0, 0x22C0, 0x22E0]
+REG2 = [0x2300, 0x2320, 0x2340, 0x2360, 0x2380, 0x23A0, 0x23C0, 0x23E0, 0x2400, 0x2420, 0x2440, 0x2460, 0x2480, 0x24A0, 0x24C0, 0x24E0, 0x2500, 0x2520]
+
+def pci_set_data8(resource, val, offset):
+ fd = open(resource, O_RDWR)
+ mm = mmap(fd, 0)
+ mm.seek(offset)
+ mm.write(struct.pack('B', val))
+ mm.close()
+ close(fd)
+
+def main():
+ for p in range(5):
+ pci_set_data8(RESOURCE, p+1, 0x2f00)
+ #print(f"SPI_MUX: set mux to {(p+1)}:\n\n")
+ for i in range(len(REG1)):
+ pci_set_data8(RESOURCE, 0x80, REG1[i]+8)
+ pci_set_data8(RESOURCE, 0x0B, REG1[i])
+ pci_set_data8(RESOURCE, 0x0, REG1[i]+4)
+ if p == 0:
+ for i in range(len(REG2)):
+ pci_set_data8(RESOURCE, 0x80, REG2[i]+8)
+ pci_set_data8(RESOURCE, 0x0B, REG2[i])
+ pci_set_data8(RESOURCE, 0x0, REG2[i]+4)
+
+ return
+
+
+if __name__ == '__main__':
+ main()
+
\ No newline at end of file
diff --git a/ixr7220h6-128/service/h6_128_platform_init.service b/ixr7220h6-128/service/h6_128_platform_init.service
new file mode 100644
index 0000000..0154996
--- /dev/null
+++ b/ixr7220h6-128/service/h6_128_platform_init.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=Nokia-IXR7220-H6-128 Platform Service
+After=sysinit.target
+Before=pmon.service determine-reboot-cause.service
+
+[Service]
+Type=oneshot
+ExecStart=/usr/local/bin/h6_128_platform_init.sh
+StandardOutput=tty
+
+[Install]
+WantedBy=multi-user.target
diff --git a/ixr7220h6-128/service/ports_notify.service b/ixr7220h6-128/service/ports_notify.service
new file mode 100644
index 0000000..db83b0e
--- /dev/null
+++ b/ixr7220h6-128/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-128/setup.py b/ixr7220h6-128/setup.py
new file mode 100644
index 0000000..8f329c2
--- /dev/null
+++ b/ixr7220h6-128/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-128 platforms',
+
+ packages=[
+ 'sonic_platform',
+ 'sonic_platform.test'
+ ],
+
+ package_dir={
+ 'sonic_platform': 'sonic_platform'
+ }
+)
diff --git a/ixr7220h6-128/sonic_platform/__init__.py b/ixr7220h6-128/sonic_platform/__init__.py
new file mode 100644
index 0000000..290d8cd
--- /dev/null
+++ b/ixr7220h6-128/sonic_platform/__init__.py
@@ -0,0 +1,2 @@
+__all__ = ["platform"]
+from sonic_platform import *
diff --git a/ixr7220h6-128/sonic_platform/chassis.py b/ixr7220h6-128/sonic_platform/chassis.py
new file mode 100644
index 0000000..7d70381
--- /dev/null
+++ b/ixr7220h6-128/sonic_platform/chassis.py
@@ -0,0 +1,402 @@
+"""
+ 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 = 128
+PORT_END = 130
+PORT_I2C_START = 2
+MAX_SELECT_DELAY = 10
+
+# Device counts
+FAN_DRAWERS = 8
+FANS_PER_DRAWER = 2
+PSU_NUM = 4
+THERMAL_NUM = 20
+COMPONENT_NUM = 11
+
+CPLD_DIR = "/sys/bus/i2c/devices/134-0071/"
+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-128 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, 'SFP56', 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.
+ """
+ return (self.REBOOT_CAUSE_NON_HARDWARE, None)
+
+ 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
+ """
+ return False
+ 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.
+ """
+ return 'N/A'
+ 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-128/sonic_platform/component.py b/ixr7220h6-128/sonic_platform/component.py
new file mode 100644
index 0000000..fd87349
--- /dev/null
+++ b/ixr7220h6-128/sonic_platform/component.py
@@ -0,0 +1,294 @@
+"""
+ NOKIA IXR7220 H6-128
+
+ 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/134-0071/",
+ "/sys/bus/i2c/devices/148-0074/",
+ "/sys/bus/i2c/devices/149-0075/",
+ "/sys/bus/i2c/devices/152-0076/",
+ "/sys/bus/i2c/devices/153-0076/",
+ "/sys/bus/i2c/devices/150-0073/",
+ "/sys/bus/i2c/devices/151-0073/",
+ "/sys/bus/i2c/devices/144-0032/hwmon/hwmon*/",
+ "/sys/bus/i2c/devices/145-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 33-48, 65-80"],
+ ["PORT_CPLD1", "Used for managing PORT 49-64, 81-96, SFP28"],
+ ["PORT_CPLD_TL", "Used for managing PORT 1-16"],
+ ["PORT_CPLD_TR", "Used for managing PORT 17-32"],
+ ["PORT_CPLD_BL", "Used for managing PORT 97-112"],
+ ["PORT_CPLD_BR", "Used for managing PORT 113-128"],
+ ["FCM0_CPLD", "Used for managing upper fan drawers"],
+ ["FCM1_CPLD", "Used for managing lower fan drawers"] ]
+ DEV_NAME = [" ", " ", "MAIN_CPLD", "MAIN_CPLD", "MAIN_CPLD", "MAIN_CPLD",
+ "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_port_cpld_tl_tfr.vme",
+ "h6_64_port_cpld_tr_tfr.vme", "h6_64_port_cpld_bl_tfr.vme",
+ "h6_64_port_cpld_br_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_CHECK_COMMAND = ['./cpldupd', '-s', '']
+ 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_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
+ """
+ if self.name == "BIOS":
+ return read_sysfs_file(self.sysfs_dir + "bios_version")
+ else:
+ return read_sysfs_file(self.sysfs_dir + "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)
+
+ 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":
+ 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":
+ 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:
+ if not os.path.isfile('/tmp/cpldupd'):
+ print("ERROR: the cpld upgrade tool /tmp/cpldupd doesn't exist ")
+ return False
+ self.CPLD_CHECK_COMMAND[2] = self.dev_name
+ try:
+ subprocess.run(self.CPLD_CHECK_COMMAND, stderr=subprocess.STDOUT)
+ except subprocess.CalledProcessError as e:
+ print(f"ERROR: Failed to Scan Jtag chain for {self.name}: rc={e.returncode}")
+ return False
+ 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
+ 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
+
+ Returns:
+ Boolean False if image_path doesn't exist instead of throwing an exception error
+ Nothing when the update is successful
+
+ Raises:
+ RuntimeError: update failed
+ """
+ return self.install_firmware(image_path)
+
+ 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
+ """
+ if image_path:
+ image_name = ntpath.basename(image_path)
+ return image_name
+
+ return 'NA'
+
+ 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+136}-00{hex(0x58+i)[2:]}/psu_rst"
+ if os.path.exists(file_path):
+ write_sysfs_file(file_path, "Reset\n")
diff --git a/ixr7220h6-128/sonic_platform/eeprom.py b/ixr7220h6-128/sonic_platform/eeprom.py
new file mode 100644
index 0000000..069fe58
--- /dev/null
+++ b/ixr7220h6-128/sonic_platform/eeprom.py
@@ -0,0 +1,220 @@
+"""
+ Nokia IXR7220 H6-128
+
+ 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 = ["163-0050", "164-0050",
+ "165-0050", "166-0050",
+ "169-0050", "170-0050",
+ "171-0050", "172-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 + "158-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-128/sonic_platform/fan.py b/ixr7220h6-128/sonic_platform/fan.py
new file mode 100644
index 0000000..ad1ea0a
--- /dev/null
+++ b/ixr7220h6-128/sonic_platform/fan.py
@@ -0,0 +1,262 @@
+"""
+ Nokia IXR7220 H6-128
+
+ 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 = 15400
+MAX_FAN_R_SPEED = 13600
+FAN_TOLERANCE = 50
+WORKING_FAN_SPEED = 2000
+
+HWMON_DIR = "/sys/bus/i2c/devices/{}/hwmon/hwmon*/"
+I2C_DEV_LIST = ["144-0032", "145-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-128/sonic_platform/fan_drawer.py b/ixr7220h6-128/sonic_platform/fan_drawer.py
new file mode 100644
index 0000000..e508186
--- /dev/null
+++ b/ixr7220h6-128/sonic_platform/fan_drawer.py
@@ -0,0 +1,208 @@
+"""
+ Nokia IXR7220 H6-128
+
+ 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 = ["144-0032", "145-0033"]
+EEPROM_ADDR = ['163', '169', '164', '170',
+ '165', '171', '166', '172']
+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
+ """
+ if not self.get_presence():
+ return False
+ 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-128/sonic_platform/platform.py b/ixr7220h6-128/sonic_platform/platform.py
new file mode 100644
index 0000000..6af8751
--- /dev/null
+++ b/ixr7220h6-128/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-128/sonic_platform/psu.py b/ixr7220h6-128/sonic_platform/psu.py
new file mode 100644
index 0000000..23d21e1
--- /dev/null
+++ b/ixr7220h6-128/sonic_platform/psu.py
@@ -0,0 +1,305 @@
+"""
+ Nokia IXR7220 H6-128
+
+ 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/1-0060/"
+MAX_VOLTAGE = 264
+MIN_VOLTAGE = 180
+I2C_BUS = [136, 137, 138, 139]
+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-128 """
+ 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-128/sonic_platform/sfp.py b/ixr7220h6-128/sonic_platform/sfp.py
new file mode 100644
index 0000000..c734132
--- /dev/null
+++ b/ixr7220h6-128/sonic_platform/sfp.py
@@ -0,0 +1,225 @@
+"""
+ Name: sfp.py, version: 1.0
+
+ Description: Module contains the definitions of SFP related APIs
+ for Nokia IXR 7220 H6-128 platform.
+
+ Copyright (c) 2026, 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 = 128
+
+SYSFS_DIR = "/sys/bus/i2c/devices/{}/"
+PORTPLD_ADDR = ["152-0076", "153-0076", "148-0074", "149-0075", "150-0073", "151-0073"]
+ADDR_IDX = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,3,3]
+PORT_IDX = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
+ 1,2,5,6,9,10,13,14,17,18,21,22,25,26,29,30,1,2,5,6,9,10,13,14,17,18,21,22,25,26,29,30,
+ 3,4,7,8,11,12,15,16,19,20,23,24,27,28,31,32,3,4,7,8,11,12,15,16,19,20,23,24,27,28,31,32,
+ 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,33,34]
+
+SYSLOG_IDENTIFIER = "sfp"
+sonic_logger = logger.Logger(SYSLOG_IDENTIFIER)
+sonic_logger.set_min_log_priority_info()
+
+class Sfp(SfpOptoeBase):
+ """
+ Nokia IXR-7220 H6-128 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
+
+ self.pld_path = SYSFS_DIR.format(PORTPLD_ADDR[ADDR_IDX[self.index-1]])
+ self.pld_port_idx = PORT_IDX[self.index-1]
+
+ 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.pld_path+f"port_{self.pld_port_idx}_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.pld_path+f"port_{self.pld_port_idx}_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.pld_path+f"port_{self.pld_port_idx}_lpmod", '0')
+ result2 = write_sysfs_file(self.pld_path+f"port_{self.pld_port_idx}_rst", '0')
+ time.sleep(0.5)
+ result2 = write_sysfs_file(self.pld_path+f"port_{self.pld_port_idx}_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.pld_path+f"port_{self.pld_port_idx}_lpmod", '0')
+ else:
+ result = write_sysfs_file(self.pld_path+f"port_{self.pld_port_idx}_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.pld_path+f"port_{self.pld_port_idx}_lpmod")
+
+ if result == '0':
+ return True
+
+ return False
diff --git a/ixr7220h6-128/sonic_platform/sfp_event.py b/ixr7220h6-128/sonic_platform/sfp_event.py
new file mode 100644
index 0000000..abf3e70
--- /dev/null
+++ b/ixr7220h6-128/sonic_platform/sfp_event.py
@@ -0,0 +1,146 @@
+""""
+ 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 = 130
+
+SYSFS_DIR = "/sys/bus/i2c/devices/{}/"
+PORTPLD_ADDR = ["152-0076", "153-0076", "148-0074", "149-0075", "150-0073", "151-0073"]
+
+_BOOL_LOOKUP_LSB = [tuple((i >> s) & 1 for s in range(8)) for i in range(256)]
+_BOOL_TABLE_A = [((i >> 0) & 1, (i >> 1) & 1, (i >> 4) & 1, (i >> 5) & 1)
+ for i in range(256)]
+_BOOL_TABLE_B = [((i >> 2) & 1, (i >> 3) & 1, (i >> 6) & 1, (i >> 7) & 1)
+ for i in range(256)]
+
+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(SYSFS_DIR.format(PORTPLD_ADDR[3])+"port_33_tx_en", '0')
+ if self.modprs_list[PORT_END-1]:
+ write_sysfs_file(SYSFS_DIR.format(PORTPLD_ADDR[3])+"port_34_tx_en", '0')
+
+ def deinitialize(self):
+ if self.handle is None:
+ return
+
+ def _get_transceiver_status(self):
+ lookup = _BOOL_LOOKUP_LSB
+ port_status = []
+ reg_value = []
+
+ for i in range(6):
+ m = read_sysfs_file(SYSFS_DIR.format(PORTPLD_ADDR[i]) + "modprs_reg1")
+ n = read_sysfs_file(SYSFS_DIR.format(PORTPLD_ADDR[i]) + "modprs_reg2")
+ reg_value.extend([m,n])
+ if i == 2 or i == 3:
+ m = read_sysfs_file(SYSFS_DIR.format(PORTPLD_ADDR[i]) + "modprs_reg3")
+ n = read_sysfs_file(SYSFS_DIR.format(PORTPLD_ADDR[i]) + "modprs_reg4")
+ reg_value.extend([m,n])
+
+ port_status.extend([bit for h in reg_value[:4] for bit in lookup[int(h, 16)]])
+ port_status.extend(self._reorder(reg_value[4:12]))
+ port_status.extend([bit for h in reg_value[-4:] for bit in lookup[int(h, 16)]])
+
+ for i in range (33, 35):
+ status = read_sysfs_file(SYSFS_DIR.format(PORTPLD_ADDR[3])+f"port_{i}_prs")
+ if status == '0':
+ port_status.append(0)
+ else:
+ port_status.append(1)
+
+ 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] == 0:
+ 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] == 0:
+ write_sysfs_file(SYSFS_DIR.format(PORTPLD_ADDR[3])+f"port_{i-95}_tx_en", '0')
+ else:
+ write_sysfs_file(SYSFS_DIR.format(PORTPLD_ADDR[3])+f"port_{i-95}_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, {}
+
+ def _reorder(self, hex_list):
+ t_a = _BOOL_TABLE_A
+ t_b = _BOOL_TABLE_B
+ res = []
+ ext = res.extend
+ data = [int(h, 16) for h in hex_list]
+ for i in range(0, len(data), 8):
+ chunk = data[i:i+8]
+ for byte in chunk:
+ ext(t_a[byte])
+ for byte in chunk:
+ ext(t_b[byte])
+ return res
diff --git a/ixr7220h6-128/sonic_platform/sysfs.py b/ixr7220h6-128/sonic_platform/sysfs.py
new file mode 100644
index 0000000..b8338be
--- /dev/null
+++ b/ixr7220h6-128/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-128/sonic_platform/test/README b/ixr7220h6-128/sonic_platform/test/README
new file mode 100644
index 0000000..3efc8fa
--- /dev/null
+++ b/ixr7220h6-128/sonic_platform/test/README
@@ -0,0 +1 @@
+This directory contains unit tests of the Platform API 2.0
diff --git a/ixr7220h6-128/sonic_platform/test/test-chassis.py b/ixr7220h6-128/sonic_platform/test/test-chassis.py
new file mode 100644
index 0000000..7d065dd
--- /dev/null
+++ b/ixr7220h6-128/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-128/sonic_platform/test/test-component.py b/ixr7220h6-128/sonic_platform/test/test-component.py
new file mode 100644
index 0000000..ee29512
--- /dev/null
+++ b/ixr7220h6-128/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-128/sonic_platform/test/test-eeprom.py b/ixr7220h6-128/sonic_platform/test/test-eeprom.py
new file mode 100644
index 0000000..d6f214c
--- /dev/null
+++ b/ixr7220h6-128/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-128/sonic_platform/test/test-fan-drawer.py b/ixr7220h6-128/sonic_platform/test/test-fan-drawer.py
new file mode 100644
index 0000000..20860e4
--- /dev/null
+++ b/ixr7220h6-128/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-128/sonic_platform/test/test-fan.py b/ixr7220h6-128/sonic_platform/test/test-fan.py
new file mode 100644
index 0000000..ae647aa
--- /dev/null
+++ b/ixr7220h6-128/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-128/sonic_platform/test/test-psu.py b/ixr7220h6-128/sonic_platform/test/test-psu.py
new file mode 100644
index 0000000..2db5878
--- /dev/null
+++ b/ixr7220h6-128/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-128/sonic_platform/test/test-sfp.py b/ixr7220h6-128/sonic_platform/test/test-sfp.py
new file mode 100644
index 0000000..f76c2e7
--- /dev/null
+++ b/ixr7220h6-128/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-128/sonic_platform/test/test-thermal.py b/ixr7220h6-128/sonic_platform/test/test-thermal.py
new file mode 100644
index 0000000..ce008de
--- /dev/null
+++ b/ixr7220h6-128/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-128/sonic_platform/test/test-watchdog.py b/ixr7220h6-128/sonic_platform/test/test-watchdog.py
new file mode 100644
index 0000000..135c26f
--- /dev/null
+++ b/ixr7220h6-128/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-128/sonic_platform/thermal.py b/ixr7220h6-128/sonic_platform/thermal.py
new file mode 100644
index 0000000..aa11b60
--- /dev/null
+++ b/ixr7220h6-128/sonic_platform/thermal.py
@@ -0,0 +1,262 @@
+"""
+ Nokia IXR7220-H6-128
+ 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 = 20
+
+class Thermal(ThermalBase):
+ """Nokia platform-specific Thermal class"""
+
+ HWMON_DIR = "/sys/bus/i2c/devices/{}/hwmon/hwmon*/"
+ I2C_DEV_LIST = ["143-0048", "154-004b", "154-004c", "154-004d",
+ "154-0049", "154-0048", "154-004a", "174-0048", "177-0048",
+ "180-0048", "183-0048", "167-004d","168-004e", "161-004d",
+ "162-004e", "0-0021", "0-0021"]
+ THERMAL_NAME = ["Carrier Board", "MB Top U34", "MB Top U178", "MB Bottom U196",
+ "MB Bottom U183", "MB Top U3", "MB Top U15", "LDB Left", "LDB Right",
+ "UDB Left", "UDB Right", "Top FCM 1", "Top FCM 2", "Bottom FCM 1",
+ "Bottom FCM 2", "CPU", "DDR", "Max Port Temp.", "SSD",
+ "ASIC TH6"]
+
+ THRESHHOLD = [62.0, 75.0, 75.0, 75.0,
+ 75.0, 75.0, 75.0, 60.0, 60.0,
+ 60.0, 60.0, 60.0, 60.0, 62.0,
+ 62.0, 95.0, 70.0, 75.0, 70.0,
+ 95.0]
+ CRITICAL_THRESHHOLD = [70.0, 85.0, 85.0, 85.0,
+ 85.0, 85.0, 85.0, 70.0, 70.0,
+ 70.0, 70.0, 70.0, 70.0, 72.0,
+ 72.0, 99.0, 80.0, 77.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.thermal_temperature_file = "/sys/class/hwmon/hwmon1/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
+ """
+ return self.CRITICAL_THRESHHOLD[self.index - 1]
+
+ 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-128/sonic_platform/thermal_actions.py b/ixr7220h6-128/sonic_platform/thermal_actions.py
new file mode 100644
index 0000000..c98a85b
--- /dev/null
+++ b/ixr7220h6-128/sonic_platform/thermal_actions.py
@@ -0,0 +1,237 @@
+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_THRESHOLD2_SPEED = 'threshold2_speed'
+ JSON_FIELD_HIGHTEMP_SPEED = 'hightemp_speed'
+
+ def __init__(self):
+ """
+ Constructor of SetFanSpeedAction
+ """
+ self.default_speed = 47
+ self.threshold1_speed = 60
+ self.threshold2_speed = 80
+ 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": "25",
+ "threshold1_speed": "40",
+ "threshold2_speed": "75",
+ "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_THRESHOLD2_SPEED in json_obj:
+ threshold2_speed = float(json_obj[SetFanSpeedAction.JSON_FIELD_THRESHOLD2_SPEED])
+ if threshold2_speed < 0 or threshold2_speed > 100:
+ raise ValueError('SetFanSpeedAction invalid default speed value {} in JSON policy file, valid value should be [0, 100]'.
+ format(threshold2_speed))
+ self.threshold2_speed = float(json_obj[SetFanSpeedAction.JSON_FIELD_THRESHOLD2_SPEED])
+ else:
+ raise ValueError('SetFanSpeedAction missing mandatory field {} in JSON policy file'.
+ format(SetFanSpeedAction.JSON_FIELD_THRESHOLD2_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: {}, threshold2: {}, hightemp: {}".format(self.default_speed, self.threshold1_speed, self.threshold2_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_two_speed():
+ ThermalRecoverAction.set_all_fan_speed(thermal_info_dict, self.threshold2_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:
+ """
+ try:
+ import os
+ from sonic_platform.chassis import Chassis
+ for i in range(8):
+ fan_obj = Chassis().get_fan_drawer(i)
+ if fan_obj.get_presence():
+ sonic_logger.log_warning(f"Fan {fan_obj.get_name()} speed: "
+ f"{fan_obj.get_fan(0).get_speed()}%, {fan_obj.get_fan(1).get_speed()}%.")
+ else:
+ sonic_logger.log_warning(f"Fan {fan_obj.get_name()} not presence.")
+ for i in range(4):
+ psu_obj = Chassis().get_psu(i)
+ if psu_obj.get_presence():
+ sonic_logger.log_warning(f"{psu_obj.get_name()}: {psu_obj.get_voltage()}V, "
+ f"{psu_obj.get_current()}A, {psu_obj.get_power()}W.")
+ else:
+ sonic_logger.log_warning(f"{fan_obj.get_name()} not presence.")
+ except Exception as e:
+ sonic_logger.log_warning(" Fail to save fan and psu info {}".format(repr(e)))
+
+ sonic_logger.log_error("Alarm for temperature critical is detected, reboot Device")
+ os.system('reboot')
+
+@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('Invalid {} field value, please specify true of false'.
+ format(ControlThermalAlgoAction.JSON_FIELD_STATUS))
+ else:
+ raise ValueError('ControlThermalAlgoAction '
+ 'missing mandatory field {} in JSON policy file'.
+ format(ControlThermalAlgoAction.JSON_FIELD_STATUS))
+
+ 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-128/sonic_platform/thermal_conditions.py b/ixr7220h6-128/sonic_platform/thermal_conditions.py
new file mode 100644
index 0000000..d19fd4f
--- /dev/null
+++ b/ixr7220h6-128/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-128/sonic_platform/thermal_infos.py b/ixr7220h6-128/sonic_platform/thermal_infos.py
new file mode 100644
index 0000000..27b6d4a
--- /dev/null
+++ b/ixr7220h6-128/sonic_platform/thermal_infos.py
@@ -0,0 +1,249 @@
+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 = 3
+ self._level_up_threshold = [[44, 54, 51, 52, 45, 53, 50, 44, 43, 45, 44, 43, 43, 47, 47, 83, 56, 58, 47, 73],
+ [50, 60, 57, 58, 51, 59, 56, 51, 50, 52, 51, 50, 50, 53, 53, 88, 61, 68, 51, 86],
+ [55, 65, 62, 63, 56, 64, 61, 56, 55, 57, 56, 55, 55, 57, 57, 93, 66, 73, 55, 91]]
+
+ self._level_down_threshold = [[31, 44, 41, 42, 35, 43, 40, 34, 33, 35, 34, 32, 32, 35, 35, 70, 42, 50, 33, 70],
+ [39, 52, 49, 50, 43, 51, 48, 42, 41, 43, 42, 41, 41, 45, 45, 78, 48, 60, 40, 75],
+ [48, 58, 55, 56, 49, 57, 54, 49, 48, 50, 49, 48, 48, 51, 51, 83, 54, 69, 47, 80]]
+
+ 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_threshold_two_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 = 3
+
+ # 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_threshold_two_speed = True
+ elif self._current_threshold_level == 3:
+ 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_threshold_two_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_two_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-128/sonic_platform/thermal_manager.py b/ixr7220h6-128/sonic_platform/thermal_manager.py
new file mode 100644
index 0000000..7d84fa9
--- /dev/null
+++ b/ixr7220h6-128/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-128/sonic_platform/watchdog.py b/ixr7220h6-128/sonic_platform/watchdog.py
new file mode 100644
index 0000000..5bb2e00
--- /dev/null
+++ b/ixr7220h6-128/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)