From a44cfbf96572f3a3f94b8545bba6299dbda16dcd Mon Sep 17 00:00:00 2001
From: "Dante (Kuo-Jung) Su" <dante.su@broadcom.com>
Date: Fri, 20 Nov 2020 12:29:55 +0800
Subject: [PATCH] [device/celestica/x86_64-cel_silverstone-r0] Fix QSFP-DD
 support (#92)

* Add sysfs for XCVR interrupt event status

* implement get_transceiver_change_event in sfutil of silverstone

* add cmis init script

* update perssion for cmis-init script

* clean cmis init code

* clean sfputil code

* SilverStone: fix QSFPDD support

Signed-off-by: Dante Su <dante.su@broadcom.com>

* skip cmis_init if module absent, add error message upon retry timeout

Signed-off-by: Dante Su <dante.su@broadcom.com>

* Silverstone-128x100: fix the naming defect in port_config.ini

Signed-off-by: Dante Su <dante.su@broadcom.com>

* sfputil: automatically perform QSFP-DD software reset if module state != ModuleReady for 3+ seconds

Signed-off-by: Dante Su <dante.su@broadcom.com>

* sfputil: always perform a software reset upon QSFPDD module insertion

* sfputil.py: add extra delays for init sequence

* sfputil.py: Monitoring the QSFP-DD initialization state, and reinitiate it if necessary

Signed-off-by: Dante Su <dante.su@broadcom.com>

* sfputil.py: drop the QSFPDD software reset from the insertion event

In the current implementation, the SFP change events are actually software
simulated by polling the presence flags, and the INSERTION events will be
be generated upon pmon restart and reboot.

And hence, significant packet drops could be observed upon both pmon restart
and a warm-reboot, as we'll always fire a software reset to the transceivers.

i.e.
It's better to drop the QSFPDD software reset from the insertion event

Signed-off-by: Dante Su <dante.su@broadcom.com>

* sfputil.py: clean up

Signed-off-by: Dante Su <dante.su@broadcom.com>

* sfputil.py: address review comments

1. Add sanity check to application advertisement decoder to prevent
   infinite loop

2. Create a function wrapper for module-specific CMIS init

Signed-off-by: Dante Su <dante.su@broadcom.com>

* device/celestica/x86_64-cel_silverstone-r0: drop cmis-init.sh

Signed-off-by: Dante Su <dante.su@broadcom.com>

Co-authored-by: Wirut Getbamrung <wgetbum@icloud.com>
---
 .../Silverstone-128x100/port_config.ini       |   4 +-
 .../plugins/sfputil.py                        | 246 +++++++++++++++++-
 .../silverstone/modules/switchboard.c         | 230 ++++++++++++++--
 3 files changed, 455 insertions(+), 25 deletions(-)

diff --git a/device/celestica/x86_64-cel_silverstone-r0/Silverstone-128x100/port_config.ini b/device/celestica/x86_64-cel_silverstone-r0/Silverstone-128x100/port_config.ini
index f04fb03264cb..77deb1931856 100644
--- a/device/celestica/x86_64-cel_silverstone-r0/Silverstone-128x100/port_config.ini
+++ b/device/celestica/x86_64-cel_silverstone-r0/Silverstone-128x100/port_config.ini
@@ -112,8 +112,8 @@ Ethernet218     187,188     QSFP28/2        28          100000
 Ethernet220     189,190     QSFP28/3        28          100000
 Ethernet222     191,192     QSFP28/4        28          100000
 Ethernet224     193,194     QSFP29/1        29          100000
-Ethernet216     195,196     QSFP29/2        29          100000
-Ethernet218     197,198     QSFP29/3        29          100000
+Ethernet226     195,196     QSFP29/2        29          100000
+Ethernet228     197,198     QSFP29/3        29          100000
 Ethernet230     199,200     QSFP29/4        29          100000
 Ethernet232     201,202     QSFP30/1        30          100000
 Ethernet234     203,204     QSFP30/2        30          100000
diff --git a/device/celestica/x86_64-cel_silverstone-r0/plugins/sfputil.py b/device/celestica/x86_64-cel_silverstone-r0/plugins/sfputil.py
index fa2283831c87..d1d048a27bd6 100755
--- a/device/celestica/x86_64-cel_silverstone-r0/plugins/sfputil.py
+++ b/device/celestica/x86_64-cel_silverstone-r0/plugins/sfputil.py
@@ -4,8 +4,18 @@
 # This plugin supports QSFP-DD, QSFP and SFP.
 
 try:
+    import syslog
     import time
+    import subprocess
     from sonic_platform_base.sonic_sfp.sfputilbase import SfpUtilBase
+    from sonic_platform_base.sonic_sfp.sff8024 import type_of_transceiver
+    from sonic_platform_base.sonic_sfp.sff8024 import type_of_media_interface
+    from sonic_platform_base.sonic_sfp.sff8024 import host_electrical_interface
+    from sonic_platform_base.sonic_sfp.sff8024 import nm_850_media_interface
+    from sonic_platform_base.sonic_sfp.sff8024 import sm_media_interface
+    from sonic_platform_base.sonic_sfp.sff8024 import passive_copper_media_interface
+    from sonic_platform_base.sonic_sfp.sff8024 import active_cable_media_interface
+    from sonic_platform_base.sonic_sfp.sff8024 import base_t_media_interface
     from sonic_platform_base.sonic_sfp.sff8472 import sff8472InterfaceId, sff8472Dom
     from sonic_platform_base.sonic_sfp.sff8436 import sff8436InterfaceId, sff8436Dom
     from sonic_platform_base.sonic_sfp.inf8628 import inf8628InterfaceId
@@ -15,6 +25,21 @@
     raise ImportError("%s - required module not found" % str(e))
 
 
+PLATFORM_ROOT_PATH = '/usr/share/sonic/device'
+SONIC_CFGGEN_PATH = '/usr/local/bin/sonic-cfggen'
+HWSKU_KEY = 'DEVICE_METADATA.localhost.hwsku'
+PLATFORM_KEY = 'DEVICE_METADATA.localhost.platform'
+
+SYSLOG_IDENTIFIER = "sfputil.py"
+
+def log_info(msg, also_print_to_console=False):
+    syslog.openlog(SYSLOG_IDENTIFIER)
+    syslog.syslog(syslog.LOG_INFO, msg)
+    syslog.closelog()
+
+    if also_print_to_console:
+        print msg
+
 class QSFPDDDomPaser(qsfp_dd_Dom):
 
     def __init__(self, eeprom_raw_data):
@@ -173,7 +198,6 @@ def __init__(self, eeprom_raw_data):
     def get_data_pretty(self):
         return sffbase.get_data_pretty(self, self.dom_data)
 
-
 class SfpUtil(SfpUtilBase):
     """Platform-specific SfpUtil class"""
 
@@ -184,10 +208,15 @@ class SfpUtil(SfpUtilBase):
     SFP_PORT_START = 33
     SFP_PORT_END = 34
 
+    NUM_OSFP = 32
+
     EEPROM_OFFSET = 9
     PORT_INFO_PATH = '/sys/class/silverstone_fpga'
     QSFP_DD_DOM_OFFSET = 2304
 
+    # polling interval in seconds
+    POLL_INTERVAL = 1
+
     _port_name = ""
     _port_to_eeprom_mapping = {}
     _port_to_i2cbus_mapping = {}
@@ -232,10 +261,30 @@ def get_eeprom_dom_raw(self, port_num):
             return self._read_eeprom_devid(port_num, self.DOM_EEPROM_ADDR, 256)
 
     def __init__(self):
+        self.inf8628 = inf8628InterfaceId()
+
+        self.mod_presence = {}
+        self.mod_failure = {}
+        for x in range(self.PORT_START, self.PORT_END + 1):
+            self.mod_failure[x] = 0
+            self.mod_presence[x] = False
+
+        self.hwsku = None
+        try:
+            proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-d', '-v', HWSKU_KEY],
+                                    stdout=subprocess.PIPE,
+                                    shell=False,
+                                    stderr=subprocess.STDOUT)
+            stdout = proc.communicate()[0]
+            proc.wait()
+            self.hwsku = stdout.rstrip('\n')
+        except:
+            log_info("Cannot detect HwSku")
+
         # Override port_to_eeprom_mapping for class initialization
         eeprom_path = '/sys/bus/i2c/devices/i2c-{0}/{0}-0050/eeprom'
 
-        for x in range(self.PORT_START, self.PORT_END+1):
+        for x in range(self.PORT_START, self.PORT_END + 1):
             self.port_to_i2cbus_mapping[x] = (x + self.EEPROM_OFFSET)
             self.port_to_eeprom_mapping[x] = eeprom_path.format(
                 x + self.EEPROM_OFFSET)
@@ -344,11 +393,152 @@ def reset(self, port_num):
 
         return True
 
+    def _page_to_flat(self, addr, page = -1):
+        flat = 0
+        if addr < 128:
+            flat = addr
+        else:
+            flat = ((page + 1) << 7) | (addr & 0x7f)
+        return flat
+
+    def _write_byte(self, port_num, devid, page, off, val):
+        eeprom_path = self._get_port_eeprom_path(port_num, devid)
+        addr = self._page_to_flat(off, page)
+        try:
+            f = open(eeprom_path, "wb", 0)
+            f.seek(addr)
+            f.write(chr(val))
+        except Exception as ex:
+            log_info("write failed: {0}".format(ex))
+        finally:
+            f.close()
+
+    def _init_cmis_module_custom(self, port_num, xcvr, hwsku):
+        # As of now, init sequence is only necessary for 'INNOLIGHT T-DP4CNT-N00'
+        if xcvr != 'INNOLIGHT T-DP4CNT-N00':
+            return True
+
+        log_info("PORT {0}: {1}: _init_cmis_module_custom".format(port_num, xcvr))
+
+        # Allow 1s for software reset
+        self._write_byte(port_num, self.IDENTITY_EEPROM_ADDR, -1, 26, 0x08)
+        time.sleep(1)
+        # Deinitialize datapath
+        self._write_byte(port_num, self.IDENTITY_EEPROM_ADDR, 0x10, 128, 0xff)
+        time.sleep(0.5)
+        # Hi-Power
+        self._write_byte(port_num, self.IDENTITY_EEPROM_ADDR, -1, 26, 0x00)
+        # Application selection
+        if '128x100' in hwsku:
+            self._write_byte(port_num, self.IDENTITY_EEPROM_ADDR, 0x10, 145, 0x21)
+            self._write_byte(port_num, self.IDENTITY_EEPROM_ADDR, 0x10, 146, 0x21)
+            self._write_byte(port_num, self.IDENTITY_EEPROM_ADDR, 0x10, 147, 0x25)
+            self._write_byte(port_num, self.IDENTITY_EEPROM_ADDR, 0x10, 148, 0x25)
+            self._write_byte(port_num, self.IDENTITY_EEPROM_ADDR, 0x10, 149, 0x29)
+            self._write_byte(port_num, self.IDENTITY_EEPROM_ADDR, 0x10, 150, 0x29)
+            self._write_byte(port_num, self.IDENTITY_EEPROM_ADDR, 0x10, 151, 0x2d)
+            self._write_byte(port_num, self.IDENTITY_EEPROM_ADDR, 0x10, 152, 0x2d)
+        else:
+            self._write_byte(port_num, self.IDENTITY_EEPROM_ADDR, 0x10, 145, 0x11)
+            self._write_byte(port_num, self.IDENTITY_EEPROM_ADDR, 0x10, 146, 0x11)
+            self._write_byte(port_num, self.IDENTITY_EEPROM_ADDR, 0x10, 147, 0x11)
+            self._write_byte(port_num, self.IDENTITY_EEPROM_ADDR, 0x10, 148, 0x11)
+            self._write_byte(port_num, self.IDENTITY_EEPROM_ADDR, 0x10, 149, 0x11)
+            self._write_byte(port_num, self.IDENTITY_EEPROM_ADDR, 0x10, 150, 0x11)
+            self._write_byte(port_num, self.IDENTITY_EEPROM_ADDR, 0x10, 151, 0x11)
+            self._write_byte(port_num, self.IDENTITY_EEPROM_ADDR, 0x10, 152, 0x11)
+        self._write_byte(port_num, self.IDENTITY_EEPROM_ADDR, 0x10, 143, 0xff)
+        # Initialize datapath
+        self._write_byte(port_num, self.IDENTITY_EEPROM_ADDR, 0x10, 128, 0x00)
+        time.sleep(0.5)
+        # Validate configuration status
+        buf = self._read_eeprom_devid(port_num, self.IDENTITY_EEPROM_ADDR, self._page_to_flat(202, 0x11), 4)
+        err = "".join(buf)
+        if err != '11111111':
+            log_info("PORT {0}: ConfigErr={1}".format(port_num, err))
+            return False
+
+        return True
+
+    def _init_cmis_module(self, port_num):
+        buf = self._read_eeprom_devid(port_num, self.IDENTITY_EEPROM_ADDR, 0x80, 48)
+        if buf is None:
+            log_info("PORT {0}: unable to read PAGE0".format(port_num))
+            return False
+
+        # Skip, in case of QSFP28
+        if buf[0] not in "18,19":
+            log_info("PORT {0}: skipped, it's not a QSFPDD".format(port_num))
+            return True
+
+        # Decode the transceiver ids
+        name = self.inf8628.parse_vendor_name(buf, 1)['data']['Vendor Name']['value']
+        part = self.inf8628.parse_vendor_pn(buf, 20)['data']['Vendor PN']['value']
+        #log_info("_init_cmis_module: p={0}, id='{1}', name='{2}', part='{3}'".format(port_num, buf[0], name, part))
+        xcvr = name.upper() + " " + part.upper()
+
+        return self._init_cmis_module_custom(port_num, xcvr, self.hwsku)
+
     def get_transceiver_change_event(self, timeout=0):
         """
-        TBD
+        :param timeout in milliseconds. The method is a blocking call. When timeout is 
+         zero, it only returns when there is change event, i.e., transceiver plug-in/out
+         event. When timeout is non-zero, the function can also return when the timer expires.
+         When timer expires, the return status is True and events is empty.
+        :returns: (status, events)
+        :status: Boolean, True if call successful and no system level event/error occurred, 
+         False if call not success or system level event/error occurred.
+        :events: dictionary for physical port index and the SFP status,
+         status='1' represent plug in, '0' represent plug out like {'0': '1', '31':'0'}
+         when it comes to system level event/error, the index will be '-1',
+         and status can be 'system_not_ready', 'system_become_ready', 'system_fail',
+         like {'-1':'system_not_ready'}.
         """
-        raise NotImplementedError
+        int_sfp = {}
+        end_time = time.time() + (float(timeout) / 1000.0)
+        while end_time > time.time():
+            for x in range(self.PORT_START, self.PORT_END + 1):
+                flag = self.get_presence(x)
+                if flag != self.mod_presence[x]:
+                    int_sfp[str(x)] = '1' if flag else '0'
+                    self.mod_failure[x] = 0
+                    self.mod_presence[x] = flag
+
+                # skip the following logic in case of module absent
+                if not flag:
+                    continue
+
+                # Monitoring the QSFP-DD module state, and initiate software reset when failure count > 2
+                buf = self._read_eeprom_devid(x, self.IDENTITY_EEPROM_ADDR, 0x0, 4)
+                if buf is None:
+                    continue
+                # skip, in case of QSFP28
+                if buf[0] not in "18,19":
+                    continue
+                # skip, in case that CMIS < 3.0
+                if int(buf[1], 16) < 0x30:
+                    continue
+                # advance the failure counter if state != ModuleReady
+                if ((int(buf[3], 16) >> 1) & 0x7) != 3:
+                    self.mod_failure[x] += 1
+                # initiate QSFP-DD software reset if failure counter > 2
+                if self.mod_failure[x] > 2:
+                    self.mod_failure[x] = 0
+                    self._write_byte(x, self.IDENTITY_EEPROM_ADDR, -1, 26, 0x08)
+
+                # Monitoring the QSFP-DD initialization state, and reinitiate it if necessary
+                buf = self._read_eeprom_devid(x, self.IDENTITY_EEPROM_ADDR, self._page_to_flat(202, 0x11), 4)
+                if buf is None:
+                    continue
+                err = "".join(buf)
+                if err != '11111111':
+                    if not self._init_cmis_module(x):
+                        log_info("PORT {0}: Unable to initialize the module".format(x))
+            # break if the SFP change event is not empty
+            if len(int_sfp) > 0:
+                break
+            time.sleep(1)
+        return True, int_sfp
 
     def get_qsfp_data(self, eeprom_ifraw):
         sfp_data = {}
@@ -359,6 +549,27 @@ def get_qsfp_data(self, eeprom_ifraw):
         sfp_data['dom'] = sfpd_obj.get_data_pretty() if sfpd_obj else {}
         return sfp_data
 
+    def parse_media_type(self, eeprom_data, offset):
+        media_type_code = eeprom_data[offset]
+        dict_name = type_of_media_interface[media_type_code]
+        if dict_name == "nm_850_media_interface":
+            return nm_850_media_interface
+        elif dict_name == "sm_media_interface":
+            return sm_media_interface
+        elif dict_name == "passive_copper_media_interface":
+            return passive_copper_media_interface
+        elif dict_name == "active_cable_media_interface":
+            return active_cable_media_interface
+        elif dict_name == "base_t_media_interface":
+            return base_t_media_interface
+        else:
+             return None
+
+    def parse_application(self, sfp_media_type_dict, host_interface, media_interface):
+        host_result = host_electrical_interface[host_interface]
+        media_result = sfp_media_type_dict[media_interface]
+        return host_result, media_result
+
     def get_eeprom_dict(self, port_num):
         """Returns dictionary of interface and dom data.
         format: {<port_num> : {'interface': {'version' : '1.0', 'data' : {...}},
@@ -379,9 +590,34 @@ def get_eeprom_dict(self, port_num):
                 sfp_data['interface'] = sfpi_obj.get_data_pretty()
 
                 # check if it is a 100G module
-                if sfp_data['interface']['data']['Identifier'] == 'QSFP28 or later':
+                if sfp_data['interface']['data']['Identifier'] not in [type_of_transceiver['18'], type_of_transceiver['19']]:
                     return self.get_qsfp_data(eeprom_ifraw)
 
+                # decode application advertisement
+                offset = 85
+                tbl = self.parse_media_type(eeprom_ifraw, offset)
+                ret = ""
+                if tbl is not None:
+                    app = 1
+                    hid = int(eeprom_ifraw[1 + offset], 16)
+                    while (app <= 8) and (hid != 0) and (hid != 0xff):
+                        (ht, mt) = self.parse_application(tbl, eeprom_ifraw[1 + offset], eeprom_ifraw[2 + offset])
+                        ret += "\n            {0}: {1} | {2}".format(app, ht, mt)
+                        app += 1
+                        offset += 4
+                        hid = int(eeprom_ifraw[1 + offset], 16)
+                if len(ret) > 0:
+                    sfp_data['interface']['data']['Application Advertisement'] = ret
+
+                # decode the running application code
+                sel = 1
+                eeprom_data = self._read_eeprom_devid(port_num, self.IDENTITY_EEPROM_ADDR, 0x880, 32)
+                if eeprom_data is not None:
+                    sel = int(eeprom_data[145 - 128], 16) >> 4
+                    if sel < 1 or sel >= app:
+                        sel = 1
+                sfp_data['interface']['data']['Application Selected'] = "{0}".format(sel)
+
             sfpd_obj = QSFPDDDomPaser(
                 eeprom_ifraw + eeprom_domraw) if eeprom_domraw else None
             sfp_data['dom'] = sfpd_obj.get_data_pretty() if sfpd_obj else {}
diff --git a/platform/broadcom/sonic-platform-modules-cel/silverstone/modules/switchboard.c b/platform/broadcom/sonic-platform-modules-cel/silverstone/modules/switchboard.c
index b7b1812be176..f09f36281b97 100644
--- a/platform/broadcom/sonic-platform-modules-cel/silverstone/modules/switchboard.c
+++ b/platform/broadcom/sonic-platform-modules-cel/silverstone/modules/switchboard.c
@@ -25,7 +25,7 @@
  */
 
 #ifndef TEST_MODE
-#define MOD_VERSION "1.2.1"
+#define MOD_VERSION "1.3.0"
 #else
 #define MOD_VERSION "TEST"
 #endif
@@ -193,7 +193,6 @@ PORT XCVR       0x00004000 - 0x00004FFF.
 */
 #define INTR_INT_N      5
 #define INTR_PRESENT    4
-#define INTR_TXFAULT    2
 #define INTR_RXLOS      1
 #define INTR_MODABS     0
 
@@ -208,7 +207,6 @@ PORT XCVR       0x00004000 - 0x00004FFF.
 */
 #define MASK_INT_N      5
 #define MASK_PRESENT    4
-#define MASK_TXFAULT    2
 #define MASK_RXLOS      1
 #define MASK_MODABS     0
 
@@ -555,14 +553,14 @@ static struct attribute_group fpga_attr_grp = {
     .attrs = fpga_attrs,
 };
 
-static ssize_t cpld1_version_show(struct device *dev, 
+static ssize_t cpld1_version_show(struct device *dev,
                                   struct device_attribute *attr, char *buf)
 {
     u8 version;
     int err;
-    err = fpga_i2c_access(fpga_data->i2c_adapter[SW_I2C_CPLD_INDEX], 
-                          CPLD1_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x00, 
-                          I2C_SMBUS_BYTE_DATA, 
+    err = fpga_i2c_access(fpga_data->i2c_adapter[SW_I2C_CPLD_INDEX],
+                          CPLD1_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x00,
+                          I2C_SMBUS_BYTE_DATA,
                           (union i2c_smbus_data *)&version);
     if (err < 0)
         return err;
@@ -667,14 +665,14 @@ static struct attribute_group cpld1_attr_grp = {
     .attrs = cpld1_attrs,
 };
 
-static ssize_t cpld2_version_show(struct device *dev, 
+static ssize_t cpld2_version_show(struct device *dev,
                                   struct device_attribute *attr, char *buf)
 {
     u8 version;
     int err;
-    err = fpga_i2c_access(fpga_data->i2c_adapter[SW_I2C_CPLD_INDEX], 
-                          CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x00, 
-                          I2C_SMBUS_BYTE_DATA, 
+    err = fpga_i2c_access(fpga_data->i2c_adapter[SW_I2C_CPLD_INDEX],
+                          CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x00,
+                          I2C_SMBUS_BYTE_DATA,
                           (union i2c_smbus_data *)&version);
     if (err < 0)
         return err;
@@ -929,6 +927,106 @@ static ssize_t qsfp_reset_store(struct device *dev, struct device_attribute *att
 }
 DEVICE_ATTR_RW(qsfp_reset);
 
+static ssize_t qsfp_isr_flags_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+    u8 data;
+    u8 valid_bits;
+    struct sff_device_data *dev_data = dev_get_drvdata(dev);
+    unsigned int portid = dev_data->portid;
+    unsigned int REGISTER = SFF_PORT_INT_STATUS_BASE + (portid - 1) * 0x10;
+    valid_bits = BIT(INTR_INT_N) | BIT(INTR_PRESENT);
+
+    mutex_lock(&fpga_data->fpga_lock);
+    data = (u8) ioread32(fpga_dev.data_base_addr + REGISTER);
+    mutex_unlock(&fpga_data->fpga_lock);
+
+    /*
+     * Unify the return pattern to 2-bit
+     *  [1] : module interrupt
+     *  [0] : presence
+     */
+    data = data & valid_bits;
+    data = data >> 4;
+
+    return sprintf(buf, "0x%2.2x\n", data);
+}
+
+static ssize_t qsfp_isr_flags_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+    ssize_t status;
+    u32 value;
+    u8 valid_bits;
+    struct sff_device_data *dev_data = dev_get_drvdata(dev);
+    unsigned int portid = dev_data->portid;
+    unsigned int REGISTER = SFF_PORT_INT_STATUS_BASE + (portid - 1) * 0x10;
+    valid_bits = BIT(INTR_INT_N) | BIT(INTR_PRESENT);
+
+    mutex_lock(&fpga_data->fpga_lock);
+    status = kstrtou32(buf, 0, &value);
+    if (status == 0) {
+        value = value << 4;
+        value = value & valid_bits;
+        iowrite32(value, fpga_dev.data_base_addr + REGISTER);
+        status = count;
+    }
+    mutex_unlock(&fpga_data->fpga_lock);
+    return status;
+}
+DEVICE_ATTR_RW(qsfp_isr_flags);
+
+static ssize_t qsfp_isr_mask_show(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+    u32 data;
+    u8 valid_bits;
+    struct sff_device_data *dev_data = dev_get_drvdata(dev);
+    unsigned int portid = dev_data->portid;
+    unsigned int REGISTER = SFF_PORT_INT_MASK_BASE + (portid - 1) * 0x10;
+    valid_bits = BIT(INTR_INT_N) | BIT(INTR_PRESENT);
+
+    mutex_lock(&fpga_data->fpga_lock);
+    data = ioread32(fpga_dev.data_base_addr + REGISTER);
+    mutex_unlock(&fpga_data->fpga_lock);
+
+    /*
+     * Unify the return pattern to 2-bit
+     *  [1] : module interrupt
+     *  [0] : presence
+     */
+    data = data & valid_bits;
+    data = data >> 4;
+
+    return sprintf(buf, "0x%2.2x\n", data);
+}
+
+static ssize_t qsfp_isr_mask_store(struct device *dev,
+                                   struct device_attribute *attr,
+                                   const char *buf, size_t count)
+{
+    ssize_t status;
+    u32 value;
+    u8 valid_bits;
+    struct sff_device_data *dev_data = dev_get_drvdata(dev);
+    unsigned int portid = dev_data->portid;
+    unsigned int REGISTER = SFF_PORT_INT_MASK_BASE + (portid - 1) * 0x10;
+    valid_bits = BIT(INTR_INT_N) | BIT(INTR_PRESENT);
+
+    mutex_lock(&fpga_data->fpga_lock);
+    status = kstrtou32(buf, 0, &value);
+    if (status == 0) {
+        value = value << 4;
+        value = value & valid_bits;
+        iowrite32(value, fpga_dev.data_base_addr + REGISTER);
+        status = count;
+    }
+    mutex_unlock(&fpga_data->fpga_lock);
+    return status;
+}
+DEVICE_ATTR_RW(qsfp_isr_mask);
+
 static ssize_t sfp_txdisable_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
     u32 data;
@@ -967,15 +1065,105 @@ static ssize_t sfp_txdisable_store(struct device *dev, struct device_attribute *
 }
 DEVICE_ATTR_RW(sfp_txdisable);
 
+static ssize_t sfp_isr_flags_show(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+    u8 data;
+    u8 valid_bits;
+    struct sff_device_data *dev_data = dev_get_drvdata(dev);
+    unsigned int portid = dev_data->portid;
+    unsigned int REGISTER = SFF_PORT_INT_STATUS_BASE + (portid - 1) * 0x10;
+    valid_bits = BIT(INTR_RXLOS) | BIT(INTR_MODABS);
+
+    mutex_lock(&fpga_data->fpga_lock);
+    data = (u8) ioread32(fpga_dev.data_base_addr + REGISTER);
+    mutex_unlock(&fpga_data->fpga_lock);
+
+    data = data & valid_bits;
+
+    return sprintf(buf, "0x%2.2x\n", data);
+}
+
+static ssize_t sfp_isr_flags_store(struct device *dev,
+                                   struct device_attribute *attr,
+                                   const char *buf, size_t count)
+{
+    ssize_t status;
+    u32 value;
+    u8 valid_bits;
+    struct sff_device_data *dev_data = dev_get_drvdata(dev);
+    unsigned int portid = dev_data->portid;
+    unsigned int REGISTER = SFF_PORT_INT_STATUS_BASE + (portid - 1) * 0x10;
+    valid_bits = BIT(INTR_INT_N) | BIT(INTR_PRESENT);
+
+    mutex_lock(&fpga_data->fpga_lock);
+    status = kstrtou32(buf, 0, &value);
+    if (status == 0) {
+        value = value & valid_bits;
+        iowrite32(value, fpga_dev.data_base_addr + REGISTER);
+        status = count;
+    }
+    mutex_unlock(&fpga_data->fpga_lock);
+    return status;
+}
+DEVICE_ATTR_RW(sfp_isr_flags);
+
+static ssize_t sfp_isr_mask_show(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+    u32 data;
+    u8 valid_bits;
+    struct sff_device_data *dev_data = dev_get_drvdata(dev);
+    unsigned int portid = dev_data->portid;
+    unsigned int REGISTER = SFF_PORT_INT_MASK_BASE + (portid - 1) * 0x10;
+    valid_bits = BIT(INTR_RXLOS) | BIT(INTR_MODABS);
+
+    mutex_lock(&fpga_data->fpga_lock);
+    data = ioread32(fpga_dev.data_base_addr + REGISTER);
+    mutex_unlock(&fpga_data->fpga_lock);
+
+    data = data & valid_bits;
+
+    return sprintf(buf, "0x%2.2x\n", data);
+}
+
+static ssize_t sfp_isr_mask_store(struct device *dev,
+                                  struct device_attribute *attr,
+                                  const char *buf, size_t count)
+{
+    ssize_t status;
+    u32 value;
+    u8 valid_bits;
+    struct sff_device_data *dev_data = dev_get_drvdata(dev);
+    unsigned int portid = dev_data->portid;
+    unsigned int REGISTER = SFF_PORT_INT_MASK_BASE + (portid - 1) * 0x10;
+    valid_bits = BIT(INTR_RXLOS) | BIT(INTR_MODABS);
+
+    mutex_lock(&fpga_data->fpga_lock);
+    status = kstrtou32(buf, 0, &value);
+    if (status == 0) {
+        value = value & valid_bits;
+        iowrite32(value, fpga_dev.data_base_addr + REGISTER);
+        status = count;
+    }
+    mutex_unlock(&fpga_data->fpga_lock);
+    return status;
+}
+DEVICE_ATTR_RW(sfp_isr_mask);
+
 static struct attribute *sff_attrs[] = {
     &dev_attr_qsfp_modirq.attr,
     &dev_attr_qsfp_modprs.attr,
     &dev_attr_qsfp_lpmode.attr,
     &dev_attr_qsfp_reset.attr,
+    &dev_attr_qsfp_isr_flags.attr,
+    &dev_attr_qsfp_isr_mask.attr,
     &dev_attr_sfp_txfault.attr,
     &dev_attr_sfp_rxlos.attr,
     &dev_attr_sfp_modabs.attr,
     &dev_attr_sfp_txdisable.attr,
+    &dev_attr_sfp_isr_flags.attr,
+    &dev_attr_sfp_isr_mask.attr,
     NULL,
 };
 
@@ -1036,9 +1224,9 @@ static ssize_t port_led_color_show(struct device *dev, struct device_attribute *
     if (err < 0)
         return err;
     return sprintf(buf, "%s %s\n",
-                   led_color1 == 0x07 ? "off" : led_color1 == 0x06 ? "green" : led_color1 == 0x05 ?  "red" : led_color1 == 0x04 ? 
+                   led_color1 == 0x07 ? "off" : led_color1 == 0x06 ? "green" : led_color1 == 0x05 ?  "red" : led_color1 == 0x04 ?
                     "yellow" : led_color1 == 0x03 ? "blue" : led_color1 == 0x02 ?  "cyan" : led_color1 == 0x01 ?  "magenta" : "white",
-                   led_color1 == 0x07 ? "off" : led_color1 == 0x06 ? "green" : led_color1 == 0x05 ?  "red" : led_color1 == 0x04 ? 
+                   led_color1 == 0x07 ? "off" : led_color1 == 0x06 ? "green" : led_color1 == 0x05 ?  "red" : led_color1 == 0x04 ?
                     "yellow" : led_color1 == 0x03 ? "blue" : led_color1 == 0x02 ?  "cyan" : led_color1 == 0x01 ?  "magenta" : "white");
 }
 
@@ -1137,7 +1325,7 @@ static int i2c_wait_ack(struct i2c_adapter *a, unsigned long timeout, int writin
     timeout = jiffies + msecs_to_jiffies(timeout);
     while (1) {
         Status = ioread8(pci_bar + REG_SR0);
-        if (jiffies > timeout) {
+        if (!time_is_after_jiffies(timeout)) {
             info("Status %2.2X", Status);
             info("Error Timeout");
             error = -ETIMEDOUT;
@@ -1152,6 +1340,7 @@ static int i2c_wait_ack(struct i2c_adapter *a, unsigned long timeout, int writin
         if (writing == 0 && (Status & (1 << I2C_SR_BIT_MCF))) {
             break;
         }
+        schedule();
     }
     Status = ioread8(pci_bar + REG_SR0);
     iowrite8(0, pci_bar + REG_SR0);
@@ -1242,7 +1431,11 @@ static int smbus_access(struct i2c_adapter *adapter, u16 addr,
         goto Done;
     }
 
+#if 1 /* 100 kHz */
+    iowrite8(portid | 0x40, pci_bar + REG_ID0);
+#else
     iowrite8(portid, pci_bar + REG_ID0);
+#endif
 
     ////[S][ADDR/R]
     //Clear status register
@@ -1503,7 +1696,7 @@ static int fpga_i2c_access(struct i2c_adapter *adapter, u16 addr,
             }
             if(retry == 0)
                 goto release_unlock;
-            // update lasted port
+            // update latest port
             fpga_i2c_lasted_access_port[master_bus - 1] = switch_addr << 8 | channel;
 
         } else {
@@ -1934,7 +2127,9 @@ static int fpga_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
     printk(KERN_INFO "");
     fpga_version = ioread32(fpga_dev.data_base_addr);
     printk(KERN_INFO "FPGA VERSION : %8.8x\n", fpga_version);
-    fpgafw_init();
+    if ((err = fpgafw_init()) < 0){
+        goto pci_release;
+    }
     platform_device_register(&silverstone_dev);
     platform_driver_register(&silverstone_drv);
     return 0;
@@ -1943,7 +2138,7 @@ static int fpga_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
     pci_release_regions(pdev);
 pci_disable:
     pci_disable_device(pdev);
-    return -EBUSY;
+    return err;
 }
 
 static void fpga_pci_remove(struct pci_dev *pdev)
@@ -2077,7 +2272,6 @@ static int fpgafw_init(void) {
 
 static void fpgafw_exit(void) {
     device_destroy(fpgafwclass, MKDEV(majorNumber, 0));     // remove the device
-    class_unregister(fpgafwclass);                          // unregister the device class
     class_destroy(fpgafwclass);                             // remove the device class
     unregister_chrdev(majorNumber, DEVICE_NAME);            // unregister the major number
     printk(KERN_INFO "Goodbye!\n");