diff --git a/device/accton/x86_64-accton_minipack-r0/plugins/led_control.py b/device/accton/x86_64-accton_minipack-r0/plugins/led_control.py new file mode 100755 index 000000000000..26e2a7f7c8a2 --- /dev/null +++ b/device/accton/x86_64-accton_minipack-r0/plugins/led_control.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# +# led_control.py +# +# Platform-specific LED control functionality for SONiC +# + +try: + from sonic_led.led_control_base import LedControlBase + import swsssdk + import threading + import os + import logging + import struct + import time + import syslog + from socket import * + from select import * + from minipack.pimutil import PimUtil +except ImportError, e: + raise ImportError(str(e) + " - required module not found") + + +class LedControl(LedControlBase): + """Platform specific LED control class""" + SONIC_PORT_NAME_PREFIX = "Ethernet" + + + def __init__(self): + pim=PimUtil() + pim.init_pim_fpga() + + def _port_name_to_index(self, port_name): + # Strip "Ethernet" off port name + if not port_name.startswith(self.SONIC_PORT_NAME_PREFIX): + return -1 + + port_idx = int(port_name[len(self.SONIC_PORT_NAME_PREFIX):]) + return port_idx + + def _port_state_to_mode(self, port_idx, state): + if state == "up": + return 1, 4 #port linkup, led is green + else: + return 0, 0 #port linkdown, led is off + + def port_link_state_change(self, portname, state): + pim=PimUtil() + port_idx = self._port_name_to_index(portname) + new_control, led_mode = self._port_state_to_mode(port_idx, state) + color, control=pim.get_port_led(port_idx) + + if color==led_mode: + if control==new_control: + return + + pim.set_port_led(port_idx, led_mode, new_control)#port linkup, led is green + #port linkdown, led is off + diff --git a/device/accton/x86_64-accton_minipack-r0/plugins/sfputil.py b/device/accton/x86_64-accton_minipack-r0/plugins/sfputil.py old mode 100644 new mode 100755 index 16105acbf78a..6f62d8ed4c07 --- a/device/accton/x86_64-accton_minipack-r0/plugins/sfputil.py +++ b/device/accton/x86_64-accton_minipack-r0/plugins/sfputil.py @@ -6,6 +6,9 @@ try: import time from sonic_sfp.sfputilbase import SfpUtilBase + import os + import sys, getopt + from minipack.pimutil import PimUtil except ImportError as e: raise ImportError("%s - required module not found" % str(e)) @@ -16,7 +19,7 @@ class SfpUtil(SfpUtilBase): PORT_START = 0 PORT_END = 128 - BASE_OOM_PATH = "/sys/bus/i2c/devices/{0}-0050/" + LOCAL_OOM_PATH = "/usr/local/bin/minipack_qsfp/port%d_eeprom" _port_to_is_present = {} _port_to_lp_mode = {} @@ -53,42 +56,91 @@ def sfp_map(self, index): def __init__(self): - eeprom_path = self.BASE_OOM_PATH + "eeprom" - - for x in range(0, self.port_end+1): - bus = self.sfp_map(x) - self.port_to_eeprom_mapping[x] = eeprom_path.format( - bus) + for x in range(0, self.port_end): + self.port_to_eeprom_mapping[x] = self.LOCAL_OOM_PATH %x SfpUtilBase.__init__(self) + pim=PimUtil() + pim.init_pim_fpga() + + def __del__(self): + self.value=0 def get_presence(self, port_num): # Check for invalid port_num if port_num < self.port_start or port_num > self.port_end: return False - eeprom_path = self.port_to_eeprom_mapping[port_num] - with open(eeprom_path) as f: - try: - content = f.read(1) - except IOError as e: - #Not print any error, for if any, treat as Not present. - return False - return True - + pim=PimUtil() + status=pim.get_qsfp_presence(port_num) + return status + def get_low_power_mode(self, port_num): - raise NotImplementedError + if port_num < self.port_start or port_num > self.port_end: + return False + + pim=PimUtil() + return pim.get_low_power_mode(port_num) def set_low_power_mode(self, port_num, lpmode): - raise NotImplementedError + if port_num < self.port_start or port_num > self.port_end: + return False + pim=PimUtil() + pim.set_low_power_mode(port_num, lpmode) + return True def reset(self, port_num): - raise NotImplementedError + if port_num < self.port_start or port_num > self.port_end: + return False + pim=PimUtil() + pim.reset(port_num) + return True + + def get_transceiver_change_event(self, timeout=0): + pim=PimUtil() + start_time = time.time() + port_dict = {} + forever = False - def get_transceiver_change_event(self): - """ - TODO: This function need to be implemented - when decide to support monitoring SFP(Xcvrd) - on this platform. - """ - raise NotImplementedError + if timeout == 0: + forever = True + elif timeout > 0: + timeout = timeout / float(1000) # Convert to secs + else: + print "get_transceiver_change_event:Invalid timeout value", timeout + return False, {} + + end_time = start_time + timeout + if start_time > end_time: + print 'get_transceiver_change_event:' \ + 'time wrap / invalid timeout value', timeout + + return False, {} # Time wrap or possibly incorrect timeout + + while timeout >= 0: + change_status=0 + port_dict = pim.get_qsfp_interrupt() + present=0 + for key, value in port_dict.iteritems(): + if value==1: + present=self.get_presence(key) + change_status=1 + if present: + port_dict[key]='1' + else: + port_dict[key]='0' + + if change_status: + return True, port_dict + 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, {} + print "get_evt_change_event: Should not reach here." + return False, {} diff --git a/platform/broadcom/sonic-platform-modules-accton/minipack/classes/pimutil.py b/platform/broadcom/sonic-platform-modules-accton/minipack/classes/pimutil.py new file mode 100755 index 000000000000..778ff76afd19 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/minipack/classes/pimutil.py @@ -0,0 +1,578 @@ +# Copyright (c) 2019 Edgecore Networks Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT +# LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS +# FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. +# +# See the Apache Version 2.0 License for specific language governing +# permissions and limitations under the License. + +# ------------------------------------------------------------------ +# HISTORY: +# mm/dd/yyyy (A.D.) +# 5/29/2019: Jostar create for minipack +# ----------------------------------------------------------- +try: + import os + import sys, getopt + import subprocess + import click + import imp + import logging + import logging.config + import logging.handlers + import types + import time # this is only being used as part of the example + import traceback + from tabulate import tabulate + import fbfpgaio + import re + import time + from select import select + #from ctypes import fbfpgaio + +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + + +# pimutil.py +# +# Platform-specific PIM interface for SONiC +# + +iob = { + "revision": 0x0, + "scratchpad": 0x4, + "interrupt_status": 0x2C, + "pim_status": 0x40, + "pim_present_intr_mask": 0x44, +} + +dom_base = [ + 0xFFFFFFFF, # Padding + 0x40000, + 0x48000, + 0x50000, + 0x58000, + 0x60000, + 0x68000, + 0x70000, + 0x78000, +] + + +dom = { + "revision": 0x0, + "system_led": 0xC, + "intr_status": 0x2C, + "qsfp_present": 0x48, + "qsfp_present_intr": 0x50, + "qsfp_present_intr_mask": 0x58, + "qsfp_intr": 0x60, + "qsfp_intr_mask": 0x68, + "qsfp_reset": 0x70, + "qsfp_lp_mode": 0x78, + "device_power_bad_status": 0x90, + "port_led_color_profile": { + 0: 0x300, + 1: 0x300, + 2: 0x304, + 3: 0x304, + 4: 0x308, + 5: 0x308, + 6: 0x30C, + 7: 0x30C, + }, + "port_led_control": { + 1: 0x310, + 2: 0x314, + 3: 0x318, + 4: 0x31C, + 5: 0x320, + 6: 0x324, + 7: 0x328, + 8: 0x32C, + 9: 0x330, + 10: 0x334, + 11: 0x338, + 12: 0x33C, + 13: 0x340, + 14: 0x344, + 15: 0x348, + 16: 0x34C, + }, + "dom_control_config": 0x410, + "dom_global_status": 0x414, + "dom_data": 0x4000, + + + "mdio": { + "config": 0x0200, + "command": 0x0204, + "write": 0x0208, + "read": 0x020C, + "status": 0x0210, + "intr_mask": 0x0214, + "source_sel": 0x0218, + }, # mdio +} + +mdio_read_cmd = 0x1 +mdio_write_cmd = 0x0 +mdio_device_type = 0x1F + + +#fbfpgaio=cdll.LoadLibrary('./fbfpgaio.so') + +def init_resources(): + fbfpgaio.hw_init() + return + +def release_resources(): + fbfpgaio.hw_release() + return + +def fpga_io(offset, data=None): + if data is None: + return fbfpgaio.hw_io(offset) + else: + fbfpgaio.hw_io(offset, data) + return + +def pim_io(pim, offset, data=None): + global dom_base + target_offset = dom_base[pim]+offset + if data is None: + retval = fpga_io(target_offset) + #print ("0x%04X" % retval) # idebug + return retval + else: + retval = fpga_io(target_offset, data) + return retval + + +def show_pim_present(): + pim_status = fpga_io(iob["pim_status"]) + + header = "PIM # " + status_str = " " + for shift in range(0,8): + status = pim_status & (0x10000 << shift) #[23:16] from pim_0 to pim_7 + header += " %d " % (shift+1) + if status: + status_str += (" | ") + else: + status_str += (" X ") + print(header) + print(status_str) + +def show_qsfp_present_status(pim_num): + status = fpga_io(dom_base[pim_num]+dom["qsfp_present"]) + interrupt = fpga_io(dom_base[pim_num]+dom["qsfp_present_intr"]) + mask = fpga_io(dom_base[pim_num]+dom["qsfp_present_intr_mask"]) + + print + print(" (0x48) (0x50) (0x58)") + print(" 0x%08X 0x%08X 0x%08X" %(status, interrupt, mask)) + print(" Status Interrupt Mask") + for row in range(8): + output_str = str() + status_left = bool(status & (0x1 << row*2)) + status_right = bool(status & (0x2 << row*2)) + interrupt_left = bool(interrupt & (0x1 << row*2)) + interrupt_right = bool(interrupt & (0x2 << row*2)) + mask_left = bool(mask & (0x1 << row*2)) + mask_right = bool(mask & (0x2 << row*2)) + print("%2d: %d %d %d %d %d %d" % \ + (row*2+1, status_left, status_right, \ + interrupt_left, interrupt_right, \ + mask_left, mask_right)) + print + + + +#pim_index start from 0 to 7 +#port_index start from 0 to 127. Each 16-port is to one pim card. +class PimUtil(object): + + PORT_START = 0 + PORT_END = 127 + + def __init__(self): + self.value=1 + + def __del__(self): + self.value=0 + + def init_pim_fpga(self): + init_resources() + + def release_pim_fpga(self): + release_resources() + + def get_pim_by_port(self, port_num): + if port_num < self.PORT_START or port_num > self.PORT_END: + return False + pim_num=port_num/16 + return True, pim_num+1 + + def get_onepimport_by_port(self, port_num): + if port_num < self.PORT_START or port_num > self.PORT_END: + return False + if port_num < 16: + return True, port_num + else: + return True, port_num%16 + + def get_pim_presence(self, pim_num): + if pim_num <0 or pim_num > 7: + return 0 + pim_status = fpga_io(iob["pim_status"]) + status = pim_status & (0x10000 << pim_num) + if status: + return 1 #present + else: + return 0 #not present + + #return code=0:100G. return code=1:400G + def get_pim_board_id(self, pim_num): + if pim_num <0 or pim_num > 7: + return False + board_id = fpga_io(dom_base[pim_num+1]+dom["revision"]) + board_id = board_id & 0x1 + if board_id==0x0: + return 0 + else: + return 1 + + + def get_pim_status(self, pim_num): + if pim_num <0 or pim_num > 7: + return 0xFF + power_status =0 + #device_power_bad_status + status=fpga_io(dom_base[pim_num+1]+dom["device_power_bad_status"]) + + for x in range(0, 5): + if status & ( (0xf) << (4*x) ) : + power_status = power_status | (0x1 << x) + + if ( status & 0x1000000): + power_status=power_status | (0x1 << 5) + if ( status & 0x2000000): + power_status=power_status | (0x1 << 6) + if ( status & 0x8000000): + power_status=power_status | (0x1 << 7) + if ( status & 0x10000000): + power_status=power_status | (0x1 << 8) + if ( status & 0x40000000): + power_status=power_status | (0x1 << 9) + if ( status & 0x80000000): + power_status=power_status | (0x1 << 10) + + return power_status + #path=0:MDIO path is set on TH3. path=1:MDIO path is set on FPGA. + def set_pim_mdio_source_sel(self, pim_num, path): + if pim_num <0 or pim_num > 7: + return False + status= pim_io(pim_num+1, dom["mdio"]["source_sel"]) + + if path==1: + status = status | 0x2 + else: + status = status & 0xfffffffd + + pim_io(pim_num+1, dom["mdio"]["source_sel"], status) + return True + #retrun code=0, path is TH3. retrun code=1, path is FPGA + def get_pim_mdio_source_sel(sefl, pim_num): + if pim_num <0 or pim_num > 7: + return False + path= pim_io(pim_num+1, dom["mdio"]["source_sel"]) + path = path & 0x2 + if path: + return 1 + else: + return 0 + + #This api will set mdio path to MAC side.(At default, mdio path is set to FPGA side). + def pim_init(self, pim_num): + if pim_num <0 or pim_num > 7: + return False + status=self.set_pim_mdio_source_sel(pim_num, 0) + #put init phy cmd here + + + #return code="pim_dict[pim_num]='1' ":insert evt. return code="pim_dict[pim_num]='0' ":remove evt + def get_pim_change_event(self, timeout=0): + start_time = time.time() + pim_dict = {} + forever = False + + if timeout == 0: + forever = True + elif timeout > 0: + timeout = timeout / float(1000) # Convert to secs + else: + print "get_transceiver_change_event:Invalid timeout value", timeout + return False, {} + + end_time = start_time + timeout + if start_time > end_time: + print 'get_transceiver_change_event:' \ + 'time wrap / invalid timeout value', timeout + + return False, {} # Time wrap or possibly incorrect timeout + + pim_mask_status = fpga_io(iob["pim_present_intr_mask"], 0xffff00000) + + while timeout >= 0: + new_pim_status=0 + pim_status = fpga_io(iob["pim_status"]) + present_status= pim_status & 0xff0000 + change_status=pim_status & 0xff + interrupt_status = fpga_io(iob["interrupt_status"]) + + for pim_num in range(0,8): + if change_status & (0x1 << pim_num) : + status = present_status & (0x10000 << pim_num) + new_pim_status = new_pim_status | (0x1 << pim_num) #prepare to W1C to clear + if status: + pim_dict[pim_num]='1' + else: + pim_dict[pim_num]='0' + + if change_status: + new_pim_status = pim_status | new_pim_status #Write one to clear interrupt bit + fpga_io(iob["pim_status"], new_pim_status) + return True, pim_dict + 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, {} + print "get_evt_change_event: Should not reach here." + return False, {} + + + def get_pim_max_number(self): + return 8 + + #pim_num start from 0 to 7 + #color:0=amber, 1=blue + #contrl:off(0),on(1), flash(2) + def set_pim_led(self, pim_num, color, control): + if pim_num <0 or pim_num > 7: + return False + + led_val=fpga_io(dom_base[pim_num+1]+dom["system_led"]) + + if color==1: + led_val = led_val | (0x8000 | 0x4000) #blue + elif color==0: + led_val = (led_val & ( ~ 0x8000)) | 0x4000 #amber + else: + print "Set RGB control to Green1" + led_val = led_val & (~ 0x4000) + led_val = led_val & (~ 0xfff) + led_val = led_val | 0x0f0 #B.G.R Birghtness, set to Green + + if control==0: + led_val = led_val & ( ~ 0x3000) #Off + elif control==1: + led_val = led_val & ( ~ 0x3000) #Off + led_val = led_val | 0x1000 #On + else: + led_val = led_val | 0x3000 #Flash + + fpga_io(dom_base[pim_num+1]+dom["system_led"], led_val) + + def get_qsfp_presence(self, port_num): + #xlate port to get pim_num + status, pim_num=self.get_pim_by_port(port_num) + + if status==0: + return False + else: + present = fpga_io(dom_base[pim_num]+dom["qsfp_present"]) + status, shift = self.get_onepimport_by_port(port_num) + if status==0: + return False + else: + if bool(present & (0x1 << shift)): + return 1 #present + else: + return 0 #not present + + #return code: low_power(1) or high_power(0) + def get_low_power_mode(self, port_num): + status, pim_num=self.get_pim_by_port(port_num) + + if status==0: + return False + else: + lp_mode = fpga_io(dom_base[pim_num]+dom["qsfp_lp_mode"]) + + status, shift=self.get_onepimport_by_port(port_num) + if status==0: + return False + else: + if (lp_mode & (0x1 << shift)): + return 1 #low + else: + return 0 #high + + #lpmode=1 to hold QSFP in low power mode. lpmode=0 to release QSFP from low power mode. + def set_low_power_mode(self, port_num, mode): + status, pim_num=self.get_pim_by_port(port_num) + if status==0: + return False + val = fpga_io(dom_base[pim_num]+dom["qsfp_lp_mode"]) + status, shift=self.get_onepimport_by_port(port_num) + if status==0: + return False + else: + if mode==0: + new_val = val & (~(0x1 << shift)) + else: + new_val=val|(0x1 << shift) + status=fpga_io(dom_base[pim_num]+dom["qsfp_lp_mode"], new_val) + return status + + #port_dict[idx]=1 means get interrupt(change evt), port_dict[idx]=0 means no get interrupt + def get_qsfp_interrupt(self): + port_dict={} + #show_qsfp_present_status(1) + for pim_num in range(0, 8): + fpga_io(dom_base[pim_num+1]+dom["qsfp_present_intr_mask"], 0xffff0000) + fpga_io(dom_base[pim_num+1]+dom["qsfp_intr_mask"], 0xffff0000) + for pim_num in range(0, 8): + clear_bit=0 + qsfp_present_intr_status = fpga_io(dom_base[pim_num+1]+dom["qsfp_present_intr"]) + interrupt_status = qsfp_present_intr_status & 0xffff + #time.sleep(2) + if interrupt_status: + for idx in range (0,16): + port_idx=idx + (pim_num*16) + if interrupt_status & (0x1< +#include +#include +#include +#include + +//#define IDEBUG(...) printf(__VA_ARGS__) +#define IDEBUG(...) + +#define FPGA_RESOURCE_NODE "/sys/devices/pci0000:00/0000:00:03.0/0000:05:00.0/resource0" +#define FPGA_RESOURCE_LENGTH 0x80000 + + +static int hw_handle = -1; +static void *io_base = NULL; + +static PyObject *fbfpgaio_hw_init(PyObject *self) +{ + const char fpga_resource_node[] = FPGA_RESOURCE_NODE; + + /* Open hardware resource node */ + hw_handle = open(fpga_resource_node, O_RDWR|O_SYNC); + if (hw_handle == -1) { + IDEBUG("[ERROR] %s: open hw resource node\n", __func__); + return Py_False; + } + + IDEBUG("[PASS] %s: open hw resource node\n", __func__); + + /* Mapping hardware resource */ + io_base = mmap(NULL, FPGA_RESOURCE_LENGTH, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_NORESERVE, hw_handle, 0); + if (io_base == MAP_FAILED) { + IDEBUG("[ERROR] %s: mapping resource node\n", __func__); + perror("map_failed"); + fprintf(stderr,"%d %s\\n",errno,strerror(errno)); + return Py_False; + } + + IDEBUG("[PASS] %s: mapping resource node\n", __func__); + + return Py_True; +} + +static PyObject *fbfpgaio_hw_release(PyObject *self) +{ + int retval = 0; + + if ((io_base != NULL) && (io_base != MAP_FAILED)) { + retval = munmap(io_base, FPGA_RESOURCE_LENGTH); + if (retval == 0) { + IDEBUG("[PASS] %s: Unmapping hardware resources\n", __func__); + close(hw_handle); + return Py_True; + } + } + + IDEBUG("[ERROR] %s: unmapping resource node\n", __func__); + return Py_False; +} + +static PyObject *fbfpgaio_hw_io(PyObject *self, PyObject *args) +{ + void *offset = NULL; + /* We are not able to diffrentiate the input data between an unsigned value or a + 'None' object. We assume that the input data (if any) will be an unsigned integer. + The default value of 'data' is larger than the max. number of unsigned integer. + This value signify that the caller of this function does not input a data argument. */ + unsigned long input_data = 0x1FFFFFFFF; + + if (!PyArg_ParseTuple(args, "I|k", &offset, &input_data)) { + return NULL; + } + + if (input_data == 0x1FFFFFFFF) { + // Read operation + IDEBUG("Read operation\n"); + unsigned int *address = (unsigned int *) ((unsigned long) io_base + (unsigned long) offset); + return Py_BuildValue("k", *address); + } else { + // Write operation + IDEBUG("Write operation\n"); + unsigned int *address = (unsigned int *) ((unsigned long) io_base + (unsigned long) offset); + unsigned int data = (unsigned int) (input_data & 0xFFFFFFFF); + *address = data; + + Py_INCREF(Py_None); + return Py_None; + } +} + +static PyMethodDef FbfpgaMethods[] = { + { "hw_init", (PyCFunction) fbfpgaio_hw_init, METH_NOARGS, "Initialize resources for accessing FPGA" }, + { "hw_release", (PyCFunction) fbfpgaio_hw_release, METH_NOARGS, "Release resources for accessing FPGA" }, + { "hw_io", fbfpgaio_hw_io, METH_VARARGS, "Access FPGA" }, + { NULL, NULL, 0, NULL }, +}; + +PyMODINIT_FUNC +initfbfpgaio(void) +{ + char docstr[] = "\ +1. hw_init():\n\ + return value: True/False\n\ +2. hw_release():\n\ + return value: True/False\n\ +3. hw_io(offset,[data])\n\ + return value:\n\ + In reading operation: data which is read from FPGA\n\ + In writing operation: None\n"; + + (void) Py_InitModule3("fbfpgaio", FbfpgaMethods, docstr); +} diff --git a/platform/broadcom/sonic-platform-modules-accton/minipack/service/minipack-setup-qsfp-oom.service b/platform/broadcom/sonic-platform-modules-accton/minipack/service/minipack-setup-qsfp-oom.service new file mode 100644 index 000000000000..df1c961bf181 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/minipack/service/minipack-setup-qsfp-oom.service @@ -0,0 +1,16 @@ +[Unit] +Description=Accton MiniPack Platform setup qsfp oom service +Before=pmon.service +After=minipack-platform-init.service +DefaultDependencies=no + +[Service] +ExecStart=/usr/local/bin/setup_qsfp_eeprom.py +KillSignal=SIGKILL +SuccessExitStatus=SIGKILL + +# Resource Limitations +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-accton/minipack/setup.py b/platform/broadcom/sonic-platform-modules-accton/minipack/setup.py old mode 100644 new mode 100755 index aca53ea12798..b5d08c090bab --- a/platform/broadcom/sonic-platform-modules-accton/minipack/setup.py +++ b/platform/broadcom/sonic-platform-modules-accton/minipack/setup.py @@ -2,9 +2,11 @@ import os import sys -from setuptools import setup +from setuptools import setup, Extension os.listdir +module1 = Extension("fbfpgaio", sources = ["minipack/lib/fbfpgaiomodule.c"]) + setup( name='minipack', version='1.0', @@ -12,5 +14,7 @@ packages=['minipack'], package_dir={'minipack': 'minipack/classes'}, + ext_modules=[module1], + ) diff --git a/platform/broadcom/sonic-platform-modules-accton/minipack/utils/accton_minipack_util.py b/platform/broadcom/sonic-platform-modules-accton/minipack/utils/accton_minipack_util.py index 6f3485579c52..261074c5d99a 100755 --- a/platform/broadcom/sonic-platform-modules-accton/minipack/utils/accton_minipack_util.py +++ b/platform/broadcom/sonic-platform-modules-accton/minipack/utils/accton_minipack_util.py @@ -238,7 +238,7 @@ def device_install(): for i in range(0,len(mknod)): #for pca932x need times to built new i2c buses if mknod[i].find('pca954') != -1: - time.sleep(1) + time.sleep(2) status, output = log_os_system(mknod[i], 1) if status: @@ -246,49 +246,14 @@ def device_install(): if FORCE == 0: return status - # initialize multiplexer for 8 PIMs - cmdl = "echo pca9548 0x%x > /sys/bus/i2c/devices/i2c-%d/new_device" - for pim in range(2, 10): - cmdm = cmdl % (0x72, pim) - status, output =log_os_system(cmdm, 1) - cmdm = cmdl % (0x71, pim) - status, output =log_os_system(cmdm, 1) - - for i in range(0, NO_QSFP): - bus = sfp_map(i) - status, output =log_os_system( - "echo optoe1 0x50 > /sys/bus/i2c/devices/i2c-"+str(bus)+"/new_device", 1) - if status: - print output - if FORCE == 0: - return status - status, output =log_os_system( - "echo port"+str(i+1)+" > /sys/bus/i2c/devices/"+str(bus)+"-0050/port_name", 1) - if status: - print output - if FORCE == 0: - return status + rm_cmd="rm -rf /usr/local/bin/minipack_qsfp > /dev/null 2>&1" + log_os_system(rm_cmd, 1) + mk_cmd= "mkdir /usr/local/bin/minipack_qsfp" + log_os_system(mk_cmd, 1) return def device_uninstall(): - for i in range(0,NO_QSFP): - bus = sfp_map(i) - target = "/sys/bus/i2c/devices/i2c-"+str(bus)+"/delete_device" - status, output =log_os_system("echo 0x50 > "+ target, 1) - if status: - print output - if FORCE == 0: - return status - - # Multiplexer for 8 PIMs - cmdl = "echo 0x%x > /sys/bus/i2c/devices/i2c-%d/delete_device" - for pim in range(2, 10): - cmdm = cmdl % (0x72, pim) - status, output =log_os_system(cmdm, 1) - cmdm = cmdl % (0x71, pim) - status, output =log_os_system(cmdm, 1) - nodelist = mknod for i in range(len(nodelist)): target = nodelist[-(i+1)] diff --git a/platform/broadcom/sonic-platform-modules-accton/minipack/utils/setup_qsfp_eeprom.py b/platform/broadcom/sonic-platform-modules-accton/minipack/utils/setup_qsfp_eeprom.py new file mode 100755 index 000000000000..84388520b902 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/minipack/utils/setup_qsfp_eeprom.py @@ -0,0 +1,317 @@ +#!/usr/bin/env python +# +# Copyright (c) 2019 Edgecore Networks Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT +# LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS +# FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. +# +# See the Apache Version 2.0 License for specific language governing +# permissions and limitations under the License. + +# ------------------------------------------------------------------ +# HISTORY: +# mm/dd/yyyy (A.D.) +# 7/22/2019: Jostar create for minipack +# ------------------------------------------------------------------ + +try: + import os + import sys, getopt + import subprocess + import subprocess + import click + import imp + import commands + import logging + import logging.config + import logging.handlers + import types + import time # this is only being used as part of the example + import traceback + from tabulate import tabulate + from minipack.pimutil import PimUtil +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + +#2:idle state. 1:insert state, 0:remove state. +pim_state=[2,2,2,2,2,2,2,2] + +# port_use_i2c_bus[idx], idx means port_idx. Idx start from 0 to 127 +# port_use_i2c_bus[0] means port0 use i2c-device number +# port_use_i2c_bus[1] means port1 use i2c-device number +#At default , port_use_i2c_bus are 0. When PIM insert, it will no be 0 +port_use_i2c_bus= [0] * 128 + +#pim_port_use_bus[idx] are for 8 channel use. At default, pim_port_use_bus[idx]=0 +#pim_port_use_bus[idx] will save pim_idx(0 to 7). +#setup service will check when pim insert at first time. +#It will find +# 1. Does this pim card insert or not in the past. So it will check pim_port_use_bus[idx] +# 2. If make sure that pim_port_use_bus[idx]==0. Assgin oom start i2c dev numer to pim_port_use_bus[idx]. +#Never set pim_port_use_bus[idx] to 0 +#So if pim next insert, it need to check whether pim_port_use_bus[idx] is 0 or not. +#if pim_port_use_bus[idx]!=0, means this pim card has setup oom sysfs +pim_port_use_bus=[0] * 8 + +oom_i2c_bus_table=1 + +pim_dev=PimUtil() + +# Deafults +VERSION = '1.0' +FUNCTION_NAME = '/usr/local/bin/setup_qsfp_eeprom' +DEBUG = False +PIM_MIN=0 +PIM_MAX=8 + +def my_log(txt): + if DEBUG == True: + print "[ACCTON DBG]: "+txt + return + +def log_os_system(cmd): + logging.info('Run :'+cmd) + status = 1 + output = "" + status, output = commands.getstatusoutput(cmd) + if status: + logging.info('Failed :'+cmd) + return status, output + +def qsfp_map_bus(idx): + port = idx + 1 + base = ((port-1)/8*8) + 10 + idx = (port - 1) % 8 + idx = 7 - idx + if (idx%2): + idx = idx -1 + else: + idx = idx +1 + bus = base + idx + return bus + +def pca9548_sysfs(i2c_bus, create): + if create==1: + cmdl = "echo pca9548 0x%x > /sys/bus/i2c/devices/i2c-%d/new_device" + else: + cmdl = "echo 0x%x > /sys/bus/i2c/devices/i2c-%d/delete_device" + + cmdm = cmdl % (0x72, i2c_bus) + status1, output =log_os_system(cmdm) + cmdm = cmdl % (0x71, i2c_bus) + status2, output =log_os_system(cmdm) + return (status1 | status2) + + +def qsfp_eeprom_sys(pim_idx, i2c_bus_order, create): + # initialize multiplexer for 8 PIMs + global port_use_i2c_bus + start_port=pim_idx*16 + end_port = (pim_idx+1)*16 + start_bus=(i2c_bus_order-1)*16 + end_bus = i2c_bus_order*16 + k=start_port + for i in range(start_bus, end_bus): + bus = qsfp_map_bus(i) + if create==1: + status, output =log_os_system( + "echo optoe1 0x50 > /sys/bus/i2c/devices/i2c-"+str(bus)+"/new_device") + if status: + print output + return 1 + status, output =log_os_system( + "echo port"+str(k+1)+" > /sys/bus/i2c/devices/"+str(bus)+"-0050/port_name") + + status, output =log_os_system( + "ln -s -f /sys/bus/i2c/devices/"+str(bus)+"-0050/eeprom" + " /usr/local/bin/minipack_qsfp/port" + str(k) + "_eeprom") + if status: + print output + return 1 + else: + status, output =log_os_system( + "echo 0x50 > /sys/bus/i2c/devices/i2c-"+str(bus)+"/delete_device") + if status: + print output + + k=k+1 + + return 0 + +def check_pca_active( i2c_addr, bus): + cmd = "i2cget -y -f %d 0x%x 0x0" + cmd = cmd %(bus, i2c_addr) + status, output = commands.getstatusoutput(cmd) + return status + +def set_pim_port_use_bus(pim_idx): + + global pim_port_use_bus + global oom_i2c_bus_table + + if pim_port_use_bus[pim_idx]!=0: + return 0 + + pim_port_use_bus[pim_idx]=oom_i2c_bus_table + oom_i2c_bus_table=oom_i2c_bus_table+1 + + return pim_port_use_bus[pim_idx] + + +def del_pim_port_use_bus(pim_idx): + global oom_i2c_bus_table + + oom_i2c_bus_table=oom_i2c_bus_table-1 + pim_port_use_bus[pim_idx]=0 + + +def device_remove(): + cmd1 = "echo 0x%x > /sys/bus/i2c/devices/i2c-%d/delete_device" + + for bus in range(2, 10): + #ret=check_pca_active(0x72, bus) + #if ret==0: + + cmdm= cmd1 % (0x72, bus) + status, output = commands.getstatusoutput(cmdm) + print "Remove %d-0072 i2c device"%bus + cmdm= cmd1 % (0x71, bus) + status, output = commands.getstatusoutput(cmdm) + print "Remove %d-0071 i2c device"%bus + + cmd="rm -f /usr/local/bin/minipack_qsfp/port*" + status, output=log_os_system(cmd) + return status + +class device_monitor(object): + + PIM_STATE_REMOVE = 0 + PIM_STATE_INSERT = 1 + PIM_STATE_IDLE=2 + + def __init__(self, log_file, log_level): + """Needs a logger and a logger level.""" + # set up logging to file + logging.basicConfig( + filename=log_file, + filemode='w', + level=log_level, + format= '[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s', + datefmt='%H:%M:%S' + ) + # set up logging to console + if log_level == logging.DEBUG: + console = logging.StreamHandler() + console.setLevel(log_level) + formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') + console.setFormatter(formatter) + logging.getLogger('').addHandler(console) + + sys_handler = handler = logging.handlers.SysLogHandler(address = '/dev/log') + sys_handler.setLevel(logging.WARNING) + logging.getLogger('').addHandler(sys_handler) + + #logging.debug('SET. logfile:%s / loglevel:%d', log_file, log_level) + + def manage_pim(self): + global pim_dev + global pim_state + + for pim_idx in range(PIM_MIN, PIM_MAX): + presence=pim_dev.get_pim_presence(pim_idx) + if presence==1: + if pim_state[pim_idx]!=self.PIM_STATE_INSERT: + #find which i2c_device can use. It start from 2 + i2c_bus_order=set_pim_port_use_bus(pim_idx) + if i2c_bus_order==0: + logging.info("pim_state[%d] PIM_STATE_INSERT", pim_idx); + pim_state[pim_idx]=self.PIM_STATE_INSERT + continue + + logging.info ("pim_idx=%d oom use i2c_bus_order=%d", pim_idx, i2c_bus_order) + ready=0 + retry_limit=100 + retry_count=0 + while retry_count < retry_limit: + ret=check_pca_active(0x72, pim_idx+2) + if ret==0: + ready=1 + break + retry_count=retry_count+1 + time.sleep(0.2) + + if ready==1: + status=pca9548_sysfs(pim_idx+2, 1) + if status: + status=pca9548_sysfs(pim_idx+2, 0) #del pca i2c device, give up set oom at this time. + del_pim_port_use_bus(pim_idx) + continue + status=qsfp_eeprom_sys(pim_idx,i2c_bus_order, 1) + if status: + status=pca9548_sysfs(pim_idx+2, 0) #del pca i2c device, give up set oom at this time. + del_pim_port_use_bus(pim_idx) + continue + #ret_2=check_pca_active(0x72, pim_idx+2) + #ret_1=check_pca_active(0x71, pim_idx+2) + + pim_state[pim_idx]=self.PIM_STATE_INSERT + logging.info("pim_state[%d] PIM_STATE_INSERT", pim_idx); + else: + print "retry check 100 times for check pca addr" + del_pim_port_use_bus(pim_idx) + else: + if pim_state[pim_idx]==self.PIM_STATE_INSERT: + #pca9548_sysfs(pim_idx+2, 0) + pim_state[pim_idx]=self.PIM_STATE_REMOVE + logging.info("pim_state[%d] PIM_STATE_REMOVE", pim_idx); + +def main(argv): + log_file = '%s.log' % FUNCTION_NAME + log_level = logging.INFO + remove_dev=0 + cpu_pca_i2c_ready=0 + + if len(sys.argv) != 1: + try: + opts, args = getopt.getopt(argv,'hdlr',['lfile=']) + except getopt.GetoptError: + print 'A:Usage: %s [-d] [-l ]' % sys.argv[0] + return 0 + for opt, arg in opts: + if opt == '-h': + print 'B:Usage: %s [-d] [-l ]' % sys.argv[0] + return 0 + elif opt in ('-d', '--debug'): + log_level = logging.DEBUG + elif opt in ('-l', '--lfile'): + log_file = arg + elif opt in ('-r', '--remove'): + remove_dev=1 + + if remove_dev==1: + device_remove() + return 0 + monitor = device_monitor(log_file, log_level) + global pim_dev + pim_dev.init_pim_fpga() + + while cpu_pca_i2c_ready==0: + status=check_pca_active(0x70, 1) + time.sleep(0.5) + if status==0: + cpu_pca_i2c_ready=1 + print "Make sure CPU pca i2c device is ready" + break + + while True: + monitor.manage_pim() + time.sleep(2) + + +if __name__ == "__main__": + main(sys.argv[1:])