From 6cdb64488f4d2562a4a3d086595364f16770cd26 Mon Sep 17 00:00:00 2001 From: philo Date: Thu, 8 Jun 2023 18:55:05 +0800 Subject: [PATCH 1/7] Add new platform m2-b6010 Signed-off-by: philo --- .azure-pipelines/azure-pipelines-build.yml | 4 +- .../azure-pipelines-image-template.yml | 2 +- .gitignore | 2 +- 0001-update-swss-main-cpp.patch | 42 + 0002-update-swss-orch-h.patch | 10 + Makefile.work | 11 +- azure-pipelines.yml | 171 +- .../M2-w6010-48gt4x-fa-board.json | 868 ++++ .../M2-w6010-48gt4x-fa-p-chip-profile.txt | 96 + .../M2-w6010-48gt4x-fa-p-datapath.txt | 281 ++ .../M2-W6010-48GT4X-FA/default_sku | 1 + .../M2-W6010-48GT4X-FA/phy_mapping.cfg | 198 + .../M2-W6010-48GT4X-FA/platform_asic | 1 + .../M2-W6010-48GT4X-FA/port_config.ini | 53 + .../M2-W6010-48GT4X-FA/sai.profile | 4 + .../M2-W6010-48GT4X-FA/start_up.cfg | 48 + .../default_sku | 1 + .../installer.conf | 9 + .../platform.json | 173 + .../platform_asic | 1 + .../plugins/sfputil.py | 428 ++ .../plugins/ssd_util.py | 112 + .../pmon_daemon_control.json | 7 + .../system_health_monitoring_config.json | 0 .../M2-w6010-48gt4x-ra-board.json | 868 ++++ .../M2-w6010-48gt4x-ra-p-chip-profile.txt | 96 + .../M2-w6010-48gt4x-ra-p-datapath.txt | 281 ++ .../M2-W6010-48GT4X-RA/default_sku | 1 + .../M2-W6010-48GT4X-RA/phy_mapping.cfg | 198 + .../M2-W6010-48GT4X-RA/platform_asic | 1 + .../M2-W6010-48GT4X-RA/port_config.ini | 53 + .../M2-W6010-48GT4X-RA/sai.profile | 4 + .../M2-W6010-48GT4X-RA/start_up.cfg | 48 + .../default_sku | 1 + .../installer.conf | 9 + .../platform.json | 173 + .../platform_asic | 1 + .../plugins/sfputil.py | 428 ++ .../plugins/ssd_util.py | 112 + .../pmon_daemon_control.json | 7 + .../system_health_monitoring_config.json | 0 files/image_config/platform/rc.local | 2 +- platform/centec-arm64/one-image.mk | 2 + .../centec-arm64/platform-modules-micas.dep | 9 + .../centec-arm64/platform-modules-micas.mk | 15 + platform/centec-arm64/platform.conf | 4 +- platform/centec-arm64/rules.mk | 1 + platform/centec-arm64/sai.mk | 5 +- .../sonic-platform-modules-micas/LICENSE | 15 + .../sonic-platform-modules-micas/README.md | 1 + .../common/Makefile | 50 + .../common/app/Makefile | 26 + .../common/app/firmware_upgrade/Makefile | 20 + .../common/app/firmware_upgrade/Rules.mk | 45 + .../firmware-cpld-ispvme.spec | 25 + .../firmware_driver_ispvme/Makefile | 22 + .../firmware_driver_ispvme/config_gpio.c | 473 ++ .../firmware_driver_ispvme/config_gpio.h | 29 + .../firmware_cpld_ispvme.c | 280 ++ .../firmware_cpld_upgrade_ispvme.c | 326 ++ .../firmware_driver_ispvme/firmware_ispvme.c | 139 + .../include/config_ispvme.h | 10 + .../include/firmware_cpld_ispvme.h | 52 + .../include/firmware_ispvme.h | 90 + .../firmware_upgrade_ispvme/Makefile | 39 + .../firmware_upgrade_ispvme/common.h | 28 + .../firmware_upgrade_ispvme/debug_ispvme.c | 64 + .../firmware_upgrade_ispvme/dfd_fpga_debug.c | 117 + .../firmware_upgrade_ispvme/dfd_fpga_debug.h | 70 + .../firmware_upgrade_ispvme/dfd_fpga_pkt.c | 318 ++ .../firmware_upgrade_ispvme/dfd_fpga_pkt.h | 95 + .../firmware_upgrade_ispvme/dfd_fpga_upg.c | 1440 +++++++ .../firmware_app_ispvme.c | 903 ++++ .../firmware_upgrade_ispvme/hardware.c | 249 ++ .../include/debug_ispvme.h | 21 + .../include/firmware_app_ispvme.h | 119 + .../firmware_upgrade_ispvme/ispvm_ui.c | 845 ++++ .../firmware_upgrade_ispvme/ivm_core.c | 3033 +++++++++++++ .../firmware_upgrade_ispvme/vmopcode.h | 192 + .../common/app/libextphy.so | Bin 0 -> 722264 bytes .../common/lib/eepromutil/__init__.py | 0 .../common/lib/eepromutil/fantlv.py | 210 + .../common/lib/eepromutil/fru.py | 957 +++++ .../common/lib/wbutil/__init__.py | 0 .../common/lib/wbutil/baseutil.py | 23 + .../common/lib/wbutil/logutil.py | 67 + .../common/lib/wbutil/smbus.py | 774 ++++ .../common/modules/Makefile | 28 + .../common/modules/dfd_tlveeprom.c | 521 +++ .../common/modules/dfd_tlveeprom.h | 121 + .../common/modules/i2c-mux-pca954x.c | 3817 +++++++++++++++++ .../common/modules/i2c-mux-pca9641.c | 643 +++ .../common/modules/optoe.c | 1195 ++++++ .../common/modules/platform.h | 155 + .../common/modules/platform_common_module.c | 210 + .../common/modules/rtcpcf85063.c | 258 ++ .../common/modules/wb_at24.c | 858 ++++ .../common/modules/wb_platform.c | 79 + .../common/script/device_i2c.py | 283 ++ .../common/script/fancontrol.py | 495 +++ .../common/script/interface.py | 77 + .../common/script/platform_common.py | 1313 ++++++ .../common/script/platform_config.py | 205 + .../common/script/platform_sensors.py | 92 + .../common/script/platform_util.py | 1942 +++++++++ .../common/script/sensors | 8 + .../common/service/device_i2c.service | 15 + .../common/service/fancontrol.service | 12 + .../common_custom/common_micas/Makefile | 24 + .../common_micas/lib/logutil/__init__.py | 0 .../common_micas/lib/logutil/logutil.py | 64 + .../lib/redfishutil/redfish_api.py | 304 ++ .../common_micas/lib/sonic_fwmgr/__init__.py | 0 .../lib/sonic_fwmgr/fwgmr_base.py | 141 + .../common_micas/lib/wbutil/__init__.py | 0 .../common_micas/lib/wbutil/platformutil.py | 290 ++ .../common_custom/common_micas/script/ctccmd | 3 + .../common_micas/script/privatenetwork.py | 50 + .../common_micas/script/privatenetwork.sh | 26 + .../script/update_machine_config.sh | 24 + .../service/privatenetwork.service | 13 + .../debian/changelog | 6 + .../debian/compat | 1 + .../debian/control | 14 + ...form-modules-micas-m2-w6010-48gt4x-fa.init | 61 + ...m-modules-micas-m2-w6010-48gt4x-fa.install | 2 + ...-modules-micas-m2-w6010-48gt4x-fa.postinst | 8 + ...form-modules-micas-m2-w6010-48gt4x-ra.init | 61 + ...m-modules-micas-m2-w6010-48gt4x-ra.install | 2 + ...-modules-micas-m2-w6010-48gt4x-ra.postinst | 8 + .../sonic-platform-modules-micas/debian/rules | 114 + ...rm64_micas_m2_w6010_48gt4x_fa_r0_config.py | 90 + .../config/fan_ctrl_cfg.json | 40 + .../m2-w6010-48gt4x-fa/modules/Makefile | 1 + .../m2-w6010-48gt4x-fa/modules/wb_cpld.c | 336 ++ .../service/m2-w6010-48gt4x_platform.service | 13 + .../m2-w6010-48gt4x-fa/setup.py | 15 + .../sonic_platform/__init__.py | 3 + .../sonic_platform/chassis.py | 444 ++ .../sonic_platform/component.py | 93 + .../sonic_platform/eeprom.py | 38 + .../m2-w6010-48gt4x-fa/sonic_platform/fan.py | 178 + .../sonic_platform/fan_drawer.py | 124 + .../sonic_platform/hwaccess.py | 46 + .../sonic_platform/platform.py | 15 + .../m2-w6010-48gt4x-fa/sonic_platform/psu.py | 231 + .../sonic_platform/redfish_api.py | 304 ++ .../m2-w6010-48gt4x-fa/sonic_platform/sfp.py | 325 ++ .../sonic_platform/thermal.py | 172 + ...rm64_micas_m2_w6010_48gt4x_ra_r0_config.py | 91 + .../config/fan_ctrl_cfg.json | 40 + .../m2-w6010-48gt4x-ra/modules/Makefile | 1 + .../m2-w6010-48gt4x-ra/modules/wb_cpld.c | 337 ++ .../service/m2-w6010-48gt4x_platform.service | 13 + .../m2-w6010-48gt4x-ra/setup.py | 15 + .../sonic_platform/__init__.py | 3 + .../sonic_platform/chassis.py | 444 ++ .../sonic_platform/component.py | 93 + .../sonic_platform/eeprom.py | 38 + .../m2-w6010-48gt4x-ra/sonic_platform/fan.py | 178 + .../sonic_platform/fan_drawer.py | 125 + .../sonic_platform/hwaccess.py | 46 + .../sonic_platform/platform.py | 15 + .../m2-w6010-48gt4x-ra/sonic_platform/psu.py | 231 + .../sonic_platform/redfish_api.py | 304 ++ .../m2-w6010-48gt4x-ra/sonic_platform/sfp.py | 325 ++ .../sonic_platform/thermal.py | 172 + platform/centec-arm64/sonic_fit.its | 132 +- .../centec-arm64/tsingma-bsp/debian/rules | 2 + .../tsingma-bsp/debian/tsingma-bsp.install | 1 + .../tsingma-bsp/src/ctc-phy/mars.c | 3 + .../tsingma-bsp/src/ctcmac/ctcmac.c | 7 +- .../tsingma-bsp/src/i2c-ctc/i2c-ctc.c | 5 + .../tsingma-bsp/src/m2-w6010-48gt4x/Makefile | 5 + .../tsingma-bsp/src/m2-w6010-48gt4x/arm-gic.h | 23 + .../src/m2-w6010-48gt4x/ctc5236-clks.h | 25 + .../src/m2-w6010-48gt4x/ctc5236-clock.dtsi | 63 + .../src/m2-w6010-48gt4x/ctc5236.dtsi | 428 ++ .../tsingma-bsp/src/m2-w6010-48gt4x/irq.h | 20 + .../m2-w6010-48gt4x/m2-w6010-48gt4x-r0.dts | 331 ++ src/sonic-config-engine/minigraph.py | 5 - 181 files changed, 35305 insertions(+), 236 deletions(-) create mode 100644 0001-update-swss-main-cpp.patch create mode 100644 0002-update-swss-orch-h.patch create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/M2-w6010-48gt4x-fa-board.json create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/M2-w6010-48gt4x-fa-p-chip-profile.txt create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/M2-w6010-48gt4x-fa-p-datapath.txt create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/default_sku create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/phy_mapping.cfg create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/platform_asic create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/port_config.ini create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/sai.profile create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/start_up.cfg create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/default_sku create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/installer.conf create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/platform.json create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/platform_asic create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/plugins/sfputil.py create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/plugins/ssd_util.py create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/pmon_daemon_control.json create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/system_health_monitoring_config.json create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/M2-w6010-48gt4x-ra-board.json create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/M2-w6010-48gt4x-ra-p-chip-profile.txt create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/M2-w6010-48gt4x-ra-p-datapath.txt create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/default_sku create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/phy_mapping.cfg create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/platform_asic create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/port_config.ini create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/sai.profile create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/start_up.cfg create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/default_sku create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/installer.conf create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/platform.json create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/platform_asic create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/plugins/sfputil.py create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/plugins/ssd_util.py create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/pmon_daemon_control.json create mode 100644 device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/system_health_monitoring_config.json create mode 100644 platform/centec-arm64/platform-modules-micas.dep create mode 100644 platform/centec-arm64/platform-modules-micas.mk create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/LICENSE create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/README.md create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/Makefile create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/Makefile create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/Makefile create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/Rules.mk create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware-cpld-ispvme.spec create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/Makefile create mode 100755 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/config_gpio.c create mode 100755 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/config_gpio.h create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/firmware_cpld_ispvme.c create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/firmware_cpld_upgrade_ispvme.c create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/firmware_ispvme.c create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/include/config_ispvme.h create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/include/firmware_cpld_ispvme.h create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/include/firmware_ispvme.h create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/Makefile create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/common.h create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/debug_ispvme.c create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/dfd_fpga_debug.c create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/dfd_fpga_debug.h create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/dfd_fpga_pkt.c create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/dfd_fpga_pkt.h create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/dfd_fpga_upg.c create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/firmware_app_ispvme.c create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/hardware.c create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/include/debug_ispvme.h create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/include/firmware_app_ispvme.h create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/ispvm_ui.c create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/ivm_core.c create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/vmopcode.h create mode 100755 platform/centec-arm64/sonic-platform-modules-micas/common/app/libextphy.so create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/lib/eepromutil/__init__.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/lib/eepromutil/fantlv.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/lib/eepromutil/fru.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/lib/wbutil/__init__.py create mode 100755 platform/centec-arm64/sonic-platform-modules-micas/common/lib/wbutil/baseutil.py create mode 100755 platform/centec-arm64/sonic-platform-modules-micas/common/lib/wbutil/logutil.py create mode 100755 platform/centec-arm64/sonic-platform-modules-micas/common/lib/wbutil/smbus.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/modules/Makefile create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/modules/dfd_tlveeprom.c create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/modules/dfd_tlveeprom.h create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/modules/i2c-mux-pca954x.c create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/modules/i2c-mux-pca9641.c create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/modules/optoe.c create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/modules/platform.h create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/modules/platform_common_module.c create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/modules/rtcpcf85063.c create mode 100755 platform/centec-arm64/sonic-platform-modules-micas/common/modules/wb_at24.c create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/modules/wb_platform.c create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/script/device_i2c.py create mode 100755 platform/centec-arm64/sonic-platform-modules-micas/common/script/fancontrol.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/script/interface.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/script/platform_common.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/script/platform_config.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/script/platform_sensors.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/script/platform_util.py create mode 100755 platform/centec-arm64/sonic-platform-modules-micas/common/script/sensors create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/service/device_i2c.service create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common/service/fancontrol.service create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/Makefile create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/logutil/__init__.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/logutil/logutil.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/redfishutil/redfish_api.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/sonic_fwmgr/__init__.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/sonic_fwmgr/fwgmr_base.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/wbutil/__init__.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/wbutil/platformutil.py create mode 100755 platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/script/ctccmd create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/script/privatenetwork.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/script/privatenetwork.sh create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/script/update_machine_config.sh create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/service/privatenetwork.service create mode 100755 platform/centec-arm64/sonic-platform-modules-micas/debian/changelog create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/debian/compat create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/debian/control create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-fa.init create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-fa.install create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-fa.postinst create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-ra.init create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-ra.install create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-ra.postinst create mode 100755 platform/centec-arm64/sonic-platform-modules-micas/debian/rules create mode 100755 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/config/arm64_micas_m2_w6010_48gt4x_fa_r0_config.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/config/fan_ctrl_cfg.json create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/modules/Makefile create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/modules/wb_cpld.c create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/service/m2-w6010-48gt4x_platform.service create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/setup.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/__init__.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/chassis.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/component.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/eeprom.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/fan.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/fan_drawer.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/hwaccess.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/platform.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/psu.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/redfish_api.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/sfp.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/thermal.py create mode 100755 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/config/arm64_micas_m2_w6010_48gt4x_ra_r0_config.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/config/fan_ctrl_cfg.json create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/modules/Makefile create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/modules/wb_cpld.c create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/service/m2-w6010-48gt4x_platform.service create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/setup.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/__init__.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/chassis.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/component.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/eeprom.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/fan.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/fan_drawer.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/hwaccess.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/platform.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/psu.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/redfish_api.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/sfp.py create mode 100644 platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/thermal.py create mode 100755 platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/Makefile create mode 100755 platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/arm-gic.h create mode 100755 platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/ctc5236-clks.h create mode 100755 platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/ctc5236-clock.dtsi create mode 100755 platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/ctc5236.dtsi create mode 100755 platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/irq.h create mode 100755 platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/m2-w6010-48gt4x-r0.dts diff --git a/.azure-pipelines/azure-pipelines-build.yml b/.azure-pipelines/azure-pipelines-build.yml index 2a2f6797af63..930b7af08173 100644 --- a/.azure-pipelines/azure-pipelines-build.yml +++ b/.azure-pipelines/azure-pipelines-build.yml @@ -6,7 +6,7 @@ parameters: default: '' - name: 'buildOptions' type: string - default: 'SONIC_CONFIG_BUILD_JOBS=1' + default: 'SONIC_CONFIG_BUILD_JOBS=4' - name: 'preSteps' type: stepList default: [] @@ -111,6 +111,8 @@ jobs: - template: .azure-pipelines/template-skipvstest.yml@buildimage - template: .azure-pipelines/template-daemon.yml@buildimage - bash: | + patch -p1 < 0001-update-swss-main-cpp.patch + patch -p1 < 0002-update-swss-orch-h.patch set -ex if [ $(GROUP_NAME) == vs ]; then if [ $(dbg_image) == yes ]; then diff --git a/.azure-pipelines/azure-pipelines-image-template.yml b/.azure-pipelines/azure-pipelines-image-template.yml index 1311ab0608c9..909bb63a5b70 100644 --- a/.azure-pipelines/azure-pipelines-image-template.yml +++ b/.azure-pipelines/azure-pipelines-image-template.yml @@ -52,7 +52,7 @@ jobs: postSteps: - script: | mkdir -p $(Build.ArtifactStagingDirectory)/target - mv target/* $(Build.ArtifactStagingDirectory)/target/ + mv target/*.bin $(Build.ArtifactStagingDirectory)/target/ displayName: Copy Artifacts condition: always() - publish: $(Build.ArtifactStagingDirectory) diff --git a/.gitignore b/.gitignore index 60c328d62289..090389701ba4 100644 --- a/.gitignore +++ b/.gitignore @@ -62,7 +62,7 @@ src/**/*.o platform/**/*.egg-info platform/**/*-none-any.whl platform/**/.pybuild -platform/**/debian/* +#platform/**/debian/* platform/**/build platform/**/*.ko platform/**/*.mod.c diff --git a/0001-update-swss-main-cpp.patch b/0001-update-swss-main-cpp.patch new file mode 100644 index 000000000000..97c2776f33a3 --- /dev/null +++ b/0001-update-swss-main-cpp.patch @@ -0,0 +1,42 @@ +--- sonic-buildimage-one/src/sonic-swss/orchagent/main.cpp 2023-06-07 15:33:45.516035105 +0800 ++++ sonic-buildimage-two/src/sonic-swss/orchagent/main.cpp 2023-06-07 15:37:23.330336171 +0800 +@@ -569,7 +569,9 @@ + attr.value.u64 = gSwitchId; + attrs.push_back(attr); + +- if (gMySwitchType == "voq" || gMySwitchType == "fabric" || gMySwitchType == "chassis-packet") ++ /* centec create switch take longtime */ ++ char *platform = getenv("platform"); ++ if (gMySwitchType == "voq" || gMySwitchType == "fabric" || gMySwitchType == "chassis-packet" || (platform && strstr(platform, CENTEC_PLATFORM_SUBSTRING))) + { + /* We set this long timeout in order for orchagent to wait enough time for + * response from syncd. It is needed since switch create takes more time +@@ -585,6 +587,10 @@ + { + attr.value.u64 = (10 * SAI_REDIS_DEFAULT_SYNC_OPERATION_RESPONSE_TIMEOUT); + } ++ else if (platform && strstr(platform, CENTEC_PLATFORM_SUBSTRING)) ++ { ++ attr.value.u64 = (10 * SAI_REDIS_DEFAULT_SYNC_OPERATION_RESPONSE_TIMEOUT); ++ } + + attr.id = SAI_REDIS_SWITCH_ATTR_SYNC_OPERATION_RESPONSE_TIMEOUT; + status = sai_switch_api->set_switch_attribute(gSwitchId, &attr); +@@ -607,11 +613,16 @@ + } + SWSS_LOG_NOTICE("Create a switch, id:%" PRIu64, gSwitchId); + +- if (gMySwitchType == "voq" || gMySwitchType == "fabric" || gMySwitchType == "chassis-packet") ++ if (gMySwitchType == "voq" || gMySwitchType == "fabric" || gMySwitchType == "chassis-packet" || (platform && strstr(platform, CENTEC_PLATFORM_SUBSTRING))) + { + /* Set syncd response timeout back to the default value */ + attr.id = SAI_REDIS_SWITCH_ATTR_SYNC_OPERATION_RESPONSE_TIMEOUT; + attr.value.u64 = SAI_REDIS_DEFAULT_SYNC_OPERATION_RESPONSE_TIMEOUT; ++ /* centec platform set 3X default timeout*/ ++ if (platform && strstr(platform, CENTEC_PLATFORM_SUBSTRING)) ++ { ++ attr.value.u64 = (3 * SAI_REDIS_DEFAULT_SYNC_OPERATION_RESPONSE_TIMEOUT); ++ } + status = sai_switch_api->set_switch_attribute(gSwitchId, &attr); + + if (status != SAI_STATUS_SUCCESS) diff --git a/0002-update-swss-orch-h.patch b/0002-update-swss-orch-h.patch new file mode 100644 index 000000000000..44b724716181 --- /dev/null +++ b/0002-update-swss-orch-h.patch @@ -0,0 +1,10 @@ +--- sonic-buildimage-one/src/sonic-swss/orchagent/orch.h 2023-06-07 15:33:45.528034877 +0800 ++++ sonic-buildimage-two/src/sonic-swss/orchagent/orch.h 2023-06-07 15:37:55.085516362 +0800 +@@ -40,6 +40,7 @@ + #define NPS_PLATFORM_SUBSTRING "nephos" + #define MRVL_PLATFORM_SUBSTRING "marvell" + #define CISCO_8000_PLATFORM_SUBSTRING "cisco-8000" ++#define CENTEC_PLATFORM_SUBSTRING "centec" + + #define CONFIGDB_KEY_SEPARATOR "|" + #define DEFAULT_KEY_SEPARATOR ":" diff --git a/Makefile.work b/Makefile.work index 462f2736aa38..890aab86610e 100644 --- a/Makefile.work +++ b/Makefile.work @@ -332,6 +332,9 @@ DOCKER_BASE_BUILD = docker build --no-cache \ DOCKER_BASE_PULL = docker pull \ $(REGISTRY_SERVER):$(REGISTRY_PORT)$(REGISTRY_SERVER_PATH)/$(SLAVE_BASE_IMAGE):$(SLAVE_BASE_TAG) +DOCKER_BASE_PUSH = docker push \ + $(REGISTRY_SERVER):$(REGISTRY_PORT)/$(SLAVE_BASE_IMAGE):$(SLAVE_BASE_TAG) + DOCKER_BUILD = docker build --no-cache \ --build-arg user=$(USER) \ --build-arg uid=$(shell id -u) \ @@ -435,7 +438,9 @@ endif scripts/collect_docker_version_files.sh $(SLAVE_BASE_IMAGE):$(SLAVE_BASE_TAG) target ; } } || \ { echo Image $(SLAVE_BASE_IMAGE):$(SLAVE_BASE_TAG) not found. Building... ; \ $(DOCKER_BASE_BUILD) ; \ - scripts/collect_docker_version_files.sh $(SLAVE_BASE_IMAGE):$(SLAVE_BASE_TAG) target ; } + scripts/collect_docker_version_files.sh $(SLAVE_BASE_IMAGE):$(SLAVE_BASE_TAG) target ; \ + docker tag $(SLAVE_BASE_IMAGE):$(SLAVE_BASE_TAG) $(REGISTRY_SERVER):$(REGISTRY_PORT)/$(SLAVE_BASE_IMAGE):$(SLAVE_BASE_TAG) ; \ + $(DOCKER_BASE_PUSH) ; } @docker inspect --type image $(SLAVE_IMAGE):$(SLAVE_TAG) &> /dev/null || \ { echo Image $(SLAVE_IMAGE):$(SLAVE_TAG) not found. Building... ; \ $(DOCKER_BUILD) ; } @@ -470,7 +475,9 @@ endif scripts/collect_docker_version_files.sh $(SLAVE_BASE_IMAGE):$(SLAVE_BASE_TAG) target ; } } || \ { echo Image $(SLAVE_BASE_IMAGE):$(SLAVE_BASE_TAG) not found. Building... ; \ $(DOCKER_BASE_BUILD) ; \ - scripts/collect_docker_version_files.sh $(SLAVE_BASE_IMAGE):$(SLAVE_BASE_TAG) target ; } + scripts/collect_docker_version_files.sh $(SLAVE_BASE_IMAGE):$(SLAVE_BASE_TAG) target ; \ + docker tag $(SLAVE_BASE_IMAGE):$(SLAVE_BASE_TAG) $(REGISTRY_SERVER):$(REGISTRY_PORT)/$(SLAVE_BASE_IMAGE):$(SLAVE_BASE_TAG) ; \ + $(DOCKER_BASE_PUSH) ; } sonic-slave-build : sonic-slave-base-build @echo Checking sonic-slave image: $(SLAVE_IMAGE):$(SLAVE_TAG) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a5353ffb2001..cad5bb9febe3 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -6,7 +6,6 @@ trigger: branches: include: - - master - 202205 paths: exclude: @@ -15,9 +14,7 @@ trigger: pr: branches: include: - - master - 202205 - - bullseye paths: exclude: - .github @@ -50,7 +47,7 @@ stages: jobs: - template: .azure-pipelines/azure-pipelines-build.yml parameters: - buildOptions: 'USERNAME=admin SONIC_BUILD_JOBS=$(nproc) BUILD_MULTIASIC_KVM=y ${{ variables.VERSION_CONTROL_OPTIONS }}' + buildOptions: 'USERNAME=admin SONIC_BUILD_JOBS=$(nproc) BUILD_MULTIASIC_KVM=y' jobGroups: - name: vs @@ -60,168 +57,10 @@ stages: jobs: - template: .azure-pipelines/azure-pipelines-build.yml parameters: - buildOptions: 'USERNAME=admin SONIC_BUILD_JOBS=$(nproc) ${{ variables.VERSION_CONTROL_OPTIONS }}' + buildOptions: 'USERNAME=admin ENABLE_DOCKER_BASE_PULL=y SONIC_VERSION_CONTROL_COMPONENTS=deb,py2,py3,web,git,docker SONIC_DPKG_CACHE_METHOD=rcache SONIC_DPKG_CACHE_SOURCE=/nfs/dpkg_cache/centec-arm64 SONIC_BUILD_JOBS=$(nproc)' jobGroups: - - name: broadcom - variables: - dbg_image: yes - swi_image: yes - docker_syncd_rpc_image: yes - platform_rpc: brcm - - name: mellanox - variables: - dbg_image: yes - docker_syncd_rpc_image: yes - platform_rpc: mlnx - - name: marvell-armhf - pool: sonicbld-armhf + - name: centec-arm64 + pool: sonicbld-arm64 timeoutInMinutes: 1200 variables: - PLATFORM_ARCH: armhf - -- stage: Test - dependsOn: BuildVS - condition: and(succeeded(), and(ne(stageDependencies.BuildVS.outputs['vs.SetVar.SKIP_VSTEST'], 'YES'), in(dependencies.BuildVS.result, 'Succeeded', 'SucceededWithIssues'))) - variables: - - group: SONiC-Elastictest - - name: inventory - value: veos_vtb - - name: testbed_file - value: vtestbed.csv - - jobs: - - job: - pool: sonictest - displayName: "vstest" - timeoutInMinutes: 60 - steps: - - checkout: self - clean: true - submodules: recursive - displayName: 'Checkout code' - - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: build - pipeline: 9 - artifact: sonic-swss-common.amd64.ubuntu20_04 - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/master' - displayName: "Download sonic swss common deb packages" - - - task: DownloadPipelineArtifact@2 - inputs: - artifact: sonic-buildimage.vs - displayName: "Download sonic-buildimage.vs artifact" - - - script: | - set -x - sudo apt-get update - sudo apt-get install libyang0.16 -y - sudo dpkg -i --force-confask,confnew ../libswsscommon_1.0.0_amd64.deb - sudo dpkg -i ../python3-swsscommon_1.0.0_amd64.deb - sudo docker load -i ../target/docker-sonic-vs.gz - docker tag docker-sonic-vs:latest docker-sonic-vs:$(Build.BuildNumber) - username=$(id -un) - - trap "docker ps; docker images; ip netns list; \ - docker rmi docker-sonic-vs:$(Build.BuildNumber); \ - ip netns list | grep -E [-]srv[0-9]+ | awk '{print $1}' | xargs -I {} sudo ip netns delete {}; \ - sudo chown -R ${username}.${username} .; \ - sudo chown -R ${username}.${username} $(System.DefaultWorkingDirectory)" EXIT - pushd platform/vs/tests - sudo py.test -v --junitxml=tr.xml --imgname=docker-sonic-vs:$(Build.BuildNumber) - displayName: "Run vs tests" - - - task: PublishTestResults@2 - inputs: - testResultsFiles: '**/tr.xml' - testRunTitle: vstest - - - job: t0_elastictest - pool: ubuntu-20.04 - displayName: "kvmtest-t0 by Elastictest" - timeoutInMinutes: 240 - continueOnError: false - steps: - - template: .azure-pipelines/run-test-scheduler-template.yml - parameters: - TOPOLOGY: t0 - MIN_WORKER: $(T0_INSTANCE_NUM) - MAX_WORKER: $(T0_INSTANCE_NUM) - MGMT_BRANCH: 202205 - - - job: t0_2vlans_elastictest - pool: ubuntu-20.04 - displayName: "kvmtest-t0-2vlans by Elastictest" - timeoutInMinutes: 240 - continueOnError: false - steps: - - template: .azure-pipelines/run-test-scheduler-template.yml - parameters: - TOPOLOGY: t0 - TEST_SET: t0-2vlans - MIN_WORKER: $(T0_2VLANS_INSTANCE_NUM) - MAX_WORKER: $(T0_2VLANS_INSTANCE_NUM) - DEPLOY_MG_EXTRA_PARAMS: "-e vlan_config=two_vlan_a" - MGMT_BRANCH: 202205 - - - job: t1_lag_elastictest - pool: ubuntu-20.04 - displayName: "kvmtest-t1-lag by Elastictest" - timeoutInMinutes: 240 - continueOnError: false - steps: - - template: .azure-pipelines/run-test-scheduler-template.yml - parameters: - TOPOLOGY: t1-lag - MIN_WORKER: $(T1_LAG_INSTANCE_NUM) - MAX_WORKER: $(T1_LAG_INSTANCE_NUM) - MGMT_BRANCH: 202205 - - - job: sonic_t0_elastictest - pool: ubuntu-20.04 - displayName: "kvmtest-t0-sonic by Elastictest" - timeoutInMinutes: 240 - continueOnError: false - steps: - - template: .azure-pipelines/run-test-scheduler-template.yml - parameters: - TOPOLOGY: t0-64-32 - MIN_WORKER: $(T0_SONIC_INSTANCE_NUM) - MAX_WORKER: $(T0_SONIC_INSTANCE_NUM) - TEST_SET: t0-sonic - COMMON_EXTRA_PARAMS: "--neighbor_type=sonic --enable_macsec --macsec_profile=128_SCI,256_XPN_SCI" - VM_TYPE: vsonic - SPECIFIED_PARAMS: '{\"test_pretest.py\":[\"--completeness_level=confident\",\"--allow_recover\"],\"test_posttest.py\":[\"--completeness_level=confident\",\"--allow_recover\"]}' - MGMT_BRANCH: 202205 - - - job: dualtor_elastictest - pool: ubuntu-20.04 - displayName: "kvmtest-dualtor-t0 by Elastictest" - timeoutInMinutes: 240 - continueOnError: false - steps: - - template: .azure-pipelines/run-test-scheduler-template.yml - parameters: - TOPOLOGY: dualtor - MIN_WORKER: $(T0_DUALTOR_INSTANCE_NUM) - MAX_WORKER: $(T0_DUALTOR_INSTANCE_NUM) - COMMON_EXTRA_PARAMS: "--disable_loganalyzer " - MGMT_BRANCH: 202205 - - - job: multi_asic_elastictest - pool: ubuntu-20.04 - displayName: "kvmtest-multi-asic-t1-lag by Elastictest" - timeoutInMinutes: 1080 - continueOnError: false - steps: - - template: .azure-pipelines/run-test-scheduler-template.yml - parameters: - TOPOLOGY: t1-8-lag - TEST_SET: multi-asic-t1-lag - MIN_WORKER: $(MULTI_ASIC_INSTANCE_NUM) - MAX_WORKER: $(MULTI_ASIC_INSTANCE_NUM) - NUM_ASIC: 4 - MGMT_BRANCH: 202205 + PLATFORM_ARCH: arm64 diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/M2-w6010-48gt4x-fa-board.json b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/M2-w6010-48gt4x-fa-board.json new file mode 100644 index 000000000000..80fb22238078 --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/M2-w6010-48gt4x-fa-board.json @@ -0,0 +1,868 @@ +{ + "macleds" : { + "polarity" : 1, + "freq" : 2500, + "interval" : 50000000, + "maps" : [ + { + "port_id" : 0, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port1/brightness" + }, + { + "port_id" : 1, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port2/brightness" + }, + { + "port_id" : 2, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port3/brightness" + }, + { + "port_id" : 3, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port4/brightness" + }, + { + "port_id" : 4, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port5/brightness" + }, + { + "port_id" : 5, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port6/brightness" + }, + { + "port_id" : 6, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port7/brightness" + }, + { + "port_id" : 7, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port8/brightness" + }, + { + "port_id" : 16, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port9/brightness" + }, + { + "port_id" : 17, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port10/brightness" + }, + { + "port_id" : 18, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port11/brightness" + }, + { + "port_id" : 19, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port12/brightness" + }, + { + "port_id" : 20, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port13/brightness" + }, + { + "port_id" : 21, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port14/brightness" + }, + { + "port_id" : 22, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port15/brightness" + }, + { + "port_id" : 23, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port16/brightness" + }, + { + "port_id" : 8, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port17/brightness" + }, + { + "port_id" : 9, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port18/brightness" + }, + { + "port_id" : 10, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port19/brightness" + }, + { + "port_id" : 11, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port20/brightness" + }, + { + "port_id" : 32, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port21/brightness" + }, + { + "port_id" : 33, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port22/brightness" + }, + { + "port_id" : 34, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port23/brightness" + }, + { + "port_id" : 35, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port24/brightness" + }, + { + "port_id" : 36, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port25/brightness" + }, + { + "port_id" : 37, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port26/brightness" + }, + { + "port_id" : 38, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port27/brightness" + }, + { + "port_id" : 39, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port28/brightness" + }, + { + "port_id" : 40, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port29/brightness" + }, + { + "port_id" : 41, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port30/brightness" + }, + { + "port_id" : 42, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port31/brightness" + }, + { + "port_id" : 43, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port32/brightness" + }, + { + "port_id" : 24, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port33/brightness" + }, + { + "port_id" : 25, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port34/brightness" + }, + { + "port_id" : 26, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port35/brightness" + }, + { + "port_id" : 27, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port36/brightness" + }, + { + "port_id" : 48, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port37/brightness" + }, + { + "port_id" : 49, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port38/brightness" + }, + { + "port_id" : 50, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port39/brightness" + }, + { + "port_id" : 51, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port40/brightness" + }, + { + "port_id" : 52, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port41/brightness" + }, + { + "port_id" : 53, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port42/brightness" + }, + { + "port_id" : 54, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port43/brightness" + }, + { + "port_id" : 55, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port44/brightness" + }, + { + "port_id" : 56, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port45/brightness" + }, + { + "port_id" : 57, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port46/brightness" + }, + { + "port_id" : 58, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port47/brightness" + }, + { + "port_id" : 59, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port48/brightness" + }, + { + "port_id" : 12, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port49/brightness" + }, + { + "port_id" : 13, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port50/brightness" + }, + { + "port_id" : 14, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port51/brightness" + }, + { + "port_id" : 15, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port52/brightness" + } + ] + }, + "phys" : [ + { + "macid" : 0, + "busid" : 0, + "addr" : 0, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 1, + "busid" : 0, + "addr" : 1, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 2, + "busid" : 0, + "addr" : 2, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 3, + "busid" : 0, + "addr" : 3, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 4, + "busid" : 0, + "addr" : 4, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 5, + "busid" : 0, + "addr" : 5, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 6, + "busid" : 0, + "addr" : 6, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 7, + "busid" : 0, + "addr" : 7, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 16, + "busid" : 0, + "addr" : 8, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 17, + "busid" : 0, + "addr" : 9, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 18, + "busid" : 0, + "addr" : 10, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 19, + "busid" : 0, + "addr" : 11, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 20, + "busid" : 0, + "addr" : 12, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 21, + "busid" : 0, + "addr" : 13, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 22, + "busid" : 0, + "addr" : 14, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 23, + "busid" : 0, + "addr" : 15, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 8, + "busid" : 0, + "addr" : 16, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 9, + "busid" : 0, + "addr" : 17, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 10, + "busid" : 0, + "addr" : 18, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 11, + "busid" : 0, + "addr" : 19, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 32, + "busid" : 0, + "addr" : 20, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 33, + "busid" : 0, + "addr" : 21, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 34, + "busid" : 0, + "addr" : 22, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 35, + "busid" : 0, + "addr" : 23, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 36, + "busid" : 1, + "addr" : 0, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 37, + "busid" : 1, + "addr" : 1, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 38, + "busid" : 1, + "addr" : 2, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 39, + "busid" : 1, + "addr" : 3, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 40, + "busid" : 1, + "addr" : 4, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 41, + "busid" : 1, + "addr" : 5, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 42, + "busid" : 1, + "addr" : 6, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 43, + "busid" : 1, + "addr" : 7, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 24, + "busid" : 1, + "addr" : 8, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 25, + "busid" : 1, + "addr" : 9, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 26, + "busid" : 1, + "addr" : 10, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 27, + "busid" : 1, + "addr" : 11, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 48, + "busid" : 1, + "addr" : 12, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 49, + "busid" : 1, + "addr" : 13, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 50, + "busid" : 1, + "addr" : 14, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 51, + "busid" : 1, + "addr" : 15, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 52, + "busid" : 1, + "addr" : 16, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 53, + "busid" : 1, + "addr" : 17, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 54, + "busid" : 1, + "addr" : 18, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 55, + "busid" : 1, + "addr" : 19, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 56, + "busid" : 1, + "addr" : 20, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 57, + "busid" : 1, + "addr" : 21, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 58, + "busid" : 1, + "addr" : 22, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 59, + "busid" : 1, + "addr" : 23, + "base_port": 1, + "last_port": 4 + } + ], + "ffe" : { + "board_material" : "BOARD_MATERIAL_M4", + "config" : [ + { + "serdes_id" : [0], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [0, 255, 10, 0] + }, + { + "serdes_id" : [1], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [0, 240, 10, 0] + }, + { + "serdes_id" : [2], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [0, 230, 10, 0] + }, + { + "serdes_id" : [3], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [0, 220, 7, 0] + }, + { + "serdes_id" : [4], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [0, 220, 8, 0] + }, + { + "serdes_id" : [5], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [0, 210, 6, 0] + }, + { + "serdes_id" : [6], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [0, 180, 6, 0] + }, + { + "serdes_id" : [7], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [0, 180, 5, 0] + }, + { + "serdes_id" : [8], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [2, 160, 4, 0] + }, + { + "serdes_id" : [9], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [2, 160, 4, 0] + }, + { + "serdes_id" : [10], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [3, 140, 9, 0] + }, + { + "serdes_id" : [11], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [3, 140, 5, 0] + }, + { + "serdes_id" : [12, 13, 14, 15], + "is_dac" : 0, + "speed" : [10000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [3, 94, 15, 0] + } + ] + }, + "ctle" : { + "config" : [ + { + "serdes_id" : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], + "auto-en" : 0, + "cfg" : [6, 14, 2] + } + ] + } +} diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/M2-w6010-48gt4x-fa-p-chip-profile.txt b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/M2-w6010-48gt4x-fa-p-chip-profile.txt new file mode 100644 index 000000000000..2373e27f5f8b --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/M2-w6010-48gt4x-fa-p-chip-profile.txt @@ -0,0 +1,96 @@ +#----------------- SDK Feature Support -------------- +[MPLS_SUPPORT] = 1; +[APS_SUPPORT] = 1; +[OAM_SUPPORT] = 1; +[PTP_SUPPORT] = 0; +[SYNCE_SUPPORT] = 0; +[STACKING_SUPPORT] = 1; +[BPE_SUPPORT] = 0; +[IPFIX_SUPPORT] = 1; +[MONITOR_SUPPORT] = 1; +[OVERLAY_SUPPORT] = 1; +[EFD_SUPPORT] = 1; +[FCOE_SUPPORT] = 0; +[TRILL_SUPPORT] = 0; +[WLAN_SUPPORT] = 1; +[NPM_SUPPORT] = 1; +[DOT1AE_SUPPORT] = 1; + +#----------------- Chip Init Parameter -------------- +#Local chip number and global chip id +[Local chip_num] = 1 +[Local chip0] = 0 +[Local chip1] = 1 + +#Cut through mode 0: Disable; 1:10/40/100G; 2:1/10/100G; 3:1/10/40G; other:Flex, refer to CUT_THROUGH_BITMAP +[CUT_THROUGH_SPEED] = 0 +#Flex cut through mode, speed enable by bitmap, refer to ctc_port_speed_t, Notice: 10M/100M/1G treat as the same speed +[CUT_THROUGH_BITMAP] = 0 + +#Network cpu port +[CPU_NETWORK_PORT_EN] = 0 +[CPU_NETWORK_PORT_ID] = 47 + +#Enable parity error and multi-bit ecc recover +[ECC_RECOVER_EN] = 0 +[TCAM_SCAN_EN] = 0 + +#----------------- Interrupt Init Parameter -------------- +#0: pin, 1: msi +[Interrupt_mode] = 0 +[IRQ] = 69 + +#----------------- NextHop Init Parameter -------------- +#0: SDK work in pizzbox (single chip system), 1: SDK work in multi-chip system +[Nexthop Edit Mode] = 0 +[External Nexthop Number] = 16384 +[MPLS Tunnel Number] = 1024 + +#----------------- L2 Init Parameter -------------- +[FDB Hw Learning] = 1 +[Logic Port Num] = 1024 +#0: 128 instance per port, 1: 64 instance per port, 2: 32 instance per port +[STP MODE] = 0 +[MAX_FID_NUM] = 5120 + +#----------------- Stats Init Parameter -------------- +[STATS_PORT_EN] = 0 +[STATS_ECMP_EN] = 0 + +#----------------- BPE Init Parameter -------------- +[BPE_BR_PORT_EXTENDER_EN] = 0 +[BPE_BR_UC_MAX_ECID] = 1024 +[BPE_BR_MC_MAX_ECID] = 4096 +[BPE_BR_PORT_BASE] = 0 + +#----------------- Ipuc Init Parameter -------------- +#0: tcam use prefix 16; 1: tcam use prefix 8 +[IPUC_TCAM_PREFIX_8] = 1 + +#----------------- QoS Init Parameter -------------- +#QoS policer number support 1K/2K/4K/8K, default 4K +[QOS_POLICER_NUM] = 4096 +#QoS port queue number support 16/8/8 BPE/4 BPE, +#When resrc_profile.cfg exist, queue number valid, +#Default 8 queue mode +#8 queue = 8 +#16 queue = 16 +#4 queue BPE = 17 +#8 queue BPE = 18 +[QOS_PORT_QUEUE_NUM] = 8 +#QoS port extend queue number support 0/4, default 0 +[QOS_PORT_EXT_QUEUE_NUM] = 0 +#QoS CPU reason queue number support 128/64/32, default 128 +[QOS_CPU_QUEUE_NUM] = 128 +[QOS_INGRESS_VLAN_POLICER_NUM] = 0 +[QOS_EGRESS_VLAN_POLICER_NUM] = 0 +[QOS_POLICER_MERGE_MODE] = 0 +#QOS service queue mode, default 0,0:logic scr port + dstport enq 1:service id + dstport enq +[QOS_SERVICE_QUEUE_MODE] = 0 +#Global enable logic dst port + dstport enq +[QOS_SERVICE_QUEUE_EGRESS_EN] = 0 + +#----------------- Stacking Init Parameter -------------- +#0: normal mode; 1: spine-leaf mode +[FABRIC MODE] = 0 +[STACKING VERSION] = 1 diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/M2-w6010-48gt4x-fa-p-datapath.txt b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/M2-w6010-48gt4x-fa-p-datapath.txt new file mode 100644 index 000000000000..959b97a150cc --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/M2-w6010-48gt4x-fa-p-datapath.txt @@ -0,0 +1,281 @@ +#Generated by 'CTC DataPath Tools' on Fri Dec 18 19:15:31 2020 +#Version 1.0, Supported by TsingMa SDK + + +#SERDES_MODE : 0-NONE, 1-XFI, 2-SGMII, 3-Not Support, 4-QSGMII, 5-XAUI, 6-DXAUI, 7-XLG, 8-CG, 9-SGMII2G5 +# 10-USXGMII-S, 11-USXGMII-M2G5, 12-USXGMII-M5G, 13-XXVG, 14-LG, 15-100BASE-FX +#SERDES_RX_POLY: 0-Normal, 1-Inverse +#SERDES_SWITCH : 0-Not Support Dynamic Switch, 1-Support Dynamic Switch + +[WLAN_ENABLE] = 1 +[DOT1AE_ENABLE] = 1 + +[CORE_PLLA] = 600 +#{ +[SERDES_ITEM] +#repeat 32 step 6 +[SERDES_ID] = 0 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 1 +[SERDES_TX_POLY] = 1 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 1 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 2 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 1 +[SERDES_TX_POLY] = 1 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 3 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 4 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 5 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 6 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 7 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 1 +[SERDES_TX_POLY] = 1 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 8 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 1 +[SERDES_TX_POLY] = 1 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 9 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 10 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 11 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 12 +[SERDES_MODE] = 1 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 1 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 13 +[SERDES_MODE] = 1 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 1 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 14 +[SERDES_MODE] = 1 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 1 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 15 +[SERDES_MODE] = 1 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 1 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 16 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 17 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 18 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 19 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 20 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 21 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 22 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 23 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 24 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 25 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 26 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 1 + +[SERDES_ID] = 27 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 1 + +[SERDES_ID] = 28 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 29 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 30 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 1 + +[SERDES_ID] = 31 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 1 + +#repeat end +#} + + +#[SERDES_TO_LPORT] +#{ +# | QSGMII/USXGMII-M2G5 | USXGMII-M5G | USXGMII-S | 100BASE-FX | SGMII/SGMII2G5/XFI | XAUI/DXAUI | XLG | XXVG | LG | CG +#---------|---------------------|-------------|-----------|------------|--------------------|------------|-----|------|----|--- +#serdes 0 |0 /1 /2 /3 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 1 |4 /5 /6 /7 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 2 |16/17/18/19 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 3 |20/21/22/23 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 4 |8 /9 /10/11 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 5 |32/33/34/35 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 6 |36/37/38/39 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 7 |40/41/42/43 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 8 |24/25/26/27 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 9 |48/49/50/51 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 10|52/53/54/55 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 11|56/57/58/59 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 12|NA |NA |NA |12 |12 |12 |12 |NA |NA |NA +#serdes 13|NA |NA |NA |13 |13 |12 |12 |NA |NA |NA +#serdes 14|NA |NA |NA |14 |14 |12 |12 |NA |NA |NA +#serdes 15|NA |NA |NA |15 |15 |12 |12 |NA |NA |NA +#serdes 16|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 17|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 18|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 19|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 20|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 21|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 22|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 23|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 24|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 25|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 26|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 27|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 28|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 29|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 30|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 31|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#} diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/default_sku b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/default_sku new file mode 100644 index 000000000000..c29b09ab9378 --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/default_sku @@ -0,0 +1 @@ +M2-W6010-48GT4X-FA l1 diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/phy_mapping.cfg b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/phy_mapping.cfg new file mode 100644 index 000000000000..eefd67e9c28b --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/phy_mapping.cfg @@ -0,0 +1,198 @@ +#######################PHY_MAPPING_48+4####################### + +#{ +[PHY_MAPPING_ITEM] + +[API_PORT] = 0 +[PHY_ADDR] = 0 +[MDIO_BUS] = 0 + +[API_PORT] = 1 +[PHY_ADDR] = 1 +[MDIO_BUS] = 0 + +[API_PORT] = 2 +[PHY_ADDR] = 2 +[MDIO_BUS] = 0 + +[API_PORT] = 3 +[PHY_ADDR] = 3 +[MDIO_BUS] = 0 + +[API_PORT] = 4 +[PHY_ADDR] = 4 +[MDIO_BUS] = 0 + +[API_PORT] = 5 +[PHY_ADDR] = 5 +[MDIO_BUS] = 0 + +[API_PORT] = 6 +[PHY_ADDR] = 6 +[MDIO_BUS] = 0 + +[API_PORT] = 7 +[PHY_ADDR] = 7 +[MDIO_BUS] = 0 + +[API_PORT] = 16 +[PHY_ADDR] = 8 +[MDIO_BUS] = 0 + +[API_PORT] = 17 +[PHY_ADDR] = 9 +[MDIO_BUS] = 0 + +[API_PORT] = 18 +[PHY_ADDR] = 10 +[MDIO_BUS] = 0 + +[API_PORT] = 19 +[PHY_ADDR] = 11 +[MDIO_BUS] = 0 + +[API_PORT] = 20 +[PHY_ADDR] = 12 +[MDIO_BUS] = 0 + +[API_PORT] = 21 +[PHY_ADDR] = 13 +[MDIO_BUS] = 0 + +[API_PORT] = 22 +[PHY_ADDR] = 14 +[MDIO_BUS] = 0 + +[API_PORT] = 23 +[PHY_ADDR] = 15 +[MDIO_BUS] = 0 + +[API_PORT] = 8 +[PHY_ADDR] = 16 +[MDIO_BUS] = 0 + +[API_PORT] = 9 +[PHY_ADDR] = 17 +[MDIO_BUS] = 0 + +[API_PORT] = 10 +[PHY_ADDR] = 18 +[MDIO_BUS] = 0 + +[API_PORT] = 11 +[PHY_ADDR] = 19 +[MDIO_BUS] = 0 + +[API_PORT] = 32 +[PHY_ADDR] = 20 +[MDIO_BUS] = 0 + +[API_PORT] = 33 +[PHY_ADDR] = 21 +[MDIO_BUS] = 0 + +[API_PORT] = 34 +[PHY_ADDR] = 22 +[MDIO_BUS] = 0 + +[API_PORT] = 35 +[PHY_ADDR] = 23 +[MDIO_BUS] = 0 + +[API_PORT] = 36 +[PHY_ADDR] = 0 +[MDIO_BUS] = 1 + +[API_PORT] = 37 +[PHY_ADDR] = 1 +[MDIO_BUS] = 1 + +[API_PORT] = 38 +[PHY_ADDR] = 2 +[MDIO_BUS] = 1 + +[API_PORT] = 39 +[PHY_ADDR] = 3 +[MDIO_BUS] = 1 + +[API_PORT] = 40 +[PHY_ADDR] = 4 +[MDIO_BUS] = 1 + +[API_PORT] = 41 +[PHY_ADDR] = 5 +[MDIO_BUS] = 1 + +[API_PORT] = 42 +[PHY_ADDR] = 6 +[MDIO_BUS] = 1 + +[API_PORT] = 43 +[PHY_ADDR] = 7 +[MDIO_BUS] = 1 + +[API_PORT] = 24 +[PHY_ADDR] = 8 +[MDIO_BUS] = 1 + +[API_PORT] = 25 +[PHY_ADDR] = 9 +[MDIO_BUS] = 1 + +[API_PORT] = 26 +[PHY_ADDR] = 10 +[MDIO_BUS] = 1 + +[API_PORT] = 27 +[PHY_ADDR] = 11 +[MDIO_BUS] = 1 + +[API_PORT] = 48 +[PHY_ADDR] = 12 +[MDIO_BUS] = 1 + +[API_PORT] = 49 +[PHY_ADDR] = 13 +[MDIO_BUS] = 1 + +[API_PORT] = 50 +[PHY_ADDR] = 14 +[MDIO_BUS] = 1 + +[API_PORT] = 51 +[PHY_ADDR] = 15 +[MDIO_BUS] = 1 + +[API_PORT] = 52 +[PHY_ADDR] = 16 +[MDIO_BUS] = 1 + +[API_PORT] = 53 +[PHY_ADDR] = 17 +[MDIO_BUS] = 1 + +[API_PORT] = 54 +[PHY_ADDR] = 18 +[MDIO_BUS] = 1 + +[API_PORT] = 55 +[PHY_ADDR] = 19 +[MDIO_BUS] = 1 + +[API_PORT] = 56 +[PHY_ADDR] = 20 +[MDIO_BUS] = 1 + +[API_PORT] = 57 +[PHY_ADDR] = 21 +[MDIO_BUS] = 1 + +[API_PORT] = 58 +[PHY_ADDR] = 22 +[MDIO_BUS] = 1 + +[API_PORT] = 59 +[PHY_ADDR] = 23 +[MDIO_BUS] = 1 + +#} diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/platform_asic b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/platform_asic new file mode 100644 index 000000000000..0815c8a78c0b --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/platform_asic @@ -0,0 +1 @@ +centec-arm64 diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/port_config.ini b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/port_config.ini new file mode 100644 index 000000000000..cd9f488c53b2 --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/port_config.ini @@ -0,0 +1,53 @@ +# name lanes alias index speed admin_status +Ethernet1 0 eth-0-1 1 1000 up +Ethernet2 1 eth-0-2 2 1000 up +Ethernet3 2 eth-0-3 3 1000 up +Ethernet4 3 eth-0-4 4 1000 up +Ethernet5 4 eth-0-5 5 1000 up +Ethernet6 5 eth-0-6 6 1000 up +Ethernet7 6 eth-0-7 7 1000 up +Ethernet8 7 eth-0-8 8 1000 up +Ethernet9 16 eth-0-9 9 1000 up +Ethernet10 17 eth-0-10 10 1000 up +Ethernet11 18 eth-0-11 11 1000 up +Ethernet12 19 eth-0-12 12 1000 up +Ethernet13 20 eth-0-13 13 1000 up +Ethernet14 21 eth-0-14 14 1000 up +Ethernet15 22 eth-0-15 15 1000 up +Ethernet16 23 eth-0-16 16 1000 up +Ethernet17 8 eth-0-17 17 1000 up +Ethernet18 9 eth-0-18 18 1000 up +Ethernet19 10 eth-0-19 19 1000 up +Ethernet20 11 eth-0-20 20 1000 up +Ethernet21 32 eth-0-21 21 1000 up +Ethernet22 33 eth-0-22 22 1000 up +Ethernet23 34 eth-0-23 23 1000 up +Ethernet24 35 eth-0-24 24 1000 up +Ethernet25 36 eth-0-25 25 1000 up +Ethernet26 37 eth-0-26 26 1000 up +Ethernet27 38 eth-0-27 27 1000 up +Ethernet28 39 eth-0-28 28 1000 up +Ethernet29 40 eth-0-29 29 1000 up +Ethernet30 41 eth-0-30 30 1000 up +Ethernet31 42 eth-0-31 31 1000 up +Ethernet32 43 eth-0-32 32 1000 up +Ethernet33 24 eth-0-33 33 1000 up +Ethernet34 25 eth-0-34 34 1000 up +Ethernet35 26 eth-0-35 35 1000 up +Ethernet36 27 eth-0-36 36 1000 up +Ethernet37 48 eth-0-37 37 1000 up +Ethernet38 49 eth-0-38 38 1000 up +Ethernet39 50 eth-0-39 39 1000 up +Ethernet40 51 eth-0-40 40 1000 up +Ethernet41 52 eth-0-41 41 1000 up +Ethernet42 53 eth-0-42 42 1000 up +Ethernet43 54 eth-0-43 43 1000 up +Ethernet44 55 eth-0-44 44 1000 up +Ethernet45 56 eth-0-45 45 1000 up +Ethernet46 57 eth-0-46 46 1000 up +Ethernet47 58 eth-0-47 47 1000 up +Ethernet48 59 eth-0-48 48 1000 up +Ethernet49 12 eth-0-49 49 10000 up +Ethernet50 13 eth-0-50 50 10000 up +Ethernet51 14 eth-0-51 51 10000 up +Ethernet52 15 eth-0-52 52 10000 up diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/sai.profile b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/sai.profile new file mode 100644 index 000000000000..edeaddd8e482 --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/sai.profile @@ -0,0 +1,4 @@ +SAI_INIT_CONFIG_FILE=/usr/share/sonic/hwsku/M2-w6010-48gt4x-fa-p-chip-profile.txt +SAI_HW_PORT_PROFILE_ID_CONFIG_FILE=/usr/share/sonic/hwsku/M2-w6010-48gt4x-fa-p-datapath.txt +SAI_PLATFORM_CFG_FILE=/usr/share/sonic/hwsku/M2-w6010-48gt4x-fa-board.json +SAI_PHY_DRIVER_PATH=/usr/share/sonic/hwsku/phy_drv \ No newline at end of file diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/start_up.cfg b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/start_up.cfg new file mode 100644 index 000000000000..b012adedb252 --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA/start_up.cfg @@ -0,0 +1,48 @@ + +chip set serdes 0 ffe mode user-define c0 0 c1 255 c2 10 c3 0 c4 0 +chip set serdes 1 ffe mode user-define c0 0 c1 240 c2 10 c3 0 c4 0 +chip set serdes 2 ffe mode user-define c0 0 c1 230 c2 10 c3 0 c4 0 +chip set serdes 3 ffe mode user-define c0 0 c1 220 c2 7 c3 0 c4 0 +chip set serdes 4 ffe mode user-define c0 0 c1 220 c2 8 c3 0 c4 0 +chip set serdes 5 ffe mode user-define c0 0 c1 210 c2 6 c3 0 c4 0 +chip set serdes 6 ffe mode user-define c0 0 c1 180 c2 6 c3 0 c4 0 +chip set serdes 7 ffe mode user-define c0 0 c1 180 c2 5 c3 0 c4 0 +chip set serdes 8 ffe mode user-define c0 2 c1 160 c2 4 c3 0 c4 0 +chip set serdes 9 ffe mode user-define c0 2 c1 160 c2 4 c3 0 c4 0 +chip set serdes 10 ffe mode user-define c0 3 c1 140 c2 9 c3 0 c4 0 +chip set serdes 11 ffe mode user-define c0 3 c1 140 c2 5 c3 0 c4 0 +chip set serdes 12 ffe mode user-define c0 3 c1 94 c2 15 c3 0 c4 0 +chip set serdes 13 ffe mode user-define c0 3 c1 94 c2 15 c3 0 c4 0 +chip set serdes 14 ffe mode user-define c0 3 c1 94 c2 15 c3 0 c4 0 +chip set serdes 15 ffe mode user-define c0 3 c1 94 c2 15 c3 0 c4 0 + + +chip set serdes 0 ctle 6 14 2 +chip set serdes 1 ctle 6 14 2 +chip set serdes 2 ctle 6 14 2 +chip set serdes 3 ctle 6 14 2 +chip set serdes 4 ctle 6 14 2 +chip set serdes 5 ctle 6 14 2 +chip set serdes 6 ctle 6 14 2 +chip set serdes 7 ctle 6 14 2 +chip set serdes 8 ctle 6 14 2 +chip set serdes 9 ctle 6 14 2 +chip set serdes 10 ctle 6 14 2 +chip set serdes 11 ctle 6 14 2 + + + +# rtk eye param, post_amp|main_amp|pre_amp +port 0 phy-attr type 2001 value 0x1c1e00 +port 4 phy-attr type 2001 value 0x101600 +port 16 phy-attr type 2001 value 0x111500 +port 20 phy-attr type 2001 value 0x0e1100 +port 8 phy-attr type 2001 value 0x0f1000 +port 32 phy-attr type 2001 value 0x0e0f00 +port 36 phy-attr type 2001 value 0x0a0a00 +port 40 phy-attr type 2001 value 0x0a0900 +port 24 phy-attr type 2001 value 0x000c00 +port 48 phy-attr type 2001 value 0x000b00 +port 52 phy-attr type 2001 value 0x000b00 +port 56 phy-attr type 2001 value 0x000a00 + diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/default_sku b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/default_sku new file mode 100644 index 000000000000..c29b09ab9378 --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/default_sku @@ -0,0 +1 @@ +M2-W6010-48GT4X-FA l1 diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/installer.conf b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/installer.conf new file mode 100644 index 000000000000..6e00c7ff77f1 --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/installer.conf @@ -0,0 +1,9 @@ +CONSOLE_SPEED=115200 + +#fix env +config_env(){ + dd if=/dev/mtd1 of=env.bin + flashcp -v env.bin /dev/mtd4 +} +trap_push "config_env || true" + diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/platform.json b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/platform.json new file mode 100644 index 000000000000..36f541f1ef02 --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/platform.json @@ -0,0 +1,173 @@ +{ + "chassis": { + "name": "M2-W6010-48GT4X-FA", + "thermal_manager": false, + "status_led": { + "controllable": false, + "colors": ["green", "blinking_green", "amber", "blinking_amber"] + }, + "components": [ + { + "name": "CPU CPLD" + }, + { + "name": "MAC1 CPLD" + } + ], + "fans": [ + { + "name": "Fantray1_1", + "speed": { + "controllable": true, + "minimum": 30, + "maximum": 99 + }, + "status_led": { + "available": false, + "colors": ["off", "red", "amber", "green"] + } + }, + { + "name": "Fantray2_1", + "speed": { + "controllable": true, + "minimum": 30, + "maximum": 99 + }, + "status_led": { + "available": false, + "colors": ["off", "red", "amber", "green"] + } + } + ], + "fan_drawers":[ + { + "name": "Fantray1", + "num_fans" : 1, + "status_led": { + "controllable": false, + "colors": ["amber", "green", "off"] + }, + "fans": [ + { + "name": "FanTray1_1", + "speed": { + "controllable": true, + "minimum": 30, + "maximum": 99 + }, + "status_led": { + "available": false + } + } + ] + }, + { + "name": "Fantray2", + "num_fans" : 1, + "status_led": { + "controllable": false, + "colors": ["amber", "green", "off"] + }, + "fans": [ + { + "name": "FanTray2_1", + "speed": { + "controllable": true, + "minimum": 30, + "maximum": 99 + }, + "status_led": { + "available": false + } + } + ] + } + ], + "psus": [ + { + "name": "PSU 1", + "voltage": true, + "current": true, + "power": true, + "max_power": false, + "voltage_high_threshold": true, + "voltage_low_threshold": true, + "temperature": true, + "fans_target_speed": false, + "status_led": { + "controllable": false + } + }, + { + "name": "PSU 2", + "voltage": true, + "current": true, + "power": true, + "max_power": false, + "voltage_high_threshold": true, + "voltage_low_threshold": true, + "temperature": true, + "fans_target_speed": false, + "status_led": { + "controllable": false + } + } + ], + "thermals": [ + { + "name": "ASIC_TEMP", + "controllable": false, + "low-crit-threshold": true, + "high-crit-threshold": true, + "low-threshold": true, + "high-threshold": true, + "minimum-recorded": true, + "maximum-recorded": true + }, + { + "name": "CPU_TEMP", + "controllable": false, + "low-crit-threshold": true, + "high-crit-threshold": true, + "low-threshold": true, + "high-threshold": true, + "minimum-recorded": true, + "maximum-recorded": true + }, + { + "name": "INLET_TEMP", + "controllable": false, + "low-crit-threshold": true, + "high-crit-threshold": true, + "low-threshold": true, + "high-threshold": true, + "minimum-recorded": true, + "maximum-recorded": true + }, + { + "name": "OUTLET_TEMP", + "controllable": false, + "low-crit-threshold": true, + "high-crit-threshold": true, + "low-threshold": true, + "high-threshold": true, + "minimum-recorded": true, + "maximum-recorded": true + }, + { + "name": "TPS53688_TEMP", + "controllable": false, + "low-crit-threshold": true, + "high-crit-threshold": true, + "low-threshold": true, + "high-threshold": true, + "minimum-recorded": true, + "maximum-recorded": true + } + ], + "modules": [], + "sfps": [] + }, + "interfaces": {} +} diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/platform_asic b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/platform_asic new file mode 100644 index 000000000000..0815c8a78c0b --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/platform_asic @@ -0,0 +1 @@ +centec-arm64 diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/plugins/sfputil.py b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/plugins/sfputil.py new file mode 100644 index 000000000000..c4fdbe2c3a8c --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/plugins/sfputil.py @@ -0,0 +1,428 @@ +# sfputil.py +# +# Platform-specific SFP transceiver interface for SONiC +# + +try: + import time + import subprocess + import re + import os + import threading + from sonic_sfp.sfputilbase import SfpUtilBase +except ImportError as e: + raise ImportError("%s - required module not found" % str(e)) + + +class SfpUtil(SfpUtilBase): + """Platform-specific SfpUtil class""" + + PORT_START = 49 + PORT_END = 52 + PORTS_IN_BLOCK = 53 + EEPROM_OFFSET = 9 + SFP_DEVICE_TYPE = "optoe2" + QSFP_DEVICE_TYPE = "optoe1" + I2C_MAX_ATTEMPT = 3 + + SFP_STATUS_INSERTED = '1' + SFP_STATUS_REMOVED = '0' + + TXWRT_PROTECT = 0X4E + TXWRT_NO_PROTECT = 0X59 + + _port_to_eeprom_mapping = {} + port_to_i2cbus_mapping ={} + port_dict = {} + port_presence_info = {} + port_reset_info = {} + port_txdis_info = {} + port_txwrt_info = {} + port_led_info = {} + + port_rxlos_info = {} + port_txfault_info = {} + port_drop_info = {} + + @property + def port_start(self): + return self.PORT_START + + @property + def port_end(self): + return self.PORT_END + + @property + def sfp_ports(self): + return list(range(self.PORT_START, self.PORTS_IN_BLOCK)) + + @property + def qsfp_ports(self): + return [] + + @property + def port_to_eeprom_mapping(self): + return self._port_to_eeprom_mapping + + def __init__(self): + for x in range(self.PORT_START, self.PORTS_IN_BLOCK): + self.port_to_i2cbus_mapping[x] = (x - self.PORT_START + self.EEPROM_OFFSET) + self.port_presence_info["/sys/bus/i2c/devices/3-0030/sfp_presence1"] = [49, 50, 51, 52] + self.port_txdis_info["/sys/bus/i2c/devices/3-0030/tx_disable"] = [49, 50, 51, 52] + self.port_txwrt_info["/sys/bus/i2c/devices/3-0030/tx_write_protect"] = [49, 50, 51, 52] + + # bit 1: los + self.port_rxlos_info["/sys/bus/i2c/devices/3-0030/sfp_rx_loss1"] = [49, 50, 51, 52] + + # bit 1: fault + self.port_txfault_info["/sys/bus/i2c/devices/3-0030/sfp_tx_fault1"] = [49, 50, 51, 52] + + # bit 1: drop + self.port_drop_info["/sys/bus/i2c/devices/3-0030/sfp_drop_record1"] = [49, 50, 51, 52] + + SfpUtilBase.__init__(self) + + def _sfp_read_file_path(self, file_path, offset, num_bytes): + attempts = 0 + while attempts < self.I2C_MAX_ATTEMPT: + try: + file_path.seek(offset) + read_buf = file_path.read(num_bytes) + except Exception: + attempts += 1 + time.sleep(0.05) + else: + return True, read_buf + return False, None + + def _sfp_eeprom_present(self, sysfs_sfp_i2c_client_eeprompath, offset): + """Tries to read the eeprom file to determine if the + device/sfp is present or not. If sfp present, the read returns + valid bytes. If not, read returns error 'Connection timed out""" + + if not os.path.exists(sysfs_sfp_i2c_client_eeprompath): + return False + else: + with open(sysfs_sfp_i2c_client_eeprompath, "rb", buffering=0) as sysfsfile: + rv, buf = self._sfp_read_file_path(sysfsfile, offset, 1) + return rv + + def _add_new_sfp_device(self, sysfs_sfp_i2c_adapter_path, devaddr, devtype): + try: + sysfs_nd_path = "%s/new_device" % sysfs_sfp_i2c_adapter_path + + # Write device address to new_device file + nd_file = open(sysfs_nd_path, "w") + nd_str = "%s %s" % (devtype, hex(devaddr)) + nd_file.write(nd_str) + nd_file.close() + + except Exception as err: + print(("Error writing to new device file: %s" % str(err))) + return 1 + else: + return 0 + + def _get_port_eeprom_path(self, port_num, devid): + if port_num in list(self.port_to_eeprom_mapping.keys()): + sysfs_sfp_i2c_client_eeprom_path = self.port_to_eeprom_mapping[port_num] + else: + sysfs_i2c_adapter_base_path = "/sys/class/i2c-adapter" + + i2c_adapter_id = self._get_port_i2c_adapter_id(port_num) + if i2c_adapter_id is None: + print("Error getting i2c bus num") + return None + + # Get i2c virtual bus path for the sfp + sysfs_sfp_i2c_adapter_path = "%s/i2c-%s" % (sysfs_i2c_adapter_base_path, + str(i2c_adapter_id)) + + # If i2c bus for port does not exist + if not os.path.exists(sysfs_sfp_i2c_adapter_path): + print(("Could not find i2c bus %s. Driver not loaded?" % sysfs_sfp_i2c_adapter_path)) + return None + + sysfs_sfp_i2c_client_path = "%s/%s-00%s" % (sysfs_sfp_i2c_adapter_path, + str(i2c_adapter_id), + hex(devid)[-2:]) + + # If sfp device is not present on bus, Add it + if not os.path.exists(sysfs_sfp_i2c_client_path): + if port_num in self.qsfp_ports: + ret = self._add_new_sfp_device( + sysfs_sfp_i2c_adapter_path, devid, self.QSFP_DEVICE_TYPE) + else: + ret = self._add_new_sfp_device( + sysfs_sfp_i2c_adapter_path, devid, self.SFP_DEVICE_TYPE) + if ret != 0: + print("Error adding sfp device") + return None + + sysfs_sfp_i2c_client_eeprom_path = "%s/eeprom" % sysfs_sfp_i2c_client_path + return sysfs_sfp_i2c_client_eeprom_path + + def _read_eeprom_specific_bytes(self, sysfsfile_eeprom, offset, num_bytes): + eeprom_raw = [] + for i in range(0, num_bytes): + eeprom_raw.append("0x00") + + rv, raw = self._sfp_read_file_path(sysfsfile_eeprom, offset, num_bytes) + if rv == False: + return None + + try: + if isinstance(raw, str): + for n in range(0, num_bytes): + eeprom_raw[n] = hex(ord(raw[n]))[2:].zfill(2) + else: + for n in range(0, num_bytes): + eeprom_raw[n] = hex(raw[n])[2:].zfill(2) + except Exception as err: + return None + return eeprom_raw + + def get_eeprom_dom_raw(self, port_num): + if port_num in self.qsfp_ports: + # QSFP DOM EEPROM is also at addr 0x50 and thus also stored in eeprom_ifraw + return None + else: + # Read dom eeprom at addr 0x51 + return self._read_eeprom_devid(port_num, self.IDENTITY_EEPROM_ADDR, 256) + + 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 + + presence_path = None + for presence_key in self.port_presence_info: + if port_num in self.port_presence_info[presence_key]: + presence_path = presence_key + presence_offset = self.port_presence_info[presence_key].index(port_num) + break + if presence_path == None: + return False + + try: + data = open(presence_path, "rb") + except IOError: + return False + + presence_data = data.read(2) + if presence_data == "": + return False + result = int(presence_data, 16) + data.close() + + # ModPrsL is active low + if result & (1 << presence_offset) == 0: + return True + + return False + + def get_low_power_mode(self, port_num): + return False + + def set_low_power_mode(self, port_num, lpmode): + return False + + def reset(self, port_num): + return False + + def reset_all(self): + return False + + def _do_write_file(self, file_handle, offset, value): + file_handle.seek(offset) + file_handle.write(hex(value)) + + def get_transceiver_change_event(self, timeout=0): + + start_time = time.time() + currernt_port_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 + + while timeout >= 0: + # Check for OIR events and return updated port_dict + for x in range(self.PORT_START, self.PORTS_IN_BLOCK): + if self.get_presence(x): + currernt_port_dict[x] = self.SFP_STATUS_INSERTED + else: + currernt_port_dict[x] = self.SFP_STATUS_REMOVED + if (currernt_port_dict == self.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, {} + else: + # Update reg value + self.port_dict = currernt_port_dict + return True, self.port_dict + print ("get_transceiver_change_event: Should not reach here.") + return False, {} + + def tx_disable(self, port_num, disable): + if not self.get_presence(port_num): + return False + + if port_num in self.sfp_ports: + txwrt_path = None + txdis_path = None + txdis_offset = 0 + + for key in self.port_txwrt_info: + if port_num in self.port_txwrt_info[key]: + txwrt_path = key + break + if txwrt_path == None: + return False + + for key in self.port_txdis_info: + if port_num in self.port_txdis_info[key]: + txdis_path = key + txdis_offset = self.port_txdis_info[key].index(port_num) + break + if txdis_path == None: + return False + + + try: + with open(txwrt_path, "r+") as sys_file: + sres = hex(self.TXWRT_NO_PROTECT)[2:] + sys_file.write(sres) + + with open(txdis_path, "r+") as sys_file: + txdis_data = sys_file.read(2) + if not txdis_data: + return False + result = int(txdis_data, 16) + if disable: + result = result | (1 << txdis_offset) + else: + result = result & (~(1 << txdis_offset)) + sys_file.seek(0) + sres = hex(result)[2:] + print(result,sres) + sys_file.write(sres) + + with open(txwrt_path, "r+") as sys_file: + sres = hex(self.TXWRT_PROTECT)[2:] + sys_file.write(sres) + except Exception as err: + print(err) + return False + + return True + else: + return False + +########### sysdiag ########### + def _get_cpld_info(self, port_num, info): + path = None + offset = 0 + for key in info: + if port_num in info[key]: + path = key + offset = info[key].index(port_num) + break + return path, offset + + def get_tx_disable(self, port_num): + # cur only support sfp moudle + if port_num not in self.sfp_ports: + return False + + if not self.get_presence(port_num): + return False + + path, offset = self._get_cpld_info(port_num, self.port_txdis_info) + if path == None: + return False + + result = 0 + try: + with open(path, "r") as sys_file: + data = sys_file.read(2) + result = int(data, 16) + except Exception as e: + print((str(e))) + return False + + # 1: disable + if result & (1 << offset): + return True + else: + return False + + def get_rx_los(self, port_num): + # cur only support sfp moudle + if port_num not in self.sfp_ports: + return False + + path, offset = self._get_cpld_info(port_num, self.port_rxlos_info) + if path == None: + return False + + result = 0 + try: + with open(path, "r") as sys_file: + data = sys_file.read(2) + result = int(data, 16) + except Exception as e: + print((str(e))) + return False + + # 1: los + if result & (1 << offset): + return True + else: + return False + + def get_tx_fault(self, port_num): + # cur only support sfp moudle + if port_num not in self.sfp_ports: + return False + + if not self.get_presence(port_num): + return False + + path, offset = self._get_cpld_info(port_num, self.port_txfault_info) + if path == None: + return False + + result = 0 + try: + with open(path, "r") as sys_file: + data = sys_file.read(2) + result = int(data, 16) + except Exception as e: + print((str(e))) + return False + + # 1: fault + if result & (1 << offset): + return True + + return False diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/plugins/ssd_util.py b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/plugins/ssd_util.py new file mode 100644 index 000000000000..95ca23e8b351 --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/plugins/ssd_util.py @@ -0,0 +1,112 @@ +# +# ssd_util.py +# +# Generic implementation of the SSD health API +# SSD models supported: +# - InnoDisk +# - StorFly +# - Virtium + +try: + import subprocess + from sonic_platform_base.sonic_ssd.ssd_base import SsdBase +except ImportError as e: + raise ImportError (str(e) + "- required module not found") + +HEALTH_CMD = "cat /sys/kernel/debug/mmc0/mmc0:0001/ext_csd | cut -c 537-538" +SERIAL_CMD = "cat /sys/bus/mmc/devices/mmc0\\:0001/serial" +FIRMWARE_CMD = "cat /sys/kernel/debug/mmc0/mmc0:0001/ext_csd | cut -c 509-522" +NOT_AVAILABLE = "N/A" + +class SsdUtil(SsdBase): + """ + Generic implementation of the SSD health API + """ + def __init__(self, diskdev): + self.model = "KLMCG4JETD-B041" + self.temperature = NOT_AVAILABLE + self.vendor_ssd_info = "====No vendor information====" + self.health_list = [100,90,80,70,60,50,40,30,20,10,0] + try: + life_time = self._execute_shell(HEALTH_CMD) + if int(life_time) in range(1,12): + self.health = self.health_list[int(life_time) - 1] + else: + self.health = NOT_AVAILABLE + except Exception as e: + self.health = NOT_AVAILABLE + + try: + self.firmware = self._execute_shell(FIRMWARE_CMD) + except Exception as e: + self.firmware = NOT_AVAILABLE + + try: + serial = self._execute_shell(SERIAL_CMD) + self.serial = serial.replace("0x",'') + except Exception as e: + self.serial = NOT_AVAILABLE + + def _execute_shell(self, cmd): + status, output = subprocess.getstatusoutput(cmd) + if status: + return None + + return output + + def get_health(self): + """ + Retrieves current disk health in percentages + + Returns: + A float number of current ssd health + e.g. 83.5 + """ + return self.health + + def get_temperature(self): + """ + Retrieves current disk temperature in Celsius + + Returns: + A float number of current temperature in Celsius + e.g. 40.1 + """ + return self.temperature + + def get_model(self): + """ + Retrieves model for the given disk device + + Returns: + A string holding disk model as provided by the manufacturer + """ + return self.model + + def get_firmware(self): + """ + Retrieves firmware version for the given disk device + + Returns: + A string holding disk firmware version as provided by the manufacturer + """ + return self.firmware + + def get_serial(self): + """ + Retrieves serial number for the given disk device + + Returns: + A string holding disk serial number as provided by the manufacturer + """ + return self.serial + + def get_vendor_output(self): + """ + Retrieves vendor specific data for the given disk device + + Returns: + A string holding some vendor specific disk information + """ + return self.vendor_ssd_info + diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/pmon_daemon_control.json b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/pmon_daemon_control.json new file mode 100644 index 000000000000..5fdf83f1b9e6 --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/pmon_daemon_control.json @@ -0,0 +1,7 @@ +{ + "skip_ledd": true, + "skip_xcvrd": false, + "skip_syseepromd": false, + "skip_thermalctld": false, + "skip_psud": false +} diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/system_health_monitoring_config.json b/device/micas/arm64-micas_m2-w6010-48gt4x-fa-r0/system_health_monitoring_config.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/M2-w6010-48gt4x-ra-board.json b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/M2-w6010-48gt4x-ra-board.json new file mode 100644 index 000000000000..80fb22238078 --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/M2-w6010-48gt4x-ra-board.json @@ -0,0 +1,868 @@ +{ + "macleds" : { + "polarity" : 1, + "freq" : 2500, + "interval" : 50000000, + "maps" : [ + { + "port_id" : 0, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port1/brightness" + }, + { + "port_id" : 1, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port2/brightness" + }, + { + "port_id" : 2, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port3/brightness" + }, + { + "port_id" : 3, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port4/brightness" + }, + { + "port_id" : 4, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port5/brightness" + }, + { + "port_id" : 5, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port6/brightness" + }, + { + "port_id" : 6, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port7/brightness" + }, + { + "port_id" : 7, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port8/brightness" + }, + { + "port_id" : 16, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port9/brightness" + }, + { + "port_id" : 17, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port10/brightness" + }, + { + "port_id" : 18, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port11/brightness" + }, + { + "port_id" : 19, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port12/brightness" + }, + { + "port_id" : 20, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port13/brightness" + }, + { + "port_id" : 21, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port14/brightness" + }, + { + "port_id" : 22, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port15/brightness" + }, + { + "port_id" : 23, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port16/brightness" + }, + { + "port_id" : 8, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port17/brightness" + }, + { + "port_id" : 9, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port18/brightness" + }, + { + "port_id" : 10, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port19/brightness" + }, + { + "port_id" : 11, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port20/brightness" + }, + { + "port_id" : 32, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port21/brightness" + }, + { + "port_id" : 33, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port22/brightness" + }, + { + "port_id" : 34, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port23/brightness" + }, + { + "port_id" : 35, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port24/brightness" + }, + { + "port_id" : 36, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port25/brightness" + }, + { + "port_id" : 37, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port26/brightness" + }, + { + "port_id" : 38, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port27/brightness" + }, + { + "port_id" : 39, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port28/brightness" + }, + { + "port_id" : 40, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port29/brightness" + }, + { + "port_id" : 41, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port30/brightness" + }, + { + "port_id" : 42, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port31/brightness" + }, + { + "port_id" : 43, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port32/brightness" + }, + { + "port_id" : 24, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port33/brightness" + }, + { + "port_id" : 25, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port34/brightness" + }, + { + "port_id" : 26, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port35/brightness" + }, + { + "port_id" : 27, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port36/brightness" + }, + { + "port_id" : 48, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port37/brightness" + }, + { + "port_id" : 49, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port38/brightness" + }, + { + "port_id" : 50, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port39/brightness" + }, + { + "port_id" : 51, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port40/brightness" + }, + { + "port_id" : 52, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port41/brightness" + }, + { + "port_id" : 53, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port42/brightness" + }, + { + "port_id" : 54, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port43/brightness" + }, + { + "port_id" : 55, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port44/brightness" + }, + { + "port_id" : 56, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port45/brightness" + }, + { + "port_id" : 57, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port46/brightness" + }, + { + "port_id" : 58, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port47/brightness" + }, + { + "port_id" : 59, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port48/brightness" + }, + { + "port_id" : 12, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port49/brightness" + }, + { + "port_id" : 13, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port50/brightness" + }, + { + "port_id" : 14, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port51/brightness" + }, + { + "port_id" : 15, + "lchip" : 0, + "ctl_id" : 0, + "mode" : "LED_MODE_1_RXLNK_BIACT", + "fixed" : 0, + "sysfs_path" : "/sys/class/leds/port52/brightness" + } + ] + }, + "phys" : [ + { + "macid" : 0, + "busid" : 0, + "addr" : 0, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 1, + "busid" : 0, + "addr" : 1, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 2, + "busid" : 0, + "addr" : 2, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 3, + "busid" : 0, + "addr" : 3, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 4, + "busid" : 0, + "addr" : 4, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 5, + "busid" : 0, + "addr" : 5, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 6, + "busid" : 0, + "addr" : 6, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 7, + "busid" : 0, + "addr" : 7, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 16, + "busid" : 0, + "addr" : 8, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 17, + "busid" : 0, + "addr" : 9, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 18, + "busid" : 0, + "addr" : 10, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 19, + "busid" : 0, + "addr" : 11, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 20, + "busid" : 0, + "addr" : 12, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 21, + "busid" : 0, + "addr" : 13, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 22, + "busid" : 0, + "addr" : 14, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 23, + "busid" : 0, + "addr" : 15, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 8, + "busid" : 0, + "addr" : 16, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 9, + "busid" : 0, + "addr" : 17, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 10, + "busid" : 0, + "addr" : 18, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 11, + "busid" : 0, + "addr" : 19, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 32, + "busid" : 0, + "addr" : 20, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 33, + "busid" : 0, + "addr" : 21, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 34, + "busid" : 0, + "addr" : 22, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 35, + "busid" : 0, + "addr" : 23, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 36, + "busid" : 1, + "addr" : 0, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 37, + "busid" : 1, + "addr" : 1, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 38, + "busid" : 1, + "addr" : 2, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 39, + "busid" : 1, + "addr" : 3, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 40, + "busid" : 1, + "addr" : 4, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 41, + "busid" : 1, + "addr" : 5, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 42, + "busid" : 1, + "addr" : 6, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 43, + "busid" : 1, + "addr" : 7, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 24, + "busid" : 1, + "addr" : 8, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 25, + "busid" : 1, + "addr" : 9, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 26, + "busid" : 1, + "addr" : 10, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 27, + "busid" : 1, + "addr" : 11, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 48, + "busid" : 1, + "addr" : 12, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 49, + "busid" : 1, + "addr" : 13, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 50, + "busid" : 1, + "addr" : 14, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 51, + "busid" : 1, + "addr" : 15, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 52, + "busid" : 1, + "addr" : 16, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 53, + "busid" : 1, + "addr" : 17, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 54, + "busid" : 1, + "addr" : 18, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 55, + "busid" : 1, + "addr" : 19, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 56, + "busid" : 1, + "addr" : 20, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 57, + "busid" : 1, + "addr" : 21, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 58, + "busid" : 1, + "addr" : 22, + "base_port": 1, + "last_port": 4 + }, + { + "macid" : 59, + "busid" : 1, + "addr" : 23, + "base_port": 1, + "last_port": 4 + } + ], + "ffe" : { + "board_material" : "BOARD_MATERIAL_M4", + "config" : [ + { + "serdes_id" : [0], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [0, 255, 10, 0] + }, + { + "serdes_id" : [1], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [0, 240, 10, 0] + }, + { + "serdes_id" : [2], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [0, 230, 10, 0] + }, + { + "serdes_id" : [3], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [0, 220, 7, 0] + }, + { + "serdes_id" : [4], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [0, 220, 8, 0] + }, + { + "serdes_id" : [5], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [0, 210, 6, 0] + }, + { + "serdes_id" : [6], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [0, 180, 6, 0] + }, + { + "serdes_id" : [7], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [0, 180, 5, 0] + }, + { + "serdes_id" : [8], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [2, 160, 4, 0] + }, + { + "serdes_id" : [9], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [2, 160, 4, 0] + }, + { + "serdes_id" : [10], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [3, 140, 9, 0] + }, + { + "serdes_id" : [11], + "is_dac" : 0, + "speed" : [1000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [3, 140, 5, 0] + }, + { + "serdes_id" : [12, 13, 14, 15], + "is_dac" : 0, + "speed" : [10000], + "mode" : "CTC_CHIP_SERDES_FFE_MODE_DEFINE", + "cfg" : [3, 94, 15, 0] + } + ] + }, + "ctle" : { + "config" : [ + { + "serdes_id" : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], + "auto-en" : 0, + "cfg" : [6, 14, 2] + } + ] + } +} diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/M2-w6010-48gt4x-ra-p-chip-profile.txt b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/M2-w6010-48gt4x-ra-p-chip-profile.txt new file mode 100644 index 000000000000..2373e27f5f8b --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/M2-w6010-48gt4x-ra-p-chip-profile.txt @@ -0,0 +1,96 @@ +#----------------- SDK Feature Support -------------- +[MPLS_SUPPORT] = 1; +[APS_SUPPORT] = 1; +[OAM_SUPPORT] = 1; +[PTP_SUPPORT] = 0; +[SYNCE_SUPPORT] = 0; +[STACKING_SUPPORT] = 1; +[BPE_SUPPORT] = 0; +[IPFIX_SUPPORT] = 1; +[MONITOR_SUPPORT] = 1; +[OVERLAY_SUPPORT] = 1; +[EFD_SUPPORT] = 1; +[FCOE_SUPPORT] = 0; +[TRILL_SUPPORT] = 0; +[WLAN_SUPPORT] = 1; +[NPM_SUPPORT] = 1; +[DOT1AE_SUPPORT] = 1; + +#----------------- Chip Init Parameter -------------- +#Local chip number and global chip id +[Local chip_num] = 1 +[Local chip0] = 0 +[Local chip1] = 1 + +#Cut through mode 0: Disable; 1:10/40/100G; 2:1/10/100G; 3:1/10/40G; other:Flex, refer to CUT_THROUGH_BITMAP +[CUT_THROUGH_SPEED] = 0 +#Flex cut through mode, speed enable by bitmap, refer to ctc_port_speed_t, Notice: 10M/100M/1G treat as the same speed +[CUT_THROUGH_BITMAP] = 0 + +#Network cpu port +[CPU_NETWORK_PORT_EN] = 0 +[CPU_NETWORK_PORT_ID] = 47 + +#Enable parity error and multi-bit ecc recover +[ECC_RECOVER_EN] = 0 +[TCAM_SCAN_EN] = 0 + +#----------------- Interrupt Init Parameter -------------- +#0: pin, 1: msi +[Interrupt_mode] = 0 +[IRQ] = 69 + +#----------------- NextHop Init Parameter -------------- +#0: SDK work in pizzbox (single chip system), 1: SDK work in multi-chip system +[Nexthop Edit Mode] = 0 +[External Nexthop Number] = 16384 +[MPLS Tunnel Number] = 1024 + +#----------------- L2 Init Parameter -------------- +[FDB Hw Learning] = 1 +[Logic Port Num] = 1024 +#0: 128 instance per port, 1: 64 instance per port, 2: 32 instance per port +[STP MODE] = 0 +[MAX_FID_NUM] = 5120 + +#----------------- Stats Init Parameter -------------- +[STATS_PORT_EN] = 0 +[STATS_ECMP_EN] = 0 + +#----------------- BPE Init Parameter -------------- +[BPE_BR_PORT_EXTENDER_EN] = 0 +[BPE_BR_UC_MAX_ECID] = 1024 +[BPE_BR_MC_MAX_ECID] = 4096 +[BPE_BR_PORT_BASE] = 0 + +#----------------- Ipuc Init Parameter -------------- +#0: tcam use prefix 16; 1: tcam use prefix 8 +[IPUC_TCAM_PREFIX_8] = 1 + +#----------------- QoS Init Parameter -------------- +#QoS policer number support 1K/2K/4K/8K, default 4K +[QOS_POLICER_NUM] = 4096 +#QoS port queue number support 16/8/8 BPE/4 BPE, +#When resrc_profile.cfg exist, queue number valid, +#Default 8 queue mode +#8 queue = 8 +#16 queue = 16 +#4 queue BPE = 17 +#8 queue BPE = 18 +[QOS_PORT_QUEUE_NUM] = 8 +#QoS port extend queue number support 0/4, default 0 +[QOS_PORT_EXT_QUEUE_NUM] = 0 +#QoS CPU reason queue number support 128/64/32, default 128 +[QOS_CPU_QUEUE_NUM] = 128 +[QOS_INGRESS_VLAN_POLICER_NUM] = 0 +[QOS_EGRESS_VLAN_POLICER_NUM] = 0 +[QOS_POLICER_MERGE_MODE] = 0 +#QOS service queue mode, default 0,0:logic scr port + dstport enq 1:service id + dstport enq +[QOS_SERVICE_QUEUE_MODE] = 0 +#Global enable logic dst port + dstport enq +[QOS_SERVICE_QUEUE_EGRESS_EN] = 0 + +#----------------- Stacking Init Parameter -------------- +#0: normal mode; 1: spine-leaf mode +[FABRIC MODE] = 0 +[STACKING VERSION] = 1 diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/M2-w6010-48gt4x-ra-p-datapath.txt b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/M2-w6010-48gt4x-ra-p-datapath.txt new file mode 100644 index 000000000000..959b97a150cc --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/M2-w6010-48gt4x-ra-p-datapath.txt @@ -0,0 +1,281 @@ +#Generated by 'CTC DataPath Tools' on Fri Dec 18 19:15:31 2020 +#Version 1.0, Supported by TsingMa SDK + + +#SERDES_MODE : 0-NONE, 1-XFI, 2-SGMII, 3-Not Support, 4-QSGMII, 5-XAUI, 6-DXAUI, 7-XLG, 8-CG, 9-SGMII2G5 +# 10-USXGMII-S, 11-USXGMII-M2G5, 12-USXGMII-M5G, 13-XXVG, 14-LG, 15-100BASE-FX +#SERDES_RX_POLY: 0-Normal, 1-Inverse +#SERDES_SWITCH : 0-Not Support Dynamic Switch, 1-Support Dynamic Switch + +[WLAN_ENABLE] = 1 +[DOT1AE_ENABLE] = 1 + +[CORE_PLLA] = 600 +#{ +[SERDES_ITEM] +#repeat 32 step 6 +[SERDES_ID] = 0 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 1 +[SERDES_TX_POLY] = 1 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 1 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 2 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 1 +[SERDES_TX_POLY] = 1 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 3 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 4 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 5 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 6 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 7 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 1 +[SERDES_TX_POLY] = 1 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 8 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 1 +[SERDES_TX_POLY] = 1 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 9 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 10 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 11 +[SERDES_MODE] = 4 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 12 +[SERDES_MODE] = 1 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 1 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 13 +[SERDES_MODE] = 1 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 1 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 14 +[SERDES_MODE] = 1 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 1 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 15 +[SERDES_MODE] = 1 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 1 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 16 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 17 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 18 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 19 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 20 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 21 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 22 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 23 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 24 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 25 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 26 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 1 + +[SERDES_ID] = 27 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 1 + +[SERDES_ID] = 28 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 29 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 0 + +[SERDES_ID] = 30 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 1 + +[SERDES_ID] = 31 +[SERDES_MODE] = 3 +[SERDES_RX_POLY] = 0 +[SERDES_TX_POLY] = 0 +[SERDES_SWITCH] = 0 +[SERDES_GROUP] = 1 + +#repeat end +#} + + +#[SERDES_TO_LPORT] +#{ +# | QSGMII/USXGMII-M2G5 | USXGMII-M5G | USXGMII-S | 100BASE-FX | SGMII/SGMII2G5/XFI | XAUI/DXAUI | XLG | XXVG | LG | CG +#---------|---------------------|-------------|-----------|------------|--------------------|------------|-----|------|----|--- +#serdes 0 |0 /1 /2 /3 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 1 |4 /5 /6 /7 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 2 |16/17/18/19 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 3 |20/21/22/23 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 4 |8 /9 /10/11 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 5 |32/33/34/35 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 6 |36/37/38/39 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 7 |40/41/42/43 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 8 |24/25/26/27 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 9 |48/49/50/51 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 10|52/53/54/55 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 11|56/57/58/59 |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 12|NA |NA |NA |12 |12 |12 |12 |NA |NA |NA +#serdes 13|NA |NA |NA |13 |13 |12 |12 |NA |NA |NA +#serdes 14|NA |NA |NA |14 |14 |12 |12 |NA |NA |NA +#serdes 15|NA |NA |NA |15 |15 |12 |12 |NA |NA |NA +#serdes 16|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 17|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 18|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 19|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 20|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 21|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 22|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 23|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 24|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 25|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 26|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 27|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 28|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 29|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 30|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#serdes 31|NA |NA |NA |NA |NA |NA |NA |NA |NA |NA +#} diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/default_sku b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/default_sku new file mode 100644 index 000000000000..c9a5806d0236 --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/default_sku @@ -0,0 +1 @@ +M2-W6010-48GT4X-RA l1 diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/phy_mapping.cfg b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/phy_mapping.cfg new file mode 100644 index 000000000000..eefd67e9c28b --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/phy_mapping.cfg @@ -0,0 +1,198 @@ +#######################PHY_MAPPING_48+4####################### + +#{ +[PHY_MAPPING_ITEM] + +[API_PORT] = 0 +[PHY_ADDR] = 0 +[MDIO_BUS] = 0 + +[API_PORT] = 1 +[PHY_ADDR] = 1 +[MDIO_BUS] = 0 + +[API_PORT] = 2 +[PHY_ADDR] = 2 +[MDIO_BUS] = 0 + +[API_PORT] = 3 +[PHY_ADDR] = 3 +[MDIO_BUS] = 0 + +[API_PORT] = 4 +[PHY_ADDR] = 4 +[MDIO_BUS] = 0 + +[API_PORT] = 5 +[PHY_ADDR] = 5 +[MDIO_BUS] = 0 + +[API_PORT] = 6 +[PHY_ADDR] = 6 +[MDIO_BUS] = 0 + +[API_PORT] = 7 +[PHY_ADDR] = 7 +[MDIO_BUS] = 0 + +[API_PORT] = 16 +[PHY_ADDR] = 8 +[MDIO_BUS] = 0 + +[API_PORT] = 17 +[PHY_ADDR] = 9 +[MDIO_BUS] = 0 + +[API_PORT] = 18 +[PHY_ADDR] = 10 +[MDIO_BUS] = 0 + +[API_PORT] = 19 +[PHY_ADDR] = 11 +[MDIO_BUS] = 0 + +[API_PORT] = 20 +[PHY_ADDR] = 12 +[MDIO_BUS] = 0 + +[API_PORT] = 21 +[PHY_ADDR] = 13 +[MDIO_BUS] = 0 + +[API_PORT] = 22 +[PHY_ADDR] = 14 +[MDIO_BUS] = 0 + +[API_PORT] = 23 +[PHY_ADDR] = 15 +[MDIO_BUS] = 0 + +[API_PORT] = 8 +[PHY_ADDR] = 16 +[MDIO_BUS] = 0 + +[API_PORT] = 9 +[PHY_ADDR] = 17 +[MDIO_BUS] = 0 + +[API_PORT] = 10 +[PHY_ADDR] = 18 +[MDIO_BUS] = 0 + +[API_PORT] = 11 +[PHY_ADDR] = 19 +[MDIO_BUS] = 0 + +[API_PORT] = 32 +[PHY_ADDR] = 20 +[MDIO_BUS] = 0 + +[API_PORT] = 33 +[PHY_ADDR] = 21 +[MDIO_BUS] = 0 + +[API_PORT] = 34 +[PHY_ADDR] = 22 +[MDIO_BUS] = 0 + +[API_PORT] = 35 +[PHY_ADDR] = 23 +[MDIO_BUS] = 0 + +[API_PORT] = 36 +[PHY_ADDR] = 0 +[MDIO_BUS] = 1 + +[API_PORT] = 37 +[PHY_ADDR] = 1 +[MDIO_BUS] = 1 + +[API_PORT] = 38 +[PHY_ADDR] = 2 +[MDIO_BUS] = 1 + +[API_PORT] = 39 +[PHY_ADDR] = 3 +[MDIO_BUS] = 1 + +[API_PORT] = 40 +[PHY_ADDR] = 4 +[MDIO_BUS] = 1 + +[API_PORT] = 41 +[PHY_ADDR] = 5 +[MDIO_BUS] = 1 + +[API_PORT] = 42 +[PHY_ADDR] = 6 +[MDIO_BUS] = 1 + +[API_PORT] = 43 +[PHY_ADDR] = 7 +[MDIO_BUS] = 1 + +[API_PORT] = 24 +[PHY_ADDR] = 8 +[MDIO_BUS] = 1 + +[API_PORT] = 25 +[PHY_ADDR] = 9 +[MDIO_BUS] = 1 + +[API_PORT] = 26 +[PHY_ADDR] = 10 +[MDIO_BUS] = 1 + +[API_PORT] = 27 +[PHY_ADDR] = 11 +[MDIO_BUS] = 1 + +[API_PORT] = 48 +[PHY_ADDR] = 12 +[MDIO_BUS] = 1 + +[API_PORT] = 49 +[PHY_ADDR] = 13 +[MDIO_BUS] = 1 + +[API_PORT] = 50 +[PHY_ADDR] = 14 +[MDIO_BUS] = 1 + +[API_PORT] = 51 +[PHY_ADDR] = 15 +[MDIO_BUS] = 1 + +[API_PORT] = 52 +[PHY_ADDR] = 16 +[MDIO_BUS] = 1 + +[API_PORT] = 53 +[PHY_ADDR] = 17 +[MDIO_BUS] = 1 + +[API_PORT] = 54 +[PHY_ADDR] = 18 +[MDIO_BUS] = 1 + +[API_PORT] = 55 +[PHY_ADDR] = 19 +[MDIO_BUS] = 1 + +[API_PORT] = 56 +[PHY_ADDR] = 20 +[MDIO_BUS] = 1 + +[API_PORT] = 57 +[PHY_ADDR] = 21 +[MDIO_BUS] = 1 + +[API_PORT] = 58 +[PHY_ADDR] = 22 +[MDIO_BUS] = 1 + +[API_PORT] = 59 +[PHY_ADDR] = 23 +[MDIO_BUS] = 1 + +#} diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/platform_asic b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/platform_asic new file mode 100644 index 000000000000..0815c8a78c0b --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/platform_asic @@ -0,0 +1 @@ +centec-arm64 diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/port_config.ini b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/port_config.ini new file mode 100644 index 000000000000..cd9f488c53b2 --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/port_config.ini @@ -0,0 +1,53 @@ +# name lanes alias index speed admin_status +Ethernet1 0 eth-0-1 1 1000 up +Ethernet2 1 eth-0-2 2 1000 up +Ethernet3 2 eth-0-3 3 1000 up +Ethernet4 3 eth-0-4 4 1000 up +Ethernet5 4 eth-0-5 5 1000 up +Ethernet6 5 eth-0-6 6 1000 up +Ethernet7 6 eth-0-7 7 1000 up +Ethernet8 7 eth-0-8 8 1000 up +Ethernet9 16 eth-0-9 9 1000 up +Ethernet10 17 eth-0-10 10 1000 up +Ethernet11 18 eth-0-11 11 1000 up +Ethernet12 19 eth-0-12 12 1000 up +Ethernet13 20 eth-0-13 13 1000 up +Ethernet14 21 eth-0-14 14 1000 up +Ethernet15 22 eth-0-15 15 1000 up +Ethernet16 23 eth-0-16 16 1000 up +Ethernet17 8 eth-0-17 17 1000 up +Ethernet18 9 eth-0-18 18 1000 up +Ethernet19 10 eth-0-19 19 1000 up +Ethernet20 11 eth-0-20 20 1000 up +Ethernet21 32 eth-0-21 21 1000 up +Ethernet22 33 eth-0-22 22 1000 up +Ethernet23 34 eth-0-23 23 1000 up +Ethernet24 35 eth-0-24 24 1000 up +Ethernet25 36 eth-0-25 25 1000 up +Ethernet26 37 eth-0-26 26 1000 up +Ethernet27 38 eth-0-27 27 1000 up +Ethernet28 39 eth-0-28 28 1000 up +Ethernet29 40 eth-0-29 29 1000 up +Ethernet30 41 eth-0-30 30 1000 up +Ethernet31 42 eth-0-31 31 1000 up +Ethernet32 43 eth-0-32 32 1000 up +Ethernet33 24 eth-0-33 33 1000 up +Ethernet34 25 eth-0-34 34 1000 up +Ethernet35 26 eth-0-35 35 1000 up +Ethernet36 27 eth-0-36 36 1000 up +Ethernet37 48 eth-0-37 37 1000 up +Ethernet38 49 eth-0-38 38 1000 up +Ethernet39 50 eth-0-39 39 1000 up +Ethernet40 51 eth-0-40 40 1000 up +Ethernet41 52 eth-0-41 41 1000 up +Ethernet42 53 eth-0-42 42 1000 up +Ethernet43 54 eth-0-43 43 1000 up +Ethernet44 55 eth-0-44 44 1000 up +Ethernet45 56 eth-0-45 45 1000 up +Ethernet46 57 eth-0-46 46 1000 up +Ethernet47 58 eth-0-47 47 1000 up +Ethernet48 59 eth-0-48 48 1000 up +Ethernet49 12 eth-0-49 49 10000 up +Ethernet50 13 eth-0-50 50 10000 up +Ethernet51 14 eth-0-51 51 10000 up +Ethernet52 15 eth-0-52 52 10000 up diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/sai.profile b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/sai.profile new file mode 100644 index 000000000000..0194d4ecdd3a --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/sai.profile @@ -0,0 +1,4 @@ +SAI_INIT_CONFIG_FILE=/usr/share/sonic/hwsku/M2-w6010-48gt4x-ra-p-chip-profile.txt +SAI_HW_PORT_PROFILE_ID_CONFIG_FILE=/usr/share/sonic/hwsku/M2-w6010-48gt4x-ra-p-datapath.txt +SAI_PLATFORM_CFG_FILE=/usr/share/sonic/hwsku/M2-w6010-48gt4x-ra-board.json +SAI_PHY_DRIVER_PATH=/usr/share/sonic/hwsku/phy_drv \ No newline at end of file diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/start_up.cfg b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/start_up.cfg new file mode 100644 index 000000000000..b012adedb252 --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA/start_up.cfg @@ -0,0 +1,48 @@ + +chip set serdes 0 ffe mode user-define c0 0 c1 255 c2 10 c3 0 c4 0 +chip set serdes 1 ffe mode user-define c0 0 c1 240 c2 10 c3 0 c4 0 +chip set serdes 2 ffe mode user-define c0 0 c1 230 c2 10 c3 0 c4 0 +chip set serdes 3 ffe mode user-define c0 0 c1 220 c2 7 c3 0 c4 0 +chip set serdes 4 ffe mode user-define c0 0 c1 220 c2 8 c3 0 c4 0 +chip set serdes 5 ffe mode user-define c0 0 c1 210 c2 6 c3 0 c4 0 +chip set serdes 6 ffe mode user-define c0 0 c1 180 c2 6 c3 0 c4 0 +chip set serdes 7 ffe mode user-define c0 0 c1 180 c2 5 c3 0 c4 0 +chip set serdes 8 ffe mode user-define c0 2 c1 160 c2 4 c3 0 c4 0 +chip set serdes 9 ffe mode user-define c0 2 c1 160 c2 4 c3 0 c4 0 +chip set serdes 10 ffe mode user-define c0 3 c1 140 c2 9 c3 0 c4 0 +chip set serdes 11 ffe mode user-define c0 3 c1 140 c2 5 c3 0 c4 0 +chip set serdes 12 ffe mode user-define c0 3 c1 94 c2 15 c3 0 c4 0 +chip set serdes 13 ffe mode user-define c0 3 c1 94 c2 15 c3 0 c4 0 +chip set serdes 14 ffe mode user-define c0 3 c1 94 c2 15 c3 0 c4 0 +chip set serdes 15 ffe mode user-define c0 3 c1 94 c2 15 c3 0 c4 0 + + +chip set serdes 0 ctle 6 14 2 +chip set serdes 1 ctle 6 14 2 +chip set serdes 2 ctle 6 14 2 +chip set serdes 3 ctle 6 14 2 +chip set serdes 4 ctle 6 14 2 +chip set serdes 5 ctle 6 14 2 +chip set serdes 6 ctle 6 14 2 +chip set serdes 7 ctle 6 14 2 +chip set serdes 8 ctle 6 14 2 +chip set serdes 9 ctle 6 14 2 +chip set serdes 10 ctle 6 14 2 +chip set serdes 11 ctle 6 14 2 + + + +# rtk eye param, post_amp|main_amp|pre_amp +port 0 phy-attr type 2001 value 0x1c1e00 +port 4 phy-attr type 2001 value 0x101600 +port 16 phy-attr type 2001 value 0x111500 +port 20 phy-attr type 2001 value 0x0e1100 +port 8 phy-attr type 2001 value 0x0f1000 +port 32 phy-attr type 2001 value 0x0e0f00 +port 36 phy-attr type 2001 value 0x0a0a00 +port 40 phy-attr type 2001 value 0x0a0900 +port 24 phy-attr type 2001 value 0x000c00 +port 48 phy-attr type 2001 value 0x000b00 +port 52 phy-attr type 2001 value 0x000b00 +port 56 phy-attr type 2001 value 0x000a00 + diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/default_sku b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/default_sku new file mode 100644 index 000000000000..c9a5806d0236 --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/default_sku @@ -0,0 +1 @@ +M2-W6010-48GT4X-RA l1 diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/installer.conf b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/installer.conf new file mode 100644 index 000000000000..6e00c7ff77f1 --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/installer.conf @@ -0,0 +1,9 @@ +CONSOLE_SPEED=115200 + +#fix env +config_env(){ + dd if=/dev/mtd1 of=env.bin + flashcp -v env.bin /dev/mtd4 +} +trap_push "config_env || true" + diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/platform.json b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/platform.json new file mode 100644 index 000000000000..53a47b144c4f --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/platform.json @@ -0,0 +1,173 @@ +{ + "chassis": { + "name": "M2-W6010-48GT4X-RA", + "thermal_manager": false, + "status_led": { + "controllable": false, + "colors": ["green", "blinking_green", "amber", "blinking_amber"] + }, + "components": [ + { + "name": "CPU CPLD" + }, + { + "name": "MAC1 CPLD" + } + ], + "fans": [ + { + "name": "Fantray1_1", + "speed": { + "controllable": true, + "minimum": 30, + "maximum": 99 + }, + "status_led": { + "available": false, + "colors": ["off", "red", "amber", "green"] + } + }, + { + "name": "Fantray2_1", + "speed": { + "controllable": true, + "minimum": 30, + "maximum": 99 + }, + "status_led": { + "available": false, + "colors": ["off", "red", "amber", "green"] + } + } + ], + "fan_drawers":[ + { + "name": "Fantray1", + "num_fans" : 1, + "status_led": { + "controllable": false, + "colors": ["amber", "green", "off"] + }, + "fans": [ + { + "name": "FanTray1_1", + "speed": { + "controllable": true, + "minimum": 30, + "maximum": 99 + }, + "status_led": { + "available": false + } + } + ] + }, + { + "name": "Fantray2", + "num_fans" : 1, + "status_led": { + "controllable": false, + "colors": ["amber", "green", "off"] + }, + "fans": [ + { + "name": "FanTray2_1", + "speed": { + "controllable": true, + "minimum": 30, + "maximum": 99 + }, + "status_led": { + "available": false + } + } + ] + } + ], + "psus": [ + { + "name": "PSU 1", + "voltage": true, + "current": true, + "power": true, + "max_power": false, + "voltage_high_threshold": true, + "voltage_low_threshold": true, + "temperature": true, + "fans_target_speed": false, + "status_led": { + "controllable": false + } + }, + { + "name": "PSU 2", + "voltage": true, + "current": true, + "power": true, + "max_power": false, + "voltage_high_threshold": true, + "voltage_low_threshold": true, + "temperature": true, + "fans_target_speed": false, + "status_led": { + "controllable": false + } + } + ], + "thermals": [ + { + "name": "ASIC_TEMP", + "controllable": false, + "low-crit-threshold": true, + "high-crit-threshold": true, + "low-threshold": true, + "high-threshold": true, + "minimum-recorded": true, + "maximum-recorded": true + }, + { + "name": "CPU_TEMP", + "controllable": false, + "low-crit-threshold": true, + "high-crit-threshold": true, + "low-threshold": true, + "high-threshold": true, + "minimum-recorded": true, + "maximum-recorded": true + }, + { + "name": "INLET_TEMP", + "controllable": false, + "low-crit-threshold": true, + "high-crit-threshold": true, + "low-threshold": true, + "high-threshold": true, + "minimum-recorded": true, + "maximum-recorded": true + }, + { + "name": "OUTLET_TEMP", + "controllable": false, + "low-crit-threshold": true, + "high-crit-threshold": true, + "low-threshold": true, + "high-threshold": true, + "minimum-recorded": true, + "maximum-recorded": true + }, + { + "name": "TPS53688_TEMP", + "controllable": false, + "low-crit-threshold": true, + "high-crit-threshold": true, + "low-threshold": true, + "high-threshold": true, + "minimum-recorded": true, + "maximum-recorded": true + } + ], + "modules": [], + "sfps": [] + }, + "interfaces": {} +} diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/platform_asic b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/platform_asic new file mode 100644 index 000000000000..0815c8a78c0b --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/platform_asic @@ -0,0 +1 @@ +centec-arm64 diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/plugins/sfputil.py b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/plugins/sfputil.py new file mode 100644 index 000000000000..c4fdbe2c3a8c --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/plugins/sfputil.py @@ -0,0 +1,428 @@ +# sfputil.py +# +# Platform-specific SFP transceiver interface for SONiC +# + +try: + import time + import subprocess + import re + import os + import threading + from sonic_sfp.sfputilbase import SfpUtilBase +except ImportError as e: + raise ImportError("%s - required module not found" % str(e)) + + +class SfpUtil(SfpUtilBase): + """Platform-specific SfpUtil class""" + + PORT_START = 49 + PORT_END = 52 + PORTS_IN_BLOCK = 53 + EEPROM_OFFSET = 9 + SFP_DEVICE_TYPE = "optoe2" + QSFP_DEVICE_TYPE = "optoe1" + I2C_MAX_ATTEMPT = 3 + + SFP_STATUS_INSERTED = '1' + SFP_STATUS_REMOVED = '0' + + TXWRT_PROTECT = 0X4E + TXWRT_NO_PROTECT = 0X59 + + _port_to_eeprom_mapping = {} + port_to_i2cbus_mapping ={} + port_dict = {} + port_presence_info = {} + port_reset_info = {} + port_txdis_info = {} + port_txwrt_info = {} + port_led_info = {} + + port_rxlos_info = {} + port_txfault_info = {} + port_drop_info = {} + + @property + def port_start(self): + return self.PORT_START + + @property + def port_end(self): + return self.PORT_END + + @property + def sfp_ports(self): + return list(range(self.PORT_START, self.PORTS_IN_BLOCK)) + + @property + def qsfp_ports(self): + return [] + + @property + def port_to_eeprom_mapping(self): + return self._port_to_eeprom_mapping + + def __init__(self): + for x in range(self.PORT_START, self.PORTS_IN_BLOCK): + self.port_to_i2cbus_mapping[x] = (x - self.PORT_START + self.EEPROM_OFFSET) + self.port_presence_info["/sys/bus/i2c/devices/3-0030/sfp_presence1"] = [49, 50, 51, 52] + self.port_txdis_info["/sys/bus/i2c/devices/3-0030/tx_disable"] = [49, 50, 51, 52] + self.port_txwrt_info["/sys/bus/i2c/devices/3-0030/tx_write_protect"] = [49, 50, 51, 52] + + # bit 1: los + self.port_rxlos_info["/sys/bus/i2c/devices/3-0030/sfp_rx_loss1"] = [49, 50, 51, 52] + + # bit 1: fault + self.port_txfault_info["/sys/bus/i2c/devices/3-0030/sfp_tx_fault1"] = [49, 50, 51, 52] + + # bit 1: drop + self.port_drop_info["/sys/bus/i2c/devices/3-0030/sfp_drop_record1"] = [49, 50, 51, 52] + + SfpUtilBase.__init__(self) + + def _sfp_read_file_path(self, file_path, offset, num_bytes): + attempts = 0 + while attempts < self.I2C_MAX_ATTEMPT: + try: + file_path.seek(offset) + read_buf = file_path.read(num_bytes) + except Exception: + attempts += 1 + time.sleep(0.05) + else: + return True, read_buf + return False, None + + def _sfp_eeprom_present(self, sysfs_sfp_i2c_client_eeprompath, offset): + """Tries to read the eeprom file to determine if the + device/sfp is present or not. If sfp present, the read returns + valid bytes. If not, read returns error 'Connection timed out""" + + if not os.path.exists(sysfs_sfp_i2c_client_eeprompath): + return False + else: + with open(sysfs_sfp_i2c_client_eeprompath, "rb", buffering=0) as sysfsfile: + rv, buf = self._sfp_read_file_path(sysfsfile, offset, 1) + return rv + + def _add_new_sfp_device(self, sysfs_sfp_i2c_adapter_path, devaddr, devtype): + try: + sysfs_nd_path = "%s/new_device" % sysfs_sfp_i2c_adapter_path + + # Write device address to new_device file + nd_file = open(sysfs_nd_path, "w") + nd_str = "%s %s" % (devtype, hex(devaddr)) + nd_file.write(nd_str) + nd_file.close() + + except Exception as err: + print(("Error writing to new device file: %s" % str(err))) + return 1 + else: + return 0 + + def _get_port_eeprom_path(self, port_num, devid): + if port_num in list(self.port_to_eeprom_mapping.keys()): + sysfs_sfp_i2c_client_eeprom_path = self.port_to_eeprom_mapping[port_num] + else: + sysfs_i2c_adapter_base_path = "/sys/class/i2c-adapter" + + i2c_adapter_id = self._get_port_i2c_adapter_id(port_num) + if i2c_adapter_id is None: + print("Error getting i2c bus num") + return None + + # Get i2c virtual bus path for the sfp + sysfs_sfp_i2c_adapter_path = "%s/i2c-%s" % (sysfs_i2c_adapter_base_path, + str(i2c_adapter_id)) + + # If i2c bus for port does not exist + if not os.path.exists(sysfs_sfp_i2c_adapter_path): + print(("Could not find i2c bus %s. Driver not loaded?" % sysfs_sfp_i2c_adapter_path)) + return None + + sysfs_sfp_i2c_client_path = "%s/%s-00%s" % (sysfs_sfp_i2c_adapter_path, + str(i2c_adapter_id), + hex(devid)[-2:]) + + # If sfp device is not present on bus, Add it + if not os.path.exists(sysfs_sfp_i2c_client_path): + if port_num in self.qsfp_ports: + ret = self._add_new_sfp_device( + sysfs_sfp_i2c_adapter_path, devid, self.QSFP_DEVICE_TYPE) + else: + ret = self._add_new_sfp_device( + sysfs_sfp_i2c_adapter_path, devid, self.SFP_DEVICE_TYPE) + if ret != 0: + print("Error adding sfp device") + return None + + sysfs_sfp_i2c_client_eeprom_path = "%s/eeprom" % sysfs_sfp_i2c_client_path + return sysfs_sfp_i2c_client_eeprom_path + + def _read_eeprom_specific_bytes(self, sysfsfile_eeprom, offset, num_bytes): + eeprom_raw = [] + for i in range(0, num_bytes): + eeprom_raw.append("0x00") + + rv, raw = self._sfp_read_file_path(sysfsfile_eeprom, offset, num_bytes) + if rv == False: + return None + + try: + if isinstance(raw, str): + for n in range(0, num_bytes): + eeprom_raw[n] = hex(ord(raw[n]))[2:].zfill(2) + else: + for n in range(0, num_bytes): + eeprom_raw[n] = hex(raw[n])[2:].zfill(2) + except Exception as err: + return None + return eeprom_raw + + def get_eeprom_dom_raw(self, port_num): + if port_num in self.qsfp_ports: + # QSFP DOM EEPROM is also at addr 0x50 and thus also stored in eeprom_ifraw + return None + else: + # Read dom eeprom at addr 0x51 + return self._read_eeprom_devid(port_num, self.IDENTITY_EEPROM_ADDR, 256) + + 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 + + presence_path = None + for presence_key in self.port_presence_info: + if port_num in self.port_presence_info[presence_key]: + presence_path = presence_key + presence_offset = self.port_presence_info[presence_key].index(port_num) + break + if presence_path == None: + return False + + try: + data = open(presence_path, "rb") + except IOError: + return False + + presence_data = data.read(2) + if presence_data == "": + return False + result = int(presence_data, 16) + data.close() + + # ModPrsL is active low + if result & (1 << presence_offset) == 0: + return True + + return False + + def get_low_power_mode(self, port_num): + return False + + def set_low_power_mode(self, port_num, lpmode): + return False + + def reset(self, port_num): + return False + + def reset_all(self): + return False + + def _do_write_file(self, file_handle, offset, value): + file_handle.seek(offset) + file_handle.write(hex(value)) + + def get_transceiver_change_event(self, timeout=0): + + start_time = time.time() + currernt_port_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 + + while timeout >= 0: + # Check for OIR events and return updated port_dict + for x in range(self.PORT_START, self.PORTS_IN_BLOCK): + if self.get_presence(x): + currernt_port_dict[x] = self.SFP_STATUS_INSERTED + else: + currernt_port_dict[x] = self.SFP_STATUS_REMOVED + if (currernt_port_dict == self.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, {} + else: + # Update reg value + self.port_dict = currernt_port_dict + return True, self.port_dict + print ("get_transceiver_change_event: Should not reach here.") + return False, {} + + def tx_disable(self, port_num, disable): + if not self.get_presence(port_num): + return False + + if port_num in self.sfp_ports: + txwrt_path = None + txdis_path = None + txdis_offset = 0 + + for key in self.port_txwrt_info: + if port_num in self.port_txwrt_info[key]: + txwrt_path = key + break + if txwrt_path == None: + return False + + for key in self.port_txdis_info: + if port_num in self.port_txdis_info[key]: + txdis_path = key + txdis_offset = self.port_txdis_info[key].index(port_num) + break + if txdis_path == None: + return False + + + try: + with open(txwrt_path, "r+") as sys_file: + sres = hex(self.TXWRT_NO_PROTECT)[2:] + sys_file.write(sres) + + with open(txdis_path, "r+") as sys_file: + txdis_data = sys_file.read(2) + if not txdis_data: + return False + result = int(txdis_data, 16) + if disable: + result = result | (1 << txdis_offset) + else: + result = result & (~(1 << txdis_offset)) + sys_file.seek(0) + sres = hex(result)[2:] + print(result,sres) + sys_file.write(sres) + + with open(txwrt_path, "r+") as sys_file: + sres = hex(self.TXWRT_PROTECT)[2:] + sys_file.write(sres) + except Exception as err: + print(err) + return False + + return True + else: + return False + +########### sysdiag ########### + def _get_cpld_info(self, port_num, info): + path = None + offset = 0 + for key in info: + if port_num in info[key]: + path = key + offset = info[key].index(port_num) + break + return path, offset + + def get_tx_disable(self, port_num): + # cur only support sfp moudle + if port_num not in self.sfp_ports: + return False + + if not self.get_presence(port_num): + return False + + path, offset = self._get_cpld_info(port_num, self.port_txdis_info) + if path == None: + return False + + result = 0 + try: + with open(path, "r") as sys_file: + data = sys_file.read(2) + result = int(data, 16) + except Exception as e: + print((str(e))) + return False + + # 1: disable + if result & (1 << offset): + return True + else: + return False + + def get_rx_los(self, port_num): + # cur only support sfp moudle + if port_num not in self.sfp_ports: + return False + + path, offset = self._get_cpld_info(port_num, self.port_rxlos_info) + if path == None: + return False + + result = 0 + try: + with open(path, "r") as sys_file: + data = sys_file.read(2) + result = int(data, 16) + except Exception as e: + print((str(e))) + return False + + # 1: los + if result & (1 << offset): + return True + else: + return False + + def get_tx_fault(self, port_num): + # cur only support sfp moudle + if port_num not in self.sfp_ports: + return False + + if not self.get_presence(port_num): + return False + + path, offset = self._get_cpld_info(port_num, self.port_txfault_info) + if path == None: + return False + + result = 0 + try: + with open(path, "r") as sys_file: + data = sys_file.read(2) + result = int(data, 16) + except Exception as e: + print((str(e))) + return False + + # 1: fault + if result & (1 << offset): + return True + + return False diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/plugins/ssd_util.py b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/plugins/ssd_util.py new file mode 100644 index 000000000000..95ca23e8b351 --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/plugins/ssd_util.py @@ -0,0 +1,112 @@ +# +# ssd_util.py +# +# Generic implementation of the SSD health API +# SSD models supported: +# - InnoDisk +# - StorFly +# - Virtium + +try: + import subprocess + from sonic_platform_base.sonic_ssd.ssd_base import SsdBase +except ImportError as e: + raise ImportError (str(e) + "- required module not found") + +HEALTH_CMD = "cat /sys/kernel/debug/mmc0/mmc0:0001/ext_csd | cut -c 537-538" +SERIAL_CMD = "cat /sys/bus/mmc/devices/mmc0\\:0001/serial" +FIRMWARE_CMD = "cat /sys/kernel/debug/mmc0/mmc0:0001/ext_csd | cut -c 509-522" +NOT_AVAILABLE = "N/A" + +class SsdUtil(SsdBase): + """ + Generic implementation of the SSD health API + """ + def __init__(self, diskdev): + self.model = "KLMCG4JETD-B041" + self.temperature = NOT_AVAILABLE + self.vendor_ssd_info = "====No vendor information====" + self.health_list = [100,90,80,70,60,50,40,30,20,10,0] + try: + life_time = self._execute_shell(HEALTH_CMD) + if int(life_time) in range(1,12): + self.health = self.health_list[int(life_time) - 1] + else: + self.health = NOT_AVAILABLE + except Exception as e: + self.health = NOT_AVAILABLE + + try: + self.firmware = self._execute_shell(FIRMWARE_CMD) + except Exception as e: + self.firmware = NOT_AVAILABLE + + try: + serial = self._execute_shell(SERIAL_CMD) + self.serial = serial.replace("0x",'') + except Exception as e: + self.serial = NOT_AVAILABLE + + def _execute_shell(self, cmd): + status, output = subprocess.getstatusoutput(cmd) + if status: + return None + + return output + + def get_health(self): + """ + Retrieves current disk health in percentages + + Returns: + A float number of current ssd health + e.g. 83.5 + """ + return self.health + + def get_temperature(self): + """ + Retrieves current disk temperature in Celsius + + Returns: + A float number of current temperature in Celsius + e.g. 40.1 + """ + return self.temperature + + def get_model(self): + """ + Retrieves model for the given disk device + + Returns: + A string holding disk model as provided by the manufacturer + """ + return self.model + + def get_firmware(self): + """ + Retrieves firmware version for the given disk device + + Returns: + A string holding disk firmware version as provided by the manufacturer + """ + return self.firmware + + def get_serial(self): + """ + Retrieves serial number for the given disk device + + Returns: + A string holding disk serial number as provided by the manufacturer + """ + return self.serial + + def get_vendor_output(self): + """ + Retrieves vendor specific data for the given disk device + + Returns: + A string holding some vendor specific disk information + """ + return self.vendor_ssd_info + diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/pmon_daemon_control.json b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/pmon_daemon_control.json new file mode 100644 index 000000000000..5fdf83f1b9e6 --- /dev/null +++ b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/pmon_daemon_control.json @@ -0,0 +1,7 @@ +{ + "skip_ledd": true, + "skip_xcvrd": false, + "skip_syseepromd": false, + "skip_thermalctld": false, + "skip_psud": false +} diff --git a/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/system_health_monitoring_config.json b/device/micas/arm64-micas_m2-w6010-48gt4x-ra-r0/system_health_monitoring_config.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/files/image_config/platform/rc.local b/files/image_config/platform/rc.local index d0abf78ca9b0..7d32efa5f567 100755 --- a/files/image_config/platform/rc.local +++ b/files/image_config/platform/rc.local @@ -186,7 +186,7 @@ value_extract() { program_console_speed() { - speed=$(cat /proc/cmdline | grep -Eo 'console=ttyS[0-9]+,[0-9]+' | cut -d "," -f2) + speed=$(cat /proc/cmdline | grep -Eo 'console=tty.*[0-9],[0-9]+' | cut -d "," -f2) if [ -z "$speed" ]; then CONSOLE_SPEED=9600 else diff --git a/platform/centec-arm64/one-image.mk b/platform/centec-arm64/one-image.mk index 87fd7627b5e4..8c7216bef385 100755 --- a/platform/centec-arm64/one-image.mk +++ b/platform/centec-arm64/one-image.mk @@ -10,6 +10,8 @@ $(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(CENTEC_E530_48T4X_P_PLATFORM_MODULE) $(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(CENTEC_E530_24X2C_PLATFORM_MODULE) $(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(CENTEC_E530_48S4X_PLATFORM_MODULE) $(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(CENTEC_E530_24X2Q_PLATFORM_MODULE) +$(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(MICAS_M2_W6010_48GT4X_PLATFORM_MODULE) +$(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(MICAS_M2_W6010_48GT4X_R_PLATFORM_MODULE) ifeq ($(INSTALL_DEBUG_TOOLS),y) $(SONIC_ONE_IMAGE)_DOCKERS += $(SONIC_INSTALL_DOCKER_DBG_IMAGES) diff --git a/platform/centec-arm64/platform-modules-micas.dep b/platform/centec-arm64/platform-modules-micas.dep new file mode 100644 index 000000000000..ad741d08d3d6 --- /dev/null +++ b/platform/centec-arm64/platform-modules-micas.dep @@ -0,0 +1,9 @@ +MPATH := $($(MICAS_M2_W6010_48GT4X_PLATFORM_MODULE)_SRC_PATH) +DEP_FILES := $(SONIC_COMMON_FILES_LIST) platform/centec-arm64/platform-modules-micas.mk platform/centec-arm64/platform-modules-micas.dep +DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST) +DEP_FILES += $(addprefix $(MPATH)/,$(shell cd $(MPATH) && git ls-files)) + +$(MICAS_M2_W6010_48GT4X_PLATFORM_MODULE)_CACHE_MODE := GIT_CONTENT_SHA +$(MICAS_M2_W6010_48GT4X_PLATFORM_MODULE)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST) +$(MICAS_M2_W6010_48GT4X_PLATFORM_MODULE)_DEP_FILES := $(DEP_FILES) + diff --git a/platform/centec-arm64/platform-modules-micas.mk b/platform/centec-arm64/platform-modules-micas.mk new file mode 100644 index 000000000000..b0784f4f98cb --- /dev/null +++ b/platform/centec-arm64/platform-modules-micas.mk @@ -0,0 +1,15 @@ +## M2-W6010-48GT4X +MICAS_M2_W6010_48GT4X_PLATFORM_MODULE_VERSION =1.3 +MICAS_M2_W6010_48GT4X_R_PLATFORM_MODULE_VERSION =1.3 +export MICAS_M2_W6010_48GT4X_PLATFORM_MODULE_VERSION + +MICAS_M2_W6010_48GT4X_PLATFORM_MODULE = platform-modules-micas-m2-w6010-48gt4x-fa_$(MICAS_M2_W6010_48GT4X_PLATFORM_MODULE_VERSION)_arm64.deb +$(MICAS_M2_W6010_48GT4X_PLATFORM_MODULE)_SRC_PATH = $(PLATFORM_PATH)/sonic-platform-modules-micas +$(MICAS_M2_W6010_48GT4X_PLATFORM_MODULE)_PLATFORM = arm64-micas_m2-w6010-48gt4x-fa-r0 +$(MICAS_M2_W6010_48GT4X_PLATFORM_MODULE)_DEPENDS += $(LINUX_HEADERS) $(LINUX_HEADERS_COMMON) +SONIC_DPKG_DEBS += $(MICAS_M2_W6010_48GT4X_PLATFORM_MODULE) + +# M2-W6010-48GT4X-R +MICAS_M2_W6010_48GT4X_R_PLATFORM_MODULE = platform-modules-micas-m2-w6010-48gt4x-ra_$(MICAS_M2_W6010_48GT4X_R_PLATFORM_MODULE_VERSION)_arm64.deb +$(MICAS_M2_W6010_48GT4X_R_PLATFORM_MODULE)_PLATFORM = arm64-micas_m2-w6010-48gt4x-ra-r0 +$(eval $(call add_extra_package,$(MICAS_M2_W6010_48GT4X_PLATFORM_MODULE),$(MICAS_M2_W6010_48GT4X_R_PLATFORM_MODULE))) diff --git a/platform/centec-arm64/platform.conf b/platform/centec-arm64/platform.conf index 09cf9be6e8cc..9241088c8858 100755 --- a/platform/centec-arm64/platform.conf +++ b/platform/centec-arm64/platform.conf @@ -19,7 +19,7 @@ bootloader_menu_config() { fw_setenv -f linuxargs "${extra_cmdline_linux}" fw_setenv -f nos_bootcmd "test -n \$boot_once && setenv do_boot_once \$boot_once && setenv boot_once && saveenv && run do_boot_once; run boot_next" - fw_setenv -f sonic_image_1 "ext4load mmc 0:1 \$loadaddr \$sonic_dir_1/boot/sonic_arm64.fit && setenv bootargs quiet console=\$consoledev,\$baudrate root=/dev/mmcblk0p1 rw rootwait rootfstype=ext4 loopfstype=squashfs loop=\$sonic_dir_1/fs.squashfs systemd.unified_cgroup_hierarchy=0 \${linuxargs} && bootm \$loadaddr" + fw_setenv -f sonic_image_1 "ext4load mmc 0:1 \$loadaddr \$sonic_dir_1/boot/sonic_arm64.fit && setenv bootargs quiet console=\$consoledev,\$baudrate root=/dev/mmcblk0p1 rw rootwait rootfstype=ext4 loopfstype=squashfs loop=\$sonic_dir_1/fs.squashfs systemd.unified_cgroup_hierarchy=0 \${linuxargs} && bootm \$loadaddr#\$onie_platform" fw_setenv -f sonic_image_2 "NONE" fw_setenv -f sonic_dir_1 $image_dir fw_setenv -f sonic_dir_2 "NONE" @@ -41,7 +41,7 @@ bootloader_menu_config() { fw_setenv linuxargs "${extra_cmdline_linux}" fw_setenv nos_bootcmd "test -n \$boot_once && setenv do_boot_once \$boot_once && setenv boot_once && saveenv && run do_boot_once; run boot_next" - fw_setenv sonic_image_$idx "ext4load mmc 0:1 \$loadaddr \$sonic_dir_$idx/boot/sonic_arm64.fit && setenv bootargs quiet console=\$consoledev,\$baudrate root=/dev/mmcblk0p1 rw rootwait rootfstype=ext4 loopfstype=squashfs loop=\$sonic_dir_$idx/fs.squashfs systemd.unified_cgroup_hierarchy=0 \${linuxargs} && bootm \$loadaddr" + fw_setenv sonic_image_$idx "ext4load mmc 0:1 \$loadaddr \$sonic_dir_$idx/boot/sonic_arm64.fit && setenv bootargs quiet console=\$consoledev,\$baudrate root=/dev/mmcblk0p1 rw rootwait rootfstype=ext4 loopfstype=squashfs loop=\$sonic_dir_$idx/fs.squashfs systemd.unified_cgroup_hierarchy=0 \${linuxargs} && bootm \$loadaddr#\$onie_platform" fw_setenv sonic_dir_$idx $image_dir fw_setenv sonic_version_$idx `echo $image_dir | sed "s/^image-/SONiC-OS-/g"` diff --git a/platform/centec-arm64/rules.mk b/platform/centec-arm64/rules.mk index 9873e8b31ceb..606fb1deefb6 100755 --- a/platform/centec-arm64/rules.mk +++ b/platform/centec-arm64/rules.mk @@ -6,6 +6,7 @@ include $(PLATFORM_PATH)/one-image.mk include $(PLATFORM_PATH)/libsaithrift-dev.mk include $(PLATFORM_PATH)/tsingma-bsp.mk include $(PLATFORM_PATH)/platform-modules-centec-e530.mk +include $(PLATFORM_PATH)/platform-modules-micas.mk SONIC_ALL += $(SONIC_ONE_IMAGE) \ $(DOCKER_FPM) diff --git a/platform/centec-arm64/sai.mk b/platform/centec-arm64/sai.mk index a6fd05c858c0..cf993835f392 100755 --- a/platform/centec-arm64/sai.mk +++ b/platform/centec-arm64/sai.mk @@ -1,9 +1,8 @@ # Centec SAI -export CENTEC_SAI_VERSION = 1.9.1-0 +export CENTEC_SAI_VERSION = 1.10.2-1 export CENTEC_SAI = libsai_$(CENTEC_SAI_VERSION)_$(PLATFORM_ARCH).deb -$(CENTEC_SAI)_URL = https://github.com/CentecNetworks/sonic-binaries/raw/master/$(PLATFORM_ARCH)/sai/$(CENTEC_SAI) -$(eval $(call add_conflict_package,$(CENTEC_SAI),$(LIBSAIVS_DEV))) +$(CENTEC_SAI)_URL = https://raw.githubusercontent.com/philo-micas/sonic-binaries/main/centec-arm64/sai/$(CENTEC_SAI) SONIC_ONLINE_DEBS += $(CENTEC_SAI) diff --git a/platform/centec-arm64/sonic-platform-modules-micas/LICENSE b/platform/centec-arm64/sonic-platform-modules-micas/LICENSE new file mode 100644 index 000000000000..2d14d2b7785f --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/LICENSE @@ -0,0 +1,15 @@ +Copyright (C) 2019 Centec, Inc + +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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. diff --git a/platform/centec-arm64/sonic-platform-modules-micas/README.md b/platform/centec-arm64/sonic-platform-modules-micas/README.md new file mode 100644 index 000000000000..6b907de17378 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/README.md @@ -0,0 +1 @@ +platform drivers for Centec E530 for the SONiC project diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/Makefile b/platform/centec-arm64/sonic-platform-modules-micas/common/Makefile new file mode 100644 index 000000000000..47f99c941f77 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/Makefile @@ -0,0 +1,50 @@ +PWD = $(shell pwd) +CC ?=gcc +INSTALL_MOD_DIR ?=extra +KVERSION ?= $(shell uname -r) +KERNEL_SRC ?= /lib/modules/$(KVERSION) +EXTRA_CFLAGS:= -I$(M)/include +EXTRA_CFLAGS+= -Wall +ifdef ENABLE_GCOV +ifeq ($(ENABLE_GCOV), y) +EXTRA_CFLAGS+= -fprofile-arcs -ftest-coverage -lgcov +endif +endif # ENABLE_GCOV +SUB_BUILD_DIR = $(PWD)/build +DIR_KERNEL_SRC = $(PWD)/modules +SCRIPT_DIR = $(PWD)/script +SERVICE_DIR = $(PWD)/service +BLACK_DRIVER_CONF_DIR = $(PWD)/modprobe_conf + +app_dir = $(PWD)/app +app_build_dir = $(app_dir)/build +modules_build_dir = $(DIR_KERNEL_SRC)/build + +INSTALL_MODULE_DIR = $(SUB_BUILD_DIR)/$(KERNEL_SRC)/$(INSTALL_MOD_DIR) +INSTALL_SCRIPT_DIR = $(SUB_BUILD_DIR)/usr/local/bin +INSTALL_SERVICE_DIR = $(SUB_BUILD_DIR)/lib/systemd/system +INSTALL_LIB_DIR = $(SUB_BUILD_DIR)/usr/lib/python3.9/dist-packages +INSTALL_BLACK_DRIVER = $(SUB_BUILD_DIR)/etc/modprobe.d +CTC_48GT4X_PHY_DIR = $(SUB_BUILD_DIR)/usr/share/sonic/device/arm64-micas_m2-w6010-48gt4x-fa-r0/M2-W6010-48GT4X-FA +CTC_48GT4X_R_PHY_DIR = $(SUB_BUILD_DIR)/usr/share/sonic/device/arm64-micas_m2-w6010-48gt4x-ra-r0/M2-W6010-48GT4X-RA + +all: + $(MAKE) -C $(app_dir) + $(MAKE) -C $(DIR_KERNEL_SRC) + @if [ ! -d ${INSTALL_MODULE_DIR} ]; then mkdir -p ${INSTALL_MODULE_DIR} ;fi + @if [ ! -d ${INSTALL_SCRIPT_DIR} ]; then mkdir -p ${INSTALL_SCRIPT_DIR} ;fi + @if [ ! -d ${INSTALL_SERVICE_DIR} ]; then mkdir -p ${INSTALL_SERVICE_DIR} ;fi + @if [ ! -d ${CTC_48GT4X_PHY_DIR} ]; then mkdir -p ${CTC_48GT4X_PHY_DIR}/phy_drv ;fi + @if [ ! -d ${CTC_48GT4X_R_PHY_DIR} ]; then mkdir -p ${CTC_48GT4X_R_PHY_DIR}/phy_drv ;fi + @if [ ! -d ${INSTALL_LIB_DIR} ]; then mkdir -p ${INSTALL_LIB_DIR} ;fi + @if [ -d $(PWD)/lib/ ]; then cp -r $(PWD)/lib/* ${INSTALL_LIB_DIR} ;fi + cp -r $(app_dir)/*.so $(CTC_48GT4X_PHY_DIR)/phy_drv + cp -r $(app_dir)/*.so $(CTC_48GT4X_R_PHY_DIR)/phy_drv + cp -r $(app_build_dir)/module/*.ko $(INSTALL_MODULE_DIR) + cp -r $(modules_build_dir)/*.ko $(INSTALL_MODULE_DIR) + cp -r $(app_dir)/build/app/* $(INSTALL_SCRIPT_DIR) + if [ -d $(SCRIPT_DIR) ]; then cp -r $(SCRIPT_DIR)/* $(INSTALL_SCRIPT_DIR) ;fi + if [ -d $(SERVICE_DIR) ]; then cp -r $(SERVICE_DIR)/* $(INSTALL_SERVICE_DIR) ;fi + @if [ -d $(INSTALL_SCRIPT_DIR) ]; then chmod +x $(INSTALL_SCRIPT_DIR)/* ;fi +clean: + rm -rf $(SUB_BUILD_DIR) diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/Makefile b/platform/centec-arm64/sonic-platform-modules-micas/common/app/Makefile new file mode 100644 index 000000000000..2d86cdf4c429 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/Makefile @@ -0,0 +1,26 @@ +pes_parent_dir:=$(shell pwd)/$(lastword $(MAKEFILE_LIST)) +pes_parent_dir:=$(shell dirname $(pes_parent_dir)) + +SUBDIRS=$(shell ls -l | grep ^d | awk '{if($$9 != "build") print $$9}') +INC = -I./inc + +COMMON_OUT_PUT := $(shell pwd)/build +common_out_put_dir := $(COMMON_OUT_PUT)/app +common_module_dir := $(COMMON_OUT_PUT)/module/ +export common_out_put_dir common_module_dir + +all : CHECK $(SUBDIRS) +CHECK : + @echo $(pes_parent_dir) + +$(SUBDIRS):ECHO +# make all + #@echo $@ + make -C $@ + +ECHO: + @echo $(SUBDIRS) + +.PHONY : clean +clean : + -rm -rf $(COMMON_OUT_PUT) diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/Makefile b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/Makefile new file mode 100644 index 000000000000..448b52f44f21 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/Makefile @@ -0,0 +1,20 @@ +top_srcdir:= $(shell pwd) +include $(top_srcdir)/Rules.mk +export top_srcdir + +firmware-y:= +firmware-y += firmware_driver_ispvme +firmware-y += firmware_upgrade_ispvme + +.PHONY: all +all: build + +.PHONY: build +build: $(firmware-y) +$(foreach dir,$(firmware-y),$(eval $(call compile_dirs,$(dir)))) + +.PHONY: rpmpkg +rpmpkg: +ifeq ("$(CONFIG_CPLD_UPGRADE_ISPVME)", "y") + #$(RPMPKG) $(install_cpld_dir) firmware-cpld-ispvme.spec git +endif diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/Rules.mk b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/Rules.mk new file mode 100644 index 000000000000..c3423986747b --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/Rules.mk @@ -0,0 +1,45 @@ +CC ?= $(CROSS)gcc +AR ?= $(CROSS)ar +AS ?= $(CROSS)as +LD ?= $(CROSS)ld +STRIP ?= $(CROSS)strip + +install_root:=${top_srcdir}/images + +install_header_dir:=${install_root}/header +install_adir:=$(install_root)/lib +install_symbol_dir:=$(install_root)/symbol +# symbol_files +symbol_files:=$(shell find $(EXPORT_SYMBOL) -name 'Module.symvers') +# +# symbol_files += $(shell find $(install_symbol_dir) -name 'Module.symvers') + +# top root: install_rootfs_dir +# install_rootfs_dir +install_rootfs_dir:=$(install_root)/rootfs + +# install_sodir +install_sodir:=$(install_rootfs_dir)/$(INSTALL_SODIR) + +install_usr_bin_dir:=$(install_rootfs_dir)/usr/bin +# install_sbin_dir +install_sbin_dir:=$(install_rootfs_dir)/sbin +install_etc_dir:=$(install_rootfs_dir)/etc + +export INSTALL_MOD_PATH:=$(ROOT) + +BUILD_CFLAGS:=$(CFLAGS) -I$(install_header_dir) +BUILD_LDFLAGS:=$(LDFLAGS) -L/$(install_sodir) -L/$(install_adir) + +define compile_dirs +.PHONY: $(1) +$(1): + @echo;echo "building $(1)..." + @$(MAKE) -C ${1} +endef + +compile.c = $(CC) $(BUILD_CFLAGS) -d -c -o $@ $< + +%.o: %.c + $(compile.c) + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware-cpld-ispvme.spec b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware-cpld-ispvme.spec new file mode 100644 index 000000000000..3a2271157613 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware-cpld-ispvme.spec @@ -0,0 +1,25 @@ +Summary: Firmware Upgrade Package + +Name: firmware-cpld + +Version: 1.0.0.%patch + +Release: firmware + +License: Micas + +Group: common + +AutoReqProv: no + +%clean +%description +[common] +%des_name +%des_ver +%des_target + +%files + +%attr(755,root,root) +/* diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/Makefile b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/Makefile new file mode 100644 index 000000000000..96c7910269f3 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/Makefile @@ -0,0 +1,22 @@ +PWD = $(shell pwd) + +EXTRA_CFLAGS:= -I$(M)/include +EXTRA_CFLAGS+= -Wall +ifdef ENABLE_GCOV +ifeq ($(ENABLE_GCOV), y) +EXTRA_CFLAGS+= -fprofile-arcs -ftest-coverage -lgcov +endif +endif # ENABLE_GCOV +PLATFORM_PRODUCT = config_gpio +firmware_driver-objs := firmware_ispvme.o +firmware_driver-objs += firmware_cpld_ispvme.o firmware_cpld_upgrade_ispvme.o + +#ifndef CONFIG_FRM_PRODUCT_FILE + +firmware_driver-objs += $(PLATFORM_PRODUCT).o +$(warning $(firmware_driver-objs)) +obj-m := firmware_driver.o +all: + $(MAKE) -C $(KERNEL_SRC)/build M=$(PWD) modules + @if [ ! -d $(common_module_dir) ]; then mkdir -p $(common_module_dir) ;fi + cp -p $(PWD)/*.ko $(common_module_dir) \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/config_gpio.c b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/config_gpio.c new file mode 100755 index 000000000000..ccd0a565c4e8 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/config_gpio.c @@ -0,0 +1,473 @@ +/* + * config_gpio.c + * Original Author : support 2013-10-25 + * + * gpio cpld driver + * + * History + * v1.0 support 2013-10-25 Initial version. + * + */ +#include +#include +#include +#include +#include +#include "config_gpio.h" + +/* extern void cmic_gpio_set_output_value(int gpio, int val); */ +firmware_device_info_t *current_info = NULL; +static firmware_device_info_t set_gpio_info; +static int set_gpio_info_flag = 0; +firmware_device_info_t default_cpld_info = { + .type = 0, + .tdi = JTAG_TDI, + .tck = JTAG_TCK, + .tms = JTAG_TMS, + .tdo = JTAG_TDO, + .jtag_en = JTAG_EN, + .select = -1, + .cmic_start_gpio = -1, + .cmic_end_gpio = -1, +}; + +static int firmware_cpld_gpio_is_from_cmic(int gpio) +{ + if (current_info == NULL) { + return -1; + } + + dev_debug(firmware_debug(), "gpio %d current_info.cmic_start_gpio %d current_info.cmic_end_gpio %d.\n", + gpio, current_info->cmic_start_gpio, current_info->cmic_end_gpio); + + if ((current_info->cmic_start_gpio == -1) || (current_info->cmic_end_gpio == -1)) { + return 0; + } + + if ((gpio >= current_info->cmic_start_gpio) && (gpio <= current_info->cmic_end_gpio)) { + return 1; + } + + return 0; +} + +static int firmware_cpld_gpio_get_value(int gpio) +{ +#if 0 + int ret; + + ret = firmware_cpld_gpio_is_from_cmic(gpio); + if (ret < 0) { + dev_debug(firmware_debug(), "firmware_cpld_gpio_is_from_cmic gpio %d failed ret %d.\n", + gpio, ret); + return -1; + } + + if (ret == 1) { + /* Not currently supported */ + dev_debug(firmware_debug(), "gpio %d not support to get value.\n", gpio); + return -1; + } else { + return __gpio_get_value(gpio); + } +#endif + + return __gpio_get_value(gpio); +} + +static void firmware_cpld_gpio_set_output_value(int gpio, int val) +{ +#if 0 + int ret; + + ret = firmware_cpld_gpio_is_from_cmic(gpio); + if (ret < 0) { + dev_debug(firmware_debug(), "firmware_cpld_gpio_is_from_cmic gpio %d failed ret %d.\n", + gpio, ret); + return; + } + + if (ret == 1) { + __gpio_set_value(gpio, val); + /*cmic_gpio_set_output_value(gpio, val);*/ + } else { + __gpio_set_value(gpio, val); + } +#endif + + __gpio_set_value(gpio, val); +} + +static void firmware_cpld_gpio_set_direction(int gpio, int out) +{ + if (out) { + gpio_direction_output(gpio, 1); + } else { + gpio_direction_input(gpio); + } + + return; +} + +static void firmware_cpld_gpio_request(int gpio, char *name) +{ + int ret; + + ret = firmware_cpld_gpio_is_from_cmic(gpio); + if (ret < 0) { + dev_debug(firmware_debug(), "firmware_cpld_gpio_is_from_cmic gpio %d failed ret %d.\n", + gpio, ret); + return; + } + + if (ret == 1) { + /* do nothing */ + } else { + gpio_request(gpio, name); + } + + return; +} + +static void firmware_cpld_gpio_free(int gpio) +{ + int ret; + + ret = firmware_cpld_gpio_is_from_cmic(gpio); + if (ret < 0) { + dev_debug(firmware_debug(), "firmware_cpld_gpio_is_from_cmic gpio %d failed ret %d.\n", + gpio, ret); + return; + } + + if (ret == 1) { + /* do nothing */ + } else { + gpio_free(gpio); + } + + return; +} + +/* CPLD upgrade initialization operation */ +static int init_cpld(void) +{ + if (current_info == NULL) { + return -1; + } + + firmware_cpld_gpio_request(current_info->tdi, "cpld_upgrade"); + firmware_cpld_gpio_request(current_info->tck, "cpld_upgrade"); + firmware_cpld_gpio_request(current_info->tms, "cpld_upgrade"); + firmware_cpld_gpio_request(current_info->jtag_en, "cpld_upgrade"); + if (current_info->select >= 0) { + firmware_cpld_gpio_request(current_info->select, "cpld_upgrade"); + } + firmware_cpld_gpio_request(current_info->tdo, "cpld_upgrade"); + if (current_info->jtag_4.pin >= 0){ + firmware_cpld_gpio_request(current_info->jtag_1.pin, "cpld_upgrade"); + firmware_cpld_gpio_request(current_info->jtag_2.pin, "cpld_upgrade"); + firmware_cpld_gpio_request(current_info->jtag_3.pin, "cpld_upgrade"); + firmware_cpld_gpio_request(current_info->jtag_4.pin, "cpld_upgrade"); + firmware_cpld_gpio_request(current_info->jtag_5.pin, "cpld_upgrade"); + } + return 0; +} + +/* CPLD upgrade completion operation */ +static int finish_cpld(void) +{ + if (current_info == NULL) { + return -1; + } + + firmware_cpld_gpio_set_output_value(current_info->jtag_en, 0); + if (current_info->select >= 0) { + firmware_cpld_gpio_set_output_value(current_info->select, 0); + } + if (current_info->jtag_4.pin >= 0) { + gpio_direction_input(current_info->jtag_4.pin); + gpio_direction_input(current_info->jtag_1.pin); + gpio_direction_input(current_info->jtag_2.pin); + gpio_direction_input(current_info->jtag_3.pin); + gpio_direction_input(current_info->jtag_5.pin); + } + firmware_cpld_gpio_free(current_info->tdi); + firmware_cpld_gpio_free(current_info->tck); + firmware_cpld_gpio_free(current_info->tms); + firmware_cpld_gpio_free(current_info->jtag_en); + firmware_cpld_gpio_free(current_info->tdo); + if (current_info->jtag_4.pin >= 0) { + firmware_cpld_gpio_free(current_info->jtag_1.pin); + firmware_cpld_gpio_free(current_info->jtag_2.pin); + firmware_cpld_gpio_free(current_info->jtag_3.pin); + firmware_cpld_gpio_free(current_info->jtag_4.pin); + firmware_cpld_gpio_free(current_info->jtag_5.pin); + } + + if (current_info->select >= 0) { + firmware_cpld_gpio_free(current_info->select); + } + + if (set_gpio_info_flag == 1) { + memcpy(current_info, &set_gpio_info, sizeof(firmware_device_info_t)); + set_gpio_info_flag = 0; + } + + dev_debug(firmware_debug(), "%s %d\n", __func__, __LINE__); + + return 0; +} + +static void init_chip_pre(void) +{ + + dev_debug(firmware_debug(), "%s %d\n", __func__, __LINE__); + + /* to be reset every time when upgrade,solve the use of MAC side GPIO, + During the startup process, the MAC terminal will be reset, causing the problem of invalid settings */ + firmware_cpld_gpio_set_direction(current_info->tdi, 1); + firmware_cpld_gpio_set_direction(current_info->tck, 1); + firmware_cpld_gpio_set_direction(current_info->tms, 1); + firmware_cpld_gpio_set_direction(current_info->jtag_en, 1); + if (current_info->select >= 0) { + firmware_cpld_gpio_set_direction(current_info->select, 1); + } + if (current_info->jtag_4.pin >= 0) { + firmware_cpld_gpio_set_direction(current_info->jtag_4.pin, 1); + firmware_cpld_gpio_set_direction(current_info->jtag_3.pin, 1); + firmware_cpld_gpio_set_direction(current_info->jtag_2.pin, 1); + firmware_cpld_gpio_set_direction(current_info->jtag_1.pin, 1); + firmware_cpld_gpio_set_direction(current_info->jtag_5.pin, 1); + } + + firmware_cpld_gpio_set_output_value(current_info->tdi, 1); + firmware_cpld_gpio_set_output_value(current_info->tck, 1); + firmware_cpld_gpio_set_output_value(current_info->tms, 1); + firmware_cpld_gpio_set_output_value(current_info->jtag_en, 1); + if (current_info->jtag_4.pin >= 0) { + firmware_cpld_gpio_set_output_value(current_info->jtag_1.pin, current_info->jtag_1.val); + firmware_cpld_gpio_set_output_value(current_info->jtag_2.pin, current_info->jtag_2.val); + firmware_cpld_gpio_set_output_value(current_info->jtag_3.pin, current_info->jtag_3.val); + firmware_cpld_gpio_set_output_value(current_info->jtag_4.pin, current_info->jtag_4.val); + firmware_cpld_gpio_set_output_value(current_info->jtag_5.pin, current_info->jtag_5.val); + } + if (current_info->select >= 0) { + firmware_cpld_gpio_set_output_value(current_info->select, 1); + } + + firmware_cpld_gpio_set_direction(current_info->tdo, 0); + return; +} + +static int init_chip(int slot) +{ + + dev_debug(firmware_debug(), "%s %d\n", __func__, __LINE__); + + if (current_info == NULL) { + return -1; + } + init_chip_pre(); + + dev_debug(firmware_debug(), "tdi %d %d\n",current_info->tdi, firmware_cpld_gpio_get_value(current_info->tdi)); + dev_debug(firmware_debug(), "tdo %d %d\n",current_info->tdo, firmware_cpld_gpio_get_value(current_info->tdo)); + dev_debug(firmware_debug(), "tck %d %d\n",current_info->tck, firmware_cpld_gpio_get_value(current_info->tck)); + dev_debug(firmware_debug(), "tms %d %d\n",current_info->tms, firmware_cpld_gpio_get_value(current_info->tms)); + dev_debug(firmware_debug(), " jtag_en:%d %d\n",current_info->jtag_en, firmware_cpld_gpio_get_value(current_info->jtag_en)); + if (current_info->select >= 0) { + dev_debug(firmware_debug(), " select:%d %d\n",current_info->select, firmware_cpld_gpio_get_value(current_info->select)); + } + + return 0; +} + +static int finish_chip(int slot) +{ + if (current_info == NULL) { + return -1; + } + + firmware_cpld_gpio_set_output_value(current_info->jtag_en, 0); + if (current_info->select >= 0) { + firmware_cpld_gpio_set_output_value(current_info->select, 0); + } + if (current_info->jtag_4.pin >= 0) { + firmware_cpld_gpio_set_output_value(current_info->jtag_4.pin, 1); + } + + return 0; +} + +/* TDI pull up */ +static void pull_tdi_up(void) +{ + if (current_info == NULL) { + return; + } + firmware_cpld_gpio_set_output_value(current_info->tdi, 1); +} + +/* TDI pull dowm */ +static void pull_tdi_down(void) +{ + if (current_info == NULL) { + return; + } + firmware_cpld_gpio_set_output_value(current_info->tdi, 0); +} + +/* TCK pull up */ +static void pull_tck_up(void) +{ + if (current_info == NULL) { + return; + } + firmware_cpld_gpio_set_output_value(current_info->tck, 1); +} + +/* TCK pull down */ +static void pull_tck_down(void) +{ + if (current_info == NULL) { + return; + } + firmware_cpld_gpio_set_output_value(current_info->tck, 0); +} + +/* TMS pull up */ +static void pull_tms_up(void) +{ + if (current_info == NULL) { + return; + } + firmware_cpld_gpio_set_output_value(current_info->tms, 1); +} + +/* TCK pull dowm */ +static void pull_tms_down(void) +{ + if (current_info == NULL) { + return; + } + firmware_cpld_gpio_set_output_value(current_info->tms, 0); +} + +/* read TDO */ +static int read_tdo(void) +{ + if (current_info == NULL) { + return -1; + } + return firmware_cpld_gpio_get_value(current_info->tdo); +} + +int B6510_fmw_set_gpio_info(firmware_upg_gpio_info_t *info) +{ + if (info == NULL) { + dev_debug(firmware_debug(), "set gpio info info %p is null.\n", info); + return -1; + } + + set_gpio_info.tdi = info->tdi; + set_gpio_info.tck = info->tck; + set_gpio_info.tms = info->tms; + set_gpio_info.tdo = info->tdo; + set_gpio_info.jtag_en = info->jtag_en; + set_gpio_info.select= info->select; + set_gpio_info.jtag_5.pin = info->jtag_5.pin; + set_gpio_info.jtag_4.pin = info->jtag_4.pin; + set_gpio_info.jtag_3.pin = info->jtag_3.pin; + set_gpio_info.jtag_2.pin = info->jtag_2.pin; + set_gpio_info.jtag_1.pin = info->jtag_1.pin; + set_gpio_info.jtag_5.val = info->jtag_5.val; + set_gpio_info.jtag_4.val = info->jtag_4.val; + set_gpio_info.jtag_3.val = info->jtag_3.val; + set_gpio_info.jtag_2.val = info->jtag_2.val; + set_gpio_info.jtag_1.val = info->jtag_1.val; + set_gpio_info_flag = 1; + dev_debug(firmware_debug(), "set gpio info[tdi:%d tck:%d tms:%d tdo:%d jtag_en:%d select:%d].\n", + info->tdi, info->tck, info->tms, info->tdo, info->jtag_en, info->select); + return 0; +} + +firmware_cpld_t fmw_cpld0 = { + .devname = "firmware_cpld_ispvme0", + .slot = 1, + .chip_index = 1, + .is_used = 1, + .tck_delay = 60, + .pull_tdi_up = pull_tdi_up, + .pull_tdi_down = pull_tdi_down, + .pull_tck_up = pull_tck_up, + .pull_tck_down = pull_tck_down, + .pull_tms_up = pull_tms_up, + .pull_tms_down = pull_tms_down, + .read_tdo = read_tdo, + .init_cpld = init_cpld, + .init_chip = init_chip, + .finish_chip = finish_chip, + .finish_cpld = finish_cpld, + .get_version = NULL, +}; + +static void cpld_release(struct device *dev) +{ + return; +} + +static struct platform_device cpld = { + .name = "firmware_cpld_ispvme", + .id = 0, + .num_resources = 0, + .dev = { + .release = cpld_release, + } +}; + +extern void set_currrent_cpld_info(firmware_cpld_t *info); +extern int dfd_get_my_card_type(void); + +int fmw_cpld_product_init(void) +{ + current_info = NULL; +#if 0 + int dev_type; + int i; + + dev_type = drv_get_my_dev_type(); + if (dev_type < 0) { + printk(KERN_ERR "Failed to get device type, when upgrade cpld.\n"); + return FIRMWARE_FAILED; + } + + for (i = 0; i < sizeof(cpld_info)/sizeof(cpld_info[0]); i++) { + if (cpld_info[i].type == dev_type) { + current_info = &cpld_info[i]; + printk(KERN_ERR "device type 0x%x match i %d.\n", dev_type, i); + printk(KERN_ERR "tdi[%d] tck[%d] tms[%d] tdo[%d] jtat_en[%d].\n", current_info->tdi, + current_info->tck, current_info->tms, current_info->tdo, current_info->jtag_en); + } + } +#endif + + if (current_info == NULL) { + current_info = &default_cpld_info; + } + + platform_device_register(&cpld); + fmw_cpld_upg_copy_firmware_info(&fmw_cpld0); + + /* fmw_cpld0.init_cpld(); */ + set_currrent_cpld_info(&fmw_cpld0); + + fmw_cpld_reg_gpio_info_set_func(B6510_fmw_set_gpio_info); + + return FIRMWARE_SUCCESS; +} + +void fmw_cpld_product_exit(void) +{ + platform_device_unregister(&cpld); + return; +} \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/config_gpio.h b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/config_gpio.h new file mode 100755 index 000000000000..90307c4117f5 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/config_gpio.h @@ -0,0 +1,29 @@ +#ifndef __CONFIG_H__ +#define __CONFIG_H__ + +#include + +#define JTAG_TDI (32) +#define JTAG_TDO (67) +#define JTAG_TCK (65) +#define JTAG_TMS (6) +#define JTAG_EN (50) + +typedef struct firmware_device_info_s { + int type; + int tdi; + int tck; + int tms; + int tdo; + int jtag_en; + int select; + gpio_group_t jtag_5; + gpio_group_t jtag_4; + gpio_group_t jtag_3; + gpio_group_t jtag_2; + gpio_group_t jtag_1; + int cmic_start_gpio; + int cmic_end_gpio; +} firmware_device_info_t; + +#endif /* __CONFIG_H__ */ \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/firmware_cpld_ispvme.c b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/firmware_cpld_ispvme.c new file mode 100644 index 000000000000..9abeacbb622d --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/firmware_cpld_ispvme.c @@ -0,0 +1,280 @@ +/* + * firmware_cpld.c + * Original Author : support 2013-10-25 + * + * CPLD driver + * + * History + * v1.0 support 2013-10-25 Initial version. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int firmware_cpld_open(struct inode *inode, struct file *file) +{ + firmware_cpld_t *cpld_info; + firmware_device_t *frm_dev; + + dev_debug(firmware_debug(), "Open cpld device.\n"); + + frm_dev = firmware_get_device_by_minor(FIRMWARE_CPLD, MINOR(inode->i_rdev)); + if (frm_dev == NULL) { + return -ENXIO; + } + + file->private_data = frm_dev; + cpld_info = (firmware_cpld_t *)frm_dev->priv; + + if (cpld_info != NULL && cpld_info->init_cpld) { + cpld_info->init_cpld(); + } + + if (cpld_info != NULL && cpld_info->init_chip) { + cpld_info->init_chip(0); + } + + return FIRMWARE_SUCCESS; +} + +static ssize_t firmware_cpld_read (struct file *file, char __user *buf, size_t count, + loff_t *offset) +{ + return 0; +} + +static ssize_t firmware_cpld_write (struct file *file, const char __user *buf, size_t count, + loff_t *offset) +{ + return 0; +} + +static loff_t firmware_cpld_llseek(struct file *file, loff_t offset, int origin) +{ + return 0; +} + +static long firmware_cpld_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + char value; + void __user *argp; + cmd_info_t cmd_info; + char *buf; + int ret; + + argp = (void __user *)arg; + + switch (cmd) { + case FIRMWARE_JTAG_TDI: + if (copy_from_user(&value, argp, sizeof(value))) { + return -EFAULT; + } + fwm_cpld_tdi_op(value); + break; + case FIRMWARE_JTAG_TCK: + if (copy_from_user(&value, argp, sizeof(value))) { + return -EFAULT; + } + fwm_cpld_tck_op(value); + break; + + case FIRMWARE_JTAG_TMS: + if (copy_from_user(&value, argp, sizeof(value))) { + return -EFAULT; + } + fwm_cpld_tms_op(value); + break; + + case FIRMWARE_JTAG_TDO: + value = fwm_cpld_tdo_op(); + + if (copy_to_user(argp, &value, sizeof(value))) { + return -EFAULT; + } + + break; + + case FIRMWARE_SET_GPIO_INFO: + /* set GPIO information */ + if (copy_from_user(&cmd_info, argp, sizeof(cmd_info_t))) { + return -EFAULT; + } + + buf = (char *) kzalloc(cmd_info.size + 1, GFP_KERNEL); + if (buf == NULL) { + return -ENOMEM; + } + if (copy_from_user(buf, cmd_info.data, cmd_info.size)) { + kfree(buf); + return -EFAULT; + } + + ret = fmw_cpld_set_gpio_info((firmware_upg_gpio_info_t*)buf); + if (ret != FIRMWARE_SUCCESS) { + dev_debug(firmware_debug(), "Failed to set gpio info.\n"); + kfree(buf); + return -ESRCH; + } + + kfree(buf); + break; + + case FIRMWARE_SET_DEBUG_ON: + /* DEBUG ON */ + firmware_set_debug(1); + break; + case FIRMWARE_SET_DEBUG_OFF: + /* DEBUG_OFF */ + firmware_set_debug(0); + break; + + default: + return -ENOTTY; + } + + return FIRMWARE_SUCCESS; +} + +static int firmware_cpld_release(struct inode *inode, struct file *file) +{ + firmware_cpld_t *cpld_info; + firmware_device_t *frm_dev; + + frm_dev = (firmware_device_t *)(file->private_data); + cpld_info = (firmware_cpld_t *)(frm_dev->priv); + + if (cpld_info != NULL && cpld_info->finish_chip) { + cpld_info->finish_chip(0); + } + + if (cpld_info != NULL && cpld_info->finish_cpld) { + cpld_info->finish_cpld(); + } + return 0; +} + +static const struct file_operations cpld_dev_fops = { + .owner = THIS_MODULE, + .llseek = firmware_cpld_llseek, + .read = firmware_cpld_read, + .write = firmware_cpld_write, + .unlocked_ioctl = firmware_cpld_ioctl, + .open = firmware_cpld_open, + .release = firmware_cpld_release, +}; + +static int firmware_cpld_probe(struct platform_device *pdev) +{ + const __be32 *slot; + int len; + int ret; + firmware_cpld_t *cpld_info; + firmware_device_t *frm_dev; + + frm_dev = (firmware_device_t *) kzalloc(sizeof(firmware_device_t), GFP_KERNEL); + if (frm_dev == NULL) { + dev_debug(firmware_debug(), "Failed to kzalloc firmware device.\n"); + return -EPERM; + } + + slot = of_get_property(pdev->dev.of_node, "slot", &len); + if (slot && len == sizeof(*slot)) { + frm_dev->slot = be32_to_cpup(slot); + } else { + frm_dev->slot = firmware_get_device_num(FIRMWARE_CPLD) + 1; + } + + snprintf(frm_dev->name, FIRMWARE_NAME_LEN - 1, "firmware_cpld_ispvme%d", frm_dev->slot - 1); + cpld_info = fmw_cpld_upg_get_cpld(frm_dev->name); + + INIT_LIST_HEAD(&frm_dev->list); + frm_dev->type = FIRMWARE_CPLD; + frm_dev->dev.minor = MISC_DYNAMIC_MINOR; + frm_dev->dev.name = frm_dev->name; + frm_dev->dev.fops = &cpld_dev_fops; + frm_dev->priv = cpld_info; + + dev_debug(firmware_debug(), "Register cpld firmware slot:%d, name:%s.\n", frm_dev->slot, frm_dev->name); + + ret = firmware_device_register(frm_dev); + if (ret < 0) { + dev_debug(firmware_debug(), "Failed to register firmware device.\n"); + kfree(frm_dev); + return -EPERM; + } + + platform_set_drvdata(pdev, frm_dev); + + return 0; +} + +static int __exit firmware_cpld_remove(struct platform_device *dev) +{ + firmware_device_t *frm_dev; + + frm_dev = (firmware_device_t *)platform_get_drvdata(dev); + firmware_device_unregister(frm_dev); + + kfree(frm_dev); + + return 0; +} + +static struct of_device_id cpld_match[] = { + { + .compatible = "firmware_cpld_ispvme", + }, + {}, +}; + +static struct platform_driver cpld_driver = { + .driver = { + .name = "firmware_cpld_ispvme", + .owner = THIS_MODULE, + .of_match_table = cpld_match, + }, + .probe = firmware_cpld_probe, + .remove = firmware_cpld_remove, +}; + +static firmware_driver_t fmw_drv = { + .name = "firmware_cpld_ispvme", + .type = FIRMWARE_CPLD, + .drv = &cpld_driver, +}; + +void firmware_cpld_init(void) +{ + int ret; +#if 0 + struct device_node* node; + node = of_find_node_by_name(NULL, "cpld_upgrade"); + if (node == NULL) { + pr_notice("No cpld_upgrade\r\n"); + return; + } + pr_notice("Found cpld_upgrade\r\n"); +#else + printk(KERN_INFO "Do init cpld_upgrade\r\n"); +#endif + INIT_LIST_HEAD(&fmw_drv.list); + ret = fmw_cpld_upg_init(); + if (ret < 0) { + return; + } + firmware_driver_register(&fmw_drv); +} + +void firmware_cpld_exit(void) +{ + fmw_cpld_upg_exit(); + firmware_driver_unregister(&fmw_drv); + INIT_LIST_HEAD(&fmw_drv.list); +} \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/firmware_cpld_upgrade_ispvme.c b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/firmware_cpld_upgrade_ispvme.c new file mode 100644 index 000000000000..26ae23921a2e --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/firmware_cpld_upgrade_ispvme.c @@ -0,0 +1,326 @@ +/* + * firmware_cpld_upgrade.c + * Original Author : support 2013-10-25 + * + * CPLD upgrade driver + * + * History + * v1.0 support +#include +#include +#include +#include +#include +#include +#include +#include + +/* TCK clock MAX 16MHz */ +#define TCK_DELAY (current_fmw_cpld->tck_delay) +#if 0 +static firmware_cpld_t default_fmw_cpld; +#endif +static firmware_cpld_t fmw_cpld[FIRMWARE_MAX_CPLD_NUM]; +static firmware_cpld_t *current_fmw_cpld; + +static firmware_set_gpio_info_func_t g_set_gpio_info_func = NULL; + +void set_currrent_cpld_info(firmware_cpld_t *info) +{ + current_fmw_cpld = info; +} + +static void TDI_PULL_DOWN(void) +{ + if (current_fmw_cpld != NULL && current_fmw_cpld->pull_tdi_down) { + current_fmw_cpld->pull_tdi_down(); + } else { + dev_debug(firmware_debug(), "NO support TDI_PULL_DOWN.\n"); + } +} + +static void TDI_PULL_UP(void) +{ + if (current_fmw_cpld != NULL && current_fmw_cpld->pull_tdi_up) { + current_fmw_cpld->pull_tdi_up(); + } else { + dev_debug(firmware_debug(), "NO support TDI_PULL_UP.\n"); + } +} + +static void TCK_PULL_DOWN(void) +{ + if (current_fmw_cpld != NULL && current_fmw_cpld->pull_tck_down) { + current_fmw_cpld->pull_tck_down(); + } else { + dev_debug(firmware_debug(), "NO support TCK_PULL_DOWN.\n"); + } +} + +static void TCK_PULL_UP(void) +{ + if (current_fmw_cpld != NULL && current_fmw_cpld->pull_tck_up) { + current_fmw_cpld->pull_tck_up(); + } else { + dev_debug(firmware_debug(), "NO support TCK_PULL_UP.\n"); + } +} + +static void TMS_PULL_DOWN(void) +{ + if (current_fmw_cpld != NULL && current_fmw_cpld->pull_tms_down) { + current_fmw_cpld->pull_tms_down(); + } else { + dev_debug(firmware_debug(), "NO support TMS_PULL_DOWN.\n"); + } +} + +static void TMS_PULL_UP(void) +{ + if (current_fmw_cpld != NULL && current_fmw_cpld->pull_tms_up) { + current_fmw_cpld->pull_tms_up(); + } else { + dev_debug(firmware_debug(), "NO support TMS_PULL_UP.\n"); + } +} + +static int TDO_READ(void) +{ + if (current_fmw_cpld != NULL && current_fmw_cpld->read_tdo) { + return current_fmw_cpld->read_tdo(); + } else { + dev_debug(firmware_debug(), "NO support TDO_READ.\n"); + return -1; + } +} + +firmware_cpld_t *fmw_cpld_upg_get_cpld(char *name) +{ + int i; + + for (i = 0; i < FIRMWARE_MAX_CPLD_NUM; i++) { + if (fmw_cpld[i].is_used == 1 && strcmp(name, fmw_cpld[i].devname) == 0) { + return &fmw_cpld[i]; + } + } + + return NULL; +} + +int fmw_cpld_upg_copy_firmware_info(firmware_cpld_t *info) +{ + int i; + + for (i = 0; i < FIRMWARE_MAX_CPLD_NUM; i++) { + if (fmw_cpld[i].is_used == 1) { + continue; + } else { + strncpy(fmw_cpld[i].devname, info->devname, FIRMWARE_DEV_NAME_LEN); + fmw_cpld[i].slot = info->slot; + fmw_cpld[i].chip_index = info->chip_index; + fmw_cpld[i].is_used = info->is_used; + fmw_cpld[i].tck_delay = info->tck_delay; + fmw_cpld[i].pull_tdi_up = info->pull_tdi_up; + fmw_cpld[i].pull_tdi_down = info->pull_tdi_down; + fmw_cpld[i].pull_tck_up = info->pull_tck_up; + fmw_cpld[i].pull_tck_down = info->pull_tck_down; + fmw_cpld[i].pull_tms_up = info->pull_tms_up; + fmw_cpld[i].pull_tms_down = info->pull_tms_down; + fmw_cpld[i].read_tdo = info->read_tdo; + fmw_cpld[i].init_cpld = info->init_cpld; + fmw_cpld[i].init_chip = info->init_chip; + fmw_cpld[i].finish_chip = info->finish_chip; + fmw_cpld[i].finish_cpld = info->finish_cpld; + fmw_cpld[i].touch_watch_dog = info->touch_watch_dog; + fmw_cpld[i].keeplive = info->keeplive; + fmw_cpld[i].get_version = info->get_version; + fmw_cpld[i].get_card_name = info->get_card_name; + return 0; + } + } + return -1; +} + +int fmw_cpld_set_gpio_info(firmware_upg_gpio_info_t *info) +{ + if (g_set_gpio_info_func == NULL) { + dev_debug(firmware_debug(), "g_set_gpio_info_func is null.\n"); + return -1; + } + + return g_set_gpio_info_func(info); +} + +void fmw_cpld_reg_gpio_info_set_func(firmware_set_gpio_info_func_t func) +{ + if (func == NULL) { + dev_debug(firmware_debug(), "fmw_cpld_register_gpio_info_set_func func = NULL.\n"); + return; + } + g_set_gpio_info_func = func; + return; + +} +#if 0 +/* CPLD upgrade initialization operation */ +static int fmw_cpld_upg_init_cpld(void) +{ + gpio_request(JTAG_TDI, "cpld_upgrade"); + gpio_request(JTAG_TCK, "cpld_upgrade"); + gpio_request(JTAG_TMS, "cpld_upgrade"); + gpio_request(JTAG_EN, "cpld_upgrade"); + gpio_request(JTAG_TDO, "cpld_upgrade"); + + gpio_direction_output(JTAG_TDI, 1); + gpio_direction_output(JTAG_TCK, 1); + gpio_direction_output(JTAG_TMS, 1); + gpio_direction_output(JTAG_EN, 1); + + gpio_direction_input(JTAG_TDO); + return 0; +} + +/* CPLD upgrade completion operation */ +static int fmw_cpld_upg_finish_cpld(void) +{ + gpio_direction_output(JTAG_EN, 0); + + gpio_free(JTAG_TDI); + gpio_free(JTAG_TCK); + gpio_free(JTAG_TMS); + gpio_free(JTAG_EN); + gpio_free(JTAG_TDO); + return 0; +} + +/* TDI pull up */ +static void fmw_cpld_upg_pull_tdi_up(void) +{ + __gpio_set_value(JTAG_TDI, 1); +} + +/* TDI pull down */ +static void fmw_cpld_upg_pull_tdi_down(void) +{ + __gpio_set_value(JTAG_TDI, 0); +} + +/* TCK pull up */ +static void fmw_cpld_upg_pull_tck_up(void) +{ + __gpio_set_value(JTAG_TCK, 1); +} + +/* TCK pull down */ +static void fmw_cpld_upg_pull_tck_down(void) +{ + __gpio_set_value(JTAG_TCK, 0); +} + +/* TMS pull up */ +static void fmw_cpld_upg_pull_tms_up(void) +{ + __gpio_set_value(JTAG_TMS, 1); +} + +/* TCK pull down */ +static void fmw_cpld_upg_pull_tms_down(void) +{ + __gpio_set_value(JTAG_TMS, 0); +} + +/* read TDO */ +static int fmw_cpld_upg_read_tdo(void) +{ + return __gpio_get_value(JTAG_TDO); +} +#endif +#if 0 +static firmware_cpld_t default_fmw_cpld = { + .devname = "default_firmware_cpld", + .slot = 1, + .is_used = 1, + .tck_delay = 50, + .pull_tdi_up = fmw_cpld_upg_pull_tdi_up, + .pull_tdi_down = fmw_cpld_upg_pull_tdi_down, + .pull_tck_up = fmw_cpld_upg_pull_tck_up, + .pull_tck_down = fmw_cpld_upg_pull_tck_down, + .pull_tms_up = fmw_cpld_upg_pull_tms_up, + .pull_tms_down = fmw_cpld_upg_pull_tms_down, + .read_tdo = fmw_cpld_upg_read_tdo, + .init_cpld = fmw_cpld_upg_init_cpld, + .finish_cpld = fmw_cpld_upg_finish_cpld, +}; +#endif + +/** + * Each product initializes its own related CPLD driver and needs to re-define the interface + * In the new interface, assign the relevant driver to fmw_cpld through the fmw_cpld_upg_copy_firmware_info interface + */ +int __attribute__ ((weak))fmw_cpld_product_init(void) +{ + dev_debug(firmware_debug(), "Nothing cpld init for this product.\n"); + return 0; +} + +void __attribute__ ((weak))fmw_cpld_product_exit(void) +{ + dev_debug(firmware_debug(), "Nothing exit init for this product.\n"); + return; +} + +int fmw_cpld_upg_init(void) +{ + int ret; + mem_clear(fmw_cpld, FIRMWARE_MAX_CPLD_NUM * sizeof(firmware_cpld_t)); + ret = fmw_cpld_product_init(); + if (ret < 0) { + return ret; + } +#if 0 + set_currrent_cpld_info(&default_fmw_cpld); +#endif + + return 0; +} + +void fmw_cpld_upg_exit(void) +{ + fmw_cpld_product_exit(); + return; +} + +void fwm_cpld_tdi_op(int value) +{ + if (value) { + TDI_PULL_UP(); + } else { + TDI_PULL_DOWN(); + } +} + +void fwm_cpld_tck_op(int value) +{ + if (value) { + TCK_PULL_UP(); + } else { + TCK_PULL_DOWN(); + } +} + +void fwm_cpld_tms_op(int value) +{ + if (value) { + TMS_PULL_UP(); + } else { + TMS_PULL_DOWN(); + } +} + +int fwm_cpld_tdo_op() +{ + return TDO_READ(); +} diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/firmware_ispvme.c b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/firmware_ispvme.c new file mode 100644 index 000000000000..4086d26280cf --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/firmware_ispvme.c @@ -0,0 +1,139 @@ +/* + * firmware.c + * Original Author : support 2013-10-25 + * + * firmware upgrade driver + * + * History + * v1.0 support 2013-10-25 Initial version. + * + */ +#include +#include +#include +#include + +int drv_debug = 0; +module_param(drv_debug, int, S_IRUGO | S_IWUSR); + +static LIST_HEAD(drv_list); +static LIST_HEAD(dev_list); + +void firmware_set_debug(int value) +{ + drv_debug = value; + return; +} + +int firmware_debug(void) +{ + return drv_debug; +} + +int firmware_driver_register(firmware_driver_t *fw_drv) +{ + int ret; + + if (fw_drv == NULL) { + return FIRMWARE_FAILED; + } + + ret = platform_driver_register(fw_drv->drv); + if (ret < 0) { + return FIRMWARE_FAILED; + } + + list_add(&fw_drv->list, &drv_list); + + return FIRMWARE_SUCCESS; +} + +void firmware_driver_unregister(firmware_driver_t *fw_drv) +{ + list_del_init(&fw_drv->list); + platform_driver_unregister(fw_drv->drv); +} + +int firmware_get_device_num(int type) +{ + int num; + firmware_device_t *tmp; + + num = 0; + list_for_each_entry(tmp, &dev_list, list) { + if (tmp->type == type) { + num++; + } + } + + return num; +} + +firmware_device_t *firmware_get_device_by_minor(int type, int minor) +{ + firmware_device_t *tmp; + + list_for_each_entry(tmp, &dev_list, list) { + if (tmp->type == type && tmp->dev.minor == minor) { + return tmp; + } + } + + return NULL; +} + +int firmware_device_register(firmware_device_t *fw_dev) +{ + int ret; + firmware_device_t *tmp; + + if (fw_dev == NULL) { + return FIRMWARE_FAILED; + } + + list_for_each_entry(tmp, &dev_list, list) { + if (strcmp(tmp->name, fw_dev->name) == 0) { + return FIRMWARE_FAILED; + } + } + + ret = misc_register(&fw_dev->dev); + if (ret < 0) { + return FIRMWARE_FAILED; + } + + list_add(&fw_dev->list, &dev_list); + return FIRMWARE_SUCCESS; +} + +void firmware_device_unregister(firmware_device_t *fw_dev) +{ + list_del(&fw_dev->list); + misc_deregister(&fw_dev->dev); +} + +static int __init firmware_driver_init(void) +{ + INIT_LIST_HEAD(&drv_list); + INIT_LIST_HEAD(&dev_list); + dev_debug(firmware_debug(), "firmware_driver_init cpld init.\n"); + firmware_cpld_init(); + + return FIRMWARE_SUCCESS; +} + +static void __exit firmware_driver_exit(void) +{ + dev_debug(firmware_debug(), "firmware_driver_exit cpld exit.\n"); + firmware_cpld_exit(); + INIT_LIST_HEAD(&drv_list); + INIT_LIST_HEAD(&dev_list); + return; +} + +module_init(firmware_driver_init); +module_exit(firmware_driver_exit); + +MODULE_AUTHOR("support "); +MODULE_DESCRIPTION("Platform Support"); +MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/include/config_ispvme.h b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/include/config_ispvme.h new file mode 100644 index 000000000000..ac9f12006c1d --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/include/config_ispvme.h @@ -0,0 +1,10 @@ +#ifndef __CONFIG_H__ +#define __CONFIG_H__ + +#define JTAG_TDI (32) +#define JTAG_TDO (67) +#define JTAG_TCK (65) +#define JTAG_TMS (6) +#define JTAG_EN (50) + +#endif /* __CONFIG_H__ */ \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/include/firmware_cpld_ispvme.h b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/include/firmware_cpld_ispvme.h new file mode 100644 index 000000000000..6c809aa0b908 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/include/firmware_cpld_ispvme.h @@ -0,0 +1,52 @@ +#ifndef __FIRMWARE_CPLD_H__ +#define __FIRMWARE_CPLD_H__ + +#define FIRMWARE_DEV_NAME_LEN 32 +#define FIRMWARE_MAX_CPLD_NUM 16 + +typedef struct firmware_cpld_s { + char devname[FIRMWARE_DEV_NAME_LEN]; + int slot; + int chip_index; + int is_used; /* 0:unused 1:used */ + u32 tck_delay; /* delay time */ + void (*pull_tdi_up)(void); /* TDI pull up */ + void (*pull_tdi_down)(void); /* TDI pull dowm */ + void (*pull_tck_up)(void); /* TCK pull up */ + void (*pull_tck_down)(void); /* TCK pull dowm */ + void (*pull_tms_up)(void); /* TMS pull up */ + void (*pull_tms_down)(void); /* TCK pull dowm */ + int (*read_tdo)(void); /* read ?TDO */ + int (*init_cpld)(void); /* CPLD upgrade initialization operation */ + int (*init_chip)(int slot); /* Chip related initialization operations */ + int (*finish_chip)(int slot); /* Chip related completion operations */ + int (*finish_cpld)(void); /* CPLD upgrade completion operation */ + int (*check_upgrade_data)(char *src, int src_len, int *dst, int dst_len); + int (*touch_watch_dog)(void); /* touch watch dog related operation */ + int (*keeplive)(void); /* KEEPLIVE */ + int (*get_version)(int slot, char *ver, int len); + int (*get_card_name)(char *name, int len); /* get card name */ +} firmware_cpld_t; + +typedef int (*firmware_set_gpio_info_func_t)(firmware_upg_gpio_info_t *info); + +extern int fmw_cpld_upg_get_chip_name(int slot, firmware_cpld_t *cpld, char *info, int len); +extern int fmw_cpld_upg_get_card_name(int slot, firmware_cpld_t *cpld, char *info, int len); +extern int fmw_cpld_upg_program(int slot, firmware_cpld_t *cpld, char *info, int len); +extern int fmw_cpld_upg_get_version(int slot, firmware_cpld_t *cpld, char *info, int len); +extern firmware_cpld_t *fmw_cpld_upg_get_cpld(char *name); +extern int fmw_cpld_upg_init(void); +extern void fmw_cpld_upg_exit(void); +extern int fmw_cpld_upg_copy_firmware_info(firmware_cpld_t *info); +extern int fmw_cpld_upg_get_chip_info(int slot, firmware_cpld_t *cpld, void *info, int len); + +extern void fwm_cpld_tdi_op(int value); +extern void fwm_cpld_tck_op(int value); +extern void fwm_cpld_tms_op(int value); +extern int fwm_cpld_tdo_op(void); +extern void firmware_cpld_upgrade_init(void); +extern void firmware_cpld_upgrade_finish(void); +extern int fmw_cpld_set_gpio_info(firmware_upg_gpio_info_t *info); +extern void fmw_cpld_reg_gpio_info_set_func(firmware_set_gpio_info_func_t func); + +#endif /* __FIRMWARE_CPLD_H__ */ \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/include/firmware_ispvme.h b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/include/firmware_ispvme.h new file mode 100644 index 000000000000..7568602770eb --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_driver_ispvme/include/firmware_ispvme.h @@ -0,0 +1,90 @@ +#ifndef __FIRMWARE_H__ +#define __FIRMWARE_H__ + +#include +#include + +#include +#include + +#define mem_clear(data, size) memset((data), 0, (size)) +#define dev_debug(debug, fmt, arg...) \ + if (debug == 1) { do{printk(KERN_ERR fmt,##arg);} while(0); } + +#define FIRMWARE_NAME_LEN 48 + +#define FIRMWARE_FAILED (-1) +#define FIRMWARE_SUCCESS 0 + +enum firmware_type_s { + FIRMWARE_CPLD = 0, + FIRMWARE_FPGA, +}; + +/* ioctl command */ +#define FIRMWARE_TYPE 'F' +#define FIRMWARE_JTAG_TDI _IOR(FIRMWARE_TYPE, 0, char) +#define FIRMWARE_JTAG_TDO _IOR(FIRMWARE_TYPE, 1, char) +#define FIRMWARE_JTAG_TCK _IOR(FIRMWARE_TYPE, 2, char) +#define FIRMWARE_JTAG_TMS _IOR(FIRMWARE_TYPE, 3, char) +#define FIRMWARE_JTAG_EN _IOR(FIRMWARE_TYPE, 4, char) +#define FIRMWARE_SET_DEBUG_ON _IOW(FIRMWARE_TYPE, 5, int) /* debug on */ +#define FIRMWARE_SET_DEBUG_OFF _IOW(FIRMWARE_TYPE, 6, int) /* debug off */ +#define FIRMWARE_SET_GPIO_INFO _IOR(FIRMWARE_TYPE, 7, int) /* Set GPIO pin configuration */ + +typedef struct cmd_info_s { + int size; + void __user *data; +} cmd_info_t; + +typedef struct firmware_device_s { + struct list_head list; /* device linked list */ + int type; /* the type of device */ + int slot; /* position */ + char name[FIRMWARE_NAME_LEN]; /* name */ + struct miscdevice dev; /* device */ + void *priv; /* private data */ +} firmware_device_t; + +typedef struct firmware_driver_s { + struct list_head list; /* linked list */ + int type; /* type */ + char name[FIRMWARE_NAME_LEN]; /* name */ + struct platform_driver *drv; /* driver */ + void *priv; /* private data */ +} firmware_driver_t; + +typedef struct gpio_group_s { + int pin; + int val; + int dir; +} gpio_group_t; + +typedef struct firmware_upg_gpio_info_s { + int tdi; + int tck; + int tms; + int tdo; + int jtag_en; + int select; + gpio_group_t jtag_5; + gpio_group_t jtag_4; + gpio_group_t jtag_3; + gpio_group_t jtag_2; + gpio_group_t jtag_1; +} firmware_upg_gpio_info_t; + +extern int firmware_debug(void); +extern void firmware_set_debug(int value); +extern firmware_device_t *firmware_get_device_by_minor(int type, int minor); +extern int firmware_get_device_num(int type); +extern int firmware_device_register(firmware_device_t *fw_dev); +extern void firmware_device_unregister(firmware_device_t *fw_dev); +extern int firmware_driver_register(firmware_driver_t *fw_drv); +extern void firmware_driver_unregister(firmware_driver_t *fw_drv); +extern void firmware_fpga_init(void); +extern void firmware_cpld_init(void); +extern void firmware_fpga_exit(void); +extern void firmware_cpld_exit(void); + +#endif /* end of __FIRMWARE_H__ */ \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/Makefile b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/Makefile new file mode 100644 index 000000000000..c42c9bf3d253 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/Makefile @@ -0,0 +1,39 @@ +include $(top_srcdir)/Rules.mk + +OBJ = firmware_app_ispvme.o debug_ispvme.o hardware.o ispvm_ui.o ivm_core.o dfd_fpga_pkt.o dfd_fpga_upg.o dfd_fpga_debug.o +LIB += $(BUILD_CFALGS) $(BUILD_LDFLAGS) -lpthread -lreadline -lncurses +ifdef ENABLE_GCOV +ifeq ($(ENABLE_GCOV), y) +LIB += -lgcov +endif +endif # ENABLE_GCOV +APP = firmware_upgrade +BUILD_DIR = tmp +ELF_FILE = $(BUILD_DIR)/$(APP) +MAP_FILE = $(BUILD_DIR)/$(APP).map.sym +INCLUDE = -Iinclude + +.PHONY: build +build:make-dir $(addprefix $(BUILD_DIR)/,$(OBJ)) + $(CC) -o $(ELF_FILE) $(addprefix $(BUILD_DIR)/,$(OBJ)) $(LINKFLAGS) $(LIB) + $(NM) $(ELF_FILE) | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' \ + | sort > $(MAP_FILE) + @if [ ! -d $(common_out_put_dir) ]; then mkdir -p $(common_out_put_dir) ;fi + cp -p $(ELF_FILE) $(common_out_put_dir) + +# build +.PHONY: make-dir +make-dir: + @mkdir -p $(BUILD_DIR) + +$(BUILD_DIR)/%.o:%.c + $(CC) -c $(CFLAGS) $(INCLUDE) $< -o $@ + +.PHONY: install +install: + echo "firmware_upgrade install success." + cp -p $(ELF_FILE) $(common_out_put_dir) + +.PHONY: clean +clean: + rm -rf $(BUILD_DIR) diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/common.h b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/common.h new file mode 100644 index 000000000000..208c415f36ea --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/common.h @@ -0,0 +1,28 @@ +#ifndef __COMMON_H__ +#define __COMMON_H__ + +#include +#include +#include +#include +#include +#include + +#define FIRMWARE_TYPE 'F' +#define FIRMWARE_JTAG_TDI _IOR(FIRMWARE_TYPE, 0, char) +#define FIRMWARE_JTAG_TDO _IOR(FIRMWARE_TYPE, 1, char) +#define FIRMWARE_JTAG_TCK _IOR(FIRMWARE_TYPE, 2, char) +#define FIRMWARE_JTAG_TMS _IOR(FIRMWARE_TYPE, 3, char) +#define FIRMWARE_JTAG_EN _IOR(FIRMWARE_TYPE, 4, char) +#define FIRMWARE_SET_DEBUG_ON _IOW(FIRMWARE_TYPE, 5, int) /* debug on */ +#define FIRMWARE_SET_DEBUG_OFF _IOW(FIRMWARE_TYPE, 6, int) /* debug off */ +#define FIRMWARE_SET_GPIO_INFO _IOR(FIRMWARE_TYPE, 7, int) /* set GPIO pin configuration */ + +#define JTAG_TDI (1) +#define JTAG_TDO (2) +#define JTAG_TCK (3) +#define JTAG_TMS (4) +#define JTAG_ENABLE (5) +#define JTAG_TRST (6) + +#endif \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/debug_ispvme.c b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/debug_ispvme.c new file mode 100644 index 000000000000..829109c44904 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/debug_ispvme.c @@ -0,0 +1,64 @@ +/* + * debug.c + * + * firmware upgrade debug control + * + * Original Author : support 2013-10-25 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * firmware_upgrade_debug:handle debug switch + * + * analyse file /tmp/firmware_upgrade_debug,return the information correspoding to debug + * + * return:return DEBUG_OFF when debug off,return DEBUG_ON when debug on,return DEBUG_IGNORE in other cases + */ +int firmware_upgrade_debug(void) +{ + int size; + FILE *fp; + char debug_info[DEBUG_INFO_LEN] = {0}; + + fp = fopen(DEBUG_FILE, "r"); + if (fp == NULL) { + return DEBUG_IGNORE; + } + + size = fread(debug_info, DEBUG_INFO_LEN - 1, 1, fp); + if (size < 0) { + fclose(fp); + return DEBUG_IGNORE; + } + + if (strncmp(debug_info, DEBUG_ON_INFO, 1) == 0) { + fclose(fp); + return DEBUG_APP_ON; + } + + if (strncmp(debug_info, DEBUG_ON_KERN, 1) == 0) { + fclose(fp); + return DEBUG_KERN_ON; + } + + if (strncmp(debug_info, DEBUG_ON_ALL, 1) == 0) { + fclose(fp); + return DEBUG_ALL_ON; + } + + if (strncmp(debug_info, DEBUG_OFF_INFO, 1) == 0) { + fclose(fp); + return DEBUG_OFF; + } + + fclose(fp); + return DEBUG_IGNORE; +} \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/dfd_fpga_debug.c b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/dfd_fpga_debug.c new file mode 100644 index 000000000000..b12e566f0dd2 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/dfd_fpga_debug.c @@ -0,0 +1,117 @@ +/* + * dfd_debug.c + * + * Function:Device framework driver debugging interface + * + * History + * v1.0 support 2013-10-25 Initial version. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "dfd_fpga_debug.h" + +#undef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a) /sizeof((a)[0])) +/* Debug switch storage of dfd module */ +int g_dfd_fpga_debug = 0x0; + +/** + * dfd_fpga_pkt_debug_set - Debug switch setting interface of dfd module + * @type: Types of debugging information + * @enable: enable/Disable debugging information + * + * return 0 if success, otherwise reuturn -1. + */ +static int dfd_fpga_pkt_debug_set(int type, int enable) +{ + + if (type >= DFD_DBG_CNT || type < 0) { + DFD_ERROR("unknow dfd debug type=%d\n", type); + return -1; + } + + if (enable) { + g_dfd_fpga_debug |= 1U << type; + } else { + g_dfd_fpga_debug &= ~(1U << type); + } + + return 0; +} + +void dfd_fpga_open_debug(int val) +{ + if (val == 1) { + (void)dfd_fpga_pkt_debug_set(DFD_DBG_ERR, 1); + (void)dfd_fpga_pkt_debug_set(DFD_DBG_WARN, 1); + (void)dfd_fpga_pkt_debug_set(DFD_DBG_VBOSE, 1); + } else if (val == 2) { + (void)dfd_fpga_pkt_debug_set(DFD_FLOCK_DBG_VBOSE, 1); + } else if (val == 3) { + (void)dfd_fpga_pkt_debug_set(DFD_DBG_ERR, 1); + (void)dfd_fpga_pkt_debug_set(DFD_DBG_WARN, 1); + (void)dfd_fpga_pkt_debug_set(DFD_DBG_VBOSE, 1); + (void)dfd_fpga_pkt_debug_set(DFD_FLOCK_DBG_VBOSE, 1); + (void)dfd_fpga_pkt_debug_set(DFD_DBG_DBG, 1); + } else if (val == 4) { + (void)dfd_fpga_pkt_debug_set(DFD_DBG_DBG, 1); + } else { + (void)dfd_fpga_pkt_debug_set(DFD_DBG_ERR, 0); + (void)dfd_fpga_pkt_debug_set(DFD_DBG_WARN, 0); + (void)dfd_fpga_pkt_debug_set(DFD_DBG_VBOSE, 0); + (void)dfd_fpga_pkt_debug_set(DFD_FLOCK_DBG_VBOSE, 0); + (void)dfd_fpga_pkt_debug_set(DFD_DBG_DBG, 0); + } + + return; +} + +void dfd_fpga_debug_init(void) +{ + FILE *fp; + char buf[10] = {0}; + + fp = fopen(DFD_DEBUG_FILE, "r"); + if (fp != NULL) { + if (fgets(buf, sizeof(buf), fp) != NULL) { + if (strstr(buf, DFD_DEBUG_SET_NO_WARN) != NULL) { + (void)dfd_fpga_pkt_debug_set(DFD_DBG_ERR, 1); + (void)dfd_fpga_pkt_debug_set(DFD_DBG_WARN, 0); + (void)dfd_fpga_pkt_debug_set(DFD_DBG_VBOSE, 0); + (void)dfd_fpga_pkt_debug_set(DFD_FLOCK_DBG_VBOSE, 0); + } else if (strstr(buf, DFD_DEBUG_SET_NO_VBOSE) != NULL) { + (void)dfd_fpga_pkt_debug_set(DFD_DBG_ERR, 1); + (void)dfd_fpga_pkt_debug_set(DFD_DBG_WARN, 1); + (void)dfd_fpga_pkt_debug_set(DFD_DBG_VBOSE, 0); + (void)dfd_fpga_pkt_debug_set(DFD_FLOCK_DBG_VBOSE, 0); + } else if (strstr(buf, DFD_DEBUG_SET_NO_FLOCK_VBOSE) != NULL) { + (void)dfd_fpga_pkt_debug_set(DFD_DBG_ERR, 1); + (void)dfd_fpga_pkt_debug_set(DFD_DBG_WARN, 1); + (void)dfd_fpga_pkt_debug_set(DFD_DBG_VBOSE, 1); + (void)dfd_fpga_pkt_debug_set(DFD_FLOCK_DBG_VBOSE, 0); + } else if (strstr(buf, DFD_DEBUG_SET_ALL) != NULL) { + (void)dfd_fpga_pkt_debug_set(DFD_DBG_ERR, 1); + (void)dfd_fpga_pkt_debug_set(DFD_DBG_WARN, 1); + (void)dfd_fpga_pkt_debug_set(DFD_DBG_VBOSE, 1); + (void)dfd_fpga_pkt_debug_set(DFD_FLOCK_DBG_VBOSE, 1); + (void)dfd_fpga_pkt_debug_set(DFD_DBG_DBG, 1); + } else if (strstr(buf, DFD_DEBUG_SET_DBG) != NULL) { + (void)dfd_fpga_pkt_debug_set(DFD_DBG_DBG, 1); + } else if (strstr(buf, DFD_DEBUG_SET_FLOCK) != NULL) { + (void)dfd_fpga_pkt_debug_set(DFD_FLOCK_DBG_VBOSE, 1); + } + } + + fclose(fp); + } + + return; +} \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/dfd_fpga_debug.h b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/dfd_fpga_debug.h new file mode 100644 index 000000000000..501ba7f37368 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/dfd_fpga_debug.h @@ -0,0 +1,70 @@ +#ifndef _DFD_FPGA_DEBUG_H_ +#define _DFD_FPGA_DEBUG_H_ + +#include +#include +#include + +#define DFD_DEBUG_FILE "/sbin/.dfd_debug_flag" + +#define DFD_DEBUG_SET_NO_WARN "0x1" +#define DFD_DEBUG_SET_NO_VBOSE "0x3" +#define DFD_DEBUG_SET_NO_FLOCK_VBOSE "0x7" +#define DFD_DEBUG_SET_ALL "0xf" +#define DFD_DEBUG_SET_DBG "0xd" +#define DFD_DEBUG_SET_FLOCK "0xe" +#define mem_clear(data, size) memset((data), 0, (size)) + +#define DFD_DEBUG_CHECK(type) (g_dfd_fpga_debug & (1U << (type))) + +#define DFD_ERROR(fmt, args...) do { \ + if (DFD_DEBUG_CHECK(DFD_DBG_ERR)) { \ + printf("[%s-%s]:\n" fmt, "DFD", "err", \ + __FILE__, __FUNCTION__, __LINE__, ##args); \ + } \ +} while (0) + +#define DFD_WARN(fmt, args...) do { \ + if (DFD_DEBUG_CHECK(DFD_DBG_WARN)) { \ + printf("[%s-%s]:\n" fmt, "DFD", "warn", \ + __FILE__, __FUNCTION__, __LINE__, ##args); \ + } \ +} while (0) + +#define DFD_VERBOS(fmt, args...) do { \ + if (DFD_DEBUG_CHECK(DFD_DBG_VBOSE)) { \ + printf("[%s-%s]:\n" fmt, "DFD", "vbose", \ + __FILE__, __FUNCTION__, __LINE__, ##args); \ + } \ +} while (0) + +#define DFD_FLOCK_VERBOS(fmt, args...) do { \ + if (DFD_DEBUG_CHECK(DFD_FLOCK_DBG_VBOSE)) { \ + printf("[%s-%s]:\n" fmt, "DFD", "flock_vbose", \ + __FILE__, __FUNCTION__, __LINE__, ##args); \ + } \ +} while (0) + +#define DFD_DBG(fmt, args...) do { \ + if (DFD_DEBUG_CHECK(DFD_DBG_DBG)) { \ + printf("" fmt,\ + ##args); \ + } \ + } while (0) +/* define the type of debugging information */ +typedef enum { + DFD_DBG_ERR, + DFD_DBG_WARN, + DFD_DBG_VBOSE, + DFD_FLOCK_DBG_VBOSE, + DFD_DBG_DBG, + DFD_DBG_CNT +} DFD_DEBUG_TYPE_E; + +extern int g_dfd_fpga_debug; + +int dfd_fpga_debug_set(int type, int enable); +void dfd_fpga_open_debug(int val); +void dfd_fpga_debug_init(void); + +#endif \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/dfd_fpga_pkt.c b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/dfd_fpga_pkt.c new file mode 100644 index 000000000000..ccf84e9ae5ce --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/dfd_fpga_pkt.c @@ -0,0 +1,318 @@ +/* + * dfd_fpga_pkt.c + * + * FPGA message interaction related interface + * + * History + * v1.0 support 2013-10-25 Initial version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dfd_fpga_pkt.h" +#include "dfd_fpga_debug.h" + +#define DFD_FPGA_FAC_MODE_CONFIG_FILE "/tmp/.factory_disabale_cli_tty" +#if 1 +#define DFD_FPGA_PKT_SEND_PKT_TO_FRAME +#endif + +void dfd_fpga_pkt_print(uint8_t *buf, int buf_len) +{ + int i; + + for (i = 0; i < buf_len; i++) { + if ((i % 16) == 0) { + DFD_DBG("\n"); + } + DFD_DBG("%02x ", buf[i]); + } + + DFD_DBG("\n"); + return; +} + +static unsigned int littel_endian_byte_to_word32(uint8_t *byte_buf, int len) +{ + uint8_t tmp_buf[4]; + unsigned int word; + + word = 0; + + mem_clear(tmp_buf, 4); + memcpy(tmp_buf, byte_buf, len < 4 ? len : 4); + + word = tmp_buf[0] | (tmp_buf[1] << 8) | (tmp_buf[2] << 16) | (tmp_buf[3] << 24); + + return word; +} + +static int littel_endian_word32_to_byte(uint8_t *byte_buf, int len, unsigned int word) +{ + uint8_t tmp_buf[4]; + int ret; + + if (len < 4) { + DFD_ERROR("Not enough buf, word32 to byte: len[%d], word[0x%x]\n"); + return -1; + } + + mem_clear(tmp_buf, 4); + tmp_buf[0] = word & 0xff; + tmp_buf[1] = (word >> 8) & 0xff; + tmp_buf[2] = (word >> 16) & 0xff; + tmp_buf[3] = (word >> 24) & 0xff; + + memcpy(byte_buf, tmp_buf, 4); + + return 0; +} + +static int open_pci_dev(dfd_pci_dev_priv_t *pci_priv, int is_cfg) +{ + int file, ret; + char filename[DFD_PCI_MAX_NAME_SIZE]; + + if (is_cfg) { + ret = snprintf(filename, DFD_PCI_MAX_NAME_SIZE, + "/sys/class/pci_bus/%04x:%02x/device/%04x:%02x:%02x.%d/config", + 0, pci_priv->pcibus, 0, pci_priv->pcibus, pci_priv->slot, pci_priv->fn); + } else { + ret = snprintf(filename, DFD_PCI_MAX_NAME_SIZE, + "/sys/class/pci_bus/%04x:%02x/device/%04x:%02x:%02x.%d/resource%d", + 0, pci_priv->pcibus, 0, pci_priv->pcibus, pci_priv->slot, pci_priv->fn, + pci_priv->bar); + } + + filename[ret] = '\0'; + if ((file = open(filename, O_RDWR, S_IRWXU | S_IRWXG | S_IRWXO)) < 0) { + DFD_ERROR("Error: Could not open file %s\n", filename); + } + + return file; +} + +/** + * dfd_fpga_buf_read - provide FPGA write register interface (address must be four-byte aligned) + * @dst: The data structure of sslot and unit corresponding to the target chip + * @addr: address (four-byte alignment). For the byte alignment required by the specific FPGA address, please refer to the FPGA manual + * @buf: Buffer for reading data + * @wr_len: the length of reading data,please refer to the FPGA manual for the length of the specific FPGA address requirements + * return: return if success,else return -1 + */ +int dfd_fpga_pci_read(dfd_pci_dev_priv_t *pci_priv, int offset, uint8_t *buf, int rd_len) +{ + int ret, fd; + unsigned int data; + uint8_t *ptr, *ptr_data; + struct stat sb; + int i; + int len, align; + + if ((pci_priv == NULL) || (buf == NULL)) { + DFD_ERROR("pci_prive or read buf is null.\n"); + return -1; + } + + if ((pci_priv->align < 1) || (offset & (pci_priv->align - 1)) || (rd_len & (pci_priv->align - 1))) { + DFD_ERROR("offset[%d] or rd_len[%d] don't align[%d].\n", offset, rd_len, pci_priv->align); + return -1; + } + + if ((fd = open_pci_dev(pci_priv, 0)) < 0) { + return -1; + } + + if ((ret = fstat(fd, &sb)) == -1) { + DFD_ERROR("Error: Could not fstat : %s\n", strerror(errno)); + close(fd); + return -1; + } + + if (offset + rd_len >= sb.st_size) { + DFD_ERROR("Error: offset is out of range\n"); + close(fd); + return -1; + } + + if ((ptr = mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0)) == MAP_FAILED) { + DFD_ERROR("Error: Could not mmap : %s or resource is IO\n", strerror(errno)); + close(fd); + return -1; + } + + align = pci_priv->align; + len = rd_len; + ret = 0; + i = 0; + ptr_data = ptr + offset; + + while((i < len) && (ret == 0)){ + if (align == 4) { + data = *((volatile unsigned int *)(ptr_data + i)); + ret = littel_endian_word32_to_byte(buf + i, len - i, data); + i += 4; + } else { + ret = -1; + } + } + + munmap(ptr, sb.st_size); + close(fd); + return ret; + +} + +/** + * dfd_fpga_buf_write -provide FPGA write register interface (address must be four-byte aligned) + * @dst: The data structure of sslot and unit corresponding to the target chip + * @addr: address (four-byte alignment). For the byte alignment required by the specific FPGA address, please refer to the FPGA manual + * @buf: Buffer for reading data + * @wr_len: the length of reading data,please refer to the FPGA manual for the length of the specific FPGA address requirements + * return: return if success,else return -1 + */ + +int dfd_fpga_pci_write(dfd_pci_dev_priv_t *pci_priv, int offset, uint8_t *buf, int wr_len) +{ + int ret, fd; + unsigned int data; + uint8_t *ptr, *ptr_data; + struct stat sb; + int i; + int len, align; + + if ((pci_priv == NULL) || (buf == NULL)) { + DFD_ERROR("pci_prive or write buf is null.\n"); + return -1; + } + + if ((pci_priv->align < 1) || (offset & (pci_priv->align - 1)) || (wr_len & (pci_priv->align - 1))) { + DFD_ERROR("offset[%d] or rd_len[%d] don't align[%d].\n", offset, wr_len, pci_priv->align); + return -1; + } + + if ((fd = open_pci_dev(pci_priv, 0)) < 0) { + return -1; + } + + if ((ret = fstat(fd, &sb)) == -1) { + DFD_ERROR("Error: Could not fstat : %s\n", strerror(errno)); + close(fd); + return -1; + } + + if (offset + wr_len >= sb.st_size) { + DFD_ERROR("Error: offset is out of range\n"); + close(fd); + return -1; + } + + if ((ptr = mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0)) == MAP_FAILED) { + DFD_ERROR("Error: Could not mmap : %s or resource is IO\n", strerror(errno)); + close(fd); + return -1; + } + + align = pci_priv->align; + len = wr_len; + ret = 0; + i = 0; + ptr_data = ptr + offset; + + while((i < len) && (ret == 0)){ + if (align == 4) { + data = littel_endian_byte_to_word32(buf + i,len - i); + *((volatile unsigned int *)(ptr_data + i)) = data; + i += 4; + } else { + ret = -1; + } + } + + munmap(ptr, sb.st_size); + close(fd); + return ret; + +} + +/** + * dfd_fpga_read_word -provide FPGA read register interface (address must be four-byte aligned) + * @addr: address (four-byte alignment) + * @val: the returned number of reading + * return: return 0 if success,else return failure + */ +int dfd_fpga_read_word(dfd_pci_dev_priv_t *pci_priv, int addr, int *val) +{ + int ret, i; + uint8_t tmp[DFD_FPGA_PKT_WORD_LEN]; + + if ((pci_priv == NULL) || (val == NULL) || (addr & 0x03)) { + DFD_ERROR("Input para invalid pci_priv %p val %p addr 0x%x.\n", pci_priv, val, addr); + return -1; + } + + ret = dfd_fpga_pci_read(pci_priv, addr, tmp, DFD_FPGA_PKT_WORD_LEN); + if (ret) { + DFD_ERROR("dfd_fpga_pci_read addr 0x%x failed ret %d.\n", addr, ret); + return ret; + } + + *val = littel_endian_byte_to_word32(tmp,DFD_FPGA_PKT_WORD_LEN); + for (i = 0; i < DFD_FPGA_PKT_WORD_LEN; i++) { + DFD_VERBOS("tmp[%d]: 0x%x.\n", i, tmp[i]); + } + DFD_VERBOS("dfd_fpga_read_word addr 0x%x val 0x%x.\n", addr, *val); + + return 0; +} + +/** + * dfd_fpga_write_word -provide FPGA write register interface (address must be four-byte aligned) + * @addr: address (four-byte alignment) + * @val: Data written + * return: return 0 if success,else return failure + */ +int dfd_fpga_write_word(dfd_pci_dev_priv_t *pci_priv, int addr, int val) +{ + int ret, i; + uint8_t tmp[DFD_FPGA_PKT_WORD_LEN]; + + if ((pci_priv == NULL) || (addr & 0x03)) { + DFD_ERROR("Input para invalid pci_priv %p addr 0x%x.\n", pci_priv, addr); + return -1; + } + + littel_endian_word32_to_byte(tmp, DFD_FPGA_PKT_WORD_LEN, val); + for (i = 0; i < DFD_FPGA_PKT_WORD_LEN; i++) { + DFD_VERBOS("tmp[%d]: 0x%x.\n", i, tmp[i]); + } + + ret = dfd_fpga_pci_write(pci_priv, addr, tmp, DFD_FPGA_PKT_WORD_LEN); + if (ret) { + DFD_ERROR("dfd_fpga_pci_write addr 0x%x failed ret %d.\n", addr, ret); + return ret; + } + + DFD_VERBOS("dfd_fpga_write_word addr 0x%x val 0x%x.\n", addr, val); + return 0; +} \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/dfd_fpga_pkt.h b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/dfd_fpga_pkt.h new file mode 100644 index 000000000000..0d3c3fc9f145 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/dfd_fpga_pkt.h @@ -0,0 +1,95 @@ +#ifndef __DFD_FPGA_PKT_H__ +#define __DFD_FPGA_PKT_H__ + +typedef enum dfd_fpga_pkt_op_type_e { + DFD_FPGA_PKT_OP_TYPE_READ = 0, /* read */ + DFD_FPGA_PKT_OP_TYPE_WRITE = 1, /* write */ + DFD_FPGA_PKT_OP_TYPE_END, +} dfd_fpga_pkt_op_type_t; + +typedef enum dfd_fpga_pkt_ack_type_e { + DFD_FPGA_PKT_ACK_TYPE_NO_ERROR = 0, /* successful operation */ + DFD_FPGA_PKT_ACK_TYPE_FCS_ERROR = 1, /* operation FCS check error */ + DFD_FPGA_PKT_ACK_TYPE_FAIL_ERROR = 2, /* operation failed */ + DFD_FPGA_PKT_ACK_TYPE_END, +} dfd_fpga_pkt_ack_type_t; + +typedef enum dfd_rv_s { + DFD_RV_OK = 0, + DFD_RV_INIT_ERR = 1, + DFD_RV_SLOT_INVALID = 2, + DFD_RV_MODE_INVALID = 3, + DFD_RV_MODE_NOTSUPPORT = 4, + DFD_RV_TYPE_ERR = 5, + DFD_RV_DEV_NOTSUPPORT = 6, + DFD_RV_DEV_FAIL = 7, + DFD_RV_INDEX_INVALID = 8, + DFD_RV_NO_INTF = 9, + DFD_RV_NO_NODE = 10, + DFD_RV_NODE_FAIL = 11, +} dfd_rv_t; + +typedef struct dfd_pci_dev_priv_s { + int pcibus; + int slot; + int fn; + int bar; + int offset; + int times; + int align; + int fpga_upg_base; +}dfd_pci_dev_priv_t; + +#define DFD_PCI_MAX_NAME_SIZE 256 + +#define DFD_MAX_FPGA_NUM (8) +#define DFD_FPGA_PKT_WORD_LEN (4) + +#define DFD_FPGA_PKT_MAC_LEN (6) +#define DFD_FPGA_PKT_PAYLOAD_WORD_DATA_LEN (4) + +#define DFD_FPGA_PKT_WORD_RW_LEN (1)/* for each access, according to 1 WORD, 4 bytes to access FPGA */ + +#define DFD_FPGA_PKT_ETYPE (0xfff9) + +#define DFD_FPGA_PKT_PAYLOAD_ADDR_LEN (4) +#define DFD_FPGA_PKT_PAYLOAD_LENGTH_LEN (2) + +#define DFD_FPGA_PKT_PAYLOAD_ADDR_OFFSET (0) +#define DFD_FPGA_PKT_PAYLOAD_LENGTH_OFFSET (DFD_FPGA_PKT_PAYLOAD_ADDR_LEN) +#define DFD_FPGA_PKT_PAYLOAD_DATA_OFFSET ((DFD_FPGA_PKT_PAYLOAD_ADDR_LEN) + (DFD_FPGA_PKT_PAYLOAD_LENGTH_LEN)) + +#define DFD_FPGA_PKT_GET_DATA(payload) (((uint8_t*)(payload)) + DFD_FPGA_PKT_PAYLOAD_DATA_OFFSET) +#define DFD_FPGA_PKT_GET_PAYLOAD_LEN(len) ((DFD_FPGA_PKT_PAYLOAD_ADDR_LEN) + (DFD_FPGA_PKT_PAYLOAD_LENGTH_LEN) + (len)) + +#pragma pack (1) +typedef struct dfd_fpga_pkt_payload_s { + uint32_t addr; /* the address of reading and writting */ + uint16_t length; /* the length of reading and writting */ + uint32_t data; /* read and write data (for read operations, you don't need to care about this field) */ +} dfd_fpga_pkt_payload_t; + +typedef struct dfd_fpga_pkt_rd_payload_s { + uint32_t addr; /* the address of reading */ + uint16_t length; /* the length of reading */ +} dfd_fpga_pkt_rd_payload_t; +#pragma pack () + +typedef enum fpga_version_e { + FPGA_VER_00 = 0x00, + FPGA_VER_01, + FPGA_VER_02, + FPGA_VER_03, + FPGA_VER_04, + FPGA_VER_05, + FPGA_VER_06, +} fpga_version_t; + +int dfd_fpga_upg_init(void); +int dfd_fpga_write_word(dfd_pci_dev_priv_t *pci_priv, int addr, int val); +int dfd_fpga_read_word(dfd_pci_dev_priv_t *pci_priv, int addr, int *val); +int dfd_fpga_pci_write(dfd_pci_dev_priv_t *pci_priv, int offset, uint8_t *buf, int wr_len); +int dfd_fpga_pci_read(dfd_pci_dev_priv_t *pci_priv, int offset, uint8_t *buf, int rd_len); +extern int drv_get_my_dev_type(void); + +#endif \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/dfd_fpga_upg.c b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/dfd_fpga_upg.c new file mode 100644 index 000000000000..ca1b43666271 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/dfd_fpga_upg.c @@ -0,0 +1,1440 @@ +/* + * dfd_fpga_upg.c + * + * FPGA upgrade related interface + * v1.0 support 2013-10-25 Initial version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dfd_fpga_pkt.h" +#include "dfd_fpga_debug.h" + +typedef struct dfd_fpga_upg_node_s { + int sslot; /* Expansion card slot number */ + int extype; /* Expansion card type */ + int fpga_ver; /* Expansion card FPGA version number */ +} dfd_fpga_upg_node_t; + +/* FPGA upgrade related registers */ +#define FPGA_UPG_CONTENT_BASE_REG_A00 (0xa00) +#define FPGA_UPG_CONTENT_BASE_REG_E00 (0xe00) + +#define FPGA_UPG_STATUS_REG (0x180) +#define FPGA_UPG_SPI_CTRL_REG (0x184) +#define FPGA_UPG_WR_FLASH_STATUS_REG (0x188) +#define FPGA_UPG_RD_FLASH_STATUS_REG (0x18C) +#define FPGA_UPG_INSTRUCTION_REG (0x190) + +#define FPGA_UPG_ADDR_REG (0x194) +#define FPGA_UPG_LENGTH_REG (0x198) +#define FPGA_UPG_DEVICE_ID_REG (0x19C) + +#define FPGA_UPG_DROP_REQ_NUM_REG (0x1A8) + +#define FPGA_VER_ADDRESS (0x00000000) + +#define FPGA_VER_MASK (0xFFFF) +#define FPGA_VERSION(ver) ((ver & FPGA_VER_MASK) >> 8) + +/* define FPGA upgrade related instructions */ +#define FPGA_UPG_INSTRUTION_SE (0xD8) +#define FPGA_UPG_INSTRUTION_RDID (0x9F) +#define FPGA_UPG_INSTRUTION_WRSR (0x01) +#define FPGA_UPG_INSTRUTION_RDSR (0x05) +#define FPGA_UPG_INSTRUTION_WREN (0x06) +#define FPGA_UPG_INSTRUTION_WRDI (0x04) +#define FPGA_UPG_INSTRUTION_BE (0xC7) +#define FPGA_UPG_INSTRUTION_PP (0x02) +#define FPGA_UPG_INSTRUTION_FR (0x0B) +#define FPGA_UPG_INSTRUTION_P4E (0x20) + +#define FPGA_UPG_CONTENT_LENGTH (256) + +#define FPGA_UPG_STATUS_MASK (0x1) +#define FPGA_UPG_ACCESS_ENABLE (0x3) +#define FPGA_UPG_STATUS_RESET (0x0) + +#define FPGA_UPG_SPI_STATUS_MASK (0x1) + +#define FPGA_UPG_RETRY_SLEEP_TIME (10) /* 10us */ +#define FPGA_UPG_RETRY_CNT (1000) + +#define FPGA_UPG_PKT_RETRY_CNT (100) + +#define DFD_FPGA_VERSION_REG (0x10D0) + +#define DFD_FPGA_UPGRADE_BUFF_SIZE (256) +#define DFD_FPGA_UPGADE_RETRY_CNT (10) +#define DFD_FPGA_UPGRADE_CMD_BUFF_SIZE (100) +#define DFD_FPGA_UPGRADE_MAX_NODE (16) + +#define DFD_FPGA_UPDATE_BOOT_ADDR (0x1A0000) /* UPDATE area start address */ +#define DFD_FPGA_UPDATE_BOOT_ADDR_FIX (0x2F0000) +#define DFD_FPGA_SPI_SECTOR_SIZE (0x10000) /* One sector is 64k */ +#define DFD_FPGA_UPDATE_FLASH_SIZE (0x4000000) +#define DFD_FPGA_BASE_TEST_ADD (DFD_FPGA_UPDATE_FLASH_SIZE - DFD_FPGA_SPI_SECTOR_SIZE) + +#define DFD_FPGA_CRITICAL_SWITCH_PAGE_ADDR (0xF00) /* CRITICAL SWITCH page address */ +#define DFD_FPGA_CRITICAL_SWITCH_PAGE_OFFSET (0xFC) /* CRITICAL SWITCH in-page offset address */ +#define DFD_FPGA_CRITICAL_SWITCH_WORD (0xAA995566) /* CRITICAL SWITCH value */ + +#define DFD_FPGA_ERASE_P4E_SIZE (0x1000)/* erase p4e,4k at a time*/ + +#define FPGA_UPG_WAIT_SPI_RETRY_CNT (1000) +#define FPGA_UPG_WAIT_SPI_RETRY_SLEEP_TIME (1000 * 10) /* 10ms */ +#define FPGA_RETRY_TIMES (3) + +static dfd_pci_dev_priv_t default_pci_priv = { + .pcibus = 8, + .slot = 0, + .fn = 0, + .bar = 0, + .align = 4, +}; + +static dfd_pci_dev_priv_t *current_pci_priv = NULL; + +static void dfd_utest_printf_reg(uint8_t *buf, int buf_len) +{ + int i; + + for (i = 0; i < buf_len; i++) { + if ((i % 16) == 0) { + printf("\n"); + } + printf("%02x ", buf[i]); + } + + printf("\n"); + return; +} + +static int dfd_fpga_upg_write_word(dfd_pci_dev_priv_t *pci_priv, int addr, int val) +{ + int ret; + int i; + int cnt; + + i = 0; + cnt = FPGA_UPG_PKT_RETRY_CNT; + while(i < cnt) { + ret = dfd_fpga_write_word(pci_priv, addr, val); + if (ret) { + i++; + DFD_VERBOS("dfd_fpga_write_word addr 0x%x val 0x%x i %d failed ret %d.\n", addr, val, i, + ret); + continue; + } else { + DFD_VERBOS("dfd_fpga_write_word addr 0x%x val 0x%x success.\n", addr, val); + return 0; + } + } + + DFD_VERBOS("dfd_fpga_upg_write_word addr 0x%x val 0x%x i %d failed ret %d.\n", addr, val, i, ret); + return -1; +} + +static int dfd_fpga_upg_read_word(dfd_pci_dev_priv_t *pci_priv, int addr, int *val) +{ + int ret; + int i; + int cnt; + + i = 0; + cnt = FPGA_UPG_PKT_RETRY_CNT; + while(i < cnt) { + ret = dfd_fpga_read_word(pci_priv, addr, val); + if (ret) { + i++; + DFD_VERBOS("dfd_fpga_read_word addr 0x%x i %d failed ret %d.\n", addr, i, + ret); + continue; + } else { + DFD_VERBOS("dfd_fpga_read_word addr 0x%x val 0x%x success.\n", addr, *val); + return 0; + } + } + + DFD_VERBOS("dfd_fpga_read_word addr 0x%x i %d failed ret %d.\n", addr, i, ret); + return -1; +} + +static int dfd_fpga_upg_buf_write(dfd_pci_dev_priv_t *pci_priv, int addr, uint8_t *buf, int wr_len) +{ + int ret; + int i; + int cnt; + + i = 0; + cnt = FPGA_UPG_PKT_RETRY_CNT; + while(i < cnt) { + ret = dfd_fpga_pci_write(pci_priv, addr, buf, wr_len); + if (ret) { + i++; + DFD_VERBOS("dfd_fpga_buf_write addr 0x%x wr_len %d i %d failed ret %d.\n", addr, wr_len, i, + ret); + continue; + } else { + DFD_VERBOS("dfd_fpga_buf_write addr 0x%x wr_len %d success.\n", addr, wr_len); + return 0; + } + } + + DFD_VERBOS("dfd_fpga_buf_write addr 0x%x wr_len %d i %d failed ret %d.\n", addr, wr_len, i, ret); + return -1; +} + +static int dfd_fpga_upg_buf_read(dfd_pci_dev_priv_t *pci_priv, int addr, uint8_t *buf, int rd_len) +{ + int ret; + int i; + int cnt; + + i = 0; + cnt = FPGA_UPG_PKT_RETRY_CNT; + while(i < cnt) { + ret = dfd_fpga_pci_read(pci_priv, addr, buf, rd_len); + if (ret) { + i++; + DFD_VERBOS("dfd_fpga_buf_read addr 0x%x rd_len %d i %d failed ret %d.\n", addr, rd_len, i, + ret); + continue; + } else { + DFD_VERBOS("dfd_fpga_buf_read addr 0x%x rd_len %d success.\n", addr, rd_len); + return 0; + } + } + + DFD_VERBOS("dfd_fpga_buf_read addr 0x%x rd_len %d i %d failed ret %d.\n", addr, rd_len, i, ret); + return -1; +} + +/* Configuring boot Access */ +static int dfd_fpga_upg_set_access(dfd_pci_dev_priv_t *pci_priv, int cmd) +{ + int ret; + int val; + int addr; + + addr = pci_priv->fpga_upg_base + FPGA_UPG_INSTRUCTION_REG; + val = cmd; + ret = dfd_fpga_upg_write_word(pci_priv, addr, val); + if (ret) { + DFD_ERROR("dfd_fpga_upg_write_word addr 0x%x val 0x%x failed ret %d.\n", addr, val, ret); + return -1; + } + + addr = pci_priv->fpga_upg_base + FPGA_UPG_SPI_CTRL_REG; + val = FPGA_UPG_ACCESS_ENABLE; + ret = dfd_fpga_upg_write_word(pci_priv, addr, val); + if (ret) { + DFD_ERROR("dfd_fpga_upg_write_word addr 0x%x val 0x%x failed ret %d.\n", addr, val, ret); + return -1; + } + + DFD_VERBOS("Success: cmd %d.\n", cmd); + return 0; +} + +static int dfd_fpga_upg_reset(dfd_pci_dev_priv_t *pci_priv) +{ + int ret; + int val; + int addr; + + addr = pci_priv->fpga_upg_base + FPGA_UPG_SPI_CTRL_REG; + val = FPGA_UPG_STATUS_RESET; + ret = dfd_fpga_upg_write_word(pci_priv, addr, val); + if (ret) { + DFD_ERROR("dfd_fpga_upg_write_word addr 0x%x val 0x%x failed ret %d.\n", addr, val, ret); + return -1; + } + + DFD_VERBOS("Success: reset %d.\n", val); + return 0; +} + +/* Whether the SPI port is idle. 0 indicates idle, and 1 indicates busy */ +static int dfd_fpga_upg_get_status(dfd_pci_dev_priv_t *pci_priv, char *status) +{ + int ret; + int val; + int addr; + + addr = pci_priv->fpga_upg_base + FPGA_UPG_STATUS_REG; + ret = dfd_fpga_upg_read_word(pci_priv, addr, &val); + DFD_VERBOS("dfd_fpga_upg_read_word return.\n"); + if (ret) { + DFD_ERROR("dfd_fpga_upg_read_word addr 0x%x failed ret %d.\n", addr, ret); + return -1; + } + + *status = val & FPGA_UPG_STATUS_MASK; + DFD_VERBOS("Success: val %d status %d.\n", val, *status); + return 0; +} + +/* Wait for the SPI port to become idle */ +static int dfd_fpga_upg_wait_ready(dfd_pci_dev_priv_t *pci_priv) +{ + int timeout; + char status; + int ret; + + timeout = FPGA_UPG_RETRY_CNT; + while (timeout--) { + DFD_VERBOS("timeout %d.\n", timeout); + ret = dfd_fpga_upg_get_status(pci_priv, &status); + if (ret) { + DFD_ERROR("dfd_fpga_upg_get_status failed ret %d.\n", ret); + continue; + } + + DFD_VERBOS("timeout %d status %d.\n", timeout, status); + /* Determine whether to be idle */ + if (!status) { + DFD_VERBOS("FPGA SPI READY.\n"); + return 0; + } + usleep(FPGA_UPG_RETRY_SLEEP_TIME); + } + + return -2; +} + +/* Configure the FPGA upgrade write function */ +static int dfd_fpga_upg_set_wr_enable(dfd_pci_dev_priv_t *pci_priv) +{ + int ret; + int cmd; + + cmd = FPGA_UPG_INSTRUTION_WREN; + ret = dfd_fpga_upg_set_access(pci_priv, cmd); + if (ret) { + DFD_ERROR("dfd_fpga_upg_set_access cmd %d failed ret %d.\n", cmd, ret); + return -1; + } + + DFD_VERBOS("Success.\n"); + return 0; +} + +/* get SPI's STATUS register */ +static int dfd_fpga_upg_get_spi_status(dfd_pci_dev_priv_t *pci_priv, char *status) +{ + int ret; + int val; + int addr; + int cmd; + + cmd = FPGA_UPG_INSTRUTION_RDSR; + ret = dfd_fpga_upg_set_access(pci_priv, cmd); + if (ret) { + DFD_ERROR("dfd_fpga_upg_set_access cmd %d failed ret %d.\n", cmd, ret); + return -1; + } + + addr = pci_priv->fpga_upg_base + FPGA_UPG_RD_FLASH_STATUS_REG; + ret = dfd_fpga_upg_read_word(pci_priv, addr, &val); + if (ret) { + DFD_ERROR("dfd_fpga_upg_read_word addr 0x%x failed ret %d.\n", addr, ret); + return -1; + } + + ret = dfd_fpga_upg_reset(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_reset failed ret %d.\n", ret); + return -1; + } + + *status = val & FPGA_UPG_SPI_STATUS_MASK; + DFD_VERBOS("Success: val %d spi_status %d.\n", val, *status); + return 0; +} + +/* waiting for SPI chip opreation to complete */ +static int dfd_fpga_wait_spi_ready(dfd_pci_dev_priv_t *pci_priv) +{ + int timeout; + char status; + int ret; + + timeout = FPGA_UPG_WAIT_SPI_RETRY_CNT; + while (timeout--) { + DFD_VERBOS("timeout %d.\n", timeout); + ret = dfd_fpga_upg_get_spi_status(pci_priv, &status); + if (ret) { + DFD_ERROR("dfd_fpga_upg_get_spi_status failed ret %d.\n", ret); + continue; + } + DFD_VERBOS("timeout %d status %d.\n", timeout, status); + /* assert whether it is free */ + if (!status) { + DFD_VERBOS("SPI CHIP READY.\n"); + return 0; + } + usleep(FPGA_UPG_RETRY_SLEEP_TIME); + } + + return -2; +} + +/* Erase the entire chip */ +static int dfd_fpga_upg_set_erase_all(dfd_pci_dev_priv_t *pci_priv) +{ + int ret; + int cmd; + + /* waiting for FPGA's SPI port to become free */ + ret = dfd_fpga_upg_wait_ready(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_wait_ready failed ret %d.\n", ret); + return -1; + } + + /* configure write enable */ + ret = dfd_fpga_upg_set_wr_enable(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_set_wr_enable failed ret %d.\n", ret); + return -1; + } + + cmd = FPGA_UPG_INSTRUTION_BE; + ret = dfd_fpga_upg_set_access(pci_priv, cmd); + if (ret) { + DFD_ERROR("dfd_fpga_upg_set_access cmd %d failed ret %d.\n", cmd, ret); + return -1; + } + + /* Hardware requirements, delay 1s */ + sleep(1); + + /* Waiting for the SPI chip operation to complete */ + ret = dfd_fpga_wait_spi_ready(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_wait_ready sslot %d unit %d failed ret %d.\n", ret); + return -1; + } + + ret = dfd_fpga_upg_reset(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_reset failed ret %d.\n", ret); + return -1; + } + + DFD_VERBOS("Success.\n"); + return 0; +} + +/* Erase sector (256 pages, 64k in total) */ +int dfd_fpga_upg_set_erase_sector(dfd_pci_dev_priv_t *pci_priv, int spi_addr) +{ + int ret; + int cmd, val, addr; + + DFD_VERBOS("Enter spi_addr 0x%x.\n", spi_addr); + + /* waiting for FPGA's SPI port to become free*/ + ret = dfd_fpga_upg_wait_ready(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_wait_ready failed ret %d.\n", ret); + return -1; + } + + /* Start write enable */ + ret = dfd_fpga_upg_set_wr_enable(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_set_wr_enable failed ret %d.\n", ret); + return -1; + } + + /* Write erase address */ + val = spi_addr; + addr = pci_priv->fpga_upg_base + FPGA_UPG_ADDR_REG; + ret = dfd_fpga_upg_write_word(pci_priv, addr, val); + if (ret) { + DFD_ERROR("dfd_fpga_upg_write_word addr 0x%x val 0x%x failed ret %d.\n", addr, val, ret); + return -1; + } + + /* Start sector erase? */ + cmd = FPGA_UPG_INSTRUTION_SE; + ret = dfd_fpga_upg_set_access(pci_priv, cmd); + if (ret) { + DFD_ERROR("dfd_fpga_upg_set_access cmd %d failed ret %d.\n", cmd, ret); + return -1; + } + + /* hardware requirment ,delay 500ms */ + usleep(500 * 1000); + + /* waiting for SPI chip operation to complete */ + ret = dfd_fpga_wait_spi_ready(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_wait_ready failed ret %d.\n", ret); + return -1; + } + + ret = dfd_fpga_upg_reset(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_reset failed ret %d.\n", ret); + return -1; + } + + DFD_VERBOS("Success.\n"); + + return 0; +} + +/* eara 4k area */ +int dfd_fpga_upg_set_erase_p4e(dfd_pci_dev_priv_t *pci_priv, int spi_addr) +{ + int ret; + int cmd, val, addr; + + DFD_VERBOS("Enter spi_addr 0x%x.\n", spi_addr); + + /* waiting for FPGA's SPI port to become free */ + ret = dfd_fpga_upg_wait_ready(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_wait_ready failed ret %d.\n", ret); + return -1; + } + + /* start write enable */ + ret = dfd_fpga_upg_set_wr_enable(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_set_wr_enable failed ret %d.\n", ret); + return -1; + } + + /* write erase address */ + val = spi_addr; + addr = pci_priv->fpga_upg_base + FPGA_UPG_ADDR_REG; + ret = dfd_fpga_upg_write_word(pci_priv, addr, val); + if (ret) { + DFD_ERROR("dfd_fpga_upg_write_word addr 0x%x val 0x%x failed ret %d.\n", addr, val, ret); + return -1; + } + + /* start 4k erase */ + cmd = FPGA_UPG_INSTRUTION_P4E; + ret = dfd_fpga_upg_set_access(pci_priv, cmd); + if (ret) { + DFD_ERROR("dfd_fpga_upg_set_access cmd %d failed ret %d.\n", cmd, ret); + return -1; + } + + /* hardware requirment,delay 200ms */ + usleep(200 * 1000); + + /* waiting for SPI chip operation to complete */ + ret = dfd_fpga_wait_spi_ready(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_wait_ready failed ret %d.\n", ret); + return -1; + } + + ret = dfd_fpga_upg_reset(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_reset failed ret %d.\n", ret); + return -1; + } + + DFD_VERBOS("Success.\n"); + + return 0; +} + +static int dfd_fpga_upg_program(dfd_pci_dev_priv_t *pci_priv, int spi_addr, char *buf, int len) +{ + int ret; + int addr; + int val; + int cmd; + int step; + int wr_len; + + /* Write data to upgrade content register */ + step = 1; + #if 0 + /* FPGA temporarily only supports 4 bytes of read and write */ + for (i = 0; i < len; i += 4) { + addr = pci_priv->fpga_upg_base + i; + wr_len = ((i + 4) <= len) ? (4) : (len - i); + DFD_VERBOS("dfd_fpga_buf_write sslot %d unit %d i %d addr 0x%x wr_len %d.\n", + dst->sslot, dst->unit, i, addr, wr_len); + ret = dfd_fpga_upg_buf_write(dst, addr, (uint8_t*)&buf[i], wr_len); + if (ret) { + DFD_ERROR("dfd_fpga_upg_buf_write addr 0x%x wr_len %d failed ret %d.\n", addr, len, ret); + return -1; + } + } + #else + addr = pci_priv->fpga_upg_base; + wr_len = len; + DFD_VERBOS("dfd_fpga_buf_write addr 0x%x wr_len %d.\n", addr, wr_len); + ret = dfd_fpga_upg_buf_write(pci_priv, addr, (uint8_t*)buf, wr_len); + if (ret) { + DFD_ERROR("dfd_fpga_upg_buf_write addr 0x%x wr_len %d failed ret %d.\n", addr, len, ret); + return -1; + } + #endif + + /* Write length register */ + step++; + val = FPGA_UPG_CONTENT_LENGTH;/* Fpga is always written in 256 length */ + addr = pci_priv->fpga_upg_base + FPGA_UPG_LENGTH_REG; + ret = dfd_fpga_upg_write_word(pci_priv, addr, val); + if (ret) { + DFD_ERROR("dfd_fpga_upg_write_word addr 0x%x val 0x%x failed ret %d.\n", addr, val, ret); + return -step; + } + + /* write address register */ + step++; + val = spi_addr; + addr = pci_priv->fpga_upg_base + FPGA_UPG_ADDR_REG; + ret = dfd_fpga_upg_write_word(pci_priv, addr, val); + if (ret) { + DFD_ERROR("dfd_fpga_upg_write_word addr 0x%x val 0x%x failed ret %d.\n", addr, val, ret); + return -step; + } + + /* Start writing upgrade data to SPI */ + step++; + cmd = FPGA_UPG_INSTRUTION_PP; + ret = dfd_fpga_upg_set_access(pci_priv, cmd); + if (ret) { + DFD_ERROR("dfd_fpga_upg_set_access cmd %d failed ret %d.\n", cmd, ret); + return -step; + } + + /* Wait for the SPI port of FPGA to recover from idle */ + step++; + ret = dfd_fpga_upg_wait_ready(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_wait_ready failed ret %d.\n", ret); + return -step; + } + + step++; + ret = dfd_fpga_upg_reset(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_reset failed ret %d.\n", ret); + return -step; + } + + return 0; +} + +/** + * dfd_fpga_upg_write -write operation interface provided to upgrade module + * @dst: the data structure of sslot and unit corresponding to the target chip to be upgraded + * @addr: write address (must ensure 256-byte alignment) + * @buf: write address buffer + * @len: Write length (when upgrading, 256 bytes of data must be written every time, only the last set of data can be less than 256) + * return: return 0 if success,else return failure + * + */ +int dfd_fpga_upg_write(dfd_pci_dev_priv_t *pci_priv, int addr, char *buf, int len) +{ + int ret; + int step; + + /* address must be 256-byte alignment */ + step = 1; + if ((pci_priv == NULL) || (buf == NULL) || (addr & 0xff) || (len > 256)) { + DFD_ERROR("Input para invalid pci_priv %p buf %p addr 0x%x len %d.\n", pci_priv, buf, addr, len); + return -step; + } + + DFD_VERBOS("Enter: addr 0x%x len %d.\n", addr, len); + + /* waiting for FPGA's SPI port to become free */ + step++; + ret = dfd_fpga_upg_wait_ready(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_wait_ready failed ret %d.\n", ret); + return -step; + } + + /* configure write enable */ + step++; + ret = dfd_fpga_upg_set_wr_enable(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_set_wr_enable failed ret %d.\n", ret); + return -step; + } + + /* write upgrade data */ + step++; + ret = dfd_fpga_upg_program(pci_priv, addr, buf, len); + if (ret) { + DFD_ERROR("dfd_fpga_upg_program addr 0x%x len %d failed ret %d.\n", addr, len, ret); + return -step; + } + + DFD_VERBOS("Success: addr 0x%x len %d.\n", addr, len); + return 0; +} + +static int dfd_fpga_upg_fast_read(dfd_pci_dev_priv_t *pci_priv, int spi_addr, char *buf, int len) +{ + int ret; + uint32_t val; + int addr; + int cmd; + int step; + + step = 0; + + /* clear register value */ + step++; + addr = pci_priv->fpga_upg_base; + ret = dfd_fpga_upg_buf_write(pci_priv, addr, buf, len); + if (ret) { + DFD_ERROR("dfd_fpga_upg_buf_write addr 0x%x len %d failed ret %d.\n", addr, len, ret); + return -step; + } + /* write length register */ + step++; + val = FPGA_UPG_CONTENT_LENGTH; + addr = pci_priv->fpga_upg_base + FPGA_UPG_LENGTH_REG; + ret = dfd_fpga_upg_write_word(pci_priv, addr, val); + if (ret) { + DFD_ERROR("dfd_fpga_upg_write_word addr 0x%x val 0x%x failed ret %d.\n", addr, val, ret); + return -step; + } + + /* write address register */ + step++; + val = spi_addr; + addr = pci_priv->fpga_upg_base + FPGA_UPG_ADDR_REG; + ret = dfd_fpga_upg_write_word(pci_priv, addr, val); + if (ret) { + DFD_ERROR("dfd_fpga_upg_write_word addr 0x%x val 0x%x failed ret %d.\n", addr, val, ret); + return -step; + } + + /* start reading SPI data */ + step++; + cmd = FPGA_UPG_INSTRUTION_FR; + ret = dfd_fpga_upg_set_access(pci_priv, cmd); + if (ret) { + DFD_ERROR("dfd_fpga_upg_set_access cmd %d failed ret %d.\n", cmd, ret); + return -step; + } + + /* waiting for FPGA's SPI port to become free */ + step++; + ret = dfd_fpga_upg_wait_ready(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_wait_ready failed ret %d.\n", ret); + return -step; + } + + /* Read upgrade content register to buffer */ + step++; + + /* FPGA temporarily only supports 4 bytes of read and write */ + #if 1 + addr = pci_priv->fpga_upg_base; + ret = dfd_fpga_upg_buf_read(pci_priv, addr, (uint8_t*)buf, len); + if (ret) { + DFD_ERROR("dfd_fpga_upg_buf_read addr 0x%x len %d failed ret %d.\n", addr, len, ret); + return -step; + } + + step++; + ret = dfd_fpga_upg_reset(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_reset failed ret %d.\n", ret); + return -step; + } + + #else + for (i = 0; i < len; i += 4) { + addr = pci_priv->fpga_upg_base + i; + rd_len = ((i + 4) <= len) ? (4) : (len - i); + DFD_VERBOS("dfd_fpga_upg_buf_read sslot %d unit %d i %d addr 0x%x rd_len %d.\n", + dst->sslot, dst->unit, i, addr, rd_len); + ret = dfd_fpga_upg_buf_read(dst, addr, (uint8_t*)(&buf[i]), rd_len); + if (ret) { + DFD_ERROR("dfd_fpga_upg_buf_read addr 0x%x rd_len %d failed ret %d.\n", addr, rd_len, ret); + return -step; + } + + for (j = 0; j < rd_len; j++) { + DFD_VERBOS("buf[%d]: 0x%x.\n", i, buf[i]); + } + } + #endif + + return 0; +} + +/** + * dfd_fpga_upg_read -read operation interface provided to upgrade module + * @dst: the data structure of sslot and unit corresponding to the target chip + * @addr: read address (must ensure 256-byte alignment) + * @buf: buffer for reading data + * @len: read length (the data read each time must be 256 bytes, only the last time can be less than 256 bytes) + * return: return 0 if success,else return failure + * + */ +int dfd_fpga_upg_read(dfd_pci_dev_priv_t *pci_priv, int addr, char *buf, int len) +{ + int ret; + int step; + + /* address must be 256-byte alignment */ + step = 1; + if ((pci_priv == NULL) || (buf == NULL) || (addr & 0xff) || (len > 256)) { + DFD_ERROR("Input para invalid pci_priv %p buf %p addr 0x%x len %d.\n", pci_priv, buf, addr, len); + return -step; + } + + DFD_VERBOS("Enter: addr 0x%x len %d.\n", addr, len); + + /* waiting for FPGA's SPI port to become free */ + step++; + ret = dfd_fpga_upg_wait_ready(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_wait_ready failed ret %d.\n", ret); + return -step; + } + + /* configure write enable */ + step++; + ret = dfd_fpga_upg_set_wr_enable(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_set_wr_enable failed ret %d.\n", ret); + return -step; + } + + /* read upgrade data */ + step++; + ret = dfd_fpga_upg_fast_read(pci_priv, addr, buf, len); + if (ret) { + DFD_ERROR("dfd_fpga_upg_fast_read addr 0x%x len %d failed ret %d.\n", addr, len, ret); + return -step; + } + + DFD_VERBOS("Success: addr 0x%x len %d.\n", addr, len); + return 0; + +} + +/** + * dfd_fpga_upg_hw_init -upgrade initialization interface provided to the upgrade module (call before starting the upgrade) + * @dst: the data structure of sslot and unit corresponding to the target chip to be upgraded + * return: return 0 if success,else return failure + * Before the interface returns, it will actively delay 1s (required by FPGA) + */ +int dfd_fpga_upg_hw_init(dfd_pci_dev_priv_t *pci_priv) +{ + int ret; + + ret = dfd_fpga_upg_set_erase_all(pci_priv); + if (ret) { + DFD_ERROR("dfd_fpga_upg_set_wr_enable failed ret %d.\n", ret); + return -2; + } + + DFD_VERBOS("Success.\n"); + return 0; +} + +static int dfd_fpga_upgrade_get_fpga_version(dfd_pci_dev_priv_t *pci_priv, int *ver) +{ + int addr; + int ret; + int val; + + addr = DFD_FPGA_VERSION_REG; + ret = dfd_fpga_upg_read_word(pci_priv, addr, &val); + if (ret) { + DFD_ERROR("dfd_fpga_upg_read_word addr 0x%x failed ret %d.\n", addr, ret); + return -1; + } + + *ver = val; + DFD_VERBOS("ver 0x%x.\n", *ver); + return 0; +} + +unsigned long dfd_fpga_upg_get_file_size(const char *path) +{ + unsigned long filesize; + struct stat statbuff; + + if(stat(path, &statbuff) < 0){ + filesize = -1; + } else{ + filesize = statbuff.st_size; + } + + DFD_VERBOS("file %s size is %lu.\n", path, filesize); + return filesize; +} + +static int dfd_fpga_address_init(void) +{ + int fw_version; + int ret; + + ret = dfd_fpga_upg_read_word(current_pci_priv, FPGA_VER_ADDRESS, &fw_version); + switch (FPGA_VERSION(fw_version)) { + case FPGA_VER_00: + case FPGA_VER_02: + case FPGA_VER_03: + case FPGA_VER_06: + current_pci_priv->fpga_upg_base = FPGA_UPG_CONTENT_BASE_REG_A00; + break; + case FPGA_VER_05: + current_pci_priv->fpga_upg_base = FPGA_UPG_CONTENT_BASE_REG_E00; + break; + default: + current_pci_priv->fpga_upg_base = FPGA_UPG_CONTENT_BASE_REG_A00; + break; + } + return ret; +} + +static int dfd_fpga_device_init(void) +{ + current_pci_priv = &default_pci_priv; + if (drv_get_my_dev_type() == 0x4075){ + current_pci_priv->pcibus = 1; /* ATS48's pcie channel is 1 */ + } + + return dfd_fpga_address_init(); +} + +/** + * dfd_fpga_pkt_init -DFD FPGA driver library initialization interface (if you need to use DFD FPGA driver, you must initialize first) + * return: return 0 if success,else return failure + */ + +int dfd_fpga_upg_init(void) +{ + int ret; + static int flag; + + if (flag) { + DFD_VERBOS("Already init.\n"); + return 0; + } + + /* debug initialization */ + dfd_fpga_debug_init(); + + ret = dfd_fpga_device_init(); + if (ret) { + DFD_ERROR("dfd_fpga_upg_init failed ret %d.\n", ret); + return ret; + } + + flag = 1; + return 0; +} + +int dfd_fpga_erase64_sector(dfd_pci_dev_priv_t *pci_priv, int offset) +{ + int ret; + ret = -1; + + if ((offset % DFD_FPGA_SPI_SECTOR_SIZE) == 0) { + DFD_VERBOS("erase 64k area, offset 0x%x.\n", offset); + ret = dfd_fpga_upg_set_erase_sector(pci_priv, offset); + if (ret) { + DFD_ERROR("dfd_fpga_upg_set_erase_sector offset 0x%x failed ret %d.\n", offset, ret); + return ret; + } + } + DFD_ERROR("Input para invalid, offset 0x%x.\n", offset); + return ret; +} + +int dfd_fpga_upgrade_test(void) +{ + int ret, i, j, offset, num, len, res, retry; + char wbuf[DFD_FPGA_SPI_SECTOR_SIZE]; + char rbuf[DFD_FPGA_UPGRADE_BUFF_SIZE]; + + offset = DFD_FPGA_BASE_TEST_ADD; + len = DFD_FPGA_UPGRADE_BUFF_SIZE; + dfd_pci_dev_priv_t *pci_priv; + dfd_fpga_upg_init(); + + if (current_pci_priv == NULL) { + printf("fpga test input para invalid pci_priv %p.\n", current_pci_priv); + return -DFD_RV_INIT_ERR; + } + pci_priv = current_pci_priv; + + mem_clear(wbuf, DFD_FPGA_SPI_SECTOR_SIZE); + /* get random data */ + for (j = 0; j < DFD_FPGA_SPI_SECTOR_SIZE; j++) { + num = rand() % 256; + wbuf[j] = num & 0xff; + } + ret = dfd_fpga_erase64_sector(pci_priv, offset); + if (ret) { + goto exit; + } + + for (i = 0; i < DFD_FPGA_UPGRADE_BUFF_SIZE; i++) { + mem_clear(rbuf, DFD_FPGA_UPGRADE_BUFF_SIZE); + /* write data first */ + ret = dfd_fpga_upg_write(pci_priv, offset, &wbuf[i * DFD_FPGA_UPGRADE_BUFF_SIZE], len); + if (ret) { + DFD_ERROR("fpga upg write offset 0x%x len %d failed ret %d.\n", offset, len, ret); + ret = -DFD_RV_DEV_FAIL; + goto exit; + } else { + DFD_VERBOS("page %d upg write offset 0x%x len %d success.\n", i, offset, len); + } + + /* go back to read the data*/ + for (retry = 0; retry < FPGA_RETRY_TIMES; retry++) { /*retry 3 times*/ + ret = dfd_fpga_upg_read(pci_priv, offset, rbuf, len); + res = memcmp(rbuf, &wbuf[i * DFD_FPGA_UPGRADE_BUFF_SIZE], len); + if (ret || res) { + usleep(1000); + continue; + } + break; + } + if (ret) { + DFD_ERROR("fpga upg read offset 0x%x len %d failed ret %d.\n", offset, len, ret); + ret = -DFD_RV_DEV_FAIL; + goto exit; + } else { + DFD_VERBOS("page %d upg read offset 0x%x len %d success.\n", i, offset, len); + } + + if (res) { + DFD_ERROR("rbuf wbuf not equal, len %d, check failed.\n", len); + DFD_ERROR("wbuf: \n"); + dfd_utest_printf_reg((uint8_t*)wbuf, len); + DFD_ERROR("rbuf: \n"); + dfd_utest_printf_reg((uint8_t*)rbuf, len); + ret = -DFD_RV_DEV_FAIL; + goto exit; + } + + offset += len; + } + + offset = DFD_FPGA_BASE_TEST_ADD; + ret = dfd_fpga_erase64_sector(pci_priv, offset); + if (ret) { + goto exit; + } +exit: + return ret; +} + +static int dfd_fpga_upgrade_do_upgrade_onetime(dfd_pci_dev_priv_t *pci_priv, int fd, unsigned long filesize) +{ + int ret, res; + int i, len, read_len, retry; + char wbuf[DFD_FPGA_UPGRADE_BUFF_SIZE]; + char rbuf[DFD_FPGA_UPGRADE_BUFF_SIZE]; + int offset; + + if (current_pci_priv == NULL) { + DFD_ERROR("Input para invalid pci_priv %p.\n", pci_priv); + return -DFD_RV_INDEX_INVALID; + } + +#if 0 + /* handle before upgrading */ + ret = dfd_fpga_upg_do_pre(dst, filesize); + if (ret) { + DFD_ERROR("fpga hw init failed ret %d.\n", ret); + ret = -DFD_RV_DEV_FAIL; + goto exit; + } +#endif + + i = 0; + offset = DFD_FPGA_UPDATE_BOOT_ADDR; + ret = lseek(fd, 0, SEEK_SET); + if (ret == -1) { + DFD_ERROR("seek file failed ret %d.\n", ret); + ret = -DFD_RV_DEV_FAIL; + goto exit; + } + while(1) { + len = DFD_FPGA_UPGRADE_BUFF_SIZE; +#if 0 + if ((offset % DFD_FPGA_ERASE_P4E_SIZE) == 0) { + DFD_VERBOS("erase 4k area, offset 0x%x.\n", offset); + ret = dfd_fpga_upg_set_erase_p4e(dst, offset); + if (ret) { + DFD_ERROR("dfd_fpga_upg_set_erase_p4e offset 0x%x failed ret %d.\n", offset, ret); + goto exit; + } + } +#else + if ((offset % DFD_FPGA_SPI_SECTOR_SIZE) == 0) { + DFD_VERBOS("erase 64k area, offset 0x%x.\n", offset); + ret = dfd_fpga_upg_set_erase_sector(pci_priv, offset); + if (ret) { + DFD_ERROR("dfd_fpga_upg_set_erase_sector offset 0x%x failed ret %d.\n", offset, ret); + goto exit; + } + } +#endif + + mem_clear(wbuf, DFD_FPGA_UPGRADE_BUFF_SIZE); + read_len = read(fd, wbuf, len); + i++; + if ((read_len > 0) && (read_len <= len)) { + /* write data first */ + ret = dfd_fpga_upg_write(pci_priv, offset, wbuf, read_len); + if (ret) { + DFD_ERROR("fpga upg write offset 0x%x len %d failed ret %d.\n", offset, read_len, ret); + ret = -DFD_RV_DEV_FAIL; + goto exit; + } else { + DFD_VERBOS("page %d upg write offset 0x%x len %d success.\n", i, offset, read_len); + } + + /* go back to read data */ + for (retry = 0; retry < FPGA_RETRY_TIMES; retry++) { /*retry 3 times*/ + mem_clear(rbuf, DFD_FPGA_UPGRADE_BUFF_SIZE); + ret = dfd_fpga_upg_read(pci_priv, offset, rbuf, read_len); + res = memcmp(rbuf, wbuf, read_len); + if (ret || res) { + usleep(1000); + continue; + } + break; + } + if (ret) { + DFD_ERROR("fpga upg read offset 0x%x len %d failed ret %d.\n", offset, read_len, ret); + ret = -DFD_RV_DEV_FAIL; + goto exit; + } else { + DFD_VERBOS("page %d upg read offset 0x%x len %d success.\n", i, offset, read_len); + } + + if (res) { + DFD_ERROR("rbuf wbuf not equal, read_len %d, check failed.\n", read_len); + DFD_ERROR("wbuf: \n"); + dfd_utest_printf_reg((uint8_t*)wbuf, read_len); + DFD_ERROR("rbuf: \n"); + dfd_utest_printf_reg((uint8_t*)rbuf, read_len); + ret = -DFD_RV_DEV_FAIL; + goto exit; + } + + DFD_VERBOS("page %d upg check offset 0x%x len %d success.\n", i, offset, read_len); + offset += read_len; + if (read_len != len) { + DFD_VERBOS("page %d read_len %d len %d, last page exit.\n", i, read_len, len); + break; + } + } else if (read_len == 0) { + DFD_VERBOS("read_len %d exit.\n", read_len); + break; + } else { + DFD_ERROR("len %d read_len %d, read failed, errno(%s).\n", len, read_len, strerror(errno)); + ret = -DFD_RV_DEV_FAIL; + goto exit; + } + } + +#if 0 + ret = dfd_fpga_upg_do_post(dst); + if (ret) { + DFD_ERROR("dfd_fpga_upg_do_post dst->sslot %d dst->unit %d failed ret %d.\n", dst->sslot, + dst->unit, ret); + ret = -DFD_RV_DEV_FAIL; + goto exit; + } +#endif + + ret = 0; + DFD_VERBOS("Update success.\n"); +exit: + return ret; +} + +/* external FPGA upgrade interface */ +int dfd_fpga_upgrade_do_upgrade(char* upg_file) +{ + int ret; + int i; + int cnt; + unsigned long filesize; + int fd; + + DFD_VERBOS("Enter.\n"); + + if (upg_file == NULL) { + DFD_ERROR("Input para invalid upg_file %p.\n", upg_file); + return -DFD_RV_INDEX_INVALID; + } + + dfd_fpga_upg_init(); + + filesize = dfd_fpga_upg_get_file_size(upg_file); + if (filesize <= 0) { + DFD_ERROR("invalid filesize %lu.\n", filesize); + return -DFD_RV_DEV_FAIL; + } + + fd = open(upg_file, O_RDONLY); + if (fd < 0) { + DFD_ERROR("open file[%s] fail.\n", upg_file); + return -DFD_RV_DEV_FAIL; + } + + i = 0; + cnt = DFD_FPGA_UPGADE_RETRY_CNT; + while(i < cnt) { + ret = dfd_fpga_upgrade_do_upgrade_onetime(current_pci_priv, fd, filesize); + if (ret) { + i++; + DFD_ERROR("dfd_fpga_upgrade_do_upgrade_onetime upg_file %s failed ret %d.\n", upg_file, ret); + continue; + } else { + DFD_ERROR("dfd_fpga_upgrade_do_upgrade_onetime upg_file %s success.\n", upg_file); + close(fd); + return 0; + } + } + + DFD_ERROR("upg_file %s failed ret %d.\n", upg_file, ret); + close(fd); + return ret; +} + +static int dfd_fpga_upgrade_do_upgrade_onetime_all(dfd_pci_dev_priv_t *pci_priv, char* upg_file) +{ + int ret; + int i, len, fd, read_len; + char wbuf[DFD_FPGA_UPGRADE_BUFF_SIZE]; + char rbuf[DFD_FPGA_UPGRADE_BUFF_SIZE]; + int offset; + + DFD_VERBOS("Update upg_file: %s.\n", upg_file); + + if ((current_pci_priv == NULL) || (upg_file == NULL)) { + DFD_ERROR("Input para invalid pci_priv %p upg_file %p.\n", pci_priv, upg_file); + return -DFD_RV_INDEX_INVALID; + } + + fd = open(upg_file, O_RDONLY); + if (fd < 0) { + DFD_ERROR("open file[%s] fail.\n", upg_file); + return -DFD_RV_DEV_FAIL; + } + + /* Before upgrading, first initialize the configuration and erase the entire SPI chip */ + ret = dfd_fpga_upg_hw_init(pci_priv); + if (ret) { + DFD_ERROR("fpga hw init failed ret %d.\n", ret); + ret = -DFD_RV_DEV_FAIL; + goto exit; + } + + i = 0; + offset = 0; + while(1) { + len = DFD_FPGA_UPGRADE_BUFF_SIZE; + mem_clear(wbuf, DFD_FPGA_UPGRADE_BUFF_SIZE); + read_len = read(fd, wbuf, len); + i++; + if ((read_len > 0) && (read_len <= len)) { + /* write data first */ + ret = dfd_fpga_upg_write(pci_priv, offset, wbuf, read_len); + if (ret) { + DFD_ERROR("fpga upg write offset 0x%x len %d failed ret %d.\n", offset, read_len, ret); + ret = -DFD_RV_DEV_FAIL; + goto exit; + } else { + DFD_VERBOS("page %d upg write offset 0x%x len %d success.\n", i, offset, read_len); + } + + /* go back to read data */ + mem_clear(rbuf, DFD_FPGA_UPGRADE_BUFF_SIZE); + ret = dfd_fpga_upg_read(pci_priv, offset, rbuf, read_len); + if (ret) { + DFD_ERROR("fpga upg read offset 0x%x len %d failed ret %d.\n", offset, read_len, ret); + ret = -DFD_RV_DEV_FAIL; + goto exit; + } else { + DFD_VERBOS("page %d upg read offset 0x%x len %d success.\n", i, offset, read_len); + } + + if (memcmp(rbuf, wbuf, read_len)) { + DFD_ERROR("rbuf wbuf not equal, read_len %d, check failed.\n", read_len); + ret = -DFD_RV_DEV_FAIL; + goto exit; + } + + DFD_VERBOS("page %d upg check offset 0x%x len %d success.\n", i, offset, read_len); + offset += read_len; + if (read_len != len) { + DFD_VERBOS("page %d read_len %d len %d, last page exit.\n", i, read_len, len); + break; + } + } else if (read_len == 0) { + DFD_VERBOS("read_len %d exit.\n", read_len); + break; + } else { + DFD_ERROR("len %d read_len %d, read failed.\n", len, read_len); + ret = -DFD_RV_DEV_FAIL; + goto exit; + } + } + + ret = 0; + DFD_VERBOS("Update upg_file: %s success.\n", upg_file); +exit: + close(fd); + return ret; +} + +int dfd_fpga_upgrade_do_upgrade_all(char* upg_file) +{ + int ret; + int i; + int cnt; + + DFD_VERBOS("Enter.\n"); + + if (upg_file == NULL) { + DFD_ERROR("Input para invalid upg_file %p.\n", upg_file); + return -DFD_RV_INDEX_INVALID; + } + + dfd_fpga_upg_init(); + + i = 0; + cnt = DFD_FPGA_UPGADE_RETRY_CNT; + while(i < cnt) { + ret = dfd_fpga_upgrade_do_upgrade_onetime_all(current_pci_priv, upg_file); + if (ret) { + i++; + DFD_ERROR("dfd_fpga_upgrade_do_upgrade_onetime upg_file %s failed ret %d.\n", upg_file, ret); + continue; + } else { + DFD_ERROR("dfd_fpga_upgrade_do_upgrade_onetime upg_file %s success.\n", upg_file); + return 0; + } + } + + DFD_ERROR("upg_file %s failed ret %d.\n", upg_file, ret); + return ret; +} + +/* External fpga dump interface */ +int dfd_fpga_upgrade_dump_flash(int argc, char* argv[]) +{ + int offset, addr, len, dlen; + int size, cnt, i; + char *stopstring; + int ret, fd; + char filename[DFD_FPGA_UPGRADE_BUFF_SIZE]; + char tmp[DFD_FPGA_UPGRADE_BUFF_SIZE]; + char is_print; + char buf[DFD_FPGA_UPGRADE_BUFF_SIZE]; + dfd_pci_dev_priv_t *pci_priv; + + ret = DFD_RV_OK; + if (argc != 6) { + printf("fpga dump flash Input invalid.\n"); + return -DFD_RV_INDEX_INVALID; + } + + dfd_fpga_upg_init(); + + if (current_pci_priv == NULL) { + printf("fpga dump flash Input para invalid pci_priv %p.\n", current_pci_priv); + return -DFD_RV_INIT_ERR; + } + + offset = strtol(argv[3], &stopstring, 16); + size = strtol(argv[4], &stopstring, 16); + + if (strcmp(argv[5], "print") != 0) { + is_print = 0; + mem_clear(filename, DFD_FPGA_UPGRADE_BUFF_SIZE); + strncpy(filename, argv[5], (DFD_FPGA_UPGRADE_BUFF_SIZE - 1)); + fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, S_IRWXG|S_IRWXU|S_IRWXO); + if (fd < 0) { + snprintf(tmp, sizeof(tmp), "%s", filename); + printf("open file %s fail(err:%d)!\r\n", tmp, errno); + ret = -DFD_RV_DEV_FAIL; + goto exit; + } + } else { + is_print = 1; + } + + pci_priv = current_pci_priv; + cnt = size / DFD_FPGA_UPGRADE_BUFF_SIZE; + if (size % DFD_FPGA_UPGRADE_BUFF_SIZE) { + cnt++; + } + len = DFD_FPGA_UPGRADE_BUFF_SIZE; + printf("cnt %d.\n", cnt); + for (i = 0; i < cnt; i++) { + mem_clear(buf, len); + addr = offset + i * DFD_FPGA_UPGRADE_BUFF_SIZE; + if (i == (cnt - 1)) { + dlen = size - len * i; + if (dlen > len) { + printf("dlen %d len %d error.\n", dlen, len); + } + } else { + dlen = len; + } + ret = dfd_fpga_upg_read(pci_priv, addr, buf, dlen); + if (ret < 0) { + printf("addr 0x%x failed ret %d\n", addr, ret); + goto exit_close; + } + if (is_print) { + dfd_utest_printf_reg((uint8_t*)buf, dlen); + } else { + ret = write(fd, buf, dlen); + if (ret < 0) { + printf("write failed (errno: %d).\n", errno); + ret = -DFD_RV_DEV_FAIL; + goto exit_close; + } + } + } + +exit_close: + if (!is_print) { + close(fd); + } +exit: + return ret; +} \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/firmware_app_ispvme.c b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/firmware_app_ispvme.c new file mode 100644 index 000000000000..524d67099774 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/firmware_app_ispvme.c @@ -0,0 +1,903 @@ +/* + * firmware_app.c + + * firmware upgrade + * v1.0 support 2013-10-25 Initial version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#include +#include +#endif +#include +static firmware_card_info_t g_card_info[] = { + { + .dev_type = M2_W6010_48GT4X_FA, + .slot_num = 1, + .card_name = "M2_W6010_48GT4X_FA", + .gpio_info = { + /* slot 0 */ + { + .tdi = 507, + .tck = 505, + .tms = 506, + .tdo = 508, + .jtag_en = 504, + .select = -1, + .jtag_5 = GPIO(-1, 0,1) + .jtag_4 = GPIO(-1, 0,1) + .jtag_3 = GPIO(-1, 1,1) + .jtag_2 = GPIO(-1, 0,1) + .jtag_1 = GPIO(-1, 1,1) + }, + }, + + }, + + { + .dev_type = M2_W6010_48GT4X_RA, + .slot_num = 1, + .card_name = "M2_W6010_48GT4X_RA", + .gpio_info = { + /* slot 0 */ + { + .tdi = 507, + .tck = 505, + .tms = 506, + .tdo = 508, + .jtag_en = 504, + .select = -1, + .jtag_5 = GPIO(-1, 0,1) + .jtag_4 = GPIO(-1, 0,1) + .jtag_3 = GPIO(-1, 1,1) + .jtag_2 = GPIO(-1, 0,1) + .jtag_1 = GPIO(-1, 1,1) + }, + }, + }, +}; + +static int is_debug_on; +static int dfd_my_type = 0; + +#if 0 +#if defined(CONFIG_FRM_PRODUCT_NAME) +#define CONFIG_PRODUCT_NAME CONFIG_FRM_PRODUCT_NAME +#else +#define CONFIG_PRODUCT_NAME "card" +#endif +#endif + +#define DFD_TYPE_FILE "/sys/module/platform_common/parameters/dfd_my_type" +#define DFD_TYPE_BUFF_SIZE (256) + +static int is_vme_file(char *file_name) +{ + char *tmp; + + tmp = strchr(file_name, '.'); + if (strcmp(tmp, ".bin") == 0) { + return 0; + } else if (strcmp(tmp, ".vme") == 0) { + return 1; + } else { + return -1; + } +} + +int drv_get_my_dev_type(void) +{ + int type; + int fd; + char rbuf[DFD_TYPE_BUFF_SIZE]; + int read_len; + + if (dfd_my_type != 0) { + dbg_print(is_debug_on, "my_type = 0x%x\r\n", dfd_my_type); + return dfd_my_type; + } + + fd = open(DFD_TYPE_FILE, O_RDONLY); + if (fd < 0) { + dbg_print(is_debug_on, "can't open device %s.\r\n", DFD_TYPE_FILE); + return M2_W6010_48GT4X_FA; /* Avoid 6010 to obtain different device types */ + } + + mem_clear(rbuf, DFD_TYPE_BUFF_SIZE); + type = 0; + read_len = read(fd, rbuf, DFD_TYPE_BUFF_SIZE - 1); + if (read_len > 0) { + type = strtoul(rbuf, NULL, 0); + } + close(fd); + + dfd_my_type = type; + + dbg_print(is_debug_on, "read dfd type file is %s read_len %d, dfd_my_type 0x%x\n", rbuf, read_len, dfd_my_type); + return dfd_my_type; +} + +firmware_card_info_t* firmware_get_card_info(int dev_type) +{ + int i; + int size; + + size = (sizeof(g_card_info) /sizeof((g_card_info)[0])); + + dbg_print(is_debug_on, "Enter dev_type 0x%x size %d.\n", dev_type, size); + for (i = 0; i < size; i++) { + if (g_card_info[i].dev_type == dev_type) { + dbg_print(is_debug_on, "match dev_type 0x%x.\n", dev_type); + return &g_card_info[i]; + } + } + + dbg_print(is_debug_on, "dismatch dev_type 0x%x.\n", dev_type); + return NULL; +} + +int firmware_get_card_name(char *name, int len) +{ + int dev_type; + firmware_card_info_t *info; + + dbg_print(is_debug_on, "Enter len %d.\n", len); + dev_type = drv_get_my_dev_type(); + if (dev_type < 0) { + dbg_print(is_debug_on, "drv_get_my_dev_type failed ret %d.\n", dev_type); + return FIRMWARE_FAILED; + } + + info = firmware_get_card_info(dev_type); + if (info == NULL) { + dbg_print(is_debug_on, "firmware_get_card_info dev_type %d failed.\n", dev_type ); + return FIRMWARE_FAILED; + } + + strncpy(name, info->card_name, len - 1); + dbg_print(is_debug_on, "Leave dev_type 0x%x name %s, info->name %s.\n", dev_type, + name, info->card_name); + return FIRMWARE_SUCCESS; +} + +int get_debug_value(void) +{ + return is_debug_on; +} + +#if 0 +/* each device implements its own corresponding interface */ +int __attribute__ ((weak)) firmware_get_card_name(char *name, int len) +{ + strncpy(name, CONFIG_PRODUCT_NAME, len - 1); + return FIRMWARE_SUCCESS; +} +#endif + +static int firmware_check_file_is_dir(char *dir, char *file_name) +{ + int ret; + struct stat buf; + char tmp[FIRMWARE_FILE_DIR_LEN]; + + if (strcmp(file_name, ".") == 0 || strcmp(file_name, "..") == 0) { + return -1; + } + + mem_clear(tmp, FIRMWARE_FILE_DIR_LEN); + snprintf(tmp, FIRMWARE_FILE_DIR_LEN - 1, "%s/%s", dir, file_name); + ret = stat(tmp, &buf); + if (ret < 0) { + return -1; + } + + if (S_ISDIR(buf.st_mode)) { + return 1; + } + + return 0; +} + +static inline int firmware_error_type(int action, name_info_t *info) +{ + if (info == NULL) { + return ERR_FW_UPGRADE; + } + if (info->type == FIRMWARE_CPLD) { + switch (action) { + case FIRMWARE_ACTION_CHECK: + return ERR_FW_CHECK_CPLD_UPGRADE; + case FIRMWARE_ACTION_UPGRADE: + return ERR_FW_DO_CPLD_UPGRADE; + default: + return ERR_FW_UPGRADE; + } + } else if (info->type == FIRMWARE_FPGA) { + switch (action) { + case FIRMWARE_ACTION_CHECK: + return ERR_FW_CHECK_FPGA_UPGRADE; + case FIRMWARE_ACTION_UPGRADE: + return ERR_FW_DO_FPGA_UPGRADE; + default: + return ERR_FW_UPGRADE; + } + } else { + return ERR_FW_UPGRADE; + } +} + +/* analyze file's name,Name rule: card name_firmware type_slot number_firmware chip name.bin*/ +static int firmware_parse_file_name(char *name, name_info_t *info) +{ + int i; + char *tmp, *start; + char slot[FIRMWARE_NAME_LEN]; + + dbg_print(is_debug_on, "Parse file name: %s\n", name); + + start = name; + /* card name */ + tmp = strchr(start, '_'); + if (tmp == NULL) { + dbg_print(is_debug_on, "Failed to get card name form file name: %s.\n", name); + return firmware_error_type(FIRMWARE_ACTION_CHECK, NULL); + } + + strncpy(info->card_name, start, + (tmp - start > FIRMWARE_NAME_LEN - 1) ? (FIRMWARE_NAME_LEN - 1) : (tmp - start)); + + /* firmware type */ + start = tmp + 1; + tmp = strchr(start, '_'); + if (tmp == NULL) { + dbg_print(is_debug_on, "Failed to get upgrade type form file name: %s.\n", name); + return firmware_error_type(FIRMWARE_ACTION_CHECK, NULL); + } + + if (strncmp(start, FIRMWARE_CPLD_NAME, tmp - start) == 0) { + info->type = FIRMWARE_CPLD; + } else if (strncmp(start, FIRMWARE_FPGA_NAME, tmp - start) == 0) { + info->type = FIRMWARE_FPGA; + } else { + info->type = FIRMWARE_OTHER; + } + + /* slot number */ + start = tmp + 1; + tmp = strchr(start, '_'); + if (tmp == NULL) { + dbg_print(is_debug_on, "Failed to get slot form file name: %s.\n", name); + return firmware_error_type(FIRMWARE_ACTION_CHECK, info); + } + + mem_clear(slot, FIRMWARE_NAME_LEN); + strncpy(slot, start, + ((tmp - start > FIRMWARE_NAME_LEN - 1) ? FIRMWARE_NAME_LEN - 1 : tmp - start)); + + for (i = 0; i < FIRMWARE_NAME_LEN && slot[i] != '\0'; i++) { + if (!isdigit(slot[i])) { + return firmware_error_type(FIRMWARE_ACTION_CHECK, info); + } + } + + dbg_print(is_debug_on, "get slot info: %s.\n", name); + info->slot = strtoul(slot, NULL, 10); + dbg_print(is_debug_on, "get slot info slot: %d.\n", info->slot); + + /* firmware chip name */ + start = tmp + 1; + tmp = strchr(start, '_'); + if (tmp == NULL) { + dbg_print(is_debug_on, "Failed to get chip name form file name: %s.\n", name); + return firmware_error_type(FIRMWARE_ACTION_CHECK, info); + } + strncpy(info->chip_name, start, + (tmp - start > FIRMWARE_NAME_LEN - 1) ? (FIRMWARE_NAME_LEN - 1) : (tmp - start)); + + /* version */ + start = tmp + 1; + tmp = strstr(start, ".vme"); + if (tmp == NULL) { + dbg_print(is_debug_on, "Failed to get chip version form file name: %s.\n", name); + return firmware_error_type(FIRMWARE_ACTION_CHECK, info); + } + strncpy(info->version, start, + (tmp - start > FIRMWARE_NAME_LEN - 1) ? (FIRMWARE_NAME_LEN - 1) : (tmp - start)); + + /* finish checking */ + if (strcmp(tmp, ".vme") != 0) { + dbg_print(is_debug_on, "The file format is wrong: %s.\n", name); + return firmware_error_type(FIRMWARE_ACTION_CHECK, info); + } + + return FIRMWARE_SUCCESS; +} + +/* check the file information to determine whether the file can be used in the device */ +static int firmware_check_file_info(name_info_t *info) +{ + int ret; + char card_name[FIRMWARE_NAME_LEN]; + + dbg_print(is_debug_on, "Check file info.\n"); + + /* get card name */ + mem_clear(card_name, FIRMWARE_NAME_LEN); + ret = firmware_get_card_name(card_name, FIRMWARE_NAME_LEN); + if (ret != FIRMWARE_SUCCESS) { + dbg_print(is_debug_on, "Failed to get card name.(%s)\n", info->card_name); + return firmware_error_type(FIRMWARE_ACTION_CHECK, info); + } + + /* check card name */ + dbg_print(is_debug_on, "The card name: %s, the file card name: %s.\n", + card_name, info->card_name); + if (strcmp(card_name, info->card_name) != 0) { + dbg_print(is_debug_on, "The file card name %s is wrong.(real: %s)\n", + info->card_name, card_name); + return firmware_error_type(FIRMWARE_ACTION_CHECK, info); + } + + /* check type */ + if (info->type != FIRMWARE_CPLD && info->type != FIRMWARE_FPGA) { + dbg_print(is_debug_on, "The file type %d is wrong.(cpld %d, fpga %d)\n", + info->type, FIRMWARE_CPLD, FIRMWARE_FPGA); + return firmware_error_type(FIRMWARE_ACTION_CHECK, info); + } + + /* check slot */ + if (info->slot < 1 || info->slot > FIRMWARE_MAX_SLOT_NUM) { + dbg_print(is_debug_on, "The file slot %d is wrong.\n", info->slot); + return firmware_error_type(FIRMWARE_ACTION_CHECK, info); + } + + dbg_print(is_debug_on, "Success check file info.\n"); + return FIRMWARE_SUCCESS; +} + +static void firmware_get_dev_file_name(name_info_t *info, char *file_name, int len) +{ + if (info->type == FIRMWARE_CPLD) { + snprintf(file_name, len, "/dev/firmware_cpld_ispvme%d", info->slot - 1); + } else if (info->type == FIRMWARE_FPGA) { + snprintf(file_name, len, "/dev/firmware_fpga_ispvme%d", info->slot - 1); + } else { + snprintf(file_name, len, "/dev/firmware_ispvme%d", info->slot - 1); + } +} + +static void firmware_set_driver_debug(int fd) +{ + int ret; + + if (is_debug_on == DEBUG_ALL_ON || is_debug_on == DEBUG_KERN_ON) { + ret = ioctl(fd, FIRMWARE_SET_DEBUG_ON, NULL); + if (ret < 0) { + dbg_print(is_debug_on, "Failed to set driver debug on.\n"); + } else { + dbg_print(is_debug_on, "Open driver debug.\n"); + } + } else if (is_debug_on == DEBUG_OFF) { + ret = ioctl(fd, FIRMWARE_SET_DEBUG_OFF, NULL); + if (ret < 0) { + dbg_print(is_debug_on, "Failed to set driver debug off.\n"); + } else { + dbg_print(is_debug_on, "OFF driver debug.\n"); + } + } else { + dbg_print(is_debug_on, "Ignore driver debug.\n"); + } +} + +static int firmware_check_chip_name(int fd, name_info_t *info) +{ + return FIRMWARE_SUCCESS; +} + +static int firmware_check_chip_verison(int fd, name_info_t *info) +{ + return FIRMWARE_SUCCESS; +} + +static int firmware_get_file_size(char *file_name, int *size) +{ + int ret; + struct stat buf; + + ret = stat(file_name, &buf); + if (ret < 0) { + return FIRMWARE_FAILED; + } + + if (buf.st_size < 0) { + return FIRMWARE_FAILED; + } + + *size = buf.st_size; + + return FIRMWARE_SUCCESS; +} + +static int firmware_get_file_info(char *file_name, char *buf, int size) +{ + FILE *fp; + int len; + + fp = fopen(file_name, "r"); + if (fp == NULL) { + return FIRMWARE_FAILED; + } + + len = fread(buf, size, 1, fp); + if (len < 0) { + fclose(fp); + return FIRMWARE_FAILED; + } + + fclose(fp); + + return FIRMWARE_SUCCESS; +} + +static int firmware_upgrade_info(int fd, char *buf, int size) +{ + return FIRMWARE_SUCCESS; +} + +/* upgrade CPLD */ +static int firmware_upgrade(char *dir, char *file_name, name_info_t *info) +{ + return FIRMWARE_SUCCESS; +} + +static int firmware_upgrade_file(char *dir, char *file_name) +{ + int ret, argc; + name_info_t info; + char *argv[2]; + char tmp_file[FIRMWARE_FILE_DIR_LEN]; + + mem_clear(&info, sizeof(name_info_t)); + ret = firmware_parse_file_name(file_name, &info); + if (ret != FIRMWARE_SUCCESS) { + dbg_print(is_debug_on, "Failed to parse file name: %s\n", file_name); + return ret; + } + + dbg_print(is_debug_on, "The file name parse:(%s) \n" + " card name: %s, \n" + " type : %d, \n" + " slot : %d, \n" + " chip name: %s \n" + " version : %s \n", + file_name, info.card_name, info.type, info.slot, info.chip_name, info.version); + + ret = firmware_check_file_info(&info); + if (ret != FIRMWARE_SUCCESS) { + dbg_print(is_debug_on, "Failed to check file name: %s\n", file_name); + return ret; + } + + /* get the information of upgrade file */ + mem_clear(tmp_file, FIRMWARE_FILE_DIR_LEN); + if (dir != NULL) { + snprintf(tmp_file, FIRMWARE_FILE_DIR_LEN - 1, "%s/%s", dir, file_name); + } else { + snprintf(tmp_file, FIRMWARE_FILE_DIR_LEN - 1, "%s", file_name); + } + + argc = 2; + argv[1] = tmp_file; + ret = ispvme_main(argc, (char **)&argv); + if (ret != FIRMWARE_SUCCESS){ + dbg_print(is_debug_on, "Failed to upgrade file name: %s\n", file_name); + } + + return ret; +} + +static int firmware_upgrade_set_gpio_info(int slot) +{ + firmware_card_info_t *hw_info; + cmd_info_t cmd_info; + firmware_upg_gpio_info_t *gpio_info; + int ret; + int fd; + int dev_type; + int my_slot; + + ret = 0; + + dev_type = drv_get_my_dev_type(); + fd = open("/dev/firmware_cpld_ispvme0", O_RDWR); + if (fd < 0) { + dbg_print(is_debug_on, "%s can't open device\r\n", __func__); + return -1; + } + + hw_info = firmware_get_card_info(dev_type); + + if (hw_info == NULL) { + dbg_print(is_debug_on, "card type 0x%x don't support firmware.\n", dev_type); + ret = -1; + goto gpio_info_err; + } + + if (slot >= hw_info->slot_num) { + dbg_print(is_debug_on, "slot %d is too large, support slot num %d.\n", slot, hw_info->slot_num); + ret = -1; + goto gpio_info_err; + } + + gpio_info = &(hw_info->gpio_info[slot]); + cmd_info.size = sizeof(firmware_upg_gpio_info_t); + cmd_info.data = (void *)gpio_info; + + dbg_print(is_debug_on, "slot = %d, gpio_info[jtag_en:%d select:%d tck:%d tdi:%d tdo=%d tms=%d]\n",slot, \ + gpio_info->jtag_en, gpio_info->select,gpio_info->tck,gpio_info->tdi,gpio_info->tdo,gpio_info->tms); + + ret = ioctl(fd, FIRMWARE_SET_GPIO_INFO, &cmd_info); + +gpio_info_err: + if (ret < 0) { + dbg_print(is_debug_on, "Failed due to:set gpio info.\n"); + } + + close(fd); + + return ret; +} + +/** + * argv[1]: file name + * argv[2]: type + * argv[3]: slot number + * argv[4]: chip name + */ +static int firmware_upgrade_one_file(int argc, char *argv[]) +{ + int ret; + name_info_t info; + char tmp[FIRMWARE_FILE_DIR_LEN]; + + mem_clear(&info, sizeof(name_info_t)); + + info.slot = strtoul(argv[3], NULL, 10); + strncpy(info.chip_name, argv[4], FIRMWARE_NAME_LEN - 1); + + if (strcmp(argv[2], FIRMWARE_CPLD_NAME) == 0) { /* CPLD upgrade */ + if(is_vme_file(argv[1]) == 1){ /* VME upgrade file */ + dbg_print(is_debug_on, "vme file\n"); + info.type = FIRMWARE_CPLD; + ret = firmware_upgrade_set_gpio_info(info.slot); + if (ret < 0) { + goto upgrade_err; + } + ret = ispvme_main(2, argv); + } + else if(is_vme_file(argv[1]) == 0){ /* bin upgrade file */ + dbg_print(is_debug_on, "bin file\n"); + mem_clear(tmp, FIRMWARE_FILE_DIR_LEN); + snprintf(tmp, FIRMWARE_FILE_DIR_LEN, "firmware_upgrade_bin %s %s %s %s", argv[1], argv[2], argv[3], argv[4]); + ret = system(tmp); + } + else{ + dbg_print(is_debug_on, "unknow file\n"); + return FIRMWARE_FAILED; + } + } + else if (strcmp(argv[2], FIRMWARE_FPGA_NAME) == 0) { /* FPGA upgrade */ + info.type = FIRMWARE_FPGA; + ret = dfd_fpga_upgrade_do_upgrade(argv[1]); + } else { + dbg_print(is_debug_on, "Failed to get upgrade type: %s.\n", argv[2]); + return ERR_FW_UPGRADE; + } + +upgrade_err: + if (ret != FIRMWARE_SUCCESS){ + dbg_print(is_debug_on, "Failed to upgrade: %s.\n", argv[1]); + } + + return ret; +} + +static void firmware_get_err_type(int err, int *real_err) +{ + int tmp_err; + + tmp_err = *real_err; + + if (tmp_err == err) { + return; + } + switch (err) { + case ERR_FW_DO_CPLD_UPGRADE: + case ERR_FW_DO_FPGA_UPGRADE: + if (tmp_err == ERR_FW_CHECK_CPLD_UPGRADE + || tmp_err == ERR_FW_CHECK_FPGA_UPGRADE + || tmp_err == FIRMWARE_SUCCESS || tmp_err == ERR_FW_UPGRADE) { + tmp_err = err; + } + break; + case ERR_FW_CHECK_CPLD_UPGRADE: + case ERR_FW_CHECK_FPGA_UPGRADE: + if (tmp_err == FIRMWARE_SUCCESS || tmp_err == ERR_FW_UPGRADE) { + tmp_err = err; + } + break; + case ERR_FW_UPGRADE: + if (tmp_err == FIRMWARE_SUCCESS) { + tmp_err = err; + } + break; + case FIRMWARE_SUCCESS: + break; + default: + return; + } + + *real_err = tmp_err; + + return; +} + +/* argv[1]: Pathname */ +static int firmware_upgrade_one_dir(int argc, char *argv[]) +{ + int ret, real_ret; + int flag; + DIR *dirp; + struct dirent *dp; + char *dir; + + dir = argv[1]; + dirp = opendir(dir); + if (dirp == NULL) { + dbg_print(is_debug_on, "Failed to open the dir: %s.\n", dir); + return ERR_FW_UPGRADE; + } + + ret = ERR_FW_UPGRADE; + real_ret = FIRMWARE_SUCCESS; + flag = 0; + for (;;) { + /* read the pathname of the file */ + dp = readdir(dirp); + if (dp == NULL) { + break; + } + + dbg_print(is_debug_on, "The file name: %s.\n", dp->d_name); + /* check whether it is a file */ + if (firmware_check_file_is_dir(dir, dp->d_name) != 0) { + continue; + } + + dbg_print(is_debug_on, "\n=========== Start: %s ===========\n", dp->d_name); + + /* upgrade a upgrade file */ + ret = firmware_upgrade_file(dir, dp->d_name); + if (ret != FIRMWARE_SUCCESS) { + firmware_get_err_type(ret, &real_ret); + dbg_print(is_debug_on, "Failed to upgrade the file: %s.(%d, %d)\n", dp->d_name, ret, real_ret); + } else { + flag = 1; + dbg_print(is_debug_on, "Upgrade the file: %s success.\n", dp->d_name); + } + + dbg_print(is_debug_on, "=========== End: %s ===========\n", dp->d_name); + } + + if (flag == 1 && (real_ret == ERR_FW_CHECK_CPLD_UPGRADE || real_ret == ERR_FW_CHECK_FPGA_UPGRADE)) { + real_ret = FIRMWARE_SUCCESS; + } + + if (real_ret != FIRMWARE_SUCCESS) { + dbg_print(is_debug_on, "Failed to upgrade: %s.\n", dir); + } else { + dbg_print(is_debug_on, "Upgrade success: %s.\n", dir); + } + + closedir(dirp); + return real_ret; +} + +/** + * argv[1]: file name + * argv[2]: type + * argv[3]: slot number + */ +static int firmware_upgrade_read_chip(int argc, char *argv[]) +{ + + return FIRMWARE_SUCCESS; +} + +static int firmware_upgrade_test_fpga(int argc, char *argv[]) +{ + int ret; + char tmp1[128]; + char tmp2[128]; + + if ((strcmp(argv[1], FIRMWARE_FPGA_NAME) != 0) + || (strcmp(argv[2], FIRMWARE_FPGA_TEST) != 0)) { + snprintf(tmp1, sizeof(tmp1), "%s", argv[1]); + snprintf(tmp2, sizeof(tmp2), "%s", argv[2]); + printf( "fpga test:Failed to Input ERR Parm, argv[1]:%s, agrv[2]:%s\n", tmp1,tmp2); + return FIRMWARE_FAILED; + } + ret = dfd_fpga_upgrade_test(); + return ret; +} + +static int firmware_upgrade_test_chip(int argc, char *argv[]) +{ + int ret,dev_type,slot; + int err_ret=0; + firmware_card_info_t *hw_info; + char tmp1[128]; + char tmp2[128]; + + if ((strcmp(argv[1], FIRMWARE_CPLD_NAME) != 0) + || (strcmp(argv[2], FIRMWARE_CPLD_TEST) != 0)) { + snprintf(tmp1, sizeof(tmp1), "%s", argv[1]); + snprintf(tmp2, sizeof(tmp2), "%s", argv[2]); + printf( "gpio test:Failed to Input ERR Parm, argv[1]:%s, agrv[2]:%s\n", tmp1, tmp2); + return FIRMWARE_FAILED; + } + + dev_type = drv_get_my_dev_type(); /* get the type of card first */ + if (dev_type < 0) { + printf("gpio test:drv_get_my_dev_type failed ret 0x%x.\n", dev_type); + return FIRMWARE_FAILED; + } + + hw_info = firmware_get_card_info(dev_type); /* get the detail information of card */ + if (hw_info == NULL) { + printf( "gpio test:card type 0x%x don't support firmware.\n", dev_type); + return FIRMWARE_FAILED; + } + + for(slot = 0; slot < hw_info->slot_num; slot++){ + ret = firmware_upgrade_set_gpio_info(slot); /* set GPIO information */ + if(ret < 0){ + err_ret++; + printf( "gpio test:Failed to set gpio info,dev_type 0x%x,slot %d\n", dev_type, slot); + continue; + } + ret = ispvme_test(); /* GPIO path test */ + if(ret != 0){ + err_ret++; + printf("gpio test:ispvme_test failed,dev_type 0x%x,slot %d\n", dev_type, slot); + } + } + if(err_ret != 0) + return FIRMWARE_FAILED; + return FIRMWARE_SUCCESS; +} + +int main(int argc, char *argv[]) +{ + int ret; + + is_debug_on = firmware_upgrade_debug(); + + if (argc != 2 && argc != 5 && argc != 4 && argc != 3 && argc != 6) { + printf("Use:\n"); + printf(" upgrade dir : firmware_upgrade_ispvme dir\n"); + printf(" upgrade file : firmware_upgrade_ispvme file type slot chip_name\n"); + printf(" read chip : firmware_upgrade_ispvme file type slot\n"); + dbg_print(is_debug_on, "Failed to upgrade the number of argv: %d.\n", argc); + return ERR_FW_UPGRADE; + } +#if 0 + ret = dev_drv_init(); + if (ret) { + dbg_print(is_debug_on, "failed to init dfd ret %d.", ret); + return ERR_FW_UPGRADE; + } +#endif + /* dump fpga flash operation */ + if (argc == 6) { + if (strcmp(argv[1], "fpga_dump_flash") == 0) { + ret = dfd_fpga_upgrade_dump_flash(argc, argv); + printf("fpga_dump_flash ret %d.\n", ret); + return FIRMWARE_SUCCESS; + } else { + printf("Not support, please check your cmd.\n"); + return FIRMWARE_SUCCESS; + } + } + + /* upgrade individual files */ + if (argc == 5) { + printf("+================================+\n"); + printf("|Begin to upgrade, please wait...|\n"); + ret = firmware_upgrade_one_file(argc, argv); + if (ret != FIRMWARE_SUCCESS) { + if(strcmp(argv[2], FIRMWARE_CPLD_NAME) == 0) + printf("| CPLD Upgrade failed! |\n"); + else if(strcmp(argv[2], FIRMWARE_FPGA_NAME) == 0) + printf("| FPGA Upgrade failed! |\n"); + else + printf("| Failed to get upgrade type! |\n"); + printf("+================================+\n"); + dbg_print(is_debug_on, "Failed to upgrade a firmware file: %s.\n", argv[1]); + return ret; + } + if(strcmp(argv[2], FIRMWARE_CPLD_NAME) == 0) + printf("| CPLD Upgrade succeeded! |\n"); + else + printf("| FPGA Upgrade succeeded! |\n"); + printf("+================================+\n"); + return FIRMWARE_SUCCESS; + } + + if (argc == 2) { + printf("+================================+\n"); + printf("|Begin to upgrade, please wait...|\n"); + + ret = firmware_upgrade_one_dir(argc, argv); + if (ret != FIRMWARE_SUCCESS) { + printf("| Upgrade failed! |\n"); + printf("+================================+\n"); + dbg_print(is_debug_on, "Failed to upgrade a firmware dir: %s.\n", argv[1]); + return ret; + } + printf("| Upgrade succeeded! |\n"); + printf("+================================+\n"); + return FIRMWARE_SUCCESS; + } + + if (argc == 4) { + ret = firmware_upgrade_read_chip(argc, argv); + if (ret != FIRMWARE_SUCCESS) { + dbg_print(is_debug_on, "Failed to read chip: %s.\n", argv[1]); + return ret; + } + + return FIRMWARE_SUCCESS; + } + + if (argc == 3) { + if (strcmp(argv[1], FIRMWARE_FPGA_NAME) == 0) { + ret = firmware_upgrade_test_fpga(argc, argv); + if (ret != FIRMWARE_SUCCESS) { + printf("+=================+\n"); + printf("| FPGA TEST FAIL! |\n"); + printf("+=================+\n"); + return FIRMWARE_FAILED; + } else { + printf("+=================+\n"); + printf("| FPGA TEST PASS! |\n"); + printf("+=================+\n"); + return FIRMWARE_SUCCESS; + } + } else { + ret = firmware_upgrade_test_chip(argc, argv); + if (ret != FIRMWARE_SUCCESS) { + printf("+=================+\n"); + printf("| GPIO TEST FAIL! |\n"); + printf("+=================+\n"); + return FIRMWARE_FAILED; + } else { + printf("+=================+\n"); + printf("| GPIO TEST PASS! |\n"); + printf("+=================+\n"); + return FIRMWARE_SUCCESS; + } + } + } + + return ERR_FW_UPGRADE; +} \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/hardware.c b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/hardware.c new file mode 100644 index 000000000000..8f8ca9ed8c86 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/hardware.c @@ -0,0 +1,249 @@ +/********************************************************************************* +* Lattice Semiconductor Corp. Copyright 2000-2008 +* +* This is the hardware.c of ispVME V12.1 for JTAG programmable devices. +* All the functions requiring customization are organized into this file for +* the convinience of porting. +*********************************************************************************/ +/********************************************************************************* +* Revision History: +* +* 09/11/07 NN Type cast mismatch variables +* 09/24/07 NN Added calibration function. +* Calibration will help to determine the system clock frequency +* and the count value for one micro-second delay of the target +* specific hardware. +* Modified the ispVMDelay function +* Removed Delay Percent support +* Moved the sclock() function from ivm_core.c to hardware.c +*********************************************************************************/ + +/* #include */ +#include +#include +#include "common.h" + +/******************************************************************************** +* Declaration of global variables +* +*********************************************************************************/ + +unsigned char g_siIspPins = 0x00; /*Keeper of JTAG pin state*/ +unsigned short g_usInPort = 0x379; /*Address of the TDO pin*/ +unsigned short g_usOutPort = 0x378; /*Address of TDI, TMS, TCK pin*/ +unsigned short g_usCpu_Frequency = 1000; /*Enter your CPU frequency here, unit in MHz.*/ + +/********************************************************************************* +* This is the definition of the bit locations of each respective +* signal in the global variable g_siIspPins. +* +* NOTE: Users must add their own implementation here to define +* the bit location of the signal to target their hardware. +* The example below is for the Lattice download cable on +* on the parallel port. +* +*********************************************************************************/ + +#if 0 +const unsigned char g_ucPinTDI = JTAG_TDI; /* Bit address of TDI */ +const unsigned char g_ucPinTCK = JTAG_TCK; /* Bit address of TCK */ +const unsigned char g_ucPinTMS = JTAG_TMS; /* Bit address of TMS */ +const unsigned char g_ucPinENABLE = JTAG_ENABLE; /* Bit address of ENABLE */ +const unsigned char g_ucPinTRST = JTAG_TRST; /* Bit address of TRST */ +const unsigned char g_ucPinTDO = JTAG_TDO; /* Bit address of TDO*/ +#endif +int g_file_fd; +/*************************************************************** +* +* Functions declared in hardware.c module. +* +***************************************************************/ +void writePort(unsigned char a_ucPins, unsigned char a_ucValue); +unsigned char readPort(); +void sclock(); +void ispVMDelay(unsigned short a_usTimeDelay); +void calibration(void); + +/******************************************************************************** +* writePort +* To apply the specified value to the pins indicated. This routine will +* be modified for specific systems. +* As an example, this code uses the IBM-PC standard Parallel port, along with the +* schematic shown in Lattice documentation, to apply the signals to the +* JTAG pins. +* +* PC Parallel port pin Signal name Port bit address +* 2 g_ucPinTDI 1 +* 3 g_ucPinTCK 2 +* 4 g_ucPinTMS 4 +* 5 g_ucPinENABLE 8 +* 6 g_ucPinTRST 16 +* 10 g_ucPinTDO 64 +* +* Parameters: +* - a_ucPins, which is actually a set of bit flags (defined above) +* that correspond to the bits of the data port. Each of the I/O port +* bits that drives an isp programming pin is assigned a flag +* (through a #define) corresponding to the signal it drives. To +* change the value of more than one pin at once, the flags are added +* together, much like file access flags are. +* +* The bit flags are only set if the pin is to be changed. Bits that +* do not have their flags set do not have their levels changed. The +* state of the port is always manintained in the static global +* variable g_siIspPins, so that each pin can be addressed individually +* without disturbing the others. +* +* - a_ucValue, which is either HIGH (0x01 ) or LOW (0x00 ). Only these two +* values are valid. Any non-zero number sets the pin(s) high. +* +*********************************************************************************/ + +void writePort(unsigned char a_ucPins, unsigned char a_ucValue) +{ + switch (a_ucPins) { + case JTAG_TCK: + ioctl(g_file_fd, FIRMWARE_JTAG_TCK, &a_ucValue); + break; + case JTAG_TDI: + ioctl(g_file_fd, FIRMWARE_JTAG_TDI, &a_ucValue); + break; + case JTAG_TMS: + ioctl(g_file_fd, FIRMWARE_JTAG_TMS, &a_ucValue); + break; + case JTAG_ENABLE: + ioctl(g_file_fd, FIRMWARE_JTAG_EN, &a_ucValue); + break; + case JTAG_TRST: + /* ioctl(g_file_fd, FIRMWARE_JTAG_TRST, &a_ucValue); */ + break; + default: + break; + } +} + +/********************************************************************************* +* +* readPort +* +* Returns the value of the TDO from the device. +* +**********************************************************************************/ +unsigned char readPort() +{ + unsigned char ucRet = 0; + + ioctl(g_file_fd, FIRMWARE_JTAG_TDO, &ucRet); + return (ucRet); +} + +/********************************************************************************* +* sclock +* +* Apply a pulse to TCK. +* +* This function is located here so that users can modify to slow down TCK if +* it is too fast (> 25MHZ). Users can change the IdleTime assignment from 0 to +* 1, 2... to effectively slowing down TCK by half, quarter... +* +*********************************************************************************/ +void sclock() +{ + unsigned short IdleTime = 0; /* change to > 0 if need to slow down TCK */ + unsigned short usIdleIndex = 0; + IdleTime++; + for (usIdleIndex = 0; usIdleIndex < IdleTime; usIdleIndex++) { + writePort(JTAG_TCK, 0x01); + } + for (usIdleIndex = 0; usIdleIndex < IdleTime; usIdleIndex++) { + writePort(JTAG_TCK, 0x00); + } +} +/******************************************************************************** +* +* ispVMDelay +* +* +* Users must implement a delay to observe a_usTimeDelay, where +* bit 15 of the a_usTimeDelay defines the unit. +* 1 = milliseconds +* 0 = microseconds +* Example: +* a_usTimeDelay = 0x0001 = 1 microsecond delay. +* a_usTimeDelay = 0x8001 = 1 millisecond delay. +* +* This subroutine is called upon to provide a delay from 1 millisecond to a few +* hundreds milliseconds each time. +* It is understood that due to a_usTimeDelay is defined as unsigned short, a 16 bits +* integer, this function is restricted to produce a delay to 64000 micro-seconds +* or 32000 milli-second maximum. The VME file will never pass on to this function +* a delay time > those maximum number. If it needs more than those maximum, the VME +* file will launch the delay function several times to realize a larger delay time +* cummulatively. +* It is perfectly alright to provide a longer delay than required. It is not +* acceptable if the delay is shorter. +* +* Delay function example--using the machine clock signal of the native CPU------ +* When porting ispVME to a native CPU environment, the speed of CPU or +* the system clock that drives the CPU is usually known. +* The speed or the time it takes for the native CPU to execute one for loop +* then can be calculated as follows: +* The for loop usually is compiled into the ASSEMBLY code as shown below: +* LOOP: DEC RA; +* JNZ LOOP; +* If each line of assembly code needs 4 machine cycles to execute, +* the total number of machine cycles to execute the loop is 2 x 4 = 8. +* Usually system clock = machine clock (the internal CPU clock). +* Note: Some CPU has a clock multiplier to double the system clock for + the machine clock. +* +* Let the machine clock frequency of the CPU be F, or 1 machine cycle = 1/F. +* The time it takes to execute one for loop = (1/F ) x 8. +* Or one micro-second = F(MHz)/8; +* +* Example: The CPU internal clock is set to 100Mhz, then one micro-second = 100/8 = 12 +* +* The C code shown below can be used to create the milli-second accuracy. +* Users only need to enter the speed of the cpu. +* +**********************************************************************************/ +void ispVMDelay(unsigned short a_usTimeDelay) +{ + if (a_usTimeDelay & 0x8000) { + a_usTimeDelay &= ~0x8000; /* change it to ms */ + usleep(a_usTimeDelay * 1000); /* Use usleep to improve accuracy */ + } else + usleep(a_usTimeDelay); + return; +} + +/********************************************************************************* +* +* calibration +* +* It is important to confirm if the delay function is indeed providing +* the accuracy required. Also one other important parameter needed +* checking is the clock frequency. +* Calibration will help to determine the system clock frequency +* and the loop_per_micro value for one micro-second delay of the target +* specific hardware. +* +**********************************************************************************/ +void calibration(void) +{ + /*Apply 2 pulses to TCK.*/ + writePort(JTAG_TCK, 0x00); + writePort(JTAG_TCK, 0x01); + writePort(JTAG_TCK, 0x00); + writePort(JTAG_TCK, 0x01); + writePort(JTAG_TCK, 0x00); + + /*Delay for 1 millisecond. Pass on 1000 or 0x8001 both = 1ms delay.*/ + ispVMDelay(0x8001); + + /*Apply 2 pulses to TCK*/ + writePort(JTAG_TCK, 0x01); + writePort(JTAG_TCK, 0x00); + writePort(JTAG_TCK, 0x01); + writePort(JTAG_TCK, 0x00); +} \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/include/debug_ispvme.h b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/include/debug_ispvme.h new file mode 100644 index 000000000000..17c6b02261ae --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/include/debug_ispvme.h @@ -0,0 +1,21 @@ +#ifndef __FIRMWARE_UPGRADE_DEBUG_H__ +#define __FIRMWARE_UPGRADE_DEBUG_H__ + +#define DEBUG_INFO_LEN 20 +#define DEBUG_FILE "/.firmware_upgrade_debug" +#define DEBUG_ON_ALL "3" +#define DEBUG_ON_KERN "2" +#define DEBUG_ON_INFO "1" +#define DEBUG_OFF_INFO "0" + +enum debug_s { + DEBUG_OFF = 0, /* debug off */ + DEBUG_APP_ON, /* debug app on */ + DEBUG_KERN_ON, /* kernel debug on */ + DEBUG_ALL_ON, /* debug app and kernel debug on */ + DEBUG_IGNORE, /* ignore debug */ +}; + +extern int firmware_upgrade_debug(void); + +#endif /* End of __FIRMWARE_UPGRADE_DEBUG_H__ */ \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/include/firmware_app_ispvme.h b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/include/firmware_app_ispvme.h new file mode 100644 index 000000000000..9cbb8c45caff --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/include/firmware_app_ispvme.h @@ -0,0 +1,119 @@ +#ifndef __FIRMWARE_APP_H__ +#define __FIRMWARE_APP_H__ + +#include +#include +#include + +/* Agreement with PKG_MGMT module */ +#define ERR_FW_CHECK_CPLD_UPGRADE (440 - 256) +#define ERR_FW_CHECK_FPGA_UPGRADE (441 - 256) +#define ERR_FW_DO_CPLD_UPGRADE (442 - 256) +#define ERR_FW_DO_FPGA_UPGRADE (443 - 256) +#define ERR_FW_UPGRADE (444 - 256) + +#define FIRMWARE_FAILED -1 +#define FIRMWARE_SUCCESS 0 + +#define FIRMWARE_ACTION_CHECK 0 +#define FIRMWARE_ACTION_UPGRADE 1 + +#define mem_clear(data, size) memset((data), 0, (size)) + +#define dbg_print(debug, fmt, arg...) \ + if (debug == DEBUG_APP_ON || debug == DEBUG_ALL_ON) \ + { do{printf(fmt,##arg);} while(0); } + +#define FIRMWARE_FILE_DIR_LEN 128 +#define FIRMWARE_NAME_LEN 48 +#define FIRMWARE_MAX_SLOT_NUM 32 + +#define FIRMWARE_CPLD_NAME "cpld" +#define FIRMWARE_FPGA_NAME "fpga" + +#define FIRMWARE_CPLD_EPM1270F256 "EPM1270F256" +#define FIRMWARE_CPLD_5M1270 "5M1270" +#define FIRMWARE_CPLD_TEST "test" +#define FIRMWARE_FPGA_TEST "test" + +/* ioctl command */ +#define FIRMWARE_TYPE 'F' + +#define FIRMWARE_JTAG_TDI _IOR(FIRMWARE_TYPE, 0, char) +#define FIRMWARE_JTAG_TDO _IOR(FIRMWARE_TYPE, 1, char) +#define FIRMWARE_JTAG_TCK _IOR(FIRMWARE_TYPE, 2, char) +#define FIRMWARE_JTAG_TMS _IOR(FIRMWARE_TYPE, 3, char) +#define FIRMWARE_JTAG_EN _IOR(FIRMWARE_TYPE, 4, char) +#define FIRMWARE_SET_DEBUG_ON _IOW(FIRMWARE_TYPE, 5, int) /* debug on */ +#define FIRMWARE_SET_DEBUG_OFF _IOW(FIRMWARE_TYPE, 6, int) /* debug off */ +#define FIRMWARE_SET_GPIO_INFO _IOR(FIRMWARE_TYPE, 7, int) /* GPIO info */ + +#define FIRMWARE_MAX_SUB_SLOT_NUM (8) +#define FIRMWARE_MAX_CARD_SLOT_NUM ((FIRMWARE_MAX_SUB_SLOT_NUM) + (1))/* max slot num */ +#define FIRMWARE_CARD_NAME_MAX_LEN (64) +#define GPIO(p, v, d) { \ + .pin = p, \ + .val = v, \ + .dir = d, \ + }, + +enum firmware_type_s { + FIRMWARE_CPLD = 0, + FIRMWARE_FPGA, + FIRMWARE_OTHER, +}; + +typedef struct name_info_s { + char card_name[FIRMWARE_NAME_LEN]; /* card name */ + int type; /* upgrade file type */ + int slot; /* slot number correspoding to upgrade file */ + char chip_name[FIRMWARE_NAME_LEN]; /* chip name */ + char version[FIRMWARE_NAME_LEN]; /* version */ +} name_info_t; + +typedef struct cmd_info_s { + int size; + void *data; +} cmd_info_t; + +typedef struct gpio_group_s { + int pin; + int val; + int dir; +} gpio_group_t; + +typedef struct firmware_upg_gpio_info_s { + int tdi; + int tck; + int tms; + int tdo; + int jtag_en; + int select; + gpio_group_t jtag_5; + gpio_group_t jtag_4; + gpio_group_t jtag_3; + gpio_group_t jtag_2; + gpio_group_t jtag_1; +} firmware_upg_gpio_info_t; + +typedef struct firmware_card_info_s { + int dev_type; /* the type of card */ + int slot_num; + char card_name[FIRMWARE_CARD_NAME_MAX_LEN]; + firmware_upg_gpio_info_t gpio_info[FIRMWARE_MAX_CARD_SLOT_NUM]; /* private data */ +} firmware_card_info_t; + +typedef enum card_type_e { + M2_W6010_48GT4X_FA = 0X4065, + M2_W6010_48GT4X_RA = 0X4065, +} card_type_t; + +extern firmware_card_info_t* firmware_get_card_info(int dev_type); +extern int dfd_fpga_upgrade_do_upgrade(char* upg_file); +extern int dfd_fpga_upgrade_test(void); +extern int ispvme_test(void); +extern int ispvme_main(int argc, char *argv[]); +/* External fpga dump interface */ +extern int dfd_fpga_upgrade_dump_flash(int argc, char* argv[]); + +#endif /* End of __FIRMWARE_APP_H__ */ \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/ispvm_ui.c b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/ispvm_ui.c new file mode 100644 index 000000000000..a06f9de9a808 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/ispvm_ui.c @@ -0,0 +1,845 @@ +/************************************************************** +* +* Lattice Semiconductor Corp. Copyright 2008 +* +* ispVME Embedded allows programming of Lattice's suite of FPGA +* devices on embedded systems through the JTAG port. The software +* is distributed in source code form and is open to re - distribution +* and modification where applicable. +* +* ispVME Embedded C Source comprised with 3 modules: +* ispvm_ui.c is the module provides input and output support. +* ivm_core.c is the module interpret the VME file(s). +* hardware.c is the module access the JTAG port of the device(s). +* +* The optional module cable.c is for supporting Lattice's parallel +* port ispDOWNLOAD cable on DOS and Windows 95/98 O/S. It can be +* requested from Lattice's ispVMSupport. +* +***************************************************************/ + +/************************************************************** +* +* Revision History of ispvm_ui.c +* +* 3/6/07 ht Added functions vme_out_char(),vme_out_hex(), +* vme_out_string() to provide output resources. +* Consolidate all printf() calls into the added output +* functions. +* +* 09/11/07 NN Added Global variables initialization +* 09/24/07 NN Added a switch allowing users to do calibration. +* Calibration will help to determine the system clock frequency +* and the count value for one micro-second delay of the target +* specific hardware. +* Removed Delay Percent support +* 11/15/07 NN moved the checking of the File CRC to the end of processing +* 08/28/08 NN Added Calculate checksum support. +***************************************************************/ + +#include +#include +#include +#include +#include +#include "vmopcode.h" +#include "common.h" + +#define dbg_print(debug, fmt, arg...) \ + if (debug == DEBUG_APP_ON || debug == DEBUG_ALL_ON) \ + { do{printf(fmt,##arg);} while(0); } + +/*************************************************************** +* +* File pointer to the VME file. +* +***************************************************************/ + +FILE *g_pVMEFile = NULL; + +/*************************************************************** +* +* Functions declared in this ispvm_ui.c module +* +***************************************************************/ +unsigned char GetByte(void); +void vme_out_char(unsigned char charOut); +void vme_out_hex(unsigned char hexOut); +void vme_out_string(char *stringOut); +void ispVMMemManager(signed char cTarget, unsigned short usSize); +void ispVMFreeMem(void); +void error_handler(short a_siRetCode, char *pszMessage); +signed char ispVM(const char *a_pszFilename); + +/*************************************************************** +* +* Global variables. +* +***************************************************************/ +unsigned short g_usPreviousSize = 0; +unsigned short g_usExpectedCRC = 0; + +/*************************************************************** +* +* External variables and functions declared in ivm_core.c module. +* +***************************************************************/ +extern signed char ispVMCode(); +extern void ispVMCalculateCRC32(unsigned char a_ucData); +extern void ispVMStart(); +extern void ispVMEnd(); +extern void ispVMStateMachine(signed char NextState); +extern void writePort(unsigned char a_ucPins, unsigned char a_ucValue); +extern void sclock(); +extern unsigned char readPort(); +extern unsigned short g_usCalculatedCRC; +extern unsigned short g_usDataType; +extern unsigned char *g_pucOutMaskData, +*g_pucInData, +*g_pucOutData, +*g_pucHIRData, +*g_pucTIRData, +*g_pucHDRData, +*g_pucTDRData, +*g_pucOutDMaskData, +*g_pucIntelBuffer; +extern unsigned char *g_pucHeapMemory; +extern unsigned short g_iHeapCounter; +extern unsigned short g_iHEAPSize; +extern unsigned short g_usIntelDataIndex; +extern unsigned short g_usIntelBufferSize; +extern LVDSPair *g_pLVDSList; +/* 08/28/08 NN Added Calculate checksum support. */ +extern unsigned long g_usChecksum; +extern unsigned int g_uiChecksumIndex; +/*************************************************************** +* +* External variables and functions declared in hardware.c module. +* +***************************************************************/ +extern void calibration(void); +extern unsigned short g_usCpu_Frequency; + +static int is_debug_on = -1; + +/*************************************************************** +* +* Supported VME versions. +* +***************************************************************/ + +const char *const g_szSupportedVersions[] = { "__VME2.0", "__VME3.0", "____12.0", "____12.1", 0 }; + +/*************************************************************** +* +* GetByte +* +* Returns a byte to the caller. The returned byte depends on the +* g_usDataType register. If the HEAP_IN bit is set, then the byte +* is returned from the HEAP. If the LHEAP_IN bit is set, then +* the byte is returned from the intelligent buffer. Otherwise, +* the byte is returned directly from the VME file. +* +***************************************************************/ + +char* strlwr(char *str) +{ + char *orig = str; + /* process the string */ + for (; *str != '\0'; str++) + *str = tolower(*str); + return orig; +} + +unsigned char GetByte() +{ + unsigned char ucData = 0; + + if (g_usDataType & HEAP_IN) { + + /*************************************************************** + * + * Get data from repeat buffer. + * + ***************************************************************/ + + if (g_iHeapCounter > g_iHEAPSize) { + + /*************************************************************** + * + * Data over-run. + * + ***************************************************************/ + + return 0xFF; + } + + ucData = g_pucHeapMemory[g_iHeapCounter++]; + } + else if ( g_usDataType & LHEAP_IN ) { + + /*************************************************************** + * + * Get data from intel buffer. + * + ***************************************************************/ + + if (g_usIntelDataIndex >= g_usIntelBufferSize) { + + /*************************************************************** + * + * Data over-run. + * + ***************************************************************/ + + return 0xFF; + } + + ucData = g_pucIntelBuffer[g_usIntelDataIndex++]; + } + else { + + /*************************************************************** + * + * Get data from file. + * + ***************************************************************/ + + ucData = (unsigned char)fgetc(g_pVMEFile); + + + if (feof(g_pVMEFile)) { + + /*************************************************************** + * + * Reached EOF. + * + ***************************************************************/ + + return 0xFF; + } + /*************************************************************** + * + * Calculate the 32-bit CRC if the expected CRC exist. + * + ***************************************************************/ + if( g_usExpectedCRC != 0) + { + ispVMCalculateCRC32(ucData); + } + } + + return (ucData); +} + +/*************************************************************** +* +* vme_out_char +* +* Send a character out to the output resource if available. +* The monitor is the default output resource. +* +* +***************************************************************/ +void vme_out_char(unsigned char charOut) +{ + dbg_print(is_debug_on, "%c", charOut); +} +/*************************************************************** +* +* vme_out_hex +* +* Send a character out as in hex format to the output resource +* if available. The monitor is the default output resource. +* +* +***************************************************************/ +void vme_out_hex(unsigned char hexOut) +{ + dbg_print(is_debug_on, "%.2X", hexOut); +} +/*************************************************************** +* +* vme_out_string +* +* Send a text string out to the output resource if available. +* The monitor is the default output resource. +* +* +***************************************************************/ +void vme_out_string(char *stringOut) +{ + if (is_debug_on == -1) { + is_debug_on = firmware_upgrade_debug(); + } + + dbg_print(is_debug_on, stringOut); +} +/*************************************************************** +* +* ispVMMemManager +* +* Allocate memory based on cTarget. The memory size is specified +* by usSize. +* +***************************************************************/ + +void ispVMMemManager(signed char cTarget, unsigned short usSize) +{ + switch (cTarget) { + case XTDI: + case TDI: + if (g_pucInData != NULL) { + if (g_usPreviousSize == usSize) { /*memory exist*/ + break; + } + else { + free(g_pucInData); + g_pucInData = NULL; + } + } + g_pucInData = (unsigned char *)malloc(usSize / 8 + 2); + g_usPreviousSize = usSize; + case XTDO: + case TDO: + if (g_pucOutData != NULL) { + if (g_usPreviousSize == usSize) { /*already exist*/ + break; + } + else { + free(g_pucOutData); + g_pucOutData = NULL; + } + } + g_pucOutData = (unsigned char *)malloc(usSize / 8 + 2); + g_usPreviousSize = usSize; + break; + case MASK: + if (g_pucOutMaskData != NULL) { + if (g_usPreviousSize == usSize) { /*already allocated*/ + break; + } + else { + free(g_pucOutMaskData); + g_pucOutMaskData = NULL; + } + } + g_pucOutMaskData = (unsigned char *)malloc(usSize / 8 + 2); + g_usPreviousSize = usSize; + break; + case HIR: + if (g_pucHIRData != NULL) { + free(g_pucHIRData); + g_pucHIRData = NULL; + } + g_pucHIRData = (unsigned char *)malloc(usSize / 8 + 2); + break; + case TIR: + if (g_pucTIRData != NULL) { + free(g_pucTIRData); + g_pucTIRData = NULL; + } + g_pucTIRData = (unsigned char *)malloc(usSize / 8 + 2); + break; + case HDR: + if (g_pucHDRData != NULL) { + free(g_pucHDRData); + g_pucHDRData = NULL; + } + g_pucHDRData = (unsigned char *)malloc(usSize / 8 + 2); + break; + case TDR: + if (g_pucTDRData != NULL) { + free(g_pucTDRData); + g_pucTDRData = NULL; + } + g_pucTDRData = (unsigned char *)malloc(usSize / 8 + 2); + break; + case HEAP: + if (g_pucHeapMemory != NULL) { + free(g_pucHeapMemory); + g_pucHeapMemory = NULL; + } + g_pucHeapMemory = (unsigned char *)malloc(usSize + 2); + break; + case DMASK: + if (g_pucOutDMaskData != NULL) { + if (g_usPreviousSize == usSize) { /*already allocated*/ + break; + } + else { + free(g_pucOutDMaskData); + g_pucOutDMaskData = NULL; + } + } + g_pucOutDMaskData = (unsigned char *)malloc(usSize / 8 + 2); + g_usPreviousSize = usSize; + break; + case LHEAP: + if (g_pucIntelBuffer != NULL) { + free(g_pucIntelBuffer); + g_pucIntelBuffer = NULL; + } + g_pucIntelBuffer = (unsigned char *)malloc(usSize + 2); + break; + case LVDS: + if (g_pLVDSList != NULL) { + free(g_pLVDSList); + g_pLVDSList = NULL; + } + g_pLVDSList = (LVDSPair * )calloc(usSize, sizeof(LVDSPair)); + break; + default: + return; + } +} + +/*************************************************************** +* +* ispVMFreeMem +* +* Free memory that were dynamically allocated. +* +***************************************************************/ + +void ispVMFreeMem() +{ + if (g_pucHeapMemory != NULL) { + free(g_pucHeapMemory); + g_pucHeapMemory = NULL; + } + + if (g_pucOutMaskData != NULL) { + free(g_pucOutMaskData); + g_pucOutMaskData = NULL; + } + + if (g_pucInData != NULL) { + free(g_pucInData); + g_pucInData = NULL; + } + + if (g_pucOutData != NULL) { + free(g_pucOutData); + g_pucOutData = NULL; + } + + if (g_pucHIRData != NULL) { + free(g_pucHIRData); + g_pucHIRData = NULL; + } + + if (g_pucTIRData != NULL) { + free(g_pucTIRData); + g_pucTIRData = NULL; + } + + if (g_pucHDRData != NULL) { + free(g_pucHDRData); + g_pucHDRData = NULL; + } + + if (g_pucTDRData != NULL) { + free(g_pucTDRData); + g_pucTDRData = NULL; + } + + if (g_pucOutDMaskData != NULL) { + free(g_pucOutDMaskData); + g_pucOutDMaskData = NULL; + } + + if (g_pucIntelBuffer != NULL) { + free(g_pucIntelBuffer); + g_pucIntelBuffer = NULL; + } + + if (g_pLVDSList != NULL) { + free(g_pLVDSList); + g_pLVDSList = NULL; + } +} + +/*************************************************************** +* +* error_handler +* +* Reports the error message. +* +***************************************************************/ + +void error_handler(short a_siRetCode, char *pszMessage) +{ + const char *pszErrorMessage[] = { "pass", + "verification fail", + "can't find the file", + "wrong file type", + "file error", + "option error", + "crc verification error" }; + + strcpy(pszMessage, pszErrorMessage[-a_siRetCode]); +} +/*************************************************************** +* +* ispVM +* +* The entry point of the ispVM embedded. If the version and CRC +* are verified, then the VME will be processed. +* +***************************************************************/ + +signed char ispVM(const char *a_pszFilename) +{ + char szFileVersion[9] = { 0 }; + signed char cRetCode = 0; + signed char cIndex = 0; + signed char cVersionIndex = 0; + unsigned char ucReadByte = 0; + + /*************************************************************** + * + * Global variables initialization. + * + * 09/11/07 NN Added + ***************************************************************/ + g_pucHeapMemory = NULL; + g_iHeapCounter = 0; + g_iHEAPSize = 0; + g_usIntelDataIndex = 0; + g_usIntelBufferSize = 0; + g_usPreviousSize = 0; + + /*************************************************************** + * + * Open a file pointer to the VME file. + * + ***************************************************************/ + + + + if ((g_pVMEFile = fopen(a_pszFilename, "rb")) == NULL) { + return VME_FILE_READ_FAILURE; + } + g_usCalculatedCRC = 0; + g_usExpectedCRC = 0; + ucReadByte = GetByte(); + //dbg_print(is_debug_on, "data: 0x%x.\n", ucReadByte); + switch (ucReadByte) { + case FILE_CRC: + + /*************************************************************** + * + * Read and store the expected CRC to do the comparison at the end. + * Only versions 3.0 and higher support CRC protection. + * + ***************************************************************/ + + g_usExpectedCRC = (unsigned char)fgetc(g_pVMEFile); + g_usExpectedCRC <<= 8; + g_usExpectedCRC |= fgetc(g_pVMEFile); + + /*************************************************************** + * + * Read and store the version of the VME file. + * + ***************************************************************/ + + + for (cIndex = 0; cIndex < 8; cIndex++) { + szFileVersion[cIndex] = GetByte(); + } + break; + default: + + /*************************************************************** + * + * Read and store the version of the VME file. Must be version 2.0. + * + ***************************************************************/ + + szFileVersion[0] = (signed char)ucReadByte; + for (cIndex = 1; cIndex < 8; cIndex++) { + szFileVersion[cIndex] = GetByte(); + } + + break; + } + + /*************************************************************** + * + * Compare the VME file version against the supported version. + * + ***************************************************************/ + /* check the file version: the value of 0x03-0x0a */ + for (cVersionIndex = 0; g_szSupportedVersions[cVersionIndex] != 0; cVersionIndex++) { + for (cIndex = 0; cIndex < 8; cIndex++) { + if (szFileVersion[cIndex] != g_szSupportedVersions[cVersionIndex][cIndex]) { + cRetCode = VME_VERSION_FAILURE; + break; + } + cRetCode = 0; + } + + if (cRetCode == 0) { + + /*************************************************************** + * + * Found matching version, break. + * + ***************************************************************/ + + break; + } + } + + if (cRetCode < 0) { + + /*************************************************************** + * + * VME file version failed to match the supported versions. + * + ***************************************************************/ + + fclose(g_pVMEFile); + g_pVMEFile = NULL; + return VME_VERSION_FAILURE; + } + + /*************************************************************** + * + * Enable the JTAG port to communicate with the device. + * Set the JTAG state machine to the Test-Logic/Reset State. + * + ***************************************************************/ + ispVMStart(); + + + /*************************************************************** + * + * Process the VME file. + * + ***************************************************************/ + + cRetCode = ispVMCode(); + + /*************************************************************** + * + * Set the JTAG State Machine to Test-Logic/Reset state then disable + * the communication with the JTAG port. + * + ***************************************************************/ + + ispVMEnd(); + + fclose(g_pVMEFile); + g_pVMEFile = NULL; + + ispVMFreeMem(); + + /*************************************************************** + * + * Compare the expected CRC versus the calculated CRC. + * + ***************************************************************/ + + if (cRetCode == 0 && g_usExpectedCRC != 0 && (g_usExpectedCRC != g_usCalculatedCRC)) { + printf("Expected CRC: 0x%.4X\n", g_usExpectedCRC); + printf("Calculated CRC: 0x%.4X\n", g_usCalculatedCRC); + return VME_CRC_FAILURE; + } + + return (cRetCode); +} + +/*************************************************************** +* +* main +* +***************************************************************/ +extern int g_file_fd; +int ispvme_main(int argc, char *argv[]) +{ + unsigned short iCommandLineIndex = 0; + short siRetCode = 0; + char szExtension[5] = { 0 }; + char szCommandLineArg[300] = { 0 }; + short sicalibrate = 0; + + /* 08/28/08 NN Added Calculate checksum support. */ + g_usChecksum = 0; + g_uiChecksumIndex = 0; + + g_file_fd = open("/dev/firmware_cpld_ispvme0", O_RDWR); + if (g_file_fd < 0) { + printf("can't open device\r\n"); + return -1; + } + + vme_out_string(" Lattice Semiconductor Corp.\n"); + vme_out_string("\n ispVME(tm) V"); + vme_out_string(VME_VERSION_NUMBER); + vme_out_string(" Copyright 1998-2011.\n"); + vme_out_string("\nFor daisy chain programming of all in-system programmable devices\n\n"); + + if (argc < 2) { + vme_out_string("\nUsage: vme [option] vme_file [vme_file]\n"); + vme_out_string("Example: vme vme_file1.vme vme_file2.vme\n"); + vme_out_string("option -c: do the calibration.\n"); + vme_out_string("Example: vme -c\n"); + vme_out_string("Example: vme -c vme_file1.vme vme_file2.vme\n"); + vme_out_string("\n\n"); + close(g_file_fd); + return -1; /* Used by superiors to judge whether the upgrade is successful */ + //exit(1); + } + for (iCommandLineIndex = 1; iCommandLineIndex < argc; iCommandLineIndex++) { + strcpy(szCommandLineArg, argv[iCommandLineIndex]); + if (!strcmp(strlwr(szCommandLineArg), "-c") && (iCommandLineIndex == 1)) { + sicalibrate = 1; + } else if (!strcmp(strlwr(szCommandLineArg), "-c") && (iCommandLineIndex != 1)) { + vme_out_string("Error: calibrate option -c must be the first argument\n\n"); + close(g_file_fd); + return -1; /* Used by superiors to judge whether the upgrade is successful */ + //exit(1); + } else { + strcpy(szExtension, &szCommandLineArg[strlen(szCommandLineArg) - 4]); + strlwr(szExtension); + if (strcmp(szExtension, ".vme")) { + vme_out_string("Error: VME files must end with the extension *.vme\n\n"); + close(g_file_fd); + return -1; /* Used by superiors to judge whether the upgrade is successful */ + //exit(1); + } + } + } + siRetCode = 0; + + if (sicalibrate) { + calibration(); + } + for (iCommandLineIndex = 1; iCommandLineIndex < argc; iCommandLineIndex++) { /* Process all VME files sequentially */ + strcpy(szCommandLineArg, argv[iCommandLineIndex]); + if (!strcmp(strlwr(szCommandLineArg), "-c") && (iCommandLineIndex == 1)) { + + } else if (!strcmp(strlwr(szCommandLineArg), "-checksum")) { + + } else { + vme_out_string("Processing virtual machine file ("); + vme_out_string(szCommandLineArg); + vme_out_string(")......\n\n"); + siRetCode = ispVM(szCommandLineArg); + if (siRetCode < 0) { + break; + } + } + } + + if (siRetCode < 0) { + error_handler(siRetCode, szCommandLineArg); + vme_out_string("Failed due to "); + vme_out_string(szCommandLineArg); + vme_out_string("\n\n"); + vme_out_string("+=======+\n"); + vme_out_string("| FAIL! |\n"); + vme_out_string("+=======+\n\n"); + } else { + vme_out_string("+=======+\n"); + vme_out_string("| PASS! |\n"); + vme_out_string("+=======+\n\n"); + /* 08/28/08 NN Added Calculate checksum support. */ + if (g_usChecksum != 0) { + g_usChecksum &= 0xFFFF; + printf("Data Checksum: %.4X\n\n", (unsigned int)g_usChecksum); + g_usChecksum = 0; + } + } + close(g_file_fd); + return siRetCode; /* Used by superiors to judge whether the upgrade is successful */ + //exit(siRetCode); +} + +/** +* function:CPLD online upgrade channel test program +* input parameters: None +* output parameters:None +* return: 0 means test successfully -1 means test failed +*/ +int ispvme_test(void) +{ + int ret = 0; + unsigned char index = 0; + unsigned char indata = 0,outdata = 0; + unsigned char datasize = 8; + unsigned int tmpdata = 0; + char curbit = 0; + int nDevices = 0; + + g_file_fd = open("/dev/firmware_cpld_ispvme0", O_RDWR); + if (g_file_fd < 0) { + printf("can't open device\r\n"); + return -1; + } + + /* go to Shift-IR */ + ispVMStart(); + ispVMStateMachine(IDLE); + ispVMStateMachine(IRPAUSE); + ispVMStateMachine(SHIFTIR); + + /* Send plenty of ones into the IR registers,that makes sure all devices are in BYPASS! */ + for (index = 0; index < 100; index++) { + writePort(JTAG_TDI, 1); + sclock(); + } + + /* exit Shift-IR,and go to Shift-DR */ + ispVMStateMachine(IRPAUSE); + ispVMStateMachine(DRPAUSE); + ispVMStateMachine(SHIFTDR); + + /* Send plenty of zeros into the DR registers to flush them */ + for (index = 0; index < 100; index++) { + writePort(JTAG_TDI, 0); + sclock(); + } + + /* now send ones until we receive one back,to find out number of device(s) */ + for(index = 0; index < 100; index++){ + writePort(JTAG_TDI, 1); + sclock(); + curbit = readPort(); + if(1 == curbit){ + nDevices = index + 1; + break; + } + } + + /*flush again */ + for (index = 0; index < 100; index++) { + writePort(JTAG_TDI, 0); + sclock(); + } + + /* test data probe */ + indata = 0x5a; + for (index = 0; index < datasize + nDevices; index++) { + tmpdata <<= 1; + curbit = readPort(); + tmpdata |= curbit; + writePort(JTAG_TDI, ((indata << index) & 0x80) ? 0x01:0x00); + sclock(); + } + outdata = (tmpdata & 0xFF); + if(outdata != indata){ + ret = -1; + } + + /* go to Test-Logic-Reset */ + ispVMStateMachine(IDLE); + ispVMStart(); + + /* close ispvme */ + close(g_file_fd); + return ret; +} \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/ivm_core.c b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/ivm_core.c new file mode 100644 index 000000000000..7d4f0ba13787 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/ivm_core.c @@ -0,0 +1,3033 @@ +/*************************************************************** +* +* Lattice Semiconductor Corp. Copyright 2009 +* +* ispVME Embedded allows programming of Lattice's suite of FPGA +* devices on embedded systems through the JTAG port. The software +* is distributed in source code form and is open to re - distribution +* and modification where applicable. +* +* Revision History of ivm_core.c module: +* 4/25/06 ht Change some variables from unsigned short or int +* to long int to make the code compiler independent. +* 5/24/06 ht Support using RESET (TRST) pin as a special purpose +* control pin such as triggering the loading of known +* state exit. +* 3/6/07 ht added functions to support output to terminals +* +* 09/24/07 NN Type cast mismatch variables +* Moved the sclock() function to hardware.c +* 08/28/08 NN Added Calculate checksum support. +* 4/1/09 Nguyen replaced the recursive function call codes on +* the ispVMLCOUNT function +* +***************************************************************/ + +#include +#include +#include "vmopcode.h" +#include "common.h" + +/*************************************************************** +* +* Global variables used to specify the flow control and data type. +* +* g_usFlowControl: flow control register. Each bit in the +* register can potentially change the +* personality of the embedded engine. +* g_usDataType: holds the data type of the current row. +* +***************************************************************/ + +unsigned short g_usFlowControl = 0x0000; +unsigned short g_usDataType = 0x0000; + +/*************************************************************** +* +* Global variables used to specify the ENDDR and ENDIR. +* +* g_ucEndDR: the state that the device goes to after SDR. +* g_ucEndIR: the state that the device goes to after SIR. +* +***************************************************************/ + +unsigned char g_ucEndDR = DRPAUSE; +unsigned char g_ucEndIR = IRPAUSE; + +/*************************************************************** +* +* Global variables used to support header/trailer. +* +* g_usHeadDR: the number of lead devices in bypass. +* g_usHeadIR: the sum of IR length of lead devices. +* g_usTailDR: the number of tail devices in bypass. +* g_usTailIR: the sum of IR length of tail devices. +* +***************************************************************/ + +unsigned short g_usHeadDR = 0; +unsigned short g_usHeadIR = 0; +unsigned short g_usTailDR = 0; +unsigned short g_usTailIR = 0; + +/*************************************************************** +* +* Global variable to store the number of bits of data or instruction +* to be shifted into or out from the device. +* +***************************************************************/ + +unsigned short g_usiDataSize = 0; + +/*************************************************************** +* +* Stores the frequency. Default to 1 MHz. +* +***************************************************************/ + +int g_iFrequency = 1000; + +/*************************************************************** +* +* Stores the maximum amount of ram needed to hold a row of data. +* +***************************************************************/ + +unsigned short g_usMaxSize = 0; + +/*************************************************************** +* +* Stores the LSH or RSH value. +* +***************************************************************/ + +unsigned short g_usShiftValue = 0; + +/*************************************************************** +* +* Stores the current repeat loop value. +* +***************************************************************/ + +unsigned short g_usRepeatLoops = 0; + +/*************************************************************** +* +* Stores the current vendor. +* +***************************************************************/ + +signed char g_cVendor = LATTICE; + +/*************************************************************** +* +* Stores the VME file CRC. +* +***************************************************************/ + +unsigned short g_usCalculatedCRC = 0; + +/*************************************************************** +* +* Stores the Device Checksum. +* +***************************************************************/ +//08/28/08 NN Added Calculate checksum support. +unsigned long g_usChecksum = 0; +unsigned int g_uiChecksumIndex = 0; + +/*************************************************************** +* +* Stores the current state of the JTAG state machine. +* +***************************************************************/ + +signed char g_cCurrentJTAGState = 0; + +/*************************************************************** +* +* Global variables used to support looping. +* +* g_pucHeapMemory: holds the entire repeat loop. +* g_iHeapCounter: points to the current byte in the repeat loop. +* g_iHEAPSize: the current size of the repeat in bytes. +* +***************************************************************/ + +unsigned char *g_pucHeapMemory = NULL; +unsigned short g_iHeapCounter = 0; +unsigned short g_iHEAPSize = 0; + +/*************************************************************** +* +* Global variables used to support intelligent programming. +* +* g_usIntelDataIndex: points to the current byte of the +* intelligent buffer. +* g_usIntelBufferSize: holds the size of the intelligent +* buffer. +* +***************************************************************/ + +unsigned short g_usIntelDataIndex = 0; +unsigned short g_usIntelBufferSize = 0; + +/**************************************************************************** +* +* Holds the maximum size of each respective buffer. These variables are used +* to write the HEX files when converting VME to HEX. +* +*****************************************************************************/ + +unsigned short g_usTDOSize = 0; +unsigned short g_usMASKSize = 0; +unsigned short g_usTDISize = 0; +unsigned short g_usDMASKSize = 0; +unsigned short g_usLCOUNTSize = 0; +unsigned short g_usHDRSize = 0; +unsigned short g_usTDRSize = 0; +unsigned short g_usHIRSize = 0; +unsigned short g_usTIRSize = 0; +unsigned short g_usHeapSize = 0; + +/*************************************************************** +* +* Global variables used to store data. +* +* g_pucOutMaskData: local RAM to hold one row of MASK data. +* g_pucInData: local RAM to hold one row of TDI data. +* g_pucOutData: local RAM to hold one row of TDO data. +* g_pucHIRData: local RAM to hold the current SIR header. +* g_pucTIRData: local RAM to hold the current SIR trailer. +* g_pucHDRData: local RAM to hold the current SDR header. +* g_pucTDRData: local RAM to hold the current SDR trailer. +* g_pucIntelBuffer: local RAM to hold the current intelligent buffer. +* g_pucOutDMaskData: local RAM to hold one row of DMASK data. +* +***************************************************************/ + +unsigned char *g_pucOutMaskData = NULL, +*g_pucInData = NULL, +*g_pucOutData = NULL, +*g_pucHIRData = NULL, +*g_pucTIRData = NULL, +*g_pucHDRData = NULL, +*g_pucTDRData = NULL, +*g_pucIntelBuffer = NULL, +*g_pucOutDMaskData = NULL; + +/*************************************************************** +* +* JTAG state machine transition table. +* +***************************************************************/ + +struct { + unsigned char CurState; /* From this state */ + unsigned char NextState; /* Step to this state */ + unsigned char Pattern; /* The tragetory of TMS */ + unsigned char Pulses; /* The number of steps */ +} g_JTAGTransistions[25] = { + { RESET, RESET, 0xFC, 6 }, /* Transitions from RESET */ + { RESET, IDLE, 0x00, 1 }, + { RESET, DRPAUSE, 0x50, 5 }, + { RESET, IRPAUSE, 0x68, 6 }, + { IDLE, RESET, 0xE0, 3 }, /* Transitions from IDLE */ + { IDLE, DRPAUSE, 0xA0, 4 }, + { IDLE, IRPAUSE, 0xD0, 5 }, + { DRPAUSE, RESET, 0xF8, 5 }, /* Transitions from DRPAUSE */ + { DRPAUSE, IDLE, 0xC0, 3 }, + { DRPAUSE, IRPAUSE, 0xF4, 7 }, + { DRPAUSE, DRPAUSE, 0xE8, 6 }, /* 06/14/06 Support POLING STATUS LOOP*/ + { IRPAUSE, RESET, 0xF8, 5 }, /* Transitions from IRPAUSE */ + { IRPAUSE, IDLE, 0xC0, 3 }, + { IRPAUSE, DRPAUSE, 0xE8, 6 }, + { DRPAUSE, SHIFTDR, 0x80, 2 }, /* Extra transitions using SHIFTDR */ + { IRPAUSE, SHIFTDR, 0xE0, 5 }, + { SHIFTDR, DRPAUSE, 0x80, 2 }, + { SHIFTDR, IDLE, 0xC0, 3 }, + { IRPAUSE, SHIFTIR, 0x80, 2 }, /* Extra transitions using SHIFTIR */ + { SHIFTIR, IRPAUSE, 0x80, 2 }, + { SHIFTIR, IDLE, 0xC0, 3 }, + { DRPAUSE, DRCAPTURE, 0xE0, 4 }, /* 11/15/05 Support DRCAPTURE*/ + { DRCAPTURE, DRPAUSE, 0x80, 2 }, + { IDLE, DRCAPTURE, 0x80, 2 }, + { IRPAUSE, DRCAPTURE, 0xE0, 4 } +}; + +/*************************************************************** +* +* List to hold all LVDS pairs. +* +***************************************************************/ + +LVDSPair *g_pLVDSList = NULL; +unsigned short g_usLVDSPairCount = 0; + +/*************************************************************** +* +* Function prototypes. +* +***************************************************************/ + +signed char ispVMCode(); +signed char ispVMDataCode(); +long int ispVMDataSize(); +void ispVMData(unsigned char *Data); +signed char ispVMShift(signed char Code); +signed char ispVMAmble(signed char Code); +signed char ispVMLoop(unsigned short a_usLoopCount); +signed char ispVMBitShift(signed char mode, unsigned short bits); +void ispVMComment(unsigned short a_usCommentSize); +void ispVMHeader(unsigned short a_usHeaderSize); +signed char ispVMLCOUNT(unsigned short a_usCountSize); +void ispVMClocks(unsigned short Clocks); +void ispVMBypass(signed char ScanType, unsigned short Bits); +void ispVMStateMachine(signed char NextState); +void ispVMStart(); +void ispVMEnd(); +signed char ispVMSend(unsigned short int); +signed char ispVMRead(unsigned short int); +signed char ispVMReadandSave(unsigned short int); +signed char ispVMProcessLVDS(unsigned short a_usLVDSCount); + +/*************************************************************** +* +* External variables and functions in ispvm_ui.c module +* +***************************************************************/ +extern void vme_out_char(unsigned char charOut); +extern void vme_out_hex(unsigned char hexOut); +extern void vme_out_string(char *stringOut); +extern unsigned char GetByte(); +extern void ispVMMemManager(signed char types, unsigned short size); + +/*************************************************************** +* +* External variables and functions in hardware.c module +* +***************************************************************/ +extern void ispVMDelay(unsigned short int a_usMicroSecondDelay); +extern unsigned char readPort(); +extern void writePort(unsigned char pins, unsigned char value); +extern void sclock(); +extern signed char g_cCurrentJTAGState; +#ifdef VME_DEBUG + +/*************************************************************** +* +* GetState +* +* Returns the state as a string based on the opcode. Only used +* for debugging purposes. +* +***************************************************************/ + +const char* GetState(unsigned char a_ucState) +{ + switch (a_ucState) { + case RESET: + return ("RESET"); + case IDLE: + return ("IDLE"); + case IRPAUSE: + return ("IRPAUSE"); + case DRPAUSE: + return ("DRPAUSE"); + case SHIFTIR: + return ("SHIFTIR"); + case SHIFTDR: + return ("SHIFTDR"); + case DRCAPTURE: /* 11/15/05 support DRCAPTURE*/ + return ("DRCAPTURE"); + default: + break; + } + + return 0; +} + +/*************************************************************** +* +* PrintData +* +* Prints the data. Only used for debugging purposes. +* +***************************************************************/ + +void PrintData(unsigned short a_iDataSize, unsigned char *a_pucData) +{ + //09/11/07 NN added local variables initialization + unsigned short usByteSize = 0; + unsigned short usBitIndex = 0; + signed short usByteIndex = 0; + unsigned char ucByte = 0; + unsigned char ucFlipByte = 0; + + if (a_iDataSize % 8) { + //09/11/07 NN Type cast mismatch variables + usByteSize = (unsigned short)(a_iDataSize / 8 + 1); + } else { + //09/11/07 NN Type cast mismatch variables + usByteSize = (unsigned short)(a_iDataSize / 8);// 4 + } + printf("("); + //09/11/07 NN Type cast mismatch variables + for (usByteIndex = (signed short)(usByteSize - 1); usByteIndex >= 0; usByteIndex--) { + ucByte = a_pucData[usByteIndex]; + ucFlipByte = 0x00; + + /*************************************************************** + * + * Flip each byte. + * + ***************************************************************/ + + for (usBitIndex = 0; usBitIndex < 8; usBitIndex++) { + ucFlipByte <<= 1; + if (ucByte & 0x1) { + ucFlipByte |= 0x1; + } + + ucByte >>= 1; + } + + /*************************************************************** + * + * Print the flipped byte. + * + ***************************************************************/ + + printf("%.02X", ucFlipByte); + if ((usByteSize - usByteIndex) % 40 == 39) { + printf("\n\t\t"); + } + if (usByteIndex < 0) + break; + } + printf(")"); +} +#endif /* VME_DEBUG */ + +/*************************************************************** +* +* ispVMDataSize +* +* Returns a VME-encoded number, usually used to indicate the +* bit length of an SIR/SDR command. +* +***************************************************************/ + +long int ispVMDataSize() +{ + /* 09/11/07 NN added local variables initialization */ + long int iSize = 0; + signed char cCurrentByte = 0; + signed char cIndex = 0; + cIndex = 0; + + while ((cCurrentByte = GetByte()) & 0x80) { + iSize |= ((long int)(cCurrentByte & 0x7F)) << cIndex; + cIndex += 7; + } + iSize |= ((long int)(cCurrentByte & 0x7F)) << cIndex; + + return iSize; +} + +/*************************************************************** +* +* ispVMCode +* +* This is the heart of the embedded engine. All the high-level opcodes +* are extracted here. Once they have been identified, then it +* will call other functions to handle the processing. +* +***************************************************************/ + +signed char ispVMCode() +{ + /* 09/11/07 NN added local variables initialization */ + unsigned short iRepeatSize = 0; + signed char cOpcode = 0; + signed char cRetCode = 0; + unsigned char ucState = 0; + unsigned short usDelay = 0; + unsigned short usToggle = 0; + unsigned char usByte = 0; + + /*************************************************************** + * + * Check the compression flag only if this is the first time + * this function is entered. Do not check the compression flag if + * it is being called recursively from other functions within + * the embedded engine. + * + ***************************************************************/ + + if (!(g_usDataType & LHEAP_IN) && !(g_usDataType & HEAP_IN)) { + usByte = GetByte(); + if (usByte == 0xf1) { + g_usDataType |= COMPRESS; + } else if (usByte == 0xf2) { + g_usDataType &= ~COMPRESS; + } else { + return VME_INVALID_FILE; + } + } + + + /*************************************************************** + * + * Begin looping through all the VME opcodes. + * + ***************************************************************/ + while ((cOpcode = GetByte()) >= 0) { + switch (cOpcode) { + case STATE: + + /*************************************************************** + * + * Step the JTAG state machine. + * + ***************************************************************/ + + ucState = GetByte(); + /*************************************************************** + * + * Step the JTAG state machine to DRCAPTURE to support Looping. + * + ***************************************************************/ + + if ((g_usDataType & LHEAP_IN) && + (ucState == DRPAUSE) && + (g_cCurrentJTAGState == ucState)) { + ispVMStateMachine(DRCAPTURE); + } + + ispVMStateMachine(ucState); + +#ifdef VME_DEBUG + if (g_usDataType & LHEAP_IN) { + printf("LDELAY %s ", GetState(ucState)); + } else { + printf("STATE %s;\n", GetState(ucState)); + } +#endif /* VME_DEBUG */ + break; + case SIR: + case SDR: + case XSDR: + +#ifdef VME_DEBUG + switch (cOpcode) { + case SIR: + printf("SIR "); + break; + case SDR: + case XSDR: + if (g_usDataType & LHEAP_IN) { + printf("LSDR "); + } else { + printf("SDR "); + } + break; + } +#endif /* VME_DEBUG */ + /*************************************************************** + * + * Shift in data into the device. + * + ***************************************************************/ + cRetCode = ispVMShift(cOpcode); + if (cRetCode != 0) { + return (cRetCode); + } + break; + case WAIT: + + /*************************************************************** + * + * Observe delay. + * + ***************************************************************/ + + /* 9/11/07 NN Type cast mismatch variables */ + usDelay = (unsigned short)ispVMDataSize(); + ispVMDelay(usDelay); + +#ifdef VME_DEBUG + if (usDelay & 0x8000) { + + /*************************************************************** + * + * Since MSB is set, the delay time must be decoded to + * millisecond. The SVF2VME encodes the MSB to represent + * millisecond. + * + ***************************************************************/ + + usDelay &= ~0x8000; + if (g_usDataType & LHEAP_IN) { + printf("%.2E SEC;\n", (float)usDelay / 1000); + } else { + printf("RUNTEST %.2E SEC;\n", (float)usDelay / 1000); + } + } else { + + /*************************************************************** + * + * Since MSB is not set, the delay time is given as microseconds. + * + ***************************************************************/ + + if (g_usDataType & LHEAP_IN) { + printf("%.2E SEC;\n", (float)usDelay / 1000000); + } else { + printf("RUNTEST %.2E SEC;\n", (float)usDelay / 1000000); + } + } +#endif /* VME_DEBUG */ + break; + case TCK: + + /*************************************************************** + * + * Issue clock toggles. + * + ***************************************************************/ + + /* 09/11/07 NN Type cast mismatch variables */ + usToggle = (unsigned short)ispVMDataSize(); + ispVMClocks(usToggle); + +#ifdef VME_DEBUG + printf("RUNTEST %d TCK;\n", usToggle); +#endif /* VME_DEBUG */ + break; + case ENDDR: + + /*************************************************************** + * + * Set the ENDDR. + * + ***************************************************************/ + + g_ucEndDR = GetByte(); + +#ifdef VME_DEBUG + printf("ENDDR %s;\n", GetState(g_ucEndDR)); +#endif /* VME_DEBUG */ + break; + case ENDIR: + + /*************************************************************** + * + * Set the ENDIR. + * + ***************************************************************/ + + g_ucEndIR = GetByte(); + +#ifdef VME_DEBUG + printf("ENDIR %s;\n", GetState(g_ucEndIR)); +#endif /* VME_DEBUG */ + break; + case HIR: + case TIR: + case HDR: + case TDR: + +#ifdef VME_DEBUG + switch (cOpcode) { + case HIR: + printf("HIR "); + break; + case TIR: + printf("TIR "); + break; + case HDR: + printf("HDR "); + break; + case TDR: + printf("TDR "); + break; + } +#endif /* VME_DEBUG */ + + /*************************************************************** + * + * Set the header/trailer of the device in order to bypass + * successfully. + * + ***************************************************************/ + + cRetCode = ispVMAmble(cOpcode); + if (cRetCode != 0) { + return (cRetCode); + } + +#ifdef VME_DEBUG + printf(";\n"); +#endif /* VME_DEBUG */ + break; + case MEM: + + /*************************************************************** + * + * The maximum RAM required to support processing one row of the + * VME file. + * + ***************************************************************/ + + //09/11/07 NN Type cast mismatch variables + g_usMaxSize = (unsigned short)ispVMDataSize(); + +#ifdef VME_DEBUG + printf("// MEMSIZE %d\n", g_usMaxSize); +#endif /* VME_DEBUG */ + break; + case VENDOR: + + /*************************************************************** + * + * Set the VENDOR type. + * + ***************************************************************/ + + cOpcode = GetByte(); + switch (cOpcode) { + case LATTICE: +#ifdef VME_DEBUG + printf("// VENDOR LATTICE\n"); +#endif /* VME_DEBUG */ + g_cVendor = LATTICE; + break; + case ALTERA: +#ifdef VME_DEBUG + printf("// VENDOR ALTERA\n"); +#endif /* VME_DEBUG */ + g_cVendor = ALTERA; + break; + case XILINX: +#ifdef VME_DEBUG + printf("// VENDOR XILINX\n"); +#endif /* VME_DEBUG */ + g_cVendor = XILINX; + break; + default: + break; + } + break; + case SETFLOW: + + /*************************************************************** + * + * Set the flow control. Flow control determines the personality + * of the embedded engine. + * + ***************************************************************/ + + /* 09/11/07 NN Type cast mismatch variables */ + g_usFlowControl |= (unsigned short)ispVMDataSize(); + break; + case RESETFLOW: + + /*************************************************************** + * + * Unset the flow control. + * + ***************************************************************/ + + /* 09/11/07 NN Type cast mismatch variables */ + g_usFlowControl &= (unsigned short)~(ispVMDataSize()); + break; + case HEAP: + + /*************************************************************** + * + * Allocate heap size to store loops. + * + ***************************************************************/ + + cRetCode = GetByte(); + if (cRetCode != SECUREHEAP) { + return VME_INVALID_FILE; + } + /* 09/11/07 NN Type cast mismatch variables */ + g_iHEAPSize = (unsigned short)ispVMDataSize(); + + /**************************************************************************** + * + * Store the maximum size of the HEAP buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if (g_iHEAPSize > g_usHeapSize) { + g_usHeapSize = g_iHEAPSize; + } + + ispVMMemManager(HEAP, (unsigned short)g_iHEAPSize); + break; + case REPEAT: + + /*************************************************************** + * + * Execute loops. + * + ***************************************************************/ + + g_usRepeatLoops = 0; + + /* 09/11/07 NN Type cast mismatch variables */ + iRepeatSize = (unsigned short)ispVMDataSize(); + + cRetCode = ispVMLoop((unsigned short)iRepeatSize); + if (cRetCode != 0) { + return (cRetCode); + } + break; + case ENDLOOP: + + /*************************************************************** + * + * Exit point from processing loops. + * + ***************************************************************/ + + return (cRetCode); + case ENDVME: + + /*************************************************************** + * + * The only valid exit point that indicates end of programming. + * + ***************************************************************/ + + return (cRetCode); + case SHR: + + /*************************************************************** + * + * Right-shift address. + * + ***************************************************************/ + + g_usFlowControl |= SHIFTRIGHT; + + /* 09/11/07 NN Type cast mismatch variables */ + g_usShiftValue = (unsigned short)(g_usRepeatLoops * (unsigned short)GetByte()); + break; + case SHL: + + /*************************************************************** + * + * Left-shift address. + * + ***************************************************************/ + + g_usFlowControl |= SHIFTLEFT; + + /* 09/11/07 NN Type cast mismatch variables */ + g_usShiftValue = (unsigned short)(g_usRepeatLoops * (unsigned short)GetByte()); + break; + case FREQUENCY: + + /*************************************************************** + * + * Set the frequency. + * + ***************************************************************/ + + /* 09/11/07 NN Type cast mismatch variables */ + g_iFrequency = (int)(ispVMDataSize()); + /* 10/23/08 NN changed to check if the frequency smaller than 1000 */ + if (g_iFrequency >= 1000) { + g_iFrequency = g_iFrequency / 1000; + if (g_iFrequency == 1) + g_iFrequency = 1000; +#ifdef VME_DEBUG + printf("FREQUENCY %.2E HZ;\n", (float)g_iFrequency * 1000); +#endif /* VME_DEBUG */ + } else { + if (g_iFrequency == 0) + g_iFrequency = 1000; +#ifdef VME_DEBUG + printf("FREQUENCY %.2E HZ;\n", (float)g_iFrequency); +#endif /* VME_DEBUG */ + } + break; + case LCOUNT: + + /*************************************************************** + * + * Process LCOUNT command. + * + ***************************************************************/ + + cRetCode = ispVMLCOUNT((unsigned short)ispVMDataSize()); + if (cRetCode != 0) { + return (cRetCode); + } + break; + case VUES: + + /*************************************************************** + * + * Set the flow control to verify USERCODE. + * + ***************************************************************/ + + g_usFlowControl |= VERIFYUES; + break; + case COMMENT: + + /*************************************************************** + * + * Display comment. + * + ***************************************************************/ + + ispVMComment((unsigned short)ispVMDataSize()); + break; + case LVDS: + + /*************************************************************** + * + * Process LVDS command. + * + ***************************************************************/ + + ispVMProcessLVDS((unsigned short)ispVMDataSize()); + break; + case HEADER: + + /*************************************************************** + * + * Discard header. + * + ***************************************************************/ + + ispVMHeader((unsigned short)ispVMDataSize()); + break; + /* 03/14/06 Support Toggle ispENABLE signal */ + case ispEN: + ucState = GetByte(); + if ((ucState == ON) || (ucState == 0x01)) + writePort(JTAG_ENABLE, 0x01); + else + writePort(JTAG_ENABLE, 0x00); + ispVMDelay(1); + break; + /* 05/24/06 support Toggle TRST pin */ + case TRST: + ucState = GetByte(); + if (ucState == 0x01) + writePort(JTAG_TRST, 0x01); + else + writePort(JTAG_TRST, 0x00); + ispVMDelay(1); + break; + default: + + /*************************************************************** + * + * Invalid opcode encountered. + * + ***************************************************************/ + +#ifdef VME_DEBUG + printf("\nINVALID OPCODE: 0x%.2X\n", cOpcode); +#endif /* VME_DEBUG */ + + return VME_INVALID_FILE; + } + } + + /*************************************************************** + * + * Invalid exit point. Processing the token 'ENDVME' is the only + * valid way to exit the embedded engine. + * + ***************************************************************/ + + return (VME_INVALID_FILE); +} + +/*************************************************************** +* +* ispVMDataCode +* +* Processes the TDI/TDO/MASK/DMASK etc of an SIR/SDR command. +* +***************************************************************/ + +signed char ispVMDataCode() +{ + /* 09/11/07 NN added local variables initialization */ + signed char cDataByte = 0; + signed char siDataSource = 0; /* source of data from file by default */ + + if (g_usDataType & HEAP_IN) { + siDataSource = 1; /* the source of data from memory */ + } + + /**************************************************************************** + * + * Clear the data type register. + * + *****************************************************************************/ + + g_usDataType &= ~(MASK_DATA + TDI_DATA + TDO_DATA + DMASK_DATA + CMASK_DATA); + + /**************************************************************************** + * + * Iterate through SIR/SDR command and look for TDI, TDO, MASK, etc. + * + *****************************************************************************/ + + while ((cDataByte = GetByte()) >= 0) { + + ispVMMemManager(cDataByte, g_usMaxSize); + switch (cDataByte) { + case TDI: + + /**************************************************************************** + * + * Store the maximum size of the TDI buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if (g_usiDataSize > g_usTDISize) { + g_usTDISize = g_usiDataSize; + } + /**************************************************************************** + * + * Updated data type register to indicate that TDI data is currently being + * used. Process the data in the VME file into the TDI buffer. + * + *****************************************************************************/ + + g_usDataType |= TDI_DATA; + ispVMData(g_pucInData); + break; + case XTDO: + + /**************************************************************************** + * + * Store the maximum size of the TDO buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if (g_usiDataSize > g_usTDOSize) { + g_usTDOSize = g_usiDataSize; + } + + /**************************************************************************** + * + * Updated data type register to indicate that TDO data is currently being + * used. + * + *****************************************************************************/ + + g_usDataType |= TDO_DATA; + break; + case TDO: + + /**************************************************************************** + * + * Store the maximum size of the TDO buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if (g_usiDataSize > g_usTDOSize) { + g_usTDOSize = g_usiDataSize; + } + + /**************************************************************************** + * + * Updated data type register to indicate that TDO data is currently being + * used. Process the data in the VME file into the TDO buffer. + * + *****************************************************************************/ + + g_usDataType |= TDO_DATA; + ispVMData(g_pucOutData); + break; + case MASK: + + /**************************************************************************** + * + * Store the maximum size of the MASK buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if (g_usiDataSize > g_usMASKSize) { + g_usMASKSize = g_usiDataSize; + } + + /**************************************************************************** + * + * Updated data type register to indicate that MASK data is currently being + * used. Process the data in the VME file into the MASK buffer. + * + *****************************************************************************/ + + g_usDataType |= MASK_DATA; + ispVMData(g_pucOutMaskData); + break; + case DMASK: + + /**************************************************************************** + * + * Store the maximum size of the DMASK buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if (g_usiDataSize > g_usDMASKSize) { + g_usDMASKSize = g_usiDataSize; + } + + /**************************************************************************** + * + * Updated data type register to indicate that DMASK data is currently being + * used. Process the data in the VME file into the DMASK buffer. + * + *****************************************************************************/ + + g_usDataType |= DMASK_DATA; + ispVMData(g_pucOutDMaskData); + break; + case CMASK: + + /**************************************************************************** + * + * Updated data type register to indicate that CMASK data is currently being + * used. Process the data in the VME file into the CMASK buffer. + * + *****************************************************************************/ + + g_usDataType |= CMASK_DATA; + ispVMData(g_pucOutMaskData); + break; + case CONTINUE: + return (0); + default: + + /**************************************************************************** + * + * Encountered invalid opcode. + * + *****************************************************************************/ + + return (VME_INVALID_FILE); + } + + switch (cDataByte) { + case TDI: + + /**************************************************************************** + * + * Left bit shift. Used when performing algorithm looping. + * + *****************************************************************************/ + + if (g_usFlowControl & SHIFTLEFT) { + ispVMBitShift(SHL, g_usShiftValue); + g_usFlowControl &= ~SHIFTLEFT; + } + + /**************************************************************************** + * + * Right bit shift. Used when performing algorithm looping. + * + *****************************************************************************/ + + if (g_usFlowControl & SHIFTRIGHT) { + ispVMBitShift(SHR, g_usShiftValue); + g_usFlowControl &= ~SHIFTRIGHT; + } + default: + break; + } + + if (siDataSource) { + g_usDataType |= HEAP_IN; /*restore data from memory*/ + } + } + + if (siDataSource) { /*fetch data from heap memory upon return*/ + g_usDataType |= HEAP_IN; + } + + if (cDataByte < 0) { + + /**************************************************************************** + * + * Encountered invalid opcode. + * + *****************************************************************************/ + + return (VME_INVALID_FILE); + } else { + return (0); + } +} + +/*************************************************************** +* +* ispVMData +* Extract one row of data operand from the current data type opcode. Perform +* the decompression if necessary. Extra RAM is not required for the +* decompression process. The decompression scheme employed in this module +* is on row by row basis. The format of the data stream: +* [compression code][compressed data stream] +* 0x00 --No compression +* 0x01 --Compress by 0x00. +* Example: +* Original stream: 0x000000000000000000000001 +* Compressed stream: 0x01000901 +* Detail: 0x01 is the code, 0x00 is the key, +* 0x09 is the count of 0x00 bytes, +* 0x01 is the uncompressed byte. +* 0x02 --Compress by 0xFF. +* Example: +* Original stream: 0xFFFFFFFFFFFFFFFFFFFFFF01 +* Compressed stream: 0x02FF0901 +* Detail: 0x02 is the code, 0xFF is the key, +* 0x09 is the count of 0xFF bytes, +* 0x01 is the uncompressed byte. +* 0x03 +* : : +* 0xFE -- Compress by nibble blocks. +* Example: +* Original stream: 0x84210842108421084210 +* Compressed stream: 0x0584210 +* Detail: 0x05 is the code, means 5 nibbles block. +* 0x84210 is the 5 nibble blocks. +* The whole row is 80 bits given by g_usiDataSize. +* The number of times the block repeat itself +* is found by g_usiDataSize/(4*0x05) which is 4. +* 0xFF -- Compress by the most frequently happen byte. +* Example: +* Original stream: 0x04020401030904040404 +* Compressed stream: 0xFF04(0,1,0x02,0,1,0x01,1,0x03,1,0x09,0,0,0) +* or: 0xFF044090181C240 +* Detail: 0xFF is the code, 0x04 is the key. +* a bit of 0 represent the key shall be put into +* the current bit position and a bit of 1 +* represent copying the next of 8 bits of data +* in. +* +***************************************************************/ + +void ispVMData(unsigned char *ByteData) +{ + /* 09/11/07 NN added local variables initialization */ + unsigned short size = 0; + unsigned short i, j, m, getData = 0; + unsigned char cDataByte = 0; + unsigned char compress = 0; + unsigned short FFcount = 0; + unsigned char compr_char = 0xFF; + unsigned short index = 0; + signed char compression = 0; + + /* convert number in bits to bytes */ + if (g_usiDataSize % 8 > 0) { + /* 09/11/07 NN Type cast mismatch variables */ + size = (unsigned short)(g_usiDataSize / 8 + 1); + } else { + /* 09/11/07 NN Type cast mismatch variables */ + size = (unsigned short)(g_usiDataSize / 8); + } + + /* If there is compression, then check if compress by key of 0x00 or 0xFF + or by other keys or by nibble blocks*/ + + if (g_usDataType & COMPRESS) { + compression = 1; + if (((compress = GetByte()) == VAR) && (g_usDataType & HEAP_IN)) { + getData = 1; + g_usDataType &= ~(HEAP_IN); + compress = GetByte(); + } + + switch (compress) { + case 0x00: + /* No compression */ + compression = 0; + break; + case 0x01: + /* Compress by byte 0x00 */ + compr_char = 0x00; + break; + case 0x02: + /* Compress by byte 0xFF */ + compr_char = 0xFF; + break; + case 0xFF: + /* Huffman encoding */ + compr_char = GetByte(); + i = 8; + for (index = 0; index < size; index++) { + ByteData[index] = 0x00; + if (i > 7) { + cDataByte = GetByte(); + i = 0; + } + if ((cDataByte << i++) & 0x80) + m = 8; + else { + ByteData[index] = compr_char; + m = 0; + } + + for (j = 0; j < m; j++) { + if (i > 7) { + cDataByte = GetByte(); + i = 0; + } + ByteData[index] |= ((cDataByte << i++) & 0x80) >> j; + } + } + size = 0; + break; + default: + for (index = 0; index < size; index++) + ByteData[index] = 0x00; + for (index = 0; index < compress; index++) { + if (index % 2 == 0) + cDataByte = GetByte(); + for (i = 0; i < size * 2 / compress; i++) { + /* 09/11/07 NN Type cast mismatch variables */ + j = (unsigned short)(index + (i * (unsigned short)compress)); + /* clear the nibble to zero first */ + if (j % 2) { + if (index % 2) + ByteData[j / 2] |= cDataByte & 0x0F; + else + ByteData[j / 2] |= cDataByte >> 4; + } else { + if (index % 2) + ByteData[j / 2] |= cDataByte << 4; + else + ByteData[j / 2] |= cDataByte & 0xF0; + } + } + } + size = 0; + break; + } + } + + FFcount = 0; + + /* Decompress by byte 0x00 or 0xFF */ + for (index = 0; index < size; index++) { + if (FFcount <= 0) { + cDataByte = GetByte(); + if ((cDataByte == VAR) && (g_usDataType & HEAP_IN) && !getData && !(g_usDataType & COMPRESS)) { + getData = 1; + g_usDataType &= ~(HEAP_IN); + cDataByte = GetByte(); + } + ByteData[index] = cDataByte; + if ((compression) && (cDataByte == compr_char)) /* decompression is on */ + /* 09/11/07 NN Type cast mismatch variables */ + FFcount = (unsigned short)ispVMDataSize(); /* The number of 0xFF or 0x00 bytes */ + } else { + FFcount--; /* Use up the 0xFF chain first */ + ByteData[index] = compr_char; + } + } + + if (getData) { + g_usDataType |= HEAP_IN; + getData = 0; + } +} + +/*************************************************************** +* +* ispVMShift +* +* Processes the SDR/XSDR/SIR commands. +* +***************************************************************/ + +signed char ispVMShift(signed char a_cCode) +{ + /* 09/11/07 NN added local variables initialization */ + unsigned short iDataIndex = 0; + unsigned short iReadLoop = 0; + signed char cRetCode = 0; + + cRetCode = 0; + /* 09/11/07 NN Type cast mismatch variables */ + g_usiDataSize = (unsigned short)ispVMDataSize(); + + g_usDataType &= ~(SIR_DATA + EXPRESS + SDR_DATA); /* clear the flags first */ + + switch (a_cCode) { + case SIR: + g_usDataType |= SIR_DATA; + /* 1/15/04 If performing cascading, then go directly to SHIFTIR. Else, + go to IRPAUSE before going to SHIFTIR */ + if (g_usFlowControl & CASCADE) { + ispVMStateMachine(SHIFTIR); + } else { + ispVMStateMachine(IRPAUSE); + ispVMStateMachine(SHIFTIR); + if (g_usHeadIR > 0) { + ispVMBypass(HIR, g_usHeadIR); + sclock(); + } + } + break; + case XSDR: + g_usDataType |= EXPRESS; /* mark simultaneous in and out */ + case SDR: + g_usDataType |= SDR_DATA; + /* 1/15/04 If already in SHIFTDR, then do not move state or shift in header. + This would imply that the previously shifted frame was a cascaded frame. */ + if (g_cCurrentJTAGState != SHIFTDR) { + /* 1/15/04 If performing cascading, then go directly to SHIFTDR. Else, + go to DRPAUSE before going to SHIFTDR */ + if (g_usFlowControl & CASCADE) { + if (g_cCurrentJTAGState == DRPAUSE) { + ispVMStateMachine(SHIFTDR); + /* 1/15/04 If cascade flag has been set and the current state is + DRPAUSE, this implies that the first cascaded frame is about to + be shifted in. The header must be shifted prior to shifting + the first cascaded frame. */ + if (g_usHeadDR > 0) { + ispVMBypass(HDR, g_usHeadDR); + sclock(); + } + } else { + ispVMStateMachine(SHIFTDR); + } + } else { + ispVMStateMachine(DRPAUSE); + ispVMStateMachine(SHIFTDR); + if (g_usHeadDR > 0) { + ispVMBypass(HDR, g_usHeadDR); + sclock(); + } + } + } + break; + default: + return (VME_INVALID_FILE); + } + + cRetCode = ispVMDataCode(); + if (cRetCode != 0) { + return (VME_INVALID_FILE); + } + +#ifdef VME_DEBUG + if (g_usDataType & TDI_DATA) { + printf("\n\t\tTDI "); + PrintData(g_usiDataSize, g_pucInData); + } + + if (g_usDataType & TDO_DATA) { + printf("\n\t\tTDO "); + PrintData(g_usiDataSize, g_pucOutData); + } + + if (g_usDataType & MASK_DATA) { + printf("\n\t\tMASK "); + PrintData(g_usiDataSize, g_pucOutMaskData); + } + + if (g_usDataType & DMASK_DATA) { + printf("\n\t\tDMASK "); + PrintData(g_usiDataSize, g_pucOutDMaskData); + } + + printf(";\n"); +#endif /* VME_DEBUG */ + if (g_usDataType & TDO_DATA || g_usDataType & DMASK_DATA) { + if (g_usDataType & DMASK_DATA) { + + cRetCode = ispVMReadandSave(g_usiDataSize); + + if (!cRetCode) { + if (g_usTailDR > 0) { + sclock(); + ispVMBypass(TDR, g_usTailDR); + } + ispVMStateMachine(DRPAUSE); + ispVMStateMachine(SHIFTDR); + if (g_usHeadDR > 0) { + ispVMBypass(HDR, g_usHeadDR); + sclock(); + } + for (iDataIndex = 0; iDataIndex < g_usiDataSize / 8 + 1; iDataIndex++) + g_pucInData[iDataIndex] = g_pucOutData[iDataIndex]; + g_usDataType &= ~(TDO_DATA + DMASK_DATA); + cRetCode = ispVMSend(g_usiDataSize); + } + } else { + + cRetCode = ispVMRead(g_usiDataSize); + if (cRetCode == -1 && g_cVendor == XILINX) { + for (iReadLoop = 0; iReadLoop < 30; iReadLoop++) { + cRetCode = ispVMRead(g_usiDataSize); + if (!cRetCode) { + break; + } else { + ispVMStateMachine(DRPAUSE); /*Always DRPAUSE*/ + /*Bypass other devices when appropriate*/ + ispVMBypass(TDR, g_usTailDR); + ispVMStateMachine(g_ucEndDR); + ispVMStateMachine(IDLE); + ispVMDelay(1000); + } + } + } + } + } else { /* TDI only */ + cRetCode = ispVMSend(g_usiDataSize); + + } + + /* transfer the input data to the output buffer for the next verify */ + if ((g_usDataType & EXPRESS) || (a_cCode == SDR)) { + if (g_pucOutData) { + for (iDataIndex = 0; iDataIndex < g_usiDataSize / 8 + 1; iDataIndex++) + g_pucOutData[iDataIndex] = g_pucInData[iDataIndex]; + } + } + + switch (a_cCode) { + case SIR: + /* 1/15/04 If not performing cascading, then shift ENDIR */ + if (!(g_usFlowControl & CASCADE)) { + if (g_usTailIR > 0) { + sclock(); + ispVMBypass(TIR, g_usTailIR); + } + ispVMStateMachine(g_ucEndIR); + } + break; + case XSDR: + case SDR: + /* 1/15/04 If not performing cascading, then shift ENDDR */ + if (!(g_usFlowControl & CASCADE)) { + if (g_usTailDR > 0) { + sclock(); + ispVMBypass(TDR, g_usTailDR); + } + ispVMStateMachine(g_ucEndDR); + } + break; + default: + break; + } + + return (cRetCode); +} + +/*************************************************************** +* +* ispVMAmble +* +* This routine is to extract Header and Trailer parameter for SIR and +* SDR operations. +* +* The Header and Trailer parameter are the pre-amble and post-amble bit +* stream need to be shifted into TDI or out of TDO of the devices. Mostly +* is for the purpose of bypassing the leading or trailing devices. ispVM +* supports only shifting data into TDI to bypass the devices. +* +* For a single device, the header and trailer parameters are all set to 0 +* as default by ispVM. If it is for multiple devices, the header and trailer +* value will change as specified by the VME file. +* +***************************************************************/ + +signed char ispVMAmble(signed char Code) +{ + signed char compress = 0; + /* 09/11/07 NN Type cast mismatch variables */ + g_usiDataSize = (unsigned short)ispVMDataSize(); + +#ifdef VME_DEBUG + printf("%d", g_usiDataSize); +#endif /* VME_DEBUG */ + + if (g_usiDataSize) { + + /**************************************************************************** + * + * Discard the TDI byte and set the compression bit in the data type register + * to false if compression is set because TDI data after HIR/HDR/TIR/TDR is not + * compressed. + * + *****************************************************************************/ + + GetByte(); + if (g_usDataType & COMPRESS) { + g_usDataType &= ~(COMPRESS); + compress = 1; + } + } + + switch (Code) { + case HIR: + + /**************************************************************************** + * + * Store the maximum size of the HIR buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if (g_usiDataSize > g_usHIRSize) { + g_usHIRSize = g_usiDataSize; + } + + /**************************************************************************** + * + * Assign the HIR value and allocate memory. + * + *****************************************************************************/ + + g_usHeadIR = g_usiDataSize; + if (g_usHeadIR) { + ispVMMemManager(HIR, g_usHeadIR); + ispVMData(g_pucHIRData); + +#ifdef VME_DEBUG + printf(" TDI "); + PrintData(g_usHeadIR, g_pucHIRData); +#endif /* VME_DEBUG */ + } + break; + case TIR: + + /**************************************************************************** + * + * Store the maximum size of the TIR buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if (g_usiDataSize > g_usTIRSize) { + g_usTIRSize = g_usiDataSize; + } + + /**************************************************************************** + * + * Assign the TIR value and allocate memory. + * + *****************************************************************************/ + + g_usTailIR = g_usiDataSize; + if (g_usTailIR) { + ispVMMemManager(TIR, g_usTailIR); + ispVMData(g_pucTIRData); + +#ifdef VME_DEBUG + printf(" TDI "); + PrintData(g_usTailIR, g_pucTIRData); +#endif /* VME_DEBUG */ + } + break; + case HDR: + + /**************************************************************************** + * + * Store the maximum size of the HDR buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if (g_usiDataSize > g_usHDRSize) { + g_usHDRSize = g_usiDataSize; + } + + /**************************************************************************** + * + * Assign the HDR value and allocate memory. + * + *****************************************************************************/ + + g_usHeadDR = g_usiDataSize; + if (g_usHeadDR) { + ispVMMemManager(HDR, g_usHeadDR); + ispVMData(g_pucHDRData); + +#ifdef VME_DEBUG + printf(" TDI "); + PrintData(g_usHeadDR, g_pucHDRData); +#endif /* VME_DEBUG */ + } + break; + case TDR: + + /**************************************************************************** + * + * Store the maximum size of the TDR buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if (g_usiDataSize > g_usTDRSize) { + g_usTDRSize = g_usiDataSize; + } + + /**************************************************************************** + * + * Assign the TDR value and allocate memory. + * + *****************************************************************************/ + + g_usTailDR = g_usiDataSize; + if (g_usTailDR) { + ispVMMemManager(TDR, g_usTailDR); + ispVMData(g_pucTDRData); + +#ifdef VME_DEBUG + printf(" TDI "); + PrintData(g_usTailDR, g_pucTDRData); +#endif /* VME_DEBUG */ + } + break; + default: + break; + } + + /**************************************************************************** + * + * Re-enable compression if it was previously set. + * + *****************************************************************************/ + + if (compress) { + g_usDataType |= COMPRESS; + } + + if (g_usiDataSize) { + Code = GetByte(); + if (Code == CONTINUE) { + return 0; + } else { + + /**************************************************************************** + * + * Encountered invalid opcode. + * + *****************************************************************************/ + + return VME_INVALID_FILE; + } + } + + return 0; +} + +/*************************************************************** +* +* ispVMLoop +* +* Perform the function call upon by the REPEAT opcode. +* Memory is to be allocated to store the entire loop from REPEAT to ENDLOOP. +* After the loop is stored then execution begin. The REPEATLOOP flag is set +* on the g_usFlowControl register to indicate the repeat loop is in session +* and therefore fetch opcode from the memory instead of from the file. +* +***************************************************************/ + +signed char ispVMLoop(unsigned short a_usLoopCount) +{ + /* 09/11/07 NN added local variables initialization */ + signed char cRetCode = 0; + unsigned short iHeapIndex = 0; + unsigned short iLoopIndex = 0; + + g_usShiftValue = 0; + for (iHeapIndex = 0; iHeapIndex < g_iHEAPSize; iHeapIndex++) { + g_pucHeapMemory[iHeapIndex] = GetByte(); + } + + if (g_pucHeapMemory[iHeapIndex - 1] != ENDLOOP) { + return (VME_INVALID_FILE); + } + + g_usFlowControl |= REPEATLOOP; + g_usDataType |= HEAP_IN; + + for (iLoopIndex = 0; iLoopIndex < a_usLoopCount; iLoopIndex++) { + g_iHeapCounter = 0; + cRetCode = ispVMCode(); + g_usRepeatLoops++; + if (cRetCode < 0) { + break; + } + } + + g_usDataType &= ~(HEAP_IN); + g_usFlowControl &= ~(REPEATLOOP); + return (cRetCode); +} + +/*************************************************************** +* +* ispVMBitShift +* +* Shift the TDI stream left or right by the number of bits. The data in +* *g_pucInData is of the VME format, so the actual shifting is the reverse of +* IEEE 1532 or SVF format. +* +***************************************************************/ + +signed char ispVMBitShift(signed char mode, unsigned short bits) +{ + /* 09/11/07 NN added local variables initialization */ + unsigned short i = 0; + unsigned short size = 0; + unsigned short tmpbits = 0; + + if (g_usiDataSize % 8 > 0) { + /* 09/11/07 NN Type cast mismatch variables */ + size = (unsigned short)(g_usiDataSize / 8 + 1); + } else { + /* 09/11/07 NN Type cast mismatch variables */ + size = (unsigned short)(g_usiDataSize / 8); + } + + switch (mode) { + case SHR: + for (i = 0; i < size; i++) { + if (g_pucInData[i] != 0) { + tmpbits = bits; + while (tmpbits > 0) { + g_pucInData[i] <<= 1; + if (g_pucInData[i] == 0) { + i--; + g_pucInData[i] = 1; + } + tmpbits--; + } + } + } + break; + case SHL: + for (i = 0; i < size; i++) { + if (g_pucInData[i] != 0) { + tmpbits = bits; + while (tmpbits > 0) { + g_pucInData[i] >>= 1; + if (g_pucInData[i] == 0) { + i--; + g_pucInData[i] = 8; + } + tmpbits--; + } + } + } + break; + default: + return (VME_INVALID_FILE); + } + + return (0); +} + +/*************************************************************** +* +* ispVMComment +* +* Displays the SVF comments. +* +***************************************************************/ + +void ispVMComment(unsigned short a_usCommentSize) +{ + char cCurByte = 0; + int count = 0; + for (; a_usCommentSize > 0; a_usCommentSize--) { + /**************************************************************************** + * + * Print character to the terminal. + * + *****************************************************************************/ + cCurByte = GetByte(); + vme_out_char(cCurByte); + count ++ ; + } + cCurByte = '\n'; + vme_out_char(cCurByte); +} + +/*************************************************************** +* +* ispVMHeader +* +* Iterate the length of the header and discard it. +* +***************************************************************/ + +void ispVMHeader(unsigned short a_usHeaderSize) +{ + for (; a_usHeaderSize > 0; a_usHeaderSize--) { + GetByte(); + } +} + +/*************************************************************** +* +* ispVMCalculateCRC32 +* +* Calculate the 32-bit CRC. +* +***************************************************************/ + +void ispVMCalculateCRC32(unsigned char a_ucData) +{ + /* 09/11/07 NN added local variables initialization */ + unsigned char ucIndex = 0; + unsigned char ucFlipData = 0; + unsigned short usCRCTableEntry = 0; + unsigned int crc_table[16] = { + 0x0000, 0xCC01, 0xD801, + 0x1400, 0xF001, 0x3C00, + 0x2800, 0xE401, 0xA001, + 0x6C00, 0x7800, 0xB401, + 0x5000, 0x9C01, 0x8801, + 0x4400 + }; + + for (ucIndex = 0; ucIndex < 8; ucIndex++) { + ucFlipData <<= 1; + if (a_ucData & 0x01) { + ucFlipData |= 0x01; + } + a_ucData >>= 1; + } + + /* 09/11/07 NN Type cast mismatch variables */ + usCRCTableEntry = (unsigned short)(crc_table[g_usCalculatedCRC & 0xF]); + g_usCalculatedCRC = (unsigned short)((g_usCalculatedCRC >> 4) & 0x0FFF); + g_usCalculatedCRC = (unsigned short)(g_usCalculatedCRC ^ usCRCTableEntry ^ crc_table[ucFlipData & 0xF]); + usCRCTableEntry = (unsigned short)(crc_table[g_usCalculatedCRC & 0xF]); + g_usCalculatedCRC = (unsigned short)((g_usCalculatedCRC >> 4) & 0x0FFF); + g_usCalculatedCRC = (unsigned short)(g_usCalculatedCRC ^ usCRCTableEntry ^ crc_table[(ucFlipData >> 4) & 0xF]); +} + +/*************************************************************** +* +* ispVMLCOUNT +* +* Process the intelligent programming loops. +* +***************************************************************/ + +signed char ispVMLCOUNT(unsigned short a_usCountSize) +{ + unsigned short usContinue = 1; + unsigned short usIntelBufferIndex = 0; + unsigned short usCountIndex = 0; + signed char cRetCode = 0; + signed char cRepeatHeap = 0; + signed char cOpcode = 0; + unsigned char ucState = 0; + unsigned short usDelay = 0; + unsigned short usToggle = 0; + unsigned char usByte = 0; + + g_usIntelBufferSize = (unsigned short)ispVMDataSize(); + + /**************************************************************************** + * + * Allocate memory for intel buffer. + * + *****************************************************************************/ + + ispVMMemManager(LHEAP, g_usIntelBufferSize); + + /**************************************************************************** + * + * Store the maximum size of the intelligent buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if (g_usIntelBufferSize > g_usLCOUNTSize) { + g_usLCOUNTSize = g_usIntelBufferSize; + } + + /**************************************************************************** + * + * Copy intel data to the buffer. + * + *****************************************************************************/ + + for (usIntelBufferIndex = 0; usIntelBufferIndex < g_usIntelBufferSize; usIntelBufferIndex++) { + g_pucIntelBuffer[usIntelBufferIndex] = GetByte(); + } + + /**************************************************************************** + * + * Set the data type register to get data from the intelligent data buffer. + * + *****************************************************************************/ + + g_usDataType |= LHEAP_IN; + + /**************************************************************************** + * + * If the HEAP_IN flag is set, temporarily unset the flag so data will be + * retrieved from the status buffer. + * + *****************************************************************************/ + + if (g_usDataType & HEAP_IN) { + g_usDataType &= ~HEAP_IN; + cRepeatHeap = 1; + } + +#ifdef VME_DEBUG + printf("LCOUNT %d;\n", a_usCountSize); +#endif /* VME_DEBUG */ + + /**************************************************************************** + * + * Iterate through the intelligent programming command. + * + *****************************************************************************/ + + for (usCountIndex = 0; usCountIndex < a_usCountSize; usCountIndex++) { + + /**************************************************************************** + * + * Initialize the intel data index to 0 before each iteration. + * + *****************************************************************************/ + + g_usIntelDataIndex = 0; + cOpcode = 0; + ucState = 0; + usDelay = 0; + usToggle = 0; + usByte = 0; + usContinue = 1; + + /*************************************************************** + * + * Begin looping through all the VME opcodes. + * + ***************************************************************/ + /*************************************************************** + * 4/1/09 Nguyen replaced the recursive function call codes on + * the ispVMLCOUNT function + * + ***************************************************************/ + while (usContinue) { + cOpcode = GetByte(); + switch (cOpcode) { + case HIR: + case TIR: + case HDR: + case TDR: + /*************************************************************** + * + * Set the header/trailer of the device in order to bypass + * successfully. + * + ***************************************************************/ + + ispVMAmble(cOpcode); + break; + case STATE: + + /*************************************************************** + * + * Step the JTAG state machine. + * + ***************************************************************/ + + ucState = GetByte(); + /*************************************************************** + * + * Step the JTAG state machine to DRCAPTURE to support Looping. + * + ***************************************************************/ + + if ((g_usDataType & LHEAP_IN) && + (ucState == DRPAUSE) && + (g_cCurrentJTAGState == ucState)) { + ispVMStateMachine(DRCAPTURE); + } + ispVMStateMachine(ucState); +#ifdef VME_DEBUG + printf("LDELAY %s ", GetState(ucState)); +#endif /* VME_DEBUG */ + break; + case SIR: +#ifdef VME_DEBUG + printf("SIR "); +#endif /* VME_DEBUG */ + /*************************************************************** + * + * Shift in data into the device. + * + ***************************************************************/ + + cRetCode = ispVMShift(cOpcode); + break; + case SDR: + +#ifdef VME_DEBUG + printf("LSDR "); +#endif /* VME_DEBUG */ + /*************************************************************** + * + * Shift in data into the device. + * + ***************************************************************/ + + cRetCode = ispVMShift(cOpcode); + break; + case WAIT: + + /*************************************************************** + * + * Observe delay. + * + ***************************************************************/ + + usDelay = (unsigned short)ispVMDataSize(); + ispVMDelay(usDelay); + +#ifdef VME_DEBUG + if (usDelay & 0x8000) { + + /*************************************************************** + * + * Since MSB is set, the delay time must be decoded to + * millisecond. The SVF2VME encodes the MSB to represent + * millisecond. + * + ***************************************************************/ + + usDelay &= ~0x8000; + printf("%.2E SEC;\n", (float)usDelay / 1000); + } else { + + /*************************************************************** + * + * Since MSB is not set, the delay time is given as microseconds. + * + ***************************************************************/ + + printf("%.2E SEC;\n", (float)usDelay / 1000000); + } +#endif /* VME_DEBUG */ + break; + case TCK: + + /*************************************************************** + * + * Issue clock toggles. + * + ***************************************************************/ + + usToggle = (unsigned short)ispVMDataSize(); + ispVMClocks(usToggle); + +#ifdef VME_DEBUG + printf("RUNTEST %d TCK;\n", usToggle); +#endif /* VME_DEBUG */ + break; + case ENDLOOP: + + /*************************************************************** + * + * Exit point from processing loops. + * + ***************************************************************/ + usContinue = 0; + break; + + case COMMENT: + + /*************************************************************** + * + * Display comment. + * + ***************************************************************/ + + ispVMComment((unsigned short)ispVMDataSize()); + break; + case ispEN: + ucState = GetByte(); + if ((ucState == ON) || (ucState == 0x01)) + writePort(JTAG_ENABLE, 0x01); + else + writePort(JTAG_ENABLE, 0x00); + ispVMDelay(1); + break; + case TRST: + if (GetByte() == 0x01) + writePort(JTAG_TRST, 0x01); + else + writePort(JTAG_TRST, 0x00); + ispVMDelay(1); + break; + default: + + /*************************************************************** + * + * Invalid opcode encountered. + * + ***************************************************************/ + +#ifdef VME_DEBUG + printf("\nINVALID OPCODE: 0x%.2X\n", cOpcode); +#endif /* VME_DEBUG */ + + return VME_INVALID_FILE; + } + } + if (cRetCode >= 0) { + /**************************************************************************** + * + * Break if intelligent programming is successful. + * + *****************************************************************************/ + + break; + } + + } + /**************************************************************************** + * + * If HEAP_IN flag was temporarily disabled, re-enable it before exiting. + * + *****************************************************************************/ + + if (cRepeatHeap) { + g_usDataType |= HEAP_IN; + } + + /**************************************************************************** + * + * Set the data type register to not get data from the intelligent data buffer. + * + *****************************************************************************/ + + g_usDataType &= ~LHEAP_IN; + return cRetCode; +} + +/*************************************************************** +* +* ispVMClocks +* +* Applies the specified number of pulses to TCK. +* +***************************************************************/ + +void ispVMClocks(unsigned short Clocks) +{ + unsigned short iClockIndex = 0; + for (iClockIndex = 0; iClockIndex < Clocks; iClockIndex++) { + sclock(); + } +} + +/*************************************************************** +* +* ispVMBypass +* +* This procedure takes care of the HIR, HDR, TIR, TDR for the +* purpose of putting the other devices into Bypass mode. The +* current state is checked to find out if it is at DRPAUSE or +* IRPAUSE. If it is at DRPAUSE, perform bypass register scan. +* If it is at IRPAUSE, scan into instruction registers the bypass +* instruction. +* +***************************************************************/ + +void ispVMBypass(signed char ScanType, unsigned short Bits) +{ + /* 09/11/07 NN added local variables initialization */ + unsigned short iIndex = 0; + unsigned short iSourceIndex = 0; + unsigned char cBitState = 0; + unsigned char cCurByte = 0; + unsigned char *pcSource = NULL; + + if (Bits <= 0) { + return; + } + + switch (ScanType) { + case HIR: + pcSource = g_pucHIRData; + break; + case TIR: + pcSource = g_pucTIRData; + break; + case HDR: + pcSource = g_pucHDRData; + break; + case TDR: + pcSource = g_pucTDRData; + break; + default: + break; + } + if (pcSource) { + iSourceIndex = 0; + cBitState = 0; + for (iIndex = 0; iIndex < Bits - 1; iIndex++) { + /* Scan instruction or bypass register */ + if (iIndex % 8 == 0) { + cCurByte = pcSource[iSourceIndex++]; + } + cBitState = (unsigned char)(((cCurByte << iIndex % 8) & 0x80) ? 0x01 : 0x00); + writePort(JTAG_TDI, cBitState); + sclock(); + } + + if (iIndex % 8 == 0) { + cCurByte = pcSource[iSourceIndex++]; + } + + cBitState = (unsigned char)(((cCurByte << iIndex % 8) & 0x80) ? 0x01 : 0x00); + writePort(JTAG_TDI, cBitState); + } +} + +/*************************************************************** +* +* ispVMStateMachine +* +* This procedure steps all devices in the daisy chain from a given +* JTAG state to the next desirable state. If the next state is TLR, +* the JTAG state machine is brute forced into TLR by driving TMS +* high and pulse TCK 6 times. +* +***************************************************************/ + +void ispVMStateMachine(signed char cNextJTAGState) +{ + /* 09/11/07 NN added local variables initialization */ + signed char cPathIndex = 0; + signed char cStateIndex = 0; + short int found = 0; + + if ((g_cCurrentJTAGState == cNextJTAGState) && (cNextJTAGState != RESET)) { + return; + } + + for (cStateIndex = 0; cStateIndex < 25; cStateIndex++) { + if ((g_cCurrentJTAGState == g_JTAGTransistions[cStateIndex].CurState) && (cNextJTAGState == g_JTAGTransistions[cStateIndex].NextState)) { + found = 1; + break; + } + } + if (found) { + g_cCurrentJTAGState = cNextJTAGState; + for (cPathIndex = 0; cPathIndex < g_JTAGTransistions[cStateIndex].Pulses; cPathIndex++) { + if ((g_JTAGTransistions[cStateIndex].Pattern << cPathIndex) & 0x80) { + writePort(JTAG_TMS, (unsigned char)0x01); + } else { + writePort(JTAG_TMS, (unsigned char)0x00); + } + sclock(); + } + + writePort(JTAG_TDI, 0x00); + writePort(JTAG_TMS, 0x00); + } +} + +/*************************************************************** +* +* ispVMStart +* +* Enable the port to the device and set the state to RESET (TLR). +* +***************************************************************/ + +void ispVMStart() +{ +#ifdef VME_DEBUG + printf("// ISPVM EMBEDDED ADDED\n"); + printf("STATE RESET;\n"); +#endif + + ispVMStateMachine(RESET); /* step devices to RESET state */ + +} + +/*************************************************************** +* +* ispVMEnd +* +* Set the state of devices to RESET to enable the devices and disable +* the port. +* +***************************************************************/ + +void ispVMEnd() +{ +#ifdef VME_DEBUG + printf("// ISPVM EMBEDDED ADDED\n"); + printf("STATE RESET;\n"); + printf("RUNTEST 1.00E-001 SEC;\n"); +#endif + + ispVMStateMachine(RESET); /* step devices to RESET state */ + ispVMDelay(1000); /* wake up devices */ +} + +/*************************************************************** +* +* ispVMSend +* +* Send the TDI data stream to devices. The data stream can be +* instructions or data. +* +***************************************************************/ + +signed char ispVMSend(unsigned short a_usiDataSize) +{ + /* 09/11/07 NN added local variables initialization */ + unsigned short iIndex = 0; + unsigned short iInDataIndex = 0; + unsigned char cCurByte = 0; + unsigned char cBitState = 0; + + for (iIndex = 0; iIndex < a_usiDataSize - 1; iIndex++) { + if (iIndex % 8 == 0) { + cCurByte = g_pucInData[iInDataIndex++]; + } + cBitState = (unsigned char)(((cCurByte << iIndex % 8) & 0x80) ? 0x01 : 0x00); + writePort(JTAG_TDI, cBitState); + sclock(); + } + + if (iIndex % 8 == 0) { + /* Take care of the last bit */ + cCurByte = g_pucInData[iInDataIndex]; + } + + cBitState = (unsigned char)(((cCurByte << iIndex % 8) & 0x80) ? 0x01 : 0x00); + + writePort(JTAG_TDI, cBitState); + if (g_usFlowControl & CASCADE) { + /* 1/15/04 Clock in last bit for the first n-1 cascaded frames */ + sclock(); + } + + return 0; +} + +/*************************************************************** +* +* ispVMRead +* +* Read the data stream from devices and verify. +* +***************************************************************/ + +signed char ispVMRead(unsigned short a_usiDataSize) +{ + /* 09/11/07 NN added local variables initialization */ + unsigned short usDataSizeIndex = 0; + unsigned short usErrorCount = 0; + unsigned short usLastBitIndex = 0; + unsigned char cDataByte = 0; + unsigned char cMaskByte = 0; + unsigned char cInDataByte = 0; + unsigned char cCurBit = 0; + unsigned char cByteIndex = 0; + unsigned short usBufferIndex = 0; + unsigned char ucDisplayByte = 0x00; + unsigned char ucDisplayFlag = 0x01; + char StrChecksum[256] = { 0 }; + unsigned char g_usCalculateChecksum = 0x00; + + /* 09/11/07 NN Type cast mismatch variables */ + usLastBitIndex = (unsigned short)(a_usiDataSize - 1); + + /**************************************************************************** + * + * If mask is not all zeros, then set the display flag to 0x00, otherwise + * it shall be set to 0x01 to indicate that data read from the device shall + * be displayed. If VME_DEBUG is defined, always display data. + * + *****************************************************************************/ + + + for (usDataSizeIndex = 0; usDataSizeIndex < (a_usiDataSize + 7) / 8; usDataSizeIndex++) { + + if (g_usDataType & MASK_DATA) { + if (g_pucOutMaskData[usDataSizeIndex] != 0x00) { + ucDisplayFlag = 0x00; + break; + } + } else if (g_usDataType & CMASK_DATA) { + g_usCalculateChecksum = 0x01; + ucDisplayFlag = 0x00; + break; + } else { + ucDisplayFlag = 0x00; + break; + } + } + + /**************************************************************************** + * + * Begin shifting data in and out of the device. + * + *****************************************************************************/ + for (usDataSizeIndex = 0; usDataSizeIndex < a_usiDataSize; usDataSizeIndex++) { + if (cByteIndex == 0) { + + /*************************************************************** + * + * Grab byte from TDO buffer. + * + ***************************************************************/ + + if (g_usDataType & TDO_DATA) { + cDataByte = g_pucOutData[usBufferIndex]; + } + + /*************************************************************** + * + * Grab byte from MASK buffer. + * + ***************************************************************/ + + if (g_usDataType & MASK_DATA) { + cMaskByte = g_pucOutMaskData[usBufferIndex]; + } else { + cMaskByte = 0xFF; + } + + /*************************************************************** + * + * Grab byte from CMASK buffer. + * + ***************************************************************/ + + if (g_usDataType & CMASK_DATA) { + cMaskByte = 0x00; + g_usCalculateChecksum = 0x01; + } + + /*************************************************************** + * + * Grab byte from TDI buffer. + * + ***************************************************************/ + + if (g_usDataType & TDI_DATA) { + cInDataByte = g_pucInData[usBufferIndex]; + } + + usBufferIndex++; + } + + cCurBit = readPort(); + + if (ucDisplayFlag) { + ucDisplayByte <<= 1; + ucDisplayByte |= cCurBit; + } + + /**************************************************************************** + * + * Check if data read from port matches with expected TDO. + * + *****************************************************************************/ + + + if (g_usDataType & TDO_DATA) { + /* 08/28/08 NN Added Calculate checksum support. */ + if (g_usCalculateChecksum) { + if (cCurBit == 0x01) + g_usChecksum += (1 << (g_uiChecksumIndex % 8)); + g_uiChecksumIndex++; + } else { + if ((((cMaskByte << cByteIndex) & 0x80) ? 0x01 : 0x00)) { + if (cCurBit != (unsigned char)(((cDataByte << cByteIndex) & 0x80) ? 0x01 : 0x00)) { + usErrorCount++; + } + } + } + } + + /**************************************************************************** + * + * Write TDI data to the port. + * + *****************************************************************************/ + + writePort(JTAG_TDI, (unsigned char)(((cInDataByte << cByteIndex) & 0x80) ? 0x01 : 0x00)); + + if (usDataSizeIndex < usLastBitIndex) { + + /**************************************************************************** + * + * Clock data out from the data shift register. + * + *****************************************************************************/ + + sclock(); + } else if (g_usFlowControl & CASCADE) { + + /**************************************************************************** + * + * Clock in last bit for the first N - 1 cascaded frames. + * + *****************************************************************************/ + + sclock(); + } + + /*************************************************************** + * + * Increment the byte index. If it exceeds 7, then reset it back + * to zero. + * + ***************************************************************/ + + cByteIndex++; + if (cByteIndex >= 8) { + if (ucDisplayFlag) { + + /*************************************************************** + * + * Store displayed data in the TDO buffer. By reusing the TDO + * buffer to store displayed data, there is no need to allocate + * a buffer simply to hold display data. This will not cause any + * false verification errors because the true TDO byte has already + * been consumed. + * + ***************************************************************/ + + g_pucOutData[usBufferIndex - 1] = ucDisplayByte; + ucDisplayByte = 0; + } + + cByteIndex = 0; + } + /* 09/12/07 Nguyen changed to display the 1 bit expected data */ + else if (a_usiDataSize == 1) { + if (ucDisplayFlag) { + + /*************************************************************** + * + * Store displayed data in the TDO buffer. By reusing the TDO + * buffer to store displayed data, there is no need to allocate + * a buffer simply to hold display data. This will not cause any + * false verification errors because the true TDO byte has already + * been consumed. + * + ***************************************************************/ + + /**************************************************************************** + * + * Flip ucDisplayByte and store it in cDataByte. + * + *****************************************************************************/ + cDataByte = 0x00; + for (usBufferIndex = 0; usBufferIndex < 8; usBufferIndex++) { + cDataByte <<= 1; + if (ucDisplayByte & 0x01) { + cDataByte |= 0x01; + } + ucDisplayByte >>= 1; + } + g_pucOutData[0] = cDataByte; + ucDisplayByte = 0; + } + + cByteIndex = 0; + } + } + if (ucDisplayFlag) { + + /**************************************************************************** + * + * Display data read from the device. + * + *****************************************************************************/ + +#ifdef VME_DEBUG + printf("RECIEVED TDO ("); +#else + vme_out_string("Display Data: 0x"); +#endif /* VME_DEBUG */ + + /* 09/11/07 NN Type cast mismatch variables */ + for (usDataSizeIndex = (unsigned short)((a_usiDataSize + 7) / 8); usDataSizeIndex > 0; usDataSizeIndex--) { + cMaskByte = g_pucOutData[usDataSizeIndex - 1]; + cDataByte = 0x00; + + /**************************************************************************** + * + * Flip cMaskByte and store it in cDataByte. + * + *****************************************************************************/ + + for (usBufferIndex = 0; usBufferIndex < 8; usBufferIndex++) { + cDataByte <<= 1; + if (cMaskByte & 0x01) { + cDataByte |= 0x01; + } + cMaskByte >>= 1; + } +#ifdef VME_DEBUG + printf("%.2X", cDataByte); + if ((((a_usiDataSize + 7) / 8) - usDataSizeIndex) % 40 == 39) { + printf("\n\t\t"); + } +#else + vme_out_hex(cDataByte); +#endif /* VME_DEBUG */ + } + +#ifdef VME_DEBUG + printf(")\n\n"); +#else + vme_out_string("\n\n"); +#endif /* VME_DEBUG */ + /* 09/02/08 Nguyen changed to display the data Checksum */ + vme_out_string("g_usChecksum:"); + sprintf(StrChecksum, "%.4X\n\n", (unsigned int)g_usChecksum); + vme_out_string(StrChecksum); + vme_out_string("\n\n"); + if (g_usChecksum != 0) { + g_usChecksum &= 0xFFFF; + sprintf(StrChecksum, "Data Checksum: %.4X\n\n", (unsigned int)g_usChecksum); + vme_out_string(StrChecksum); + g_usChecksum = 0; + } + } + + if (usErrorCount > 0) { + + if (g_usFlowControl & VERIFYUES) { + vme_out_string("USERCODE verification failed. Continue programming......\n\n"); + g_usFlowControl &= ~(VERIFYUES); + return 0; + } else { + +#ifdef VME_DEBUG + printf("TOTAL ERRORS: %d\n", usErrorCount); +#endif /* VME_DEBUG */ + + return VME_VERIFICATION_FAILURE; + } + } else { + if (g_usFlowControl & VERIFYUES) { + vme_out_string("USERCODE verification passed. Programming aborted. \n\n"); + g_usFlowControl &= ~(VERIFYUES); + return 1; + } else { + return 0; + } + } +} + +/*************************************************************** +* +* ispVMReadandSave +* +* Support dynamic I/O. +* +***************************************************************/ + +signed char ispVMReadandSave(unsigned short int a_usiDataSize) +{ + /* 09/11/07 NN added local variables initialization */ + unsigned short int usDataSizeIndex = 0; + unsigned short int usLastBitIndex = 0; + unsigned short int usBufferIndex = 0; + unsigned short int usOutBitIndex = 0; + unsigned short int usLVDSIndex = 0; + unsigned char cDataByte = 0; + unsigned char cDMASKByte = 0; + unsigned char cInDataByte = 0; + unsigned char cCurBit = 0; + unsigned char cByteIndex = 0; + signed char cLVDSByteIndex = 0; + + /* 09/11/07 NN Type cast mismatch variables */ + usLastBitIndex = (unsigned short)(a_usiDataSize - 1); + + /*************************************************************** + * + * Iterate through the data bits. + * + ***************************************************************/ + + for (usDataSizeIndex = 0; usDataSizeIndex < a_usiDataSize; usDataSizeIndex++) { + if (cByteIndex == 0) { + + /*************************************************************** + * + * Grab byte from DMASK buffer. + * + ***************************************************************/ + + if (g_usDataType & DMASK_DATA) { + cDMASKByte = g_pucOutDMaskData[usBufferIndex]; + } else { + cDMASKByte = 0x00; + } + + /*************************************************************** + * + * Grab byte from TDI buffer. + * + ***************************************************************/ + + if (g_usDataType & TDI_DATA) { + cInDataByte = g_pucInData[usBufferIndex]; + } + + usBufferIndex++; + } + + cCurBit = readPort(); + cDataByte = (unsigned char)(((cInDataByte << cByteIndex) & 0x80) ? 0x01 : 0x00); + + /*************************************************************** + * + * Initialize the byte to be zero. + * + ***************************************************************/ + + if (usOutBitIndex % 8 == 0) { + g_pucOutData[usOutBitIndex / 8] = 0x00; + } + + /*************************************************************** + * + * Use TDI, DMASK, and device TDO to create new TDI (actually + * stored in g_pucOutData). + * + ***************************************************************/ + + if ((((cDMASKByte << cByteIndex) & 0x80) ? 0x01 : 0x00)) { + + if (g_pLVDSList) { + for (usLVDSIndex = 0; usLVDSIndex < g_usLVDSPairCount; usLVDSIndex++) { + if (g_pLVDSList[usLVDSIndex].usNegativeIndex == usDataSizeIndex) { + g_pLVDSList[usLVDSIndex].ucUpdate = 0x01; + break; + } + } + } + + /*************************************************************** + * + * DMASK bit is 1, use TDI. + * + ***************************************************************/ + + g_pucOutData[usOutBitIndex / 8] |= (unsigned char)(((cDataByte & 0x1) ? 0x01 : 0x00) << (7 - usOutBitIndex % 8)); + } else { + + /*************************************************************** + * + * DMASK bit is 0, use device TDO. + * + ***************************************************************/ + + g_pucOutData[usOutBitIndex / 8] |= (unsigned char)(((cCurBit & 0x1) ? 0x01 : 0x00) << (7 - usOutBitIndex % 8)); + } + + /*************************************************************** + * + * Shift in TDI in order to get TDO out. + * + ***************************************************************/ + + usOutBitIndex++; + writePort(JTAG_TDI, cDataByte); + if (usDataSizeIndex < usLastBitIndex) { + sclock(); + } + + /*************************************************************** + * + * Increment the byte index. If it exceeds 7, then reset it back + * to zero. + * + ***************************************************************/ + + cByteIndex++; + if (cByteIndex >= 8) { + cByteIndex = 0; + } + } + + /*************************************************************** + * + * If g_pLVDSList exists and pairs need updating, then update + * the negative-pair to receive the flipped positive-pair value. + * + ***************************************************************/ + + if (g_pLVDSList) { + for (usLVDSIndex = 0; usLVDSIndex < g_usLVDSPairCount; usLVDSIndex++) { + if (g_pLVDSList[usLVDSIndex].ucUpdate) { + + /*************************************************************** + * + * Read the positive value and flip it. + * + ***************************************************************/ + + cDataByte = (unsigned char)(((g_pucOutData[g_pLVDSList[usLVDSIndex].usPositiveIndex / 8] << (g_pLVDSList[usLVDSIndex].usPositiveIndex % 8)) & 0x80) ? 0x01 : 0x00); + //09/11/07 NN Type cast mismatch variables + cDataByte = (unsigned char)(!cDataByte); + + /*************************************************************** + * + * Get the byte that needs modification. + * + ***************************************************************/ + + cInDataByte = g_pucOutData[g_pLVDSList[usLVDSIndex].usNegativeIndex / 8]; + + if (cDataByte) { + + /*************************************************************** + * + * Copy over the current byte and set the negative bit to 1. + * + ***************************************************************/ + + cDataByte = 0x00; + for (cLVDSByteIndex = 7; cLVDSByteIndex >= 0; cLVDSByteIndex--) { + cDataByte <<= 1; + if (7 - (g_pLVDSList[usLVDSIndex].usNegativeIndex % 8) == cLVDSByteIndex) { + + /*************************************************************** + * + * Set negative bit to 1. + * + ***************************************************************/ + + cDataByte |= 0x01; + } else if (cInDataByte & 0x80) { + cDataByte |= 0x01; + } + + cInDataByte <<= 1; + } + + /*************************************************************** + * + * Store the modified byte. + * + ***************************************************************/ + + g_pucOutData[g_pLVDSList[usLVDSIndex].usNegativeIndex / 8] = cDataByte; + } else { + + /*************************************************************** + * + * Copy over the current byte and set the negative bit to 0. + * + ***************************************************************/ + + cDataByte = 0x00; + for (cLVDSByteIndex = 7; cLVDSByteIndex >= 0; cLVDSByteIndex--) { + cDataByte <<= 1; + if (7 - (g_pLVDSList[usLVDSIndex].usNegativeIndex % 8) == cLVDSByteIndex) { + + /*************************************************************** + * + * Set negative bit to 0. + * + ***************************************************************/ + + cDataByte |= 0x00; + } else if (cInDataByte & 0x80) { + cDataByte |= 0x01; + } + + cInDataByte <<= 1; + } + + /*************************************************************** + * + * Store the modified byte. + * + ***************************************************************/ + + g_pucOutData[g_pLVDSList[usLVDSIndex].usNegativeIndex / 8] = cDataByte; + } + + break; + } + } + } + + return (0); +} + +signed char ispVMProcessLVDS(unsigned short a_usLVDSCount) +{ + unsigned short usLVDSIndex = 0; + + /*************************************************************** + * + * Allocate memory to hold LVDS pairs. + * + ***************************************************************/ + + ispVMMemManager(LVDS, a_usLVDSCount); + g_usLVDSPairCount = a_usLVDSCount; + +#ifdef VME_DEBUG + printf("LVDS %d (", a_usLVDSCount); +#endif /* VME_DEBUG */ + + /*************************************************************** + * + * Iterate through each given LVDS pair. + * + ***************************************************************/ + + for (usLVDSIndex = 0; usLVDSIndex < g_usLVDSPairCount; usLVDSIndex++) { + + /*************************************************************** + * + * Assign the positive and negative indices of the LVDS pair. + * + ***************************************************************/ + + /* 09/11/07 NN Type cast mismatch variables */ + g_pLVDSList[usLVDSIndex].usPositiveIndex = (unsigned short)ispVMDataSize(); + /* 09/11/07 NN Type cast mismatch variables */ + g_pLVDSList[usLVDSIndex].usNegativeIndex = (unsigned short)ispVMDataSize(); + +#ifdef VME_DEBUG + if (usLVDSIndex < g_usLVDSPairCount - 1) { + printf("%d:%d, ", g_pLVDSList[usLVDSIndex].usPositiveIndex, g_pLVDSList[usLVDSIndex].usNegativeIndex); + } else { + printf("%d:%d", g_pLVDSList[usLVDSIndex].usPositiveIndex, g_pLVDSList[usLVDSIndex].usNegativeIndex); + } +#endif /* VME_DEBUG */ + + } + +#ifdef VME_DEBUG + printf(") -- %d;\n", a_usLVDSCount); +#endif /* VME_DEBUG */ + + return (0); +} \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/vmopcode.h b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/vmopcode.h new file mode 100644 index 000000000000..1c0277306b63 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/app/firmware_upgrade/firmware_upgrade_ispvme/vmopcode.h @@ -0,0 +1,192 @@ +/*************************************************************** +* +* This is the include file for Lattice Semiconductor's ispVM +* Embedded software application. +* +***************************************************************/ + +/*************************************************************** +* +* VME version. +* +* History: +* +***************************************************************/ + +#define VME_VERSION_NUMBER "12.2" + +/*************************************************************** +* +* Maximum declarations. +* +***************************************************************/ + +#define VMEHEXMAX 60000L /* The hex file is split 60K per file. */ +#define SCANMAX 64000L /* The maximum SDR/SIR burst. */ + +/*************************************************************** +* +* Supported JTAG state transitions. +* +***************************************************************/ + +#define RESET 0x00 +#define IDLE 0x01 +#define IRPAUSE 0x02 +#define DRPAUSE 0x03 +#define SHIFTIR 0x04 +#define SHIFTDR 0x05 +#define DRCAPTURE 0x06 + +/*************************************************************** +* +* Flow control register bit definitions. A set bit indicates +* that the register currently exhibits the corresponding mode. +* +***************************************************************/ + +#define INTEL_PRGM 0x0001 /* Intelligent programming is in effect. */ +#define CASCADE 0x0002 /* Currently splitting large SDR. */ +#define REPEATLOOP 0x0008 /* Currently executing a repeat loop. */ +#define SHIFTRIGHT 0x0080 /* The next data stream needs a right shift. */ +#define SHIFTLEFT 0x0100 /* The next data stream needs a left shift. */ +#define VERIFYUES 0x0200 /* Continue if fail is in effect. */ + +/*************************************************************** +* +* DataType register bit definitions. A set bit indicates +* that the register currently holds the corresponding type of data. +* +***************************************************************/ + +#define EXPRESS 0x0001 /* Simultaneous program and verify. */ +#define SIR_DATA 0x0002 /* SIR is the active SVF command. */ +#define SDR_DATA 0x0004 /* SDR is the active SVF command. */ +#define COMPRESS 0x0008 /* Data is compressed. */ +#define TDI_DATA 0x0010 /* TDI data is present. */ +#define TDO_DATA 0x0020 /* TDO data is present. */ +#define MASK_DATA 0x0040 /* MASK data is present. */ +#define HEAP_IN 0x0080 /* Data is from the heap. */ +#define LHEAP_IN 0x0200 /* Data is from intel data buffer. */ +#define VARIABLE 0x0400 /* Data is from a declared variable. */ +#define CRC_DATA 0x0800 /* CRC data is pressent. */ +#define CMASK_DATA 0x1000 /* CMASK data is pressent. */ +#define RMASK_DATA 0x2000 /* RMASK data is pressent. */ +#define READ_DATA 0x4000 /* READ data is pressent. */ +#define DMASK_DATA 0x8000 /* DMASK data is pressent. */ + +/*************************************************************** +* +* Pin opcodes. +* +***************************************************************/ + +#define signalENABLE 0x1C /* ispENABLE pin. */ +#define signalTMS 0x1D /* TMS pin. */ +#define signalTCK 0x1E /* TCK pin. */ +#define signalTDI 0x1F /* TDI pin. */ +#define signalTRST 0x20 /* TRST pin. */ + +/*************************************************************** +* +* Supported vendors. +* +***************************************************************/ + +#define VENDOR 0x56 +#define LATTICE 0x01 +#define ALTERA 0x02 +#define XILINX 0x03 + +/*************************************************************** +* +* Opcode definitions. +* +* Note: opcodes must be unique. +* +***************************************************************/ + +#define ENDDATA 0x00 /* The end of the current SDR data stream. */ +#define RUNTEST 0x01 /* The duration to stay at the stable state. */ +#define ENDDR 0x02 /* The stable state after SDR. */ +#define ENDIR 0x03 /* The stable state after SIR. */ +#define ENDSTATE 0x04 /* The stable state after RUNTEST. */ +#define TRST 0x05 /* Assert the TRST pin. */ +#define HIR 0x06 /* The sum of the IR bits of the leading devices. */ +#define TIR 0x07 /* The sum of the IR bits of the trailing devices. */ +#define HDR 0x08 /* The number of leading devices. */ +#define TDR 0x09 /* The number of trailing devices. */ +#define ispEN 0x0A /* Assert the ispEN pin. */ +#define FREQUENCY 0x0B /* The maximum clock rate to run the JTAG state machine. */ +#define STATE 0x10 /* Move to the next stable state. */ +#define SIR 0x11 /* The instruction stream follows. */ +#define SDR 0x12 /* The data stream follows. */ +#define TDI 0x13 /* The following data stream feeds into the device. */ +#define TDO 0x14 /* The following data stream is compared against the device. */ +#define MASK 0x15 /* The following data stream is used as mask. */ +#define XSDR 0x16 /* The following data stream is for simultaneous program and verify. */ +#define XTDI 0x17 /* The following data stream is for shift in only. It must be stored for the next XSDR. */ +#define XTDO 0x18 /* There is not data stream. The data stream was stored from the previous XTDI. */ +#define MEM 0x19 /* The maximum memory needed to allocate in order hold one row of data. */ +#define WAIT 0x1A /* The duration of delay to observe. */ +#define TCK 0x1B /* The number of TCK pulses. */ +#define SHR 0x23 /* Set the flow control register for right shift. */ +#define SHL 0x24 /* Set the flow control register for left shift. */ +#define HEAP 0x32 /* The memory size needed to hold one loop. */ +#define REPEAT 0x33 /* The beginning of the loop. */ +#define LEFTPAREN 0x35 /* The beginning of data following the loop. */ +#define VAR 0x55 /* Plac holder for loop data. */ +#define SEC 0x1C /* The delay time in seconds that must be observed. */ +#define SMASK 0x1D /* The mask for TDI data. */ +#define MAX 0x1E /* The absolute maximum wait time. */ +#define ON 0x1F /* Assert the targeted pin. */ +#define OFF 0x20 /* Dis-assert the targeted pin. */ +#define SETFLOW 0x30 /* Change the flow control register. */ +#define RESETFLOW 0x31 /* Clear the flow control register. */ +#define CRC 0x47 /* The following data stream is used for CRC calculation. */ +#define CMASK 0x48 /* The following data stream is used as mask for CRC calculation. */ +#define RMASK 0x49 /* The following data stream is used as mask for read and save. */ +#define READ 0x50 /* The following data stream is used for read and save. */ +#define ENDLOOP 0x59 /* The end of the repeat loop. */ +#define SECUREHEAP 0x60 /* Used to secure the HEAP opcode. */ +#define VUES 0x61 /* Support continue if fail. */ +#define DMASK 0x62 /* The following data stream is used for dynamic I/O. */ +#define COMMENT 0x63 /* Support SVF comments in the VME file. */ +#define HEADER 0x64 /* Support header in VME file. */ +#define FILE_CRC 0x65 /* Support crc-protected VME file. */ +#define LCOUNT 0x66 /* Support intelligent programming. */ +#define LDELAY 0x67 /* Support intelligent programming. */ +#define LSDR 0x68 /* Support intelligent programming. */ +#define LHEAP 0x69 /* Memory needed to hold intelligent data buffer */ +#define CONTINUE 0x70 /* Allow continuation. */ +#define LVDS 0x71 /* Support LVDS. */ +#define ENDVME 0x7F /* End of the VME file. */ +#define HIGH 0x80 /* Assert the targeted pin. */ +#define LOW 0x81 /* Dis-assert the targeted pin. */ +#define ENDFILE 0xFF /* End of file. */ + +/*************************************************************** +* +* ispVM Embedded Return Codes. +* +***************************************************************/ + +#define VME_VERIFICATION_FAILURE -1 +#define VME_FILE_READ_FAILURE -2 +#define VME_VERSION_FAILURE -3 +#define VME_INVALID_FILE -4 +#define VME_ARGUMENT_FAILURE -5 +#define VME_CRC_FAILURE -6 + +/*************************************************************** +* +* Type definitions. +* +***************************************************************/ + +/* Support LVDS */ +typedef struct { + unsigned short usPositiveIndex; + unsigned short usNegativeIndex; + unsigned char ucUpdate; +} LVDSPair; \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/app/libextphy.so b/platform/centec-arm64/sonic-platform-modules-micas/common/app/libextphy.so new file mode 100755 index 0000000000000000000000000000000000000000..f9b6b9fe0e6a96a130ef0ebae1ffa96933fa8817 GIT binary patch literal 722264 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�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 literal 0 HcmV?d00001 diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/lib/eepromutil/__init__.py b/platform/centec-arm64/sonic-platform-modules-micas/common/lib/eepromutil/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/lib/eepromutil/fantlv.py b/platform/centec-arm64/sonic-platform-modules-micas/common/lib/eepromutil/fantlv.py new file mode 100644 index 000000000000..fa480bb50abb --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/lib/eepromutil/fantlv.py @@ -0,0 +1,210 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +class FantlvException(Exception): + def __init__(self, message='fantlverror', code=-100): + err = 'errcode: {0} message:{1}'.format(code, message) + Exception.__init__(self, err) + self.code = code + self.message = message + +class fan_tlv(object): + HEAD_INFO = "\x01\x7e\x01\xf1" + VERSION = 0x01 # E2PROM file init version is 0x01 + FLAG = 0x7E # new version E2PROM mark as 0x7E + HW_VER = 0X01 # consists of master version and revised version + TYPE = 0xf1 # hardware type define + TLV_LEN = 00 # vaild data length(16bit) + _FAN_TLV_HDR_LEN = 6 + _FAN_TLV_CRC_LEN = 2 + + _FAN_TLV_TYPE_NAME = 0x02 + _FAN_TLV_TYPE_SN = 0x03 + _FAN_TLV_TYPE_HW_INFO = 0x05 + _FAN_TLV_TYPE_DEV_TYPE = 0x06 + + _fandecodetime = 0 + + @property + def dstatus(self): + return self._dstatus + + @property + def typename(self): + return self._typename + + @property + def typesn(self): + return self._typesn + + @property + def typehwinfo(self): + return self._typehwinfo + + @property + def typedevtype(self): + return self._typedevtype + + @property + def fanbus(self): + return self._fanbus + + @property + def fanloc(self): + return self._fanloc + + @property + def fandecodetime(self): + return self._fandecodetime + + def __init__(self): + self._typename = "" + self._typesn = "" + self._typehwinfo = "" + self._typedevtype = "" + self._dstatus = 0 + + def strtoarr(self, str): + s = [] + if str is not None: + for index in range(len(str)): + s.append(str[index]) + return s + + def str_to_hex(self,rest_v): + value = 0 + for index in range(len(rest_v)): + value |= ord(rest_v[index]) << ((len(rest_v) - index - 1) * 8) + return value + + def hex_to_str(self,s): + len_t = len(s) + if len_t % 2 != 0: + return 0 + ret = "" + for t in range(0, int(len_t / 2)): + ret += chr(int(s[2 * t:2 * t + 2], 16)) + return ret + + def generate_fan_value(self): + bin_buffer = [chr(0xff)] * 256 + bin_buffer[0] = chr(self.VERSION) + bin_buffer[1] = chr(self.FLAG) + bin_buffer[2] = chr(self.HW_VER) + bin_buffer[3] = chr(self.TYPE) + + temp_t = "%08x" % self.typedevtype # handle devtype first + typedevtype_t = self.hex_to_str(temp_t) + total_len = len(self.typename) + len(self.typesn) + \ + len(self.typehwinfo) + len(typedevtype_t) + 8 + + bin_buffer[4] = chr(total_len >> 8) + bin_buffer[5] = chr(total_len & 0x00FF) + + index_start = 6 + bin_buffer[index_start] = chr(self._FAN_TLV_TYPE_NAME) + bin_buffer[index_start + 1] = chr(len(self.typename)) + bin_buffer[index_start + 2: index_start + 2 + + len(self.typename)] = self.strtoarr(self.typename) + index_start = index_start + 2 + len(self.typename) + + bin_buffer[index_start] = chr(self._FAN_TLV_TYPE_SN) + bin_buffer[index_start + 1] = chr(len(self.typesn)) + bin_buffer[index_start + 2:index_start + 2 + + len(self.typesn)] = self.strtoarr(self.typesn) + index_start = index_start + 2 + len(self.typesn) + + bin_buffer[index_start] = chr(self._FAN_TLV_TYPE_HW_INFO) + bin_buffer[index_start + 1] = chr(len(self.typehwinfo)) + bin_buffer[index_start + 2:index_start + 2 + + len(self.typehwinfo)] = self.strtoarr(self.typehwinfo) + index_start = index_start + 2 + len(self.typehwinfo) + + bin_buffer[index_start] = chr(self._FAN_TLV_TYPE_DEV_TYPE) + bin_buffer[index_start + 1] = chr(len(typedevtype_t)) + bin_buffer[index_start + 2:index_start + 2 + + len(typedevtype_t)] = self.strtoarr(typedevtype_t) + index_start = index_start + 2 + len(typedevtype_t) + + crcs = fan_tlv.fancrc(''.join(bin_buffer[0:index_start])) # 2bytes checking + bin_buffer[index_start] = chr(crcs >> 8) + bin_buffer[index_start + 1] = chr(crcs & 0x00ff) + # printvalue(bin_buffer) + return bin_buffer + + def decode(self, e2): + if e2[0:4] != self.HEAD_INFO: + raise FantlvException("Fan tlv head info error,not fan tlv type", -10) + ret = [] + self.VERSION = ord(e2[0]) + self.FLAG = ord(e2[1]) + self.HW_VER = ord(e2[2]) + self.TYPE = ord(e2[3]) + self.TLV_LEN = (ord(e2[4]) << 8) | ord(e2[5]) + + tlv_index = self._FAN_TLV_HDR_LEN + tlv_end = self._FAN_TLV_HDR_LEN + self.TLV_LEN + + # check sum + if len(e2) < self._FAN_TLV_HDR_LEN + self.TLV_LEN + 2: + raise FantlvException("Fan tlv eeprom len error!", -2) + sumcrc = fan_tlv.fancrc(e2[0:self._FAN_TLV_HDR_LEN + self.TLV_LEN]) + readcrc = ord(e2[self._FAN_TLV_HDR_LEN + self.TLV_LEN] + ) << 8 | ord(e2[self._FAN_TLV_HDR_LEN + self.TLV_LEN + 1]) + if sumcrc != readcrc: + raise FantlvException("Fan tlv eeprom checksum error!", -1) + else: + self._dstatus = 0 + while (tlv_index + 2) < len(e2) and tlv_index < tlv_end: + s = self.decoder( + e2[tlv_index:tlv_index + 2 + ord(e2[tlv_index + 1])]) + tlv_index += ord(e2[tlv_index + 1]) + 2 + ret.append(s) + + return ret + + @staticmethod + def fancrc(t): + sum = 0 + for index in range(len(t)): + sum += ord(t[index]) + return sum + + def decoder(self, t): + try: + name = "" + value = "" + _len = ord(t[1]) + if ord(t[0]) == self._FAN_TLV_TYPE_NAME: + name = "Product Name" + value = t[2:2 + ord(t[1])] + self._typename = value + elif ord(t[0]) == self._FAN_TLV_TYPE_SN: + name = "serial Number" + value = t[2:2 + ord(t[1])] + self._typesn = value + elif ord(t[0]) == self._FAN_TLV_TYPE_HW_INFO: + name = "hardware info" + value = t[2:2 + ord(t[1])] + self._typehwinfo = value + elif ord(t[0]) == self._FAN_TLV_TYPE_DEV_TYPE: + name = "dev type" + value = "0x" + for c in t[2:2 + ord(t[1])]: + value += "%02X" % (ord(c),) + self._typedevtype = int(value,16) + return {"name": name, "code": ord(t[0]), "value": value,"lens": _len} + except Exception as e: + print(e) + return None + + def __str__(self): + formatstr = "VERSION : 0x%02x \n" \ + " FLAG : 0x%02x \n" \ + " HW_VER : 0x%02x \n" \ + " TYPE : 0x%02x \n" \ + "typename : %s \n" \ + "typesn : %s \n" \ + "typehwinfo : %s \n" + return formatstr % (self.VERSION, self.FLAG, self.HW_VER, self.TYPE, self.typename, self.typesn, self.typehwinfo) + + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/lib/eepromutil/fru.py b/platform/centec-arm64/sonic-platform-modules-micas/common/lib/eepromutil/fru.py new file mode 100644 index 000000000000..4848530a8eaa --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/lib/eepromutil/fru.py @@ -0,0 +1,957 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +import collections +from bitarray import bitarray +from datetime import datetime, timedelta +import sys + +__all__ = ["FruException", "FruUtil", "BaseArea", "BoardInfoArea", "ProductInfoArea", + "MultiRecordArea", "Field", "ipmifru"] + +__DEBUG__ = "N" + + +class FruException(Exception): + def __init__(self, message='fruerror', code=-100): + err = 'errcode: {0} message:{1}'.format(code, message) + Exception.__init__(self, err) + self.code = code + self.message = message + + +def e_print(err): + print("ERROR: " + err) + + +def d_print(debug_info): + if(__DEBUG__ == "Y"): + print(debug_info) + + +class FruUtil(): + @staticmethod + def decodeLength(value): + a = bitarray(8) + a.setall(True) + a[0:1] = 0 + a[1:2] = 0 + x = ord(a.tobytes()) + return x & ord(value) + + @staticmethod + def minToData(): + starttime = datetime(1996, 1, 1, 0, 0, 0) + endtime = datetime.now() + seconds = (endtime - starttime).total_seconds() + mins = seconds / 60 + m = int(round(mins)) + return m + + @staticmethod + def getTimeFormat(): + return datetime.now().strftime('%Y-%m-%d') + + @staticmethod + def getTypeLength(value): + if value is None: + return 0 + a = bitarray(8) + a.setall(False) + a[0:1] = 1 + a[1:2] = 1 + x = ord(a.tobytes()) + return x | len(value) + + @staticmethod + def checksum(b): + result = 0 + for i in range(len(b)): + result += ord(b[i]) + return (0x100 - (result & 0xff)) & 0xff + + +class BaseArea(object): + SUGGESTED_SIZE_COMMON_HEADER = 8 + SUGGESTED_SIZE_INTERNAL_USE_AREA = 72 + SUGGESTED_SIZE_CHASSIS_INFO_AREA = 32 + SUGGESTED_SIZE_BOARD_INFO_AREA = 80 + SUGGESTED_SIZE_PRODUCT_INFO_AREA = 80 + + INITVALUE = b'\x00' + resultvalue = INITVALUE * 256 + COMMON_HEAD_VERSION = b'\x01' + __childList = None + + def __init__(self, name="", size=0, offset=0): + self.__childList = [] + self._offset = offset + self.name = name + self._size = size + self._isPresent = False + self._data = b'\x00' * size + self.__dataoffset = 0 + + @property + def childList(self): + return self.__childList + + @childList.setter + def childList(self, value): + self.__childList = value + + @property + def offset(self): + return self._offset + + @offset.setter + def offset(self, value): + self._offset = value + + @property + def size(self): + return self._size + + @size.setter + def size(self, value): + self._size = value + + @property + def data(self): + return self._data + + @data.setter + def data(self, value): + self._data = value + + @property + def isPresent(self): + return self._isPresent + + @isPresent.setter + def isPresent(self, value): + self._isPresent = value + + +class InternalUseArea(BaseArea): + pass + + +class ChassisInfoArea(BaseArea): + pass + + +class BoardInfoArea(BaseArea): + _boardTime = None + _fields = None + _mfg_date = None + + def __str__(self): + formatstr = "version : %x\n" \ + "length : %d \n" \ + "language : %x \n" \ + "mfg_date : %s \n" \ + "boardManufacturer : %s \n" \ + "boardProductName : %s \n" \ + "boardSerialNumber : %s \n" \ + "boardPartNumber : %s \n" \ + "fruFileId : %s \n" + + tmpstr = formatstr % (ord(self.boardversion), self.size, + self.language, self.getMfgRealData(), + self.boardManufacturer, self.boardProductName, + self.boardSerialNumber, self.boardPartNumber, + self.fruFileId) + for i in range(1, 11): + valtmp = "boardextra%d" % i + if hasattr(self, valtmp): + valtmpval = getattr(self, valtmp) + tmpstr += "boardextra%d : %s \n" % (i, valtmpval) + else: + break + + return tmpstr + + def todict(self): + dic = collections.OrderedDict() + dic["boardversion"] = ord(self.boardversion) + dic["boardlength"] = self.size + dic["boardlanguage"] = self.language + dic["boardmfg_date"] = self.getMfgRealData() + dic["boardManufacturer"] = self.boardManufacturer + dic["boardProductName"] = self.boardProductName + dic["boardSerialNumber"] = self.boardSerialNumber + dic["boardPartNumber"] = self.boardPartNumber + dic["boardfruFileId"] = self.fruFileId + for i in range(1, 11): + valtmp = "boardextra%d" % i + if hasattr(self, valtmp): + valtmpval = getattr(self, valtmp) + dic[valtmp] = valtmpval + else: + break + return dic + + def decodedata(self): + index = 0 + self.areaversion = self.data[index] + index += 1 + d_print("decode length :%d class size:%d" % + ((ord(self.data[index]) * 8), self.size)) + index += 2 + + timetmp = self.data[index: index + 3] + self.mfg_date = ord(timetmp[0]) | ( + ord(timetmp[1]) << 8) | (ord(timetmp[2]) << 16) + d_print("decode getMfgRealData :%s" % self.getMfgRealData()) + index += 3 + + templen = FruUtil.decodeLength(self.data[index]) + self.boardManufacturer = self.data[index + 1: index + templen + 1] + index += templen + 1 + d_print("decode boardManufacturer:%s" % self.boardManufacturer) + + templen = FruUtil.decodeLength(self.data[index]) + self.boardProductName = self.data[index + 1: index + templen + 1] + index += templen + 1 + d_print("decode boardProductName:%s" % self.boardProductName) + + templen = FruUtil.decodeLength(self.data[index]) + self.boardSerialNumber = self.data[index + 1: index + templen + 1] + index += templen + 1 + d_print("decode boardSerialNumber:%s" % self.boardSerialNumber) + + templen = FruUtil.decodeLength(self.data[index]) + self.boardPartNumber = self.data[index + 1: index + templen + 1] + index += templen + 1 + d_print("decode boardPartNumber:%s" % self.boardPartNumber) + + templen = FruUtil.decodeLength(self.data[index]) + self.fruFileId = self.data[index + 1: index + templen + 1] + index += templen + 1 + d_print("decode fruFileId:%s" % self.fruFileId) + + + for i in range(1, 11): + valtmp = "boardextra%d" % i + if self.data[index] != chr(0xc1): + templen = FruUtil.decodeLength(self.data[index]) + tmpval = self.data[index + 1: index + templen + 1] + setattr(self, valtmp, tmpval) + index += templen + 1 + d_print("decode boardextra%d:%s" % (i, tmpval)) + else: + break + + def recalcute(self): + d_print("boardInfoArea version:%x" % ord(self.boardversion)) + d_print("boardInfoArea length:%d" % self.size) + d_print("boardInfoArea language:%x" % self.language) + self.mfg_date = FruUtil.minToData() + d_print("boardInfoArea mfg_date:%x" % self.mfg_date) + + self.data = chr(ord(self.boardversion)) + \ + chr(self.size / 8) + chr(self.language) + + self.data += chr(self.mfg_date & 0xFF) + self.data += chr((self.mfg_date >> 8) & 0xFF) + self.data += chr((self.mfg_date >> 16) & 0xFF) + + d_print("boardInfoArea boardManufacturer:%s" % self.boardManufacturer) + typelength = FruUtil.getTypeLength(self.boardManufacturer) + self.data += chr(typelength) + self.data += self.boardManufacturer + + d_print("boardInfoArea boardProductName:%s" % self.boardProductName) + self.data += chr(FruUtil.getTypeLength(self.boardProductName)) + self.data += self.boardProductName + + d_print("boardInfoArea boardSerialNumber:%s" % self.boardSerialNumber) + self.data += chr(FruUtil.getTypeLength(self.boardSerialNumber)) + self.data += self.boardSerialNumber + + d_print("boardInfoArea boardPartNumber:%s" % self.boardPartNumber) + self.data += chr(FruUtil.getTypeLength(self.boardPartNumber)) + self.data += self.boardPartNumber + + d_print("boardInfoArea fruFileId:%s" % self.fruFileId) + self.data += chr(FruUtil.getTypeLength(self.fruFileId)) + self.data += self.fruFileId + + for i in range(1, 11): + valtmp = "boardextra%d" % i + if hasattr(self, valtmp): + valtmpval = getattr(self, valtmp) + d_print("boardInfoArea boardextra%d:%s" % (i, valtmpval)) + self.data += chr(FruUtil.getTypeLength(valtmpval)) + if valtmpval is None: + pass + else: + self.data += valtmpval + else: + break + + self.data += chr(0xc1) + + if len(self.data) > (self.size - 1): + incr = (len(self.data) - self.size) / 8 + 1 + self.size += incr * 8 + + self.data = self.data[0:1] + chr(self.size / 8) + self.data[2:] + d_print("self data:%d" % len(self.data)) + d_print("self size:%d" % self.size) + d_print("adjust size:%d" % (self.size - len(self.data) - 1)) + self.data = self.data.ljust((self.size - 1), self.INITVALUE) + + # checksum + checksum = FruUtil.checksum(self.data) + d_print("board info checksum:%x" % checksum) + self.data += chr(checksum) + + def getMfgRealData(self): + starttime = datetime(1996, 1, 1, 0, 0, 0) + mactime = starttime + timedelta(minutes=self.mfg_date) + return mactime + + @property + def language(self): + self._language = 25 + return self._language + + @property + def mfg_date(self): + return self._mfg_date + + @mfg_date.setter + def mfg_date(self, val): + self._mfg_date = val + + @property + def boardversion(self): + self._boardversion = self.COMMON_HEAD_VERSION + return self._boardversion + + @property + def fruFileId(self): + return self._FRUFileID + + @fruFileId.setter + def fruFileId(self, val): + self._FRUFileID = val + + @property + def boardPartNumber(self): + return self._boardPartNumber + + @boardPartNumber.setter + def boardPartNumber(self, val): + self._boardPartNumber = val + + @property + def boardSerialNumber(self): + return self._boardSerialNumber + + @boardSerialNumber.setter + def boardSerialNumber(self, val): + self._boardSerialNumber = val + + @property + def boardProductName(self): + return self._boradProductName + + @boardProductName.setter + def boardProductName(self, val): + self._boradProductName = val + + @property + def boardManufacturer(self): + return self._boardManufacturer + + @boardManufacturer.setter + def boardManufacturer(self, val): + self._boardManufacturer = val + + @property + def boardTime(self): + return self._boardTime + + @boardTime.setter + def boardTime(self, val): + self._boardTime = val + + @property + def fields(self): + return self._fields + + @fields.setter + def fields(self, val): + self._fields = val + + +class ProductInfoArea(BaseArea): + _productManufacturer = None + _productAssetTag = None + _FRUFileID = None + + def __str__(self): + formatstr = "version : %x\n" \ + "length : %d \n" \ + "language : %x \n" \ + "productManufacturer : %s \n" \ + "productName : %s \n" \ + "productPartModelName: %s \n" \ + "productVersion : %s \n" \ + "productSerialNumber : %s \n" \ + "productAssetTag : %s \n" \ + "fruFileId : %s \n" + + tmpstr = formatstr % (ord(self.areaversion), self.size, + self.language, self.productManufacturer, + self.productName, self.productPartModelName, + self.productVersion, self.productSerialNumber, + self.productAssetTag, self.fruFileId) + + for i in range(1, 11): + valtmp = "productextra%d" % i + if hasattr(self, valtmp): + valtmpval = getattr(self, valtmp) + tmpstr += "productextra%d : %s \n" % (i, valtmpval) + else: + break + + return tmpstr + + def todict(self): + dic = collections.OrderedDict() + dic["productversion"] = ord(self.areaversion) + dic["productlength"] = self.size + dic["productlanguage"] = self.language + dic["productManufacturer"] = self.productManufacturer + dic["productName"] = self.productName + dic["productPartModelName"] = self.productPartModelName + dic["productVersion"] = int(self.productVersion, 16) + dic["productSerialNumber"] = self.productSerialNumber + dic["productAssetTag"] = self.productAssetTag + dic["productfruFileId"] = self.fruFileId + for i in range(1, 11): + valtmp = "productextra%d" % i + if hasattr(self, valtmp): + valtmpval = getattr(self, valtmp) + dic[valtmp] = valtmpval + else: + break + return dic + + def decodedata(self): + index = 0 + self.areaversion = self.data[index] # 0 + index += 1 + d_print("decode length %d" % (ord(self.data[index]) * 8)) + d_print("class size %d" % self.size) + index += 2 + + templen = FruUtil.decodeLength(self.data[index]) + self.productManufacturer = self.data[index + 1: index + templen + 1] + index += templen + 1 + d_print("decode productManufacturer:%s" % self.productManufacturer) + + templen = FruUtil.decodeLength(self.data[index]) + self.productName = self.data[index + 1: index + templen + 1] + index += templen + 1 + d_print("decode productName:%s" % self.productName) + + templen = FruUtil.decodeLength(self.data[index]) + self.productPartModelName = self.data[index + 1: index + templen + 1] + index += templen + 1 + d_print("decode productPartModelName:%s" % self.productPartModelName) + + templen = FruUtil.decodeLength(self.data[index]) + self.productVersion = self.data[index + 1: index + templen + 1] + index += templen + 1 + d_print("decode productVersion:%s" % self.productVersion) + + templen = FruUtil.decodeLength(self.data[index]) + self.productSerialNumber = self.data[index + 1: index + templen + 1] + index += templen + 1 + d_print("decode productSerialNumber:%s" % self.productSerialNumber) + + templen = FruUtil.decodeLength(self.data[index]) + self.productAssetTag = self.data[index + 1: index + templen + 1] + index += templen + 1 + d_print("decode productAssetTag:%s" % self.productAssetTag) + + templen = FruUtil.decodeLength(self.data[index]) + self.fruFileId = self.data[index + 1: index + templen + 1] + index += templen + 1 + d_print("decode fruFileId:%s" % self.fruFileId) + + for i in range(1, 11): + valtmp = "productextra%d" % i + if self.data[index] != chr(0xc1) and index < self.size - 1: + templen = FruUtil.decodeLength(self.data[index]) + if templen == 0: + break + tmpval = self.data[index + 1: index + templen + 1] + d_print("decode boardextra%d:%s" % (i, tmpval)) + setattr(self, valtmp, tmpval) + index += templen + 1 + else: + break + + @property + def productVersion(self): + return self._productVersion + + @productVersion.setter + def productVersion(self, name): + self._productVersion = name + + @property + def areaversion(self): + self._areaversion = self.COMMON_HEAD_VERSION + return self._areaversion + + @areaversion.setter + def areaversion(self, name): + self._areaversion = name + + @property + def language(self): + self._language = 25 + return self._language + + @property + def productManufacturer(self): + return self._productManufacturer + + @productManufacturer.setter + def productManufacturer(self, name): + self._productManufacturer = name + + @property + def productName(self): + return self._productName + + @productName.setter + def productName(self, name): + self._productName = name + + @property + def productPartModelName(self): + return self._productPartModelName + + @productPartModelName.setter + def productPartModelName(self, name): + self._productPartModelName = name + + @property + def productSerialNumber(self): + return self._productSerialNumber + + @productSerialNumber.setter + def productSerialNumber(self, name): + self._productSerialNumber = name + + @property + def productAssetTag(self): + return self._productAssetTag + + @productAssetTag.setter + def productAssetTag(self, name): + self._productAssetTag = name + + @property + def fruFileId(self): + return self._FRUFileID + + @fruFileId.setter + def fruFileId(self, name): + self._FRUFileID = name + + def recalcute(self): + d_print("product version:%x" % ord(self.areaversion)) + d_print("product length:%d" % self.size) + d_print("product language:%x" % self.language) + self.data = chr(ord(self.areaversion)) + \ + chr(self.size / 8) + chr(self.language) + + typelength = FruUtil.getTypeLength(self.productManufacturer) + self.data += chr(typelength) + self.data += self.productManufacturer + + self.data += chr(FruUtil.getTypeLength(self.productName)) + self.data += self.productName + + self.data += chr(FruUtil.getTypeLength(self.productPartModelName)) + self.data += self.productPartModelName + + self.data += chr(FruUtil.getTypeLength(self.productVersion)) + self.data += self.productVersion + + self.data += chr(FruUtil.getTypeLength(self.productSerialNumber)) + self.data += self.productSerialNumber + + self.data += chr(FruUtil.getTypeLength(self.productAssetTag)) + if self.productAssetTag is not None: + self.data += self.productAssetTag + + self.data += chr(FruUtil.getTypeLength(self.fruFileId)) + self.data += self.fruFileId + + # whether the extended field exists or not + for i in range(1, 11): + valtmp = "productextra%d" % i + if hasattr(self, valtmp): + valtmpval = getattr(self, valtmp) + d_print("boardInfoArea productextra%d:%s" % (i, valtmpval)) + self.data += chr(FruUtil.getTypeLength(valtmpval)) + if valtmpval is None: + pass + else: + self.data += valtmpval + else: + break + + self.data += chr(0xc1) + if len(self.data) > (self.size - 1): + incr = (len(self.data) - self.size) / 8 + 1 + self.size += incr * 8 + d_print("self.data:%d" % len(self.data)) + d_print("self.size:%d" % self.size) + + self.data = self.data[0:1] + chr(self.size / 8) + self.data[2:] + self.data = self.data.ljust((self.size - 1), self.INITVALUE) + checksum = FruUtil.checksum(self.data) + d_print("board info checksum:%x" % checksum) + self.data += chr(checksum) + + +class MultiRecordArea(BaseArea): + pass + + +class Field(object): + + def __init__(self, fieldType="ASCII", fieldData=""): + self.fieldData = fieldData + self.fieldType = fieldType + + @property + def data(self): + return self._data + + @property + def fieldType(self): + return self._fieldType + + @property + def fieldData(self): + return self._fieldData + + +class ipmifru(BaseArea): + _BoardInfoArea = None + _ProductInfoArea = None + _InternalUseArea = None + _ChassisInfoArea = None + _multiRecordArea = None + _productinfoAreaOffset = BaseArea.INITVALUE + _boardInfoAreaOffset = BaseArea.INITVALUE + _internalUserAreaOffset = BaseArea.INITVALUE + _chassicInfoAreaOffset = BaseArea.INITVALUE + _multiRecordAreaOffset = BaseArea.INITVALUE + _bindata = None + _bodybin = None + _version = BaseArea.COMMON_HEAD_VERSION + _zeroCheckSum = None + _frusize = 256 + + def __str__(self): + tmpstr = "" + if self.boardInfoArea.isPresent: + tmpstr += "\nboardinfoarea: \n" + tmpstr += self.boardInfoArea.__str__() + if self.productInfoArea.isPresent: + tmpstr += "\nproductinfoarea: \n" + tmpstr += self.productInfoArea.__str__() + return tmpstr + + def decodeBin(self, eeprom): + commonHead = eeprom[0:8] + d_print("decode version %x" % ord(commonHead[0])) + if self.COMMON_HEAD_VERSION != commonHead[0]: + raise FruException("HEAD VERSION error,not Fru format!", -10) + if FruUtil.checksum(commonHead[0:7]) != ord(commonHead[7]): + strtemp = "check header checksum error [cal:%02x data:%02x]" % ( + FruUtil.checksum(commonHead[0:7]), ord(commonHead[7])) + raise FruException(strtemp, -3) + if commonHead[1] != self.INITVALUE: + d_print("Internal Use Area is present") + self.internalUseArea = InternalUseArea( + name="Internal Use Area", size=self.SUGGESTED_SIZE_INTERNAL_USE_AREA) + self.internalUseArea.isPresent = True + self.internalUserAreaOffset = ord(commonHead[1]) + self.internalUseArea.data = eeprom[self.internalUserAreaOffset * 8: ( + self.internalUserAreaOffset * 8 + self.internalUseArea.size)] + if commonHead[2] != self.INITVALUE: + d_print("Chassis Info Area is present") + self.chassisInfoArea = ChassisInfoArea( + name="Chassis Info Area", size=self.SUGGESTED_SIZE_CHASSIS_INFO_AREA) + self.chassisInfoArea.isPresent = True + self.chassicInfoAreaOffset = ord(commonHead[2]) + self.chassisInfoArea.data = eeprom[self.chassicInfoAreaOffset * 8: ( + self.chassicInfoAreaOffset * 8 + self.chassisInfoArea.size)] + if commonHead[3] != self.INITVALUE: + self.boardInfoArea = BoardInfoArea( + name="Board Info Area", size=self.SUGGESTED_SIZE_BOARD_INFO_AREA) + self.boardInfoArea.isPresent = True + self.boardInfoAreaOffset = ord(commonHead[3]) + self.boardInfoArea.size = ord( + eeprom[self.boardInfoAreaOffset * 8 + 1]) * 8 + d_print("Board Info Area is present size:%d" % + (self.boardInfoArea.size)) + self.boardInfoArea.data = eeprom[self.boardInfoAreaOffset * 8: ( + self.boardInfoAreaOffset * 8 + self.boardInfoArea.size)] + if FruUtil.checksum(self.boardInfoArea.data[:-1]) != ord(self.boardInfoArea.data[-1:]): + strtmp = "check boardInfoArea checksum error[cal:%02x data:%02x]" % \ + (FruUtil.checksum( + self.boardInfoArea.data[:-1]), ord(self.boardInfoArea.data[-1:])) + raise FruException(strtmp, -3) + self.boardInfoArea.decodedata() + if commonHead[4] != self.INITVALUE: + d_print("Product Info Area is present") + self.productInfoArea = ProductInfoArea( + name="Product Info Area ", size=self.SUGGESTED_SIZE_PRODUCT_INFO_AREA) + self.productInfoArea.isPresent = True + self.productinfoAreaOffset = ord(commonHead[4]) + d_print("length offset value: %02x" % + ord(eeprom[self.productinfoAreaOffset * 8 + 1])) + self.productInfoArea.size = ord( + eeprom[self.productinfoAreaOffset * 8 + 1]) * 8 + d_print("Product Info Area is present size:%d" % + (self.productInfoArea.size)) + + self.productInfoArea.data = eeprom[self.productinfoAreaOffset * 8: ( + self.productinfoAreaOffset * 8 + self.productInfoArea.size)] + if FruUtil.checksum(self.productInfoArea.data[:-1]) != ord(self.productInfoArea.data[-1:]): + strtmp = "check productInfoArea checksum error [cal:%02x data:%02x]" % ( + FruUtil.checksum(self.productInfoArea.data[:-1]), ord(self.productInfoArea.data[-1:])) + raise FruException(strtmp, -3) + self.productInfoArea.decodedata() + if commonHead[5] != self.INITVALUE: + self.multiRecordArea = MultiRecordArea( + name="MultiRecord record Area ") + d_print("MultiRecord record present") + self.multiRecordArea.isPresent = True + self.multiRecordAreaOffset = ord(commonHead[5]) + self.multiRecordArea.data = eeprom[self.multiRecordAreaOffset * 8: ( + self.multiRecordAreaOffset * 8 + self.multiRecordArea.size)] + + def initDefault(self): + self.version = self.COMMON_HEAD_VERSION + self.internalUserAreaOffset = self.INITVALUE + self.chassicInfoAreaOffset = self.INITVALUE + self.boardInfoAreaOffset = self.INITVALUE + self.productinfoAreaOffset = self.INITVALUE + self.multiRecordAreaOffset = self.INITVALUE + self.PAD = self.INITVALUE + self.zeroCheckSum = self.INITVALUE + self.offset = self.SUGGESTED_SIZE_COMMON_HEADER + self.productInfoArea = None + self.internalUseArea = None + self.boardInfoArea = None + self.chassisInfoArea = None + self.multiRecordArea = None + # self.recalcute() + + @property + def version(self): + return self._version + + @version.setter + def version(self, name): + self._version = name + + @property + def internalUserAreaOffset(self): + return self._internalUserAreaOffset + + @internalUserAreaOffset.setter + def internalUserAreaOffset(self, obj): + self._internalUserAreaOffset = obj + + @property + def chassicInfoAreaOffset(self): + return self._chassicInfoAreaOffset + + @chassicInfoAreaOffset.setter + def chassicInfoAreaOffset(self, obj): + self._chassicInfoAreaOffset = obj + + @property + def productinfoAreaOffset(self): + return self._productinfoAreaOffset + + @productinfoAreaOffset.setter + def productinfoAreaOffset(self, obj): + self._productinfoAreaOffset = obj + + @property + def boardInfoAreaOffset(self): + return self._boardInfoAreaOffset + + @boardInfoAreaOffset.setter + def boardInfoAreaOffset(self, obj): + self._boardInfoAreaOffset = obj + + @property + def multiRecordAreaOffset(self): + return self._multiRecordAreaOffset + + @multiRecordAreaOffset.setter + def multiRecordAreaOffset(self, obj): + self._multiRecordAreaOffset = obj + + @property + def zeroCheckSum(self): + return self._zeroCheckSum + + @zeroCheckSum.setter + def zeroCheckSum(self, obj): + self._zeroCheckSum = obj + + @property + def productInfoArea(self): + return self._ProductInfoArea + + @productInfoArea.setter + def productInfoArea(self, obj): + self._ProductInfoArea = obj + + @property + def internalUseArea(self): + return self._InternalUseArea + + @internalUseArea.setter + def internalUseArea(self, obj): + self.internalUseArea = obj + + @property + def boardInfoArea(self): + return self._BoardInfoArea + + @boardInfoArea.setter + def boardInfoArea(self, obj): + self._BoardInfoArea = obj + + @property + def chassisInfoArea(self): + return self._ChassisInfoArea + + @chassisInfoArea.setter + def chassisInfoArea(self, obj): + self._ChassisInfoArea = obj + + @property + def multiRecordArea(self): + return self._multiRecordArea + + @multiRecordArea.setter + def multiRecordArea(self, obj): + self._multiRecordArea = obj + + @property + def bindata(self): + return self._bindata + + @bindata.setter + def bindata(self, obj): + self._bindata = obj + + @property + def bodybin(self): + return self._bodybin + + @bodybin.setter + def bodybin(self, obj): + self._bodybin = obj + + def recalcuteCommonHead(self): + self.bindata = "" + self.offset = self.SUGGESTED_SIZE_COMMON_HEADER + d_print("common Header %d" % self.offset) + d_print("fru eeprom size %d" % self._frusize) + if self.internalUseArea is not None and self.internalUseArea.isPresent: + self.internalUserAreaOffset = self.offset / 8 + self.offset += self.internalUseArea.size + d_print("internalUseArea is present offset:%d" % self.offset) + + if self.chassisInfoArea is not None and self.chassisInfoArea.isPresent: + self.chassicInfoAreaOffset = self.offset / 8 + self.offset += self.chassisInfoArea.size + d_print("chassisInfoArea is present offset:%d" % self.offset) + + if self.boardInfoArea is not None and self.boardInfoArea.isPresent: + self.boardInfoAreaOffset = self.offset / 8 + self.offset += self.boardInfoArea.size + d_print("boardInfoArea is present offset:%d" % self.offset) + d_print("boardInfoArea is present size:%d" % + self.boardInfoArea.size) + + if self.productInfoArea is not None and self.productInfoArea.isPresent: + self.productinfoAreaOffset = self.offset / 8 + self.offset += self.productInfoArea.size + d_print("productInfoArea is present offset:%d" % self.offset) + + if self.multiRecordArea is not None and self.multiRecordArea.isPresent: + self.multiRecordAreaOffset = self.offset / 8 + d_print("multiRecordArea is present offset:%d" % self.offset) + + if self.internalUserAreaOffset == self.INITVALUE: + self.internalUserAreaOffset = 0 + if self.productinfoAreaOffset == self.INITVALUE: + self.productinfoAreaOffset = 0 + if self.chassicInfoAreaOffset == self.INITVALUE: + self.chassicInfoAreaOffset = 0 + if self.boardInfoAreaOffset == self.INITVALUE: + self.boardInfoAreaOffset = 0 + if self.multiRecordAreaOffset == self.INITVALUE: + self.multiRecordAreaOffset = 0 + + self.zeroCheckSum = (0x100 - ord(self.version) - self.internalUserAreaOffset - self.chassicInfoAreaOffset - self.productinfoAreaOffset + - self.boardInfoAreaOffset - self.multiRecordAreaOffset) & 0xff + d_print("zerochecksum:%x" % self.zeroCheckSum) + self.data = self.version + chr(self.internalUserAreaOffset) + chr(self.chassicInfoAreaOffset) + chr( + self.boardInfoAreaOffset) + chr(self.productinfoAreaOffset) + chr(self.multiRecordAreaOffset) + self.INITVALUE + chr(self.zeroCheckSum) + + self.bindata = self.data + self.bodybin + totallen = len(self.bindata) + d_print("totallen %d" % totallen) + if (totallen < self._frusize): + self.bindata = self.bindata.ljust(self._frusize, self.INITVALUE) + else: + raise FruException('bin data more than %d' % self._frusize, -2) + + def recalcutebin(self): + self.bodybin = "" + if self.internalUseArea is not None and self.internalUseArea.isPresent: + d_print("internalUseArea present") + self.bodybin += self.internalUseArea.data + if self.chassisInfoArea is not None and self.chassisInfoArea.isPresent: + d_print("chassisInfoArea present") + self.bodybin += self.chassisInfoArea.data + if self.boardInfoArea is not None and self.boardInfoArea.isPresent: + d_print("boardInfoArea present") + self.boardInfoArea.recalcute() + self.bodybin += self.boardInfoArea.data + if self.productInfoArea is not None and self.productInfoArea.isPresent: + d_print("productInfoAreapresent") + self.productInfoArea.recalcute() + self.bodybin += self.productInfoArea.data + if self.multiRecordArea is not None and self.multiRecordArea.isPresent: + d_print("multiRecordArea present") + self.bodybin += self.productInfoArea.data + + def recalcute(self, fru_eeprom_size = 256): + self._frusize = fru_eeprom_size + self.recalcutebin() + self.recalcuteCommonHead() diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/lib/wbutil/__init__.py b/platform/centec-arm64/sonic-platform-modules-micas/common/lib/wbutil/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/lib/wbutil/baseutil.py b/platform/centec-arm64/sonic-platform-modules-micas/common/lib/wbutil/baseutil.py new file mode 100755 index 000000000000..1cf74d32d22f --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/lib/wbutil/baseutil.py @@ -0,0 +1,23 @@ +# -*- coding: UTF-8 -*- +import os + +def get_machine_info(): + if not os.path.isfile('/host/machine.conf'): + return None + machine_vars = {} + with open('/host/machine.conf') as machine_file: + for line in machine_file: + tokens = line.split('=') + if len(tokens) < 2: + continue + machine_vars[tokens[0]] = tokens[1].strip() + return machine_vars + +def get_platform_info(machine_info): + if machine_info != None: + if 'onie_platform' in machine_info: + return machine_info['onie_platform'] + elif 'aboot_platform' in machine_info: + return machine_info['aboot_platform'] + return None + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/lib/wbutil/logutil.py b/platform/centec-arm64/sonic-platform-modules-micas/common/lib/wbutil/logutil.py new file mode 100755 index 000000000000..2b001f21d72c --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/lib/wbutil/logutil.py @@ -0,0 +1,67 @@ +# -*- coding: UTF-8 -*- + +import logging +from syslog import ( + syslog, + openlog, + LOG_WARNING, + LOG_CRIT, + LOG_DEBUG, + LOG_ERR, + LOG_PID, + LOG_INFO, +) + +class Logger(): + def __init__(self, prefix, filepath=None, syslog=False, dbg_mask=0x0): + self.logger = None + if syslog is False: + if filepath is None: + raise AttributeError("filepath needed") + + # init logging + formatter = logging.Formatter( "%(asctime)s %(levelname)s %(filename)s[%(funcName)s][%(lineno)s]: %(message)s") + handler = logging.FileHandler(self.filepath) + handler.setFormatter(formatter) + self.logger = logging.getLogger(__name__) + self.logger.setLevel(logging.DEBUG) + self.logger.addHandler(handler) + + self.prefix = prefix + self.use_syslog = syslog + self.dbg_mask = dbg_mask + + def info(self, s): + if self.use_syslog: + self._syslog(s, LOG_INFO) + else: + self.logger.info(s) + + def debug(self, dbg_lvl, s): + if dbg_lvl & self.dbg_mask: + if self.use_syslog: + self._syslog(s, LOG_DEBUG) + else: + self.logger.debug(s) + + def warn(self, s): + if self.use_syslog: + self._syslog(s, LOG_WARNING) + else: + self.logger.warning(s) + + def error(self, s): + if self.use_syslog: + self._syslog(s, LOG_ERR) + else: + self.logger.error(s) + + def crit(self, s): + if self.use_syslog: + self._syslog(s, LOG_CRIT) + else: + self.logger.critical(s) + + def _syslog(self, s, t): + openlog(self.prefix, LOG_PID) + syslog(t, s) diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/lib/wbutil/smbus.py b/platform/centec-arm64/sonic-platform-modules-micas/common/lib/wbutil/smbus.py new file mode 100755 index 000000000000..cfef713c3b9d --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/lib/wbutil/smbus.py @@ -0,0 +1,774 @@ +"""smbus2 - A drop-in replacement for smbus-cffi/smbus-python""" +# The MIT License (MIT) +# Copyright (c) 2017 Karl-Petter Lindegaard +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import os +import sys +from fcntl import ioctl +from ctypes import c_uint32, c_uint8, c_uint16, c_char, POINTER, Structure, Array, Union, create_string_buffer, string_at + + +# Commands from uapi/linux/i2c-dev.h +I2C_SLAVE = 0x0703 # Use this slave address +I2C_SLAVE_FORCE = 0x0706 # Use this slave address, even if it is already in use by a driver! +I2C_FUNCS = 0x0705 # Get the adapter functionality mask +I2C_RDWR = 0x0707 # Combined R/W transfer (one STOP only) +I2C_SMBUS = 0x0720 # SMBus transfer. Takes pointer to i2c_smbus_ioctl_data +I2C_PEC = 0x0708 + +# SMBus transfer read or write markers from uapi/linux/i2c.h +I2C_SMBUS_WRITE = 0 +I2C_SMBUS_READ = 1 + +# Size identifiers uapi/linux/i2c.h +I2C_SMBUS_QUICK = 0 +I2C_SMBUS_BYTE = 1 +I2C_SMBUS_BYTE_DATA = 2 +I2C_SMBUS_WORD_DATA = 3 +I2C_SMBUS_PROC_CALL = 4 +I2C_SMBUS_BLOCK_DATA = 5 # This isn't supported by Pure-I2C drivers with SMBUS emulation, like those in RaspberryPi, OrangePi, etc :( +I2C_SMBUS_BLOCK_PROC_CALL = 7 # Like I2C_SMBUS_BLOCK_DATA, it isn't supported by Pure-I2C drivers either. +I2C_SMBUS_I2C_BLOCK_DATA = 8 +I2C_SMBUS_BLOCK_MAX = 32 + +# To determine what functionality is present (uapi/linux/i2c.h) +try: + from enum import IntFlag +except ImportError: + IntFlag = int + + +class I2cFunc(IntFlag): + """ + These flags identify the operations supported by an I2C/SMBus device. + + You can test these flags on your `smbus.funcs` + + On newer python versions, I2cFunc is an IntFlag enum, but it + falls back to class with a bunch of int constants on older releases. + """ + I2C = 0x00000001 + ADDR_10BIT = 0x00000002 + PROTOCOL_MANGLING = 0x00000004 # I2C_M_IGNORE_NAK etc. + SMBUS_PEC = 0x00000008 + NOSTART = 0x00000010 # I2C_M_NOSTART + SLAVE = 0x00000020 + SMBUS_BLOCK_PROC_CALL = 0x00008000 # SMBus 2.0 + SMBUS_QUICK = 0x00010000 + SMBUS_READ_BYTE = 0x00020000 + SMBUS_WRITE_BYTE = 0x00040000 + SMBUS_READ_BYTE_DATA = 0x00080000 + SMBUS_WRITE_BYTE_DATA = 0x00100000 + SMBUS_READ_WORD_DATA = 0x00200000 + SMBUS_WRITE_WORD_DATA = 0x00400000 + SMBUS_PROC_CALL = 0x00800000 + SMBUS_READ_BLOCK_DATA = 0x01000000 + SMBUS_WRITE_BLOCK_DATA = 0x02000000 + SMBUS_READ_I2C_BLOCK = 0x04000000 # I2C-like block xfer + SMBUS_WRITE_I2C_BLOCK = 0x08000000 # w/ 1-byte reg. addr. + SMBUS_HOST_NOTIFY = 0x10000000 + + SMBUS_BYTE = 0x00060000 + SMBUS_BYTE_DATA = 0x00180000 + SMBUS_WORD_DATA = 0x00600000 + SMBUS_BLOCK_DATA = 0x03000000 + SMBUS_I2C_BLOCK = 0x0c000000 + SMBUS_EMUL = 0x0eff0008 + + +# i2c_msg flags from uapi/linux/i2c.h +I2C_M_RD = 0x0001 + +# Pointer definitions +LP_c_uint8 = POINTER(c_uint8) +LP_c_uint16 = POINTER(c_uint16) +LP_c_uint32 = POINTER(c_uint32) + + +############################################################# +# Type definitions as in i2c.h + + +class i2c_smbus_data(Array): + """ + Adaptation of the i2c_smbus_data union in ``i2c.h``. + + Data for SMBus messages. + """ + _length_ = I2C_SMBUS_BLOCK_MAX + 2 + _type_ = c_uint8 + + +class union_i2c_smbus_data(Union): + _fields_ = [ + ("byte", c_uint8), + ("word", c_uint16), + ("block", i2c_smbus_data) + ] + + +union_pointer_type = POINTER(union_i2c_smbus_data) + + +class i2c_smbus_ioctl_data(Structure): + """ + As defined in ``i2c-dev.h``. + """ + _fields_ = [ + ('read_write', c_uint8), + ('command', c_uint8), + ('size', c_uint32), + ('data', union_pointer_type)] + __slots__ = [name for name, type in _fields_] + + @staticmethod + def create(read_write=I2C_SMBUS_READ, command=0, size=I2C_SMBUS_BYTE_DATA): + u = union_i2c_smbus_data() + return i2c_smbus_ioctl_data( + read_write=read_write, command=command, size=size, + data=union_pointer_type(u)) + + +############################################################# +# Type definitions for i2c_rdwr combined transactions + + +class i2c_msg(Structure): + """ + As defined in ``i2c.h``. + """ + _fields_ = [ + ('addr', c_uint16), + ('flags', c_uint16), + ('len', c_uint16), + ('buf', POINTER(c_char))] + + def __iter__(self): + """ Iterator / Generator + + :return: iterates over :py:attr:`buf` + :rtype: :py:class:`generator` which returns int values + """ + idx = 0 + while idx < self.len: + yield ord(self.buf[idx]) + idx += 1 + + def __len__(self): + return self.len + + def __bytes__(self): + return string_at(self.buf, self.len) + + def __repr__(self): + return 'i2c_msg(%d,%d,%r)' % (self.addr, self.flags, self.__bytes__()) + + def __str__(self): + s = self.__bytes__() + if sys.version_info.major >= 3: + s = ''.join(map(chr, s)) + return s + + @staticmethod + def read(address, length): + """ + Prepares an i2c read transaction. + + :param address: Slave address. + :type: address: int + :param length: Number of bytes to read. + :type: length: int + :return: New :py:class:`i2c_msg` instance for read operation. + :rtype: :py:class:`i2c_msg` + """ + arr = create_string_buffer(length) + return i2c_msg( + addr=address, flags=I2C_M_RD, len=length, + buf=arr) + + @staticmethod + def write(address, buf): + """ + Prepares an i2c write transaction. + + :param address: Slave address. + :type address: int + :param buf: Bytes to write. Either list of values or str. + :type buf: list + :return: New :py:class:`i2c_msg` instance for write operation. + :rtype: :py:class:`i2c_msg` + """ + if sys.version_info.major >= 3: + if type(buf) is str: + buf = bytes(map(ord, buf)) + else: + buf = bytes(buf) + else: + if type(buf) is not str: + buf = ''.join([chr(x) for x in buf]) + arr = create_string_buffer(buf, len(buf)) + return i2c_msg( + addr=address, flags=0, len=len(arr), + buf=arr) + + +class i2c_rdwr_ioctl_data(Structure): + """ + As defined in ``i2c-dev.h``. + """ + _fields_ = [ + ('msgs', POINTER(i2c_msg)), + ('nmsgs', c_uint32) + ] + __slots__ = [name for name, type in _fields_] + + @staticmethod + def create(*i2c_msg_instances): + """ + Factory method for creating a i2c_rdwr_ioctl_data struct that can + be called with ``ioctl(fd, I2C_RDWR, data)``. + + :param i2c_msg_instances: Up to 42 i2c_msg instances + :rtype: i2c_rdwr_ioctl_data + """ + n_msg = len(i2c_msg_instances) + msg_array = (i2c_msg * n_msg)(*i2c_msg_instances) + return i2c_rdwr_ioctl_data( + msgs=msg_array, + nmsgs=n_msg + ) + + +############################################################# + + +class SMBus(object): + + def __init__(self, bus=None, force=False): + """ + Initialize and (optionally) open an i2c bus connection. + + :param bus: i2c bus number (e.g. 0 or 1) + or an absolute file path (e.g. `/dev/i2c-42`). + If not given, a subsequent call to ``open()`` is required. + :type bus: int or str + :param force: force using the slave address even when driver is + already using it. + :type force: boolean + """ + self.fd = None + self.funcs = I2cFunc(0) + if bus is not None: + self.open(bus) + self.address = None + self.force = force + self._force_last = None + + def __enter__(self): + """Enter handler.""" + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + """Exit handler.""" + self.close() + + def open(self, bus): + """ + Open a given i2c bus. + + :param bus: i2c bus number (e.g. 0 or 1) + or an absolute file path (e.g. '/dev/i2c-42'). + :type bus: int or str + :raise TypeError: if type(bus) is not in (int, str) + """ + if isinstance(bus, int): + filepath = "/dev/i2c-{}".format(bus) + elif isinstance(bus, str): + filepath = bus + else: + raise TypeError("Unexpected type(bus)={}".format(type(bus))) + + self.fd = os.open(filepath, os.O_RDWR) + self.funcs = self._get_funcs() + + def close(self): + """ + Close the i2c connection. + """ + if self.fd: + os.close(self.fd) + self.fd = None + + def _set_address(self, address, force=None): + """ + Set i2c slave address to use for subsequent calls. + + :param address: + :type address: int + :param force: + :type force: Boolean + """ + force = force if force is not None else self.force + if self.address != address or self._force_last != force: + if force is True: + ioctl(self.fd, I2C_SLAVE_FORCE, address) + else: + ioctl(self.fd, I2C_SLAVE, address) + self.address = address + self._force_last = force + + def _get_funcs(self): + """ + Returns a 32-bit value stating supported I2C functions. + + :rtype: int + """ + f = c_uint32() + ioctl(self.fd, I2C_FUNCS, f) + return f.value + + def write_quick(self, i2c_addr, force=None): + """ + Perform quick transaction. Throws IOError if unsuccessful. + :param i2c_addr: i2c address + :type i2c_addr: int + :param force: + :type force: Boolean + """ + self._set_address(i2c_addr, force=force) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_WRITE, command=0, size=I2C_SMBUS_QUICK) + ioctl(self.fd, I2C_SMBUS, msg) + + def read_byte(self, i2c_addr, force=None): + """ + Read a single byte from a device. + + :rtype: int + :param i2c_addr: i2c address + :type i2c_addr: int + :param force: + :type force: Boolean + :return: Read byte value + """ + self._set_address(i2c_addr, force=force) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_READ, command=0, size=I2C_SMBUS_BYTE + ) + ioctl(self.fd, I2C_SMBUS, msg) + return msg.data.contents.byte + + def write_byte(self, i2c_addr, value, force=None): + """ + Write a single byte to a device. + + :param i2c_addr: i2c address + :type i2c_addr: int + :param value: value to write + :type value: int + :param force: + :type force: Boolean + """ + self._set_address(i2c_addr, force=force) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_WRITE, command=value, size=I2C_SMBUS_BYTE + ) + ioctl(self.fd, I2C_SMBUS, msg) + + def read_byte_data(self, i2c_addr, register, force=None): + """ + Read a single byte from a designated register. + + :param i2c_addr: i2c address + :type i2c_addr: int + :param register: Register to read + :type register: int + :param force: + :type force: Boolean + :return: Read byte value + :rtype: int + """ + val_t = -1 + returnmsg="" + try: + self._set_address(i2c_addr, force=force) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_READ, command=register, size=I2C_SMBUS_BYTE_DATA + ) + val_t = ioctl(self.fd, I2C_SMBUS, msg) + except Exception as e: + self.close() + returnmsg = str(e) + if val_t < 0: + return False, returnmsg + else: + return True, msg.data.contents.byte + + def write_byte_data(self, i2c_addr, register, value, force=None): + """ + Write a byte to a given register. + + :param i2c_addr: i2c address + :type i2c_addr: int + :param register: Register to write to + :type register: int + :param value: Byte value to transmit + :type value: int + :param force: + :type force: Boolean + :rtype: None + """ + val_t = -1 + returnmsg = "" + try: + self._set_address(i2c_addr, force=force) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_WRITE, command=register, size=I2C_SMBUS_BYTE_DATA + ) + msg.data.contents.byte = value + val_t = ioctl(self.fd, I2C_SMBUS, msg) + except Exception as e: + returnmsg = str(e) + self.close() + if val_t < 0: + return False, returnmsg or "" + else: + return True, "" + + def write_byte_data_pec(self, i2c_addr, register, value, force=None): + """ + Write a byte to a given register. + + :param i2c_addr: i2c address + :type i2c_addr: int + :param register: Register to write to + :type register: int + :param value: Byte value to transmit + :type value: int + :param force: + :type force: Boolean + :rtype: None + """ + val_t = -1 + returnmsg = "" + try: + val_t = ioctl(self.fd, I2C_PEC, 1) + if val_t < 0: + raise Exception("set pec mod error") + self._set_address(i2c_addr, force=force) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_WRITE, command=register, size=I2C_SMBUS_BYTE_DATA + ) + msg.data.contents.byte = value + val_t = ioctl(self.fd, I2C_SMBUS, msg) + except Exception as e: + returnmsg = str(e) + self.close() + if val_t < 0: + return False, returnmsg or "" + else: + return True, "" + + def read_word_data(self, i2c_addr, register, force=None): + """ + Read a single word (2 bytes) from a given register. + + :param i2c_addr: i2c address + :type i2c_addr: int + :param register: Register to read + :type register: int + :param force: + :type force: Boolean + :return: 2-byte word + :rtype: int + """ + val_t = -1 + returnmsg = "" + try: + self._set_address(i2c_addr, force=force) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_READ, command=register, size=I2C_SMBUS_WORD_DATA + ) + val_t = ioctl(self.fd, I2C_SMBUS, msg) + except Exception as e: + returnmsg = str(e) + self.close() + if val_t < 0: + return False, returnmsg or "" + else: + return True, msg.data.contents.word + + def write_word_data_pec(self, i2c_addr, register, value, force=None): + """ + Write a byte to a given register. + + :param i2c_addr: i2c address + :type i2c_addr: int + :param register: Register to write to + :type register: int + :param value: Word value to transmit + :type value: int + :param force: + :type force: Boolean + :rtype: None + """ + val_t = -1 + returnmsg = "" + try: + val_t = ioctl(self.fd, I2C_PEC, 1) + if val_t < 0: + raise Exception("set pec mod error") + self._set_address(i2c_addr, force=force) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_WRITE, command=register, size=I2C_SMBUS_WORD_DATA + ) + msg.data.contents.word = value + val_t = ioctl(self.fd, I2C_SMBUS, msg) + except Exception as e: + returnmsg = str(e) + self.close() + if val_t < 0: + return False, returnmsg or "" + else: + return True, "" + + def write_word_data(self, i2c_addr, register, value, force=None): + """ + Write a byte to a given register. + + :param i2c_addr: i2c address + :type i2c_addr: int + :param register: Register to write to + :type register: int + :param value: Word value to transmit + :type value: int + :param force: + :type force: Boolean + :rtype: None + """ + val_t = -1 + returnmsg = "" + try: + self._set_address(i2c_addr, force=force) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_WRITE, command=register, size=I2C_SMBUS_WORD_DATA + ) + msg.data.contents.word = value + val_t = ioctl(self.fd, I2C_SMBUS, msg) + except Exception as e: + returnmsg = str(e) + self.close() + if val_t < 0: + return False, returnmsg or "" + else: + return True, "" + + def process_call(self, i2c_addr, register, value, force=None): + """ + Executes a SMBus Process Call, sending a 16-bit value and receiving a 16-bit response + + :param i2c_addr: i2c address + :type i2c_addr: int + :param register: Register to read/write to + :type register: int + :param value: Word value to transmit + :type value: int + :param force: + :type force: Boolean + :rtype: int + """ + self._set_address(i2c_addr, force=force) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_WRITE, command=register, size=I2C_SMBUS_PROC_CALL + ) + msg.data.contents.word = value + ioctl(self.fd, I2C_SMBUS, msg) + return msg.data.contents.word + + def read_block_data(self, i2c_addr, register, force=None): + """ + Read a block of up to 32-bytes from a given register. + + :param i2c_addr: i2c address + :type i2c_addr: int + :param register: Start register + :type register: int + :param force: + :type force: Boolean + :return: List of bytes + :rtype: list + """ + self._set_address(i2c_addr, force=force) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_READ, command=register, size=I2C_SMBUS_BLOCK_DATA + ) + ioctl(self.fd, I2C_SMBUS, msg) + length = msg.data.contents.block[0] + return msg.data.contents.block[1:length + 1] + + def write_block_data(self, i2c_addr, register, data, force=None): + """ + Write a block of byte data to a given register. + + :param i2c_addr: i2c address + :type i2c_addr: int + :param register: Start register + :type register: int + :param data: List of bytes + :type data: list + :param force: + :type force: Boolean + :rtype: None + """ + length = len(data) + if length > I2C_SMBUS_BLOCK_MAX: + raise ValueError("Data length cannot exceed %d bytes" % I2C_SMBUS_BLOCK_MAX) + self._set_address(i2c_addr, force=force) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_WRITE, command=register, size=I2C_SMBUS_BLOCK_DATA + ) + msg.data.contents.block[0] = length + msg.data.contents.block[1:length + 1] = data + ioctl(self.fd, I2C_SMBUS, msg) + + def block_process_call(self, i2c_addr, register, data, force=None): + """ + Executes a SMBus Block Process Call, sending a variable-size data + block and receiving another variable-size response + + :param i2c_addr: i2c address + :type i2c_addr: int + :param register: Register to read/write to + :type register: int + :param data: List of bytes + :type data: list + :param force: + :type force: Boolean + :return: List of bytes + :rtype: list + """ + length = len(data) + if length > I2C_SMBUS_BLOCK_MAX: + raise ValueError("Data length cannot exceed %d bytes" % I2C_SMBUS_BLOCK_MAX) + self._set_address(i2c_addr, force=force) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_WRITE, command=register, size=I2C_SMBUS_BLOCK_PROC_CALL + ) + msg.data.contents.block[0] = length + msg.data.contents.block[1:length + 1] = data + ioctl(self.fd, I2C_SMBUS, msg) + length = msg.data.contents.block[0] + return msg.data.contents.block[1:length + 1] + + def read_i2c_block_data(self, i2c_addr, register, length, force=None): + """ + Read a block of byte data from a given register. + + :param i2c_addr: i2c address + :type i2c_addr: int + :param register: Start register + :type register: int + :param length: Desired block length + :type length: int + :param force: + :type force: Boolean + :return: List of bytes + :rtype: list + """ + if length > I2C_SMBUS_BLOCK_MAX: + raise ValueError("Desired block length over %d bytes" % I2C_SMBUS_BLOCK_MAX) + self._set_address(i2c_addr, force=force) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_READ, command=register, size=I2C_SMBUS_I2C_BLOCK_DATA + ) + msg.data.contents.byte = length + ioctl(self.fd, I2C_SMBUS, msg) + return msg.data.contents.block[1:length + 1] + + def write_i2c_block_data(self, i2c_addr, register, data, force=None): + """ + Write a block of byte data to a given register. + + :param i2c_addr: i2c address + :type i2c_addr: int + :param register: Start register + :type register: int + :param data: List of bytes + :type data: list + :param force: + :type force: Boolean + :rtype: None + """ + length = len(data) + if length > I2C_SMBUS_BLOCK_MAX: + raise ValueError("Data length cannot exceed %d bytes" % I2C_SMBUS_BLOCK_MAX) + self._set_address(i2c_addr, force=force) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_WRITE, command=register, size=I2C_SMBUS_I2C_BLOCK_DATA + ) + msg.data.contents.block[0] = length + msg.data.contents.block[1:length + 1] = data + ioctl(self.fd, I2C_SMBUS, msg) + + def i2c_rdwr(self, *i2c_msgs): + """ + Combine a series of i2c read and write operations in a single + transaction (with repeated start bits but no stop bits in between). + + This method takes i2c_msg instances as input, which must be created + first with :py:meth:`i2c_msg.read` or :py:meth:`i2c_msg.write`. + + :param i2c_msgs: One or more i2c_msg class instances. + :type i2c_msgs: i2c_msg + :rtype: None + """ + ioctl_data = i2c_rdwr_ioctl_data.create(*i2c_msgs) + ioctl(self.fd, I2C_RDWR, ioctl_data) + + +class SMBusWrapper: + """ + Wrapper class around the SMBus. + Deprecated as of version 0.3.0. Please replace with :py:class:`SMBus`. + + Enables the user to wrap access to the :py:class:`SMBus` class in a + "with" statement. If auto_cleanup is True (default), the + :py:class:`SMBus` handle will be automatically closed + upon exit of the ``with`` block. + """ + def __init__(self, bus_number=0, auto_cleanup=True, force=False): + """ + :param auto_cleanup: Close bus when leaving scope. + :type auto_cleanup: Boolean + :param force: Force using the slave address even when driver is already using it. + :type force: Boolean + """ + self.bus_number = bus_number + self.auto_cleanup = auto_cleanup + self.force = force + + def __enter__(self): + self.bus = SMBus(bus=self.bus_number, force=self.force) + return self.bus + + def __exit__(self, exc_type, exc_val, exc_tb): + if self.auto_cleanup: + self.bus.close() diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/modules/Makefile b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/Makefile new file mode 100644 index 000000000000..5723fdfd4d2a --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/Makefile @@ -0,0 +1,28 @@ +PWD = $(shell pwd) +EXTRA_CFLAGS:= -I$(M)/include +EXTRA_CFLAGS+= -Wall +KVERSION ?= $(shell uname -r) +KERNEL_SRC ?= /lib/modules/$(KVERSION) + +module_out_put_dir := $(PWD)/build +export module_out_put_dir + +platform_common-objs := platform_common_module.o dfd_tlveeprom.o +obj-m += wb_platform.o +obj-m += i2c-mux-pca9641.o +obj-m += i2c-mux-pca954x.o +obj-m += platform_common.o +obj-m += rtcpcf85063.o +obj-m += optoe.o +obj-m += wb_at24.o + +all : + $(MAKE) -C $(KERNEL_SRC)/build M=$(PWD) modules + @if [ ! -d $(module_out_put_dir) ]; then mkdir -p $(module_out_put_dir) ;fi + cp -p $(PWD)/*.ko $(module_out_put_dir) + +clean : + rm -rf $(module_out_put_dir) + rm -f ${PWD}/*.o ${PWD}/*.ko ${PWD}/*.mod.c ${PWD}/.*.cmd ${PWD}/.*.o.d + rm -f ${PWD}/Module.markers ${PWD}/Module.symvers ${PWD}/modules.order + rm -rf ${PWD}/.tmp_versions diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/modules/dfd_tlveeprom.c b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/dfd_tlveeprom.c new file mode 100644 index 000000000000..f6f9df1f6e73 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/dfd_tlveeprom.c @@ -0,0 +1,521 @@ +/* + * Copyright (C) 2003-2014 FreeIPMI Core Team + * + * 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 + * (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, see . + * + */ +/*****************************************************************************\ + * Copyright (C) 2007-2014 Lawrence Livermore National Security, LLC. + * Copyright (C) 2007 The Regents of the University of California. + * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). + * Written by Albert Chu + * UCRL-CODE-232183 + * + * This file is part of Ipmi-fru, a tool used for retrieving + * motherboard field replaceable unit (FRU) information. For details, + * see http://www.llnl.gov/linux/. + * + * Ipmi-fru 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 (at your + * option) any later version. + * + * Ipmi-fru 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 Ipmi-fru. If not, see . +\*****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform.h" +#include "dfd_tlveeprom.h" + +/* using in is_valid_tlvinfo_header */ +static u_int32_t eeprom_size; + +/* + * List of TLV codes and names. + */ +static const struct tlv_code_desc tlv_code_list[] = { + { TLV_CODE_PRODUCT_NAME , "Product Name"}, + { TLV_CODE_PART_NUMBER , "Part Number"}, + { TLV_CODE_SERIAL_NUMBER , "Serial Number"}, + { TLV_CODE_MAC_BASE , "Base MAC Address"}, + { TLV_CODE_MANUF_DATE , "Manufacture Date"}, + { TLV_CODE_DEVICE_VERSION , "Device Version"}, + { TLV_CODE_LABEL_REVISION , "Label Revision"}, + { TLV_CODE_PLATFORM_NAME , "Platform Name"}, + { TLV_CODE_ONIE_VERSION , "ONIE Version"}, + { TLV_CODE_MAC_SIZE , "MAC Addresses"}, + { TLV_CODE_MANUF_NAME , "Manufacturer"}, + { TLV_CODE_MANUF_COUNTRY , "Country Code"}, + { TLV_CODE_VENDOR_NAME , "Vendor Name"}, + { TLV_CODE_DIAG_VERSION , "Diag Version"}, + { TLV_CODE_SERVICE_TAG , "Service Tag"}, + { TLV_CODE_VENDOR_EXT , "Vendor Extension"}, + { TLV_CODE_CRC_32 , "CRC-32"}, +}; + +#if 0 +#define OPENBMC_VPD_KEY_INVAIL_VAL 0 + +static const tlv_code_map_t tlv_code_map[] = { + { TLV_CODE_PRODUCT_NAME , OPENBMC_VPD_KEY_PRODUCT_NAME}, + { TLV_CODE_PART_NUMBER , OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM}, + { TLV_CODE_SERIAL_NUMBER , OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM}, + { TLV_CODE_MAC_BASE , OPENBMC_VPD_KEY_INVAIL_VAL}, + { TLV_CODE_MANUF_DATE , OPENBMC_VPD_KEY_BOARD_MFG_DATE}, + { TLV_CODE_DEVICE_VERSION , OPENBMC_VPD_KEY_PRODUCT_VER}, + { TLV_CODE_LABEL_REVISION , OPENBMC_VPD_KEY_PRODUCT_CUSTOM7}, + { TLV_CODE_PLATFORM_NAME , OPENBMC_VPD_KEY_PRODUCT_CUSTOM1}, + { TLV_CODE_ONIE_VERSION , OPENBMC_VPD_KEY_PRODUCT_CUSTOM2}, + { TLV_CODE_MAC_SIZE , OPENBMC_VPD_KEY_INVAIL_VAL}, + { TLV_CODE_MANUF_NAME , OPENBMC_VPD_KEY_PRODUCT_MFR}, + { TLV_CODE_MANUF_COUNTRY , OPENBMC_VPD_KEY_PRODUCT_CUSTOM3}, + { TLV_CODE_VENDOR_NAME , OPENBMC_VPD_KEY_PRODUCT_CUSTOM4}, + { TLV_CODE_DIAG_VERSION , OPENBMC_VPD_KEY_PRODUCT_CUSTOM8}, + { TLV_CODE_SERVICE_TAG , OPENBMC_VPD_KEY_PRODUCT_CUSTOM5}, + { TLV_CODE_VENDOR_EXT , OPENBMC_VPD_KEY_PRODUCT_CUSTOM6}, + { TLV_CODE_CRC_32 , OPENBMC_VPD_KEY_INVAIL_VAL}, +}; +#endif + +#define TLV_CODE_NUM (sizeof(tlv_code_list) / sizeof(tlv_code_list[0])) + +#if 0 +#define TLV_CODE_MAP_NUM (sizeof(tlv_code_map) / sizeof(tlv_code_map[0])) +#endif + +const unsigned long crc_table[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, +}; + +static unsigned long crc32(unsigned long crc, const unsigned char *buf, unsigned len) +{ + unsigned i; + if (len < 1) + return 0xffffffff; + + for (i = 0; i != len; ++i) + { + crc = crc_table[(crc ^ buf[i]) & 0xff] ^ (crc >> 8); + } + + crc = crc ^ 0xffffffff; + + return crc; +} + +/* + * is_valid_tlv + * + * Perform basic sanity checks on a TLV field. The TLV is pointed to + * by the parameter provided. + * 1. The type code is not reserved (0x00 or 0xFF) + */ +static inline bool is_valid_tlv(tlvinfo_tlv_t *tlv) +{ + return ((tlv->type != 0x00) && (tlv->type != 0xFF)); +} + +/* + * is_valid_tlvinfo_header + * + * Perform sanity checks on the first 11 bytes of the TlvInfo EEPROM + * data pointed to by the parameter: + * 1. First 8 bytes contain null-terminated ASCII string "TlvInfo" + * 2. Version byte is 1 + * 3. Total length bytes contain value which is less than or equal + * to the allowed maximum (2048-11) + * + */ +static inline bool is_valid_tlvinfo_header(tlvinfo_header_t *hdr) +{ + int max_size = eeprom_size; + return((strcmp(hdr->signature, TLV_INFO_ID_STRING) == 0) && + (hdr->version == TLV_INFO_VERSION) && + (be16_to_cpu(hdr->totallen) <= max_size) ); +} + +/* + * decode_tlv_value + * + * Decode a single TLV value into a string. + + * The validity of EEPROM contents and the TLV field have been verified + * prior to calling this function. + */ +static void decode_tlv_value(tlvinfo_tlv_t *tlv, tlv_decode_value_t *decode_value) +{ + int i; + char *value; + u_int32_t length; + + value = (char *)decode_value->value; + + switch (tlv->type) { + case TLV_CODE_PRODUCT_NAME: + case TLV_CODE_PART_NUMBER: + case TLV_CODE_SERIAL_NUMBER: + case TLV_CODE_MANUF_DATE: + case TLV_CODE_LABEL_REVISION: + case TLV_CODE_PLATFORM_NAME: + case TLV_CODE_ONIE_VERSION: + case TLV_CODE_MANUF_NAME: + case TLV_CODE_MANUF_COUNTRY: + case TLV_CODE_VENDOR_NAME: + case TLV_CODE_DIAG_VERSION: + case TLV_CODE_SERVICE_TAG: + case TLV_CODE_VENDOR_EXT: + memcpy(value, tlv->value, tlv->length); + value[tlv->length] = 0; + length = tlv->length; + break; + case TLV_CODE_MAC_BASE: + length = sprintf(value, "%02X:%02X:%02X:%02X:%02X:%02X", + tlv->value[0], tlv->value[1], tlv->value[2], + tlv->value[3], tlv->value[4], tlv->value[5]); + break; + case TLV_CODE_DEVICE_VERSION: + length = sprintf(value, "%u", tlv->value[0]); + break; + case TLV_CODE_MAC_SIZE: + length = sprintf(value, "%u", (tlv->value[0] << 8) | tlv->value[1]); + break; + #if 0 + case TLV_CODE_VENDOR_EXT: + value[0] = 0; + length = 0; + for (i = 0; (i < (TLV_DECODE_VALUE_MAX_LEN/5)) && (i < tlv->length); i++) { + length += sprintf(value, "%s 0x%02X", value, tlv->value[i]); + } + break; + #endif + case TLV_CODE_CRC_32: + length = sprintf(value, "0x%02X%02X%02X%02X", tlv->value[0], + tlv->value[1], tlv->value[2], tlv->value[3]); + break; + default: + value[0] = 0; + length = 0; + for (i = 0; (i < (TLV_DECODE_VALUE_MAX_LEN/5)) && (i < tlv->length); i++) { + length += sprintf(value, "%s 0x%02X", value, tlv->value[i]); + } + break; + } + + decode_value->length = length; +} + +/* + * is_checksum_valid + * + * Validate the checksum in the provided TlvInfo EEPROM data. First, + * verify that the TlvInfo header is valid, then make sure the last + * TLV is a CRC-32 TLV. Then calculate the CRC over the EEPROM data + * and compare it to the value stored in the EEPROM CRC-32 TLV. + */ +static bool is_checksum_valid(u_int8_t *eeprom) +{ + tlvinfo_header_t *eeprom_hdr; + tlvinfo_tlv_t *eeprom_crc; + unsigned int calc_crc; + unsigned int stored_crc; + + eeprom_hdr = (tlvinfo_header_t *) eeprom; + + /* Is the eeprom header valid? */ + if (!is_valid_tlvinfo_header(eeprom_hdr)) { + return false; + } + + /* Is the last TLV a CRC? */ + eeprom_crc = (tlvinfo_tlv_t *) &eeprom[sizeof(tlvinfo_header_t) + + be16_to_cpu(eeprom_hdr->totallen) - (sizeof(tlvinfo_tlv_t) + 4)]; + if ((eeprom_crc->type != TLV_CODE_CRC_32) || (eeprom_crc->length != 4)) { + return false; + } + + /* Calculate the checksum */ + calc_crc = crc32(0xffffffffL, (const unsigned char *)eeprom, sizeof(tlvinfo_header_t) + + be16_to_cpu(eeprom_hdr->totallen) - 4); + stored_crc = ((eeprom_crc->value[0] << 24) | (eeprom_crc->value[1] << 16) | + (eeprom_crc->value[2] << 8) | eeprom_crc->value[3]); + + return (calc_crc == stored_crc); +} + +/* + * tlvinfo_find_tlv + * + * This function finds the TLV with the supplied code in the EERPOM. + * An offset from the beginning of the EEPROM is returned in the + * eeprom_index parameter if the TLV is found. + */ +static bool tlvinfo_find_tlv(u_int8_t *eeprom, u_int8_t tcode, int *eeprom_index) +{ + tlvinfo_header_t *eeprom_hdr; + tlvinfo_tlv_t *eeprom_tlv; + int eeprom_end; + + eeprom_hdr = (tlvinfo_header_t *) eeprom; + + /* Search through the TLVs, looking for the first one which matches the + supplied type code. */ + *eeprom_index = sizeof(tlvinfo_header_t); + eeprom_end = sizeof(tlvinfo_header_t) + be16_to_cpu(eeprom_hdr->totallen); + while (*eeprom_index < eeprom_end) { + eeprom_tlv = (tlvinfo_tlv_t *) &eeprom[*eeprom_index]; + if (!is_valid_tlv(eeprom_tlv)) { + return false; + } + + if (eeprom_tlv->type == tcode) { + return true; + } + + *eeprom_index += sizeof(tlvinfo_tlv_t) + eeprom_tlv->length; + } + + return false; +} + +/* + * tlvinfo_decode_tlv + * + * This function finds the TLV with the supplied code in the EERPOM + * and decodes the value into the buffer provided. + */ +static bool tlvinfo_decode_tlv(u_int8_t *eeprom, u_int8_t tcode, tlv_decode_value_t *decode_value) +{ + int eeprom_index; + tlvinfo_tlv_t *eeprom_tlv; + + /* Find the TLV and then decode it */ + if (tlvinfo_find_tlv(eeprom, tcode, &eeprom_index)) { + eeprom_tlv = (tlvinfo_tlv_t *) &eeprom[eeprom_index]; + decode_tlv_value(eeprom_tlv, decode_value); + return true; + } + + return false; +} + +/* + * parse_tlv_eeprom + * + * parse the EEPROM into memory, if it hasn't already been read. + */ +int parse_tlv_eeprom(u_int8_t *eeprom, u_int32_t size) +{ + unsigned int i; + bool ret; + tlvinfo_header_t *eeprom_hdr; + //tlv_info_vec_t tlv_info; + tlv_decode_value_t decode_value; + int j; + + eeprom_hdr = (tlvinfo_header_t *) eeprom; + eeprom_size = size; /* eeprom real size */ + + if (!is_valid_tlvinfo_header(eeprom_hdr)) { + DBG_ERROR("Failed to check tlv header.\n"); + return -1; + } + + if (!is_checksum_valid(eeprom)) { + DBG_ERROR("Failed to check tlv crc.\n"); + return -1; + } + + for (i = 0; i < TLV_CODE_NUM; i++) { + mem_clear((void *)&decode_value, sizeof(tlv_decode_value_t)); + ret = tlvinfo_decode_tlv(eeprom, tlv_code_list[i].m_code, &decode_value); + if (!ret) { + DBG_ERROR("No found type: %s\n", tlv_code_list[i].m_name); + continue; + } + + DBG_DEBUG("i: %d,Found type: %s tlv[%d]:%s\n", i, tlv_code_list[i].m_name, tlv_code_list[i].m_code, + decode_value.value); + for (j = 0; j < decode_value.length; j++) { + if ((j % 16) == 0) { + DBG_DEBUG("\n"); + } + DBG_DEBUG("%02x ", decode_value.value[j]); + } + DBG_DEBUG("\n\n"); + } + return 0; +} +static int dfd_parse_tlv_eeprom(u_int8_t *eeprom, u_int32_t size, u_int8_t main_type, tlv_decode_value_t *decode_value) +{ + bool ret; + tlvinfo_header_t *eeprom_hdr; + /* tlv_info_vec_t tlv_info; */ + int j; + + eeprom_hdr = (tlvinfo_header_t *) eeprom; + eeprom_size = size; /* eeprom real size */ + + if (!is_valid_tlvinfo_header(eeprom_hdr)) { + DBG_ERROR("Failed to check tlv header.\n"); + return -1; + } + + if (!is_checksum_valid(eeprom)) { + DBG_ERROR("Failed to check tlv crc.\n"); + return -1; + } + + ret = tlvinfo_decode_tlv(eeprom, main_type, decode_value); + if (!ret) { + DBG_ERROR("No found type: %d\n", main_type); + return -1; + } + + DBG_DEBUG("Found type: %d, value: %s\n", main_type,decode_value->value); + for (j = 0; j < decode_value->length; j++) { + if ((j % 16) == 0) { + DBG_DEBUG("\n"); + } + DBG_DEBUG("%02x ", decode_value->value[j]); + } + DBG_DEBUG("\n\n"); + + return 0; +} + +/* analyze the extended custom TLV format */ +static int tlvinfo_find_wb_ext_tlv(tlv_decode_value_t *ext_tlv_value, u_int8_t ext_type, + u_int8_t *buf, u_int8_t *buf_len) +{ + tlvinfo_tlv_t *eeprom_tlv; + int eeprom_end, eeprom_index; + + /* Search through the TLVs, looking for the first one which matches the + supplied type code. */ + DBG_DEBUG("ext_tlv_value->length: %d.\n", ext_tlv_value->length); + for (eeprom_index = 0; eeprom_index < ext_tlv_value->length; eeprom_index++) { + if ((eeprom_index % 16) == 0) { + DBG_DEBUG("\n"); + } + DBG_DEBUG("%02x ", ext_tlv_value->value[eeprom_index]); + } + + DBG_DEBUG("\n"); + + eeprom_index = 0; + eeprom_end = ext_tlv_value->length; + while (eeprom_index < eeprom_end) { + eeprom_tlv = (tlvinfo_tlv_t *) &(ext_tlv_value->value[eeprom_index]); + if (!is_valid_tlv(eeprom_tlv)) { + DBG_ERROR("tlv is not valid, eeprom_tlv->type 0x%x.\n", eeprom_tlv->type); + return -1; + } + + DBG_DEBUG("eeprom_tlv->length %d.\n", eeprom_tlv->length); + if (eeprom_tlv->type == ext_type) { + if (*buf_len >= eeprom_tlv->length) { + memcpy(buf, eeprom_tlv->value, eeprom_tlv->length); + DBG_DEBUG("eeprom_tlv->length %d.\n", eeprom_tlv->length); + *buf_len = eeprom_tlv->length; + return 0; + } + DBG_ERROR("buf_len %d small than info_len %d.\n", *buf_len, eeprom_tlv->length); + return -1; + } + + eeprom_index += sizeof(tlvinfo_tlv_t) + eeprom_tlv->length; + } + + DBG_ERROR("ext_type %d: tlv is not found.\n", ext_type); + return -1; +} + +/* get EEPROM information */ +int dfd_tlvinfo_get_e2prom_info(u_int8_t *eeprom, u_int32_t size, dfd_tlv_type_t *tlv_type, u_int8_t* buf, u_int8_t *buf_len) +{ + tlv_decode_value_t decode_value; + int ret; + + if (eeprom == NULL || tlv_type == NULL || buf == NULL) { + DBG_ERROR("Input para invalid.\n"); + return -1; + } + + mem_clear((void *)&decode_value, sizeof(tlv_decode_value_t)); + ret = dfd_parse_tlv_eeprom(eeprom, size, tlv_type->main_type, &decode_value); + if (ret) { + DBG_ERROR("dfd_parse_tlv_eeprom failed ret %d.\n", ret); + return ret; + } + + /* For non-extended types, return data directly */ + if (tlv_type->main_type != TLV_CODE_VENDOR_EXT) { + if (*buf_len >= decode_value.length) { + memcpy(buf, decode_value.value, decode_value.length); + *buf_len = decode_value.length; + return 0; + } + DBG_ERROR("buf_len %d small than info_len %d.\n", *buf_len, decode_value.length); + return -1; + } + DBG_DEBUG("info_len %d.\n", decode_value.length); + + /* For the extended type, continue with the secondary TLV analysis to obtain + the data corresponding to the sub-TLV type */ + return tlvinfo_find_wb_ext_tlv(&decode_value, tlv_type->ext_type, buf, buf_len); +} diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/modules/dfd_tlveeprom.h b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/dfd_tlveeprom.h new file mode 100644 index 000000000000..6c5b2e98be52 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/dfd_tlveeprom.h @@ -0,0 +1,121 @@ +#ifndef DFD_OPENBMC_TLVEEPROM_H +#define DFD_OPENBMC_TLVEEPROM_H + +#ifndef u_int8_t +#define u_int8_t unsigned char +#endif + +#ifndef u_int16_t +#define u_int16_t unsigned short +#endif + +#ifndef u_int32_t +#define u_int32_t unsigned int +#endif + +#ifndef be16_to_cpu +#define be16_to_cpu(x) ntohs(x) +#endif + +#ifndef cpu_to_be16 +#define cpu_to_be16(x) htons(x) +#endif + +/** + * The TLV Types. + * + * Keep these in sync with tlv_code_list in cmd_sys_eeprom.c + */ +#define TLV_CODE_PRODUCT_NAME 0x21 +#define TLV_CODE_PART_NUMBER 0x22 +#define TLV_CODE_SERIAL_NUMBER 0x23 +#define TLV_CODE_MAC_BASE 0x24 +#define TLV_CODE_MANUF_DATE 0x25 +#define TLV_CODE_DEVICE_VERSION 0x26 +#define TLV_CODE_LABEL_REVISION 0x27 +#define TLV_CODE_PLATFORM_NAME 0x28 +#define TLV_CODE_ONIE_VERSION 0x29 +#define TLV_CODE_MAC_SIZE 0x2A +#define TLV_CODE_MANUF_NAME 0x2B +#define TLV_CODE_MANUF_COUNTRY 0x2C +#define TLV_CODE_VENDOR_NAME 0x2D +#define TLV_CODE_DIAG_VERSION 0x2E +#define TLV_CODE_SERVICE_TAG 0x2F +#define TLV_CODE_VENDOR_EXT 0xFD +#define TLV_CODE_CRC_32 0xFE + +#define TLV_CODE_NAME_LEN 64 +/* + * Struct for displaying the TLV codes and names. + */ +struct tlv_code_desc { + u_int8_t m_code; + char m_name[TLV_CODE_NAME_LEN]; +}; +/* ONIE TLV Type and Extended TLV Type Definition */ +typedef struct dfd_tlv_type_s { + u_int8_t main_type;/* ONIE standard TLV TYPE type */ + u_int8_t ext_type; /* Extended TLV TYPE type */ +} dfd_tlv_type_t; + +/* Header Field Constants */ +#define TLV_INFO_ID_STRING "TlvInfo" +#define TLV_INFO_VERSION 0x01 +/*#define TLV_TOTAL_LEN_MAX (XXXXXXXX - sizeof(tlvinfo_header_t))*/ + +struct __attribute__ ((__packed__)) tlvinfo_header_s { + char signature[8]; /* 0x00 - 0x07 EEPROM Tag "TlvInfo" */ + u_int8_t version; /* 0x08 Structure version */ + u_int16_t totallen; /* 0x09 - 0x0A Length of all data which follows */ +}; +typedef struct tlvinfo_header_s tlvinfo_header_t; + +/* + * TlvInfo TLV: Layout of a TLV field + */ +struct __attribute__ ((__packed__)) tlvinfo_tlv_s { + u_int8_t type; + u_int8_t length; + u_int8_t value[0]; +}; +typedef struct tlvinfo_tlv_s tlvinfo_tlv_t; + +#define TLV_VALUE_MAX_LEN 255 +/* + * The max decode value is currently for the 'raw' type or the 'vendor + * extension' type, both of which have the same decode format. The + * max decode string size is computed as follows: + * + * strlen(" 0xFF") * TLV_VALUE_MAX_LEN + 1 + * + */ +#define TLV_DECODE_VALUE_MAX_LEN ((5 * TLV_VALUE_MAX_LEN) + 1) + +typedef struct tlv_decode_value_s { + u_int8_t value[TLV_DECODE_VALUE_MAX_LEN]; + u_int32_t length; +} tlv_decode_value_t; + +typedef enum dfd_tlvinfo_ext_tlv_type_e { + DFD_TLVINFO_EXT_TLV_TYPE_DEV_TYPE = 1, +} dfd_tlvinfo_ext_tlv_type_t; + +#if 0 +#define TLV_TIME_LEN 64 + +int ipmi_tlv_validate_fru_area(const uint8_t fruid, const char *fru_file_name, + sd_bus *bus_type, const bool bmc_fru); + +extern const char *get_vpd_key_names(int key_id); +extern std::string getService(sdbusplus::bus::bus& bus, + const std::string& intf, + const std::string& path); +extern std::string getFRUValue(const std::string& section, + const std::string& key, + const std::string& delimiter, + IPMIFruInfo& fruData); +#endif + +int dfd_tlvinfo_get_e2prom_info(u_int8_t *eeprom, u_int32_t size, dfd_tlv_type_t *tlv_type, u_int8_t* buf, u_int8_t *buf_len); + +#endif /* endif DFD_OPENBMC_TLVEEPROM_H */ diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/modules/i2c-mux-pca954x.c b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/i2c-mux-pca954x.c new file mode 100644 index 000000000000..ada87d5d8f2f --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/i2c-mux-pca954x.c @@ -0,0 +1,3817 @@ +/* + * I2C multiplexer + * + * Copyright (c) 2008-2009 Rodolfo Giometti + * Copyright (c) 2008-2009 Eurotech S.p.A. + * + * This module supports the PCA954x series of I2C multiplexer/switch chips + * made by Philips Semiconductors. + * This includes the: + * PCA9540, PCA9542, PCA9543, PCA9544, PCA9545, PCA9546, PCA9547 + * and PCA9548. + * + * These chips are all controlled via the I2C bus itself, and all have a + * single 8-bit register. The upstream "parent" bus fans out to two, + * four, or eight downstream busses or channels; which of these + * are selected is determined by the chip type and register contents. A + * mux can select only one sub-bus at a time; a switch can select any + * combination simultaneously. + * + * 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 + +#define mem_clear(data, size) memset((data), 0, (size)) + +#if LINUX_VERSION_CODE > KERNEL_VERSION(4,0,36) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCA954X_MAX_NCHANS 8 + +#define PCA954X_IRQ_OFFSET 4 + +extern int pca9641_setmuxflag(int nr, int flag); + +int force_create_bus = 0; +module_param(force_create_bus, int, S_IRUGO | S_IWUSR); + +enum pca_type { + pca_9540, + pca_9542, + pca_9543, + pca_9544, + pca_9545, + pca_9546, + pca_9547, + pca_9548, +}; + +struct chip_desc { + u8 nchans; + u8 enable; /* used for muxes only */ + u8 has_irq; + enum muxtype { + pca954x_ismux = 0, + pca954x_isswi + } muxtype; +}; + +struct pca954x { + const struct chip_desc *chip; + + u8 last_chan; /* last register value */ + s32 idle_state; + u8 deselect; + struct i2c_client *client; + + struct irq_domain *irq; + unsigned int irq_mask; + raw_spinlock_t lock; +}; + +/* Provide specs for the PCA954x types we know about */ +static const struct chip_desc chips[] = { + [pca_9540] = { + .nchans = 2, + .enable = 0x4, + .muxtype = pca954x_ismux, + }, + [pca_9542] = { + .nchans = 2, + .enable = 0x4, + .has_irq = 1, + .muxtype = pca954x_ismux, + }, + [pca_9543] = { + .nchans = 2, + .has_irq = 1, + .muxtype = pca954x_isswi, + }, + [pca_9544] = { + .nchans = 4, + .enable = 0x4, + .has_irq = 1, + .muxtype = pca954x_ismux, + }, + [pca_9545] = { + .nchans = 4, + .has_irq = 1, + .muxtype = pca954x_isswi, + }, + [pca_9546] = { + .nchans = 4, + .muxtype = pca954x_isswi, + }, + [pca_9547] = { + .nchans = 8, + .enable = 0x8, + .muxtype = pca954x_ismux, + }, + [pca_9548] = { + .nchans = 8, + .muxtype = pca954x_isswi, + }, +}; + +static const struct i2c_device_id pca954x_id[] = { + { "pca9540", pca_9540 }, + { "pca9542", pca_9542 }, + { "pca9543", pca_9543 }, + { "pca9544", pca_9544 }, + { "pca9545", pca_9545 }, + { "pca9546", pca_9546 }, + { "pca9547", pca_9547 }, + { "pca9548", pca_9548 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pca954x_id); + +#ifdef CONFIG_OF +static const struct of_device_id pca954x_of_match[] = { + { .compatible = "nxp,pca9540", .data = &chips[pca_9540] }, + { .compatible = "nxp,pca9542", .data = &chips[pca_9542] }, + { .compatible = "nxp,pca9543", .data = &chips[pca_9543] }, + { .compatible = "nxp,pca9544", .data = &chips[pca_9544] }, + { .compatible = "nxp,pca9545", .data = &chips[pca_9545] }, + { .compatible = "nxp,pca9546", .data = &chips[pca_9546] }, + { .compatible = "nxp,pca9547", .data = &chips[pca_9547] }, + { .compatible = "nxp,pca9548", .data = &chips[pca_9548] }, + {} +}; +MODULE_DEVICE_TABLE(of, pca954x_of_match); +#endif + +/* 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 pca954x_reg_write(struct i2c_adapter *adap, + struct i2c_client *client, u8 val) +{ + int ret = -ENODEV; + + if (adap->algo->master_xfer) { + struct i2c_msg msg; + char buf[1]; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 1; + buf[0] = val; + msg.buf = buf; + ret = __i2c_transfer(adap, &msg, 1); + + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + } else { + union i2c_smbus_data data; + ret = adap->algo->smbus_xfer(adap, client->addr, + client->flags, + I2C_SMBUS_WRITE, + val, I2C_SMBUS_BYTE, &data); + } + + return ret; +} + +static u8 pca954x_regval(struct pca954x *data, u8 chan) +{ + /* We make switches look like muxes, not sure how to be smarter. */ + if (data->chip->muxtype == pca954x_ismux) + return chan | data->chip->enable; + else + return 1 << chan; +} + static int pca954x_setmuxflag(struct i2c_client *client, int flag) + { + struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); + pca9641_setmuxflag(adap->nr, flag); + return 0; + } + +static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan) +{ + struct pca954x *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + u8 regval; + int ret = 0; + regval = pca954x_regval(data, chan); + + /* we make switches look like muxes, not sure how to be smarter */ + + /* Only select the channel if its different from the last channel */ + if (data->last_chan != regval) { + pca954x_setmuxflag(client, 0); + ret = pca954x_reg_write(muxc->parent, client, regval); + data->last_chan = ret < 0 ? 0 : regval; + } + + return ret; +} + +typedef void (*pca954x_hw_do_reset_func_t)(int busid, int addr); +pca954x_hw_do_reset_func_t g_notify_to_do_reset = NULL; + +void pca954x_hw_do_reset_func_register(void* func) +{ + if (func == NULL) { + return ; + } + g_notify_to_do_reset = func; + return; +} +EXPORT_SYMBOL(pca954x_hw_do_reset_func_register); + +static int pca954x_hw_do_reset(int busid, int addr) +{ + if (g_notify_to_do_reset != NULL) { + (*g_notify_to_do_reset)(busid, addr); + return 0; + } + + return 0; +} +/***************************************9548 reset*****************************************/ +#define DEV_TYPE 0x4040 /* BT2575 */ +#define PCA9548_MAX_CPLD_NUM (32) /* PCA9548 max number */ +#define PCA9548_MAX_CPLD_LAYER (8) /* PCA9548 max layer */ +#define DFD_PID_BUF_LEN (32) +#define DFD_PRODUCT_ID_LENGTH (8) +#define CPLD_PCA9548_RESET 0x023500b0 /* bus:2, addr:0x35, offset:0xb0 */ +#define B6510_32CQ_CPLD_PCA9548_RESET 0x060d0060 /* bus:6, addr:0x0d, offset:0x60 */ + +#define DFD_PUB_CARDTYPE_FILE "/sys/module/platform_common/parameters/dfd_my_type" +#define DFD_MAX_PRODUCT_NUM (32) + +#define I2C_RETRY_TIMES 5 +#define I2C_RETRY_WAIT_TIMES 10 /*delay 10ms*/ + +#define PCA9548_I2C_GET_BUS(addr) (((addr) >> 24) & 0x00ff) +#define PCA9548_I2C_GET_CLIENT(addr) (((addr) >> 16) & 0x00ff) +#define PCA9548_I2C_GET_OFFSET(addr) (addr & 0xffff) + +typedef enum pca9548_reset_type { + PCA9548_RESET_FUNC = 0, + PCA9548_RESET_GPIO = 1, +} pca9548_reset_type_t; + +typedef void (*pca954x_hw_do_reset_func_t_new)(int io_port, u8 value); +typedef u8 (*pca954x_get_umask_func_t)(int io_port); + +void pca954x_hw_do_reset_by_i2c(int addr, u8 value); +u8 pca954x_get_umask_by_i2c(int addr); +void pca954x_hw_do_reset_by_lpc(int io_port, u8 value); +u8 pca954x_get_umask_by_lpc(int io_port); + +typedef struct func_attr_s { + int cfg_offset[PCA9548_MAX_CPLD_LAYER]; + int umask[PCA9548_MAX_CPLD_LAYER]; + pca954x_hw_do_reset_func_t_new reset_func; /* 9548 reset function */ + pca954x_get_umask_func_t get_umask_func; /* get reset mask */ +} func_attr_t; + +typedef struct gpio_attr_s { + int gpio; + int gpio_init; + u8 reset_on; + u8 reset_off; +} gpio_attr_t; + +typedef struct pca9548_cfg_info_s { + int pca9548_reset_type; + int pca9548_bus; + int pca9548_addr; + int rst_delay_b; /* delay time before reset(us) */ + int rst_delay; /* reset time(us) */ + int rst_delay_a; /* delay time after reset(us) */ + union { + func_attr_t func_attr; + gpio_attr_t gpio_attr; + } attr; +} pca9548_cfg_info_t; + +typedef struct fpga_pcie_card_info_s { + int dev_type[DFD_MAX_PRODUCT_NUM]; /* dev type */ + pca9548_cfg_info_t pca9548_cfg_info[PCA9548_MAX_CPLD_NUM]; +} pca9548_card_info_t; + +static pca9548_card_info_t g_pca9548_card_info[] = { + { + .dev_type = {0x4040,0x4061,0x4071}, /*B6510,BT2575,TCS81*/ + .pca9548_cfg_info = { + /* psu fan */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 2, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 7, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 1, + .gpio_attr.reset_off = 0, + }, + }, + /* sff1 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(0), -1}, + }, + }, + /* sff2 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x71, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(1), -1}, + }, + }, + /* sff3 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x72, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(2), -1}, + }, + }, + /* sff4 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x73, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(3), -1}, + }, + }, + /* sff5 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x74, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(4), -1}, + }, + }, + /* sff6 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x75, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(5), -1}, + }, + }, + /* sff7 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x76, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(6), -1}, + }, + }, + }, + }, + { + .dev_type = {0x4041}, /*B6520*/ + .pca9548_cfg_info = { + /* psu fan */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 2, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 7, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 1, + .gpio_attr.reset_off = 0, + }, + }, + /* sff1 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(0), -1}, + }, + }, + /* sff2 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x71, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(1), -1}, + }, + }, + /* sff3 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x72, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(2), -1}, + }, + }, + /* sff4 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x73, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(3), -1}, + }, + }, + /* sff5 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x74, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(4), -1}, + }, + }, + /* sff6 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x75, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(5), -1}, + }, + }, + /* sff7 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x76, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(6), -1}, + }, + }, + /* sff8 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x77, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(7), -1}, + }, + }, + }, + }, + { + .dev_type = {0x4044,0x4072,0x4048}, /*B6920,TCS83,BS100R0*/ + .pca9548_cfg_info = { + /* 9548 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 2, + .pca9548_addr = 0x76, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0x936, -1}, + .func_attr.umask = {BIT(4), -1}, + }, + }, + /* base board */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 8, + .pca9548_addr = 0x77, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0x917, -1}, + .func_attr.umask = {BIT(4), -1}, + }, + }, + /* sff1 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 9, + .pca9548_addr = 0x77, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0x917, -1}, + .func_attr.umask = {BIT(0), -1}, + }, + }, + /* sff2 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 12, + .pca9548_addr = 0x77, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0x917, -1}, + .func_attr.umask = {BIT(1), -1}, + }, + }, + /* sff3 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 11, + .pca9548_addr = 0x77, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0x917, -1}, + .func_attr.umask = {BIT(2), -1}, + }, + }, + /* sff4 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 7, + .pca9548_addr = 0x77, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0x917, -1}, + .func_attr.umask = {BIT(3), -1}, + }, + }, + /* fanA */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 14, + .pca9548_addr = 0x77, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb10, -1}, + .func_attr.umask = {BIT(5), -1}, + }, + }, + /* fanB */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 13, + .pca9548_addr = 0x77, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb10, -1}, + .func_attr.umask = {BIT(7), -1}, + }, + }, + /* sff1 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 3, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(0), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 3, + .pca9548_addr = 0x71, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(0), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 3, + .pca9548_addr = 0x72, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(0), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 3, + .pca9548_addr = 0x73, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(0), -1}, + }, + }, + /* sff2 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 4, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(1), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 4, + .pca9548_addr = 0x71, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(1), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 4, + .pca9548_addr = 0x72, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(1), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 4, + .pca9548_addr = 0x73, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(1), -1}, + }, + }, + /* sff3 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 5, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(2), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 5, + .pca9548_addr = 0x71, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(2), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 5, + .pca9548_addr = 0x72, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(2), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 5, + .pca9548_addr = 0x73, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(2), -1}, + }, + }, + /* sff4 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 6, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(3), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 6, + .pca9548_addr = 0x71, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(3), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 6, + .pca9548_addr = 0x72, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(3), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 6, + .pca9548_addr = 0x73, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(3), -1}, + }, + }, + }, + }, + { + .dev_type = {0x4058,0x4073}, /* B6510-32CQ, TCS82 */ + .pca9548_cfg_info = { + /* psu */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 4, + .pca9548_addr = 0x77, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0x960, -1}, + .func_attr.umask = {BIT(0), -1}, + }, + }, + /* fan */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 2, + .pca9548_addr = 0x77, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0x960, -1}, + .func_attr.umask = {BIT(1), -1}, + }, + }, + }, + }, + { + .dev_type = {0x4065}, /* AS61-48E4T */ + .pca9548_cfg_info = { + /* SOC */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 0, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + /* SFP+ */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 8, + .pca9548_addr = 0x74, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + }, + }, + { + .dev_type = {0x4066}, /* AS61-48X4T */ + .pca9548_cfg_info = { + /* SOC */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 0, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + /* SFP1 */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 7, + .pca9548_addr = 0x71, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + /* SFP2 */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 7, + .pca9548_addr = 0x72, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + /* SFP3 */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 7, + .pca9548_addr = 0x73, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + /* SFP4 */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 8, + .pca9548_addr = 0x71, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + /* SFP5 */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 8, + .pca9548_addr = 0x72, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + /* SFP6 */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 8, + .pca9548_addr = 0x73, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + /* SFP+ */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 8, + .pca9548_addr = 0x74, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + }, + }, + { + .dev_type = {0x4087}, /* AS61-48E4T-LC */ + .pca9548_cfg_info = { + /* SOC */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 0, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + /* SFP+ */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 8, + .pca9548_addr = 0x74, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + }, + }, + { + .dev_type = {0x4088}, /* AS61-48E4T-LD */ + .pca9548_cfg_info = { + /* SOC */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 0, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + /* SFP+ */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 8, + .pca9548_addr = 0x74, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + }, + }, +}; +int g_pca954x_debug = 0; +module_param(g_pca954x_debug, int, S_IRUGO | S_IWUSR); + +#define PCA954X_DEBUG(fmt, args...) do { \ + if (g_pca954x_debug) { \ + printk(KERN_ERR "[PCA95x][VER][func:%s line:%d]\r\n"fmt, __func__, __LINE__, ## args); \ + } \ +} while (0) + +/* x86 device get type method */ +static int dfd_get_my_dev_type_by_file(void) +{ + struct file *fp; + /* mm_segment_t fs;*/ + loff_t pos; + static int card_type; + char buf[DFD_PID_BUF_LEN]; + + if (card_type != 0) { + return card_type; + } + + fp= filp_open(DFD_PUB_CARDTYPE_FILE, O_RDONLY, 0); + if (IS_ERR(fp)) { + PCA954X_DEBUG("open file fail!\r\n"); + return -1; + } + /* fs = get_fs(); */ + /* set_fs(KERNEL_DS); */ + mem_clear(buf, DFD_PID_BUF_LEN); + pos = 0; + kernel_read(fp, buf, DFD_PRODUCT_ID_LENGTH + 1, &pos); + if (pos < 0) { + PCA954X_DEBUG("read file fail!\r\n"); + goto exit; + } + + card_type = simple_strtoul(buf, NULL, 10); + PCA954X_DEBUG("card_type 0x%x.\n", card_type); + +exit: + /* set_fs(fs); */ + filp_close(fp, NULL); + return card_type; +} + +static int drv_get_my_dev_type(void) +{ + static int type = -1; + + if (type > 0) { + return type; + } + type = dfd_get_my_dev_type_by_file(); + PCA954X_DEBUG("ko board type %d\r\n", type); + + return type; +} + +pca9548_card_info_t* pca9548_get_card_info(int dev_type) +{ + int i, j; + int size; + + size = ARRAY_SIZE(g_pca9548_card_info); + + PCA954X_DEBUG("Enter dev_type 0x%x size %d.\n", dev_type, size); + for (i = 0; i < size; i++) { + for(j = 0; j < DFD_MAX_PRODUCT_NUM; j++) { + if (g_pca9548_card_info[i].dev_type[j] == dev_type) { + PCA954X_DEBUG("match dev_type 0x%x.\n", dev_type); + return &g_pca9548_card_info[i]; + } + } + } + + PCA954X_DEBUG("dismatch dev_type 0x%x.\n", dev_type); + return NULL; +} + +pca9548_cfg_info_t* get_pca9548_cfg_info(int bus, int addr) +{ + int dev_type; + pca9548_card_info_t *info; + pca9548_cfg_info_t *pca9548_cfg_info; + int i; + int size; + + dev_type = drv_get_my_dev_type(); + if (dev_type < 0) { + PCA954X_DEBUG("drv_get_my_dev_type failed ret %d.\n", dev_type); + return NULL; + } + + info = pca9548_get_card_info(dev_type); + if (info == NULL) { + PCA954X_DEBUG("fpga_pcie_get_card_info dev_type %d failed.\n", dev_type); + return NULL; + } + + size = PCA9548_MAX_CPLD_NUM; + for (i = 0; i < size; i++) { + pca9548_cfg_info = &(info->pca9548_cfg_info[i]); + if ((pca9548_cfg_info->pca9548_bus == bus) && (pca9548_cfg_info->pca9548_addr == addr)) { + PCA954X_DEBUG("match dev_type 0x%x bus %d addr 0x%x.\n", dev_type, bus, addr); + return pca9548_cfg_info; + } + } + + PCA954X_DEBUG("dismatch dev_type 0x%x bus %d addr 0x%x.\n", dev_type, bus, addr); + return NULL; +} + +static void pca9548_gpio_init(gpio_attr_t *gpio_attr) +{ + if (gpio_attr->gpio_init == 0) { + PCA954X_DEBUG("gpio%d init.\n", gpio_attr->gpio); + gpio_request(gpio_attr->gpio, "pca9548_reset"); + gpio_direction_output(gpio_attr->gpio, gpio_attr->reset_off); + gpio_attr->gpio_init = 1; + } +} + +static void pca9548_gpio_free(gpio_attr_t *gpio_attr) +{ + if (gpio_attr == NULL) { + PCA954X_DEBUG("pca9548_gpio_free,params error\n"); + return ; + } + if (gpio_attr->gpio_init == 1) { + PCA954X_DEBUG("gpio%d release.\n", gpio_attr->gpio); + gpio_free(gpio_attr->gpio); + gpio_attr->gpio_init = 0; + } +} + +static int pca954x_do_gpio_reset(pca9548_cfg_info_t *cfg_info, struct i2c_adapter *adap, + struct i2c_client *client, u32 chan) +{ + struct i2c_mux_core *muxc = i2c_get_clientdata(client); + struct pca954x *data = i2c_mux_priv(muxc); + int ret = -1; + gpio_attr_t *tmp_gpio_attr; + int timeout; + int val; + struct i2c_adapter *adapter; + int adapter_timeout; + + if (cfg_info == NULL) { + PCA954X_DEBUG("pca9548 cfg info is null.\n"); + return ret; + } + + if (cfg_info->pca9548_reset_type == PCA9548_RESET_GPIO) { + tmp_gpio_attr = &(cfg_info->attr.gpio_attr); + timeout = cfg_info->rst_delay_a; + + pca9548_gpio_init(tmp_gpio_attr); + udelay(cfg_info->rst_delay_b); + /* reset on */ + PCA954X_DEBUG("set gpio%d %d.\n", tmp_gpio_attr->gpio, tmp_gpio_attr->reset_on); + __gpio_set_value(tmp_gpio_attr->gpio, tmp_gpio_attr->reset_on); + udelay(cfg_info->rst_delay); + /* reset off */ + PCA954X_DEBUG("set gpio%d %d.\n", tmp_gpio_attr->gpio, tmp_gpio_attr->reset_off); + __gpio_set_value(tmp_gpio_attr->gpio, tmp_gpio_attr->reset_off); + + while (timeout > 0) { + udelay(1); + val = __gpio_get_value(tmp_gpio_attr->gpio); + if (val == tmp_gpio_attr->reset_off) { + adapter = adap; + /* get bus info */ + while(i2c_parent_is_i2c_adapter(adapter)){ + adapter = to_i2c_adapter(adapter->dev.parent); + } + + adapter_timeout = adapter->timeout; + adapter->timeout = msecs_to_jiffies(50); + pca954x_reg_write(adap, client, data->last_chan); + adapter->timeout = adapter_timeout; + ret = 0; + PCA954X_DEBUG("pca954x_do_gpio_reset success.\n"); + break; + } + + if (timeout >= 1000 && (timeout % 1000 == 0)) { + /* 1MS schedule*/ + schedule(); + } + timeout--; + } + + if (ret) { + PCA954X_DEBUG("pca954x_do_gpio_reset failed.\n"); + } + pca9548_gpio_free(&(cfg_info->attr.gpio_attr)); + } else { + PCA954X_DEBUG("pca9548_reset_type invalid, pca954x_do_gpio_reset failed.\n"); + } + + return ret; +} + +static int pca954x_do_func_reset(pca9548_cfg_info_t *cfg_info, struct i2c_adapter *adap, + struct i2c_client *client, u32 chan) +{ + struct i2c_mux_core *muxc = i2c_get_clientdata(client); + struct pca954x *data = i2c_mux_priv(muxc); + int ret = -1; + func_attr_t *tmp_func_attr; + int timeout; + int val; + struct i2c_adapter *adapter; + int adapter_timeout; + int i; + u8 old_value; + + if (cfg_info == NULL) { + PCA954X_DEBUG("pca9548 cfg info is null.\n"); + return ret; + } + + if (cfg_info->pca9548_reset_type == PCA9548_RESET_FUNC) { + tmp_func_attr = &(cfg_info->attr.func_attr); + timeout = cfg_info->rst_delay_a; + + if ((tmp_func_attr->reset_func == NULL) || (tmp_func_attr->get_umask_func == NULL)) { + PCA954X_DEBUG("pca954x hw do reset func or get umask func is null.\n"); + return ret; + } + + for(i = 0; (i < PCA9548_MAX_CPLD_LAYER) && (tmp_func_attr->cfg_offset[i] != -1) + && (tmp_func_attr->umask[i] != -1); i++) { + old_value = (*tmp_func_attr->get_umask_func)(tmp_func_attr->cfg_offset[i]); + PCA954X_DEBUG("cfg info: offset:0x%x umask:0x%x, old_value:0x%x\n", + tmp_func_attr->cfg_offset[i], tmp_func_attr->umask[i],old_value); + (*tmp_func_attr->reset_func)(tmp_func_attr->cfg_offset[i], old_value & ~tmp_func_attr->umask[i]); + udelay(cfg_info->rst_delay); + (*tmp_func_attr->reset_func)(tmp_func_attr->cfg_offset[i], old_value | tmp_func_attr->umask[i]); + } + + while (timeout > 0) { + udelay(1); + val = (*tmp_func_attr->get_umask_func)(tmp_func_attr->cfg_offset[i - 1]); + val &= (tmp_func_attr->umask[i - 1]); + if (val == tmp_func_attr->umask[i - 1]) { + adapter = adap; + /* get bus info */ + while(i2c_parent_is_i2c_adapter(adapter)){ + adapter = to_i2c_adapter(adapter->dev.parent); + } + + adapter_timeout = adapter->timeout; + adapter->timeout = msecs_to_jiffies(50); + pca954x_reg_write(adap, client, data->last_chan); + adapter->timeout = adapter_timeout; + ret = 0; + PCA954X_DEBUG("pca954x_do_func_reset success.\n"); + break; + } + + if (timeout >= 1000 && (timeout % 1000 == 0)) { + /* 1MS schedule*/ + schedule(); + } + timeout--; + } + + if (ret) { + PCA954X_DEBUG("pca954x_do_func_reset failed.\n"); + } + } else { + PCA954X_DEBUG("pca9548_reset_type invalid, pca954x_do_func_reset failed.\n"); + } + + return ret; +} + +static int pca9548_reset_ctrl(pca9548_cfg_info_t *cfg_info, struct i2c_adapter *adap, + struct i2c_client *client, u32 chan) +{ + int ret = -1; + + if (cfg_info == NULL) { + PCA954X_DEBUG("pca9548 cfg info is null.\n"); + return ret; + } + + if (cfg_info->pca9548_reset_type == PCA9548_RESET_FUNC) { + ret = pca954x_do_func_reset(cfg_info, adap, client, chan); + } else if (cfg_info->pca9548_reset_type == PCA9548_RESET_GPIO) { + ret = pca954x_do_gpio_reset(cfg_info, adap, client, chan); + } + + if (ret < 0) { + PCA954X_DEBUG("pca9548_reset_ctrl failed.\n"); + } + return ret; +} + +static int pca954x_reset_i2c_read(uint32_t bus, uint32_t addr, uint32_t offset_addr, + unsigned char *buf, uint32_t size) +{ + struct file *fp; + /* mm_segment_t fs; */ + struct i2c_client client; + char i2c_path[32]; + int i ,j ; + int rv; + + rv = 0; + mem_clear(i2c_path, sizeof(i2c_path)); + snprintf(i2c_path, sizeof(i2c_path), "/dev/i2c-%d", bus); + fp = filp_open(i2c_path, O_RDWR, S_IRUSR | S_IWUSR); + if (IS_ERR(fp)) { + PCA954X_DEBUG("i2c open fail.\n"); + return -1; + } + memcpy(&client, fp->private_data, sizeof(struct i2c_client)); + client.addr = addr; + /* fs = get_fs(); */ + /* set_fs(KERNEL_DS); */ + for (j = 0 ;j < size ;j++){ + for (i = 0; i < I2C_RETRY_TIMES; i++) { + rv = i2c_smbus_read_byte_data(&client, (offset_addr + j)); + if (rv < 0) { + PCA954X_DEBUG("i2c read failed, try again.\n"); + msleep(I2C_RETRY_WAIT_TIMES); + if (i >= (I2C_RETRY_TIMES - 1)) { + goto out; + } + continue; + } + *(buf + j) = (unsigned char)rv; + break; + } + } +out: + filp_close(fp, NULL); + /* set_fs(fs); */ + return rv; +} + +static int pca954x_reset_i2c_write(uint32_t bus, uint32_t dev_addr, uint32_t offset_addr, + uint8_t write_buf) +{ + struct file *fp; + /* mm_segment_t fs; */ + struct i2c_client client; + char i2c_path[32]; + int i; + int rv; + + rv = 0; + mem_clear(i2c_path, sizeof(i2c_path)); + snprintf(i2c_path, sizeof(i2c_path), "/dev/i2c-%d", bus); + fp = filp_open(i2c_path, O_RDWR, S_IRUSR | S_IWUSR); + if (IS_ERR(fp)) { + PCA954X_DEBUG("i2c open fail.\n"); + return -1; + } + memcpy(&client, fp->private_data, sizeof(struct i2c_client)); + client.addr = dev_addr; + /* fs = get_fs(); */ + /* set_fs(KERNEL_DS); */ + for (i = 0; i < I2C_RETRY_TIMES; i++) { + rv = i2c_smbus_write_byte_data(&client, offset_addr, write_buf); + if (rv < 0) { + PCA954X_DEBUG("i2c write failed, try again.\n"); + msleep(I2C_RETRY_WAIT_TIMES); + if (i >= (I2C_RETRY_TIMES - 1)) { + goto out; + } + continue; + } + break; + } +out: + filp_close(fp, NULL); + /* set_fs(fs); */ + return rv; +} + +int pca954x_reset_reg_i2c_read_byte(int addr, u8 *value) +{ + int bus; + int client_addr; + int offset; + int ret; + + bus = PCA9548_I2C_GET_BUS(addr); + client_addr = PCA9548_I2C_GET_CLIENT(addr); + offset = PCA9548_I2C_GET_OFFSET(addr); + + ret = pca954x_reset_i2c_read(bus, client_addr, offset, value, 1); + if (ret < 0) { + PCA954X_DEBUG(" 0x%x read fail\r\n", addr); + goto end; + } +end: + return ret; +} + +int pca954x_reset_reg_i2c_write_byte(int addr, u8 value) +{ + int bus; + int client_addr; + int offset; + int ret; + + bus = PCA9548_I2C_GET_BUS(addr); + client_addr = PCA9548_I2C_GET_CLIENT(addr); + offset = PCA9548_I2C_GET_OFFSET(addr); + + ret = pca954x_reset_i2c_write(bus, client_addr, offset, value); + if (ret < 0) { + PCA954X_DEBUG(" 0x%x write fail\r\n", addr); + goto end; + } +end: + return ret; +} + +void pca954x_hw_do_reset_by_i2c(int addr, u8 value) +{ + int ret; + + PCA954X_DEBUG("write i2c cpld[0x%x], value[%d]\n", addr, value); + ret = pca954x_reset_reg_i2c_write_byte(addr, value); + if (ret < 0) { + PCA954X_DEBUG("write cpld pca9548 reset reg failed, ret = %d \n", ret); + } +} + +u8 pca954x_get_umask_by_i2c(int addr) +{ + u8 value = 0xFF; + int ret; + + ret = pca954x_reset_reg_i2c_read_byte(addr, &value); + PCA954X_DEBUG("read i2c cpld[0x%x], value[%d], ret = %d\n", addr, value, ret); + + return value; +} + +void pca954x_hw_do_reset_by_lpc(int io_port, u8 value) +{ + PCA954X_DEBUG("write lpc offset[0x%x], value[%d]\n", (u16)io_port, value); + outb(value, (u16)io_port); +} + +u8 pca954x_get_umask_by_lpc(int io_port) +{ + u8 value; + + value = inb(io_port); + PCA954X_DEBUG("read lpc offset[0x%x], value[%d]\n", (u16)io_port, value); + + return value; +} + +int pca954x_hw_do_reset_new(struct i2c_adapter *adap, + struct i2c_client *client, u32 chan) +{ + pca9548_cfg_info_t *cfg_info; + int ret = -1; + + cfg_info = get_pca9548_cfg_info(adap->nr, client->addr); + if (cfg_info == NULL && g_notify_to_do_reset == NULL) { + PCA954X_DEBUG("fpga_do_pca954x_reset_func do nothing.\n"); + return ret; + } + if(cfg_info != NULL) { + ret = pca9548_reset_ctrl(cfg_info, adap, client, chan); + } else { + ret = pca954x_hw_do_reset(adap->nr, client->addr); + } + if (ret < 0) { + PCA954X_DEBUG("pca954x_hw_do_reset failed.\n"); + } + return ret; +} +/******************************end 9548 reset***********************************/ + +static int pca954x_do_reset(struct i2c_adapter *adap, + void *client, u32 chan) +{ + struct i2c_client *new_client; + int ret = -1; + + PCA954X_DEBUG("do pca954x reset x86\n"); + new_client =(struct i2c_client *) client; + ret = pca954x_hw_do_reset_new(adap, new_client, chan); + if (ret < 0) { + PCA954X_DEBUG("pca954x_do_reset failed.\n"); + return ret; + } + + PCA954X_DEBUG("pca954x_do_reset success.\n"); + ret = 0; + return ret; +} +static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan) +{ + struct pca954x *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + s32 idle_state; + int ret, rv; + struct i2c_client * new_client; + + idle_state = READ_ONCE(data->idle_state); + if (idle_state >= 0) + /* Set the mux back to a predetermined channel */ + return pca954x_select_chan(muxc, idle_state); + + if (idle_state == MUX_IDLE_DISCONNECT) { + /* Deselect active channel */ + data->last_chan = 0; + ret = pca954x_reg_write(muxc->parent, client, + data->last_chan); + if (ret < 0) { + new_client =(struct i2c_client *) client; + dev_warn(&new_client->dev, "pca954x close chn failed, do reset.\n"); + rv = pca954x_do_reset(client->adapter, client, chan); + if (rv == 0) { + ret = 0; + } + } + return ret; + } + + /* otherwise leave as-is */ + + return 0; +} + +static ssize_t idle_state_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct i2c_mux_core *muxc = i2c_get_clientdata(client); + struct pca954x *data = i2c_mux_priv(muxc); + + return sprintf(buf, "%d\n", READ_ONCE(data->idle_state)); +} + +static ssize_t idle_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct i2c_mux_core *muxc = i2c_get_clientdata(client); + struct pca954x *data = i2c_mux_priv(muxc); + int val; + int ret; + + ret = kstrtoint(buf, 0, &val); + if (ret < 0) + return ret; + + if (val != MUX_IDLE_AS_IS && val != MUX_IDLE_DISCONNECT && + (val < 0 || val >= data->chip->nchans)) + return -EINVAL; + + i2c_lock_bus(muxc->parent, I2C_LOCK_SEGMENT); + + WRITE_ONCE(data->idle_state, val); + /* + * Set the mux into a state consistent with the new + * idle_state. + */ + if (data->last_chan || val != MUX_IDLE_DISCONNECT) + ret = pca954x_deselect_mux(muxc, 0); + + i2c_unlock_bus(muxc->parent, I2C_LOCK_SEGMENT); + + return ret < 0 ? ret : count; +} + +static DEVICE_ATTR_RW(idle_state); + +static irqreturn_t pca954x_irq_handler(int irq, void *dev_id) +{ + struct pca954x *data = dev_id; + unsigned long pending; + int ret, i; + + ret = i2c_smbus_read_byte(data->client); + if (ret < 0) + return IRQ_NONE; + + pending = (ret >> PCA954X_IRQ_OFFSET) & (BIT(data->chip->nchans) - 1); + for_each_set_bit(i, &pending, data->chip->nchans) + handle_nested_irq(irq_linear_revmap(data->irq, i)); + + return IRQ_RETVAL(pending); +} + +static int pca954x_init(struct i2c_client *client, struct pca954x *data) +{ + int ret; + + if (data->idle_state >= 0) + data->last_chan = pca954x_regval(data, data->idle_state); + else + data->last_chan = 0; /* Disconnect multiplexer */ + + ret = i2c_smbus_write_byte(client, data->last_chan); + if (ret < 0) + data->last_chan = 0; + return ret; +} + +static void pca954x_irq_mask(struct irq_data *idata) +{ + struct pca954x *data = irq_data_get_irq_chip_data(idata); + unsigned int pos = idata->hwirq; + unsigned long flags; + + raw_spin_lock_irqsave(&data->lock, flags); + + data->irq_mask &= ~BIT(pos); + if (!data->irq_mask) + disable_irq(data->client->irq); + + raw_spin_unlock_irqrestore(&data->lock, flags); +} + +static void pca954x_irq_unmask(struct irq_data *idata) +{ + struct pca954x *data = irq_data_get_irq_chip_data(idata); + unsigned int pos = idata->hwirq; + unsigned long flags; + + raw_spin_lock_irqsave(&data->lock, flags); + + if (!data->irq_mask) + enable_irq(data->client->irq); + data->irq_mask |= BIT(pos); + + raw_spin_unlock_irqrestore(&data->lock, flags); +} + +static int pca954x_irq_set_type(struct irq_data *idata, unsigned int type) +{ + if ((type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_LOW) + return -EINVAL; + return 0; +} + +static struct irq_chip pca954x_irq_chip = { + .name = "i2c-mux-pca954x", + .irq_mask = pca954x_irq_mask, + .irq_unmask = pca954x_irq_unmask, + .irq_set_type = pca954x_irq_set_type, +}; + +static int pca954x_irq_setup(struct i2c_mux_core *muxc) +{ + struct pca954x *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + int c, err, irq; + + if (!data->chip->has_irq || client->irq <= 0) + return 0; + + raw_spin_lock_init(&data->lock); + + data->irq = irq_domain_add_linear(client->dev.of_node, + data->chip->nchans, + &irq_domain_simple_ops, data); + if (!data->irq) + return -ENODEV; + + for (c = 0; c < data->chip->nchans; c++) { + irq = irq_create_mapping(data->irq, c); + irq_set_chip_data(irq, data); + irq_set_chip_and_handler(irq, &pca954x_irq_chip, + handle_simple_irq); + } + + err = devm_request_threaded_irq(&client->dev, data->client->irq, NULL, + pca954x_irq_handler, + IRQF_ONESHOT | IRQF_SHARED, + "pca954x", data); + if (err) + goto err_req_irq; + + disable_irq(data->client->irq); + + return 0; +err_req_irq: + for (c = 0; c < data->chip->nchans; c++) { + irq = irq_find_mapping(data->irq, c); + irq_dispose_mapping(irq); + } + irq_domain_remove(data->irq); + + return err; +} + +/* + * I2C init/probing/exit functions + */ +static int pca954x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); + struct device_node *of_node = client->dev.of_node; + bool idle_disconnect_dt; + struct gpio_desc *gpio; + int num; + struct i2c_mux_core *muxc; + struct pca954x *data; + const struct of_device_id *match; + int ret; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE)) + return -ENODEV; + + muxc = i2c_mux_alloc(adap, &client->dev, + PCA954X_MAX_NCHANS, sizeof(*data), 0, + pca954x_select_chan, pca954x_deselect_mux); + if (!muxc) + return -ENOMEM; + data = i2c_mux_priv(muxc); + + i2c_set_clientdata(client, muxc); + data->client = client; + + /* Get the mux out of reset if a reset GPIO is specified. */ + gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(gpio)) + return PTR_ERR(gpio); + data->idle_state = MUX_IDLE_AS_IS; + if (device_property_read_u32(&client->dev, "idle-state", &data->idle_state)) { + if (device_property_read_bool(&client->dev, "i2c-mux-idle-disconnect")) + data->idle_state = MUX_IDLE_DISCONNECT; + } + + /* + * Write the mux register at addr to verify + * that the mux is in fact present. This also + * initializes the mux to a channel + * or disconnected state. + */ + ret = pca954x_init(client, data); + if (ret < 0) { + dev_warn(&client->dev, "probe failed\n"); + return -ENODEV; + } + + match = of_match_device(of_match_ptr(pca954x_of_match), &client->dev); + if (match) + data->chip = of_device_get_match_data(&client->dev); + else + data->chip = &chips[id->driver_data]; + + data->last_chan = 0; /* force the first selection */ + + idle_disconnect_dt = of_node && + of_property_read_bool(of_node, "i2c-mux-idle-disconnect"); + + ret = pca954x_irq_setup(muxc); + if (ret) + goto fail_del_adapters; + + /* Now create an adapter for each channel */ + for (num = 0; num < data->chip->nchans; num++) { + ret = i2c_mux_add_adapter(muxc, 0, num, 0); + if (ret) + goto fail_del_adapters; + } + + device_create_file(&client->dev, &dev_attr_idle_state); + dev_info(&client->dev, + "registered %d multiplexed busses for I2C %s %s\n", + num, data->chip->muxtype == pca954x_ismux + ? "mux" : "switch", client->name); + + return 0; + +fail_del_adapters: + i2c_mux_del_adapters(muxc); + return ret; +} + +static int pca954x_remove(struct i2c_client *client) +{ + struct i2c_mux_core *muxc = i2c_get_clientdata(client); + struct pca954x *data = i2c_mux_priv(muxc); + int c, irq; + + if (data->irq) { + for (c = 0; c < data->chip->nchans; c++) { + irq = irq_find_mapping(data->irq, c); + irq_dispose_mapping(irq); + } + irq_domain_remove(data->irq); + } + device_remove_file(&client->dev, &dev_attr_idle_state); + + i2c_mux_del_adapters(muxc); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int pca954x_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct i2c_mux_core *muxc = i2c_get_clientdata(client); + struct pca954x *data = i2c_mux_priv(muxc); + int ret; + + ret = pca954x_init(client, data); + if (ret < 0) + dev_err(&client->dev, "failed to verify mux presence\n"); + + return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(pca954x_pm, NULL, pca954x_resume); + +static struct i2c_driver pca954x_driver = { + .driver = { + .name = "pca954x", + .pm = &pca954x_pm, + .of_match_table = of_match_ptr(pca954x_of_match), + }, + .probe = pca954x_probe, + .remove = pca954x_remove, + .id_table = pca954x_id, +}; + +module_i2c_driver(pca954x_driver); + +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern int pca9641_setmuxflag(int nr, int flag); + +int force_create_bus = 0; +module_param(force_create_bus, int, S_IRUGO | S_IWUSR); + +#define PCA954X_MAX_NCHANS 8 + +enum pca_type { + pca_9540, + pca_9542, + pca_9543, + pca_9544, + pca_9545, + pca_9546, + pca_9547, + pca_9548, +}; + +struct pca954x { + enum pca_type type; + struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS]; + + u8 last_chan; /* last register value */ +}; + +struct chip_desc { + u8 nchans; + u8 enable; /* used for muxes only */ + enum muxtype { + pca954x_ismux = 0, + pca954x_isswi + } muxtype; +}; + +/* Provide specs for the PCA954x types we know about */ +static const struct chip_desc chips[] = { + [pca_9540] = { + .nchans = 2, + .enable = 0x4, + .muxtype = pca954x_ismux, + }, + [pca_9543] = { + .nchans = 2, + .muxtype = pca954x_isswi, + }, + [pca_9544] = { + .nchans = 4, + .enable = 0x4, + .muxtype = pca954x_ismux, + }, + [pca_9545] = { + .nchans = 4, + .muxtype = pca954x_isswi, + }, + [pca_9547] = { + .nchans = 8, + .enable = 0x8, + .muxtype = pca954x_ismux, + }, + [pca_9548] = { + .nchans = 8, + .muxtype = pca954x_isswi, + }, +}; + +static const struct i2c_device_id pca954x_id[] = { + { "pca9540", pca_9540 }, + { "pca9542", pca_9540 }, + { "pca9543", pca_9543 }, + { "pca9544", pca_9544 }, + { "pca9545", pca_9545 }, + { "pca9546", pca_9545 }, + { "pca9547", pca_9547 }, + { "pca9548", pca_9548 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pca954x_id); + +/* 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 pca954x_reg_write(struct i2c_adapter *adap, + struct i2c_client *client, u8 val) +{ + int ret = -ENODEV; + + if (adap->algo->master_xfer) { + struct i2c_msg msg; + char buf[1]; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 1; + buf[0] = val; + msg.buf = buf; + ret = adap->algo->master_xfer(adap, &msg, 1); + + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + } else { + union i2c_smbus_data data; + ret = adap->algo->smbus_xfer(adap, client->addr, + client->flags, + I2C_SMBUS_WRITE, + val, I2C_SMBUS_BYTE, &data); + } + + return ret; +} + +static int pca954x_setmuxflag(struct i2c_adapter *adap, int flag) +{ + pca9641_setmuxflag(adap->nr, flag); + return 0; +} + +static int pca954x_select_chan(struct i2c_adapter *adap, + void *client, u32 chan) +{ + struct pca954x *data = i2c_get_clientdata(client); + const struct chip_desc *chip = &chips[data->type]; + u8 regval; + int ret = 0; + + /* we make switches look like muxes, not sure how to be smarter */ + if (chip->muxtype == pca954x_ismux) + regval = chan | chip->enable; + else + regval = 1 << chan; + + /* Only select the channel if its different from the last channel */ + if (data->last_chan != regval) { + pca954x_setmuxflag(adap, 0); + ret = pca954x_reg_write(adap, client, regval); + data->last_chan = ret < 0 ? 0 : regval; + } + + return ret; +} + +typedef void (*pca954x_hw_do_reset_func_t)(int busid, int addr); +pca954x_hw_do_reset_func_t g_notify_to_do_reset = NULL; + +void pca954x_hw_do_reset_func_register(void* func) +{ + if (func == NULL) { + return ; + } + g_notify_to_do_reset = func; + return; +} +EXPORT_SYMBOL(pca954x_hw_do_reset_func_register); + +static int pca954x_hw_do_reset(int busid, int addr) +{ + if (g_notify_to_do_reset != NULL) { + (*g_notify_to_do_reset)(busid, addr); + return 0; + } + return 0; +} +/***************************************9548 reset*****************************************/ +#define DEV_TYPE 0x4040 /* BT2575 */ +#define PCA9548_MAX_CPLD_NUM (32) /* PCA9548 max number */ +#define PCA9548_MAX_CPLD_LAYER (8) /* PCA9548 max layer */ +#define DFD_PID_BUF_LEN (32) +#define DFD_PRODUCT_ID_LENGTH (8) +#define CPLD_PCA9548_RESET 0x023500b0 /* bus:2, addr:0x35, offset:0xb0 */ +#define B6510_32CQ_CPLD_PCA9548_RESET 0x060d0060 /* bus:6, addr:0x0d, offset:0x60 */ +#define DFD_PUB_CARDTYPE_FILE "/sys/module/platform_common/parameters/dfd_my_type" +#define DFD_MAX_PRODUCT_NUM (32) + +#define I2C_RETRY_TIMES 5 +#define I2C_RETRY_WAIT_TIMES 10 /*delay 10ms*/ + +#define PCA9548_I2C_GET_BUS(addr) (((addr) >> 24) & 0x00ff) +#define PCA9548_I2C_GET_CLIENT(addr) (((addr) >> 16) & 0x00ff) +#define PCA9548_I2C_GET_OFFSET(addr) (addr & 0xffff) + +typedef enum pca9548_reset_type { + PCA9548_RESET_FUNC = 0, + PCA9548_RESET_GPIO = 1, +} pca9548_reset_type_t; + +typedef void (*pca954x_hw_do_reset_func_t_new)(int io_port, u8 value); +typedef u8 (*pca954x_get_umask_func_t)(int io_port); + +void pca954x_hw_do_reset_by_i2c(int addr, u8 value); +u8 pca954x_get_umask_by_i2c(int addr); +void pca954x_hw_do_reset_by_lpc(int io_port, u8 value); +u8 pca954x_get_umask_by_lpc(int io_port); + +typedef struct func_attr_s { + int cfg_offset[PCA9548_MAX_CPLD_LAYER]; + int umask[PCA9548_MAX_CPLD_LAYER]; + pca954x_hw_do_reset_func_t_new reset_func; /* 9548 reset function */ + pca954x_get_umask_func_t get_umask_func; /* get reset mask */ +} func_attr_t; + +typedef struct gpio_attr_s { + int gpio; + int gpio_init; + u8 reset_on; + u8 reset_off; +} gpio_attr_t; + +typedef struct pca9548_cfg_info_s { + int pca9548_reset_type; + int pca9548_bus; + int pca9548_addr; + int rst_delay_b; /* delay time before reset(us) */ + int rst_delay; /* reset time(us) */ + int rst_delay_a; /* delay time after reset(us) */ + union { + func_attr_t func_attr; + gpio_attr_t gpio_attr; + } attr; +} pca9548_cfg_info_t; + +typedef struct fpga_pcie_card_info_s { + int dev_type[DFD_MAX_PRODUCT_NUM]; /* dev type */ + pca9548_cfg_info_t pca9548_cfg_info[PCA9548_MAX_CPLD_NUM]; +} pca9548_card_info_t; + +static pca9548_card_info_t g_pca9548_card_info[] = { + { + .dev_type = {0x4040,0x4061,0x4071}, /*B6510,BT2575,TCS81*/ + .pca9548_cfg_info = { + /* psu fan */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 2, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 7, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 1, + .gpio_attr.reset_off = 0, + }, + }, + /* sff1 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(0), -1}, + }, + }, + /* sff2 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x71, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(1), -1}, + }, + }, + /* sff3 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x72, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(2), -1}, + }, + }, + /* sff4 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x73, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(3), -1}, + }, + }, + /* sff5 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x74, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(4), -1}, + }, + }, + /* sff6 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x75, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(5), -1}, + }, + }, + /* sff7 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x76, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(6), -1}, + }, + }, + }, + }, + { + .dev_type = {0x4041}, /*B6520*/ + .pca9548_cfg_info = { + /* psu fan */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 2, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 7, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 1, + .gpio_attr.reset_off = 0, + }, + }, + /* sff1 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(0), -1}, + }, + }, + /* sff2 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x71, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(1), -1}, + }, + }, + /* sff3 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x72, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(2), -1}, + }, + }, + /* sff4 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x73, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(3), -1}, + }, + }, + /* sff5 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x74, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(4), -1}, + }, + }, + /* sff6 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x75, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(5), -1}, + }, + }, + /* sff7 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x76, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(6), -1}, + }, + }, + /* sff8 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 1, + .pca9548_addr = 0x77, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_i2c, + .func_attr.get_umask_func = pca954x_get_umask_by_i2c, + .func_attr.cfg_offset = {CPLD_PCA9548_RESET, -1}, + .func_attr.umask = {BIT(7), -1}, + }, + }, + }, + }, + { + .dev_type = {0x4044,0x4072,0x4048}, /*B6920,TCS83,BS100R0*/ + .pca9548_cfg_info = { + /* 9548 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 2, + .pca9548_addr = 0x76, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0x936, -1}, + .func_attr.umask = {BIT(4), -1}, + }, + }, + /* base board */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 8, + .pca9548_addr = 0x77, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0x917, -1}, + .func_attr.umask = {BIT(4), -1}, + }, + }, + /* sff1 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 9, + .pca9548_addr = 0x77, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0x917, -1}, + .func_attr.umask = {BIT(0), -1}, + }, + }, + /* sff2 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 12, + .pca9548_addr = 0x77, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0x917, -1}, + .func_attr.umask = {BIT(1), -1}, + }, + }, + /* sff3 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 11, + .pca9548_addr = 0x77, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0x917, -1}, + .func_attr.umask = {BIT(2), -1}, + }, + }, + /* sff4 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 7, + .pca9548_addr = 0x77, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0x917, -1}, + .func_attr.umask = {BIT(3), -1}, + }, + }, + /* fanA */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 14, + .pca9548_addr = 0x77, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb10, -1}, + .func_attr.umask = {BIT(5), -1}, + }, + }, + /* fanB */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 13, + .pca9548_addr = 0x77, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb10, -1}, + .func_attr.umask = {BIT(7), -1}, + }, + }, + /* sff1 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 3, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(0), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 3, + .pca9548_addr = 0x71, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(0), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 3, + .pca9548_addr = 0x72, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(0), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 3, + .pca9548_addr = 0x73, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(0), -1}, + }, + }, + /* sff2 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 4, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(1), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 4, + .pca9548_addr = 0x71, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(1), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 4, + .pca9548_addr = 0x72, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(1), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 4, + .pca9548_addr = 0x73, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(1), -1}, + }, + }, + /* sff3 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 5, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(2), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 5, + .pca9548_addr = 0x71, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(2), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 5, + .pca9548_addr = 0x72, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(2), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 5, + .pca9548_addr = 0x73, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(2), -1}, + }, + }, + /* sff4 */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 6, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(3), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 6, + .pca9548_addr = 0x71, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(3), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 6, + .pca9548_addr = 0x72, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(3), -1}, + }, + }, + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 6, + .pca9548_addr = 0x73, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0xb17, -1}, + .func_attr.umask = {BIT(3), -1}, + }, + }, + }, + }, + { + .dev_type = {0x4058,0x4073}, /* B6510-32CQ, TCS82 */ + .pca9548_cfg_info = { + /* psu */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 4, + .pca9548_addr = 0x77, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0x960, -1}, + .func_attr.umask = {BIT(0), -1}, + }, + }, + /* fan */ + { + .pca9548_reset_type = PCA9548_RESET_FUNC, + .pca9548_bus = 2, + .pca9548_addr = 0x77, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .func_attr.reset_func = pca954x_hw_do_reset_by_lpc, + .func_attr.get_umask_func = pca954x_get_umask_by_lpc, + .func_attr.cfg_offset = {0x960, -1}, + .func_attr.umask = {BIT(1), -1}, + }, + }, + }, + }, + { + .dev_type = {0x4065}, /* AS61-48E4T */ + .pca9548_cfg_info = { + /* SOC */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 0, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + /* SFP+ */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 8, + .pca9548_addr = 0x74, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + }, + }, + { + .dev_type = {0x4066}, /* AS61-48X4T */ + .pca9548_cfg_info = { + /* SOC */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 0, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + /* SFP1 */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 7, + .pca9548_addr = 0x71, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + /* SFP2 */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 7, + .pca9548_addr = 0x72, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + /* SFP3 */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 7, + .pca9548_addr = 0x73, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + /* SFP4 */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 8, + .pca9548_addr = 0x71, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + /* SFP5 */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 8, + .pca9548_addr = 0x72, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + /* SFP6 */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 8, + .pca9548_addr = 0x73, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + /* SFP+ */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 8, + .pca9548_addr = 0x74, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + }, + }, + { + .dev_type = {0x4087}, /* AS61-48E4T-LC */ + .pca9548_cfg_info = { + /* SOC */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 0, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + /* SFP+ */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 8, + .pca9548_addr = 0x74, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + }, + }, + { + .dev_type = {0x4088}, /* AS61-48E4T-LD */ + .pca9548_cfg_info = { + /* SOC */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 0, + .pca9548_addr = 0x70, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + /* SFP+ */ + { + .pca9548_reset_type = PCA9548_RESET_GPIO, + .pca9548_bus = 8, + .pca9548_addr = 0x74, + .rst_delay_b = 0, + .rst_delay = 1000, + .rst_delay_a = 1000, + .attr = { + .gpio_attr.gpio = 509, + .gpio_attr.gpio_init = 0, + .gpio_attr.reset_on = 0, + .gpio_attr.reset_off = 1, + }, + }, + }, + }, +}; + +int g_pca954x_debug = 0; +module_param(g_pca954x_debug, int, S_IRUGO | S_IWUSR); + +#define PCA954X_DEBUG(fmt, args...) do { \ + if (g_pca954x_debug) { \ + printk(KERN_ERR "[PCA95x][VER][func:%s line:%d]\r\n"fmt, __func__, __LINE__, ## args); \ + } \ +} while (0) + +/* x86 device get card method */ +static int dfd_get_my_dev_type_by_file(void) +{ + struct file *fp; + mm_segment_t fs; + loff_t pos; + static int card_type; + char buf[DFD_PID_BUF_LEN]; + + if (card_type != 0) { + return card_type; + } + + fp= filp_open(DFD_PUB_CARDTYPE_FILE, O_RDONLY, 0); + if (IS_ERR(fp)) { + PCA954X_DEBUG("open file fail!\r\n"); + return -1; + } + fs = get_fs(); + set_fs(KERNEL_DS); + mem_clear(buf, DFD_PID_BUF_LEN); + pos = 0; + vfs_read(fp, buf, DFD_PRODUCT_ID_LENGTH + 1, &pos); + if (pos < 0) { + PCA954X_DEBUG("read file fail!\r\n"); + goto exit; + } + + card_type = simple_strtoul(buf, NULL, 10); + PCA954X_DEBUG("card_type 0x%x.\n", card_type); + +exit: + set_fs(fs); + filp_close(fp, NULL); + return card_type; +} + +static int drv_get_my_dev_type(void) +{ + static int type = -1; + + if (type > 0) { + return type; + } + type = dfd_get_my_dev_type_by_file(); + PCA954X_DEBUG("ko board type %d\r\n", type); + + return type; +} + +pca9548_card_info_t* pca9548_get_card_info(int dev_type) +{ + int i, j; + int size; + + size = ARRAY_SIZE(g_pca9548_card_info); + + PCA954X_DEBUG("Enter dev_type 0x%x size %d.\n", dev_type, size); + for (i = 0; i < size; i++) { + for(j = 0; j < DFD_MAX_PRODUCT_NUM; j++) { + if (g_pca9548_card_info[i].dev_type[j] == dev_type) { + PCA954X_DEBUG("match dev_type 0x%x.\n", dev_type); + return &g_pca9548_card_info[i]; + } + } + } + + PCA954X_DEBUG("dismatch dev_type 0x%x.\n", dev_type); + return NULL; +} + +pca9548_cfg_info_t* get_pca9548_cfg_info(int bus, int addr) +{ + int dev_type; + pca9548_card_info_t *info; + pca9548_cfg_info_t *pca9548_cfg_info; + int i; + int size; + + dev_type = drv_get_my_dev_type(); + if (dev_type < 0) { + PCA954X_DEBUG("drv_get_my_dev_type failed ret %d.\n", dev_type); + return NULL; + } + + info = pca9548_get_card_info(dev_type); + if (info == NULL) { + PCA954X_DEBUG("fpga_pcie_get_card_info dev_type %d failed.\n", dev_type); + return NULL; + } + + size = PCA9548_MAX_CPLD_NUM; + for (i = 0; i < size; i++) { + pca9548_cfg_info = &(info->pca9548_cfg_info[i]); + if ((pca9548_cfg_info->pca9548_bus == bus) && (pca9548_cfg_info->pca9548_addr == addr)) { + PCA954X_DEBUG("match dev_type 0x%x bus %d addr 0x%x.\n", dev_type, bus, addr); + return pca9548_cfg_info; + } + } + + PCA954X_DEBUG("dismatch dev_type 0x%x bus %d addr 0x%x.\n", dev_type, bus, addr); + return NULL; +} + +static void pca9548_gpio_init(gpio_attr_t *gpio_attr) +{ + if (gpio_attr->gpio_init == 0) { + PCA954X_DEBUG("gpio%d init.\n", gpio_attr->gpio); + gpio_request(gpio_attr->gpio, "pca9548_reset"); + gpio_direction_output(gpio_attr->gpio, gpio_attr->reset_off); + gpio_attr->gpio_init = 1; + } +} + +static void pca9548_gpio_free(gpio_attr_t *gpio_attr) +{ + if (gpio_attr == NULL) { + PCA954X_DEBUG("pca9548_gpio_free,params error\n"); + return ; + } + if (gpio_attr->gpio_init == 1) { + PCA954X_DEBUG("gpio%d release.\n", gpio_attr->gpio); + gpio_free(gpio_attr->gpio); + gpio_attr->gpio_init = 0; + } +} + +static int pca954x_do_gpio_reset(pca9548_cfg_info_t *cfg_info, struct i2c_adapter *adap, + struct i2c_client *client, u32 chan) +{ + struct pca954x *data = i2c_get_clientdata(client); + int ret = -1; + gpio_attr_t *tmp_gpio_attr; + int timeout; + int val; + struct i2c_adapter *adapter; + int adapter_timeout; + + if (cfg_info == NULL) { + PCA954X_DEBUG("pca9548 cfg info is null.\n"); + return ret; + } + + if (cfg_info->pca9548_reset_type == PCA9548_RESET_GPIO) { + tmp_gpio_attr = &(cfg_info->attr.gpio_attr); + timeout = cfg_info->rst_delay_a; + + pca9548_gpio_init(tmp_gpio_attr); + udelay(cfg_info->rst_delay_b); + /* reset on */ + PCA954X_DEBUG("set gpio%d %d.\n", tmp_gpio_attr->gpio, tmp_gpio_attr->reset_on); + __gpio_set_value(tmp_gpio_attr->gpio, tmp_gpio_attr->reset_on); + udelay(cfg_info->rst_delay); + /* reset off */ + PCA954X_DEBUG("set gpio%d %d.\n", tmp_gpio_attr->gpio, tmp_gpio_attr->reset_off); + __gpio_set_value(tmp_gpio_attr->gpio, tmp_gpio_attr->reset_off); + + while (timeout > 0) { + udelay(1); + val = __gpio_get_value(tmp_gpio_attr->gpio); + if (val == tmp_gpio_attr->reset_off) { + adapter = adap; + /* get bus info */ + while(i2c_parent_is_i2c_adapter(adapter)){ + adapter = to_i2c_adapter(adapter->dev.parent); + } + + adapter_timeout = adapter->timeout; + adapter->timeout = msecs_to_jiffies(50); + pca954x_reg_write(adap, client, data->last_chan); + adapter->timeout = adapter_timeout; + ret = 0; + PCA954X_DEBUG("pca954x_do_gpio_reset success.\n"); + break; + } + + if (timeout >= 1000 && (timeout % 1000 == 0)) { + /* 1MS schedule*/ + schedule(); + } + timeout--; + } + + if (ret) { + PCA954X_DEBUG("pca954x_do_gpio_reset failed.\n"); + } + pca9548_gpio_free(&(cfg_info->attr.gpio_attr)); + } else { + PCA954X_DEBUG("pca9548_reset_type invalid, pca954x_do_gpio_reset failed.\n"); + } + + return ret; +} + +static int pca954x_do_func_reset(pca9548_cfg_info_t *cfg_info, struct i2c_adapter *adap, + struct i2c_client *client, u32 chan) +{ + struct pca954x *data = i2c_get_clientdata(client); + int ret = -1; + func_attr_t *tmp_func_attr; + int timeout; + int val; + struct i2c_adapter *adapter; + int adapter_timeout; + int i; + u8 old_value; + + if (cfg_info == NULL) { + PCA954X_DEBUG("pca9548 cfg info is null.\n"); + return ret; + } + + if (cfg_info->pca9548_reset_type == PCA9548_RESET_FUNC) { + tmp_func_attr = &(cfg_info->attr.func_attr); + timeout = cfg_info->rst_delay_a; + + if ((tmp_func_attr->reset_func == NULL) || (tmp_func_attr->get_umask_func == NULL)) { + PCA954X_DEBUG("pca954x hw do reset func or get umask func is null.\n"); + return ret; + } + + for(i = 0; (i < PCA9548_MAX_CPLD_LAYER) && (tmp_func_attr->cfg_offset[i] != -1) + && (tmp_func_attr->umask[i] != -1); i++) { + old_value = (*tmp_func_attr->get_umask_func)(tmp_func_attr->cfg_offset[i]); + PCA954X_DEBUG("cfg info: offset:0x%x umask:0x%x, old_value:0x%x\n", + tmp_func_attr->cfg_offset[i], tmp_func_attr->umask[i],old_value); + (*tmp_func_attr->reset_func)(tmp_func_attr->cfg_offset[i], old_value & ~tmp_func_attr->umask[i]); + udelay(cfg_info->rst_delay); + (*tmp_func_attr->reset_func)(tmp_func_attr->cfg_offset[i], old_value | tmp_func_attr->umask[i]); + } + + while (timeout > 0) { + udelay(1); + val = (*tmp_func_attr->get_umask_func)(tmp_func_attr->cfg_offset[i - 1]); + val &= (tmp_func_attr->umask[i - 1]); + if (val == tmp_func_attr->umask[i - 1]) { + adapter = adap; + /* get bus info */ + while(i2c_parent_is_i2c_adapter(adapter)){ + adapter = to_i2c_adapter(adapter->dev.parent); + } + + adapter_timeout = adapter->timeout; + adapter->timeout = msecs_to_jiffies(50); + pca954x_reg_write(adap, client, data->last_chan); + adapter->timeout = adapter_timeout; + ret = 0; + PCA954X_DEBUG("pca954x_do_func_reset success.\n"); + break; + } + + if (timeout >= 1000 && (timeout % 1000 == 0)) { + /* 1MS schedule*/ + schedule(); + } + timeout--; + } + + if (ret) { + PCA954X_DEBUG("pca954x_do_func_reset failed.\n"); + } + } else { + PCA954X_DEBUG("pca9548_reset_type invalid, pca954x_do_func_reset failed.\n"); + } + + return ret; +} + +static int pca9548_reset_ctrl(pca9548_cfg_info_t *cfg_info, struct i2c_adapter *adap, + struct i2c_client *client, u32 chan) +{ + int ret = -1; + + if (cfg_info == NULL) { + PCA954X_DEBUG("pca9548 cfg info is null.\n"); + return ret; + } + + if (cfg_info->pca9548_reset_type == PCA9548_RESET_FUNC) { + ret = pca954x_do_func_reset(cfg_info, adap, client, chan); + } else if (cfg_info->pca9548_reset_type == PCA9548_RESET_GPIO) { + ret = pca954x_do_gpio_reset(cfg_info, adap, client, chan); + } + + if (ret < 0) { + PCA954X_DEBUG("pca9548_reset_ctrl failed.\n"); + } + return ret; +} + +static int pca954x_reset_i2c_read(uint32_t bus, uint32_t addr, uint32_t offset_addr, + unsigned char *buf, uint32_t size) +{ + struct file *fp; + mm_segment_t fs; + struct i2c_client client; + char i2c_path[32]; + int i ,j ; + int rv; + + rv = 0; + mem_clear(i2c_path, sizeof(i2c_path)); + snprintf(i2c_path, sizeof(i2c_path), "/dev/i2c-%d", bus); + fp = filp_open(i2c_path, O_RDWR, S_IRUSR | S_IWUSR); + if (IS_ERR(fp)) { + PCA954X_DEBUG("i2c open fail.\n"); + return -1; + } + memcpy(&client, fp->private_data, sizeof(struct i2c_client)); + client.addr = addr; + fs = get_fs(); + set_fs(KERNEL_DS); + for (j = 0 ;j < size ;j++){ + for (i = 0; i < I2C_RETRY_TIMES; i++) { + rv = i2c_smbus_read_byte_data(&client, (offset_addr + j)); + if (rv < 0) { + PCA954X_DEBUG("i2c read failed, try again.\n"); + msleep(I2C_RETRY_WAIT_TIMES); + if (i >= (I2C_RETRY_TIMES - 1)) { + goto out; + } + continue; + } + *(buf + j) = (unsigned char)rv; + break; + } + } +out: + filp_close(fp, NULL); + set_fs(fs); + return rv; +} + +static int pca954x_reset_i2c_write(uint32_t bus, uint32_t dev_addr, uint32_t offset_addr, + uint8_t write_buf) +{ + struct file *fp; + mm_segment_t fs; + struct i2c_client client; + char i2c_path[32]; + int i; + int rv; + + rv = 0; + mem_clear(i2c_path, sizeof(i2c_path)); + snprintf(i2c_path, sizeof(i2c_path), "/dev/i2c-%d", bus); + fp = filp_open(i2c_path, O_RDWR, S_IRUSR | S_IWUSR); + if (IS_ERR(fp)) { + PCA954X_DEBUG("i2c open fail.\n"); + return -1; + } + memcpy(&client, fp->private_data, sizeof(struct i2c_client)); + client.addr = dev_addr; + fs = get_fs(); + set_fs(KERNEL_DS); + for (i = 0; i < I2C_RETRY_TIMES; i++) { + rv = i2c_smbus_write_byte_data(&client, offset_addr, write_buf); + if (rv < 0) { + PCA954X_DEBUG("i2c write failed, try again.\n"); + msleep(I2C_RETRY_WAIT_TIMES); + if (i >= (I2C_RETRY_TIMES - 1)) { + goto out; + } + continue; + } + break; + } +out: + filp_close(fp, NULL); + set_fs(fs); + return rv; +} + +int pca954x_reset_reg_i2c_read_byte(int addr, u8 *value) +{ + int bus; + int client_addr; + int offset; + int ret; + + bus = PCA9548_I2C_GET_BUS(addr); + client_addr = PCA9548_I2C_GET_CLIENT(addr); + offset = PCA9548_I2C_GET_OFFSET(addr); + + ret = pca954x_reset_i2c_read(bus, client_addr, offset, value, 1); + if (ret < 0) { + PCA954X_DEBUG(" 0x%x read fail\r\n", addr); + goto end; + } +end: + return ret; +} + +int pca954x_reset_reg_i2c_write_byte(int addr, u8 value) +{ + int bus; + int client_addr; + int offset; + int ret; + + bus = PCA9548_I2C_GET_BUS(addr); + client_addr = PCA9548_I2C_GET_CLIENT(addr); + offset = PCA9548_I2C_GET_OFFSET(addr); + + ret = pca954x_reset_i2c_write(bus, client_addr, offset, value); + if (ret < 0) { + PCA954X_DEBUG(" 0x%x write fail\r\n", addr); + goto end; + } +end: + return ret; +} + +void pca954x_hw_do_reset_by_i2c(int addr, u8 value) +{ + int ret; + + PCA954X_DEBUG("write i2c cpld[0x%x], value[%d]\n", addr, value); + ret = pca954x_reset_reg_i2c_write_byte(addr, value); + if (ret < 0) { + PCA954X_DEBUG("write cpld pca9548 reset reg failed, ret = %d \n", ret); + } +} + +u8 pca954x_get_umask_by_i2c(int addr) +{ + u8 value = 0xFF; + int ret; + + ret = pca954x_reset_reg_i2c_read_byte(addr, &value); + PCA954X_DEBUG("read i2c cpld[0x%x], value[%d], ret = %d\n", addr, value, ret); + + return value; +} + +void pca954x_hw_do_reset_by_lpc(int io_port, u8 value) +{ + PCA954X_DEBUG("write lpc offset[0x%x], value[%d]\n", (u16)io_port, value); + outb(value, (u16)io_port); +} + +u8 pca954x_get_umask_by_lpc(int io_port) +{ + u8 value; + + value = inb(io_port); + PCA954X_DEBUG("read lpc offset[0x%x], value[%d]\n", (u16)io_port, value); + + return value; +} + +int pca954x_hw_do_reset_new(struct i2c_adapter *adap, + struct i2c_client *client, u32 chan) +{ + pca9548_cfg_info_t *cfg_info; + int ret = -1; + + cfg_info = get_pca9548_cfg_info(adap->nr, client->addr); + if (cfg_info == NULL && g_notify_to_do_reset == NULL) { + PCA954X_DEBUG("fpga_do_pca954x_reset_func do nothing.\n"); + return ret; + } + if (cfg_info != NULL) { + ret = pca9548_reset_ctrl(cfg_info, adap, client, chan); + } else { + ret = pca954x_hw_do_reset(adap->nr, client->addr); + } + + if (ret < 0) { + PCA954X_DEBUG("pca954x_hw_do_reset failed.\n"); + } + return ret; +} +/******************************end 9548 reset***********************************/ + +static int pca954x_do_reset(struct i2c_adapter *adap, + void *client, u32 chan) +{ + struct i2c_client *new_client; + int ret = -1; + + PCA954X_DEBUG("do pca954x reset x86\n"); + new_client =(struct i2c_client *) client; + ret = pca954x_hw_do_reset_new(adap, new_client, chan); + if (ret < 0) { + PCA954X_DEBUG("pca954x_do_reset failed.\n"); + return ret; + } + + PCA954X_DEBUG("pca954x_do_reset success.\n"); + ret = 0; + return ret; +} + +static int pca954x_deselect_mux(struct i2c_adapter *adap, + void *client, u32 chan) +{ + struct pca954x *data = i2c_get_clientdata(client); + int ret, rv; + struct i2c_client * new_client; + + /* Deselect active channel */ + data->last_chan = 0; + + ret = pca954x_reg_write(adap, client, data->last_chan); + + if (ret < 0) { + new_client =(struct i2c_client *) client; + dev_warn(&new_client->dev, "pca954x close chn failed, do reset.\n"); + rv = pca954x_do_reset(adap, client, chan); + if (rv == 0) { + ret = 0; + } + + } + + pca954x_setmuxflag(adap, 1); + (void)pca954x_reg_write(adap, client, data->last_chan); + + return ret; +} + +/* + * I2C init/probing/exit functions + */ +static int pca954x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); + struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev); + struct gpio_desc *gpio; + int num, force, class; + struct pca954x *data; + int ret; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE)) + return -ENODEV; + + data = devm_kzalloc(&client->dev, sizeof(struct pca954x), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + + /* Get the mux out of reset if a reset GPIO is specified. */ + gpio = devm_gpiod_get(&client->dev, "reset"); + if (!IS_ERR(gpio)) + gpiod_direction_output(gpio, 0); + + /* Write the mux register at addr to verify + * that the mux is in fact present. This also + * initializes the mux to disconnected state. + */ + if ((i2c_smbus_write_byte(client, 0) < 0) && (force_create_bus == 0)) { + dev_warn(&client->dev, "probe failed\n"); + return -ENODEV; + } + + data->type = id->driver_data; + data->last_chan = 0; /* force the first selection */ + + /* Now create an adapter for each channel */ + for (num = 0; num < chips[data->type].nchans; num++) { + force = 0; /* dynamic adap number */ + class = 0; /* no class by default */ + if (pdata) { + if (num < pdata->num_modes) { + /* force static number */ + force = pdata->modes[num].adap_id; + class = pdata->modes[num].class; + } else + /* discard unconfigured channels */ + break; + } + + data->virt_adaps[num] = + i2c_add_mux_adapter(adap, &client->dev, client, + force, num, class, pca954x_select_chan, pca954x_deselect_mux); + + if (data->virt_adaps[num] == NULL) { + ret = -ENODEV; + dev_err(&client->dev, + "failed to register multiplexed adapter" + " %d as bus %d\n", num, force); + goto virt_reg_failed; + } + } + + dev_info(&client->dev, + "registered %d multiplexed busses for I2C %s %s\n", + num, chips[data->type].muxtype == pca954x_ismux + ? "mux" : "switch", client->name); + + return 0; + +virt_reg_failed: + for (num--; num >= 0; num--) + i2c_del_mux_adapter(data->virt_adaps[num]); + return ret; +} + +static int pca954x_remove(struct i2c_client *client) +{ + struct pca954x *data = i2c_get_clientdata(client); + const struct chip_desc *chip = &chips[data->type]; + int i; + + for (i = 0; i < chip->nchans; ++i) + if (data->virt_adaps[i]) { + i2c_del_mux_adapter(data->virt_adaps[i]); + data->virt_adaps[i] = NULL; + } + + return 0; +} + +static struct i2c_driver pca954x_driver = { + .driver = { + .name = "pca954x", + .owner = THIS_MODULE, + }, + .probe = pca954x_probe, + .remove = pca954x_remove, + .id_table = pca954x_id, +}; + +module_i2c_driver(pca954x_driver); +#endif + +MODULE_AUTHOR("Rodolfo Giometti "); +MODULE_DESCRIPTION("PCA954x I2C mux/switch driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/modules/i2c-mux-pca9641.c b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/i2c-mux-pca9641.c new file mode 100644 index 000000000000..b3f70bf1405b --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/i2c-mux-pca9641.c @@ -0,0 +1,643 @@ +/* + * I2C multiplexer driver for PCA9541 bus master selector + * + * Copyright (c) 2010 Ericsson AB. + * + * Author: Guenter Roeck + * + * Derived from: + * pca954x.c + * + * Copyright (c) 2008-2009 Rodolfo Giometti + * Copyright (c) 2008-2009 Eurotech S.p.A. + * + * 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 + +/* + * The PCA9541 is a bus master selector. It supports two I2C masters connected + * to a single slave bus. + * + * Before each bus transaction, a master has to acquire bus ownership. After the + * transaction is complete, bus ownership has to be released. This fits well + * into the I2C multiplexer framework, which provides select and release + * functions for this purpose. For this reason, this driver is modeled as + * single-channel I2C bus multiplexer. + * + * This driver assumes that the two bus masters are controlled by two different + * hosts. If a single host controls both masters, platform code has to ensure + * that only one of the masters is instantiated at any given time. + */ + +#define PCA9541_CONTROL 0x01 +#define PCA9541_ISTAT 0x02 + +#define PCA9541_CTL_MYBUS (1 << 0) +#define PCA9541_CTL_NMYBUS (1 << 1) +#define PCA9541_CTL_BUSON (1 << 2) +#define PCA9541_CTL_NBUSON (1 << 3) +#define PCA9541_CTL_BUSINIT (1 << 4) +#define PCA9541_CTL_TESTON (1 << 6) +#define PCA9541_CTL_NTESTON (1 << 7) +#define PCA9541_ISTAT_INTIN (1 << 0) +#define PCA9541_ISTAT_BUSINIT (1 << 1) +#define PCA9541_ISTAT_BUSOK (1 << 2) +#define PCA9541_ISTAT_BUSLOST (1 << 3) +#define PCA9541_ISTAT_MYTEST (1 << 6) +#define PCA9541_ISTAT_NMYTEST (1 << 7) +#define PCA9641_ID 0x00 +#define PCA9641_ID_MAGIC 0x38 +#define PCA9641_CONTROL 0x01 +#define PCA9641_STATUS 0x02 +#define PCA9641_TIME 0x03 +#define PCA9641_CTL_LOCK_REQ BIT(0) +#define PCA9641_CTL_LOCK_GRANT BIT(1) +#define PCA9641_CTL_BUS_CONNECT BIT(2) +#define PCA9641_CTL_BUS_INIT BIT(3) +#define PCA9641_CTL_SMBUS_SWRST BIT(4) +#define PCA9641_CTL_IDLE_TIMER_DIS BIT(5) +#define PCA9641_CTL_SMBUS_DIS BIT(6) +#define PCA9641_CTL_PRIORITY BIT(7) +#define PCA9641_STS_OTHER_LOCK BIT(0) +#define PCA9641_STS_BUS_INIT_FAIL BIT(1) +#define PCA9641_STS_BUS_HUNG BIT(2) +#define PCA9641_STS_MBOX_EMPTY BIT(3) +#define PCA9641_STS_MBOX_FULL BIT(4) +#define PCA9641_STS_TEST_INT BIT(5) +#define PCA9641_STS_SCL_IO BIT(6) +#define PCA9641_STS_SDA_IO BIT(7) +#define PCA9641_RES_TIME 0x03 +#define BUSON (PCA9541_CTL_BUSON | PCA9541_CTL_NBUSON) +#define MYBUS (PCA9541_CTL_MYBUS | PCA9541_CTL_NMYBUS) +#define mybus(x) (!((x) & MYBUS) || ((x) & MYBUS) == MYBUS) +#define busoff(x) (!((x) & BUSON) || ((x) & BUSON) == BUSON) +#define BUSOFF(x, y) (!((x) & PCA9641_CTL_LOCK_GRANT) && \ + !((y) & PCA9641_STS_OTHER_LOCK)) +#define other_lock(x) ((x) & PCA9641_STS_OTHER_LOCK) +#define lock_grant(x) ((x) & PCA9641_CTL_LOCK_GRANT) + +#define PCA9641_RETRY_TIME 8 + +typedef struct i2c_muxs_struct_flag +{ + int nr; + char name[48]; + struct mutex update_lock; + int flag; +}i2c_mux_flag; + +i2c_mux_flag pca_flag = { + .flag = -1, +}; + +int pca9641_setmuxflag(int nr, int flag) +{ + if (pca_flag.nr == nr) { + pca_flag.flag = flag; + } + return 0; +} +EXPORT_SYMBOL(pca9641_setmuxflag); + +int g_debug = 0; +module_param(g_debug, int, S_IRUGO | S_IWUSR); + +#define PCA_DEBUG(fmt, args...) do { \ + if (g_debug) { \ + printk(KERN_ERR "[pca9641][VER][func:%s line:%d]\r\n"fmt, __func__, __LINE__, ## args); \ + } \ +} while (0) + +/* arbitration timeouts, in jiffies */ +#define ARB_TIMEOUT (HZ / 8) /* 125 ms until forcing bus ownership */ +#define ARB2_TIMEOUT (HZ / 4) /* 250 ms until acquisition failure */ + +/* arbitration retry delays, in us */ +#define SELECT_DELAY_SHORT 50 +#define SELECT_DELAY_LONG 1000 + +struct pca9541 { + struct i2c_client *client; + unsigned long select_timeout; + unsigned long arb_timeout; +}; + +static const struct i2c_device_id pca9541_id[] = { + {"pca9541", 0}, + {"pca9641", 1}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, pca9541_id); + +#ifdef CONFIG_OF +static const struct of_device_id pca9541_of_match[] = { + { .compatible = "nxp,pca9541" }, + { .compatible = "nxp,pca9641" }, + {} +}; +MODULE_DEVICE_TABLE(of, pca9541_of_match); +#endif + +/* + * Write to chip register. Don't use i2c_transfer()/i2c_smbus_xfer() + * as they will try to lock the adapter a second time. + */ +static int pca9541_reg_write(struct i2c_client *client, u8 command, u8 val) +{ + struct i2c_adapter *adap = client->adapter; + int ret; + + if (adap->algo->master_xfer) { + struct i2c_msg msg; + char buf[2]; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 2; + buf[0] = command; + buf[1] = val; + msg.buf = buf; + ret = __i2c_transfer(adap, &msg, 1); + } else { + union i2c_smbus_data data; + + data.byte = val; + ret = adap->algo->smbus_xfer(adap, client->addr, + client->flags, + I2C_SMBUS_WRITE, + command, + I2C_SMBUS_BYTE_DATA, &data); + } + + return ret; +} + +/* + * Read from chip register. Don't use i2c_transfer()/i2c_smbus_xfer() + * as they will try to lock adapter a second time. + */ +static int pca9541_reg_read(struct i2c_client *client, u8 command) +{ + struct i2c_adapter *adap = client->adapter; + int ret; + u8 val; + + if (adap->algo->master_xfer) { + struct i2c_msg msg[2] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &command + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = 1, + .buf = &val + } + }; + ret = __i2c_transfer(adap, msg, 2); + if (ret == 2) + ret = val; + else if (ret >= 0) + ret = -EIO; + } else { + union i2c_smbus_data data; + + ret = adap->algo->smbus_xfer(adap, client->addr, + client->flags, + I2C_SMBUS_READ, + command, + I2C_SMBUS_BYTE_DATA, &data); + if (!ret) + ret = data.byte; + } + return ret; +} + +/* + * Arbitration management functions + */ + +/* Release bus. Also reset NTESTON and BUSINIT if it was set. */ +static void pca9541_release_bus(struct i2c_client *client) +{ + int reg; + + reg = pca9541_reg_read(client, PCA9541_CONTROL); + if (reg >= 0 && !busoff(reg) && mybus(reg)) + pca9541_reg_write(client, PCA9541_CONTROL, + (reg & PCA9541_CTL_NBUSON) >> 1); +} + +/* + * Arbitration is defined as a two-step process. A bus master can only activate + * the slave bus if it owns it; otherwise it has to request ownership first. + * This multi-step process ensures that access contention is resolved + * gracefully. + * + * Bus Ownership Other master Action + * state requested access + * ---------------------------------------------------- + * off - yes wait for arbitration timeout or + * for other master to drop request + * off no no take ownership + * off yes no turn on bus + * on yes - done + * on no - wait for arbitration timeout or + * for other master to release bus + * + * The main contention point occurs if the slave bus is off and both masters + * request ownership at the same time. In this case, one master will turn on + * the slave bus, believing that it owns it. The other master will request + * bus ownership. Result is that the bus is turned on, and master which did + * _not_ own the slave bus before ends up owning it. + */ + +/* Control commands per PCA9541 datasheet */ +static const u8 pca9541_control[16] = { + 4, 0, 1, 5, 4, 4, 5, 5, 0, 0, 1, 1, 0, 4, 5, 1 +}; + +/* + * Channel arbitration + * + * Return values: + * <0: error + * 0 : bus not acquired + * 1 : bus acquired + */ +static int pca9541_arbitrate(struct i2c_client *client) +{ + struct i2c_mux_core *muxc = i2c_get_clientdata(client); + struct pca9541 *data = i2c_mux_priv(muxc); + int reg; + + reg = pca9541_reg_read(client, PCA9541_CONTROL); + if (reg < 0) + return reg; + + if (busoff(reg)) { + int istat; + /* + * Bus is off. Request ownership or turn it on unless + * other master requested ownership. + */ + istat = pca9541_reg_read(client, PCA9541_ISTAT); + if (!(istat & PCA9541_ISTAT_NMYTEST) + || time_is_before_eq_jiffies(data->arb_timeout)) { + /* + * Other master did not request ownership, + * or arbitration timeout expired. Take the bus. + */ + pca9541_reg_write(client, + PCA9541_CONTROL, + pca9541_control[reg & 0x0f] + | PCA9541_CTL_NTESTON); + data->select_timeout = SELECT_DELAY_SHORT; + } else { + /* + * Other master requested ownership. + * Set extra long timeout to give it time to acquire it. + */ + data->select_timeout = SELECT_DELAY_LONG * 2; + } + } else if (mybus(reg)) { + /* + * Bus is on, and we own it. We are done with acquisition. + * Reset NTESTON and BUSINIT, then return success. + */ + if (reg & (PCA9541_CTL_NTESTON | PCA9541_CTL_BUSINIT)) + pca9541_reg_write(client, + PCA9541_CONTROL, + reg & ~(PCA9541_CTL_NTESTON + | PCA9541_CTL_BUSINIT)); + return 1; + } else { + /* + * Other master owns the bus. + * If arbitration timeout has expired, force ownership. + * Otherwise request it. + */ + data->select_timeout = SELECT_DELAY_LONG; + if (time_is_before_eq_jiffies(data->arb_timeout)) { + /* Time is up, take the bus and reset it. */ + pca9541_reg_write(client, + PCA9541_CONTROL, + pca9541_control[reg & 0x0f] + | PCA9541_CTL_BUSINIT + | PCA9541_CTL_NTESTON); + } else { + /* Request bus ownership if needed */ + if (!(reg & PCA9541_CTL_NTESTON)) + pca9541_reg_write(client, + PCA9541_CONTROL, + reg | PCA9541_CTL_NTESTON); + } + } + return 0; +} + +static int pca9541_select_chan(struct i2c_mux_core *muxc, u32 chan) +{ + struct pca9541 *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + int ret; + unsigned long timeout = jiffies + ARB2_TIMEOUT; + /* give up after this time */ + + data->arb_timeout = jiffies + ARB_TIMEOUT; + /* force bus ownership after this time */ + + do { + ret = pca9541_arbitrate(client); + if (ret) + return ret < 0 ? ret : 0; + + if (data->select_timeout == SELECT_DELAY_SHORT) + udelay(data->select_timeout); + else + msleep(data->select_timeout / 1000); + } while (time_is_after_eq_jiffies(timeout)); + + return -ETIMEDOUT; +} + +static int pca9541_release_chan(struct i2c_mux_core *muxc, u32 chan) +{ + struct pca9541 *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + pca9541_release_bus(client); + return 0; +} + +/* +* Arbitration management functions +*/ +static void pca9641_release_bus(struct i2c_client *client) +{ + pca9541_reg_write(client, PCA9641_CONTROL, 0x80); /* master 0x80 */ +} + +/* +* Channel arbitration +* +* Return values: +* <0: error +* 0 : bus not acquired +* 1 : bus acquired +*/ +static int pca9641_arbitrate(struct i2c_client *client) +{ + struct i2c_mux_core *muxc = i2c_get_clientdata(client); + struct pca9541 *data = i2c_mux_priv(muxc); + int reg_ctl, reg_sts; + + reg_ctl = pca9541_reg_read(client, PCA9641_CONTROL); + if (reg_ctl < 0) + return reg_ctl; + reg_sts = pca9541_reg_read(client, PCA9641_STATUS); + + if (BUSOFF(reg_ctl, reg_sts)) { + /* + * Bus is off. Request ownership or turn it on unless + * other master requested ownership. + */ + reg_ctl |= PCA9641_CTL_LOCK_REQ; + pca9541_reg_write(client, PCA9641_CONTROL, reg_ctl); + reg_ctl = pca9541_reg_read(client, PCA9641_CONTROL); + + if (lock_grant(reg_ctl)) { + /* + * Other master did not request ownership, + * or arbitration timeout expired. Take the bus. + */ + reg_ctl |= PCA9641_CTL_BUS_CONNECT + | PCA9641_CTL_LOCK_REQ; + pca9541_reg_write(client, PCA9641_CONTROL, reg_ctl); + data->select_timeout = SELECT_DELAY_SHORT; + + return 1; + } else { + /* + * Other master requested ownership. + * Set extra long timeout to give it time to acquire it. + */ + data->select_timeout = SELECT_DELAY_LONG * 2; + } + } else if (lock_grant(reg_ctl)) { + /* + * Bus is on, and we own it. We are done with acquisition. + */ + reg_ctl |= PCA9641_CTL_BUS_CONNECT | PCA9641_CTL_LOCK_REQ; + pca9541_reg_write(client, PCA9641_CONTROL, reg_ctl); + + return 1; + } else if (other_lock(reg_sts)) { + /* + * Other master owns the bus. + * If arbitration timeout has expired, force ownership. + * Otherwise request it. + */ + data->select_timeout = SELECT_DELAY_LONG; + reg_ctl |= PCA9641_CTL_LOCK_REQ; + pca9541_reg_write(client, PCA9641_CONTROL, reg_ctl); + } + return 0; +} + +int pca9641_select_chan(struct i2c_mux_core *muxc, u32 chan) +{ + struct pca9541 *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + int ret; + int result; + unsigned long timeout = jiffies + ARB2_TIMEOUT; + /* give up after this time */ + data->arb_timeout = jiffies + ARB_TIMEOUT; + /* force bus ownership after this time */ + for (result = 0 ; result < PCA9641_RETRY_TIME ; result ++) { + do { + ret = pca9641_arbitrate(client); + if (ret == 1) { + return 0; + } + if (data->select_timeout == SELECT_DELAY_SHORT) + udelay(data->select_timeout); + else + msleep(data->select_timeout / 1000); + } while (time_is_after_eq_jiffies(timeout)); + timeout = jiffies + ARB2_TIMEOUT; + } + return -ETIMEDOUT; +} +EXPORT_SYMBOL(pca9641_select_chan); + +static int pca9641_release_chan(struct i2c_mux_core *muxc, u32 chan) +{ + struct pca9541 *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + if (pca_flag.flag) { + pca9641_release_bus(client); + } + return 0; +} + +static int pca9641_detect_id(struct i2c_client *client) +{ + int reg; + + reg = pca9541_reg_read(client, PCA9641_ID); + if (reg == PCA9641_ID_MAGIC) + return 1; + else + return 0; +} + +/** + ** Limited: 20180827 supports one PCA9641 + **/ +static int pca9641_recordflag(struct i2c_adapter *adap) { + if (pca_flag.flag != -1) { + pr_err(" %s %d has init already!!!", __func__, __LINE__); + return -1 ; + } + pca_flag.nr = adap->nr; + PCA_DEBUG(" adap->nr:%d\n", adap->nr); + snprintf(pca_flag.name, sizeof(pca_flag.name),adap->name); + return 0; +} + +static void i2c_lock_adapter(struct i2c_adapter *adapter){ + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); + if (parent) + i2c_lock_adapter(parent); + else + rt_mutex_lock(&adapter->bus_lock); +} + +void i2c_unlock_adapter(struct i2c_adapter *adapter) +{ + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); + + if (parent) + i2c_unlock_adapter(parent); + else + rt_mutex_unlock(&adapter->bus_lock); +} +/* + * I2C init/probing/exit functions + */ +static int pca9541_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adap = client->adapter; + struct i2c_mux_core *muxc; + struct pca9541 *data; + int force; + int ret = -ENODEV; + int detect_id; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + detect_id = pca9641_detect_id(client); + + /* + * I2C accesses are unprotected here. + * We have to lock the adapter before releasing the bus. + */ + #if 0 + i2c_lock_adapter(adap); + pca9541_release_bus(client); + i2c_unlock_adapter(adap); + #endif + if (detect_id == 0) { + i2c_lock_adapter(adap); + pca9541_release_bus(client); + i2c_unlock_adapter(adap); + } else { + i2c_lock_adapter(adap); + pca9641_release_bus(client); + i2c_unlock_adapter(adap); + } + + /* Create mux adapter */ + + if (detect_id == 0) { + muxc = i2c_mux_alloc(adap, &client->dev, 1, sizeof(*data), + I2C_MUX_ARBITRATOR, + pca9541_select_chan, pca9541_release_chan); + if (!muxc) + return -ENOMEM; + + data = i2c_mux_priv(muxc); + data->client = client; + + i2c_set_clientdata(client, muxc); + ret = i2c_mux_add_adapter(muxc, 0, 0, 0); + + if (ret) + return ret; + } else { + muxc = i2c_mux_alloc(adap, &client->dev, 1, sizeof(*data), + I2C_MUX_ARBITRATOR, + pca9641_select_chan, pca9641_release_chan); + if (!muxc) + return -ENOMEM; + + data = i2c_mux_priv(muxc); + data->client = client; + + i2c_set_clientdata(client, muxc); + + ret = i2c_mux_add_adapter(muxc, force, 0, 0); + if (ret) + return ret; + } + pca9641_recordflag(muxc->adapter[0]); + + dev_info(&client->dev, "registered master selector for I2C %s\n", + client->name); + + return 0; + +} + +static int pca9541_remove(struct i2c_client *client) +{ + struct i2c_mux_core *muxc = i2c_get_clientdata(client); + + i2c_mux_del_adapters(muxc); + return 0; +} + +static struct i2c_driver pca9641_driver = { + .driver = { + .name = "pca9641", + .of_match_table = of_match_ptr(pca9541_of_match), + }, + .probe = pca9541_probe, + .remove = pca9541_remove, + .id_table = pca9541_id, +}; + +module_i2c_driver(pca9641_driver); + +MODULE_AUTHOR("Guenter Roeck "); +MODULE_DESCRIPTION("PCA9541 I2C master selector driver"); +MODULE_LICENSE("GPL v2"); diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/modules/optoe.c b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/optoe.c new file mode 100644 index 000000000000..22c5d335e11a --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/optoe.c @@ -0,0 +1,1195 @@ +/* + * optoe.c - A driver to read and write the EEPROM on optical transceivers + * (SFP, QSFP and similar I2C based devices) + * + * Copyright (C) 2014 Cumulus networks Inc. + * Copyright (C) 2017 Finisar Corp. + * + * 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 Freeoftware Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +/* + * Description: + * a) Optical transceiver EEPROM read/write transactions are just like + * the at24 eeproms managed by the at24.c i2c driver + * b) The register/memory layout is up to 256 128 byte pages defined by + * a "pages valid" register and switched via a "page select" + * register as explained in below diagram. + * c) 256 bytes are mapped at a time. 'Lower page 00h' is the first 128 + * bytes of address space, and always references the same + * location, independent of the page select register. + * All mapped pages are mapped into the upper 128 bytes + * (offset 128-255) of the i2c address. + * d) Devices with one I2C address (eg QSFP) use I2C address 0x50 + * (A0h in the spec), and map all pages in the upper 128 bytes + * of that address. + * e) Devices with two I2C addresses (eg SFP) have 256 bytes of data + * at I2C address 0x50, and 256 bytes of data at I2C address + * 0x51 (A2h in the spec). Page selection and paged access + * only apply to this second I2C address (0x51). + * e) The address space is presented, by the driver, as a linear + * address space. For devices with one I2C client at address + * 0x50 (eg QSFP), offset 0-127 are in the lower + * half of address 50/A0h/client[0]. Offset 128-255 are in + * page 0, 256-383 are page 1, etc. More generally, offset + * 'n' resides in page (n/128)-1. ('page -1' is the lower + * half, offset 0-127). + * f) For devices with two I2C clients at address 0x50 and 0x51 (eg SFP), + * the address space places offset 0-127 in the lower + * half of 50/A0/client[0], offset 128-255 in the upper + * half. Offset 256-383 is in the lower half of 51/A2/client[1]. + * Offset 384-511 is in page 0, in the upper half of 51/A2/... + * Offset 512-639 is in page 1, in the upper half of 51/A2/... + * Offset 'n' is in page (n/128)-3 (for n > 383) + * + * One I2c addressed (eg QSFP) Memory Map + * + * 2-Wire Serial Address: 1010000x + * + * Lower Page 00h (128 bytes) + * ===================== + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * |Page Select Byte(127)| + * ===================== + * | + * | + * | + * | + * V + * ------------------------------------------------------------ + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * V V V V + * ------------ -------------- --------------- -------------- + * | | | | | | | | + * | Upper | | Upper | | Upper | | Upper | + * | Page 00h | | Page 01h | | Page 02h | | Page 03h | + * | | | (Optional) | | (Optional) | | (Optional | + * | | | | | | | for Cable | + * | | | | | | | Assemblies) | + * | ID | | AST | | User | | | + * | Fields | | Table | | EEPROM Data | | | + * | | | | | | | | + * | | | | | | | | + * | | | | | | | | + * ------------ -------------- --------------- -------------- + * + * The SFF 8436 (QSFP) spec only defines the 4 pages described above. + * In anticipation of future applications and devices, this driver + * supports access to the full architected range, 256 pages. + * + * The CMIS (Common Management Interface Specification) defines use of + * considerably more pages (at least to page 0xAF), which this driver + * supports. + * + * NOTE: This version of the driver ONLY SUPPORTS BANK 0 PAGES on CMIS + * devices. + * + **/ + +/* #define DEBUG 1 */ + +#undef EEPROM_CLASS +#ifdef CONFIG_EEPROM_CLASS +#define EEPROM_CLASS +#endif +#ifdef CONFIG_EEPROM_CLASS_MODULE +#define EEPROM_CLASS +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef EEPROM_CLASS +#include +#endif + +#include + +/* The maximum length of a port name */ +#define MAX_PORT_NAME_LEN 20 + +struct optoe_platform_data { + u32 byte_len; /* size (sum of all addr) */ + u16 page_size; /* for writes */ + u8 flags; + void *dummy1; /* backward compatibility */ + void *dummy2; /* backward compatibility */ + +#ifdef EEPROM_CLASS + struct eeprom_platform_data *eeprom_data; +#endif + char port_name[MAX_PORT_NAME_LEN]; +}; + +/* fundamental unit of addressing for EEPROM */ +#define OPTOE_PAGE_SIZE 128 +/* + * Single address devices (eg QSFP) have 256 pages, plus the unpaged + * low 128 bytes. If the device does not support paging, it is + * only 2 'pages' long. + */ +#define OPTOE_ARCH_PAGES 256 +#define ONE_ADDR_EEPROM_SIZE ((1 + OPTOE_ARCH_PAGES) * OPTOE_PAGE_SIZE) +#define ONE_ADDR_EEPROM_UNPAGED_SIZE (2 * OPTOE_PAGE_SIZE) +/* + * Dual address devices (eg SFP) have 256 pages, plus the unpaged + * low 128 bytes, plus 256 bytes at 0x50. If the device does not + * support paging, it is 4 'pages' long. + */ +#define TWO_ADDR_EEPROM_SIZE ((3 + OPTOE_ARCH_PAGES) * OPTOE_PAGE_SIZE) +#define TWO_ADDR_EEPROM_UNPAGED_SIZE (4 * OPTOE_PAGE_SIZE) +#define TWO_ADDR_NO_0X51_SIZE (2 * OPTOE_PAGE_SIZE) + +/* a few constants to find our way around the EEPROM */ +#define OPTOE_PAGE_SELECT_REG 0x7F +#define ONE_ADDR_PAGEABLE_REG 0x02 +#define QSFP_NOT_PAGEABLE (1<<2) +#define CMIS_NOT_PAGEABLE (1<<7) +#define TWO_ADDR_PAGEABLE_REG 0x40 +#define TWO_ADDR_PAGEABLE (1<<4) +#define TWO_ADDR_0X51_REG 92 +#define TWO_ADDR_0X51_SUPP (1<<6) +#define OPTOE_ID_REG 0 +#define OPTOE_READ_OP 0 +#define OPTOE_WRITE_OP 1 +#define OPTOE_EOF 0 /* used for access beyond end of device */ + +#define mem_clear(data, size) memset((data), 0, (size)) + +struct optoe_data { + struct optoe_platform_data chip; + int use_smbus; + char port_name[MAX_PORT_NAME_LEN]; + + /* + * Lock protects against activities from other Linux tasks, + * but not from changes by other I2C masters. + */ + struct mutex lock; + struct bin_attribute bin; + struct attribute_group attr_group; + + u8 *writebuf; + unsigned int write_max; + + unsigned int num_addresses; + +#ifdef EEPROM_CLASS + struct eeprom_device *eeprom_dev; +#endif + + /* dev_class: ONE_ADDR (QSFP) or TWO_ADDR (SFP) */ + int dev_class; + + struct i2c_client *client[]; +}; + +/* + * This parameter is to help this driver avoid blocking other drivers out + * of I2C for potentially troublesome amounts of time. With a 100 kHz I2C + * clock, one 256 byte read takes about 1/43 second which is excessive; + * but the 1/170 second it takes at 400 kHz may be quite reasonable; and + * at 1 MHz (Fm+) a 1/430 second delay could easily be invisible. + * + * This value is forced to be a power of two so that writes align on pages. + */ +static unsigned int io_limit = OPTOE_PAGE_SIZE; + +/* + * specs often allow 5 msec for a page write, sometimes 20 msec; + * it's important to recover from write timeouts. + */ +static unsigned int write_timeout = 25; + +/* + * flags to distinguish one-address (QSFP family) from two-address (SFP family) + * If the family is not known, figure it out when the device is accessed + */ +#define ONE_ADDR 1 +#define TWO_ADDR 2 +#define CMIS_ADDR 3 + +static const struct i2c_device_id optoe_ids[] = { + { "optoe1", ONE_ADDR }, + { "optoe2", TWO_ADDR }, + { "optoe3", CMIS_ADDR }, + { "sff8436", ONE_ADDR }, + { "24c04", TWO_ADDR }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(i2c, optoe_ids); + +/*-------------------------------------------------------------------------*/ +/* + * This routine computes the addressing information to be used for + * a given r/w request. + * + * Task is to calculate the client (0 = i2c addr 50, 1 = i2c addr 51), + * the page, and the offset. + * + * Handles both single address (eg QSFP) and two address (eg SFP). + * For SFP, offset 0-255 are on client[0], >255 is on client[1] + * Offset 256-383 are on the lower half of client[1] + * Pages are accessible on the upper half of client[1]. + * Offset >383 are in 128 byte pages mapped into the upper half + * + * For QSFP, all offsets are on client[0] + * offset 0-127 are on the lower half of client[0] (no paging) + * Pages are accessible on the upper half of client[1]. + * Offset >127 are in 128 byte pages mapped into the upper half + * + * Callers must not read/write beyond the end of a client or a page + * without recomputing the client/page. Hence offset (within page) + * plus length must be less than or equal to 128. (Note that this + * routine does not have access to the length of the call, hence + * cannot do the validity check.) + * + * Offset within Lower Page 00h and Upper Page 00h are not recomputed + */ + +static uint8_t optoe_translate_offset(struct optoe_data *optoe, + loff_t *offset, struct i2c_client **client) +{ + unsigned int page = 0; + + *client = optoe->client[0]; + + /* if SFP style, offset > 255, shift to i2c addr 0x51 */ + if (optoe->dev_class == TWO_ADDR) { + if (*offset > 255) { + /* like QSFP, but shifted to client[1] */ + *client = optoe->client[1]; + *offset -= 256; + } + } + + /* + * if offset is in the range 0-128... + * page doesn't matter (using lower half), return 0. + * offset is already correct (don't add 128 to get to paged area) + */ + if (*offset < OPTOE_PAGE_SIZE) + return page; + + /* note, page will always be positive since *offset >= 128 */ + page = (*offset >> 7)-1; + /* 0x80 places the offset in the top half, offset is last 7 bits */ + *offset = OPTOE_PAGE_SIZE + (*offset & 0x7f); + + return page; /* note also returning client and offset */ +} + +static ssize_t optoe_eeprom_read(struct optoe_data *optoe, + struct i2c_client *client, + char *buf, unsigned int offset, size_t count) +{ + struct i2c_msg msg[2]; + u8 msgbuf[2]; + unsigned long timeout, read_time; + int status, i; + + mem_clear(msg, sizeof(msg)); + + switch (optoe->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + /*smaller eeproms can work given some SMBus extension calls */ + if (count > I2C_SMBUS_BLOCK_MAX) + count = I2C_SMBUS_BLOCK_MAX; + break; + case I2C_SMBUS_WORD_DATA: + /* Check for odd length transaction */ + count = (count == 1) ? 1 : 2; + break; + case I2C_SMBUS_BYTE_DATA: + count = 1; + break; + default: + /* + * When we have a better choice than SMBus calls, use a + * combined I2C message. Write address; then read up to + * io_limit data bytes. msgbuf is u8 and will cast to our + * needs. + */ + i = 0; + msgbuf[i++] = offset; + + msg[0].addr = client->addr; + msg[0].buf = msgbuf; + msg[0].len = i; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; + } + + /* + * Reads fail if the previous write didn't complete yet. We may + * loop a few times until this one succeeds, waiting at least + * long enough for one entire page write to work. + */ + timeout = jiffies + msecs_to_jiffies(write_timeout); + do { + read_time = jiffies; + + switch (optoe->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + status = i2c_smbus_read_i2c_block_data(client, offset, + count, buf); + break; + case I2C_SMBUS_WORD_DATA: + status = i2c_smbus_read_word_data(client, offset); + if (status >= 0) { + buf[0] = status & 0xff; + if (count == 2) + buf[1] = status >> 8; + status = count; + } + break; + case I2C_SMBUS_BYTE_DATA: + status = i2c_smbus_read_byte_data(client, offset); + if (status >= 0) { + buf[0] = status; + status = count; + } + break; + default: + status = i2c_transfer(client->adapter, msg, 2); + if (status == 2) + status = count; + } + + dev_dbg(&client->dev, "eeprom read %zu@%d --> %d (%ld)\n", + count, offset, status, jiffies); + + if (status == count) /* happy path */ + return count; + + if (status == -ENXIO) /* no module present */ + return status; + + /* REVISIT: at HZ=100, this is sloooow */ + usleep_range(1000, 2000); + } while (time_before(read_time, timeout)); + + return -ETIMEDOUT; +} + +static ssize_t optoe_eeprom_write(struct optoe_data *optoe, + struct i2c_client *client, + const char *buf, + unsigned int offset, size_t count) +{ + struct i2c_msg msg; + ssize_t status; + unsigned long timeout, write_time; + unsigned int next_page_start; + int i = 0; + + /* write max is at most a page + * (In this driver, write_max is actually one byte!) + */ + if (count > optoe->write_max) + count = optoe->write_max; + + /* shorten count if necessary to avoid crossing page boundary */ + next_page_start = roundup(offset + 1, OPTOE_PAGE_SIZE); + if (offset + count > next_page_start) + count = next_page_start - offset; + + switch (optoe->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + /*smaller eeproms can work given some SMBus extension calls */ + if (count > I2C_SMBUS_BLOCK_MAX) + count = I2C_SMBUS_BLOCK_MAX; + break; + case I2C_SMBUS_WORD_DATA: + /* Check for odd length transaction */ + count = (count == 1) ? 1 : 2; + break; + case I2C_SMBUS_BYTE_DATA: + count = 1; + break; + default: + /* If we'll use I2C calls for I/O, set up the message */ + msg.addr = client->addr; + msg.flags = 0; + + /* msg.buf is u8 and casts will mask the values */ + msg.buf = optoe->writebuf; + + msg.buf[i++] = offset; + memcpy(&msg.buf[i], buf, count); + msg.len = i + count; + break; + } + + /* + * Reads fail if the previous write didn't complete yet. We may + * loop a few times until this one succeeds, waiting at least + * long enough for one entire page write to work. + */ + timeout = jiffies + msecs_to_jiffies(write_timeout); + do { + write_time = jiffies; + + switch (optoe->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + status = i2c_smbus_write_i2c_block_data(client, + offset, count, buf); + if (status == 0) + status = count; + break; + case I2C_SMBUS_WORD_DATA: + if (count == 2) { + status = i2c_smbus_write_word_data(client, + offset, (u16)((buf[0])|(buf[1] << 8))); + } else { + /* count = 1 */ + status = i2c_smbus_write_byte_data(client, + offset, buf[0]); + } + if (status == 0) + status = count; + break; + case I2C_SMBUS_BYTE_DATA: + status = i2c_smbus_write_byte_data(client, offset, + buf[0]); + if (status == 0) + status = count; + break; + default: + status = i2c_transfer(client->adapter, &msg, 1); + if (status == 1) + status = count; + break; + } + + dev_dbg(&client->dev, "eeprom write %zu@%d --> %ld (%lu)\n", + count, offset, (long int) status, jiffies); + + if (status == count) + return count; + + /* REVISIT: at HZ=100, this is sloooow */ + usleep_range(1000, 2000); + } while (time_before(write_time, timeout)); + + return -ETIMEDOUT; +} + +static ssize_t optoe_eeprom_update_client(struct optoe_data *optoe, + char *buf, loff_t off, + size_t count, int opcode) +{ + struct i2c_client *client; + ssize_t retval = 0; + uint8_t page = 0; + loff_t phy_offset = off; + int ret = 0; + + page = optoe_translate_offset(optoe, &phy_offset, &client); + dev_dbg(&client->dev, + "%s off %lld page:%d phy_offset:%lld, count:%ld, opcode:%d\n", + __func__, off, page, phy_offset, (long int) count, opcode); + if (page > 0) { + ret = optoe_eeprom_write(optoe, client, &page, + OPTOE_PAGE_SELECT_REG, 1); + if (ret < 0) { + dev_dbg(&client->dev, + "Write page register for page %d failed ret:%d!\n", + page, ret); + return ret; + } + } + + while (count) { + ssize_t status; + + if (opcode == OPTOE_READ_OP) { + status = optoe_eeprom_read(optoe, client, + buf, phy_offset, count); + } else { + status = optoe_eeprom_write(optoe, client, + buf, phy_offset, count); + } + if (status <= 0) { + if (retval == 0) + retval = status; + break; + } + buf += status; + phy_offset += status; + count -= status; + retval += status; + } + + if (page > 0) { + /* return the page register to page 0 (why?) */ + page = 0; + ret = optoe_eeprom_write(optoe, client, &page, + OPTOE_PAGE_SELECT_REG, 1); + if (ret < 0) { + dev_err(&client->dev, + "Restore page register to 0 failed:%d!\n", ret); + /* error only if nothing has been transferred */ + if (retval == 0) + retval = ret; + } + } + return retval; +} + +/* + * Figure out if this access is within the range of supported pages. + * Note this is called on every access because we don't know if the + * module has been replaced since the last call. + * If/when modules support more pages, this is the routine to update + * to validate and allow access to additional pages. + * + * Returns updated len for this access: + * - entire access is legal, original len is returned. + * - access begins legal but is too long, len is truncated to fit. + * - initial offset exceeds supported pages, return OPTOE_EOF (zero) + */ +static ssize_t optoe_page_legal(struct optoe_data *optoe, + loff_t off, size_t len) +{ + struct i2c_client *client = optoe->client[0]; + u8 regval; + int not_pageable; + int status; + size_t maxlen; + + if (off < 0) + return -EINVAL; + if (optoe->dev_class == TWO_ADDR) { + /* SFP case */ + /* if only using addr 0x50 (first 256 bytes) we're good */ + if ((off + len) <= TWO_ADDR_NO_0X51_SIZE) + return len; + /* if offset exceeds possible pages, we're not good */ + if (off >= TWO_ADDR_EEPROM_SIZE) + return OPTOE_EOF; + /* in between, are pages supported? */ + status = optoe_eeprom_read(optoe, client, ®val, + TWO_ADDR_PAGEABLE_REG, 1); + if (status < 0) + return status; /* error out (no module?) */ + if (regval & TWO_ADDR_PAGEABLE) { + /* Pages supported, trim len to the end of pages */ + maxlen = TWO_ADDR_EEPROM_SIZE - off; + } else { + /* pages not supported, trim len to unpaged size */ + if (off >= TWO_ADDR_EEPROM_UNPAGED_SIZE) + return OPTOE_EOF; + + /* will be accessing addr 0x51, is that supported? */ + /* byte 92, bit 6 implies DDM support, 0x51 support */ + status = optoe_eeprom_read(optoe, client, ®val, + TWO_ADDR_0X51_REG, 1); + if (status < 0) + return status; + if (regval & TWO_ADDR_0X51_SUPP) { + /* addr 0x51 is OK */ + maxlen = TWO_ADDR_EEPROM_UNPAGED_SIZE - off; + } else { + /* addr 0x51 NOT supported, trim to 256 max */ + if (off >= TWO_ADDR_NO_0X51_SIZE) + return OPTOE_EOF; + maxlen = TWO_ADDR_NO_0X51_SIZE - off; + } + } + len = (len > maxlen) ? maxlen : len; + dev_dbg(&client->dev, + "page_legal, SFP, off %lld len %ld\n", + off, (long int) len); + } else { + /* QSFP case, CMIS case */ + /* if no pages needed, we're good */ + if ((off + len) <= ONE_ADDR_EEPROM_UNPAGED_SIZE) + return len; + /* if offset exceeds possible pages, we're not good */ + if (off >= ONE_ADDR_EEPROM_SIZE) + return OPTOE_EOF; + /* in between, are pages supported? */ + status = optoe_eeprom_read(optoe, client, ®val, + ONE_ADDR_PAGEABLE_REG, 1); + if (status < 0) + return status; /* error out (no module?) */ + + if (optoe->dev_class == ONE_ADDR) { + not_pageable = QSFP_NOT_PAGEABLE; + } else { + not_pageable = CMIS_NOT_PAGEABLE; + } + dev_dbg(&client->dev, + "Paging Register: 0x%x; not_pageable mask: 0x%x\n", + regval, not_pageable); + + if (regval & not_pageable) { + /* pages not supported, trim len to unpaged size */ + if (off >= ONE_ADDR_EEPROM_UNPAGED_SIZE) + return OPTOE_EOF; + maxlen = ONE_ADDR_EEPROM_UNPAGED_SIZE - off; + } else { + /* Pages supported, trim len to the end of pages */ + maxlen = ONE_ADDR_EEPROM_SIZE - off; + } + len = (len > maxlen) ? maxlen : len; + dev_dbg(&client->dev, + "page_legal, QSFP, off %lld len %ld\n", + off, (long int) len); + } + return len; +} + +static ssize_t optoe_read_write(struct optoe_data *optoe, + char *buf, loff_t off, size_t len, int opcode) +{ + struct i2c_client *client = optoe->client[0]; + int chunk; + int status = 0; + ssize_t retval; + size_t pending_len = 0, chunk_len = 0; + loff_t chunk_offset = 0, chunk_start_offset = 0; + loff_t chunk_end_offset = 0; + + dev_dbg(&client->dev, + "%s: off %lld len:%ld, opcode:%s\n", + __func__, off, (long int) len, + (opcode == OPTOE_READ_OP) ? "r" : "w"); + if (unlikely(!len)) + return len; + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&optoe->lock); + + /* + * Confirm this access fits within the device suppored addr range + */ + status = optoe_page_legal(optoe, off, len); + if ((status == OPTOE_EOF) || (status < 0)) { + mutex_unlock(&optoe->lock); + return status; + } + len = status; + + /* + * For each (128 byte) chunk involved in this request, issue a + * separate call to sff_eeprom_update_client(), to + * ensure that each access recalculates the client/page + * and writes the page register as needed. + * Note that chunk to page mapping is confusing, is different for + * QSFP and SFP, and never needs to be done. Don't try! + */ + pending_len = len; /* amount remaining to transfer */ + retval = 0; /* amount transferred */ + for (chunk = off >> 7; chunk <= (off + len - 1) >> 7; chunk++) { + + /* + * Compute the offset and number of bytes to be read/write + * + * 1. start at an offset not equal to 0 (within the chunk) + * and read/write less than the rest of the chunk + * 2. start at an offset not equal to 0 and read/write the rest + * of the chunk + * 3. start at offset 0 (within the chunk) and read/write less + * than entire chunk + * 4. start at offset 0 (within the chunk), and read/write + * the entire chunk + */ + chunk_start_offset = chunk * OPTOE_PAGE_SIZE; + chunk_end_offset = chunk_start_offset + OPTOE_PAGE_SIZE; + + if (chunk_start_offset < off) { + chunk_offset = off; + if ((off + pending_len) < chunk_end_offset) + chunk_len = pending_len; + else + chunk_len = chunk_end_offset - off; + } else { + chunk_offset = chunk_start_offset; + if (pending_len < OPTOE_PAGE_SIZE) + chunk_len = pending_len; + else + chunk_len = OPTOE_PAGE_SIZE; + } + + dev_dbg(&client->dev, + "sff_r/w: off %lld, len %ld, chunk_start_offset %lld, chunk_offset %lld, chunk_len %ld, pending_len %ld\n", + off, (long int) len, chunk_start_offset, chunk_offset, + (long int) chunk_len, (long int) pending_len); + + /* + * note: chunk_offset is from the start of the EEPROM, + * not the start of the chunk + */ + status = optoe_eeprom_update_client(optoe, buf, + chunk_offset, chunk_len, opcode); + if (status != chunk_len) { + /* This is another 'no device present' path */ + dev_dbg(&client->dev, + "o_u_c: chunk %d c_offset %lld c_len %ld failed %d!\n", + chunk, chunk_offset, (long int) chunk_len, status); + if (status > 0) + retval += status; + if (retval == 0) + retval = status; + break; + } + buf += status; + pending_len -= status; + retval += status; + } + mutex_unlock(&optoe->lock); + + return retval; +} + +static ssize_t optoe_bin_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = to_i2c_client(container_of(kobj, + struct device, kobj)); + struct optoe_data *optoe = i2c_get_clientdata(client); + + return optoe_read_write(optoe, buf, off, count, OPTOE_READ_OP); +} + +static ssize_t optoe_bin_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = to_i2c_client(container_of(kobj, + struct device, kobj)); + struct optoe_data *optoe = i2c_get_clientdata(client); + + return optoe_read_write(optoe, buf, off, count, OPTOE_WRITE_OP); +} + +static int optoe_remove(struct i2c_client *client) +{ + struct optoe_data *optoe; + int i; + + optoe = i2c_get_clientdata(client); + sysfs_remove_group(&client->dev.kobj, &optoe->attr_group); + sysfs_remove_bin_file(&client->dev.kobj, &optoe->bin); + + for (i = 1; i < optoe->num_addresses; i++) + i2c_unregister_device(optoe->client[i]); + +#ifdef EEPROM_CLASS + eeprom_device_unregister(optoe->eeprom_dev); +#endif + + kfree(optoe->writebuf); + kfree(optoe); + return 0; +} + +static ssize_t show_dev_class(struct device *dev, + struct device_attribute *dattr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct optoe_data *optoe = i2c_get_clientdata(client); + ssize_t count; + + mutex_lock(&optoe->lock); + count = sprintf(buf, "%d\n", optoe->dev_class); + mutex_unlock(&optoe->lock); + + return count; +} + +static ssize_t set_dev_class(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct optoe_data *optoe = i2c_get_clientdata(client); + int dev_class; + + /* + * dev_class is actually the number of i2c addresses used, thus + * legal values are "1" (QSFP class) and "2" (SFP class) + * And... CMIS spec is 1 i2c address, but puts the pageable + * bit in a different location, so CMIS devices are "3" + */ + + if (kstrtoint(buf, 0, &dev_class) != 0 || + dev_class < 1 || dev_class > 3) + return -EINVAL; + + mutex_lock(&optoe->lock); + if (dev_class == TWO_ADDR) { + /* SFP family */ + /* if it doesn't exist, create 0x51 i2c address */ + if (!optoe->client[1]) { + optoe->client[1] = i2c_new_dummy_device(client->adapter, 0x51); + if (!optoe->client[1]) { + dev_err(&client->dev, + "address 0x51 unavailable\n"); + mutex_unlock(&optoe->lock); + return -EADDRINUSE; + } + } + optoe->bin.size = TWO_ADDR_EEPROM_SIZE; + optoe->num_addresses = 2; + } else { + /* one-address (eg QSFP) and CMIS family */ + /* if it exists, remove 0x51 i2c address */ + if (optoe->client[1]) + i2c_unregister_device(optoe->client[1]); + optoe->bin.size = ONE_ADDR_EEPROM_SIZE; + optoe->num_addresses = 1; + } + optoe->dev_class = dev_class; + mutex_unlock(&optoe->lock); + + return count; +} + +/* + * if using the EEPROM CLASS driver, we don't report a port_name, + * the EEPROM CLASS drive handles that. Hence all this code is + * only compiled if we are NOT using the EEPROM CLASS driver. + */ +#ifndef EEPROM_CLASS + +static ssize_t show_port_name(struct device *dev, + struct device_attribute *dattr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct optoe_data *optoe = i2c_get_clientdata(client); + ssize_t count; + + mutex_lock(&optoe->lock); + count = sprintf(buf, "%s\n", optoe->port_name); + mutex_unlock(&optoe->lock); + + return count; +} + +static ssize_t set_port_name(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct optoe_data *optoe = i2c_get_clientdata(client); + char port_name[MAX_PORT_NAME_LEN]; + + /* no checking, this value is not used except by show_port_name */ + + if (sscanf(buf, "%19s", port_name) != 1) + return -EINVAL; + + mutex_lock(&optoe->lock); + strcpy(optoe->port_name, port_name); + mutex_unlock(&optoe->lock); + + return count; +} + +static DEVICE_ATTR(port_name, 0644, show_port_name, set_port_name); +#endif /* if NOT defined EEPROM_CLASS, the common case */ + +static DEVICE_ATTR(dev_class, 0644, show_dev_class, set_dev_class); + +static struct attribute *optoe_attrs[] = { +#ifndef EEPROM_CLASS + &dev_attr_port_name.attr, +#endif + &dev_attr_dev_class.attr, + NULL, +}; + +static struct attribute_group optoe_attr_group = { + .attrs = optoe_attrs, +}; + +static int optoe_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err; + int use_smbus = 0; + struct optoe_platform_data chip; + struct optoe_data *optoe; + int num_addresses = 0; + char port_name[MAX_PORT_NAME_LEN]; + + if (client->addr != 0x50) { + dev_dbg(&client->dev, "probe, bad i2c addr: 0x%x\n", + client->addr); + err = -EINVAL; + goto exit; + } + + if (client->dev.platform_data) { + chip = *(struct optoe_platform_data *)client->dev.platform_data; + /* take the port name from the supplied platform data */ +#ifdef EEPROM_CLASS + strncpy(port_name, chip.eeprom_data->label, MAX_PORT_NAME_LEN); +#else + memcpy(port_name, chip.port_name, MAX_PORT_NAME_LEN); +#endif + dev_dbg(&client->dev, + "probe, chip provided, flags:0x%x; name: %s\n", + chip.flags, client->name); + } else { + if (!id->driver_data) { + err = -ENODEV; + goto exit; + } + dev_dbg(&client->dev, "probe, building chip\n"); + strcpy(port_name, "unitialized"); + chip.flags = 0; +#ifdef EEPROM_CLASS + chip.eeprom_data = NULL; +#endif + } + + /* Use I2C operations unless we're stuck with SMBus extensions. */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + use_smbus = I2C_SMBUS_I2C_BLOCK_DATA; + } else if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) { + use_smbus = I2C_SMBUS_WORD_DATA; + } else if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA)) { + use_smbus = I2C_SMBUS_BYTE_DATA; + } else { + err = -EPFNOSUPPORT; + goto exit; + } + } + + /* + * Make room for two i2c clients + */ + num_addresses = 2; + + optoe = kzalloc(sizeof(struct optoe_data) + + num_addresses * sizeof(struct i2c_client *), + GFP_KERNEL); + if (!optoe) { + err = -ENOMEM; + goto exit; + } + + mutex_init(&optoe->lock); + + /* determine whether this is a one-address or two-address module */ + if ((strcmp(client->name, "optoe1") == 0) || + (strcmp(client->name, "sff8436") == 0)) { + /* one-address (eg QSFP) family */ + optoe->dev_class = ONE_ADDR; + chip.byte_len = ONE_ADDR_EEPROM_SIZE; + num_addresses = 1; + } else if ((strcmp(client->name, "optoe2") == 0) || + (strcmp(client->name, "24c04") == 0)) { + /* SFP family */ + optoe->dev_class = TWO_ADDR; + chip.byte_len = TWO_ADDR_EEPROM_SIZE; + num_addresses = 2; + } else if (strcmp(client->name, "optoe3") == 0) { + /* CMIS spec */ + optoe->dev_class = CMIS_ADDR; + chip.byte_len = ONE_ADDR_EEPROM_SIZE; + num_addresses = 1; + } else { /* those were the only choices */ + err = -EINVAL; + goto exit; + } + + dev_dbg(&client->dev, "dev_class: %d\n", optoe->dev_class); + optoe->use_smbus = use_smbus; + optoe->chip = chip; + optoe->num_addresses = num_addresses; + memcpy(optoe->port_name, port_name, MAX_PORT_NAME_LEN); + + /* + * Export the EEPROM bytes through sysfs, since that's convenient. + * By default, only root should see the data (maybe passwords etc) + */ + sysfs_bin_attr_init(&optoe->bin); + optoe->bin.attr.name = "eeprom"; + optoe->bin.attr.mode = 0444; + optoe->bin.read = optoe_bin_read; + optoe->bin.size = chip.byte_len; + + if (!use_smbus || + (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) || + i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_WORD_DATA) || + i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + /* + * NOTE: AN-2079 + * Finisar recommends that the host implement 1 byte writes + * only since this module only supports 32 byte page boundaries. + * 2 byte writes are acceptable for PE and Vout changes per + * Application Note AN-2071. + */ + unsigned int write_max = 1; + + optoe->bin.write = optoe_bin_write; + optoe->bin.attr.mode |= 0200; + + if (write_max > io_limit) + write_max = io_limit; + if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX) + write_max = I2C_SMBUS_BLOCK_MAX; + optoe->write_max = write_max; + + /* buffer (data + address at the beginning) */ + optoe->writebuf = kmalloc(write_max + 2, GFP_KERNEL); + if (!optoe->writebuf) { + err = -ENOMEM; + goto exit_kfree; + } + } else { + dev_warn(&client->dev, + "cannot write due to controller restrictions."); + } + + optoe->client[0] = client; + + /* SFF-8472 spec requires that the second I2C address be 0x51 */ + if (num_addresses == 2) { + optoe->client[1] = i2c_new_dummy_device(client->adapter, 0x51); + if (!optoe->client[1]) { + dev_err(&client->dev, "address 0x51 unavailable\n"); + err = -EADDRINUSE; + goto err_struct; + } + } + + /* create the sysfs eeprom file */ + err = sysfs_create_bin_file(&client->dev.kobj, &optoe->bin); + if (err) + goto err_struct; + + optoe->attr_group = optoe_attr_group; + + err = sysfs_create_group(&client->dev.kobj, &optoe->attr_group); + if (err) { + dev_err(&client->dev, "failed to create sysfs attribute group.\n"); + goto err_struct; + } + +#ifdef EEPROM_CLASS + optoe->eeprom_dev = eeprom_device_register(&client->dev, + chip.eeprom_data); + if (IS_ERR(optoe->eeprom_dev)) { + dev_err(&client->dev, "error registering eeprom device.\n"); + err = PTR_ERR(optoe->eeprom_dev); + goto err_sysfs_cleanup; + } +#endif + + i2c_set_clientdata(client, optoe); + + dev_info(&client->dev, "%zu byte %s EEPROM, %s\n", + optoe->bin.size, client->name, + optoe->bin.write ? "read/write" : "read-only"); + + if (use_smbus == I2C_SMBUS_WORD_DATA || + use_smbus == I2C_SMBUS_BYTE_DATA) { + dev_notice(&client->dev, + "Falling back to %s reads, performance will suffer\n", + use_smbus == I2C_SMBUS_WORD_DATA ? "word" : "byte"); + } + + return 0; + +#ifdef EEPROM_CLASS +err_sysfs_cleanup: + sysfs_remove_group(&client->dev.kobj, &optoe->attr_group); + sysfs_remove_bin_file(&client->dev.kobj, &optoe->bin); +#endif + +err_struct: + if (num_addresses == 2) { + if (optoe->client[1]) + i2c_unregister_device(optoe->client[1]); + } + + kfree(optoe->writebuf); +exit_kfree: + kfree(optoe); +exit: + dev_dbg(&client->dev, "probe error %d\n", err); + + return err; +} + +/*-------------------------------------------------------------------------*/ + +static struct i2c_driver optoe_driver = { + .driver = { + .name = "optoe", + .owner = THIS_MODULE, + }, + .probe = optoe_probe, + .remove = optoe_remove, + .id_table = optoe_ids, +}; + +static int __init optoe_init(void) +{ + + if (!io_limit) { + pr_err("optoe: io_limit must not be 0!\n"); + return -EINVAL; + } + + io_limit = rounddown_pow_of_two(io_limit); + return i2c_add_driver(&optoe_driver); +} +module_init(optoe_init); + +static void __exit optoe_exit(void) +{ + i2c_del_driver(&optoe_driver); +} +module_exit(optoe_exit); + +MODULE_DESCRIPTION("Driver for optical transceiver (SFP, QSFP, ...) EEPROMs"); +MODULE_AUTHOR("DON BOLLINGER "); +MODULE_LICENSE("GPL"); diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/modules/platform.h b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/platform.h new file mode 100644 index 000000000000..869bc5322af6 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/platform.h @@ -0,0 +1,155 @@ +#ifndef __PLATFORM_H__ +#define __PLATFORM_H__ + +#include +#include +#include +#include + +#define mem_clear(data, size) memset((data), 0, (size)) + +/* debug switch level */ +typedef enum { + DBG_START, + DBG_VERBOSE, + DBG_KEY, + DBG_WARN, + DBG_ERROR, + DBG_END, +} dbg_level_t; + +typedef enum dfd_cpld_id { + BCM_CPLD0 = 0, + BCM_CPLD1, + CPLD0_MAC0, + CPLD0_MAC1, + CPLD1_MAC0, + CPLD2_MAC1, +} dfd_cpld_id_t; + + typedef enum dfd_cpld_bus { + SMBUS_BUS = 0 , + GPIO_BUS = 1, + PCA9641_BUS = 2, +} dfd_cpld_bus_t; + + typedef struct dfd_i2c_dev_s { + int bus; /* bus number */ + int addr; /* bus address */ + } dfd_i2c_dev_t; + + typedef enum dfd_cpld_addr { + CPLD_ADDR_MIN = 0x31, + BCM_CPLD0_ADDR = 0x32, /* pca9641 up */ + CPLD0_MAC0_ADDR = 0x33, /* SMBUS down */ + CPLD0_MAC1_ADDR = 0x34, + CPLD1_MAC0_ADDR = 0x35, + CPLD2_MAC1_ADDR = 0x36, + BCM_CPLD1_ADDR = 0x37, + CPLD_ADDR_MAX, +} dfd_cpld_addr_t; + +typedef struct dfd_dev_head_info_s { + uint8_t ver; /* The version number defined by the E2PROM file, the initial version is 0x01 */ + uint8_t flag; /* The new version of E2PROM is identified as 0x7E */ + uint8_t hw_ver; /* It consists of two parts: the main version number and the revised version */ + uint8_t type; /* Hardware type definition information */ + int16_t tlv_len; /* Effective data length (16 bits) */ +} dfd_dev_head_info_t; + +typedef enum dfd_intf_e{ + DFD_INTF_GET_FAN_HW_VERSION, + DFD_INTF_GET_FAN_STATUS, + DFD_INTF_GET_FAN_SPEED_LEVEL, + DFD_INTF_GET_FAN_SPEED, + DFD_INTF_GET_FAN_ATTRIBUTE, + DFD_INTF_GET_FAN_SN, + DFD_INTF_GET_FAN_TYPE, + DFD_INTF_SET_FAN_SPEED_LEVEL, + DFD_INTF_GET_FAN_SUB_NUM, + DFD_INTF_GET_FAN_FAIL_BITMAP, +}dfd_intf_t; + +typedef struct dfd_dev_tlv_info_s { + uint8_t type; /* the type of data */ + uint8_t len; /* the length of data */ + uint8_t data[0]; /* data */ +} dfd_dev_tlv_info_t; + +typedef enum dfd_dev_info_type_e { + DFD_DEV_INFO_TYPE_MAC = 1, + DFD_DEV_INFO_TYPE_NAME = 2, + DFD_DEV_INFO_TYPE_SN = 3, + DFD_DEV_INFO_TYPE_PWR_CONS = 4, + DFD_DEV_INFO_TYPE_HW_INFO = 5, + DFD_DEV_INFO_TYPE_DEV_TYPE = 6, +} dfd_dev_tlv_type_t; + +struct sfp_drivers_t{ + /* addr = sff present bitmap addr, index from 0 */ + void (*get_number) (unsigned int *number); + int (*get_port_start) (void); + int (*get_port_end) (void); + bool (*is_qsfp_port) (const unsigned int port_num); + bool (*get_present) (unsigned long *bitmap); + bool (*read_sfp_eeprom) (const unsigned int port, + const unsigned char addr, + const unsigned char offset, + const uint32_t count, char *buf); + bool (*write_sfp_eeprom) (const unsigned int port, + const unsigned char addr, + const unsigned char offset, + const unsigned char count, + const char *buf); + bool (*read_sysfs) (const unsigned int bus, + const unsigned char addr, + const unsigned char offset, + const uint32_t count, char *buf); + bool (*write_sysfs) (const unsigned int bus, + const unsigned char addr, + const unsigned char offset, + const unsigned char count, + const char *buf); + bool (*read_block_sysfs) (const unsigned int bus, + const unsigned char addr, + const unsigned char offset, + const uint32_t count, char *buf); +}; + +extern int debuglevel; +extern int dfd_cpld_read_chipid(int cpldid , uint32_t addr, int32_t size, unsigned char *buf); +extern int dfd_cpld_read(int32_t addr, uint8_t *val); +extern int dfd_cpld_write(int32_t addr, uint8_t val); + +extern s32 platform_i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command); +extern s32 platform_i2c_smbus_read_i2c_block_data(const struct i2c_client *client, + u8 command, u8 length, u8 *values); +extern s32 platform_i2c_smbus_read_word_data(const struct i2c_client *client, u8 command); + +extern int sfp_drivers_register(struct sfp_drivers_t *drv); +extern int sfp_drivers_unregister(void); + +extern int sysfs_drivers_register(struct sfp_drivers_t *drv); +extern int sysfs_drivers_unregister(void); + +#define DBG_DEBUG(fmt, arg...) do { \ + if ( debuglevel > DBG_START && debuglevel < DBG_ERROR) { \ + printk(KERN_INFO "[DEBUG]:<%s, %d>:"fmt, __FUNCTION__, __LINE__, ##arg); \ + } else if ( debuglevel >= DBG_ERROR ) { \ + printk(KERN_ERR "[DEBUG]:<%s, %d>:"fmt, __FUNCTION__, __LINE__, ##arg); \ + } else { } \ +} while (0) + +#define DBG_INFO(fmt, arg...) do { \ + if ( debuglevel > DBG_KEY) { \ + printk(KERN_INFO "[INFO]:<%s, %d>:"fmt, __FUNCTION__, __LINE__, ##arg); \ + } \ + } while (0) + +#define DBG_ERROR(fmt, arg...) do { \ + if ( debuglevel > DBG_START) { \ + printk(KERN_ERR "[ERROR]:<%s, %d>:"fmt, __FUNCTION__, __LINE__, ##arg); \ + } \ + } while (0) + +#endif diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/modules/platform_common_module.c b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/platform_common_module.c new file mode 100644 index 000000000000..14fc1a1574c3 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/platform_common_module.c @@ -0,0 +1,210 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform.h" +#include "dfd_tlveeprom.h" + +#define PLATFORM_I2C_RETRY_TIMES 3 + +#define DFD_TLVEEPROM_I2C_BUS (0) +#define DFD_TLVEEPROM_I2C_ADDR (0x56) +#define DFD_E2PROM_MAX_LEN (256) +#define DFD_CARDTYPE_EXT_TLVLEN (4) + +#define PLATFORM_CARDTYPE_RETRY_CNT (10) +#define PLATFORM_CARDTYPE_RETRY_TIMES (1000) + +int debuglevel = 0; +module_param(debuglevel, int, S_IRUGO | S_IWUSR); + +static int dfd_my_type = 0; +module_param(dfd_my_type, int, S_IRUGO | S_IWUSR); + +int g_common_debug_error = 0; +module_param(g_common_debug_error, int, S_IRUGO | S_IWUSR); + +int g_common_debug_verbose = 0; +module_param(g_common_debug_verbose, int, S_IRUGO | S_IWUSR); + +uint32_t dfd_my_type_i2c_bus = 0; +module_param(dfd_my_type_i2c_bus, int, S_IRUGO | S_IWUSR); + +uint32_t dfd_my_type_i2c_addr = 0; +module_param(dfd_my_type_i2c_addr, int, S_IRUGO | S_IWUSR); + +#define WB_COMMON_DEBUG_VERBOSE(fmt, args...) do { \ + if (g_common_debug_verbose) { \ + printk(KERN_ERR "[WB_COMMON][VER][func:%s line:%d]\r\n"fmt, __func__, __LINE__, ## args); \ + } \ +} while (0) + +#define WB_COMMON_DEBUG_ERROR(fmt, args...) do { \ + if (g_common_debug_error) { \ + printk(KERN_ERR "[WB_COMMON][ERROR][func:%s line:%d]\r\n"fmt, __func__, __LINE__, ## args); \ + } \ +} while (0) + +static int32_t dfd_i2c_read(char *dev, uint32_t addr, uint32_t offset_addr, unsigned char * +buf, int32_t size) +{ + struct file *fp; + mm_segment_t fs; + struct i2c_client client; + int i ,j ; + int rv; + s32 val_t; + + val_t = -1; + rv = 0; + fp = filp_open(dev, O_RDWR, S_IRUSR | S_IWUSR); + if (IS_ERR(fp)) { + DBG_ERROR("i2c open fail.\n"); + WB_COMMON_DEBUG_ERROR("i2c open fail.\n"); + return -1; + } + memcpy(&client, fp->private_data, sizeof(struct i2c_client)); + client.addr = addr; + fs = get_fs(); + set_fs(KERNEL_DS); + for (j = 0 ;j < size ;j++){ + for (i = 0; i < PLATFORM_I2C_RETRY_TIMES; i++) { + if ((val_t = i2c_smbus_read_byte_data(&client, (offset_addr + j))) < 0) { + DBG_DEBUG("read try(%d)time offset_addr:%x \n", i, offset_addr); + continue; + } else { + * (buf + j) = val_t; + break; + } + } + if (val_t < 0) { + rv = -1; + break; + } + } + filp_close(fp, NULL); + set_fs(fs); + return rv; +} + +static int dfd_tlvinfo_get_cardtype(void) +{ + char i2c_path[16] = {0}; + int ret; + int cardtype; + u_int8_t eeprom[DFD_E2PROM_MAX_LEN]; + dfd_i2c_dev_t i2c_dev; + uint8_t buf[DFD_CARDTYPE_EXT_TLVLEN]; + uint8_t len; + dfd_tlv_type_t tlv_type; + + if (dfd_my_type_i2c_bus != 0) { + i2c_dev.bus = dfd_my_type_i2c_bus; + } else { + i2c_dev.bus = DFD_TLVEEPROM_I2C_BUS; + } + + if (dfd_my_type_i2c_addr != 0) { + i2c_dev.addr = dfd_my_type_i2c_addr; + } else { + i2c_dev.addr = DFD_TLVEEPROM_I2C_ADDR; + } + snprintf(i2c_path, sizeof(i2c_path), "/dev/i2c-%d", i2c_dev.bus); + WB_COMMON_DEBUG_VERBOSE("Read device eeprom info:(dev:%s, addr:%02x).\n", i2c_path, i2c_dev.addr); + + ret = dfd_i2c_read(i2c_path, i2c_dev.addr, 0, eeprom, DFD_E2PROM_MAX_LEN); + if (ret != 0) { + DBG_ERROR("Read eeprom info error(dev: %s, addr: %02x).\n", i2c_path, i2c_dev.addr); + WB_COMMON_DEBUG_ERROR("Read eeprom info error(dev: %s, addr: %02x).\n", i2c_path, i2c_dev.addr); + return ret; + } + + tlv_type.main_type = TLV_CODE_VENDOR_EXT; + tlv_type.ext_type = DFD_TLVINFO_EXT_TLV_TYPE_DEV_TYPE; + len = sizeof(buf); + mem_clear(buf, len); + ret = dfd_tlvinfo_get_e2prom_info(eeprom, DFD_E2PROM_MAX_LEN, &tlv_type, buf, &len); + if (ret) { + DBG_ERROR("dfd_tlvinfo_get_e2prom_info failed ret %d.\n", ret); + return -1; + } + for (ret = 0; ret < 4; ret++) { + DBG_DEBUG("buf 0x%02x.\n", buf[ret]); + } + + cardtype = ntohl(*((uint32_t *)buf)); + DBG_DEBUG("cardtype 0x%x.\n", cardtype); + return cardtype; +} + +static int __dfd_get_my_card_type(void) +{ + return dfd_tlvinfo_get_cardtype(); +} + +/* Get its own card type */ +int dfd_get_my_card_type(void) +{ + int type; + int cnt; + + if (dfd_my_type != 0) { + DBG_DEBUG("my_type = 0x%x\r\n", dfd_my_type); + return dfd_my_type; + } + + cnt = PLATFORM_CARDTYPE_RETRY_CNT; + while (cnt--) { + type = __dfd_get_my_card_type(); + if (type < 0) { + WB_COMMON_DEBUG_ERROR("__dfd_get_my_card_type fail cnt %d, ret %d.\n", cnt, type); + msleep(PLATFORM_CARDTYPE_RETRY_TIMES); + continue; + } + WB_COMMON_DEBUG_VERBOSE("success to get type 0x%x.\n", type); + break; + } + + dfd_my_type = type; + return dfd_my_type; +} +EXPORT_SYMBOL(dfd_get_my_card_type); + +static int __init wb_common_init(void) +{ + int ret; + + WB_COMMON_DEBUG_VERBOSE("Enter.\n"); + ret = dfd_get_my_card_type(); + if (ret <= 0) { + WB_COMMON_DEBUG_ERROR("dfd_get_my_card_type failed, ret %d.\n", ret); + printk(KERN_ERR "Warning: Device type get failed, please check the TLV-EEPROM!\n"); + return -1; + } + + WB_COMMON_DEBUG_VERBOSE("Leave success type 0x%x.\n", ret); + return 0; +} + +static void __exit wb_common_exit(void) +{ + WB_COMMON_DEBUG_VERBOSE("Exit.\n"); +} + +module_init(wb_common_init); +module_exit(wb_common_exit); + +MODULE_DESCRIPTION("Platform Support"); +MODULE_AUTHOR("support "); +MODULE_LICENSE("GPL"); diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/modules/rtcpcf85063.c b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/rtcpcf85063.c new file mode 100644 index 000000000000..bd61185036f2 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/rtcpcf85063.c @@ -0,0 +1,258 @@ +/* + * An I2C driver for the PCF85063 RTC + * Copyright 2014 Rose Technology + * + * Author: Sren Andersen + * Maintainers: http://www.nslu2-linux.org/ + * + * based on the other drivers in this same directory. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include + +/* + * Information for this driver was pulled from the following datasheets. + * + * http://www.nxp.com/documents/data_sheet/PCF85063A.pdf + * http://www.nxp.com/documents/data_sheet/PCF85063TP.pdf + * +*/ + +#define PCF85063_REG_CTRL1 0x00 /* status */ +#define PCF85063_REG_CTRL1_CAP_SEL BIT(0) +#define PCF85063_REG_CTRL1_STOP BIT(5) +#define PCF85063_REG_CTRL2 0x01 +#define PCF85063_REG_CTRL2_COF 0x07 +#define PCF85063_REG_SC 0x04 /* datetime */ +#define PCF85063_REG_SC_OS 0x80 +#define PCF85063_REG_MN 0x05 +#define PCF85063_REG_HR 0x06 +#define PCF85063_REG_DM 0x07 +#define PCF85063_REG_DW 0x08 +#define PCF85063_REG_MO 0x09 +#define PCF85063_REG_YR 0x0A +#define PCF85063_SR 0x58 + +static struct i2c_driver pcf85063_driver; + +static int pcf85063_stop_clock(struct i2c_client *client, u8 *ctrl1) +{ + s32 ret; + + ret = i2c_smbus_read_byte_data(client, PCF85063_REG_CTRL1); + if (ret < 0) { + dev_err(&client->dev, "Failing to stop the clock\n"); + return -EIO; + } + + /* stop the clock */ + ret |= PCF85063_REG_CTRL1_STOP; + + ret = i2c_smbus_write_byte_data(client, PCF85063_REG_CTRL1, ret); + if (ret < 0) { + dev_err(&client->dev, "Failing to stop the clock\n"); + return -EIO; + } + + *ctrl1 = ret; + + return 0; +} + +static int pcf85063_start_clock(struct i2c_client *client, u8 ctrl1) +{ + s32 ret; + + /* start the clock */ + ctrl1 &= ~PCF85063_REG_CTRL1_STOP; + ret = i2c_smbus_write_byte_data(client, PCF85063_REG_CTRL1, ctrl1); + if (ret < 0) { + dev_err(&client->dev, "Failing to start the clock\n"); + return -EIO; + } + + return 0; +} + +static int pcf85063_get_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + int rc; + u8 regs[7]; + + /* + * while reading, the time/date registers are blocked and not updated + * anymore until the access is finished. To not lose a second + * event, the access must be finished within one second. So, read all + * time/date registers in one turn. + */ + rc = i2c_smbus_read_i2c_block_data(client, PCF85063_REG_SC, + sizeof(regs), regs); + if (rc != sizeof(regs)) { + dev_err(&client->dev, "date/time register read error\n"); + return -EIO; + } + + /* if the clock has lost its power it makes no sense to use its time */ + if (regs[0] & PCF85063_REG_SC_OS) { + dev_warn(&client->dev, "Power loss detected, invalid time\n"); + return -EINVAL; + } + + tm->tm_sec = bcd2bin(regs[0] & 0x7F); + tm->tm_min = bcd2bin(regs[1] & 0x7F); + tm->tm_hour = bcd2bin(regs[2] & 0x3F); /* rtc hr 0-23 */ + tm->tm_mday = bcd2bin(regs[3] & 0x3F); + tm->tm_wday = regs[4] & 0x07; + tm->tm_mon = bcd2bin(regs[5] & 0x1F) - 1; /* rtc mn 1-12 */ + tm->tm_year = bcd2bin(regs[6]); + tm->tm_year += 100; + + return rtc_valid_tm(tm); +} + +static int pcf85063_set_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + int rc; + u8 regs[7]; + u8 ctrl1; + u8 cof; + s32 err; + + cof = 0; + cof |= PCF85063_REG_CTRL2_COF; + if ((tm->tm_year < 100) || (tm->tm_year > 199)) + return -EINVAL; + + /* reset rtc-pcf85063 */ + err = i2c_smbus_write_byte_data(client, PCF85063_REG_CTRL1, PCF85063_SR); + if (err < 0) { + dev_err(&client->dev, "Failing to reset rtc-pcf85063\n"); + return EIO; + } + + /* + * to accurately set the time, reset the divider chain and keep it in + * reset state until all time/date registers are written + */ + rc = pcf85063_stop_clock(client, &ctrl1); + if (rc != 0) + return rc; + + /* set CTRL1 CAP_SEL is 12.5pF */ + ctrl1 |= PCF85063_REG_CTRL1_CAP_SEL; + err = i2c_smbus_write_byte_data(client, PCF85063_REG_CTRL1, ctrl1); + if (err < 0) { + dev_err(&client->dev, "Failing to write the PCF85063_REG_CTRL1\n"); + return -EIO; + } + /* set CTRL2 CLKOUT is LOW */ + err = i2c_smbus_write_byte_data(client, PCF85063_REG_CTRL2, cof); + if (err < 0) { + dev_err(&client->dev, "Failing to write the PCF85063_REG_CTRL2\n"); + return -EIO; + } + + /* hours, minutes and seconds */ + regs[0] = bin2bcd(tm->tm_sec) & 0x7F; /* clear OS flag */ + + regs[1] = bin2bcd(tm->tm_min); + regs[2] = bin2bcd(tm->tm_hour); + + /* Day of month, 1 - 31 */ + regs[3] = bin2bcd(tm->tm_mday); + + /* Day, 0 - 6 */ + regs[4] = tm->tm_wday & 0x07; + + /* month, 1 - 12 */ + regs[5] = bin2bcd(tm->tm_mon + 1); + + /* year and century */ + regs[6] = bin2bcd(tm->tm_year - 100); + + /* write all registers at once */ + rc = i2c_smbus_write_i2c_block_data(client, PCF85063_REG_SC, + sizeof(regs), regs); + if (rc < 0) { + dev_err(&client->dev, "date/time register write error\n"); + return rc; + } + + /* + * Write the control register as a separate action since the size of + * the register space is different between the PCF85063TP and + * PCF85063A devices. The rollover point can not be used. + */ + rc = pcf85063_start_clock(client, ctrl1); + if (rc != 0) + return rc; + + return 0; +} + +static int pcf85063_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + return pcf85063_get_datetime(to_i2c_client(dev), tm); +} + +static int pcf85063_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + return pcf85063_set_datetime(to_i2c_client(dev), tm); +} + +static const struct rtc_class_ops pcf85063_rtc_ops = { + .read_time = pcf85063_rtc_read_time, + .set_time = pcf85063_rtc_set_time +}; + +static int pcf85063_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct rtc_device *rtc; + + dev_dbg(&client->dev, "%s\n", __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + rtc = devm_rtc_device_register(&client->dev, + pcf85063_driver.driver.name, + &pcf85063_rtc_ops, THIS_MODULE); + + rtc->uie_unsupported = 1; + + return PTR_ERR_OR_ZERO(rtc); +} + +static const struct i2c_device_id pcf85063_id[] = { + { "rtcpcf85063", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcf85063_id); + +static const struct of_device_id pcf85063_of_match[] = { + { .compatible = "nxp,rtcpcf85063" }, + {} +}; +MODULE_DEVICE_TABLE(of, pcf85063_of_match); + +static struct i2c_driver pcf85063_driver = { + .driver = { + .name = "rtcpcf85063", + .of_match_table = of_match_ptr(pcf85063_of_match), + }, + .probe = pcf85063_probe, + .id_table = pcf85063_id, +}; + +module_i2c_driver(pcf85063_driver); + +MODULE_AUTHOR("Sren Andersen "); +MODULE_DESCRIPTION("PCF85063 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/modules/wb_at24.c b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/wb_at24.c new file mode 100755 index 000000000000..069f429f9cea --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/wb_at24.c @@ -0,0 +1,858 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * at24.c - handle most I2C EEPROMs + * + * Copyright (C) 2005-2007 David Brownell + * Copyright (C) 2008 Wolfram Sang, Pengutronix + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Address pointer is 16 bit. */ +#define AT24_FLAG_ADDR16 BIT(7) +/* sysfs-entry will be read-only. */ +#define AT24_FLAG_READONLY BIT(6) +/* sysfs-entry will be world-readable. */ +#define AT24_FLAG_IRUGO BIT(5) +/* Take always 8 addresses (24c00). */ +#define AT24_FLAG_TAKE8ADDR BIT(4) +/* Factory-programmed serial number. */ +#define AT24_FLAG_SERIAL BIT(3) +/* Factory-programmed mac address. */ +#define AT24_FLAG_MAC BIT(2) +/* Does not auto-rollover reads to the next slave address. */ +#define AT24_FLAG_NO_RDROL BIT(1) + +/* + * I2C EEPROMs from most vendors are inexpensive and mostly interchangeable. + * Differences between different vendor product lines (like Atmel AT24C or + * MicroChip 24LC, etc) won't much matter for typical read/write access. + * There are also I2C RAM chips, likewise interchangeable. One example + * would be the PCF8570, which acts like a 24c02 EEPROM (256 bytes). + * + * However, misconfiguration can lose data. "Set 16-bit memory address" + * to a part with 8-bit addressing will overwrite data. Writing with too + * big a page size also loses data. And it's not safe to assume that the + * conventional addresses 0x50..0x57 only hold eeproms; a PCF8563 RTC + * uses 0x51, for just one example. + * + * Accordingly, explicit board-specific configuration data should be used + * in almost all cases. (One partial exception is an SMBus used to access + * "SPD" data for DRAM sticks. Those only use 24c02 EEPROMs.) + * + * So this driver uses "new style" I2C driver binding, expecting to be + * told what devices exist. That may be in arch/X/mach-Y/board-Z.c or + * similar kernel-resident tables; or, configuration data coming from + * a bootloader. + * + * Other than binding model, current differences from "eeprom" driver are + * that this one handles write access and isn't restricted to 24c02 devices. + * It also handles larger devices (32 kbit and up) with two-byte addresses, + * which won't work on pure SMBus systems. + */ + +struct at24_client { + struct i2c_client *client; + struct regmap *regmap; +}; + +struct at24_data { + /* + * Lock protects against activities from other Linux tasks, + * but not from changes by other I2C masters. + */ + struct mutex lock; + + unsigned int write_max; + unsigned int num_addresses; + unsigned int offset_adj; + + u32 byte_len; + u16 page_size; + u8 flags; + + struct nvmem_device *nvmem; + struct regulator *vcc_reg; + void (*read_post)(unsigned int off, char *buf, size_t count); + + /* + * Some chips tie up multiple I2C addresses; dummy devices reserve + * them for us, and we'll use them with SMBus calls. + */ + struct at24_client client[]; +}; + +/* + * This parameter is to help this driver avoid blocking other drivers out + * of I2C for potentially troublesome amounts of time. With a 100 kHz I2C + * clock, one 256 byte read takes about 1/43 second which is excessive; + * but the 1/170 second it takes at 400 kHz may be quite reasonable; and + * at 1 MHz (Fm+) a 1/430 second delay could easily be invisible. + * + * This value is forced to be a power of two so that writes align on pages. + */ +static unsigned int at24_io_limit = 128; +module_param_named(io_limit, at24_io_limit, uint, 0); +MODULE_PARM_DESC(at24_io_limit, "Maximum bytes per I/O (default 128)"); + +/* + * Specs often allow 5 msec for a page write, sometimes 20 msec; + * it's important to recover from write timeouts. + */ +static unsigned int at24_write_timeout = 25; +module_param_named(write_timeout, at24_write_timeout, uint, 0); +MODULE_PARM_DESC(at24_write_timeout, "Time (in ms) to try writes (default 25)"); + +struct at24_chip_data { + u32 byte_len; + u8 flags; + void (*read_post)(unsigned int off, char *buf, size_t count); +}; + +#define AT24_CHIP_DATA(_name, _len, _flags) \ + static const struct at24_chip_data _name = { \ + .byte_len = _len, .flags = _flags, \ + } + +#define AT24_CHIP_DATA_CB(_name, _len, _flags, _read_post) \ + static const struct at24_chip_data _name = { \ + .byte_len = _len, .flags = _flags, \ + .read_post = _read_post, \ + } + +static void at24_read_post_vaio(unsigned int off, char *buf, size_t count) +{ + int i; + + if (capable(CAP_SYS_ADMIN)) + return; + + /* + * Hide VAIO private settings to regular users: + * - BIOS passwords: bytes 0x00 to 0x0f + * - UUID: bytes 0x10 to 0x1f + * - Serial number: 0xc0 to 0xdf + */ + for (i = 0; i < count; i++) { + if ((off + i <= 0x1f) || + (off + i >= 0xc0 && off + i <= 0xdf)) + buf[i] = 0; + } +} + +/* needs 8 addresses as A0-A2 are ignored */ +AT24_CHIP_DATA(at24_data_24c00, 128 / 8, AT24_FLAG_TAKE8ADDR); +/* old variants can't be handled with this generic entry! */ +AT24_CHIP_DATA(at24_data_24c01, 1024 / 8, 0); +AT24_CHIP_DATA(at24_data_24cs01, 16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24c02, 2048 / 8, AT24_FLAG_IRUGO); +AT24_CHIP_DATA(at24_data_24cs02, 16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24mac402, 48 / 8, + AT24_FLAG_MAC | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24mac602, 64 / 8, + AT24_FLAG_MAC | AT24_FLAG_READONLY); +/* spd is a 24c02 in memory DIMMs */ +AT24_CHIP_DATA(at24_data_spd, 2048 / 8, + AT24_FLAG_READONLY | AT24_FLAG_IRUGO); +/* 24c02_vaio is a 24c02 on some Sony laptops */ +AT24_CHIP_DATA_CB(at24_data_24c02_vaio, 2048 / 8, + AT24_FLAG_READONLY | AT24_FLAG_IRUGO, + at24_read_post_vaio); +AT24_CHIP_DATA(at24_data_24c04, 4096 / 8, 0); +AT24_CHIP_DATA(at24_data_24cs04, 16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +/* 24rf08 quirk is handled at i2c-core */ +AT24_CHIP_DATA(at24_data_24c08, 8192 / 8, 0); +AT24_CHIP_DATA(at24_data_24cs08, 16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24c16, 16384 / 8, 0); +AT24_CHIP_DATA(at24_data_24cs16, 16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24c32, 32768 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24cs32, 16, + AT24_FLAG_ADDR16 | AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24c64, 65536 / 8, AT24_FLAG_ADDR16 | AT24_FLAG_IRUGO); +AT24_CHIP_DATA(at24_data_24cs64, 16, + AT24_FLAG_ADDR16 | AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24c128, 131072 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24c256, 262144 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24c512, 524288 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24c1024, 1048576 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24c2048, 2097152 / 8, AT24_FLAG_ADDR16); +/* identical to 24c08 ? */ +AT24_CHIP_DATA(at24_data_INT3499, 8192 / 8, 0); + +static const struct i2c_device_id at24_ids[] = { + { "wb_24c00", (kernel_ulong_t)&at24_data_24c00 }, + { "wb_24c01", (kernel_ulong_t)&at24_data_24c01 }, + { "wb_24cs01", (kernel_ulong_t)&at24_data_24cs01 }, + { "wb_24c02", (kernel_ulong_t)&at24_data_24c02 }, + { "wb_24cs02", (kernel_ulong_t)&at24_data_24cs02 }, + { "wb_24mac402", (kernel_ulong_t)&at24_data_24mac402 }, + { "wb_24mac602", (kernel_ulong_t)&at24_data_24mac602 }, + { "wb_spd", (kernel_ulong_t)&at24_data_spd }, + { "wb_24c02-vaio", (kernel_ulong_t)&at24_data_24c02_vaio }, + { "wb_24c04", (kernel_ulong_t)&at24_data_24c04 }, + { "wb_24cs04", (kernel_ulong_t)&at24_data_24cs04 }, + { "wb_24c08", (kernel_ulong_t)&at24_data_24c08 }, + { "wb_24cs08", (kernel_ulong_t)&at24_data_24cs08 }, + { "wb_24c16", (kernel_ulong_t)&at24_data_24c16 }, + { "wb_24cs16", (kernel_ulong_t)&at24_data_24cs16 }, + { "wb_24c32", (kernel_ulong_t)&at24_data_24c32 }, + { "wb_24cs32", (kernel_ulong_t)&at24_data_24cs32 }, + { "wb_24c64", (kernel_ulong_t)&at24_data_24c64 }, + { "wb_24cs64", (kernel_ulong_t)&at24_data_24cs64 }, + { "wb_24c128", (kernel_ulong_t)&at24_data_24c128 }, + { "wb_24c256", (kernel_ulong_t)&at24_data_24c256 }, + { "wb_24c512", (kernel_ulong_t)&at24_data_24c512 }, + { "wb_24c1024", (kernel_ulong_t)&at24_data_24c1024 }, + { "wb_24c2048", (kernel_ulong_t)&at24_data_24c2048 }, + { "wb_at24", 0 }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(i2c, at24_ids); + +static const struct of_device_id at24_of_match[] = { + { .compatible = "atmel,24c00", .data = &at24_data_24c00 }, + { .compatible = "atmel,24c01", .data = &at24_data_24c01 }, + { .compatible = "atmel,24cs01", .data = &at24_data_24cs01 }, + { .compatible = "atmel,24c02", .data = &at24_data_24c02 }, + { .compatible = "atmel,24cs02", .data = &at24_data_24cs02 }, + { .compatible = "atmel,24mac402", .data = &at24_data_24mac402 }, + { .compatible = "atmel,24mac602", .data = &at24_data_24mac602 }, + { .compatible = "atmel,spd", .data = &at24_data_spd }, + { .compatible = "atmel,24c04", .data = &at24_data_24c04 }, + { .compatible = "atmel,24cs04", .data = &at24_data_24cs04 }, + { .compatible = "atmel,24c08", .data = &at24_data_24c08 }, + { .compatible = "atmel,24cs08", .data = &at24_data_24cs08 }, + { .compatible = "atmel,24c16", .data = &at24_data_24c16 }, + { .compatible = "atmel,24cs16", .data = &at24_data_24cs16 }, + { .compatible = "atmel,24c32", .data = &at24_data_24c32 }, + { .compatible = "atmel,24cs32", .data = &at24_data_24cs32 }, + { .compatible = "atmel,24c64", .data = &at24_data_24c64 }, + { .compatible = "atmel,24cs64", .data = &at24_data_24cs64 }, + { .compatible = "atmel,24c128", .data = &at24_data_24c128 }, + { .compatible = "atmel,24c256", .data = &at24_data_24c256 }, + { .compatible = "atmel,24c512", .data = &at24_data_24c512 }, + { .compatible = "atmel,24c1024", .data = &at24_data_24c1024 }, + { .compatible = "atmel,24c2048", .data = &at24_data_24c2048 }, + { /* END OF LIST */ }, +}; +MODULE_DEVICE_TABLE(of, at24_of_match); + +static const struct acpi_device_id __maybe_unused at24_acpi_ids[] = { + { "INT3499", (kernel_ulong_t)&at24_data_INT3499 }, + { "TPF0001", (kernel_ulong_t)&at24_data_24c1024 }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(acpi, at24_acpi_ids); + +/* + * This routine supports chips which consume multiple I2C addresses. It + * computes the addressing information to be used for a given r/w request. + * Assumes that sanity checks for offset happened at sysfs-layer. + * + * Slave address and byte offset derive from the offset. Always + * set the byte address; on a multi-master board, another master + * may have changed the chip's "current" address pointer. + */ +static struct at24_client *at24_translate_offset(struct at24_data *at24, + unsigned int *offset) +{ + unsigned int i; + + if (at24->flags & AT24_FLAG_ADDR16) { + i = *offset >> 16; + *offset &= 0xffff; + } else { + i = *offset >> 8; + *offset &= 0xff; + } + + return &at24->client[i]; +} + +static struct device *at24_base_client_dev(struct at24_data *at24) +{ + return &at24->client[0].client->dev; +} + +static size_t at24_adjust_read_count(struct at24_data *at24, + unsigned int offset, size_t count) +{ + unsigned int bits; + size_t remainder; + + /* + * In case of multi-address chips that don't rollover reads to + * the next slave address: truncate the count to the slave boundary, + * so that the read never straddles slaves. + */ + if (at24->flags & AT24_FLAG_NO_RDROL) { + bits = (at24->flags & AT24_FLAG_ADDR16) ? 16 : 8; + remainder = BIT(bits) - offset; + if (count > remainder) + count = remainder; + } + + if (count > at24_io_limit) + count = at24_io_limit; + + return count; +} + +static ssize_t at24_regmap_read(struct at24_data *at24, char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, read_time; + struct at24_client *at24_client; + struct i2c_client *client; + struct regmap *regmap; + int ret; + + at24_client = at24_translate_offset(at24, &offset); + regmap = at24_client->regmap; + client = at24_client->client; + count = at24_adjust_read_count(at24, offset, count); + + /* adjust offset for mac and serial read ops */ + offset += at24->offset_adj; + + timeout = jiffies + msecs_to_jiffies(at24_write_timeout); + do { + /* + * The timestamp shall be taken before the actual operation + * to avoid a premature timeout in case of high CPU load. + */ + read_time = jiffies; + + ret = regmap_bulk_read(regmap, offset, buf, count); + dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n", + count, offset, ret, jiffies); + if (!ret) + return count; + + usleep_range(1000, 1500); + } while (time_before(read_time, timeout)); + + return -ETIMEDOUT; +} + +/* + * Note that if the hardware write-protect pin is pulled high, the whole + * chip is normally write protected. But there are plenty of product + * variants here, including OTP fuses and partial chip protect. + * + * We only use page mode writes; the alternative is sloooow. These routines + * write at most one page. + */ + +static size_t at24_adjust_write_count(struct at24_data *at24, + unsigned int offset, size_t count) +{ + unsigned int next_page; + + /* write_max is at most a page */ + if (count > at24->write_max) + count = at24->write_max; + + /* Never roll over backwards, to the start of this page */ + next_page = roundup(offset + 1, at24->page_size); + if (offset + count > next_page) + count = next_page - offset; + + return count; +} + +static ssize_t at24_regmap_write(struct at24_data *at24, const char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, write_time; + struct at24_client *at24_client; + struct i2c_client *client; + struct regmap *regmap; + int ret; + + at24_client = at24_translate_offset(at24, &offset); + regmap = at24_client->regmap; + client = at24_client->client; + count = at24_adjust_write_count(at24, offset, count); + timeout = jiffies + msecs_to_jiffies(at24_write_timeout); + + do { + /* + * The timestamp shall be taken before the actual operation + * to avoid a premature timeout in case of high CPU load. + */ + write_time = jiffies; + + ret = regmap_bulk_write(regmap, offset, buf, count); + dev_dbg(&client->dev, "write %zu@%d --> %d (%ld)\n", + count, offset, ret, jiffies); + if (!ret) + return count; + + usleep_range(1000, 1500); + } while (time_before(write_time, timeout)); + + return -ETIMEDOUT; +} + +static int at24_read(void *priv, unsigned int off, void *val, size_t count) +{ + struct at24_data *at24; + struct device *dev; + char *buf = val; + int i, ret; + + at24 = priv; + dev = at24_base_client_dev(at24); + + if (unlikely(!count)) + return count; + + if (off + count > at24->byte_len) + return -EINVAL; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + return ret; + } + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&at24->lock); + + for (i = 0; count; i += ret, count -= ret) { + ret = at24_regmap_read(at24, buf + i, off + i, count); + if (ret < 0) { + mutex_unlock(&at24->lock); + pm_runtime_put(dev); + return ret; + } + } + + mutex_unlock(&at24->lock); + + pm_runtime_put(dev); + + if (unlikely(at24->read_post)) + at24->read_post(off, buf, i); + + return 0; +} + +static int at24_write(void *priv, unsigned int off, void *val, size_t count) +{ + struct at24_data *at24; + struct device *dev; + char *buf = val; + int ret; + + at24 = priv; + dev = at24_base_client_dev(at24); + + if (unlikely(!count)) + return -EINVAL; + + if (off + count > at24->byte_len) + return -EINVAL; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + return ret; + } + + /* + * Write data to chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&at24->lock); + + while (count) { + ret = at24_regmap_write(at24, buf, off, count); + if (ret < 0) { + mutex_unlock(&at24->lock); + pm_runtime_put(dev); + return ret; + } + buf += ret; + off += ret; + count -= ret; + } + + mutex_unlock(&at24->lock); + + pm_runtime_put(dev); + + return 0; +} + +static const struct at24_chip_data *at24_get_chip_data(struct device *dev) +{ + struct device_node *of_node = dev->of_node; + const struct at24_chip_data *cdata; + const struct i2c_device_id *id; + + id = i2c_match_id(at24_ids, to_i2c_client(dev)); + + /* + * The I2C core allows OF nodes compatibles to match against the + * I2C device ID table as a fallback, so check not only if an OF + * node is present but also if it matches an OF device ID entry. + */ + if (of_node && of_match_device(at24_of_match, dev)) + cdata = of_device_get_match_data(dev); + else if (id) + cdata = (void *)id->driver_data; + else + cdata = acpi_device_get_match_data(dev); + + if (!cdata) + return ERR_PTR(-ENODEV); + + return cdata; +} + +static int at24_make_dummy_client(struct at24_data *at24, unsigned int index, + struct regmap_config *regmap_config) +{ + struct i2c_client *base_client, *dummy_client; + struct regmap *regmap; + struct device *dev; + + base_client = at24->client[0].client; + dev = &base_client->dev; + + dummy_client = devm_i2c_new_dummy_device(dev, base_client->adapter, + base_client->addr + index); + if (IS_ERR(dummy_client)) + return PTR_ERR(dummy_client); + + regmap = devm_regmap_init_i2c(dummy_client, regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + at24->client[index].client = dummy_client; + at24->client[index].regmap = regmap; + + return 0; +} + +static unsigned int at24_get_offset_adj(u8 flags, unsigned int byte_len) +{ + if (flags & AT24_FLAG_MAC) { + /* EUI-48 starts from 0x9a, EUI-64 from 0x98 */ + return 0xa0 - byte_len; + } else if (flags & AT24_FLAG_SERIAL && flags & AT24_FLAG_ADDR16) { + /* + * For 16 bit address pointers, the word address must contain + * a '10' sequence in bits 11 and 10 regardless of the + * intended position of the address pointer. + */ + return 0x0800; + } else if (flags & AT24_FLAG_SERIAL) { + /* + * Otherwise the word address must begin with a '10' sequence, + * regardless of the intended address. + */ + return 0x0080; + } else { + return 0; + } +} + +static int at24_probe(struct i2c_client *client) +{ + struct regmap_config regmap_config = { }; + struct nvmem_config nvmem_config = { }; + u32 byte_len, page_size, flags, addrw; + const struct at24_chip_data *cdata; + struct device *dev = &client->dev; + bool i2c_fn_i2c, i2c_fn_block; + unsigned int i, num_addresses; + struct at24_data *at24; + struct regmap *regmap; + bool writable; + u8 test_byte; + int err; + + i2c_fn_i2c = i2c_check_functionality(client->adapter, I2C_FUNC_I2C); + i2c_fn_block = i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK); + + cdata = at24_get_chip_data(dev); + if (IS_ERR(cdata)) + return PTR_ERR(cdata); + + err = device_property_read_u32(dev, "pagesize", &page_size); + if (err) + /* + * This is slow, but we can't know all eeproms, so we better + * play safe. Specifying custom eeprom-types via device tree + * or properties is recommended anyhow. + */ + page_size = 1; + + flags = cdata->flags; + if (device_property_present(dev, "read-only")) + flags |= AT24_FLAG_READONLY; + if (device_property_present(dev, "no-read-rollover")) + flags |= AT24_FLAG_NO_RDROL; + + err = device_property_read_u32(dev, "address-width", &addrw); + if (!err) { + switch (addrw) { + case 8: + if (flags & AT24_FLAG_ADDR16) + dev_warn(dev, + "Override address width to be 8, while default is 16\n"); + flags &= ~AT24_FLAG_ADDR16; + break; + case 16: + flags |= AT24_FLAG_ADDR16; + break; + default: + dev_warn(dev, "Bad \"address-width\" property: %u\n", + addrw); + } + } + + err = device_property_read_u32(dev, "size", &byte_len); + if (err) + byte_len = cdata->byte_len; + + if (!i2c_fn_i2c && !i2c_fn_block) + page_size = 1; + + if (!page_size) { + dev_err(dev, "page_size must not be 0!\n"); + return -EINVAL; + } + + if (!is_power_of_2(page_size)) + dev_warn(dev, "page_size looks suspicious (no power of 2)!\n"); + + err = device_property_read_u32(dev, "num-addresses", &num_addresses); + if (err) { + if (flags & AT24_FLAG_TAKE8ADDR) + num_addresses = 8; + else + num_addresses = DIV_ROUND_UP(byte_len, + (flags & AT24_FLAG_ADDR16) ? 65536 : 256); + } + + if ((flags & AT24_FLAG_SERIAL) && (flags & AT24_FLAG_MAC)) { + dev_err(dev, + "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC."); + return -EINVAL; + } + + regmap_config.val_bits = 8; + regmap_config.reg_bits = (flags & AT24_FLAG_ADDR16) ? 16 : 8; + regmap_config.disable_locking = true; + + regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + at24 = devm_kzalloc(dev, struct_size(at24, client, num_addresses), + GFP_KERNEL); + if (!at24) + return -ENOMEM; + + mutex_init(&at24->lock); + at24->byte_len = byte_len; + at24->page_size = page_size; + at24->flags = flags; + at24->read_post = cdata->read_post; + at24->num_addresses = num_addresses; + at24->offset_adj = at24_get_offset_adj(flags, byte_len); + at24->client[0].client = client; + at24->client[0].regmap = regmap; + + at24->vcc_reg = devm_regulator_get(dev, "vcc"); + if (IS_ERR(at24->vcc_reg)) + return PTR_ERR(at24->vcc_reg); + + writable = !(flags & AT24_FLAG_READONLY); + if (writable) { + at24->write_max = min_t(unsigned int, + page_size, at24_io_limit); + if (!i2c_fn_i2c && at24->write_max > I2C_SMBUS_BLOCK_MAX) + at24->write_max = I2C_SMBUS_BLOCK_MAX; + } + + /* use dummy devices for multiple-address chips */ + for (i = 1; i < num_addresses; i++) { + err = at24_make_dummy_client(at24, i, ®map_config); + if (err) + return err; + } + + /* + * If the 'label' property is not present for the AT24 EEPROM, + * then nvmem_config.id is initialised to NVMEM_DEVID_AUTO, + * and this will append the 'devid' to the name of the NVMEM + * device. This is purely legacy and the AT24 driver has always + * defaulted to this. However, if the 'label' property is + */ + nvmem_config.id = NVMEM_DEVID_AUTO; + + if (device_property_present(dev, "label")) { + err = device_property_read_string(dev, "label", + &nvmem_config.name); + if (err) + return err; + } else { + nvmem_config.name = dev_name(dev); + } + + nvmem_config.type = NVMEM_TYPE_EEPROM; + nvmem_config.dev = dev; + nvmem_config.read_only = !writable; + nvmem_config.root_only = !(flags & AT24_FLAG_IRUGO); + nvmem_config.owner = THIS_MODULE; + nvmem_config.compat = true; + nvmem_config.base_dev = dev; + nvmem_config.reg_read = at24_read; + nvmem_config.reg_write = at24_write; + nvmem_config.priv = at24; + nvmem_config.stride = 1; + nvmem_config.word_size = 1; + nvmem_config.size = byte_len; + + i2c_set_clientdata(client, at24); + + err = regulator_enable(at24->vcc_reg); + if (err) { + dev_err(dev, "Failed to enable vcc regulator\n"); + return err; + } + + /* enable runtime pm */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + at24->nvmem = devm_nvmem_register(dev, &nvmem_config); + if (IS_ERR(at24->nvmem)) { + pm_runtime_disable(dev); + if (!pm_runtime_status_suspended(dev)) + regulator_disable(at24->vcc_reg); + return PTR_ERR(at24->nvmem); + } + + /* + * Perform a one-byte test read to verify that the + * chip is functional. + */ + err = at24_read(at24, 0, &test_byte, 1); + if (err) { + pm_runtime_disable(dev); + if (!pm_runtime_status_suspended(dev)) + regulator_disable(at24->vcc_reg); + return -ENODEV; + } + + pm_runtime_idle(dev); + + if (writable) + dev_info(dev, "%u byte %s EEPROM, writable, %u bytes/write\n", + byte_len, client->name, at24->write_max); + else + dev_info(dev, "%u byte %s EEPROM, read-only\n", + byte_len, client->name); + + return 0; +} + +static int at24_remove(struct i2c_client *client) +{ + struct at24_data *at24 = i2c_get_clientdata(client); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + regulator_disable(at24->vcc_reg); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +static int __maybe_unused at24_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct at24_data *at24 = i2c_get_clientdata(client); + + return regulator_disable(at24->vcc_reg); +} + +static int __maybe_unused at24_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct at24_data *at24 = i2c_get_clientdata(client); + + return regulator_enable(at24->vcc_reg); +} + +static const struct dev_pm_ops at24_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(at24_suspend, at24_resume, NULL) +}; + +static struct i2c_driver at24_driver = { + .driver = { + .name = "wb_at24", + .pm = &at24_pm_ops, + .of_match_table = at24_of_match, + .acpi_match_table = ACPI_PTR(at24_acpi_ids), + }, + .probe_new = at24_probe, + .remove = at24_remove, + .id_table = at24_ids, +}; + +static int __init at24_init(void) +{ + if (!at24_io_limit) { + pr_err("at24: at24_io_limit must not be 0!\n"); + return -EINVAL; + } + + at24_io_limit = rounddown_pow_of_two(at24_io_limit); + return i2c_add_driver(&at24_driver); +} +module_init(at24_init); + +static void __exit at24_exit(void) +{ + i2c_del_driver(&at24_driver); +} +module_exit(at24_exit); + +MODULE_DESCRIPTION("Driver for most I2C EEPROMs"); +MODULE_AUTHOR("David Brownell and Wolfram Sang"); +MODULE_LICENSE("GPL"); diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/modules/wb_platform.c b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/wb_platform.c new file mode 100644 index 000000000000..c7a976d45471 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/modules/wb_platform.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform.h" + +#define PLATFORM_I2C_RETRY_TIMES 3 + +s32 platform_i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command) +{ + int try; + s32 ret; + + ret = -1; + for (try = 0; try < PLATFORM_I2C_RETRY_TIMES; try++) { + if ((ret = i2c_smbus_read_byte_data(client, command)) >= 0 ) + break; + } + return ret; + +} +EXPORT_SYMBOL(platform_i2c_smbus_read_byte_data); + +s32 platform_i2c_smbus_read_i2c_block_data(const struct i2c_client *client, + u8 command, u8 length, u8 *values) +{ + int try; + s32 ret; + + ret = -1; + for (try = 0; try < PLATFORM_I2C_RETRY_TIMES; try++) { + if ((ret = i2c_smbus_read_i2c_block_data(client, command, length, values)) >= 0 ) + break; + } + return ret; +} +EXPORT_SYMBOL(platform_i2c_smbus_read_i2c_block_data); + +s32 platform_i2c_smbus_read_word_data(const struct i2c_client *client, u8 command) +{ + int try; + s32 ret; + + ret = -1; + for (try = 0; try < PLATFORM_I2C_RETRY_TIMES; try++) { + if ((ret = i2c_smbus_read_word_data(client, command)) >= 0 ) + break; + } + return ret; +} +EXPORT_SYMBOL(platform_i2c_smbus_read_word_data); + +static int __init wb_platform_init(void) +{ + return 0; +} + +static void __exit wb_platform_exit(void) +{ + +} + +module_init(wb_platform_init); +module_exit(wb_platform_exit); + +MODULE_DESCRIPTION("Platform Support"); +MODULE_AUTHOR("support "); +MODULE_LICENSE("GPL"); diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/script/device_i2c.py b/platform/centec-arm64/sonic-platform-modules-micas/common/script/device_i2c.py new file mode 100644 index 000000000000..368b862af4f5 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/script/device_i2c.py @@ -0,0 +1,283 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- +import click +import os +import time +from platform_config import GLOBALCONFIG, GLOBALINITPARAM, GLOBALINITCOMMAND, MAC_LED_RESET, STARTMODULE, i2ccheck_params +from platform_util import wbpciwr, os_system + +CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help']) + +class AliasedGroup(click.Group): + def get_command(self, ctx, cmd_name): + rv = click.Group.get_command(self, ctx, cmd_name) + if rv is not None: + return rv + matches = [x for x in self.list_commands(ctx) + if x.startswith(cmd_name)] + if not matches: + return None + elif len(matches) == 1: + return click.Group.get_command(self, ctx, matches[0]) + ctx.fail('Too many matches: %s' % ', '.join(sorted(matches))) + +def log_os_system(cmd): + u'''execute shell command''' + status, output = os_system(cmd) + if status: + print(output) + return status, output + +def write_sysfs_value(reg_name, value): + u'''write sysfs file''' + mb_reg_file = "/sys/bus/i2c/devices/" + reg_name + if (not os.path.isfile(mb_reg_file)): + print(mb_reg_file, 'not found !') + return False + try: + with open(mb_reg_file, 'w') as fd: + fd.write(value) + except Exception as error: + return False + return True + +def check_driver(): + u'''whether there is driver start with wb''' + status, output = log_os_system("lsmod | grep wb | wc -l") + #System execution error + if status: + return False + if output.isdigit() and int(output) > 0: + return True + else: + return False + +def get_pid(name): + ret = [] + for dirname in os.listdir('/proc'): + if dirname == 'curproc': + continue + try: + with open('/proc/{}/cmdline'.format(dirname), mode='r') as fd: + content = fd.read() + except Exception: + continue + if name in content: + ret.append(dirname) + return ret + +def start_avs_ctrl(): + if STARTMODULE.get("avscontrol", 0) == 1: + cmd = "nohup avscontrol.py start >/dev/null 2>&1 &" + rets = get_pid("avscontrol.py") + if len(rets) == 0: + os.system(cmd) + +def stop_avs_ctrl(): + if STARTMODULE.get('avscontrol', 0) == 1: + rets = get_pid("avscontrol.py") # + for ret in rets: + cmd = "kill "+ ret + os.system(cmd) + +def start_fan_ctrl(): + if STARTMODULE.get('fancontrol', 0) == 1: + cmd = "nohup fancontrol.py start >/dev/null 2>&1 &" + rets = get_pid("fancontrol.py") + if len(rets) == 0: + os.system(cmd) + +def stop_fan_ctrl(): + u'''disable fan timer service''' + if STARTMODULE.get('fancontrol', 0) == 1: + rets = get_pid("fancontrol.py") # + for ret in rets: + cmd = "kill "+ ret + os.system(cmd) + +def rm_dev(bus, loc): + cmd = "echo 0x%02x > /sys/bus/i2c/devices/i2c-%d/delete_device" % (loc, bus) + devpath = "/sys/bus/i2c/devices/%d-%04x"%(bus, loc) + if os.path.exists(devpath): + log_os_system(cmd) + +def add_dev(name, bus, loc): + if name == "lm75": + time.sleep(0.1) + pdevpath = "/sys/bus/i2c/devices/i2c-%d/" % (bus) + for i in range(1, 100):#wait for mother-bus generation, maximum wait time is 10s + if os.path.exists(pdevpath) == True: + break + time.sleep(0.1) + if i % 10 == 0: + click.echo("%%DEVICE_I2C-INIT: %s not found, wait 0.1 second ! i %d " % (pdevpath,i)) + + cmd = "echo %s 0x%02x > /sys/bus/i2c/devices/i2c-%d/new_device" % (name, loc, bus) + devpath = "/sys/bus/i2c/devices/%d-%04x"%(bus, loc) + if os.path.exists(devpath) == False: + os.system(cmd) + +def removedevs(): + devs = GLOBALCONFIG["DEVS"] + for index in range(len(devs)-1, -1, -1 ): + rm_dev(devs[index]["bus"] , devs[index]["loc"]) + +def adddevs(): + devs = GLOBALCONFIG["DEVS"] + for dev in range(0, devs.__len__()): + add_dev(devs[dev]["name"], devs[dev]["bus"] , devs[dev]["loc"]) + +def checksignaldriver(name): + modisexistcmd = "lsmod | grep %s | wc -l" % name + status, output = log_os_system(modisexistcmd) + #System execution error + if status: + return False + if output.isdigit() and int(output) > 0: + return True + else: + return False + +def adddriver(name, delay): + cmd = "modprobe %s" % name + if delay != 0: + time.sleep(delay) + if checksignaldriver(name) != True: + log_os_system(cmd) + +def removedriver(name, delay): + realname = name.lstrip().split(" ")[0]; + cmd = "rmmod -f %s" % realname + if checksignaldriver(realname): + log_os_system(cmd) + +def removedrivers(): + u'''remove all drivers''' + if GLOBALCONFIG is None: + click.echo("%%DEVICE_I2C-INIT: load global config failed.") + return + drivers = GLOBALCONFIG.get("DRIVERLISTS", None) + if drivers is None: + click.echo("%%DEVICE_I2C-INIT: load driver list failed.") + return + for index in range(len(drivers)-1, -1, -1 ): + delay = 0 + name = "" + if type(drivers[index]) == dict and "delay" in drivers[index]: + name = drivers[index].get("name") + delay = drivers[index]["delay"] + else: + name = drivers[index] + removedriver(name, delay) + +def adddrivers(): + u'''add drivers''' + if GLOBALCONFIG is None: + click.echo("%%DEVICE_I2C-INIT: load global config failed.") + return + drivers = GLOBALCONFIG.get("DRIVERLISTS", None) + if drivers is None: + click.echo("%%DEVICE_I2C-INIT: load driver list failed.") + return + for index in range(0 ,len(drivers)): + delay = 0 + name = "" + if type(drivers[index]) == dict and "delay" in drivers[index]: + name = drivers[index].get("name") + delay = drivers[index]["delay"] + else: + name = drivers[index] + adddriver(name, delay) + +def otherinit(): + for index in GLOBALINITPARAM: + delay = index.get('delay',0) + if delay !=0 : + time.sleep(1) + write_sysfs_value(index["loc"], index["value"]) + + for index in GLOBALINITCOMMAND: + log_os_system(index) + +def unload_driver(): + u'''remove devices and drivers''' + stop_avs_ctrl() # disable avs-control + stop_fan_ctrl() # disable fan-control service + removedevs() # remove other devices + removedrivers() # remove drivers + +def reload_driver(): + u'''reload devices and drivers''' + removedevs() # remove other devices + removedrivers() # remove drivers + time.sleep(1) + adddrivers() + adddevs() + + +def i2c_check(bus,retrytime = 6): + try: + i2cpath = "/sys/bus/i2c/devices/" + bus + while retrytime and not os.path.exists(i2cpath): + click.echo("%%DEVICE_I2C-HA: i2c bus abnormal, last bus %s is not exist." % i2cpath) + reload_driver() + retrytime -= 1 + time.sleep(1) + except Exception as e: + click.echo("%%DEVICE_I2C-HA: %s" % str(e)) + return + +def set_mac_leds(data): + '''write pci register''' + pcibus = MAC_LED_RESET.get("pcibus") + slot = MAC_LED_RESET.get("slot") + fn = MAC_LED_RESET.get("fn") + bar = MAC_LED_RESET.get("bar") + offset = MAC_LED_RESET.get("offset") + val = MAC_LED_RESET.get(data, None) + if val is None: + click.echo("%%DEVICE_I2C-INIT: set_mac_leds wrong input") + return + wbpciwr(pcibus, slot, fn, bar, offset, val) + +def load_driver(): + u'''load devices and drivers''' + adddrivers() + adddevs() + if STARTMODULE.get("i2ccheck",0) == 1: #i2c HA + busend = i2ccheck_params.get("busend") + retrytime = i2ccheck_params.get("retrytime") + i2c_check(busend,retrytime) + start_fan_ctrl() # enable fan + start_avs_ctrl() # avs voltage-adjustment + otherinit(); # other initialization, QSFP initialization + if STARTMODULE.get("macledreset", 0) == 1: + set_mac_leds("reset") + +@click.group(cls=AliasedGroup, context_settings=CONTEXT_SETTINGS) +def main(): + '''device operator''' + pass + + +@main.command() +def start(): + '''load device ''' + if check_driver(): + unload_driver() + load_driver() + +@main.command() +def stop(): + '''stop device ''' + unload_driver() + +@main.command() +def restart(): + '''restart device''' + unload_driver() + load_driver() + +if __name__ == '__main__': + u'''device_i2c operation''' + main() diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/script/fancontrol.py b/platform/centec-arm64/sonic-platform-modules-micas/common/script/fancontrol.py new file mode 100755 index 000000000000..dbd2767abca9 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/script/fancontrol.py @@ -0,0 +1,495 @@ +#!/usr/bin/env python3.9 +# -*- coding: UTF-8 -*- +import click +import os +import time +import json +import traceback +from interface import Interface +import logging.handlers +from platform_util import CompressedRotatingFileHandler + +CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help']) + +FILE_NAME = "/var/log/fancontrol.log" +MAX_LOG_BYTES = 20*1024*1024 +BACKUP_COUNT = 9 + +logger = logging.getLogger("fancontrol") +logger.setLevel(logging.DEBUG) +fanctrl_log = CompressedRotatingFileHandler(FILE_NAME, mode='a', maxBytes=MAX_LOG_BYTES, backupCount=BACKUP_COUNT, encoding=None, delay=0) +formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s") +fanctrl_log.setFormatter(formatter) +logger.addHandler(fanctrl_log) + +DEBUG_FILE = "/etc/fancontrol_debug" +FAN_CTRL_CFG_FILE = "/usr/local/bin/fan_ctrl_cfg.json" +KEY_THERMAL = "Thermal" +KEY_FAN = "Fans" +KEY_PID = "PID" +KEY_OPEN_LOOP = "OpenLoop" +KEY_DEVICE = "Device" +KEY_FAN_ERROR = "FanError" + +KEY_FANERROR_PWM_MAX = "Fan_Pwmmax" + +KEY_INLET_TEMP = "INLET_TEMP" +KEY_OUTLET_TEMP = "OUTLET_TEMP" +KEY_SWITCH_TEMP = "SWITCH_TEMP" +KEY_TEMP = [KEY_INLET_TEMP, KEY_OUTLET_TEMP, KEY_SWITCH_TEMP] + +KEY_PID_PWM_MAX = "Pwm_Max" +KEY_PID_PWM_MIN = "Pwm_Min" +KEY_PID_SETPOINT = "SetPoint" +KEY_PID_P = "P" +KEY_PID_I = "I" +KEY_PID_D = "D" +KEY_PID_TEMP_MIN = "Temp_Min" +KEY_PID_TEMP_MAX = "Temp_Max" + +KEY_OPENLOOP_A = "a" +KEY_OPENLOOP_B = "b" +KEY_OPENLOOP_C = "c" +KEY_OPENLOOP_FIXUP = "fix_up" +KEY_OPENLOOP_PWM_MAX = "pwmMax" +KEY_OPENLOOP_PWM_MIN = "pwmMin" +KEY_OPENLOOP_TEMP_MIN = "tempMin" + +STATUS_HIGH_CRIT = 1 +STATUS_MISS_CRIT = 2 +STATUS_BAD_FAN = 4 +STATUS_LOW_FAN = 8 +STATUS_MISS_ERR = 16 + +class AliasedGroup(click.Group): + def get_command(self, ctx, cmd_name): + rv = click.Group.get_command(self, ctx, cmd_name) + if rv is not None: + return rv + matches = [x for x in self.list_commands(ctx) + if x.startswith(cmd_name)] + if not matches: + return None + elif len(matches) == 1: + return click.Group.get_command(self, ctx, matches[0]) + ctx.fail('Too many matches: %s' % ', '.join(sorted(matches))) + +def fanctrl_debug_log(s): + # s = s.decode('utf-8').encode('gb2312') + if os.path.isfile(DEBUG_FILE): + logger.debug(s) + +class OpenLoop(): + def __init__(self): + self.a = 0 + self.b = 0 + self.c = 0 + self.fix_up = 0 + self.pwmMax = 0 + self.pwmMin = 0 + self.temp = 0 + self.tempMin = 0 + + def calcPwm(self): + if self.temp < self.tempMin: + return self.pwmMin + pwm = self.a * self.temp * self.temp + self.b * self.temp + self.c + pwm = (pwm / 2.56) + pwm = min(pwm, self.pwmMax) + pwm = max(pwm, self.pwmMin) + return pwm + + +class FanPid(): + def __init__(self): + self.pwmMin = 30 + self.pwmMax = 100 + self.SetPoint = 89 + self.D = 0.196 + self.I = 0.196 + self.P = 1.176 + self.tempMin = 28.0 + self.tempMax = 45.0 + self.pwms = [30, 30] + self.temps = [-1, -1, -1] + self.last_temp = -1 + self.sensor = KEY_SWITCH_TEMP + + def calcPwm(self): + temp_delta = self.temps[2] - self.last_temp + temp_sp_delta = self.temps[2] - self.SetPoint + temp_last_delta = ((self.temps[2] - self.temps[1]) - (self.temps[1] - self.temps[0])) + + delta_pwm = self.P * temp_delta + self.I * temp_sp_delta + self.D * temp_last_delta + + self.temps[0] = self.temps[1] + self.temps[1] = self.temps[2] + self.last_temp = self.temps[2] + + self.pwms[0] = self.pwms[1] + self.pwms[1] = self.pwms[0] + delta_pwm + self.pwms[1] = max(self.pwms[1], self.pwmMin) + self.pwms[1] = min(self.pwms[1], self.pwmMax) + + return self.pwms[1] + +class FanControl(): + + def __init__(self): + self.status = 0 + self.fan_status = 0 + self.error_time = 0 + self.low_time = 0 + self.fan_pwm = 40 + self.fanerr_pwmmax = 0 + self.interface = Interface() + self.temps = {} + self.tempsMax = {} + self.tempsMin = {} + self.tempStatus = {} + self.tempCritTime = {} + self.tempMissTime = {} + + self.fans = {} + self.fanStatus = {} + self.fanErrTime = {} + self.fanLowTime = {} + + self.fanPid = None + self.openloop = None + + self.isBuildin = 0 + self.isLiquid = 0 + self.eeproms = {} + self.airflow = "" + self.pid_switch = 1 + self.openloop_switch = 1 + + def doGetAirFlow(self): + if self.isLiquid == 1: + return "Liquid" + # PSU and Fan is buildin using Tlve2 + if self.isBuildin == 1: + productName = self.interface.get_productname() + if productName != "": + tmp = productName.split("-") + fanairflow = tmp[-1] + if fanairflow == "R": + return "exhaust" + else: + return "intake" + elif self.isBuildin == 0: + return self.interface.get_airflow() + + return "" + + def doFanCtrlInit(self): + if os.path.isfile(FAN_CTRL_CFG_FILE): + fh = open(FAN_CTRL_CFG_FILE) + if not fh: + logger.error("Config file %s doesn't exist" % FAN_CTRL_CFG_FILE) + return False + cfg_json = json.load(fh) + if not cfg_json: + logger.error('Load config file %s failed' % FAN_CTRL_CFG_FILE) + fh.close() + return False + + cfg_keys = [KEY_THERMAL, KEY_FAN, KEY_PID, KEY_OPEN_LOOP, KEY_DEVICE, KEY_FAN_ERROR] + for key in cfg_keys: + if key not in cfg_json: + logger.error('Key %s not present in cfg file' % key) + return False + thermal_json = cfg_json[KEY_THERMAL] + fan_json = cfg_json[KEY_FAN] + pid_json = cfg_json[KEY_PID] + openloop_json = cfg_json[KEY_OPEN_LOOP] + device_json = cfg_json[KEY_DEVICE] + fan_error_json = cfg_json[KEY_FAN_ERROR] + + #init fanerror + self.fanerr_pwmmax = fan_error_json["Fan_Pwmmax"] + + # Get Airflow + self.isBuildin = device_json["Buildin"] + self.isLiquid = device_json["Liquid"] + self.airflow = self.doGetAirFlow() + if self.airflow == "": + logger.warning("Cannot get airflow from device!") + self.pid_switch = device_json["PID"] + self.openloop_switch = device_json["OpenLoop"] + if self.pid_switch == 0 and self.openloop_switch == 0: + logger.warning("No PID and OpenLoop found!") + # Init openloop + self.openloop = OpenLoop() + self.openloop.a = openloop_json[KEY_OPENLOOP_A] + self.openloop.b = openloop_json[KEY_OPENLOOP_B] + self.openloop.c = openloop_json[KEY_OPENLOOP_C] + self.openloop.fix_up = openloop_json[KEY_OPENLOOP_FIXUP] + self.openloop.pwmMax = openloop_json[KEY_OPENLOOP_PWM_MAX] + self.openloop.pwmMin = openloop_json[KEY_OPENLOOP_PWM_MIN] + self.openloop.tempMin = openloop_json[KEY_OPENLOOP_TEMP_MIN] + # Init PID + self.fanPid = FanPid() + self.fanPid.pwmMax = pid_json[KEY_PID_PWM_MAX] + self.fanPid.pwmMin = pid_json[KEY_PID_PWM_MIN] + self.fanPid.SetPoint = pid_json[KEY_PID_SETPOINT] + self.fanPid.P = pid_json[KEY_PID_P] + self.fanPid.I = pid_json[KEY_PID_I] + self.fanPid.D = pid_json[KEY_PID_D] + self.fanPid.tempMin = pid_json[KEY_PID_TEMP_MIN] + self.fanPid.tempMax = pid_json[KEY_PID_TEMP_MAX] + # Init thermal setting + for key, item in list(thermal_json.items()): + fanctrl_debug_log("%s %s " % (key,item)) + if key not in KEY_TEMP: + logger.error('Key %s not present in cfg file' % key) + return False + self.temps[item] = -1.0 + self.tempsMax[item] = self.interface.get_thermal_temp_max(item) + self.tempsMin[item] = self.interface.get_thermal_temp_min(item) + self.tempStatus[item] = 0 + self.tempMissTime[item] = [0, 0] + self.tempCritTime[item] = [0, 0] + if key == self.fanPid.sensor: + self.fanPid.sensor = item + + # Init fans setting + for key, item in list(fan_json.items()): + self.fans[key] = item + self.fanStatus[key] = 0 + self.fanErrTime[key] = [0, 0] + self.fanLowTime[key] = [0, 0] + fh.close() + else: + logger.error('%s is not a file' % FAN_CTRL_CFG_FILE) + return False + + fanctrl_debug_log("Device AirFlow: %s" % (self.airflow)) + self.updateThermal() + self.fanPid.last_temp = self.temps[self.fanPid.sensor] + for i in range(3): + self.fanPid.temps[i] = self.temps[self.fanPid.sensor] + return True + + def setFanSpeed(self, speed): + return self.interface.set_fan_speed_pwm(speed) + + def updateThermal(self): + for key in self.temps: + self.temps[key] = self.interface.get_thermal_temp(key) + fanctrl_debug_log("%s temps %d C" % (key, self.temps[key])) + + if self.temps[KEY_INLET_TEMP] >= self.tempsMax[KEY_INLET_TEMP] or self.temps[KEY_INLET_TEMP] <= -99999: + self.temps[KEY_INLET_TEMP] = self.tempsMax[KEY_INLET_TEMP] + self.openloop.temp = self.temps[KEY_INLET_TEMP] + self.fanPid.temps[2] = self.temps[KEY_INLET_TEMP] + + def checkThermal(self): + thermal_cnt = 0 + for key in self.temps: + if self.temps[key] <= -9999: + if self.tempStatus[key] & STATUS_MISS_CRIT != 0: + self.tempMissTime[key][0] = time.time() + else: + self.tempMissTime[key][1] = time.time() + self.tempStatus[key] = self.tempStatus[key] | STATUS_MISS_CRIT + if self.tempMissTime[key][1] - self.tempMissTime[key][0] > 15: + logger.warning("%s Read Invaild Temperautre %d " % (key, self.temps[key])) + self.tempStatus[key] = self.tempStatus[key] | STATUS_MISS_ERR + else: + self.tempStatus[key] = self.tempStatus[key] & ~(STATUS_MISS_CRIT | STATUS_MISS_ERR) + + if self.temps[key] >= self.tempsMax[key]: + self.tempCritTime[key][0] = time.time() + self.tempStatus[key] = self.tempStatus[key] | STATUS_HIGH_CRIT + logger.warning("%s Temperautre %d >= High Threshold %d" % (key, self.temps[key], self.tempsMax[key])) + elif self.tempStatus[key] & (STATUS_HIGH_CRIT) != 0: + self.tempCritTime[key][1] = time.time() + logger.warning("%s Temperautre %d Recovery" % (key, self.temps[key])) + if self.tempCritTime[key][1] - self.tempCritTime[key][0] > 300: + self.tempStatus[key] = self.tempStatus[key] & ~(STATUS_HIGH_CRIT) + + if self.tempStatus[key] & ~(STATUS_MISS_CRIT) != 0: + thermal_cnt = thermal_cnt + 1 + + self.status = thermal_cnt + fanctrl_debug_log("Thermal error num %d" % self.status) + + def checkFanSpeed(self): + fan_error_cnt = 0 + for key, item in list(self.fans.items()): + speed = self.interface.get_fan_speed_rpm(item) + rpm_max = self.interface.get_fan_rpm_max() + if self.fan_pwm == 100 and speed < rpm_max * 0.7: + if self.fanStatus[key] & STATUS_BAD_FAN != 0: + self.fanErrTime[key][0] = time.time() + else: + self.fanErrTime[key][1] = time.time() + + self.fanStatus[key] = self.fanStatus[key] | STATUS_BAD_FAN + if self.fanErrTime[key][1] - self.fanErrTime[key][0] >= 30: + logger.warning("%s PWM is %d but Speed %d <= %d RPM" % (item, self.fan_pwm, speed, rpm_max * 0.7)) + else: + self.fanStatus[key] = self.fanStatus[key] & ~(STATUS_BAD_FAN) + + if speed < 1000: + if self.fanStatus[key] & STATUS_LOW_FAN != 0: + self.fanLowTime[key][0] = time.time() + else: + self.fanLowTime[key][1] = time.time() + self.fanStatus[key] = self.fanStatus[key] | STATUS_LOW_FAN + if self.fanLowTime[key][1] - self.fanLowTime[key][0] > 30: + logger.warning("%s Speed %d <= %d RPM" % (item, speed, 1000)) + else: + self.fanStatus[key] = self.fanStatus[key] & ~(STATUS_LOW_FAN) + + fanctrl_debug_log("%s speed %d RPM" % (key, speed)) + + if self.fanStatus[key] != 0: + fan_error_cnt = fan_error_cnt + 1 + self.fan_status = fan_error_cnt + fanctrl_debug_log("Fan error num %d" % fan_error_cnt) + + def doApplyPolicy(self): + if self.isLiquid == 1: + return + + self.fan_pwm = 0 + if self.openloop_switch == 1: + openloop_pwm = int(self.openloop.calcPwm()) + fanctrl_debug_log("OpenLoop pwm %d" % (openloop_pwm)) + self.fan_pwm = max(self.fan_pwm, openloop_pwm) + + if self.pid_switch == 1: + pid_pwm = int(self.fanPid.calcPwm()) + fanctrl_debug_log("PID pwm %d" % (pid_pwm)) + self.fan_pwm = max(self.fan_pwm, pid_pwm) + + # Check fan presence + if self.interface.get_fan_presence() == False: + logger.warning("Fan presence check false, set fan pwm to 100") + self.fan_pwm = self.fanerr_pwmmax + if self.fan_status != 0 or self.status != 0: + self.fan_pwm = self.fanerr_pwmmax + fanctrl_debug_log("Fan Speed set to %d pwm" % self.fan_pwm) + for i in range(3): + ret = self.setFanSpeed(self.fan_pwm) + if ret == False: + logger.warning("Fan speed set %d pwm failed, retry %d times" % (self.fan_pwm, i + 1)) + continue + break + + def doBoardFanLedCtrl(self): + if self.interface.get_fan_status() != False and self.interface.get_fan_presence() != False and self.fan_status == 0: + fanctrl_debug_log("Fan status good setting LED to green") + self.interface.set_fan_board_led("green") + else: + fanctrl_debug_log("Fan status error setting LED to red") + self.interface.set_fan_board_led("red") + + def doBoardPsuLedCtrl(self): + if self.interface.get_psu_status() != False: + fanctrl_debug_log("PSU status good setting LED to green") + self.interface.set_psu_board_led("green") + else: + fanctrl_debug_log("PSU status error setting LED to red") + self.interface.set_psu_board_led("red") + + def doSysLedCtrl(self): + self.interface.set_sysled("green") + +def run(interval, fanCtrol): + loop = 0 + # waitForDocker() + fanCtrol.setFanSpeed(fanCtrol.fan_pwm) #init set fan speed to 50 pwm + fanCtrol.doFanCtrlInit() + if fanCtrol.airflow == "Liquid": + logger.warning('Liquid device stopping fancontrol') + return True + while True: + try: + if loop % 5 == 0: # Fan speed control + try: + fanCtrol.updateThermal() + except Exception as e: + logger.error('Failed: Update Thermal, %s' % str(e)) + logger.error('%s' % traceback.format_exc()) + time.sleep(3) + continue + + try: + fanCtrol.checkThermal() + except Exception as e: + logger.error('Failed: Check Thermal, %s' % str(e)) + logger.error('%s' % traceback.format_exc()) + time.sleep(3) + continue + + try: + fanCtrol.checkFanSpeed() + except Exception as e: + logger.error('Failed: Check Fan Speed, %s' % str(e)) + logger.error('%s' % traceback.format_exc()) + time.sleep(3) + continue + + try: + fanCtrol.doApplyPolicy() + except Exception as e: + logger.error('Failed: Apply Policy, %s' % str(e)) + logger.error('%s' % traceback.format_exc()) + time.sleep(3) + continue + + try: + fanCtrol.doBoardFanLedCtrl() + except Exception as e: + logger.error('Failed: Led Control, %s' % str(e)) + logger.error('%s' % traceback.format_exc()) + time.sleep(3) + continue + + try: + fanCtrol.doBoardPsuLedCtrl() + except Exception as e: + logger.error('Failed: Led Control, %s' % str(e)) + logger.error('%s' % traceback.format_exc()) + time.sleep(3) + continue + + try: + fanCtrol.doSysLedCtrl() + except Exception as e: + logger.error('Failed: Led Control, %s' % str(e)) + logger.error('%s' % traceback.format_exc()) + time.sleep(3) + continue + + time.sleep(interval) + loop += interval + except Exception as e: + traceback.print_exc() + logger.error(str(e)) + +@click.group(cls=AliasedGroup, context_settings=CONTEXT_SETTINGS) +def main(): + '''device operator''' + pass + +@main.command() +def start(): + '''start fan control''' + logger.info("FANCTROL start") + fanCtrol = FanControl() + interval = 1 + run(interval, fanCtrol) + +@main.command() +def stop(): + '''stop fan control ''' + logger.info("FANCTROL stop") + +# device_i2c operation +if __name__ == '__main__': + main() diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/script/interface.py b/platform/centec-arm64/sonic-platform-modules-micas/common/script/interface.py new file mode 100644 index 000000000000..140e09ec084b --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/script/interface.py @@ -0,0 +1,77 @@ +from sonic_platform.chassis import Chassis + +class Interface(): + def __init__(self): + self.chassis = Chassis() + self.fan_list = self.chassis._fan_list + self.thermal_list = self.chassis._thermal_list + self.psu_list = self.chassis._psu_list + # Device + def get_productname(self): + return self.chassis.get_name() + + def set_sysled(self, color): + return self.thermal_list[0].set_sys_led(color) + + # Thermal + def get_thermal_temp_max(self, name): + for thermal in self.thermal_list: + if name == thermal.get_real_name(): + return thermal.get_high_threshold() + + def get_thermal_temp_min(self, name): + for thermal in self.thermal_list: + if name == thermal.get_real_name(): + return thermal.get_low_threshold() + + def get_thermal_temp(self, name): + for thermal in self.thermal_list: + if name == thermal.get_real_name(): + return thermal.get_temperature() + # Fans + def set_fan_speed_pwm(self, speed): + return self.fan_list[0].set_speed_pwm(speed) + + def get_fan_status(self): + for fan in self.fan_list: + if fan.get_status() == False: + return False + return True + + def get_fan_presence(self): + for fan in self.fan_list: + if fan.get_presence() == False: + return False + return True + + def set_fan_board_led(self, color): + return self.fan_list[0].set_status_led(color) + + def get_fan_speed_rpm(self, name): + for fan in self.fan_list: + if name == fan.get_name(): + return fan.get_speed_rpm() + return 0 + + def get_fan_rpm_max(self): + return self.fan_list[0].get_high_critical_threshold() + + def get_airflow(self): + tmp1 = self.fan_list[0].get_direction() + tmp2 = self.fan_list[0].get_direction() + for fan in self.fan_list: + tmp1 = fan.get_direction() + if tmp1 != tmp2: + return "F2B" + tmp2 = tmp1 + return tmp2 + # Psus + def get_psu_status(self): + for psu in self.psu_list: + if psu.get_powergood_status == False: + return False + return True + + def set_psu_board_led(self, color): + return self.psu_list[0].set_status_led(color) + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/script/platform_common.py b/platform/centec-arm64/sonic-platform-modules-micas/common/script/platform_common.py new file mode 100644 index 000000000000..201a601820a4 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/script/platform_common.py @@ -0,0 +1,1313 @@ +# -*- coding: UTF-8 -*- +# ------------------------------------------------------------------------------- +# Name: PLATFORM python common module +# Purpose: called by other modules +# +# Author: support +# +# Created: 02/07/2018 +# Copyright: (c) rd 2018 +# ------------------------------------------------------------------------------- + +################################driver-load-adaption####################################################### +# need to export interface +################################################################################################### + +__all__ = [ + "fancontrol_loc", + "fancontrol_config_loc", + "GLOBALCONFIG", + "MONITOR_CONST", + "PLATFORM_PART_NUMBER", + "PLATFORM_LABEL_REVISION", + "PLATFORM_ONIE_VERSION", + "PLATFORM_MAC_SIZE", + "PLATFORM_MANUF_NAME", + "PLATFORM_MANUF_COUNTRY", + "PLATFORM_VENDOR_NAME", + "PLATFORM_DIAG_VERSION", + "PLATFORM_SERVICE_TAG", + "DEV_LEDS", + "MEM_SLOTS", + "LOCAL_LED_CONTROL", + "FIRMWARE_TOOLS", + "STARTMODULE", + "i2ccheck_params", + "FANS_DEF", + "factest_module", + "MONITOR_TEMP_MIN", + "MONITOR_K", + "MONITOR_MAC_IN", + "MONITOR_DEFAULT_SPEED", + "MONITOR_MAX_SPEED", + "MONITOR_MIN_SPEED", + "MONITOR_MAC_ERROR_SPEED", + "MONITOR_FAN_TOTAL_NUM", + "MONITOR_MAC_UP_TEMP", + "MONITOR_MAC_LOWER_TEMP", + "MONITOR_MAC_MAX_TEMP", + "MONITOR_FALL_TEMP", + "MONITOR_MAC_WARNING_THRESHOLD", + "MONITOR_OUTTEMP_WARNING_THRESHOLD", + "MONITOR_BOARDTEMP_WARNING_THRESHOLD", + "MONITOR_CPUTEMP_WARNING_THRESHOLD", + "MONITOR_INTEMP_WARNING_THRESHOLD", + "MONITOR_MAC_CRITICAL_THRESHOLD", + "MONITOR_OUTTEMP_CRITICAL_THRESHOLD", + "MONITOR_BOARDTEMP_CRITICAL_THRESHOLD", + "MONITOR_CPUTEMP_CRITICAL_THRESHOLD", + "MONITOR_INTEMP_CRITICAL_THRESHOLD", + "MONITOR_CRITICAL_NUM", + "MONITOR_SHAKE_TIME", + "MONITOR_INTERVAL", + "MONITOR_MAC_SOURCE_SYSFS", + "MONITOR_MAC_SOURCE_PATH", + "MAC_AVS_PARAM", + "MAC_DEFAULT_PARAM", + "MONITOR_SYS_LED", + "MONITOR_SYS_FAN_LED", + "MONITOR_FANS_LED", + "MONITOR_SYS_PSU_LED", + "MONITOR_FAN_STATUS", + "MONITOR_PSU_STATUS", + "MONITOR_DEV_STATUS", + "MONITOR_DEV_STATUS_DECODE", + "DEV_MONITOR_PARAM", + "SLOT_MONITOR_PARAM", + "fanloc", + "PCA9548START", + "PCA9548BUSEND", + "PLATFORM_CARDID", + "PLATFORM_PRODUCTNAME", + "FAN_PROTECT", + "wb_eeprom", + "E2_LOC", + "E2_PROTECT", + "MAC_LED_RESET", + "INIT_PARAM", + "INIT_COMMAND", + "CPLDVERSIONS", + "DRIVERLISTS", + "DEVICE", + "E2TYPE", + "FRULISTS", + "fanlevel_6510", + "fanlevel_6520", + "fanlevel", + "TEMPIDCHANGE", + "FACTESTMODULE", + "item1", + "test_sys_reload_item", + "test_sys_item", + "test_temp_item", + "test_mem_item", + "test_hd_item", + "test_rtc_item", + "test_i2c_item", + "test_cpld_item", + "test_portframe_item", + "test_sysled_item", + "test_fan_item", + "test_power_item", + "test_usb_item", + "test_prbs_item", + "test_portbroadcast_item", + "test_debug_level", + "test_log_level", + "test_setmac", + "test_setrtc", + "log_level_critical", + "log_level_debug", + "log_level_error", + "log_level_info", + "log_level_notset", + "log_level_warning", + "test_e2_setmac_item", + "test_bmc_setmac_item", + "test_fan_setmac_item", + "alltest", + "looptest", + "menuList", + "TESTCASE", + "PCIe_DEV_LIST", + "PCIe_SPEED_ITEM", +] + +fancontrol_loc = "/usr/local/bin" +fancontrol_config_loc = "/usr/local/bin" + +GLOBALCONFIG = "GLOBALCONFIG" +MONITOR_CONST = "MONITOR_CONST" + +PLATFORM_CARDID = 0x00004065 +PLATFORM_PRODUCTNAME = "M2-W6010-48GT4X" +PLATFORM_PART_NUMBER = "01016994" +PLATFORM_LABEL_REVISION = "R01" +PLATFORM_ONIE_VERSION = "2018.05-rc1" +PLATFORM_MAC_SIZE = 3 +PLATFORM_MANUF_NAME = "Micas" +PLATFORM_MANUF_COUNTRY = "USA" +PLATFORM_VENDOR_NAME = "Micas" +PLATFORM_DIAG_VERSION = "0.1.0.15" +PLATFORM_SERVICE_TAG = "www.micasnetworks.com" + +DEV_LEDS = {} +MEM_SLOTS = [] + +LOCAL_LED_CONTROL = {"CLOSE": {}, "OPEN": {}} + +FIRMWARE_TOOLS = {} +# start-up module +STARTMODULE = {"fancontrol": 1, "avscontrol": 1} + +i2ccheck_params = {"busend": "i2c-66", "retrytime": 6} + +################################################################################################### +##### fan board ID reference +################################################################################################### +FANS_DEF = { + 0x8100: "M6500-FAN-F", + 0x8101: "M6510-FAN-F", + 0x8102: "M6520-FAN-F", + 0x8103: "M6510-FAN-R", +} + +factest_module = { + "sysinfo_showfanmsg": 1, + "sysinfo_showPsumsg": 1, + "sysinfo_showrestfanmsg": 0, + "sysinfo_showrestpsumsg": 0, +} + +#################fan adjustment parameters ############################## +MONITOR_TEMP_MIN = 38 # temperature before speed-adjustment +MONITOR_K = 11 # adjustment algorithm +MONITOR_MAC_IN = 35 # temperature difference between mac and chip(backup) +MONITOR_DEFAULT_SPEED = 0x60 # default speed +MONITOR_MAX_SPEED = 0xFF # maximum speed +MONITOR_MIN_SPEED = 0x33 # minimum speed +MONITOR_MAC_ERROR_SPEED = 0xBB # MAC abnormal speed +MONITOR_FAN_TOTAL_NUM = 4 # 3+1 redundancy design, report to syslog if there is a error +MONITOR_MAC_UP_TEMP = 50 # MAC compared with inlet up +MONITOR_MAC_LOWER_TEMP = -50 # MAC compared with outlet down +MONITOR_MAC_MAX_TEMP = 100 # + +MONITOR_FALL_TEMP = 4 # adjustment reduced temperature +MONITOR_MAC_WARNING_THRESHOLD = 100 # 100 +MONITOR_OUTTEMP_WARNING_THRESHOLD = 85 +MONITOR_BOARDTEMP_WARNING_THRESHOLD = 85 +MONITOR_CPUTEMP_WARNING_THRESHOLD = 85 +MONITOR_INTEMP_WARNING_THRESHOLD = 70 # 70 + +MONITOR_MAC_CRITICAL_THRESHOLD = 105 # 105 +MONITOR_OUTTEMP_CRITICAL_THRESHOLD = 90 # 90 +MONITOR_BOARDTEMP_CRITICAL_THRESHOLD = 90 # 90 +MONITOR_CPUTEMP_CRITICAL_THRESHOLD = 100 # 100 +MONITOR_INTEMP_CRITICAL_THRESHOLD = 80 # 80 +MONITOR_CRITICAL_NUM = 3 # retry times +MONITOR_SHAKE_TIME = 20 # anti-shake times +MONITOR_INTERVAL = 60 + +# 1 get mac temperature from sysfs ,0 get mac temperature from bcmcmd +MONITOR_MAC_SOURCE_SYSFS = (0) +MONITOR_MAC_SOURCE_PATH = None # sysfs path + + +# default MAC AVS parameters +MAC_AVS_PARAM = { + 0x72: 0x0384, + 0x73: 0x037E, + 0x74: 0x0378, + 0x75: 0x0372, + 0x76: 0x036B, + 0x77: 0x0365, + 0x78: 0x035F, + 0x79: 0x0359, + 0x7A: 0x0352, + 0x7B: 0x034C, + 0x7C: 0x0346, + 0x7D: 0x0340, + 0x7E: 0x0339, + 0x7F: 0x0333, + 0x80: 0x032D, + 0x81: 0x0327, + 0x82: 0x0320, + 0x83: 0x031A, + 0x84: 0x0314, + 0x85: 0x030E, + 0x86: 0x0307, + 0x87: 0x0301, + 0x88: 0x02FB, + 0x89: 0x02F5, + 0x8A: 0x02EE, +} + +# default 6520 configuration +MAC_DEFAULT_PARAM = { + "type": 1, # type 1 represents default if out of range / 0 represents no voltage-adjustment if out of range + "default": 0x74, # should be used with type + "loopaddr": 0x00, # AVS loop address + "loop": 0x00, # AVS loop value + "open": 0x00, # diasble write-protection value + "close": 0x40, # enable write-protection value + "bus": 2, # AVSI2C bus address + "devno": 0x60, # AVS address + "addr": 0x21, # AVS voltage-adjustment address + "protectaddr": 0x10, # AVS write-protection address + "sdkreg": "DMU_PCU_OTP_CONFIG_8", # SDK register name + "sdktype": 1, # type 0 represents no shift operation / 1 represents shift operation + "macregloc": 24, # shift operation + "mask": 0xFF, # mask after shift +} + +MONITOR_SYS_LED = [ + {"bus": 2, "devno": 0x33, "addr": 0xB2, "yellow": 0x06, "red": 0x02, "green": 0x04}, + {"bus": 2, "devno": 0x32, "addr": 0x72, "yellow": 0x06, "red": 0x02, "green": 0x04}, +] + +MONITOR_SYS_FAN_LED = [ + {"bus": 2, "devno": 0x33, "addr": 0xB4, "yellow": 0x06, "red": 0x02, "green": 0x04}, +] + +MONITOR_FANS_LED = [ + {"bus": 2, "devno": 0x32, "addr": 0x23, "green": 0x09, "red": 0x0A}, + {"bus": 2, "devno": 0x32, "addr": 0x24, "green": 0x09, "red": 0x0A}, + {"bus": 2, "devno": 0x32, "addr": 0x25, "green": 0x09, "red": 0x0A}, + {"bus": 2, "devno": 0x32, "addr": 0x26, "green": 0x09, "red": 0x0A}, +] + + +MONITOR_SYS_PSU_LED = [ + {"bus": 2, "devno": 0x33, "addr": 0xB3, "yellow": 0x06, "red": 0x02, "green": 0x04}, +] + +MONITOR_FAN_STATUS = [ + {"status": "green", "minOkNum": 4, "maxOkNum": 4}, + {"status": "yellow", "minOkNum": 3, "maxOkNum": 3}, + {"status": "red", "minOkNum": 0, "maxOkNum": 2}, +] + +MONITOR_PSU_STATUS = [ + {"status": "green", "minOkNum": 2, "maxOkNum": 2}, + {"status": "yellow", "minOkNum": 1, "maxOkNum": 1}, + {"status": "red", "minOkNum": 0, "maxOkNum": 0}, +] + +MONITOR_DEV_STATUS = {} +MONITOR_DEV_STATUS_DECODE = {} +DEV_MONITOR_PARAM = {} +SLOT_MONITOR_PARAM = {} + + +fanloc = {"name": "fanset", "location": "0-0032/fan_speed_set"} +#####################MAC-Voltage-Adjustment-Parameters#################################### + + +####================================Adaption-Area================================ +#### PLATFORM_COMMON common configuration head +#### "platform" specific configuration head +#### +PCA9548START = 11 +PCA9548BUSEND = 74 + +PLATFORM_CARDID = 0x00004040 + +FAN_PROTECT = {"bus": 0, "devno": 0x32, "addr": 0x19, "open": 0x00, "close": 0x0F} +wb_eeprom = "2-0057/eeprom" +E2_LOC = {"bus": 2, "devno": 0x57} +E2_PROTECT = {"bus": 2, "devno": 0x33, "addr": 0xB0, "open": 0, "close": 1} +MAC_LED_RESET = {"pcibus": 8, "slot": 0, "fn": 0, "bar": 0, "offset": 64, "reset": 0x98} + +INIT_PARAM = [ + {"loc": "1-0034/sfp_enable", "value": "01"}, + {"loc": "2-0035/sfp_enable2", "value": "ff"}, + {"loc": "2-0033/mac_led", "value": "ff"}, + {"loc": "1-0034/sfp_txdis1", "value": "00"}, + {"loc": "1-0034/sfp_txdis2", "value": "00"}, + {"loc": "1-0034/sfp_txdis3", "value": "00"}, + {"loc": "1-0036/sfp_txdis4", "value": "00"}, + {"loc": "1-0036/sfp_txdis5", "value": "00"}, + {"loc": "1-0036/sfp_txdis6", "value": "00"}, + {"loc": fanloc["location"], "value": "80"}, +] + +INIT_COMMAND = [] + +CPLDVERSIONS = [ + {"loc": "2-0033/cpld_version", "des": "MAC Board CPLDA"}, + {"loc": "2-0035/cpld_version", "des": "MAC Board CPLDB"}, + {"loc": "2-0037/cpld_version", "des": "CPU Board CPLD"}, +] + +## Driver List +## + +DRIVERLISTS = [] +DEVICE = [] + +#####################FRU-Info-Adaption################################# +E2TYPE = { + "1": "tlveeprom", + "2": "x86cpueeprom", + "3": "bmceeprom", + "4": "cpueeprom", + "5": "maceeprom", + "6": "sloteeprom", + "7": "fanconnecteeprom", + "8": "M1HFANI-F", + "9": "M1HFANI-R", + "A": "M2HFANI-F", + "B": "M2HFANI-R", + "C": "psu", +} +FRULISTS = [] +################################Manufacturing-Test-Adaption-Area####################################################### +# need to export interface +fanlevel_6510 = { + "level": [51, 150, 255], + "low_speed": [500, 7500, 17000], + "high_speed": [11000, 22500, 28500], +} + +fanlevel_6520 = { + "level": [75, 150, 255], + "low_speed": [750, 4250, 6750], + "high_speed": [4500, 7500, 10000], +} + +fanlevel = fanlevel_6520 + +TEMPIDCHANGE = { + "lm75in": "inlet", + "lm75out": "outlet", + "lm75hot": "hot-point", + "inlet": "lm75in", + "outlet": "lm75out", + "hot-point": "lm75hot", +} + +# Manufacturing-Test module +FACTESTMODULE = {} + +##################################Manufacturing-Test-Menu +item1 = {"name": "Single Test", "deal": "test_signal", "childid": 1} +test_sys_reload_item = {"name": "reset-system", "deal": "test_sys_reload"} + +test_sys_item = {"name": "Product information test", "deal": "test_sysinfo"} +test_temp_item = {"name": "temperature test", "deal": "test_tempinfo"} +test_mem_item = {"name": "Memory test", "deal": "test_cpumemoryinfo"} +test_hd_item = {"name": "Hard disk test", "deal": "test_hard"} +test_rtc_item = {"name": "RTC test ", "deal": "test_rtc"} +test_i2c_item = {"name": "I2c test ", "deal": "test_i2c"} +test_cpld_item = {"name": "CPLD test", "deal": "test_cpld"} +test_portframe_item = { + "name": "Port transmit-receive frame test", + "deal": "test_portframe", +} +test_sysled_item = {"name": "System led test", "deal": "test_led"} +test_fan_item = {"name": "Fan status test", "deal": "test_fan"} +test_power_item = {"name": "PSU status test", "deal": "test_power"} +test_usb_item = {"name": "USB test", "deal": "test_usb"} +test_prbs_item = {"name": "PRBS test", "deal": "test_prbs"} +test_portbroadcast_item = {"name": "Port broadcast", "deal": "test_portbroadcast"} + +test_debug_level = {"name": "Change debug level", "deal": "test_setdebug"} +test_log_level = {"name": "Log output level", "deal": "test_loginfolevel"} +test_setmac = {"name": "setmac", "deal": "test_setmac"} +test_setrtc = {"name": "Set RTC", "deal": "test_set_rtc"} + +log_level_critical = {"name": "CRITICAL", "deal": "test_log_critical"} +log_level_debug = {"name": "DEBUG", "deal": "test_log_debug"} +log_level_error = {"name": "ERROR", "deal": "test_log_error"} +log_level_info = {"name": "INFO", "deal": "test_log_info"} +log_level_notset = {"name": "NOTSET", "deal": "test_log_notset"} +log_level_warning = {"name": "WARNING", "deal": "test_log_warning"} + + +test_e2_setmac_item = {"name": "E2SETMAC", "deal": "test_e2_setmac"} +test_bmc_setmac_item = {"name": "BMCSETMAC", "deal": "test_bmc_setmac"} +test_fan_setmac_item = {"name": "fan SETMAC", "deal": "test_fan_setmac"} + +alltest = [ + test_sys_item, + test_temp_item, + test_mem_item, + test_hd_item, + test_rtc_item, + test_i2c_item, + test_cpld_item, + test_portframe_item, + test_sysled_item, + test_fan_item, + test_power_item, + test_usb_item, + test_prbs_item, + test_portbroadcast_item, +] + +looptest = [ + test_sys_item, + test_temp_item, + test_mem_item, + test_hd_item, + test_rtc_item, + test_i2c_item, + test_cpld_item, + test_portframe_item, + test_fan_item, + test_power_item, + test_usb_item, + test_prbs_item, + test_portbroadcast_item, +] + +menuList = [ + { + "menuid": 0, + "value": [ + {"name": "Single test", "deal": "test_signal", "childid": 1}, + {"name": "All test", "deal": "test_all"}, + {"name": "Loop test", "deal": "test_loop"}, + # {"name":"Check loop-test result", "deal" :"test_loop_read"}, + # {"name":"Delete loop-test result", "deal" :"test_loop_delete"}, + # {"name":"Load configuration", "deal" :"test_config"}, + test_sys_reload_item, + {"name": "System Configuration", "deal": "test_sysconfig", "childid": 2}, + ], + }, + { + "menuid": 1, + "parentid": 0, + "value": [ + test_sys_item, + test_temp_item, + test_mem_item, + test_hd_item, + test_rtc_item, + test_i2c_item, + test_cpld_item, + test_portframe_item, + test_sysled_item, + test_fan_item, + test_power_item, + test_usb_item, + test_prbs_item, + test_portbroadcast_item, + ], + }, + { + "menuid": 2, + "parentid": 0, + "value": [test_debug_level, test_log_level, test_setmac, test_setrtc,], + }, + { + "menuid": 3, + "parentid": 2, + "value": [ + log_level_critical, + log_level_debug, + log_level_error, + log_level_info, + log_level_notset, + log_level_warning, + ], + }, + { + "menuid": 4, + "parentid": 2, + "value": [test_e2_setmac_item, test_bmc_setmac_item, test_fan_setmac_item,], + }, +] + + +TESTCASE = { + "CPLD": [ + { + "name": "CONNECT BOARD CPLD-A", + "cases": [ + {"name": "cpld32", "cmd": "grtd_test.py cpld_check 0 0x32 0xAA"}, + {"name": "cpld37", "cmd": "grtd_test.py cpld_check 2 0x37 0xAC"}, + ], + }, + { + "name": "MAC BOARD CPLD-A", + "cases": [ + {"name": "cpld33", "cmd": "grtd_test.py cpld_check 2 0x33 0xAB"}, + {"name": "cpld34", "cmd": "grtd_test.py cpld_check 1 0x34 0xAA"}, + ], + }, + { + "name": "MAC BOARD CPLD-B", + "cases": [ + {"name": "cpld36", "cmd": "grtd_test.py cpld_check 1 0x36 0xAA"}, + {"name": "cpld35", "cmd": "grtd_test.py cpld_check 2 0x35 0xAB"}, + ], + }, + ], + "TEMPERATURE": [ + { + "name": "-->temperature test", + "cases": [ + { + "name": "inlet", + "cmd": "grtd_test.py temp 2-0048/hwmon/hwmon1/temp1_input", + }, + { + "name": "outlet", + "cmd": "grtd_test.py temp 2-0049/hwmon/hwmon2/temp1_input", + }, + { + "name": "hot-point", + "cmd": "grtd_test.py temp 2-004a/hwmon/hwmon3/temp1_input", + }, + ], + } + ], + "MEMTORY": { + "cases": [ + {"name": "->memory test 1M", "cmd": "memtester 1M 1"}, + {"name": "->memory test 2M", "cmd": "memtester 2M 1"}, + {"name": "->memory test 8M", "cmd": "memtester 8M 1"}, + # {"name":"->memory test 16M","cmd":"memtester 16M 1"}, + # {"name":"->memory test 256M","cmd":"memtester 256M 1"}, + ] + }, + "SMARTCTLCMDS": { + "cases": [ + {"name": "->Check Hard Disk Info", "cmd": "smartctl -i /dev/sda"}, + {"name": "->Check Hard Disk Monitor Status", "cmd": "smartctl -H /dev/sda"}, + ] + }, + "LED": [ + { + "name": "Light Port Led test", + "cases": [ + { + "name": "-> Red Led Off", + "cmd": "grtd_test.py led loc 1-0034/sfp_led1_red,1-0034/sfp_led2_red,1-0034/sfp_led3_red,1-0034/sfp_led8_red,1-0036/sfp_led4_red,1-0036/sfp_led5_red,1-0036/sfp_led6_red,1-0036/sfp_led7_red 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00", + }, + { + "name": "-> Red Led On", + "cmd": "grtd_test.py led loc 1-0034/sfp_led1_red,1-0034/sfp_led2_red,1-0034/sfp_led3_red,1-0034/sfp_led8_red,1-0036/sfp_led4_red,1-0036/sfp_led5_red,1-0036/sfp_led6_red,1-0036/sfp_led7_red 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff", + }, + { + "name": "-> Recovery Red Led Off", + "cmd": "grtd_test.py led loc 1-0034/sfp_led1_red,1-0034/sfp_led2_red,1-0034/sfp_led3_red,1-0034/sfp_led8_red,1-0036/sfp_led4_red,1-0036/sfp_led5_red,1-0036/sfp_led6_red,1-0036/sfp_led7_red 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00", + }, + { + "name": "-> Yellow Led Off", + "cmd": "grtd_test.py led loc 1-0034/sfp_led1_yellow,1-0034/sfp_led2_yellow,1-0034/sfp_led3_yellow,1-0034/sfp_led8_yellow,1-0036/sfp_led4_yellow,1-0036/sfp_led5_yellow,1-0036/sfp_led6_yellow,1-0036/sfp_led7_yellow 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00", + }, + { + "name": "-> Yellow Led On", + "cmd": "grtd_test.py led loc 1-0034/sfp_led1_yellow,1-0034/sfp_led2_yellow,1-0034/sfp_led3_yellow,1-0034/sfp_led8_yellow,1-0036/sfp_led4_yellow,1-0036/sfp_led5_yellow,1-0036/sfp_led6_yellow,1-0036/sfp_led7_yellow 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff", + }, + { + "name": "-> Recovery Yellow Led Off", + "cmd": "grtd_test.py led loc 1-0034/sfp_led1_yellow,1-0034/sfp_led2_yellow,1-0034/sfp_led3_yellow,1-0034/sfp_led8_yellow,1-0036/sfp_led4_yellow,1-0036/sfp_led5_yellow,1-0036/sfp_led6_yellow,1-0036/sfp_led7_yellow 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00", + }, + ], + }, + { + "name": "fan 1 Led", + "cases": [ + { + "name": "-> LedOff", + "cmd": "grtd_test.py led loc 0-0032/fan0_led 0x0b", + }, + { + "name": "-> Red Led ", + "cmd": "grtd_test.py led loc 0-0032/fan0_led 0x0a", + }, + { + "name": "-> Green Led ", + "cmd": "grtd_test.py led loc 0-0032/fan0_led 0x09", + }, + { + "name": "-> Yellow Led ", + "cmd": "grtd_test.py led loc 0-0032/fan0_led 0x08", + }, + { + "name": "-> Red Led Flashing", + "cmd": "grtd_test.py led loc 0-0032/fan0_led 0x0e", + }, + { + "name": "-> Green Led Flashing", + "cmd": "grtd_test.py led loc 0-0032/fan0_led 0x0d", + }, + { + "name": "-> Yellow Led Flashing", + "cmd": "grtd_test.py led loc 0-0032/fan0_led 0x0c", + }, + { + "name": "-> Recovery Green Led ", + "cmd": "grtd_test.py led loc 0-0032/fan0_led 0x09", + }, + ], + }, + { + "name": "fan 2 Led", + "cases": [ + { + "name": "-> LedOff", + "cmd": "grtd_test.py led loc 0-0032/fan1_led 0x0b", + }, + { + "name": "-> Red Led ", + "cmd": "grtd_test.py led loc 0-0032/fan1_led 0x0a", + }, + { + "name": "-> Green Led ", + "cmd": "grtd_test.py led loc 0-0032/fan1_led 0x09", + }, + { + "name": "-> Yellow Led ", + "cmd": "grtd_test.py led loc 0-0032/fan1_led 0x08", + }, + { + "name": "-> Red Led Flashing", + "cmd": "grtd_test.py led loc 0-0032/fan1_led 0x0e", + }, + { + "name": "-> Green Led Flashing", + "cmd": "grtd_test.py led loc 0-0032/fan1_led 0x0d", + }, + { + "name": "-> Yellow Led Flashing", + "cmd": "grtd_test.py led loc 0-0032/fan1_led 0x0c", + }, + { + "name": "-> Recovery Green Led ", + "cmd": "grtd_test.py led loc 0-0032/fan1_led 0x09", + }, + ], + }, + { + "name": "fan 3 Led", + "cases": [ + { + "name": "-> LedOff", + "cmd": "grtd_test.py led loc 0-0032/fan2_led 0x0b", + }, + { + "name": "-> Red Led ", + "cmd": "grtd_test.py led loc 0-0032/fan2_led 0x0a", + }, + { + "name": "-> Green Led ", + "cmd": "grtd_test.py led loc 0-0032/fan2_led 0x09", + }, + { + "name": "-> Yellow Led ", + "cmd": "grtd_test.py led loc 0-0032/fan2_led 0x08", + }, + { + "name": "-> Red Led Flashing", + "cmd": "grtd_test.py led loc 0-0032/fan2_led 0x0e", + }, + { + "name": "-> Green Led Flashing", + "cmd": "grtd_test.py led loc 0-0032/fan2_led 0x0d", + }, + { + "name": "-> Yellow Led Flashing", + "cmd": "grtd_test.py led loc 0-0032/fan2_led 0x0c", + }, + { + "name": "-> Recovery Green Led ", + "cmd": "grtd_test.py led loc 0-0032/fan2_led 0x09", + }, + ], + }, + { + "name": "Front panel CPU Led", + "cases": [ + { + "name": "-> LedOff", + "cmd": "grtd_test.py led loc 2-0035/broad_front_cpu 0x00", + }, + { + "name": "-> Green Led not Flashing", + "cmd": "grtd_test.py led loc 2-0035/broad_front_cpu 0x01", + }, + { + "name": "-> Red Led not Flashing", + "cmd": "grtd_test.py led loc 2-0035/broad_front_cpu 0x02", + }, + { + "name": "-> Yellow Led not Flashing", + "cmd": "grtd_test.py led loc 2-0035/broad_front_cpu 0x03", + }, + { + "name": "-> Green Led 1/4sFlashing ", + "cmd": "grtd_test.py led loc 2-0035/broad_front_cpu 0x11", + }, + { + "name": "-> Green Led 1/2sFlashing ", + "cmd": "grtd_test.py led loc 2-0035/broad_front_cpu 0x21", + }, + { + "name": "-> Green Led 1sFlashing ", + "cmd": "grtd_test.py led loc 2-0035/broad_front_cpu 0x41", + }, + { + "name": "-> Green Led 2sFlashing ", + "cmd": "grtd_test.py led loc 2-0035/broad_front_cpu 0x81", + }, + { + "name": "-> Red Led 1/4sFlashing ", + "cmd": "grtd_test.py led loc 2-0035/broad_front_cpu 0x12", + }, + { + "name": "-> Red Led 1/2sFlashing ", + "cmd": "grtd_test.py led loc 2-0035/broad_front_cpu 0x22", + }, + { + "name": "-> Red Led 1sFlashing ", + "cmd": "grtd_test.py led loc 2-0035/broad_front_cpu 0x42", + }, + { + "name": "-> Red Led 2sFlashing ", + "cmd": "grtd_test.py led loc 2-0035/broad_front_cpu 0x82", + }, + { + "name": "-> Yellow Led 1/4sFlashing ", + "cmd": "grtd_test.py led loc 2-0035/broad_front_cpu 0x13", + }, + { + "name": "-> Yellow Led 1/2sFlashing ", + "cmd": "grtd_test.py led loc 2-0035/broad_front_cpu 0x23", + }, + { + "name": "-> Yellow Led 1sFlashing ", + "cmd": "grtd_test.py led loc 2-0035/broad_front_cpu 0x43", + }, + { + "name": "-> Yellow Led 2sFlashing ", + "cmd": "grtd_test.py led loc 2-0035/broad_front_cpu 0x83", + }, + { + "name": "-> Recovery Green Led ", + "cmd": "grtd_test.py led loc 2-0035/broad_front_cpu 0x01", + }, + ], + }, + { + "name": "Front panel BMC Led", + "cases": [ + { + "name": "-> LedOff", + "cmd": "grtd_test.py led loc 2-0035/broad_front_bmc 0x00", + }, + { + "name": "-> Red Led Flashing", + "cmd": "grtd_test.py led loc 2-0035/broad_front_bmc 0x01", + }, + { + "name": "-> Red Led not Flashing", + "cmd": "grtd_test.py led loc 2-0035/broad_front_bmc 0x02", + }, + { + "name": "-> Green Led Flashing", + "cmd": "grtd_test.py led loc 2-0035/broad_front_bmc 0x03", + }, + { + "name": "-> Green Led not Flashing", + "cmd": "grtd_test.py led loc 2-0035/broad_front_bmc 0x04", + }, + { + "name": "-> Yellow Led Flashing", + "cmd": "grtd_test.py led loc 2-0035/broad_front_bmc 0x05", + }, + { + "name": "-> Yellow Led not Flashing", + "cmd": "grtd_test.py led loc 2-0035/broad_front_bmc 0x06", + }, + { + "name": "-> Recovery Green Led ", + "cmd": "grtd_test.py led loc 2-0035/broad_front_bmc 0x04", + }, + ], + }, + { + "name": "Front panel location Led", + "cases": [ + { + "name": "-> LedOff", + "cmd": "grtd_test.py led loc 2-0035/broad_front_lct 0xff", + }, + { + "name": "-> LedOn", + "cmd": "grtd_test.py led loc 2-0035/broad_front_lct 0xfe", + }, + { + "name": "->Recovery LedOff", + "cmd": "grtd_test.py led loc 2-0035/broad_front_lct 0xff", + }, + ], + }, + { + "name": "Front panel pwr Led", + "cases": [ + { + "name": "-> LedOff", + "cmd": "grtd_test.py led loc 2-0035/broad_front_pwr 0x00", + }, + { + "name": "-> Red Led Flashing", + "cmd": "grtd_test.py led loc 2-0035/broad_front_pwr 0x01", + }, + { + "name": "-> Red Led not Flashing", + "cmd": "grtd_test.py led loc 2-0035/broad_front_pwr 0x02", + }, + { + "name": "-> Green Led Flashing", + "cmd": "grtd_test.py led loc 2-0035/broad_front_pwr 0x03", + }, + { + "name": "-> Green Led not Flashing", + "cmd": "grtd_test.py led loc 2-0035/broad_front_pwr 0x04", + }, + { + "name": "-> Yellow Led Flashing", + "cmd": "grtd_test.py led loc 2-0035/broad_front_pwr 0x05", + }, + { + "name": "-> Yellow Led not Flashing", + "cmd": "grtd_test.py led loc 2-0035/broad_front_pwr 0x06", + }, + { + "name": "-> Recovery Green Led ", + "cmd": "grtd_test.py led loc 2-0035/broad_front_pwr 0x04", + }, + ], + }, + { + "name": "Front panel fan Led", + "cases": [ + { + "name": "-> LedOff", + "cmd": "grtd_test.py led loc 2-0035/broad_front_fan 0x00", + }, + { + "name": "-> Red Led Flashing", + "cmd": "grtd_test.py led loc 2-0035/broad_front_fan 0x01", + }, + { + "name": "-> Red Led not Flashing", + "cmd": "grtd_test.py led loc 2-0035/broad_front_fan 0x02", + }, + { + "name": "-> Green Led Flashing", + "cmd": "grtd_test.py led loc 2-0035/broad_front_fan 0x03", + }, + { + "name": "-> Green Led not Flashing", + "cmd": "grtd_test.py led loc 2-0035/broad_front_fan 0x04", + }, + { + "name": "-> Yellow Led Flashing", + "cmd": "grtd_test.py led loc 2-0035/broad_front_fan 0x05", + }, + { + "name": "-> Yellow Led not Flashing", + "cmd": "grtd_test.py led loc 2-0035/broad_front_fan 0x06", + }, + { + "name": "-> Recovery Green Led ", + "cmd": "grtd_test.py led loc 2-0035/broad_front_fan 0x04", + }, + ], + }, + ], + "I2C": [ + ####type 1 represents value obtained compated with value + ####type 2 represents return True or False + { + "name": "I2C device test", + "cases": [ + { + "name": " PCA9641 test", + "cmd": "grtd_test.py dev_rd 0 10 0", + "deal_type": 2, + }, + { + "name": " cpld32 test", + "cmd": "grtd_test.py dev_rd 0 32 0", + "deal_type": 2, + }, + { + "name": " cpld33 test", + "cmd": "grtd_test.py dev_rd 0 32 0", + "deal_type": 2, + }, + { + "name": " cpld34 test", + "cmd": "grtd_test.py dev_rd 0 32 0", + "deal_type": 2, + }, + { + "name": " cpld35 test", + "cmd": "grtd_test.py dev_rd 0 32 0", + "deal_type": 2, + }, + { + "name": " cpld36 test", + "cmd": "grtd_test.py dev_rd 0 32 0", + "deal_type": 2, + }, + { + "name": " cpld37 test", + "cmd": "grtd_test.py dev_rd 0 32 0", + "deal_type": 2, + }, + { + "name": " inlet LM75", + "cmd": "grtd_test.py dev_rd 0 32 0", + "deal_type": 2, + }, + { + "name": " outlet LM75", + "cmd": "grtd_test.py dev_rd 0 32 0", + "deal_type": 2, + }, + { + "name": " hot-point LM75", + "cmd": "grtd_test.py dev_rd 0 32 0", + "deal_type": 2, + }, + { + "name": " EEPROM", + "cmd": "grtd_test.py dev_rd 0 32 0", + "deal_type": 2, + }, + { + "name": " Port 1", + "cmd": "grtd_test.py dev_rd 11 0050 0", + "deal_type": 2, + }, + { + "name": " Port 2", + "cmd": "grtd_test.py dev_rd 12 0050 0", + "deal_type": 2, + }, + { + "name": " Port 3", + "cmd": "grtd_test.py dev_rd 13 0050 0", + "deal_type": 2, + }, + { + "name": " Port 4", + "cmd": "grtd_test.py dev_rd 14 0050 0", + "deal_type": 2, + }, + { + "name": " Port 5", + "cmd": "grtd_test.py dev_rd 15 0050 0", + "deal_type": 2, + }, + { + "name": " Port 6", + "cmd": "grtd_test.py dev_rd 16 0050 0", + "deal_type": 2, + }, + { + "name": " Port 7", + "cmd": "grtd_test.py dev_rd 17 0050 0", + "deal_type": 2, + }, + { + "name": " Port 8", + "cmd": "grtd_test.py dev_rd 18 0050 0", + "deal_type": 2, + }, + { + "name": " Port 9", + "cmd": "grtd_test.py dev_rd 19 0050 0", + "deal_type": 2, + }, + { + "name": " Port 10", + "cmd": "grtd_test.py dev_rd 20 0050 0", + "deal_type": 2, + }, + { + "name": " Port 11", + "cmd": "grtd_test.py dev_rd 21 0050 0", + "deal_type": 2, + }, + { + "name": " Port 12", + "cmd": "grtd_test.py dev_rd 22 0050 0", + "deal_type": 2, + }, + { + "name": " Port 13", + "cmd": "grtd_test.py dev_rd 23 0050 0", + "deal_type": 2, + }, + { + "name": " Port 14", + "cmd": "grtd_test.py dev_rd 24 0050 0", + "deal_type": 2, + }, + { + "name": " Port 15", + "cmd": "grtd_test.py dev_rd 25 0050 0", + "deal_type": 2, + }, + { + "name": " Port 16", + "cmd": "grtd_test.py dev_rd 26 0050 0", + "deal_type": 2, + }, + { + "name": " Port 17", + "cmd": "grtd_test.py dev_rd 27 0050 0", + "deal_type": 2, + }, + { + "name": " Port 18", + "cmd": "grtd_test.py dev_rd 28 0050 0", + "deal_type": 2, + }, + { + "name": " Port 19", + "cmd": "grtd_test.py dev_rd 29 0050 0", + "deal_type": 2, + }, + { + "name": " Port 20", + "cmd": "grtd_test.py dev_rd 30 0050 0", + "deal_type": 2, + }, + { + "name": " Port 21", + "cmd": "grtd_test.py dev_rd 31 0050 0", + "deal_type": 2, + }, + { + "name": " Port 22", + "cmd": "grtd_test.py dev_rd 32 0050 0", + "deal_type": 2, + }, + { + "name": " Port 23", + "cmd": "grtd_test.py dev_rd 33 0050 0", + "deal_type": 2, + }, + { + "name": " Port 24", + "cmd": "grtd_test.py dev_rd 34 0050 0", + "deal_type": 2, + }, + { + "name": " Port 25", + "cmd": "grtd_test.py dev_rd 35 0050 0", + "deal_type": 2, + }, + { + "name": " Port 26", + "cmd": "grtd_test.py dev_rd 36 0050 0", + "deal_type": 2, + }, + { + "name": " Port 27", + "cmd": "grtd_test.py dev_rd 37 0050 0", + "deal_type": 2, + }, + { + "name": " Port 28", + "cmd": "grtd_test.py dev_rd 38 0050 0", + "deal_type": 2, + }, + { + "name": " Port 29", + "cmd": "grtd_test.py dev_rd 39 0050 0", + "deal_type": 2, + }, + { + "name": " Port 30", + "cmd": "grtd_test.py dev_rd 40 0050 0", + "deal_type": 2, + }, + { + "name": " Port 31", + "cmd": "grtd_test.py dev_rd 41 0050 0", + "deal_type": 2, + }, + { + "name": " Port 32", + "cmd": "grtd_test.py dev_rd 42 0050 0", + "deal_type": 2, + }, + { + "name": " Port 33", + "cmd": "grtd_test.py dev_rd 43 0050 0", + "deal_type": 2, + }, + { + "name": " Port 34", + "cmd": "grtd_test.py dev_rd 44 0050 0", + "deal_type": 2, + }, + { + "name": " Port 35", + "cmd": "grtd_test.py dev_rd 45 0050 0", + "deal_type": 2, + }, + { + "name": " Port 36", + "cmd": "grtd_test.py dev_rd 46 0050 0", + "deal_type": 2, + }, + { + "name": " Port 37", + "cmd": "grtd_test.py dev_rd 47 0050 0", + "deal_type": 2, + }, + { + "name": " Port 38", + "cmd": "grtd_test.py dev_rd 48 0050 0", + "deal_type": 2, + }, + { + "name": " Port 39", + "cmd": "grtd_test.py dev_rd 49 0050 0", + "deal_type": 2, + }, + { + "name": " Port 40", + "cmd": "grtd_test.py dev_rd 50 0050 0", + "deal_type": 2, + }, + { + "name": " Port 41", + "cmd": "grtd_test.py dev_rd 51 0050 0", + "deal_type": 2, + }, + { + "name": " Port 42", + "cmd": "grtd_test.py dev_rd 52 0050 0", + "deal_type": 2, + }, + { + "name": " Port 43", + "cmd": "grtd_test.py dev_rd 53 0050 0", + "deal_type": 2, + }, + { + "name": " Port 44", + "cmd": "grtd_test.py dev_rd 54 0050 0", + "deal_type": 2, + }, + { + "name": " Port 45", + "cmd": "grtd_test.py dev_rd 55 0050 0", + "deal_type": 2, + }, + { + "name": " Port 46", + "cmd": "grtd_test.py dev_rd 56 0050 0", + "deal_type": 2, + }, + { + "name": " Port 47", + "cmd": "grtd_test.py dev_rd 57 0050 0", + "deal_type": 2, + }, + { + "name": " Port 48", + "cmd": "grtd_test.py dev_rd 58 0050 0", + "deal_type": 2, + }, + { + "name": " Port 49", + "cmd": "grtd_test.py dev_rd 59 0050 0", + "deal_type": 2, + }, + { + "name": " Port 50", + "cmd": "grtd_test.py dev_rd 60 0050 0", + "deal_type": 2, + }, + { + "name": " Port 51", + "cmd": "grtd_test.py dev_rd 61 0050 0", + "deal_type": 2, + }, + { + "name": " Port 52", + "cmd": "grtd_test.py dev_rd 62 0050 0", + "deal_type": 2, + }, + { + "name": " Port 53", + "cmd": "grtd_test.py dev_rd 63 0050 0", + "deal_type": 2, + }, + { + "name": " Port 54", + "cmd": "grtd_test.py dev_rd 64 0050 0", + "deal_type": 2, + }, + { + "name": " Port 55", + "cmd": "grtd_test.py dev_rd 65 0050 0", + "deal_type": 2, + }, + { + "name": " Port 56", + "cmd": "grtd_test.py dev_rd 66 0050 0", + "deal_type": 2, + }, + { + "name": " Port 57", + "cmd": "grtd_test.py dev_rd 67 0050 0", + "deal_type": 2, + }, + { + "name": " Port 58", + "cmd": "grtd_test.py dev_rd 68 0050 0", + "deal_type": 2, + }, + { + "name": " Port 59", + "cmd": "grtd_test.py dev_rd 69 0050 0", + "deal_type": 2, + }, + { + "name": " Port 60", + "cmd": "grtd_test.py dev_rd 70 0050 0", + "deal_type": 2, + }, + { + "name": " Port 61", + "cmd": "grtd_test.py dev_rd 71 0050 0", + "deal_type": 2, + }, + { + "name": " Port 62", + "cmd": "grtd_test.py dev_rd 72 0050 0", + "deal_type": 2, + }, + { + "name": " Port 63", + "cmd": "grtd_test.py dev_rd 73 0050 0", + "deal_type": 2, + }, + { + "name": " Port 64", + "cmd": "grtd_test.py dev_rd 74 0050 0", + "deal_type": 2, + }, + ], + }, + ], +} + +PCIe_DEV_LIST = [] +PCIe_SPEED_ITEM = [] + +################################Manufacturing-Test-Adaption-Area####################################################### diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/script/platform_config.py b/platform/centec-arm64/sonic-platform-modules-micas/common/script/platform_config.py new file mode 100644 index 000000000000..e3b020b0209e --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/script/platform_config.py @@ -0,0 +1,205 @@ +#!/usr/bin/python3 +# -*- coding: UTF-8 -*- +# ------------------------------------------------------------------------------- +# Name: platform_config.py +# Purpose: block the difference between various product/onie version for other module +# +# Author: rd +# +# Created: 02/07/2018 +# Copyright: (c) rd 2018 +# ------------------------------------------------------------------------------- +import sys +import os +from wbutil.baseutil import get_machine_info +from wbutil.baseutil import get_platform_info + +__all__ = [ + "getdeviceplatform", + "MONITOR_CONST", + "MAILBOX_DIR", + "DEVICE", + "GLOBALCONFIG", + "GLOBALINITPARAM", + "GLOBALINITCOMMAND", + "MAC_LED_RESET", + "STARTMODULE", + "fanloc", + "PLATFORM_CARDID", + "PLATFORM_PRODUCTNAME", + "PLATFORM_PART_NUMBER", + "PLATFORM_LABEL_REVISION", + "PLATFORM_MAC_SIZE", + "PLATFORM_MANUF_NAME", + "PLATFORM_MANUF_COUNTRY", + "PLATFORM_VENDOR_NAME", + "PLATFORM_DIAG_VERSION", + "PLATFORM_SERVICE_TAG", + "E2_PROTECT", + "E2_LOC", + "FAN_PROTECT", + "FANS_DEF", + "MONITOR_FANS_LED", + "MONITOR_SYS_FAN_LED", + "MONITOR_SYS_PSU_LED", + "MONITOR_FAN_STATUS", + "MONITOR_PSU_STATUS", + "MONITOR_DEV_STATUS", + "MONITOR_DEV_STATUS_DECODE", + "DEV_LEDS", + "MAC_AVS_PARAM", + "MAC_DEFAULT_PARAM", + "FRULISTS", + "wb_eeprom", + "i2ccheck_params", + "FANCTROLDEBUG", + "DEVMONITORDEBUG", +] + + +def getdeviceplatform(): + x = get_platform_info(get_machine_info()) + if x != None: + filepath = "/usr/share/sonic/device/" + x + return filepath + return None + + +platform = get_platform_info(get_machine_info()) +platformpath = getdeviceplatform() +MAILBOX_DIR = "/sys/bus/i2c/devices/" +grtd_productfile = (platform + "_config").replace("-", "_") +common_productfile = "platform_common" +configfile_pre = "/usr/local/bin/" + +sys.path.append(platformpath) +sys.path.append(configfile_pre) + +############################################################################################ +## if there is no specific file, use common file +module_product = None +if os.path.exists(configfile_pre + grtd_productfile + ".py"): + module_product = __import__(grtd_productfile, globals(), locals(), [], 0) +elif os.path.exists(configfile_pre + common_productfile + ".py"): + module_product = __import__(common_productfile, globals(), locals(), [], 0) +else: + print("No Configuration existed, quit") + exit(-1) +############################################################################################ + +DEVICE = module_product.DEVICE + +##########Driver loading needs parameters +# get different product configuration +PLATFORM_GLOBALCONFIG = { + "DRIVERLISTS": module_product.DRIVERLISTS, + "QSFP": { + "startbus": module_product.PCA9548START, + "endbus": module_product.PCA9548BUSEND, + }, + "DEVS": DEVICE, +} +GLOBALCONFIG = PLATFORM_GLOBALCONFIG +GLOBALINITPARAM = module_product.INIT_PARAM +GLOBALINITCOMMAND = module_product.INIT_COMMAND + +fancontrol_loc = module_product.fancontrol_loc +fancontrol_config_loc = module_product.fancontrol_config_loc +MAC_LED_RESET = module_product.MAC_LED_RESET +###########Stat-up module parameters +STARTMODULE = module_product.STARTMODULE +FIRMWARE_TOOLS = module_product.FIRMWARE_TOOLS + + +##########Manufacturing-Test need parameters +FACTESTMODULE = module_product.FACTESTMODULE +TESTCASE = module_product.TESTCASE +menuList = module_product.menuList +alltest = module_product.alltest +looptest = module_product.looptest +fanloc = module_product.fanloc +fanlevel = module_product.fanlevel # fan adjustment level +TEMPIDCHANGE = module_product.TEMPIDCHANGE +CPLDVERSIONS = module_product.CPLDVERSIONS +PLATFORM_CARDID = module_product.PLATFORM_CARDID +PLATFORM_PRODUCTNAME = module_product.PLATFORM_PRODUCTNAME + +PLATFORM_PART_NUMBER = module_product.PLATFORM_PART_NUMBER +PLATFORM_LABEL_REVISION = module_product.PLATFORM_LABEL_REVISION +PLATFORM_ONIE_VERSION = module_product.PLATFORM_ONIE_VERSION +PLATFORM_MAC_SIZE = module_product.PLATFORM_MAC_SIZE +PLATFORM_MANUF_NAME = module_product.PLATFORM_MANUF_NAME +PLATFORM_MANUF_COUNTRY = module_product.PLATFORM_MANUF_COUNTRY +PLATFORM_VENDOR_NAME = module_product.PLATFORM_VENDOR_NAME +PLATFORM_DIAG_VERSION = module_product.PLATFORM_DIAG_VERSION +PLATFORM_SERVICE_TAG = module_product.PLATFORM_SERVICE_TAG + +E2_PROTECT = module_product.E2_PROTECT +E2_LOC = module_product.E2_LOC +FAN_PROTECT = module_product.FAN_PROTECT + +FANS_DEF = module_product.FANS_DEF +MONITOR_SYS_LED = module_product.MONITOR_SYS_LED +MONITOR_FANS_LED = module_product.MONITOR_FANS_LED +MONITOR_SYS_FAN_LED = module_product.MONITOR_SYS_FAN_LED +MONITOR_SYS_PSU_LED = module_product.MONITOR_SYS_PSU_LED +MONITOR_FAN_STATUS = module_product.MONITOR_FAN_STATUS +MONITOR_PSU_STATUS = module_product.MONITOR_PSU_STATUS +MONITOR_DEV_STATUS = module_product.MONITOR_DEV_STATUS +MONITOR_DEV_STATUS_DECODE = module_product.MONITOR_DEV_STATUS_DECODE +DEV_MONITOR_PARAM = module_product.DEV_MONITOR_PARAM +SLOT_MONITOR_PARAM = module_product.SLOT_MONITOR_PARAM + + +DEV_LEDS = module_product.DEV_LEDS +MEM_SLOTS = module_product.MEM_SLOTS + +MAC_AVS_PARAM = module_product.MAC_AVS_PARAM +MAC_DEFAULT_PARAM = module_product.MAC_DEFAULT_PARAM +E2TYPE = module_product.E2TYPE +FRULISTS = module_product.FRULISTS +wb_eeprom = "%d-%04x/eeprom" % (E2_LOC["bus"], E2_LOC["devno"]) +factest_module = module_product.factest_module + +LOCAL_LED_CONTROL = module_product.LOCAL_LED_CONTROL + +PCIe_DEV_LIST = module_product.PCIe_DEV_LIST +PCIe_SPEED_ITEM = module_product.PCIe_SPEED_ITEM +i2ccheck_params = module_product.i2ccheck_params + + +class MONITOR_CONST: + TEMP_MIN = module_product.MONITOR_TEMP_MIN + K = module_product.MONITOR_K + MAC_IN = module_product.MONITOR_MAC_IN + DEFAULT_SPEED = module_product.MONITOR_DEFAULT_SPEED + MAX_SPEED = module_product.MONITOR_MAX_SPEED + MIN_SPEED = module_product.MONITOR_MIN_SPEED + MAC_ERROR_SPEED = module_product.MONITOR_MAC_ERROR_SPEED + FAN_TOTAL_NUM = module_product.MONITOR_FAN_TOTAL_NUM + MAC_UP_TEMP = module_product.MONITOR_MAC_UP_TEMP + MAC_LOWER_TEMP = module_product.MONITOR_MAC_LOWER_TEMP + MAC_MAX_TEMP = module_product.MONITOR_MAC_MAX_TEMP + + MAC_WARNING_THRESHOLD = module_product.MONITOR_MAC_WARNING_THRESHOLD + OUTTEMP_WARNING_THRESHOLD = module_product.MONITOR_OUTTEMP_WARNING_THRESHOLD + BOARDTEMP_WARNING_THRESHOLD = module_product.MONITOR_BOARDTEMP_WARNING_THRESHOLD + CPUTEMP_WARNING_THRESHOLD = module_product.MONITOR_CPUTEMP_WARNING_THRESHOLD + INTEMP_WARNING_THRESHOLD = module_product.MONITOR_INTEMP_WARNING_THRESHOLD + + MAC_CRITICAL_THRESHOLD = module_product.MONITOR_MAC_CRITICAL_THRESHOLD + OUTTEMP_CRITICAL_THRESHOLD = module_product.MONITOR_OUTTEMP_CRITICAL_THRESHOLD + BOARDTEMP_CRITICAL_THRESHOLD = module_product.MONITOR_BOARDTEMP_CRITICAL_THRESHOLD + CPUTEMP_CRITICAL_THRESHOLD = module_product.MONITOR_CPUTEMP_CRITICAL_THRESHOLD + INTEMP_CRITICAL_THRESHOLD = module_product.MONITOR_INTEMP_CRITICAL_THRESHOLD + CRITICAL_NUM = module_product.MONITOR_CRITICAL_NUM + SHAKE_TIME = module_product.MONITOR_SHAKE_TIME + MONITOR_INTERVAL = module_product.MONITOR_INTERVAL + MONITOR_FALL_TEMP = module_product.MONITOR_FALL_TEMP + + MONITOR_MAC_SOURCE_SYSFS = module_product.MONITOR_MAC_SOURCE_SYSFS + MONITOR_MAC_SOURCE_PATH = module_product.MONITOR_MAC_SOURCE_PATH + + +FANCTROLDEBUG = 0 # 1 means enable +DEVMONITORDEBUG = 0 # 1 means enable diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/script/platform_sensors.py b/platform/centec-arm64/sonic-platform-modules-micas/common/script/platform_sensors.py new file mode 100644 index 000000000000..9f8a7b466dcc --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/script/platform_sensors.py @@ -0,0 +1,92 @@ +#!/usr/bin/python3 +# * onboard sensors +# +import os +import sys +from sonic_platform.redfish_api import Redfish_Api + +def get_machine_info(): + if not os.path.isfile('/host/machine.conf'): + return None + machine_vars = {} + with open('/host/machine.conf') as machine_file: + for line in machine_file: + tokens = line.split('=') + if len(tokens) < 2: + continue + machine_vars[tokens[0]] = tokens[1].strip() + return machine_vars + +def get_platform_info(machine_info): + if machine_info is not None: + if 'onie_platform' in machine_info: + return machine_info['onie_platform'] + elif 'aboot_platform' in machine_info: + return machine_info['aboot_platform'] + return None + +PLATFORM_ROOT_PATH = '/usr/share/sonic/device' +platform = None + +def get_platform_name(): + global platform + platform = get_platform_info(get_machine_info()) + return platform + +val = get_platform_name() +sys.path.append("/".join([PLATFORM_ROOT_PATH, platform])) + +def print_console(str): + print(str) + +def print_platform(): + platform_info = get_platform_name() + print_console(platform_info) + print_console('Adapter: Platform Management Controller') + print_console("") + +def get_sensor(): + sensor = Redfish_Api().get_thresholdSensors() + ctrl = sensor["Sensors"] + list_sensor =[] + for item in ctrl: + name = item.get("@odata.id").split("/",9)[9] + now = item.get("Reading") + min = item.get("Thresholds").get("LowerFatal").get("Reading") + max = item.get("Thresholds").get("UpperFatal").get("Reading") + unit = item.get("ReadingUnits") + if unit == "Amps": + unit = "A" + if min == (-1000): + min = (min/1000) + elif unit == "Volts": + unit = "V" + tmp = {} + tmp["name"]= name + tmp["now"] = ("%.3f" % now) + tmp["min"] = ("%.3f" % min) + tmp["max"] = ("%.3f" % max) + tmp["unit"] = unit + list_sensor.append(tmp) + return list_sensor + +def print_boarddcdc(): + val_ret = get_sensor() + print_info_str = "" + toptile = "Onboard Sensors:" + errformat = " {id:<26} : {errmsg}" + formatstr = " {name:<26} : {now:<6} {unit:<1} (Min = {min:<6} {unit:<1} , Max = {max:<6} {unit:<1} )" + + if len(val_ret) != 0: + print_info_str += toptile + '\n' + for item in val_ret: + realformat = formatstr if item.get('errcode', 0) == 0 else errformat + print_info_str += realformat.format(**item) + '\n' + print_console(print_info_str) + +def getsensors(): + print_platform() + print_boarddcdc() + +if __name__ == "__main__": + getsensors() diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/script/platform_util.py b/platform/centec-arm64/sonic-platform-modules-micas/common/script/platform_util.py new file mode 100644 index 000000000000..729da5faae0f --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/script/platform_util.py @@ -0,0 +1,1942 @@ +# -*- coding: UTF-8 -*- +# ------------------------------------------------------------------------- +# Name: platform_util +# Purpose: common configuration and api +# +# Author: rd +# +# Created: 02/07/2018 +# Copyright: (c) rd 2018 +# ------------------------------------------------------------------------- +import sys + +if sys.version_info >= (3, 0): + import subprocess as commands +else: + import commands +import os +import re +import syslog +import time +import binascii +import tty +import termios +import threading +import click +import mmap +from platform_config import ( + wb_eeprom, + FRULISTS, + MAC_DEFAULT_PARAM, + MAC_AVS_PARAM, + FANS_DEF, + FAN_PROTECT, + E2_LOC, + E2_PROTECT, + PLATFORM_SERVICE_TAG, + PLATFORM_DIAG_VERSION, + STARTMODULE, + PLATFORM_CARDID, + PLATFORM_PRODUCTNAME, + PLATFORM_PART_NUMBER, + PLATFORM_LABEL_REVISION, + PLATFORM_MAC_SIZE, + PLATFORM_MANUF_NAME, + PLATFORM_MANUF_COUNTRY, + PLATFORM_VENDOR_NAME, + MAILBOX_DIR, +) + +try: + from eepromutil.fru import ipmifru +except Exception or SystemExit: + print("error") + +import logging.handlers +import shutil +import gzip +import glob + +__all__ = [ + "MENUID", + "MENUPARENT", + "MENUVALUE", + "CHILDID", + "MENUITEMNAME", + "MENUITEMDEAL", + "GOBACK", + "GOQUIT", + "file_name", + "CRITICAL", + "FATAL", + "ERROR", + "WARNING", + "WARN", + "INFO", + "DEBUG", + "NOTSET", + "levelNames", + "TLV_INFO_ID_STRING", + "TLV_INFO_VERSION", + "TLV_INFO_LENGTH", + "TLV_INFO_LENGTH_VALUE", + "TLV_CODE_PRODUCT_NAME", + "TLV_CODE_PART_NUMBER", + "TLV_CODE_SERIAL_NUMBER", + "TLV_CODE_MAC_BASE", + "TLV_CODE_MANUF_DATE", + "TLV_CODE_DEVICE_VERSION", + "TLV_CODE_LABEL_REVISION", + "TLV_CODE_PLATFORM_NAME", + "TLV_CODE_ONIE_VERSION", + "TLV_CODE_MAC_SIZE", + "TLV_CODE_MANUF_NAME", + "TLV_CODE_MANUF_COUNTRY", + "TLV_CODE_VENDOR_NAME", + "TLV_CODE_DIAG_VERSION", + "TLV_CODE_SERVICE_TAG", + "TLV_CODE_VENDOR_EXT", + "TLV_CODE_CRC_32", + "_TLV_DISPLAY_VENDOR_EXT", + "TLV_CODE_CARID", + "_TLV_INFO_HDR_LEN", + "SYSLOG_IDENTIFIER", + "log_info", + "log_debug", + "log_warning", + "log_error", + "CompressedRotatingFileHandler", + "SETMACException", + "checkinput", + "checkinputproduct", + "getInputSetmac", + "fan_tlv", + "AVSUTIL", + "I2CUTIL", + "BMC", + "getSdkReg", + "getfilevalue", + "get_sysfs_value", + "write_sysfs_value", + "PRINTERR", + "strtoint", + "inttostr", + "str_to_hex", + "hex_to_str", + "str_to_bin", + "bin_to_str", + "get_mac_temp", + "get_mac_temp_sysfs", + "restartDockerService", + "wait_dhcp", + "wait_sdk", + "wait_docker", + "getTLV_BODY", + "_crc32", + "printvalue", + "generate_value", + "getsyseeprombyId", + "fac_init_cardidcheck", + "isValidMac", + "util_setmac", + "getInputCheck", + "getrawch", + "upper_input", + "astrcmp", + "generate_ext", + "wbi2cget", + "wbi2cset", + "wbpcird", + "wbpciwr", + "wbsysset", + "wbi2cget_word", + "wbi2cset_word", + "fan_setmac", + "checkfansninput", + "checkfanhwinput", + "util_show_fanse2", + "get_fane2_sysfs", + "util_show_fane2", + "getPid", + "fac_fans_setmac_tlv", + "fac_fan_setmac_fru", + "fac_fans_setmac", + "fac_fan_setmac", + "writeToEEprom", + "get_local_eth0_mac", + "getonieversion", + "createbmcMac", + "ipmi_set_mac", + "getInputValue", + "bmc_setmac", + "closeProtocol", + "checkSdkMem", + "getch", + "get_raw_input", + "getsysvalue", + "get_pmc_register", + "decoder", + "decode_eeprom", + "get_sys_eeprom", + "getCardId", + "getsysmeminfo", + "getsysmeminfo_detail", + "getDmiSysByType", + "gethwsys", + "getsysbios", + "searchDirByName", + "getUsbLocation", + "getusbinfo", + "get_cpu_info", + "io_rd", + "io_wr", +] + +MENUID = "menuid" +MENUPARENT = "parentid" +MENUVALUE = "value" +CHILDID = "childid" +MENUITEMNAME = "name" +MENUITEMDEAL = "deal" +GOBACK = "goBack" +GOQUIT = "quit" + +file_name = "/etc/init.d/opennsl-modules-3.16.0-5-amd64" +########################################################################## +# ERROR LOG LEVEL +########################################################################## +CRITICAL = 50 +FATAL = CRITICAL +ERROR = 40 +WARNING = 30 +WARN = WARNING +INFO = 20 +DEBUG = 10 +NOTSET = 0 + +levelNames = { + CRITICAL: "CRITICAL", + ERROR: "ERROR", + WARNING: "WARNING", + INFO: "INFO", + DEBUG: "DEBUG", + NOTSET: "NOTSET", + "CRITICAL": CRITICAL, + "ERROR": ERROR, + "WARN": WARNING, + "WARNING": WARNING, + "INFO": INFO, + "DEBUG": DEBUG, + "NOTSET": NOTSET, +} + +TLV_INFO_ID_STRING = "TlvInfo\x00" +TLV_INFO_VERSION = 0x01 +TLV_INFO_LENGTH = 0x00 +TLV_INFO_LENGTH_VALUE = 0xBA + +########################################################################## +# eeprom info +########################################################################## +TLV_CODE_PRODUCT_NAME = 0x21 +TLV_CODE_PART_NUMBER = 0x22 +TLV_CODE_SERIAL_NUMBER = 0x23 +TLV_CODE_MAC_BASE = 0x24 +TLV_CODE_MANUF_DATE = 0x25 +TLV_CODE_DEVICE_VERSION = 0x26 +TLV_CODE_LABEL_REVISION = 0x27 +TLV_CODE_PLATFORM_NAME = 0x28 +TLV_CODE_ONIE_VERSION = 0x29 +TLV_CODE_MAC_SIZE = 0x2A +TLV_CODE_MANUF_NAME = 0x2B +TLV_CODE_MANUF_COUNTRY = 0x2C +TLV_CODE_VENDOR_NAME = 0x2D +TLV_CODE_DIAG_VERSION = 0x2E +TLV_CODE_SERVICE_TAG = 0x2F +TLV_CODE_VENDOR_EXT = 0xFD +TLV_CODE_CRC_32 = 0xFE +_TLV_DISPLAY_VENDOR_EXT = 1 +TLV_CODE_CARID = 0x01 +_TLV_INFO_HDR_LEN = 11 + + +SYSLOG_IDENTIFIER = "UTILTOOL" + +# ========================== Syslog wrappers ========================== + + +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: + click.echo(msg) + + +def log_debug(msg, also_print_to_console=False): + try: + syslog.openlog(SYSLOG_IDENTIFIER) + syslog.syslog(syslog.LOG_DEBUG, msg) + syslog.closelog() + + if also_print_to_console: + click.echo(msg) + except Exception as e: + print(str(e)) + + +def log_warning(msg, also_print_to_console=False): + syslog.openlog(SYSLOG_IDENTIFIER) + syslog.syslog(syslog.LOG_WARNING, msg) + syslog.closelog() + + if also_print_to_console: + click.echo(msg) + + +def log_error(msg, also_print_to_console=False): + syslog.openlog(SYSLOG_IDENTIFIER) + syslog.syslog(syslog.LOG_ERR, msg) + syslog.closelog() + + if also_print_to_console: + click.echo(msg) + + +class CompressedRotatingFileHandler(logging.handlers.RotatingFileHandler): + def doRollover(self): + """ + Do a rollover, as described in __init__(). + """ + if self.stream: + self.stream.close() + self.stream = None + if self.backupCount > 0: + for i in range(self.backupCount - 1, 0, -1): + sfn = "%s.%d.gz" % (self.baseFilename, i) + dfn = "%s.%d.gz" % (self.baseFilename, i + 1) + if os.path.exists(sfn): + if os.path.exists(dfn): + os.remove(dfn) + os.rename(sfn, dfn) + dfn = self.baseFilename + ".1.gz" + if os.path.exists(dfn): + os.remove(dfn) + # These two lines below are the only new lines. I commented out the os.rename(self.baseFilename, dfn) and + # replaced it with these two lines. + with open(self.baseFilename, "rb") as f_in, gzip.open(dfn, "wb") as f_out: + shutil.copyfileobj(f_in, f_out) + self.mode = "w" + self.stream = self._open() + + +class SETMACException(Exception): + def __init__(self, param="ERROR", errno="-1"): + err = "Setmac fail[%s]: %s" % (errno, param) + Exception.__init__(self, err) + self.param = param + self.errno = errno + + +def checkinput(b): + if b.isdigit() == False: + raise Exception("Ivalid Number") + if int(b) > 0xFF or int(b) < 0: + raise Exception("Out of area") + + +def checkinputproduct(b): + if b.isalnum() == False: + raise Exception("Invalid string") + + +def getInputSetmac(val): + bia = val.boardInfoArea + pia = val.productInfoArea + if bia != None: + a = raw_input("[Board Card]Product Serial Number:") + if len(a) != 13: + raise Exception("Invalid Serial Number length") + checkinputproduct(a) + bia.boardSerialNumber = a + b = raw_input("[Board Card]Product Version:(from 1-255)") + checkinput(b) + b = "%0x" % int(b) + bia.boardextra1 = b.upper() + if pia != None: + a = raw_input("[Product Area]Product Serial Number:") + if len(a) != 13: + raise Exception("Invalid Serial Number") + checkinputproduct(a) + pia.productSerialNumber = a + b = raw_input("[Product Area]Product Version:(from 1-255)") + checkinput(b) + b = "%0x" % int(b) + pia.productVersion = b.upper() + return val + + +class fan_tlv(object): + VERSION = 0x01 # E2PROM Version, start from 0x01 + FLAG = 0x7E # New E2PROM version flag is 0x7E + HW_VER = 0x01 # compose by master version and fixed version + TYPE = 0xF1 # hw type defination + TLV_LEN = 00 # data length (16bit) + _FAN_TLV_HDR_LEN = 6 + _FAN_TLV_CRC_LEN = 2 + + _FAN_TLV_TYPE_NAME = 0x02 + _FAN_TLV_TYPE_SN = 0x03 + _FAN_TLV_TYPE_HW_INFO = 0x05 + _FAN_TLV_TYPE_DEV_TYPE = 0x06 + + _fandecodetime = 0 + + @property + def dstatus(self): + return self._dstatus + + @property + def typename(self): + return self._typename + + @property + def typesn(self): + return self._typesn + + @property + def typehwinfo(self): + return self._typehwinfo + + @property + def typedevtype(self): + return self._typedevtype + + @property + def fanbus(self): + return self._fanbus + + @property + def fanloc(self): + return self._fanloc + + @property + def fandecodetime(self): + return self._fandecodetime + + def __init__(self): + self._typename = "" + self._typesn = "" + self._typehwinfo = "" + self._typedevtype = "" + self._dstatus = 0 + + def strtoarr(self, str): + s = [] + if str is not None: + for index in range(len(str)): + s.append(str[index]) + return s + + def generate_fan_value(self): + bin_buffer = [chr(0xFF)] * 256 + bin_buffer[0] = chr(self.VERSION) + bin_buffer[1] = chr(self.FLAG) + bin_buffer[2] = chr(self.HW_VER) + bin_buffer[3] = chr(self.TYPE) + + temp_t = "%08x" % self.typedevtype # handle devtype first + typedevtype_t = hex_to_str(temp_t) + total_len = ( + len(self.typename) + + len(self.typesn) + + len(self.typehwinfo) + + len(typedevtype_t) + + 8 + ) + + bin_buffer[4] = chr(total_len >> 8) + bin_buffer[5] = chr(total_len & 0x00FF) + + index_start = 6 + bin_buffer[index_start] = chr(self._FAN_TLV_TYPE_NAME) + bin_buffer[index_start + 1] = chr(len(self.typename)) + bin_buffer[ + index_start + 2 : index_start + 2 + len(self.typename) + ] = self.strtoarr(self.typename) + index_start = index_start + 2 + len(self.typename) + + bin_buffer[index_start] = chr(self._FAN_TLV_TYPE_SN) + bin_buffer[index_start + 1] = chr(len(self.typesn)) + bin_buffer[ + index_start + 2 : index_start + 2 + len(self.typesn) + ] = self.strtoarr(self.typesn) + index_start = index_start + 2 + len(self.typesn) + + bin_buffer[index_start] = chr(self._FAN_TLV_TYPE_HW_INFO) + bin_buffer[index_start + 1] = chr(len(self.typehwinfo)) + bin_buffer[ + index_start + 2 : index_start + 2 + len(self.typehwinfo) + ] = self.strtoarr(self.typehwinfo) + index_start = index_start + 2 + len(self.typehwinfo) + + bin_buffer[index_start] = chr(self._FAN_TLV_TYPE_DEV_TYPE) + bin_buffer[index_start + 1] = chr(len(typedevtype_t)) + bin_buffer[ + index_start + 2 : index_start + 2 + len(typedevtype_t) + ] = self.strtoarr(typedevtype_t) + index_start = index_start + 2 + len(typedevtype_t) + + crcs = fan_tlv.fancrc("".join(bin_buffer[0:index_start])) # check 2bytes + bin_buffer[index_start] = chr(crcs >> 8) + bin_buffer[index_start + 1] = chr(crcs & 0x00FF) + return bin_buffer + + def decode(self, e2): + ret = [] + self.VERSION = ord(e2[0]) + self.FLAG = ord(e2[1]) + self.HW_VER = ord(e2[2]) + self.TYPE = ord(e2[3]) + self.TLV_LEN = (ord(e2[4]) << 8) | ord(e2[5]) + + tlv_index = self._FAN_TLV_HDR_LEN + tlv_end = self._FAN_TLV_HDR_LEN + self.TLV_LEN + + # check checksum + if len(e2) < self._FAN_TLV_HDR_LEN + self.TLV_LEN + 2: + self._dstatus = -2 + return ret + sumcrc = fan_tlv.fancrc(e2[0 : self._FAN_TLV_HDR_LEN + self.TLV_LEN]) + readcrc = ord(e2[self._FAN_TLV_HDR_LEN + self.TLV_LEN]) << 8 | ord( + e2[self._FAN_TLV_HDR_LEN + self.TLV_LEN + 1] + ) + if sumcrc != readcrc: + self._dstatus = -1 + return ret + else: + self._dstatus = 0 + while (tlv_index + 2) < len(e2) and tlv_index < tlv_end: + s = self.decoder(e2[tlv_index : tlv_index + 2 + ord(e2[tlv_index + 1])]) + tlv_index += ord(e2[tlv_index + 1]) + 2 + ret.append(s) + + return ret + + @staticmethod + def fancrc(t): + sum = 0 + for index in range(len(t)): + sum += ord(t[index]) + return sum + + def decoder(self, t): + try: + name = "" + value = "" + if ord(t[0]) == self._FAN_TLV_TYPE_NAME: + name = "Product Name" + value = str(t[2 : 2 + ord(t[1])]) + self._typename = value + elif ord(t[0]) == self._FAN_TLV_TYPE_SN: + name = "serial Number" + value = str(t[2 : 2 + ord(t[1])]) + self._typesn = value + elif ord(t[0]) == self._FAN_TLV_TYPE_HW_INFO: + name = "hardware info" + value = str(t[2 : 2 + ord(t[1])]) + self._typehwinfo = value + elif ord(t[0]) == self._FAN_TLV_TYPE_DEV_TYPE: + name = "dev type" + value = str(t[2 : 2 + ord(t[1])]) + value = str_to_hex(value) + self._typedevtype = value + value = "0x08%x" % value + except Exception as e: + print(e) + return {"name": name, "code": ord(t[0]), "value": value} + + def __str__(self): + formatstr = ( + "VERSION : 0x%02x \n" + " FLAG : 0x%02x \n" + " HW_VER : 0x%02x \n" + " TYPE : 0x%02x \n" + "typename : %s \n" + "typesn : %s \n" + "typehwinfo : %s \n" + ) + return formatstr % ( + self.VERSION, + self.FLAG, + self.HW_VER, + self.TYPE, + self.typename, + self.typesn, + self.typehwinfo, + ) + + +class AVSUTIL: + @staticmethod + def mac_avs_chip(bus, devno, loc, open, close, loop, protectaddr, level, loopaddr): + # disable protection + wbi2cset(bus, devno, protectaddr, open) + wbi2cset(bus, devno, loopaddr, loop) + wbi2cset_word(bus, devno, loc, level) + ret, value = wbi2cget_word(bus, devno, loc) + if strtoint(value) == level: + ret = 0 + # enable protection + wbi2cset(bus, devno, protectaddr, close) + if ret == 0: + return True + return False + + @staticmethod + def macPressure_adj(macavs, avs_param, mac_def_param): + # check whether it within range + max_adj = max(avs_param.keys()) + min_adj = min(avs_param.keys()) + type = mac_def_param["type"] + level = 0 + if type == 0: + if macavs not in range(min_adj, max_adj + 1): + return False + else: + level = macavs + else: + if macavs not in range(min_adj, max_adj + 1): + level = mac_def_param["default"] + else: + level = macavs + ret = AVSUTIL.mac_avs_chip( + mac_def_param["bus"], + mac_def_param["devno"], + mac_def_param["addr"], + mac_def_param["open"], + mac_def_param["close"], + mac_def_param["loop"], + mac_def_param["protectaddr"], + avs_param[level], + mac_def_param["loopaddr"], + ) + return ret + + @staticmethod + def mac_adj(): + macavs = 0 + name = MAC_DEFAULT_PARAM["sdkreg"] + ret, status = getSdkReg(name) + if ret == False: + return False + status = strtoint(status) + # shift operation + if MAC_DEFAULT_PARAM["sdktype"] != 0: + status = (status >> MAC_DEFAULT_PARAM["macregloc"]) & MAC_DEFAULT_PARAM[ + "mask" + ] + macavs = status + ret = AVSUTIL.macPressure_adj(macavs, MAC_AVS_PARAM, MAC_DEFAULT_PARAM) + return ret + + +class I2CUTIL: + @staticmethod + def getvaluefromdevice(name): + ret = [] + for item in DEVICE: + if item["name"] == name: + ret.append(item) + return ret + + @staticmethod + def openFanE2Protect(): + wbi2cset( + FAN_PROTECT["bus"], + FAN_PROTECT["devno"], + FAN_PROTECT["addr"], + FAN_PROTECT["open"], + ) + + @staticmethod + def closeFanE2Protect(): + wbi2cset( + FAN_PROTECT["bus"], + FAN_PROTECT["devno"], + FAN_PROTECT["addr"], + FAN_PROTECT["close"], + ) + + @staticmethod + def writeToFanE2(bus, loc, rst_arr): + index = 0 + for item in rst_arr: + wbi2cset(bus, loc, index, ord(item)) + index += 1 + + @staticmethod + def writeToE2(bus, loc, rst_arr): + index = 0 + for item in rst_arr: + wbi2cset(bus, loc, index, ord(item)) + index += 1 + + @staticmethod + def getE2File(bus, loc): + return "/sys/bus/i2c/devices/%d-00%02x/eeprom" % (bus, loc) + + +class BMC(): + _instance_lock = threading.Lock() + + def __init__(self): + pass + + def __new__(cls, *args, **kwargs): + if not hasattr(Singleton, "_instance"): + with Singleton._instance_lock: + if not hasattr(Singleton, "_instance"): + Singleton._instance = object.__new__(cls) + return Singleton._instance + + +# Internal interface + + +def getSdkReg(reg): + try: + cmd = "bcmcmd -t 1 'getr %s ' < /dev/null" % reg + ret, result = os_system(cmd) + result_t = result.strip().replace("\r", "").replace("\n", "") + if ret != 0 or "Error:" in result_t: + return False, result + patt = r"%s.(.*):(.*)>drivshell" % reg + rt = re.findall(patt, result_t, re.S) + test = re.findall("=(.*)", rt[0][0])[0] + except Exception as e: + return False, "getsdk register error" + return True, test + + +def getfilevalue(location): + try: + with open(location, "r") as fd: + value = fd.read() + return True, value.strip() + except Exception as e: + return False, "error" + + +def get_sysfs_value(location): + pos_t = str(location) + name = get_pmc_register(pos_t) + return name + + +def write_sysfs_value(reg_name, value): + fileLoc = MAILBOX_DIR + reg_name + try: + if not os.path.isfile(fileLoc): + print(fileLoc, "not found !") + return False + with open(fileLoc, "w") as fd: + fd.write(value) + except Exception as error: + log_error("Unable to open " + fileLoc + "file !") + return False + return True + + +def PRINTERR(str): + print("\033[0;31m%s\033[0m" % str) + + +def strtoint(str): # convert Hex string to int such as "4040"/"0x4040"/"0X4040" = 16448 + value = 0 + rest_v = str.replace("0X", "").replace("0x", "") + for index in range(len(rest_v)): + value |= int(rest_v[index], 16) << ((len(rest_v) - index - 1) * 4) + return value + + +def inttostr(vl, len): # convert int to string such as 0x3030 = 00 + if type(vl) != int: + raise Exception(" type error") + index = 0 + ret_t = "" + while index < len: + ret = 0xFF & (vl >> index * 8) + ret_t += chr(ret) + index += 1 + return ret_t + + +def str_to_hex(rest_v): + value = 0 + for index in range(len(rest_v)): + value |= ord(rest_v[index]) << ((len(rest_v) - index - 1) * 8) + return value + + +def hex_to_str(s): + len_t = len(s) + if len_t % 2 != 0: + return 0 + ret = "" + for t in range(0, int(len_t / 2)): + ret += chr(int(s[2 * t : 2 * t + 2], 16)) + return ret + + +def str_to_bin(s): + return " ".join([bin(ord(c)).replace("0b", "") for c in s]) + + +def bin_to_str(s): + return "".join([chr(i) for i in [int(b, 2) for b in s.split(" ")]]) + + +def get_mac_temp(): + result = {} + # wait_docker() + # exec twice, get the second result + os_system('bcmcmd -t 1 "show temp" < /dev/null') + ret, log = os_system('bcmcmd -t 1 "show temp" < /dev/null') + if ret: + return False, result + else: + # decode obtained info + logs = log.splitlines() + for line in logs: + if "average" in line: + b = re.findall(r"\d+.\d+", line) + result["average"] = b[0] + elif "maximum" in line: + b = re.findall(r"\d+.\d+", line) + result["maximum"] = b[0] + return True, result + + +def get_mac_temp_sysfs(mactempconf): + try: + temp = -1000000 + temp_list = [] + mac_temp_loc = mactempconf.get("loc", []) + mac_temp_flag = mactempconf.get("flag", None) + if mac_temp_flag is not None: # check mac temperature vaild flag + gettype = mac_temp_flag.get("gettype") + okbit = mac_temp_flag.get("okbit") + okval = mac_temp_flag.get("okval") + if gettype == "io": + io_addr = mac_temp_flag.get("io_addr") + val = io_rd(io_addr) + if val is None: + raise Exception("get mac_flag by io failed.") + else: + bus = mac_temp_flag.get("bus") + loc = mac_temp_flag.get("loc") + offset = mac_temp_flag.get("offset") + ind, val = wbi2cget(bus, loc, offset) + if ind is not True: + raise Exception("get mac_flag by i2c failed.") + val_t = (int(val, 16) & (1 << okbit)) >> okbit + if val_t != okval: + raise Exception("mac_flag invalid, val_t:%d." % val_t) + for loc in mac_temp_loc: + temp_s = get_sysfs_value(loc) + if isinstance(temp_s, str) and temp_s.startswith("ERR"): + raise Exception("get mac temp error. loc:%s" % loc) + temp_t = int(temp_s) + if temp_t == -1000000: + raise Exception("mac temp invalid.loc:%s" % loc) + temp_list.append(temp_t) + temp_list.sort(reverse=True) + temp = temp_list[0] + except Exception as e: + return False, temp + return True, temp + + +def restartDockerService(force=False): + container_name = [ + "database", + "snmp", + "syncd", + "swss", + "dhcp_relay", + "radv", + "teamd", + "pmon", + ] + ret, status = os_system("docker ps") + if ret == 0: + for tmpname in container_name: + if tmpname not in status: + if force == True: + os_system("docker restart %s" % tmpname) + else: + os_system("systemctl restart %s" % tmpname) + + +def wait_dhcp(timeout): + time_cnt = 0 + while True: + try: + ret, status = os_system("systemctl status dhcp_relay.service") + if (ret == 0 and "running" in status) or "SUCCESS" in status: + break + else: + sys.stdout.write(".") + sys.stdout.flush() + time_cnt = time_cnt + 1 + if time_cnt > timeout: + raise Exception("wait_dhcp timeout") + time.sleep(1) + except Exception as e: + return False + return True + + +def wait_sdk(sdk_fpath, timeout): + time_cnt = 0 + while True: + try: + if os.path.exists(sdk_fpath): + break + else: + sys.stdout.write(".") + sys.stdout.flush() + time_cnt = time_cnt + 1 + if time_cnt > timeout: + raise Exception("wait_sdk timeout") + time.sleep(1) + except Exception as e: + return False + return True + + +def wait_docker(need_restart=False, timeout=180): + sdkcheck_params = STARTMODULE.get("sdkcheck", {}) + if sdkcheck_params.get("checktype") == "file": # pass file check + sdk_fpath = sdkcheck_params.get("sdk_fpath") + return wait_sdk(sdk_fpath, timeout) + return wait_dhcp(timeout) + + +def getTLV_BODY(type, productname): + x = [] + temp_t = "" + if type == TLV_CODE_MAC_BASE: + arr = productname.split(":") + for tt in arr: + temp_t += chr(int(tt, 16)) + elif type == TLV_CODE_DEVICE_VERSION: + temp_t = chr(productname) + elif type == TLV_CODE_MAC_SIZE: + temp_t = chr(productname >> 8) + chr(productname & 0x00FF) + else: + temp_t = productname + x.append(chr(type)) + x.append(chr(len(temp_t))) + for i in temp_t: + x.append(i) + return x + + +def _crc32(v): + return "0x%08x" % ( + binascii.crc32(v) & 0xFFFFFFFF + ) # get 8 bytes of crc32 %x return hex + + +def printvalue(b): + index = 0 + for i in range(0, len(b)): + if index % 16 == 0: + print(" ") + print("%02x " % ord(b[i])) + index += 1 + print("\n") + + +def generate_value(_t): + ret = [] + for i in TLV_INFO_ID_STRING: + ret.append(i) + ret.append(chr(TLV_INFO_VERSION)) + ret.append(chr(TLV_INFO_LENGTH)) + ret.append(chr(TLV_INFO_LENGTH_VALUE)) + + total_len = 0 + for key in _t: + x = getTLV_BODY(key, _t[key]) + ret += x + total_len += len(x) + ret[10] = chr(total_len + 6) + + ret.append(chr(0xFE)) + ret.append(chr(0x04)) + s = _crc32("".join(ret)) + for t in range(0, 4): + ret.append(chr(int(s[2 * t + 2 : 2 * t + 4], 16))) + totallen = len(ret) + if totallen < 256: + for left_t in range(0, 256 - totallen): + ret.append(chr(0x00)) + return (ret, True) + + +def getsyseeprombyId(id): + ret = get_sys_eeprom() + for item in ret: + if item["code"] == id: + return item + return None + + +def fac_init_cardidcheck(): + rest = getsyseeprombyId(TLV_CODE_CARID) # check cardId same or not + if rest is None: + print("need to program write bin file") + return False + else: + rest_v = rest["value"] + value = strtoint(rest_v) + if value == PLATFORM_CARDID: + log_debug("check card ID pass") + else: + log_debug("check card ID error") + return False + return True + + +def isValidMac(mac): + if re.match(r"^\s*([0-9a-fA-F]{2,2}:){5,5}[0-9a-fA-F]{2,2}\s*$", mac): + return True + return False + + +# Internet cardsetmac + + +def util_setmac(eth, mac): + rulefile = "/etc/udev/rules.d/70-persistent-net.rules" + if isValidMac(mac) == False: + return False, "MAC invaild" + cmd = "ethtool -e %s | grep 0x0010 | awk '{print \"0x\"$13$12$15$14}'" % eth + ret, log = os_system(cmd) + log_debug(log) + magic = "" + if ret == 0 and len(log): + magic = log + macs = mac.upper().split(":") + + # chage ETH0 to value after setmac + ifconfigcmd = "ifconfig eth0 hw ether %s" % mac + log_debug(ifconfigcmd) + ret, status = os_system(ifconfigcmd) + if ret: + raise SETMACException("software set Internet card MAC error") + index = 0 + for item in macs: + cmd = "ethtool -E %s magic %s offset %d value 0x%s" % (eth, magic, index, item) + log_debug(cmd) + index += 1 + ret, log = os_system(cmd) + if ret != 0: + raise SETMACException("set hardware Internet card MAC error") + # get value after setting + cmd_t = "ethtool -e eth0 offset 0 length 6" + ret, log = os_system(cmd_t) + m = re.split(":", log)[-1].strip().upper() + mac_result = m.upper().split(" ") + + for ind, s in enumerate(macs): + if s != mac_result[ind]: + PRINTERR("MAC comparison error") + if os.path.exists(rulefile): + os.remove(rulefile) + print("MGMT MAC[%s]" % mac) + return True + + +def getInputCheck(tips): + str = raw_input(tips) + if ( + astrcmp(str, "y") + or astrcmp(str, "ye") + or astrcmp(str, "yes") + or astrcmp(str, "") + ): + return True + else: + return False + + +def getrawch(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(sys.stdin.fileno()) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + +def upper_input(tips): + sys.stdout.write(tips) + sys.stdout.flush() + passwd = [] + while True: + ch = getrawch().upper() + if ch == "\r" or ch == "\n": + return "".join(passwd) + elif ch == "\b" or ord(ch) == 127: + if passwd: + del passwd[-1] + sys.stdout.write("\b \b") + else: + sys.stdout.write(ch) + passwd.append(ch) + +def astrcmp(str1, str2): + return str1.lower() == str2.lower() + + +def generate_ext(cardid): + s = "%08x" % cardid + ret = "" + for t in range(0, 4): + ret += chr(int(s[2 * t : 2 * t + 2], 16)) + ret = chr(0x01) + chr(len(ret)) + ret + return ret + + +def wbi2cget(bus, devno, address): + command_line = "i2cget -f -y %d 0x%02x 0x%02x " % (bus, devno, address) + retrytime = 6 + ret_t = "" + for i in range(retrytime): + ret, ret_t = os_system(command_line) + if ret == 0: + return True, ret_t + time.sleep(0.1) + return False, ret_t + + +def wbi2cset(bus, devno, address, byte): + command_line = "i2cset -f -y %d 0x%02x 0x%02x 0x%02x" % (bus, devno, address, byte) + retrytime = 6 + ret_t = "" + for i in range(retrytime): + ret, ret_t = os_system(command_line) + if ret == 0: + return True, ret_t + return False, ret_t + + +def wbpcird(pcibus, slot, fn, bar, offset): + """read pci register""" + if offset % 4 != 0: + return False + filename = "/sys/bus/pci/devices/0000:%02x:%02x.%x/resource%d" % (int(pcibus), int(slot), int(fn), int(bar)) + with open(filename, "r+") as file: + size = os.path.getsize(filename) + data = mmap.mmap(file.fileno(), size) + result = data[offset: offset + 4] + s = result[::-1] + val = 0 + for i in range(0, len(s)): + val = val << 8 | ord(s[i]) + data.close() + return "0x%08x" % val + + +def wbpciwr(pcibus, slot, fn, bar, offset, data): + """write pci register""" + ret = inttostr(data, 4) + filename = "/sys/bus/pci/devices/0000:%02x:%02x.%x/resource%d" % (int(pcibus), int(slot), int(fn), int(bar)) + with open(filename, "r+") as file: + size = os.path.getsize(filename) + data = mmap.mmap(file.fileno(), size) + data[offset: offset + 4] = ret + result = data[offset: offset + 4] + s = result[::-1] + val = 0 + for i in range(0, len(s)): + val = val << 8 | ord(s[i]) + data.close() + + +def wbsysset(location, value): + command_line = "echo 0x%02x > %s" % (value, location) + retrytime = 6 + ret_t = "" + for i in range(retrytime): + ret, ret_t = os_system(command_line) + if ret == 0: + return True, ret_t + return False, ret_t + + +def wbi2cget_word(bus, devno, address): + command_line = "i2cget -f -y %d 0x%02x 0x%02x w" % (bus, devno, address) + retrytime = 3 + ret_t = "" + for i in range(retrytime): + ret, ret_t = os_system(command_line) + if ret == 0: + return True, ret_t + return False, ret_t + + +def wbi2cset_word(bus, devno, address, byte): + command_line = "i2cset -f -y %d 0x%02x 0x%02x 0x%x w" % (bus, devno, address, byte) + os_system(command_line) + + +def fan_setmac(): + wbi2cset( + FAN_PROTECT["bus"], + FAN_PROTECT["devno"], + FAN_PROTECT["addr"], + FAN_PROTECT["open"], + ) + wbi2cset( + FAN_PROTECT["bus"], + FAN_PROTECT["devno"], + FAN_PROTECT["addr"], + FAN_PROTECT["close"], + ) + + +def checkfansninput(fan_sn, fansntemp): + if fan_sn in fansntemp: + PRINTERR("exist same Serial Number, please input again") + return False + if len(fan_sn) != 13: + PRINTERR("Serial Number length incorrect, please input again") + return False + return True + + +# check hw version +def checkfanhwinput(hw): + if len(hw) != 4: + PRINTERR("hardware version length incorrect, please input again") + return False + if hw.find(".") != 1: + PRINTERR("hardware version incorrect, please input again") + return False + return True + + +def util_show_fanse2(fans): + formatstr = "%-8s %-20s %-20s %-20s %-20s" + print(formatstr % ("id", "Name", "hardware version", "Serial Number", "Time")) + print( + formatstr + % ("------", "---------------", "---------------", "---------------", "----") + ) + for fan in fans: + # print fan.dstatus + if fan.dstatus < 0: + print("%-8s" % ("FAN%d" % (fans.index(fan) + 1))) + PRINTERR(" decode e2 error") + else: + print( + formatstr + % ( + "FAN%d" % (fans.index(fan) + 1), + fan.typename.replace(chr(0x00), ""), + fan.typehwinfo.replace(chr(0x00), ""), + fan.typesn.replace(chr(0x00), ""), + fan.fandecodetime, + ) + ) + + +def get_fane2_sysfs(bus, loc): + wb_fan_e2 = "%d-%04x/fan" % (bus, loc) + eeprom = get_sysfs_value(wb_fan_e2) + return eeprom + + +def util_show_fane2(): + ret = sorted(I2CUTIL.getvaluefromdevice("wb_fan")) + if len(ret) <= 0: + return None + fans = [] + for index in range(len(ret)): + t1 = int(round(time.time() * 1000)) + eeprom = get_fane2_sysfs(ret[index]["bus"], ret[index]["loc"]) + t2 = int(round(time.time() * 1000)) + fane2 = fan_tlv() + fane2.fandecodetime = t2 - t1 + fane2.decode(eeprom) + fans.append(fane2) + util_show_fanse2(fans) + + +def getPid(name): + ret = [] + for dirname in os.listdir("/proc"): + if dirname == "curproc": + continue + try: + with open("/proc/{}/cmdline".format(dirname), mode="rb") as fd: + content = fd.read() + except Exception: + continue + if name in content: + ret.append(dirname) + return ret + + +def fac_fans_setmac_tlv(ret): + if len(ret) <= 0: + return False + fans = [] + fansntemp = [] + for index in range(len(ret)): + item = ret[index] + log_debug(item) + eeprom = get_fane2_sysfs(item["bus"], item["loc"]) + fane2 = fan_tlv() + fane2.decode(eeprom) + fane2.fanbus = item["bus"] + fane2.fanloc = item["loc"] + log_debug("decode eeprom success") + + print("Fan[%d]-[%s]setmac" % ((index + 1), FANS_DEF[fane2.typedevtype])) + while True: + print("Please input[%s]:" % "Serial Number") + fan_sn = raw_input() + if checkfansninput(fan_sn, fansntemp) == False: + continue + fansntemp.append(fan_sn) + fan_sn = fan_sn + chr(0x00) + fane2.typesn = fan_sn + chr(0x00) + break + while True: + print("Please input[%s]:" % "hardware version") + hwinfo = raw_input() + if checkfanhwinput(hwinfo) == False: + continue + fan_hwinfo = hwinfo + chr(0x00) + fane2.typehwinfo = fan_hwinfo + chr(0x00) + break + log_debug(fane2.typedevtype) + fane2.typename = FANS_DEF[fane2.typedevtype] + chr(0x00) + fans.append(fane2) + print("\n") + print("\n*******************************\n") + + util_show_fanse2(fans) + if getInputCheck("check input correctly or not(Yes/No):") == True: + for fan in fans: + log_debug("ouput fan") + fac_fan_setmac(fan) + else: + print("setmac quit") + return False + + +def fac_fan_setmac_fru(ret): + fans = FRULISTS.get("fans") + + fanfrus = {} + newfrus = {} + + # getmsg + try: + for fan in fans: + print("===============%s ================getmessage" % fan.get("name")) + eeprom = getsysvalue(I2CUTIL.getE2File(fan.get("bus"), fan.get("loc"))) + fru = ipmifru() + fru.decodeBin(eeprom) + fanfrus[fan.get("name")] = fru + except Exception as e: + print(str(e)) + return False + + # setmsg + for fan in fans: + print("===============%s ================setmac" % fan.get("name")) + fruold = fanfrus.get(fan.get("name")) + newfru = getInputSetmac(fruold) + newfru.recalcute() + newfrus[fan.get("name")] = newfru + # writemsg + for fan in fans: + print("===============%s ================writeToE2" % fan.get("name")) + ret_t = newfrus.get(fan.get("name")) + I2CUTIL.openFanE2Protect() + I2CUTIL.writeToFanE2(fan.get("bus"), fan.get("loc"), ret_t.bindata) + I2CUTIL.closeFanE2Protect() + # check + try: + for fan in fans: + print("===============%s ================getmessage" % fan.get("name")) + eeprom = getsysvalue(I2CUTIL.getE2File(fan.get("bus"), fan.get("loc"))) + fru = ipmifru() + fru.decodeBin(eeprom) + except Exception as e: + print(str(e)) + return False + return True + + +def fac_fans_setmac(): + ret = I2CUTIL.getvaluefromdevice("wb_fan") + if ret is not None and len(ret) > 0: + return fac_fans_setmac_tlv(ret) + fans = FRULISTS.get("fans", None) + if fans is not None and len(fans) > 0: + return fac_fan_setmac_fru(ret) + return False + + +def fac_fan_setmac(item): + I2CUTIL.openFanE2Protect() + I2CUTIL.writeToFanE2(item.fanbus, item.fanloc, item.generate_fan_value()) + I2CUTIL.closeFanE2Protect() + + +def writeToEEprom(rst_arr): + dealtype = E2_PROTECT.get("gettype", None) + if dealtype is None: + wbi2cset( + E2_PROTECT["bus"], + E2_PROTECT["devno"], + E2_PROTECT["addr"], + E2_PROTECT["open"], + ) + elif dealtype == "io": + io_wr(E2_PROTECT["io_addr"], E2_PROTECT["open"]) + index = 0 + for item in rst_arr: + wbi2cset(E2_LOC["bus"], E2_LOC["devno"], index, ord(item)) + index += 1 + + if dealtype is None: + wbi2cset( + E2_PROTECT["bus"], + E2_PROTECT["devno"], + E2_PROTECT["addr"], + E2_PROTECT["close"], + ) + elif dealtype == "io": + io_wr(E2_PROTECT["io_addr"], E2_PROTECT["close"]) + # deal last drivers + os.system("rmmod at24 ") + os.system("modprobe at24 ") + os.system("rm -f /var/cache/sonic/decode-syseeprom/syseeprom_cache") + + +def get_local_eth0_mac(): + cmd = "ifconfig eth0 |grep HWaddr" + print(os_system(cmd)) + + +def getonieversion(): + if not os.path.isfile("/host/machine.conf"): + return "" + machine_vars = {} + with open("/host/machine.conf") as machine_file: + for line in machine_file: + tokens = line.split("=") + if len(tokens) < 2: + continue + machine_vars[tokens[0]] = tokens[1].strip() + return machine_vars.get("onie_version") + + +def createbmcMac(cpumac, num=2): + bcmvalue = strtoint(cpumac[cpumac.rindex(":") + 1 : len(cpumac)]) + num + # bmcmac = + t = cpumac.split(":") + t[5] = "%02x" % bcmvalue + bmcmac = ":".join(t) + return bmcmac.upper() + +def ipmi_set_mac(mac): + macs = mac.split(":") + cmdinit = "ipmitool raw 0x0c 0x01 0x01 0xc2 0x00" + cmdset = "ipmitool raw 0x0c 0x01 0x01 0x05" + for ind in range(len(macs)): + cmdset += " 0x%02x" % int(macs[ind], 16) + os_system(cmdinit) + ret, status = os_system(cmdset) + if ret: + PRINTERR("\n\n%s\n\n" % status) + return False + return True + + +def getInputValue(title, tips): + print("Please input[%s]such as(%s):" % (title, tips)) + name = raw_input() + + return name + + +def bmc_setmac(): + tips = "BMC MAC" + print("Please input value you want to change[%s]:" % tips) + name = raw_input() + if len(name) != 12: + PRINTERR("\nMAC address invaild, try again\n") + return False + release_mac = "" + for i in range(int(len(name) / 2)): + if i == 0: + release_mac += name[i * 2 : i * 2 + 2] + else: + release_mac += ":" + name[i * 2 : i * 2 + 2] + if isValidMac(release_mac) == True: + if ipmi_set_mac(release_mac) == True: + return True + else: + PRINTERR("\nMAC address invaild, try again\n") + return False + + +def closeProtocol(): + # disable LLDP + log_info("disable LLDP") + sys.stdout.write(".") + sys.stdout.flush() + os_system("systemctl stop lldp.service") + log_info("disable lldp service") + sys.stdout.write(".") + sys.stdout.flush() + os_system("systemctl stop bgp.service") + log_info("disable bgp service") + sys.stdout.write(".") + sys.stdout.flush() + # ret, status = os_system('bcmcmd "port ce,xe stp=disable"') + + +# check SDK memory must be 256M + + +def checkSdkMem(): + ind = 0 + file_data = "" + with open(file_name, "r") as f: + for line in f: + if "dmasize=16M" in line: + line = line.replace("dmasize=16M", "dmasize=256M") + ind = -1 + file_data += line + if ind == 0: + return + with open(file_name, "w") as f: + f.write(file_data) + print("change SDK memory to 256, reboot required") + os_system("sync") + os_system("reboot") + + +########################################################################## +# receives a character setting +########################################################################## + + +def getch(msg): + ret = "" + fd = sys.stdin.fileno() + old_ttyinfo = termios.tcgetattr(fd) + new_ttyinfo = old_ttyinfo[:] + new_ttyinfo[3] &= ~termios.ICANON + new_ttyinfo[3] &= ~termios.ECHO + sys.stdout.write(msg) + sys.stdout.flush() + try: + termios.tcsetattr(fd, termios.TCSANOW, new_ttyinfo) + ret = os.read(fd, 1) + finally: + # print "try to setting" + termios.tcsetattr(fd, termios.TCSANOW, old_ttyinfo) + return ret + + +def get_raw_input(): + ret = "" + fd = sys.stdin.fileno() + old_ttyinfo = termios.tcgetattr(fd) + new_ttyinfo = old_ttyinfo[:] + new_ttyinfo[3] &= ~termios.ICANON + new_ttyinfo[3] &= ~termios.ECHO + try: + termios.tcsetattr(fd, termios.TCSANOW, new_ttyinfo) + ret = raw_input("") + except Exception as e: + print(e) + finally: + termios.tcsetattr(fd, termios.TCSANOW, old_ttyinfo) + return ret + + +def getsysvalue(location): + retval = None + mb_reg_file = location + if not os.path.isfile(mb_reg_file): + print(mb_reg_file, "not found !") + return retval + try: + if not os.path.isfile(mb_reg_file): + print(mb_reg_file, "not found !") + return retval + with open(mb_reg_file, "r") as fd: + retval = fd.read() + except Exception as error: + log_error("Unable to open " + mb_reg_file + "file !") + retval = retval.rstrip("\r\n") + retval = retval.lstrip(" ") + # log_debug(retval) + return retval + + +# get file value + + +def get_pmc_register(reg_name): + retval = "ERR" + mb_reg_file = MAILBOX_DIR + reg_name + filepath = glob.glob(mb_reg_file) + if len(filepath) == 0: + return "%s %s notfound" % (retval, mb_reg_file) + mb_reg_file = filepath[0] + if not os.path.isfile(mb_reg_file): + return "%s %s notfound" % (retval, mb_reg_file) + try: + with open(mb_reg_file, "r") as fd: + retval = fd.read() + except Exception as error: + print("error") + retval = retval.rstrip("\r\n") + retval = retval.lstrip(" ") + return retval + + +# decode EEPROM + + +def decoder(s, t): + if ord(t[0]) == TLV_CODE_PRODUCT_NAME: + name = "Product Name" + value = str(t[2 : 2 + ord(t[1])]) + elif ord(t[0]) == TLV_CODE_PART_NUMBER: + name = "Part Number" + value = t[2 : 2 + ord(t[1])] + elif ord(t[0]) == TLV_CODE_SERIAL_NUMBER: + name = "Serial Number" + value = t[2 : 2 + ord(t[1])] + elif ord(t[0]) == TLV_CODE_MAC_BASE: + name = "Base MAC Address" + value = ":".join([binascii.b2a_hex(T) for T in t[2:8]]).upper() + elif ord(t[0]) == TLV_CODE_MANUF_DATE: + name = "Manufacture Date" + value = t[2 : 2 + ord(t[1])] + elif ord(t[0]) == TLV_CODE_DEVICE_VERSION: + name = "Device Version" + value = str(ord(t[2])) + elif ord(t[0]) == TLV_CODE_LABEL_REVISION: + name = "Label Revision" + value = t[2 : 2 + ord(t[1])] + elif ord(t[0]) == TLV_CODE_PLATFORM_NAME: + name = "Platform Name" + value = t[2 : 2 + ord(t[1])] + elif ord(t[0]) == TLV_CODE_ONIE_VERSION: + name = "ONIE Version" + value = t[2 : 2 + ord(t[1])] + elif ord(t[0]) == TLV_CODE_MAC_SIZE: + name = "MAC Addresses" + value = str((ord(t[2]) << 8) | ord(t[3])) + elif ord(t[0]) == TLV_CODE_MANUF_NAME: + name = "Manufacturer" + value = t[2 : 2 + ord(t[1])] + elif ord(t[0]) == TLV_CODE_MANUF_COUNTRY: + name = "Manufacture Country" + value = t[2 : 2 + ord(t[1])] + elif ord(t[0]) == TLV_CODE_VENDOR_NAME: + name = "Vendor Name" + value = t[2 : 2 + ord(t[1])] + elif ord(t[0]) == TLV_CODE_DIAG_VERSION: + name = "Diag Version" + value = t[2 : 2 + ord(t[1])] + elif ord(t[0]) == TLV_CODE_SERVICE_TAG: + name = "Service Tag" + value = t[2 : 2 + ord(t[1])] + elif ord(t[0]) == TLV_CODE_VENDOR_EXT: + name = "Vendor Extension" + value = "" + if _TLV_DISPLAY_VENDOR_EXT: + value = t[2 : 2 + ord(t[1])] + elif ord(t[0]) == TLV_CODE_CRC_32 and len(t) == 6: + name = "CRC-32" + value = "0x%08X" % ( + ((ord(t[2]) << 24) | (ord(t[3]) << 16) | (ord(t[4]) << 8) | ord(t[5])), + ) + elif ord(t[0]) == TLV_CODE_CARID: + name = "board_cardid" + value = "" + for c in t[2 : 2 + ord(t[1])]: + value += "%02X" % (ord(c),) + else: + name = "Unknown" + value = "" + for c in t[2 : 2 + ord(t[1])]: + value += "0x%02X " % (ord(c),) + return {"name": name, "code": ord(t[0]), "value": value} + + +def decode_eeprom(e): + total_len = (ord(e[9]) << 8) | ord(e[10]) + tlv_index = _TLV_INFO_HDR_LEN + tlv_end = _TLV_INFO_HDR_LEN + total_len + ret = [] + while (tlv_index + 2) < len(e) and tlv_index < tlv_end: + rt = decoder(None, e[tlv_index : tlv_index + 2 + ord(e[tlv_index + 1])]) + ret.append(rt) + if ord(e[tlv_index]) == TLV_CODE_CRC_32: + break + tlv_index += ord(e[tlv_index + 1]) + 2 + for item in ret: + if item["code"] == TLV_CODE_VENDOR_EXT: + rt = decoder(None, item["value"][0 : 0 + 2 + ord(item["value"][0 + 1])]) + ret.append(rt) + return ret + + +def get_sys_eeprom(): + eeprom = get_sysfs_value(wb_eeprom) + return decode_eeprom(eeprom) + + +# get card ID +def getCardId(): + ret = get_sys_eeprom() + for item in ret: + if item["code"] == TLV_CODE_CARID: + return item.get("value", None) + return None + + +# ==================================== +# execute shell command +# ==================================== +def os_system(cmd): + status, output = commands.getstatusoutput(cmd) + return status, output + + +########################################### +# get memory slot and number via DMI command +########################################### +def getsysmeminfo(): + ret, log = os_system("which dmidecode ") + if ret != 0 or len(log) <= 0: + error = "cmd find dmidecode" + return False, error + cmd = log + '|grep -P -A5 "Memory\s+Device"|grep Size|grep -v Range' + # get total number first + result = [] + ret1, log1 = os_system(cmd) + if ret1 == 0 and len(log1): + log1 = log1.lstrip() + arr = log1.split("\n") + # total = len(arr) # total slot number + for i in range(len(arr)): + val = re.sub("\D", "", arr[i]) + if val == "": + val = arr[i].lstrip() + val = re.sub("Size:", "", val).lstrip() + # print val + result.append({"slot": i + 1, "size": val}) + return True, result + return False, "error" + + +########################################### +# get memory slot and number via DMI command +# return various arrays +########################################### +def getsysmeminfo_detail(): + ret, log = os_system("which dmidecode ") + if ret != 0 or len(log) <= 0: + error = "cmd find dmidecode" + return False, error + cmd = log + ' -t 17 | grep -A21 "Memory Device"' # 17 + # get total number + ret1, log1 = os_system(cmd) + if ret1 != 0 or len(log1) <= 0: + return False, "command execution error[%s]" % cmd + result_t = log1.split("--") + mem_rets = [] + for item in result_t: + its = item.replace("\t", "").strip().split("\n") + ret = {} + for it in its: + if ":" in it: + key = it.split(":")[0].lstrip() + value = it.split(":")[1].lstrip() + ret[key] = value + mem_rets.append(ret) + return True, mem_rets + + +########################################### +# get BIOS info via DMI command +########################################### +def getDmiSysByType(type_t): + ret, log = os_system("which dmidecode ") + if ret != 0 or len(log) <= 0: + error = "cmd find dmidecode" + return False, error + cmd = log + " -t %s" % type_t + # get total number + ret1, log1 = os_system(cmd) + if ret1 != 0 or len(log1) <= 0: + return False, "command execution error[%s]" % cmd + its = log1.replace("\t", "").strip().split("\n") + ret = {} + for it in its: + if ":" in it: + key = it.split(":")[0].lstrip() + value = it.split(":")[1].lstrip() + ret[key] = value + return True, ret + + +def gethwsys(): + return getDmiSysByType(1) + + +########################################### +# get BIOS info via DMI command + + +def getsysbios(): + return getDmiSysByType(0) + + +def searchDirByName(name, dir): + result = [] + try: + files = os.listdir(dir) + for file in files: + if name in file: + result.append(os.path.join(dir, file)) + except Exception as e: + print(str(e)) + return result + + +def getUsbLocation(): + dir = "/sys/block/" + spect = "sd" + usbpath = "" + result = searchDirByName(spect, dir) + if len(result) <= 0: + return False, usbpath + for item in result: + with open(os.path.join(item, "removable"), "r") as fd: + value = fd.read() + if value.strip() == "1": # U-Disk found + usbpath = item + break + if usbpath == "": # no U-Disk found + log_debug("no usb found") + return False, usbpath + return True, usbpath + + +# judge USB file +def getusbinfo(): + ret, path = getUsbLocation() + if ret == False: + return False, "not usb exists" + str = os.path.join(path, "size") + ret, value = getfilevalue(str) + if ret == True: + return ( + True, + { + "id": os.path.basename(path), + "size": float(value) * 512 / 1024 / 1024 / 1024, + }, + ) + else: + return False, "Err" + + +def get_cpu_info(): + cmd = "cat /proc/cpuinfo |grep processor -A18" # 17 + + ret, log1 = os_system(cmd) + if ret != 0 or len(log1) <= 0: + return False, "command execution error[%s]" % cmd + result_t = log1.split("--") + mem_rets = [] + for item in result_t: + its = item.replace("\t", "").strip().split("\n") + ret = {} + for it in its: + if ":" in it: + key = it.split(":")[0].lstrip() + value = it.split(":")[1].lstrip() + ret[key] = value + mem_rets.append(ret) + return True, mem_rets + +def io_rd(reg_addr, size=1): + path = "/dev/port" + ret = "" + fd = None + try: + reg_addr = int(reg_addr) + fd = os.open(path, os.O_RDWR|os.O_CREAT) + for i in range(size): + os.lseek(fd, reg_addr+i, os.SEEK_SET) + ret+="{:02x}".format(ord(os.read(fd, 1).decode('latin-1'))) + return ret + except Exception as e: + print(str(e)) + return None + finally: + if fd: os.close(fd) + + +def io_wr(reg_addr, reg_data): + u"""io write""" + fd = None + try: + regdata = 0 + regaddr = 0 + if type(reg_addr) == int: + regaddr = reg_addr + else: + regaddr = int(reg_addr, 16) + if type(reg_data) == int: + regdata = reg_data + else: + regdata = int(reg_data, 16) + devfile = "/dev/port" + fd = os.open(devfile, os.O_RDWR | os.O_CREAT) + os.lseek(fd, regaddr, os.SEEK_SET) + os.write(fd, regdata.to_bytes(2, 'little')) + return True + except ValueError as e: + print(e) + return False + except Exception as e: + print(e) + return False + finally: + if fd: os.close(fd) diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/script/sensors b/platform/centec-arm64/sonic-platform-modules-micas/common/script/sensors new file mode 100755 index 000000000000..a2c72b123a43 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/script/sensors @@ -0,0 +1,8 @@ +#!/bin/bash +#docker exec -i pmon sensors "$@" + + +#To probe sensors not part of lm-sensors +if [ -r /usr/local/bin/platform_sensors.py ]; then + python /usr/local/bin/platform_sensors.py +fi diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/service/device_i2c.service b/platform/centec-arm64/sonic-platform-modules-micas/common/service/device_i2c.service new file mode 100644 index 000000000000..94ddaaad1e41 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/service/device_i2c.service @@ -0,0 +1,15 @@ +[Unit] +Description= Platform Global Initialize I2c drivers. +After=local-fs.target +Before=pmon.service ntp.service +DefaultDependencies=no + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/device_i2c.py start +ExecStop=/usr/local/bin/device_i2c.py stop +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common/service/fancontrol.service b/platform/centec-arm64/sonic-platform-modules-micas/common/service/fancontrol.service new file mode 100644 index 000000000000..aa686c9f9d79 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common/service/fancontrol.service @@ -0,0 +1,12 @@ +[Unit] +Description= Fancontrol for Device. +After=privatenetwork.service +DefaultDependencies=no + +[Service] +ExecStart=/usr/local/bin/fancontrol.py start +Restart=on-failure + +[Install] +WantedBy=multi-user.target + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/Makefile b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/Makefile new file mode 100644 index 000000000000..320229073791 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/Makefile @@ -0,0 +1,24 @@ +PWD = $(shell pwd) +EXTRA_CFLAGS:= -I$(M)/include +EXTRA_CFLAGS+= -Wall +SUB_BUILD_DIR = $(PWD)/build +SERVICE_DIR = $(PWD)/service +INSTALL_DIR = $(SUB_BUILD_DIR)/$(KERNEL_SRC)/$(INSTALL_MOD_DIR) +INSTALL_SCRIPT_DIR = $(SUB_BUILD_DIR)/usr/local/bin +INSTALL_LIB_DIR = $(SUB_BUILD_DIR)/usr/lib/python3.9/dist-packages +INSTALL_SERVICE_DIR = $(SUB_BUILD_DIR)/lib/systemd/system +INSTALL_ETC_DIR = $(SUB_BUILD_DIR)/etc +UNSUPPORT_KERVER = 4.9.189 + +all: + @if [ ! -d ${INSTALL_LIB_DIR} ]; then mkdir -p ${INSTALL_LIB_DIR} ;fi + @if [ -d $(PWD)/lib/ ]; then cp -r $(PWD)/lib/* ${INSTALL_LIB_DIR} ;fi + @if [ ! -d ${INSTALL_SCRIPT_DIR} ]; then mkdir -p ${INSTALL_SCRIPT_DIR} ;fi + @if [ -d $(PWD)/script/ ]; then cp -r $(PWD)/script/* ${INSTALL_SCRIPT_DIR} ;fi + @if [ ! -d ${INSTALL_ETC_DIR} ]; then mkdir -p ${INSTALL_ETC_DIR} ;fi + @if [ -d $(INSTALL_SCRIPT_DIR) ]; then chmod +x $(INSTALL_SCRIPT_DIR)/* ;fi + @if [ ! -d ${INSTALL_SERVICE_DIR} ]; then mkdir -p ${INSTALL_SERVICE_DIR} ;fi + if [ -d $(SERVICE_DIR) ]; then cp -r $(SERVICE_DIR)/* $(INSTALL_SERVICE_DIR) ;fi +clean: + rm -rf $(SUB_BUILD_DIR) + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/logutil/__init__.py b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/logutil/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/logutil/logutil.py b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/logutil/logutil.py new file mode 100644 index 000000000000..0d9f6ff94c10 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/logutil/logutil.py @@ -0,0 +1,64 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +import os +import syslog +import logging +import subprocess +import shlex + +SYSLOG_IDENTIFIER = "LOGUTIL" + +# ========================== Syslog wrappers ========================== + +def log_info(msg,log_type=SYSLOG_IDENTIFIER, also_print_to_console=False): + syslog.openlog(log_type) + syslog.syslog(syslog.LOG_INFO, msg) + syslog.closelog() + + if also_print_to_console: + click.echo(msg) + + +def log_debug(msg, log_type=SYSLOG_IDENTIFIER, also_print_to_console=False): + try: + syslog.openlog(log_type) + syslog.syslog(syslog.LOG_DEBUG, msg) + syslog.closelog() + + if also_print_to_console: + click.echo(msg) + except Exception as e: + print(str(e)) + + +def log_warning(msg, log_type=SYSLOG_IDENTIFIER, also_print_to_console=False): + syslog.openlog(log_type) + syslog.syslog(syslog.LOG_WARNING, msg) + syslog.closelog() + + if also_print_to_console: + click.echo(msg) + +def log_error(msg, log_type=SYSLOG_IDENTIFIER, also_print_to_console=False): + syslog.openlog(log_type) + syslog.syslog(syslog.LOG_ERR, msg) + syslog.closelog() + + if also_print_to_console: + click.echo(msg) + +restful_logger_path = "/var/log/bmc_restful.log" +def bmc_restful_logger(message): + if not os.path.isfile(restful_logger_path): + cmd = "sudo touch %s && sudo chmod +x %s" % ( + restful_logger_path, restful_logger_path) + subprocess.Popen( + shlex.split(cmd), shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + logging.basicConfig(filename=restful_logger_path, + filemode='a', + format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s', + datefmt='%H:%M:%S', + level=logging.INFO) + + logging.info(message) diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/redfishutil/redfish_api.py b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/redfishutil/redfish_api.py new file mode 100644 index 000000000000..dd931e9acb41 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/redfishutil/redfish_api.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import shlex +import datetime +import os +import ssl +import subprocess +import syslog + +class Redfish_Api(): + BmcBaseUrl = 'http://240.1.1.1:8080' + ThermalUrl = '/redfish/v1/Chassis/1/Thermal' + PowerUrl = '/redfish/v1/Chassis/1/Power' + ThresholdSensorsUrl = '/redfish/v1/Chassis/1/ThresholdSensors' + FanSpeedUrl = '/redfish/v1/Chassis/1/Thermal/Actions/Oem/OpenBmc/Fan.SetSpeed' + BoardsUrl = '/redfish/v1/Chassis/1/Boards/' + BoardLedUrl = "/redfish/v1/Chassis/1/Boards/{}/Actions/Oem/OpenBmc/Boards.SetLED" + + # Maximum time in seconds that you allow the connection to the server to take. + connect_timeout = 30 + # Maximum time in seconds that you allow the whole operation to take + operation_timeout = 300 + + default_prefix='/redfish/v1/' + session = None + __DEBUG__ = "N" + __DUMP_RESP__ = "N" + RST_STATUS = "status" + RST_SUCCESS = "OK" + refish_logger = None + + def redfish_log_debug(self, msg): + if (self.__DEBUG__ == "Y"): + syslog.openlog("redfis_api") + syslog.syslog(syslog.LOG_DEBUG, msg) + syslog.closelog() + + def redfish_log_error(self, msg): + syslog.openlog("redfish_api") + syslog.syslog(syslog.LOG_ERR, msg) + syslog.closelog() + + def __init__(self): + pass + + def get_full_url(self, url): + return self.BmcBaseUrl + url + + def _exec_cmd(self, cmd): + self.redfish_log_debug("Cmd: %s" % cmd) + p = subprocess.Popen(shlex.split(cmd), shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p.wait() + self.redfish_log_debug("Cmd return: %d" % p.returncode) + str_stdout = p.stdout.read().decode('utf-8') + str_stderr = p.stderr.read().decode('utf-8') + self.redfish_log_debug("Cmd stdout: %s" % str_stdout) + if p.returncode !=0: + self.redfish_log_error("Cmd: %s, failed! error msg:%s" % (cmd, str_stderr)) + return None + else: + try: + ret = json.loads(str_stdout) + return ret + except Exception as e: + self.redfish_log_error("Cmd: %s, failed! stdout msg:%s" % (cmd, str_stdout)) + return None + + def _redfish_get(self, url): + self.redfish_log_debug("Get info from %s." % url) + result = None + try: + cmd_get="curl --connect-timeout %d -m %d -X GET %s" % (self.connect_timeout, self.operation_timeout, self.get_full_url(url)) + result = self._exec_cmd(cmd_get) + return result + except Exception as e: + self.redfish_log_error("error_message: %s" % e) + return None + + def _redfish_post(self, url, playload): + self.redfish_log_debug("post url: %s" % url) + self.redfish_log_debug("Playload: %s" % playload) + + playload_json = json.dumps(playload) + result = False + try: + cmd_post="curl --connect-timeout %d -m %d -X POST %s -d \'%s\'" % (self.connect_timeout, self.operation_timeout, self.get_full_url(url), playload_json) + ret_msg = self._exec_cmd(cmd_post) + if ret_msg == None: + return False + elif ret_msg["success"] == False: + redfish_log_error("Url: '%s', Playload: '%s', Bmc return failed, error_message: %s" % (url, playload_json, ret_msg["Message"])) + result = False + else: + result = True + return result + except Exception as e: + redfish_log_error("error_message: %s" % e) + return False + + def get_thermal(self): + """Get thermal info + :returns: class 'redfish.rest.v1.RestResponse' or None when failed + """ + return self._redfish_get(self.ThermalUrl) + + def get_power(self): + """Get power info + :returns: class 'redfish.rest.v1.RestResponse' or None when failed + """ + return self._redfish_get(self.PowerUrl) + + def get_thresholdSensors(self): + """Get thresholdSensors info + :returns: class 'redfish.rest.v1.RestResponse' or None when failed + """ + return self._redfish_get(self.ThresholdSensorsUrl) + + def post_odata(self, odata_id, playload): + """post odata info + :params odata_id: the specified odata_id path + :type odata_id: string + :playload: info to post + :type: dictionary + :returns: True or False + """ + if odata_id is None or playload is None: + print("post failed: odata_id or playload is None") + return False + return self._redfish_post(odata_id, playload) + + def get_odata(self, odata_id): + """Get odata info + :params odata_id: the specified odata_id path + :type odata_id: string + :returns: class 'redfish.rest.v1.RestResponse' or None when failed + """ + if odata_id is None: + print("Get odata_id failed: odata_id is None") + return None + return self._redfish_get(odata_id) + + def post_fanSpeed(self, playload): + """post odata info + :playload: info to post + :type: dictionary + :returns: True or False + """ + if playload is None: + print("post failed: playload is None") + return False + return self._redfish_post(self.FanSpeedUrl, playload) + + def get_board(self, board_name="indicatorboard"): + """Get board info + :board_name: name of board, default is "indicatorboard" + :type: string + :returns: class'redfish.rest.v1.RestResponse' or None when failed + """ + if board_name is None : + print("get failed: board_name is None") + return None + return self._redfish_get(self.BoardsUrl + board_name) + + def post_boardLed(self, playload, board_name="indicatorboard"): + """post boardLed info + :board_name: name of board, default is "indicatorboard" + :type: string + :playload: info to post + :type: dictionary + :returns: True or False + """ + if board_name is None or playload is None: + print("post failed: playload is None") + return False + return self._redfish_post(self.BoardLedUrl.format(board_name), playload) + + ''' not supported currently + def post_thermal(self, playload): + """post thermal info + :playload: info to post + :type: dictionary + :returns: True or False + """ + if playload is None: + print("post_thermal failed: playload is None") + return None + return self._redfish_post(self.ThermalUrl, playload) + + def post_power(self, playload): + """post power info + :playload: info to post + :type: dictionary + :returns: True or False + """ + if playload is None: + print("post_power failed: playload is None") + return None + return self._redfish_post(self.PowerUrl, playload) + + def post_thresholdSensors(self, playload): + """post thresholdSensors info + :playload: info to post + :type: dictionary + :returns: True or False + """ + if playload is None: + print("post_thresholdSensors failed: playload is None") + return None + return self._redfish_post(self.ThresholdSensorsUrl, playload) + + def get_fanSpeed(self): + """Get board led info + :returns: class'redfish.rest.v1.RestResponse' or None when failed + """ + return self._redfish_get(self.FanSpeedUrl) + + def post_board(self, playload, board_name="indicatorboard"): + """post board info + :board_name: name of board, default is "indicatorboard" + :type: string + :playload: info to post + :type: dictionary + :returns: True or False + """ + if board_name is None or playload is None: + print("post failed: playload is None") + return False + return self._redfish_post(self.BoardsUrl + board_name, playload) + + def get_boardLed(self, board_name="indicatorboard"): + """Get boardLed info + :board_name: name of board, default is "indicatorboard" + :type: string + :returns: class'redfish.rest.v1.RestResponse' or None when failed + """ + if board_name is None : + print("get failed: board_name is None") + return None + return self._redfish_get(self.BoardsUrl % board_name) + + ''' + +''' +if __name__ == '__main__': + redfish = Redfish_Api() + + ### get + # boards + ret = redfish.get_board() + if ret is None: + print("get failed: board") + else: + print("get succeeded, board:%s" % ret) + + ret = redfish.get_thresholdSensors() + if ret is None: + print("get failed: threshold") + else: + print("get succeeded, threshold:%s" % ret) + + ret = redfish.get_power() + if ret is None: + print("get failed: power") + else: + print("get succeeded, power:%s" % ret) + + ret = redfish.get_thermal() + if ret is None: + print("get failed:thermal") + else: + print("get succeeded,thermal:%s" % ret) + + # get playload + resp = redfish.get_thresholdSensors() + if (resp != None): + print(resp["@odata.id"]) + print(resp["@odata.type"]) + print(resp["Id"]) + print(resp["Name"]) + else: + print("Failed: get_thresholdSensors") + + ### post + # fanSpeed + playload = {} + playload["FanName"] = 'Fan0' + playload["FanSpeedLevelPercents"] = "70" + print("post fanSpeed:%s" % redfish.post_fanSpeed(playload)) + + #{"LEDs": [{"IndicatorLEDColor": "green","LEDType": "sys"},{"IndicatorLEDColor": "off","LEDType": "pwr"},{"IndicatorLEDColor": "green","LEDType": "fan"}]} + playload = {} + led = {} + led1 = {} + led_list = [] + led["IndicatorLEDColor"] = "green" + led["LEDType"] = "sys" + led1["IndicatorLEDColor"] = "off" + led1["LEDType"] = "pwr" + led_list.append(led) + led_list.append(led1) + playload["LEDs"] = led_list + # boardsLed + print("post boardLed:%s" % redfish.post_boardLed(playload)) +''' diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/sonic_fwmgr/__init__.py b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/sonic_fwmgr/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/sonic_fwmgr/fwgmr_base.py b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/sonic_fwmgr/fwgmr_base.py new file mode 100644 index 000000000000..50a4ca7bc675 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/sonic_fwmgr/fwgmr_base.py @@ -0,0 +1,141 @@ +# fwgmr_base.py +# +# Base class for creating platform-specific firmware management interfaces for SONiC +# +try: + import abc +except ImportError as e: + raise ImportError("%s - required module not found" % str(e)) + +class FwMgrUtilBase(object): + + """Base class for Platform-specific FwMgrUtil class""" + __metaclass__ = abc.ABCMeta + + def __init__(self): + """TODO: to be defined1. """ + pass + + @abc.abstractmethod + def get_bmc_version(self): + """Get BMC version from SONiC + :returns: version string + """ + return + + @abc.abstractmethod + def get_cpld_version(self): + """Get CPLD version from SONiC + :returns: dict like {'CPLD_1': version_string, 'CPLD_2': version_string} + """ + return + + @abc.abstractmethod + def get_onie_version(self): + """Get ONiE version from SONiC + :returns: version string + """ + return + + @abc.abstractmethod + def get_uboot_version(self): + """Get UBOOT version from SONiC + :returns: version string + """ + return + + # @fw_type MANDATORY, firmware type, should be one of the strings: 'cpld', 'fpga', 'bios', 'bmc' + # @fw_path MANDATORY, target firmware file + # @fw_extra OPTIONAL, extra information string, + # for fw_type 'cpld' and 'fpga': it can be used to indicate specific cpld, such as 'cpld1', 'cpld2', ... + # or 'cpld_fan_come_board', etc. For fw_type 'bios' and 'bmc', value should be one of 'master' + # or 'slave' or 'both'. For BMC, 'pingpong' stands for alternative upgrade policy. + @abc.abstractmethod + def firmware_upgrade(self, fw_type, fw_path, fw_extra): + return + + # Get last firmware upgrade information, inlcudes: + # 1) FwType: cpld/fpga/bios/bmc(passed by method 'firmware_upgrade'), string + # 2) FwPath: path and file name of firmware(passed by method 'firmware_upgrade'), string + # 3) FwExtra: designated string, econdings of this string is determined by vendor(passed by method 'firmware_upgrade') + # 4) Result: indicates whether the upgrade action is performed and success/failure status if performed. Values should be one of: "DONE"/"FAILED"/"NOT_PERFORMED". + # dict object: + # { + # "FwType": "cpld", + # "FwExtra": "specific_encoded_string" + # "Result": "DONE"/"FAILED"/"NOT_PERFORMED" + # } + @abc.abstractmethod + def get_last_upgrade_result(self): + return + + # Program FPGA and/or CPLD firmware only, but do not refresh them + # + # @param fw_type value can be: FPGA, CPLD + # @param fw_path a string of firmware file path, seperated by ':' + # @param fw_extra a string of firmware subtype, i.e CPU_CPLD, BOARD_CPLD, + # FAN_CPLD, LC_CPLD, etc. Subtypes are seperated by ':', + # the sequence should match the file nanmes in param @fw_path + # @return True when all required firmware is program succefully, + # False otherwise. + # + # Example: + # self.firmware_program("CPLD", "/cpu_cpld.vme:/lc_cpld", \ + # "CPU_CPLD:LC_CPLD") + # or + # self.firmware_program("FPGA", "/fpga.bin", "FPGA") + @abc.abstractmethod + def firmware_program(self, fw_type, fw_path, fw_extra=None): + return + + # Refresh firmware and take extra action when necessary. + # @param fpga_list a list of FPGA names + # @param cpld_list a list of CPLD names + # @return True if refresh succefully + # + # @Note extra action: + # 1) response OK to restful call + # 2) shutdown eth0.4088(keep eth0 working) + # 3) shutdown SFP power + # 4) power off all(CPU, switch, gearbox) + # 5) if power off fpga is supported: + # power off fpga + # else: + # refresh fpga + # 6) refresh linecard cpld, fan cpld + # 7) refresh cpu cpld, baseboard cpld + # 8) power on all + # 9) bring up eth0.4088 + # + # Example: + # self.firmware_refresh(["FPGA"], ["CPU_CPLD", "LC_CPLD"], "/tmp/fw/refresh.vme") + # or + # self.firmware_refresh(["FPGA"], None, None) + # or + # self.firmware_refresh(None, ["FAN_CPLD", "LC1_CPLD", "BOARD_CPLD"], "/tmp/fw/refresh.vme") + @abc.abstractmethod + def firmware_refresh(self, fpga_list, cpld_list, fw_extra=None): + return + + # Get booting flash of running BMC. + # @return a string, "master" or "slave" + @abc.abstractmethod + def get_running_bmc(self): + return + + # Set booting flash of BMC + # @param flash should be "master" or "slave" + @abc.abstractmethod + def set_bmc_boot_flash(self, flash): + return + + # Reboot BMC + @abc.abstractmethod + def reboot_bmc(self): + return + + # Get booting uboot image of current running host OS + # @return a string, "master" or "slave" + @abc.abstractmethod + def get_current_uboot(self): + return diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/wbutil/__init__.py b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/wbutil/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/wbutil/platformutil.py b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/wbutil/platformutil.py new file mode 100644 index 000000000000..d5c3e7a4edd3 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/wbutil/platformutil.py @@ -0,0 +1,290 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +import os +import re +import time +import mmap +configfile_pre = "/usr/local/bin/" +import sys +sys.path.append(configfile_pre) +from platform_config import * +import subprocess +import pexpect +import shlex + +SYSLOG_IDENTIFIER = "UTILTOOL" + +def os_system(cmd): + status, output = subprocess.getstatusoutput(cmd) + return status, output + +def get_machine_info(): + if not os.path.isfile('/host/machine.conf'): + return None + machine_vars = {} + with open('/host/machine.conf') as machine_file: + for line in machine_file: + tokens = line.split('=') + if len(tokens) < 2: + continue + machine_vars[tokens[0]] = tokens[1].strip() + return machine_vars + +def get_platform_info(machine_info): + if machine_info != None: + if machine_info.__contains__('onie_platform'): + return machine_info['onie_platform'] + elif machine_info.__contains__('aboot_platform'): + return machine_info['aboot_platform'] + return None + +''' +def cpld_version_restful(url): + if url == "" or len(url) <=0: + print("invalid url") + return + bmc = BMCMessage() + value = bmc.getBmcValue(url) + json_dicts=json.dumps(value,indent=4) + return json_dicts +''' + +def lpc_cpld_rd(reg_addr): + try: + regaddr = 0 + if type(reg_addr) == int: + regaddr = reg_addr + else: + regaddr = int(reg_addr, 16) + devfile = "/dev/lpc_cpld" + fd = os.open(devfile, os.O_RDWR|os.O_CREAT) + os.lseek(fd, regaddr, os.SEEK_SET) + str = os.read(fd, 1) + os.close(fd) + return "%02x" % ord(str) + except ValueError: + return None + except Exception as e: + print (e) + return None + + +def my_log(txt): + if DEBUG == True: + print ("[PLATFORM]:",) + print (txt) + return + +def log_os_system(cmd, show): + my_log (' Run :'+ cmd) + status, output = subprocess.getstatusoutput(cmd) + my_log (" with result :" + str(status)) + my_log (" output :" + output) + if status: + log_error('Failed :%s msg:%s'%(cmd,output)) + if show: + print ('Failed :'+ cmd) + return status, output + +def password_command(cmd, password, exec_timeout=30): + + newkey = 'continue connecting' + log_os_system("rm -rf ~/.ssh", 0) + msg = "" + try_times = 3 + try_times_conter = try_times + while try_times_conter: + child = pexpect.spawn(cmd) + if try_times != try_times_conter: + time.sleep(5) + try_times_conter -= 1 + try: + i = child.expect([pexpect.TIMEOUT, newkey, 'password: ',"refused",pexpect.EOF],timeout=30) + # If the login times out, print an error message and exit. + if i == 0: # Timeout + msg = 'connect to BMC timeout' + continue + # no public key + if i == 1: + child.sendline ('yes') + i = child.expect([pexpect.TIMEOUT, 'password: '],timeout=30) + if i == 0: # Timeout + msg = 'connect to BMC timeout' + continue + if i == 1:# Go below and enter the logic of the password + i = 2 + if i == 2: # Enter the password + child.sendline (password) + i = child.expect([pexpect.EOF, pexpect.TIMEOUT], exec_timeout) + if i == 0: + return True,child.before + if i == 1: + msg = str(child.before)+"\nBMC run commands timeout" + return False,msg + if i == 3: # BMC Connection refused + msg = 'connect to BMC failed' + continue + if i == 4: + msg = child.before + except Exception as e: + msg = str(child.before)+"\nconnect to BMC failed" + + return False,msg + +def password_command_realtime(ssh_header, ssh_cmd, password,key_words, exec_timeout=30): + + key_word_end = key_words.get("key_word_end") + key_word_pass = key_words.get("key_word_pass") + key_word_noshow = key_words.get("key_word_noshow") + # Prevents waiting caused by BMC restart + key_word_exit = key_words.get("key_word_exit") + + if None in [key_word_end,key_word_pass]: + print ("Missing parameters") + return False + + newkey = 'continue connecting' + log_os_system("rm -rf ~/.ssh", 0) + msg = "" + try_times = 3 + key_word_pass_flag = False + try_times_conter = try_times + child = pexpect.spawn(ssh_header) + try: + i = child.expect([pexpect.TIMEOUT,newkey, 'password: ',"refused",pexpect.EOF],timeout=30) + # If the login times out, print an error message and exit. + if i == 0: # Timeout + msg = 'connect to BMC timeout' + # no public key + if i == 1: + child.sendline ('yes') + i = child.expect([pexpect.TIMEOUT, 'password: '],timeout=30) + if i == 0: # Timeout + msg = 'connect to BMC timeout' + if i == 1:# Go below and enter the logic of the password + i = 2 + + if i == 2: # Enter the password + child.sendline (password) + i = child.expect([pexpect.EOF, "\r",pexpect.TIMEOUT], exec_timeout) + if i == 0: + print (child.before) + return key_word_pass_flag + if i == 1: + child.sendline(ssh_cmd) + # amount received is similar to root@switch2 in order to avoid misjudgment about the end of execution + usr_symble_first = True + bmc_str_tmp="" + while True: + i = child.expect([pexpect.EOF,"\r","\n",key_word_end, pexpect.TIMEOUT], exec_timeout) + if i == 0: + return key_word_pass_flag + elif i in [1,2]: + if key_word_noshow == None or key_word_noshow not in child.before: + bmc_str, times = re.subn("\r|\n","",child.before) + if len(bmc_str) > 1: + print (bmc_str) + bmc_str_tmp=bmc_str_tmp + bmc_str + # print bmc_str_tmp + # if key_word_pass in child.before: + if re.search(key_word_pass,bmc_str_tmp) != None: + key_word_pass_flag = True + if key_word_exit != None and key_word_exit in child.before: + # Give the BMC time to execute the last command + time.sleep(3) + return key_word_pass_flag + elif i == 3 : + if usr_symble_first: + usr_symble_first = False + else: + return key_word_pass_flag + + if i == 3: # BMC Connection refused + msg = 'connect to BMC failed' + if i == 4: + msg = child.before + except Exception as e: + print (e) + msg = str(child.before)+"\nconnect to BMC error" + print (msg) + + return False + +def get_sys_execute2(cmd, key_word_pass): + # key_word_pass_flag1 = False + key_word_pass_flag = False + filename = "/tmp/diag_excute_out" + cmd = cmd + "|tee %s" % filename + p = subprocess.Popen(shlex.split(cmd), shell=False) + p.wait() + with open(filename, 'r') as f: + str1 = f.read() + if key_word_pass in str1: + key_word_pass_flag = True + # if key_word_pass_flag1 and "100%" in str1: + # key_word_pass_flag = True + log_os_system("rm %s"%filename,0) + return key_word_pass_flag + + +BMC_PATH = "PATH=/usr/sbin/:/bin/:/usr/bin/:/sbin" +RETURN_KEY1 = "code" +RETURN_KEY2 = "msg" +DEBUG = False + +def wbi2cget(bus, devno, address): + command_line = "i2cget -f -y %d 0x%02x 0x%02x " % (bus, devno, address) + retrytime = 6 + ret_t = "" + for i in range(retrytime): + ret, ret_t = os_system(command_line) + if ret == 0: + return True, ret_t + time.sleep(0.1) + return False, ret_t + +def strtoint(str): # Hexadecimal string to int,"4040"/"0x4040"/"0X4040" = 16448 + value = 0 + rest_v = str.replace("0X", "").replace("0x", "") + for index in range(len(rest_v)): + value |= int(rest_v[index], 16) << ((len(rest_v) - index - 1) * 4) + return value + +def pci_read(pcibus, slot, fn , bar, offset): + if offset % 4 != 0: + return None + filename = "/sys/bus/pci/devices/0000:%02x:%02x.%x/resource%d" % (int(pcibus), int(slot), int(fn), int(bar)) + file = open(filename, "r+") + size = os.path.getsize(filename) + data = mmap.mmap(file.fileno(), size) + result = data[offset: offset + 4] + s = result[::-1] + val = 0 + for i in range(0, len(s)): + val = val << 8 | ord(s[i]) + data.close() + file.close() + return val + +########################################### +# Run the DMI command to obtain the BIOS information +########################################### +def getDmiSysByType(type_t): + ret, log = os_system("which dmidecode ") + if ret != 0 or len(log) <= 0: + error = "cmd find dmidecode" + return False, error + cmd = log + " -t %s" % type_t + # Get the total first + ret1, log1 = os_system(cmd) + if ret != 0 or len(log1) <= 0: + return False, "Command error[%s]" % cmd + its = log1.replace("\t", "").strip().split("\n") + ret = {} + for it in its: + if ":" in it: + key = it.split(":")[0].lstrip() + value = it.split(":")[1].lstrip() + ret[key] = value + return True, ret diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/script/ctccmd b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/script/ctccmd new file mode 100755 index 000000000000..2c4410a16d66 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/script/ctccmd @@ -0,0 +1,3 @@ +#! /bin/bash + +docker exec -i syncd ctc_shell -e "$@" \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/script/privatenetwork.py b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/script/privatenetwork.py new file mode 100644 index 000000000000..b498818b2a6d --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/script/privatenetwork.py @@ -0,0 +1,50 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +import sys +import os +import re +try: + from sonic_platform import get_machine_info + from sonic_platform import get_platform_info +except ImportError : + try: + from sonic_device_util import get_machine_info + from sonic_device_util import get_platform_info + except ImportError: + from sonic_py_common import device_info + def get_machine_info(): + print("get_machine_info is null") + return False + def get_platform_info(x): + return device_info.get_platform() + +def start(): + x = get_platform_info(get_machine_info()) + print (x) + str = re.findall(r"-(.+?)_",x) + print (str[0]) + if str[0] == 'micas': + print ("Start privatenetwork.sh") + os.system("/usr/local/bin/privatenetwork.sh start") + else: + print ("Not set private network.") +def stop(): + x = get_platform_info(get_machine_info()) + print (x) + str = re.findall(r"-(.+?)_",x) + print (str[0]) + if str[0] == 'micas': + print ("Stop privatenetwork.sh") + os.system("/usr/local/bin/privatenetwork.sh stop") + else: + print ("Not stop private network.") +def main(): + print (sys.argv[1]) + if sys.argv[1]=='start': + start() + elif sys.argv[1]=='stop': + stop() + else: + print ("Error parameter!\nRequired parameters : start or stop.") +if __name__ == '__main__': + main() diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/script/privatenetwork.sh b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/script/privatenetwork.sh new file mode 100644 index 000000000000..4105dcbfff83 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/script/privatenetwork.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +function load_eth0.4088(){ + try_times_remain=5 + state_up=$(ip -d link show eth0.4088 | awk '/state UP/{print $2}') + while [ -z "$state_up" ] && [ $try_times_remain -ne 0 ] + do + ((try_times_remain-=1)) + ip link add link eth0 name eth0.4088 type vlan id 4088 || true + ip addr add 240.1.1.2/30 brd 240.1.1.3 dev eth0.4088 || true + ip link set dev eth0.4088 up || true + state_up=$(ip -d link show eth0.4088 | awk '/state UP/{print $2}') + sleep 1 + done +} + +function unload_eth0.4088(){ + ip link set dev eth0.4088 down + ip link del eth0.4088 +} + +if [ "$1" = "start" ];then + load_eth0.4088 +elif [ "$1" = "stop" ];then + unload_eth0.4088 +fi diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/script/update_machine_config.sh b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/script/update_machine_config.sh new file mode 100644 index 000000000000..933e7cc3575e --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/script/update_machine_config.sh @@ -0,0 +1,24 @@ +#!/bin/bash +onie_dev=$(blkid | grep ONIE-BOOT | head -n 1 | awk '{print $1}' | sed -e 's/:.*$//') +mkdir -p /mnt/onie-boot +mount $onie_dev /mnt/onie-boot +onie_grub_cfg=/mnt/onie-boot/onie/grub/grub-machine.cfg + +if [ ! -e $onie_grub_cfg ]; then + echo "$onie_grub_cfg not found" +else + oldoniebuilddate=`cat /host/machine.conf|grep "onie_build_date="` + oniebuilddate=`cat $onie_grub_cfg|grep "onie_build_date="|sed "s/\"//g"` + onieversion=`cat $onie_grub_cfg|grep "onie_version="` + oniekernelversion=`cat $onie_grub_cfg|grep "onie_kernel_version="` + if [ "$oldoniebuilddate" != "$oniebuilddate" ]; then + echo "update /home/machine.conf" + sed -i "s/onie_build_date=.*/$oniebuilddate/" /host/machine.conf + sed -i "s/onie_version=.*/$onieversion/" /host/machine.conf + sed -i "s/onie_kernel_version=.*/$oniekernelversion/" /host/machine.conf + sed -i "s/\"//g" /host/machine.conf + fi +fi +umount /mnt/onie-boot + + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/service/privatenetwork.service b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/service/privatenetwork.service new file mode 100644 index 000000000000..b73d003046fe --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/service/privatenetwork.service @@ -0,0 +1,13 @@ +[Unit] +Description=Update interfaces configuration for adding eth0 private subnetwork +After=network.target m2-w6010-48gt4x_platform.service +DefaultDependencies=no + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/privatenetwork.py start +ExecStop=/usr/local/bin/privatenetwork.py stop +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/platform/centec-arm64/sonic-platform-modules-micas/debian/changelog b/platform/centec-arm64/sonic-platform-modules-micas/debian/changelog new file mode 100755 index 000000000000..ee4b9abf6c21 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/debian/changelog @@ -0,0 +1,6 @@ +sonic-centec-platform-modules (1.3) unstable; urgency=low + + * Add support for centec platform + + -- sonic_rd Mon, 21 Sep 2020 13:34:33 +0800 + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/debian/compat b/platform/centec-arm64/sonic-platform-modules-micas/debian/compat new file mode 100644 index 000000000000..f599e28b8ab0 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/debian/compat @@ -0,0 +1 @@ +10 diff --git a/platform/centec-arm64/sonic-platform-modules-micas/debian/control b/platform/centec-arm64/sonic-platform-modules-micas/debian/control new file mode 100644 index 000000000000..776b15d4e32a --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/debian/control @@ -0,0 +1,14 @@ +Source: sonic-centec-platform-modules +Section: main +Priority: extra +Maintainer: shil +Build-Depends: debhelper (>= 8.0.0), bzip2 +Standards-Version: 3.9.3 + +Package: platform-modules-micas-m2-w6010-48gt4x-fa +Architecture: arm64 +Description: kernel modules for platform devices such as fan, led, sfp + +Package: platform-modules-micas-m2-w6010-48gt4x-ra +Architecture: arm64 +Description: kernel modules for platform devices such as fan, led, sfp diff --git a/platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-fa.init b/platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-fa.init new file mode 100644 index 000000000000..cd7d03667dc0 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-fa.init @@ -0,0 +1,61 @@ +#!/bin/bash +# This script load/unload centec kernel modules + +function install_python_api_package() +{ + device="/usr/share/sonic/device" + platform=$(/usr/local/bin/sonic-cfggen -H -v DEVICE_METADATA.localhost.platform) + + rv=$(pip3 show sonic-platform > /dev/null 2>/dev/null) + if [ $? -ne 0 ]; then + echo -n "Install sonic_platform-1.0-py3-none-any.whl ..." + rv=$(pip3 install $device/$platform/sonic_platform-1.0-py3-none-any.whl) + fi +} + +function load_kernel_modules() +{ + hwaddr=`fw_printenv ethaddr | awk -F = '{print $2}'` + if [ "$hwaddr" != "" ]; then + ifconfig eth0 hw ether $hwaddr + fi + depmod -a + modprobe dal + modprobe psample +} + +function remove_kernel_modules() +{ + modprobe -r dal +} + +case "$1" in +start) + echo -n "Load Centec kernel modules... " + + load_kernel_modules + install_python_api_package + + echo "done." + ;; + +stop) + echo -n "Unload Centec kernel modules... " + + remove_kernel_modules + + echo "done." + ;; + +force-reload|restart) + echo "Not supported" + ;; + +*) + echo "Usage: /etc/init.d/platform-modules-micas-m2-w6010-48gt4x-fa {start|stop}" + exit 1 + ;; +esac + +exit 0 + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-fa.install b/platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-fa.install new file mode 100644 index 000000000000..918f5b872ec7 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-fa.install @@ -0,0 +1,2 @@ +m2-w6010-48gt4x-fa/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/arm64-micas_m2-w6010-48gt4x-fa-r0 + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-fa.postinst b/platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-fa.postinst new file mode 100644 index 000000000000..67f1fe08e3f4 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-fa.postinst @@ -0,0 +1,8 @@ +systemctl enable m2-w6010-48gt4x_platform.service +systemctl start m2-w6010-48gt4x_platform.service +systemctl enable device_i2c.service +systemctl start device_i2c.service +systemctl enable privatenetwork.service +systemctl start privatenetwork.service +systemctl enable fancontrol.service +systemctl start fancontrol.service diff --git a/platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-ra.init b/platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-ra.init new file mode 100644 index 000000000000..91dcc47dced0 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-ra.init @@ -0,0 +1,61 @@ +#!/bin/bash +# This script load/unload centec kernel modules + +function install_python_api_package() +{ + device="/usr/share/sonic/device" + platform=$(/usr/local/bin/sonic-cfggen -H -v DEVICE_METADATA.localhost.platform) + + rv=$(pip3 show sonic-platform > /dev/null 2>/dev/null) + if [ $? -ne 0 ]; then + echo -n "Install sonic_platform-1.0-py3-none-any.whl ..." + rv=$(pip3 install $device/$platform/sonic_platform-1.0-py3-none-any.whl) + fi +} + +function load_kernel_modules() +{ + hwaddr=`fw_printenv ethaddr | awk -F = '{print $2}'` + if [ "$hwaddr" != "" ]; then + ifconfig eth0 hw ether $hwaddr + fi + depmod -a + modprobe dal + modprobe psample +} + +function remove_kernel_modules() +{ + modprobe -r dal +} + +case "$1" in +start) + echo -n "Load Centec kernel modules... " + + load_kernel_modules + install_python_api_package + + echo "done." + ;; + +stop) + echo -n "Unload Centec kernel modules... " + + remove_kernel_modules + + echo "done." + ;; + +force-reload|restart) + echo "Not supported" + ;; + +*) + echo "Usage: /etc/init.d/platform-modules-micas-m2-w6010-48gt4x-ra {start|stop}" + exit 1 + ;; +esac + +exit 0 + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-ra.install b/platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-ra.install new file mode 100644 index 000000000000..3700a6fc8567 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-ra.install @@ -0,0 +1,2 @@ +m2-w6010-48gt4x-ra/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/arm64-micas_m2-w6010-48gt4x-ra-r0 + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-ra.postinst b/platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-ra.postinst new file mode 100644 index 000000000000..67f1fe08e3f4 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/debian/platform-modules-micas-m2-w6010-48gt4x-ra.postinst @@ -0,0 +1,8 @@ +systemctl enable m2-w6010-48gt4x_platform.service +systemctl start m2-w6010-48gt4x_platform.service +systemctl enable device_i2c.service +systemctl start device_i2c.service +systemctl enable privatenetwork.service +systemctl start privatenetwork.service +systemctl enable fancontrol.service +systemctl start fancontrol.service diff --git a/platform/centec-arm64/sonic-platform-modules-micas/debian/rules b/platform/centec-arm64/sonic-platform-modules-micas/debian/rules new file mode 100755 index 000000000000..b62824e822c2 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/debian/rules @@ -0,0 +1,114 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +include /usr/share/dpkg/pkg-info.mk + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +export INSTALL_MOD_DIR:=extra + +PYTHON = python3.9 + +PACKAGE_PRE_NAME := platform-modules-micas +KVERSION ?= $(shell uname -r) +KERNEL_SRC := /lib/modules/$(KVERSION) +MOD_SRC_DIR:= $(shell pwd) +MODULE_DIRS:= m2-w6010-48gt4x-fa +MODULE_DIRS_R:= m2-w6010-48gt4x-ra +MODULE_DIR := common +CUSTOMS_DIRS := common_custom/common_micas +SERVICE_DIR := service +CLASSES_DIR := classes +CONFIG_DIR := config +KDAL_DIR := ../../centec/centec-dal/ +export KERNEL_SRC + + + +clean: + dh_testdir + dh_testroot + dh_clean + +build: + #make modules -C $(KERNEL_SRC)/build M=$(MODULE_SRC) + (for mod in $(KDAL_DIR); do \ + make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/; \ + done) + (for mod in $(MODULE_DIRS); do \ + make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \ + cd $${mod}; $(PYTHON) setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \ + cd -; \ + done) + (for mod in $(MODULE_DIRS_R); do \ + make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \ + cd $${mod}; $(PYTHON) setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \ + cd -; \ + done) + make -C $(MODULE_DIR); + make -C $(CUSTOMS_DIRS); + +binary: binary-arch binary-indep + # Nothing to do + +binary-arch: + # Nothing to do + +binary-indep: + dh_testdir + dh_installdirs + + # Custom package commands + (for mod in $(MODULE_DIRS); do \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod} $(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod} lib/systemd/system; \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod} etc; \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod} boot; \ + cp -r $(MOD_SRC_DIR)/$(MODULE_DIR)/build/* debian/$(PACKAGE_PRE_NAME)-$${mod}/; \ + cp -r $(MOD_SRC_DIR)/$(CUSTOMS_DIRS)/build/* debian/$(PACKAGE_PRE_NAME)-$${mod}/; \ + cp $(MOD_SRC_DIR)/$${mod}/config/* debian/$(PACKAGE_PRE_NAME)-$${mod}/usr/local/bin/; \ + cp $(MOD_SRC_DIR)/$${mod}/modules/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + cp $(MOD_SRC_DIR)/$(KDAL_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + #cp $(MOD_SRC_DIR)/$${mod}/$(MODULE_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + cp $(MOD_SRC_DIR)/$${mod}/$(SERVICE_DIR)/*.service debian/$(PACKAGE_PRE_NAME)-$${mod}/lib/systemd/system/; \ + #cp $(MOD_SRC_DIR)/${CONFIG_DIR}/* debian/$(PACKAGE_PRE_NAME)-$${mod}/etc/; \ + cd -; \ + done) + (for mod in $(MODULE_DIRS_R); do \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod} $(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod} lib/systemd/system; \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod} etc; \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod} boot; \ + cp -r $(MOD_SRC_DIR)/$(MODULE_DIR)/build/* debian/$(PACKAGE_PRE_NAME)-$${mod}/; \ + cp -r $(MOD_SRC_DIR)/$(CUSTOMS_DIRS)/build/* debian/$(PACKAGE_PRE_NAME)-$${mod}/; \ + cp $(MOD_SRC_DIR)/$${mod}/config/* debian/$(PACKAGE_PRE_NAME)-$${mod}/usr/local/bin/; \ + cp $(MOD_SRC_DIR)/$${mod}/modules/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + cp $(MOD_SRC_DIR)/$(KDAL_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + #cp $(MOD_SRC_DIR)/$${mod}/$(MODULE_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + cp $(MOD_SRC_DIR)/$${mod}/$(SERVICE_DIR)/*.service debian/$(PACKAGE_PRE_NAME)-$${mod}/lib/systemd/system/; \ + #cp $(MOD_SRC_DIR)/${CONFIG_DIR}/* debian/$(PACKAGE_PRE_NAME)-$${mod}/etc/; \ + cd -; \ + done) + # Resuming debhelper scripts + dh_testroot + dh_install + dh_installchangelogs + dh_installdocs + dh_systemd_enable + dh_installinit + dh_systemd_start + dh_link + dh_fixperms + dh_compress + dh_strip + dh_installdeb + dh_gencontrol + dh_md5sums + dh_builddeb +.PHONY: build binary binary-arch binary-indep clean diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/config/arm64_micas_m2_w6010_48gt4x_fa_r0_config.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/config/arm64_micas_m2_w6010_48gt4x_fa_r0_config.py new file mode 100755 index 000000000000..f7ae811e5ec1 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/config/arm64_micas_m2_w6010_48gt4x_fa_r0_config.py @@ -0,0 +1,90 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +from platform_common import * +PCA9548START = -1 +PCA9548BUSEND = -2 + +PLATFORM_CARDID = 0x00004065 +PLATFORM_PRODUCTNAME = "M2-W6010-48GT4X-FA" +PLATFORM_PART_NUMBER = "01016994" +PLATFORM_LABEL_REVISION = "R01" +PLATFORM_ONIE_VERSION = "2018.05-rc1" +PLATFORM_MAC_SIZE = 3 +PLATFORM_MANUF_NAME = "Micas" +PLATFORM_MANUF_COUNTRY = "USA" +PLATFORM_VENDOR_NAME = "Micas" +PLATFORM_DIAG_VERSION = "0.1.0.15" +PLATFORM_SERVICE_TAG = "www.micasnetworks.com" + +LOCAL_LED_CONTROL = { + "CLOSE":{}, + "OPEN":{} +} + +MACLED_PARAMS = [] + +# start system modules +STARTMODULE = { + "i2ccheck":0, + "fancontrol":0, + "avscontrol":0, + "avscontrol_restful":0, + "sfptempmodule":0, + "sfptempmodule_interval":3, + "macledreset": 0, + "macledreset_interval": 5, + "macledset_param":MACLED_PARAMS, + } + +FRULISTS = [ + {"name":"mmceeprom","bus":5,"loc":0x50, "E2PRODUCT":'2', "E2TYPE":'5' , "CANRESET":'1'}, + {"name":"cpueeprom","bus":5,"loc":0x57,"E2PRODUCT":'2', "E2TYPE":'4', "CANRESET":'1' }, + ] + +# eeprom = "1-0056/eeprom" +E2_LOC = {"bus":1, "devno":0x56} +E2_PROTECT = {} + + +CPLDVERSIONS = [ + {"bus":2, "devno":0x0d, "name":"CPU_CPLD"}, + {"bus":3, "devno":0x30, "name":"MAC_BOARD_CPLD_1"}, +] + +FIRMWARE_TOOLS = {"cpld": [{"channel":"0","cmd":"firmware_upgrade %s cpld %s cpld", "successtips":"CPLD Upgrade succeeded!"} + ], + } + +# drivers list +DRIVERLISTS = [ + {"name":"i2c_dev", "delay":0}, + {"name":"i2c_algo_bit","delay":0}, + {"name":"spi-bitbang", "delay":0}, + {"name":"i2c_mux", "delay":0}, + {"name":"rtcpcf85063", "delay":0}, + {"name":"i2c_mux_pca954x", "delay":0}, # force_deselect_on_exit=1 + {"name":"platform_common dfd_my_type=0x4065", "delay":0}, + {"name":"firmware_driver", "delay":0}, + {"name":"wb_cpld", "delay":0}, + {"name":"wb_at24", "delay":0}, + {"name":"optoe", "delay":0}, +] + +DEVICE = [ + {"name":"rtcpcf85063","bus":1,"loc":0x51 }, + {"name":"wb_24c02","bus":1,"loc":0x56 }, + {"name":"wb_cpld","bus":3,"loc":0x30 }, + {"name":"wb_24c02","bus":5,"loc":0x50 }, + {"name":"wb_24c02","bus":5,"loc":0x57 }, +] + +INIT_PARAM = [ + {"loc":"3-0030/tx_write_protect","value": "59","delay":1}, + {"loc":"3-0030/tx_disable","value": "00"}, + {"loc":"3-0030/tx_write_protect","value": "4e"}, +] + +INIT_COMMAND = [ + "hwclock -s", +] + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/config/fan_ctrl_cfg.json b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/config/fan_ctrl_cfg.json new file mode 100644 index 000000000000..86fcf892f032 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/config/fan_ctrl_cfg.json @@ -0,0 +1,40 @@ +{ + "Device": { + "Liquid": 0, + "Buildin": 1, + "PID": 0, + "OpenLoop": 1 + }, + "Thermal": { + "INLET_TEMP": "INLET_TEMP", + "OUTLET_TEMP": "OUTLET_TEMP", + "SWITCH_TEMP": "TPS53688_TEMP" + }, + "Fans": { + "Fan1" : "Fantray1_1", + "Fan2" : "Fantray2_1" + }, + "PID": { + "Pwm_Max": 99, + "Pwm_Min": 30, + "SetPoint" : 90, + "P": 1.5, + "I": 1, + "D": 0.3, + "Temp_Min": 28.0, + "Temp_Max": 45.0, + "Sensor": "TPS53688_TEMP" + }, + "OpenLoop": { + "a": -0.06, + "b": 10.3, + "c": -142.0, + "fix_up": -8, + "pwmMax": 99, + "pwmMin": 30, + "tempMin": 25 + }, + "FanError": { + "Fan_Pwmmax":99 + } +} \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/modules/Makefile b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/modules/Makefile new file mode 100644 index 000000000000..c49defa72165 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/modules/Makefile @@ -0,0 +1 @@ +obj-m += wb_cpld.o diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/modules/wb_cpld.c b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/modules/wb_cpld.c new file mode 100644 index 000000000000..27d6763c4568 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/modules/wb_cpld.c @@ -0,0 +1,336 @@ +/* + * wb_cpld.c - A driver for control wb_cpld base on wb_cpld.c + * 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 + +/* debug switch level */ +typedef enum { + DBG_START, + DBG_VERBOSE, + DBG_KEY, + DBG_WARN, + DBG_ERROR, + DBG_END, +} dbg_level_t; + +static int debuglevel = 0; +module_param(debuglevel, int, S_IRUGO | S_IWUSR); + +#define DBG_DEBUG(fmt, arg...) do { \ + if ( debuglevel > DBG_START && debuglevel < DBG_ERROR) { \ + printk(KERN_INFO "[DEBUG]:<%s, %d>:"fmt, __FUNCTION__, __LINE__, ##arg); \ + } else if ( debuglevel >= DBG_ERROR ) { \ + printk(KERN_ERR "[DEBUG]:<%s, %d>:"fmt, __FUNCTION__, __LINE__, ##arg); \ + } else { } \ +} while (0) + +#define DBG_ERROR(fmt, arg...) do { \ + if ( debuglevel > DBG_START) { \ + printk(KERN_ERR "[ERROR]:<%s, %d>:"fmt, __FUNCTION__, __LINE__, ##arg); \ + } \ + } while (0) + +#define CPLD_SIZE 256 +#define CPLD_I2C_RETRY_TIMES 5 /* changed the number of retry time to 5 */ +#define CPLD_I2C_RETRY_WAIT_TIME 10 /* Delay 10ms before operation */ + +struct cpld_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 data[CPLD_SIZE]; /* Register value */ +}; + +static s32 cpld_i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command) +{ + int try; + s32 ret; + + ret = -1; + for (try = 0; try < CPLD_I2C_RETRY_TIMES; try++) { + if ((ret = i2c_smbus_read_byte_data(client, command) ) >= 0 ) + break; + msleep(CPLD_I2C_RETRY_WAIT_TIME); + } + return ret; +} + +static s32 cpld_i2c_smbus_read_i2c_block_data(const struct i2c_client *client, + u8 command, u8 length, u8 *values) +{ + int try; + s32 ret; + + ret = -1; + for (try = 0; try < CPLD_I2C_RETRY_TIMES; try++) { + if ((ret = i2c_smbus_read_i2c_block_data(client, command, length, values) ) >= 0 ) + break; + msleep(CPLD_I2C_RETRY_WAIT_TIME); + } + return ret; +} + +static ssize_t set_cpld_sysfs_value(struct device *dev, struct device_attribute *da, const char *buf, size_t +count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + unsigned long val; + int err; + + err = kstrtoul(buf, 16, &val); + if (err) + return err; + if ((val < 0) || (val > 0xff)) { + DBG_ERROR("please enter 0x00 ~ 0xff\n"); + return -1; + } + mutex_lock(&data->update_lock); + data->data[0] = (u8)val; + DBG_DEBUG("pos: 0x%02x count = %ld, data = 0x%02x\n", attr->index, count, data->data[0]); + i2c_smbus_write_byte_data(client, attr->index, data->data[0]); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_cpld_version(struct device *dev, struct device_attribute *da, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + s32 status; + + status = -1; + mutex_lock(&data->update_lock); + status = cpld_i2c_smbus_read_i2c_block_data(client, 0, 4, data->data); + if (status < 0) { + mutex_unlock(&data->update_lock); + return 0; + } + mutex_unlock(&data->update_lock); + return sprintf(buf, "%02x %02x %02x %02x \n", data->data[0], data->data[1], data->data[2], + data->data[3]); +} + +static ssize_t show_cpld_sysfs_value(struct device *dev, struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + s32 status; + + status = -1; + mutex_lock(&data->update_lock); + status = cpld_i2c_smbus_read_byte_data(client, attr->index); + if (status < 0) { + mutex_unlock(&data->update_lock); + return 0; + } + data->data[0] = status; + DBG_DEBUG("cpld reg pos:0x%x value:0x%02x\n", attr->index, data->data[0]); + mutex_unlock(&data->update_lock); + return sprintf(buf, "%02x\n", data->data[0]); +} + +/* sys */ +static SENSOR_DEVICE_ATTR(cpld_version, S_IRUGO, show_cpld_version, NULL, 0); + +/* sfp */ +static SENSOR_DEVICE_ATTR(sfp_presence1, S_IRUGO, show_cpld_sysfs_value, NULL, 0x30); +static SENSOR_DEVICE_ATTR(cable_led1, S_IRUGO | S_IWUSR, show_cpld_sysfs_value, set_cpld_sysfs_value, 0x20); +static SENSOR_DEVICE_ATTR(cable_led2, S_IRUGO | S_IWUSR, show_cpld_sysfs_value, set_cpld_sysfs_value, 0x21); +static SENSOR_DEVICE_ATTR(cable_led3, S_IRUGO | S_IWUSR, show_cpld_sysfs_value, set_cpld_sysfs_value, 0x22); +static SENSOR_DEVICE_ATTR(cable_led4, S_IRUGO | S_IWUSR, show_cpld_sysfs_value, set_cpld_sysfs_value, 0x23); +static SENSOR_DEVICE_ATTR(cable_led5, S_IRUGO | S_IWUSR, show_cpld_sysfs_value, set_cpld_sysfs_value, 0x24); +static SENSOR_DEVICE_ATTR(cable_led6, S_IRUGO | S_IWUSR, show_cpld_sysfs_value, set_cpld_sysfs_value, 0x25); +static SENSOR_DEVICE_ATTR(sfp_led1, S_IRUGO | S_IWUSR, show_cpld_sysfs_value, set_cpld_sysfs_value, 0x26); +static SENSOR_DEVICE_ATTR(sfp_drop_record1, S_IRUGO , show_cpld_sysfs_value, NULL, 0x38); +static SENSOR_DEVICE_ATTR(sfp_tx_fault1, S_IRUGO , show_cpld_sysfs_value, NULL, 0x50); +static SENSOR_DEVICE_ATTR(sfp_rx_loss1, S_IRUGO , show_cpld_sysfs_value, NULL, 0x70); +/* tx-disbale */ +static SENSOR_DEVICE_ATTR(tx_disable, S_IRUGO | S_IWUSR, show_cpld_sysfs_value, set_cpld_sysfs_value, 0x90); +static SENSOR_DEVICE_ATTR(tx_write_protect, S_IRUGO | S_IWUSR, show_cpld_sysfs_value, set_cpld_sysfs_value, 0x0e); + +static struct attribute *mac_cpld_0x30_sysfs_attrs[] = { + &sensor_dev_attr_cpld_version.dev_attr.attr, + &sensor_dev_attr_sfp_presence1.dev_attr.attr, + &sensor_dev_attr_cable_led1.dev_attr.attr, + &sensor_dev_attr_cable_led2.dev_attr.attr, + &sensor_dev_attr_cable_led3.dev_attr.attr, + &sensor_dev_attr_cable_led4.dev_attr.attr, + &sensor_dev_attr_cable_led5.dev_attr.attr, + &sensor_dev_attr_cable_led6.dev_attr.attr, + &sensor_dev_attr_sfp_led1.dev_attr.attr, + &sensor_dev_attr_tx_disable.dev_attr.attr, + &sensor_dev_attr_tx_write_protect.dev_attr.attr, + &sensor_dev_attr_sfp_drop_record1.dev_attr.attr, + &sensor_dev_attr_sfp_tx_fault1.dev_attr.attr, + &sensor_dev_attr_sfp_rx_loss1.dev_attr.attr, + NULL +}; + +static const struct attribute_group mac_cpld_0x30_sysfs_group = { + .attrs = mac_cpld_0x30_sysfs_attrs, +}; + +struct cpld_attr_match_group { + int bus_nr; /* I2C-BUS number */ + unsigned short addr; /* device adress */ + const struct attribute_group *attr_group_ptr;/* SYS attribute pointer */ + const struct attribute_group *attr_hwmon_ptr;/* HWMON Attribute pointer */ +}; + +static struct cpld_attr_match_group g_cpld_attr_match[] = { + {3, 0x30, &mac_cpld_0x30_sysfs_group, NULL}, + +}; + +static const struct attribute_group *cpld_get_attr_group(struct i2c_client *client, int is_hwmon) +{ + int i; + struct cpld_attr_match_group *group; + + for (i = 0; i < ARRAY_SIZE(g_cpld_attr_match); i++) { + group = &g_cpld_attr_match[i]; + DBG_DEBUG("is_hwmon %d i %d client(nr:%d,addr:0x%x), group(nr:%d,addr:0x0%x) .\n", is_hwmon, + i, client->adapter->nr, client->addr, group->bus_nr, group->addr); + if ((client->addr == group->addr) && (client->adapter->nr == group->bus_nr)) { + DBG_DEBUG("is_hwmon %d i %d nr %d addr %d .\n", is_hwmon, i, client->adapter->nr, client->addr); + return (is_hwmon) ? (group->attr_hwmon_ptr) : (group->attr_group_ptr); + } + } + + DBG_DEBUG("is_hwmon %d nr %d addr %d dismatch, return NULL.\n", is_hwmon, client->adapter->nr, client->addr); + return NULL; +} + +static int cpld_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct cpld_data *data; + int status; + const struct attribute_group *sysfs_group, *hwmon_group; + + status = -1; + DBG_DEBUG("=========cpld_probe(addr:0x%x, nr:%d)===========\n", client->addr, client->adapter->nr); + data = devm_kzalloc(&client->dev, sizeof(struct cpld_data), GFP_KERNEL); + if (!data) { + return -ENOMEM; + } + + data->client = client; + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + sysfs_group = NULL; + sysfs_group = cpld_get_attr_group(client, 0); + if (sysfs_group) { + status = sysfs_create_group(&client->dev.kobj, sysfs_group); + DBG_DEBUG("=========(addr:0x%x, nr:%d) sysfs_create_group status %d===========\n", client->addr, client->adapter->nr, status); + if (status != 0) { + DBG_ERROR("sysfs_create_group status %d.\n", status); + goto error; + } + } else { + DBG_DEBUG("=========(addr:0x%x, nr:%d) no sysfs_create_group \n", client->addr, client->adapter->nr); + } + + hwmon_group = NULL; + hwmon_group = cpld_get_attr_group(client, 1); + if (hwmon_group) { + data->hwmon_dev = hwmon_device_register_with_groups(&client->dev, client->name, data, (const struct attribute_group **)hwmon_group); + if (IS_ERR(data->hwmon_dev)) { + sysfs_remove_group(&client->dev.kobj, (const struct attribute_group *)sysfs_group); + DBG_ERROR("hwmon_device_register_with_groups failed ret %ld.\n", PTR_ERR(data->hwmon_dev)); + return PTR_ERR(data->hwmon_dev); + } + DBG_DEBUG("=========(addr:0x%x, nr:%d) hwmon_device_register_with_groups success===========\n", client->addr, client->adapter->nr); + if (status != 0) { + DBG_ERROR("sysfs_create_group status %d.\n", status); + goto error; + } + } else { + DBG_DEBUG("=========(addr:0x%x, nr:%d) no hwmon_device_register_with_groups \n", client->addr, client->adapter->nr); + } + +error: + return status; + +} + +static int cpld_remove(struct i2c_client *client) +{ + struct cpld_data *data = i2c_get_clientdata(client); + const struct attribute_group *sysfs_group, *hwmon_group; + + DBG_DEBUG("=========cpld_remove(addr:0x%x, nr:%d)===========\n", client->addr, client->adapter->nr); + + /* To be added the corresponding uninstall operation */ + sysfs_group = NULL; + sysfs_group = cpld_get_attr_group(client, 0); + if (sysfs_group) { + DBG_DEBUG("=========(addr:0x%x, nr:%d) do sysfs_remove_group \n", client->addr, client->adapter->nr); + sysfs_remove_group(&client->dev.kobj, (const struct attribute_group *)sysfs_group); + } else { + DBG_DEBUG("=========(addr:0x%x, nr:%d) no sysfs_remove_group \n", client->addr, client->adapter->nr); + } + + hwmon_group = NULL; + hwmon_group = cpld_get_attr_group(client, 1); + if (hwmon_group) { + DBG_DEBUG("=========(addr:0x%x, nr:%d) do hwmon_device_unregister \n", client->addr, client->adapter->nr); + hwmon_device_unregister(data->hwmon_dev); + } else { + DBG_DEBUG("=========(addr:0x%x, nr:%d) no hwmon_device_unregister \n", client->addr, client->adapter->nr); + } + + return 0; +} + +static const struct i2c_device_id cpld_id[] = { + { "wb_cpld", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, cpld_id); + +static struct i2c_driver wb_cpld_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "wb_cpld", + }, + .probe = cpld_probe, + .remove = cpld_remove, + .id_table = cpld_id, +}; + +module_i2c_driver(wb_cpld_driver); +MODULE_AUTHOR("wk "); +MODULE_DESCRIPTION("CPLD driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/service/m2-w6010-48gt4x_platform.service b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/service/m2-w6010-48gt4x_platform.service new file mode 100644 index 000000000000..ca2bf9f2c8b2 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/service/m2-w6010-48gt4x_platform.service @@ -0,0 +1,13 @@ +[Unit] +Description=Centec modules init +After=local-fs.target +Before=syncd.service + +[Service] +Type=oneshot +ExecStart=-/etc/init.d/platform-modules-micas-m2-w6010-48gt4x-fa start +ExecStop=-/etc/init.d/platform-modules-micas-m2-w6010-48gt4x-fa stop +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/setup.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/setup.py new file mode 100644 index 000000000000..b8aab82034ed --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/setup.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +import os +import sys +from setuptools import setup + +setup( + name='sonic_platform', + version='1.0', + description='Module to initialize centec platforms', + + packages=['sonic_platform'], + package_dir={'sonic_platform': 'sonic_platform'}, +) + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/__init__.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/__init__.py new file mode 100644 index 000000000000..4bfefa0fb636 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/__init__.py @@ -0,0 +1,3 @@ +__all__ = ["platform", "chassis"] +from sonic_platform import * + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/chassis.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/chassis.py new file mode 100644 index 000000000000..8cdf84cf3840 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/chassis.py @@ -0,0 +1,444 @@ + +#!/usr/bin/env python + +try: + import time + from sonic_platform_base.chassis_base import ChassisBase + from sonic_platform.eeprom import Eeprom + from sonic_platform.thermal import Thermal + from sonic_platform.fan_drawer import FanDrawer + from sonic_platform.sfp import Sfp + from sonic_platform.psu import Psu + from .component import Component +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +NUM_FAN_TRAY = 2 +NUM_FAN = 2 +NUM_THERMAL = 5 +NUM_PORT = 52 +NUM_PSU = 2 + +class Chassis(ChassisBase): + + port_dict = {} + STATUS_INSERTED = "1" + STATUS_REMOVED = "0" + + def __init__(self): + ChassisBase.__init__(self) + # Initialize EEPROM + self._eeprom = Eeprom() + #firmware + for i in range(0,2): + self._component_list.append(Component(i)) + # Initialize FAN + for i in range(NUM_FAN_TRAY): + fandrawer = FanDrawer(i) + self._fan_drawer_list.append(fandrawer) + self._fan_list.extend(fandrawer._fan_list) + # Initialize THERMAL + for index in range(0, NUM_THERMAL): + thermal = Thermal(index) + self._thermal_list.append(thermal) + # Initialize SFP + for index in range(0, NUM_PORT + 1): + sfp = Sfp(index) + self._sfp_list.append(sfp) + if sfp.get_presence(): + self.port_dict[index] = self.STATUS_INSERTED + else: + self.port_dict[index] = self.STATUS_REMOVED + # Initialize PSU + for index in range(0, NUM_PSU): + psu = Psu(index) + self._psu_list.append(psu) + +############################################## +# Device methods +############################################## + + def get_name(self): + """ + Retrieves the name of the chassis + Returns: + string: The name of the chassis + """ + name = '' + sys_eeprom = self.get_eeprom() + if sys_eeprom is None: + return '' + + e = sys_eeprom.read_eeprom() + name = sys_eeprom.modelstr(e) + if name is None: + return '' + return name + + 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 + """ + model = '' + sys_eeprom = self.get_eeprom() + if sys_eeprom is None: + return '' + + e = sys_eeprom.read_eeprom() + model = sys_eeprom.modelnumber(e) + if model is None: + return '' + return model + + def get_serial_number(self): + """ + Retrieves the hardware serial number for the chassis + + Returns: + A string containing the hardware serial number for this chassis. + """ + serial_number = '' + sys_eeprom = self.get_eeprom() + if sys_eeprom is None: + return '' + + e = sys_eeprom.read_eeprom() + serial_number = sys_eeprom.serial_number_str(e) + if serial_number is None: + return '' + + return serial_number + + def get_revision(self): + """ + Retrieves the hardware revision of the device + + Returns: + string: Revision value of device + """ + device_version = '' + sys_eeprom = self.get_eeprom() + if sys_eeprom is None: + return '' + + e = sys_eeprom.read_eeprom() + device_version = sys_eeprom.deviceversion(e) + if device_version is None: + return '' + + return device_version + + def get_serial(self): + """ + Retrieves the serial number of the chassis (Service tag) + Returns: + string: Serial number of chassis + """ + return self.get_serial_number() + + 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_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 initizalize_system_led(self): + 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 'green' + + def set_status_led(self, color): + return False +############################################## +# Chassis methods +############################################## + + 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' + """ + base_mac = '' + sys_eeprom = self.get_eeprom() + if sys_eeprom is None: + return '' + + e = sys_eeprom.read_eeprom() + base_mac = sys_eeprom.base_mac_addr(e) + if base_mac is None: + return '' + + return base_mac.upper() + + 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. + Ex. { '0x21':'AG9064', '0x22':'V1.0', '0x23':'AG9064-0109867821', + '0x24':'001c0f000fcd0a', '0x25':'02/03/2018 16:22:00', + '0x26':'01', '0x27':'REV01', '0x28':'AG9064-C2358-16G'} + """ + sys_eeprom_dict = dict() + sys_eeprom = self.get_eeprom() + if sys_eeprom is None: + return {} + + e = sys_eeprom.read_eeprom() + if sys_eeprom._TLV_HDR_ENABLED: + if not sys_eeprom.is_valid_tlvinfo_header(e): + return {} + total_len = (e[9] << 8) | e[10] + tlv_index = sys_eeprom._TLV_INFO_HDR_LEN + tlv_end = sys_eeprom._TLV_INFO_HDR_LEN + total_len + else: + tlv_index = sys_eeprom.eeprom_start + tlv_end = sys_eeprom._TLV_INFO_MAX_LEN + + while (tlv_index + 2) < len(e) and tlv_index < tlv_end: + if not sys_eeprom.is_valid_tlv(e[tlv_index:]): + break + + tlv = e[tlv_index:tlv_index + 2 + e[tlv_index + 1]] + name, value = sys_eeprom.decoder(None, tlv) + sys_eeprom_dict[name] = value + + if e[tlv_index] == sys_eeprom._TLV_CODE_QUANTA_CRC or \ + e[tlv_index] == sys_eeprom._TLV_CODE_CRC_32: + break + tlv_index += e[tlv_index + 1] + 2 + + return sys_eeprom_dict + + def get_thermal_manager(self): + """ + Retrieves thermal manager class on this chassis + :return: A class derived from ThermalManagerBase representing the + specified thermal manager. ThermalManagerBase is returned as default + """ + return False + + 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 (None, None) + + def get_module(self, index): + """ + Retrieves module represented by (0-based) index + + Args: + index: An integer, the index (0-based) of the module to + retrieve + + Returns: + An object dervied from ModuleBase representing the specified + module + """ + module = None + + try: + if self.get_num_modules(): + module = self._module_list[index] + except IndexError: + sys.stderr.write("Module index {} out of range (0-{})\n".format( + index, len(self._module_list)-1)) + + return module + + def get_fan_drawer(self, index): + """ + Retrieves fan drawers represented by (0-based) index + + Args: + index: An integer, the index (0-based) of the fan drawer to + retrieve + + Returns: + An object dervied from FanDrawerBase representing the specified fan + drawer + """ + fan_drawer = None + + try: + if self.get_num_fan_drawers(): + fan_drawer = self._fan_drawer_list[index] + except IndexError: + sys.stderr.write("Fan drawer index {} out of range (0-{})\n".format( + index, len(self._fan_drawer_list)-1)) + + return fan_drawer + + 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): + - bool: True if call successful, False if not; + - dict: 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. + The known devices's device_id and device_event was defined as table below. + ----------------------------------------------------------------- + device | device_id | device_event | annotate + ----------------------------------------------------------------- + 'fan' '' '0' Fan removed + '1' Fan inserted + + 'sfp' '' '0' Sfp removed + '1' Sfp inserted + '2' I2C bus stuck + '3' Bad eeprom + '4' Unsupported cable + '5' High Temperature + '6' Bad cable + + 'voltage' '' '0' Vout normal + '1' Vout abnormal + -------------------------------------------------------------------- + Ex. {'fan':{'0':'0', '2':'1'}, 'sfp':{'11':'0', '12':'1'}, + 'voltage':{'U20':'0', 'U21':'1'}} + Indicates that: + fan 0 has been removed, fan 2 has been inserted. + sfp 11 has been removed, sfp 12 has been inserted. + monitored voltage U20 became normal, voltage U21 became abnormal. + Note: For sfp, when event 3-6 happened, the module will not be avalaible, + XCVRD shall stop to read eeprom before SFP recovered from error status. + """ + + change_event_dict = {"fan": {}, "sfp": {}, "voltage": {}} + + start_time = time.time() + forever = False + + if timeout == 0: + forever = True + elif timeout > 0: + timeout = timeout / float(1000) # Convert to secs + else: + print("get_change_event:Invalid timeout value", timeout) + return False, change_event_dict + + end_time = start_time + timeout + if start_time > end_time: + print( + "get_change_event:" "time wrap / invalid timeout value", + timeout, + ) + return False, change_event_dict # Time wrap or possibly incorrect timeout + try: + while timeout >= 0: + # check for sfp + sfp_change_dict = self.get_transceiver_change_event() + # check for fan + # fan_change_dict = self.get_fan_change_event() + # check for voltage + # voltage_change_dict = self.get_voltage_change_event() + + if sfp_change_dict: + change_event_dict["sfp"] = sfp_change_dict + # change_event_dict["fan"] = fan_change_dict + # change_event_dict["voltage"] = voltage_change_dict + return True, change_event_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, change_event_dict + except Exception as e: + print(e) + print("get_change_event: Should not reach here.") + return False, change_event_dict + + def get_transceiver_change_event(self): + current_port_dict = {} + ret_dict = {} + + # Check for OIR events and return ret_dict + for index in range(0, NUM_PORT + 1): + if self._sfp_list[index].get_presence(): + current_port_dict[index] = self.STATUS_INSERTED + else: + current_port_dict[index] = self.STATUS_REMOVED + + if len(self.port_dict) == 0: # first time + self.port_dict = current_port_dict + return {} + + if current_port_dict == self.port_dict: + return {} + + # Update reg value + for index, status in current_port_dict.items(): + if self.port_dict[index] != status: + ret_dict[index] = status + #ret_dict[str(index)] = status + self.port_dict = current_port_dict + for index, status in ret_dict.items(): + if int(status) == 1: + pass + #self._sfp_list[int(index)].check_sfp_optoe_type() + return ret_dict diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/component.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/component.py new file mode 100644 index 000000000000..325c000eea74 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/component.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python + +try: + import subprocess + from sonic_platform_base.component_base import ComponentBase + import sonic_platform.hwaccess as hwaccess +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +def get_cpld_version(bus, i2caddr): + return '{}{}{}{}'.format(hwaccess.i2c_get(bus, i2caddr, 1), + hwaccess.i2c_get(bus, i2caddr, 2), + hwaccess.i2c_get(bus, i2caddr, 3), + hwaccess.i2c_get(bus, i2caddr, 0) + ) + +def get_cpu_cpld_version(): + return get_cpld_version(2, 0x0d) + +def get_cpld1_version(): + return get_cpld_version(3, 0x30) + +COMPONENT_LIST= [ + ['CPU CPLD', + 'cpu board', + get_cpu_cpld_version + ], + + ['MAC1 CPLD', + 'mac1 board', + get_cpld1_version + ] + ] + +class Component(ComponentBase): + """Platform-specific Component class""" + + def __init__(self, component_index=0): + ComponentBase.__init__(self) + self.index = component_index + + def get_name(self): + """ + Retrieves the name of the component + + Returns: + A string containing the name of the component + """ + return COMPONENT_LIST[self.index][0] + + def get_description(self): + """ + Retrieves the description of the component + + Returns: + A string containing the description of the component + """ + return COMPONENT_LIST[self.index][1] + + def get_firmware_version(self): + """ + Retrieves the firmware version of the component + + Returns: + A string containing the firmware version of the component + """ + return COMPONENT_LIST[self.index][2]() + + 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 + """ + try: + successtips = "CPLD Upgrade succeeded!" + status, output = subprocess.getstatusoutput("which firmware_upgrade") + if status or len(output) <= 0: + logger.error("no upgrade tool.") + return False + cmdstr = "%s %s cpld %d cpld"%(output,image_path,self.slot) + ret, log = subprocess.getstatusoutput(cmdstr) + if ret == 0 and successtips in log: + return True + logger.error("upgrade failed. ret:%d, log:\n%s" % (ret, log)) + except Exception as e: + logger.error(str(e)) + return False + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/eeprom.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/eeprom.py new file mode 100644 index 000000000000..af5da656b7b0 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/eeprom.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +try: + from sonic_eeprom import eeprom_tlvinfo + import binascii +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +class Eeprom(eeprom_tlvinfo.TlvInfoDecoder): + """Platform-specific Eeprom class""" + + def __init__(self): + eeprom_path = "/sys/bus/i2c/devices/1-0056/eeprom" + if eeprom_path is None: + raise ValueError("get eeprom path failed") + + super(Eeprom, self).__init__(eeprom_path, 0, "", True) + + + def modelnumber(self, e): + ''' + Returns the value field of the model(part) number TLV as a string + ''' + (is_valid, t) = self.get_tlv_field(e, self._TLV_CODE_PART_NUMBER) + if not is_valid: + return super(Eeprom, self).part_number_str(e) + + return t[2].decode("ascii") + + def deviceversion(self, e): + ''' + Returns the value field of the Device Version as a string + ''' + (is_valid, t) = self.get_tlv_field(e, self._TLV_CODE_DEVICE_VERSION) + if not is_valid: + return "N/A" + + return str(ord(t[2])) diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/fan.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/fan.py new file mode 100644 index 000000000000..ebbb386278ed --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/fan.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python + +import time + +try: + from sonic_platform_base.fan_base import FanBase + from .redfish_api import Redfish_Api +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +FAN_NAME_LIST = ["1", "2"] + +class Fan(FanBase): + """Platform-specific Fan class""" + + def __init__(self, fan_tray_index, fan_index=0): + self.fan_index = fan_index + self.fan_tray_index = fan_tray_index + self.redfish = Redfish_Api() + self.pinf = {} + self._fan_list = [] + FanBase.__init__(self) + self.begin = time.time() + + def get_power_3s(self): + self.elapsed = time.time() + if not self.pinf or self.elapsed - self.begin >= 3: + self.begin = time.time() + self.pinf = self.redfish.get_thermal() + + def get_speed_pwm(self): + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fan_tray_index] + speed = output.get("Oem").get("OpenBmc").get("FanSpeedLevelPercents") + return int(speed) + + def get_speed_rpm(self): + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fan_tray_index] + speed = output.get("Reading") + return int(speed) + + def get_high_critical_threshold(self): + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fan_tray_index] + high = output.get("UpperThresholdFatal") + return int(high) + + def get_low_critical_threshold(self): + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fan_tray_index] + low = output.get("LowerThresholdFatal") + return int(low) + + def set_speed_pwm(self, speed): + post_url = '/redfish/v1/Chassis/1/Thermal/Actions/Oem/OpenBmc/Fan.SetSpeed' + playload = {} + playload["FanName"] = "Fan0" + playload["FanSpeedLevelPercents"] = str(speed) + return self.redfish.post_odata(post_url, playload) + + def get_status_led(self): + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fan_tray_index] + led = output.get("Oem").get("OpenBmc").get("IndicatorLEDColor") + return led + + def set_status_led(self, color): + playload = {} + led = {} + led_list = [] + led["IndicatorLEDColor"] = color + led["LEDType"] = "fan" + led_list.append(led) + playload["LEDs"] = led_list + # boardsLed + return self.redfish.post_boardLed(playload) + + def get_direction(self): + return "intake" + + def get_name(self): + fan_name = FAN_NAME_LIST[self.fan_index] + return "Fantray{}_{}".format(self.fan_tray_index+1, fan_name) + + def get_model(self): + """ + Retrieves the part number of the FAN + Returns: + string: Part number of FAN + """ + return 'N/A' + + 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 get_revision(self): + """ + Retrieves the hardware revision of the device + + Returns: + string: Revision value of device + """ + return 'N/A' + + def get_serial(self): + """ + Retrieves the serial number of the FAN + Returns: + string: Serial number of FAN + """ + return 'N/A' + + def get_presence(self): + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fan_tray_index] + state = output.get("Status").get("Status").get("State") + if state == "Enabled" or state == "UnavailableOffline": + return True + return False + + def get_status(self): + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fan_tray_index] + if output.get("Status").get("Status").get("Health") == "OK": + return True + return False + + def get_speed(self): + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fan_tray_index] + speed = output.get("Reading") + speed_percentage = round((speed*100)/17500) + if speed_percentage > 100: + speed_percentage = 100 + return speed_percentage + else: + return speed_percentage + + 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 30 + + 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) + """ + return self.get_speed_pwm() diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/fan_drawer.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/fan_drawer.py new file mode 100644 index 000000000000..c86b85566f33 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/fan_drawer.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +import time + +try: + from sonic_platform_base.fan_drawer_base import FanDrawerBase + from sonic_platform.fan import Fan + from .redfish_api import Redfish_Api +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +class FanDrawer(FanDrawerBase): + + FANS_PER_FANTRAY = 1 + + def __init__(self, fantray_index=0): + FanDrawerBase.__init__(self) + self.fantrayindex = fantray_index + self.redfish = Redfish_Api() + self.pinf = {} + self.begin = time.time() + for i in range(self.FANS_PER_FANTRAY): + self._fan_list.append(Fan(fantray_index, i)) + + def get_power_3s(self): + self.elapsed = time.time() + if not self.pinf or self.elapsed - self.begin >= 3: + self.begin = time.time() + self.pinf = self.redfish.get_thermal() + + def get_name(self): + return "FanTray{}".format(self.fantrayindex+1) + + def get_presence(self): + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fantrayindex] + state = output.get("Status").get("Status").get("State") + if state == "Enabled" or state == "UnavailableOffline": + return True + return False + + def get_model(self): + """ + Retrieves the part number of the FAN + Returns: + string: Part number of FAN + """ + return 'N/A' + + def get_serial(self): + """ + Retrieves the serial number of the FAN + Returns: + string: Serial number of FAN + """ + return 'N/A' + + def get_revision(self): + """ + Retrieves the hardware revision of the device + + Returns: + string: Revision value of device + """ + 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 + """ + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fantrayindex] + if output.get("Status").get("Status").get("Health") == "OK": + return True + return False + + 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 get_status_led(self): + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fantrayindex] + led = output.get("Oem").get("OpenBmc").get("IndicatorLEDColor") + return led + + def set_status_led(self, color): + playload = {} + led = {} + led_list = [] + led["IndicatorLEDColor"] = color + led["LEDType"] = "fan" + led_list.append(led) + playload["LEDs"] = led_list + # boardsLed + return self.redfish.post_boardLed(playload) + + def get_maximum_consumed_power(self): + """ + Retrives the maximum power drawn by Fan Drawer + + Returns: + A float, with value of the maximum consumable power of the + component. + """ + return 'N/A' diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/hwaccess.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/hwaccess.py new file mode 100644 index 000000000000..7a04e766ae28 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/hwaccess.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +import struct +import mmap +import subprocess + +# Read PCI device + +def pci_mem_read(mm, offset): + mm.seek(offset) + read_data_stream = mm.read(4) + return struct.unpack('I',read_data_stream)[0] + +def pci_get_value(resource, offset): + with open(resource, 'r+b') as fd: + mm = mmap.mmap(fd.fileno(), 0) + val = pci_mem_read(mm, offset) + mm.close() + return val + +def pci_mem_write(memmap, offset, data): + """ Write PCI device """ + memmap.seek(offset) + memmap.write(struct.pack('I', data)) + +def pci_set_value(resource, val, offset): + """ Set a value to PCI device """ + with open(resource, 'w+b') as filed: + memmap = None + try: + memmap = mmap.mmap(filed.fileno(), 0) + pci_mem_write(memmap, offset, val) + except EnvironmentError: + print("error") + if memmap is not None: + memmap.close() + +# Read I2C device + +def i2c_get(bus, i2caddr, ofs): + try: + valx = int(subprocess.check_output(['/usr/sbin/i2cget','-f', '-y', str(bus), str(i2caddr), str(ofs)]), 16) + return "{:02x}".format(valx) + except (FileNotFoundError, subprocess.CalledProcessError): + return -1 + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/platform.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/platform.py new file mode 100644 index 000000000000..7225a71b0307 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/platform.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +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") + + +class Platform(PlatformBase): + + def __init__(self): + PlatformBase.__init__(self) + self._chassis = Chassis() + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/psu.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/psu.py new file mode 100644 index 000000000000..5176552c12e1 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/psu.py @@ -0,0 +1,231 @@ +#!/usr/bin/env python + +import time +import imp +import os +import sys + +try: + from sonic_platform_base.psu_base import PsuBase + from .redfish_api import Redfish_Api +except ImportError as e: + raise ImportError("%s - required module not found" % e) + +class Psu(PsuBase): + """Platform-specific Psu class""" + + def __init__(self, index=0): + PsuBase.__init__(self) + self.redfish = Redfish_Api() + self.pinf = {} + self.psu_index = index + self._fan_list = [] + self._thermal_list = [] + self.begin = time.time() + + def get_power_3s(self): + self.elapsed = time.time() + if not self.pinf or self.elapsed - self.begin >= 3: + self.begin = time.time() + self.pinf = self.redfish.get_power() + + def get_presence(self): + return True + + def get_fan(self, index): + """ + Retrieves fan module represented by (0-based) index + + Args: + index: An integer, the index (0-based) of the fan module to + retrieve + + Returns: + An object dervied from FanBase representing the specified fan + module + """ + return None + + def get_powergood_status(self): + self.get_power_3s() + ctrl = self.pinf["PowerSupplies"] + output = ctrl[self.psu_index] + if output.get("Status").get("Health") == "OK": + return True + else: + return False + + def get_name(self): + """ + Retrieves the name of the device + + Returns: + string: The name of the device + """ + return "PSU {}".format(self.psu_index + 1) + + def get_serial(self): + self.get_power_3s() + ctrl = self.pinf["PowerSupplies"] + output = ctrl[self.psu_index] + serial = output.get("SerialNumber") + return serial + + def get_model(self): + self.get_power_3s() + ctrl = self.pinf["PowerSupplies"] + output = ctrl[self.psu_index] + model = output.get("Model") + return model + + def get_revision(self): + self.get_power_3s() + ctrl = self.pinf["PowerSupplies"] + output = ctrl[self.psu_index] + manufacturer = output.get("Manufacturer") + return manufacturer + + def get_voltage(self): + self.get_power_3s() + ctrl = self.pinf["PowerSupplies"] + output = ctrl[self.psu_index] + voltage = output.get("Oem").get("OpenBmc").get("OutputVoltage") + return voltage + + def get_input_current(self): + self.get_power_3s() + ctrl = self.pinf["PowerSupplies"] + output = ctrl[self.psu_index] + current = output.get("Oem").get("OpenBmc").get("OutputAmperage") + return current + + def get_input_voltage(self): + self.get_power_3s() + ctrl = self.pinf["PowerSupplies"] + output = ctrl[self.psu_index] + voltage = output.get("Oem").get("OpenBmc").get("OutputVoltage") + return voltage + + def get_current(self): + self.get_power_3s() + ctrl = self.pinf["PowerSupplies"] + output = ctrl[self.psu_index] + current = output.get("Oem").get("OpenBmc").get("OutputAmperage") + return current + + def get_power(self): + self.get_power_3s() + ctrl = self.pinf["PowerSupplies"] + output = ctrl[self.psu_index] + current = output.get("Oem").get("OpenBmc").get("OutputAmperage") + voltage = output.get("Oem").get("OpenBmc").get("OutputVoltage") + power = float(current)*float(voltage) + return round(power,2) + + def get_temperature(self): + """ + Retrieves current temperature reading from PSU + + Returns: + A float number of current temperature in Celsius up to nearest thousandth + of one degree Celsius, e.g. 30.125 + """ + # no temperature sensor + value = 35 + return round(float(value), 1) + + def get_status_led(self): + return "BuildIn" + + def set_status_led(self, color): + playload = {} + led = {} + led_list = [] + led["IndicatorLEDColor"] = color + led["LEDType"] = "pwr" + led_list.append(led) + playload["LEDs"] = led_list + # boardsLed + return self.redfish.post_boardLed(playload) + + def get_maximum_supplied_power(self): + """ + Retrieves the maximum supplied power by PSU + + Returns: + A float number, the maximum power output in Watts. + e.g. 1200.1 + """ + return False + + 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 get_status(self): + """ + Retrieves the operational status of the PSU + + Returns: + bool: True if PSU is operating properly, False if not + """ + return self.get_powergood_status() + + def get_temperature_high_threshold(self): + """ + Retrieves the high threshold temperature of PSU + + Returns: + A float number, the high threshold temperature of PSU in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + value = 75 + return round(float(value), 1) + + 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 + """ + value = 14.52 + return str(round(float(value), 2)) + + 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 + """ + value = 9.72 + return str(round(float(value), 2)) + + def get_thermal(self, index): + """ + Retrieves thermal unit represented by (0-based) index + + Args: + index: An integer, the index (0-based) of the thermal to + retrieve + + Returns: + An object dervied from ThermalBase representing the specified thermal + """ + return None diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/redfish_api.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/redfish_api.py new file mode 100644 index 000000000000..a577c1b216da --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/redfish_api.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import shlex +import json +import os +import ssl +import subprocess +import syslog + +class Redfish_Api(): + BmcBaseUrl = 'http://240.1.1.1:8080' + ThermalUrl = '/redfish/v1/Chassis/1/Thermal' + PowerUrl = '/redfish/v1/Chassis/1/Power' + ThresholdSensorsUrl = '/redfish/v1/Chassis/1/ThresholdSensors' + FanSpeedUrl = '/redfish/v1/Chassis/1/Thermal/Actions/Oem/OpenBmc/Fan.SetSpeed' + BoardsUrl = '/redfish/v1/Chassis/1/Boards/' + BoardLedUrl = "/redfish/v1/Chassis/1/Boards/{}/Actions/Oem/OpenBmc/Boards.SetLED" + + # Maximum time in seconds that you allow the connection to the server to take. + connect_timeout = 30 + # Maximum time in seconds that you allow the whole operation to take + operation_timeout = 300 + + default_prefix='/redfish/v1/' + session = None + __DEBUG__ = "N" + __DUMP_RESP__ = "N" + RST_STATUS = "status" + RST_SUCCESS = "OK" + refish_logger = None + + def redfish_log_debug(self, msg): + if (self.__DEBUG__ == "Y"): + syslog.openlog("redfis_api") + syslog.syslog(syslog.LOG_DEBUG, msg) + syslog.closelog() + + def redfish_log_error(self, msg): + syslog.openlog("redfish_api") + syslog.syslog(syslog.LOG_ERR, msg) + syslog.closelog() + + def __init__(self): + pass + + def get_full_url(self, url): + return self.BmcBaseUrl + url + + def _exec_cmd(self, cmd): + self.redfish_log_debug("Cmd: %s" % cmd) + p = subprocess.Popen(shlex.split(cmd), shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p.wait() + self.redfish_log_debug("Cmd return: %d" % p.returncode) + str_stdout = p.stdout.read().decode('utf-8') + str_stderr = p.stderr.read().decode('utf-8') + self.redfish_log_debug("Cmd stdout: %s" % str_stdout) + if p.returncode !=0: + self.redfish_log_error("Cmd: %s, failed! error msg:%s" % (cmd, str_stderr)) + return None + else: + try: + ret = json.loads(str_stdout) + return ret + except Exception as e: + self.redfish_log_error("Cmd: %s, failed! stdout msg:%s" % (cmd, str_stdout)) + return None + + def _redfish_get(self, url): + self.redfish_log_debug("Get info from %s." % url) + result = None + try: + cmd_get="curl --connect-timeout %d -m %d -X GET %s" % (self.connect_timeout, self.operation_timeout, self.get_full_url(url)) + result = self._exec_cmd(cmd_get) + return result + except Exception as e: + self.redfish_log_error("error_message: %s" % e) + return None + + def _redfish_post(self, url, playload): + self.redfish_log_debug("post url: %s" % url) + self.redfish_log_debug("Playload: %s" % playload) + + playload_json = json.dumps(playload) + result = False + try: + cmd_post="curl --connect-timeout %d -m %d -X POST %s -d \'%s\'" % (self.connect_timeout, self.operation_timeout, self.get_full_url(url), playload_json) + ret_msg = self._exec_cmd(cmd_post) + if ret_msg == None: + return False + elif ret_msg["success"] == False: + redfish_log_error("Url: '%s', Playload: '%s', Bmc return failed, error_message: %s" % (url, playload_json, ret_msg["Message"])) + result = False + else: + result = True + return result + except Exception as e: + redfish_log_error("error_message: %s" % e) + return False + + def get_thermal(self): + """Get thermal info + :returns: class 'redfish.rest.v1.RestResponse' or None when failed + """ + return self._redfish_get(self.ThermalUrl) + + def get_power(self): + """Get power info + :returns: class 'redfish.rest.v1.RestResponse' or None when failed + """ + return self._redfish_get(self.PowerUrl) + + def get_thresholdSensors(self): + """Get thresholdSensors info + :returns: class 'redfish.rest.v1.RestResponse' or None when failed + """ + return self._redfish_get(self.ThresholdSensorsUrl) + + def post_odata(self, odata_id, playload): + """post odata info + :params odata_id: the specified odata_id path + :type odata_id: string + :playload: info to post + :type: dictionary + :returns: True or False + """ + if odata_id is None or playload is None: + print("post failed: odata_id or playload is None") + return False + return self._redfish_post(odata_id, playload) + + def get_odata(self, odata_id): + """Get odata info + :params odata_id: the specified odata_id path + :type odata_id: string + :returns: class 'redfish.rest.v1.RestResponse' or None when failed + """ + if odata_id is None: + print("Get odata_id failed: odata_id is None") + return None + return self._redfish_get(odata_id) + + def post_fanSpeed(self, playload): + """post odata info + :playload: info to post + :type: dictionary + :returns: True or False + """ + if playload is None: + print("post failed: playload is None") + return False + return self._redfish_post(self.FanSpeedUrl, playload) + + def get_board(self, board_name="indicatorboard"): + """Get board info + :board_name: name of board, default is "indicatorboard" + :type: string + :returns: class'redfish.rest.v1.RestResponse' or None when failed + """ + if board_name is None : + print("get failed: board_name is None") + return None + return self._redfish_get(self.BoardsUrl + board_name) + + def post_boardLed(self, playload, board_name="indicatorboard"): + """post boardLed info + :board_name: name of board, default is "indicatorboard" + :type: string + :playload: info to post + :type: dictionary + :returns: True or False + """ + if board_name is None or playload is None: + print("post failed: playload is None") + return False + return self._redfish_post(self.BoardLedUrl.format(board_name), playload) + + ''' not supported currently + def post_thermal(self, playload): + """post thermal info + :playload: info to post + :type: dictionary + :returns: True or False + """ + if playload is None: + print("post_thermal failed: playload is None") + return None + return self._redfish_post(self.ThermalUrl, playload) + + def post_power(self, playload): + """post power info + :playload: info to post + :type: dictionary + :returns: True or False + """ + if playload is None: + print("post_power failed: playload is None") + return None + return self._redfish_post(self.PowerUrl, playload) + + def post_thresholdSensors(self, playload): + """post thresholdSensors info + :playload: info to post + :type: dictionary + :returns: True or False + """ + if playload is None: + print("post_thresholdSensors failed: playload is None") + return None + return self._redfish_post(self.ThresholdSensorsUrl, playload) + + def get_fanSpeed(self): + """Get board led info + :returns: class'redfish.rest.v1.RestResponse' or None when failed + """ + return self._redfish_get(self.FanSpeedUrl) + + def post_board(self, playload, board_name="indicatorboard"): + """post board info + :board_name: name of board, default is "indicatorboard" + :type: string + :playload: info to post + :type: dictionary + :returns: True or False + """ + if board_name is None or playload is None: + print("post failed: playload is None") + return False + return self._redfish_post(self.BoardsUrl + board_name, playload) + + def get_boardLed(self, board_name="indicatorboard"): + """Get boardLed info + :board_name: name of board, default is "indicatorboard" + :type: string + :returns: class'redfish.rest.v1.RestResponse' or None when failed + """ + if board_name is None : + print("get failed: board_name is None") + return None + return self._redfish_get(self.BoardsUrl % board_name) + + ''' + +''' +if __name__ == '__main__': + redfish = Redfish_Api() + + ### get + # boards + ret = redfish.get_board() + if ret is None: + print("get failed: board") + else: + print("get succeeded, board:%s" % ret) + + ret = redfish.get_thresholdSensors() + if ret is None: + print("get failed: threshold") + else: + print("get succeeded, threshold:%s" % ret) + + ret = redfish.get_power() + if ret is None: + print("get failed: power") + else: + print("get succeeded, power:%s" % ret) + + ret = redfish.get_thermal() + if ret is None: + print("get failed:thermal") + else: + print("get succeeded,thermal:%s" % ret) + + # get playload + resp = redfish.get_thresholdSensors() + if (resp != None): + print(resp["@odata.id"]) + print(resp["@odata.type"]) + print(resp["Id"]) + print(resp["Name"]) + else: + print("Failed: get_thresholdSensors") + + ### post + # fanSpeed + playload = {} + playload["FanName"] = 'Fan0' + playload["FanSpeedLevelPercents"] = "70" + print("post fanSpeed:%s" % redfish.post_fanSpeed(playload)) + + #{"LEDs": [{"IndicatorLEDColor": "green","LEDType": "sys"},{"IndicatorLEDColor": "off","LEDType": "pwr"},{"IndicatorLEDColor": "green","LEDType": "fan"}]} + playload = {} + led = {} + led1 = {} + led_list = [] + led["IndicatorLEDColor"] = "green" + led["LEDType"] = "sys" + led1["IndicatorLEDColor"] = "off" + led1["LEDType"] = "pwr" + led_list.append(led) + led_list.append(led1) + playload["LEDs"] = led_list + # boardsLed + print("post boardLed:%s" % redfish.post_boardLed(playload)) +''' diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/sfp.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/sfp.py new file mode 100644 index 000000000000..29dd0cb079c1 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/sfp.py @@ -0,0 +1,325 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +############################################################################# +# +# Module contains an implementation of SONiC Platform Base API and +# provides the platform information +# +# +# *_device.py config version instruction: +# ver 1.0 - platform api: +# "presence_cpld": { +# "dev_id": { +# [dev_id]: { +# "offset": { +# [offset]: [port_id] +# } +# } +# } +# } +# "reset_cpld": { +# "dev_id": { +# [dev_id]: { +# "offset": { +# [offset]: [port_id] +# } +# } +# } +# } +# ver 2.0 - wb_plat: +# "presence_path": "/xx/wb_plat/xx[port_id]/present" +# "eeprom_path": "/sys/bus/i2c/devices/i2c-[bus]/[bus]-0050/eeprom" +# "reset_path": "/xx/wb_plat/xx[port_id]/reset" +############################################################################# +import sys +import time +import os +import syslog +import traceback +from abc import abstractmethod + +configfile_pre = "/usr/local/bin/" +sys.path.append(configfile_pre) + +try: + from sonic_platform_base.sonic_xcvr.sfp_optoe_base import SfpOptoeBase + +except ImportError as error: + raise ImportError(str(error) + "- required module not found") from error + +LOG_DEBUG_LEVEL = 1 +LOG_WARNING_LEVEL = 2 +LOG_ERROR_LEVEL = 3 + + +class Sfp(SfpOptoeBase): + + OPTOE_DRV_TYPE1 = 1 + OPTOE_DRV_TYPE2 = 2 + OPTOE_DRV_TYPE3 = 3 + + # index must start at 1 + def __init__(self, index): + SfpOptoeBase.__init__(self) + self.sfp_type = None + self.log_level_config = LOG_WARNING_LEVEL + # Init instance of SfpCust + self._sfp_api = SfpV2(index) + + def get_eeprom_path(self): + return self._sfp_api._get_eeprom_path() + + def read_eeprom(self, offset, num_bytes): + return self._sfp_api.read_eeprom(offset, num_bytes) + + def write_eeprom(self, offset, num_bytes, write_buffer): + return self._sfp_api.write_eeprom(offset, num_bytes, write_buffer) + + def get_presence(self): + return self._sfp_api.get_presence() + + def get_transceiver_info(self): + # temporary solution for a sonic202111 bug + transceiver_info = super().get_transceiver_info() + try: + if transceiver_info["vendor_rev"] is not None: + transceiver_info["hardware_rev"] = transceiver_info["vendor_rev"] + except BaseException: + print(traceback.format_exc()) + return None + return transceiver_info + + def set_optoe_write_max(self, write_max): + """ + This func is declared and implemented by SONiC but we're not supported + so override it as NotImplemented + """ + self._sfplog(LOG_DEBUG_LEVEL, "set_optoe_write_max NotImplemented") + + def refresh_xcvr_api(self): + """ + Updates the XcvrApi associated with this SFP + """ + self._xcvr_api = self._xcvr_api_factory.create_xcvr_api() + class_name = self._xcvr_api.__class__.__name__ + optoe_type = None + # set sfp_type + if 'CmisApi' in class_name: + self.sfp_type = 'QSFP-DD' + optoe_type = self.OPTOE_DRV_TYPE3 + elif 'Sff8472Api' in class_name: + self.sfp_type = 'SFP' + optoe_type = self.OPTOE_DRV_TYPE2 + elif ('Sff8636Api' in class_name or 'Sff8436Api' in class_name): + self.sfp_type = 'QSFP' + optoe_type = self.OPTOE_DRV_TYPE1 + # set optoe driver + if optoe_type is not None: + self._sfp_api.set_optoe_type(optoe_type) + + def _sfplog(self, log_level, msg): + if log_level >= self.log_level_config: + try: + syslog.openlog("Sfp") + if log_level == LOG_DEBUG_LEVEL: + syslog.syslog(syslog.LOG_DEBUG, msg) + elif log_level == LOG_WARNING_LEVEL: + syslog.syslog(syslog.LOG_DEBUG, msg) + elif log_level == LOG_ERROR_LEVEL: + syslog.syslog(syslog.LOG_ERR, msg) + syslog.closelog() + + except BaseException: + print(traceback.format_exc()) + + +class SfpCust(): + def __init__(self, index): + self.eeprom_path = None + self._init_config(index) + + def _init_config(self, index): + self.log_level_config = LOG_WARNING_LEVEL + self._port_id = index + self.eeprom_retry_times = 5 + self.eeprom_retry_break_sec = 0.2 + + def _get_eeprom_path(self): + return self.eeprom_path or None + + @abstractmethod + def get_presence(self): + pass + + def read_eeprom(self, offset, num_bytes): + try: + for i in range(self.eeprom_retry_times): + with open(self._get_eeprom_path(), mode='rb', buffering=0) as f: + f.seek(offset) + result = f.read(num_bytes) + # temporary solution for a sonic202111 bug + if len(result) < num_bytes: + result = result[::-1].zfill(num_bytes)[::-1] + if result is not None: + return bytearray(result) + time.sleep(self.eeprom_retry_break_sec) + continue + + except BaseException: + self._sfplog(LOG_ERROR_LEVEL, traceback.format_exc()) + return None + + def write_eeprom(self, offset, num_bytes, write_buffer): + try: + for i in range(self.eeprom_retry_times): + ret = SfpOptoeBase.write_eeprom(self, offset, num_bytes, write_buffer) + if ret is False: + time.sleep(self.eeprom_retry_break_sec) + continue + break + + return ret + except BaseException: + self._sfplog(LOG_ERROR_LEVEL, traceback.format_exc()) + return False + + @abstractmethod + def set_optoe_type(self, optoe_type): + pass + + @abstractmethod + def set_reset(self, reset): + pass + + def _convert_str_range_to_int_arr(self, range_str): + if not range_str: + return [] + + int_range_strs = range_str.split(',') + range_res = [] + for int_range_str in int_range_strs: + if '-' in int_range_str: + range_s = int(int_range_str.split('-')[0]) + range_e = int(int_range_str.split('-')[1]) + 1 + else: + range_s = int(int_range_str) + range_e = int(int_range_str) + 1 + + range_res = range_res + list(range(range_s, range_e)) + + return range_res + + def _sfplog(self, log_level, msg): + if log_level >= self.log_level_config: + try: + syslog.openlog("SfpCust") + if log_level == LOG_DEBUG_LEVEL: + syslog.syslog(syslog.LOG_DEBUG, msg) + elif log_level == LOG_WARNING_LEVEL: + syslog.syslog(syslog.LOG_DEBUG, msg) + elif log_level == LOG_ERROR_LEVEL: + syslog.syslog(syslog.LOG_ERR, msg) + syslog.closelog() + + except BaseException: + print(traceback.format_exc()) + + +class SfpV2(SfpCust): + def _init_config(self, index): + super()._init_config(index) + # init eeprom path + sfp_pt2ee_path_list = [0] * 53 + sfp_pt2ee_path_list[49:53] = [9, 10, 11, 12] + + eeprom_path_config = "/sys/bus/i2c/devices/i2c-%d/%d-0050/eeprom" + eeprom_path_key = sfp_pt2ee_path_list[self._port_id] + self.eeprom_path = None if eeprom_path_config is None or eeprom_path_key == 0 else eeprom_path_config % ( + eeprom_path_key, eeprom_path_key) + self._sfplog(LOG_DEBUG_LEVEL, "Done init eeprom path: %s" % self.eeprom_path) + + # init presence path + self.presence_path = "/sys/bus/i2c/devices/3-0030/sfp_presence1" + self.presence_val_is_present = 0 + self._sfplog(LOG_DEBUG_LEVEL, "Done init presence path: %s" % self.presence_path) + + # init optoe driver path + optoe_driver_path = "/sys/bus/i2c/devices/i2c-%d/%d-0050/dev_class" + optoe_driver_key = sfp_pt2ee_path_list[self._port_id] + self.dev_class_path = None if optoe_driver_path is None or optoe_driver_key == 0 else optoe_driver_path % ( + optoe_driver_key, optoe_driver_key) + self._sfplog(LOG_DEBUG_LEVEL, "Done init optoe driver path: %s" % self.dev_class_path) + + # init reset path + self.reset_val_is_reset = 0 + + new_device_path = "/sys/bus/i2c/devices/i2c-%d/new_device" + new_device_key = sfp_pt2ee_path_list[self._port_id] + self.new_class_path = None if new_device_path is None or new_device_key == 0 else new_device_path % new_device_key + self._sfplog(LOG_DEBUG_LEVEL, "Done init new_class path: %s" % self.new_class_path) + + if sfp_pt2ee_path_list[self._port_id] != 0: + self.add_new_sfp_device(self._port_id, 0x50) + self._sfplog(LOG_DEBUG_LEVEL, "Done add_new_sfp_device 0x50 port %d" % self._port_id) + + def sfp_add_dev(self, new_device_path, devaddr, devtype): + try: + # Write device address to new_device file + nd_file = open(new_device_path, "w") + nd_str = "%s %s" % (devtype, hex(devaddr)) + nd_file.write(nd_str) + nd_file.close() + except Exception as err: + self._sfplog(LOG_ERROR_LEVEL, ("Error writing to new device file: %s" % str(err))) + return 1 + else: + return 0 + + def add_new_sfp_device(self, port_num, devid): + if os.path.exists(self.dev_class_path): + return + + ret = self.sfp_add_dev(self.new_class_path, devid, "optoe2") + if ret != 0: + self._sfplog(LOG_ERROR_LEVEL, "Error adding sfp device") + + def get_presence(self): + sfp_ls = [49, 50, 51, 52] + if self._port_id not in sfp_ls or self.presence_path is None: + self._sfplog(LOG_ERROR_LEVEL, "presence_path is None!") + return False + try: + with open(self.presence_path, "rb") as data: + presence_data = data.read(2) + if presence_data == "": + return False + result = int(presence_data, 16) + + # ModPrsL is active low + presence_offset = sfp_ls.index(self._port_id) + if result & (1 << presence_offset) == 0: + return True + return False + except BaseException: + self._sfplog(LOG_ERROR_LEVEL, traceback.format_exc()) + return False + + def set_reset(self, reset): + return True + + def set_optoe_type(self, optoe_type): + if self.dev_class_path is None: + self._sfplog(LOG_ERROR_LEVEL, "dev_class_path is None!") + return False + try: + with open(self.dev_class_path, "r+") as dc_file: + dc_file_val = dc_file.read(1) + if int(dc_file_val) != optoe_type: + dc_str = "%s" % str(optoe_type) + dc_file.write(dc_str) + # dc_file.close() + except BaseException: + self._sfplog(LOG_ERROR_LEVEL, traceback.format_exc()) + return False + return True diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/thermal.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/thermal.py new file mode 100644 index 000000000000..5fded8d9ddeb --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/thermal.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python + +import os +import re +import os.path +import time + +try: + from sonic_platform_base.thermal_base import ThermalBase + from .redfish_api import Redfish_Api +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +class Thermal(ThermalBase): + """Platform-specific Thermal class""" + + def __init__(self, thermal_index): + ThermalBase.__init__(self) + self.index = thermal_index + self.high_threshold = float(112) + self.redfish = Redfish_Api() + self.pinf = {} + self.begin = time.time() + + def get_power_3s(self): + self.elapsed = time.time() + if not self.pinf or self.elapsed - self.begin >= 3: + self.begin = time.time() + self.pinf = self.redfish.get_thermal() + + def get_temperature(self): + self.get_power_3s() + ctrl = self.pinf["Temperatures"] + output = ctrl[self.index] + temp = output.get("ReadingCelsius") + return temp + + def get_high_threshold(self): + self.get_power_3s() + ctrl = self.pinf["Temperatures"] + output = ctrl[self.index] + temp = output.get("UpperThresholdFatal") + return temp + + def get_low_threshold(self): + self.get_power_3s() + ctrl = self.pinf["Temperatures"] + output = ctrl[self.index] + temp = output.get("LowerThresholdFatal") + return temp + + def get_high_critical_threshold(self): + self.get_power_3s() + ctrl = self.pinf["Temperatures"] + output = ctrl[self.index] + temp = output.get("UpperThresholdFatal") + return temp + + def get_low_critical_threshold(self): + self.get_power_3s() + ctrl = self.pinf["Temperatures"] + output = ctrl[self.index] + temp = output.get("LowerThresholdFatal") + return temp + + def get_name(self): + self.get_power_3s() + ctrl = self.pinf["Temperatures"] + output = ctrl[self.index].get("Name") + name = output.split("/",3)[2] + if name == "SWITCH_TEMP": + name = "ASIC_TEMP" + return "{}".format(name) + + def get_real_name(self): + self.get_power_3s() + ctrl = self.pinf["Temperatures"] + output = ctrl[self.index].get("Name") + name = output.split("/",3)[2] + return "{}".format(name) + + def get_presence(self): + self.get_power_3s() + ctrl = self.pinf["Temperatures"] + output = ctrl[self.index] + if output.get("Status").get("Status").get("State") == "Enabled": + return True + return False + + def get_status(self): + self.get_power_3s() + ctrl = self.pinf["Temperatures"] + output = ctrl[self.index] + if output.get("Status").get("Status").get("Health") == "OK": + return True + return False + + def set_sys_led(self, color): + playload = {} + led = {} + led_list = [] + led["IndicatorLEDColor"] = color + led["LEDType"] = "sys" + led_list.append(led) + playload["LEDs"] = led_list + # boardsLed + return self.redfish.post_boardLed(playload) + + def get_minimum_recorded(self): + """ + Retrieves the minimum recorded temperature of thermal + + Returns: + A float number, the minimum recorded temperature of thermal in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + return "N/A" + + def get_maximum_recorded(self): + """ + Retrieves the maximum recorded temperature of thermal + + Returns: + A float number, the maximum recorded temperature of thermal in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + return "N/A" + + def get_model(self): + """ + Retrieves the model number (or part number) of the Thermal + + Returns: + string: Model/part number of Thermal + """ + return "N/A" + + def get_serial(self): + """ + Retrieves the serial number of the Thermal + + Returns: + string: Serial number of Thermal + """ + return "N/A" + + def get_revision(self): + """ + Retrieves the hardware revision of the device + + Returns: + string: Revision value of device + """ + return "N/A" + + 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 diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/config/arm64_micas_m2_w6010_48gt4x_ra_r0_config.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/config/arm64_micas_m2_w6010_48gt4x_ra_r0_config.py new file mode 100755 index 000000000000..a547e3d7d092 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/config/arm64_micas_m2_w6010_48gt4x_ra_r0_config.py @@ -0,0 +1,91 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +from platform_common import * +PCA9548START = -1 +PCA9548BUSEND = -2 + +PLATFORM_CARDID = 0x00004065 +PLATFORM_PRODUCTNAME = "M2-W6010-48GT4X-RA" +PLATFORM_PART_NUMBER = "01017247" +PLATFORM_LABEL_REVISION = "R01" +PLATFORM_ONIE_VERSION = "2018.05-rc1" +PLATFORM_MAC_SIZE = 3 +PLATFORM_MANUF_NAME = "Micas" +PLATFORM_MANUF_COUNTRY = "USA" +PLATFORM_VENDOR_NAME = "Micas" +PLATFORM_DIAG_VERSION = "0.1.0.15" +PLATFORM_SERVICE_TAG = "www.micasnetworks.com" + + +LOCAL_LED_CONTROL = { + "CLOSE":{}, + "OPEN":{} +} + +MACLED_PARAMS = [] + +# start system modules +STARTMODULE = { + "i2ccheck":0, + "fancontrol":0, + "avscontrol":0, + "avscontrol_restful":0, + "sfptempmodule":0, + "sfptempmodule_interval":3, + "macledreset": 0, + "macledreset_interval": 5, + "macledset_param":MACLED_PARAMS, + } + +FRULISTS = [ + {"name":"mmceeprom","bus":5,"loc":0x50, "E2PRODUCT":'2', "E2TYPE":'5' , "CANRESET":'1'}, + {"name":"cpueeprom","bus":5,"loc":0x57,"E2PRODUCT":'2', "E2TYPE":'4', "CANRESET":'1' }, + ] + +# eeprom = "1-0056/eeprom" +E2_LOC = {"bus":1, "devno":0x56} +E2_PROTECT = {} + + +CPLDVERSIONS = [ + {"bus":2, "devno":0x0d, "name":"CPU_CPLD"}, + {"bus":3, "devno":0x30, "name":"MAC_BOARD_CPLD_1"}, +] + +FIRMWARE_TOOLS = {"cpld": [{"channel":"0","cmd":"firmware_upgrade %s cpld %s cpld", "successtips":"CPLD Upgrade succeeded!"} + ], + } + +# drivers list +DRIVERLISTS = [ + {"name":"i2c_dev", "delay":0}, + {"name":"i2c_algo_bit","delay":0}, + {"name":"spi-bitbang", "delay":0}, + {"name":"i2c_mux", "delay":0}, + {"name":"rtcpcf85063", "delay":0}, + {"name":"i2c_mux_pca954x", "delay":0}, # force_deselect_on_exit=1 + {"name":"platform_common dfd_my_type=0x4065", "delay":0}, + {"name":"firmware_driver", "delay":0}, + {"name":"wb_cpld", "delay":0}, + {"name":"wb_at24", "delay":0}, + {"name":"optoe", "delay":0}, +] + +DEVICE = [ + {"name":"rtcpcf85063","bus":1,"loc":0x51 }, + {"name":"wb_24c02","bus":1,"loc":0x56 }, + {"name":"wb_cpld","bus":3,"loc":0x30 }, + {"name":"wb_24c02","bus":5,"loc":0x50 }, + {"name":"wb_24c02","bus":5,"loc":0x57 }, +] + +INIT_PARAM = [ + {"loc":"3-0030/tx_write_protect","value": "59","delay":1}, + {"loc":"3-0030/tx_disable","value": "00"}, + {"loc":"3-0030/tx_write_protect","value": "4e"}, +] + +INIT_COMMAND = [ + "hwclock -s", +] + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/config/fan_ctrl_cfg.json b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/config/fan_ctrl_cfg.json new file mode 100644 index 000000000000..86fcf892f032 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/config/fan_ctrl_cfg.json @@ -0,0 +1,40 @@ +{ + "Device": { + "Liquid": 0, + "Buildin": 1, + "PID": 0, + "OpenLoop": 1 + }, + "Thermal": { + "INLET_TEMP": "INLET_TEMP", + "OUTLET_TEMP": "OUTLET_TEMP", + "SWITCH_TEMP": "TPS53688_TEMP" + }, + "Fans": { + "Fan1" : "Fantray1_1", + "Fan2" : "Fantray2_1" + }, + "PID": { + "Pwm_Max": 99, + "Pwm_Min": 30, + "SetPoint" : 90, + "P": 1.5, + "I": 1, + "D": 0.3, + "Temp_Min": 28.0, + "Temp_Max": 45.0, + "Sensor": "TPS53688_TEMP" + }, + "OpenLoop": { + "a": -0.06, + "b": 10.3, + "c": -142.0, + "fix_up": -8, + "pwmMax": 99, + "pwmMin": 30, + "tempMin": 25 + }, + "FanError": { + "Fan_Pwmmax":99 + } +} \ No newline at end of file diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/modules/Makefile b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/modules/Makefile new file mode 100644 index 000000000000..c49defa72165 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/modules/Makefile @@ -0,0 +1 @@ +obj-m += wb_cpld.o diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/modules/wb_cpld.c b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/modules/wb_cpld.c new file mode 100644 index 000000000000..662cfa7df355 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/modules/wb_cpld.c @@ -0,0 +1,337 @@ +/* + * wb_cpld.c - A driver for control wb_cpld base on wb_cpld.c + * + * 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 + +/* debug switch level */ +typedef enum { + DBG_START, + DBG_VERBOSE, + DBG_KEY, + DBG_WARN, + DBG_ERROR, + DBG_END, +} dbg_level_t; + +static int debuglevel = 0; +module_param(debuglevel, int, S_IRUGO | S_IWUSR); + +#define DBG_DEBUG(fmt, arg...) do { \ + if ( debuglevel > DBG_START && debuglevel < DBG_ERROR) { \ + printk(KERN_INFO "[DEBUG]:<%s, %d>:"fmt, __FUNCTION__, __LINE__, ##arg); \ + } else if ( debuglevel >= DBG_ERROR ) { \ + printk(KERN_ERR "[DEBUG]:<%s, %d>:"fmt, __FUNCTION__, __LINE__, ##arg); \ + } else { } \ +} while (0) + +#define DBG_ERROR(fmt, arg...) do { \ + if ( debuglevel > DBG_START) { \ + printk(KERN_ERR "[ERROR]:<%s, %d>:"fmt, __FUNCTION__, __LINE__, ##arg); \ + } \ + } while (0) + +#define CPLD_SIZE 256 +#define CPLD_I2C_RETRY_TIMES 5 /* changed the number of retry time to 5 */ +#define CPLD_I2C_RETRY_WAIT_TIME 10 /* Delay 10ms before operation */ + +struct cpld_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 data[CPLD_SIZE]; /* Register value */ +}; + +static s32 cpld_i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command) +{ + int try; + s32 ret; + + ret = -1; + for (try = 0; try < CPLD_I2C_RETRY_TIMES; try++) { + if ((ret = i2c_smbus_read_byte_data(client, command) ) >= 0 ) + break; + msleep(CPLD_I2C_RETRY_WAIT_TIME); + } + return ret; +} + +static s32 cpld_i2c_smbus_read_i2c_block_data(const struct i2c_client *client, + u8 command, u8 length, u8 *values) +{ + int try; + s32 ret; + + ret = -1; + for (try = 0; try < CPLD_I2C_RETRY_TIMES; try++) { + if ((ret = i2c_smbus_read_i2c_block_data(client, command, length, values) ) >= 0 ) + break; + msleep(CPLD_I2C_RETRY_WAIT_TIME); + } + return ret; +} + +static ssize_t set_cpld_sysfs_value(struct device *dev, struct device_attribute *da, const char *buf, size_t +count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + unsigned long val; + int err; + + err = kstrtoul(buf, 16, &val); + if (err) + return err; + if ((val < 0) || (val > 0xff)) { + DBG_ERROR("please enter 0x00 ~ 0xff\n"); + return -1; + } + mutex_lock(&data->update_lock); + data->data[0] = (u8)val; + DBG_DEBUG("pos: 0x%02x count = %ld, data = 0x%02x\n", attr->index, count, data->data[0]); + i2c_smbus_write_byte_data(client, attr->index, data->data[0]); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_cpld_version(struct device *dev, struct device_attribute *da, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + s32 status; + + status = -1; + mutex_lock(&data->update_lock); + status = cpld_i2c_smbus_read_i2c_block_data(client, 0, 4, data->data); + if (status < 0) { + mutex_unlock(&data->update_lock); + return 0; + } + mutex_unlock(&data->update_lock); + return sprintf(buf, "%02x %02x %02x %02x \n", data->data[0], data->data[1], data->data[2], + data->data[3]); +} + +static ssize_t show_cpld_sysfs_value(struct device *dev, struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + s32 status; + + status = -1; + mutex_lock(&data->update_lock); + status = cpld_i2c_smbus_read_byte_data(client, attr->index); + if (status < 0) { + mutex_unlock(&data->update_lock); + return 0; + } + data->data[0] = status; + DBG_DEBUG("cpld reg pos:0x%x value:0x%02x\n", attr->index, data->data[0]); + mutex_unlock(&data->update_lock); + return sprintf(buf, "%02x\n", data->data[0]); +} + +/* sys */ +static SENSOR_DEVICE_ATTR(cpld_version, S_IRUGO, show_cpld_version, NULL, 0); + +/* sfp */ +static SENSOR_DEVICE_ATTR(sfp_presence1, S_IRUGO, show_cpld_sysfs_value, NULL, 0x30); +static SENSOR_DEVICE_ATTR(cable_led1, S_IRUGO | S_IWUSR, show_cpld_sysfs_value, set_cpld_sysfs_value, 0x20); +static SENSOR_DEVICE_ATTR(cable_led2, S_IRUGO | S_IWUSR, show_cpld_sysfs_value, set_cpld_sysfs_value, 0x21); +static SENSOR_DEVICE_ATTR(cable_led3, S_IRUGO | S_IWUSR, show_cpld_sysfs_value, set_cpld_sysfs_value, 0x22); +static SENSOR_DEVICE_ATTR(cable_led4, S_IRUGO | S_IWUSR, show_cpld_sysfs_value, set_cpld_sysfs_value, 0x23); +static SENSOR_DEVICE_ATTR(cable_led5, S_IRUGO | S_IWUSR, show_cpld_sysfs_value, set_cpld_sysfs_value, 0x24); +static SENSOR_DEVICE_ATTR(cable_led6, S_IRUGO | S_IWUSR, show_cpld_sysfs_value, set_cpld_sysfs_value, 0x25); +static SENSOR_DEVICE_ATTR(sfp_led1, S_IRUGO | S_IWUSR, show_cpld_sysfs_value, set_cpld_sysfs_value, 0x26); +static SENSOR_DEVICE_ATTR(sfp_drop_record1, S_IRUGO , show_cpld_sysfs_value, NULL, 0x38); +static SENSOR_DEVICE_ATTR(sfp_tx_fault1, S_IRUGO , show_cpld_sysfs_value, NULL, 0x50); +static SENSOR_DEVICE_ATTR(sfp_rx_loss1, S_IRUGO , show_cpld_sysfs_value, NULL, 0x70); +/* tx-disbale */ +static SENSOR_DEVICE_ATTR(tx_disable, S_IRUGO | S_IWUSR, show_cpld_sysfs_value, set_cpld_sysfs_value, 0x90); +static SENSOR_DEVICE_ATTR(tx_write_protect, S_IRUGO | S_IWUSR, show_cpld_sysfs_value, set_cpld_sysfs_value, 0x0e); + +static struct attribute *mac_cpld_0x30_sysfs_attrs[] = { + &sensor_dev_attr_cpld_version.dev_attr.attr, + &sensor_dev_attr_sfp_presence1.dev_attr.attr, + &sensor_dev_attr_cable_led1.dev_attr.attr, + &sensor_dev_attr_cable_led2.dev_attr.attr, + &sensor_dev_attr_cable_led3.dev_attr.attr, + &sensor_dev_attr_cable_led4.dev_attr.attr, + &sensor_dev_attr_cable_led5.dev_attr.attr, + &sensor_dev_attr_cable_led6.dev_attr.attr, + &sensor_dev_attr_sfp_led1.dev_attr.attr, + &sensor_dev_attr_tx_disable.dev_attr.attr, + &sensor_dev_attr_tx_write_protect.dev_attr.attr, + &sensor_dev_attr_sfp_drop_record1.dev_attr.attr, + &sensor_dev_attr_sfp_tx_fault1.dev_attr.attr, + &sensor_dev_attr_sfp_rx_loss1.dev_attr.attr, + NULL +}; + +static const struct attribute_group mac_cpld_0x30_sysfs_group = { + .attrs = mac_cpld_0x30_sysfs_attrs, +}; + +struct cpld_attr_match_group { + int bus_nr; /* I2C-BUS number */ + unsigned short addr; /* device adress */ + const struct attribute_group *attr_group_ptr;/* SYS attribute pointer */ + const struct attribute_group *attr_hwmon_ptr;/* HWMON Attribute pointer */ +}; + +static struct cpld_attr_match_group g_cpld_attr_match[] = { + {3, 0x30, &mac_cpld_0x30_sysfs_group, NULL}, + +}; + +static const struct attribute_group *cpld_get_attr_group(struct i2c_client *client, int is_hwmon) +{ + int i; + struct cpld_attr_match_group *group; + + for (i = 0; i < ARRAY_SIZE(g_cpld_attr_match); i++) { + group = &g_cpld_attr_match[i]; + DBG_DEBUG("is_hwmon %d i %d client(nr:%d,addr:0x%x), group(nr:%d,addr:0x0%x) .\n", is_hwmon, + i, client->adapter->nr, client->addr, group->bus_nr, group->addr); + if ((client->addr == group->addr) && (client->adapter->nr == group->bus_nr)) { + DBG_DEBUG("is_hwmon %d i %d nr %d addr %d .\n", is_hwmon, i, client->adapter->nr, client->addr); + return (is_hwmon) ? (group->attr_hwmon_ptr) : (group->attr_group_ptr); + } + } + + DBG_DEBUG("is_hwmon %d nr %d addr %d dismatch, return NULL.\n", is_hwmon, client->adapter->nr, client->addr); + return NULL; +} + +static int cpld_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct cpld_data *data; + int status; + const struct attribute_group *sysfs_group, *hwmon_group; + + status = -1; + DBG_DEBUG("=========cpld_probe(addr:0x%x, nr:%d)===========\n", client->addr, client->adapter->nr); + data = devm_kzalloc(&client->dev, sizeof(struct cpld_data), GFP_KERNEL); + if (!data) { + return -ENOMEM; + } + + data->client = client; + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + sysfs_group = NULL; + sysfs_group = cpld_get_attr_group(client, 0); + if (sysfs_group) { + status = sysfs_create_group(&client->dev.kobj, sysfs_group); + DBG_DEBUG("=========(addr:0x%x, nr:%d) sysfs_create_group status %d===========\n", client->addr, client->adapter->nr, status); + if (status != 0) { + DBG_ERROR("sysfs_create_group status %d.\n", status); + goto error; + } + } else { + DBG_DEBUG("=========(addr:0x%x, nr:%d) no sysfs_create_group \n", client->addr, client->adapter->nr); + } + + hwmon_group = NULL; + hwmon_group = cpld_get_attr_group(client, 1); + if (hwmon_group) { + data->hwmon_dev = hwmon_device_register_with_groups(&client->dev, client->name, data, (const struct attribute_group **)hwmon_group); + if (IS_ERR(data->hwmon_dev)) { + sysfs_remove_group(&client->dev.kobj, (const struct attribute_group *)sysfs_group); + DBG_ERROR("hwmon_device_register_with_groups failed ret %ld.\n", PTR_ERR(data->hwmon_dev)); + return PTR_ERR(data->hwmon_dev); + } + DBG_DEBUG("=========(addr:0x%x, nr:%d) hwmon_device_register_with_groups success===========\n", client->addr, client->adapter->nr); + if (status != 0) { + DBG_ERROR("sysfs_create_group status %d.\n", status); + goto error; + } + } else { + DBG_DEBUG("=========(addr:0x%x, nr:%d) no hwmon_device_register_with_groups \n", client->addr, client->adapter->nr); + } + +error: + return status; + +} + +static int cpld_remove(struct i2c_client *client) +{ + struct cpld_data *data = i2c_get_clientdata(client); + const struct attribute_group *sysfs_group, *hwmon_group; + + DBG_DEBUG("=========cpld_remove(addr:0x%x, nr:%d)===========\n", client->addr, client->adapter->nr); + + /* To be added the corresponding uninstall operation */ + sysfs_group = NULL; + sysfs_group = cpld_get_attr_group(client, 0); + if (sysfs_group) { + DBG_DEBUG("=========(addr:0x%x, nr:%d) do sysfs_remove_group \n", client->addr, client->adapter->nr); + sysfs_remove_group(&client->dev.kobj, (const struct attribute_group *)sysfs_group); + } else { + DBG_DEBUG("=========(addr:0x%x, nr:%d) no sysfs_remove_group \n", client->addr, client->adapter->nr); + } + + hwmon_group = NULL; + hwmon_group = cpld_get_attr_group(client, 1); + if (hwmon_group) { + DBG_DEBUG("=========(addr:0x%x, nr:%d) do hwmon_device_unregister \n", client->addr, client->adapter->nr); + hwmon_device_unregister(data->hwmon_dev); + } else { + DBG_DEBUG("=========(addr:0x%x, nr:%d) no hwmon_device_unregister \n", client->addr, client->adapter->nr); + } + + return 0; +} + +static const struct i2c_device_id cpld_id[] = { + { "wb_cpld", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, cpld_id); + +static struct i2c_driver wb_cpld_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "wb_cpld", + }, + .probe = cpld_probe, + .remove = cpld_remove, + .id_table = cpld_id, +}; + +module_i2c_driver(wb_cpld_driver); +MODULE_AUTHOR("wk "); +MODULE_DESCRIPTION("CPLD driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/service/m2-w6010-48gt4x_platform.service b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/service/m2-w6010-48gt4x_platform.service new file mode 100644 index 000000000000..988739214bf7 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/service/m2-w6010-48gt4x_platform.service @@ -0,0 +1,13 @@ +[Unit] +Description=Centec modules init +After=local-fs.target +Before=syncd.service + +[Service] +Type=oneshot +ExecStart=-/etc/init.d/platform-modules-micas-m2-w6010-48gt4x-ra start +ExecStop=-/etc/init.d/platform-modules-micas-m2-w6010-48gt4x-ra stop +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/setup.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/setup.py new file mode 100644 index 000000000000..b8aab82034ed --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/setup.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +import os +import sys +from setuptools import setup + +setup( + name='sonic_platform', + version='1.0', + description='Module to initialize centec platforms', + + packages=['sonic_platform'], + package_dir={'sonic_platform': 'sonic_platform'}, +) + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/__init__.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/__init__.py new file mode 100644 index 000000000000..4bfefa0fb636 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/__init__.py @@ -0,0 +1,3 @@ +__all__ = ["platform", "chassis"] +from sonic_platform import * + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/chassis.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/chassis.py new file mode 100644 index 000000000000..8cdf84cf3840 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/chassis.py @@ -0,0 +1,444 @@ + +#!/usr/bin/env python + +try: + import time + from sonic_platform_base.chassis_base import ChassisBase + from sonic_platform.eeprom import Eeprom + from sonic_platform.thermal import Thermal + from sonic_platform.fan_drawer import FanDrawer + from sonic_platform.sfp import Sfp + from sonic_platform.psu import Psu + from .component import Component +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +NUM_FAN_TRAY = 2 +NUM_FAN = 2 +NUM_THERMAL = 5 +NUM_PORT = 52 +NUM_PSU = 2 + +class Chassis(ChassisBase): + + port_dict = {} + STATUS_INSERTED = "1" + STATUS_REMOVED = "0" + + def __init__(self): + ChassisBase.__init__(self) + # Initialize EEPROM + self._eeprom = Eeprom() + #firmware + for i in range(0,2): + self._component_list.append(Component(i)) + # Initialize FAN + for i in range(NUM_FAN_TRAY): + fandrawer = FanDrawer(i) + self._fan_drawer_list.append(fandrawer) + self._fan_list.extend(fandrawer._fan_list) + # Initialize THERMAL + for index in range(0, NUM_THERMAL): + thermal = Thermal(index) + self._thermal_list.append(thermal) + # Initialize SFP + for index in range(0, NUM_PORT + 1): + sfp = Sfp(index) + self._sfp_list.append(sfp) + if sfp.get_presence(): + self.port_dict[index] = self.STATUS_INSERTED + else: + self.port_dict[index] = self.STATUS_REMOVED + # Initialize PSU + for index in range(0, NUM_PSU): + psu = Psu(index) + self._psu_list.append(psu) + +############################################## +# Device methods +############################################## + + def get_name(self): + """ + Retrieves the name of the chassis + Returns: + string: The name of the chassis + """ + name = '' + sys_eeprom = self.get_eeprom() + if sys_eeprom is None: + return '' + + e = sys_eeprom.read_eeprom() + name = sys_eeprom.modelstr(e) + if name is None: + return '' + return name + + 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 + """ + model = '' + sys_eeprom = self.get_eeprom() + if sys_eeprom is None: + return '' + + e = sys_eeprom.read_eeprom() + model = sys_eeprom.modelnumber(e) + if model is None: + return '' + return model + + def get_serial_number(self): + """ + Retrieves the hardware serial number for the chassis + + Returns: + A string containing the hardware serial number for this chassis. + """ + serial_number = '' + sys_eeprom = self.get_eeprom() + if sys_eeprom is None: + return '' + + e = sys_eeprom.read_eeprom() + serial_number = sys_eeprom.serial_number_str(e) + if serial_number is None: + return '' + + return serial_number + + def get_revision(self): + """ + Retrieves the hardware revision of the device + + Returns: + string: Revision value of device + """ + device_version = '' + sys_eeprom = self.get_eeprom() + if sys_eeprom is None: + return '' + + e = sys_eeprom.read_eeprom() + device_version = sys_eeprom.deviceversion(e) + if device_version is None: + return '' + + return device_version + + def get_serial(self): + """ + Retrieves the serial number of the chassis (Service tag) + Returns: + string: Serial number of chassis + """ + return self.get_serial_number() + + 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_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 initizalize_system_led(self): + 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 'green' + + def set_status_led(self, color): + return False +############################################## +# Chassis methods +############################################## + + 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' + """ + base_mac = '' + sys_eeprom = self.get_eeprom() + if sys_eeprom is None: + return '' + + e = sys_eeprom.read_eeprom() + base_mac = sys_eeprom.base_mac_addr(e) + if base_mac is None: + return '' + + return base_mac.upper() + + 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. + Ex. { '0x21':'AG9064', '0x22':'V1.0', '0x23':'AG9064-0109867821', + '0x24':'001c0f000fcd0a', '0x25':'02/03/2018 16:22:00', + '0x26':'01', '0x27':'REV01', '0x28':'AG9064-C2358-16G'} + """ + sys_eeprom_dict = dict() + sys_eeprom = self.get_eeprom() + if sys_eeprom is None: + return {} + + e = sys_eeprom.read_eeprom() + if sys_eeprom._TLV_HDR_ENABLED: + if not sys_eeprom.is_valid_tlvinfo_header(e): + return {} + total_len = (e[9] << 8) | e[10] + tlv_index = sys_eeprom._TLV_INFO_HDR_LEN + tlv_end = sys_eeprom._TLV_INFO_HDR_LEN + total_len + else: + tlv_index = sys_eeprom.eeprom_start + tlv_end = sys_eeprom._TLV_INFO_MAX_LEN + + while (tlv_index + 2) < len(e) and tlv_index < tlv_end: + if not sys_eeprom.is_valid_tlv(e[tlv_index:]): + break + + tlv = e[tlv_index:tlv_index + 2 + e[tlv_index + 1]] + name, value = sys_eeprom.decoder(None, tlv) + sys_eeprom_dict[name] = value + + if e[tlv_index] == sys_eeprom._TLV_CODE_QUANTA_CRC or \ + e[tlv_index] == sys_eeprom._TLV_CODE_CRC_32: + break + tlv_index += e[tlv_index + 1] + 2 + + return sys_eeprom_dict + + def get_thermal_manager(self): + """ + Retrieves thermal manager class on this chassis + :return: A class derived from ThermalManagerBase representing the + specified thermal manager. ThermalManagerBase is returned as default + """ + return False + + 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 (None, None) + + def get_module(self, index): + """ + Retrieves module represented by (0-based) index + + Args: + index: An integer, the index (0-based) of the module to + retrieve + + Returns: + An object dervied from ModuleBase representing the specified + module + """ + module = None + + try: + if self.get_num_modules(): + module = self._module_list[index] + except IndexError: + sys.stderr.write("Module index {} out of range (0-{})\n".format( + index, len(self._module_list)-1)) + + return module + + def get_fan_drawer(self, index): + """ + Retrieves fan drawers represented by (0-based) index + + Args: + index: An integer, the index (0-based) of the fan drawer to + retrieve + + Returns: + An object dervied from FanDrawerBase representing the specified fan + drawer + """ + fan_drawer = None + + try: + if self.get_num_fan_drawers(): + fan_drawer = self._fan_drawer_list[index] + except IndexError: + sys.stderr.write("Fan drawer index {} out of range (0-{})\n".format( + index, len(self._fan_drawer_list)-1)) + + return fan_drawer + + 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): + - bool: True if call successful, False if not; + - dict: 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. + The known devices's device_id and device_event was defined as table below. + ----------------------------------------------------------------- + device | device_id | device_event | annotate + ----------------------------------------------------------------- + 'fan' '' '0' Fan removed + '1' Fan inserted + + 'sfp' '' '0' Sfp removed + '1' Sfp inserted + '2' I2C bus stuck + '3' Bad eeprom + '4' Unsupported cable + '5' High Temperature + '6' Bad cable + + 'voltage' '' '0' Vout normal + '1' Vout abnormal + -------------------------------------------------------------------- + Ex. {'fan':{'0':'0', '2':'1'}, 'sfp':{'11':'0', '12':'1'}, + 'voltage':{'U20':'0', 'U21':'1'}} + Indicates that: + fan 0 has been removed, fan 2 has been inserted. + sfp 11 has been removed, sfp 12 has been inserted. + monitored voltage U20 became normal, voltage U21 became abnormal. + Note: For sfp, when event 3-6 happened, the module will not be avalaible, + XCVRD shall stop to read eeprom before SFP recovered from error status. + """ + + change_event_dict = {"fan": {}, "sfp": {}, "voltage": {}} + + start_time = time.time() + forever = False + + if timeout == 0: + forever = True + elif timeout > 0: + timeout = timeout / float(1000) # Convert to secs + else: + print("get_change_event:Invalid timeout value", timeout) + return False, change_event_dict + + end_time = start_time + timeout + if start_time > end_time: + print( + "get_change_event:" "time wrap / invalid timeout value", + timeout, + ) + return False, change_event_dict # Time wrap or possibly incorrect timeout + try: + while timeout >= 0: + # check for sfp + sfp_change_dict = self.get_transceiver_change_event() + # check for fan + # fan_change_dict = self.get_fan_change_event() + # check for voltage + # voltage_change_dict = self.get_voltage_change_event() + + if sfp_change_dict: + change_event_dict["sfp"] = sfp_change_dict + # change_event_dict["fan"] = fan_change_dict + # change_event_dict["voltage"] = voltage_change_dict + return True, change_event_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, change_event_dict + except Exception as e: + print(e) + print("get_change_event: Should not reach here.") + return False, change_event_dict + + def get_transceiver_change_event(self): + current_port_dict = {} + ret_dict = {} + + # Check for OIR events and return ret_dict + for index in range(0, NUM_PORT + 1): + if self._sfp_list[index].get_presence(): + current_port_dict[index] = self.STATUS_INSERTED + else: + current_port_dict[index] = self.STATUS_REMOVED + + if len(self.port_dict) == 0: # first time + self.port_dict = current_port_dict + return {} + + if current_port_dict == self.port_dict: + return {} + + # Update reg value + for index, status in current_port_dict.items(): + if self.port_dict[index] != status: + ret_dict[index] = status + #ret_dict[str(index)] = status + self.port_dict = current_port_dict + for index, status in ret_dict.items(): + if int(status) == 1: + pass + #self._sfp_list[int(index)].check_sfp_optoe_type() + return ret_dict diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/component.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/component.py new file mode 100644 index 000000000000..325c000eea74 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/component.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python + +try: + import subprocess + from sonic_platform_base.component_base import ComponentBase + import sonic_platform.hwaccess as hwaccess +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +def get_cpld_version(bus, i2caddr): + return '{}{}{}{}'.format(hwaccess.i2c_get(bus, i2caddr, 1), + hwaccess.i2c_get(bus, i2caddr, 2), + hwaccess.i2c_get(bus, i2caddr, 3), + hwaccess.i2c_get(bus, i2caddr, 0) + ) + +def get_cpu_cpld_version(): + return get_cpld_version(2, 0x0d) + +def get_cpld1_version(): + return get_cpld_version(3, 0x30) + +COMPONENT_LIST= [ + ['CPU CPLD', + 'cpu board', + get_cpu_cpld_version + ], + + ['MAC1 CPLD', + 'mac1 board', + get_cpld1_version + ] + ] + +class Component(ComponentBase): + """Platform-specific Component class""" + + def __init__(self, component_index=0): + ComponentBase.__init__(self) + self.index = component_index + + def get_name(self): + """ + Retrieves the name of the component + + Returns: + A string containing the name of the component + """ + return COMPONENT_LIST[self.index][0] + + def get_description(self): + """ + Retrieves the description of the component + + Returns: + A string containing the description of the component + """ + return COMPONENT_LIST[self.index][1] + + def get_firmware_version(self): + """ + Retrieves the firmware version of the component + + Returns: + A string containing the firmware version of the component + """ + return COMPONENT_LIST[self.index][2]() + + 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 + """ + try: + successtips = "CPLD Upgrade succeeded!" + status, output = subprocess.getstatusoutput("which firmware_upgrade") + if status or len(output) <= 0: + logger.error("no upgrade tool.") + return False + cmdstr = "%s %s cpld %d cpld"%(output,image_path,self.slot) + ret, log = subprocess.getstatusoutput(cmdstr) + if ret == 0 and successtips in log: + return True + logger.error("upgrade failed. ret:%d, log:\n%s" % (ret, log)) + except Exception as e: + logger.error(str(e)) + return False + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/eeprom.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/eeprom.py new file mode 100644 index 000000000000..af5da656b7b0 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/eeprom.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +try: + from sonic_eeprom import eeprom_tlvinfo + import binascii +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +class Eeprom(eeprom_tlvinfo.TlvInfoDecoder): + """Platform-specific Eeprom class""" + + def __init__(self): + eeprom_path = "/sys/bus/i2c/devices/1-0056/eeprom" + if eeprom_path is None: + raise ValueError("get eeprom path failed") + + super(Eeprom, self).__init__(eeprom_path, 0, "", True) + + + def modelnumber(self, e): + ''' + Returns the value field of the model(part) number TLV as a string + ''' + (is_valid, t) = self.get_tlv_field(e, self._TLV_CODE_PART_NUMBER) + if not is_valid: + return super(Eeprom, self).part_number_str(e) + + return t[2].decode("ascii") + + def deviceversion(self, e): + ''' + Returns the value field of the Device Version as a string + ''' + (is_valid, t) = self.get_tlv_field(e, self._TLV_CODE_DEVICE_VERSION) + if not is_valid: + return "N/A" + + return str(ord(t[2])) diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/fan.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/fan.py new file mode 100644 index 000000000000..69cb80e53fa1 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/fan.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python + +import time + +try: + from sonic_platform_base.fan_base import FanBase + from .redfish_api import Redfish_Api +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +FAN_NAME_LIST = ["1", "2"] + +class Fan(FanBase): + """Platform-specific Fan class""" + + def __init__(self, fan_tray_index, fan_index=0): + self.fan_index = fan_index + self.fan_tray_index = fan_tray_index + self.redfish = Redfish_Api() + self.pinf = {} + self._fan_list = [] + FanBase.__init__(self) + self.begin = time.time() + + def get_power_3s(self): + self.elapsed = time.time() + if not self.pinf or self.elapsed - self.begin >= 3: + self.begin = time.time() + self.pinf = self.redfish.get_thermal() + + def get_speed_pwm(self): + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fan_tray_index] + speed = output.get("Oem").get("OpenBmc").get("FanSpeedLevelPercents") + return int(speed) + + def get_speed_rpm(self): + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fan_tray_index] + speed = output.get("Reading") + return int(speed) + + def get_high_critical_threshold(self): + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fan_tray_index] + high = output.get("UpperThresholdFatal") + return int(high) + + def get_low_critical_threshold(self): + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fan_tray_index] + low = output.get("LowerThresholdFatal") + return int(low) + + def set_speed_pwm(self, speed): + post_url = '/redfish/v1/Chassis/1/Thermal/Actions/Oem/OpenBmc/Fan.SetSpeed' + playload = {} + playload["FanName"] = "Fan0" + playload["FanSpeedLevelPercents"] = str(speed) + return self.redfish.post_odata(post_url, playload) + + def get_status_led(self): + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fan_tray_index] + led = output.get("Oem").get("OpenBmc").get("IndicatorLEDColor") + return led + + def set_status_led(self, color): + playload = {} + led = {} + led_list = [] + led["IndicatorLEDColor"] = color + led["LEDType"] = "fan" + led_list.append(led) + playload["LEDs"] = led_list + # boardsLed + return self.redfish.post_boardLed(playload) + + def get_direction(self): + return "exhaust" + + def get_name(self): + fan_name = FAN_NAME_LIST[self.fan_index] + return "Fantray{}_{}".format(self.fan_tray_index+1, fan_name) + + def get_model(self): + """ + Retrieves the part number of the FAN + Returns: + string: Part number of FAN + """ + return 'N/A' + + 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 get_revision(self): + """ + Retrieves the hardware revision of the device + + Returns: + string: Revision value of device + """ + return 'N/A' + + def get_serial(self): + """ + Retrieves the serial number of the FAN + Returns: + string: Serial number of FAN + """ + return 'N/A' + + def get_presence(self): + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fan_tray_index] + state = output.get("Status").get("Status").get("State") + if state == "Enabled" or state == "UnavailableOffline": + return True + return False + + def get_status(self): + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fan_tray_index] + if output.get("Status").get("Status").get("Health") == "OK": + return True + return False + + def get_speed(self): + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fan_tray_index] + speed = output.get("Reading") + speed_percentage = round((speed*100)/17500) + if speed_percentage > 100: + speed_percentage = 100 + return speed_percentage + else: + return speed_percentage + + 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 30 + + 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) + """ + return self.get_speed_pwm() diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/fan_drawer.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/fan_drawer.py new file mode 100644 index 000000000000..fcf7d519ca8d --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/fan_drawer.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python + +import time + +try: + from sonic_platform_base.fan_drawer_base import FanDrawerBase + from sonic_platform.fan import Fan + from .redfish_api import Redfish_Api +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +class FanDrawer(FanDrawerBase): + + FANS_PER_FANTRAY = 1 + + def __init__(self, fantray_index=0): + FanDrawerBase.__init__(self) + self.fantrayindex = fantray_index + self.redfish = Redfish_Api() + self.pinf = {} + self.begin = time.time() + for i in range(self.FANS_PER_FANTRAY): + self._fan_list.append(Fan(fantray_index, i)) + + def get_power_3s(self): + self.elapsed = time.time() + if not self.pinf or self.elapsed - self.begin >= 3: + self.begin = time.time() + self.pinf = self.redfish.get_thermal() + + def get_name(self): + return "FanTray{}".format(self.fantrayindex+1) + + def get_presence(self): + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fantrayindex] + state = output.get("Status").get("Status").get("State") + if state == "Enabled" or state == "UnavailableOffline": + return True + return False + + def get_model(self): + """ + Retrieves the part number of the FAN + Returns: + string: Part number of FAN + """ + return 'N/A' + + def get_serial(self): + """ + Retrieves the serial number of the FAN + Returns: + string: Serial number of FAN + """ + return 'N/A' + + def get_revision(self): + """ + Retrieves the hardware revision of the device + + Returns: + string: Revision value of device + """ + 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 + """ + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fantrayindex] + if output.get("Status").get("Status").get("Health") == "OK": + return True + return False + + 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 get_status_led(self): + self.get_power_3s() + ctrl = self.pinf["Fans"] + output = ctrl[self.fantrayindex] + led = output.get("Oem").get("OpenBmc").get("IndicatorLEDColor") + return led + + def set_status_led(self, color): + playload = {} + led = {} + led_list = [] + led["IndicatorLEDColor"] = color + led["LEDType"] = "fan" + led_list.append(led) + playload["LEDs"] = led_list + # boardsLed + return self.redfish.post_boardLed(playload) + + def get_maximum_consumed_power(self): + """ + Retrives the maximum power drawn by Fan Drawer + + Returns: + A float, with value of the maximum consumable power of the + component. + """ + return 'N/A' diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/hwaccess.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/hwaccess.py new file mode 100644 index 000000000000..7a04e766ae28 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/hwaccess.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +import struct +import mmap +import subprocess + +# Read PCI device + +def pci_mem_read(mm, offset): + mm.seek(offset) + read_data_stream = mm.read(4) + return struct.unpack('I',read_data_stream)[0] + +def pci_get_value(resource, offset): + with open(resource, 'r+b') as fd: + mm = mmap.mmap(fd.fileno(), 0) + val = pci_mem_read(mm, offset) + mm.close() + return val + +def pci_mem_write(memmap, offset, data): + """ Write PCI device """ + memmap.seek(offset) + memmap.write(struct.pack('I', data)) + +def pci_set_value(resource, val, offset): + """ Set a value to PCI device """ + with open(resource, 'w+b') as filed: + memmap = None + try: + memmap = mmap.mmap(filed.fileno(), 0) + pci_mem_write(memmap, offset, val) + except EnvironmentError: + print("error") + if memmap is not None: + memmap.close() + +# Read I2C device + +def i2c_get(bus, i2caddr, ofs): + try: + valx = int(subprocess.check_output(['/usr/sbin/i2cget','-f', '-y', str(bus), str(i2caddr), str(ofs)]), 16) + return "{:02x}".format(valx) + except (FileNotFoundError, subprocess.CalledProcessError): + return -1 + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/platform.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/platform.py new file mode 100644 index 000000000000..7225a71b0307 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/platform.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +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") + + +class Platform(PlatformBase): + + def __init__(self): + PlatformBase.__init__(self) + self._chassis = Chassis() + diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/psu.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/psu.py new file mode 100644 index 000000000000..5176552c12e1 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/psu.py @@ -0,0 +1,231 @@ +#!/usr/bin/env python + +import time +import imp +import os +import sys + +try: + from sonic_platform_base.psu_base import PsuBase + from .redfish_api import Redfish_Api +except ImportError as e: + raise ImportError("%s - required module not found" % e) + +class Psu(PsuBase): + """Platform-specific Psu class""" + + def __init__(self, index=0): + PsuBase.__init__(self) + self.redfish = Redfish_Api() + self.pinf = {} + self.psu_index = index + self._fan_list = [] + self._thermal_list = [] + self.begin = time.time() + + def get_power_3s(self): + self.elapsed = time.time() + if not self.pinf or self.elapsed - self.begin >= 3: + self.begin = time.time() + self.pinf = self.redfish.get_power() + + def get_presence(self): + return True + + def get_fan(self, index): + """ + Retrieves fan module represented by (0-based) index + + Args: + index: An integer, the index (0-based) of the fan module to + retrieve + + Returns: + An object dervied from FanBase representing the specified fan + module + """ + return None + + def get_powergood_status(self): + self.get_power_3s() + ctrl = self.pinf["PowerSupplies"] + output = ctrl[self.psu_index] + if output.get("Status").get("Health") == "OK": + return True + else: + return False + + def get_name(self): + """ + Retrieves the name of the device + + Returns: + string: The name of the device + """ + return "PSU {}".format(self.psu_index + 1) + + def get_serial(self): + self.get_power_3s() + ctrl = self.pinf["PowerSupplies"] + output = ctrl[self.psu_index] + serial = output.get("SerialNumber") + return serial + + def get_model(self): + self.get_power_3s() + ctrl = self.pinf["PowerSupplies"] + output = ctrl[self.psu_index] + model = output.get("Model") + return model + + def get_revision(self): + self.get_power_3s() + ctrl = self.pinf["PowerSupplies"] + output = ctrl[self.psu_index] + manufacturer = output.get("Manufacturer") + return manufacturer + + def get_voltage(self): + self.get_power_3s() + ctrl = self.pinf["PowerSupplies"] + output = ctrl[self.psu_index] + voltage = output.get("Oem").get("OpenBmc").get("OutputVoltage") + return voltage + + def get_input_current(self): + self.get_power_3s() + ctrl = self.pinf["PowerSupplies"] + output = ctrl[self.psu_index] + current = output.get("Oem").get("OpenBmc").get("OutputAmperage") + return current + + def get_input_voltage(self): + self.get_power_3s() + ctrl = self.pinf["PowerSupplies"] + output = ctrl[self.psu_index] + voltage = output.get("Oem").get("OpenBmc").get("OutputVoltage") + return voltage + + def get_current(self): + self.get_power_3s() + ctrl = self.pinf["PowerSupplies"] + output = ctrl[self.psu_index] + current = output.get("Oem").get("OpenBmc").get("OutputAmperage") + return current + + def get_power(self): + self.get_power_3s() + ctrl = self.pinf["PowerSupplies"] + output = ctrl[self.psu_index] + current = output.get("Oem").get("OpenBmc").get("OutputAmperage") + voltage = output.get("Oem").get("OpenBmc").get("OutputVoltage") + power = float(current)*float(voltage) + return round(power,2) + + def get_temperature(self): + """ + Retrieves current temperature reading from PSU + + Returns: + A float number of current temperature in Celsius up to nearest thousandth + of one degree Celsius, e.g. 30.125 + """ + # no temperature sensor + value = 35 + return round(float(value), 1) + + def get_status_led(self): + return "BuildIn" + + def set_status_led(self, color): + playload = {} + led = {} + led_list = [] + led["IndicatorLEDColor"] = color + led["LEDType"] = "pwr" + led_list.append(led) + playload["LEDs"] = led_list + # boardsLed + return self.redfish.post_boardLed(playload) + + def get_maximum_supplied_power(self): + """ + Retrieves the maximum supplied power by PSU + + Returns: + A float number, the maximum power output in Watts. + e.g. 1200.1 + """ + return False + + 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 get_status(self): + """ + Retrieves the operational status of the PSU + + Returns: + bool: True if PSU is operating properly, False if not + """ + return self.get_powergood_status() + + def get_temperature_high_threshold(self): + """ + Retrieves the high threshold temperature of PSU + + Returns: + A float number, the high threshold temperature of PSU in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + value = 75 + return round(float(value), 1) + + 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 + """ + value = 14.52 + return str(round(float(value), 2)) + + 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 + """ + value = 9.72 + return str(round(float(value), 2)) + + def get_thermal(self, index): + """ + Retrieves thermal unit represented by (0-based) index + + Args: + index: An integer, the index (0-based) of the thermal to + retrieve + + Returns: + An object dervied from ThermalBase representing the specified thermal + """ + return None diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/redfish_api.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/redfish_api.py new file mode 100644 index 000000000000..a577c1b216da --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/redfish_api.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import shlex +import json +import os +import ssl +import subprocess +import syslog + +class Redfish_Api(): + BmcBaseUrl = 'http://240.1.1.1:8080' + ThermalUrl = '/redfish/v1/Chassis/1/Thermal' + PowerUrl = '/redfish/v1/Chassis/1/Power' + ThresholdSensorsUrl = '/redfish/v1/Chassis/1/ThresholdSensors' + FanSpeedUrl = '/redfish/v1/Chassis/1/Thermal/Actions/Oem/OpenBmc/Fan.SetSpeed' + BoardsUrl = '/redfish/v1/Chassis/1/Boards/' + BoardLedUrl = "/redfish/v1/Chassis/1/Boards/{}/Actions/Oem/OpenBmc/Boards.SetLED" + + # Maximum time in seconds that you allow the connection to the server to take. + connect_timeout = 30 + # Maximum time in seconds that you allow the whole operation to take + operation_timeout = 300 + + default_prefix='/redfish/v1/' + session = None + __DEBUG__ = "N" + __DUMP_RESP__ = "N" + RST_STATUS = "status" + RST_SUCCESS = "OK" + refish_logger = None + + def redfish_log_debug(self, msg): + if (self.__DEBUG__ == "Y"): + syslog.openlog("redfis_api") + syslog.syslog(syslog.LOG_DEBUG, msg) + syslog.closelog() + + def redfish_log_error(self, msg): + syslog.openlog("redfish_api") + syslog.syslog(syslog.LOG_ERR, msg) + syslog.closelog() + + def __init__(self): + pass + + def get_full_url(self, url): + return self.BmcBaseUrl + url + + def _exec_cmd(self, cmd): + self.redfish_log_debug("Cmd: %s" % cmd) + p = subprocess.Popen(shlex.split(cmd), shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p.wait() + self.redfish_log_debug("Cmd return: %d" % p.returncode) + str_stdout = p.stdout.read().decode('utf-8') + str_stderr = p.stderr.read().decode('utf-8') + self.redfish_log_debug("Cmd stdout: %s" % str_stdout) + if p.returncode !=0: + self.redfish_log_error("Cmd: %s, failed! error msg:%s" % (cmd, str_stderr)) + return None + else: + try: + ret = json.loads(str_stdout) + return ret + except Exception as e: + self.redfish_log_error("Cmd: %s, failed! stdout msg:%s" % (cmd, str_stdout)) + return None + + def _redfish_get(self, url): + self.redfish_log_debug("Get info from %s." % url) + result = None + try: + cmd_get="curl --connect-timeout %d -m %d -X GET %s" % (self.connect_timeout, self.operation_timeout, self.get_full_url(url)) + result = self._exec_cmd(cmd_get) + return result + except Exception as e: + self.redfish_log_error("error_message: %s" % e) + return None + + def _redfish_post(self, url, playload): + self.redfish_log_debug("post url: %s" % url) + self.redfish_log_debug("Playload: %s" % playload) + + playload_json = json.dumps(playload) + result = False + try: + cmd_post="curl --connect-timeout %d -m %d -X POST %s -d \'%s\'" % (self.connect_timeout, self.operation_timeout, self.get_full_url(url), playload_json) + ret_msg = self._exec_cmd(cmd_post) + if ret_msg == None: + return False + elif ret_msg["success"] == False: + redfish_log_error("Url: '%s', Playload: '%s', Bmc return failed, error_message: %s" % (url, playload_json, ret_msg["Message"])) + result = False + else: + result = True + return result + except Exception as e: + redfish_log_error("error_message: %s" % e) + return False + + def get_thermal(self): + """Get thermal info + :returns: class 'redfish.rest.v1.RestResponse' or None when failed + """ + return self._redfish_get(self.ThermalUrl) + + def get_power(self): + """Get power info + :returns: class 'redfish.rest.v1.RestResponse' or None when failed + """ + return self._redfish_get(self.PowerUrl) + + def get_thresholdSensors(self): + """Get thresholdSensors info + :returns: class 'redfish.rest.v1.RestResponse' or None when failed + """ + return self._redfish_get(self.ThresholdSensorsUrl) + + def post_odata(self, odata_id, playload): + """post odata info + :params odata_id: the specified odata_id path + :type odata_id: string + :playload: info to post + :type: dictionary + :returns: True or False + """ + if odata_id is None or playload is None: + print("post failed: odata_id or playload is None") + return False + return self._redfish_post(odata_id, playload) + + def get_odata(self, odata_id): + """Get odata info + :params odata_id: the specified odata_id path + :type odata_id: string + :returns: class 'redfish.rest.v1.RestResponse' or None when failed + """ + if odata_id is None: + print("Get odata_id failed: odata_id is None") + return None + return self._redfish_get(odata_id) + + def post_fanSpeed(self, playload): + """post odata info + :playload: info to post + :type: dictionary + :returns: True or False + """ + if playload is None: + print("post failed: playload is None") + return False + return self._redfish_post(self.FanSpeedUrl, playload) + + def get_board(self, board_name="indicatorboard"): + """Get board info + :board_name: name of board, default is "indicatorboard" + :type: string + :returns: class'redfish.rest.v1.RestResponse' or None when failed + """ + if board_name is None : + print("get failed: board_name is None") + return None + return self._redfish_get(self.BoardsUrl + board_name) + + def post_boardLed(self, playload, board_name="indicatorboard"): + """post boardLed info + :board_name: name of board, default is "indicatorboard" + :type: string + :playload: info to post + :type: dictionary + :returns: True or False + """ + if board_name is None or playload is None: + print("post failed: playload is None") + return False + return self._redfish_post(self.BoardLedUrl.format(board_name), playload) + + ''' not supported currently + def post_thermal(self, playload): + """post thermal info + :playload: info to post + :type: dictionary + :returns: True or False + """ + if playload is None: + print("post_thermal failed: playload is None") + return None + return self._redfish_post(self.ThermalUrl, playload) + + def post_power(self, playload): + """post power info + :playload: info to post + :type: dictionary + :returns: True or False + """ + if playload is None: + print("post_power failed: playload is None") + return None + return self._redfish_post(self.PowerUrl, playload) + + def post_thresholdSensors(self, playload): + """post thresholdSensors info + :playload: info to post + :type: dictionary + :returns: True or False + """ + if playload is None: + print("post_thresholdSensors failed: playload is None") + return None + return self._redfish_post(self.ThresholdSensorsUrl, playload) + + def get_fanSpeed(self): + """Get board led info + :returns: class'redfish.rest.v1.RestResponse' or None when failed + """ + return self._redfish_get(self.FanSpeedUrl) + + def post_board(self, playload, board_name="indicatorboard"): + """post board info + :board_name: name of board, default is "indicatorboard" + :type: string + :playload: info to post + :type: dictionary + :returns: True or False + """ + if board_name is None or playload is None: + print("post failed: playload is None") + return False + return self._redfish_post(self.BoardsUrl + board_name, playload) + + def get_boardLed(self, board_name="indicatorboard"): + """Get boardLed info + :board_name: name of board, default is "indicatorboard" + :type: string + :returns: class'redfish.rest.v1.RestResponse' or None when failed + """ + if board_name is None : + print("get failed: board_name is None") + return None + return self._redfish_get(self.BoardsUrl % board_name) + + ''' + +''' +if __name__ == '__main__': + redfish = Redfish_Api() + + ### get + # boards + ret = redfish.get_board() + if ret is None: + print("get failed: board") + else: + print("get succeeded, board:%s" % ret) + + ret = redfish.get_thresholdSensors() + if ret is None: + print("get failed: threshold") + else: + print("get succeeded, threshold:%s" % ret) + + ret = redfish.get_power() + if ret is None: + print("get failed: power") + else: + print("get succeeded, power:%s" % ret) + + ret = redfish.get_thermal() + if ret is None: + print("get failed:thermal") + else: + print("get succeeded,thermal:%s" % ret) + + # get playload + resp = redfish.get_thresholdSensors() + if (resp != None): + print(resp["@odata.id"]) + print(resp["@odata.type"]) + print(resp["Id"]) + print(resp["Name"]) + else: + print("Failed: get_thresholdSensors") + + ### post + # fanSpeed + playload = {} + playload["FanName"] = 'Fan0' + playload["FanSpeedLevelPercents"] = "70" + print("post fanSpeed:%s" % redfish.post_fanSpeed(playload)) + + #{"LEDs": [{"IndicatorLEDColor": "green","LEDType": "sys"},{"IndicatorLEDColor": "off","LEDType": "pwr"},{"IndicatorLEDColor": "green","LEDType": "fan"}]} + playload = {} + led = {} + led1 = {} + led_list = [] + led["IndicatorLEDColor"] = "green" + led["LEDType"] = "sys" + led1["IndicatorLEDColor"] = "off" + led1["LEDType"] = "pwr" + led_list.append(led) + led_list.append(led1) + playload["LEDs"] = led_list + # boardsLed + print("post boardLed:%s" % redfish.post_boardLed(playload)) +''' diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/sfp.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/sfp.py new file mode 100644 index 000000000000..29dd0cb079c1 --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/sfp.py @@ -0,0 +1,325 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +############################################################################# +# +# Module contains an implementation of SONiC Platform Base API and +# provides the platform information +# +# +# *_device.py config version instruction: +# ver 1.0 - platform api: +# "presence_cpld": { +# "dev_id": { +# [dev_id]: { +# "offset": { +# [offset]: [port_id] +# } +# } +# } +# } +# "reset_cpld": { +# "dev_id": { +# [dev_id]: { +# "offset": { +# [offset]: [port_id] +# } +# } +# } +# } +# ver 2.0 - wb_plat: +# "presence_path": "/xx/wb_plat/xx[port_id]/present" +# "eeprom_path": "/sys/bus/i2c/devices/i2c-[bus]/[bus]-0050/eeprom" +# "reset_path": "/xx/wb_plat/xx[port_id]/reset" +############################################################################# +import sys +import time +import os +import syslog +import traceback +from abc import abstractmethod + +configfile_pre = "/usr/local/bin/" +sys.path.append(configfile_pre) + +try: + from sonic_platform_base.sonic_xcvr.sfp_optoe_base import SfpOptoeBase + +except ImportError as error: + raise ImportError(str(error) + "- required module not found") from error + +LOG_DEBUG_LEVEL = 1 +LOG_WARNING_LEVEL = 2 +LOG_ERROR_LEVEL = 3 + + +class Sfp(SfpOptoeBase): + + OPTOE_DRV_TYPE1 = 1 + OPTOE_DRV_TYPE2 = 2 + OPTOE_DRV_TYPE3 = 3 + + # index must start at 1 + def __init__(self, index): + SfpOptoeBase.__init__(self) + self.sfp_type = None + self.log_level_config = LOG_WARNING_LEVEL + # Init instance of SfpCust + self._sfp_api = SfpV2(index) + + def get_eeprom_path(self): + return self._sfp_api._get_eeprom_path() + + def read_eeprom(self, offset, num_bytes): + return self._sfp_api.read_eeprom(offset, num_bytes) + + def write_eeprom(self, offset, num_bytes, write_buffer): + return self._sfp_api.write_eeprom(offset, num_bytes, write_buffer) + + def get_presence(self): + return self._sfp_api.get_presence() + + def get_transceiver_info(self): + # temporary solution for a sonic202111 bug + transceiver_info = super().get_transceiver_info() + try: + if transceiver_info["vendor_rev"] is not None: + transceiver_info["hardware_rev"] = transceiver_info["vendor_rev"] + except BaseException: + print(traceback.format_exc()) + return None + return transceiver_info + + def set_optoe_write_max(self, write_max): + """ + This func is declared and implemented by SONiC but we're not supported + so override it as NotImplemented + """ + self._sfplog(LOG_DEBUG_LEVEL, "set_optoe_write_max NotImplemented") + + def refresh_xcvr_api(self): + """ + Updates the XcvrApi associated with this SFP + """ + self._xcvr_api = self._xcvr_api_factory.create_xcvr_api() + class_name = self._xcvr_api.__class__.__name__ + optoe_type = None + # set sfp_type + if 'CmisApi' in class_name: + self.sfp_type = 'QSFP-DD' + optoe_type = self.OPTOE_DRV_TYPE3 + elif 'Sff8472Api' in class_name: + self.sfp_type = 'SFP' + optoe_type = self.OPTOE_DRV_TYPE2 + elif ('Sff8636Api' in class_name or 'Sff8436Api' in class_name): + self.sfp_type = 'QSFP' + optoe_type = self.OPTOE_DRV_TYPE1 + # set optoe driver + if optoe_type is not None: + self._sfp_api.set_optoe_type(optoe_type) + + def _sfplog(self, log_level, msg): + if log_level >= self.log_level_config: + try: + syslog.openlog("Sfp") + if log_level == LOG_DEBUG_LEVEL: + syslog.syslog(syslog.LOG_DEBUG, msg) + elif log_level == LOG_WARNING_LEVEL: + syslog.syslog(syslog.LOG_DEBUG, msg) + elif log_level == LOG_ERROR_LEVEL: + syslog.syslog(syslog.LOG_ERR, msg) + syslog.closelog() + + except BaseException: + print(traceback.format_exc()) + + +class SfpCust(): + def __init__(self, index): + self.eeprom_path = None + self._init_config(index) + + def _init_config(self, index): + self.log_level_config = LOG_WARNING_LEVEL + self._port_id = index + self.eeprom_retry_times = 5 + self.eeprom_retry_break_sec = 0.2 + + def _get_eeprom_path(self): + return self.eeprom_path or None + + @abstractmethod + def get_presence(self): + pass + + def read_eeprom(self, offset, num_bytes): + try: + for i in range(self.eeprom_retry_times): + with open(self._get_eeprom_path(), mode='rb', buffering=0) as f: + f.seek(offset) + result = f.read(num_bytes) + # temporary solution for a sonic202111 bug + if len(result) < num_bytes: + result = result[::-1].zfill(num_bytes)[::-1] + if result is not None: + return bytearray(result) + time.sleep(self.eeprom_retry_break_sec) + continue + + except BaseException: + self._sfplog(LOG_ERROR_LEVEL, traceback.format_exc()) + return None + + def write_eeprom(self, offset, num_bytes, write_buffer): + try: + for i in range(self.eeprom_retry_times): + ret = SfpOptoeBase.write_eeprom(self, offset, num_bytes, write_buffer) + if ret is False: + time.sleep(self.eeprom_retry_break_sec) + continue + break + + return ret + except BaseException: + self._sfplog(LOG_ERROR_LEVEL, traceback.format_exc()) + return False + + @abstractmethod + def set_optoe_type(self, optoe_type): + pass + + @abstractmethod + def set_reset(self, reset): + pass + + def _convert_str_range_to_int_arr(self, range_str): + if not range_str: + return [] + + int_range_strs = range_str.split(',') + range_res = [] + for int_range_str in int_range_strs: + if '-' in int_range_str: + range_s = int(int_range_str.split('-')[0]) + range_e = int(int_range_str.split('-')[1]) + 1 + else: + range_s = int(int_range_str) + range_e = int(int_range_str) + 1 + + range_res = range_res + list(range(range_s, range_e)) + + return range_res + + def _sfplog(self, log_level, msg): + if log_level >= self.log_level_config: + try: + syslog.openlog("SfpCust") + if log_level == LOG_DEBUG_LEVEL: + syslog.syslog(syslog.LOG_DEBUG, msg) + elif log_level == LOG_WARNING_LEVEL: + syslog.syslog(syslog.LOG_DEBUG, msg) + elif log_level == LOG_ERROR_LEVEL: + syslog.syslog(syslog.LOG_ERR, msg) + syslog.closelog() + + except BaseException: + print(traceback.format_exc()) + + +class SfpV2(SfpCust): + def _init_config(self, index): + super()._init_config(index) + # init eeprom path + sfp_pt2ee_path_list = [0] * 53 + sfp_pt2ee_path_list[49:53] = [9, 10, 11, 12] + + eeprom_path_config = "/sys/bus/i2c/devices/i2c-%d/%d-0050/eeprom" + eeprom_path_key = sfp_pt2ee_path_list[self._port_id] + self.eeprom_path = None if eeprom_path_config is None or eeprom_path_key == 0 else eeprom_path_config % ( + eeprom_path_key, eeprom_path_key) + self._sfplog(LOG_DEBUG_LEVEL, "Done init eeprom path: %s" % self.eeprom_path) + + # init presence path + self.presence_path = "/sys/bus/i2c/devices/3-0030/sfp_presence1" + self.presence_val_is_present = 0 + self._sfplog(LOG_DEBUG_LEVEL, "Done init presence path: %s" % self.presence_path) + + # init optoe driver path + optoe_driver_path = "/sys/bus/i2c/devices/i2c-%d/%d-0050/dev_class" + optoe_driver_key = sfp_pt2ee_path_list[self._port_id] + self.dev_class_path = None if optoe_driver_path is None or optoe_driver_key == 0 else optoe_driver_path % ( + optoe_driver_key, optoe_driver_key) + self._sfplog(LOG_DEBUG_LEVEL, "Done init optoe driver path: %s" % self.dev_class_path) + + # init reset path + self.reset_val_is_reset = 0 + + new_device_path = "/sys/bus/i2c/devices/i2c-%d/new_device" + new_device_key = sfp_pt2ee_path_list[self._port_id] + self.new_class_path = None if new_device_path is None or new_device_key == 0 else new_device_path % new_device_key + self._sfplog(LOG_DEBUG_LEVEL, "Done init new_class path: %s" % self.new_class_path) + + if sfp_pt2ee_path_list[self._port_id] != 0: + self.add_new_sfp_device(self._port_id, 0x50) + self._sfplog(LOG_DEBUG_LEVEL, "Done add_new_sfp_device 0x50 port %d" % self._port_id) + + def sfp_add_dev(self, new_device_path, devaddr, devtype): + try: + # Write device address to new_device file + nd_file = open(new_device_path, "w") + nd_str = "%s %s" % (devtype, hex(devaddr)) + nd_file.write(nd_str) + nd_file.close() + except Exception as err: + self._sfplog(LOG_ERROR_LEVEL, ("Error writing to new device file: %s" % str(err))) + return 1 + else: + return 0 + + def add_new_sfp_device(self, port_num, devid): + if os.path.exists(self.dev_class_path): + return + + ret = self.sfp_add_dev(self.new_class_path, devid, "optoe2") + if ret != 0: + self._sfplog(LOG_ERROR_LEVEL, "Error adding sfp device") + + def get_presence(self): + sfp_ls = [49, 50, 51, 52] + if self._port_id not in sfp_ls or self.presence_path is None: + self._sfplog(LOG_ERROR_LEVEL, "presence_path is None!") + return False + try: + with open(self.presence_path, "rb") as data: + presence_data = data.read(2) + if presence_data == "": + return False + result = int(presence_data, 16) + + # ModPrsL is active low + presence_offset = sfp_ls.index(self._port_id) + if result & (1 << presence_offset) == 0: + return True + return False + except BaseException: + self._sfplog(LOG_ERROR_LEVEL, traceback.format_exc()) + return False + + def set_reset(self, reset): + return True + + def set_optoe_type(self, optoe_type): + if self.dev_class_path is None: + self._sfplog(LOG_ERROR_LEVEL, "dev_class_path is None!") + return False + try: + with open(self.dev_class_path, "r+") as dc_file: + dc_file_val = dc_file.read(1) + if int(dc_file_val) != optoe_type: + dc_str = "%s" % str(optoe_type) + dc_file.write(dc_str) + # dc_file.close() + except BaseException: + self._sfplog(LOG_ERROR_LEVEL, traceback.format_exc()) + return False + return True diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/thermal.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/thermal.py new file mode 100644 index 000000000000..5fded8d9ddeb --- /dev/null +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/thermal.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python + +import os +import re +import os.path +import time + +try: + from sonic_platform_base.thermal_base import ThermalBase + from .redfish_api import Redfish_Api +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +class Thermal(ThermalBase): + """Platform-specific Thermal class""" + + def __init__(self, thermal_index): + ThermalBase.__init__(self) + self.index = thermal_index + self.high_threshold = float(112) + self.redfish = Redfish_Api() + self.pinf = {} + self.begin = time.time() + + def get_power_3s(self): + self.elapsed = time.time() + if not self.pinf or self.elapsed - self.begin >= 3: + self.begin = time.time() + self.pinf = self.redfish.get_thermal() + + def get_temperature(self): + self.get_power_3s() + ctrl = self.pinf["Temperatures"] + output = ctrl[self.index] + temp = output.get("ReadingCelsius") + return temp + + def get_high_threshold(self): + self.get_power_3s() + ctrl = self.pinf["Temperatures"] + output = ctrl[self.index] + temp = output.get("UpperThresholdFatal") + return temp + + def get_low_threshold(self): + self.get_power_3s() + ctrl = self.pinf["Temperatures"] + output = ctrl[self.index] + temp = output.get("LowerThresholdFatal") + return temp + + def get_high_critical_threshold(self): + self.get_power_3s() + ctrl = self.pinf["Temperatures"] + output = ctrl[self.index] + temp = output.get("UpperThresholdFatal") + return temp + + def get_low_critical_threshold(self): + self.get_power_3s() + ctrl = self.pinf["Temperatures"] + output = ctrl[self.index] + temp = output.get("LowerThresholdFatal") + return temp + + def get_name(self): + self.get_power_3s() + ctrl = self.pinf["Temperatures"] + output = ctrl[self.index].get("Name") + name = output.split("/",3)[2] + if name == "SWITCH_TEMP": + name = "ASIC_TEMP" + return "{}".format(name) + + def get_real_name(self): + self.get_power_3s() + ctrl = self.pinf["Temperatures"] + output = ctrl[self.index].get("Name") + name = output.split("/",3)[2] + return "{}".format(name) + + def get_presence(self): + self.get_power_3s() + ctrl = self.pinf["Temperatures"] + output = ctrl[self.index] + if output.get("Status").get("Status").get("State") == "Enabled": + return True + return False + + def get_status(self): + self.get_power_3s() + ctrl = self.pinf["Temperatures"] + output = ctrl[self.index] + if output.get("Status").get("Status").get("Health") == "OK": + return True + return False + + def set_sys_led(self, color): + playload = {} + led = {} + led_list = [] + led["IndicatorLEDColor"] = color + led["LEDType"] = "sys" + led_list.append(led) + playload["LEDs"] = led_list + # boardsLed + return self.redfish.post_boardLed(playload) + + def get_minimum_recorded(self): + """ + Retrieves the minimum recorded temperature of thermal + + Returns: + A float number, the minimum recorded temperature of thermal in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + return "N/A" + + def get_maximum_recorded(self): + """ + Retrieves the maximum recorded temperature of thermal + + Returns: + A float number, the maximum recorded temperature of thermal in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + return "N/A" + + def get_model(self): + """ + Retrieves the model number (or part number) of the Thermal + + Returns: + string: Model/part number of Thermal + """ + return "N/A" + + def get_serial(self): + """ + Retrieves the serial number of the Thermal + + Returns: + string: Serial number of Thermal + """ + return "N/A" + + def get_revision(self): + """ + Retrieves the hardware revision of the device + + Returns: + string: Revision value of device + """ + return "N/A" + + 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 diff --git a/platform/centec-arm64/sonic_fit.its b/platform/centec-arm64/sonic_fit.its index 730f0e3aec3f..b9b62576cd71 100644 --- a/platform/centec-arm64/sonic_fit.its +++ b/platform/centec-arm64/sonic_fit.its @@ -6,57 +6,83 @@ /dts-v1/; / { - description = "arm64 kernel, initramfs and FDT blob"; - #address-cells = <1>; - - images { - kernel_ctc { - description = "ARM64 Kernel"; - data = /incbin/("./vmlinuz-5.10.0-18-2-arm64"); - type = "kernel"; - arch = "arm64"; - os = "linux"; - compression = "none"; - load = <0x80080000>; - entry = <0x80080000>; - hash { - algo = "crc32"; - }; - }; - initramfs { - description = "initramfs"; - data = /incbin/("./initrd.img-5.10.0-18-2-arm64"); - type = "ramdisk"; - arch = "arm64"; - os = "linux"; - compression = "gzip"; - load = <0x85000000>; - entry = <0x85000000>; - hash { - algo = "crc32"; - }; - }; - ctc_fdt { - description = "dtb for tm_ctc5236"; - data = /incbin/("./e530-ctc5236.dtb"); - type = "flat_dt"; - arch = "arm64"; - os = "linux"; - compression = "none"; - load = <0x88000000>; - hash { - algo = "crc32"; - }; - }; - }; - configurations { - default = "e530-ctc5236"; - - e530-ctc5236 { - description = "config for tm_ctc5236"; - kernel = "kernel_ctc"; - ramdisk = "initramfs"; - fdt = "ctc_fdt"; - }; - }; + description = "arm64 kernel, initramfs and FDT blob"; + #address-cells = <1>; + + images { + kernel_ctc { + description = "ARM64 Kernel"; + data = /incbin/("./vmlinuz-5.10.0-8-2-arm64"); + type = "kernel"; + arch = "arm64"; + os = "linux"; + compression = "none"; + load = <0x80080000>; + entry = <0x80080000>; + hash { + algo = "crc32"; + }; + }; + initramfs { + description = "initramfs"; + data = /incbin/("./initrd.img-5.10.0-8-2-arm64"); + type = "ramdisk"; + arch = "arm64"; + os = "linux"; + compression = "gzip"; + load = <0x85000000>; + entry = <0x85000000>; + hash { + algo = "crc32"; + }; + }; + ctc_fdt { + description = "dtb for tm_ctc5236"; + data = /incbin/("./e530-ctc5236.dtb"); + type = "flat_dt"; + arch = "arm64"; + os = "linux"; + compression = "none"; + load = <0x88000000>; + hash { + algo = "crc32"; + }; + }; + ctc_fdtw6010 { + description = "dtb for m2-w6010-48gt4x-r0"; + data = /incbin/("./m2-w6010-48gt4x-r0.dtb"); + type = "flat_dt"; + arch = "arm64"; + os = "linux"; + compression = "none"; + load = <0x88000000>; + hash { + algo = "crc32"; + }; + }; + }; + configurations { + default = "e530-ctc5236"; + + e530-ctc5236 { + description = "config for tm_ctc5236"; + kernel = "kernel_ctc"; + ramdisk = "initramfs"; + fdt = "ctc_fdt"; + }; + arm64-micas_m2-w6010-48gt4x-fa-r0 { + description = "config for micas_m2-w6010-48gt4x-fa"; + kernel = "kernel_ctc"; + ramdisk = "initramfs"; + fdt = "ctc_fdtw6010"; + }; + + arm64-micas_m2-w6010-48gt4x-ra-r0 { + description = "config for micas_m2-w6010-48gt4x-ra"; + kernel = "kernel_ctc"; + ramdisk = "initramfs"; + fdt = "ctc_fdtw6010"; + }; + }; }; + diff --git a/platform/centec-arm64/tsingma-bsp/debian/rules b/platform/centec-arm64/tsingma-bsp/debian/rules index 39744906e3cf..65e564fcedd3 100755 --- a/platform/centec-arm64/tsingma-bsp/debian/rules +++ b/platform/centec-arm64/tsingma-bsp/debian/rules @@ -17,6 +17,7 @@ KERNEL_SRC := /lib/modules/$(KVERSION) MOD_SRC_DIR:= $(shell pwd) MODULE_DIRS:= ctc5236-mc ctc5236_switch ctcmac ctc_wdt ehci-ctc gpio-ctc i2c-ctc pinctrl-ctc pwm-ctc rtc-sd2405 sdhci-ctc5236 spi-ctc-qspi ctc-phy DTS_DIR := ctc-dts +DTS_DIR0 := m2-w6010-48gt4x MODULE_DIR := src UTILS_DIR := utils SERVICE_DIR := service @@ -36,6 +37,7 @@ build: make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$(MODULE_DIR)/$${mod}; \ done) make -C $(MOD_SRC_DIR)/$(MODULE_DIR)/$(DTS_DIR) + make -C $(MOD_SRC_DIR)/$(MODULE_DIR)/$(DTS_DIR0) binary: binary-arch binary-indep # Nothing to do diff --git a/platform/centec-arm64/tsingma-bsp/debian/tsingma-bsp.install b/platform/centec-arm64/tsingma-bsp/debian/tsingma-bsp.install index e072cb175189..eaeed325eb82 100644 --- a/platform/centec-arm64/tsingma-bsp/debian/tsingma-bsp.install +++ b/platform/centec-arm64/tsingma-bsp/debian/tsingma-bsp.install @@ -13,5 +13,6 @@ src/rtc-sd2405/rtc-sd2405.ko /lib/modules/5.10.0-18-2-arm64/kernel/extra src/sdhci-ctc5236/sdhci-ctc5236.ko /lib/modules/5.10.0-18-2-arm64/kernel/extra src/spi-ctc-qspi/spi-ctc-qspi.ko /lib/modules/5.10.0-18-2-arm64/kernel/extra src/ctc-dts/e530-ctc5236.dtb /boot/ +src/m2-w6010-48gt4x/m2-w6010-48gt4x-r0.dtb /boot/ src/config/fw_env.config /etc/ src/config/tsingma-bsp.service /lib/systemd/system diff --git a/platform/centec-arm64/tsingma-bsp/src/ctc-phy/mars.c b/platform/centec-arm64/tsingma-bsp/src/ctc-phy/mars.c index 675ba528fe68..7f1cdafe2254 100644 --- a/platform/centec-arm64/tsingma-bsp/src/ctc-phy/mars.c +++ b/platform/centec-arm64/tsingma-bsp/src/ctc-phy/mars.c @@ -314,3 +314,6 @@ static struct mdio_device_id __maybe_unused mars_tbl[] = { }; MODULE_DEVICE_TABLE(mdio, mars_tbl); + +MODULE_AUTHOR("Centec, Inc."); +MODULE_LICENSE("GPL"); diff --git a/platform/centec-arm64/tsingma-bsp/src/ctcmac/ctcmac.c b/platform/centec-arm64/tsingma-bsp/src/ctcmac/ctcmac.c index ea28b561ecaf..69a423064520 100755 --- a/platform/centec-arm64/tsingma-bsp/src/ctcmac/ctcmac.c +++ b/platform/centec-arm64/tsingma-bsp/src/ctcmac/ctcmac.c @@ -1159,6 +1159,10 @@ static void ctcmac_hw_init(struct ctcmac_private *priv) BIT(CPU_MAC_SGMII_AUTO_NEG_CFG_W0_CFG_AN_ENABLE_BIT)); writel(val, &priv->cpumac_reg->CpuMacSgmiiAutoNegCfg); } + clrsetbits(&priv->cpumac_reg->CpuMacSgmiiAutoNegCfg, + 0, BIT(CPU_MAC_SGMII_AUTO_NEG_CFG_W0_CFG_IGNORE_ANEG_ERR_BIT)); + clrsetbits(&priv->cpumac_reg->CpuMacSgmiiAutoNegCfg, + 0, BIT(CPU_MAC_SGMII_AUTO_NEG_CFG_W0_CFG_IGNORE_LINK_FAILURE_BIT)); /* disable rx link filter */ clrsetbits(&priv->cpumac_reg->CpuMacSgmiiCfg[0], BIT(CPU_MAC_SGMII_CFG_W0_CFG_MII_RX_LINK_FILTER_EN_BIT), 0); @@ -1736,7 +1740,8 @@ static bool ctcmac_new_page(struct ctcmac_priv_rx_q *rxq, struct page *page; dma_addr_t addr; - page = dev_alloc_page(); + //page = dev_alloc_page(); + page = __dev_alloc_pages(GFP_DMA | GFP_ATOMIC | __GFP_NOWARN, 0); if (unlikely(!page)) return false; diff --git a/platform/centec-arm64/tsingma-bsp/src/i2c-ctc/i2c-ctc.c b/platform/centec-arm64/tsingma-bsp/src/i2c-ctc/i2c-ctc.c index 0d6b97f2b378..d6b95aec3066 100755 --- a/platform/centec-arm64/tsingma-bsp/src/i2c-ctc/i2c-ctc.c +++ b/platform/centec-arm64/tsingma-bsp/src/i2c-ctc/i2c-ctc.c @@ -789,6 +789,11 @@ static int ctc_i2c_plat_probe(struct platform_device *pdev) of_property_read_u32(pdev->dev.of_node, "clock-frequency", &clk_freq); dev->clk_freq = clk_freq; + + dev->functionality = I2C_FUNC_10BIT_ADDR | CTC_IC_DEFAULT_FUNCTIONALITY; + dev->master_cfg = CTC_IC_CON_MASTER | CTC_IC_CON_SLAVE_DISABLE | + CTC_IC_CON_RESTART_EN; + if (dev->clk_freq <= 100000) dev->master_cfg |= CTC_IC_CON_SPEED_STD; else if (dev->clk_freq <= 400000) diff --git a/platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/Makefile b/platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/Makefile new file mode 100755 index 000000000000..5837924e3dd5 --- /dev/null +++ b/platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/Makefile @@ -0,0 +1,5 @@ +m2-w6010-48gt4x-r0.dtb: m2-w6010-48gt4x-r0.dts ctc5236.dtsi ctc5236-clock.dtsi + cpp -nostdinc -I. -undef -x assembler-with-cpp m2-w6010-48gt4x-r0.dts > tmp.dts + dtc -O dtb -o m2-w6010-48gt4x-r0.dtb tmp.dts + rm tmp.dts -rf + diff --git a/platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/arm-gic.h b/platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/arm-gic.h new file mode 100755 index 000000000000..ef79498bdde5 --- /dev/null +++ b/platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/arm-gic.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * This header provides constants for the ARM GIC. + */ + +#ifndef _DT_BINDINGS_INTERRUPT_CONTROLLER_ARM_GIC_H +#define _DT_BINDINGS_INTERRUPT_CONTROLLER_ARM_GIC_H + +#include "irq.h" + +/* interrupt specifier cell 0 */ + +#define GIC_SPI 0 +#define GIC_PPI 1 + +/* + * Interrupt specifier cell 2. + * The flags in irq.h are valid, plus those below. + */ +#define GIC_CPU_MASK_RAW(x) ((x) << 8) +#define GIC_CPU_MASK_SIMPLE(num) GIC_CPU_MASK_RAW((1 << (num)) - 1) + +#endif diff --git a/platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/ctc5236-clks.h b/platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/ctc5236-clks.h new file mode 100755 index 000000000000..56d91da3130a --- /dev/null +++ b/platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/ctc5236-clks.h @@ -0,0 +1,25 @@ +/* + * ctc5236 clock tree IDs + * + * (C) Copyright 2004-2017 Centec Networks (suzhou) Co., LTD. + * + * Jay Cao + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __CTC5236_CLKS_H +#define __CTC5236_CLKs_H + +#define CLKID_UNUSED 0 +#define CLKID_PLL_FIXED 1 +#define CLKID_UART0 2 + +#endif /* __CTC5236_CLKS_H */ diff --git a/platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/ctc5236-clock.dtsi b/platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/ctc5236-clock.dtsi new file mode 100755 index 000000000000..ae6b00c00ba2 --- /dev/null +++ b/platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/ctc5236-clock.dtsi @@ -0,0 +1,63 @@ +/* + * dts file for Centec CTC5236(TsingMa) SoC + * + * (C) Copyright 2004-2017 Centec Networks (suzhou) Co., LTD. + * + * Jay Cao + * + * 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. + */ + + osc: oscillator { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <50000000>; + }; + + sup_clk: sup_clk_12m { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <1200000000>; + clock-output-names = "sup_clk"; + }; + + uart_clk: uart_clk_20m { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <20000000>; + clock-output-names = "uart_clk"; + }; + + i2c_clk: clkm { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <50000000>; + clock-output-names = "i2c_clk"; + }; + wdog_clk:wdog_clk{ + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <1000000>; + clock-output-names = "wdog_clk"; + }; + timer_clk:timer_clk{ + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <500000000>; + clock-output-names = "timer_clk"; + }; + mmc_clk:mmc_clk{ + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <100000000>; + clock-output-names = "mmc_clk"; + }; + spi_clk:spi_clk{ + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <100000000>; + clock-output-names = "spi_clk"; + }; diff --git a/platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/ctc5236.dtsi b/platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/ctc5236.dtsi new file mode 100755 index 000000000000..bacf2eb17ddc --- /dev/null +++ b/platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/ctc5236.dtsi @@ -0,0 +1,428 @@ +/* + * dts file for Centec CTC5236(TsingMa) SoC + * + * (C) Copyright 2004-2017 Centec Networks (suzhou) Co., LTD. + * + * Jay Cao + * + * 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. + */ + +#include "arm-gic.h" +#include "ctc5236-clks.h" +#include "../pinctrl-ctc/pinctrl-ctc.h" + +/ { + compatible = "centec,ctc5236"; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&gic>; + + cpus { + #address-cells = <2>; + #size-cells = <0>; + + cpu-map { + cluster0 { + core0 { + cpu = <&cpu0>; + }; + core1 { + cpu = <&cpu1>; + }; + }; + }; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a53", "arm,armv8"; + reg = <0 0x000>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x0010fff0>; + }; + + cpu1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a53", "arm,armv8"; + reg = <0 0x001>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x0010fff0>; + }; + }; + + gic: interrupt-controller@31201000 { + compatible = "arm,gic-400"; + #interrupt-cells = <3>; + interrupt-controller; + reg = <0x0 0x31201000 0 0x1000>, + <0x0 0x31202000 0 0x2000>, + <0x0 0x31204000 0 0x2000>, + <0x0 0x31206000 0 0x2000>; + interrupts = ; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupt-parent = <&gic>; + interrupts = , + , + , + ; + }; + + soc: soc { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + #include "ctc5236-clock.dtsi" + ocram: sram@00100000 { + compatible = "mmio-sram"; + reg = <0x0 0x00100000 0x0 0x10000>; + }; + + memory-controller@30600000 { + compatible = "ctc,ctc5236-ddr-ctrl"; + reg = <0x0 0x30600000 0x0 0x100000>; + interrupts = , + , + , + ; + ctc,sysctrl = <&sysctrl>; + }; + + sysctrl: sysctrl@33200000 { + compatible = "ctc,ctc5236-sysctrl", "syscon"; + reg = <0x0 0x33200000 0x0 0x100000>; + little-endian; + }; + + serial0: serial@33000000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x33000000 0x0 0x1000>; + interrupts = ; + clocks = <&uart_clk>, <&sup_clk>; + clock-names = "uart_clk", "apb_pclk"; + status="disabled"; + }; + serial1: serial@33001000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x33001000 0x0 0x1000>; + interrupts = ; + clocks = <&uart_clk>, <&sup_clk>; + clock-names = "uart_clk", "apb_pclk"; + status="disabled"; + }; + serial2: serial@33002000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x33002000 0x0 0x1000>; + interrupts = ; + clocks = <&uart_clk>, <&sup_clk>; + clock-names = "uart_clk", "apb_pclk"; + status="disabled"; + }; + mdio: mdio@33620000 { + compatible = "ctc,mdio"; + reg = <0x0 0x33620000 0x0 0x10000>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + enet0: ethernet@33410000 { + compatible = "ctc,mac"; + device_type = "network"; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&gic>; + status = "disabled"; + local-mac-address = [00 00 00 00 00 00]; + index = <0x00>; + reg = <0x0 0x33410000 0x0 0x10000>, + <0x0 0x33400000 0x0 0x10000>; + interrupts = , + , + , + , + ; + ctc,sysctrl = <&sysctrl>; + }; + + enet1: ethernet@33420000 { + compatible = "ctc,mac"; + device_type = "network"; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&gic>; + status = "disabled"; + local-mac-address = [00 00 00 00 00 00]; + index = <0x01>; + reg = <0x0 0x33420000 0x0 0x10000>, + <0x0 0x33400000 0x0 0x10000>; + interrupts = , + , + , + , + ; + ctc,sysctrl = <&sysctrl>; + }; + + ehci0: usb@30500000 { + compatible = "ctc-ehci"; + reg = <0x0 0x30500000 0x0 0x1000>; + interrupts = ; + ctc,sysctrl = <&sysctrl>; + status = "disabled"; + }; + + ohci0: usb@30580000 { + compatible = "generic-ohci"; + reg = <0x0 0x30580000 0x0 0x1000>; + interrupts = ; + status = "disabled"; + }; + + spi: spi@33100000 { + compatible = "arm,pl022","arm,primecell"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x33100000 0x0 0x100000>; + clocks = <&spi_clk>, <&sup_clk>; + clock-names = "spi_clk", "apb_pclk"; + num-cs = <4>; + interrupts = ; + ctc,sysctrl = <&sysctrl>; + status ="disabled"; + }; + + qspi: qspi@10000000 { + compatible = "ctc, igdaxi001a-qspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x10000000 0x0 0x10000>; + pclk = <500000000>; + num-cs = <2>; + idle-cycle = <2>; + post-cycle = <1>; + pre-cycle = <2>; + interrupts = ; + status = "disabled"; + }; + switch: switch@31100000 { + compatible = "centec,dal-localbus"; + reg = <0x0 0x31100000 0x0 0x1000>, + <0x0 0x33290000 0x0 0x10000>; + interrupts = , + , + , + , + , + , + , + ; + status ="disabled"; + }; + switch1: switch1@31101000 { + compatible = "centec,switch"; + reg = <0x0 0x31101000 0x0 0x1000>; + status ="disabled"; + }; + + i2c0: i2c0@33700000{ + compatible = "ctc,i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x33700000 0x0 0x1000>; + interrupts = ; + clocks = <&i2c_clk>; + ctc,sysctrl = <&sysctrl>; + i2c-num = <0>; + status ="disabled"; + }; + + i2c1: i2c1@33701000{ + compatible = "ctc,i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x33701000 0x0 0x1000>; + interrupts = ; + clocks = <&i2c_clk>; + ctc,sysctrl = <&sysctrl>; + i2c-num = <1>; + status ="disabled"; + }; + + pcie: pcie@20000000 { + compatible = "centec,ctc5236-pcie"; + reg = <0x0 0x20000000 0x0 0x10000000 + 0x0 0x30000000 0x0 0x1000>; + reg-names = "cfg", "ctrl"; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + device_type = "pci"; + interrupt-parent = <&gic>; + interrupts = , + , + ; + interrupt-names = "msi","aer","pme"; + msi-parent = <&pcie>; + bus-range = <0 0xff>; + ranges = <0x42000000 0 0x00000000 0 0x40000000 0 0x20000000 + 0x02000000 0 0x20000000 0 0x60000000 0 0x20000000>; + num-lanes = <1>; + ctc,sysctrl = <&sysctrl>; + status ="disabled"; + }; + + wtd0: wtd0@33500000{ + compatible = "arm,sp805-wdt", "arm,primecell"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x33500000 0x0 0x1000>; + clocks = <&wdog_clk>, <&sup_clk>; + clock-names = "wdog_clk", "apb_pclk"; + ctc,sysctrl = <&sysctrl>; + interrupts = ; + status="disabled"; + }; + wtd1: wtd1@33501000{ + compatible = "arm,sp805-wdt", "arm,primecell"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x33501000 0x0 0x1000>; + clocks = <&wdog_clk>, <&sup_clk>; + clock-names = "wdog_clk", "apb_pclk"; + ctc,sysctrl = <&sysctrl>; + interrupts = ; + status="disabled"; + }; + + sdhci: sdhci@30400000 { + compatible = "centec,ctc5236-sdhci"; + status = "disabled"; + interrupt-parent = <&gic>; + interrupts = ; + clocks = <&mmc_clk>; + clock-names = "mmc_clk"; + ctc,sysctrl = <&sysctrl>; + reg = <0x0 0x30400000 0x0 0x1000>; + }; + + timer0: timer0@33600000{ + compatible = "snps,dw-apb-timer"; + reg = <0x0 0x33600000 0x0 0x20>; + clocks = <&timer_clk>; + clock-names = "timer"; + interrupts = ; + DivNum = <0x3c>; + status="disabled"; + }; + timer1: timer1@33600020{ + compatible = "snps,dw-apb-timer"; + reg = <0x0 0x33600020 0x0 0x20>; + clocks = <&timer_clk>; + clock-names = "timer"; + interrupts = ; + DivNum = <0x3c>; + status="disabled"; + }; + + pwm: pwm@33200240{ + compatible = "centec-pwm"; + ctc,sysctrl = <&sysctrl>; + #pwm-cells = <2>; + + status="disabled"; + }; + + fan: fan-ctc5236 { + compatible = "fan-ctc5236"; + pwms = <&pwm 0 1000000>, + <&pwm 1 1000000>, + <&pwm 2 1000000>, + <&pwm 3 1000000>; + pwm-names = "pwm1","pwm2","pwm3","pwm4"; + }; + + gpio0: gpio@33610000 { + compatible = "ctc,apb-gpio"; + reg = <0x0 0x33610000 0x0 0x10000>; + #address-cells = <1>; + #size-cells = <0>; + ctc,sysctrl = <&sysctrl>; + + porta: gpio-port@0 { + compatible = "ctc,apb-gpio-porta"; + gpio-controller; + #gpio-cells = <2>; + ctc,nr-gpios = <16>; + reg = <0>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = ; + }; + portb: gpio-port@1 { + compatible = "ctc,apb-gpio-portb"; + gpio-controller; + #gpio-cells = <2>; + ctc,nr-gpios = <18>; + reg = <1>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = ; + }; + }; + + pinctrl: pinctrl { + compatible = "ctc,ctc5236-pinctrl"; + #address-cells = <0x2>; + #size-cells = <0x2>; + ctc,pinctrl-bank0 = <16>; + ctc,pinctrl-bank1 = <8>; + ctc,sysctrl = <&sysctrl>; + + spi { + spi_pin: spi_pin { + ctc,pins = <0 0 PIN_FUNC_SPI>, + <0 2 PIN_FUNC_SPI>, + <0 3 PIN_FUNC_SPI>, + <0 4 PIN_FUNC_SPI>, + <0 5 PIN_FUNC_SPI>, + <0 6 PIN_FUNC_SPI>, + <0 7 PIN_FUNC_SPI>; + }; + }; + + uart2 { + uart2_pin: uart2_pin { + ctc,pins = <0 10 PIN_FUNC_UART>, + <0 11 PIN_FUNC_UART>, + <0 12 PIN_FUNC_UART>, + <0 13 PIN_FUNC_UART>, + <0 14 PIN_FUNC_UART>, + <0 15 PIN_FUNC_UART>; + }; + }; + + fc { + fc_pin: fc_pin { + ctc,pins = <1 0 PIN_FUNC_FC>, + <1 1 PIN_FUNC_FC>, + <1 2 PIN_FUNC_FC>, + <1 3 PIN_FUNC_FC>, + <1 4 PIN_FUNC_FC>, + <1 5 PIN_FUNC_FC>, + <1 6 PIN_FUNC_FC>, + <1 7 PIN_FUNC_FC>; + }; + }; + }; + }; + +}; + diff --git a/platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/irq.h b/platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/irq.h new file mode 100755 index 000000000000..9e3d183e1381 --- /dev/null +++ b/platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/irq.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * This header provides constants for most IRQ bindings. + * + * Most IRQ bindings include a flags cell as part of the IRQ specifier. + * In most cases, the format of the flags cell uses the standard values + * defined in this header. + */ + +#ifndef _DT_BINDINGS_INTERRUPT_CONTROLLER_IRQ_H +#define _DT_BINDINGS_INTERRUPT_CONTROLLER_IRQ_H + +#define IRQ_TYPE_NONE 0 +#define IRQ_TYPE_EDGE_RISING 1 +#define IRQ_TYPE_EDGE_FALLING 2 +#define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING) +#define IRQ_TYPE_LEVEL_HIGH 4 +#define IRQ_TYPE_LEVEL_LOW 8 + +#endif diff --git a/platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/m2-w6010-48gt4x-r0.dts b/platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/m2-w6010-48gt4x-r0.dts new file mode 100755 index 000000000000..acc4b2627e44 --- /dev/null +++ b/platform/centec-arm64/tsingma-bsp/src/m2-w6010-48gt4x/m2-w6010-48gt4x-r0.dts @@ -0,0 +1,331 @@ +/* + * dts file for Centec CTC5236(TsingMa) SoC E530-24X2C Board + * + * (C) Copyright 2004-2018 Centec Networks (suzhou) Co., LTD. + * + * liuht + * + * 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. + */ + +/dts-v1/; +#include "ctc5236.dtsi" + +/ { + model = " CTC5236(TsingMa) E530 Board"; + compatible = "ctc5236,e530-ctc5236"; + + memory@0 { + device_type = "memory"; + reg = <0 0x80000000 0x1 0x00000000>; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + aliases { + serial0 = &serial0; + serial1 = &serial1; + ethernet0 = &enet0; + ethernet1 = &enet1; + + i2c0 = &i2c1; + /* 0 0x70 */ + i2c1 = &imux1; + i2c2 = &imux2; + i2c3 = &imux3; + i2c4 = &imux4; + i2c5 = &imux5; + i2c6 = &imux6; + i2c7 = &imux7; + i2c8 = &imux8; + /* 8 0x74 */ + i2c9 = &imux9; + i2c10 = &imux10; + i2c11 = &imux11; + i2c12 = &imux12; + i2c13 = &imux13; + i2c14 = &imux14; + i2c15 = &imux15; + i2c16 = &imux16; + }; + +}; + +&serial0 { + status = "okay"; +}; + +&serial1 { + status = "okay"; +}; + +&mdio { + status = "okay"; + phy0: ethernet-phy@0 { + index = <0x00>; + reg = <0x00>; + }; +}; + +&enet0 { + status = "okay"; + auto-nego-mode= "sgmii-mac"; + fixed-link { + speed = <1000>; + full-duplex; + }; +}; + +&qspi { + status = "okay"; + + qspiflash0: mx25u3235f@0 { + compatible = "jedec,spi-nor"; + reg = <0x0>; + spi-cpha; + spi-cpol; + spi-max-frequency = <25000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0 0x300000>; + }; + partition@300000 { + label = "uboot-env"; + reg = <0x300000 0x10000>; + }; + partition@310000 { + label = "onie"; + reg = <0x310000 0x1cf0000>; + }; + }; + }; + + qspiflash1: mx25u3235f@1 { + compatible = "jedec,spi-nor"; + reg = <0x1>; + spi-cpha; + spi-cpol; + spi-max-frequency = <25000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot1"; + reg = <0x0 0x300000>; + }; + partition@300000 { + label = "uboot1-env"; + reg = <0x300000 0x10000>; + }; + partition@310000 { + label = "onie1"; + reg = <0x310000 0x1cf0000>; + }; + }; + }; +}; + +&spi { + status = "disabled"; + pinctrl-names = "default"; + pinctrl-0 = <&spi_pin>; + + clock0: ad9559@1 { + compatible = "analog,ad9559"; + reg = <1>; + spi-max-frequency = <25000000>; + }; +}; + +&switch { + status = "okay"; +}; + +&switch1 { + status = "okay"; +}; + + +&i2c1{ + status = "okay"; + clock-frequency = <95000>; + + pca9548@70 { + compatible = "nxp,pca9548"; + reg = <0x70>; + #address-cells = <1>; + #size-cells = <0>; + + imux1:i2c@0 { + reg = <0>; + }; + imux2:i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + imux3:i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + imux4:i2c@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + imux5:i2c@4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + imux6:i2c@5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + imux7:i2c@6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + imux8:i2c@7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + pca9548@74 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "nxp,pca9548"; + reg = <0x74>; + imux9:i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + imux10:i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + imux11:i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + imux12:i2c@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + imux13:i2c@4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + imux14:i2c@5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + imux15:i2c@6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + imux16:i2c@7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + }; + }; + }; +}; + +&ehci0 { + status = "okay"; +}; + +&ohci0 { + status = "okay"; +}; + +&wtd0{ + status = "okay"; +}; + +&wtd1{ + status = "disabled"; +}; + +&sdhci { + bus-width = <8>; + max-frequency = <100000000>; + non-removable; + no-sd; + no-sdio; + voltage-ranges = <3300 3300>; + status = "okay"; +}; + +&timer0 { + status = "okay"; +}; + +&soc { + ctc-irq { + compatible = "centec,ctc-irq"; + device_type = "ctc-irq"; + interrupt-parent=<&porta>; + interrupts = < 0 IRQ_TYPE_LEVEL_LOW>, + < 1 IRQ_TYPE_LEVEL_LOW>, + <15 IRQ_TYPE_LEVEL_LOW>, + < 6 IRQ_TYPE_LEVEL_HIGH>, + < 7 IRQ_TYPE_LEVEL_HIGH>; + }; +}; + +&pwm { + status = "disabled"; +}; + + +&pinctrl { + spi { + spi_pin: spi_pin { + status = "disabled"; + ctc,pins = <0 0 PIN_FUNC_SPI>, + <0 2 PIN_FUNC_SPI>, + <0 3 PIN_FUNC_SPI>, + <0 5 PIN_FUNC_SPI>; + }; + }; + + pwm0 { + pwm0_pin: pwm0_pin { + ctc,pins = <0 8 PIN_FUNC_PWM>, + <0 9 PIN_FUNC_PWM>, + <0 10 PIN_FUNC_PWM>, + <0 11 PIN_FUNC_PWM>, + <0 12 PIN_FUNC_PWM>, + <0 13 PIN_FUNC_PWM>, + <0 14 PIN_FUNC_PWM>, + <0 15 PIN_FUNC_PWM>; + }; + }; +}; diff --git a/src/sonic-config-engine/minigraph.py b/src/sonic-config-engine/minigraph.py index 9a7902928e76..94c54d93c103 100644 --- a/src/sonic-config-engine/minigraph.py +++ b/src/sonic-config-engine/minigraph.py @@ -1945,11 +1945,6 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw 'client_auth': 'true', 'port': '50051', 'log_level': '2' - }, - 'certs': { - 'server_crt': '/etc/sonic/telemetry/streamingtelemetryserver.cer', - 'server_key': '/etc/sonic/telemetry/streamingtelemetryserver.key', - 'ca_crt': '/etc/sonic/telemetry/dsmsroot.cer' } } results['RESTAPI'] = { From 5a0a8e24b6dfb345f3cc5ee672a7bec237503d0c Mon Sep 17 00:00:00 2001 From: philo Date: Fri, 9 Jun 2023 11:02:51 +0800 Subject: [PATCH 2/7] Update kernel version Signed-off-by: philo --- platform/centec-arm64/sonic_fit.its | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/centec-arm64/sonic_fit.its b/platform/centec-arm64/sonic_fit.its index b9b62576cd71..ec07e4995d27 100644 --- a/platform/centec-arm64/sonic_fit.its +++ b/platform/centec-arm64/sonic_fit.its @@ -12,7 +12,7 @@ images { kernel_ctc { description = "ARM64 Kernel"; - data = /incbin/("./vmlinuz-5.10.0-8-2-arm64"); + data = /incbin/("./vmlinuz-5.10.0-18-2-arm64"); type = "kernel"; arch = "arm64"; os = "linux"; @@ -25,7 +25,7 @@ }; initramfs { description = "initramfs"; - data = /incbin/("./initrd.img-5.10.0-8-2-arm64"); + data = /incbin/("./initrd.img-5.10.0-18-2-arm64"); type = "ramdisk"; arch = "arm64"; os = "linux"; From 589368570fc5c18b020f061cc7f9b38f428da0ac Mon Sep 17 00:00:00 2001 From: philo Date: Thu, 7 Sep 2023 13:55:46 +0800 Subject: [PATCH 3/7] fix redfish log error Signed-off-by: philo --- .../common_custom/common_micas/lib/redfishutil/redfish_api.py | 4 ++-- .../m2-w6010-48gt4x-fa/sonic_platform/redfish_api.py | 4 ++-- .../m2-w6010-48gt4x-ra/sonic_platform/redfish_api.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/redfishutil/redfish_api.py b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/redfishutil/redfish_api.py index dd931e9acb41..76b2c1ba6a24 100644 --- a/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/redfishutil/redfish_api.py +++ b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/redfishutil/redfish_api.py @@ -88,13 +88,13 @@ def _redfish_post(self, url, playload): if ret_msg == None: return False elif ret_msg["success"] == False: - redfish_log_error("Url: '%s', Playload: '%s', Bmc return failed, error_message: %s" % (url, playload_json, ret_msg["Message"])) + self.redfish_log_error("Url: '%s', Playload: '%s', Bmc return failed, error_message: %s" % (url, playload_json, ret_msg["Message"])) result = False else: result = True return result except Exception as e: - redfish_log_error("error_message: %s" % e) + self.redfish_log_error("error_message: %s" % e) return False def get_thermal(self): diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/redfish_api.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/redfish_api.py index a577c1b216da..9d3cdd31436f 100644 --- a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/redfish_api.py +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-fa/sonic_platform/redfish_api.py @@ -88,13 +88,13 @@ def _redfish_post(self, url, playload): if ret_msg == None: return False elif ret_msg["success"] == False: - redfish_log_error("Url: '%s', Playload: '%s', Bmc return failed, error_message: %s" % (url, playload_json, ret_msg["Message"])) + self.redfish_log_error("Url: '%s', Playload: '%s', Bmc return failed, error_message: %s" % (url, playload_json, ret_msg["Message"])) result = False else: result = True return result except Exception as e: - redfish_log_error("error_message: %s" % e) + self.redfish_log_error("error_message: %s" % e) return False def get_thermal(self): diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/redfish_api.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/redfish_api.py index a577c1b216da..9d3cdd31436f 100644 --- a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/redfish_api.py +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/redfish_api.py @@ -88,13 +88,13 @@ def _redfish_post(self, url, playload): if ret_msg == None: return False elif ret_msg["success"] == False: - redfish_log_error("Url: '%s', Playload: '%s', Bmc return failed, error_message: %s" % (url, playload_json, ret_msg["Message"])) + self.redfish_log_error("Url: '%s', Playload: '%s', Bmc return failed, error_message: %s" % (url, playload_json, ret_msg["Message"])) result = False else: result = True return result except Exception as e: - redfish_log_error("error_message: %s" % e) + self.redfish_log_error("error_message: %s" % e) return False def get_thermal(self): From dd034b4ea8aacf82820af75f915e5355325547fb Mon Sep 17 00:00:00 2001 From: philo Date: Fri, 8 Sep 2023 09:06:56 +0800 Subject: [PATCH 4/7] update redfish_api.py Signed-off-by: philo --- .../m2-w6010-48gt4x-ra/sonic_platform/redfish_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/redfish_api.py b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/redfish_api.py index 9d3cdd31436f..49184830fa0e 100644 --- a/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/redfish_api.py +++ b/platform/centec-arm64/sonic-platform-modules-micas/m2-w6010-48gt4x-ra/sonic_platform/redfish_api.py @@ -94,7 +94,7 @@ def _redfish_post(self, url, playload): result = True return result except Exception as e: - self.redfish_log_error("error_message: %s" % e) + self.redfish_log_error("error_message: %s" % e) return False def get_thermal(self): From 49d1612d7fbdf385e284ce6df782ab3860655841 Mon Sep 17 00:00:00 2001 From: philo Date: Mon, 11 Sep 2023 11:35:31 +0800 Subject: [PATCH 5/7] update minigraph.py Signed-off-by: philo --- src/sonic-config-engine/minigraph.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sonic-config-engine/minigraph.py b/src/sonic-config-engine/minigraph.py index 188182b24ea6..a95b2698de14 100644 --- a/src/sonic-config-engine/minigraph.py +++ b/src/sonic-config-engine/minigraph.py @@ -1968,6 +1968,11 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw 'client_auth': 'true', 'port': '50051', 'log_level': '2' + }, + 'certs': { + 'server_crt': '/etc/sonic/telemetry/streamingtelemetryserver.cer', + 'server_key': '/etc/sonic/telemetry/streamingtelemetryserver.key', + 'ca_crt': '/etc/sonic/telemetry/dsmsroot.cer' } } results['RESTAPI'] = { From 4f2ba6dcb3a611e9c820c834756a8a9851b95c3f Mon Sep 17 00:00:00 2001 From: Philo <135693886+philo-micas@users.noreply.github.com> Date: Fri, 13 Oct 2023 17:24:47 +0800 Subject: [PATCH 6/7] Update redfish_api.py --- .../common_custom/common_micas/lib/redfishutil/redfish_api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/redfishutil/redfish_api.py b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/redfishutil/redfish_api.py index 76b2c1ba6a24..0eb7f57a11f2 100644 --- a/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/redfishutil/redfish_api.py +++ b/platform/centec-arm64/sonic-platform-modules-micas/common_custom/common_micas/lib/redfishutil/redfish_api.py @@ -7,6 +7,7 @@ import subprocess import syslog + class Redfish_Api(): BmcBaseUrl = 'http://240.1.1.1:8080' ThermalUrl = '/redfish/v1/Chassis/1/Thermal' From df90fa8395501499ead259a3ef3441134434c92c Mon Sep 17 00:00:00 2001 From: philo Date: Wed, 18 Oct 2023 15:24:45 +0800 Subject: [PATCH 7/7] fix community compile error Signed-off-by: philo --- files/build_templates/sonic_debian_extension.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/build_templates/sonic_debian_extension.j2 b/files/build_templates/sonic_debian_extension.j2 index aeae3d9ad630..cfe18138ccc3 100644 --- a/files/build_templates/sonic_debian_extension.j2 +++ b/files/build_templates/sonic_debian_extension.j2 @@ -340,7 +340,7 @@ sudo chmod 755 $FILESYSTEM_ROOT/usr/bin/restart_service sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install smartmontools=7.2-1 # Install custom-built openssh sshd -sudo dpkg --root=$FILESYSTEM_ROOT -i $debs_path/openssh-server_${OPENSSH_VERSION}_*.deb +#sudo dpkg --root=$FILESYSTEM_ROOT -i $debs_path/openssh-server_${OPENSSH_VERSION}_*.deb {% if sonic_asic_platform == 'broadcom' %} # Install custom-built flashrom